VimEditingEngine.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <LibGUI/Event.h>
  27. #include <LibGUI/TextEditor.h>
  28. #include <LibGUI/VimEditingEngine.h>
  29. namespace GUI {
  30. CursorWidth VimEditingEngine::cursor_width() const
  31. {
  32. return m_vim_mode == VimMode::Insert ? CursorWidth::NARROW : CursorWidth::WIDE;
  33. }
  34. bool VimEditingEngine::on_key(const KeyEvent& event)
  35. {
  36. if (EditingEngine::on_key(event))
  37. return true;
  38. switch (m_vim_mode) {
  39. case (VimMode::Insert):
  40. return on_key_in_insert_mode(event);
  41. case (VimMode::Visual):
  42. return on_key_in_visual_mode(event);
  43. case (VimMode::Normal):
  44. return on_key_in_normal_mode(event);
  45. default:
  46. ASSERT_NOT_REACHED();
  47. }
  48. return false;
  49. }
  50. bool VimEditingEngine::on_key_in_insert_mode(const KeyEvent& event)
  51. {
  52. if (event.key() == KeyCode::Key_Escape || (event.ctrl() && event.key() == KeyCode::Key_LeftBracket) || (event.ctrl() && event.key() == KeyCode::Key_C)) {
  53. switch_to_normal_mode();
  54. return true;
  55. }
  56. return false;
  57. }
  58. bool VimEditingEngine::on_key_in_normal_mode(const KeyEvent& event)
  59. {
  60. // FIXME: changing or deleting word methods don't correctly support 1 letter words.
  61. // For example, in the line 'return 0;' with the cursor on the '0',
  62. // keys 'cw' will move to the delete '0;' rather than just the '0'.
  63. // FIXME: Changing or deleting the last word on a line will bring the next line
  64. // up to the cursor.
  65. if (m_previous_key == KeyCode::Key_D) {
  66. if (event.key() == KeyCode::Key_D) {
  67. yank(Line);
  68. delete_line();
  69. } else if (event.key() == KeyCode::Key_W) {
  70. // If the current char is an alnum or punct, delete from said char, to the
  71. // beginning of the next word including any whitespace in between the two words.
  72. // If the current char is whitespace, delete from the cursor to the beginning of the next world.
  73. u32 current_char = m_editor->current_line().view().code_points()[m_editor->cursor().column()];
  74. TextPosition delete_to;
  75. if (isspace(current_char)) {
  76. delete_to = find_beginning_of_next_word();
  77. } else {
  78. delete_to = find_end_of_next_word();
  79. delete_to.set_column(delete_to.column() + 1);
  80. }
  81. m_editor->delete_text_range(TextRange(m_editor->cursor(), delete_to).normalized());
  82. } else if (event.key() == KeyCode::Key_B) {
  83. // Will delete from the current char to the beginning of the previous word regardless of whitespace.
  84. // Does not delete the starting char, see note below.
  85. TextPosition delete_to = find_beginning_of_previous_word();
  86. // NOTE: Intentionally don't adjust m_editor->cursor() for the wide cursor's column
  87. // because in the original vim... they don't.
  88. m_editor->delete_text_range(TextRange(delete_to, m_editor->cursor()).normalized());
  89. } else if (event.key() == KeyCode::Key_E) {
  90. // Delete from the current char to the end of the next word regardless of whitespace.
  91. TextPosition delete_to = find_end_of_next_word();
  92. delete_to.set_column(delete_to.column() + 1);
  93. m_editor->delete_text_range(TextRange(m_editor->cursor(), delete_to).normalized());
  94. }
  95. m_previous_key = {};
  96. } else if (m_previous_key == KeyCode::Key_G) {
  97. if (event.key() == KeyCode::Key_G) {
  98. move_to_first_line();
  99. } else if (event.key() == KeyCode::Key_E) {
  100. move_to_end_of_previous_word();
  101. }
  102. m_previous_key = {};
  103. } else if (m_previous_key == KeyCode::Key_Y) {
  104. if (event.key() == KeyCode::Key_Y) {
  105. yank(Line);
  106. }
  107. m_previous_key = {};
  108. } else if (m_previous_key == KeyCode::Key_C) {
  109. if (event.key() == KeyCode::Key_C) {
  110. // Needed because the code to replace the deleted line is called after delete_line() so
  111. // what was the second last line before the delete, is now the last line.
  112. bool was_second_last_line = m_editor->cursor().line() == m_editor->line_count() - 2;
  113. yank(Line);
  114. delete_line();
  115. if (was_second_last_line || (m_editor->cursor().line() != 0 && m_editor->cursor().line() != m_editor->line_count() - 1)) {
  116. move_one_up(event);
  117. move_to_line_end(event);
  118. m_editor->add_code_point(0x0A);
  119. } else if (m_editor->cursor().line() == 0) {
  120. move_to_line_beginning(event);
  121. m_editor->add_code_point(0x0A);
  122. move_one_up(event);
  123. } else if (m_editor->cursor().line() == m_editor->line_count() - 1) {
  124. m_editor->add_code_point(0x0A);
  125. }
  126. switch_to_insert_mode();
  127. } else if (event.key() == KeyCode::Key_W) {
  128. // Delete to the end of the next word, if in the middle of a word, this will delete
  129. // from the cursor to the said of said word. If the cursor is on whitespace,
  130. // any whitespace between the cursor and the beginning of the next word will be deleted.
  131. u32 current_char = m_editor->current_line().view().code_points()[m_editor->cursor().column()];
  132. TextPosition delete_to;
  133. if (isspace(current_char)) {
  134. delete_to = find_beginning_of_next_word();
  135. } else {
  136. delete_to = find_end_of_next_word();
  137. delete_to.set_column(delete_to.column() + 1);
  138. }
  139. m_editor->delete_text_range(TextRange(m_editor->cursor(), delete_to).normalized());
  140. switch_to_insert_mode();
  141. } else if (event.key() == KeyCode::Key_B) {
  142. // Delete to the beginning of the previous word, if in the middle of a word, this will delete
  143. // from the cursor to the beginning of said word. If the cursor is on whitespace
  144. // it, and the previous word will be deleted.
  145. u32 current_char = m_editor->current_line().view().code_points()[m_editor->cursor().column()];
  146. TextPosition delete_to = find_beginning_of_previous_word();
  147. TextPosition adjusted_cursor = m_editor->cursor();
  148. // Adjust cursor for the column the wide cursor is covering
  149. if (isalnum(current_char) || ispunct(current_char))
  150. adjusted_cursor.set_column(adjusted_cursor.column() + 1);
  151. m_editor->delete_text_range(TextRange(delete_to, adjusted_cursor).normalized());
  152. switch_to_insert_mode();
  153. } else if (event.key() == KeyCode::Key_E) {
  154. // Delete to the end of the next word, if in the middle of a word, this will delete
  155. // from the cursor to the end of said word. If the cursor is on whitespace
  156. // it, and the next word will be deleted.
  157. TextPosition delete_to = find_end_of_next_word();
  158. TextPosition adjusted_cursor = m_editor->cursor();
  159. delete_to.set_column(delete_to.column() + 1);
  160. m_editor->delete_text_range(TextRange(adjusted_cursor, delete_to).normalized());
  161. switch_to_insert_mode();
  162. }
  163. m_previous_key = {};
  164. } else {
  165. // Handle first any key codes that are to be applied regardless of modifiers.
  166. switch (event.key()) {
  167. case (KeyCode::Key_Dollar):
  168. move_to_line_end(event);
  169. break;
  170. case (KeyCode::Key_Escape):
  171. if (m_editor->on_escape_pressed)
  172. m_editor->on_escape_pressed();
  173. break;
  174. default:
  175. break;
  176. }
  177. // SHIFT is pressed.
  178. if (event.shift() && !event.ctrl() && !event.alt()) {
  179. switch (event.key()) {
  180. case (KeyCode::Key_A):
  181. move_to_line_end(event);
  182. switch_to_insert_mode();
  183. break;
  184. case (KeyCode::Key_G):
  185. move_to_last_line();
  186. break;
  187. case (KeyCode::Key_I):
  188. move_to_line_beginning(event);
  189. switch_to_insert_mode();
  190. break;
  191. case (KeyCode::Key_O):
  192. move_to_line_beginning(event);
  193. m_editor->add_code_point(0x0A);
  194. move_one_up(event);
  195. switch_to_insert_mode();
  196. break;
  197. default:
  198. break;
  199. }
  200. }
  201. // CTRL is pressed.
  202. if (event.ctrl() && !event.shift() && !event.alt()) {
  203. switch (event.key()) {
  204. case (KeyCode::Key_D):
  205. move_half_page_down(event);
  206. break;
  207. case (KeyCode::Key_R):
  208. m_editor->redo();
  209. break;
  210. case (KeyCode::Key_U):
  211. move_half_page_up(event);
  212. break;
  213. default:
  214. break;
  215. }
  216. }
  217. // FIXME: H and L movement keys will move to the previous or next line when reaching the beginning or end
  218. // of the line and pressed again.
  219. // No modifier is pressed.
  220. if (!event.ctrl() && !event.shift() && !event.alt()) {
  221. switch (event.key()) {
  222. case (KeyCode::Key_A):
  223. move_one_right(event);
  224. switch_to_insert_mode();
  225. break;
  226. case (KeyCode::Key_B):
  227. move_to_beginning_of_previous_word();
  228. break;
  229. case (KeyCode::Key_C):
  230. m_previous_key = event.key();
  231. break;
  232. case (KeyCode::Key_Backspace):
  233. case (KeyCode::Key_H):
  234. case (KeyCode::Key_Left):
  235. move_one_left(event);
  236. break;
  237. case (KeyCode::Key_D):
  238. m_previous_key = event.key();
  239. break;
  240. case (KeyCode::Key_E):
  241. move_to_end_of_next_word();
  242. break;
  243. case (KeyCode::Key_G):
  244. m_previous_key = event.key();
  245. break;
  246. case (KeyCode::Key_Down):
  247. case (KeyCode::Key_J):
  248. move_one_down(event);
  249. break;
  250. case (KeyCode::Key_I):
  251. switch_to_insert_mode();
  252. break;
  253. case (KeyCode::Key_K):
  254. case (KeyCode::Key_Up):
  255. move_one_up(event);
  256. break;
  257. case (KeyCode::Key_L):
  258. case (KeyCode::Key_Right):
  259. move_one_right(event);
  260. break;
  261. case (KeyCode::Key_O):
  262. move_to_line_end(event);
  263. m_editor->add_code_point(0x0A);
  264. switch_to_insert_mode();
  265. break;
  266. case (KeyCode::Key_U):
  267. m_editor->undo();
  268. break;
  269. case (KeyCode::Key_W):
  270. move_to_beginning_of_next_word();
  271. break;
  272. case (KeyCode::Key_X):
  273. yank({ m_editor->cursor(), { m_editor->cursor().line(), m_editor->cursor().column() + 1 } });
  274. delete_char();
  275. break;
  276. case (KeyCode::Key_0):
  277. move_to_line_beginning(event);
  278. break;
  279. case (KeyCode::Key_V):
  280. switch_to_visual_mode();
  281. break;
  282. case (KeyCode::Key_Y):
  283. m_previous_key = event.key();
  284. break;
  285. case (KeyCode::Key_P):
  286. put(event);
  287. break;
  288. default:
  289. break;
  290. }
  291. }
  292. }
  293. return true;
  294. }
  295. bool VimEditingEngine::on_key_in_visual_mode(const KeyEvent& event)
  296. {
  297. if (m_previous_key == KeyCode::Key_G) {
  298. if (event.key() == KeyCode::Key_G) {
  299. move_to_first_line();
  300. update_selection_on_cursor_move();
  301. } else if (event.key() == KeyCode::Key_E) {
  302. move_to_end_of_previous_word();
  303. update_selection_on_cursor_move();
  304. }
  305. m_previous_key = {};
  306. } else {
  307. // Handle first any key codes that are to be applied regardless of modifiers.
  308. switch (event.key()) {
  309. case (KeyCode::Key_Dollar):
  310. move_to_line_end(event);
  311. update_selection_on_cursor_move();
  312. break;
  313. case (KeyCode::Key_Escape):
  314. switch_to_normal_mode();
  315. if (m_editor->on_escape_pressed)
  316. m_editor->on_escape_pressed();
  317. break;
  318. default:
  319. break;
  320. }
  321. // SHIFT is pressed.
  322. if (event.shift() && !event.ctrl() && !event.alt()) {
  323. switch (event.key()) {
  324. case (KeyCode::Key_A):
  325. move_to_line_end(event);
  326. switch_to_insert_mode();
  327. break;
  328. case (KeyCode::Key_G):
  329. move_to_last_line();
  330. break;
  331. case (KeyCode::Key_I):
  332. move_to_line_beginning(event);
  333. switch_to_insert_mode();
  334. break;
  335. default:
  336. break;
  337. }
  338. }
  339. // CTRL is pressed.
  340. if (event.ctrl() && !event.shift() && !event.alt()) {
  341. switch (event.key()) {
  342. case (KeyCode::Key_D):
  343. move_half_page_down(event);
  344. update_selection_on_cursor_move();
  345. break;
  346. case (KeyCode::Key_U):
  347. move_half_page_up(event);
  348. update_selection_on_cursor_move();
  349. break;
  350. default:
  351. break;
  352. }
  353. }
  354. // No modifier is pressed.
  355. if (!event.ctrl() && !event.shift() && !event.alt()) {
  356. switch (event.key()) {
  357. case (KeyCode::Key_B):
  358. move_to_beginning_of_previous_word();
  359. update_selection_on_cursor_move();
  360. break;
  361. case (KeyCode::Key_Backspace):
  362. case (KeyCode::Key_H):
  363. case (KeyCode::Key_Left):
  364. move_one_left(event);
  365. update_selection_on_cursor_move();
  366. break;
  367. case (KeyCode::Key_D):
  368. yank(Selection);
  369. m_editor->do_delete();
  370. switch_to_normal_mode();
  371. break;
  372. case (KeyCode::Key_E):
  373. move_to_end_of_next_word();
  374. update_selection_on_cursor_move();
  375. break;
  376. case (KeyCode::Key_G):
  377. m_previous_key = event.key();
  378. break;
  379. case (KeyCode::Key_Down):
  380. case (KeyCode::Key_J):
  381. move_one_down(event);
  382. update_selection_on_cursor_move();
  383. break;
  384. case (KeyCode::Key_K):
  385. case (KeyCode::Key_Up):
  386. move_one_up(event);
  387. update_selection_on_cursor_move();
  388. break;
  389. case (KeyCode::Key_L):
  390. case (KeyCode::Key_Right):
  391. move_one_right(event);
  392. update_selection_on_cursor_move();
  393. break;
  394. case (KeyCode::Key_U):
  395. // FIXME: Set selection to uppercase.
  396. break;
  397. case (KeyCode::Key_W):
  398. move_to_beginning_of_next_word();
  399. update_selection_on_cursor_move();
  400. break;
  401. case (KeyCode::Key_X):
  402. yank(Selection);
  403. m_editor->do_delete();
  404. switch_to_normal_mode();
  405. break;
  406. case (KeyCode::Key_0):
  407. move_to_line_beginning(event);
  408. update_selection_on_cursor_move();
  409. break;
  410. case (KeyCode::Key_V):
  411. switch_to_normal_mode();
  412. break;
  413. case (KeyCode::Key_C):
  414. yank(Selection);
  415. m_editor->do_delete();
  416. switch_to_insert_mode();
  417. break;
  418. case (KeyCode::Key_Y):
  419. yank(Selection);
  420. switch_to_normal_mode();
  421. break;
  422. default:
  423. break;
  424. }
  425. }
  426. }
  427. return true;
  428. }
  429. void VimEditingEngine::switch_to_normal_mode()
  430. {
  431. m_vim_mode = VimMode::Normal;
  432. m_editor->reset_cursor_blink();
  433. m_previous_key = {};
  434. clear_visual_mode_data();
  435. };
  436. void VimEditingEngine::switch_to_insert_mode()
  437. {
  438. m_vim_mode = VimMode::Insert;
  439. m_editor->reset_cursor_blink();
  440. m_previous_key = {};
  441. clear_visual_mode_data();
  442. };
  443. void VimEditingEngine::switch_to_visual_mode()
  444. {
  445. m_vim_mode = VimMode::Visual;
  446. m_editor->reset_cursor_blink();
  447. m_previous_key = {};
  448. m_selection_start_position = m_editor->cursor();
  449. m_editor->selection()->set(m_editor->cursor(), { m_editor->cursor().line(), m_editor->cursor().column() + 1 });
  450. m_editor->did_update_selection();
  451. }
  452. void VimEditingEngine::update_selection_on_cursor_move()
  453. {
  454. auto cursor = m_editor->cursor();
  455. auto start = m_selection_start_position < cursor ? m_selection_start_position : cursor;
  456. auto end = m_selection_start_position < cursor ? cursor : m_selection_start_position;
  457. end.set_column(end.column() + 1);
  458. m_editor->selection()->set(start, end);
  459. m_editor->did_update_selection();
  460. }
  461. void VimEditingEngine::clear_visual_mode_data()
  462. {
  463. if (m_editor->has_selection()) {
  464. m_editor->selection()->clear();
  465. m_editor->did_update_selection();
  466. }
  467. m_selection_start_position = {};
  468. }
  469. void VimEditingEngine::move_half_page_up(const KeyEvent& event)
  470. {
  471. move_up(event, 0.5);
  472. };
  473. void VimEditingEngine::move_half_page_down(const KeyEvent& event)
  474. {
  475. move_down(event, 0.5);
  476. };
  477. void VimEditingEngine::yank(YankType type)
  478. {
  479. m_yank_type = type;
  480. if (type == YankType::Line) {
  481. m_yank_buffer = m_editor->current_line().to_utf8();
  482. } else {
  483. m_yank_buffer = m_editor->selected_text();
  484. }
  485. // When putting this, auto indentation (if enabled) will indent as far as
  486. // is necessary, then any whitespace captured before the yanked text will be placed
  487. // after the indentation, doubling the indentation.
  488. if (m_editor->is_automatic_indentation_enabled())
  489. m_yank_buffer = m_yank_buffer.trim_whitespace(TrimMode::Left);
  490. }
  491. void VimEditingEngine::yank(TextRange range)
  492. {
  493. m_yank_type = YankType::Selection;
  494. m_yank_buffer = m_editor->document().text_in_range(range);
  495. }
  496. void VimEditingEngine::put(const GUI::KeyEvent& event)
  497. {
  498. if (m_yank_type == YankType::Line) {
  499. move_to_line_end(event);
  500. StringBuilder sb = StringBuilder(m_yank_buffer.length() + 1);
  501. sb.append_code_point(0x0A);
  502. sb.append(m_yank_buffer);
  503. m_editor->insert_at_cursor_or_replace_selection(sb.to_string());
  504. m_editor->set_cursor({ m_editor->cursor().line(), m_editor->current_line().first_non_whitespace_column() });
  505. } else {
  506. // FIXME: If attempting to put on the last column a line,
  507. // the buffer will bne placed on the next line due to the move_one_left/right behaviour.
  508. move_one_right(event);
  509. m_editor->insert_at_cursor_or_replace_selection(m_yank_buffer);
  510. move_one_left(event);
  511. }
  512. }
  513. }