Skip to content

Commit 7145d4e

Browse files
Fix: Highlight f-string and vertical shift for code editor.
1 parent 4a7525f commit 7145d4e

3 files changed

Lines changed: 38 additions & 28 deletions

File tree

demo/index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ <h1 class="font-semibold">Edge Python Playground</h1>
6060

6161
<section aria-label="Code editor" class="w-full bg-[#222222] border border-[#2d2d2d] rounded-md flex flex-col flex-[70] basis-0 overflow-hidden">
6262
<header class="bg-[#1c1c1c] border-[#2d2d2d] flex justify-between items-center">
63-
<div class="text-[12px] tracking-tighter font-semibold text-zinc-500 flex items-center gap-1 w-full">
63+
<div class="text-[12px] tracking-tighter font-semibold text-zinc-500 flex items-center gap-1 w-full border-b border-[#2d2d2d]">
6464
<span class="px-3 py-2">
6565
<p aria-hidden="true">Editor</p>
6666
</span>
@@ -80,7 +80,8 @@ <h1 class="font-semibold">Edge Python Playground</h1>
8080

8181
<div id="ed"
8282
class="no-scrollbar flex-1 p-2 text-[#c2c2c2] overflow-y-auto"
83-
aria-label="Python source code editor"></div>
83+
aria-label="Python source code editor">
84+
</div>
8485
</div>
8586
</section>
8687

demo/main.js

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,34 +35,41 @@ const BI = new Set([
3535
]);
3636
const LIT = new Set(['True','False','None']);
3737

38+
const WORD_CLS = [
39+
[KW, 'tk-kw'],
40+
[LIT, 'tk-lit'],
41+
[BI, 'tk-bi'],
42+
];
43+
3844
const TOKEN_RE = /(#[^\n]*)|((?:\b[fFrRbBuU]{1,2})?(?:"""[\s\S]*?"""|'''[\s\S]*?'''|"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'))|(0[xX][\da-fA-F_]+|0[oO][0-7_]+|0[bB][01_]+|\d[\d_]*(?:\.[\d_]*)?(?:[eE][+-]?\d+)?[jJ]?|\.\d[\d_]*(?:[eE][+-]?\d+)?[jJ]?)|([A-Za-z_]\w*)/g;
3945

4046
const esc = (s) => s.replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
41-
42-
const highlight = (src) => {
43-
const escaped = esc(src);
44-
return escaped.replace(TOKEN_RE, (m, com, str, num, word, offset, fullStr) => {
45-
if (com) return `<span class="tk-com">${com}</span>`;
46-
if (str) return `<span class="tk-str">${str}</span>`;
47-
if (num) return `<span class="tk-num">${num}</span>`;
48-
if (word) {
49-
const isEntity = fullStr[offset - 1] === '&' && fullStr[offset + word.length] === ';';
50-
if (isEntity) return word;
51-
52-
if (KW.has(word)) return `<span class="tk-kw">${word}</span>`;
53-
if (LIT.has(word)) return `<span class="tk-lit">${word}</span>`;
54-
if (BI.has(word)) return `<span class="tk-bi">${word}</span>`;
55-
56-
const isFunction = /^\s*\(/.test(fullStr.slice(offset + word.length));
57-
if (isFunction) {
58-
return `<span class="tk-func">${word}</span>`;
59-
} else {
60-
return `<span class="tk-var">${word}</span>`;
61-
}
47+
const span = (cls, s) => `<span class="${cls}">${s}</span>`;
48+
49+
const tokenize = (m, com, str, num, word, offset, fullStr) => {
50+
if (com) return span('tk-com', com);
51+
if (str) {
52+
if (/^[fFrRbBuU]*[fF]/i.test(str)) {
53+
const body = str.replace(/\{\{|\}\}|\{([^{}]*)\}/g, (m, expr) =>
54+
expr != null
55+
? `{${expr.replace(new RegExp(TOKEN_RE.source, TOKEN_RE.flags), tokenize)}}`
56+
: m
57+
);
58+
return span('tk-str', body);
6259
}
63-
return m;
64-
});
60+
return span('tk-str', str);
61+
}
62+
if (num) return span('tk-num', num);
63+
if (word) {
64+
if (fullStr[offset - 1] === '&' && fullStr[offset + word.length] === ';') return word;
65+
for (const [set, cls] of WORD_CLS) if (set.has(word)) return span(cls, word);
66+
return span(/^\s*\(/.test(fullStr.slice(offset + word.length)) ? 'tk-func' : 'tk-var', word);
67+
}
68+
return m;
6569
};
70+
71+
const highlight = (src) => esc(src).replace(TOKEN_RE, tokenize);
72+
6673
const jar = CodeJar(ed, (editor) => {
6774
editor.innerHTML = highlight(editor.textContent);
6875
}, {
@@ -125,7 +132,7 @@ const runCode = async () => {
125132
const sync = () => {
126133
const text = jar.toString().replace(/\n$/, '');
127134
const n = Math.max(1, Math.min(text.split('\n').length, MAX_LINES));
128-
ln.textContent = Array.from({ length: n }, (_, i) => String(i + 1).padStart(2, '0')).join('\n');
135+
ln.textContent = Array.from({ length: n }, (_, i) => String(i + 1).padStart(2, '00')).join('\n');
129136
ln.scrollTop = ed.scrollTop;
130137
};
131138

demo/style.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
#ed {
1010
tab-size: 4;
11-
white-space: pre;
11+
white-space: pre !important;
1212
outline: none;
1313
font-variant-ligatures: none;
14+
overflow-x: auto;
1415
}
1516

1617
.tk-kw { color: #c586c0; }
@@ -20,4 +21,5 @@
2021
.tk-str { color: #ce9178; }
2122
.tk-com { color: #6a9955; font-style: italic; }
2223
.tk-var { color: #9cdcfe; }
23-
.tk-func { color: #dcdcaa; }
24+
.tk-func { color: #dcdcaa; }
25+
.tk-fexpr { color: #9cdcfe; }

0 commit comments

Comments
 (0)