Skip to content

Instantly share code, notes, and snippets.

@zserge
Created September 18, 2020 09:04
Show Gist options
  • Save zserge/03280e5efebae83cc41c78dbd7e73608 to your computer and use it in GitHub Desktop.
Save zserge/03280e5efebae83cc41c78dbd7e73608 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<style>
.editor { font-family: 'Roboto Mono', monospace; font-size: 12px; outline: none; overflow-y: auto; padding-left: 48px; counter-reset: line; }
.editor div { display: block; position: relative; white-space: pre-wrap; }
.editor div::before { content: counter(line); counter-increment: line; position: absolute; right: calc(100% + 16px); opacity: 0.5; }
</style>
</head>
<body>
<div class="editor" contenteditable="true" spellcheck="false">
<div>function example() {</div>
<div> return 42;</div>
<div>}</div>
</div>
<script>
// Syntax highlight for JS
const js = el => {
for (const node of el.children) {
const s = node.innerText
.replace(/(\/\/.*)/g, '<em>$1</em>')
.replace(
/\b(new|if|else|do|while|switch|for|in|of|continue|break|return|typeof|function|var|const|let|\.length|\.\w+)(?=[^\w])/g,
'<strong>$1</strong>',
)
.replace(/(".*?"|'.*?'|`.*?`)/g, '<strong><em>$1</em></strong>')
.replace(/\b(\d+)/g, '<em><strong>$1</strong></em>');
node.innerHTML = s.split('\n').join('<br/>');
}
};
const editor = (el, highlight = js, tab = ' ') => {
const caret = () => {
const range = window.getSelection().getRangeAt(0);
const prefix = range.cloneRange();
prefix.selectNodeContents(el);
prefix.setEnd(range.endContainer, range.endOffset);
return prefix.toString().length;
};
const setCaret = (pos, parent = el) => {
for (const node of parent.childNodes) {
if (node.nodeType == Node.TEXT_NODE) {
if (node.length >= pos) {
const range = document.createRange();
const sel = window.getSelection();
range.setStart(node, pos);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return -1;
} else {
pos = pos - node.length;
}
} else {
pos = setCaret(pos, node);
if (pos < 0) {
return pos;
}
}
}
return pos;
};
highlight(el);
el.addEventListener('keydown', e => {
if (e.which === 9) {
const pos = caret() + tab.length;
const range = window.getSelection().getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(tab));
highlight(el);
setCaret(pos);
e.preventDefault();
}
});
el.addEventListener('keyup', e => {
if (e.keyCode >= 0x30 || e.keyCode == 0x20) {
const pos = caret();
highlight(el);
setCaret(pos);
}
});
};
// Turn div into an editor
const el = document.querySelector('.editor');
el.focus();
editor(el);
</script>
</body>
</html>
@rald
Copy link

rald commented Feb 1, 2023

there's a line numbering bug when you press backspace at col 1 row 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment