souffle  2.0.2-371-g6315b36
UserInputReader.h
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2017, The Souffle Developers. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 #pragma once
10 
11 #include <iostream> // for operator<<, flush, basic_ostream, cout, ostream
12 #include <iterator> // for end, begin
13 #include <string>
14 #include <vector>
15 #include <termios.h> // for termios, tcsetattr, tcgetattr, ECHO, ICANON, cc_t
16 #include <unistd.h> // for read
17 
18 namespace souffle {
19 namespace profile {
20 
21 /*
22  * A class that reads user input a char at a time allowing for tab completion and history
23  */
24 class InputReader {
25 private:
26  std::string prompt = "Input: ";
27  std::vector<std::string> tab_completion;
28  std::vector<std::string> history;
29  std::string output;
30  char current_char = 0;
31  size_t cursor_pos = 0;
32  size_t hist_pos = 0;
33  size_t tab_pos = 0;
34  bool in_tab_complete = false;
35  bool in_history = false;
36  std::string original_hist_val;
37  std::string current_hist_val;
38  std::string current_tab_val;
39  std::string original_tab_val;
40  std::vector<std::string> current_tab_completes;
42  bool inputReceived = false;
43 
44 public:
47  clearHistory();
48  }
49 
50  InputReader(const InputReader& other) = default;
51 
53  return inputReceived;
54  }
55 
56  void readchar() {
57  char buf = 0;
58  struct termios old = {};
59  if (tcgetattr(0, &old) < 0) {
60  perror("tcsetattr()");
61  }
62  old.c_lflag &= ~ICANON;
63  old.c_lflag &= ~ECHO;
64  old.c_cc[VMIN] = 1;
65  old.c_cc[VTIME] = 0;
66  if (tcsetattr(0, TCSANOW, &old) < 0) {
67  perror("tcsetattr ICANON");
68  }
69  if (::read(0, &buf, 1) < 0) {
70  perror("read()");
71  }
72  old.c_lflag |= ICANON;
73  old.c_lflag |= ECHO;
74  if (tcsetattr(0, TCSADRAIN, &old) < 0) {
75  perror("tcsetattr ~ICANON");
76  }
77 
78  current_char = buf;
79  }
80  std::string getInput() {
81  output = "";
82  current_char = 0;
83  cursor_pos = 0;
84  hist_pos = 0;
85  tab_pos = 0;
86  in_tab_complete = false;
87  in_history = false;
88 
89  std::cout << this->prompt << std::flush;
90  readchar();
91  inputReceived = true;
92 
93  bool escaped = false;
94  bool arrow_key = false;
95 
96  while (current_char != '\n') {
97  if (arrow_key) {
99  escaped = false;
100  arrow_key = false;
101  } else if (escaped) {
102  if (current_char == '[') {
103  arrow_key = true;
104  }
105  } else {
106  if (current_char == 27) { // esc char for arrow keys
107  escaped = true;
108  } else if (current_char == '\t') {
109  tabComplete();
110  } else {
111  if (in_history) {
113  in_history = false;
114 
115  } else if (in_tab_complete) {
117  in_tab_complete = false;
118  }
119 
120  if (current_char == 127) {
121  backspace();
122  } else {
123  output.insert(output.begin() + (cursor_pos++), current_char);
124  std::cout << current_char << std::flush;
126  }
127  }
128  }
129 
130  readchar();
131  }
132 
133  if (in_history) {
134  return current_hist_val;
135  }
136  if (in_tab_complete) {
137  return current_tab_val;
138  }
139 
140  return output;
141  }
142  void setPrompt(std::string prompt) {
143  this->prompt = prompt;
144  }
145  void appendTabCompletion(std::vector<std::string> commands) {
146  tab_completion.insert(std::end(tab_completion), std::begin(commands), std::end(commands));
147  }
148  void appendTabCompletion(std::string command) {
149  tab_completion.push_back(command);
150  }
152  tab_completion = std::vector<std::string>();
153  }
154  void clearHistory() {
155  history = std::vector<std::string>();
156  }
157  void tabComplete() {
158  if (in_history) {
160  in_history = false;
161  }
162  if (!in_tab_complete) {
163  current_tab_completes = std::vector<std::string>();
165  bool found_tab = false;
166  for (auto& a : tab_completion) {
167  if (a.find(original_tab_val) == 0) {
168  current_tab_completes.push_back(a);
169  found_tab = true;
170  }
171  }
172  if (!found_tab) {
173  std::cout << '\a' << std::flush;
174  return;
175  } else {
176  in_tab_complete = true;
177  tab_pos = 0;
179  clearPrompt(output.size());
180 
181  cursor_pos = current_tab_val.size();
182  std::cout << current_tab_val << std::flush;
183  }
184  } else {
185  if (tab_pos + 1 >= current_tab_completes.size()) {
188  in_tab_complete = false;
189  cursor_pos = output.size();
190  std::cout << output << std::flush;
191  } else {
192  tab_pos++;
193 
196 
197  cursor_pos = current_tab_val.size();
198  std::cout << current_tab_val << std::flush;
199  }
200  }
201  }
202  void addHistory(std::string hist) {
203  if (history.size() > 0) {
204  // only add to history if the last command wasn't the same
205  if (hist == history.at(history.size() - 1)) {
206  return;
207  }
208  }
209  history.push_back(hist);
210  }
211  void historyUp() {
212  if (history.empty()) {
213  std::cout << '\a' << std::flush;
214  return;
215  }
216  if (in_tab_complete) {
218  in_tab_complete = false;
219  }
220  if (!in_history) {
223  in_history = true;
224  clearPrompt(output.size());
225  hist_pos = history.size() - 1;
226  current_hist_val = history.back();
227  cursor_pos = current_hist_val.size();
228  std::cout << current_hist_val << std::flush;
229  } else {
230  if (hist_pos > 0) {
231  hist_pos--;
233  current_hist_val = history.at((unsigned)hist_pos);
234  cursor_pos = current_hist_val.size();
235  std::cout << current_hist_val << std::flush;
236  } else {
237  std::cout << '\a' << std::flush;
238  }
239  }
240  }
241  void historyDown() {
242  if (in_history) {
244  if (hist_pos + 1 < history.size()) {
245  hist_pos++;
246  current_hist_val = history.at((unsigned)hist_pos);
247  cursor_pos = current_hist_val.size();
248  std::cout << current_hist_val << std::flush;
249  } else {
250  in_history = false;
252  std::cout << original_hist_val << std::flush;
253  }
254  } else {
255  std::cout << '\a' << std::flush;
256  }
257  }
258  void moveCursor(char direction) {
259  switch (direction) {
260  case 'A': historyUp(); break;
261  case 'B': historyDown(); break;
262  case 'C': moveCursorRight(); break;
263  case 'D': moveCursorLeft(); break;
264  default: break;
265  }
266  }
268  if (in_history) {
269  if (cursor_pos < current_hist_val.size()) {
270  cursor_pos++;
271  std::cout << (char)27 << '[' << 'C' << std::flush;
272  }
273  } else if (in_tab_complete) {
274  if (cursor_pos < current_tab_val.size()) {
275  cursor_pos++;
276  std::cout << (char)27 << '[' << 'C' << std::flush;
277  }
278  } else if (cursor_pos < output.size()) {
279  cursor_pos++;
280  std::cout << (char)27 << '[' << 'C' << std::flush;
281  }
282  }
283  void moveCursorLeft() {
284  if (cursor_pos > 0) {
285  cursor_pos--;
286  std::cout << (char)27 << '[' << 'D' << std::flush;
287  }
288  }
289  void backspace() {
290  if (cursor_pos > 0) {
291  output.erase(output.begin() + cursor_pos - 1);
292  moveCursorLeft();
294  }
295  }
296  void clearPrompt(size_t text_len) {
297  for (size_t i = cursor_pos; i < text_len + 1; i++) {
298  std::cout << (char)27 << "[C" << std::flush;
299  }
300  for (size_t i = 0; i < text_len + 1; i++) {
301  std::cout << "\b \b" << std::flush;
302  }
303  }
304  void showFullText(const std::string& text) {
305  clearPrompt(text.size());
306  for (char i : text) {
307  std::cout << i << std::flush;
308  }
309 
310  for (size_t i = cursor_pos; i < text.size(); i++) {
311  std::cout << "\b" << std::flush;
312  }
313  }
314 };
315 
316 } // namespace profile
317 } // namespace souffle
souffle::profile::InputReader::appendTabCompletion
void appendTabCompletion(std::string command)
Definition: UserInputReader.h:148
souffle::profile::InputReader::current_tab_completes
std::vector< std::string > current_tab_completes
Definition: UserInputReader.h:40
souffle::profile::InputReader::moveCursor
void moveCursor(char direction)
Definition: UserInputReader.h:258
souffle::profile::InputReader::in_history
bool in_history
Definition: UserInputReader.h:35
souffle::profile::InputReader::backspace
void backspace()
Definition: UserInputReader.h:289
souffle::profile::InputReader::prompt
std::string prompt
Definition: UserInputReader.h:26
souffle::profile::InputReader::appendTabCompletion
void appendTabCompletion(std::vector< std::string > commands)
Definition: UserInputReader.h:145
souffle::profile::InputReader::original_hist_cursor_pos
size_t original_hist_cursor_pos
Definition: UserInputReader.h:41
souffle::profile::InputReader::InputReader
InputReader()
Definition: UserInputReader.h:45
souffle::profile::InputReader::current_char
char current_char
Definition: UserInputReader.h:30
souffle::profile::InputReader::moveCursorRight
void moveCursorRight()
Definition: UserInputReader.h:267
souffle::profile::InputReader::hist_pos
size_t hist_pos
Definition: UserInputReader.h:32
souffle::profile::InputReader::cursor_pos
size_t cursor_pos
Definition: UserInputReader.h:31
souffle::profile::InputReader::getInput
std::string getInput()
Definition: UserInputReader.h:80
souffle::profile::InputReader::clearHistory
void clearHistory()
Definition: UserInputReader.h:154
souffle::profile::InputReader::showFullText
void showFullText(const std::string &text)
Definition: UserInputReader.h:304
souffle::profile::InputReader::tab_pos
size_t tab_pos
Definition: UserInputReader.h:33
souffle::profile::InputReader::current_hist_val
std::string current_hist_val
Definition: UserInputReader.h:37
souffle::profile::InputReader::current_tab_val
std::string current_tab_val
Definition: UserInputReader.h:38
souffle::profile::InputReader::clearPrompt
void clearPrompt(size_t text_len)
Definition: UserInputReader.h:296
i
size_t i
Definition: json11.h:663
souffle::profile::InputReader
Definition: UserInputReader.h:24
souffle::profile::InputReader::historyDown
void historyDown()
Definition: UserInputReader.h:241
souffle::profile::InputReader::original_tab_val
std::string original_tab_val
Definition: UserInputReader.h:39
souffle::profile::InputReader::setPrompt
void setPrompt(std::string prompt)
Definition: UserInputReader.h:142
souffle::profile::InputReader::historyUp
void historyUp()
Definition: UserInputReader.h:211
souffle::profile::InputReader::original_hist_val
std::string original_hist_val
Definition: UserInputReader.h:36
souffle::profile::InputReader::hasReceivedInput
bool hasReceivedInput()
Definition: UserInputReader.h:52
souffle::profile::InputReader::in_tab_complete
bool in_tab_complete
Definition: UserInputReader.h:34
souffle::profile::InputReader::history
std::vector< std::string > history
Definition: UserInputReader.h:28
souffle::profile::InputReader::tabComplete
void tabComplete()
Definition: UserInputReader.h:157
souffle::profile::InputReader::tab_completion
std::vector< std::string > tab_completion
Definition: UserInputReader.h:27
souffle::profile::InputReader::addHistory
void addHistory(std::string hist)
Definition: UserInputReader.h:202
souffle::profile::InputReader::output
std::string output
Definition: UserInputReader.h:29
souffle
Definition: AggregateOp.h:25
souffle::profile::InputReader::clearTabCompletion
void clearTabCompletion()
Definition: UserInputReader.h:151
souffle::profile::InputReader::moveCursorLeft
void moveCursorLeft()
Definition: UserInputReader.h:283
souffle::profile::InputReader::inputReceived
bool inputReceived
Definition: UserInputReader.h:42
souffle::profile::InputReader::readchar
void readchar()
Definition: UserInputReader.h:56