Skip to content

Commit 273e535

Browse files
committed
Add prompt to the library API
Signed-off-by: martinRenou <martin.renou@gmail.com>
1 parent ec76e8d commit 273e535

File tree

2 files changed

+115
-111
lines changed

2 files changed

+115
-111
lines changed

cpp-terminal/terminal.h

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,120 @@ class Window
711711
}
712712
};
713713

714+
// This model contains all the information about the state of the prompt in an
715+
// abstract way, irrespective of where or how it is rendered.
716+
struct Model
717+
{
718+
std::string prompt_string; // The string to show as the prompt
719+
std::string input; // The current input string in the prompt
720+
// The current cursor position in the "input" string, starting from (1,1)
721+
size_t cursor_col, cursor_row;
722+
};
723+
724+
inline std::string render(const Model &m, int prompt_row, int term_cols)
725+
{
726+
std::string out;
727+
out = cursor_off();
728+
out += move_cursor(prompt_row, 1) + m.prompt_string + m.input;
729+
size_t last_col = m.prompt_string.size() + m.input.size();
730+
for (size_t i=0; i < term_cols-last_col; i++) {
731+
out.append(" ");
732+
}
733+
out.append(move_cursor(prompt_row+m.cursor_row-1,
734+
m.prompt_string.size() + m.cursor_col));
735+
out.append(cursor_on());
736+
return out;
737+
}
738+
739+
static std::vector<std::string> PROMPT_HISTORY;
740+
741+
// Create a prompt in a given terminal.
742+
inline std::string prompt(const Terminal &term, const std::string &prompt_string,
743+
std::vector<std::string> &history = PROMPT_HISTORY)
744+
{
745+
int row, col;
746+
term.get_cursor_position(row, col);
747+
int rows, cols;
748+
term.get_term_size(rows, cols);
749+
750+
Model m;
751+
m.prompt_string = prompt_string;
752+
m.cursor_col = 1;
753+
m.cursor_row = 1;
754+
755+
// Make a local copy of history that can be modified by the user. All
756+
// changes will be forgotten once a command is submitted.
757+
std::vector<std::string> hist = history;
758+
size_t history_pos = hist.size();
759+
hist.push_back(m.input); // Push back empty input
760+
761+
int key;
762+
std::cout << render(m, row, cols) << std::flush;
763+
while ((key = term.read_key()) != Key::ENTER) {
764+
if ( (key >= 'a' && key <= 'z') ||
765+
(key >= 'A' && key <= 'Z') ||
766+
(!iscntrl(key) && key < 128) ) {
767+
std::string before = m.input.substr(0, m.cursor_col-1);
768+
std::string newchar; newchar.push_back(key);
769+
std::string after = m.input.substr(m.cursor_col-1);
770+
m.input = before + newchar + after;
771+
m.cursor_col++;
772+
} else if (key == CTRL_KEY('d')) {
773+
if (m.input.size() == 0) {
774+
m.input.push_back(CTRL_KEY('d'));
775+
break;
776+
}
777+
} else {
778+
switch (key) {
779+
case Key::BACKSPACE:
780+
if (m.cursor_col > 1) {
781+
std::string before = m.input.substr(0, m.cursor_col-2);
782+
std::string after = m.input.substr(m.cursor_col-1);
783+
m.input = before + after;
784+
m.cursor_col--;
785+
}
786+
break;
787+
case Key::ARROW_LEFT:
788+
if (m.cursor_col > 1) {
789+
m.cursor_col--;
790+
}
791+
break;
792+
case Key::ARROW_RIGHT:
793+
if (m.cursor_col <= m.input.size()) {
794+
m.cursor_col++;
795+
}
796+
break;
797+
case Key::HOME:
798+
m.cursor_col = 1;
799+
break;
800+
case Key::END:
801+
m.cursor_col = m.input.size()+1;
802+
break;
803+
case Key::ARROW_UP:
804+
if (history_pos > 0) {
805+
hist[history_pos] = m.input;
806+
history_pos--;
807+
m.input = hist[history_pos];
808+
m.cursor_col = m.input.size()+1;
809+
}
810+
break;
811+
case Key::ARROW_DOWN:
812+
if (history_pos < hist.size()-1) {
813+
hist[history_pos] = m.input;
814+
history_pos++;
815+
m.input = hist[history_pos];
816+
m.cursor_col = m.input.size()+1;
817+
}
818+
break;
819+
}
820+
}
821+
std::cout << render(m, row, cols) << std::flush;
822+
}
823+
std::cout << "\n" << std::flush;
824+
history.push_back(m.input);
825+
return m.input;
826+
}
827+
714828
} // namespace Term
715829

716830
#endif // TERMINAL_H

examples/prompt.cpp

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -3,118 +3,8 @@
33
#include <vector>
44

55
using Term::Terminal;
6-
using Term::Key;
7-
using Term::move_cursor;
8-
using Term::cursor_off;
9-
using Term::cursor_on;
6+
using Term::prompt;
107

11-
// This model contains all the information about the state of the prompt in an
12-
// abstract way, irrespective of where or how it is rendered.
13-
struct Model {
14-
std::string prompt_string; // The string to show as the prompt
15-
std::string input; // The current input string in the prompt
16-
// The current cursor position in the "input" string, starting from (1,1)
17-
size_t cursor_col, cursor_row;
18-
};
19-
20-
std::string render(const Model &m, int prompt_row, int term_cols) {
21-
std::string out;
22-
out = cursor_off();
23-
out += move_cursor(prompt_row, 1) + m.prompt_string + m.input;
24-
size_t last_col = m.prompt_string.size() + m.input.size();
25-
for (size_t i=0; i < term_cols-last_col; i++) {
26-
out.append(" ");
27-
}
28-
out.append(move_cursor(prompt_row+m.cursor_row-1,
29-
m.prompt_string.size() + m.cursor_col));
30-
out.append(cursor_on());
31-
return out;
32-
}
33-
34-
std::string prompt(const Terminal &term, const std::string &prompt_string,
35-
std::vector<std::string> &history) {
36-
int row, col;
37-
term.get_cursor_position(row, col);
38-
int rows, cols;
39-
term.get_term_size(rows, cols);
40-
41-
Model m;
42-
m.prompt_string = prompt_string;
43-
m.cursor_col = 1;
44-
m.cursor_row = 1;
45-
46-
// Make a local copy of history that can be modified by the user. All
47-
// changes will be forgotten once a command is submitted.
48-
std::vector<std::string> hist = history;
49-
size_t history_pos = hist.size();
50-
hist.push_back(m.input); // Push back empty input
51-
52-
int key;
53-
std::cout << render(m, row, cols) << std::flush;
54-
while ((key = term.read_key()) != Key::ENTER) {
55-
if ( (key >= 'a' && key <= 'z') ||
56-
(key >= 'A' && key <= 'Z') ||
57-
(!iscntrl(key) && key < 128) ) {
58-
std::string before = m.input.substr(0, m.cursor_col-1);
59-
std::string newchar; newchar.push_back(key);
60-
std::string after = m.input.substr(m.cursor_col-1);
61-
m.input = before + newchar + after;
62-
m.cursor_col++;
63-
} else if (key == CTRL_KEY('d')) {
64-
if (m.input.size() == 0) {
65-
m.input.push_back(CTRL_KEY('d'));
66-
break;
67-
}
68-
} else {
69-
switch (key) {
70-
case Key::BACKSPACE:
71-
if (m.cursor_col > 1) {
72-
std::string before = m.input.substr(0, m.cursor_col-2);
73-
std::string after = m.input.substr(m.cursor_col-1);
74-
m.input = before + after;
75-
m.cursor_col--;
76-
}
77-
break;
78-
case Key::ARROW_LEFT:
79-
if (m.cursor_col > 1) {
80-
m.cursor_col--;
81-
}
82-
break;
83-
case Key::ARROW_RIGHT:
84-
if (m.cursor_col <= m.input.size()) {
85-
m.cursor_col++;
86-
}
87-
break;
88-
case Key::HOME:
89-
m.cursor_col = 1;
90-
break;
91-
case Key::END:
92-
m.cursor_col = m.input.size()+1;
93-
break;
94-
case Key::ARROW_UP:
95-
if (history_pos > 0) {
96-
hist[history_pos] = m.input;
97-
history_pos--;
98-
m.input = hist[history_pos];
99-
m.cursor_col = m.input.size()+1;
100-
}
101-
break;
102-
case Key::ARROW_DOWN:
103-
if (history_pos < hist.size()-1) {
104-
hist[history_pos] = m.input;
105-
history_pos++;
106-
m.input = hist[history_pos];
107-
m.cursor_col = m.input.size()+1;
108-
}
109-
break;
110-
}
111-
}
112-
std::cout << render(m, row, cols) << std::flush;
113-
}
114-
std::cout << "\n" << std::flush;
115-
history.push_back(m.input);
116-
return m.input;
117-
}
1188

1199
int main() {
12010
try {

0 commit comments

Comments
 (0)