Add PageUp/PageDown and Home/End scrolling
This commit is contained in:
parent
28ec91798a
commit
4df0c70c32
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user