| 751 | } history; |
| 752 | |
| 753 | static bool readline_advanced(std::string & line, bool multiline_input) { |
| 754 | if (out != stdout) { |
| 755 | fflush(stdout); |
| 756 | } |
| 757 | |
| 758 | line.clear(); |
| 759 | std::vector<int> widths; |
| 760 | bool is_special_char = false; |
| 761 | bool end_of_stream = false; |
| 762 | |
| 763 | size_t byte_pos = 0; // current byte index |
| 764 | size_t char_pos = 0; // current character index (one char can be multiple bytes) |
| 765 | |
| 766 | char32_t input_char; |
| 767 | while (true) { |
| 768 | assert(char_pos <= byte_pos); |
| 769 | assert(char_pos <= widths.size()); |
| 770 | auto history_prev = [&]() { |
| 771 | if (!history.is_viewing()) { |
| 772 | history.begin_viewing(line); |
| 773 | } |
| 774 | std::string new_line; |
| 775 | if (!history.prev(new_line)) { |
| 776 | return; |
| 777 | } |
| 778 | set_line_contents(new_line, line, widths, char_pos, byte_pos); |
| 779 | }; |
| 780 | auto history_next = [&]() { |
| 781 | if (history.is_viewing()) { |
| 782 | std::string new_line; |
| 783 | if (!history.next(new_line)) { |
| 784 | return; |
| 785 | } |
| 786 | set_line_contents(new_line, line, widths, char_pos, byte_pos); |
| 787 | } |
| 788 | }; |
| 789 | |
| 790 | fflush(out); // Ensure all output is displayed before waiting for input |
| 791 | input_char = getchar32(); |
| 792 | |
| 793 | if (input_char == '\r' || input_char == '\n') { |
| 794 | break; |
| 795 | } |
| 796 | |
| 797 | if (completion_cb && input_char == '\t') { |
| 798 | auto candidates = completion_cb(line, byte_pos); |
| 799 | |
| 800 | if (!candidates.empty()) { |
| 801 | if (candidates.size() > 1 || candidates[0].first != line) { |
| 802 | // TODO?: Display all candidates |
| 803 | set_line_contents(candidates[0].first, line, widths, char_pos, byte_pos, candidates[0].second); |
| 804 | } else { |
| 805 | // TODO: Move cursor to new byte_pos |
| 806 | } |
| 807 | continue; |
| 808 | } |
| 809 | } |
| 810 |
no test coverage detected