Fix unimplemented login route with # prefix

Fix IDE service routing and path normalization
Refactor IDE services and add session rotation

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-22 15:59:42 +00:00
parent fe2ef1b35b
commit e678b987c2
39 changed files with 2028 additions and 522 deletions

View File

@@ -69,6 +69,19 @@ class JqhtmlDefinitionProvider {
console.log(`JQHTML: In $ attribute context:`, dollarAttrResult);
return yield this.handleDollarAttributeDefinition(document, position, dollarAttrResult);
}
// IMPORTANT: Check for slot syntax BEFORE extracting word
// This prevents slot names from being treated as component names
// Check if we're in a slot tag by looking for <# or </# before cursor
const beforeCursor = line.substring(0, position.character);
if (beforeCursor.match(/<\/?#\s*[A-Z][A-Za-z0-9_]*$/)) {
// We're in a slot tag - extract the full slot name from the line
const slotNameMatch = line.match(/<\/?#\s*([A-Z][A-Za-z0-9_]*)/);
if (slotNameMatch) {
const slotName = slotNameMatch[1];
console.log(`JQHTML: Detected slot tag syntax for slot: ${slotName}`);
return yield this.handleSlotDefinition(document, position, slotName);
}
}
// Get the word at the cursor position
const wordRange = document.getWordRangeAtPosition(position, /[A-Z][A-Za-z0-9_]*/);
if (!wordRange) {
@@ -119,11 +132,6 @@ class JqhtmlDefinitionProvider {
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);
@@ -483,6 +491,171 @@ class JqhtmlDefinitionProvider {
}
return undefined;
}
/**
* Handle goto definition for slot tags (<#SlotName>)
*
* IMPLEMENTATION SCOPE (Narrow, for now):
* - Handles direct extends="ComponentName" on <Define:> tags
* - Handles direct <ComponentName> invocation tags
* - Does NOT traverse full inheritance chain (TODO: add later)
* - Just looks for direct parent component
*
* LOGIC:
* 1. Extract slot name from cursor position
* 2. Find parent component:
* - If inside <Define extends="Parent">, use Parent
* - If inside <Parent> invocation, use Parent
* 3. Find Parent.jqhtml file
* 4. Search for <%= content('SlotName') %>
* 5. Navigate to that line
*/
handleSlotDefinition(document, position, slotName) {
return __awaiter(this, void 0, void 0, function* () {
console.log(`JQHTML: Handling slot definition for: ${slotName}`);
// Find the parent component that defines this slot
const parentComponentName = this.findParentComponentForSlot(document, position);
if (!parentComponentName) {
console.log(`JQHTML: Could not determine parent component for slot`);
return undefined;
}
console.log(`JQHTML: Parent component for slot: ${parentComponentName}`);
// Debug: Show what's in the index
const allComponents = this.componentIndex.getAllComponentNames();
console.log(`JQHTML: Index currently contains ${allComponents.length} components:`, allComponents.join(', '));
// Find the parent component definition file
const parentComponent = this.componentIndex.findComponent(parentComponentName);
if (!parentComponent) {
console.log(`JQHTML: Parent component '${parentComponentName}' not found in index`);
return undefined;
}
console.log(`JQHTML: Found parent component file: ${parentComponent.uri.fsPath}`);
// Search for content('SlotName') in the parent component file
const slotUsageLocation = yield this.findSlotUsageInTemplate(parentComponent.uri, slotName);
if (!slotUsageLocation) {
console.log(`JQHTML: Slot usage content('${slotName}') not found in ${parentComponent.uri.fsPath}`);
return undefined;
}
console.log(`JQHTML: Found slot usage at line ${slotUsageLocation.range.start.line + 1}`);
return slotUsageLocation;
});
}
/**
* Find the parent component that should define this slot
*
* Looks for either:
* 1. <Define:ChildComponent extends="ParentComponent"> - check if slots are top-level
* 2. <ParentComponent> - find enclosing component invocation tag
*/
findParentComponentForSlot(document, position) {
const currentLine = position.line;
// Strategy 1: Look for <Define extends="ParentComponent"> where slots are at top level
// Scan upward to find the Define tag
let defineTagStartLine = -1;
for (let i = currentLine; i >= 0; i--) {
const lineText = document.lineAt(i).text;
// Check if we found a <Define:ComponentName
if (lineText.match(/<Define:([A-Z][A-Za-z0-9_]*)/)) {
defineTagStartLine = i;
console.log(`JQHTML: Found <Define: tag at line ${i + 1}`);
break;
}
}
// If we found a Define tag, look for extends attribute in the tag (may be multi-line)
if (defineTagStartLine >= 0) {
// Collect all lines from Define tag start until we find the closing >
let tagContent = '';
for (let i = defineTagStartLine; i < document.lineCount; i++) {
const lineText = document.lineAt(i).text;
tagContent += lineText + ' ';
// Stop when we find the closing > of the opening tag
if (lineText.includes('>')) {
break;
}
}
// Now check if this multi-line tag has extends attribute
const extendsMatch = tagContent.match(/\bextends\s*=\s*["']([A-Z][A-Za-z0-9_]*)["']/);
if (extendsMatch) {
const parentComponentName = extendsMatch[1];
console.log(`JQHTML: Found extends="${parentComponentName}" in Define tag`);
// TODO: Verify that the slot is at top level (not nested inside other tags)
// For now, we assume if we found a Define with extends, that's the parent
return parentComponentName;
}
else {
console.log(`JQHTML: Define tag found but no extends attribute`);
}
}
// Strategy 2: Look for enclosing <ParentComponent> invocation tag
// Scan upward to find opening tag
let tagStack = [];
for (let i = currentLine; i >= 0; i--) {
const lineText = document.lineAt(i).text;
// Find all component tags on this line (both opening and closing)
// Component tags: <ComponentName> or </ComponentName>
const tagRegex = /<\/?([A-Z][A-Za-z0-9_]*)[^>]*>/g;
let match;
// Collect all tags on this line
const tagsOnLine = [];
while ((match = tagRegex.exec(lineText)) !== null) {
const fullMatch = match[0];
const componentName = match[1];
const isClosing = fullMatch.startsWith('</');
tagsOnLine.push({ tag: componentName, isClosing });
}
// Process tags in reverse order (right to left on the line)
for (let j = tagsOnLine.length - 1; j >= 0; j--) {
const { tag, isClosing } = tagsOnLine[j];
if (isClosing) {
// Closing tag - add to stack
tagStack.push(tag);
}
else {
// Opening tag
if (tagStack.length > 0 && tagStack[tagStack.length - 1] === tag) {
// This opening tag matches the last closing tag on stack - they cancel out
tagStack.pop();
}
else {
// This is an unclosed opening tag - this is our parent!
console.log(`JQHTML: Found enclosing component invocation: <${tag}>`);
return tag;
}
}
}
}
console.log(`JQHTML: No parent component found for slot`);
return undefined;
}
/**
* Search for <%= content('SlotName') %> in a template file
*/
findSlotUsageInTemplate(templateUri, slotName) {
return __awaiter(this, void 0, void 0, function* () {
try {
const templateDoc = yield vscode.workspace.openTextDocument(templateUri);
const templateText = templateDoc.getText();
// Search for content('SlotName') or content("SlotName")
// Also handle optional whitespace
const contentRegex = new RegExp(`<%=\\s*content\\s*\\(\\s*['"]${slotName}['"]\\s*\\)`, 'g');
const match = contentRegex.exec(templateText);
if (match) {
const matchPosition = templateDoc.positionAt(match.index);
console.log(`JQHTML: Found content('${slotName}') at line ${matchPosition.line + 1}`);
// Return location pointing to the slot name within content('SlotName')
const slotNameStartIndex = match.index + match[0].indexOf(slotName);
const slotNamePosition = templateDoc.positionAt(slotNameStartIndex);
const slotNameRange = new vscode.Range(slotNamePosition, new vscode.Position(slotNamePosition.line, slotNamePosition.character + slotName.length));
return new vscode.Location(templateUri, slotNameRange);
}
console.log(`JQHTML: No content('${slotName}') found in template`);
return undefined;
}
catch (error) {
console.error(`JQHTML: Error reading template file:`, error);
return undefined;
}
});
}
}
exports.JqhtmlDefinitionProvider = JqhtmlDefinitionProvider;
/**