From e7cfb65a1b8db43d7ad8d086b457bc4202f901e0 Mon Sep 17 00:00:00 2001 From: Steven E Wright Date: Tue, 1 Aug 2023 16:47:45 +0300 Subject: [PATCH] Manual Reintegration of Changes --- codejar.ts | 25 ++++++++++++-- demo.html | 65 ------------------------------------- index.html | 67 ++++++++++++++++++++++++++++++++++++++ linenumbers.ts | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 67 deletions(-) delete mode 100755 demo.html create mode 100755 index.html create mode 100644 linenumbers.ts diff --git a/codejar.ts b/codejar.ts index 0926449..0f2cd39 100644 --- a/codejar.ts +++ b/codejar.ts @@ -326,7 +326,22 @@ export function CodeJar(editor: HTMLElement, highlight: (e: HTMLElement, pos?: P function handleSelfClosingCharacters(event: KeyboardEvent) { const open = `([{'"` const close = `)]}'"` - if (open.includes(event.key)) { + const codeAfter = afterCursor() + const codeBefore = beforeCursor() + const escapeCharacter = codeBefore.substr(codeBefore.length - 1) === '\\' + const charAfter = codeAfter.substr(0, 1) + if (close.includes(event.key) && !escapeCharacter && charAfter === event.key) { + // We already have closing char next to cursor. + // Move one char to right. + const pos = save() + preventDefault(event) + pos.start = ++pos.end + restore(pos) + } else if ( + open.includes(event.key) + && !escapeCharacter + && (`"'`.includes(event.key) || ['', ' ', '\n'].includes(charAfter)) + ) { preventDefault(event) const pos = save() const wrapText = pos.start == pos.end ? '' : getSelection().toString() @@ -441,12 +456,18 @@ export function CodeJar(editor: HTMLElement, highlight: (e: HTMLElement, pos?: P function visit(editor: HTMLElement, visitor: (el: Node) => 'stop' | undefined) { const queue: Node[] = [] + if (editor.firstChild) queue.push(editor.firstChild) + let el = queue.pop() + while (el) { - if (visitor(el) === 'stop') break + if (visitor(el) === 'stop') + break + if (el.nextSibling) queue.push(el.nextSibling) if (el.firstChild) queue.push(el.firstChild) + el = queue.pop() } } diff --git a/demo.html b/demo.html deleted file mode 100755 index 93bd319..0000000 --- a/demo.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - CodeJar 🍯 - - - - - -
-
-
- - - - - diff --git a/index.html b/index.html new file mode 100755 index 0000000..eb09567 --- /dev/null +++ b/index.html @@ -0,0 +1,67 @@ + + + + + CodeJar – a micro code editor + + + + + +
+
+
+ + + + diff --git a/linenumbers.ts b/linenumbers.ts new file mode 100644 index 0000000..bc9d784 --- /dev/null +++ b/linenumbers.ts @@ -0,0 +1,88 @@ +type Options = { + class: string + wrapClass: string + width: string + backgroundColor: string + color: string +} + +export function withLineNumbers( + highlight: (e: HTMLElement) => void, + options: Partial = {} +) { + const opts: Options = { + class: "codejar-linenumbers", + wrapClass: "codejar-wrap", + width: "35px", + backgroundColor: "rgba(128, 128, 128, 0.15)", + color: "", + ...options + } + + let lineNumbers: HTMLElement + return function (editor: HTMLElement) { + highlight(editor) + + if (!lineNumbers) { + lineNumbers = init(editor, opts) + editor.addEventListener("scroll", () => lineNumbers.style.top = `-${editor.scrollTop}px`); + } + + const code = editor.textContent || "" + const linesCount = code.replace(/\n+$/, "\n").split("\n").length + 1 + + let text = "" + for (let i = 1; i < linesCount; i++) { + text += `${i}\n` + } + + lineNumbers.innerText = text + } +} + +function init(editor: HTMLElement, opts: Options): HTMLElement { + const css = getComputedStyle(editor) + + const wrap = document.createElement("div") + wrap.className = opts.wrapClass + wrap.style.position = "relative" + + const gutter = document.createElement("div") + gutter.className = opts.class + wrap.appendChild(gutter) + + // Add own styles + gutter.style.position = "absolute" + gutter.style.top = "0px" + gutter.style.left = "0px" + gutter.style.bottom = "0px" + gutter.style.width = opts.width + gutter.style.overflow = "hidden" + gutter.style.backgroundColor = opts.backgroundColor + gutter.style.color = opts.color || css.color + gutter.style.setProperty("mix-blend-mode", "difference") + + // Copy editor styles + gutter.style.fontFamily = css.fontFamily + gutter.style.fontSize = css.fontSize + gutter.style.lineHeight = css.lineHeight + gutter.style.paddingTop = css.paddingTop + gutter.style.paddingLeft = css.paddingLeft + gutter.style.borderTopLeftRadius = css.borderTopLeftRadius + gutter.style.borderBottomLeftRadius = css.borderBottomLeftRadius + + // Add line numbers + const lineNumbers = document.createElement("div"); + lineNumbers.style.position = "relative"; + lineNumbers.style.top = "0px" + gutter.appendChild(lineNumbers) + + // Tweak editor styles + editor.style.paddingLeft = `calc(${opts.width} + ${gutter.style.paddingLeft})` + editor.style.whiteSpace = "pre" + + // Swap editor with a wrap + editor.parentNode!.insertBefore(wrap, editor) + wrap.appendChild(editor) + return lineNumbers +}