Skip to content

Instantly share code, notes, and snippets.

@lqs469
Forked from zserge/editor.html
Created October 27, 2020 03:37
Show Gist options
  • Select an option

  • Save lqs469/f70a74f32f2338acc706a7aa08fb723d to your computer and use it in GitHub Desktop.

Select an option

Save lqs469/f70a74f32f2338acc706a7aa08fb723d to your computer and use it in GitHub Desktop.

Revisions

  1. @zserge zserge created this gist Sep 18, 2020.
    95 changes: 95 additions & 0 deletions editor.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,95 @@
    <!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>