Add PageUp/PageDown and Home/End scrolling

This commit is contained in:
Timothy Warren 2023-11-20 15:14:36 -05:00
parent 28ec91798a
commit 4df0c70c32
4 changed files with 73 additions and 16 deletions

View File

@ -1,17 +1,41 @@
import { chars } from './utils.ts'; import { chars } from './utils.ts';
import { getRuntime } from './runtime.ts'; import { getRuntime } from './runtime.ts';
import { TAB_SIZE } from './mod.ts';
export class Row { export class Row {
chars: string[] = []; chars: string[] = [];
render: string[] = [];
constructor(s: string = '') { constructor(s: string = '') {
this.chars = chars(s); this.chars = chars(s);
this.render = [];
} }
public get size(): number { public get size(): number {
return this.chars.length; return this.chars.length;
} }
public get rsize(): number {
return this.render.length;
}
public rstring(offset: number = 0): string {
return this.render.slice(offset).join('');
}
public cxToRx(cx: number): number {
let rx = 0;
let j = 0;
for (; j < cx; j++) {
if (this.chars[j] === '\t') {
rx += (TAB_SIZE - 1) - (rx % TAB_SIZE);
}
rx++;
}
return rx;
}
public toString(): string { public toString(): string {
return this.chars.join(''); return this.chars.join('');
} }
@ -56,7 +80,13 @@ export class Document {
} }
public appendRow(s: string): void { public appendRow(s: string): void {
this.#rows.push(new Row(s)); const at = this.numRows;
this.#rows[at] = new Row(s);
this.updateRow(this.#rows[at]);
}
private updateRow(r: Row): void {
r.render = chars(r.toString().replace('\t', ' '.repeat(TAB_SIZE)));
} }
} }

View File

@ -1,7 +1,7 @@
import Ansi, { KeyCommand } from './ansi.ts'; import Ansi, { KeyCommand } from './ansi.ts';
import Buffer from './buffer.ts'; import Buffer from './buffer.ts';
import Document, { Row } from './document.ts'; import Document, { Row } from './document.ts';
import { IPoint, ITerminalSize, logToFile, VERSION } from './mod.ts'; import { IPoint, ITerminalSize, logToFile, maxAdd, VERSION } from './mod.ts';
import { ctrlKey, posSub } from './utils.ts'; import { ctrlKey, posSub } from './utils.ts';
export class Editor { export class Editor {
@ -29,10 +29,11 @@ export class Editor {
* @private * @private
*/ */
#document: Document; #document: Document;
/**
private get currentRow(): Row | null { * The scrolling offset for the rendered row
return this.#document.row(this.#cursor.y); * @private
} */
#render: IPoint;
constructor(terminalSize: ITerminalSize) { constructor(terminalSize: ITerminalSize) {
this.#buffer = new Buffer(); this.#buffer = new Buffer();
@ -45,10 +46,18 @@ export class Editor {
x: 0, x: 0,
y: 0, y: 0,
}; };
this.#render = {
x: 0,
y: 0,
};
this.#document = Document.empty(); this.#document = Document.empty();
} }
private get currentRow(): Row | null {
return this.#document.row(this.#cursor.y);
}
public async open(filename: string): Promise<Editor> { public async open(filename: string): Promise<Editor> {
await this.#document.open(filename); await this.#document.open(filename);
@ -74,12 +83,24 @@ export class Editor {
break; break;
case KeyCommand.End: case KeyCommand.End:
this.#cursor.x = this.#screen.cols - 1; if (this.currentRow !== null) {
this.#cursor.x = this.currentRow.size - 1;
}
break; break;
case KeyCommand.PageUp: case KeyCommand.PageUp:
case KeyCommand.PageDown: case KeyCommand.PageDown:
{ {
if (input === KeyCommand.PageUp) {
this.#cursor.y = this.#offset.y;
} else if (input === KeyCommand.PageDown) {
this.#cursor.y = maxAdd(
this.#offset.y,
this.#screen.rows - 1,
this.#document.numRows,
);
}
let times = this.#screen.rows; let times = this.#screen.rows;
while (times--) { while (times--) {
this.moveCursor( this.moveCursor(
@ -146,17 +167,22 @@ export class Editor {
} }
private scroll(): void { private scroll(): void {
this.#render.x = 0;
if (this.currentRow !== null) {
this.#render.x = this.currentRow.cxToRx(this.#cursor.x);
}
if (this.#cursor.y < this.#offset.y) { if (this.#cursor.y < this.#offset.y) {
this.#offset.y = this.#cursor.y; this.#offset.y = this.#cursor.y;
} }
if (this.#cursor.y >= this.#offset.y + this.#screen.rows) { if (this.#cursor.y >= this.#offset.y + this.#screen.rows) {
this.#offset.y = this.#cursor.y - this.#screen.rows + 1; this.#offset.y = this.#cursor.y - this.#screen.rows + 1;
} }
if (this.#cursor.x < this.#offset.x) { if (this.#render.x < this.#offset.x) {
this.#offset.x = this.#cursor.x; this.#offset.x = this.#render.x;
} }
if (this.#cursor.x >= this.#offset.x + this.#screen.cols) { if (this.#render.x >= this.#offset.x + this.#screen.cols) {
this.#offset.x = this.#cursor.x - this.#screen.cols + 1; this.#offset.x = this.#render.x - this.#screen.cols + 1;
} }
} }
@ -175,7 +201,7 @@ export class Editor {
this.#buffer.append( this.#buffer.append(
Ansi.moveCursor( Ansi.moveCursor(
this.#cursor.y - this.#offset.y, this.#cursor.y - this.#offset.y,
this.#cursor.x - this.#offset.x, this.#render.x - this.#offset.x,
), ),
); );
this.#buffer.append(Ansi.ShowCursor); this.#buffer.append(Ansi.ShowCursor);
@ -192,6 +218,7 @@ export class Editor {
private drawRows(): void { private drawRows(): void {
for (let y = 0; y < this.#screen.rows; y++) { for (let y = 0; y < this.#screen.rows; y++) {
this.#buffer.append(Ansi.ClearLine);
const filerow = y + this.#offset.y; const filerow = y + this.#offset.y;
if (filerow >= this.#document.numRows) { if (filerow >= this.#document.numRows) {
this.drawPlaceholderRow(filerow); this.drawPlaceholderRow(filerow);
@ -199,7 +226,6 @@ export class Editor {
this.drawFileRow(filerow); this.drawFileRow(filerow);
} }
this.#buffer.append(Ansi.ClearLine);
if (y < this.#screen.rows - 1) { if (y < this.#screen.rows - 1) {
this.#buffer.appendLine(); this.#buffer.appendLine();
} }
@ -214,11 +240,11 @@ export class Editor {
} }
const len = Math.min( const len = Math.min(
posSub(row.chars.length, this.#offset.x), posSub(row.rsize, this.#offset.x),
this.#screen.cols, this.#screen.cols,
); );
this.#buffer.append(row.toString(), len); this.#buffer.append(row.rstring(this.#offset.x), len);
} }
private drawPlaceholderRow(y: number): void { private drawPlaceholderRow(y: number): void {

View File

@ -5,3 +5,4 @@ export * from './utils.ts';
export type * from './types.ts'; export type * from './types.ts';
export const VERSION = '0.0.1'; export const VERSION = '0.0.1';
export const TAB_SIZE = 4;

View File

@ -39,7 +39,7 @@ const DenoFFI: IFFI = {
tcsetattr, tcsetattr,
cfmakeraw, cfmakeraw,
getPointer: Deno.UnsafePointer.of, getPointer: Deno.UnsafePointer.of,
close: cStdLib.close, close: () => {},
}; };
export default DenoFFI; export default DenoFFI;