1 /**
2  * Reference: 
3  * https://github.com/antirez/linenoise
4  * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
5  * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
6  **/
7 module vrepl.repl;
8 
9 import std.stdio: write, writeln;
10 import std.conv: to;
11 import dutil.containers;
12 import std.process;
13 import vrepl.shell;
14 
15 alias StringSet = HashSet!string;
16 
17 enum SIGTTIN = 21;
18 enum SIGTTOU = 22;
19 enum ES_CLEAR = "\x1b[H\x1b[2J";
20 enum ES_BEEP = "\x07";
21 enum Mode { SHELL, LINE, MLINE, EDIT }
22 
23 class State {
24     Mode mode = Mode.LINE;
25 }
26 
27 extern(C) void sig_hand(int signal) nothrow @nogc @system {
28     import core.stdc.stdio: printf;
29     printf("signal %d catched!\n", signal);
30 }
31 
32 class Config {
33     string[Mode] prompt;
34     StringSet quits;
35     void delegate(string) onInput;
36 
37     this() {
38         with(Mode) prompt = [
39             SHELL: "$",
40             LINE: ">",
41             MLINE: ">>",
42             EDIT: "*>"
43         ];
44 
45         quits.add("quit", "bye", "exit");
46     }
47 }
48 
49 class Vrepl {
50     Config config;
51     State state;
52     Shell shell;
53 
54     this() {
55         config = new Config();
56         state = new State();
57         shell = new Shell();
58     }
59 
60     void setMode(Mode m) {
61         this.state.mode = m;
62     }
63 
64     void prompt() {
65         if (state.mode == Mode.SHELL) {
66             write(config.prompt[Mode.SHELL] ~ " ");
67         } else {
68             write(config.prompt[state.mode] ~ " ");
69         }
70     }
71 
72     bool checkMode(string line) {
73         import std.string: chomp;
74         if (line == "mode shell") {
75             state.mode = Mode.SHELL;
76             writeln("Change Mode: ", "shell");
77             return false;
78         } else if (line == "mode line") {
79             state.mode = Mode.LINE;
80             writeln("Change Mode: ", "line");
81             return false;
82         } else if (line.chomp == "") {
83             writeln("empty");
84             return false;
85         } else {
86             return true;
87         }
88     }
89 
90     void quit() { }
91 
92     bool isQuit(string line) {
93         return config.quits.contains(line);
94     }
95 
96     ProcessPipes pipes;
97     void loop() {
98         import std.string: chomp;
99         import std.stdio: stdin, readln;
100 
101         /*
102         import core.stdc.signal;
103         signal(SIGTTIN, &sig_hand);
104         signal(SIGTTOU, &sig_hand);
105         */
106         string line;
107         prompt();
108         while ((line = stdin.readln) !is null) {
109 
110             line = line.chomp;
111             if (isQuit(line)) return;
112             if (!checkMode(line)) {
113                 prompt();
114                 continue;
115             }
116             with (Mode) switch (state.mode) {
117                 case LINE:
118                     if (line == "clear") {
119                         writeln(ES_CLEAR);
120                     } else if (line == "beep") {
121                         writeln(ES_BEEP);
122                     } else {
123                         if (config.onInput != null) {
124                             config.onInput(line);
125                         } else {
126                             writeln("your wrote:", line);
127                         }
128                     }
129                     prompt();
130                     quit();
131                     break;
132                 case SHELL:
133                     {
134                         write(shell.send(line));
135                     }
136                     prompt();
137                     break;
138                 default:
139                     break;
140             }
141             quit();
142 
143         }
144     }
145 
146 }