keybinding.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. #include "lumacs/keybinding.hpp"
  2. #include "lumacs/command_system.hpp" // Include for CommandSystem
  3. #include <sstream>
  4. #include <algorithm>
  5. #include <cctype>
  6. namespace lumacs {
  7. // ============================================================================
  8. // Key Implementation
  9. // ============================================================================
  10. namespace {
  11. // Helper to convert string to BaseKey
  12. BaseKey string_to_base_key(const std::string& s) {
  13. if (s.length() == 1) {
  14. char c = s[0];
  15. if (c >= 'a' && c <= 'z') return static_cast<BaseKey>(static_cast<int>(BaseKey::A) + (c - 'a'));
  16. if (c >= 'A' && c <= 'Z') return static_cast<BaseKey>(static_cast<int>(BaseKey::A) + (c - 'A'));
  17. if (c >= '0' && c <= '9') return static_cast<BaseKey>(static_cast<int>(BaseKey::D0) + (c - '0'));
  18. }
  19. if (s == "Space") return BaseKey::Space;
  20. if (s == "Return") return BaseKey::Return;
  21. if (s == "Tab") return BaseKey::Tab;
  22. if (s == "Escape") return BaseKey::Escape;
  23. if (s == "Backspace") return BaseKey::Backspace;
  24. if (s == "Delete") return BaseKey::Delete;
  25. if (s == "ArrowUp") return BaseKey::ArrowUp;
  26. if (s == "ArrowDown") return BaseKey::ArrowDown;
  27. if (s == "ArrowLeft") return BaseKey::ArrowLeft;
  28. if (s == "ArrowRight") return BaseKey::ArrowRight;
  29. if (s == "Home") return BaseKey::Home;
  30. if (s == "End") return BaseKey::End;
  31. if (s == "PageUp") return BaseKey::PageUp;
  32. if (s == "PageDown") return BaseKey::PageDown;
  33. if (s == "Insert") return BaseKey::Insert;
  34. if (s == ";") return BaseKey::Semicolon;
  35. if (s == "=") return BaseKey::Equal;
  36. if (s == ",") return BaseKey::Comma;
  37. if (s == "-") return BaseKey::Minus;
  38. if (s == ".") return BaseKey::Period;
  39. if (s == "/") return BaseKey::Slash;
  40. if (s == "`") return BaseKey::Backtick;
  41. if (s == "[") return BaseKey::LeftBracket;
  42. if (s == "\\") return BaseKey::Backslash;
  43. if (s == "]") return BaseKey::RightBracket;
  44. if (s == "'") return BaseKey::Quote;
  45. // Add more keys as needed
  46. return BaseKey::Unknown;
  47. }
  48. // Helper to convert BaseKey to string
  49. std::string base_key_to_string(BaseKey bk) {
  50. if (bk >= BaseKey::A && bk <= BaseKey::Z) return std::string(1, static_cast<char>(static_cast<int>(bk) - static_cast<int>(BaseKey::A) + 'a'));
  51. if (bk >= BaseKey::D0 && bk <= BaseKey::D9) return std::string(1, static_cast<char>(static_cast<int>(bk) - static_cast<int>(BaseKey::D0) + '0'));
  52. switch (bk) {
  53. case BaseKey::Space: return "Space";
  54. case BaseKey::Return: return "Return";
  55. case BaseKey::Tab: return "Tab";
  56. case BaseKey::Escape: return "Escape";
  57. case BaseKey::Backspace: return "Backspace";
  58. case BaseKey::Delete: return "Delete";
  59. case BaseKey::ArrowUp: return "ArrowUp";
  60. case BaseKey::ArrowDown: return "ArrowDown";
  61. case BaseKey::ArrowLeft: return "ArrowLeft";
  62. case BaseKey::ArrowRight: return "ArrowRight";
  63. case BaseKey::Home: return "Home";
  64. case BaseKey::End: return "End";
  65. case BaseKey::PageUp: return "PageUp";
  66. case BaseKey::PageDown: return "PageDown";
  67. case BaseKey::Insert: return "Insert";
  68. case BaseKey::F1: return "F1"; // Handle F keys properly
  69. case BaseKey::F2: return "F2";
  70. case BaseKey::F3: return "F3";
  71. case BaseKey::F4: return "F4";
  72. case BaseKey::F5: return "F5";
  73. case BaseKey::F6: return "F6";
  74. case BaseKey::F7: return "F7";
  75. case BaseKey::F8: return "F8";
  76. case BaseKey::F9: return "F9";
  77. case BaseKey::F10: return "F10";
  78. case BaseKey::F11: return "F11";
  79. case BaseKey::F12: return "F12";
  80. case BaseKey::Semicolon: return ";";
  81. case BaseKey::Equal: return "=";
  82. case BaseKey::Comma: return ",";
  83. case BaseKey::Minus: return "-";
  84. case BaseKey::Period: return ".";
  85. case BaseKey::Slash: return "/";
  86. case BaseKey::Backtick: return "`";
  87. case BaseKey::LeftBracket: return "[";
  88. case BaseKey::Backslash: return "\\";
  89. case BaseKey::RightBracket: return "]";
  90. case BaseKey::Quote: return "'";
  91. default: return "Unknown";
  92. }
  93. }
  94. } // anonymous namespace
  95. Key::Key(const std::string& key_name_str) {
  96. *this = parse(key_name_str);
  97. }
  98. Key Key::parse(const std::string& key_str) {
  99. Key key;
  100. std::string remaining = key_str;
  101. // Parse modifiers
  102. while (true) {
  103. if (remaining.length() >= 2 && remaining.substr(0, 2) == "C-") {
  104. key.modifiers = static_cast<Modifier>(static_cast<uint16_t>(key.modifiers) | static_cast<uint16_t>(Modifier::Control));
  105. remaining = remaining.substr(2);
  106. } else if (remaining.length() >= 2 && (remaining.substr(0, 2) == "M-" || remaining.substr(0, 2) == "A-")) {
  107. key.modifiers = static_cast<Modifier>(static_cast<uint16_t>(key.modifiers) | static_cast<uint16_t>(Modifier::Meta));
  108. remaining = remaining.substr(2);
  109. } else if (remaining.length() >= 2 && remaining.substr(0, 2) == "S-") {
  110. key.modifiers = static_cast<Modifier>(static_cast<uint16_t>(key.modifiers) | static_cast<uint16_t>(Modifier::Shift));
  111. remaining = remaining.substr(2);
  112. } else {
  113. break;
  114. }
  115. }
  116. key.base_key = string_to_base_key(remaining);
  117. // If Shift is pressed and the base key is a letter, convert base_key to uppercase if necessary,
  118. // but the BaseKey enum already handles 'a' through 'z' as lowercase by default for simplicity
  119. // If the original input was 'S-a', string_to_base_key("a") gives BaseKey::A.
  120. // So the explicit shift modifier should be enough.
  121. return key;
  122. }
  123. std::string Key::to_string() const {
  124. std::string result;
  125. if (static_cast<uint16_t>(modifiers) & static_cast<uint16_t>(Modifier::Control)) result += "C-";
  126. if (static_cast<uint16_t>(modifiers) & static_cast<uint16_t>(Modifier::Meta)) result += "M-";
  127. if (static_cast<uint16_t>(modifiers) & static_cast<uint16_t>(Modifier::Shift)) result += "S-";
  128. result += base_key_to_string(base_key);
  129. return result;
  130. }
  131. bool Key::operator==(const Key& other) const {
  132. return base_key == other.base_key && modifiers == other.modifiers;
  133. }
  134. bool Key::operator<(const Key& other) const {
  135. if (base_key != other.base_key) return base_key < other.base_key;
  136. return modifiers < other.modifiers;
  137. }
  138. // ============================================================================
  139. // KeySequence Implementation
  140. // ============================================================================
  141. KeySequence::KeySequence(const std::vector<Key>& keys) : keys_(keys) {
  142. }
  143. KeySequence::KeySequence(const std::string& key_sequence_str) {
  144. if (key_sequence_str.empty()) {
  145. return;
  146. }
  147. // Split by spaces, but handle quoted strings
  148. std::istringstream iss(key_sequence_str);
  149. std::string token;
  150. while (iss >> token) {
  151. keys_.emplace_back(Key::parse(token));
  152. }
  153. }
  154. void KeySequence::add_key(const Key& key) {
  155. keys_.push_back(key);
  156. }
  157. void KeySequence::add_key(const std::string& key_str) {
  158. keys_.emplace_back(Key::parse(key_str));
  159. }
  160. bool KeySequence::is_prefix_of(const KeySequence& other) const {
  161. if (keys_.size() > other.keys_.size()) {
  162. return false;
  163. }
  164. return std::equal(keys_.begin(), keys_.end(), other.keys_.begin());
  165. }
  166. bool KeySequence::has_prefix(const KeySequence& prefix) const {
  167. return prefix.is_prefix_of(*this);
  168. }
  169. std::string KeySequence::to_string() const {
  170. if (keys_.empty()) {
  171. return "";
  172. }
  173. std::string result = keys_[0].to_string();
  174. for (size_t i = 1; i < keys_.size(); ++i) {
  175. result += " " + keys_[i].to_string();
  176. }
  177. return result;
  178. }
  179. void KeySequence::clear() {
  180. keys_.clear();
  181. }
  182. KeySequence KeySequence::subsequence(size_t start_index) const {
  183. if (start_index >= keys_.size()) {
  184. return KeySequence();
  185. }
  186. std::vector<Key> sub_keys(keys_.begin() + start_index, keys_.end());
  187. return KeySequence(sub_keys);
  188. }
  189. bool KeySequence::operator==(const KeySequence& other) const {
  190. return keys_ == other.keys_;
  191. }
  192. bool KeySequence::operator<(const KeySequence& other) const {
  193. return keys_ < other.keys_;
  194. }
  195. // ============================================================================
  196. // KeyBinding Implementation
  197. // ============================================================================
  198. KeyBinding::KeyBinding(const KeySequence& seq, std::string cmd_name, std::string desc)
  199. : sequence(seq), command_name(std::move(cmd_name)), description(std::move(desc)) {
  200. }
  201. KeyBinding::KeyBinding(const std::string& key_str, std::string cmd_name, std::string desc)
  202. : sequence(key_str), command_name(std::move(cmd_name)), description(std::move(desc)) {
  203. }
  204. // ============================================================================
  205. // KeyBindingManager Implementation
  206. // ============================================================================
  207. KeyBindingManager::KeyBindingManager(CommandSystem* command_system)
  208. : root_(std::make_unique<TrieNode>()), // Initialize root_
  209. sequence_timeout_(std::chrono::milliseconds(1000)),
  210. command_system_(command_system) {
  211. }
  212. void KeyBindingManager::bind(const KeySequence& sequence, std::string cmd_name, const std::string& description) {
  213. if (sequence.empty() || cmd_name.empty()) {
  214. return;
  215. }
  216. TrieNode* current = root_.get();
  217. for (const Key& key : sequence.keys()) {
  218. if (current->children.find(key) == current->children.end()) {
  219. current->children[key] = std::make_unique<TrieNode>();
  220. }
  221. current = current->children[key].get();
  222. current->ref_count++; // Increment ref count for this path
  223. }
  224. current->binding = KeyBinding(sequence, std::move(cmd_name), description);
  225. }
  226. void KeyBindingManager::bind(const std::string& key_str, std::string cmd_name, const std::string& description) {
  227. bind(KeySequence(key_str), std::move(cmd_name), description);
  228. }
  229. void KeyBindingManager::unbind(const KeySequence& sequence) {
  230. if (sequence.empty()) return;
  231. // Find the node corresponding to the end of the sequence
  232. TrieNode* current = root_.get();
  233. std::vector<TrieNode*> path_nodes; // To decrement ref_counts later
  234. path_nodes.push_back(current);
  235. for (const Key& key : sequence.keys()) {
  236. if (current->children.find(key) == current->children.end()) {
  237. return; // Sequence not found
  238. }
  239. current = current->children[key].get();
  240. path_nodes.push_back(current);
  241. }
  242. if (!current->binding) {
  243. return; // No exact binding at this node
  244. }
  245. // Remove the binding
  246. current->binding.reset();
  247. // Decrement ref counts along the path, and remove nodes if ref_count becomes 0
  248. // Traverse backwards from the node just before the binding
  249. for (int i = static_cast<int>(sequence.length()) - 1; i >= 0; --i) {
  250. TrieNode* parent = path_nodes[i];
  251. TrieNode* child = path_nodes[i+1];
  252. child->ref_count--; // Decrement ref count
  253. // If child's ref_count is 0 AND it has no own binding, we can remove it
  254. if (child->ref_count == 0 && !child->binding.has_value() && child->children.empty()) {
  255. parent->children.erase(sequence.keys()[i]);
  256. } else {
  257. // Stop if the node is still referenced or has a binding/children
  258. break;
  259. }
  260. }
  261. }
  262. void KeyBindingManager::unbind(const std::string& key_str) {
  263. unbind(KeySequence(key_str));
  264. }
  265. KeyProcessingResult KeyBindingManager::process_key(const Key& key) {
  266. // Check for timeout first
  267. if (is_building_sequence() && has_sequence_timed_out()) {
  268. clear_partial_sequence();
  269. // Fall through to process this key as a new sequence
  270. }
  271. // Add this key to the current sequence
  272. current_sequence_.add_key(key);
  273. // Update timestamp
  274. sequence_start_time_ = std::chrono::steady_clock::now();
  275. // Check for exact binding
  276. std::optional<KeyBinding> exact_binding = find_exact_binding(current_sequence_);
  277. if (exact_binding.has_value()) {
  278. // Found exact match
  279. clear_partial_sequence();
  280. try {
  281. // Execute the command via the CommandSystem
  282. if (command_system_) {
  283. CommandResult cmd_result = command_system_->execute(exact_binding->command_name, {}); // No args for now
  284. return {cmd_result.success ? KeyResult::Executed : KeyResult::Failed, cmd_result};
  285. }
  286. return {KeyResult::Failed, CommandResult{false, "No CommandSystem available"}};
  287. } catch (const std::exception& e) {
  288. return {KeyResult::Failed, CommandResult{false, std::string("Command execution failed: ") + e.what()}};
  289. } catch (...) {
  290. return {KeyResult::Failed, CommandResult{false, "Command execution failed with unknown error"}};
  291. }
  292. }
  293. // Check if this could be a prefix for other bindings
  294. if (has_prefix_bindings_impl(current_sequence_)) {
  295. return {KeyResult::Partial, std::nullopt};
  296. }
  297. // No binding found, clear sequence and return unbound
  298. clear_partial_sequence();
  299. return {KeyResult::Unbound, std::nullopt};
  300. }
  301. KeyProcessingResult KeyBindingManager::process_key(const std::string& key_str) {
  302. return process_key(Key::parse(key_str));
  303. }
  304. bool KeyBindingManager::has_binding(const KeySequence& sequence) const {
  305. // A sequence has a binding if it's an exact binding or a prefix for other bindings
  306. TrieNode* node = find_node(sequence);
  307. if (!node) return false;
  308. return node->binding.has_value() || !node->children.empty();
  309. }
  310. bool KeyBindingManager::has_exact_binding(const KeySequence& sequence) const {
  311. TrieNode* node = find_node(sequence);
  312. return node && node->binding.has_value();
  313. }
  314. bool KeyBindingManager::has_prefix_bindings(const KeySequence& sequence) const {
  315. TrieNode* node = find_node(sequence);
  316. return node && !node->children.empty();
  317. }
  318. void KeyBindingManager::clear_partial_sequence() {
  319. current_sequence_.clear();
  320. sequence_start_time_ = std::chrono::steady_clock::time_point{};
  321. }
  322. bool KeyBindingManager::is_building_sequence() const {
  323. return !current_sequence_.empty();
  324. }
  325. std::string KeyBindingManager::current_sequence_display() const {
  326. if (current_sequence_.empty()) {
  327. return "";
  328. }
  329. return current_sequence_.to_string() + "-";
  330. }
  331. std::vector<KeyBinding> KeyBindingManager::get_all_bindings() const {
  332. std::vector<KeyBinding> result;
  333. collect_bindings(root_.get(), KeySequence(), result);
  334. return result;
  335. }
  336. void KeyBindingManager::set_sequence_timeout(std::chrono::milliseconds timeout) {
  337. sequence_timeout_ = timeout;
  338. }
  339. bool KeyBindingManager::has_sequence_timed_out() const {
  340. if (!is_building_sequence()) {
  341. return false;
  342. }
  343. auto now = std::chrono::steady_clock::now();
  344. return (now - sequence_start_time_) > sequence_timeout_;
  345. }
  346. // Helper function to find a node in the trie
  347. KeyBindingManager::TrieNode* KeyBindingManager::find_node(const KeySequence& sequence) const {
  348. TrieNode* current = root_.get();
  349. for (const Key& key : sequence.keys()) {
  350. if (current->children.find(key) == current->children.end()) {
  351. return nullptr;
  352. }
  353. current = current->children[key].get();
  354. }
  355. return current;
  356. }
  357. // Helper to traverse trie and collect all bindings
  358. void KeyBindingManager::collect_bindings(TrieNode* node, KeySequence current_prefix, std::vector<KeyBinding>& result) const {
  359. if (!node) return;
  360. if (node->binding.has_value()) {
  361. result.push_back(node->binding.value());
  362. }
  363. for (const auto& pair : node->children) {
  364. KeySequence next_prefix = current_prefix;
  365. next_prefix.add_key(pair.first);
  366. collect_bindings(pair.second.get(), next_prefix, result);
  367. }
  368. }
  369. std::optional<KeyBinding> KeyBindingManager::find_exact_binding(const KeySequence& sequence) const {
  370. TrieNode* node = find_node(sequence);
  371. if (node && node->binding.has_value()) {
  372. return node->binding;
  373. }
  374. return std::nullopt;
  375. }
  376. bool KeyBindingManager::has_prefix_bindings_impl(const KeySequence& sequence) const {
  377. TrieNode* node = find_node(sequence);
  378. return node && !node->children.empty();
  379. }
  380. } // namespace lumacs
  381. } // namespace lumacs