Skip to content

Commit 77168ab

Browse files
committed
refactor: start implementing InputBuffer
1 parent 743cd25 commit 77168ab

File tree

7 files changed

+275
-119
lines changed

7 files changed

+275
-119
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ console.log(`input data: ${data}`);
4444
4545
## API
4646

47-
### stdin(query: null | string, options?: StdinOptions): Promise< string >
47+
### stdin(title: null | string, options?: StdinOptions): Promise< string >
4848
Query paramaters can be set to `null` to disable the title. Options is described by the following TypeScript interface:
4949

5050
```ts
5151
interface StdinOptions {
52-
history?: string[];
53-
autocomplete?: string[];
52+
history?: string[];
53+
autocomplete?: string[];
5454
}
5555
```
5656

src/class/Completion.class.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class Completion {
2121
this.choices = choices.slice(0);
2222
}
2323

24-
clear(
24+
clearHint(
2525
force = false
2626
) {
2727
if (this.hint.length > 0) {
@@ -33,22 +33,21 @@ export class Completion {
3333
}
3434
}
3535

36-
lookFor(
37-
rawStr: string,
38-
str?: string,
36+
findHint(
37+
rawInput: string,
38+
currentInput?: string,
3939
forceNextMatch = false
4040
) {
4141
if (this.choices.length === 0) {
4242
return true;
4343
}
44-
const localMatch = localMatchOf(this.choices, rawStr, forceNextMatch);
44+
const localMatch = localMatchOf(this.choices, rawInput, forceNextMatch);
4545

46-
this.clear();
47-
if (localMatch !== null && localMatch !== "") {
46+
this.clearHint();
47+
if (localMatch) {
4848
this.hint.setValue(localMatch);
49-
5049
this.output.write(
51-
this.hint.prefix(str)
50+
this.hint.prefix(currentInput)
5251
);
5352
this.output.moveCursor(
5453
-this.hint.length,

src/class/Cursor.class.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Import Node.js Dependencies
2+
import * as TTY from "node:tty";
3+
4+
export class Cursor {
5+
position = 0;
6+
offset = 0;
7+
8+
private output: TTY.WriteStream;
9+
10+
constructor(
11+
output: TTY.WriteStream
12+
) {
13+
this.output = output;
14+
}
15+
16+
left() {
17+
if (this.position > 0) {
18+
this.output.moveCursor(-1, 0);
19+
this.position--;
20+
21+
return true;
22+
}
23+
24+
return false;
25+
}
26+
27+
right() {
28+
if (this.position < this.offset) {
29+
process.stdout.moveCursor(1, 0);
30+
this.position++;
31+
32+
return true;
33+
}
34+
35+
return false;
36+
}
37+
}

src/class/History.class.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,28 @@ export class History {
22
#commands: string[] = [];
33
#index = 0;
44

5+
private isOriginal = true;
6+
57
constructor(
68
commands: string[] = []
79
) {
810
this.#commands = commands;
911
this.#index = this.#commands.length;
1012
}
1113

14+
keep(
15+
command: string
16+
) {
17+
if (this.isOriginal) {
18+
this.push(command);
19+
this.isOriginal = false;
20+
21+
return true;
22+
}
23+
24+
return false;
25+
}
26+
1227
push(
1328
command: string,
1429
noDuplicateCheck = false

src/class/InputBuffer.class.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Import Node.js Dependencies
2+
import * as TTY from "node:tty";
3+
4+
// Import Internal Dependencies
5+
import { Cursor } from "./Cursor.class.js";
6+
7+
export interface ReplaceOptions {
8+
clearOutput?: boolean;
9+
}
10+
11+
export class InputBuffer {
12+
cursor: Cursor;
13+
14+
private output: TTY.WriteStream;
15+
private value: string = "";
16+
17+
constructor(
18+
output: TTY.WriteStream
19+
) {
20+
this.output = output;
21+
this.cursor = new Cursor(this.output);
22+
}
23+
24+
clear() {
25+
this.output.moveCursor(this.cursor.offset - this.cursor.position, 0);
26+
this.output.moveCursor(-this.value.length, 0);
27+
this.output.clearLine(1);
28+
}
29+
30+
cursorIsAtEnd() {
31+
return this.cursor.position === this.value.length;
32+
}
33+
34+
append(
35+
input: string
36+
): string {
37+
this.output.write(input);
38+
39+
return this.appendValue(input);
40+
}
41+
42+
appendAtCursor(
43+
input: string
44+
): string {
45+
const rightSideOfValue = this.value.slice(
46+
this.cursor.position
47+
);
48+
49+
this.replaceValue(
50+
`${this.value.slice(0, this.cursor.position)}${input}${rightSideOfValue}`,
51+
++this.cursor.position
52+
);
53+
54+
this.output.write(`${input}${rightSideOfValue}`);
55+
this.output.moveCursor(-rightSideOfValue.length, 0);
56+
57+
return this.value;
58+
}
59+
60+
appendValue(
61+
input: string,
62+
cursorLength: number = input.length
63+
): string {
64+
this.value += input;
65+
this.cursor.position += cursorLength;
66+
67+
return this.value;
68+
}
69+
70+
appendChar(
71+
char: string
72+
): { autocomplete: boolean; } {
73+
if (this.cursorIsAtEnd()) {
74+
this.appendValue(char);
75+
76+
return { autocomplete: true };
77+
}
78+
79+
this.appendAtCursor(char);
80+
81+
return { autocomplete: false };
82+
}
83+
84+
replace(
85+
input: string,
86+
options: ReplaceOptions = {}
87+
): string {
88+
const { clearOutput = false } = options;
89+
90+
if (clearOutput) {
91+
this.clear();
92+
}
93+
this.output.write(input);
94+
95+
return this.replaceValue(input);
96+
}
97+
98+
replaceValue(
99+
input: string,
100+
cursorLength: number = input.length
101+
): string {
102+
this.value = input;
103+
this.cursor.position = cursorLength;
104+
105+
return this.value;
106+
}
107+
108+
toString() {
109+
return this.value;
110+
}
111+
}

0 commit comments

Comments
 (0)