From 2e1356b4c67206e1eda82d842fe4280452a048ff Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka <ueokande@i-beam.org>
Date: Tue, 24 Mar 2020 21:53:09 +0900
Subject: Add command-line parser on console scripts

---
 src/console/commandline/CommandLineParser.ts       | 38 ++++++++++++++++
 src/console/commandline/CommandParser.ts           | 52 ++++++++++++++++++++++
 src/shared/Command.ts                              | 15 +++++++
 test/console/commandline/CommandLineParser.test.ts | 29 ++++++++++++
 test/console/commandline/CommandParser.test.ts     | 15 +++++++
 5 files changed, 149 insertions(+)
 create mode 100644 src/console/commandline/CommandLineParser.ts
 create mode 100644 src/console/commandline/CommandParser.ts
 create mode 100644 src/shared/Command.ts
 create mode 100644 test/console/commandline/CommandLineParser.test.ts
 create mode 100644 test/console/commandline/CommandParser.test.ts

diff --git a/src/console/commandline/CommandLineParser.ts b/src/console/commandline/CommandLineParser.ts
new file mode 100644
index 0000000..a166f49
--- /dev/null
+++ b/src/console/commandline/CommandLineParser.ts
@@ -0,0 +1,38 @@
+import CommandParser from "./CommandParser";
+import { Command } from "../../shared/Command";
+
+export type CommandLine = {
+  readonly command: Command,
+  readonly args: string
+}
+
+export enum InputPhase {
+  OnCommand,
+  OnArgs,
+}
+
+export default class CommandLineParser {
+  private commandParser: CommandParser = new CommandParser();
+
+  inputPhase(line: string): InputPhase {
+    line = line.trimLeft();
+    if (line.length == 0) {
+      return InputPhase.OnCommand
+    }
+    const command = line.split(/\s+/, 1)[0];
+    if (line.length == command.length) {
+      return InputPhase.OnCommand
+    }
+    return InputPhase.OnArgs;
+  }
+
+  parse(line: string): CommandLine {
+    const trimLeft = line.trimLeft();
+    const command = trimLeft.split(/\s+/, 1)[0];
+    const args = trimLeft.slice(command.length).trimLeft();
+    return {
+      command: this.commandParser.parse(command),
+      args: args,
+    }
+  }
+}
diff --git a/src/console/commandline/CommandParser.ts b/src/console/commandline/CommandParser.ts
new file mode 100644
index 0000000..5228c77
--- /dev/null
+++ b/src/console/commandline/CommandParser.ts
@@ -0,0 +1,52 @@
+import { Command } from "../../shared/Command";
+
+export class UnknownCommandError extends Error {
+  constructor(value: string) {
+    super(`unknown command '${value}'`);
+  }
+}
+
+export default class CommandParser {
+  parse(value: string): Command {
+    switch (value) {
+    case 'o':
+    case 'open':
+      return Command.Open;
+    case 't':
+    case 'tabopen':
+      return Command.TabOpen;
+    case 'w':
+    case 'winopen':
+      return Command.WindowOpen;
+    case 'b':
+    case 'buffer':
+      return Command.Buffer;
+    case 'bd':
+    case 'bdel':
+    case 'bdelete':
+      return Command.BufferDelete;
+    case 'bd!':
+    case 'bdel!':
+    case 'bdelete!':
+      return Command.BufferDeleteForce;
+    case 'bdeletes':
+      return Command.BuffersDelete;
+    case 'bdeletes!':
+      return Command.BuffersDeleteForce;
+    case 'addbookmark':
+      return Command.AddBookmark;
+    case 'q':
+    case 'quit':
+      return Command.Quit;
+    case 'qa':
+    case 'quitall':
+      return Command.QuitAll;
+    case 'set':
+      return Command.Set;
+    case 'h':
+    case 'help':
+      return Command.Help;
+    }
+    throw new UnknownCommandError(value);
+  }
+}
diff --git a/src/shared/Command.ts b/src/shared/Command.ts
new file mode 100644
index 0000000..e492f4a
--- /dev/null
+++ b/src/shared/Command.ts
@@ -0,0 +1,15 @@
+export enum Command {
+    Open = "open",
+    TabOpen = "tabopen",
+    WindowOpen = "winopen",
+    Buffer = "buffer",
+    BufferDelete = "bdelete",
+    BufferDeleteForce = "bdelete!",
+    BuffersDelete = "bdeletes",
+    BuffersDeleteForce = "bdeletes!",
+    AddBookmark = "addbookmark",
+    Quit = "quit",
+    QuitAll = "quitall",
+    Set = "set",
+    Help = "help",
+}
diff --git a/test/console/commandline/CommandLineParser.test.ts b/test/console/commandline/CommandLineParser.test.ts
new file mode 100644
index 0000000..6aec682
--- /dev/null
+++ b/test/console/commandline/CommandLineParser.test.ts
@@ -0,0 +1,29 @@
+import CommandLineParser, {InputPhase} from "../../../src/console/commandline/CommandLineParser";
+import { Command } from "../../../src/shared/Command";
+import { expect } from "chai";
+
+describe("CommandLineParser", () => {
+  describe("#inputPhase", () => {
+    it("returns parsed command-line", () => {
+      const sut = new CommandLineParser();
+      expect(sut.inputPhase("")).to.equal(InputPhase.OnCommand);
+      expect(sut.inputPhase("op")).to.equal(InputPhase.OnCommand);
+      expect(sut.inputPhase("open ")).to.equal(InputPhase.OnArgs);
+      expect(sut.inputPhase("open apple")).to.equal(InputPhase.OnArgs)
+    });
+  });
+  describe("#parse", () => {
+    it("returns parsed command-line", () => {
+      const sut = new CommandLineParser();
+      expect(sut.parse("open google  apple")).to.deep.equal({
+        command: Command.Open,
+        args: "google  apple",
+      });
+
+      expect(sut.parse("qa")).to.deep.equal({
+        command: Command.QuitAll,
+        args: "",
+      });
+    })
+  })
+});
diff --git a/test/console/commandline/CommandParser.test.ts b/test/console/commandline/CommandParser.test.ts
new file mode 100644
index 0000000..4ad78fd
--- /dev/null
+++ b/test/console/commandline/CommandParser.test.ts
@@ -0,0 +1,15 @@
+import CommandParser, { UnknownCommandError } from "../../../src/console/commandline/CommandParser";
+import { Command } from "../../../src/shared/Command";
+import { expect } from "chai"
+
+describe("CommandParser", () => {
+  describe("#parse", () => {
+    it("returns matched command with the string", () => {
+      const sut = new CommandParser();
+      expect(sut.parse("open")).to.equal(Command.Open);
+      expect(sut.parse("w")).to.equal(Command.WindowOpen);
+      expect(sut.parse("bdelete!")).to.equal(Command.BufferDeleteForce);
+      expect(() => sut.parse("harakiri")).to.throw(UnknownCommandError);
+    })
+  })
+});
-- 
cgit v1.2.3