import { moveUp, moveDown, moveLeft, moveRight } from "./cursor.js"; import { hasSelection, selectedRange, clearSelection, deleteSelection } from "./selection.js"; import { elementToPos } from "./mouse.js"; export function setupInput(buffer, cursor, selection, onChange) { let isMouseDown = false; function startDrag(e) { if (!e.target.closest("#text")) return; isMouseDown = true; const pos = elementToPos(e.clientX, e.clientY, buffer); cursor.line = pos.line; cursor.col = pos.col; selection.anchor = { ...pos }; selection.head = { ...pos }; onChange(); } function drag(e) { if (!isMouseDown) return; const pos = elementToPos(e.clientX, e.clientY, buffer); cursor.line = pos.line; cursor.col = pos.col; selection.head = { ...pos }; onChange(); } function endDrag() { isMouseDown = false; } function selectWord(e) { if (!e.target.closest("#text")) return; const pos = elementToPos(e.clientX, e.clientY, buffer); const line = buffer[pos.line]; let start = pos.col; let end = pos.col; while (start > 0 && /\w/.test(line[start - 1])) start--; while (end < line.length && /\w/.test(line[end])) end++; cursor.line = pos.line; cursor.col = end; selection.anchor = { line: pos.line, col: start }; selection.head = { line: pos.line, col: end }; onChange(); } function moveCursor(newLine, newCol, extending) { if (extending) { if (selection.anchor === null) selection.anchor = { line: cursor.line, col: cursor.col }; selection.head = { line: newLine, col: newCol }; } else { clearSelection(selection); } cursor.line = newLine; cursor.col = newCol; } function getSelectedText() { const range = selectedRange(selection); if (!range) return ""; const { start, end } = range; if (start.line === end.line) return buffer[start.line].slice(start.col, end.col); const lines = [buffer[start.line].slice(start.col)]; for (let i = start.line + 1; i < end.line; i++) lines.push(buffer[i]); lines.push(buffer[end.line].slice(0, end.col)); return lines.join("\n"); } function insertText(text) { if (hasSelection(selection)) deleteSelection(buffer, cursor, selection); const pasteLines = text.split("\n"); const line = buffer[cursor.line]; const before = line.slice(0, cursor.col); const after = line.slice(cursor.col); buffer[cursor.line] = before + pasteLines[0] + after; for (let i = 1; i < pasteLines.length; i++) buffer.splice(cursor.line + i, 0, pasteLines[i]); cursor.line += pasteLines.length - 1; cursor.col = pasteLines[pasteLines.length - 1].length + (pasteLines.length === 1 ? before.length : 0); clearSelection(selection); } function selectAll() { selection.anchor = { line: 0, col: 0 }; selection.head = { line: buffer.length - 1, col: buffer[buffer.length - 1].length }; cursor.line = buffer.length - 1; cursor.col = buffer[buffer.length - 1].length; } document.addEventListener("mousedown", startDrag); document.addEventListener("mousemove", drag); document.addEventListener("mouseup", endDrag); document.addEventListener("dblclick", selectWord); document.addEventListener("paste", (e) => { e.preventDefault(); const text = e.clipboardData.getData("text"); if (!text) return; insertText(text); onChange(); }); document.addEventListener("keydown", (e) => { const mod = e.ctrlKey || e.metaKey; const extending = e.shiftKey; if (mod) { if (e.key === "c") { e.preventDefault(); const text = getSelectedText(); if (text) navigator.clipboard.writeText(text); return; } if (e.key === "x") { e.preventDefault(); const text = getSelectedText(); if (text) navigator.clipboard.writeText(text); deleteSelection(buffer, cursor, selection); onChange(); return; } if (e.key === "v") { return; } if (e.key === "a") { e.preventDefault(); selectAll(); onChange(); return; } return; } if (e.key === "ArrowUp") { e.preventDefault(); const newLine = Math.max(0, cursor.line - 1); const newCol = Math.min(cursor.col, buffer[newLine].length); moveCursor(newLine, newCol, extending); onChange(); return; } if (e.key === "ArrowDown") { e.preventDefault(); const newLine = Math.min(buffer.length - 1, cursor.line + 1); const newCol = Math.min(cursor.col, buffer[newLine].length); moveCursor(newLine, newCol, extending); onChange(); return; } if (e.key === "ArrowLeft") { e.preventDefault(); let newLine = cursor.line; let newCol = cursor.col; if (newCol > 0) newCol--; else if (newLine > 0) { newLine--; newCol = buffer[newLine].length; } moveCursor(newLine, newCol, extending); onChange(); return; } if (e.key === "ArrowRight") { e.preventDefault(); let newLine = cursor.line; let newCol = cursor.col; if (newCol < buffer[cursor.line].length) newCol++; else if (newLine < buffer.length - 1) { newLine++; newCol = 0; } moveCursor(newLine, newCol, extending); onChange(); return; } if (e.key === "Backspace") { e.preventDefault(); if (hasSelection(selection)) { deleteSelection(buffer, cursor, selection); } else if (cursor.col > 0) { const line = buffer[cursor.line]; buffer[cursor.line] = line.slice(0, cursor.col - 1) + line.slice(cursor.col); cursor.col--; } else if (cursor.line > 0) { cursor.col = buffer[cursor.line - 1].length; buffer[cursor.line - 1] += buffer[cursor.line]; buffer.splice(cursor.line, 1); cursor.line--; } clearSelection(selection); onChange(); return; } if (e.key === "Enter") { e.preventDefault(); if (hasSelection(selection)) deleteSelection(buffer, cursor, selection); const line = buffer[cursor.line]; const rest = line.slice(cursor.col); buffer[cursor.line] = line.slice(0, cursor.col); buffer.splice(cursor.line + 1, 0, rest); cursor.line++; cursor.col = 0; clearSelection(selection); onChange(); return; } if (e.key.length === 1) { e.preventDefault(); if (hasSelection(selection)) deleteSelection(buffer, cursor, selection); const line = buffer[cursor.line]; buffer[cursor.line] = line.slice(0, cursor.col) + e.key + line.slice(cursor.col); cursor.col++; clearSelection(selection); onChange(); } }); }