Files
rspade_system/node_modules/@jqhtml/vscode-extension/out/definitionProvider.js
root f6fac6c4bc 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>
2025-10-21 02:08:33 +00:00

590 lines
32 KiB
JavaScript
Executable File

"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