EditingEngine.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGUI/EditingEngine.h>
  7. #include <LibGUI/Event.h>
  8. #include <LibGUI/TextEditor.h>
  9. namespace GUI {
  10. EditingEngine::~EditingEngine()
  11. {
  12. }
  13. void EditingEngine::attach(TextEditor& editor)
  14. {
  15. VERIFY(!m_editor);
  16. m_editor = editor;
  17. }
  18. void EditingEngine::detach()
  19. {
  20. VERIFY(m_editor);
  21. m_editor = nullptr;
  22. }
  23. bool EditingEngine::on_key(const KeyEvent& event)
  24. {
  25. if (event.key() == KeyCode::Key_Left) {
  26. if (!event.shift() && m_editor->selection()->is_valid()) {
  27. m_editor->set_cursor(m_editor->selection()->normalized().start());
  28. m_editor->selection()->clear();
  29. m_editor->did_update_selection();
  30. if (!event.ctrl()) {
  31. m_editor->update();
  32. return true;
  33. }
  34. }
  35. if (event.ctrl()) {
  36. m_editor->update_selection(event.shift());
  37. move_to_previous_span();
  38. if (event.shift() && m_editor->selection()->start().is_valid()) {
  39. m_editor->selection()->set_end(m_editor->cursor());
  40. m_editor->did_update_selection();
  41. }
  42. return true;
  43. }
  44. m_editor->update_selection(event.shift());
  45. move_one_left();
  46. if (event.shift() && m_editor->selection()->start().is_valid()) {
  47. m_editor->selection()->set_end(m_editor->cursor());
  48. m_editor->did_update_selection();
  49. }
  50. return true;
  51. }
  52. if (event.key() == KeyCode::Key_Right) {
  53. if (!event.shift() && m_editor->selection()->is_valid()) {
  54. m_editor->set_cursor(m_editor->selection()->normalized().end());
  55. m_editor->selection()->clear();
  56. m_editor->did_update_selection();
  57. if (!event.ctrl()) {
  58. m_editor->update();
  59. return true;
  60. }
  61. }
  62. if (event.ctrl()) {
  63. move_to_next_span(event);
  64. return true;
  65. }
  66. m_editor->update_selection(event.shift());
  67. move_one_right();
  68. if (event.shift() && m_editor->selection()->start().is_valid()) {
  69. m_editor->selection()->set_end(m_editor->cursor());
  70. m_editor->did_update_selection();
  71. }
  72. return true;
  73. }
  74. if (event.key() == KeyCode::Key_Up) {
  75. if (m_editor->cursor().line() > 0 || m_editor->is_wrapping_enabled()) {
  76. m_editor->update_selection(event.shift());
  77. }
  78. move_one_up(event);
  79. if (event.shift() && m_editor->selection()->start().is_valid()) {
  80. m_editor->selection()->set_end(m_editor->cursor());
  81. m_editor->did_update_selection();
  82. }
  83. return true;
  84. }
  85. if (event.key() == KeyCode::Key_Down) {
  86. if (m_editor->cursor().line() < (m_editor->line_count() - 1) || m_editor->is_wrapping_enabled()) {
  87. m_editor->update_selection(event.shift());
  88. }
  89. move_one_down(event);
  90. if (event.shift() && m_editor->selection()->start().is_valid()) {
  91. m_editor->selection()->set_end(m_editor->cursor());
  92. m_editor->did_update_selection();
  93. }
  94. return true;
  95. }
  96. if (event.key() == KeyCode::Key_Home) {
  97. if (event.ctrl()) {
  98. move_to_first_line();
  99. if (event.shift() && m_editor->selection()->start().is_valid()) {
  100. m_editor->selection()->set_end(m_editor->cursor());
  101. m_editor->did_update_selection();
  102. }
  103. } else {
  104. m_editor->update_selection(event.shift());
  105. move_to_line_beginning();
  106. if (event.shift() && m_editor->selection()->start().is_valid()) {
  107. m_editor->selection()->set_end(m_editor->cursor());
  108. m_editor->did_update_selection();
  109. }
  110. }
  111. return true;
  112. }
  113. if (event.key() == KeyCode::Key_End) {
  114. if (event.ctrl()) {
  115. move_to_last_line();
  116. if (event.shift() && m_editor->selection()->start().is_valid()) {
  117. m_editor->selection()->set_end(m_editor->cursor());
  118. m_editor->did_update_selection();
  119. }
  120. } else {
  121. m_editor->update_selection(event.shift());
  122. move_to_line_end();
  123. if (event.shift() && m_editor->selection()->start().is_valid()) {
  124. m_editor->selection()->set_end(m_editor->cursor());
  125. m_editor->did_update_selection();
  126. }
  127. }
  128. return true;
  129. }
  130. if (event.key() == KeyCode::Key_PageUp) {
  131. if (m_editor->cursor().line() > 0 || m_editor->is_wrapping_enabled()) {
  132. m_editor->update_selection(event.shift());
  133. }
  134. move_page_up();
  135. if (event.shift() && m_editor->selection()->start().is_valid()) {
  136. m_editor->selection()->set_end(m_editor->cursor());
  137. m_editor->did_update_selection();
  138. }
  139. return true;
  140. }
  141. if (event.key() == KeyCode::Key_PageDown) {
  142. if (m_editor->cursor().line() < (m_editor->line_count() - 1) || m_editor->is_wrapping_enabled()) {
  143. m_editor->update_selection(event.shift());
  144. }
  145. move_page_down();
  146. if (event.shift() && m_editor->selection()->start().is_valid()) {
  147. m_editor->selection()->set_end(m_editor->cursor());
  148. m_editor->did_update_selection();
  149. }
  150. return true;
  151. }
  152. return false;
  153. }
  154. void EditingEngine::move_one_left()
  155. {
  156. if (m_editor->cursor().column() > 0) {
  157. int new_column = m_editor->cursor().column() - 1;
  158. m_editor->set_cursor(m_editor->cursor().line(), new_column);
  159. } else if (m_editor->cursor().line() > 0) {
  160. int new_line = m_editor->cursor().line() - 1;
  161. int new_column = m_editor->lines()[new_line].length();
  162. m_editor->set_cursor(new_line, new_column);
  163. }
  164. }
  165. void EditingEngine::move_one_right()
  166. {
  167. int new_line = m_editor->cursor().line();
  168. int new_column = m_editor->cursor().column();
  169. if (m_editor->cursor().column() < m_editor->current_line().length()) {
  170. new_line = m_editor->cursor().line();
  171. new_column = m_editor->cursor().column() + 1;
  172. } else if (m_editor->cursor().line() != m_editor->line_count() - 1) {
  173. new_line = m_editor->cursor().line() + 1;
  174. new_column = 0;
  175. }
  176. m_editor->set_cursor(new_line, new_column);
  177. }
  178. void EditingEngine::move_to_previous_span()
  179. {
  180. TextPosition new_cursor;
  181. if (m_editor->document().has_spans()) {
  182. auto span = m_editor->document().first_non_skippable_span_before(m_editor->cursor());
  183. if (span.has_value()) {
  184. new_cursor = span.value().range.start();
  185. } else {
  186. // No remaining spans, just use word break calculation
  187. new_cursor = m_editor->document().first_word_break_before(m_editor->cursor(), true);
  188. }
  189. } else {
  190. new_cursor = m_editor->document().first_word_break_before(m_editor->cursor(), true);
  191. }
  192. m_editor->set_cursor(new_cursor);
  193. }
  194. void EditingEngine::move_to_next_span(const KeyEvent& event)
  195. {
  196. TextPosition new_cursor;
  197. if (m_editor->document().has_spans()) {
  198. auto span = m_editor->document().first_non_skippable_span_after(m_editor->cursor());
  199. if (span.has_value()) {
  200. new_cursor = span.value().range.start();
  201. } else {
  202. // No remaining spans, just use word break calculation
  203. new_cursor = m_editor->document().first_word_break_after(m_editor->cursor());
  204. }
  205. } else {
  206. new_cursor = m_editor->document().first_word_break_after(m_editor->cursor());
  207. }
  208. m_editor->set_cursor(new_cursor);
  209. if (event.shift() && m_editor->selection()->start().is_valid()) {
  210. m_editor->selection()->set_end(m_editor->cursor());
  211. m_editor->did_update_selection();
  212. }
  213. }
  214. void EditingEngine::move_to_line_beginning()
  215. {
  216. TextPosition new_cursor;
  217. if (m_editor->is_wrapping_enabled()) {
  218. // FIXME: Replicate the first_nonspace_column behavior in wrapping mode.
  219. auto home_position = m_editor->cursor_content_rect().location().translated(-m_editor->width(), 0);
  220. new_cursor = m_editor->text_position_at_content_position(home_position);
  221. } else {
  222. size_t first_nonspace_column = m_editor->current_line().first_non_whitespace_column();
  223. if (m_editor->cursor().column() == first_nonspace_column) {
  224. new_cursor = { m_editor->cursor().line(), 0 };
  225. } else {
  226. new_cursor = { m_editor->cursor().line(), first_nonspace_column };
  227. }
  228. }
  229. m_editor->set_cursor(new_cursor);
  230. }
  231. void EditingEngine::move_to_line_end()
  232. {
  233. TextPosition new_cursor;
  234. if (m_editor->is_wrapping_enabled()) {
  235. auto end_position = m_editor->cursor_content_rect().location().translated(m_editor->width(), 0);
  236. new_cursor = m_editor->text_position_at_content_position(end_position);
  237. } else {
  238. new_cursor = { m_editor->cursor().line(), m_editor->current_line().length() };
  239. }
  240. m_editor->set_cursor(new_cursor);
  241. }
  242. void EditingEngine::move_one_up(const KeyEvent& event)
  243. {
  244. if (m_editor->cursor().line() > 0 || m_editor->is_wrapping_enabled()) {
  245. if (event.ctrl() && event.shift()) {
  246. move_selected_lines_up();
  247. return;
  248. }
  249. TextPosition new_cursor;
  250. if (m_editor->is_wrapping_enabled()) {
  251. auto position_above = m_editor->cursor_content_rect().location().translated(0, -m_editor->line_height());
  252. new_cursor = m_editor->text_position_at_content_position(position_above);
  253. } else {
  254. size_t new_line = m_editor->cursor().line() - 1;
  255. size_t new_column = min(m_editor->cursor().column(), m_editor->line(new_line).length());
  256. new_cursor = { new_line, new_column };
  257. }
  258. m_editor->set_cursor(new_cursor);
  259. }
  260. };
  261. void EditingEngine::move_one_down(const KeyEvent& event)
  262. {
  263. if (m_editor->cursor().line() < (m_editor->line_count() - 1) || m_editor->is_wrapping_enabled()) {
  264. if (event.ctrl() && event.shift()) {
  265. move_selected_lines_down();
  266. return;
  267. }
  268. TextPosition new_cursor;
  269. if (m_editor->is_wrapping_enabled()) {
  270. auto position_below = m_editor->cursor_content_rect().location().translated(0, m_editor->line_height());
  271. new_cursor = m_editor->text_position_at_content_position(position_below);
  272. } else {
  273. size_t new_line = m_editor->cursor().line() + 1;
  274. size_t new_column = min(m_editor->cursor().column(), m_editor->line(new_line).length());
  275. new_cursor = { new_line, new_column };
  276. }
  277. m_editor->set_cursor(new_cursor);
  278. }
  279. };
  280. void EditingEngine::move_up(double page_height_factor)
  281. {
  282. if (m_editor->cursor().line() > 0 || m_editor->is_wrapping_enabled()) {
  283. int pixels = (int)(m_editor->visible_content_rect().height() * page_height_factor);
  284. TextPosition new_cursor;
  285. if (m_editor->is_wrapping_enabled()) {
  286. auto position_above = m_editor->cursor_content_rect().location().translated(0, -pixels);
  287. new_cursor = m_editor->text_position_at_content_position(position_above);
  288. } else {
  289. size_t page_step = (size_t)pixels / (size_t)m_editor->line_height();
  290. size_t new_line = m_editor->cursor().line() < page_step ? 0 : m_editor->cursor().line() - page_step;
  291. size_t new_column = min(m_editor->cursor().column(), m_editor->line(new_line).length());
  292. new_cursor = { new_line, new_column };
  293. }
  294. m_editor->set_cursor(new_cursor);
  295. }
  296. };
  297. void EditingEngine::move_down(double page_height_factor)
  298. {
  299. if (m_editor->cursor().line() < (m_editor->line_count() - 1) || m_editor->is_wrapping_enabled()) {
  300. int pixels = (int)(m_editor->visible_content_rect().height() * page_height_factor);
  301. TextPosition new_cursor;
  302. if (m_editor->is_wrapping_enabled()) {
  303. auto position_below = m_editor->cursor_content_rect().location().translated(0, pixels);
  304. new_cursor = m_editor->text_position_at_content_position(position_below);
  305. } else {
  306. size_t new_line = min(m_editor->line_count() - 1, m_editor->cursor().line() + pixels / m_editor->line_height());
  307. size_t new_column = min(m_editor->cursor().column(), m_editor->lines()[new_line].length());
  308. new_cursor = { new_line, new_column };
  309. }
  310. m_editor->set_cursor(new_cursor);
  311. };
  312. }
  313. void EditingEngine::move_page_up()
  314. {
  315. move_up(1);
  316. };
  317. void EditingEngine::move_page_down()
  318. {
  319. move_down(1);
  320. };
  321. void EditingEngine::move_to_first_line()
  322. {
  323. m_editor->set_cursor(0, 0);
  324. };
  325. void EditingEngine::move_to_last_line()
  326. {
  327. m_editor->set_cursor(m_editor->line_count() - 1, m_editor->lines()[m_editor->line_count() - 1].length());
  328. };
  329. void EditingEngine::get_selection_line_boundaries(size_t& first_line, size_t& last_line)
  330. {
  331. auto selection = m_editor->normalized_selection();
  332. if (!selection.is_valid()) {
  333. first_line = m_editor->cursor().line();
  334. last_line = m_editor->cursor().line();
  335. return;
  336. }
  337. first_line = selection.start().line();
  338. last_line = selection.end().line();
  339. if (first_line != last_line && selection.end().column() == 0)
  340. last_line -= 1;
  341. }
  342. TextPosition EditingEngine::find_beginning_of_next_word()
  343. {
  344. /* The rules that have been coded in:
  345. * Jump to the next punct or alnum after any whitespace
  346. * Jump to the next non-consecutive punct regardless of whitespace
  347. * Jump to the next alnum if started on punct regardless of whitespace
  348. * If the end of the input is reached, jump there
  349. */
  350. auto vim_isalnum = [](int c) {
  351. return c == '_' || isalnum(c);
  352. };
  353. auto vim_ispunct = [](int c) {
  354. return c != '_' && ispunct(c);
  355. };
  356. bool started_on_punct = vim_ispunct(m_editor->current_line().to_utf8().characters()[m_editor->cursor().column()]);
  357. bool has_seen_whitespace = false;
  358. bool is_first_line = true;
  359. auto& lines = m_editor->lines();
  360. auto cursor = m_editor->cursor();
  361. for (size_t line_index = cursor.line(); line_index < lines.size(); line_index++) {
  362. auto& line = lines.at(line_index);
  363. if (line.is_empty() && !is_first_line) {
  364. return { line_index, 0 };
  365. } else if (line.is_empty()) {
  366. has_seen_whitespace = true;
  367. }
  368. is_first_line = false;
  369. for (size_t column_index = 0; column_index < lines.at(line_index).length(); column_index++) {
  370. if (line_index == cursor.line() && column_index < cursor.column())
  371. continue;
  372. const u32* line_chars = line.view().code_points();
  373. const u32 current_char = line_chars[column_index];
  374. if (started_on_punct && vim_isalnum(current_char)) {
  375. return { line_index, column_index };
  376. }
  377. if (vim_ispunct(current_char) && !started_on_punct) {
  378. return { line_index, column_index };
  379. }
  380. if (isspace(current_char))
  381. has_seen_whitespace = true;
  382. if (has_seen_whitespace && (vim_isalnum(current_char) || vim_ispunct(current_char))) {
  383. return { line_index, column_index };
  384. }
  385. if (line_index == lines.size() - 1 && column_index == line.length() - 1) {
  386. return { line_index, column_index };
  387. }
  388. // Implicit newline
  389. if (column_index == line.length() - 1)
  390. has_seen_whitespace = true;
  391. }
  392. }
  393. VERIFY_NOT_REACHED();
  394. }
  395. void EditingEngine::move_to_beginning_of_next_word()
  396. {
  397. m_editor->set_cursor(find_beginning_of_next_word());
  398. }
  399. TextPosition EditingEngine::find_end_of_next_word()
  400. {
  401. /* The rules that have been coded in:
  402. * If the current_char is alnum and the next is whitespace or punct
  403. * If the current_char is punct and the next is whitespace or alnum
  404. * If the end of the input is reached, jump there
  405. */
  406. auto vim_isalnum = [](int c) {
  407. return c == '_' || isalnum(c);
  408. };
  409. auto vim_ispunct = [](int c) {
  410. return c != '_' && ispunct(c);
  411. };
  412. bool is_first_line = true;
  413. bool is_first_iteration = true;
  414. auto& lines = m_editor->lines();
  415. auto cursor = m_editor->cursor();
  416. if ((lines.at(cursor.line()).length() - cursor.column()) <= 1)
  417. return { cursor.line(), cursor.column() };
  418. for (size_t line_index = cursor.line(); line_index < lines.size(); line_index++) {
  419. auto& line = lines.at(line_index);
  420. if (line.is_empty() && !is_first_line) {
  421. return { line_index, 0 };
  422. }
  423. is_first_line = false;
  424. for (size_t column_index = 0; column_index < lines.at(line_index).length(); column_index++) {
  425. if (line_index == cursor.line() && column_index < cursor.column())
  426. continue;
  427. const u32* line_chars = line.view().code_points();
  428. const u32 current_char = line_chars[column_index];
  429. if (column_index == lines.at(line_index).length() - 1 && !is_first_iteration && (vim_isalnum(current_char) || vim_ispunct(current_char)))
  430. return { line_index, column_index };
  431. else if (column_index == lines.at(line_index).length() - 1) {
  432. is_first_iteration = false;
  433. continue;
  434. }
  435. const u32 next_char = line_chars[column_index + 1];
  436. if (!is_first_iteration && vim_isalnum(current_char) && (isspace(next_char) || vim_ispunct(next_char)))
  437. return { line_index, column_index };
  438. if (!is_first_iteration && vim_ispunct(current_char) && (isspace(next_char) || vim_isalnum(next_char)))
  439. return { line_index, column_index };
  440. if (line_index == lines.size() - 1 && column_index == line.length() - 1) {
  441. return { line_index, column_index };
  442. }
  443. is_first_iteration = false;
  444. }
  445. }
  446. VERIFY_NOT_REACHED();
  447. }
  448. void EditingEngine::move_to_end_of_next_word()
  449. {
  450. m_editor->set_cursor(find_end_of_next_word());
  451. }
  452. TextPosition EditingEngine::find_end_of_previous_word()
  453. {
  454. auto vim_isalnum = [](int c) {
  455. return c == '_' || isalnum(c);
  456. };
  457. auto vim_ispunct = [](int c) {
  458. return c != '_' && ispunct(c);
  459. };
  460. bool started_on_punct = vim_ispunct(m_editor->current_line().to_utf8().characters()[m_editor->cursor().column()]);
  461. bool is_first_line = true;
  462. bool has_seen_whitespace = false;
  463. auto& lines = m_editor->lines();
  464. auto cursor = m_editor->cursor();
  465. for (size_t line_index = cursor.line(); (int)line_index >= 0; line_index--) {
  466. auto& line = lines.at(line_index);
  467. if (line.is_empty() && !is_first_line) {
  468. return { line_index, 0 };
  469. } else if (line.is_empty()) {
  470. has_seen_whitespace = true;
  471. }
  472. is_first_line = false;
  473. size_t line_length = lines.at(line_index).length();
  474. for (size_t column_index = line_length - 1; (int)column_index >= 0; column_index--) {
  475. if (line_index == cursor.line() && column_index > cursor.column())
  476. continue;
  477. const u32* line_chars = line.view().code_points();
  478. const u32 current_char = line_chars[column_index];
  479. if (started_on_punct && vim_isalnum(current_char)) {
  480. return { line_index, column_index };
  481. }
  482. if (vim_ispunct(current_char) && !started_on_punct) {
  483. return { line_index, column_index };
  484. }
  485. if (isspace(current_char)) {
  486. has_seen_whitespace = true;
  487. }
  488. if (has_seen_whitespace && (vim_isalnum(current_char) || vim_ispunct(current_char))) {
  489. return { line_index, column_index };
  490. }
  491. if (line_index == 0 && column_index == 0) {
  492. return { line_index, column_index };
  493. }
  494. // Implicit newline when wrapping back up to the end of the previous line.
  495. if (column_index == 0)
  496. has_seen_whitespace = true;
  497. }
  498. }
  499. VERIFY_NOT_REACHED();
  500. }
  501. void EditingEngine::move_to_end_of_previous_word()
  502. {
  503. m_editor->set_cursor(find_end_of_previous_word());
  504. }
  505. TextPosition EditingEngine::find_beginning_of_previous_word()
  506. {
  507. auto vim_isalnum = [](int c) {
  508. return c == '_' || isalnum(c);
  509. };
  510. auto vim_ispunct = [](int c) {
  511. return c != '_' && ispunct(c);
  512. };
  513. bool is_first_iterated_line = true;
  514. bool is_first_iteration = true;
  515. auto& lines = m_editor->lines();
  516. auto cursor = m_editor->cursor();
  517. if ((lines.at(cursor.line()).length() - cursor.column()) <= 1)
  518. return { cursor.line(), cursor.column() };
  519. for (size_t line_index = cursor.line(); (int)line_index >= 0; line_index--) {
  520. auto& line = lines.at(line_index);
  521. if (line.is_empty() && !is_first_iterated_line) {
  522. return { line_index, 0 };
  523. }
  524. is_first_iterated_line = false;
  525. size_t line_length = lines.at(line_index).length();
  526. for (size_t column_index = line_length; (int)column_index >= 0; column_index--) {
  527. if (line_index == cursor.line() && column_index > cursor.column())
  528. continue;
  529. if (column_index == line_length) {
  530. is_first_iteration = false;
  531. continue;
  532. }
  533. const u32* line_chars = line.view().code_points();
  534. const u32 current_char = line_chars[column_index];
  535. if (column_index == 0 && !is_first_iteration && (vim_isalnum(current_char) || vim_ispunct(current_char))) {
  536. return { line_index, column_index };
  537. } else if (line_index == 0 && column_index == 0) {
  538. return { line_index, column_index };
  539. } else if (column_index == 0 && is_first_iteration) {
  540. is_first_iteration = false;
  541. continue;
  542. }
  543. const u32 next_char = line_chars[column_index - 1];
  544. if (!is_first_iteration && vim_isalnum(current_char) && (isspace(next_char) || vim_ispunct(next_char)))
  545. return { line_index, column_index };
  546. if (!is_first_iteration && vim_ispunct(current_char) && (isspace(next_char) || vim_isalnum(next_char)))
  547. return { line_index, column_index };
  548. is_first_iteration = false;
  549. }
  550. }
  551. VERIFY_NOT_REACHED();
  552. }
  553. void EditingEngine::move_to_beginning_of_previous_word()
  554. {
  555. m_editor->set_cursor(find_beginning_of_previous_word());
  556. }
  557. void EditingEngine::move_selected_lines_up()
  558. {
  559. if (!m_editor->is_editable())
  560. return;
  561. size_t first_line;
  562. size_t last_line;
  563. get_selection_line_boundaries(first_line, last_line);
  564. if (first_line == 0)
  565. return;
  566. auto& lines = m_editor->document().lines();
  567. lines.insert((int)last_line, lines.take((int)first_line - 1));
  568. m_editor->set_cursor({ first_line - 1, 0 });
  569. if (m_editor->has_selection()) {
  570. m_editor->selection()->set_start({ first_line - 1, 0 });
  571. m_editor->selection()->set_end({ last_line - 1, m_editor->line(last_line - 1).length() });
  572. }
  573. m_editor->did_change();
  574. m_editor->update();
  575. }
  576. void EditingEngine::move_selected_lines_down()
  577. {
  578. if (!m_editor->is_editable())
  579. return;
  580. size_t first_line;
  581. size_t last_line;
  582. get_selection_line_boundaries(first_line, last_line);
  583. auto& lines = m_editor->document().lines();
  584. VERIFY(lines.size() != 0);
  585. if (last_line >= lines.size() - 1)
  586. return;
  587. lines.insert((int)first_line, lines.take((int)last_line + 1));
  588. m_editor->set_cursor({ first_line + 1, 0 });
  589. if (m_editor->has_selection()) {
  590. m_editor->selection()->set_start({ first_line + 1, 0 });
  591. m_editor->selection()->set_end({ last_line + 1, m_editor->line(last_line + 1).length() });
  592. }
  593. m_editor->did_change();
  594. m_editor->update();
  595. }
  596. void EditingEngine::delete_char()
  597. {
  598. if (!m_editor->is_editable())
  599. return;
  600. m_editor->do_delete();
  601. };
  602. void EditingEngine::delete_line()
  603. {
  604. if (!m_editor->is_editable())
  605. return;
  606. m_editor->delete_current_line();
  607. };
  608. }