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