@markup-carve/carve-lsp 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/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/analyze.d.ts +6 -0
- package/dist/analyze.js +156 -0
- package/dist/analyze.js.map +1 -0
- package/dist/analyze.test.d.ts +1 -0
- package/dist/analyze.test.js +207 -0
- package/dist/analyze.test.js.map +1 -0
- package/dist/codelens.d.ts +7 -0
- package/dist/codelens.js +36 -0
- package/dist/codelens.js.map +1 -0
- package/dist/codelens.test.d.ts +1 -0
- package/dist/codelens.test.js +18 -0
- package/dist/codelens.test.js.map +1 -0
- package/dist/completion.d.ts +9 -0
- package/dist/completion.js +91 -0
- package/dist/completion.js.map +1 -0
- package/dist/completion.test.d.ts +1 -0
- package/dist/completion.test.js +35 -0
- package/dist/completion.test.js.map +1 -0
- package/dist/definition.d.ts +12 -0
- package/dist/definition.js +280 -0
- package/dist/definition.js.map +1 -0
- package/dist/definition.test.d.ts +1 -0
- package/dist/definition.test.js +136 -0
- package/dist/definition.test.js.map +1 -0
- package/dist/folding.d.ts +8 -0
- package/dist/folding.js +64 -0
- package/dist/folding.js.map +1 -0
- package/dist/folding.test.d.ts +1 -0
- package/dist/folding.test.js +20 -0
- package/dist/folding.test.js.map +1 -0
- package/dist/format.d.ts +1 -0
- package/dist/format.js +83 -0
- package/dist/format.js.map +1 -0
- package/dist/format.test.d.ts +1 -0
- package/dist/format.test.js +46 -0
- package/dist/format.test.js.map +1 -0
- package/dist/hover.d.ts +2 -0
- package/dist/hover.js +222 -0
- package/dist/hover.js.map +1 -0
- package/dist/migration-actions.d.ts +15 -0
- package/dist/migration-actions.js +79 -0
- package/dist/migration-actions.js.map +1 -0
- package/dist/references.d.ts +15 -0
- package/dist/references.js +430 -0
- package/dist/references.js.map +1 -0
- package/dist/references.test.d.ts +1 -0
- package/dist/references.test.js +159 -0
- package/dist/references.test.js.map +1 -0
- package/dist/rename.d.ts +8 -0
- package/dist/rename.js +66 -0
- package/dist/rename.js.map +1 -0
- package/dist/rename.test.d.ts +1 -0
- package/dist/rename.test.js +39 -0
- package/dist/rename.test.js.map +1 -0
- package/dist/semantic.d.ts +15 -0
- package/dist/semantic.js +458 -0
- package/dist/semantic.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +111 -0
- package/dist/server.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Scherer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# carve-lsp
|
|
2
|
+
|
|
3
|
+
Language server (LSP) for [Carve](https://markup-carve.github.io/carve/) markup documents.
|
|
4
|
+
Provides editor intelligence for `.crv` files via the
|
|
5
|
+
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @markup-carve/carve-lsp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or run without installing:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @markup-carve/carve-lsp --stdio
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The server communicates over **stdio** (`--stdio` flag).
|
|
20
|
+
|
|
21
|
+
## Supported capabilities
|
|
22
|
+
|
|
23
|
+
| Capability | Details |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Diagnostics | Syntax errors from the Carve parser; advisory warnings for Djot/Markdown delimiter collisions |
|
|
26
|
+
| Document symbols | Headings as an outline tree |
|
|
27
|
+
| Hover | Contextual information on hover |
|
|
28
|
+
| Completion | Trigger characters `:` `#` `^` `[` |
|
|
29
|
+
| Go to definition | Jump to heading / reference targets |
|
|
30
|
+
| Find references | All uses of a heading id or reference label |
|
|
31
|
+
| Rename | Prepare + apply renames across the document |
|
|
32
|
+
| Code actions | Migration quick-fixes for deprecated Carve syntax |
|
|
33
|
+
| Code lens | Inline annotations on headings and references |
|
|
34
|
+
| Folding ranges | Fold sections and block containers |
|
|
35
|
+
| Formatting | Format the whole document |
|
|
36
|
+
| Semantic tokens | Token-based syntax highlighting |
|
|
37
|
+
|
|
38
|
+
## Editor setup
|
|
39
|
+
|
|
40
|
+
### VS Code
|
|
41
|
+
|
|
42
|
+
Install the
|
|
43
|
+
[vscode-carve](https://marketplace.visualstudio.com/items?itemName=markup-carve.vscode-carve)
|
|
44
|
+
extension, which bundles and auto-starts this server.
|
|
45
|
+
|
|
46
|
+
For a generic LSP client (e.g.
|
|
47
|
+
[vscode-languageclient](https://marketplace.visualstudio.com/items?itemName=adamvoss.vscode-languageclient)),
|
|
48
|
+
add to `.vscode/settings.json`:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"languageServerExample.serverCommand": "carve-lsp",
|
|
53
|
+
"languageServerExample.serverArgs": ["--stdio"]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or wire it up in a custom extension:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const serverOptions: ServerOptions = {
|
|
61
|
+
command: 'carve-lsp',
|
|
62
|
+
args: ['--stdio'],
|
|
63
|
+
};
|
|
64
|
+
const clientOptions: LanguageClientOptions = {
|
|
65
|
+
documentSelector: [{ scheme: 'file', language: 'carve' }],
|
|
66
|
+
};
|
|
67
|
+
new LanguageClient('carve-lsp', 'Carve Language Server', serverOptions, clientOptions).start();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Neovim (nvim-lspconfig)
|
|
71
|
+
|
|
72
|
+
```lua
|
|
73
|
+
local lspconfig = require('lspconfig')
|
|
74
|
+
local configs = require('lspconfig.configs')
|
|
75
|
+
|
|
76
|
+
if not configs.carve_lsp then
|
|
77
|
+
configs.carve_lsp = {
|
|
78
|
+
default_config = {
|
|
79
|
+
cmd = { 'carve-lsp', '--stdio' },
|
|
80
|
+
filetypes = { 'carve', 'crv' },
|
|
81
|
+
root_dir = lspconfig.util.root_pattern('.git', '.'),
|
|
82
|
+
single_file_support = true,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
lspconfig.carve_lsp.setup({})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Add a filetype detection entry if your Neovim does not already recognize `.crv`:
|
|
91
|
+
|
|
92
|
+
```lua
|
|
93
|
+
vim.filetype.add({
|
|
94
|
+
extension = {
|
|
95
|
+
crv = 'carve',
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Other editors
|
|
101
|
+
|
|
102
|
+
Any editor with LSP support can start the server as an external process:
|
|
103
|
+
|
|
104
|
+
- **Command:** `carve-lsp --stdio`
|
|
105
|
+
- **File extension:** `.crv` (language ID: `carve`)
|
|
106
|
+
- **Root pattern:** `.git` or the project root
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm install
|
|
112
|
+
npm run build
|
|
113
|
+
npm test
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Run the server directly over stdio:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
node dist/server.js --stdio
|
|
120
|
+
```
|
package/dist/analyze.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { DiagnosticSeverity, SymbolKind, } from 'vscode-languageserver/node.js';
|
|
2
|
+
import { djotMigrationWarnings, lintCarve, parse, resolve, } from '@markup-carve/carve';
|
|
3
|
+
export function analyzeCarve(source) {
|
|
4
|
+
const diagnostics = [];
|
|
5
|
+
let doc = null;
|
|
6
|
+
try {
|
|
7
|
+
doc = resolve(parse(source));
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
diagnostics.push({
|
|
11
|
+
severity: DiagnosticSeverity.Error,
|
|
12
|
+
range: rangeAt(source, 0, 1),
|
|
13
|
+
source: 'carve',
|
|
14
|
+
message: error instanceof Error ? error.message : String(error),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
// warning.start/end are offsets into the line-ending-normalized source;
|
|
18
|
+
// positionAt over that same text yields the precise multi-character range
|
|
19
|
+
// for every rule (including `+` bullets, which the old fix-range lookup
|
|
20
|
+
// could not cover).
|
|
21
|
+
const norm = source.replace(/\r\n?/g, '\n');
|
|
22
|
+
// Diagnostics target hand-written Carve, so surface only the migration
|
|
23
|
+
// constructs that actually mis-render in Carve (`carve-breakage`). Djot
|
|
24
|
+
// semantic shifts that are valid Carve — `_x_` (underline), `~x~`
|
|
25
|
+
// (strikethrough), `{=x=}` (highlight) — are intentional here, not mistakes,
|
|
26
|
+
// so they must not raise editor diagnostics. Their quick fixes remain
|
|
27
|
+
// available through `migrationCodeActions` for anyone migrating from Djot.
|
|
28
|
+
for (const warning of djotMigrationWarnings(source)) {
|
|
29
|
+
if (warning.category !== 'carve-breakage')
|
|
30
|
+
continue;
|
|
31
|
+
diagnostics.push({
|
|
32
|
+
severity: DiagnosticSeverity.Warning,
|
|
33
|
+
range: {
|
|
34
|
+
start: positionAt(norm, warning.start),
|
|
35
|
+
end: positionAt(norm, warning.end),
|
|
36
|
+
},
|
|
37
|
+
source: 'carve',
|
|
38
|
+
code: warning.rule,
|
|
39
|
+
message: `${warning.message} Suggestion: ${warning.suggestion}`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Silent-failure lint: markup that parses but renders as the wrong thing
|
|
43
|
+
// (broken cross-references, duplicate heading ids, trailing heading
|
|
44
|
+
// attributes, legacy raw fences, leaked block markers).
|
|
45
|
+
for (const warning of lintCarve(source)) {
|
|
46
|
+
const len = Math.max(1, warning.end - warning.start);
|
|
47
|
+
diagnostics.push({
|
|
48
|
+
severity: DiagnosticSeverity.Warning,
|
|
49
|
+
range: {
|
|
50
|
+
start: { line: warning.line - 1, character: warning.column - 1 },
|
|
51
|
+
end: { line: warning.line - 1, character: warning.column - 1 + len },
|
|
52
|
+
},
|
|
53
|
+
source: 'carve',
|
|
54
|
+
code: warning.rule,
|
|
55
|
+
message: warning.message,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
diagnostics,
|
|
60
|
+
symbols: doc ? documentSymbols(doc) : [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function documentSymbols(doc) {
|
|
64
|
+
const stack = [];
|
|
65
|
+
const roots = [];
|
|
66
|
+
for (const heading of walkHeadings(doc.children)) {
|
|
67
|
+
const symbol = headingSymbol(heading);
|
|
68
|
+
while (stack.length && stack[stack.length - 1].level >= heading.level) {
|
|
69
|
+
stack.pop();
|
|
70
|
+
}
|
|
71
|
+
const parent = stack[stack.length - 1];
|
|
72
|
+
if (parent) {
|
|
73
|
+
parent.symbol.children ??= [];
|
|
74
|
+
parent.symbol.children.push(symbol);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
roots.push(symbol);
|
|
78
|
+
}
|
|
79
|
+
stack.push({ level: heading.level, symbol });
|
|
80
|
+
}
|
|
81
|
+
return roots;
|
|
82
|
+
}
|
|
83
|
+
function* walkHeadings(nodes) {
|
|
84
|
+
for (const node of nodes) {
|
|
85
|
+
if (node.type === 'heading')
|
|
86
|
+
yield node;
|
|
87
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
88
|
+
yield* walkHeadings(node.children.filter(isBlockNode));
|
|
89
|
+
}
|
|
90
|
+
if (node.type === 'figure') {
|
|
91
|
+
if ('children' in node.target && Array.isArray(node.target.children)) {
|
|
92
|
+
yield* walkHeadings(node.target.children.filter(isBlockNode));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function isBlockNode(node) {
|
|
98
|
+
return Boolean(node && typeof node === 'object' && 'type' in node);
|
|
99
|
+
}
|
|
100
|
+
function headingSymbol(heading) {
|
|
101
|
+
const range = heading.pos
|
|
102
|
+
? {
|
|
103
|
+
start: { line: heading.pos.startLine - 1, character: 0 },
|
|
104
|
+
end: { line: heading.pos.endLine - 1, character: 200 },
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
start: { line: 0, character: 0 },
|
|
108
|
+
end: { line: 0, character: 0 },
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
name: plainText(heading.children) || `Heading ${heading.level}`,
|
|
112
|
+
kind: SymbolKind.String,
|
|
113
|
+
range,
|
|
114
|
+
selectionRange: range,
|
|
115
|
+
children: [],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function plainText(nodes) {
|
|
119
|
+
let out = '';
|
|
120
|
+
for (const node of nodes) {
|
|
121
|
+
if (node.type === 'text')
|
|
122
|
+
out += node.value;
|
|
123
|
+
else if ('children' in node && Array.isArray(node.children)) {
|
|
124
|
+
out += plainText(node.children);
|
|
125
|
+
}
|
|
126
|
+
else if (node.type === 'code')
|
|
127
|
+
out += node.value;
|
|
128
|
+
else if (node.type === 'symbol')
|
|
129
|
+
out += `:${node.name}:`;
|
|
130
|
+
else if (node.type === 'mention')
|
|
131
|
+
out += `@${node.user}`;
|
|
132
|
+
else if (node.type === 'tag')
|
|
133
|
+
out += `#${node.name}`;
|
|
134
|
+
}
|
|
135
|
+
return out.trim();
|
|
136
|
+
}
|
|
137
|
+
function positionAt(source, offset) {
|
|
138
|
+
const before = source.slice(0, offset);
|
|
139
|
+
const line = before.split('\n').length - 1;
|
|
140
|
+
const lastNewline = before.lastIndexOf('\n');
|
|
141
|
+
return {
|
|
142
|
+
line,
|
|
143
|
+
character: offset - lastNewline - 1,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function rangeAt(source, index, length) {
|
|
147
|
+
const before = source.slice(0, index);
|
|
148
|
+
const line = before.split('\n').length - 1;
|
|
149
|
+
const lastNewline = before.lastIndexOf('\n');
|
|
150
|
+
const character = index - lastNewline - 1;
|
|
151
|
+
return {
|
|
152
|
+
start: { line, character },
|
|
153
|
+
end: { line, character: character + length },
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { analyzeCarve } from './analyze.js';
|
|
4
|
+
import { hoverAt } from './hover.js';
|
|
5
|
+
import { migrationCodeActions, migrationFixes } from './migration-actions.js';
|
|
6
|
+
import { semanticTokens } from './semantic.js';
|
|
7
|
+
test('returns heading document symbols', () => {
|
|
8
|
+
const result = analyzeCarve('# One\n\n## Two\n');
|
|
9
|
+
assert.equal(result.symbols.length, 1);
|
|
10
|
+
assert.equal(result.symbols[0].name, 'One');
|
|
11
|
+
assert.equal(result.symbols[0].children?.[0]?.name, 'Two');
|
|
12
|
+
});
|
|
13
|
+
test('reports carve-breakage migration warnings', () => {
|
|
14
|
+
const result = analyzeCarve('**bold**');
|
|
15
|
+
assert.equal(result.diagnostics.length, 1);
|
|
16
|
+
assert.equal(result.diagnostics[0].code, 'markdown-strong-double-star');
|
|
17
|
+
assert.deepEqual(result.diagnostics[0].range, {
|
|
18
|
+
start: { line: 0, character: 0 },
|
|
19
|
+
end: { line: 0, character: 8 },
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
test('does not diagnose djot-shift constructs (valid Carve)', () => {
|
|
23
|
+
// `_x_` (underline), `~x~` (strikethrough) and `{=x=}` (highlight) are valid
|
|
24
|
+
// Carve; on a hand-written document they are intentional, not mistakes, so
|
|
25
|
+
// they raise no diagnostic. See carve lint's default --from-djot gate.
|
|
26
|
+
for (const src of ['_italic_', 'H~2~O', '{=mark=}']) {
|
|
27
|
+
assert.equal(analyzeCarve(src).diagnostics.length, 0, `expected no diagnostic for ${JSON.stringify(src)}`);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
test('surfaces lintCarve silent-failure warnings as diagnostics', () => {
|
|
31
|
+
const result = analyzeCarve('# Intro\n\nSee </#nope>.');
|
|
32
|
+
const lint = result.diagnostics.find((d) => d.code === 'broken-crossref');
|
|
33
|
+
assert.ok(lint, 'expected a broken-crossref diagnostic');
|
|
34
|
+
assert.equal(lint.source, 'carve');
|
|
35
|
+
assert.equal(lint.range.start.line, 2);
|
|
36
|
+
});
|
|
37
|
+
test('returns quick fixes for migration warnings', () => {
|
|
38
|
+
const source = '**bold** and ~~strike~~';
|
|
39
|
+
const diagnostics = analyzeCarve(source).diagnostics;
|
|
40
|
+
const actions = migrationCodeActions('file:///demo.crv', source, diagnostics);
|
|
41
|
+
// Two per-diagnostic quick fixes, plus the document-wide "fix all" action.
|
|
42
|
+
assert.equal(actions.length, 3);
|
|
43
|
+
assert.equal(actions[0].title, 'Convert to Carve syntax: *bold*');
|
|
44
|
+
assert.deepEqual(actions[0].edit?.changes?.['file:///demo.crv']?.[0], {
|
|
45
|
+
range: {
|
|
46
|
+
start: { line: 0, character: 0 },
|
|
47
|
+
end: { line: 0, character: 8 },
|
|
48
|
+
},
|
|
49
|
+
newText: '*bold*',
|
|
50
|
+
});
|
|
51
|
+
const fixAll = actions.find((action) => action.title.startsWith('Fix all'));
|
|
52
|
+
assert.ok(fixAll);
|
|
53
|
+
assert.equal(fixAll.edit?.changes?.['file:///demo.crv']?.[0]?.newText, '*bold* and ~strike~');
|
|
54
|
+
});
|
|
55
|
+
test('migration quick fixes use canonical single-char Carve delimiters', () => {
|
|
56
|
+
// Guards the delimiter targets the carve-js library hands the LSP through
|
|
57
|
+
// `warning.suggestion`. Carve highlight is a single `=`, and a *doubled*
|
|
58
|
+
// `==x==` is literal text by the same-delimiter-adjacency rule. Subscript
|
|
59
|
+
// and superscript are braced-only: there is no bare `,x,` / `^x^` form (a
|
|
60
|
+
// bare comma or caret is always literal text), so the only spelling is the
|
|
61
|
+
// brace-forced `{,x,}` / `{^x^}`. Suggestions are therefore `{=x=}` and
|
|
62
|
+
// `{,x,}`, never a bare or doubled delimiter form.
|
|
63
|
+
const cases = [
|
|
64
|
+
// Djot highlight `{=x=}` is also valid Carve highlight: kept as the
|
|
65
|
+
// identity brace form, NOT reduced to a doubled `==x==` (which is literal).
|
|
66
|
+
{ source: '{=mark=}', code: 'djot-highlight-braces', newText: '{=mark=}' },
|
|
67
|
+
// Djot subscript `~x~` would render as Carve strikethrough; the gated
|
|
68
|
+
// `djot-subscript-tilde` rule rewrites it to the brace-forced subscript.
|
|
69
|
+
{ source: 'H~2~O', code: 'djot-subscript-tilde', newText: '{,2,}' },
|
|
70
|
+
];
|
|
71
|
+
// These are djot-shift constructs, so `analyzeCarve` raises no diagnostic for
|
|
72
|
+
// them by default. The suggestion text they carry still has to be canonical,
|
|
73
|
+
// so assert it on `migrationFixes` (the full rule set) directly — that is
|
|
74
|
+
// what feeds the --from-djot quick fixes.
|
|
75
|
+
for (const { source, code, newText } of cases) {
|
|
76
|
+
const fix = migrationFixes(source).find((f) => f.rule === code);
|
|
77
|
+
assert.ok(fix, `expected a ${code} fix for ${JSON.stringify(source)}`);
|
|
78
|
+
assert.equal(fix.suggestion, newText);
|
|
79
|
+
// Never emit the literal doubled delimiter forms.
|
|
80
|
+
assert.doesNotMatch(fix.suggestion, /==|,,/);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
test('offers a quick fix for + bullets (was missing from the old span map)', () => {
|
|
84
|
+
const source = '+ item';
|
|
85
|
+
const diagnostics = analyzeCarve(source).diagnostics;
|
|
86
|
+
assert.equal(diagnostics[0].code, 'djot-plus-bullet');
|
|
87
|
+
const actions = migrationCodeActions('file:///demo.crv', source, diagnostics);
|
|
88
|
+
const quickFix = actions.find((a) => a.title === 'Convert to Carve syntax: -');
|
|
89
|
+
assert.ok(quickFix);
|
|
90
|
+
assert.deepEqual(quickFix.edit?.changes?.['file:///demo.crv']?.[0], {
|
|
91
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
|
|
92
|
+
newText: '-',
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
test('returns hover help for inline markup', () => {
|
|
96
|
+
const hover = hoverAt('Use /italic/ text', { line: 0, character: 6 });
|
|
97
|
+
assert.ok(hover);
|
|
98
|
+
assert.equal(typeof hover.contents, 'object');
|
|
99
|
+
assert.ok(!Array.isArray(hover.contents));
|
|
100
|
+
assert.notEqual(typeof hover.contents, 'string');
|
|
101
|
+
const contents = hover.contents;
|
|
102
|
+
assert.match(contents.value ?? '', /Italic/);
|
|
103
|
+
});
|
|
104
|
+
test('returns semantic tokens for headings and inline markup', () => {
|
|
105
|
+
const result = semanticTokens('# Title\n\nHi *bold* @mark {#id}\n');
|
|
106
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [
|
|
107
|
+
[0, 0, 1, 'operator'],
|
|
108
|
+
[0, 2, 5, 'type'],
|
|
109
|
+
[2, 3, 6, 'keyword'],
|
|
110
|
+
[2, 10, 5, 'variable'],
|
|
111
|
+
// carve-js parses the trailing `{#id}` here as a literal `#id` hashtag
|
|
112
|
+
// (tag node) rather than an attribute attachment, so it surfaces as a
|
|
113
|
+
// `variable` token like any other hashtag.
|
|
114
|
+
[2, 17, 3, 'variable'],
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
test('highlights trailing %% comment from the marker to end of line', () => {
|
|
118
|
+
const result = semanticTokens('Hello %% this is a comment\n');
|
|
119
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 6, 20, 'comment']]);
|
|
120
|
+
});
|
|
121
|
+
test('does not treat %% without preceding space or tab as a comment', () => {
|
|
122
|
+
const noSpace = semanticTokens('50%% off\n');
|
|
123
|
+
assert.deepEqual(noSpace.map((t) => t.type), []);
|
|
124
|
+
const midWord = semanticTokens('a%%b\n');
|
|
125
|
+
assert.deepEqual(midWord.map((t) => t.type), []);
|
|
126
|
+
});
|
|
127
|
+
test('does not treat escaped %% as a trailing comment', () => {
|
|
128
|
+
// backslash before %% prevents comment
|
|
129
|
+
const result = semanticTokens('literal \x5c%% stuff\n');
|
|
130
|
+
assert.deepEqual(result.filter((t) => t.type === 'comment'), []);
|
|
131
|
+
});
|
|
132
|
+
test('does not treat %% inside a backtick code span as a trailing comment', () => {
|
|
133
|
+
const result = semanticTokens('\x60code %%\x60 text\n');
|
|
134
|
+
// only the code span token, no comment token
|
|
135
|
+
assert.equal(result.length, 1);
|
|
136
|
+
assert.equal(result[0].type, 'string');
|
|
137
|
+
});
|
|
138
|
+
test('highlights trailing %% preceded by a tab', () => {
|
|
139
|
+
const result = semanticTokens('Hello\t%% comment\n');
|
|
140
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 6, 10, 'comment']]);
|
|
141
|
+
});
|
|
142
|
+
test('returns semantic tokens for fenced blocks', () => {
|
|
143
|
+
const result = semanticTokens('``` js\nconst x = 1\n```\n');
|
|
144
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [
|
|
145
|
+
[0, 0, 6, 'string'],
|
|
146
|
+
[1, 0, 11, 'string'],
|
|
147
|
+
[2, 0, 3, 'string'],
|
|
148
|
+
]);
|
|
149
|
+
});
|
|
150
|
+
test('returns semantic tokens for critic insert markup', () => {
|
|
151
|
+
const result = semanticTokens('{+ inserted text +}');
|
|
152
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 19, 'type']]);
|
|
153
|
+
});
|
|
154
|
+
test('returns semantic tokens for critic delete markup', () => {
|
|
155
|
+
const result = semanticTokens('{- removed text -}');
|
|
156
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 18, 'comment']]);
|
|
157
|
+
});
|
|
158
|
+
test('returns semantic tokens for critic substitute markup', () => {
|
|
159
|
+
const result = semanticTokens('{~ old ~>new ~}');
|
|
160
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 15, 'keyword']]);
|
|
161
|
+
});
|
|
162
|
+
test('returns semantic tokens for critic annotation markup', () => {
|
|
163
|
+
const result = semanticTokens('{= highlight =}');
|
|
164
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 15, 'keyword']]);
|
|
165
|
+
});
|
|
166
|
+
test('returns semantic tokens for critic comment markup', () => {
|
|
167
|
+
const result = semanticTokens('{# a note #}');
|
|
168
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 12, 'comment']]);
|
|
169
|
+
});
|
|
170
|
+
test('returns semantic tokens for display math', () => {
|
|
171
|
+
const result = semanticTokens('$$E=mc2$$');
|
|
172
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 9, 'string']]);
|
|
173
|
+
});
|
|
174
|
+
test('returns semantic tokens for inline math', () => {
|
|
175
|
+
const result = semanticTokens('$x=1$');
|
|
176
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 5, 'string']]);
|
|
177
|
+
});
|
|
178
|
+
test('returns semantic tokens for reference definition', () => {
|
|
179
|
+
const result = semanticTokens('[carve]: https://example.com');
|
|
180
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [
|
|
181
|
+
[0, 0, 1, 'operator'],
|
|
182
|
+
[0, 1, 5, 'type'],
|
|
183
|
+
[0, 6, 3, 'operator'],
|
|
184
|
+
[0, 9, 19, 'string'],
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
test('returns semantic tokens for footnote definition', () => {
|
|
188
|
+
const result = semanticTokens('[^fn]: footnote text');
|
|
189
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [
|
|
190
|
+
[0, 0, 2, 'operator'],
|
|
191
|
+
[0, 2, 2, 'type'],
|
|
192
|
+
[0, 4, 3, 'operator'],
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
test('returns semantic tokens for abbreviation definition', () => {
|
|
196
|
+
const result = semanticTokens('*[HTML]: HyperText Markup Language');
|
|
197
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [[0, 0, 34, 'property']]);
|
|
198
|
+
});
|
|
199
|
+
test('returns semantic tokens for YAML frontmatter', () => {
|
|
200
|
+
const result = semanticTokens('---\ntitle: Test\n---\n');
|
|
201
|
+
assert.deepEqual(result.map((token) => [token.line, token.character, token.length, token.type]), [
|
|
202
|
+
[0, 0, 3, 'string'],
|
|
203
|
+
[1, 0, 11, 'string'],
|
|
204
|
+
[2, 0, 3, 'string'],
|
|
205
|
+
]);
|
|
206
|
+
});
|
|
207
|
+
//# sourceMappingURL=analyze.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.test.js","sourceRoot":"","sources":["../src/analyze.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type CodeLens } from 'vscode-languageserver/node.js';
|
|
2
|
+
/**
|
|
3
|
+
* A non-interactive code lens above each footnote definition showing how many
|
|
4
|
+
* times it is referenced in the document. A definition with zero references is
|
|
5
|
+
* dropped by the renderer, so the count doubles as a dead-footnote hint.
|
|
6
|
+
*/
|
|
7
|
+
export declare function codeLenses(source: string): CodeLens[];
|
package/dist/codelens.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A non-interactive code lens above each footnote definition showing how many
|
|
3
|
+
* times it is referenced in the document. A definition with zero references is
|
|
4
|
+
* dropped by the renderer, so the count doubles as a dead-footnote hint.
|
|
5
|
+
*/
|
|
6
|
+
export function codeLenses(source) {
|
|
7
|
+
const lines = source.split(/\r?\n/);
|
|
8
|
+
// Count references: `[^label]` occurrences that are not a definition (`[^label]:`).
|
|
9
|
+
const refCounts = new Map();
|
|
10
|
+
lines.forEach((line) => {
|
|
11
|
+
for (const m of line.matchAll(/\[\^([^\]\s]+)\](?!:)/g)) {
|
|
12
|
+
refCounts.set(m[1], (refCounts.get(m[1]) ?? 0) + 1);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
const lenses = [];
|
|
16
|
+
lines.forEach((line, lineNo) => {
|
|
17
|
+
const def = /^( {0,3})\[\^([^\]\s]+)\]:/.exec(line);
|
|
18
|
+
if (!def)
|
|
19
|
+
return;
|
|
20
|
+
const label = def[2];
|
|
21
|
+
const count = refCounts.get(label) ?? 0;
|
|
22
|
+
const start = def[1].length;
|
|
23
|
+
lenses.push({
|
|
24
|
+
range: {
|
|
25
|
+
start: { line: lineNo, character: start },
|
|
26
|
+
end: { line: lineNo, character: start + label.length + 3 },
|
|
27
|
+
},
|
|
28
|
+
command: {
|
|
29
|
+
title: count === 1 ? '1 reference' : `${count} references`,
|
|
30
|
+
command: '',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return lenses;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=codelens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codelens.js","sourceRoot":"","sources":["../src/codelens.ts"],"names":[],"mappings":"AAEA;;;;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,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { codeLenses } from './codelens.js';
|
|
4
|
+
test('counts references for each footnote definition', () => {
|
|
5
|
+
const source = 'a[^x] b[^x]\n\n[^x]: defined\n[^y]: orphan';
|
|
6
|
+
const lenses = codeLenses(source);
|
|
7
|
+
const titles = lenses.map((l) => l.command?.title);
|
|
8
|
+
assert.ok(titles.includes('2 references'));
|
|
9
|
+
assert.ok(titles.includes('0 references'));
|
|
10
|
+
});
|
|
11
|
+
test('uses the singular form for a single reference', () => {
|
|
12
|
+
const lenses = codeLenses('a[^x]\n\n[^x]: defined');
|
|
13
|
+
assert.equal(lenses[0]?.command?.title, '1 reference');
|
|
14
|
+
});
|
|
15
|
+
test('emits no lenses without footnote definitions', () => {
|
|
16
|
+
assert.deepEqual(codeLenses('just prose with [^ref] but no def line that matches? [^ref]'), []);
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=codelens.test.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CompletionItem, type Position } from 'vscode-languageserver/node.js';
|
|
2
|
+
/**
|
|
3
|
+
* Context-aware completions driven by the text immediately before the cursor:
|
|
4
|
+
* - `::: ` opens an admonition -> canonical kinds
|
|
5
|
+
* - `</#` cross-reference -> heading ids in the document
|
|
6
|
+
* - `[^` footnote reference -> defined footnote labels
|
|
7
|
+
* - `][` reference link -> defined link reference labels
|
|
8
|
+
*/
|
|
9
|
+
export declare function completionAt(source: string, position: Position): CompletionItem[];
|