Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e2e9a00

Browse filesBrowse files
committed
highlight
1 parent 948ce9c commit e2e9a00
Copy full SHA for e2e9a00

1 file changed

+154-1Lines changed: 154 additions & 1 deletion

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/components/AnimatedTerminal.tsx‎

Copy file name to clipboardExpand all lines: src/components/AnimatedTerminal.tsx
+154-1Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,151 @@ export default function AnimatedTerminal({ version }: AnimatedTerminalProps) {
267267
return "text-gray-700 dark:text-gray-300";
268268
};
269269

270+
/**
271+
* Highlight Python syntax in a line of code
272+
*/
273+
const highlightPython = (text: string): JSX.Element[] => {
274+
if (!text.trim()) {
275+
return [<span key="empty">{text || "\u00A0"}</span>];
276+
}
277+
278+
const keywords = [
279+
"def",
280+
"async",
281+
"await",
282+
"import",
283+
"from",
284+
"if",
285+
"elif",
286+
"else",
287+
"return",
288+
"for",
289+
"in",
290+
"as",
291+
"and",
292+
"or",
293+
"not",
294+
"True",
295+
"False",
296+
"None",
297+
];
298+
299+
// Find comment position (comments take priority)
300+
const commentMatch = text.match(/#.*$/);
301+
const commentIndex = commentMatch ? commentMatch.index! : text.length;
302+
const codeText = text.slice(0, commentIndex);
303+
const commentText = commentMatch ? commentMatch[0] : "";
304+
305+
// Match strings (single and double quoted)
306+
const stringRegex = /(['"])(?:(?=(\\?))\2.)*?\1/g;
307+
let stringMatch;
308+
const stringMatches: Array<{ start: number; end: number; text: string }> = [];
309+
while ((stringMatch = stringRegex.exec(codeText)) !== null) {
310+
stringMatches.push({
311+
start: stringMatch.index,
312+
end: stringMatch.index + stringMatch[0].length,
313+
text: stringMatch[0],
314+
});
315+
}
316+
317+
// Match numbers
318+
const numberRegex = /\b\d+\.?\d*\b/g;
319+
let numberMatch;
320+
const numberMatches: Array<{ start: number; end: number; text: string }> = [];
321+
while ((numberMatch = numberRegex.exec(codeText)) !== null) {
322+
numberMatches.push({
323+
start: numberMatch.index,
324+
end: numberMatch.index + numberMatch[0].length,
325+
text: numberMatch[0],
326+
});
327+
}
328+
329+
// Match keywords
330+
const keywordRegex = new RegExp(`\\b(${keywords.join("|")})\\b`, "g");
331+
let keywordMatch;
332+
const keywordMatches: Array<{ start: number; end: number; text: string }> = [];
333+
while ((keywordMatch = keywordRegex.exec(codeText)) !== null) {
334+
keywordMatches.push({
335+
start: keywordMatch.index,
336+
end: keywordMatch.index + keywordMatch[0].length,
337+
text: keywordMatch[0],
338+
});
339+
}
340+
341+
// Combine all matches and sort by position
342+
const allMatches = [
343+
...stringMatches.map((m) => ({ ...m, type: "string" })),
344+
...numberMatches.map((m) => ({ ...m, type: "number" })),
345+
...keywordMatches.map((m) => ({ ...m, type: "keyword" })),
346+
].sort((a, b) => a.start - b.start);
347+
348+
// Remove overlapping matches (strings take priority, then numbers, then keywords)
349+
const filteredMatches: typeof allMatches = [];
350+
for (const match of allMatches) {
351+
const overlaps = filteredMatches.some(
352+
(existing) =>
353+
(match.start >= existing.start && match.start < existing.end) ||
354+
(match.end > existing.start && match.end <= existing.end) ||
355+
(match.start <= existing.start && match.end >= existing.end)
356+
);
357+
if (!overlaps) {
358+
filteredMatches.push(match);
359+
}
360+
}
361+
362+
// Build parts array for the code part (before comment)
363+
const resultParts: Array<{ text: string; type: string }> = [];
364+
let currentIndex = 0;
365+
filteredMatches.forEach((match) => {
366+
if (match.start > currentIndex) {
367+
resultParts.push({
368+
text: codeText.slice(currentIndex, match.start),
369+
type: "text",
370+
});
371+
}
372+
resultParts.push({ text: match.text, type: match.type });
373+
currentIndex = match.end;
374+
});
375+
if (currentIndex < codeText.length) {
376+
resultParts.push({
377+
text: codeText.slice(currentIndex),
378+
type: "text",
379+
});
380+
}
381+
382+
// If no matches were found, add the whole codeText as text
383+
if (resultParts.length === 0 && codeText.length > 0) {
384+
resultParts.push({ text: codeText, type: "text" });
385+
}
386+
387+
// Add comment if it exists
388+
if (commentText) {
389+
resultParts.push({ text: commentText, type: "comment" });
390+
}
391+
392+
if (resultParts.length === 0) {
393+
resultParts.push({ text, type: "text" });
394+
}
395+
396+
return resultParts.map((part, index) => {
397+
const className =
398+
part.type === "keyword"
399+
? "text-theme-primary"
400+
: part.type === "string"
401+
? "text-theme-secondary"
402+
: part.type === "number"
403+
? "text-theme-accent"
404+
: part.type === "comment"
405+
? "text-gray-500 dark:text-gray-400 italic opacity-75"
406+
: "";
407+
return (
408+
<span key={index} className={className}>
409+
{part.text}
410+
</span>
411+
);
412+
});
413+
};
414+
270415
// Calculate max height based on the longest example
271416
const maxLines = Math.max(...examples.map((ex) => ex.lines.length));
272417
const lineHeight = 1.25; // rem (20px for text-sm)
@@ -292,11 +437,19 @@ export default function AnimatedTerminal({ version }: AnimatedTerminalProps) {
292437
{displayedLines.map((line, index) => {
293438
const { prefix, content, prefixColor } = getLinePrefix(line);
294439
const lineColor = getLineColor(line);
440+
441+
// Check if this line should have syntax highlighting (Python code)
442+
const isPythonCode =
443+
line.startsWith("In [") ||
444+
line.startsWith(" ...:") ||
445+
(line.startsWith("Out[") && content.trim().length > 0);
295446

296447
return (
297448
<div key={index} className={`${lineColor} whitespace-pre`}>
298449
{prefix && <span className={prefixColor}>{prefix}</span>}
299-
{content || "\u00A0"}
450+
{isPythonCode && content.trim()
451+
? highlightPython(content)
452+
: content || "\u00A0"}
300453
</div>
301454
);
302455
})}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.