@@ -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
0 commit comments