c-repl/src/input.cpp

148 lines
5.0 KiB
C++

#include <string>
#include <vector>
#include <iomanip> // for setfill
#include <iostream>
#include <unistd.h> // for STDIN_FILENO
#include <termios.h> // for tc(get/set)attr
using namespace std;
#include "include/config.h"
#include "include/colors.h"
char silent_get_char() { // Get char without echoing it
struct termios oldt, newt;
char ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
void show_prompt(int line_number) {
cout << BOLD << setw(4) << setfill(' ') << line_number << " " RESET;
}
string get_line_with_hist(vector<string> history, int line_num) {
show_prompt(line_num);
string input;
int history_index = history.size();
int input_index = 0;
char ch = 0;
while (ch != '\n') {
ch = silent_get_char();
if (ch == 4 || ch == EOF) { // 4 is End Of Transmission which can also be received
cout << "\rReceived EOF\033[K" << endl;
exit(0);
}
if (ch == 27) { // Special sequences
silent_get_char();
ch = silent_get_char();
switch (ch) {
case 'A': { //* Up arrow
if (history_index > 0) {
history_index--;
cout << "\r\033[K"; // Clear the end of the line
show_prompt(line_num);
cout << history[history_index];
input = history[history_index];
input_index = input.size();
}
break;
} case 'B': { //* Down arrow
if (history_index < (int)history.size() - 1) {
history_index++;
cout << "\r\033[K"; // Clear the end of the line
show_prompt(line_num);
cout << history[history_index];
input = history[history_index];
input_index = input.size();
}
break;
} case 'D': { //* Left arrow
if (input_index > 0) {
input_index--;
cout << "\033[1D"; // Go left 1
}
break;
} case 'C': { //* Right arrow
if (input_index < (int)input.size()) {
input_index++;
cout << "\033[1C"; // Go right 1
}
break;
} case '3': { //* Suppr
silent_get_char(); // Remove last printed char
if (input_index < (int)input.length()) {
input.erase(input_index, 1);
cout << "\033[K" << input.substr(input_index);
if (input_index < (int)input.size())
cout << "\033[" << input.size() - input_index << "D";
}
break;
} case 'H': { //* Begin
if (input_index > 0) {
cout << "\033[" << input_index << "D"; // Go left input_index
input_index = 0;
}
break;
} case 'F': { //* End
if (input_index < (int)input.size()) {
cout << "\033[" << (input.size() - input_index) << "C"; // Go right input.size() - input_index
input_index = input.size();
}
break;
}
}
} else if (ch == '\b' || ch == 127) { //* Backspace
if (input_index > 0) {
input.erase(input_index-1, 1);
input_index--;
cout << "\033[1D\033[K"; // Go left 1, clear the end of the line
cout << input.substr(input_index); // Print the input after the deleted character
if (input_index < (int)input.size())
cout << "\033[" << input.size() - input_index << "D"; // Place the cursor back in the right position
}
} else if (ch != '\n') {
input.insert(input_index, 1, ch);
cout << input.substr(input_index);
input_index++;
if (input_index < (int)input.size())
cout << "\033[" << input.size() - input_index << "D"; // Place the cursor back in the right position
}
}
cout << endl;
return input;
}
vector<string> get_input(vector<string> input) {
int line_num = input.size();
while (1) {
line_num++;
input.push_back(get_line_with_hist(input, line_num));
int n = input.back().length();
if (n >= 2 && input.back()[n-1] == ';' && input.back()[n-2] == ';') {
input.back().erase(n-1, 1);
break;
}
}
return input;
}