@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Drouvin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coherent.js Object Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Uses TypeScript AST to detect and analyze Coherent.js element objects
|
|
5
|
+
* in source code. Identifies tag names, attributes, children, and nesting.
|
|
6
|
+
*/
|
|
7
|
+
import * as ts from 'typescript';
|
|
8
|
+
import { Range, Position } from 'vscode-languageserver';
|
|
9
|
+
/**
|
|
10
|
+
* Information about a detected Coherent.js element.
|
|
11
|
+
*/
|
|
12
|
+
export interface CoherentElementInfo {
|
|
13
|
+
/** HTML tag name (e.g., 'div', 'input') */
|
|
14
|
+
tagName: string;
|
|
15
|
+
/** Source code range of the entire element object */
|
|
16
|
+
range: Range;
|
|
17
|
+
/** Source code range of just the tag name property */
|
|
18
|
+
tagNameRange: Range;
|
|
19
|
+
/** Attributes found on this element */
|
|
20
|
+
attributes: AttributeInfo[];
|
|
21
|
+
/** Parent element info (if nested) */
|
|
22
|
+
parent: CoherentElementInfo | null;
|
|
23
|
+
/** Children elements */
|
|
24
|
+
children: CoherentElementInfo[];
|
|
25
|
+
/** The TypeScript AST node */
|
|
26
|
+
node: ts.ObjectLiteralExpression;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Information about an attribute on a Coherent element.
|
|
30
|
+
*/
|
|
31
|
+
export interface AttributeInfo {
|
|
32
|
+
/** Attribute name (e.g., 'className', 'onClick') */
|
|
33
|
+
name: string;
|
|
34
|
+
/** Attribute value as string (for display) */
|
|
35
|
+
value: string;
|
|
36
|
+
/** Source code range of the attribute */
|
|
37
|
+
range: Range;
|
|
38
|
+
/** Source code range of just the attribute name */
|
|
39
|
+
nameRange: Range;
|
|
40
|
+
/** The TypeScript AST node */
|
|
41
|
+
node: ts.PropertyAssignment | ts.ShorthandPropertyAssignment;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Context for position-based queries.
|
|
45
|
+
*/
|
|
46
|
+
export interface PositionContext {
|
|
47
|
+
/** Type of context at the position */
|
|
48
|
+
type: 'tag-name' | 'attribute-name' | 'attribute-value' | 'children' | 'outside';
|
|
49
|
+
/** Current element (if inside one) */
|
|
50
|
+
element?: CoherentElementInfo;
|
|
51
|
+
/** Current attribute (if inside one) */
|
|
52
|
+
attribute?: AttributeInfo;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a node is a Coherent.js element object.
|
|
56
|
+
*
|
|
57
|
+
* A Coherent element is an object with a single property whose name is an HTML tag,
|
|
58
|
+
* and whose value is an object containing attributes.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isCoherentElement(node: ts.Node, sourceFile: ts.SourceFile): node is ts.ObjectLiteralExpression;
|
|
61
|
+
/**
|
|
62
|
+
* Find all Coherent.js elements in a source file.
|
|
63
|
+
*
|
|
64
|
+
* @param sourceFile - TypeScript source file to analyze
|
|
65
|
+
* @returns Array of found Coherent element info
|
|
66
|
+
*/
|
|
67
|
+
export declare function findCoherentElements(sourceFile: ts.SourceFile): CoherentElementInfo[];
|
|
68
|
+
/**
|
|
69
|
+
* Get the Coherent element at a specific position.
|
|
70
|
+
*
|
|
71
|
+
* @param sourceFile - TypeScript source file
|
|
72
|
+
* @param position - LSP position
|
|
73
|
+
* @param elements - Previously found elements (optional, will find if not provided)
|
|
74
|
+
* @returns Element info at position or undefined
|
|
75
|
+
*/
|
|
76
|
+
export declare function getElementAtPosition(sourceFile: ts.SourceFile, position: Position, elements?: CoherentElementInfo[]): CoherentElementInfo | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Get the context at a specific position for completion/hover.
|
|
79
|
+
*
|
|
80
|
+
* @param sourceFile - TypeScript source file
|
|
81
|
+
* @param position - LSP position
|
|
82
|
+
* @returns Context information
|
|
83
|
+
*/
|
|
84
|
+
export declare function getPositionContext(sourceFile: ts.SourceFile, position: Position): PositionContext;
|
|
85
|
+
/**
|
|
86
|
+
* Create a TypeScript source file from text content.
|
|
87
|
+
*
|
|
88
|
+
* @param content - Source file content
|
|
89
|
+
* @param fileName - File name for the source file
|
|
90
|
+
* @returns TypeScript SourceFile
|
|
91
|
+
*/
|
|
92
|
+
export declare function createSourceFile(content: string, fileName: string): ts.SourceFile;
|
|
93
|
+
//# sourceMappingURL=coherent-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coherent-analyzer.d.ts","sourceRoot":"","sources":["../../src/analysis/coherent-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAEhB,qDAAqD;IACrD,KAAK,EAAE,KAAK,CAAC;IAEb,sDAAsD;IACtD,YAAY,EAAE,KAAK,CAAC;IAEpB,uCAAuC;IACvC,UAAU,EAAE,aAAa,EAAE,CAAC;IAE5B,sCAAsC;IACtC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAEnC,wBAAwB;IACxB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAEhC,8BAA8B;IAC9B,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IAEb,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IAEd,yCAAyC;IACzC,KAAK,EAAE,KAAK,CAAC;IAEb,mDAAmD;IACnD,SAAS,EAAE,KAAK,CAAC;IAEjB,8BAA8B;IAC9B,IAAI,EAAE,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,2BAA2B,CAAC;CAC9D;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,UAAU,GAAG,SAAS,CAAC;IAEjF,sCAAsC;IACtC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAE9B,wCAAwC;IACxC,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AA8CD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,uBAAuB,CA4B9G;AAmGD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,mBAAmB,EAAE,CAyBrF;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,EAAE,CAAC,UAAU,EACzB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,mBAAmB,EAAE,GAC/B,mBAAmB,GAAG,SAAS,CAgBjC;AAcD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,EAAE,CAAC,UAAU,EACzB,QAAQ,EAAE,QAAQ,GACjB,eAAe,CA+CjB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAAC,UAAU,CAQjF"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coherent.js Object Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Uses TypeScript AST to detect and analyze Coherent.js element objects
|
|
5
|
+
* in source code. Identifies tag names, attributes, children, and nesting.
|
|
6
|
+
*/
|
|
7
|
+
import * as ts from 'typescript';
|
|
8
|
+
import { HTML_ELEMENTS } from '../data/element-attributes.js';
|
|
9
|
+
/**
|
|
10
|
+
* Convert TypeScript position to LSP position.
|
|
11
|
+
*/
|
|
12
|
+
function tsPositionToLsp(sourceFile, pos) {
|
|
13
|
+
const lineAndChar = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
14
|
+
return {
|
|
15
|
+
line: lineAndChar.line,
|
|
16
|
+
character: lineAndChar.character,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Convert TypeScript range to LSP range.
|
|
21
|
+
*/
|
|
22
|
+
function tsRangeToLsp(sourceFile, start, end) {
|
|
23
|
+
return {
|
|
24
|
+
start: tsPositionToLsp(sourceFile, start),
|
|
25
|
+
end: tsPositionToLsp(sourceFile, end),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Convert LSP position to TypeScript offset.
|
|
30
|
+
*/
|
|
31
|
+
function lspPositionToTs(sourceFile, position) {
|
|
32
|
+
return sourceFile.getPositionOfLineAndCharacter(position.line, position.character);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a position is within a range.
|
|
36
|
+
*/
|
|
37
|
+
function isPositionInRange(position, range) {
|
|
38
|
+
if (position.line < range.start.line || position.line > range.end.line) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (position.line === range.start.line && position.character < range.start.character) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (position.line === range.end.line && position.character > range.end.character) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if a node is a Coherent.js element object.
|
|
51
|
+
*
|
|
52
|
+
* A Coherent element is an object with a single property whose name is an HTML tag,
|
|
53
|
+
* and whose value is an object containing attributes.
|
|
54
|
+
*/
|
|
55
|
+
export function isCoherentElement(node, sourceFile) {
|
|
56
|
+
if (!ts.isObjectLiteralExpression(node)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
// Must have exactly one property
|
|
60
|
+
if (node.properties.length !== 1) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const prop = node.properties[0];
|
|
64
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
// Property name must be an HTML element
|
|
68
|
+
const name = prop.name;
|
|
69
|
+
let tagName;
|
|
70
|
+
if (ts.isIdentifier(name)) {
|
|
71
|
+
tagName = name.text;
|
|
72
|
+
}
|
|
73
|
+
else if (ts.isStringLiteral(name)) {
|
|
74
|
+
tagName = name.text;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return HTML_ELEMENTS.has(tagName);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extract element information from a Coherent element node.
|
|
83
|
+
*/
|
|
84
|
+
function extractElementInfo(node, sourceFile, parent = null) {
|
|
85
|
+
const prop = node.properties[0];
|
|
86
|
+
const name = prop.name;
|
|
87
|
+
let tagName;
|
|
88
|
+
if (ts.isIdentifier(name)) {
|
|
89
|
+
tagName = name.text;
|
|
90
|
+
}
|
|
91
|
+
else if (ts.isStringLiteral(name)) {
|
|
92
|
+
tagName = name.text;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
tagName = 'unknown';
|
|
96
|
+
}
|
|
97
|
+
const elementInfo = {
|
|
98
|
+
tagName,
|
|
99
|
+
range: tsRangeToLsp(sourceFile, node.getStart(sourceFile), node.getEnd()),
|
|
100
|
+
tagNameRange: tsRangeToLsp(sourceFile, name.getStart(sourceFile), name.getEnd()),
|
|
101
|
+
attributes: [],
|
|
102
|
+
parent,
|
|
103
|
+
children: [],
|
|
104
|
+
node,
|
|
105
|
+
};
|
|
106
|
+
// Extract attributes from the property value
|
|
107
|
+
if (ts.isObjectLiteralExpression(prop.initializer)) {
|
|
108
|
+
const attrs = prop.initializer;
|
|
109
|
+
for (const attrProp of attrs.properties) {
|
|
110
|
+
if (ts.isPropertyAssignment(attrProp)) {
|
|
111
|
+
const attrName = ts.isIdentifier(attrProp.name)
|
|
112
|
+
? attrProp.name.text
|
|
113
|
+
: ts.isStringLiteral(attrProp.name)
|
|
114
|
+
? attrProp.name.text
|
|
115
|
+
: null;
|
|
116
|
+
if (attrName) {
|
|
117
|
+
const attrInfo = {
|
|
118
|
+
name: attrName,
|
|
119
|
+
value: attrProp.initializer.getText(sourceFile),
|
|
120
|
+
range: tsRangeToLsp(sourceFile, attrProp.getStart(sourceFile), attrProp.getEnd()),
|
|
121
|
+
nameRange: tsRangeToLsp(sourceFile, attrProp.name.getStart(sourceFile), attrProp.name.getEnd()),
|
|
122
|
+
node: attrProp,
|
|
123
|
+
};
|
|
124
|
+
elementInfo.attributes.push(attrInfo);
|
|
125
|
+
// Check for children array and extract nested elements
|
|
126
|
+
if (attrName === 'children') {
|
|
127
|
+
extractChildElements(attrProp.initializer, sourceFile, elementInfo);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (ts.isShorthandPropertyAssignment(attrProp)) {
|
|
132
|
+
const attrName = attrProp.name.text;
|
|
133
|
+
const attrInfo = {
|
|
134
|
+
name: attrName,
|
|
135
|
+
value: attrName, // Shorthand uses same name as value
|
|
136
|
+
range: tsRangeToLsp(sourceFile, attrProp.getStart(sourceFile), attrProp.getEnd()),
|
|
137
|
+
nameRange: tsRangeToLsp(sourceFile, attrProp.name.getStart(sourceFile), attrProp.name.getEnd()),
|
|
138
|
+
node: attrProp,
|
|
139
|
+
};
|
|
140
|
+
elementInfo.attributes.push(attrInfo);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return elementInfo;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Extract child Coherent elements from a children property value.
|
|
148
|
+
*/
|
|
149
|
+
function extractChildElements(node, sourceFile, parent) {
|
|
150
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
151
|
+
// Children array
|
|
152
|
+
for (const element of node.elements) {
|
|
153
|
+
if (isCoherentElement(element, sourceFile)) {
|
|
154
|
+
const childInfo = extractElementInfo(element, sourceFile, parent);
|
|
155
|
+
parent.children.push(childInfo);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (isCoherentElement(node, sourceFile)) {
|
|
160
|
+
// Single child element
|
|
161
|
+
const childInfo = extractElementInfo(node, sourceFile, parent);
|
|
162
|
+
parent.children.push(childInfo);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Find all Coherent.js elements in a source file.
|
|
167
|
+
*
|
|
168
|
+
* @param sourceFile - TypeScript source file to analyze
|
|
169
|
+
* @returns Array of found Coherent element info
|
|
170
|
+
*/
|
|
171
|
+
export function findCoherentElements(sourceFile) {
|
|
172
|
+
const elements = [];
|
|
173
|
+
const processed = new WeakSet();
|
|
174
|
+
function visit(node, parent = null) {
|
|
175
|
+
if (isCoherentElement(node, sourceFile) && !processed.has(node)) {
|
|
176
|
+
processed.add(node);
|
|
177
|
+
const elementInfo = extractElementInfo(node, sourceFile, parent);
|
|
178
|
+
elements.push(elementInfo);
|
|
179
|
+
// Also collect nested elements that were added as children
|
|
180
|
+
function collectChildren(el) {
|
|
181
|
+
for (const child of el.children) {
|
|
182
|
+
elements.push(child);
|
|
183
|
+
collectChildren(child);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
collectChildren(elementInfo);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
ts.forEachChild(node, (child) => visit(child, parent));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
visit(sourceFile);
|
|
193
|
+
return elements;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get the Coherent element at a specific position.
|
|
197
|
+
*
|
|
198
|
+
* @param sourceFile - TypeScript source file
|
|
199
|
+
* @param position - LSP position
|
|
200
|
+
* @param elements - Previously found elements (optional, will find if not provided)
|
|
201
|
+
* @returns Element info at position or undefined
|
|
202
|
+
*/
|
|
203
|
+
export function getElementAtPosition(sourceFile, position, elements) {
|
|
204
|
+
const allElements = elements || findCoherentElements(sourceFile);
|
|
205
|
+
// Find the most specific (deepest) element containing the position
|
|
206
|
+
let result;
|
|
207
|
+
for (const element of allElements) {
|
|
208
|
+
if (isPositionInRange(position, element.range)) {
|
|
209
|
+
// Prefer more specific (smaller range) element
|
|
210
|
+
if (!result || isRangeContained(element.range, result.range)) {
|
|
211
|
+
result = element;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Check if range a is contained within range b.
|
|
219
|
+
*/
|
|
220
|
+
function isRangeContained(a, b) {
|
|
221
|
+
if (a.start.line > b.start.line || (a.start.line === b.start.line && a.start.character >= b.start.character)) {
|
|
222
|
+
if (a.end.line < b.end.line || (a.end.line === b.end.line && a.end.character <= b.end.character)) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get the context at a specific position for completion/hover.
|
|
230
|
+
*
|
|
231
|
+
* @param sourceFile - TypeScript source file
|
|
232
|
+
* @param position - LSP position
|
|
233
|
+
* @returns Context information
|
|
234
|
+
*/
|
|
235
|
+
export function getPositionContext(sourceFile, position) {
|
|
236
|
+
const elements = findCoherentElements(sourceFile);
|
|
237
|
+
const element = getElementAtPosition(sourceFile, position, elements);
|
|
238
|
+
if (!element) {
|
|
239
|
+
return { type: 'outside' };
|
|
240
|
+
}
|
|
241
|
+
// Check if on tag name
|
|
242
|
+
if (isPositionInRange(position, element.tagNameRange)) {
|
|
243
|
+
return { type: 'tag-name', element };
|
|
244
|
+
}
|
|
245
|
+
// Check if on an attribute
|
|
246
|
+
for (const attr of element.attributes) {
|
|
247
|
+
if (isPositionInRange(position, attr.nameRange)) {
|
|
248
|
+
return { type: 'attribute-name', element, attribute: attr };
|
|
249
|
+
}
|
|
250
|
+
if (isPositionInRange(position, attr.range) && !isPositionInRange(position, attr.nameRange)) {
|
|
251
|
+
return { type: 'attribute-value', element, attribute: attr };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Check if in children context (after children: [ or just after children:)
|
|
255
|
+
const offset = lspPositionToTs(sourceFile, position);
|
|
256
|
+
const text = sourceFile.text;
|
|
257
|
+
// Look backward from position to see context
|
|
258
|
+
let i = offset - 1;
|
|
259
|
+
while (i >= 0 && (text[i] === ' ' || text[i] === '\n' || text[i] === '\r' || text[i] === '\t')) {
|
|
260
|
+
i--;
|
|
261
|
+
}
|
|
262
|
+
if (i >= 0) {
|
|
263
|
+
// Check if we're right after [ or { in a children context
|
|
264
|
+
if (text[i] === '[' || text[i] === '{' || text[i] === ',') {
|
|
265
|
+
// Check if this is within a children attribute
|
|
266
|
+
const childrenAttr = element.attributes.find(a => a.name === 'children');
|
|
267
|
+
if (childrenAttr && isPositionInRange(position, childrenAttr.range)) {
|
|
268
|
+
return { type: 'children', element };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Default to attribute-name context if inside element but not on specific attribute
|
|
273
|
+
// This allows completion of new attributes
|
|
274
|
+
return { type: 'attribute-name', element };
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Create a TypeScript source file from text content.
|
|
278
|
+
*
|
|
279
|
+
* @param content - Source file content
|
|
280
|
+
* @param fileName - File name for the source file
|
|
281
|
+
* @returns TypeScript SourceFile
|
|
282
|
+
*/
|
|
283
|
+
export function createSourceFile(content, fileName) {
|
|
284
|
+
return ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, // setParentNodes
|
|
285
|
+
ts.ScriptKind.TSX // Support both JS and TSX syntax
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=coherent-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coherent-analyzer.js","sourceRoot":"","sources":["../../src/analysis/coherent-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element Attribute Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates attributes on Coherent.js elements against the allowed
|
|
5
|
+
* attributes for each HTML element type.
|
|
6
|
+
*/
|
|
7
|
+
import { Range } from 'vscode-languageserver';
|
|
8
|
+
import { CoherentElementInfo } from './coherent-analyzer.js';
|
|
9
|
+
/**
|
|
10
|
+
* Validation error for an attribute.
|
|
11
|
+
*/
|
|
12
|
+
export interface AttributeValidationError {
|
|
13
|
+
/** Error message */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Source range of the error */
|
|
16
|
+
range: Range;
|
|
17
|
+
/** Error code for categorization */
|
|
18
|
+
code: 'invalid-attribute' | 'typo-attribute' | 'void-children';
|
|
19
|
+
/** Severity of the error */
|
|
20
|
+
severity: 'error' | 'warning';
|
|
21
|
+
/** Suggestion for fix (for typos) */
|
|
22
|
+
suggestion?: string;
|
|
23
|
+
/** Additional data for code actions */
|
|
24
|
+
data?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate attributes on a Coherent element.
|
|
28
|
+
*
|
|
29
|
+
* Checks for:
|
|
30
|
+
* - Invalid attribute names for the element
|
|
31
|
+
* - Typos in attribute names (with suggestions)
|
|
32
|
+
* - Children on void elements
|
|
33
|
+
*
|
|
34
|
+
* @param element - The Coherent element to validate
|
|
35
|
+
* @returns Array of validation errors
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateAttributes(element: CoherentElementInfo): AttributeValidationError[];
|
|
38
|
+
/**
|
|
39
|
+
* Validate multiple elements at once.
|
|
40
|
+
*
|
|
41
|
+
* @param elements - Array of Coherent elements to validate
|
|
42
|
+
* @returns Array of all validation errors
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateAllAttributes(elements: CoherentElementInfo[]): AttributeValidationError[];
|
|
45
|
+
//# sourceMappingURL=element-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-validator.d.ts","sourceRoot":"","sources":["../../src/analysis/element-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAiB,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAO5E;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAEhB,gCAAgC;IAChC,KAAK,EAAE,KAAK,CAAC;IAEb,oCAAoC;IACpC,IAAI,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,eAAe,CAAC;IAE/D,4BAA4B;IAC5B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAE9B,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,wBAAwB,EAAE,CAsD3F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,wBAAwB,EAAE,CAQjG"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element Attribute Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates attributes on Coherent.js elements against the allowed
|
|
5
|
+
* attributes for each HTML element type.
|
|
6
|
+
*/
|
|
7
|
+
import { isValidAttribute, isVoidElement, getSuggestions, } from '../data/element-attributes.js';
|
|
8
|
+
/**
|
|
9
|
+
* Validate attributes on a Coherent element.
|
|
10
|
+
*
|
|
11
|
+
* Checks for:
|
|
12
|
+
* - Invalid attribute names for the element
|
|
13
|
+
* - Typos in attribute names (with suggestions)
|
|
14
|
+
* - Children on void elements
|
|
15
|
+
*
|
|
16
|
+
* @param element - The Coherent element to validate
|
|
17
|
+
* @returns Array of validation errors
|
|
18
|
+
*/
|
|
19
|
+
export function validateAttributes(element) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const tagName = element.tagName;
|
|
22
|
+
for (const attr of element.attributes) {
|
|
23
|
+
// Skip Coherent-specific properties that we handle separately
|
|
24
|
+
if (attr.name === 'children' || attr.name === 'text' || attr.name === 'html' || attr.name === 'key') {
|
|
25
|
+
// Check for children on void elements
|
|
26
|
+
if (attr.name === 'children' && isVoidElement(tagName)) {
|
|
27
|
+
errors.push({
|
|
28
|
+
message: `<${tagName}> is a void element and cannot have children`,
|
|
29
|
+
range: attr.range,
|
|
30
|
+
code: 'void-children',
|
|
31
|
+
severity: 'error',
|
|
32
|
+
data: { tagName, attributeName: attr.name },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
// Check if attribute is valid for this element
|
|
38
|
+
if (!isValidAttribute(tagName, attr.name)) {
|
|
39
|
+
// Check for typo suggestions
|
|
40
|
+
const suggestions = getSuggestions(tagName, attr.name);
|
|
41
|
+
if (suggestions.length > 0) {
|
|
42
|
+
errors.push({
|
|
43
|
+
message: `Unknown attribute '${attr.name}' on <${tagName}>. Did you mean '${suggestions[0]}'?`,
|
|
44
|
+
range: attr.nameRange,
|
|
45
|
+
code: 'typo-attribute',
|
|
46
|
+
severity: 'error',
|
|
47
|
+
suggestion: suggestions[0],
|
|
48
|
+
data: {
|
|
49
|
+
tagName,
|
|
50
|
+
attributeName: attr.name,
|
|
51
|
+
suggestions,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
errors.push({
|
|
57
|
+
message: `Attribute '${attr.name}' is not valid for <${tagName}>`,
|
|
58
|
+
range: attr.nameRange,
|
|
59
|
+
code: 'invalid-attribute',
|
|
60
|
+
severity: 'error',
|
|
61
|
+
data: {
|
|
62
|
+
tagName,
|
|
63
|
+
attributeName: attr.name,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return errors;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate multiple elements at once.
|
|
73
|
+
*
|
|
74
|
+
* @param elements - Array of Coherent elements to validate
|
|
75
|
+
* @returns Array of all validation errors
|
|
76
|
+
*/
|
|
77
|
+
export function validateAllAttributes(elements) {
|
|
78
|
+
const errors = [];
|
|
79
|
+
for (const element of elements) {
|
|
80
|
+
errors.push(...validateAttributes(element));
|
|
81
|
+
}
|
|
82
|
+
return errors;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=element-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-validator.js","sourceRoot":"","sources":["../../src/analysis/element-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,+BAA+B,CAAC;AAyBvC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA4B;IAC7D,MAAM,MAAM,GAA+B,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACtC,8DAA8D;QAC9D,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACpG,sCAAsC;YACtC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvD,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,OAAO,8CAA8C;oBAClE,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,IAAI,EAAE,eAAe;oBACrB,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE;iBAC5C,CAAC,CAAC;YACL,CAAC;YACD,SAAS;QACX,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,6BAA6B;YAC7B,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAEvD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,sBAAsB,IAAI,CAAC,IAAI,SAAS,OAAO,oBAAoB,WAAW,CAAC,CAAC,CAAC,IAAI;oBAC9F,KAAK,EAAE,IAAI,CAAC,SAAS;oBACrB,IAAI,EAAE,gBAAgB;oBACtB,QAAQ,EAAE,OAAO;oBACjB,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;oBAC1B,IAAI,EAAE;wBACJ,OAAO;wBACP,aAAa,EAAE,IAAI,CAAC,IAAI;wBACxB,WAAW;qBACZ;iBACF,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,cAAc,IAAI,CAAC,IAAI,uBAAuB,OAAO,GAAG;oBACjE,KAAK,EAAE,IAAI,CAAC,SAAS;oBACrB,IAAI,EAAE,mBAAmB;oBACzB,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE;wBACJ,OAAO;wBACP,aAAa,EAAE,IAAI,CAAC,IAAI;qBACzB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA+B;IACnE,MAAM,MAAM,GAA+B,EAAE,CAAC;IAE9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML Nesting Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates parent/child relationships of Coherent.js elements
|
|
5
|
+
* against HTML5 content model rules.
|
|
6
|
+
*/
|
|
7
|
+
import { Range } from 'vscode-languageserver';
|
|
8
|
+
import { CoherentElementInfo } from './coherent-analyzer.js';
|
|
9
|
+
/**
|
|
10
|
+
* Nesting validation error.
|
|
11
|
+
*/
|
|
12
|
+
export interface NestingValidationError {
|
|
13
|
+
/** Error message */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Source range of the error */
|
|
16
|
+
range: Range;
|
|
17
|
+
/** Error code for categorization */
|
|
18
|
+
code: 'invalid-nesting' | 'invalid-parent' | 'block-in-inline' | 'invalid-child';
|
|
19
|
+
/** Severity of the error */
|
|
20
|
+
severity: 'error' | 'warning';
|
|
21
|
+
/** The child element tag name */
|
|
22
|
+
childTag: string;
|
|
23
|
+
/** The parent element tag name */
|
|
24
|
+
parentTag: string;
|
|
25
|
+
/** Additional data for code actions */
|
|
26
|
+
data?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate nesting of an element within its parent.
|
|
30
|
+
*
|
|
31
|
+
* @param element - The Coherent element to validate
|
|
32
|
+
* @returns Array of nesting validation errors
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateElementNesting(element: CoherentElementInfo): NestingValidationError[];
|
|
35
|
+
/**
|
|
36
|
+
* Validate nesting for multiple elements.
|
|
37
|
+
*
|
|
38
|
+
* @param elements - Array of Coherent elements to validate
|
|
39
|
+
* @returns Array of all nesting validation errors
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateAllNesting(elements: CoherentElementInfo[]): NestingValidationError[];
|
|
42
|
+
/**
|
|
43
|
+
* Get suggestions for fixing nesting errors.
|
|
44
|
+
*
|
|
45
|
+
* @param error - The nesting validation error
|
|
46
|
+
* @returns Suggested fix description or null
|
|
47
|
+
*/
|
|
48
|
+
export declare function getNestingFixSuggestion(error: NestingValidationError): string | null;
|
|
49
|
+
//# sourceMappingURL=nesting-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nesting-validator.d.ts","sourceRoot":"","sources":["../../src/analysis/nesting-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG7D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAEhB,gCAAgC;IAChC,KAAK,EAAE,KAAK,CAAC;IAEb,oCAAoC;IACpC,IAAI,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,eAAe,CAAC;IAEjF,4BAA4B;IAC5B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAE9B,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IAEjB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAElB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,sBAAsB,EAAE,CAuB7F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,sBAAsB,EAAE,CAQ5F;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiBpF"}
|