@formspec/language-server 0.1.0-alpha.16 → 0.1.0-alpha.19

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 CHANGED
@@ -1,96 +1,31 @@
1
1
  # @formspec/language-server
2
2
 
3
- Language server protocol (LSP) features for FormSpec, providing editor intelligence for JSDoc constraint tags.
3
+ Language-server support for FormSpec TSDoc tags.
4
4
 
5
- ## Installation
5
+ ## Install
6
6
 
7
7
  ```bash
8
- npm install @formspec/language-server
9
- # or
10
8
  pnpm add @formspec/language-server
11
9
  ```
12
10
 
13
- ## Requirements
11
+ ## Features
14
12
 
15
- This package is ESM-only and requires:
13
+ - completion items for FormSpec tags
14
+ - hover documentation for recognized tags
15
+ - go-to-definition support for known tags
16
16
 
17
- ```json
18
- // package.json
19
- {
20
- "type": "module"
21
- }
22
- ```
23
-
24
- ```json
25
- // tsconfig.json
26
- {
27
- "compilerOptions": {
28
- "module": "NodeNext",
29
- "moduleResolution": "NodeNext"
30
- }
31
- }
32
- ```
33
-
34
- ## Overview
35
-
36
- This package provides language server features for FormSpec's JSDoc constraint tags (`@Minimum`, `@Maximum`, `@Pattern`, etc.). It can be integrated into any LSP-compatible editor.
37
-
38
- ### Features
39
-
40
- - **Completion** — Autocomplete for constraint tag names inside JSDoc comments
41
- - **Hover** — Documentation on hover for constraint tags
42
- - **Go to Definition** — Navigate to constraint definitions _(placeholder — not yet implemented)_
43
-
44
- ## API Reference
45
-
46
- ### Functions
47
-
48
- | Function | Description |
49
- | ------------------------- | ----------------------------------------------- |
50
- | `createServer()` | Create a full LSP server connection |
51
- | `getCompletionItems()` | Get completion items for constraint tags |
52
- | `getDefinition()` | Get definition location for a constraint tag |
53
- | `getHoverForTag(tagName)` | Get hover information for a constraint tag name |
54
-
55
- ### `createServer()`
56
-
57
- Creates a Language Server Protocol connection that handles `initialize`, `textDocument/completion`, `textDocument/hover`, and `textDocument/definition` requests.
17
+ Diagnostics are intentionally handled elsewhere; this package focuses on editor assistance.
58
18
 
59
- ```typescript
60
- import { createServer } from "@formspec/language-server";
19
+ ## Usage
61
20
 
62
- const connection = createServer();
63
- connection.listen();
64
- ```
65
-
66
- ### `getCompletionItems()`
67
-
68
- Returns completion items for all known FormSpec constraint tags.
69
-
70
- ```typescript
71
- import { getCompletionItems } from "@formspec/language-server";
72
-
73
- const items = getCompletionItems();
74
- // [{ label: "@Minimum", kind: CompletionItemKind.Keyword, ... }, ...]
75
- ```
21
+ ```ts
22
+ import { createServer, getCompletionItems, getHoverForTag } from "@formspec/language-server";
76
23
 
77
- ### `getHoverForTag(tagName)`
78
-
79
- Returns hover documentation for a given tag name, or `null` if the tag is not recognized.
80
-
81
- ```typescript
82
- import { getHoverForTag } from "@formspec/language-server";
83
-
84
- const hover = getHoverForTag("Minimum");
85
- // { contents: { kind: "markdown", value: "..." } }
24
+ const server = createServer();
25
+ const completions = getCompletionItems();
26
+ const hover = getHoverForTag("minimum");
86
27
  ```
87
28
 
88
- ## Editor Integration
89
-
90
- ### VS Code
91
-
92
- Use with a VS Code extension that connects to the language server. The server communicates over the standard LSP protocol via `vscode-languageserver/node.js`.
93
-
94
29
  ## License
95
30
 
96
31
  UNLICENSED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/server.test.ts"],"names":[],"mappings":""}
package/dist/index.cjs CHANGED
@@ -48,12 +48,22 @@ var CONSTRAINT_DETAIL = {
48
48
  enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions ["a","b","c"]`',
49
49
  const: 'Require a constant JSON value. Example: `@const "USD"`'
50
50
  };
51
- function getCompletionItems() {
52
- return Object.keys(import_core.BUILTIN_CONSTRAINT_DEFINITIONS).map((name) => ({
53
- label: `@${name}`,
54
- kind: import_node.CompletionItemKind.Keyword,
55
- detail: CONSTRAINT_DETAIL[name]
56
- }));
51
+ function getCompletionItems(extensions) {
52
+ const builtins = Object.keys(import_core.BUILTIN_CONSTRAINT_DEFINITIONS).map(
53
+ (name) => ({
54
+ label: `@${name}`,
55
+ kind: import_node.CompletionItemKind.Keyword,
56
+ detail: CONSTRAINT_DETAIL[name]
57
+ })
58
+ );
59
+ const customItems = extensions?.flatMap(
60
+ (extension) => (extension.constraintTags ?? []).map((tag) => ({
61
+ label: `@${tag.tagName}`,
62
+ kind: import_node.CompletionItemKind.Keyword,
63
+ detail: `Extension constraint tag from ${extension.extensionId}`
64
+ }))
65
+ ) ?? [];
66
+ return [...builtins, ...customItems];
57
67
  }
58
68
 
59
69
  // src/providers/hover.ts
@@ -229,11 +239,31 @@ var CONSTRAINT_HOVER_DOCS = {
229
239
  "```"
230
240
  ].join("\n")
231
241
  };
232
- function getHoverForTag(tagName) {
242
+ function getHoverForTag(tagName, extensions) {
233
243
  const raw = tagName.startsWith("@") ? tagName.slice(1) : tagName;
234
244
  const name = (0, import_core2.normalizeConstraintTagName)(raw);
235
245
  if (!(0, import_core2.isBuiltinConstraintName)(name)) {
236
- return null;
246
+ const registration = extensions?.flatMap(
247
+ (extension) => (extension.constraintTags ?? []).map((tag) => ({
248
+ extensionId: extension.extensionId,
249
+ tag
250
+ }))
251
+ ).find(({ tag }) => tag.tagName === name);
252
+ if (registration === void 0) {
253
+ return null;
254
+ }
255
+ return {
256
+ contents: {
257
+ kind: "markdown",
258
+ value: [
259
+ `**@${registration.tag.tagName}** \`<value>\``,
260
+ "",
261
+ `Extension-defined constraint tag from \`${registration.extensionId}\`.`,
262
+ "",
263
+ "Validated through the registered FormSpec extension surface."
264
+ ].join("\n")
265
+ }
266
+ };
237
267
  }
238
268
  return {
239
269
  contents: {
@@ -249,7 +279,7 @@ function getDefinition() {
249
279
  }
250
280
 
251
281
  // src/server.ts
252
- function createServer() {
282
+ function createServer(options = {}) {
253
283
  const connection = (0, import_node2.createConnection)(import_node2.ProposedFeatures.all);
254
284
  connection.onInitialize(() => {
255
285
  return {
@@ -269,10 +299,10 @@ function createServer() {
269
299
  };
270
300
  });
271
301
  connection.onCompletion(() => {
272
- return getCompletionItems();
302
+ return getCompletionItems(options.extensions);
273
303
  });
274
304
  connection.onHover((_params) => {
275
- return getHoverForTag("");
305
+ return getHoverForTag("", options.extensions);
276
306
  });
277
307
  connection.onDefinition((_params) => {
278
308
  return getDefinition();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/providers/completion.ts","../src/providers/hover.ts","../src/providers/definition.ts"],"sourcesContent":["/**\n * \\@formspec/language-server\n *\n * Language server for FormSpec — provides completions, hover documentation,\n * and go-to-definition for FormSpec JSDoc constraint tags (`@Minimum`,\n * `@Maximum`, `@Pattern`, etc.) in TypeScript files.\n *\n * This package implements the Language Server Protocol (LSP) using the\n * `vscode-languageserver` library. Constraint names are sourced from\n * `BUILTIN_CONSTRAINT_DEFINITIONS` in `@formspec/core`, ensuring the\n * language server stays in sync with the single source of truth.\n *\n * Diagnostics are intentionally omitted per design decision A7.\n *\n * @example\n * ```ts\n * import { createServer } from '@formspec/language-server';\n *\n * const connection = createServer();\n * connection.listen();\n * ```\n *\n * @packageDocumentation\n */\n\nexport { createServer } from \"./server.js\";\nexport { getCompletionItems } from \"./providers/completion.js\";\nexport { getHoverForTag } from \"./providers/hover.js\";\nexport { getDefinition } from \"./providers/definition.js\";\n","/**\n * FormSpec Language Server\n *\n * Sets up an LSP server connection and registers handlers for:\n * - `textDocument/completion` — FormSpec JSDoc constraint tag completions\n * - `textDocument/hover` — Documentation for recognized constraint tags\n * - `textDocument/definition` — Go-to-definition (stub, returns null)\n *\n * Diagnostics are intentionally omitted per design decision A7.\n */\n\nimport {\n createConnection,\n ProposedFeatures,\n TextDocumentSyncKind,\n type Connection,\n type InitializeResult,\n} from \"vscode-languageserver/node.js\";\nimport { getCompletionItems } from \"./providers/completion.js\";\nimport { getHoverForTag } from \"./providers/hover.js\";\nimport { getDefinition } from \"./providers/definition.js\";\n\n/**\n * Creates and configures the FormSpec language server connection.\n *\n * Registers LSP capability handlers and returns the connection.\n * Call `connection.listen()` to start accepting messages.\n *\n * @returns The configured LSP connection (not yet listening)\n */\nexport function createServer(): Connection {\n const connection = createConnection(ProposedFeatures.all);\n\n connection.onInitialize((): InitializeResult => {\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n completionProvider: {\n // Trigger completions inside JSDoc comments when `@` is typed\n triggerCharacters: [\"@\"],\n },\n hoverProvider: true,\n definitionProvider: true,\n },\n serverInfo: {\n name: \"formspec-language-server\",\n version: \"0.1.0\",\n },\n };\n });\n\n connection.onCompletion(() => {\n // Return all FormSpec constraint tag completions.\n // Future phases will add context-aware filtering based on field type and\n // cursor position within JSDoc comment ranges.\n return getCompletionItems();\n });\n\n connection.onHover((_params) => {\n // Extract the word under the cursor and look up hover documentation.\n // This is a stub — precise JSDoc token detection (checking that the\n // cursor is within a JSDoc comment and extracting the tag name) will be\n // added in a future phase.\n //\n // For now we return null to signal no hover is available until the\n // token extraction is implemented.\n return getHoverForTag(\"\");\n });\n\n connection.onDefinition((_params) => {\n // Go-to-definition is not yet implemented.\n return getDefinition();\n });\n\n return connection;\n}\n","/**\n * Completion provider for FormSpec JSDoc constraint tags.\n *\n * Returns completion items for all recognized FormSpec JSDoc constraint tags\n * (e.g., `@minimum`, `@maximum`, `@pattern`), derived from\n * `BUILTIN_CONSTRAINT_DEFINITIONS`. This is a skeleton — context-aware\n * filtering will be added in a future phase.\n */\n\nimport { BUILTIN_CONSTRAINT_DEFINITIONS, type BuiltinConstraintName } from \"@formspec/core\";\nimport { CompletionItem, CompletionItemKind } from \"vscode-languageserver/node.js\";\n\n/**\n * Human-readable detail strings for each built-in constraint tag.\n *\n * Keys match the camelCase constraint names (matching keys in `BUILTIN_CONSTRAINT_DEFINITIONS`).\n * Values are shown as the detail string in completion items.\n */\nconst CONSTRAINT_DETAIL: Record<BuiltinConstraintName, string> = {\n minimum: \"Minimum numeric value (inclusive). Example: `@minimum 0`\",\n maximum: \"Maximum numeric value (inclusive). Example: `@maximum 100`\",\n exclusiveMinimum: \"Minimum numeric value (exclusive). Example: `@exclusiveMinimum 0`\",\n exclusiveMaximum: \"Maximum numeric value (exclusive). Example: `@exclusiveMaximum 100`\",\n multipleOf: \"Value must be a multiple of this number. Example: `@multipleOf 0.01`\",\n minLength: \"Minimum string length. Example: `@minLength 1`\",\n maxLength: \"Maximum string length. Example: `@maxLength 255`\",\n minItems: \"Minimum number of array items. Example: `@minItems 1`\",\n maxItems: \"Maximum number of array items. Example: `@maxItems 10`\",\n uniqueItems: \"Require all array items to be distinct. Example: `@uniqueItems`\",\n pattern: \"Regular expression pattern for string validation. Example: `@pattern ^[a-z]+$`\",\n enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions [\"a\",\"b\",\"c\"]`',\n const: 'Require a constant JSON value. Example: `@const \"USD\"`',\n};\n\n/**\n * Returns completion items for all FormSpec JSDoc constraint tags.\n *\n * Items are derived from `BUILTIN_CONSTRAINT_DEFINITIONS`, ensuring this list\n * stays in sync with the single source of truth in `@formspec/core`.\n *\n * Each item uses `CompletionItemKind.Keyword` since these are annotation\n * tags used within JSDoc comments rather than language symbols.\n *\n * @returns An array of LSP completion items for FormSpec constraint tags\n */\nexport function getCompletionItems(): CompletionItem[] {\n return (Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS) as BuiltinConstraintName[]).map((name) => ({\n label: `@${name}`,\n kind: CompletionItemKind.Keyword,\n detail: CONSTRAINT_DETAIL[name],\n }));\n}\n","/**\n * Hover provider for FormSpec JSDoc constraint tags.\n *\n * Returns Markdown documentation for a recognized FormSpec JSDoc tag when\n * the cursor is positioned over it. This is a skeleton — precise token\n * detection within JSDoc comment ranges will be added in a future phase.\n */\n\nimport {\n normalizeConstraintTagName,\n isBuiltinConstraintName,\n type BuiltinConstraintName,\n} from \"@formspec/core\";\nimport type { Hover } from \"vscode-languageserver/node.js\";\n\n/**\n * Markdown documentation for each built-in FormSpec constraint tag.\n *\n * Keys are the canonical constraint names from `BUILTIN_CONSTRAINT_DEFINITIONS`.\n * Values are Markdown strings suitable for LSP hover responses.\n */\nconst CONSTRAINT_HOVER_DOCS: Record<BuiltinConstraintName, string> = {\n minimum: [\n \"**@minimum** `<number>`\",\n \"\",\n \"Sets an inclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `minimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minimum 0 */\",\n \"amount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n maximum: [\n \"**@maximum** `<number>`\",\n \"\",\n \"Sets an inclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `maximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maximum 100 */\",\n \"percentage: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMinimum: [\n \"**@exclusiveMinimum** `<number>`\",\n \"\",\n \"Sets an exclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMinimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMinimum 0 */\",\n \"positiveAmount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMaximum: [\n \"**@exclusiveMaximum** `<number>`\",\n \"\",\n \"Sets an exclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMaximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMaximum 1 */\",\n \"ratio: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n multipleOf: [\n \"**@multipleOf** `<number>`\",\n \"\",\n \"Requires the numeric value to be a multiple of the given number.\",\n \"\",\n \"Maps to `multipleOf` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @multipleOf 0.01 */\",\n \"price: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n minLength: [\n \"**@minLength** `<number>`\",\n \"\",\n \"Sets a minimum character length on a string field.\",\n \"\",\n \"Maps to `minLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minLength 1 */\",\n \"name: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n maxLength: [\n \"**@maxLength** `<number>`\",\n \"\",\n \"Sets a maximum character length on a string field.\",\n \"\",\n \"Maps to `maxLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxLength 255 */\",\n \"description: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n minItems: [\n \"**@minItems** `<number>`\",\n \"\",\n \"Sets a minimum number of items in an array field.\",\n \"\",\n \"Maps to `minItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minItems 1 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n maxItems: [\n \"**@maxItems** `<number>`\",\n \"\",\n \"Sets a maximum number of items in an array field.\",\n \"\",\n \"Maps to `maxItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxItems 10 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n uniqueItems: [\n \"**@uniqueItems**\",\n \"\",\n \"Requires all items in an array field to be distinct.\",\n \"\",\n \"Maps to `uniqueItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @uniqueItems */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n pattern: [\n \"**@pattern** `<regex>`\",\n \"\",\n \"Sets a regular expression pattern that a string field must match.\",\n \"\",\n \"Maps to `pattern` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @pattern ^[a-z0-9]+$ */\",\n \"slug: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n enumOptions: [\n \"**@enumOptions** `<json-array>`\",\n \"\",\n \"Specifies the allowed values for an enum field as an inline JSON array.\",\n \"\",\n \"Maps to `enum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @enumOptions [\"draft\",\"sent\",\"archived\"] */',\n \"status: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n const: [\n \"**@const** `<json-literal>`\",\n \"\",\n \"Requires the field value to equal a single constant JSON value.\",\n \"\",\n \"Maps to `const` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @const \"USD\" */',\n \"currency: string;\",\n \"```\",\n ].join(\"\\n\"),\n} satisfies Record<BuiltinConstraintName, string>;\n\n/**\n * Returns hover documentation for a FormSpec JSDoc tag name.\n *\n * Accepts both camelCase (`\"minimum\"`) and PascalCase (`\"Minimum\"`) forms.\n * The `@` prefix is stripped before lookup if present.\n * Returns `null` when the tag is not a recognized FormSpec constraint tag.\n *\n * @param tagName - The tag name to look up (e.g., `\"minimum\"`, `\"@pattern\"`)\n * @returns An LSP `Hover` response, or `null` if the tag is not recognized\n */\nexport function getHoverForTag(tagName: string): Hover | null {\n // Strip leading `@` prefix if present\n const raw = tagName.startsWith(\"@\") ? tagName.slice(1) : tagName;\n const name = normalizeConstraintTagName(raw);\n\n if (!isBuiltinConstraintName(name)) {\n return null;\n }\n\n return {\n contents: {\n kind: \"markdown\",\n value: CONSTRAINT_HOVER_DOCS[name],\n },\n };\n}\n","/**\n * Go-to-definition provider for FormSpec.\n *\n * This is a stub — go-to-definition support (e.g., navigating from a\n * `field.text(\"name\")` call to the form definition that references it) will\n * be implemented in a future phase.\n */\n\nimport type { Location } from \"vscode-languageserver/node.js\";\n\n/**\n * Returns the definition location for a symbol at the given position.\n *\n * Always returns `null` in this stub implementation.\n *\n * @returns `null` — not yet implemented\n */\nexport function getDefinition(): Location | null {\n return null;\n}\n"],"mappings":"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","names":["import_node","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/providers/completion.ts","../src/providers/hover.ts","../src/providers/definition.ts"],"sourcesContent":["/**\n * \\@formspec/language-server\n *\n * Language server for FormSpec — provides completions, hover documentation,\n * and go-to-definition for FormSpec JSDoc constraint tags (`@Minimum`,\n * `@Maximum`, `@Pattern`, etc.) in TypeScript files.\n *\n * This package implements the Language Server Protocol (LSP) using the\n * `vscode-languageserver` library. Constraint names are sourced from\n * `BUILTIN_CONSTRAINT_DEFINITIONS` in `@formspec/core`, ensuring the\n * language server stays in sync with the single source of truth.\n *\n * Diagnostics are intentionally omitted per design decision A7.\n *\n * @example\n * ```ts\n * import { createServer } from '@formspec/language-server';\n *\n * const connection = createServer();\n * connection.listen();\n * ```\n *\n * @packageDocumentation\n */\n\nexport { createServer } from \"./server.js\";\nexport type { CreateServerOptions } from \"./server.js\";\nexport { getCompletionItems } from \"./providers/completion.js\";\nexport { getHoverForTag } from \"./providers/hover.js\";\nexport { getDefinition } from \"./providers/definition.js\";\n","/**\n * FormSpec Language Server\n *\n * Sets up an LSP server connection and registers handlers for:\n * - `textDocument/completion` — FormSpec JSDoc constraint tag completions\n * - `textDocument/hover` — Documentation for recognized constraint tags\n * - `textDocument/definition` — Go-to-definition (stub, returns null)\n *\n * Diagnostics are intentionally omitted per design decision A7.\n */\n\nimport {\n createConnection,\n ProposedFeatures,\n TextDocumentSyncKind,\n type Connection,\n type InitializeResult,\n} from \"vscode-languageserver/node.js\";\nimport type { ExtensionDefinition } from \"@formspec/core\";\nimport { getCompletionItems } from \"./providers/completion.js\";\nimport { getHoverForTag } from \"./providers/hover.js\";\nimport { getDefinition } from \"./providers/definition.js\";\n\nexport interface CreateServerOptions {\n /** Optional extension definitions whose custom tags should be surfaced by tooling. */\n readonly extensions?: readonly ExtensionDefinition[];\n}\n\n/**\n * Creates and configures the FormSpec language server connection.\n *\n * Registers LSP capability handlers and returns the connection.\n * Call `connection.listen()` to start accepting messages.\n *\n * @returns The configured LSP connection (not yet listening)\n */\nexport function createServer(options: CreateServerOptions = {}): Connection {\n const connection = createConnection(ProposedFeatures.all);\n\n connection.onInitialize((): InitializeResult => {\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n completionProvider: {\n // Trigger completions inside JSDoc comments when `@` is typed\n triggerCharacters: [\"@\"],\n },\n hoverProvider: true,\n definitionProvider: true,\n },\n serverInfo: {\n name: \"formspec-language-server\",\n version: \"0.1.0\",\n },\n };\n });\n\n connection.onCompletion(() => {\n // Return all FormSpec constraint tag completions.\n // Future phases will add context-aware filtering based on field type and\n // cursor position within JSDoc comment ranges.\n return getCompletionItems(options.extensions);\n });\n\n connection.onHover((_params) => {\n // Extract the word under the cursor and look up hover documentation.\n // This is a stub — precise JSDoc token detection (checking that the\n // cursor is within a JSDoc comment and extracting the tag name) will be\n // added in a future phase.\n //\n // For now we return null to signal no hover is available until the\n // token extraction is implemented.\n return getHoverForTag(\"\", options.extensions);\n });\n\n connection.onDefinition((_params) => {\n // Go-to-definition is not yet implemented.\n return getDefinition();\n });\n\n return connection;\n}\n","/**\n * Completion provider for FormSpec JSDoc constraint tags.\n *\n * Returns completion items for all recognized FormSpec JSDoc constraint tags\n * (e.g., `@minimum`, `@maximum`, `@pattern`), derived from\n * `BUILTIN_CONSTRAINT_DEFINITIONS`. This is a skeleton — context-aware\n * filtering will be added in a future phase.\n */\n\nimport {\n BUILTIN_CONSTRAINT_DEFINITIONS,\n type BuiltinConstraintName,\n type ExtensionDefinition,\n} from \"@formspec/core\";\nimport { CompletionItem, CompletionItemKind } from \"vscode-languageserver/node.js\";\n\n/**\n * Human-readable detail strings for each built-in constraint tag.\n *\n * Keys match the camelCase constraint names (matching keys in `BUILTIN_CONSTRAINT_DEFINITIONS`).\n * Values are shown as the detail string in completion items.\n */\nconst CONSTRAINT_DETAIL: Record<BuiltinConstraintName, string> = {\n minimum: \"Minimum numeric value (inclusive). Example: `@minimum 0`\",\n maximum: \"Maximum numeric value (inclusive). Example: `@maximum 100`\",\n exclusiveMinimum: \"Minimum numeric value (exclusive). Example: `@exclusiveMinimum 0`\",\n exclusiveMaximum: \"Maximum numeric value (exclusive). Example: `@exclusiveMaximum 100`\",\n multipleOf: \"Value must be a multiple of this number. Example: `@multipleOf 0.01`\",\n minLength: \"Minimum string length. Example: `@minLength 1`\",\n maxLength: \"Maximum string length. Example: `@maxLength 255`\",\n minItems: \"Minimum number of array items. Example: `@minItems 1`\",\n maxItems: \"Maximum number of array items. Example: `@maxItems 10`\",\n uniqueItems: \"Require all array items to be distinct. Example: `@uniqueItems`\",\n pattern: \"Regular expression pattern for string validation. Example: `@pattern ^[a-z]+$`\",\n enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions [\"a\",\"b\",\"c\"]`',\n const: 'Require a constant JSON value. Example: `@const \"USD\"`',\n};\n\n/**\n * Returns completion items for all FormSpec JSDoc constraint tags.\n *\n * Items are derived from `BUILTIN_CONSTRAINT_DEFINITIONS`, ensuring this list\n * stays in sync with the single source of truth in `@formspec/core`.\n *\n * Each item uses `CompletionItemKind.Keyword` since these are annotation\n * tags used within JSDoc comments rather than language symbols.\n *\n * @returns An array of LSP completion items for FormSpec constraint tags\n */\nexport function getCompletionItems(extensions?: readonly ExtensionDefinition[]): CompletionItem[] {\n const builtins = (Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS) as BuiltinConstraintName[]).map(\n (name) => ({\n label: `@${name}`,\n kind: CompletionItemKind.Keyword,\n detail: CONSTRAINT_DETAIL[name],\n })\n );\n\n const customItems =\n extensions?.flatMap((extension) =>\n (extension.constraintTags ?? []).map((tag) => ({\n label: `@${tag.tagName}`,\n kind: CompletionItemKind.Keyword,\n detail: `Extension constraint tag from ${extension.extensionId}`,\n }))\n ) ?? [];\n\n return [...builtins, ...customItems];\n}\n","/**\n * Hover provider for FormSpec JSDoc constraint tags.\n *\n * Returns Markdown documentation for a recognized FormSpec JSDoc tag when\n * the cursor is positioned over it. This is a skeleton — precise token\n * detection within JSDoc comment ranges will be added in a future phase.\n */\n\nimport {\n normalizeConstraintTagName,\n isBuiltinConstraintName,\n type BuiltinConstraintName,\n type ExtensionDefinition,\n} from \"@formspec/core\";\nimport type { Hover } from \"vscode-languageserver/node.js\";\n\n/**\n * Markdown documentation for each built-in FormSpec constraint tag.\n *\n * Keys are the canonical constraint names from `BUILTIN_CONSTRAINT_DEFINITIONS`.\n * Values are Markdown strings suitable for LSP hover responses.\n */\nconst CONSTRAINT_HOVER_DOCS: Record<BuiltinConstraintName, string> = {\n minimum: [\n \"**@minimum** `<number>`\",\n \"\",\n \"Sets an inclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `minimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minimum 0 */\",\n \"amount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n maximum: [\n \"**@maximum** `<number>`\",\n \"\",\n \"Sets an inclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `maximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maximum 100 */\",\n \"percentage: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMinimum: [\n \"**@exclusiveMinimum** `<number>`\",\n \"\",\n \"Sets an exclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMinimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMinimum 0 */\",\n \"positiveAmount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMaximum: [\n \"**@exclusiveMaximum** `<number>`\",\n \"\",\n \"Sets an exclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMaximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMaximum 1 */\",\n \"ratio: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n multipleOf: [\n \"**@multipleOf** `<number>`\",\n \"\",\n \"Requires the numeric value to be a multiple of the given number.\",\n \"\",\n \"Maps to `multipleOf` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @multipleOf 0.01 */\",\n \"price: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n minLength: [\n \"**@minLength** `<number>`\",\n \"\",\n \"Sets a minimum character length on a string field.\",\n \"\",\n \"Maps to `minLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minLength 1 */\",\n \"name: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n maxLength: [\n \"**@maxLength** `<number>`\",\n \"\",\n \"Sets a maximum character length on a string field.\",\n \"\",\n \"Maps to `maxLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxLength 255 */\",\n \"description: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n minItems: [\n \"**@minItems** `<number>`\",\n \"\",\n \"Sets a minimum number of items in an array field.\",\n \"\",\n \"Maps to `minItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minItems 1 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n maxItems: [\n \"**@maxItems** `<number>`\",\n \"\",\n \"Sets a maximum number of items in an array field.\",\n \"\",\n \"Maps to `maxItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxItems 10 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n uniqueItems: [\n \"**@uniqueItems**\",\n \"\",\n \"Requires all items in an array field to be distinct.\",\n \"\",\n \"Maps to `uniqueItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @uniqueItems */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n pattern: [\n \"**@pattern** `<regex>`\",\n \"\",\n \"Sets a regular expression pattern that a string field must match.\",\n \"\",\n \"Maps to `pattern` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @pattern ^[a-z0-9]+$ */\",\n \"slug: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n enumOptions: [\n \"**@enumOptions** `<json-array>`\",\n \"\",\n \"Specifies the allowed values for an enum field as an inline JSON array.\",\n \"\",\n \"Maps to `enum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @enumOptions [\"draft\",\"sent\",\"archived\"] */',\n \"status: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n const: [\n \"**@const** `<json-literal>`\",\n \"\",\n \"Requires the field value to equal a single constant JSON value.\",\n \"\",\n \"Maps to `const` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @const \"USD\" */',\n \"currency: string;\",\n \"```\",\n ].join(\"\\n\"),\n} satisfies Record<BuiltinConstraintName, string>;\n\n/**\n * Returns hover documentation for a FormSpec JSDoc tag name.\n *\n * Accepts both camelCase (`\"minimum\"`) and PascalCase (`\"Minimum\"`) forms.\n * The `@` prefix is stripped before lookup if present.\n * Returns `null` when the tag is not a recognized FormSpec constraint tag.\n *\n * @param tagName - The tag name to look up (e.g., `\"minimum\"`, `\"@pattern\"`)\n * @returns An LSP `Hover` response, or `null` if the tag is not recognized\n */\nexport function getHoverForTag(\n tagName: string,\n extensions?: readonly ExtensionDefinition[]\n): Hover | null {\n // Strip leading `@` prefix if present\n const raw = tagName.startsWith(\"@\") ? tagName.slice(1) : tagName;\n const name = normalizeConstraintTagName(raw);\n\n if (!isBuiltinConstraintName(name)) {\n const registration = extensions\n ?.flatMap((extension) =>\n (extension.constraintTags ?? []).map((tag) => ({\n extensionId: extension.extensionId,\n tag,\n }))\n )\n .find(({ tag }) => tag.tagName === name);\n\n if (registration === undefined) {\n return null;\n }\n\n return {\n contents: {\n kind: \"markdown\",\n value: [\n `**@${registration.tag.tagName}** \\`<value>\\``,\n \"\",\n `Extension-defined constraint tag from \\`${registration.extensionId}\\`.`,\n \"\",\n \"Validated through the registered FormSpec extension surface.\",\n ].join(\"\\n\"),\n },\n };\n }\n\n return {\n contents: {\n kind: \"markdown\",\n value: CONSTRAINT_HOVER_DOCS[name],\n },\n };\n}\n","/**\n * Go-to-definition provider for FormSpec.\n *\n * This is a stub — go-to-definition support (e.g., navigating from a\n * `field.text(\"name\")` call to the form definition that references it) will\n * be implemented in a future phase.\n */\n\nimport type { Location } from \"vscode-languageserver/node.js\";\n\n/**\n * Returns the definition location for a symbol at the given position.\n *\n * Always returns `null` in this stub implementation.\n *\n * @returns `null` — not yet implemented\n */\nexport function getDefinition(): Location | null {\n return null;\n}\n"],"mappings":"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","names":["import_node","import_core"]}
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@
23
23
  * @packageDocumentation
24
24
  */
25
25
  export { createServer } from "./server.js";
26
+ export type { CreateServerOptions } from "./server.js";
26
27
  export { getCompletionItems } from "./providers/completion.js";
27
28
  export { getHoverForTag } from "./providers/hover.js";
28
29
  export { getDefinition } from "./providers/definition.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -6,7 +6,9 @@ import {
6
6
  } from "vscode-languageserver/node.js";
7
7
 
8
8
  // src/providers/completion.ts
9
- import { BUILTIN_CONSTRAINT_DEFINITIONS } from "@formspec/core";
9
+ import {
10
+ BUILTIN_CONSTRAINT_DEFINITIONS
11
+ } from "@formspec/core";
10
12
  import { CompletionItemKind } from "vscode-languageserver/node.js";
11
13
  var CONSTRAINT_DETAIL = {
12
14
  minimum: "Minimum numeric value (inclusive). Example: `@minimum 0`",
@@ -23,12 +25,22 @@ var CONSTRAINT_DETAIL = {
23
25
  enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions ["a","b","c"]`',
24
26
  const: 'Require a constant JSON value. Example: `@const "USD"`'
25
27
  };
26
- function getCompletionItems() {
27
- return Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS).map((name) => ({
28
- label: `@${name}`,
29
- kind: CompletionItemKind.Keyword,
30
- detail: CONSTRAINT_DETAIL[name]
31
- }));
28
+ function getCompletionItems(extensions) {
29
+ const builtins = Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS).map(
30
+ (name) => ({
31
+ label: `@${name}`,
32
+ kind: CompletionItemKind.Keyword,
33
+ detail: CONSTRAINT_DETAIL[name]
34
+ })
35
+ );
36
+ const customItems = extensions?.flatMap(
37
+ (extension) => (extension.constraintTags ?? []).map((tag) => ({
38
+ label: `@${tag.tagName}`,
39
+ kind: CompletionItemKind.Keyword,
40
+ detail: `Extension constraint tag from ${extension.extensionId}`
41
+ }))
42
+ ) ?? [];
43
+ return [...builtins, ...customItems];
32
44
  }
33
45
 
34
46
  // src/providers/hover.ts
@@ -207,11 +219,31 @@ var CONSTRAINT_HOVER_DOCS = {
207
219
  "```"
208
220
  ].join("\n")
209
221
  };
210
- function getHoverForTag(tagName) {
222
+ function getHoverForTag(tagName, extensions) {
211
223
  const raw = tagName.startsWith("@") ? tagName.slice(1) : tagName;
212
224
  const name = normalizeConstraintTagName(raw);
213
225
  if (!isBuiltinConstraintName(name)) {
214
- return null;
226
+ const registration = extensions?.flatMap(
227
+ (extension) => (extension.constraintTags ?? []).map((tag) => ({
228
+ extensionId: extension.extensionId,
229
+ tag
230
+ }))
231
+ ).find(({ tag }) => tag.tagName === name);
232
+ if (registration === void 0) {
233
+ return null;
234
+ }
235
+ return {
236
+ contents: {
237
+ kind: "markdown",
238
+ value: [
239
+ `**@${registration.tag.tagName}** \`<value>\``,
240
+ "",
241
+ `Extension-defined constraint tag from \`${registration.extensionId}\`.`,
242
+ "",
243
+ "Validated through the registered FormSpec extension surface."
244
+ ].join("\n")
245
+ }
246
+ };
215
247
  }
216
248
  return {
217
249
  contents: {
@@ -227,7 +259,7 @@ function getDefinition() {
227
259
  }
228
260
 
229
261
  // src/server.ts
230
- function createServer() {
262
+ function createServer(options = {}) {
231
263
  const connection = createConnection(ProposedFeatures.all);
232
264
  connection.onInitialize(() => {
233
265
  return {
@@ -247,10 +279,10 @@ function createServer() {
247
279
  };
248
280
  });
249
281
  connection.onCompletion(() => {
250
- return getCompletionItems();
282
+ return getCompletionItems(options.extensions);
251
283
  });
252
284
  connection.onHover((_params) => {
253
- return getHoverForTag("");
285
+ return getHoverForTag("", options.extensions);
254
286
  });
255
287
  connection.onDefinition((_params) => {
256
288
  return getDefinition();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts","../src/providers/completion.ts","../src/providers/hover.ts","../src/providers/definition.ts"],"sourcesContent":["/**\n * FormSpec Language Server\n *\n * Sets up an LSP server connection and registers handlers for:\n * - `textDocument/completion` — FormSpec JSDoc constraint tag completions\n * - `textDocument/hover` — Documentation for recognized constraint tags\n * - `textDocument/definition` — Go-to-definition (stub, returns null)\n *\n * Diagnostics are intentionally omitted per design decision A7.\n */\n\nimport {\n createConnection,\n ProposedFeatures,\n TextDocumentSyncKind,\n type Connection,\n type InitializeResult,\n} from \"vscode-languageserver/node.js\";\nimport { getCompletionItems } from \"./providers/completion.js\";\nimport { getHoverForTag } from \"./providers/hover.js\";\nimport { getDefinition } from \"./providers/definition.js\";\n\n/**\n * Creates and configures the FormSpec language server connection.\n *\n * Registers LSP capability handlers and returns the connection.\n * Call `connection.listen()` to start accepting messages.\n *\n * @returns The configured LSP connection (not yet listening)\n */\nexport function createServer(): Connection {\n const connection = createConnection(ProposedFeatures.all);\n\n connection.onInitialize((): InitializeResult => {\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n completionProvider: {\n // Trigger completions inside JSDoc comments when `@` is typed\n triggerCharacters: [\"@\"],\n },\n hoverProvider: true,\n definitionProvider: true,\n },\n serverInfo: {\n name: \"formspec-language-server\",\n version: \"0.1.0\",\n },\n };\n });\n\n connection.onCompletion(() => {\n // Return all FormSpec constraint tag completions.\n // Future phases will add context-aware filtering based on field type and\n // cursor position within JSDoc comment ranges.\n return getCompletionItems();\n });\n\n connection.onHover((_params) => {\n // Extract the word under the cursor and look up hover documentation.\n // This is a stub — precise JSDoc token detection (checking that the\n // cursor is within a JSDoc comment and extracting the tag name) will be\n // added in a future phase.\n //\n // For now we return null to signal no hover is available until the\n // token extraction is implemented.\n return getHoverForTag(\"\");\n });\n\n connection.onDefinition((_params) => {\n // Go-to-definition is not yet implemented.\n return getDefinition();\n });\n\n return connection;\n}\n","/**\n * Completion provider for FormSpec JSDoc constraint tags.\n *\n * Returns completion items for all recognized FormSpec JSDoc constraint tags\n * (e.g., `@minimum`, `@maximum`, `@pattern`), derived from\n * `BUILTIN_CONSTRAINT_DEFINITIONS`. This is a skeleton — context-aware\n * filtering will be added in a future phase.\n */\n\nimport { BUILTIN_CONSTRAINT_DEFINITIONS, type BuiltinConstraintName } from \"@formspec/core\";\nimport { CompletionItem, CompletionItemKind } from \"vscode-languageserver/node.js\";\n\n/**\n * Human-readable detail strings for each built-in constraint tag.\n *\n * Keys match the camelCase constraint names (matching keys in `BUILTIN_CONSTRAINT_DEFINITIONS`).\n * Values are shown as the detail string in completion items.\n */\nconst CONSTRAINT_DETAIL: Record<BuiltinConstraintName, string> = {\n minimum: \"Minimum numeric value (inclusive). Example: `@minimum 0`\",\n maximum: \"Maximum numeric value (inclusive). Example: `@maximum 100`\",\n exclusiveMinimum: \"Minimum numeric value (exclusive). Example: `@exclusiveMinimum 0`\",\n exclusiveMaximum: \"Maximum numeric value (exclusive). Example: `@exclusiveMaximum 100`\",\n multipleOf: \"Value must be a multiple of this number. Example: `@multipleOf 0.01`\",\n minLength: \"Minimum string length. Example: `@minLength 1`\",\n maxLength: \"Maximum string length. Example: `@maxLength 255`\",\n minItems: \"Minimum number of array items. Example: `@minItems 1`\",\n maxItems: \"Maximum number of array items. Example: `@maxItems 10`\",\n uniqueItems: \"Require all array items to be distinct. Example: `@uniqueItems`\",\n pattern: \"Regular expression pattern for string validation. Example: `@pattern ^[a-z]+$`\",\n enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions [\"a\",\"b\",\"c\"]`',\n const: 'Require a constant JSON value. Example: `@const \"USD\"`',\n};\n\n/**\n * Returns completion items for all FormSpec JSDoc constraint tags.\n *\n * Items are derived from `BUILTIN_CONSTRAINT_DEFINITIONS`, ensuring this list\n * stays in sync with the single source of truth in `@formspec/core`.\n *\n * Each item uses `CompletionItemKind.Keyword` since these are annotation\n * tags used within JSDoc comments rather than language symbols.\n *\n * @returns An array of LSP completion items for FormSpec constraint tags\n */\nexport function getCompletionItems(): CompletionItem[] {\n return (Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS) as BuiltinConstraintName[]).map((name) => ({\n label: `@${name}`,\n kind: CompletionItemKind.Keyword,\n detail: CONSTRAINT_DETAIL[name],\n }));\n}\n","/**\n * Hover provider for FormSpec JSDoc constraint tags.\n *\n * Returns Markdown documentation for a recognized FormSpec JSDoc tag when\n * the cursor is positioned over it. This is a skeleton — precise token\n * detection within JSDoc comment ranges will be added in a future phase.\n */\n\nimport {\n normalizeConstraintTagName,\n isBuiltinConstraintName,\n type BuiltinConstraintName,\n} from \"@formspec/core\";\nimport type { Hover } from \"vscode-languageserver/node.js\";\n\n/**\n * Markdown documentation for each built-in FormSpec constraint tag.\n *\n * Keys are the canonical constraint names from `BUILTIN_CONSTRAINT_DEFINITIONS`.\n * Values are Markdown strings suitable for LSP hover responses.\n */\nconst CONSTRAINT_HOVER_DOCS: Record<BuiltinConstraintName, string> = {\n minimum: [\n \"**@minimum** `<number>`\",\n \"\",\n \"Sets an inclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `minimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minimum 0 */\",\n \"amount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n maximum: [\n \"**@maximum** `<number>`\",\n \"\",\n \"Sets an inclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `maximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maximum 100 */\",\n \"percentage: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMinimum: [\n \"**@exclusiveMinimum** `<number>`\",\n \"\",\n \"Sets an exclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMinimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMinimum 0 */\",\n \"positiveAmount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMaximum: [\n \"**@exclusiveMaximum** `<number>`\",\n \"\",\n \"Sets an exclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMaximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMaximum 1 */\",\n \"ratio: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n multipleOf: [\n \"**@multipleOf** `<number>`\",\n \"\",\n \"Requires the numeric value to be a multiple of the given number.\",\n \"\",\n \"Maps to `multipleOf` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @multipleOf 0.01 */\",\n \"price: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n minLength: [\n \"**@minLength** `<number>`\",\n \"\",\n \"Sets a minimum character length on a string field.\",\n \"\",\n \"Maps to `minLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minLength 1 */\",\n \"name: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n maxLength: [\n \"**@maxLength** `<number>`\",\n \"\",\n \"Sets a maximum character length on a string field.\",\n \"\",\n \"Maps to `maxLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxLength 255 */\",\n \"description: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n minItems: [\n \"**@minItems** `<number>`\",\n \"\",\n \"Sets a minimum number of items in an array field.\",\n \"\",\n \"Maps to `minItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minItems 1 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n maxItems: [\n \"**@maxItems** `<number>`\",\n \"\",\n \"Sets a maximum number of items in an array field.\",\n \"\",\n \"Maps to `maxItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxItems 10 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n uniqueItems: [\n \"**@uniqueItems**\",\n \"\",\n \"Requires all items in an array field to be distinct.\",\n \"\",\n \"Maps to `uniqueItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @uniqueItems */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n pattern: [\n \"**@pattern** `<regex>`\",\n \"\",\n \"Sets a regular expression pattern that a string field must match.\",\n \"\",\n \"Maps to `pattern` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @pattern ^[a-z0-9]+$ */\",\n \"slug: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n enumOptions: [\n \"**@enumOptions** `<json-array>`\",\n \"\",\n \"Specifies the allowed values for an enum field as an inline JSON array.\",\n \"\",\n \"Maps to `enum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @enumOptions [\"draft\",\"sent\",\"archived\"] */',\n \"status: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n const: [\n \"**@const** `<json-literal>`\",\n \"\",\n \"Requires the field value to equal a single constant JSON value.\",\n \"\",\n \"Maps to `const` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @const \"USD\" */',\n \"currency: string;\",\n \"```\",\n ].join(\"\\n\"),\n} satisfies Record<BuiltinConstraintName, string>;\n\n/**\n * Returns hover documentation for a FormSpec JSDoc tag name.\n *\n * Accepts both camelCase (`\"minimum\"`) and PascalCase (`\"Minimum\"`) forms.\n * The `@` prefix is stripped before lookup if present.\n * Returns `null` when the tag is not a recognized FormSpec constraint tag.\n *\n * @param tagName - The tag name to look up (e.g., `\"minimum\"`, `\"@pattern\"`)\n * @returns An LSP `Hover` response, or `null` if the tag is not recognized\n */\nexport function getHoverForTag(tagName: string): Hover | null {\n // Strip leading `@` prefix if present\n const raw = tagName.startsWith(\"@\") ? tagName.slice(1) : tagName;\n const name = normalizeConstraintTagName(raw);\n\n if (!isBuiltinConstraintName(name)) {\n return null;\n }\n\n return {\n contents: {\n kind: \"markdown\",\n value: CONSTRAINT_HOVER_DOCS[name],\n },\n };\n}\n","/**\n * Go-to-definition provider for FormSpec.\n *\n * This is a stub — go-to-definition support (e.g., navigating from a\n * `field.text(\"name\")` call to the form definition that references it) will\n * be implemented in a future phase.\n */\n\nimport type { Location } from \"vscode-languageserver/node.js\";\n\n/**\n * Returns the definition location for a symbol at the given position.\n *\n * Always returns `null` in this stub implementation.\n *\n * @returns `null` — not yet implemented\n */\nexport function getDefinition(): Location | null {\n return null;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/server.ts","../src/providers/completion.ts","../src/providers/hover.ts","../src/providers/definition.ts"],"sourcesContent":["/**\n * FormSpec Language Server\n *\n * Sets up an LSP server connection and registers handlers for:\n * - `textDocument/completion` — FormSpec JSDoc constraint tag completions\n * - `textDocument/hover` — Documentation for recognized constraint tags\n * - `textDocument/definition` — Go-to-definition (stub, returns null)\n *\n * Diagnostics are intentionally omitted per design decision A7.\n */\n\nimport {\n createConnection,\n ProposedFeatures,\n TextDocumentSyncKind,\n type Connection,\n type InitializeResult,\n} from \"vscode-languageserver/node.js\";\nimport type { ExtensionDefinition } from \"@formspec/core\";\nimport { getCompletionItems } from \"./providers/completion.js\";\nimport { getHoverForTag } from \"./providers/hover.js\";\nimport { getDefinition } from \"./providers/definition.js\";\n\nexport interface CreateServerOptions {\n /** Optional extension definitions whose custom tags should be surfaced by tooling. */\n readonly extensions?: readonly ExtensionDefinition[];\n}\n\n/**\n * Creates and configures the FormSpec language server connection.\n *\n * Registers LSP capability handlers and returns the connection.\n * Call `connection.listen()` to start accepting messages.\n *\n * @returns The configured LSP connection (not yet listening)\n */\nexport function createServer(options: CreateServerOptions = {}): Connection {\n const connection = createConnection(ProposedFeatures.all);\n\n connection.onInitialize((): InitializeResult => {\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n completionProvider: {\n // Trigger completions inside JSDoc comments when `@` is typed\n triggerCharacters: [\"@\"],\n },\n hoverProvider: true,\n definitionProvider: true,\n },\n serverInfo: {\n name: \"formspec-language-server\",\n version: \"0.1.0\",\n },\n };\n });\n\n connection.onCompletion(() => {\n // Return all FormSpec constraint tag completions.\n // Future phases will add context-aware filtering based on field type and\n // cursor position within JSDoc comment ranges.\n return getCompletionItems(options.extensions);\n });\n\n connection.onHover((_params) => {\n // Extract the word under the cursor and look up hover documentation.\n // This is a stub — precise JSDoc token detection (checking that the\n // cursor is within a JSDoc comment and extracting the tag name) will be\n // added in a future phase.\n //\n // For now we return null to signal no hover is available until the\n // token extraction is implemented.\n return getHoverForTag(\"\", options.extensions);\n });\n\n connection.onDefinition((_params) => {\n // Go-to-definition is not yet implemented.\n return getDefinition();\n });\n\n return connection;\n}\n","/**\n * Completion provider for FormSpec JSDoc constraint tags.\n *\n * Returns completion items for all recognized FormSpec JSDoc constraint tags\n * (e.g., `@minimum`, `@maximum`, `@pattern`), derived from\n * `BUILTIN_CONSTRAINT_DEFINITIONS`. This is a skeleton — context-aware\n * filtering will be added in a future phase.\n */\n\nimport {\n BUILTIN_CONSTRAINT_DEFINITIONS,\n type BuiltinConstraintName,\n type ExtensionDefinition,\n} from \"@formspec/core\";\nimport { CompletionItem, CompletionItemKind } from \"vscode-languageserver/node.js\";\n\n/**\n * Human-readable detail strings for each built-in constraint tag.\n *\n * Keys match the camelCase constraint names (matching keys in `BUILTIN_CONSTRAINT_DEFINITIONS`).\n * Values are shown as the detail string in completion items.\n */\nconst CONSTRAINT_DETAIL: Record<BuiltinConstraintName, string> = {\n minimum: \"Minimum numeric value (inclusive). Example: `@minimum 0`\",\n maximum: \"Maximum numeric value (inclusive). Example: `@maximum 100`\",\n exclusiveMinimum: \"Minimum numeric value (exclusive). Example: `@exclusiveMinimum 0`\",\n exclusiveMaximum: \"Maximum numeric value (exclusive). Example: `@exclusiveMaximum 100`\",\n multipleOf: \"Value must be a multiple of this number. Example: `@multipleOf 0.01`\",\n minLength: \"Minimum string length. Example: `@minLength 1`\",\n maxLength: \"Maximum string length. Example: `@maxLength 255`\",\n minItems: \"Minimum number of array items. Example: `@minItems 1`\",\n maxItems: \"Maximum number of array items. Example: `@maxItems 10`\",\n uniqueItems: \"Require all array items to be distinct. Example: `@uniqueItems`\",\n pattern: \"Regular expression pattern for string validation. Example: `@pattern ^[a-z]+$`\",\n enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions [\"a\",\"b\",\"c\"]`',\n const: 'Require a constant JSON value. Example: `@const \"USD\"`',\n};\n\n/**\n * Returns completion items for all FormSpec JSDoc constraint tags.\n *\n * Items are derived from `BUILTIN_CONSTRAINT_DEFINITIONS`, ensuring this list\n * stays in sync with the single source of truth in `@formspec/core`.\n *\n * Each item uses `CompletionItemKind.Keyword` since these are annotation\n * tags used within JSDoc comments rather than language symbols.\n *\n * @returns An array of LSP completion items for FormSpec constraint tags\n */\nexport function getCompletionItems(extensions?: readonly ExtensionDefinition[]): CompletionItem[] {\n const builtins = (Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS) as BuiltinConstraintName[]).map(\n (name) => ({\n label: `@${name}`,\n kind: CompletionItemKind.Keyword,\n detail: CONSTRAINT_DETAIL[name],\n })\n );\n\n const customItems =\n extensions?.flatMap((extension) =>\n (extension.constraintTags ?? []).map((tag) => ({\n label: `@${tag.tagName}`,\n kind: CompletionItemKind.Keyword,\n detail: `Extension constraint tag from ${extension.extensionId}`,\n }))\n ) ?? [];\n\n return [...builtins, ...customItems];\n}\n","/**\n * Hover provider for FormSpec JSDoc constraint tags.\n *\n * Returns Markdown documentation for a recognized FormSpec JSDoc tag when\n * the cursor is positioned over it. This is a skeleton — precise token\n * detection within JSDoc comment ranges will be added in a future phase.\n */\n\nimport {\n normalizeConstraintTagName,\n isBuiltinConstraintName,\n type BuiltinConstraintName,\n type ExtensionDefinition,\n} from \"@formspec/core\";\nimport type { Hover } from \"vscode-languageserver/node.js\";\n\n/**\n * Markdown documentation for each built-in FormSpec constraint tag.\n *\n * Keys are the canonical constraint names from `BUILTIN_CONSTRAINT_DEFINITIONS`.\n * Values are Markdown strings suitable for LSP hover responses.\n */\nconst CONSTRAINT_HOVER_DOCS: Record<BuiltinConstraintName, string> = {\n minimum: [\n \"**@minimum** `<number>`\",\n \"\",\n \"Sets an inclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `minimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minimum 0 */\",\n \"amount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n maximum: [\n \"**@maximum** `<number>`\",\n \"\",\n \"Sets an inclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `maximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maximum 100 */\",\n \"percentage: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMinimum: [\n \"**@exclusiveMinimum** `<number>`\",\n \"\",\n \"Sets an exclusive lower bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMinimum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMinimum 0 */\",\n \"positiveAmount: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n exclusiveMaximum: [\n \"**@exclusiveMaximum** `<number>`\",\n \"\",\n \"Sets an exclusive upper bound on a numeric field.\",\n \"\",\n \"Maps to `exclusiveMaximum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @exclusiveMaximum 1 */\",\n \"ratio: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n multipleOf: [\n \"**@multipleOf** `<number>`\",\n \"\",\n \"Requires the numeric value to be a multiple of the given number.\",\n \"\",\n \"Maps to `multipleOf` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @multipleOf 0.01 */\",\n \"price: number;\",\n \"```\",\n ].join(\"\\n\"),\n\n minLength: [\n \"**@minLength** `<number>`\",\n \"\",\n \"Sets a minimum character length on a string field.\",\n \"\",\n \"Maps to `minLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minLength 1 */\",\n \"name: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n maxLength: [\n \"**@maxLength** `<number>`\",\n \"\",\n \"Sets a maximum character length on a string field.\",\n \"\",\n \"Maps to `maxLength` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxLength 255 */\",\n \"description: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n minItems: [\n \"**@minItems** `<number>`\",\n \"\",\n \"Sets a minimum number of items in an array field.\",\n \"\",\n \"Maps to `minItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @minItems 1 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n maxItems: [\n \"**@maxItems** `<number>`\",\n \"\",\n \"Sets a maximum number of items in an array field.\",\n \"\",\n \"Maps to `maxItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @maxItems 10 */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n uniqueItems: [\n \"**@uniqueItems**\",\n \"\",\n \"Requires all items in an array field to be distinct.\",\n \"\",\n \"Maps to `uniqueItems` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @uniqueItems */\",\n \"tags: string[];\",\n \"```\",\n ].join(\"\\n\"),\n\n pattern: [\n \"**@pattern** `<regex>`\",\n \"\",\n \"Sets a regular expression pattern that a string field must match.\",\n \"\",\n \"Maps to `pattern` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n \"/** @pattern ^[a-z0-9]+$ */\",\n \"slug: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n enumOptions: [\n \"**@enumOptions** `<json-array>`\",\n \"\",\n \"Specifies the allowed values for an enum field as an inline JSON array.\",\n \"\",\n \"Maps to `enum` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @enumOptions [\"draft\",\"sent\",\"archived\"] */',\n \"status: string;\",\n \"```\",\n ].join(\"\\n\"),\n\n const: [\n \"**@const** `<json-literal>`\",\n \"\",\n \"Requires the field value to equal a single constant JSON value.\",\n \"\",\n \"Maps to `const` in JSON Schema.\",\n \"\",\n \"**Example:**\",\n \"```typescript\",\n '/** @const \"USD\" */',\n \"currency: string;\",\n \"```\",\n ].join(\"\\n\"),\n} satisfies Record<BuiltinConstraintName, string>;\n\n/**\n * Returns hover documentation for a FormSpec JSDoc tag name.\n *\n * Accepts both camelCase (`\"minimum\"`) and PascalCase (`\"Minimum\"`) forms.\n * The `@` prefix is stripped before lookup if present.\n * Returns `null` when the tag is not a recognized FormSpec constraint tag.\n *\n * @param tagName - The tag name to look up (e.g., `\"minimum\"`, `\"@pattern\"`)\n * @returns An LSP `Hover` response, or `null` if the tag is not recognized\n */\nexport function getHoverForTag(\n tagName: string,\n extensions?: readonly ExtensionDefinition[]\n): Hover | null {\n // Strip leading `@` prefix if present\n const raw = tagName.startsWith(\"@\") ? tagName.slice(1) : tagName;\n const name = normalizeConstraintTagName(raw);\n\n if (!isBuiltinConstraintName(name)) {\n const registration = extensions\n ?.flatMap((extension) =>\n (extension.constraintTags ?? []).map((tag) => ({\n extensionId: extension.extensionId,\n tag,\n }))\n )\n .find(({ tag }) => tag.tagName === name);\n\n if (registration === undefined) {\n return null;\n }\n\n return {\n contents: {\n kind: \"markdown\",\n value: [\n `**@${registration.tag.tagName}** \\`<value>\\``,\n \"\",\n `Extension-defined constraint tag from \\`${registration.extensionId}\\`.`,\n \"\",\n \"Validated through the registered FormSpec extension surface.\",\n ].join(\"\\n\"),\n },\n };\n }\n\n return {\n contents: {\n kind: \"markdown\",\n value: CONSTRAINT_HOVER_DOCS[name],\n },\n };\n}\n","/**\n * Go-to-definition provider for FormSpec.\n *\n * This is a stub — go-to-definition support (e.g., navigating from a\n * `field.text(\"name\")` call to the form definition that references it) will\n * be implemented in a future phase.\n */\n\nimport type { Location } from \"vscode-languageserver/node.js\";\n\n/**\n * Returns the definition location for a symbol at the given position.\n *\n * Always returns `null` in this stub implementation.\n *\n * @returns `null` — not yet implemented\n */\nexport function getDefinition(): Location | null {\n return null;\n}\n"],"mappings":"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","names":[]}
@@ -25,6 +25,7 @@
25
25
 
26
26
  import { CompletionItem } from 'vscode-languageserver/node.js';
27
27
  import { Connection } from 'vscode-languageserver/node.js';
28
+ import { ExtensionDefinition } from '@formspec/core';
28
29
  import type { Hover } from 'vscode-languageserver/node.js';
29
30
  import type { Location } from 'vscode-languageserver/node.js';
30
31
 
@@ -36,7 +37,12 @@ import type { Location } from 'vscode-languageserver/node.js';
36
37
  *
37
38
  * @returns The configured LSP connection (not yet listening)
38
39
  */
39
- export declare function createServer(): Connection;
40
+ export declare function createServer(options?: CreateServerOptions): Connection;
41
+
42
+ export declare interface CreateServerOptions {
43
+ /** Optional extension definitions whose custom tags should be surfaced by tooling. */
44
+ readonly extensions?: readonly ExtensionDefinition[];
45
+ }
40
46
 
41
47
  /**
42
48
  * Returns completion items for all FormSpec JSDoc constraint tags.
@@ -49,7 +55,7 @@ export declare function createServer(): Connection;
49
55
  *
50
56
  * @returns An array of LSP completion items for FormSpec constraint tags
51
57
  */
52
- export declare function getCompletionItems(): CompletionItem[];
58
+ export declare function getCompletionItems(extensions?: readonly ExtensionDefinition[]): CompletionItem[];
53
59
 
54
60
  /**
55
61
  * Returns the definition location for a symbol at the given position.
@@ -70,6 +76,6 @@ export declare function getDefinition(): Location | null;
70
76
  * @param tagName - The tag name to look up (e.g., `"minimum"`, `"@pattern"`)
71
77
  * @returns An LSP `Hover` response, or `null` if the tag is not recognized
72
78
  */
73
- export declare function getHoverForTag(tagName: string): Hover | null;
79
+ export declare function getHoverForTag(tagName: string, extensions?: readonly ExtensionDefinition[]): Hover | null;
74
80
 
75
81
  export { }
@@ -6,6 +6,7 @@
6
6
  * `BUILTIN_CONSTRAINT_DEFINITIONS`. This is a skeleton — context-aware
7
7
  * filtering will be added in a future phase.
8
8
  */
9
+ import { type ExtensionDefinition } from "@formspec/core";
9
10
  import { CompletionItem } from "vscode-languageserver/node.js";
10
11
  /**
11
12
  * Returns completion items for all FormSpec JSDoc constraint tags.
@@ -18,5 +19,5 @@ import { CompletionItem } from "vscode-languageserver/node.js";
18
19
  *
19
20
  * @returns An array of LSP completion items for FormSpec constraint tags
20
21
  */
21
- export declare function getCompletionItems(): CompletionItem[];
22
+ export declare function getCompletionItems(extensions?: readonly ExtensionDefinition[]): CompletionItem[];
22
23
  //# sourceMappingURL=completion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/providers/completion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,cAAc,EAAsB,MAAM,+BAA+B,CAAC;AAwBnF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,EAAE,CAMrD"}
1
+ {"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/providers/completion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAsB,MAAM,+BAA+B,CAAC;AAwBnF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,SAAS,mBAAmB,EAAE,GAAG,cAAc,EAAE,CAmBhG"}
@@ -5,6 +5,7 @@
5
5
  * the cursor is positioned over it. This is a skeleton — precise token
6
6
  * detection within JSDoc comment ranges will be added in a future phase.
7
7
  */
8
+ import { type ExtensionDefinition } from "@formspec/core";
8
9
  import type { Hover } from "vscode-languageserver/node.js";
9
10
  /**
10
11
  * Returns hover documentation for a FormSpec JSDoc tag name.
@@ -16,5 +17,5 @@ import type { Hover } from "vscode-languageserver/node.js";
16
17
  * @param tagName - The tag name to look up (e.g., `"minimum"`, `"@pattern"`)
17
18
  * @returns An LSP `Hover` response, or `null` if the tag is not recognized
18
19
  */
19
- export declare function getHoverForTag(tagName: string): Hover | null;
20
+ export declare function getHoverForTag(tagName: string, extensions?: readonly ExtensionDefinition[]): Hover | null;
20
21
  //# sourceMappingURL=hover.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/providers/hover.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAgM3D;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAe5D"}
1
+ {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/providers/hover.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAgM3D;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,SAAS,mBAAmB,EAAE,GAC1C,KAAK,GAAG,IAAI,CAuCd"}
package/dist/server.d.ts CHANGED
@@ -9,6 +9,11 @@
9
9
  * Diagnostics are intentionally omitted per design decision A7.
10
10
  */
11
11
  import { type Connection } from "vscode-languageserver/node.js";
12
+ import type { ExtensionDefinition } from "@formspec/core";
13
+ export interface CreateServerOptions {
14
+ /** Optional extension definitions whose custom tags should be surfaced by tooling. */
15
+ readonly extensions?: readonly ExtensionDefinition[];
16
+ }
12
17
  /**
13
18
  * Creates and configures the FormSpec language server connection.
14
19
  *
@@ -17,5 +22,5 @@ import { type Connection } from "vscode-languageserver/node.js";
17
22
  *
18
23
  * @returns The configured LSP connection (not yet listening)
19
24
  */
20
- export declare function createServer(): Connection;
25
+ export declare function createServer(options?: CreateServerOptions): Connection;
21
26
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,+BAA+B,CAAC;AAKvC;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,UAAU,CA6CzC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAK1D,MAAM,WAAW,mBAAmB;IAClC,sFAAsF;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACtD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA6C1E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formspec/language-server",
3
- "version": "0.1.0-alpha.16",
3
+ "version": "0.1.0-alpha.19",
4
4
  "description": "Language server for FormSpec — completions, hover, and go-to-definition for JSDoc constraint tags",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "vscode-languageserver": "^9.0.1",
22
22
  "vscode-languageserver-textdocument": "^1.0.12",
23
- "@formspec/core": "0.1.0-alpha.16"
23
+ "@formspec/core": "0.1.0-alpha.19"
24
24
  },
25
25
  "devDependencies": {
26
26
  "vitest": "^3.0.0"