\brief Handle arrow keys (up/down/left/right)
| 754 | |
| 755 | /// \brief Handle arrow keys (up/down/left/right) |
| 756 | CLIWide::KeyAction CLIWide::handle_arrow_keys(const KEY_EVENT_RECORD& keyEvent, InputState& state) { |
| 757 | WORD vkCode = keyEvent.wVirtualKeyCode; |
| 758 | |
| 759 | if (vkCode == VK_UP) { |
| 760 | handle_history_navigation(true, state); |
| 761 | handle_multiline_navigation(true, state); |
| 762 | } else if (vkCode == VK_DOWN) { |
| 763 | handle_history_navigation(false, state); |
| 764 | handle_multiline_navigation(false, state); |
| 765 | } else if (vkCode == VK_LEFT) { |
| 766 | if (state.cursor_pos > 0) { |
| 767 | // Normal left movement within current line |
| 768 | state.cursor_pos--; |
| 769 | // Handle UTF-8 continuation bytes |
| 770 | while (state.cursor_pos > 0 && (static_cast<unsigned char>(state.lines[state.current_line_index][state.cursor_pos]) & 0xC0) == 0x80) { |
| 771 | state.cursor_pos--; |
| 772 | } |
| 773 | // Position cursor at the correct position |
| 774 | { |
| 775 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 776 | if (GetConsoleScreenBufferInfo(hConsoleOutput, &csbi)) { |
| 777 | int visual_cursor_col = state.prompt_len + get_visual_cursor_position(state.lines[state.current_line_index], state.cursor_pos); |
| 778 | COORD pos = {static_cast<SHORT>(visual_cursor_col), csbi.dwCursorPosition.Y}; |
| 779 | SetConsoleCursorPosition(hConsoleOutput, pos); |
| 780 | } |
| 781 | } |
| 782 | } else if (state.lines.size() > 1 && state.current_line_index > 0) { |
| 783 | // At beginning of line and we have multiple lines - move to end of previous line |
| 784 | state.current_line_index--; |
| 785 | state.cursor_pos = state.lines[state.current_line_index].length(); |
| 786 | state.prompt_len = (state.current_line_index == 0) ? 4 : 4; // ">>> " or "... " |
| 787 | |
| 788 | // Move cursor up one row and position it correctly |
| 789 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 790 | if (GetConsoleScreenBufferInfo(hConsoleOutput, &csbi)) { |
| 791 | SHORT new_y = static_cast<SHORT>(csbi.dwCursorPosition.Y - 1); |
| 792 | |
| 793 | // Move cursor up one row |
| 794 | COORD pos = {0, new_y}; |
| 795 | SetConsoleCursorPosition(hConsoleOutput, pos); |
| 796 | |
| 797 | // Clear and redraw the previous line |
| 798 | clear_line_from_cursor(); |
| 799 | bool is_first = (state.current_line_index == 0); |
| 800 | std::cout << (is_first ? ">>> " : "... ") << visualize_tabs(state.lines[state.current_line_index]); |
| 801 | |
| 802 | // Position cursor at the end of the previous line |
| 803 | int visual_cursor_col = state.prompt_len + get_visual_cursor_position(state.lines[state.current_line_index], state.cursor_pos); |
| 804 | COORD cursor_pos = {static_cast<SHORT>(visual_cursor_col), new_y}; |
| 805 | SetConsoleCursorPosition(hConsoleOutput, cursor_pos); |
| 806 | } |
| 807 | } |
| 808 | } else if (vkCode == VK_RIGHT) { |
| 809 | if (state.cursor_pos < state.lines[state.current_line_index].length()) { |
| 810 | // Normal right movement within current line |
| 811 | state.cursor_pos++; |
| 812 | // Handle UTF-8 continuation bytes |
| 813 | while (state.cursor_pos < state.lines[state.current_line_index].length() && (static_cast<unsigned char>(state.lines[state.current_line_index][state.cursor_pos]) & 0xC0) == 0x80) { |