@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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/analyze.d.ts +6 -0
  4. package/dist/analyze.js +156 -0
  5. package/dist/analyze.js.map +1 -0
  6. package/dist/analyze.test.d.ts +1 -0
  7. package/dist/analyze.test.js +207 -0
  8. package/dist/analyze.test.js.map +1 -0
  9. package/dist/codelens.d.ts +7 -0
  10. package/dist/codelens.js +36 -0
  11. package/dist/codelens.js.map +1 -0
  12. package/dist/codelens.test.d.ts +1 -0
  13. package/dist/codelens.test.js +18 -0
  14. package/dist/codelens.test.js.map +1 -0
  15. package/dist/completion.d.ts +9 -0
  16. package/dist/completion.js +91 -0
  17. package/dist/completion.js.map +1 -0
  18. package/dist/completion.test.d.ts +1 -0
  19. package/dist/completion.test.js +35 -0
  20. package/dist/completion.test.js.map +1 -0
  21. package/dist/definition.d.ts +12 -0
  22. package/dist/definition.js +280 -0
  23. package/dist/definition.js.map +1 -0
  24. package/dist/definition.test.d.ts +1 -0
  25. package/dist/definition.test.js +136 -0
  26. package/dist/definition.test.js.map +1 -0
  27. package/dist/folding.d.ts +8 -0
  28. package/dist/folding.js +64 -0
  29. package/dist/folding.js.map +1 -0
  30. package/dist/folding.test.d.ts +1 -0
  31. package/dist/folding.test.js +20 -0
  32. package/dist/folding.test.js.map +1 -0
  33. package/dist/format.d.ts +1 -0
  34. package/dist/format.js +83 -0
  35. package/dist/format.js.map +1 -0
  36. package/dist/format.test.d.ts +1 -0
  37. package/dist/format.test.js +46 -0
  38. package/dist/format.test.js.map +1 -0
  39. package/dist/hover.d.ts +2 -0
  40. package/dist/hover.js +222 -0
  41. package/dist/hover.js.map +1 -0
  42. package/dist/migration-actions.d.ts +15 -0
  43. package/dist/migration-actions.js +79 -0
  44. package/dist/migration-actions.js.map +1 -0
  45. package/dist/references.d.ts +15 -0
  46. package/dist/references.js +430 -0
  47. package/dist/references.js.map +1 -0
  48. package/dist/references.test.d.ts +1 -0
  49. package/dist/references.test.js +159 -0
  50. package/dist/references.test.js.map +1 -0
  51. package/dist/rename.d.ts +8 -0
  52. package/dist/rename.js +66 -0
  53. package/dist/rename.js.map +1 -0
  54. package/dist/rename.test.d.ts +1 -0
  55. package/dist/rename.test.js +39 -0
  56. package/dist/rename.test.js.map +1 -0
  57. package/dist/semantic.d.ts +15 -0
  58. package/dist/semantic.js +458 -0
  59. package/dist/semantic.js.map +1 -0
  60. package/dist/server.d.ts +2 -0
  61. package/dist/server.js +111 -0
  62. package/dist/server.js.map +1 -0
  63. 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
+ ```
@@ -0,0 +1,6 @@
1
+ import { DocumentSymbol, type Diagnostic } from 'vscode-languageserver/node.js';
2
+ export interface Analysis {
3
+ diagnostics: Diagnostic[];
4
+ symbols: DocumentSymbol[];
5
+ }
6
+ export declare function analyzeCarve(source: string): Analysis;
@@ -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[];
@@ -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[];