TextDocument.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Badge.h>
  8. #include <AK/CharacterTypes.h>
  9. #include <AK/QuickSort.h>
  10. #include <AK/ScopeGuard.h>
  11. #include <AK/StdLibExtras.h>
  12. #include <AK/StringBuilder.h>
  13. #include <AK/Utf8View.h>
  14. #include <LibCore/Timer.h>
  15. #include <LibGUI/TextDocument.h>
  16. #include <LibRegex/Regex.h>
  17. #include <LibUnicode/CharacterTypes.h>
  18. namespace GUI {
  19. NonnullRefPtr<TextDocument> TextDocument::create(Client* client)
  20. {
  21. return adopt_ref(*new TextDocument(client));
  22. }
  23. TextDocument::TextDocument(Client* client)
  24. {
  25. if (client)
  26. m_clients.set(client);
  27. append_line(make<TextDocumentLine>(*this));
  28. set_unmodified();
  29. m_undo_stack.on_state_change = [this] {
  30. if (m_client_notifications_enabled) {
  31. for (auto* client : m_clients)
  32. client->document_did_update_undo_stack();
  33. }
  34. };
  35. }
  36. bool TextDocument::set_text(StringView text, AllowCallback allow_callback)
  37. {
  38. m_client_notifications_enabled = false;
  39. m_undo_stack.clear();
  40. m_spans.clear();
  41. remove_all_lines();
  42. ArmedScopeGuard clear_text_guard([this]() {
  43. set_text({});
  44. });
  45. size_t start_of_current_line = 0;
  46. auto add_line = [&](size_t current_position) -> bool {
  47. size_t line_length = current_position - start_of_current_line;
  48. auto line = make<TextDocumentLine>(*this);
  49. bool success = true;
  50. if (line_length)
  51. success = line->set_text(*this, text.substring_view(start_of_current_line, current_position - start_of_current_line));
  52. if (!success)
  53. return false;
  54. append_line(move(line));
  55. start_of_current_line = current_position + 1;
  56. return true;
  57. };
  58. size_t i = 0;
  59. for (i = 0; i < text.length(); ++i) {
  60. if (text[i] != '\n')
  61. continue;
  62. auto success = add_line(i);
  63. if (!success)
  64. return false;
  65. }
  66. auto success = add_line(i);
  67. if (!success)
  68. return false;
  69. // Don't show the file's trailing newline as an actual new line.
  70. if (line_count() > 1 && line(line_count() - 1).is_empty())
  71. (void)m_lines.take_last();
  72. m_client_notifications_enabled = true;
  73. for (auto* client : m_clients)
  74. client->document_did_set_text(allow_callback);
  75. clear_text_guard.disarm();
  76. // FIXME: Should the modified state be cleared on some of the earlier returns as well?
  77. set_unmodified();
  78. return true;
  79. }
  80. size_t TextDocumentLine::first_non_whitespace_column() const
  81. {
  82. for (size_t i = 0; i < length(); ++i) {
  83. auto code_point = code_points()[i];
  84. if (!is_ascii_space(code_point))
  85. return i;
  86. }
  87. return length();
  88. }
  89. Optional<size_t> TextDocumentLine::last_non_whitespace_column() const
  90. {
  91. for (ssize_t i = length() - 1; i >= 0; --i) {
  92. auto code_point = code_points()[i];
  93. if (!is_ascii_space(code_point))
  94. return i;
  95. }
  96. return {};
  97. }
  98. bool TextDocumentLine::ends_in_whitespace() const
  99. {
  100. if (!length())
  101. return false;
  102. return is_ascii_space(code_points()[length() - 1]);
  103. }
  104. bool TextDocumentLine::can_select() const
  105. {
  106. if (is_empty())
  107. return false;
  108. for (size_t i = 0; i < length(); ++i) {
  109. auto code_point = code_points()[i];
  110. if (code_point != '\n' && code_point != '\r' && code_point != '\f' && code_point != '\v')
  111. return true;
  112. }
  113. return false;
  114. }
  115. size_t TextDocumentLine::leading_spaces() const
  116. {
  117. size_t count = 0;
  118. for (; count < m_text.size(); ++count) {
  119. if (m_text[count] != ' ') {
  120. break;
  121. }
  122. }
  123. return count;
  124. }
  125. DeprecatedString TextDocumentLine::to_utf8() const
  126. {
  127. StringBuilder builder;
  128. builder.append(view());
  129. return builder.to_deprecated_string();
  130. }
  131. TextDocumentLine::TextDocumentLine(TextDocument& document)
  132. {
  133. clear(document);
  134. }
  135. TextDocumentLine::TextDocumentLine(TextDocument& document, StringView text)
  136. {
  137. set_text(document, text);
  138. }
  139. void TextDocumentLine::clear(TextDocument& document)
  140. {
  141. m_text.clear();
  142. document.update_views({});
  143. }
  144. void TextDocumentLine::set_text(TextDocument& document, Vector<u32> const text)
  145. {
  146. m_text = move(text);
  147. document.update_views({});
  148. }
  149. bool TextDocumentLine::set_text(TextDocument& document, StringView text)
  150. {
  151. if (text.is_empty()) {
  152. clear(document);
  153. return true;
  154. }
  155. m_text.clear();
  156. Utf8View utf8_view(text);
  157. if (!utf8_view.validate()) {
  158. return false;
  159. }
  160. for (auto code_point : utf8_view)
  161. m_text.append(code_point);
  162. document.update_views({});
  163. return true;
  164. }
  165. void TextDocumentLine::append(TextDocument& document, u32 const* code_points, size_t length)
  166. {
  167. if (length == 0)
  168. return;
  169. m_text.append(code_points, length);
  170. document.update_views({});
  171. }
  172. void TextDocumentLine::append(TextDocument& document, u32 code_point)
  173. {
  174. insert(document, length(), code_point);
  175. }
  176. void TextDocumentLine::prepend(TextDocument& document, u32 code_point)
  177. {
  178. insert(document, 0, code_point);
  179. }
  180. void TextDocumentLine::insert(TextDocument& document, size_t index, u32 code_point)
  181. {
  182. if (index == length()) {
  183. m_text.append(code_point);
  184. } else {
  185. m_text.insert(index, code_point);
  186. }
  187. document.update_views({});
  188. }
  189. void TextDocumentLine::remove(TextDocument& document, size_t index)
  190. {
  191. if (index == length()) {
  192. m_text.take_last();
  193. } else {
  194. m_text.remove(index);
  195. }
  196. document.update_views({});
  197. }
  198. void TextDocumentLine::remove_range(TextDocument& document, size_t start, size_t length)
  199. {
  200. VERIFY(length <= m_text.size());
  201. Vector<u32> new_data;
  202. new_data.ensure_capacity(m_text.size() - length);
  203. for (size_t i = 0; i < start; ++i)
  204. new_data.append(m_text[i]);
  205. for (size_t i = (start + length); i < m_text.size(); ++i)
  206. new_data.append(m_text[i]);
  207. m_text = move(new_data);
  208. document.update_views({});
  209. }
  210. void TextDocumentLine::keep_range(TextDocument& document, size_t start_index, size_t length)
  211. {
  212. VERIFY(start_index + length < m_text.size());
  213. Vector<u32> new_data;
  214. new_data.ensure_capacity(m_text.size());
  215. for (size_t i = start_index; i <= (start_index + length); i++)
  216. new_data.append(m_text[i]);
  217. m_text = move(new_data);
  218. document.update_views({});
  219. }
  220. void TextDocumentLine::truncate(TextDocument& document, size_t length)
  221. {
  222. m_text.resize(length);
  223. document.update_views({});
  224. }
  225. void TextDocument::append_line(NonnullOwnPtr<TextDocumentLine> line)
  226. {
  227. lines().append(move(line));
  228. if (m_client_notifications_enabled) {
  229. for (auto* client : m_clients)
  230. client->document_did_append_line();
  231. }
  232. }
  233. void TextDocument::insert_line(size_t line_index, NonnullOwnPtr<TextDocumentLine> line)
  234. {
  235. lines().insert(line_index, move(line));
  236. if (m_client_notifications_enabled) {
  237. for (auto* client : m_clients)
  238. client->document_did_insert_line(line_index);
  239. }
  240. }
  241. NonnullOwnPtr<TextDocumentLine> TextDocument::take_line(size_t line_index)
  242. {
  243. auto line = lines().take(line_index);
  244. if (m_client_notifications_enabled) {
  245. for (auto* client : m_clients)
  246. client->document_did_remove_line(line_index);
  247. }
  248. return line;
  249. }
  250. void TextDocument::remove_line(size_t line_index)
  251. {
  252. lines().remove(line_index);
  253. if (m_client_notifications_enabled) {
  254. for (auto* client : m_clients)
  255. client->document_did_remove_line(line_index);
  256. }
  257. }
  258. void TextDocument::remove_all_lines()
  259. {
  260. lines().clear();
  261. if (m_client_notifications_enabled) {
  262. for (auto* client : m_clients)
  263. client->document_did_remove_all_lines();
  264. }
  265. }
  266. void TextDocument::register_client(Client& client)
  267. {
  268. m_clients.set(&client);
  269. }
  270. void TextDocument::unregister_client(Client& client)
  271. {
  272. m_clients.remove(&client);
  273. }
  274. void TextDocument::update_views(Badge<TextDocumentLine>)
  275. {
  276. notify_did_change();
  277. }
  278. void TextDocument::notify_did_change()
  279. {
  280. if (m_client_notifications_enabled) {
  281. for (auto* client : m_clients)
  282. client->document_did_change();
  283. }
  284. m_regex_needs_update = true;
  285. }
  286. void TextDocument::set_all_cursors(TextPosition const& position)
  287. {
  288. if (m_client_notifications_enabled) {
  289. for (auto* client : m_clients)
  290. client->document_did_set_cursor(position);
  291. }
  292. }
  293. DeprecatedString TextDocument::text() const
  294. {
  295. StringBuilder builder;
  296. for (size_t i = 0; i < line_count(); ++i) {
  297. auto& line = this->line(i);
  298. builder.append(line.view());
  299. if (i != line_count() - 1)
  300. builder.append('\n');
  301. }
  302. return builder.to_deprecated_string();
  303. }
  304. DeprecatedString TextDocument::text_in_range(TextRange const& a_range) const
  305. {
  306. auto range = a_range.normalized();
  307. if (is_empty() || line_count() < range.end().line() - range.start().line())
  308. return DeprecatedString("");
  309. StringBuilder builder;
  310. for (size_t i = range.start().line(); i <= range.end().line(); ++i) {
  311. auto& line = this->line(i);
  312. size_t selection_start_column_on_line = range.start().line() == i ? range.start().column() : 0;
  313. size_t selection_end_column_on_line = range.end().line() == i ? range.end().column() : line.length();
  314. if (!line.is_empty()) {
  315. builder.append(
  316. Utf32View(
  317. line.code_points() + selection_start_column_on_line,
  318. selection_end_column_on_line - selection_start_column_on_line));
  319. }
  320. if (i != range.end().line())
  321. builder.append('\n');
  322. }
  323. return builder.to_deprecated_string();
  324. }
  325. u32 TextDocument::code_point_at(TextPosition const& position) const
  326. {
  327. VERIFY(position.line() < line_count());
  328. auto& line = this->line(position.line());
  329. if (position.column() == line.length())
  330. return '\n';
  331. return line.code_points()[position.column()];
  332. }
  333. TextPosition TextDocument::next_position_after(TextPosition const& position, SearchShouldWrap should_wrap) const
  334. {
  335. auto& line = this->line(position.line());
  336. if (position.column() == line.length()) {
  337. if (position.line() == line_count() - 1) {
  338. if (should_wrap == SearchShouldWrap::Yes)
  339. return { 0, 0 };
  340. return {};
  341. }
  342. return { position.line() + 1, 0 };
  343. }
  344. return { position.line(), position.column() + 1 };
  345. }
  346. TextPosition TextDocument::previous_position_before(TextPosition const& position, SearchShouldWrap should_wrap) const
  347. {
  348. if (position.column() == 0) {
  349. if (position.line() == 0) {
  350. if (should_wrap == SearchShouldWrap::Yes) {
  351. auto& last_line = this->line(line_count() - 1);
  352. return { line_count() - 1, last_line.length() };
  353. }
  354. return {};
  355. }
  356. auto& prev_line = this->line(position.line() - 1);
  357. return { position.line() - 1, prev_line.length() };
  358. }
  359. return { position.line(), position.column() - 1 };
  360. }
  361. void TextDocument::update_regex_matches(StringView needle)
  362. {
  363. if (m_regex_needs_update || needle != m_regex_needle) {
  364. Regex<PosixExtended> re(needle);
  365. Vector<RegexStringView> views;
  366. for (size_t line = 0; line < m_lines.size(); ++line) {
  367. views.append(m_lines.at(line).view());
  368. }
  369. re.search(views, m_regex_result);
  370. m_regex_needs_update = false;
  371. m_regex_needle = DeprecatedString { needle };
  372. m_regex_result_match_index = -1;
  373. m_regex_result_match_capture_group_index = -1;
  374. }
  375. }
  376. TextRange TextDocument::find_next(StringView needle, TextPosition const& start, SearchShouldWrap should_wrap, bool regmatch, bool match_case)
  377. {
  378. if (needle.is_empty())
  379. return {};
  380. if (regmatch) {
  381. if (!m_regex_result.matches.size())
  382. return {};
  383. regex::Match match;
  384. bool use_whole_match { false };
  385. auto next_match = [&] {
  386. m_regex_result_match_capture_group_index = 0;
  387. if (m_regex_result_match_index == m_regex_result.matches.size() - 1) {
  388. if (should_wrap == SearchShouldWrap::Yes)
  389. m_regex_result_match_index = 0;
  390. else
  391. ++m_regex_result_match_index;
  392. } else
  393. ++m_regex_result_match_index;
  394. };
  395. if (m_regex_result.n_capture_groups) {
  396. if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
  397. next_match();
  398. else {
  399. // check if last capture group has been reached
  400. if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size()) {
  401. next_match();
  402. } else {
  403. // get to the next capture group item
  404. ++m_regex_result_match_capture_group_index;
  405. }
  406. }
  407. // use whole match, if there is no capture group for current index
  408. if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
  409. use_whole_match = true;
  410. else if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
  411. next_match();
  412. } else {
  413. next_match();
  414. }
  415. if (use_whole_match || !m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
  416. match = m_regex_result.matches.at(m_regex_result_match_index);
  417. else
  418. match = m_regex_result.capture_group_matches.at(m_regex_result_match_index).at(m_regex_result_match_capture_group_index);
  419. return TextRange { { match.line, match.column }, { match.line, match.column + match.view.length() } };
  420. }
  421. TextPosition position = start.is_valid() ? start : TextPosition(0, 0);
  422. TextPosition original_position = position;
  423. TextPosition start_of_potential_match;
  424. size_t needle_index = 0;
  425. Utf8View unicode_needle(needle);
  426. Vector<u32> needle_code_points;
  427. for (u32 code_point : unicode_needle)
  428. needle_code_points.append(code_point);
  429. do {
  430. auto ch = code_point_at(position);
  431. bool code_point_matches = false;
  432. if (needle_index >= needle_code_points.size())
  433. code_point_matches = false;
  434. else if (match_case)
  435. code_point_matches = ch == needle_code_points[needle_index];
  436. else
  437. code_point_matches = Unicode::to_unicode_lowercase(ch) == Unicode::to_unicode_lowercase(needle_code_points[needle_index]);
  438. if (code_point_matches) {
  439. if (needle_index == 0)
  440. start_of_potential_match = position;
  441. ++needle_index;
  442. if (needle_index >= needle_code_points.size())
  443. return { start_of_potential_match, next_position_after(position, should_wrap) };
  444. } else {
  445. if (needle_index > 0)
  446. position = start_of_potential_match;
  447. needle_index = 0;
  448. }
  449. position = next_position_after(position, should_wrap);
  450. } while (position.is_valid() && position != original_position);
  451. return {};
  452. }
  453. TextRange TextDocument::find_previous(StringView needle, TextPosition const& start, SearchShouldWrap should_wrap, bool regmatch, bool match_case)
  454. {
  455. if (needle.is_empty())
  456. return {};
  457. if (regmatch) {
  458. if (!m_regex_result.matches.size())
  459. return {};
  460. regex::Match match;
  461. bool use_whole_match { false };
  462. auto next_match = [&] {
  463. if (m_regex_result_match_index == 0) {
  464. if (should_wrap == SearchShouldWrap::Yes)
  465. m_regex_result_match_index = m_regex_result.matches.size() - 1;
  466. else
  467. --m_regex_result_match_index;
  468. } else
  469. --m_regex_result_match_index;
  470. m_regex_result_match_capture_group_index = m_regex_result.capture_group_matches.at(m_regex_result_match_index).size() - 1;
  471. };
  472. if (m_regex_result.n_capture_groups) {
  473. if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
  474. next_match();
  475. else {
  476. // check if last capture group has been reached
  477. if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size()) {
  478. next_match();
  479. } else {
  480. // get to the next capture group item
  481. --m_regex_result_match_capture_group_index;
  482. }
  483. }
  484. // use whole match, if there is no capture group for current index
  485. if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
  486. use_whole_match = true;
  487. else if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
  488. next_match();
  489. } else {
  490. next_match();
  491. }
  492. if (use_whole_match || !m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
  493. match = m_regex_result.matches.at(m_regex_result_match_index);
  494. else
  495. match = m_regex_result.capture_group_matches.at(m_regex_result_match_index).at(m_regex_result_match_capture_group_index);
  496. return TextRange { { match.line, match.column }, { match.line, match.column + match.view.length() } };
  497. }
  498. TextPosition position = start.is_valid() ? start : TextPosition(0, 0);
  499. position = previous_position_before(position, should_wrap);
  500. if (position.line() >= line_count())
  501. return {};
  502. TextPosition original_position = position;
  503. Utf8View unicode_needle(needle);
  504. Vector<u32> needle_code_points;
  505. for (u32 code_point : unicode_needle)
  506. needle_code_points.append(code_point);
  507. TextPosition end_of_potential_match;
  508. size_t needle_index = needle_code_points.size() - 1;
  509. do {
  510. auto ch = code_point_at(position);
  511. bool code_point_matches = false;
  512. if (needle_index >= needle_code_points.size())
  513. code_point_matches = false;
  514. else if (match_case)
  515. code_point_matches = ch == needle_code_points[needle_index];
  516. else
  517. code_point_matches = Unicode::to_unicode_lowercase(ch) == Unicode::to_unicode_lowercase(needle_code_points[needle_index]);
  518. if (code_point_matches) {
  519. if (needle_index == needle_code_points.size() - 1)
  520. end_of_potential_match = position;
  521. if (needle_index == 0)
  522. return { position, next_position_after(end_of_potential_match, should_wrap) };
  523. --needle_index;
  524. } else {
  525. if (needle_index < needle_code_points.size() - 1)
  526. position = end_of_potential_match;
  527. needle_index = needle_code_points.size() - 1;
  528. }
  529. position = previous_position_before(position, should_wrap);
  530. } while (position.is_valid() && position != original_position);
  531. return {};
  532. }
  533. Vector<TextRange> TextDocument::find_all(StringView needle, bool regmatch, bool match_case)
  534. {
  535. Vector<TextRange> ranges;
  536. TextPosition position;
  537. for (;;) {
  538. auto range = find_next(needle, position, SearchShouldWrap::No, regmatch, match_case);
  539. if (!range.is_valid())
  540. break;
  541. ranges.append(range);
  542. position = range.end();
  543. }
  544. return ranges;
  545. }
  546. Optional<TextDocumentSpan> TextDocument::first_non_skippable_span_before(TextPosition const& position) const
  547. {
  548. for (int i = m_spans.size() - 1; i >= 0; --i) {
  549. if (!m_spans[i].range.contains(position))
  550. continue;
  551. while ((i - 1) >= 0 && m_spans[i - 1].is_skippable)
  552. --i;
  553. if (i <= 0)
  554. return {};
  555. return m_spans[i - 1];
  556. }
  557. return {};
  558. }
  559. Optional<TextDocumentSpan> TextDocument::first_non_skippable_span_after(TextPosition const& position) const
  560. {
  561. size_t i = 0;
  562. // Find the first span containing the cursor
  563. for (; i < m_spans.size(); ++i) {
  564. if (m_spans[i].range.contains(position))
  565. break;
  566. }
  567. // Find the first span *after* the cursor
  568. // TODO: For a large number of spans, binary search would be faster.
  569. for (; i < m_spans.size(); ++i) {
  570. if (!m_spans[i].range.contains(position))
  571. break;
  572. }
  573. // Skip skippable spans
  574. for (; i < m_spans.size(); ++i) {
  575. if (!m_spans[i].is_skippable)
  576. break;
  577. }
  578. if (i < m_spans.size())
  579. return m_spans[i];
  580. return {};
  581. }
  582. TextPosition TextDocument::first_word_break_before(TextPosition const& position, bool start_at_column_before) const
  583. {
  584. if (position.column() == 0) {
  585. if (position.line() == 0) {
  586. return TextPosition(0, 0);
  587. }
  588. auto previous_line = this->line(position.line() - 1);
  589. return TextPosition(position.line() - 1, previous_line.length());
  590. }
  591. auto target = position;
  592. auto line = this->line(target.line());
  593. auto modifier = start_at_column_before ? 1 : 0;
  594. if (target.column() == line.length())
  595. modifier = 1;
  596. while (target.column() > 0 && is_ascii_blank(line.code_points()[target.column() - modifier]))
  597. target.set_column(target.column() - 1);
  598. auto is_start_alphanumeric = is_ascii_alphanumeric(line.code_points()[target.column() - modifier]);
  599. while (target.column() > 0) {
  600. auto prev_code_point = line.code_points()[target.column() - 1];
  601. if ((is_start_alphanumeric && !is_ascii_alphanumeric(prev_code_point)) || (!is_start_alphanumeric && is_ascii_alphanumeric(prev_code_point)))
  602. break;
  603. target.set_column(target.column() - 1);
  604. }
  605. return target;
  606. }
  607. TextPosition TextDocument::first_word_break_after(TextPosition const& position) const
  608. {
  609. auto target = position;
  610. auto line = this->line(target.line());
  611. if (position.column() >= line.length()) {
  612. if (position.line() >= this->line_count() - 1) {
  613. return position;
  614. }
  615. return TextPosition(position.line() + 1, 0);
  616. }
  617. while (target.column() < line.length() && is_ascii_space(line.code_points()[target.column()]))
  618. target.set_column(target.column() + 1);
  619. auto is_start_alphanumeric = is_ascii_alphanumeric(line.code_points()[target.column()]);
  620. while (target.column() < line.length()) {
  621. auto next_code_point = line.code_points()[target.column()];
  622. if ((is_start_alphanumeric && !is_ascii_alphanumeric(next_code_point)) || (!is_start_alphanumeric && is_ascii_alphanumeric(next_code_point)))
  623. break;
  624. target.set_column(target.column() + 1);
  625. }
  626. return target;
  627. }
  628. TextPosition TextDocument::first_word_before(TextPosition const& position, bool start_at_column_before) const
  629. {
  630. if (position.column() == 0) {
  631. if (position.line() == 0) {
  632. return TextPosition(0, 0);
  633. }
  634. auto previous_line = this->line(position.line() - 1);
  635. return TextPosition(position.line() - 1, previous_line.length());
  636. }
  637. auto target = position;
  638. auto line = this->line(target.line());
  639. if (target.column() == line.length())
  640. start_at_column_before = 1;
  641. auto nonblank_passed = !is_ascii_blank(line.code_points()[target.column() - start_at_column_before]);
  642. while (target.column() > 0) {
  643. auto prev_code_point = line.code_points()[target.column() - 1];
  644. nonblank_passed |= !is_ascii_blank(prev_code_point);
  645. if (nonblank_passed && is_ascii_blank(prev_code_point)) {
  646. break;
  647. } else if (is_ascii_punctuation(prev_code_point)) {
  648. target.set_column(target.column() - 1);
  649. break;
  650. }
  651. target.set_column(target.column() - 1);
  652. }
  653. return target;
  654. }
  655. void TextDocument::undo()
  656. {
  657. if (!can_undo())
  658. return;
  659. m_undo_stack.undo();
  660. notify_did_change();
  661. }
  662. void TextDocument::redo()
  663. {
  664. if (!can_redo())
  665. return;
  666. m_undo_stack.redo();
  667. notify_did_change();
  668. }
  669. void TextDocument::add_to_undo_stack(NonnullOwnPtr<TextDocumentUndoCommand> undo_command)
  670. {
  671. m_undo_stack.push(move(undo_command));
  672. }
  673. TextDocumentUndoCommand::TextDocumentUndoCommand(TextDocument& document)
  674. : m_document(document)
  675. {
  676. }
  677. InsertTextCommand::InsertTextCommand(TextDocument& document, DeprecatedString const& text, TextPosition const& position)
  678. : TextDocumentUndoCommand(document)
  679. , m_text(text)
  680. , m_range({ position, position })
  681. {
  682. }
  683. DeprecatedString InsertTextCommand::action_text() const
  684. {
  685. return "Insert Text";
  686. }
  687. bool InsertTextCommand::merge_with(GUI::Command const& other)
  688. {
  689. if (!is<InsertTextCommand>(other) || commit_time_expired())
  690. return false;
  691. auto const& typed_other = static_cast<InsertTextCommand const&>(other);
  692. if (typed_other.m_text.is_whitespace() && !m_text.is_whitespace())
  693. return false; // Skip if other is whitespace while this is not
  694. if (m_range.end() != typed_other.m_range.start())
  695. return false;
  696. if (m_range.start().line() != m_range.end().line())
  697. return false;
  698. StringBuilder builder(m_text.length() + typed_other.m_text.length());
  699. builder.append(m_text);
  700. builder.append(typed_other.m_text);
  701. m_text = builder.to_deprecated_string();
  702. m_range.set_end(typed_other.m_range.end());
  703. m_timestamp = Time::now_monotonic();
  704. return true;
  705. }
  706. void InsertTextCommand::perform_formatting(TextDocument::Client const& client)
  707. {
  708. const size_t tab_width = client.soft_tab_width();
  709. auto const& dest_line = m_document.line(m_range.start().line());
  710. bool const should_auto_indent = client.is_automatic_indentation_enabled();
  711. StringBuilder builder;
  712. size_t column = m_range.start().column();
  713. size_t line_indentation = dest_line.leading_spaces();
  714. bool at_start_of_line = line_indentation == column;
  715. for (auto input_char : m_text) {
  716. if (input_char == '\n') {
  717. size_t spaces_at_end = 0;
  718. if (column < line_indentation)
  719. spaces_at_end = line_indentation - column;
  720. line_indentation -= spaces_at_end;
  721. builder.append('\n');
  722. column = 0;
  723. if (should_auto_indent) {
  724. for (; column < line_indentation; ++column) {
  725. builder.append(' ');
  726. }
  727. }
  728. at_start_of_line = true;
  729. } else if (input_char == '\t') {
  730. size_t next_soft_tab_stop = ((column + tab_width) / tab_width) * tab_width;
  731. size_t spaces_to_insert = next_soft_tab_stop - column;
  732. for (size_t i = 0; i < spaces_to_insert; ++i) {
  733. builder.append(' ');
  734. }
  735. column = next_soft_tab_stop;
  736. if (at_start_of_line) {
  737. line_indentation = column;
  738. }
  739. } else {
  740. if (input_char == ' ') {
  741. if (at_start_of_line) {
  742. ++line_indentation;
  743. }
  744. } else {
  745. at_start_of_line = false;
  746. }
  747. builder.append(input_char);
  748. ++column;
  749. }
  750. }
  751. m_text = builder.to_deprecated_string();
  752. }
  753. void InsertTextCommand::redo()
  754. {
  755. auto new_cursor = m_document.insert_at(m_range.start(), m_text, m_client);
  756. // NOTE: We don't know where the range ends until after doing redo().
  757. // This is okay since we always do redo() after adding this to the undo stack.
  758. m_range.set_end(new_cursor);
  759. m_document.set_all_cursors(new_cursor);
  760. }
  761. void InsertTextCommand::undo()
  762. {
  763. m_document.remove(m_range);
  764. m_document.set_all_cursors(m_range.start());
  765. }
  766. RemoveTextCommand::RemoveTextCommand(TextDocument& document, DeprecatedString const& text, TextRange const& range)
  767. : TextDocumentUndoCommand(document)
  768. , m_text(text)
  769. , m_range(range)
  770. {
  771. }
  772. DeprecatedString RemoveTextCommand::action_text() const
  773. {
  774. return "Remove Text";
  775. }
  776. bool RemoveTextCommand::merge_with(GUI::Command const& other)
  777. {
  778. if (!is<RemoveTextCommand>(other) || commit_time_expired())
  779. return false;
  780. auto const& typed_other = static_cast<RemoveTextCommand const&>(other);
  781. if (m_range.start() != typed_other.m_range.end())
  782. return false;
  783. if (m_range.start().line() != m_range.end().line())
  784. return false;
  785. // Merge backspaces
  786. StringBuilder builder(m_text.length() + typed_other.m_text.length());
  787. builder.append(typed_other.m_text);
  788. builder.append(m_text);
  789. m_text = builder.to_deprecated_string();
  790. m_range.set_start(typed_other.m_range.start());
  791. m_timestamp = Time::now_monotonic();
  792. return true;
  793. }
  794. void RemoveTextCommand::redo()
  795. {
  796. m_document.remove(m_range);
  797. m_document.set_all_cursors(m_range.start());
  798. }
  799. void RemoveTextCommand::undo()
  800. {
  801. auto new_cursor = m_document.insert_at(m_range.start(), m_text);
  802. m_document.set_all_cursors(new_cursor);
  803. }
  804. InsertLineCommand::InsertLineCommand(TextDocument& document, TextPosition cursor, DeprecatedString&& text, InsertPosition pos)
  805. : TextDocumentUndoCommand(document)
  806. , m_cursor(cursor)
  807. , m_text(move(text))
  808. , m_pos(pos)
  809. {
  810. }
  811. void InsertLineCommand::redo()
  812. {
  813. size_t line_number = compute_line_number();
  814. m_document.insert_line(line_number, make<TextDocumentLine>(m_document, m_text));
  815. m_document.set_all_cursors(TextPosition { line_number, m_document.line(line_number).length() });
  816. }
  817. void InsertLineCommand::undo()
  818. {
  819. size_t line_number = compute_line_number();
  820. m_document.remove_line(line_number);
  821. m_document.set_all_cursors(m_cursor);
  822. }
  823. size_t InsertLineCommand::compute_line_number() const
  824. {
  825. if (m_pos == InsertPosition::Above)
  826. return m_cursor.line();
  827. if (m_pos == InsertPosition::Below)
  828. return m_cursor.line() + 1;
  829. VERIFY_NOT_REACHED();
  830. }
  831. DeprecatedString InsertLineCommand::action_text() const
  832. {
  833. StringBuilder action_text_builder;
  834. action_text_builder.append("Insert Line"sv);
  835. if (m_pos == InsertPosition::Above) {
  836. action_text_builder.append(" (Above)"sv);
  837. } else if (m_pos == InsertPosition::Below) {
  838. action_text_builder.append(" (Below)"sv);
  839. } else {
  840. VERIFY_NOT_REACHED();
  841. }
  842. return action_text_builder.to_deprecated_string();
  843. }
  844. ReplaceAllTextCommand::ReplaceAllTextCommand(GUI::TextDocument& document, DeprecatedString const& text, GUI::TextRange const& range, DeprecatedString const& action_text)
  845. : TextDocumentUndoCommand(document)
  846. , m_original_text(document.text())
  847. , m_new_text(text)
  848. , m_range(range)
  849. , m_action_text(action_text)
  850. {
  851. }
  852. void ReplaceAllTextCommand::redo()
  853. {
  854. m_document.remove(m_range);
  855. m_document.set_all_cursors(m_range.start());
  856. auto new_cursor = m_document.insert_at(m_range.start(), m_new_text, m_client);
  857. m_range.set_end(new_cursor);
  858. m_document.set_all_cursors(new_cursor);
  859. }
  860. void ReplaceAllTextCommand::undo()
  861. {
  862. m_document.remove(m_range);
  863. m_document.set_all_cursors(m_range.start());
  864. auto new_cursor = m_document.insert_at(m_range.start(), m_original_text, m_client);
  865. m_range.set_end(new_cursor);
  866. m_document.set_all_cursors(new_cursor);
  867. }
  868. bool ReplaceAllTextCommand::merge_with(GUI::Command const&)
  869. {
  870. return false;
  871. }
  872. DeprecatedString ReplaceAllTextCommand::action_text() const
  873. {
  874. return m_action_text;
  875. }
  876. IndentSelection::IndentSelection(TextDocument& document, size_t tab_width, TextRange const& range)
  877. : TextDocumentUndoCommand(document)
  878. , m_tab_width(tab_width)
  879. , m_range(range)
  880. {
  881. }
  882. void IndentSelection::redo()
  883. {
  884. auto const tab = DeprecatedString::repeated(' ', m_tab_width);
  885. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  886. m_document.insert_at({ i, 0 }, tab, m_client);
  887. }
  888. m_document.set_all_cursors(m_range.start());
  889. }
  890. void IndentSelection::undo()
  891. {
  892. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  893. m_document.remove({ { i, 0 }, { i, m_tab_width } });
  894. }
  895. m_document.set_all_cursors(m_range.start());
  896. }
  897. UnindentSelection::UnindentSelection(TextDocument& document, size_t tab_width, TextRange const& range)
  898. : TextDocumentUndoCommand(document)
  899. , m_tab_width(tab_width)
  900. , m_range(range)
  901. {
  902. }
  903. void UnindentSelection::redo()
  904. {
  905. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  906. if (m_document.line(i).leading_spaces() >= m_tab_width)
  907. m_document.remove({ { i, 0 }, { i, m_tab_width } });
  908. else
  909. m_document.remove({ { i, 0 }, { i, m_document.line(i).leading_spaces() } });
  910. }
  911. m_document.set_all_cursors(m_range.start());
  912. }
  913. void UnindentSelection::undo()
  914. {
  915. auto const tab = DeprecatedString::repeated(' ', m_tab_width);
  916. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++)
  917. m_document.insert_at({ i, 0 }, tab, m_client);
  918. m_document.set_all_cursors(m_range.start());
  919. }
  920. CommentSelection::CommentSelection(TextDocument& document, StringView prefix, StringView suffix, TextRange const& range)
  921. : TextDocumentUndoCommand(document)
  922. , m_prefix(prefix)
  923. , m_suffix(suffix)
  924. , m_range(range)
  925. {
  926. }
  927. void CommentSelection::undo()
  928. {
  929. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  930. if (m_document.line(i).is_empty())
  931. continue;
  932. auto line = m_document.line(i).to_utf8();
  933. auto prefix_start = line.find(m_prefix).value_or(0);
  934. m_document.line(i).keep_range(
  935. m_document,
  936. prefix_start + m_prefix.length(),
  937. m_document.line(i).last_non_whitespace_column().value_or(line.length()) - prefix_start - m_prefix.length() - m_suffix.length());
  938. }
  939. m_document.set_all_cursors(m_range.start());
  940. }
  941. void CommentSelection::redo()
  942. {
  943. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  944. if (m_document.line(i).is_empty())
  945. continue;
  946. m_document.insert_at({ i, 0 }, m_prefix, m_client);
  947. for (auto const& b : m_suffix.bytes()) {
  948. m_document.line(i).append(m_document, b);
  949. }
  950. }
  951. m_document.set_all_cursors(m_range.start());
  952. }
  953. UncommentSelection::UncommentSelection(TextDocument& document, StringView prefix, StringView suffix, TextRange const& range)
  954. : TextDocumentUndoCommand(document)
  955. , m_prefix(prefix)
  956. , m_suffix(suffix)
  957. , m_range(range)
  958. {
  959. }
  960. void UncommentSelection::undo()
  961. {
  962. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  963. if (m_document.line(i).is_empty())
  964. continue;
  965. m_document.insert_at({ i, 0 }, m_prefix, m_client);
  966. for (auto const& b : m_suffix.bytes()) {
  967. m_document.line(i).append(m_document, b);
  968. }
  969. }
  970. m_document.set_all_cursors(m_range.start());
  971. }
  972. void UncommentSelection::redo()
  973. {
  974. for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
  975. if (m_document.line(i).is_empty())
  976. continue;
  977. auto line = m_document.line(i).to_utf8();
  978. auto prefix_start = line.find(m_prefix).value_or(0);
  979. m_document.line(i).keep_range(
  980. m_document,
  981. prefix_start + m_prefix.length(),
  982. m_document.line(i).last_non_whitespace_column().value_or(line.length()) - prefix_start - m_prefix.length() - m_suffix.length());
  983. }
  984. m_document.set_all_cursors(m_range.start());
  985. }
  986. TextPosition TextDocument::insert_at(TextPosition const& position, StringView text, Client const* client)
  987. {
  988. TextPosition cursor = position;
  989. Utf8View utf8_view(text);
  990. for (auto code_point : utf8_view)
  991. cursor = insert_at(cursor, code_point, client);
  992. return cursor;
  993. }
  994. TextPosition TextDocument::insert_at(TextPosition const& position, u32 code_point, Client const*)
  995. {
  996. if (code_point == '\n') {
  997. auto new_line = make<TextDocumentLine>(*this);
  998. new_line->append(*this, line(position.line()).code_points() + position.column(), line(position.line()).length() - position.column());
  999. line(position.line()).truncate(*this, position.column());
  1000. insert_line(position.line() + 1, move(new_line));
  1001. notify_did_change();
  1002. return { position.line() + 1, 0 };
  1003. } else {
  1004. line(position.line()).insert(*this, position.column(), code_point);
  1005. notify_did_change();
  1006. return { position.line(), position.column() + 1 };
  1007. }
  1008. }
  1009. void TextDocument::remove(TextRange const& unnormalized_range)
  1010. {
  1011. if (!unnormalized_range.is_valid())
  1012. return;
  1013. auto range = unnormalized_range.normalized();
  1014. // First delete all the lines in between the first and last one.
  1015. for (size_t i = range.start().line() + 1; i < range.end().line();) {
  1016. remove_line(i);
  1017. range.end().set_line(range.end().line() - 1);
  1018. }
  1019. if (range.start().line() == range.end().line()) {
  1020. // Delete within same line.
  1021. auto& line = this->line(range.start().line());
  1022. if (line.length() == 0)
  1023. return;
  1024. bool whole_line_is_selected = range.start().column() == 0 && range.end().column() == line.length();
  1025. if (whole_line_is_selected) {
  1026. line.clear(*this);
  1027. } else {
  1028. line.remove_range(*this, range.start().column(), range.end().column() - range.start().column());
  1029. }
  1030. } else {
  1031. // Delete across a newline, merging lines.
  1032. VERIFY(range.start().line() == range.end().line() - 1);
  1033. auto& first_line = line(range.start().line());
  1034. auto& second_line = line(range.end().line());
  1035. Vector<u32> code_points;
  1036. code_points.append(first_line.code_points(), range.start().column());
  1037. if (!second_line.is_empty())
  1038. code_points.append(second_line.code_points() + range.end().column(), second_line.length() - range.end().column());
  1039. first_line.set_text(*this, move(code_points));
  1040. remove_line(range.end().line());
  1041. }
  1042. if (lines().is_empty()) {
  1043. append_line(make<TextDocumentLine>(*this));
  1044. }
  1045. notify_did_change();
  1046. }
  1047. bool TextDocument::is_empty() const
  1048. {
  1049. return line_count() == 1 && line(0).is_empty();
  1050. }
  1051. TextRange TextDocument::range_for_entire_line(size_t line_index) const
  1052. {
  1053. if (line_index >= line_count())
  1054. return {};
  1055. return { { line_index, 0 }, { line_index, line(line_index).length() } };
  1056. }
  1057. TextDocumentSpan const* TextDocument::span_at(TextPosition const& position) const
  1058. {
  1059. for (auto& span : m_spans) {
  1060. if (span.range.contains(position))
  1061. return &span;
  1062. }
  1063. return nullptr;
  1064. }
  1065. void TextDocument::set_unmodified()
  1066. {
  1067. m_undo_stack.set_current_unmodified();
  1068. }
  1069. void TextDocument::set_spans(u32 span_collection_index, Vector<TextDocumentSpan> spans)
  1070. {
  1071. m_span_collections.set(span_collection_index, move(spans));
  1072. merge_span_collections();
  1073. }
  1074. struct SpanAndCollectionIndex {
  1075. TextDocumentSpan span;
  1076. u32 collection_index { 0 };
  1077. };
  1078. void TextDocument::merge_span_collections()
  1079. {
  1080. Vector<SpanAndCollectionIndex> sorted_spans;
  1081. auto collection_indices = m_span_collections.keys();
  1082. quick_sort(collection_indices);
  1083. for (auto collection_index : collection_indices) {
  1084. auto spans = m_span_collections.get(collection_index).value();
  1085. for (auto span : spans) {
  1086. sorted_spans.append({ move(span), collection_index });
  1087. }
  1088. }
  1089. quick_sort(sorted_spans, [](SpanAndCollectionIndex const& a, SpanAndCollectionIndex const& b) {
  1090. if (a.span.range.start() == b.span.range.start()) {
  1091. return a.collection_index < b.collection_index;
  1092. }
  1093. return a.span.range.start() < b.span.range.start();
  1094. });
  1095. // The end of the TextRanges of spans are non-inclusive, i.e span range = [X,y).
  1096. // This transforms the span's range to be inclusive, i.e [X,Y].
  1097. auto adjust_end = [](GUI::TextDocumentSpan span) -> GUI::TextDocumentSpan {
  1098. span.range.set_end({ span.range.end().line(), span.range.end().column() == 0 ? 0 : span.range.end().column() - 1 });
  1099. return span;
  1100. };
  1101. Vector<SpanAndCollectionIndex> merged_spans;
  1102. for (auto& span_and_collection_index : sorted_spans) {
  1103. if (merged_spans.is_empty()) {
  1104. merged_spans.append(span_and_collection_index);
  1105. continue;
  1106. }
  1107. auto const& span = span_and_collection_index.span;
  1108. auto last_span_and_collection_index = merged_spans.last();
  1109. auto const& last_span = last_span_and_collection_index.span;
  1110. if (adjust_end(span).range.start() > adjust_end(last_span).range.end()) {
  1111. // Current span does not intersect with previous one, can simply append to merged list.
  1112. merged_spans.append(span_and_collection_index);
  1113. continue;
  1114. }
  1115. merged_spans.take_last();
  1116. if (span.range.start() > last_span.range.start()) {
  1117. SpanAndCollectionIndex first_part = last_span_and_collection_index;
  1118. first_part.span.range.set_end(span.range.start());
  1119. merged_spans.append(move(first_part));
  1120. }
  1121. SpanAndCollectionIndex merged_span;
  1122. merged_span.collection_index = span_and_collection_index.collection_index;
  1123. merged_span.span.range = { span.range.start(), min(span.range.end(), last_span.range.end()) };
  1124. merged_span.span.is_skippable = span.is_skippable | last_span.is_skippable;
  1125. merged_span.span.data = span.data ? span.data : last_span.data;
  1126. merged_span.span.attributes.color = span_and_collection_index.collection_index > last_span_and_collection_index.collection_index ? span.attributes.color : last_span.attributes.color;
  1127. merged_span.span.attributes.bold = span.attributes.bold | last_span.attributes.bold;
  1128. merged_span.span.attributes.background_color = span.attributes.background_color.has_value() ? span.attributes.background_color.value() : last_span.attributes.background_color;
  1129. merged_span.span.attributes.underline = span.attributes.underline | last_span.attributes.underline;
  1130. merged_span.span.attributes.underline_color = span.attributes.underline_color.has_value() ? span.attributes.underline_color.value() : last_span.attributes.underline_color;
  1131. merged_span.span.attributes.underline_style = span.attributes.underline_style;
  1132. merged_spans.append(move(merged_span));
  1133. if (span.range.end() == last_span.range.end())
  1134. continue;
  1135. if (span.range.end() > last_span.range.end()) {
  1136. SpanAndCollectionIndex last_part = span_and_collection_index;
  1137. last_part.span.range.set_start(last_span.range.end());
  1138. merged_spans.append(move(last_part));
  1139. continue;
  1140. }
  1141. SpanAndCollectionIndex last_part = last_span_and_collection_index;
  1142. last_part.span.range.set_start(span.range.end());
  1143. merged_spans.append(move(last_part));
  1144. }
  1145. m_spans.clear();
  1146. for (auto span : merged_spans) {
  1147. m_spans.append(move(span.span));
  1148. }
  1149. }
  1150. }