Skip to content

Commit

Permalink
Further improve auto completions
Browse files Browse the repository at this point in the history
  • Loading branch information
pindab0ter committed Apr 10, 2024
1 parent 528acce commit b61bce5
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 34 deletions.
2 changes: 1 addition & 1 deletion assets/ts/terminal/AutocompletingCommand.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Command } from "./Command";

export abstract class AutocompletingCommand extends Command {
abstract autocomplete(arg: string): string[];
abstract suggestAutocompletions(arg: string): string[];
}
65 changes: 40 additions & 25 deletions assets/ts/terminal/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class Terminal {
* Handle enter key press to clear the prompt input.
*/
document.addEventListener("keydown", (event: KeyboardEvent) => {
const input = this.inputElement.textContent?.replace(/\xA0/g, " ").trim() ?? "";
const input = this.getInput();

switch (event.key) {
case "ArrowLeft":
Expand Down Expand Up @@ -91,8 +91,8 @@ export class Terminal {
});
}

private onEnter(input: string) {
const { command, args } = getCommandFromInput(input);
private onEnter(input: string): void {
const { command, args } = getCommandFromInput(input.trim());
this.setInput();
this.clearOutput();

Expand All @@ -105,31 +105,42 @@ export class Terminal {
return;
}

private onTab(input: string) {
private onTab(input: string): void {
this.clearOutput();

if (input.length === 0) {
this.print(...Terminal.commands.map((command: Command) => command.name));
return;
}

this.clearOutput();

const { command, args } = getCommandFromInput(input);

// Autocomplete command arguments
if (command && command instanceof AutocompletingCommand && args.length > 0) {
const completions = command.autocomplete(args[args.length - 1]);
if (completions.length === 1) {
args[args.length - 1] = completions[0];
this.setInput(`${command.name} ${args.join(" ")}`);
}
if (command && command instanceof AutocompletingCommand) {
// If the input is exactly the command name (with no space after), don't autocomplete arguments.
if (input === command.name) return;

if (completions.length > 1) {
this.print(...completions);
this.setInput(`${command.name} ${longestCommonPrefix(completions)}`);
}
this.autocompleteArguments(command, args);
return;
}

// Autocomplete command names
this.autocompleteCommands(input);
}

private autocompleteArguments(command: AutocompletingCommand, args: string[] = []) {
const argument = args[args.length - 1] ?? "";
const suggestions = command.suggestAutocompletions(argument);

if (suggestions.length === 1) {
this.setInput(`${command.name} ${suggestions[0]}`);
}

if (suggestions.length > 1) {
this.print(...suggestions);
this.setInput(`${command.name} ${longestCommonPrefix(suggestions)}`);
}
}

private autocompleteCommands(input: string): void {
const matchingCommandNames = Terminal.commands
.filter((command: Command) => command.name.startsWith(input))
.map((command: Command) => command.name);
Expand All @@ -148,10 +159,9 @@ export class Terminal {
this.print(...matchingCommandNames);
this.setInput(longestCommonPrefix(matchingCommandNames));
}
return;
}

private moveCaretToEnd() {
private moveCaretToEnd(): void {
const range = document.createRange();
const selection = window.getSelection();

Expand All @@ -162,21 +172,26 @@ export class Terminal {
selection?.addRange(range);
}

public print(...lines: string[]) {
public print(...lines: string[]): void {
lines.forEach((line: string) => {
const outputElement = document.createElement("pre");
outputElement.textContent = line;
this.terminalOutputElement.appendChild(outputElement);
});
}

public setInput(newInput: string = "") {
this.inputElement.textContent = newInput;
this.promptBlurElement.textContent = newInput;
private getInput() {
return this.inputElement.textContent?.replace(/\xA0/g, " ") ?? "";
}

public setInput(input: string = ""): void {
const formattedInput = input.replace(/ /g, "\xA0");
this.inputElement.textContent = formattedInput;
this.promptBlurElement.textContent = formattedInput;
this.moveCaretToEnd();
}

public clearOutput() {
public clearOutput(): void {
while (this.terminalOutputElement?.firstChild) {
this.terminalOutputElement.removeChild(this.terminalOutputElement.firstChild);
}
Expand Down
8 changes: 1 addition & 7 deletions assets/ts/terminal/commands/ChangeDirectory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Command } from "../Command";
import { HugoPage } from "../../types/hugo";
import { Terminal } from "../Terminal";
import { getAllPages, getPagesInPath, slugPath } from "../helpers";
Expand Down Expand Up @@ -66,11 +65,6 @@ export class ChangeDirectory extends AutocompletingCommand {
// Change to a relative path
if (
this.pagesInPath.find((p: HugoPage) => {
console.log(
inputPath,
p.Path.replace(window.location.pathname, "").toLowerCase(),
slugPath(p).replace(window.location.pathname, "").toLowerCase(),
);
return (
p.Path.replace(window.location.pathname, "").toLowerCase() === inputPath ||
slugPath(p).replace(window.location.pathname, "").toLowerCase() === inputPath
Expand All @@ -82,7 +76,7 @@ export class ChangeDirectory extends AutocompletingCommand {
}
}

public autocomplete(arg: string): string[] {
public suggestAutocompletions(arg: string): string[] {
const suggestions = this.pagesInPath
.map((page: HugoPage) => slugPath(page))
.map((path: string) => path.replace(window.location.pathname, ""));
Expand Down
2 changes: 1 addition & 1 deletion assets/ts/terminal/commands/Help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class Help extends AutocompletingCommand {
}
}

public autocomplete(arg: string): string[] {
public suggestAutocompletions(arg: string): string[] {
return Terminal.commands
.map((command: Command) => command.name)
.filter((name: string) => name.startsWith(arg));
Expand Down

0 comments on commit b61bce5

Please sign in to comment.