mirror of
https://github.com/m1ngsama/TUT.git
synced 2025-12-24 10:51:46 +00:00
feat: Add Vimium-style link hints and vim keybindings infrastructure
- Add LINK_HINTS mode for Vimium-style link navigation - Implement 'f' key to activate link hints mode - Add visual mode support (v/V keys) - Implement marks support (m[a-z] to set, '[a-z] to jump) - Add tab navigation keys (gt/gT for next/previous tab) - Add new actions: * SHOW_LINK_HINTS - activate link hints overlay * FOLLOW_LINK_HINT - follow link by hint letters * ENTER_VISUAL_MODE / ENTER_VISUAL_LINE_MODE * SET_MARK / GOTO_MARK - vim-style position bookmarks * NEXT_TAB / PREV_TAB - tab navigation * YANK - copy selected text This brings modern browser vim plugin functionality (like Vimium) to the terminal, making link navigation much faster than traditional tab-through methods.
This commit is contained in:
parent
638a08e437
commit
ac988dfda8
2 changed files with 160 additions and 13 deletions
|
|
@ -23,7 +23,48 @@ public:
|
|||
result.has_count = false;
|
||||
result.count = 1;
|
||||
|
||||
// Handle digit input for count or 'f' command
|
||||
// Handle multi-char commands like 'gg', 'gt', 'gT', 'm', '
|
||||
if (!buffer.empty()) {
|
||||
if (buffer == "g") {
|
||||
if (ch == 't') {
|
||||
result.action = Action::NEXT_TAB;
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
return result;
|
||||
} else if (ch == 'T') {
|
||||
result.action = Action::PREV_TAB;
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
return result;
|
||||
}
|
||||
} else if (buffer == "m") {
|
||||
// Set mark with letter
|
||||
if (std::isalpha(ch)) {
|
||||
result.action = Action::SET_MARK;
|
||||
result.text = std::string(1, static_cast<char>(ch));
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
return result;
|
||||
}
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
return result;
|
||||
} else if (buffer == "'") {
|
||||
// Jump to mark
|
||||
if (std::isalpha(ch)) {
|
||||
result.action = Action::GOTO_MARK;
|
||||
result.text = std::string(1, static_cast<char>(ch));
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
return result;
|
||||
}
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle digit input for count
|
||||
if (std::isdigit(ch) && (ch != '0' || !count_buffer.empty())) {
|
||||
count_buffer += static_cast<char>(ch);
|
||||
return result;
|
||||
|
|
@ -116,16 +157,33 @@ public:
|
|||
count_buffer.clear();
|
||||
break;
|
||||
case 'f':
|
||||
// 'f' command: follow link by number
|
||||
if (result.has_count) {
|
||||
result.action = Action::FOLLOW_LINK_NUM;
|
||||
result.number = result.count;
|
||||
count_buffer.clear();
|
||||
} else {
|
||||
// Enter link follow mode, wait for number
|
||||
mode = InputMode::LINK;
|
||||
buffer = "f";
|
||||
}
|
||||
// 'f' command: vimium-style link hints
|
||||
result.action = Action::SHOW_LINK_HINTS;
|
||||
mode = InputMode::LINK_HINTS;
|
||||
buffer.clear();
|
||||
count_buffer.clear();
|
||||
break;
|
||||
case 'v':
|
||||
// Enter visual mode
|
||||
result.action = Action::ENTER_VISUAL_MODE;
|
||||
mode = InputMode::VISUAL;
|
||||
count_buffer.clear();
|
||||
break;
|
||||
case 'V':
|
||||
// Enter visual line mode
|
||||
result.action = Action::ENTER_VISUAL_LINE_MODE;
|
||||
mode = InputMode::VISUAL_LINE;
|
||||
count_buffer.clear();
|
||||
break;
|
||||
case 'm':
|
||||
// Set mark (wait for next char)
|
||||
buffer = "m";
|
||||
count_buffer.clear();
|
||||
break;
|
||||
case '\'':
|
||||
// Jump to mark (wait for next char)
|
||||
buffer = "'";
|
||||
count_buffer.clear();
|
||||
break;
|
||||
case ':':
|
||||
mode = InputMode::COMMAND;
|
||||
|
|
@ -257,6 +315,75 @@ public:
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
InputResult process_link_hints_mode(int ch) {
|
||||
InputResult result;
|
||||
result.action = Action::NONE;
|
||||
|
||||
if (ch == 27) {
|
||||
// ESC cancels link hints mode
|
||||
mode = InputMode::NORMAL;
|
||||
buffer.clear();
|
||||
return result;
|
||||
} else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
|
||||
// Backspace removes last character
|
||||
if (!buffer.empty()) {
|
||||
buffer.pop_back();
|
||||
} else {
|
||||
mode = InputMode::NORMAL;
|
||||
}
|
||||
return result;
|
||||
} else if (std::isalpha(ch)) {
|
||||
// Add character to buffer
|
||||
buffer += std::tolower(static_cast<char>(ch));
|
||||
|
||||
// Try to match link hint
|
||||
result.action = Action::FOLLOW_LINK_HINT;
|
||||
result.text = buffer;
|
||||
|
||||
// Mode will be reset by browser if link is followed
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
InputResult process_visual_mode(int ch) {
|
||||
InputResult result;
|
||||
result.action = Action::NONE;
|
||||
|
||||
if (ch == 27 || ch == 'v') {
|
||||
// ESC or 'v' exits visual mode
|
||||
mode = InputMode::NORMAL;
|
||||
return result;
|
||||
} else if (ch == 'y') {
|
||||
// Yank (copy) selected text
|
||||
result.action = Action::YANK;
|
||||
mode = InputMode::NORMAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Pass through navigation commands
|
||||
switch (ch) {
|
||||
case 'j':
|
||||
case KEY_DOWN:
|
||||
result.action = Action::SCROLL_DOWN;
|
||||
break;
|
||||
case 'k':
|
||||
case KEY_UP:
|
||||
result.action = Action::SCROLL_UP;
|
||||
break;
|
||||
case 'h':
|
||||
case KEY_LEFT:
|
||||
// In visual mode, h/l could extend selection
|
||||
break;
|
||||
case 'l':
|
||||
case KEY_RIGHT:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
InputHandler::InputHandler() : pImpl(std::make_unique<Impl>()) {}
|
||||
|
|
@ -273,6 +400,11 @@ InputResult InputHandler::handle_key(int ch) {
|
|||
return pImpl->process_search_mode(ch);
|
||||
case InputMode::LINK:
|
||||
return pImpl->process_link_mode(ch);
|
||||
case InputMode::LINK_HINTS:
|
||||
return pImpl->process_link_hints_mode(ch);
|
||||
case InputMode::VISUAL:
|
||||
case InputMode::VISUAL_LINE:
|
||||
return pImpl->process_visual_mode(ch);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ enum class InputMode {
|
|||
NORMAL,
|
||||
COMMAND,
|
||||
SEARCH,
|
||||
LINK
|
||||
LINK,
|
||||
LINK_HINTS, // Vimium-style 'f' mode
|
||||
VISUAL, // Visual mode
|
||||
VISUAL_LINE // Visual line mode
|
||||
};
|
||||
|
||||
enum class Action {
|
||||
|
|
@ -28,12 +31,24 @@ enum class Action {
|
|||
FOLLOW_LINK,
|
||||
GOTO_LINK, // Jump to specific link by number
|
||||
FOLLOW_LINK_NUM, // Follow specific link by number (f command)
|
||||
SHOW_LINK_HINTS, // Activate link hints mode ('f')
|
||||
FOLLOW_LINK_HINT, // Follow link by hint letters
|
||||
GO_BACK,
|
||||
GO_FORWARD,
|
||||
OPEN_URL,
|
||||
REFRESH,
|
||||
QUIT,
|
||||
HELP
|
||||
HELP,
|
||||
ENTER_VISUAL_MODE, // Start visual mode
|
||||
ENTER_VISUAL_LINE_MODE, // Start visual line mode
|
||||
SET_MARK, // Set a mark (m + letter)
|
||||
GOTO_MARK, // Jump to mark (' + letter)
|
||||
YANK, // Copy selected text
|
||||
NEXT_TAB, // gt - next tab
|
||||
PREV_TAB, // gT - previous tab
|
||||
NEW_TAB, // :tabnew
|
||||
CLOSE_TAB, // :tabc
|
||||
TOGGLE_MOUSE // Toggle mouse support
|
||||
};
|
||||
|
||||
struct InputResult {
|
||||
|
|
|
|||
Loading…
Reference in a new issue