UNPKG

primrose

Version:

Syntax-highlighting text editor that renders to an HTML5 Canvas element

261 lines (236 loc) 6.75 kB
import { reverse } from "./reverse.js"; export class Cursor { static min(a, b) { if (a.i <= b.i) { return a; } return b; } static max(a, b) { if (a.i > b.i) { return a; } return b; } constructor(i, x, y) { this.i = i || 0; this.x = x || 0; this.y = y || 0; Object.seal(this); } clone() { return new Cursor(this.i, this.x, this.y); } toString() { return `[i:${this.i} x:${this.x} y:${this.y}]`; } copy(cursor) { this.i = cursor.i; this.x = cursor.x; this.y = cursor.y; } fullHome() { this.i = 0; this.x = 0; this.y = 0; } fullEnd(rows) { this.i = 0; let lastLength = 0; for (let y = 0; y < rows.length; ++y) { const row = rows[y]; lastLength = row.stringLength; this.i += lastLength; } this.y = rows.length - 1; this.x = lastLength; } left(rows, skipAdjust = false) { if (this.i > 0) { --this.i; --this.x; if (this.x < 0) { --this.y; const row = rows[this.y]; this.x = row.stringLength - 1; } else if (!skipAdjust) { rows[this.y].adjust(this, -1); } } } skipLeft(rows) { if (this.x <= 1) { this.left(rows); } else { const x = this.x - 1, row = rows[this.y], word = reverse(row.substring(0, x)), m = word.match(/\w+/), dx = m ? (m.index + m[0].length + 1) : this.x; this.i -= dx; this.x -= dx; rows[this.y].adjust(this, -1); } } right(rows, skipAdjust = false) { const row = rows[this.y]; if (this.y < rows.length - 1 || this.x < row.stringLength) { ++this.i; ++this.x; if (this.y < rows.length - 1 && this.x === row.stringLength) { this.x = 0; ++this.y; } else if (!skipAdjust) { rows[this.y].adjust(this, 1); } } } skipRight(rows) { const row = rows[this.y]; if (this.x < row.stringLength - 1) { const x = this.x + 1, subrow = row.substring(x), m = subrow.match(/\w+/), dx = m ? (m.index + m[0].length + 1) : (row.stringLength - this.x); this.i += dx; this.x += dx; if (this.x > 0 && this.x === row.stringLength && this.y < rows.length - 1) { --this.x; --this.i; } rows[this.y].adjust(this, 1); } else if (this.y < rows.length - 1) { this.right(rows); } } home() { this.i -= this.x; this.x = 0; } end(rows) { const row = rows[this.y]; let dx = row.stringLength - this.x; if (this.y < rows.length - 1) { --dx; } this.i += dx; this.x += dx; } up(rows, skipAdjust = false) { if (this.y > 0) { --this.y; const row = rows[this.y], dx = Math.min(0, row.stringLength - this.x - 1); this.x += dx; this.i -= row.stringLength - dx; if (!skipAdjust) { rows[this.y].adjust(this, 1); } } } down(rows, skipAdjust = false) { if (this.y < rows.length - 1) { const prevRow = rows[this.y]; ++this.y; this.i += prevRow.stringLength; const row = rows[this.y]; if (this.x >= row.stringLength) { let dx = this.x - row.stringLength; if (this.y < rows.length - 1) { ++dx; } this.i -= dx; this.x -= dx; } if (!skipAdjust) { rows[this.y].adjust(this, 1); } } } incX(rows, dx) { const dir = Math.sign(dx); dx = Math.abs(dx); if (dir === -1) { for (let i = 0; i < dx; ++i) { this.left(rows, true); } rows[this.y].adjust(this, -1); } else if (dir === 1) { for (let i = 0; i < dx; ++i) { this.right(rows, true); } rows[this.y].adjust(this, 1); } } incY(rows, dy) { const dir = Math.sign(dy); dy = Math.abs(dy); if (dir === -1) { for (let i = 0; i < dy; ++i) { this.up(rows, true); } } else if (dir === 1) { for (let i = 0; i < dy; ++i) { this.down(rows, true); } } rows[this.y].adjust(this, 1); } setXY(rows, x, y) { x = Math.floor(x); y = Math.floor(y); this.y = Math.max(0, Math.min(rows.length - 1, y)); const row = rows[this.y]; this.x = Math.max(0, Math.min(row.stringLength, x)); this.i = this.x; for (let i = 0; i < this.y; ++i) { this.i += rows[i].stringLength; } if (this.x > 0 && this.x === row.stringLength && this.y < rows.length - 1) { --this.x; --this.i; } rows[this.y].adjust(this, 1); } setI(rows, i) { const delta = this.i - i, dir = Math.sign(delta); this.x = this.i = i; this.y = 0; let total = 0, row = rows[this.y]; while (this.x > row.stringLength) { this.x -= row.stringLength; total += row.stringLength; if (this.y >= rows.length - 1) { this.i = total; this.x = row.stringLength; break; } ++this.y; row = rows[this.y]; } if (this.y < rows.length - 1 && this.x === row.stringLength) { this.x = 0; ++this.y; } rows[this.y].adjust(this, dir); } }