Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

1
node_modules/@jqhtml/vscode-extension/.version generated vendored Executable file
View File

@@ -0,0 +1 @@
2.2.137

51
node_modules/@jqhtml/vscode-extension/CHANGELOG.md generated vendored Executable file
View File

@@ -0,0 +1,51 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2.2.13 (2025-09-21)
**Note:** Version bump only for package @jqhtml/vscode-extension
## 2.1.10 (2025-09-18)
**Note:** Version bump only for package @jqhtml/vscode-extension
## 2.1.9 (2025-09-18)
**Note:** Version bump only for package @jqhtml/vscode-extension
# Change Log
All notable changes to the JQHTML VS Code extension will be documented in this file.
## [2.0.0] - 2024-12-30
### Initial Release
- Full syntax highlighting for JQHTML v2 templates
- Support for both colon and brace control flow styles
- Component definition highlighting with `<Define:ComponentName>`
- Slot syntax highlighting with `<#slotname>` and let:prop support
- Data binding syntax with `:property`
- Event handler syntax with `@event`
- Special attribute syntax with `$attribute`
- Template expression highlighting `<%= expression %>`
- Comment support with `<%-- comment --%>`
- Auto-closing pairs for tags and brackets
- Code folding for component definitions
- Smart indentation for control structures
- 16 code snippets for common patterns
- Language configuration for optimal editing experience

21
node_modules/@jqhtml/vscode-extension/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 JQHTML Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

144
node_modules/@jqhtml/vscode-extension/LLM_REFERENCE.md generated vendored Executable file
View File

@@ -0,0 +1,144 @@
# JQHTML VS Code Extension - LLM Reference
## Overview
The JQHTML VS Code extension provides syntax highlighting, snippets, and language support for `.jqhtml` template files. It enables developers to work efficiently with JQHTML templates in Visual Studio Code.
## Features
### Syntax Highlighting
- Full syntax highlighting for JQHTML template syntax
- JavaScript expression highlighting within `<% %>` blocks
- HTML structure highlighting
- Component definition and invocation highlighting
- Special attribute (`$`, `@`) recognition
### Language Configuration
- Auto-closing pairs for JQHTML tags
- Bracket matching for template blocks
- Comment toggling support (HTML-style comments)
- Indentation rules for nested templates
### Code Snippets
#### Component Definition
- `jqhtml:component` - Creates a new component definition
- `jqhtml:define` - Define block with component name
- `jqhtml:slot` - Slot definition snippet
#### Control Flow
- `jqhtml:for` - For loop with colon syntax
- `jqhtml:if` - If statement with colon syntax
- `jqhtml:each` - Each iteration helper
#### Data Binding
- `jqhtml:bind` - Data binding expression `<%= %>`
- `jqhtml:raw` - Raw output expression `<%! %>`
- `jqhtml:stmt` - Statement block `<% %>`
### File Association
- Automatically recognizes `.jqhtml` files
- Sets appropriate language mode for syntax highlighting
- Configures editor settings optimized for template editing
## Grammar Scopes
### Top-Level Scopes
- `source.jqhtml` - Root scope for JQHTML files
- `meta.component.jqhtml` - Component definition blocks
- `meta.tag.jqhtml` - Component invocation tags
### Expression Scopes
- `meta.expression.escaped.jqhtml` - `<%= %>` expressions
- `meta.expression.raw.jqhtml` - `<%! %>` expressions
- `meta.statement.jqhtml` - `<% %>` statement blocks
### Attribute Scopes
- `meta.attribute.dollar.jqhtml` - `$` prefixed attributes
- `meta.attribute.event.jqhtml` - `@` prefixed event handlers
- `meta.attribute.regular.jqhtml` - Standard HTML attributes
## Extension Configuration
### Recommended Settings
```json
{
"[jqhtml]": {
"editor.wordWrap": "on",
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
},
"editor.autoClosingBrackets": "always",
"editor.autoClosingQuotes": "always"
}
}
```
### File Associations
```json
{
"files.associations": {
"*.jqhtml": "jqhtml"
}
}
```
## Integration with JQHTML Toolchain
### Parser Integration
The extension works seamlessly with:
- `@jqhtml/parser` - For template compilation
- `@jqhtml/webpack-loader` - For build-time processing
- `@jqhtml/core` - For runtime component handling
### Development Workflow
1. Create `.jqhtml` template files with full syntax support
2. Use snippets for rapid component development
3. Leverage syntax highlighting for error prevention
4. Build with webpack-loader for production
## Language Server Protocol (Future)
Future versions may include:
- Semantic token support
- Go-to-definition for components
- Auto-completion for component names
- Template validation and diagnostics
- Hover information for JQHTML syntax
## Known Patterns
### Component Registration
```jqhtml
<Define:MyComponent as="div">
<!-- component template -->
</Define:MyComponent>
```
### Component Usage
```jqhtml
<MyComponent $id="instance" @click="handleClick">
<#slot>Content here</#slot>
</MyComponent>
```
### Control Flow
```jqhtml
<% for (let item of this.data.items): %>
<div $id="item_<%= item.id %>">
<%= item.name %>
</div>
<% endfor; %>
```
## VS Code API Usage
- Contributes grammar through TextMate format
- Registers language configuration
- Provides snippet completion
- Sets editor defaults for `.jqhtml` files
## Extension Publishing
- Published as `@jqhtml/vscode-extension` npm package
- Includes `.version` file for version tracking
- Compatible with VS Code 1.74.0 and later
- MIT licensed

240
node_modules/@jqhtml/vscode-extension/README.md generated vendored Executable file
View File

@@ -0,0 +1,240 @@
# JQHTML VS Code Extension
Syntax highlighting and language support for JQHTML template files.
## Features
### Syntax Highlighting
Full syntax highlighting for all JQHTML constructs:
- **Component Definitions**: `<Define:ComponentName>`
- **Template Expressions**: `<%= expression %>`
- **Control Flow**: Both colon and brace styles
- `<% if (condition): %> ... <% endif; %>`
- `<% if (condition) { %> ... <% } %>`
- **Slots**: `<#slotname>` with let:prop support
- **Data Bindings**: `:property="value"`
- **Event Handlers**: `@click="handler"`
- **Special Attributes**: `$id="name"`, `$property="value"`
- **Components**: `<MyComponent />`
- **Comments**: `<%-- comment --%>`
### Language Configuration
- **Auto-closing pairs**: Automatically close tags, brackets, and quotes
- **Bracket matching**: Highlight matching brackets and tags
- **Code folding**: Fold component definitions
- **Smart indentation**: Handles both colon and brace control flow styles
- **Comment toggling**: Use standard VS Code shortcuts to toggle comments
### Code Snippets
Quick snippets for common patterns:
| Prefix | Description |
|--------|-------------|
| `define` | Component definition |
| `definecomp` | Component with structure |
| `if:` | If statement (colon style) |
| `ifelse:` | If-else (colon style) |
| `if{` | If statement (brace style) |
| `for:` | For loop (colon style) |
| `for{` | For loop (brace style) |
| `exp` | Expression `<%= %>` |
| `$id` | Scoped ID attribute |
| `:prop` | Property binding |
| `@event` | Event handler |
| `slot` | Named slot |
| `slotprop` | Slot with props |
| `comment` | Comment block |
## Installation
### From Marketplace (when published)
1. Open VS Code
2. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
3. Search for "JQHTML"
4. Click Install
### From Source (Development)
1. Clone the JQHTML repository
2. Navigate to `packages/vscode-extension`
3. Run `npm install`
4. Run `npm run compile`
5. Copy the folder to VS Code extensions directory:
- Windows: `%USERPROFILE%\.vscode\extensions`
- macOS/Linux: `~/.vscode/extensions`
6. Restart VS Code
### Using .vsix Package
1. Package the extension: `vsce package`
2. In VS Code: Extensions → ... → Install from VSIX
3. Select the generated .vsix file
## Usage
The extension automatically activates for `.jqhtml` files. Features include:
### Syntax Highlighting
All JQHTML syntax is highlighted with semantic colors:
```jqhtml
<Define:UserCard>
<div class="user-card" $id="card">
<h2><%= this.data.name %></h2>
<% if (this.data.isAdmin): %>
<span class="admin">Admin</span>
<% endif; %>
<button @click="handleClick">Click Me</button>
<% for (const skill of this.data.skills) { %>
<div class="skill"><%= skill %></div>
<% } %>
</div>
</Define:UserCard>
```
### IntelliSense
Basic HTML tag and attribute completion is provided through VS Code's built-in HTML support.
### Code Folding
Component definitions can be folded at the `<Define:>` level:
```
▼ <Define:MyComponent>
...
</Define:MyComponent>
```
### Formatting Support
The extension respects VS Code's formatting settings and supports both colon and brace control flow styles without enforcing either.
## Configuration
The extension sets these defaults for JQHTML files:
```json
{
"[jqhtml]": {
"editor.wordWrap": "on",
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
}
}
}
```
You can override these in your VS Code settings.
## Theme Support
The extension uses standard TextMate scopes and works with all VS Code themes. For best results, use a theme with good HTML/JavaScript support.
### Scope Reference
- `entity.name.class.component.jqhtml` - Component names
- `entity.name.tag.slot.jqhtml` - Slot names (header, row, footer, etc.)
- `keyword.control.slot.jqhtml` - Slot prefix `#` symbol
- `keyword.control.flow.jqhtml` - Control flow keywords (if, for, etc.)
- `punctuation.definition.attribute.special.jqhtml` - `$` prefix
- `punctuation.definition.attribute.binding.jqhtml` - `:` prefix
- `punctuation.definition.attribute.event.jqhtml` - `@` prefix
## Known Issues
### Bracket Matching Errors with Split Control Flow
When using brace-style control flow split across multiple `<% %>` blocks, VS Code may show bracket matching errors:
```jqhtml
<% if (condition) { %>
<div>Content</div>
<% } else if (otherCondition) { %> ⚠️ VS Code shows bracket error here
<div>Other content</div>
<% } %>
```
**Why this happens:** VS Code's bracket matcher can't track bracket state across separate template blocks. It sees a closing `}` without a matching opening `{` in the same block.
**Solutions:**
1. **Use colon syntax** (recommended for complex control flow):
```jqhtml
<% if (condition): %>
<div>Content</div>
<% else: %>
<div>Other content</div>
<% endif; %>
```
2. **Disable bracket colorization** (already set by default):
```json
{
"[jqhtml]": {
"editor.bracketPairColorization.enabled": false,
"editor.guides.bracketPairs": false
}
}
```
3. **Ignore the visual errors** - they don't affect functionality, just appearance.
The extension automatically disables bracket colorization for `.jqhtml` files to minimize these false positives.
### Other Known Issues
- Complex nested template expressions may not highlight perfectly
- Some edge cases in mixed HTML/JavaScript contexts
## Contributing
Contributions are welcome! The extension source is in the JQHTML repository under `packages/vscode-extension`.
### Development
1. Clone the repository
2. Open in VS Code
3. Run `npm install`
4. Press F5 to launch a new VS Code window with the extension
5. Open a `.jqhtml` file to test
### Testing
Create test files in `test-files/` to verify syntax highlighting:
```bash
# Run automated tests
npm test
# Manual testing
# 1. Launch extension (F5)
# 2. Open test files
# 3. Verify highlighting
```
## License
MIT License - See LICENSE file in the JQHTML repository
## Changelog
### 2.0.0
- Initial release
- Full JQHTML v2 syntax support
- Colon and brace control flow styles
- Slot syntax with let:prop
- Data binding and event handlers
- Component highlighting
- Code snippets

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" fill="none"/>
<path d="M2 2v12h12V2H2zm11 11H3V3h10v10z" fill="#e8ab53"/>
<text x="8" y="11" font-family="monospace" font-size="8" font-weight="bold" text-anchor="middle" fill="#e8ab53">%</text>
</svg>

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

View File

@@ -0,0 +1,38 @@
{
"comments": {
"blockComment": ["<%--", "--%>"]
},
"brackets": [
["<", ">"],
["<%", "%>"],
["<#", "</#"]
],
"autoClosingPairs": [
{ "open": "<", "close": ">" },
{ "open": "<%", "close": "%>" },
{ "open": "<#", "close": "</#" },
{ "open": "<Define:", "close": "</Define:" },
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "'", "close": "'", "notIn": ["string"] },
{ "open": "`", "close": "`", "notIn": ["string"] },
{ "open": "/**", "close": " */", "notIn": ["string"] }
],
"surroundingPairs": [
["<", ">"],
["<%", "%>"],
["\"", "\""],
["'", "'"],
["`", "`"]
],
"folding": {
"markers": {
"start": "^\\s*<Define:\\w+>",
"end": "^\\s*</Define:\\w+>"
}
},
"wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)",
"indentationRules": {
"increaseIndentPattern": "^\\s*(<(?!\\/)(?!.*\\/[>\\s]).*>|<%(\\s)*(if|for|while|foreach)\\s*\\(.*\\)\\s*(:)?|\\{(?!.*\\}.*)|\\[(?!.*\\].*))\\s*$",
"decreaseIndentPattern": "^\\s*(</.*>|<%\\s*(endif|endfor|endwhile|endforeach)\\s*;?\\s*%>|\\}|\\])"
}
}

218
node_modules/@jqhtml/vscode-extension/out/componentIndex.js generated vendored Executable file
View File

@@ -0,0 +1,218 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JqhtmlComponentIndex = void 0;
const vscode = __importStar(require("vscode"));
/**
* JQHTML Component Indexer
*
* Maintains an index of all JQHTML component definitions in the workspace
* for fast lookup during goto definition operations.
*/
class JqhtmlComponentIndex {
constructor() {
this.componentMap = new Map();
// Start initial indexing
this.reindexWorkspace();
// Watch for changes to .jqhtml files
this.setupFileWatcher();
}
/**
* Set up file system watcher for .jqhtml files
*/
setupFileWatcher() {
this.fileWatcher = vscode.workspace.createFileSystemWatcher('**/*.jqhtml');
// Re-index when files are created, changed, or deleted
this.fileWatcher.onDidCreate(uri => this.indexFile(uri));
this.fileWatcher.onDidChange(uri => this.indexFile(uri));
this.fileWatcher.onDidDelete(uri => this.removeFileFromIndex(uri));
}
/**
* Re-index all .jqhtml files in the workspace
*/
reindexWorkspace() {
return __awaiter(this, void 0, void 0, function* () {
// Avoid multiple concurrent reindexing
if (this.indexPromise) {
return this.indexPromise;
}
this.indexPromise = this._reindexWorkspace();
yield this.indexPromise;
this.indexPromise = undefined;
});
}
_reindexWorkspace() {
return __awaiter(this, void 0, void 0, function* () {
console.log('JQHTML: Starting workspace component indexing...');
this.componentMap.clear();
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders) {
console.log('JQHTML: No workspace folders found');
return;
}
// Search each workspace folder explicitly for multi-root workspace support
const allFiles = [];
for (const folder of workspaceFolders) {
const files = yield vscode.workspace.findFiles(new vscode.RelativePattern(folder, '**/*.jqhtml'), new vscode.RelativePattern(folder, '**/node_modules/**'));
allFiles.push(...files);
}
// Index each file
const promises = allFiles.map(uri => this.indexFile(uri));
yield Promise.all(promises);
console.log(`JQHTML: Indexed ${this.componentMap.size} components from ${allFiles.length} files`);
});
}
/**
* Index a single .jqhtml file
*/
indexFile(uri) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Remove old entries from this file
this.removeFileFromIndex(uri);
// Read file content
const document = yield vscode.workspace.openTextDocument(uri);
const text = document.getText();
const lines = text.split('\n');
// Look for component definitions
// Pattern: <Define:ComponentName (followed by non-alphanumeric or end of tag)
//
// DIAGNOSTIC HISTORY:
// - Issue: Component "Contacts_Datagrid" not found in index
// - This regex SHOULD match: <Define:Contacts_Datagrid...
// - Component name pattern: [A-Z][A-Za-z0-9_]* (starts uppercase, then alphanum+underscore)
// - Contacts_Datagrid matches this pattern
//
// POSSIBLE REASONS FOR MISSED COMPONENTS:
// 1. File not in workspace folders (check workspaceFolders in console)
// 2. File in node_modules (explicitly excluded line 75)
// 3. Syntax variations:
// - Extra whitespace: <Define: Contacts_Datagrid> (space after colon) ❌
// - Wrong case: <define:Contacts_Datagrid> (lowercase 'define') ❌
// - Missing colon: <DefineContacts_Datagrid> ❌
// 4. Indexing hasn't completed yet (async operation)
// 5. File watcher didn't fire (check file modification timestamp)
//
// DEBUGGING STEPS:
// 1. Check console output "JQHTML: Indexed X components from Y files"
// 2. Check console log when file is saved (should trigger onDidChange)
// 3. Manually reload VS Code window to force reindex
// 4. Check if file path contains "node_modules"
const definePattern = /<Define:([A-Z][A-Za-z0-9_]*)(?:[^\w]|>|$)/g;
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
const line = lines[lineNum];
let match;
// Reset regex for each line
definePattern.lastIndex = 0;
while ((match = definePattern.exec(line)) !== null) {
const componentName = match[1];
const charPos = match.index + '<Define:'.length;
// Store component definition
this.componentMap.set(componentName, {
name: componentName,
uri: uri,
position: new vscode.Position(lineNum, charPos),
line: line.trim()
});
// Debug: Log each component as it's indexed (commented out - too verbose)
// console.log(`JQHTML Index: Indexed "${componentName}" from ${path.basename(uri.fsPath)}:${lineNum + 1}`);
}
}
}
catch (error) {
console.error(`JQHTML: Error indexing file ${uri.fsPath}:`, error);
}
});
}
/**
* Remove all components from a file from the index
*/
removeFileFromIndex(uri) {
// Remove all components defined in this file
const toRemove = [];
this.componentMap.forEach((def, name) => {
if (def.uri.toString() === uri.toString()) {
toRemove.push(name);
}
});
// Verbose logging commented out to reduce console noise
// if (toRemove.length > 0) {
// console.log(`JQHTML Index: Removing ${toRemove.length} component(s) from deleted file: ${uri.fsPath}`);
// console.log(`JQHTML Index: Components removed: ${toRemove.join(', ')}`);
// }
toRemove.forEach(name => {
this.componentMap.delete(name);
});
// console.log(`JQHTML Index: Current index size after removal: ${this.componentMap.size} components`);
}
/**
* Find a component definition by name
*/
findComponent(name) {
return this.componentMap.get(name);
}
/**
* Get all component names (for autocomplete)
*/
getAllComponentNames() {
return Array.from(this.componentMap.keys());
}
/**
* Check if a string is a component reference (starts with capital letter)
*/
static isComponentReference(tagName) {
return /^[A-Z]/.test(tagName);
}
/**
* Dispose of resources
*/
dispose() {
if (this.fileWatcher) {
this.fileWatcher.dispose();
}
this.componentMap.clear();
}
}
exports.JqhtmlComponentIndex = JqhtmlComponentIndex;
//# sourceMappingURL=componentIndex.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"componentIndex.js","sourceRoot":"","sources":["../src/componentIndex.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,590 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JqhtmlHoverProvider = exports.JqhtmlDefinitionProvider = void 0;
const vscode = __importStar(require("vscode"));
const componentIndex_1 = require("./componentIndex");
/**
* JQHTML Definition Provider
*
* Provides "Go to Definition" functionality for JQHTML components.
* When a user Ctrl/Cmd+clicks on a component name or uses F12,
* this provider will locate the component definition.
*/
class JqhtmlDefinitionProvider {
constructor(componentIndex) {
this.componentIndex = componentIndex;
}
provideDefinition(document, position, token) {
return __awaiter(this, void 0, void 0, function* () {
console.log(`\n========== JQHTML: provideDefinition called ==========`);
console.log(`File: ${document.uri.fsPath}`);
console.log(`Position: Line ${position.line + 1}, Character ${position.character}`);
const line = document.lineAt(position.line).text;
console.log(`JQHTML: Line text: "${line}"`);
// Check if we're in a $ attribute with unquoted value
const dollarAttrResult = this.checkDollarAttributeContext(document, position, line);
if (dollarAttrResult) {
console.log(`JQHTML: In $ attribute context:`, dollarAttrResult);
return yield this.handleDollarAttributeDefinition(document, position, dollarAttrResult);
}
// Get the word at the cursor position
const wordRange = document.getWordRangeAtPosition(position, /[A-Z][A-Za-z0-9_]*/);
if (!wordRange) {
console.log(`JQHTML: No word range found at position`);
return undefined;
}
const word = document.getText(wordRange);
console.log(`JQHTML: Word at cursor: "${word}"`);
// Check if this looks like a component reference
if (!componentIndex_1.JqhtmlComponentIndex.isComponentReference(word)) {
console.log(`JQHTML: "${word}" is not a component reference (doesn't start with capital)`);
return undefined;
}
// line already declared at top of function
const charBefore = wordRange.start.character > 0 ?
line.charAt(wordRange.start.character - 1) : '';
// Check if this word is inside an extends="" attribute value
let beforeWord = line.substring(0, wordRange.start.character);
if (beforeWord.match(/extends\s*=\s*["']?\s*$/)) {
console.log(`JQHTML: "${word}" is in extends attribute, treating as component reference`);
// This is in extends="ComponentName", treat as component reference
const componentDef = this.componentIndex.findComponent(word);
if (!componentDef) {
console.log(`JQHTML: Component '${word}' not found in index`);
return undefined;
}
// Verify the file still exists
try {
yield vscode.workspace.fs.stat(componentDef.uri);
}
catch (error) {
console.log(`JQHTML: Component '${word}' definition file no longer exists`);
this.componentIndex.reindexWorkspace();
return undefined;
}
console.log(`JQHTML: Found definition for '${word}' at ${componentDef.uri.fsPath}:${componentDef.position.line + 1}`);
return new vscode.Location(componentDef.uri, componentDef.position);
}
// Check if this word is in a tag context
// Look for < before the component name (accounting for Define: prefix)
let isInTagContext = false;
// Check for opening tag: <ComponentName or <Define:ComponentName
beforeWord = line.substring(0, wordRange.start.character);
if (beforeWord.match(/<\s*$/) || beforeWord.match(/<Define:\s*$/)) {
isInTagContext = true;
}
// Check for closing tag: </ComponentName or </Define:ComponentName
if (beforeWord.match(/<\/\s*$/) || beforeWord.match(/<\/Define:\s*$/)) {
isInTagContext = true;
}
// Check for slot syntax: <#slotname> or </#slotname>
if (beforeWord.match(/<#\s*$/) || beforeWord.match(/<\/#\s*$/)) {
// This is a slot, not a component
return undefined;
}
if (!isInTagContext) {
// Also check if cursor is inside the tag name (not in attributes)
const afterWord = line.substring(wordRange.end.character);
// If there's a space or > after the word, and < before it somewhere
if ((afterWord.match(/^[\s>]/) || afterWord.length === 0) && beforeWord.includes('<')) {
// Verify we're not in an attribute value
const lastLessThan = beforeWord.lastIndexOf('<');
const lastGreaterThan = beforeWord.lastIndexOf('>');
if (lastLessThan > lastGreaterThan) {
isInTagContext = true;
}
}
}
if (!isInTagContext) {
console.log(`JQHTML: "${word}" not in tag context, ignoring`);
return undefined;
}
console.log(`JQHTML: "${word}" IS in tag context, looking up in index...`);
console.log(`JQHTML: Current index size: ${this.componentIndex.getAllComponentNames().length} components`);
console.log(`JQHTML: Index contains: ${this.componentIndex.getAllComponentNames().join(', ')}`);
// Look up the component in our index
const componentDef = this.componentIndex.findComponent(word);
if (!componentDef) {
// Component not found in index
//
// DIAGNOSTIC HISTORY:
// - Issue: Go to Definition goes to random CSS file instead of component
// - Symptom: Component correctly identified (e.g., "Contacts_Datagrid")
// Tag context correctly detected
// BUT findComponent() returns undefined
// - Root cause: Component not indexed by the indexing system
//
// POSSIBLE REASONS:
// 1. Component file not in workspace or not discovered during indexing
// 2. Component definition syntax not matching regex in componentIndex.ts
// 3. File watcher didn't detect the file creation/modification
// 4. Index hasn't run yet (extension just activated)
//
// WHEN THIS RETURNS UNDEFINED:
// VS Code falls back to built-in text search providers, which may find
// the component name in CSS class names (.Contacts_Datagrid), causing
// navigation to wrong files.
//
// NEXT DIAGNOSTIC STEPS:
// 1. Check if component file exists and is in workspace
// 2. Verify Define tag syntax matches indexing regex
// 3. Check file watcher is working (create new component, see if indexed)
// 4. Manually trigger reindex and check if component appears
console.log(`JQHTML: Component '${word}' not found in index`);
console.log(`JQHTML: RETURNING UNDEFINED - VS Code may fall back to other definition providers!`);
return undefined;
}
// Verify the file still exists (catches stale index entries)
try {
yield vscode.workspace.fs.stat(componentDef.uri);
}
catch (error) {
// File no longer exists - trigger reindex and return undefined
console.log(`JQHTML: Component '${word}' definition file no longer exists: ${componentDef.uri.fsPath}`);
console.log(`JQHTML: Triggering workspace reindex...`);
this.componentIndex.reindexWorkspace(); // Async, non-blocking
return undefined;
}
console.log(`JQHTML: Found definition for '${word}' at ${componentDef.uri.fsPath}:${componentDef.position.line + 1}`);
console.log(`JQHTML: RETURNING LOCATION - This should be the ONLY result!`);
console.log(`========== JQHTML: provideDefinition done ==========\n`);
// Return the location of the component definition
return new vscode.Location(componentDef.uri, componentDef.position);
});
}
/**
* Check if cursor is in a $ attribute with unquoted value like $handler=Controller.method
* Returns the parsed segments and position info, or undefined if not in such context
*/
checkDollarAttributeContext(document, position, line) {
var _a;
const char = position.character;
const beforeCursor = line.substring(0, char);
const afterCursor = line.substring(char);
// Look for pattern: $attributeName=FirstSegment.secondSegment
// Match: $ followed by word, =, then identifier chains
const dollarAttrMatch = beforeCursor.match(/\$\w+\s*=\s*([a-zA-Z_$][a-zA-Z0-9_$]*(?:\.[a-zA-Z_$][a-zA-Z0-9_$]*)*)$/);
if (!dollarAttrMatch) {
return undefined;
}
// Get the full expression (before cursor + until space or >)
const expressionBeforeCursor = dollarAttrMatch[1];
const expressionAfterCursor = ((_a = afterCursor.match(/^([a-zA-Z0-9_$]*)/)) === null || _a === void 0 ? void 0 : _a[1]) || '';
const fullExpression = expressionBeforeCursor + expressionAfterCursor;
console.log(`JQHTML: Full $ attribute expression: "${fullExpression}"`);
// Split by dots
const segments = fullExpression.split('.');
// Determine which segment we're on based on cursor position
const expressionStartChar = char - expressionBeforeCursor.length;
const cursorOffsetInExpression = char - expressionStartChar;
let currentSegmentIndex = 0;
let charCount = 0;
for (let i = 0; i < segments.length; i++) {
const segmentLength = segments[i].length;
if (cursorOffsetInExpression <= charCount + segmentLength) {
currentSegmentIndex = i;
break;
}
charCount += segmentLength + 1; // +1 for the dot
}
console.log(`JQHTML: Cursor on segment ${currentSegmentIndex}: "${segments[currentSegmentIndex]}"`);
// Handle "this" keyword - resolve to containing Define component
let className = segments[0];
if (className === 'this') {
const containingComponent = this.findContainingDefineComponent(document, position.line);
if (!containingComponent) {
console.log(`JQHTML: "this" used but no containing Define component found`);
return undefined;
}
className = containingComponent;
console.log(`JQHTML: Resolved "this" to component: ${className}`);
}
if (currentSegmentIndex === 0) {
// First segment - just the class name
return { className, isFirstSegment: true };
}
else {
// Second or later segment - class + member
return {
className,
memberName: segments[currentSegmentIndex],
isFirstSegment: false
};
}
}
/**
* Find the containing <Define:ComponentName> for a given line
*/
findContainingDefineComponent(document, currentLine) {
// Search backwards from current line to find <Define:ComponentName>
for (let i = currentLine; i >= 0; i--) {
const lineText = document.lineAt(i).text;
const defineMatch = lineText.match(/<Define:([A-Z][A-Za-z0-9_]*)/);
if (defineMatch) {
return defineMatch[1];
}
}
return undefined;
}
/**
* Handle goto definition for $ attribute values
*
* SEARCH PRIORITY MATRIX:
*
* Single Segment (e.g., $handler=Controller):
* 1. PHP class
* 2. JS class
* 3. Standalone JS function (only if first segment is not "this")
*
* Multiple Segments - First Segment (e.g., $handler=Controller.method, click on "Controller"):
* 1. PHP class
* 2. JS class (if no PHP class found, or if first segment is "this")
* 3. Standalone JS function (only if not "this")
*
* Multiple Segments - Second+ Segment (e.g., $handler=Controller.method, click on "method"):
* 1. PHP method in PHP class → Fall back to PHP class if method not found
* 2. JS method in JS class → Fall back to JS class if method not found (if no PHP class found, or if "this")
* (No standalone function search for second+ segments)
*
* Special Case: "this" keyword
* - Resolves to containing <Define:ComponentName>
* - Only searches JS (PHP search skipped)
*/
handleDollarAttributeDefinition(document, position, context) {
return __awaiter(this, void 0, void 0, function* () {
console.log(`JQHTML: Looking for class "${context.className}"${context.memberName ? `, member "${context.memberName}"` : ''}`);
const isThisKeyword = context.className === 'this';
// Priority 1: Search PHP classes/methods (skip if "this" keyword)
if (!isThisKeyword) {
const phpResult = yield this.searchPhpDefinition(context);
if (phpResult) {
return phpResult;
}
}
// Priority 2: Search JS classes/methods
const jsClassResult = yield this.searchJsClassDefinition(context);
if (jsClassResult) {
return jsClassResult;
}
// Priority 3: Search standalone JS functions (only for single segment, not "this")
if (context.isFirstSegment && !context.memberName && !isThisKeyword) {
const jsFunctionResult = yield this.searchStandaloneJsFunction(context.className);
if (jsFunctionResult) {
return jsFunctionResult;
}
}
console.log(`JQHTML: No definition found for "${context.className}"${context.memberName ? `.${context.memberName}` : ''}`);
return undefined;
});
}
/**
* Search for PHP class and optionally method
*
* IMPLEMENTATION: Uses VS Code's workspace symbol provider API to query Intelephense's symbol index.
*
* This approach:
* - Leverages Intelephense's existing indexed symbol database
* - No temporary documents or window management issues
* - Fast symbol lookup via vscode.executeWorkspaceSymbolProvider
* - Gracefully falls back if Intelephense not installed
* - No manual indexing or file scanning required
*
* We query workspace symbols by name (class or method), then filter results
* to find PHP symbols and navigate to their definitions.
*/
searchPhpDefinition(context) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Check if Intelephense is installed
const intelephenseExt = vscode.extensions.getExtension('bmewburn.vscode-intelephense-client');
if (!intelephenseExt) {
console.log(`JQHTML: Intelephense extension not installed, skipping PHP lookup`);
return undefined;
}
// Search for the class first
console.log(`JQHTML: Searching workspace symbols for PHP class: ${context.className}`);
const classSymbols = yield vscode.commands.executeCommand('vscode.executeWorkspaceSymbolProvider', context.className);
if (!classSymbols || classSymbols.length === 0) {
console.log(`JQHTML: No workspace symbols found for class: ${context.className}`);
return undefined;
}
// Filter to PHP class symbols
const phpClassSymbol = classSymbols.find(s => s.name === context.className &&
s.kind === vscode.SymbolKind.Class &&
s.location.uri.fsPath.endsWith('.php'));
if (!phpClassSymbol) {
console.log(`JQHTML: No PHP class symbol found for: ${context.className}`);
return undefined;
}
console.log(`JQHTML: Found PHP class ${context.className} in ${phpClassSymbol.location.uri.fsPath}`);
// If we're looking for the class itself (first segment, no member)
if (context.isFirstSegment && !context.memberName) {
return phpClassSymbol.location;
}
// If we're looking for a method within the class
if (context.memberName) {
console.log(`JQHTML: Searching for method: ${context.memberName} in class ${context.className}`);
// Search for the method name in workspace symbols
const methodSymbols = yield vscode.commands.executeCommand('vscode.executeWorkspaceSymbolProvider', context.memberName);
// Filter to methods in the same PHP file as the class
const phpMethodSymbol = methodSymbols === null || methodSymbols === void 0 ? void 0 : methodSymbols.find(s => s.name === context.memberName &&
s.kind === vscode.SymbolKind.Method &&
s.location.uri.toString() === phpClassSymbol.location.uri.toString());
if (phpMethodSymbol) {
console.log(`JQHTML: Found PHP method ${context.memberName} in ${phpMethodSymbol.location.uri.fsPath}`);
return phpMethodSymbol.location;
}
// Method not found - fall back to class definition
console.log(`JQHTML: Method ${context.memberName} not found, falling back to class definition`);
return phpClassSymbol.location;
}
return phpClassSymbol.location;
}
catch (error) {
console.error(`JQHTML: Error using workspace symbol provider:`, error);
return undefined;
}
});
}
/**
* Search for JS class and optionally method
*/
searchJsClassDefinition(context) {
return __awaiter(this, void 0, void 0, function* () {
const jsFiles = yield vscode.workspace.findFiles('**/*.js', '**/node_modules/**');
for (const fileUri of jsFiles) {
const fileDoc = yield vscode.workspace.openTextDocument(fileUri);
const fileText = fileDoc.getText();
// Look for class definition: class ClassName
const classRegex = new RegExp(`^\\s*(?:export\\s+)?class\\s+${context.className}\\b`, 'm');
const classMatch = classRegex.exec(fileText);
if (classMatch) {
console.log(`JQHTML: Found JS class ${context.className} in ${fileUri.fsPath}`);
// If we're on the first segment, go to the class definition
if (context.isFirstSegment && !context.memberName) {
const classPos = fileDoc.positionAt(classMatch.index + classMatch[0].indexOf(context.className));
return new vscode.Location(fileUri, classPos);
}
// If we're on a later segment, try to find the method/property in the class
if (context.memberName) {
const memberLocation = this.findJsClassMember(fileDoc, fileText, classMatch.index, context.memberName);
if (memberLocation) {
console.log(`JQHTML: Found JS method ${context.memberName} in class ${context.className}`);
return new vscode.Location(fileUri, memberLocation);
}
// Method not found, but we found the class - fall back to class definition
console.log(`JQHTML: JS method ${context.memberName} not found, falling back to JS class definition`);
const classPos = fileDoc.positionAt(classMatch.index + classMatch[0].indexOf(context.className));
return new vscode.Location(fileUri, classPos);
}
}
}
return undefined;
});
}
/**
* Search for standalone JS function (not in a class)
* Only called for single-segment expressions where first segment is not "this"
*/
searchStandaloneJsFunction(functionName) {
return __awaiter(this, void 0, void 0, function* () {
const jsFiles = yield vscode.workspace.findFiles('**/*.js', '**/node_modules/**');
for (const fileUri of jsFiles) {
const fileDoc = yield vscode.workspace.openTextDocument(fileUri);
const fileText = fileDoc.getText();
// Look for function declaration: function functionName
// or const/let/var functionName = function
const functionRegex = new RegExp(`^\\s*(?:export\\s+)?(?:async\\s+)?function\\s+${functionName}\\b`, 'm');
const functionMatch = functionRegex.exec(fileText);
if (functionMatch) {
console.log(`JQHTML: Found standalone JS function ${functionName} in ${fileUri.fsPath}`);
const functionPos = fileDoc.positionAt(functionMatch.index + functionMatch[0].indexOf(functionName));
return new vscode.Location(fileUri, functionPos);
}
// Also check for: const functionName = ...
const constFunctionRegex = new RegExp(`^\\s*(?:export\\s+)?(?:const|let|var)\\s+${functionName}\\s*=`, 'm');
const constMatch = constFunctionRegex.exec(fileText);
if (constMatch) {
console.log(`JQHTML: Found standalone JS function ${functionName} (const/let/var) in ${fileUri.fsPath}`);
const functionPos = fileDoc.positionAt(constMatch.index + constMatch[0].indexOf(functionName));
return new vscode.Location(fileUri, functionPos);
}
}
return undefined;
});
}
/**
* Find a method or property within a JS class definition
*/
findJsClassMember(document, fileText, classStartIndex, memberName) {
// Find the class body (starts at { after class declaration)
const classBodyStart = fileText.indexOf('{', classStartIndex);
if (classBodyStart === -1)
return undefined;
// Find matching closing brace
let braceCount = 1;
let classBodyEnd = classBodyStart + 1;
while (classBodyEnd < fileText.length && braceCount > 0) {
if (fileText[classBodyEnd] === '{')
braceCount++;
if (fileText[classBodyEnd] === '}')
braceCount--;
classBodyEnd++;
}
const classBody = fileText.substring(classBodyStart, classBodyEnd);
// Look for method: methodName() { or property: methodName =
const methodRegex = new RegExp(`^\\s*(?:async\\s+)?${memberName}\\s*[=(]`, 'm');
const methodMatch = methodRegex.exec(classBody);
if (methodMatch) {
const absoluteIndex = classBodyStart + methodMatch.index + methodMatch[0].indexOf(memberName);
return document.positionAt(absoluteIndex);
}
return undefined;
}
}
exports.JqhtmlDefinitionProvider = JqhtmlDefinitionProvider;
/**
* JQHTML Hover Provider
*
* Provides hover information for JQHTML components.
* Shows the file and line where the component is defined.
*/
class JqhtmlHoverProvider {
constructor(componentIndex) {
this.componentIndex = componentIndex;
}
provideHover(document, position, token) {
return __awaiter(this, void 0, void 0, function* () {
const line = document.lineAt(position.line).text;
const char = position.character;
// Check for $redrawable attribute
const redrawableMatch = line.match(/\$redrawable(?=\s|>|\/)/);
if (redrawableMatch && line.indexOf('$redrawable') <= char && char <= line.indexOf('$redrawable') + '$redrawable'.length) {
const markdown = new vscode.MarkdownString();
markdown.appendMarkdown(`**\`$redrawable\` Attribute**\n\n`);
markdown.appendMarkdown(`Converts this tag into an anonymous component class, allowing it to be redrawn on demand.\n\n`);
markdown.appendMarkdown(`**Usage:**\n\`\`\`javascript\nthis.$id('element_id').render()\n\`\`\`\n\n`);
markdown.appendMarkdown(`Call \`render()\` on the element's scoped ID to trigger a re-render of just this element without affecting the rest of the component.`);
const wordRange = new vscode.Range(new vscode.Position(position.line, line.indexOf('$redrawable')), new vscode.Position(position.line, line.indexOf('$redrawable') + '$redrawable'.length));
return new vscode.Hover(markdown, wordRange);
}
// Check for tag="" attribute on components (Define tags or component invocations)
// Look backwards from cursor to find if we're in a tag="" attribute
const beforeCursor = line.substring(0, char);
const afterCursor = line.substring(char);
// Check if we're hovering over "tag" attribute name or its value
const tagAttrMatch = beforeCursor.match(/<(Define:[A-Z][A-Za-z0-9_]*|[A-Z][A-Za-z0-9_]*)[^>]*\btag\s*=\s*["']?(\w*)$/);
if (tagAttrMatch) {
// We're in or near a tag attribute
const markdown = new vscode.MarkdownString();
markdown.appendMarkdown(`**\`tag\` Attribute**\n\n`);
markdown.appendMarkdown(`Sets the HTML element type for this component.\n\n`);
markdown.appendMarkdown(`**Default:** \`div\`\n\n`);
markdown.appendMarkdown(`**Examples:**\n`);
markdown.appendMarkdown(`- \`tag="button"\` - Creates a \`<button>\` element\n`);
markdown.appendMarkdown(`- \`tag="span"\` - Creates a \`<span>\` element\n`);
markdown.appendMarkdown(`- \`tag="a"\` - Creates an \`<a>\` element\n\n`);
markdown.appendMarkdown(`Use this when your component should be a specific HTML element instead of the default \`<div>\`.`);
return new vscode.Hover(markdown);
}
// Original component hover logic
// Get the word at the cursor position
const wordRange = document.getWordRangeAtPosition(position, /[A-Z][A-Za-z0-9_]*/);
if (!wordRange) {
return undefined;
}
const word = document.getText(wordRange);
// Check if this looks like a component reference
if (!componentIndex_1.JqhtmlComponentIndex.isComponentReference(word)) {
return undefined;
}
// Verify this is in a tag context (same logic as definition provider)
// line already declared at top of function
const beforeWord = line.substring(0, wordRange.start.character);
let isInTagContext = false;
if (beforeWord.match(/<\s*$/) || beforeWord.match(/<Define:\s*$/) ||
beforeWord.match(/<\/\s*$/) || beforeWord.match(/<\/Define:\s*$/)) {
isInTagContext = true;
}
if (!isInTagContext) {
const afterWord = line.substring(wordRange.end.character);
if ((afterWord.match(/^[\s>]/) || afterWord.length === 0) && beforeWord.includes('<')) {
const lastLessThan = beforeWord.lastIndexOf('<');
const lastGreaterThan = beforeWord.lastIndexOf('>');
if (lastLessThan > lastGreaterThan) {
isInTagContext = true;
}
}
}
if (!isInTagContext) {
return undefined;
}
// Look up the component in our index
const componentDef = this.componentIndex.findComponent(word);
if (!componentDef) {
// Show that component is not defined
const markdown = new vscode.MarkdownString();
markdown.appendMarkdown(`**JQHTML Component:** \`${word}\`\n\n`);
markdown.appendMarkdown(`⚠️ *Component definition not found in workspace*`);
return new vscode.Hover(markdown, wordRange);
}
// Create hover content
const markdown = new vscode.MarkdownString();
markdown.appendMarkdown(`**JQHTML Component:** \`${word}\`\n\n`);
// Show file location
const relativePath = vscode.workspace.asRelativePath(componentDef.uri);
markdown.appendMarkdown(`📁 **Defined in:** \`${relativePath}:${componentDef.position.line + 1}\`\n\n`);
// Show the definition line
if (componentDef.line) {
markdown.appendCodeblock(componentDef.line, 'jqhtml');
}
// Make the file path clickable
markdown.isTrusted = true;
return new vscode.Hover(markdown, wordRange);
});
}
}
exports.JqhtmlHoverProvider = JqhtmlHoverProvider;
//# sourceMappingURL=definitionProvider.js.map

File diff suppressed because one or more lines are too long

159
node_modules/@jqhtml/vscode-extension/out/extension.js generated vendored Executable file
View File

@@ -0,0 +1,159 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.activate = activate;
exports.deactivate = deactivate;
const vscode = __importStar(require("vscode"));
const formatter_1 = require("./formatter");
const componentIndex_1 = require("./componentIndex");
const definitionProvider_1 = require("./definitionProvider");
function activate(context) {
console.log('JQHTML extension activated');
// Initialize component index
const componentIndex = new componentIndex_1.JqhtmlComponentIndex();
context.subscriptions.push({
dispose: () => componentIndex.dispose()
});
// Register the formatter
const formatter = new formatter_1.JqhtmlFormattingEditProvider();
const formatterProvider = vscode.languages.registerDocumentFormattingEditProvider('jqhtml', formatter);
context.subscriptions.push(formatterProvider);
// Register definition provider for goto definition (Ctrl+Click, F12)
const definitionProvider = new definitionProvider_1.JqhtmlDefinitionProvider(componentIndex);
const definitionProviderDisposable = vscode.languages.registerDefinitionProvider('jqhtml', definitionProvider);
context.subscriptions.push(definitionProviderDisposable);
// Register hover provider for component information
const hoverProvider = new definitionProvider_1.JqhtmlHoverProvider(componentIndex);
const hoverProviderDisposable = vscode.languages.registerHoverProvider('jqhtml', hoverProvider);
context.subscriptions.push(hoverProviderDisposable);
// Register auto-closing tag functionality
const autoCloseDisposable = vscode.workspace.onDidChangeTextDocument((event) => {
if (event.document.languageId !== 'jqhtml') {
return;
}
// Check if we should auto-close
const activeEditor = vscode.window.activeTextEditor;
if (!activeEditor || activeEditor.document !== event.document) {
return;
}
// Only process single character changes (typing)
if (event.contentChanges.length !== 1) {
return;
}
const change = event.contentChanges[0];
const text = change.text;
// Check if user typed '>'
if (text === '>') {
const position = change.range.start;
const line = event.document.lineAt(position.line);
const lineText = line.text.substring(0, position.character + 1);
// Match opening tags: <ComponentName>, <Define:Name>, or regular HTML tags
// Look for self-closing indicators /> or existing closing tags
const openingTagMatch = lineText.match(/<(\/?)(Define:|#)?([A-Z][A-Za-z0-9_]*|\w+)(?:\s+[^>]*)?>$/);
if (openingTagMatch && !openingTagMatch[1]) { // Not a closing tag (no /)
const tagPrefix = openingTagMatch[2] || ''; // 'Define:' or '#' or ''
const tagName = openingTagMatch[3];
// Check if it's self-closing or already has a closing tag
const beforeTag = lineText.substring(0, lineText.lastIndexOf('<'));
if (beforeTag.endsWith('/')) {
return; // Self-closing tag
}
// Check if this is a slot tag (starts with #)
const isSlot = tagPrefix === '#';
// For slots, check if it's self-closing syntax
if (isSlot && lineText.match(/<#\w+\s*\/?>$/)) {
// Don't auto-close self-closing slots
if (lineText.endsWith('/>')) {
return;
}
}
// Check if we should auto-close this tag
// Component tags (start with capital), Define: tags, and slot tags
const shouldAutoClose = tagName[0] === tagName[0].toUpperCase() ||
tagPrefix === 'Define:' ||
isSlot ||
isHtmlTag(tagName);
if (shouldAutoClose) {
// Build the closing tag
let closingTag = '';
if (isSlot) {
closingTag = `</#${tagName}>`;
}
else {
closingTag = `</${tagPrefix}${tagName}>`;
}
// Insert the closing tag
activeEditor.edit((editBuilder) => {
const insertPosition = position.translate(0, 1);
editBuilder.insert(insertPosition, closingTag);
}, { undoStopBefore: false, undoStopAfter: false }).then(() => {
// Move cursor between the tags
const newPosition = position.translate(0, 1);
activeEditor.selection = new vscode.Selection(newPosition, newPosition);
});
}
}
}
});
context.subscriptions.push(autoCloseDisposable);
// Register format on save if enabled
const config = vscode.workspace.getConfiguration('editor');
if (config.get('formatOnSave')) {
console.log('JQHTML: Format on save is enabled');
}
console.log('JQHTML: All features registered (formatter, auto-close, goto definition, hover)');
// Return public API for other extensions
return {
findComponent: (name) => componentIndex.findComponent(name),
getAllComponentNames: () => componentIndex.getAllComponentNames(),
reindexWorkspace: () => componentIndex.reindexWorkspace()
};
}
// Helper function to check if a tag is a standard HTML tag
function isHtmlTag(tagName) {
const htmlTags = [
'div', 'span', 'p', 'a', 'button', 'input', 'form', 'header', 'footer',
'section', 'article', 'nav', 'main', 'aside', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'ul', 'ol', 'li', 'table', 'tr', 'td', 'th', 'thead', 'tbody', 'tfoot',
'img', 'video', 'audio', 'canvas', 'svg', 'iframe', 'label', 'select', 'option',
'textarea', 'fieldset', 'legend', 'details', 'summary', 'dialog', 'template',
'blockquote', 'pre', 'code', 'em', 'strong', 'small', 'mark', 'del', 'ins', 'sub', 'sup'
];
return htmlTags.includes(tagName.toLowerCase());
}
function deactivate() {
console.log('JQHTML extension deactivated');
}
//# sourceMappingURL=extension.js.map

1
node_modules/@jqhtml/vscode-extension/out/extension.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"}

313
node_modules/@jqhtml/vscode-extension/out/formatter.js generated vendored Executable file
View File

@@ -0,0 +1,313 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.JqhtmlFormattingEditProvider = void 0;
const vscode = __importStar(require("vscode"));
class JqhtmlFormattingEditProvider {
constructor() {
this.indentSize = 2;
// IMPORTANT: DO NOT USE REGEX FOR PARSING IN THIS FORMATTER
// This formatter uses string manipulation and indexOf for reliability
// Regex should only be used in the syntax highlighter, not here
// Based on the RS3 formatter (reformat_html.php) logic
// Patterns that increase indent on the NEXT line
this.indentIncrease = [': %>', ': ?>', '{ %>', '{ ?>'];
// Patterns that decrease indent
this.indentDecrease = [
'<% end; %>',
'<% endif; %>',
'<% endfor; %>',
'<% endforeach; %>',
'<% endfunction; %>',
'<% } %>',
'<% }); %>',
'<% } else',
'<% else'
];
// Known self-closing HTML tags
this.selfClosingTags = [
'area', 'base', 'br', 'embed', 'hr', 'iframe',
'img', 'input', 'link', 'meta', 'param', 'source', 'track'
];
}
provideDocumentFormattingEdits(document, options) {
this.indentSize = options.tabSize || 2;
const formatted = this.formatDocument(document);
const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(document.getText().length));
return [vscode.TextEdit.replace(fullRange, formatted)];
}
formatDocument(document) {
const text = document.getText();
const safeCode = [];
let working = text;
let reading = text;
// Step 1: Escape HTML comments
working = '';
while (reading.indexOf('<!--') !== -1) {
const pos = reading.indexOf('<!--');
working += reading.substring(0, pos);
reading = reading.substring(pos);
const closePos = reading.indexOf('-->');
if (closePos === -1) {
// Parse error, return original
return text;
}
safeCode.push(reading.substring(0, closePos + 3));
reading = reading.substring(closePos + 3);
working += '@@__SAFE__(' + (safeCode.length - 1) + ')';
}
working += reading;
reading = working;
// Step 2: Escape multiline <% %> blocks
working = '';
while (reading.indexOf('<%') !== -1) {
const pos = reading.indexOf('<%');
working += reading.substring(0, pos);
reading = reading.substring(pos);
const closePos = reading.indexOf('%>');
if (closePos === -1) {
// Parse error, return original
return text;
}
const nlPos = reading.indexOf('\n');
if (nlPos === -1 || nlPos > closePos) {
// Not multiline, keep it
working += reading.substring(0, closePos + 2);
reading = reading.substring(closePos + 2);
continue;
}
// It's multiline, escape it
safeCode.push(reading.substring(0, closePos + 2));
reading = reading.substring(closePos + 2);
working += '@@__SAFE__(' + (safeCode.length - 1) + ')';
}
working += reading;
// Step 3: Split into lines with indent levels
const lines = [];
const splitLines = working.split('\n');
for (const line of splitLines) {
lines.push([0, line.trim()]);
}
// Step 4: Handle JS/control flow indents (if:, endif;, etc)
let jsIndent = 0;
for (let i = 0; i < lines.length; i++) {
const trimmedLine = lines[i][1];
let plus = 0;
let minus = 0;
// Check for indent increase patterns
for (const pattern of this.indentIncrease) {
if (trimmedLine.indexOf(pattern) !== -1) {
plus++;
}
}
// Check for indent decrease patterns
for (const pattern of this.indentDecrease) {
if (trimmedLine.indexOf(pattern) !== -1) {
minus++;
}
}
// Apply indent changes
if (plus > minus) {
lines[i][0] = jsIndent;
jsIndent += plus;
jsIndent -= minus;
}
else {
jsIndent += plus;
jsIndent -= minus;
lines[i][0] = jsIndent;
}
// Special handling for else statements
if (trimmedLine.startsWith('<% else') ||
trimmedLine.startsWith('<% } else')) {
lines[i][0]--;
}
}
// Step 5: Escape remaining single-line code blocks
for (let i = 0; i < lines.length; i++) {
reading = lines[i][1];
working = '';
// Escape <% %> blocks
while (reading.indexOf('<%') !== -1) {
const pos = reading.indexOf('<%');
working += reading.substring(0, pos);
reading = reading.substring(pos);
const closePos = reading.indexOf('%>');
if (closePos === -1) {
working += reading;
reading = '';
continue;
}
safeCode.push(reading.substring(0, closePos + 2));
reading = reading.substring(closePos + 2);
working += '@@__SAFE__(' + (safeCode.length - 1) + ')';
}
working += reading;
lines[i][1] = working;
}
// Step 6: Handle HTML tag indents
let htmlIndent = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i][1];
let thisIndent = 0;
// Count opening tags
thisIndent += this.countOccurrences(line, '<');
// Subtract self-closing tags
thisIndent -= this.countOccurrences(line, '/>');
// Subtract closing tags (count double)
thisIndent -= this.countOccurrences(line, '</') * 2;
// Handle known self-closing tags
for (const tag of this.selfClosingTags) {
const searchStr = '<' + tag;
if (line.indexOf(searchStr) !== -1) {
// Split by this tag and check each occurrence
const parts = line.split(searchStr);
for (let j = 1; j < parts.length; j++) {
// Check if it's not self-closed with />
if (parts[j].indexOf('/>') === -1 && parts[j].indexOf('>') !== -1) {
thisIndent--;
}
else if (parts[j].indexOf('/>') !== -1 &&
parts[j].indexOf('>') !== -1 &&
parts[j].indexOf('/>') > parts[j].indexOf('>')) {
// Has both > and />, but > comes first
thisIndent--;
}
}
}
}
// Special case for DOCTYPE
if (line.indexOf('<!DOCTYPE') !== -1) {
thisIndent--;
}
// Apply indent changes
if (thisIndent < 0) {
// Negative change - apply before this line
htmlIndent += thisIndent;
lines[i][0] += htmlIndent;
}
else {
// Positive/zero change - apply after this line
lines[i][0] += htmlIndent;
htmlIndent += thisIndent;
}
// Multiline self-closing tag (line has /> but no <)
if (line.indexOf('/>') !== -1 && line.indexOf('<') === -1) {
lines[i][0]++;
}
}
// Step 7: Build result with proper indentation
let result = '';
for (const [indent, line] of lines) {
const finalIndent = Math.max(0, indent);
if (line.length > 0) {
result += ' '.repeat(finalIndent * this.indentSize) + line + '\n';
}
else {
result += '\n';
}
}
// Step 8: Restore safe blocks
for (let attempt = 0; attempt < 10; attempt++) {
let hasChanges = false;
for (let i = 0; i < safeCode.length; i++) {
const placeholder = '@@__SAFE__(' + i + ')';
if (result.indexOf(placeholder) !== -1) {
result = result.replace(placeholder, safeCode[i]);
hasChanges = true;
}
}
if (!hasChanges || result.indexOf('@@__SAFE__') === -1) {
break;
}
}
// Step 9: Add blank lines around Define tag contents
result = this.addDefineTagSpacing(result);
return result.trim();
}
addDefineTagSpacing(text) {
const lines = text.split('\n');
const resultLines = [];
for (let i = 0; i < lines.length; i++) {
const currentLine = lines[i];
const trimmedCurrent = currentLine.trim();
// Check if this is an opening Define tag
if (trimmedCurrent.startsWith('<Define:') && trimmedCurrent.endsWith('>') && !trimmedCurrent.includes('</')) {
resultLines.push(currentLine);
// Check if next line exists and is not already blank
if (i + 1 < lines.length) {
const nextLine = lines[i + 1];
const trimmedNext = nextLine.trim();
// Only add blank line if:
// 1. Next line is not already blank
// 2. Next line is not the closing Define tag
if (trimmedNext.length > 0 && !trimmedNext.startsWith('</Define:')) {
resultLines.push('');
}
}
}
// Check if this is a closing Define tag
else if (trimmedCurrent.startsWith('</Define:') && trimmedCurrent.endsWith('>')) {
// Check if previous line exists and is not already blank
if (i > 0) {
const prevLine = lines[i - 1];
const trimmedPrev = prevLine.trim();
// Only add blank line if:
// 1. Previous line is not already blank
// 2. Previous line is not the opening Define tag
if (trimmedPrev.length > 0 && !trimmedPrev.startsWith('<Define:') && resultLines[resultLines.length - 1].trim().length > 0) {
resultLines.push('');
}
}
resultLines.push(currentLine);
}
else {
resultLines.push(currentLine);
}
}
return resultLines.join('\n');
}
countOccurrences(str, search) {
let count = 0;
let pos = 0;
while ((pos = str.indexOf(search, pos)) !== -1) {
count++;
pos += search.length;
}
return count;
}
}
exports.JqhtmlFormattingEditProvider = JqhtmlFormattingEditProvider;
//# sourceMappingURL=formatter.js.map

1
node_modules/@jqhtml/vscode-extension/out/formatter.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

107
node_modules/@jqhtml/vscode-extension/package.json generated vendored Executable file
View File

@@ -0,0 +1,107 @@
{
"name": "@jqhtml/vscode-extension",
"displayName": "JQHTML",
"description": "Syntax highlighting and language support for JQHTML template files",
"version": "2.2.137",
"publisher": "jqhtml",
"license": "MIT",
"publishConfig": {
"access": "public",
"registry": "https://privatenpm.hanson.xyz/"
},
"scripts": {
"vscode:prepublish": "echo Skipping prepublish",
"compile": "tsc -p ./ || echo 'TypeScript compilation issues, continuing...'",
"watch": "tsc -watch -p ./",
"test": "echo 'No tests configured'",
"build": "npm run compile"
},
"files": [
"out",
"syntaxes",
"snippets",
"images",
"language-configuration.json",
".version",
"README.md",
"LLM_REFERENCE.md",
"CHANGELOG.md",
"LICENSE",
"*.vsix"
],
"engines": {
"vscode": "^1.74.0"
},
"categories": [
"Programming Languages",
"Snippets"
],
"keywords": [
"jqhtml",
"jquery",
"template",
"component",
"html"
],
"repository": {
"type": "git",
"url": "git+https://github.com/jqhtml/jqhtml.git"
},
"bugs": {
"url": "https://github.com/jqhtml/jqhtml/issues"
},
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"languages": [
{
"id": "jqhtml",
"aliases": [
"JQHTML",
"jqhtml"
],
"extensions": [
".jqhtml"
],
"configuration": "./language-configuration.json",
"icon": {
"light": "./images/jqhtml-icon.svg",
"dark": "./images/jqhtml-icon.svg"
}
}
],
"grammars": [
{
"language": "jqhtml",
"scopeName": "source.jqhtml",
"path": "./syntaxes/jqhtml.tmLanguage.json"
}
],
"snippets": [
{
"language": "jqhtml",
"path": "./snippets/jqhtml.json"
}
],
"configurationDefaults": {
"[jqhtml]": {
"editor.wordWrap": "on",
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
},
"editor.bracketPairColorization.enabled": false,
"editor.guides.bracketPairs": false
}
}
},
"devDependencies": {
"@types/node": "^16.18.126",
"@types/vscode": "^1.74.0",
"typescript": "^5.8.3"
},
"author": "",
"homepage": "https://github.com/jqhtml/jqhtml#readme",
"dependencies": null
}

139
node_modules/@jqhtml/vscode-extension/snippets/jqhtml.json generated vendored Executable file
View File

@@ -0,0 +1,139 @@
{
"Component Definition": {
"prefix": "define",
"body": [
"<Define:${1:ComponentName}>",
"\t$0",
"</Define:${1:ComponentName}>"
],
"description": "Create a new JQHTML component"
},
"Component with Structure": {
"prefix": "definecomp",
"body": [
"<Define:${1:ComponentName}>",
"\t<div class=\"${2:component-class}\">",
"\t\t<h2><%= this.data.${3:title} %></h2>",
"\t\t$0",
"\t</div>",
"</Define:${1:ComponentName}>"
],
"description": "Create a component with basic structure"
},
"If Statement (Colon Style)": {
"prefix": "if:",
"body": [
"<% if (${1:condition}): %>",
"\t$0",
"<% endif; %>"
],
"description": "If statement with colon syntax"
},
"If-Else Statement (Colon Style)": {
"prefix": "ifelse:",
"body": [
"<% if (${1:condition}): %>",
"\t$2",
"<% else: %>",
"\t$0",
"<% endif; %>"
],
"description": "If-else statement with colon syntax"
},
"If Statement (Brace Style)": {
"prefix": "if{",
"body": [
"<% if (${1:condition}) { %>",
"\t$0",
"<% } %>"
],
"description": "If statement with brace syntax"
},
"For Loop (Colon Style)": {
"prefix": "for:",
"body": [
"<% for (const ${1:item} of ${2:items}): %>",
"\t$0",
"<% endfor; %>"
],
"description": "For loop with colon syntax"
},
"For Loop (Brace Style)": {
"prefix": "for{",
"body": [
"<% for (const ${1:item} of ${2:items}) { %>",
"\t$0",
"<% } %>"
],
"description": "For loop with brace syntax"
},
"Expression": {
"prefix": "exp",
"body": "<%= ${1:expression} %>",
"description": "Template expression"
},
"Scoped ID": {
"prefix": "$id",
"body": "$id=\"${1:elementId}\"",
"description": "Component-scoped ID attribute"
},
"Data Binding": {
"prefix": ":prop",
"body": ":${1:property}=\"${2:value}\"",
"description": "Property binding"
},
"Event Handler": {
"prefix": "@event",
"body": "@${1:click}=\"${2:handler}\"",
"description": "Event handler binding"
},
"Named Slot": {
"prefix": "slot",
"body": [
"<#${1:slotname}>",
"\t$0",
"</#${1:slotname}>"
],
"description": "Named slot definition"
},
"Slot with Props": {
"prefix": "slotprop",
"body": [
"<#${1:slotname} let:${2:item}>",
"\t$0",
"</#${1:slotname}>"
],
"description": "Named slot with props"
},
"Self-Closing Slot": {
"prefix": "slotself",
"body": "<#${1:slotname} />",
"description": "Self-closing slot"
},
"Comment Block": {
"prefix": "comment",
"body": [
"<%-- ",
"\t$0",
" --%>"
],
"description": "JQHTML comment block"
},
"Component Usage": {
"prefix": "comp",
"body": "<${1:ComponentName} ${2:prop}=\"${3:value}\" />",
"description": "Use a component"
},
"Component with Slots": {
"prefix": "compslot",
"body": [
"<${1:ComponentName}>",
"\t<#${2:header}>",
"\t\t$3",
"\t</#${2:header}>",
"\t$0",
"</${1:ComponentName}>"
],
"description": "Component with slot content"
}
}

View File

@@ -0,0 +1,677 @@
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "JQHTML",
"scopeName": "source.jqhtml",
"fileTypes": ["jqhtml"],
"patterns": [
{ "include": "#component-definition" },
{ "include": "#comments" },
{ "include": "#javascript-block" },
{ "include": "#expression-block" },
{ "include": "#html-tag" },
{ "include": "#text" }
],
"repository": {
"component-definition": {
"comment": "Matches <Define:ComponentName> tags with any attributes, including special ones like as='span', class='foo', $prop='value'",
"patterns": [
{
"comment": "Closing Define tag - standalone pattern for proper highlighting",
"name": "meta.tag.component-definition.close.jqhtml",
"match": "(</)(Define)(:)(\\w+)(>)",
"captures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "keyword.control.define.jqhtml" },
"3": { "name": "punctuation.separator.key-value.jqhtml" },
"4": { "name": "entity.name.class.component.jqhtml" },
"5": { "name": "punctuation.definition.tag.end.jqhtml" }
}
},
{
"comment": "Opening Define tag with attributes",
"name": "meta.tag.component-definition.jqhtml",
"begin": "(<)(Define)(:)(\\w+)",
"beginCaptures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "keyword.control.define.jqhtml" },
"3": { "name": "punctuation.separator.key-value.jqhtml" },
"4": { "name": "entity.name.class.component.jqhtml" }
},
"end": "(/?>)",
"endCaptures": {
"1": { "name": "punctuation.definition.tag.end.jqhtml" }
},
"patterns": [
{ "include": "#define-special-attributes" },
{ "include": "#tag-attributes" }
]
},
{
"comment": "Component definition body - everything between <Define:Name> and </Define:Name>",
"name": "meta.component.body.jqhtml",
"begin": "(<)(Define)(:)(\\w+)([^>]*)(>)",
"beginCaptures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "keyword.control.define.jqhtml" },
"3": { "name": "punctuation.separator.key-value.jqhtml" },
"4": { "name": "entity.name.class.component.jqhtml" },
"5": {
"patterns": [
{ "include": "#tag-attributes" }
]
},
"6": { "name": "punctuation.definition.tag.end.jqhtml" }
},
"end": "(</)(Define)(:)(\\4)(>)",
"endCaptures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "keyword.control.define.jqhtml" },
"3": { "name": "punctuation.separator.key-value.jqhtml" },
"4": { "name": "entity.name.class.component.jqhtml" },
"5": { "name": "punctuation.definition.tag.end.jqhtml" }
},
"patterns": [
{ "include": "$self" }
]
}
]
},
"comments": {
"comment": "JQHTML supports both <%-- --%> style comments and HTML <!-- --> comments",
"patterns": [
{
"comment": "JQHTML-specific comment syntax <%-- --%>",
"name": "comment.block.jqhtml",
"begin": "<%--",
"end": "--%>",
"captures": {
"0": { "name": "punctuation.definition.comment.jqhtml" }
}
},
{
"comment": "Standard HTML comments <!-- -->",
"name": "comment.block.html",
"begin": "<!--",
"end": "-->",
"captures": {
"0": { "name": "punctuation.definition.comment.html" }
}
}
]
},
"javascript-block": {
"comment": "JavaScript code blocks <% %> for control flow and arbitrary JS - uses js-fragment to handle unbalanced brackets",
"patterns": [
{
"name": "meta.embedded.block.javascript",
"begin": "<%(?!=|--)",
"beginCaptures": {
"0": { "name": "punctuation.section.embedded.begin.jqhtml" }
},
"end": "%>",
"endCaptures": {
"0": { "name": "punctuation.section.embedded.end.jqhtml" }
},
"patterns": [
{ "include": "#js-fragment" }
]
}
]
},
"js-fragment": {
"comment": "JavaScript fragments that may have unmatched braces",
"patterns": [
{
"comment": "JavaScript keywords",
"match": "\\b(if|else|for|while|do|switch|case|break|continue|return|function|var|let|const|new|typeof|instanceof|try|catch|finally|throw|async|await)\\b",
"name": "keyword.control.js"
},
{
"comment": "JavaScript constants",
"match": "\\b(true|false|null|undefined|NaN|Infinity)\\b",
"name": "constant.language.js"
},
{
"comment": "this keyword",
"match": "\\bthis\\b",
"name": "variable.language.this.js"
},
{
"comment": "Numbers",
"match": "\\b\\d+(\\.\\d+)?\\b",
"name": "constant.numeric.js"
},
{
"comment": "Double-quoted strings",
"name": "string.quoted.double.js",
"begin": "\"",
"end": "\"",
"patterns": [
{
"match": "\\\\.",
"name": "constant.character.escape.js"
}
]
},
{
"comment": "Single-quoted strings",
"name": "string.quoted.single.js",
"begin": "'",
"end": "'",
"patterns": [
{
"match": "\\\\.",
"name": "constant.character.escape.js"
}
]
},
{
"comment": "Template literals",
"name": "string.template.js",
"begin": "`",
"end": "`",
"patterns": [
{
"match": "\\\\.",
"name": "constant.character.escape.js"
}
]
},
{
"comment": "Comments",
"match": "//.*$",
"name": "comment.line.double-slash.js"
},
{
"comment": "Property access",
"match": "\\.(\\w+)",
"captures": {
"1": { "name": "variable.other.property.js" }
}
},
{
"comment": "Function calls",
"match": "(\\w+)\\s*\\(",
"captures": {
"1": { "name": "entity.name.function.js" }
}
},
{
"comment": "Operators",
"match": "(===|!==|==|!=|<=|>=|&&|\\|\\||\\+\\+|--|\\+|\\-|\\*|/|%|=|<|>|!|\\?|:)",
"name": "keyword.operator.js"
},
{
"comment": "Braces and brackets - using keyword.operator to prevent bracket matching and show in purple",
"match": "[{}\\[\\]()]",
"name": "keyword.operator.bracket.js"
},
{
"comment": "Semicolons and commas",
"match": "[;,]",
"name": "punctuation.separator.js"
}
]
},
"expression-block": {
"comment": "Expression blocks <%= %> for outputting escaped values and <%!= %> for unescaped",
"name": "meta.embedded.expression.javascript",
"begin": "<%[=!]=?",
"beginCaptures": {
"0": { "name": "punctuation.section.embedded.begin.expression.jqhtml" }
},
"end": "%>",
"endCaptures": {
"0": { "name": "punctuation.section.embedded.end.expression.jqhtml" }
},
"contentName": "source.js",
"patterns": [
{ "include": "source.js" }
]
},
"control-flow": {
"comment": "Control flow keywords that support both colon and brace syntax",
"patterns": [
{
"match": "\\b(if|else|elseif|endif|for|endfor|while|endwhile|foreach|endforeach)\\b",
"name": "keyword.control.flow.jqhtml"
},
{
"comment": "Colon at end of control flow statement (Python-style)",
"match": "(:)\\s*$",
"name": "punctuation.separator.key-value.jqhtml"
}
]
},
"html-tag": {
"patterns": [
{ "include": "#slot-tag" },
{ "include": "#component-tag" },
{ "include": "#standard-tag" }
]
},
"slot-tag": {
"comment": "Slot tags <#slotname> for defining named content areas",
"patterns": [
{
"comment": "Opening slot tag <#name> or self-closing <#name />",
"name": "meta.tag.slot.jqhtml",
"match": "(<)(#)(\\w+)\\s*([^>]*?)(/?>)",
"captures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "keyword.control.slot.jqhtml" },
"3": { "name": "entity.name.tag.slot.jqhtml" },
"4": {
"patterns": [
{ "include": "#tag-attributes" }
]
},
"5": { "name": "punctuation.definition.tag.end.jqhtml" }
}
},
{
"comment": "Closing slot tag </#name>",
"name": "meta.tag.slot.close.jqhtml",
"match": "(</)(#)(\\w+)(>)",
"captures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "keyword.control.slot.jqhtml" },
"3": { "name": "entity.name.tag.slot.jqhtml" },
"4": { "name": "punctuation.definition.tag.end.jqhtml" }
}
}
]
},
"component-tag": {
"comment": "Component invocations - tags starting with capital letter",
"patterns": [
{
"comment": "Opening component tag <ComponentName> with attributes",
"name": "meta.tag.component.jqhtml",
"begin": "(<)([A-Z]\\w*)(?=\\s|>)",
"beginCaptures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "entity.name.class.component.jqhtml" }
},
"end": "(/?>)",
"endCaptures": {
"1": { "name": "punctuation.definition.tag.end.jqhtml" }
},
"patterns": [
{ "include": "#define-special-attributes" },
{ "include": "#tag-attributes" }
]
},
{
"comment": "Closing component tag </ComponentName>",
"name": "meta.tag.component.close.jqhtml",
"match": "(</)([A-Z]\\w*)(>)",
"captures": {
"1": { "name": "punctuation.definition.tag.begin.jqhtml" },
"2": { "name": "entity.name.class.component.jqhtml" },
"3": { "name": "punctuation.definition.tag.end.jqhtml" }
}
}
]
},
"standard-tag": {
"comment": "Standard HTML tags - lowercase",
"patterns": [
{
"comment": "Opening HTML tag <div>, <span>, etc.",
"name": "meta.tag.html",
"begin": "(<)([a-z][a-z0-9\\-]*)(?=\\s|>)",
"beginCaptures": {
"1": { "name": "punctuation.definition.tag.begin.html" },
"2": { "name": "entity.name.tag.html" }
},
"end": "(/?>)",
"endCaptures": {
"1": { "name": "punctuation.definition.tag.end.html" }
},
"patterns": [
{ "include": "#tag-attributes" }
]
},
{
"comment": "Closing HTML tag </div>, </span>, etc.",
"name": "meta.tag.close.html",
"match": "(</)(\\w+)(>)",
"captures": {
"1": { "name": "punctuation.definition.tag.begin.html" },
"2": { "name": "entity.name.tag.html" },
"3": { "name": "punctuation.definition.tag.end.html" }
}
}
]
},
"define-special-attributes": {
"comment": "Special attributes that only appear on Define tags: extends and tag",
"patterns": [
{
"comment": "extends attribute with component name value - highlighted in orange and teal",
"name": "meta.attribute.extends.jqhtml",
"match": "(extends)(=)(\"([A-Z]\\w*)\"|'([A-Z]\\w*)')",
"captures": {
"1": { "name": "keyword.control.extends.jqhtml" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": { "name": "string.quoted.html" },
"4": { "name": "entity.name.class.component.jqhtml" },
"5": { "name": "entity.name.class.component.jqhtml" }
}
},
{
"comment": "tag attribute in Define or component tags - attribute name in orange",
"name": "meta.attribute.tag.jqhtml",
"match": "(tag)(=)(\"[^\"]*\"|'[^']*')",
"captures": {
"1": { "name": "keyword.control.tag.jqhtml" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": {
"patterns": [
{ "include": "#attribute-value" }
]
}
}
}
]
},
"tag-attributes": {
"comment": "All attribute types that can appear on tags",
"patterns": [
{ "include": "#special-attributes" },
{ "include": "#binding-attributes" },
{ "include": "#event-attributes" },
{ "include": "#standard-attributes" }
]
},
"special-attributes": {
"comment": "$ prefixed attributes for component data and special handling",
"patterns": [
{
"comment": "$redrawable boolean attribute (no value) - $ in purple, redrawable in purple",
"name": "meta.attribute.special.redrawable.jqhtml",
"match": "(\\$)(redrawable)(?!\\s*=)",
"captures": {
"1": { "name": "keyword.control.slot.jqhtml" },
"2": { "name": "keyword.control.tag.jqhtml" }
}
},
{
"comment": "Expression syntax: $attr=(expression) - parentheses with any JS expression",
"name": "meta.attribute.special.expression.jqhtml",
"match": "(\\$)(\\w+)(=)(\\([^)]*\\))",
"captures": {
"1": { "name": "keyword.control.slot.jqhtml" },
"2": { "name": "entity.other.attribute-name.special.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" },
"4": {
"name": "meta.embedded.expression.javascript",
"patterns": [
{ "include": "source.js" }
]
}
}
},
{
"comment": "$ attributes with unquoted JS expression - custom minimal highlighter for property chains",
"name": "meta.attribute.special.unquoted-js.jqhtml",
"begin": "(\\$)(\\w+)(=)(?=[a-zA-Z_$0-9])",
"beginCaptures": {
"1": { "name": "keyword.control.slot.jqhtml" },
"2": { "name": "entity.other.attribute-name.special.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" }
},
"end": "(?=[\\s>])",
"patterns": [
{
"comment": "Literal constants",
"match": "\\b(true|false|null|undefined)\\b",
"name": "constant.language.js"
},
{
"comment": "Number literals",
"match": "\\b\\d+(\\.\\d+)?\\b",
"name": "constant.numeric.js"
},
{
"comment": "First identifier in property chain - class/controller name (teal)",
"match": "\\G[a-zA-Z_$][a-zA-Z0-9_$]*",
"name": "entity.name.class.jqhtml"
},
{
"comment": "Property accessor: dot + property name (dot purple, property yellow like JS functions)",
"match": "(\\.)([a-zA-Z_$][a-zA-Z0-9_$]*)",
"captures": {
"1": { "name": "keyword.operator.accessor.js" },
"2": { "name": "entity.name.function.js" }
}
},
{
"comment": "Parentheses for function calls",
"match": "[()]",
"name": "keyword.operator.bracket.js"
},
{
"comment": "Comma separator in function arguments",
"match": ",",
"name": "punctuation.separator.js"
},
{
"comment": "String literals in function arguments",
"match": "\"[^\"]*\"|'[^']*'",
"name": "string.quoted.js"
}
]
},
{
"comment": "Standard $ attributes: $id='foo', $prop='value' with quoted strings",
"name": "meta.attribute.special.jqhtml",
"match": "(\\$)(id|\\w+)(=)(\"[^\"]*\"|'[^']*')",
"captures": {
"1": { "name": "keyword.control.slot.jqhtml" },
"2": { "name": "entity.other.attribute-name.special.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" },
"4": {
"patterns": [
{ "include": "#attribute-value" }
]
}
}
}
]
},
"binding-attributes": {
"comment": "Property binding with : prefix (future feature)",
"patterns": [
{
"name": "meta.attribute.binding.jqhtml",
"match": "(:)(\\w+)(=)(\"[^\"]*\"|'[^']*'|[^\\s>]+)",
"captures": {
"1": { "name": "punctuation.definition.attribute.binding.jqhtml" },
"2": { "name": "entity.other.attribute-name.binding.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" },
"4": {
"patterns": [
{ "include": "#attribute-value" }
]
}
}
}
]
},
"event-attributes": {
"comment": "Event binding with @ prefix - unquoted values are function references, quoted are strings",
"patterns": [
{
"comment": "Event attribute with unquoted method reference: @click=this.handleClick",
"name": "meta.attribute.event.unquoted.method.jqhtml",
"match": "(@)(\\w+)(=)(this\\.)(\\w+)",
"captures": {
"1": { "name": "punctuation.definition.attribute.event.jqhtml" },
"2": { "name": "entity.other.attribute-name.event.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" },
"4": { "name": "variable.language.this.jqhtml" },
"5": { "name": "entity.name.function.jqhtml" }
}
},
{
"comment": "Event attribute with unquoted function reference: @click=handleClick",
"name": "meta.attribute.event.unquoted.function.jqhtml",
"match": "(@)(\\w+)(=)([a-zA-Z_$][a-zA-Z0-9_$]*)",
"captures": {
"1": { "name": "punctuation.definition.attribute.event.jqhtml" },
"2": { "name": "entity.other.attribute-name.event.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" },
"4": { "name": "entity.name.function.jqhtml" }
}
},
{
"comment": "Event attribute with quoted string value (likely an error)",
"name": "meta.attribute.event.quoted.jqhtml",
"match": "(@)(\\w+)(=)(\"[^\"]*\"|'[^']*')",
"captures": {
"1": { "name": "punctuation.definition.attribute.event.jqhtml" },
"2": { "name": "entity.other.attribute-name.event.jqhtml" },
"3": { "name": "punctuation.separator.key-value.html" },
"4": { "name": "invalid.illegal.quoted-event-handler.jqhtml" }
}
}
]
},
"standard-attributes": {
"comment": "Regular HTML attributes like class, id, style, etc.",
"patterns": [
{
"comment": "Event handler attributes with unquoted method reference: onclick=this.handleClick",
"name": "meta.attribute.event-handler.method.html",
"match": "(on\\w+)(=)(this\\.)(\\w+)",
"captures": {
"1": { "name": "entity.other.attribute-name.event.html" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": { "name": "variable.language.this.jqhtml" },
"4": { "name": "entity.name.function.jqhtml" }
}
},
{
"comment": "Event handler attributes with unquoted function reference: onclick=handleClick",
"name": "meta.attribute.event-handler.function.html",
"match": "(on\\w+)(=)([a-zA-Z_$][a-zA-Z0-9_$]*)",
"captures": {
"1": { "name": "entity.other.attribute-name.event.html" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": { "name": "entity.name.function.jqhtml" }
}
},
{
"comment": "Attribute with unquoted JavaScript identifier or property access",
"name": "meta.attribute.unquoted-identifier.html",
"match": "(\\w[\\w\\-]*)(=)([a-zA-Z_$][a-zA-Z0-9_$.]*)",
"captures": {
"1": { "name": "entity.other.attribute-name.html" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": { "name": "variable.other.property.jqhtml" }
}
},
{
"comment": "Attribute with quoted value: name='value' or name=\"value\"",
"name": "meta.attribute.html",
"match": "(\\w[\\w\\-]*)(=)(\"[^\"]*\"|'[^']*')",
"captures": {
"1": { "name": "entity.other.attribute-name.html" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": {
"patterns": [
{ "include": "#attribute-value" }
]
}
}
},
{
"comment": "Attribute with other unquoted values (numbers, etc)",
"name": "meta.attribute.html",
"match": "(\\w[\\w\\-]*)(=)([^\\s>]+)",
"captures": {
"1": { "name": "entity.other.attribute-name.html" },
"2": { "name": "punctuation.separator.key-value.html" },
"3": { "name": "string.unquoted.html" }
}
},
{
"comment": "Boolean attributes without value: disabled, checked, etc.",
"name": "entity.other.attribute-name.html",
"match": "\\w[\\w\\-]*"
}
]
},
"attribute-value": {
"comment": "Attribute values can contain embedded expressions",
"patterns": [
{
"comment": "Double-quoted string value",
"name": "string.quoted.double.html",
"begin": "\"",
"beginCaptures": {
"0": { "name": "punctuation.definition.string.begin.html" }
},
"end": "\"",
"endCaptures": {
"0": { "name": "punctuation.definition.string.end.html" }
},
"patterns": [
{ "include": "#embedded-expression" }
]
},
{
"comment": "Single-quoted string value",
"name": "string.quoted.single.html",
"begin": "'",
"beginCaptures": {
"0": { "name": "punctuation.definition.string.begin.html" }
},
"end": "'",
"endCaptures": {
"0": { "name": "punctuation.definition.string.end.html" }
},
"patterns": [
{ "include": "#embedded-expression" }
]
},
{
"comment": "Unquoted attribute value",
"name": "string.unquoted.html",
"match": "[^\\s>]+"
}
]
},
"embedded-expression": {
"comment": "Embedded expressions within attribute values: 'text <%= expr %> more'",
"patterns": [
{
"name": "meta.embedded.inline.javascript",
"begin": "<%=",
"beginCaptures": {
"0": { "name": "punctuation.section.embedded.begin.jqhtml" }
},
"end": "%>",
"endCaptures": {
"0": { "name": "punctuation.section.embedded.end.jqhtml" }
},
"contentName": "source.js",
"patterns": [
{ "include": "source.js" }
]
}
]
},
"text": {
"comment": "Plain text content between tags",
"patterns": [
{
"name": "text.html.jqhtml",
"match": "[^<]+"
}
]
}
}
}