@coherent.js/language-server 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/analysis/coherent-analyzer.d.ts +93 -0
- package/dist/analysis/coherent-analyzer.d.ts.map +1 -0
- package/dist/analysis/coherent-analyzer.js +288 -0
- package/dist/analysis/coherent-analyzer.js.map +1 -0
- package/dist/analysis/element-validator.d.ts +45 -0
- package/dist/analysis/element-validator.d.ts.map +1 -0
- package/dist/analysis/element-validator.js +84 -0
- package/dist/analysis/element-validator.js.map +1 -0
- package/dist/analysis/nesting-validator.d.ts +49 -0
- package/dist/analysis/nesting-validator.d.ts.map +1 -0
- package/dist/analysis/nesting-validator.js +68 -0
- package/dist/analysis/nesting-validator.js.map +1 -0
- package/dist/data/element-attributes.d.ts +92 -0
- package/dist/data/element-attributes.d.ts.map +1 -0
- package/dist/data/element-attributes.generated.json +7085 -0
- package/dist/data/element-attributes.js +282 -0
- package/dist/data/element-attributes.js.map +1 -0
- package/dist/data/nesting-rules.d.ts +67 -0
- package/dist/data/nesting-rules.d.ts.map +1 -0
- package/dist/data/nesting-rules.js +240 -0
- package/dist/data/nesting-rules.js.map +1 -0
- package/dist/providers/code-actions.d.ts +15 -0
- package/dist/providers/code-actions.d.ts.map +1 -0
- package/dist/providers/code-actions.js +191 -0
- package/dist/providers/code-actions.js.map +1 -0
- package/dist/providers/completion.d.ts +15 -0
- package/dist/providers/completion.d.ts.map +1 -0
- package/dist/providers/completion.js +247 -0
- package/dist/providers/completion.js.map +1 -0
- package/dist/providers/diagnostics.d.ts +26 -0
- package/dist/providers/diagnostics.d.ts.map +1 -0
- package/dist/providers/diagnostics.js +143 -0
- package/dist/providers/diagnostics.js.map +1 -0
- package/dist/providers/hover.d.ts +15 -0
- package/dist/providers/hover.d.ts.map +1 -0
- package/dist/providers/hover.js +215 -0
- package/dist/providers/hover.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +82 -0
- package/dist/server.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Action Provider
|
|
3
|
+
*
|
|
4
|
+
* Provides quick fix code actions for validation errors.
|
|
5
|
+
*/
|
|
6
|
+
import { CodeActionKind, TextEdit, } from 'vscode-languageserver';
|
|
7
|
+
/**
|
|
8
|
+
* Register the code action provider.
|
|
9
|
+
*
|
|
10
|
+
* @param connection - LSP connection
|
|
11
|
+
* @param documents - Text document manager
|
|
12
|
+
*/
|
|
13
|
+
export function registerCodeActionProvider(connection, documents) {
|
|
14
|
+
connection.onCodeAction((params) => {
|
|
15
|
+
const document = documents.get(params.textDocument.uri);
|
|
16
|
+
if (!document) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const actions = [];
|
|
20
|
+
for (const diagnostic of params.context.diagnostics) {
|
|
21
|
+
// Only process diagnostics from our language server
|
|
22
|
+
if (diagnostic.source !== 'coherent') {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const codeActions = getCodeActionsForDiagnostic(diagnostic, document, params);
|
|
26
|
+
actions.push(...codeActions);
|
|
27
|
+
}
|
|
28
|
+
return actions;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get code actions for a specific diagnostic.
|
|
33
|
+
*/
|
|
34
|
+
function getCodeActionsForDiagnostic(diagnostic, document, params) {
|
|
35
|
+
const actions = [];
|
|
36
|
+
const uri = params.textDocument.uri;
|
|
37
|
+
switch (diagnostic.code) {
|
|
38
|
+
case 'typo-attribute': {
|
|
39
|
+
// Get suggestion from diagnostic data
|
|
40
|
+
const suggestion = diagnostic.data?.suggestion;
|
|
41
|
+
if (suggestion) {
|
|
42
|
+
actions.push({
|
|
43
|
+
title: `Change to '${suggestion}'`,
|
|
44
|
+
kind: CodeActionKind.QuickFix,
|
|
45
|
+
diagnostics: [diagnostic],
|
|
46
|
+
isPreferred: true,
|
|
47
|
+
edit: {
|
|
48
|
+
changes: {
|
|
49
|
+
[uri]: [
|
|
50
|
+
TextEdit.replace(diagnostic.range, suggestion),
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Also offer to remove the attribute
|
|
57
|
+
actions.push(createRemoveAttributeAction(diagnostic, uri, document));
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'invalid-attribute': {
|
|
61
|
+
// Offer to remove the invalid attribute
|
|
62
|
+
actions.push(createRemoveAttributeAction(diagnostic, uri, document));
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case 'void-children': {
|
|
66
|
+
// Offer to remove the children property
|
|
67
|
+
const removeAction = createRemovePropertyAction(diagnostic, uri, document, 'Remove children property', 'children');
|
|
68
|
+
if (removeAction) {
|
|
69
|
+
actions.push(removeAction);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'invalid-nesting':
|
|
74
|
+
case 'invalid-parent':
|
|
75
|
+
case 'block-in-inline':
|
|
76
|
+
case 'invalid-child': {
|
|
77
|
+
// For nesting errors, we can't easily auto-fix, but we can provide documentation
|
|
78
|
+
actions.push({
|
|
79
|
+
title: 'Learn about HTML nesting rules',
|
|
80
|
+
kind: CodeActionKind.QuickFix,
|
|
81
|
+
diagnostics: [diagnostic],
|
|
82
|
+
command: {
|
|
83
|
+
title: 'Open HTML nesting documentation',
|
|
84
|
+
command: 'coherent.openNestingDocs',
|
|
85
|
+
arguments: [diagnostic.data],
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return actions;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create a code action to remove an invalid attribute.
|
|
95
|
+
*/
|
|
96
|
+
function createRemoveAttributeAction(diagnostic, uri, document) {
|
|
97
|
+
// Find the full property (including value and trailing comma)
|
|
98
|
+
const range = expandRangeToFullProperty(diagnostic.range, document);
|
|
99
|
+
return {
|
|
100
|
+
title: 'Remove invalid attribute',
|
|
101
|
+
kind: CodeActionKind.QuickFix,
|
|
102
|
+
diagnostics: [diagnostic],
|
|
103
|
+
edit: {
|
|
104
|
+
changes: {
|
|
105
|
+
[uri]: [TextEdit.del(range)],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create a code action to remove a specific property.
|
|
112
|
+
*/
|
|
113
|
+
function createRemovePropertyAction(diagnostic, uri, document, title, _propertyName) {
|
|
114
|
+
// Find the full property line
|
|
115
|
+
const range = expandRangeToFullProperty(diagnostic.range, document);
|
|
116
|
+
return {
|
|
117
|
+
title,
|
|
118
|
+
kind: CodeActionKind.QuickFix,
|
|
119
|
+
diagnostics: [diagnostic],
|
|
120
|
+
edit: {
|
|
121
|
+
changes: {
|
|
122
|
+
[uri]: [TextEdit.del(range)],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Expand a range to include the full property assignment and trailing comma.
|
|
129
|
+
*
|
|
130
|
+
* This ensures clean removal without leaving orphan commas or whitespace.
|
|
131
|
+
*/
|
|
132
|
+
function expandRangeToFullProperty(range, document) {
|
|
133
|
+
const text = document.getText();
|
|
134
|
+
const startOffset = document.offsetAt(range.start);
|
|
135
|
+
const endOffset = document.offsetAt(range.end);
|
|
136
|
+
// Find start of the line (for property removal)
|
|
137
|
+
let newStart = startOffset;
|
|
138
|
+
while (newStart > 0 && text[newStart - 1] !== '\n') {
|
|
139
|
+
newStart--;
|
|
140
|
+
}
|
|
141
|
+
// Find end of the property (including : and value)
|
|
142
|
+
let newEnd = endOffset;
|
|
143
|
+
// Skip to end of value
|
|
144
|
+
let depth = 0;
|
|
145
|
+
let inString = false;
|
|
146
|
+
let stringChar = '';
|
|
147
|
+
while (newEnd < text.length) {
|
|
148
|
+
const char = text[newEnd];
|
|
149
|
+
// Handle string literals
|
|
150
|
+
if ((char === '"' || char === "'") && text[newEnd - 1] !== '\\') {
|
|
151
|
+
if (!inString) {
|
|
152
|
+
inString = true;
|
|
153
|
+
stringChar = char;
|
|
154
|
+
}
|
|
155
|
+
else if (char === stringChar) {
|
|
156
|
+
inString = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!inString) {
|
|
160
|
+
// Track nesting depth
|
|
161
|
+
if (char === '{' || char === '[' || char === '(') {
|
|
162
|
+
depth++;
|
|
163
|
+
}
|
|
164
|
+
else if (char === '}' || char === ']' || char === ')') {
|
|
165
|
+
if (depth === 0)
|
|
166
|
+
break;
|
|
167
|
+
depth--;
|
|
168
|
+
}
|
|
169
|
+
else if (depth === 0 && char === ',') {
|
|
170
|
+
newEnd++; // Include the comma
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
else if (depth === 0 && (char === '\n' || char === '\r')) {
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
newEnd++;
|
|
178
|
+
}
|
|
179
|
+
// Skip trailing whitespace and newline
|
|
180
|
+
while (newEnd < text.length && (text[newEnd] === ' ' || text[newEnd] === '\t')) {
|
|
181
|
+
newEnd++;
|
|
182
|
+
}
|
|
183
|
+
if (newEnd < text.length && text[newEnd] === '\n') {
|
|
184
|
+
newEnd++;
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
start: document.positionAt(newStart),
|
|
188
|
+
end: document.positionAt(newEnd),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=code-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-actions.js","sourceRoot":"","sources":["../../src/providers/code-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion Provider
|
|
3
|
+
*
|
|
4
|
+
* Provides autocomplete suggestions for Coherent.js elements and attributes.
|
|
5
|
+
*/
|
|
6
|
+
import { Connection, TextDocuments } from 'vscode-languageserver';
|
|
7
|
+
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
8
|
+
/**
|
|
9
|
+
* Register the completion provider.
|
|
10
|
+
*
|
|
11
|
+
* @param connection - LSP connection
|
|
12
|
+
* @param documents - Text document manager
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerCompletionProvider(connection: Connection, documents: TextDocuments<TextDocument>): void;
|
|
15
|
+
//# sourceMappingURL=completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/providers/completion.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,aAAa,EAMd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAalE;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,aAAa,CAAC,YAAY,CAAC,GACrC,IAAI,CAkEN"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion Provider
|
|
3
|
+
*
|
|
4
|
+
* Provides autocomplete suggestions for Coherent.js elements and attributes.
|
|
5
|
+
*/
|
|
6
|
+
import { CompletionItemKind, InsertTextFormat, } from 'vscode-languageserver';
|
|
7
|
+
import { createSourceFile, getPositionContext, } from '../analysis/coherent-analyzer.js';
|
|
8
|
+
import { HTML_ELEMENTS, getAttributesForElement, getElementDescription, getAttributeDescription, isVoidElement, } from '../data/element-attributes.js';
|
|
9
|
+
/**
|
|
10
|
+
* Register the completion provider.
|
|
11
|
+
*
|
|
12
|
+
* @param connection - LSP connection
|
|
13
|
+
* @param documents - Text document manager
|
|
14
|
+
*/
|
|
15
|
+
export function registerCompletionProvider(connection, documents) {
|
|
16
|
+
// Handle completion requests
|
|
17
|
+
connection.onCompletion((params) => {
|
|
18
|
+
const document = documents.get(params.textDocument.uri);
|
|
19
|
+
if (!document) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const content = document.getText();
|
|
24
|
+
const sourceFile = createSourceFile(content, params.textDocument.uri);
|
|
25
|
+
const context = getPositionContext(sourceFile, params.position);
|
|
26
|
+
switch (context.type) {
|
|
27
|
+
case 'tag-name':
|
|
28
|
+
return getTagNameCompletions();
|
|
29
|
+
case 'attribute-name':
|
|
30
|
+
if (context.element) {
|
|
31
|
+
return getAttributeCompletions(context.element.tagName);
|
|
32
|
+
}
|
|
33
|
+
return [];
|
|
34
|
+
case 'children':
|
|
35
|
+
return getChildCompletions();
|
|
36
|
+
case 'attribute-value':
|
|
37
|
+
// Future: could provide value suggestions for specific attributes
|
|
38
|
+
return [];
|
|
39
|
+
case 'outside':
|
|
40
|
+
// When outside any element, suggest starting an element
|
|
41
|
+
return getElementStartCompletions();
|
|
42
|
+
default:
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error('[coherent-lsp] Completion error:', error);
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Handle completion item resolve for additional detail
|
|
52
|
+
connection.onCompletionResolve((item) => {
|
|
53
|
+
// Add documentation on resolve
|
|
54
|
+
if (item.data?.type === 'tag') {
|
|
55
|
+
const tagName = item.label;
|
|
56
|
+
item.documentation = {
|
|
57
|
+
kind: 'markdown',
|
|
58
|
+
value: getElementDescription(tagName) + '\n\n' + getElementDocumentation(tagName),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
else if (item.data?.type === 'attribute' && item.data?.tagName) {
|
|
62
|
+
const attrName = item.label;
|
|
63
|
+
const tagName = item.data.tagName;
|
|
64
|
+
const description = getAttributeDescription(tagName, attrName);
|
|
65
|
+
if (description) {
|
|
66
|
+
item.documentation = {
|
|
67
|
+
kind: 'markdown',
|
|
68
|
+
value: description,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return item;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get completions for HTML tag names.
|
|
77
|
+
*/
|
|
78
|
+
function getTagNameCompletions() {
|
|
79
|
+
const items = [];
|
|
80
|
+
// Sort elements by common usage
|
|
81
|
+
const commonElements = [
|
|
82
|
+
'div', 'span', 'p', 'a', 'button', 'input', 'img', 'form',
|
|
83
|
+
'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'section', 'article',
|
|
84
|
+
];
|
|
85
|
+
for (const tagName of commonElements) {
|
|
86
|
+
if (HTML_ELEMENTS.has(tagName)) {
|
|
87
|
+
items.push(createTagCompletion(tagName, true));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Add remaining elements
|
|
91
|
+
for (const tagName of HTML_ELEMENTS) {
|
|
92
|
+
if (!commonElements.includes(tagName)) {
|
|
93
|
+
items.push(createTagCompletion(tagName, false));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return items;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a completion item for a tag name.
|
|
100
|
+
*/
|
|
101
|
+
function createTagCompletion(tagName, isCommon) {
|
|
102
|
+
return {
|
|
103
|
+
label: tagName,
|
|
104
|
+
kind: CompletionItemKind.Class,
|
|
105
|
+
detail: isVoidElement(tagName) ? `<${tagName}/> (void element)` : `<${tagName}>`,
|
|
106
|
+
sortText: isCommon ? `0${tagName}` : `1${tagName}`,
|
|
107
|
+
data: { type: 'tag', tagName },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get completions for attributes on a specific element.
|
|
112
|
+
*/
|
|
113
|
+
function getAttributeCompletions(tagName) {
|
|
114
|
+
const attributes = getAttributesForElement(tagName);
|
|
115
|
+
const items = [];
|
|
116
|
+
// Common attributes first
|
|
117
|
+
const commonAttrs = ['className', 'id', 'onClick', 'style', 'children', 'text'];
|
|
118
|
+
for (const attr of attributes) {
|
|
119
|
+
const isCommon = commonAttrs.includes(attr.name);
|
|
120
|
+
items.push({
|
|
121
|
+
label: attr.name,
|
|
122
|
+
kind: CompletionItemKind.Property,
|
|
123
|
+
detail: formatType(attr.type),
|
|
124
|
+
sortText: isCommon ? `0${attr.name}` : `1${attr.name}`,
|
|
125
|
+
insertText: getAttributeInsertText(attr.name, attr.type),
|
|
126
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
127
|
+
data: { type: 'attribute', tagName, attributeName: attr.name },
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return items;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get insert text for an attribute with appropriate value placeholder.
|
|
134
|
+
*/
|
|
135
|
+
function getAttributeInsertText(name, type) {
|
|
136
|
+
// Boolean attributes
|
|
137
|
+
if (type === 'boolean') {
|
|
138
|
+
return `${name}: \${1|true,false|}`;
|
|
139
|
+
}
|
|
140
|
+
// String attributes
|
|
141
|
+
if (type === 'string' || type.includes('string')) {
|
|
142
|
+
return `${name}: '\${1}'`;
|
|
143
|
+
}
|
|
144
|
+
// Number attributes
|
|
145
|
+
if (type === 'number') {
|
|
146
|
+
return `${name}: \${1:0}`;
|
|
147
|
+
}
|
|
148
|
+
// Event handlers
|
|
149
|
+
if (name.startsWith('on') && name[2] === name[2].toUpperCase()) {
|
|
150
|
+
return `${name}: () => {\n \${1}\n}`;
|
|
151
|
+
}
|
|
152
|
+
// Children array
|
|
153
|
+
if (name === 'children') {
|
|
154
|
+
return `${name}: [\n \${1}\n]`;
|
|
155
|
+
}
|
|
156
|
+
// Style object
|
|
157
|
+
if (name === 'style') {
|
|
158
|
+
return `${name}: {\n \${1}\n}`;
|
|
159
|
+
}
|
|
160
|
+
// Default
|
|
161
|
+
return `${name}: \${1}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Format type for display.
|
|
165
|
+
*/
|
|
166
|
+
function formatType(type) {
|
|
167
|
+
// Simplify complex types for display
|
|
168
|
+
if (type.length > 50) {
|
|
169
|
+
return type.substring(0, 47) + '...';
|
|
170
|
+
}
|
|
171
|
+
return type;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get completions for starting a child element.
|
|
175
|
+
*/
|
|
176
|
+
function getChildCompletions() {
|
|
177
|
+
const items = [];
|
|
178
|
+
// Suggest opening an element
|
|
179
|
+
items.push({
|
|
180
|
+
label: '{ element }',
|
|
181
|
+
kind: CompletionItemKind.Snippet,
|
|
182
|
+
detail: 'Create a child element',
|
|
183
|
+
insertText: '{ ${1:div}: { ${2} } }',
|
|
184
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
185
|
+
sortText: '0element',
|
|
186
|
+
});
|
|
187
|
+
// Common child elements
|
|
188
|
+
const commonChildren = ['div', 'span', 'p', 'a', 'button', 'li'];
|
|
189
|
+
for (const tagName of commonChildren) {
|
|
190
|
+
items.push({
|
|
191
|
+
label: `{ ${tagName}: {} }`,
|
|
192
|
+
kind: CompletionItemKind.Snippet,
|
|
193
|
+
detail: `Create a <${tagName}> child`,
|
|
194
|
+
insertText: `{ ${tagName}: { \${1} } }`,
|
|
195
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
196
|
+
sortText: `1${tagName}`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return items;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get completions for starting an element (outside any existing element).
|
|
203
|
+
*/
|
|
204
|
+
function getElementStartCompletions() {
|
|
205
|
+
const items = [];
|
|
206
|
+
// Coherent element snippet
|
|
207
|
+
items.push({
|
|
208
|
+
label: 'coherent-element',
|
|
209
|
+
kind: CompletionItemKind.Snippet,
|
|
210
|
+
detail: 'Create a Coherent.js element',
|
|
211
|
+
insertText: '{\n ${1:div}: {\n ${2:className}: \'${3}\',\n ${4:children}: [${5}]\n }\n}',
|
|
212
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
213
|
+
sortText: '0coherent',
|
|
214
|
+
});
|
|
215
|
+
// Coherent component snippet
|
|
216
|
+
items.push({
|
|
217
|
+
label: 'coherent-component',
|
|
218
|
+
kind: CompletionItemKind.Snippet,
|
|
219
|
+
detail: 'Create a Coherent.js component function',
|
|
220
|
+
insertText: 'function ${1:ComponentName}(${2:props}) {\n return {\n ${3:div}: {\n className: \'${4:component}\',\n children: [${5}]\n }\n };\n}',
|
|
221
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
222
|
+
sortText: '0component',
|
|
223
|
+
});
|
|
224
|
+
return items;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get detailed documentation for an element.
|
|
228
|
+
*/
|
|
229
|
+
function getElementDocumentation(tagName) {
|
|
230
|
+
const attrs = getAttributesForElement(tagName);
|
|
231
|
+
const isVoid = isVoidElement(tagName);
|
|
232
|
+
let doc = `### Example\n\`\`\`javascript\n`;
|
|
233
|
+
if (isVoid) {
|
|
234
|
+
doc += `{ ${tagName}: { /* attributes */ } }\n`;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
doc += `{ ${tagName}: {\n className: 'example',\n children: [/* elements */]\n} }\n`;
|
|
238
|
+
}
|
|
239
|
+
doc += `\`\`\`\n\n`;
|
|
240
|
+
// List some common attributes
|
|
241
|
+
const commonAttrs = attrs.slice(0, 5).map(a => `- \`${a.name}\`: ${a.type}`).join('\n');
|
|
242
|
+
if (commonAttrs) {
|
|
243
|
+
doc += `### Common Attributes\n${commonAttrs}\n`;
|
|
244
|
+
}
|
|
245
|
+
return doc;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/providers/completion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics Provider
|
|
3
|
+
*
|
|
4
|
+
* Validates documents and publishes diagnostics (errors/warnings)
|
|
5
|
+
* to the LSP client for display in the IDE.
|
|
6
|
+
*/
|
|
7
|
+
import { Connection, TextDocuments, Diagnostic } from 'vscode-languageserver';
|
|
8
|
+
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
9
|
+
/**
|
|
10
|
+
* Validate a single document and return diagnostics.
|
|
11
|
+
*
|
|
12
|
+
* @param document - Text document to validate
|
|
13
|
+
* @returns Array of diagnostics
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateDocument(document: TextDocument): Diagnostic[];
|
|
16
|
+
/**
|
|
17
|
+
* Register the diagnostics provider.
|
|
18
|
+
*
|
|
19
|
+
* Sets up document change listeners to trigger validation
|
|
20
|
+
* with debouncing.
|
|
21
|
+
*
|
|
22
|
+
* @param connection - LSP connection
|
|
23
|
+
* @param documents - Text document manager
|
|
24
|
+
*/
|
|
25
|
+
export declare function registerDiagnosticProvider(connection: Connection, documents: TextDocuments<TextDocument>): void;
|
|
26
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/providers/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,UAAU,EACV,aAAa,EACb,UAAU,EAEX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AA+ClE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,UAAU,EAAE,CA4CrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,aAAa,CAAC,YAAY,CAAC,GACrC,IAAI,CAuBN"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics Provider
|
|
3
|
+
*
|
|
4
|
+
* Validates documents and publishes diagnostics (errors/warnings)
|
|
5
|
+
* to the LSP client for display in the IDE.
|
|
6
|
+
*/
|
|
7
|
+
import { DiagnosticSeverity, } from 'vscode-languageserver';
|
|
8
|
+
import { findCoherentElements, createSourceFile } from '../analysis/coherent-analyzer.js';
|
|
9
|
+
import { validateAllAttributes } from '../analysis/element-validator.js';
|
|
10
|
+
import { validateAllNesting } from '../analysis/nesting-validator.js';
|
|
11
|
+
/**
|
|
12
|
+
* Debounce timeout for validation (ms).
|
|
13
|
+
* Prevents excessive validation during rapid typing.
|
|
14
|
+
*/
|
|
15
|
+
const VALIDATION_DEBOUNCE_MS = 300;
|
|
16
|
+
/**
|
|
17
|
+
* Map of document URIs to debounce timers.
|
|
18
|
+
*/
|
|
19
|
+
const validationTimers = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* Convert an attribute validation error to an LSP diagnostic.
|
|
22
|
+
*/
|
|
23
|
+
function attributeErrorToDiagnostic(error) {
|
|
24
|
+
return {
|
|
25
|
+
severity: error.severity === 'error' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
|
|
26
|
+
range: error.range,
|
|
27
|
+
message: error.message,
|
|
28
|
+
source: 'coherent',
|
|
29
|
+
code: error.code,
|
|
30
|
+
data: {
|
|
31
|
+
...error.data,
|
|
32
|
+
suggestion: error.suggestion,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert a nesting validation error to an LSP diagnostic.
|
|
38
|
+
*/
|
|
39
|
+
function nestingErrorToDiagnostic(error) {
|
|
40
|
+
return {
|
|
41
|
+
severity: error.severity === 'error' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
|
|
42
|
+
range: error.range,
|
|
43
|
+
message: error.message,
|
|
44
|
+
source: 'coherent',
|
|
45
|
+
code: error.code,
|
|
46
|
+
data: error.data,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate a single document and return diagnostics.
|
|
51
|
+
*
|
|
52
|
+
* @param document - Text document to validate
|
|
53
|
+
* @returns Array of diagnostics
|
|
54
|
+
*/
|
|
55
|
+
export function validateDocument(document) {
|
|
56
|
+
const diagnostics = [];
|
|
57
|
+
try {
|
|
58
|
+
const content = document.getText();
|
|
59
|
+
const uri = document.uri;
|
|
60
|
+
// Determine file type for proper parsing
|
|
61
|
+
const isTypeScript = uri.endsWith('.ts') || uri.endsWith('.tsx');
|
|
62
|
+
const isJavaScript = uri.endsWith('.js') || uri.endsWith('.jsx') || uri.endsWith('.mjs');
|
|
63
|
+
// Skip non-JS/TS files
|
|
64
|
+
if (!isTypeScript && !isJavaScript) {
|
|
65
|
+
return diagnostics;
|
|
66
|
+
}
|
|
67
|
+
// Parse the document
|
|
68
|
+
const sourceFile = createSourceFile(content, uri);
|
|
69
|
+
// Find all Coherent elements
|
|
70
|
+
const elements = findCoherentElements(sourceFile);
|
|
71
|
+
if (elements.length === 0) {
|
|
72
|
+
// No Coherent elements found, nothing to validate
|
|
73
|
+
return diagnostics;
|
|
74
|
+
}
|
|
75
|
+
// Validate attributes
|
|
76
|
+
const attributeErrors = validateAllAttributes(elements);
|
|
77
|
+
for (const error of attributeErrors) {
|
|
78
|
+
diagnostics.push(attributeErrorToDiagnostic(error));
|
|
79
|
+
}
|
|
80
|
+
// Validate nesting
|
|
81
|
+
const nestingErrors = validateAllNesting(elements);
|
|
82
|
+
for (const error of nestingErrors) {
|
|
83
|
+
diagnostics.push(nestingErrorToDiagnostic(error));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// Log error but don't crash - malformed documents are expected during editing
|
|
88
|
+
console.error('[coherent-lsp] Validation error:', error);
|
|
89
|
+
}
|
|
90
|
+
return diagnostics;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Register the diagnostics provider.
|
|
94
|
+
*
|
|
95
|
+
* Sets up document change listeners to trigger validation
|
|
96
|
+
* with debouncing.
|
|
97
|
+
*
|
|
98
|
+
* @param connection - LSP connection
|
|
99
|
+
* @param documents - Text document manager
|
|
100
|
+
*/
|
|
101
|
+
export function registerDiagnosticProvider(connection, documents) {
|
|
102
|
+
// Validate on document open
|
|
103
|
+
documents.onDidOpen((event) => {
|
|
104
|
+
scheduleValidation(connection, event.document);
|
|
105
|
+
});
|
|
106
|
+
// Validate on document change (debounced)
|
|
107
|
+
documents.onDidChangeContent((event) => {
|
|
108
|
+
scheduleValidation(connection, event.document);
|
|
109
|
+
});
|
|
110
|
+
// Clear diagnostics when document closes
|
|
111
|
+
documents.onDidClose((event) => {
|
|
112
|
+
// Cancel any pending validation
|
|
113
|
+
const timer = validationTimers.get(event.document.uri);
|
|
114
|
+
if (timer) {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
validationTimers.delete(event.document.uri);
|
|
117
|
+
}
|
|
118
|
+
// Clear diagnostics
|
|
119
|
+
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Schedule document validation with debouncing.
|
|
124
|
+
*
|
|
125
|
+
* @param connection - LSP connection
|
|
126
|
+
* @param document - Document to validate
|
|
127
|
+
*/
|
|
128
|
+
function scheduleValidation(connection, document) {
|
|
129
|
+
const uri = document.uri;
|
|
130
|
+
// Cancel existing timer
|
|
131
|
+
const existingTimer = validationTimers.get(uri);
|
|
132
|
+
if (existingTimer) {
|
|
133
|
+
clearTimeout(existingTimer);
|
|
134
|
+
}
|
|
135
|
+
// Schedule new validation
|
|
136
|
+
const timer = setTimeout(() => {
|
|
137
|
+
validationTimers.delete(uri);
|
|
138
|
+
const diagnostics = validateDocument(document);
|
|
139
|
+
connection.sendDiagnostics({ uri, diagnostics });
|
|
140
|
+
}, VALIDATION_DEBOUNCE_MS);
|
|
141
|
+
validationTimers.set(uri, timer);
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/providers/diagnostics.ts"],"names":[],"mappings":"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"}
|