Skip to content

Commit b53ba41

Browse files
more cleaning
1 parent d279162 commit b53ba41

File tree

3 files changed

+134
-32
lines changed

3 files changed

+134
-32
lines changed

src/core/__tests__/incompleteTagState.test.ts

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,30 @@ describe('updateIncompleteTagState', () => {
160160
expect(state.tagCounts.component).toBe(0);
161161
});
162162

163-
it('should not track {{ that is not component', () => {
163+
it('should track ANY {{ as component when not in code', () => {
164164
const state = updateIncompleteTagState(INITIAL_INCOMPLETE_STATE, '{{ test');
165165

166-
expect(state.stack).toHaveLength(0);
166+
expect(state.stack).toHaveLength(1);
167+
expect(state.stack[0].type).toBe('component');
168+
expect(state.tagCounts.component).toBe(1);
169+
});
170+
171+
it('should NOT track {{ when inside inline code', () => {
172+
let state = updateIncompleteTagState(INITIAL_INCOMPLETE_STATE, '`{{ json');
173+
174+
// Should have code tag but NO component tag
175+
expect(state.tagCounts.code).toBe(1);
167176
expect(state.tagCounts.component).toBe(0);
177+
expect(state.inInlineCode).toBe(true);
178+
});
179+
180+
it('should NOT track {{ when inside code block', () => {
181+
let state = updateIncompleteTagState(INITIAL_INCOMPLETE_STATE, '```\n{{ json');
182+
183+
// Should have codeBlock tag but NO component tag
184+
expect(state.tagCounts.codeBlock).toBe(1);
185+
expect(state.tagCounts.component).toBe(0);
186+
expect(state.inCodeBlock).toBe(true);
168187
});
169188
});
170189

@@ -301,18 +320,38 @@ describe('updateIncompleteTagState', () => {
301320
expect(state.tagCounts.component).toBe(0);
302321
});
303322

304-
it('should track incomplete tag counts', () => {
323+
it('should track incomplete tag counts with code context', () => {
324+
// Use clearer test case: regular tags, then code block with content that looks like tags
305325
const state = updateIncompleteTagState(
306326
INITIAL_INCOMPLETE_STATE,
307-
'**bold *italic `code ```block [link {{component:'
327+
'**bold** text ```\ncode {{ and [link\n```'
308328
);
309329

310-
expect(state.tagCounts.bold).toBe(1);
311-
expect(state.tagCounts.italic).toBe(1);
312-
expect(state.tagCounts.code).toBe(1);
313-
expect(state.tagCounts.codeBlock).toBe(1);
314-
expect(state.tagCounts.link).toBe(1);
315-
expect(state.tagCounts.component).toBe(1);
330+
expect(state.tagCounts.bold).toBe(0); // Closed
331+
expect(state.tagCounts.codeBlock).toBe(0); // Closed
332+
expect(state.tagCounts.component).toBe(0); // Was inside code block, not tracked
333+
expect(state.tagCounts.link).toBe(0); // Was inside code block, not tracked
334+
expect(state.inCodeBlock).toBe(false); // Exited after closing
335+
});
336+
337+
it('should track tags outside code, ignore inside code', () => {
338+
const state = updateIncompleteTagState(
339+
INITIAL_INCOMPLETE_STATE,
340+
'{{comp `{{ in code` {{outside'
341+
);
342+
343+
expect(state.tagCounts.component).toBe(2); // First and last, middle was in code
344+
expect(state.tagCounts.code).toBe(0); // Closed
345+
});
346+
347+
it('should track component outside code correctly', () => {
348+
const state = updateIncompleteTagState(
349+
INITIAL_INCOMPLETE_STATE,
350+
'**bold** and {{component text'
351+
);
352+
353+
expect(state.tagCounts.bold).toBe(0); // Closed
354+
expect(state.tagCounts.component).toBe(1); // Open, not in code
316355
});
317356
});
318357
});

src/core/parseIncomplete.ts

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,66 @@ function hideIncompleteCodeBlockMarkers(text: string): string {
127127
* Returns the text with incomplete components removed and the buffered component text
128128
*/
129129
function hideIncompleteComponents(text: string): { cleanText: string; bufferedComponent: string } {
130-
// Find the last occurrence of {{component:
131-
const lastComponentStart = text.lastIndexOf('{{component:');
130+
// Find the last occurrence of {{ (any component start, not just {{component:)
131+
const lastComponentStart = text.lastIndexOf('{{');
132+
133+
// Check if the last {{ is complete
134+
let lastComponentIsComplete = false;
135+
136+
if (lastComponentStart !== -1) {
137+
const textAfterComponentStart = text.substring(lastComponentStart);
138+
139+
// Quick check: does it have closing }}?
140+
let braceCount = 0;
141+
let inString = false;
142+
let escapeNext = false;
143+
144+
for (let i = 0; i < textAfterComponentStart.length; i++) {
145+
const char = textAfterComponentStart[i];
146+
const nextChar = textAfterComponentStart[i + 1];
147+
148+
if (escapeNext) {
149+
escapeNext = false;
150+
continue;
151+
}
152+
153+
if (char === '\\') {
154+
escapeNext = true;
155+
continue;
156+
}
157+
158+
if (char === '"') {
159+
inString = !inString;
160+
continue;
161+
}
162+
163+
if (inString) continue;
164+
165+
if (char === '{') {
166+
braceCount++;
167+
} else if (char === '}') {
168+
braceCount--;
169+
170+
if (braceCount === 1 && nextChar === '}') {
171+
lastComponentIsComplete = true;
172+
break;
173+
}
174+
}
175+
}
176+
}
177+
178+
// Hide single trailing { if no unclosed component
179+
if (text.endsWith('{') && !text.endsWith('{{') && (lastComponentStart === -1 || lastComponentIsComplete)) {
180+
console.log('🚫 Hiding single trailing {');
181+
return { cleanText: text.slice(0, -1), bufferedComponent: '{' };
182+
}
132183

133184
if (lastComponentStart === -1) {
134185
// No component syntax found
135186
return { cleanText: text, bufferedComponent: '' };
136187
}
137188

138-
// Check if there's a complete component (closing }}) after the last opening
189+
// Re-check completion (we already did this above, but keep for clarity)
139190
const textAfterComponentStart = text.substring(lastComponentStart);
140191

141192
// Count braces to find matching closing }}
@@ -181,14 +232,16 @@ function hideIncompleteComponents(text: string): { cleanText: string; bufferedCo
181232

182233
if (foundClosing) {
183234
// Component is complete, don't hide anything
235+
console.log('✅ Complete component found, not hiding');
184236
return { cleanText: text, bufferedComponent: '' };
185237
}
186238

187-
// Component is incomplete, hide everything from {{component: onwards
239+
// Component is incomplete, hide everything from {{ onwards
188240
const cleanText = text.substring(0, lastComponentStart);
189241
const bufferedComponent = text.substring(lastComponentStart);
190242

191243
console.log('🔒 Buffering incomplete component:', {
244+
cleanText: cleanText.substring(Math.max(0, cleanText.length - 50)),
192245
bufferLength: bufferedComponent.length,
193246
bufferPreview: bufferedComponent.substring(0, 100)
194247
});
@@ -660,6 +713,8 @@ export function updateIncompleteTagState(
660713
// Start with current state
661714
let stack = [...state.stack];
662715
let tagCounts = { ...state.tagCounts };
716+
let inCodeBlock = state.inCodeBlock;
717+
let inInlineCode = state.inInlineCode;
663718

664719
// Process each new character
665720
for (let i = 0; i < newChars.length; i++) {
@@ -681,6 +736,7 @@ export function updateIncompleteTagState(
681736
// Close the most recent code block
682737
stack.splice(codeBlockIndex, 1);
683738
tagCounts.codeBlock--;
739+
inCodeBlock = false; // Exit code block context
684740
} else {
685741
// Open new code block
686742
stack.push({
@@ -690,6 +746,7 @@ export function updateIncompleteTagState(
690746
openingText: newText.slice(Math.max(0, position - 10), position + 13),
691747
});
692748
tagCounts.codeBlock++;
749+
inCodeBlock = true; // Enter code block context
693750
}
694751
i += 2; // Skip next 2 characters
695752
continue;
@@ -764,6 +821,7 @@ export function updateIncompleteTagState(
764821
// Close the most recent code
765822
stack.splice(codeIndex, 1);
766823
tagCounts.code--;
824+
inInlineCode = false; // Exit inline code context
767825
} else {
768826
// Open new code
769827
stack.push({
@@ -773,6 +831,7 @@ export function updateIncompleteTagState(
773831
openingText: newText.slice(Math.max(0, position - 10), position + 11),
774832
});
775833
tagCounts.code++;
834+
inInlineCode = true; // Enter inline code context
776835
}
777836
continue;
778837
}
@@ -812,8 +871,8 @@ export function updateIncompleteTagState(
812871
continue;
813872
}
814873

815-
// Links: [
816-
if (char === '[') {
874+
// Links: [ (but only if NOT in code context)
875+
if (char === '[' && !inCodeBlock && !inInlineCode) {
817876
// Open new link
818877
stack.push({
819878
type: 'link',
@@ -843,22 +902,18 @@ export function updateIncompleteTagState(
843902
continue;
844903
}
845904

846-
// Components: {{
847-
if (char === '{' && nextChar === '{') {
848-
// Check if next chars are 'component:'
849-
const ahead = newText.slice(position, position + 12);
850-
if (ahead.startsWith('{{component:')) {
851-
// Open new component
852-
stack.push({
853-
type: 'component',
854-
position,
855-
marker: '{{',
856-
openingText: newText.slice(Math.max(0, position - 10), position + 22),
857-
});
858-
tagCounts.component++;
859-
i += 1; // Skip next character
860-
continue;
861-
}
905+
// Components: {{ (but only if NOT in code context)
906+
if (char === '{' && nextChar === '{' && !inCodeBlock && !inInlineCode) {
907+
// Track ANY {{ as potential component when not in code
908+
stack.push({
909+
type: 'component',
910+
position,
911+
marker: '{{',
912+
openingText: newText.slice(Math.max(0, position - 10), position + 22),
913+
});
914+
tagCounts.component++;
915+
i += 1; // Skip next character
916+
continue;
862917
}
863918

864919
// Components: closing }}
@@ -885,6 +940,8 @@ export function updateIncompleteTagState(
885940
earliestPosition,
886941
previousTextLength: newText.length,
887942
tagCounts,
943+
inCodeBlock,
944+
inInlineCode,
888945
};
889946
}
890947

src/core/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ export interface IncompleteTagState {
129129
link: number;
130130
component: number;
131131
};
132+
133+
// Code context tracking - are we inside code?
134+
inCodeBlock: boolean;
135+
inInlineCode: boolean;
132136
}
133137

134138
/**
@@ -146,6 +150,8 @@ export const INITIAL_INCOMPLETE_STATE: IncompleteTagState = {
146150
link: 0,
147151
component: 0,
148152
},
153+
inCodeBlock: false,
154+
inInlineCode: false,
149155
};
150156

151157
/**

0 commit comments

Comments
 (0)