coherent-language-support 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscodeignore +8 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/coherent-language-support-1.0.0.vsix +0 -0
- package/dist/extension.js +18075 -0
- package/dist/extension.js.map +7 -0
- package/esbuild.config.mjs +24 -0
- package/icon.png +0 -0
- package/icon.svg +10 -0
- package/package.json +78 -0
- package/server/analysis/coherent-analyzer.d.ts +93 -0
- package/server/analysis/coherent-analyzer.d.ts.map +1 -0
- package/server/analysis/coherent-analyzer.js +288 -0
- package/server/analysis/coherent-analyzer.js.map +1 -0
- package/server/analysis/element-validator.d.ts +45 -0
- package/server/analysis/element-validator.d.ts.map +1 -0
- package/server/analysis/element-validator.js +84 -0
- package/server/analysis/element-validator.js.map +1 -0
- package/server/analysis/nesting-validator.d.ts +49 -0
- package/server/analysis/nesting-validator.d.ts.map +1 -0
- package/server/analysis/nesting-validator.js +68 -0
- package/server/analysis/nesting-validator.js.map +1 -0
- package/server/data/element-attributes.d.ts +92 -0
- package/server/data/element-attributes.d.ts.map +1 -0
- package/server/data/element-attributes.generated.json +7085 -0
- package/server/data/element-attributes.js +282 -0
- package/server/data/element-attributes.js.map +1 -0
- package/server/data/nesting-rules.d.ts +67 -0
- package/server/data/nesting-rules.d.ts.map +1 -0
- package/server/data/nesting-rules.js +240 -0
- package/server/data/nesting-rules.js.map +1 -0
- package/server/providers/code-actions.d.ts +15 -0
- package/server/providers/code-actions.d.ts.map +1 -0
- package/server/providers/code-actions.js +191 -0
- package/server/providers/code-actions.js.map +1 -0
- package/server/providers/completion.d.ts +15 -0
- package/server/providers/completion.d.ts.map +1 -0
- package/server/providers/completion.js +247 -0
- package/server/providers/completion.js.map +1 -0
- package/server/providers/diagnostics.d.ts +26 -0
- package/server/providers/diagnostics.d.ts.map +1 -0
- package/server/providers/diagnostics.js +143 -0
- package/server/providers/diagnostics.js.map +1 -0
- package/server/providers/hover.d.ts +15 -0
- package/server/providers/hover.d.ts.map +1 -0
- package/server/providers/hover.js +215 -0
- package/server/providers/hover.js.map +1 -0
- package/server/server.d.ts +17 -0
- package/server/server.d.ts.map +1 -0
- package/server/server.js +82 -0
- package/server/server.js.map +1 -0
- package/snippets/coherent.json +226 -0
- package/src/extension.ts +75 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
import { cp, mkdir } from 'fs/promises';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
// Bundle extension
|
|
9
|
+
await esbuild.build({
|
|
10
|
+
entryPoints: ['src/extension.ts'],
|
|
11
|
+
bundle: true,
|
|
12
|
+
outfile: 'dist/extension.js',
|
|
13
|
+
external: ['vscode'],
|
|
14
|
+
platform: 'node',
|
|
15
|
+
format: 'cjs', // VS Code requires CommonJS for extension main
|
|
16
|
+
sourcemap: true,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Copy language server from sibling package
|
|
20
|
+
await mkdir(join(__dirname, 'server'), { recursive: true });
|
|
21
|
+
const serverSource = join(__dirname, '../language-server/dist');
|
|
22
|
+
await cp(serverSource, join(__dirname, 'server'), { recursive: true });
|
|
23
|
+
|
|
24
|
+
console.log('Build complete: extension bundled, server copied to server/');
|
package/icon.png
ADDED
|
Binary file
|
package/icon.svg
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" style="stop-color:#7c3aed"/>
|
|
5
|
+
<stop offset="100%" style="stop-color:#2563eb"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="128" height="128" rx="24" fill="url(#bg)"/>
|
|
9
|
+
<text x="64" y="88" font-family="Arial, sans-serif" font-size="72" font-weight="bold" fill="white" text-anchor="middle">C</text>
|
|
10
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coherent-language-support",
|
|
3
|
+
"displayName": "Coherent.js Language Support",
|
|
4
|
+
"description": "IntelliSense, validation, and snippets for Coherent.js",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"publisher": "coherentjs",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Tomdrouv1/coherent.js"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"vscode": "^1.85.0"
|
|
13
|
+
},
|
|
14
|
+
"categories": [
|
|
15
|
+
"Programming Languages",
|
|
16
|
+
"Linters",
|
|
17
|
+
"Snippets"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"coherent",
|
|
21
|
+
"ssr",
|
|
22
|
+
"server-side-rendering",
|
|
23
|
+
"html",
|
|
24
|
+
"components"
|
|
25
|
+
],
|
|
26
|
+
"icon": "icon.png",
|
|
27
|
+
"activationEvents": [],
|
|
28
|
+
"main": "./dist/extension.js",
|
|
29
|
+
"contributes": {
|
|
30
|
+
"snippets": [
|
|
31
|
+
{
|
|
32
|
+
"language": "javascript",
|
|
33
|
+
"path": "./snippets/coherent.json"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"language": "typescript",
|
|
37
|
+
"path": "./snippets/coherent.json"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"language": "javascriptreact",
|
|
41
|
+
"path": "./snippets/coherent.json"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"language": "typescriptreact",
|
|
45
|
+
"path": "./snippets/coherent.json"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"configuration": {
|
|
49
|
+
"title": "Coherent.js",
|
|
50
|
+
"properties": {
|
|
51
|
+
"coherent.trace.server": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": [
|
|
54
|
+
"off",
|
|
55
|
+
"messages",
|
|
56
|
+
"verbose"
|
|
57
|
+
],
|
|
58
|
+
"default": "off",
|
|
59
|
+
"description": "Traces communication between VS Code and the language server"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"vscode-languageclient": "^9.0.1"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/vscode": "^1.85.0",
|
|
69
|
+
"@vscode/vsce": "^3.3.2",
|
|
70
|
+
"esbuild": "^0.25.0",
|
|
71
|
+
"sharp": "0.34.5",
|
|
72
|
+
"typescript": "^5.9.3"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "node esbuild.config.mjs",
|
|
76
|
+
"package": "vsce package --no-dependencies"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -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"}
|