Skip to content

Instantly share code, notes, and snippets.

@amw-zero
Created December 14, 2022 03:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amw-zero/0996d24ac5f9ccf4d89bffcb28e51dba to your computer and use it in GitHub Desktop.
Save amw-zero/0996d24ac5f9ccf4d89bffcb28e51dba to your computer and use it in GitHub Desktop.
import { assert } from "https://deno.land/std@0.149.0/testing/asserts.ts";
import fc from 'https://cdn.skypack.dev/fast-check';
type BufferPosition = {
x: number;
y: number;
}
type ContentIndex = {
x: number;
y: number;
}
type TextContent = string[][];
type TextBuffer = string[][];
function contentToBuffer(content: TextContent, lineWidth: number): TextBuffer {
const buffer: string[][] = [];
content.forEach(line => {
const numBufferLines = Math.ceil(line.length / lineWidth);
for(let i = 0; i < numBufferLines; i++) {
const lineOffset = i * lineWidth;
buffer.push(line.slice(lineOffset, lineOffset + lineWidth));
}
});
return buffer;
}
function positionToContentIdx(pos: BufferPosition, content: TextContent, lineWidth: number): ContentIndex {
let bufferX = 0;
let bufferY = 0;
let contentX = 0;
let contentY = 0;
let contentIdx: ContentIndex | null = null;
content.forEach(line => {
line.forEach(_ => {
if (bufferX === pos.x && bufferY === pos.y) {
contentIdx = {
x: contentX,
y: contentY,
}
return
} else if (bufferX === lineWidth - 1) {
bufferX = 0;
bufferY += 1;
contentX += 1;
} else {
bufferX += 1;
contentX += 1;
}
});
if (contentIdx !== null) {
return
}
bufferX = 0;
bufferY += 1;
contentX = 0;
contentY += 1;
});
if (contentIdx !== null) {
return contentIdx;
}
console.error("Unable to find content index for position!");
return {
x: 0,
y: 0
}
}
class TextEdit {
lineWidth;
content: TextContent;
cursor: BufferPosition;
constructor(lineWidth: number = 80, content: TextContent = [[]], cursor: BufferPosition = { x: 0, y: 0 }) {
this.lineWidth = lineWidth, this.content = content, this.cursor = cursor;
}
Type(char: string) {
if (char === "\n") {
this.content.push([]);
this.cursor.x = 0;
this.cursor.y += 1;
return;
}
this.content[this.content.length - 1].push(char);
if (this.cursor.x === this.lineWidth) {
this.cursor.x = 0;
this.cursor.y += 1;
} else {
this.cursor.x += 1;
}
}
Click(pos: BufferPosition) {
this.cursor = pos;
}
Delete() {
const contentIdx = positionToContentIdx(this.cursor, this.content, this.lineWidth);
const line = this.content[contentIdx.y];
line.splice(contentIdx.x, 1);
}
Render() {
return contentToBuffer(this.content, this.lineWidth);
}
}
Deno.test("Line widths are always preserved", (_) => {
const contentGen = fc.array(fc.array(fc.char()));
const lineWidthGen = fc.integer({ min: 1, max: 160 });
return fc.assert(
fc.property(contentGen, lineWidthGen, (content: TextContent, lineWidth: number) => {
const textEdit = new TextEdit(lineWidth, content);
const buffer = textEdit.Render();
assert(buffer.every(line => line.length <= lineWidth));
})
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment