PageRenderTime 72ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/main.cpp

https://github.com/fdidier/expedit
C++ | 2454 lines | 1671 code | 350 blank | 433 comment | 460 complexity | dad1301f54e127d29a73e2f2b52dc380 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. #include "definition.h"
  2. #include "term.h"
  3. // original argv[1]
  4. char *file;
  5. // different components of the edited file name
  6. char *file_name;
  7. char *file_dir;
  8. char *file_ext;
  9. // used to compute internal name
  10. char *temp_name;
  11. // selection
  12. vector<int> selection;
  13. // **************************************************************
  14. // **************************************************************
  15. // interface with screen
  16. // All the code here could work withouth interacting with screen.cc
  17. // screen.cc will read the values in some exported variable to be able
  18. // to draw the text
  19. int search_highlight=0;
  20. string text_message;
  21. // We can modify this to change the screen display
  22. // It is just an hint, screen.cc will change it to the
  23. // exact value after each refresh.
  24. // So we can save this together with the cursor postion if
  25. // we want to restore the screen later exactly as it was.
  26. int first_screen_line = 0;
  27. // Indicate if the text as changed and is saved.
  28. int text_saved = 1;
  29. int text_modif = 0;
  30. // **************************************************************
  31. // **************************************************************
  32. // The main text class is implemented by a gap buffer
  33. // plus a small cache for the positions of some EOL
  34. // the data is in [text] with index in
  35. // [0,text_gap)
  36. // [text_restart, text_end)
  37. vector<int> text;
  38. int text_gap = 0;
  39. int text_restart = 0;
  40. int text_end = 0;
  41. // We keep the total numbers of lines
  42. // and the line number of the cursor.
  43. int text_lines = 0;
  44. int text_l = 0;
  45. // **************************************************************
  46. // **************************************************************
  47. // We have two kind of text position:
  48. // - a normal position which is an index in text and should skip the gap.
  49. // - an absolute position, which is the n-th caracter of the data.
  50. int text_index_from_absolute(int i)
  51. {
  52. if (i<text_gap) return i;
  53. else return i + text_restart - text_gap;
  54. }
  55. int text_absolute_from_index(int i)
  56. {
  57. if (i<text_gap) return i;
  58. else return i - text_restart + text_gap;
  59. }
  60. int text_compute_position(int begin, int size)
  61. {
  62. int res = begin + size;
  63. if (begin < text_gap && res >= text_gap)
  64. res += text_restart - text_gap;
  65. return res;
  66. }
  67. // **************************************************************
  68. // **************************************************************
  69. // Cache EOL number i
  70. // it goes into position i%cache_size of the cache.
  71. const int cache_size = 50;
  72. int cache_num[cache_size]= {0};
  73. int cache_pos[cache_size]= {0};
  74. void cache_init()
  75. {
  76. for (int i = 0; i < cache_size; ++i) {
  77. if (cache_num[i] != 0) {
  78. printf("error !!!!!!\n");
  79. exit(0);
  80. }
  81. if (cache_pos[i] != 0) {
  82. printf("error !!!!!!\n");
  83. exit(0);
  84. }
  85. cache_num[i] = 0;
  86. cache_pos[i] = 0;
  87. }
  88. }
  89. // Add an EOL to the cache
  90. void cache_add(int num, int pos)
  91. {
  92. int i = num % cache_size;
  93. cache_num[i] = num;
  94. cache_pos[i] = pos;
  95. }
  96. // Update the cache on EOL insert
  97. void cache_line_insert(int line_num, int pos)
  98. {
  99. int m = line_num % cache_size;
  100. int i = m;
  101. do {
  102. int old_index = i;
  103. i = (i + cache_size - 1) % cache_size;
  104. if (cache_num[i] >= line_num) {
  105. if (cache_num[old_index] == 0) {
  106. cache_num[old_index] = cache_num[i]+1;
  107. cache_pos[old_index] = cache_pos[i];
  108. }
  109. cache_num[i] = 0;
  110. }
  111. } while (i != m);
  112. cache_add(line_num, pos);
  113. }
  114. // Update the cache on EOL delete
  115. void cache_line_delete(int line_num)
  116. {
  117. int m = line_num % cache_size;
  118. if (cache_num[m] == line_num) {
  119. cache_num[m] = 0;
  120. }
  121. int i = m;
  122. do {
  123. int old_index = i;
  124. i = (i+1) % cache_size;
  125. if (cache_num[i] > line_num) {
  126. if (cache_num[old_index] == 0) {
  127. cache_num[old_index] = cache_num[i] - 1;
  128. cache_pos[old_index] = cache_pos[i];
  129. }
  130. cache_num[i]=0;
  131. }
  132. } while (i != m);
  133. }
  134. // **************************************************************
  135. // **************************************************************
  136. // Maintains a list of jump positions.
  137. const int max_jump_size = 6;
  138. int jump_size = 0;
  139. int jump_pos[max_jump_size];
  140. int jump_screen[max_jump_size];
  141. // The jump position are maintained as exact position in the text.
  142. // The only places we need to update them is on text_move
  143. // We also erase there the position not corresponding to real one anymore.
  144. // move is the new position of gap/restart in the text.
  145. void update_jump_on_move(int move)
  146. {
  147. fi (jump_size) {
  148. int p = jump_pos[i];
  149. if (p >= text_gap && p < text_restart) {
  150. jump_size --;
  151. swap(jump_pos[i], jump_pos[jump_size]);
  152. swap(jump_screen[i], jump_screen[jump_size]);
  153. } else if (p >= move && p < text_gap) {
  154. jump_pos[i] += text_restart - text_gap;
  155. } else if (p >= text_restart && p < move) {
  156. jump_pos[i] -= text_restart - text_gap;
  157. }
  158. }
  159. }
  160. void add_jump_pos()
  161. {
  162. // Don't add the same pos twice.
  163. // TODO: search for dup in the whole vector ?
  164. if (text_restart == jump_pos[0]) return;
  165. // Moves everything by one.
  166. jump_size++;
  167. if (jump_size > max_jump_size) {
  168. jump_size = max_jump_size;
  169. }
  170. for (int i = jump_size - 1; i > 0; i--) {
  171. jump_pos[i] = jump_pos[i - 1];
  172. jump_screen[i] = jump_screen[i - 1];
  173. }
  174. jump_pos[0] = text_restart;
  175. jump_screen[0] = screen_get_first_line();
  176. }
  177. // **************************************************************
  178. // **************************************************************
  179. const int text_blocsize = 1024;
  180. // check text size and realloc if needed
  181. // TODO : use realloc ?
  182. // TODO : problem with undo ? no because only happen on add char.
  183. void text_check(int l)
  184. {
  185. int gapsize = text_restart - text_gap;
  186. if (gapsize >= l) return;
  187. int num=0;
  188. while (gapsize + num < l) {
  189. num += text_blocsize;
  190. }
  191. int oldsize = text_end;
  192. for (int i=0; i<num; i++) text.push_back('#');
  193. for (int i = oldsize - 1; i >= text_restart; i--) {
  194. text[i + num] = text[i];
  195. text[i] = '#';
  196. }
  197. text_restart += num;
  198. text_end = text.size();
  199. // Update cache.
  200. fi (cache_size) {
  201. if (cache_pos[i] >= text_gap)
  202. cache_pos[i] += num;
  203. }
  204. // Update jump.
  205. fi (jump_size) {
  206. if (jump_pos[i] >= text_gap)
  207. jump_pos[i] += num;
  208. }
  209. }
  210. // **************************************************************
  211. // **************************************************************
  212. // Only functions that modify the text !
  213. // Not most efficient, but fast enough for normal use
  214. // assertion are not needed
  215. void text_add(int c)
  216. {
  217. text_check(1);
  218. text[text_gap++] = c;
  219. if (c == EOL) {
  220. text_l++;
  221. text_lines++;
  222. cache_line_insert(text_l, text_gap-1);
  223. }
  224. }
  225. void text_del()
  226. {
  227. if (text_restart>=text_end) return;
  228. if (text[text_restart]==EOL) {
  229. cache_line_delete(text_l+1);
  230. text_lines--;
  231. }
  232. text_restart++;
  233. }
  234. void text_back()
  235. {
  236. if (text_gap==0) return;
  237. text_gap--;
  238. if (text[text_gap]==EOL) {
  239. cache_line_delete(text_l);
  240. text_l--;
  241. text_lines--;
  242. }
  243. }
  244. // **************************************************************
  245. // **************************************************************
  246. // Put the cursor at a given position
  247. // [i] must design a caracter stored in text
  248. // that is in [0,text_gap) or [text_restart,text_end)
  249. // Everything else should not happen.
  250. // TODO: We enforce here that text_restart != text_end, but I am not sure
  251. // about this. This way we just ensure that we cannot get pass the
  252. // last EOL... Need to enforce this properly or remove it.
  253. void text_internal_move(int i) {
  254. if (i<0 || (i>=text_gap && i<text_restart) || i>=text_end) {
  255. text_message= "internal wrong move ";
  256. return;
  257. }
  258. update_jump_on_move(i);
  259. if (i >= 0 && i < text_gap) {
  260. while (text_gap != i) {
  261. text_restart--;
  262. text_gap--;
  263. text[text_restart]=text[text_gap];
  264. // text[text_gap] = '#';
  265. if (text[text_restart]==EOL) {
  266. cache_add(text_l, text_restart);
  267. text_l--;
  268. }
  269. }
  270. } else if (i > text_restart && i <= text_end) {
  271. while (text_restart != i) {
  272. if (text[text_restart]==EOL) {
  273. text_l++;
  274. cache_add(text_l, text_gap);
  275. }
  276. text[text_gap] = text[text_restart];
  277. // This do not work if text_gap == text_restart.
  278. // Another solution would be to never allow equality.
  279. // text[text_restart] = '#';
  280. text_gap++;
  281. text_restart++;
  282. }
  283. }
  284. }
  285. // **************************************************************
  286. // **************************************************************
  287. // undo stuff.
  288. // [pos] is the begining of the inserted/deleted text
  289. // [mark] is where the cursor was when the operation started
  290. // [del] is a flag to know if the [content] was deleted/inserted
  291. struct s_undo {
  292. int pos;
  293. int mrk;
  294. int del;
  295. vector<int> content;
  296. };
  297. // apply an operation to the text.
  298. void text_apply(struct s_undo op)
  299. {
  300. text_internal_move(text_index_from_absolute(op.pos));
  301. if (op.del) {
  302. fi (op.content.sz)
  303. text_add(op.content[i]);
  304. } else {
  305. fi (op.content.sz)
  306. text_del();
  307. }
  308. if (op.mrk>=0)
  309. text_internal_move(text_index_from_absolute(op.mrk));
  310. }
  311. vector<struct s_undo> undo_stack;
  312. vector<struct s_undo> redo_stack;
  313. int undo_pos=-1;
  314. int undo_mrk=-1;
  315. int undo_last;
  316. void undo_flush()
  317. {
  318. // Check if there is any pending operation
  319. if (undo_pos==-1) return;
  320. if (text_gap == undo_pos) {
  321. undo_pos=-1;
  322. return;
  323. }
  324. // At this point we are going to store a new
  325. // operation to the undo_stack.
  326. redo_stack.clear();
  327. int b=min(text_gap,undo_pos);
  328. int e=max(text_gap,undo_pos);
  329. struct s_undo op;
  330. op.mrk = undo_mrk;
  331. op.pos = b;
  332. if (undo_pos<text_gap)
  333. op.del = 0;
  334. else
  335. op.del = 1;
  336. while (b<e) {
  337. op.content.pb(text[b]);
  338. b++;
  339. }
  340. undo_stack.pb(op);
  341. undo_pos=-1;
  342. undo_mrk=-1;
  343. }
  344. int text_undo()
  345. {
  346. undo_flush();
  347. if (undo_stack.empty()) return 0;
  348. struct s_undo op;
  349. do {
  350. op=undo_stack.back();
  351. text_apply(op);
  352. op.del=1-op.del;
  353. redo_stack.pb(op);
  354. undo_stack.pop_back();
  355. } while (!undo_stack.empty() && op.mrk<0);
  356. return 1;
  357. }
  358. int text_redo()
  359. {
  360. undo_flush();
  361. if (redo_stack.empty()) return 0;
  362. struct s_undo op;
  363. do {
  364. op=redo_stack.back();
  365. text_apply(op);
  366. op.del=1-op.del;
  367. undo_stack.pb(op);
  368. redo_stack.pop_back();
  369. } while (!redo_stack.empty() && op.mrk<0);
  370. return 1;
  371. }
  372. void undo_savepos() {
  373. undo_last = text_gap;
  374. }
  375. void undo_start() {
  376. undo_flush();
  377. undo_pos = text_gap;
  378. undo_mrk = undo_last;
  379. undo_last=-1;
  380. }
  381. // ******************************************************
  382. // same as above but with undo support
  383. // ******************************************************
  384. void edit_text() {
  385. text_modif++;
  386. text_saved=0;
  387. }
  388. void text_putchar(int c)
  389. {
  390. edit_text();
  391. if (undo_pos == -1 || undo_pos>text_gap) undo_start();
  392. text_add(c);
  393. }
  394. void text_backspace()
  395. {
  396. if (text_gap==0) return;
  397. edit_text();
  398. if (undo_pos<0) undo_start();
  399. text_back();
  400. }
  401. void text_delete()
  402. {
  403. if (text_restart>=text_end) return;
  404. edit_text();
  405. if (undo_pos<text_gap) undo_start();
  406. text[undo_pos]=text[text_restart];
  407. undo_pos++;
  408. text_del();
  409. }
  410. /******************************************************/
  411. /******************************************************/
  412. // Put the cursor at a given position
  413. // [i] must design a caracter stored in text
  414. // that is in [0,text_gap) or [text_restart,text_end)
  415. // Everything else should not happen.
  416. void text_move(int i)
  417. {
  418. // assert that i is in range
  419. if (i<0 || (i>=text_gap && i<text_restart) || i>=text_end) {
  420. text_message= "wrong move ";
  421. return;
  422. }
  423. // we move so save current undo
  424. undo_flush();
  425. text_internal_move(i);
  426. }
  427. void text_move_to_absolute(int i)
  428. {
  429. text_move(text_index_from_absolute(i));
  430. }
  431. // **********************************************************
  432. // **********************************************************
  433. // return the indice of the begining of the line l
  434. // in [0,gap[ [restart,end[
  435. // this is mainly used by the displaying part of the editor.
  436. int text_line_begin(int l)
  437. {
  438. // special case
  439. if (l>text_lines) l=text_lines;
  440. if (l<=0) return 0;
  441. // beginning cached ?
  442. if (cache_num[l % cache_size] == l) {
  443. int t = cache_pos[l % cache_size] + 1;
  444. if (t == text_gap) t = text_restart;
  445. return t;
  446. }
  447. // line num and line begin
  448. int n=0;
  449. int p=0;
  450. // are we looking before text_gap
  451. // or after text_restart ?
  452. if (l>text_l) {
  453. n = text_l;
  454. p = text_restart;
  455. // find the next EOL and cache it
  456. while (n<l && p<text_end) {
  457. while (p<text_end && text[p] != EOL) p++;
  458. cache_add(n+1,p);
  459. n++;
  460. p++;
  461. }
  462. } else {
  463. n = text_l+1;
  464. p = text_gap;
  465. // find the previous EOL and cache it
  466. while (n>l && p>=0) {
  467. do {
  468. p--;
  469. } while (p>=0 && text[p] !=EOL);
  470. n--;
  471. cache_add(n,p);
  472. }
  473. p++;
  474. }
  475. // return
  476. if (p==text_gap) p=text_restart;
  477. return p;
  478. }
  479. int line_begin()
  480. {
  481. return text_line_begin(text_l);
  482. }
  483. int line_end()
  484. {
  485. int i = (text_l+1) % cache_size;
  486. if (cache_num[i] == text_l+1)
  487. return cache_pos[i];
  488. i=text_restart;
  489. while (i<text_end && text[i]!=EOL)
  490. i++;
  491. cache_add(text_l+1,i);
  492. return i;
  493. }
  494. // **********************************************************
  495. // after a change of line,
  496. // base pos should be the original position the cursor goto
  497. int base_pos;
  498. // return pos in the current line
  499. int compute_pos()
  500. {
  501. int t=line_begin();
  502. if (t==text_restart) return 0;
  503. return text_gap - t;
  504. }
  505. // goto a given pos in the current line.
  506. void line_goto(int pos)
  507. {
  508. int b=line_begin();
  509. int e=line_end();
  510. int i = text_compute_position(b, pos);
  511. if (i > e) i = e;
  512. text_move(i);
  513. }
  514. /**************************************************/
  515. // Here is where we get new char from screen and
  516. // record them if we need to.
  517. /**************************************************/
  518. int record=0;
  519. vector<int> record_data;
  520. void start_record() {
  521. record_data.clear();
  522. record = 1;
  523. }
  524. void end_record() {
  525. record = 0;
  526. }
  527. /* Swap file implementation in case of a crash:
  528. *
  529. * Just dump every keystroke in the swap file.
  530. * The behaviour is perfectly reproducible.
  531. * TODO : for this to be true, we need to convert
  532. * screen commands to char ...
  533. *
  534. * At some point I wanted to use it to undo stuff, but seems
  535. * like too much trouble. However, if we keep the same file
  536. * from one edit to another, we can reconstruct the undo struct,
  537. * jump between save and even go to some points just before an
  538. * undo that are lost in the undo struct ..
  539. *
  540. * timestamp and show the date of the save ?
  541. *
  542. */
  543. /* cool:
  544. * We refresh the screen each time the program is waiting for a
  545. * char from the keyboard. And only then.
  546. *
  547. * So everything here can work without any display.
  548. * like in redo mode. or macro.
  549. */
  550. // sould be with the other is_ function below.
  551. int is_indent()
  552. {
  553. int i=text_gap-1;
  554. while (i>=0 && text[i]==' ') i--;
  555. if (text[i]==EOL) return 1;
  556. else 0;
  557. }
  558. // if not -1, replay this char on the next getchar()
  559. int replay = -1;
  560. // buffer of char that will be used for processing
  561. // should be a queue.
  562. int pending = 0;
  563. vector<int> play_macro;
  564. // closure to call if non-null when play_macro is empty.
  565. // This way we can read a file small part by small part.
  566. int text_getchar()
  567. {
  568. // if replay >= 0, just return it
  569. // We only replay command char normally
  570. if (replay >= 0) {
  571. int c = replay;
  572. replay = -1;
  573. return c;
  574. }
  575. // if we are playing a macro,
  576. // get the char from there.
  577. if (!play_macro.empty()) {
  578. int c = play_macro[0];
  579. play_macro.erase(play_macro.begin());
  580. return c;
  581. }
  582. // add (+) to the title to reflect a modified text
  583. static int oldstatus;
  584. if (oldstatus != text_saved) {
  585. string title(file);
  586. if (!text_saved) title += " (+)";
  587. term_set_title((uchar *) title.c_str());
  588. oldstatus=text_saved;
  589. }
  590. // read the next input char
  591. // As a side effect, this update the screen
  592. int res = screen_getchar();
  593. // clear text message
  594. text_message.clear();
  595. // to group operation in the undo struct corresponding to
  596. // only one keystroke and remember the position that started it all
  597. undo_savepos();
  598. // record char ?
  599. if (record) {
  600. // Hack for completion in a macro...
  601. if (res == KEY_TAB &&
  602. record_data.size() > 0 &&
  603. record_data.back() == KEY_TAB &&
  604. !is_indent()) {
  605. // do not add extra tab.
  606. } else {
  607. record_data.pb(res);
  608. }
  609. }
  610. // return char
  611. return res;
  612. }
  613. /*******************************************************************/
  614. // useful functions
  615. /*******************************************************************/
  616. int is_begin()
  617. {
  618. int i=text_gap;
  619. return (i==0 || text[i-1]==EOL);
  620. }
  621. int is_end()
  622. {
  623. int i=text_restart;
  624. return (i==text_end || text[i]==EOL);
  625. }
  626. int is_space_before()
  627. {
  628. int i=text_gap;
  629. return (i==0 || text[i-1]==EOL || text[i-1]==' ');
  630. }
  631. int is_letter_before()
  632. {
  633. int i=text_gap-1;
  634. return (i>=0 && isletter(text[i]));
  635. }
  636. /*******************************************************************/
  637. // implementation of the commands
  638. void yank_line()
  639. {
  640. selection.clear();
  641. int c;
  642. text_move(line_begin());
  643. do {
  644. int i=text_gap;
  645. text_move(line_end()+1);
  646. while (i<text_gap) {
  647. selection.pb(text[i]);
  648. i++;
  649. }
  650. c =text_getchar();
  651. } while (c==KEY_COPY);
  652. replay=c;
  653. }
  654. void del_line()
  655. {
  656. selection.clear();
  657. // move to line begin
  658. text_move(line_begin());
  659. int c;
  660. do {
  661. // no more line ??
  662. if (text_lines==0) return;
  663. // save line
  664. int i=text_restart;
  665. do {
  666. selection.pb(text[i]);
  667. i++;
  668. } while (i<text.sz && selection[selection.sz-1]!=EOL);
  669. // delete it
  670. while (text_restart+1<text.sz && text[text_restart]!=EOL) {
  671. text_delete();
  672. }
  673. text_delete();
  674. c = text_getchar();
  675. } while (c==KEY_CUT);
  676. replay = c;
  677. }
  678. void text_print()
  679. {
  680. fi (selection.sz)
  681. text_putchar(selection[i]);
  682. }
  683. void insert_indent()
  684. {
  685. // move to first non blanc char
  686. while (text_restart<text_end && text[text_restart]==' ') {
  687. text_move(text_restart+1);
  688. }
  689. // add indent.
  690. int pos=compute_pos();
  691. do {
  692. text_putchar(' ');
  693. pos++;
  694. } while (pos % TABSTOP != 0);
  695. }
  696. void smart_backspace()
  697. {
  698. int i=text_gap;
  699. // remove trailing char
  700. if (i>0 && text[i-1]==EOL) {
  701. do {
  702. text_backspace();
  703. i--;
  704. } while (i>0 && text[i-1]==' ');
  705. return;
  706. }
  707. // remove indent if necessary
  708. if (i>0 && is_indent()) {
  709. // if (i>0 && text[i-1]==' ') {
  710. int pos = compute_pos();
  711. do {
  712. text_backspace();
  713. pos--;
  714. i--;
  715. } while (i>0 && text[i-1]==' ' && pos % TABSTOP !=0);
  716. return;
  717. }
  718. // normal behavior
  719. text_backspace();
  720. }
  721. void smart_delete()
  722. {
  723. int i=text_restart;
  724. // deal with indent
  725. if (i+1<text_end && text[i]==' ') {
  726. int pos=compute_pos();
  727. int pos2=0;
  728. while (i<text_end && text[i]==' ') {
  729. i++;
  730. pos2++;
  731. }
  732. do {
  733. text_delete();
  734. pos2--;
  735. } while (pos2>0 && (pos+pos2) % TABSTOP !=0);
  736. return;
  737. }
  738. // deal with end of line
  739. if (i+1<text_end && text[i]==EOL && text_gap>0 && text[text_gap-1]!=EOL) {
  740. do {
  741. text_delete();
  742. i++;
  743. } while (i+1<text_end && text[i+1]==' ');
  744. return;
  745. }
  746. // normal behavior
  747. text_delete();
  748. }
  749. int auto_indent=1;
  750. void smart_enter()
  751. {
  752. if (auto_indent==0) {
  753. text_putchar(EOL);
  754. return;
  755. }
  756. // compute line indent from current position
  757. int i=line_begin();
  758. int pos=0;
  759. while (i<text_gap && text[i]==' ') {
  760. i++;
  761. pos++;
  762. }
  763. // put EOL and correct indent
  764. text_putchar(EOL);
  765. fj(pos) text_putchar(' ');
  766. }
  767. void toggle_ai()
  768. {
  769. if (auto_indent) {
  770. text_message="auto indent off";
  771. auto_indent=0;
  772. } else {
  773. text_message="auto indent on";
  774. auto_indent=1;
  775. };
  776. }
  777. void open_line_after()
  778. {
  779. text_move(line_end());
  780. smart_enter();
  781. }
  782. void open_line_before()
  783. {
  784. text_move(line_begin());
  785. // compute line indent
  786. int pos=0;
  787. int i=text_restart;
  788. while (text[i++]==' ') pos++;
  789. // insert it
  790. for (int j=0; j<pos; j++) text_putchar(' ');
  791. text_putchar(EOL);
  792. text_move(text_gap-1);
  793. }
  794. //*************************************************
  795. //** Internal search functions
  796. //*************************************************
  797. // check if string [s] appears at position [i] in [text]
  798. // return 0 if not or the position just after the end if yes
  799. // treat whitespace at begining/end of [s] in a special way
  800. int match(vector<int> &s, int i)
  801. {
  802. int j=0;
  803. if (s.sz==0) return 0;
  804. // space at begining
  805. if (s[0]==' ') {
  806. int t=i;
  807. if (t>text_gap && t<=text_restart) t=text_gap;
  808. if (t>0 && isletter(text[t-1])) return 0;
  809. j=1;
  810. }
  811. // space at the end;
  812. int size = s.sz;
  813. if (s[size-1]==' ') size--;
  814. if (size==0) return 0;
  815. // match
  816. if (i<0) return 0;
  817. while(j<size && i<text_end) {
  818. if (i>=text_gap && i<text_restart) i=text_restart;
  819. if (s[j]==text[i]) {
  820. i++;
  821. j++;
  822. } else {
  823. return 0;
  824. }
  825. }
  826. if (i==text_end) return 0;
  827. // space at the end
  828. if (i>=text_gap && i<text_restart) i=text_restart;
  829. if (j<s.sz && isletter(text[i])) return 0;
  830. // return correct pos
  831. return i;
  832. }
  833. // search backward for [s] in [text], starting just before [i]
  834. // return the position at the beginning of the match
  835. // or -1 if no match.
  836. int search_prev(vector<int> &s, int i, int limit=0)
  837. {
  838. limit=max(0,limit);
  839. if (i>text_end) return -1;
  840. while (i>limit) {
  841. i--;
  842. if (i>=text_gap && i<text_restart) i=text_gap-1;
  843. if (match(s,i))
  844. return i;
  845. }
  846. return -1;
  847. }
  848. // search forward for [s] in [text], starting at [i]
  849. // return the position just after the match
  850. // or -1 if no match.
  851. int search_next(vector<int> &s, int i, int limit=text_end)
  852. {
  853. limit=min(text_end,limit);
  854. if (i<0) return -1;
  855. while (i<limit) {
  856. if (i>=text_gap && i<text_restart) i=text_restart;
  857. int t=match(s,i);
  858. if (t) return t;
  859. i++;
  860. }
  861. return -1;
  862. }
  863. //******************************************************************
  864. //** Completion
  865. //******************************************************************
  866. // dictionnary
  867. // so we do not propose twice the same completion !
  868. set< vector<int> > possibilities;
  869. // first completion = last one with the same begin
  870. // I think this is cool
  871. map< vector<int> , vector<int> > last_completions;
  872. // Interface function for the completion
  873. void text_complete()
  874. {
  875. undo_flush();
  876. possibilities.clear();
  877. vector<int> begin;
  878. vector<int> end;
  879. possibilities.insert(end);
  880. // find begining of current word
  881. int i=text_gap-1;
  882. while (i>=0 && !isalphanum(text[i])) i--;
  883. while (i>=0 && isalphanum(text[i])) i--;
  884. i++;
  885. // not after a word? return.
  886. if (i==text_gap) return;
  887. // [pos] always corresponds to the beginning of the last match
  888. int pos=i;
  889. // compute [begin] (that is the search pattern)
  890. // put white space first so we don't match partial word.
  891. begin.pb(' ');
  892. while (i<text_gap) {
  893. begin.pb(text[i]);
  894. i++;
  895. }
  896. // look first in the map
  897. int c = KEY_TAB;
  898. if (last_completions.find(begin)!=last_completions.end()) {
  899. end = last_completions[begin];
  900. possibilities.insert(end);
  901. fi (end.sz) text_putchar(end[i]);
  902. c = text_getchar();
  903. }
  904. // first search backward
  905. int backward=1;
  906. // No match or user not happy,
  907. // look in the text
  908. while (c==KEY_TAB)
  909. {
  910. // remove current completion proposal
  911. if (end.sz>0) {
  912. fi (end.sz) text_backspace();
  913. }
  914. // compute end
  915. // use possibilities to not propose the same thing twice
  916. do {
  917. end.clear();
  918. if (backward) {
  919. pos = search_prev(begin, pos);
  920. if (pos>=0) {
  921. int i=pos + begin.sz-1;
  922. while (isalphanum(text[i]) && i<text_gap) {
  923. end.pb(text[i]);
  924. i++;
  925. }
  926. } else {
  927. backward = 0;
  928. pos = text_restart;
  929. }
  930. } else {
  931. // pos is not at the beginning anymore,
  932. // but no problem
  933. pos = search_next(begin, pos);
  934. if (pos>=0) {
  935. int i=pos;
  936. while (isalphanum(text[i]) && i<text_end) {
  937. end.pb(text[i]);
  938. i++;
  939. }
  940. } else {
  941. // no more match,
  942. // quit the completion functions
  943. return;
  944. }
  945. }
  946. } while (possibilities.find(end)!=possibilities.end());
  947. // use current end,
  948. possibilities.insert(end);
  949. fi (end.sz) text_putchar(end[i]);
  950. c = text_getchar();
  951. }
  952. replay=c;
  953. // save completed text in map
  954. // except if it is empty
  955. if (!end.empty())
  956. last_completions[begin]=end;
  957. return;
  958. }
  959. // Almost same as previous one ...
  960. void text_search_complete(vector<int> &str)
  961. {
  962. possibilities.clear();
  963. vector<int> begin;
  964. vector<int> end;
  965. possibilities.insert(end);
  966. // do not complete if str is empty
  967. if (str.empty()) return;
  968. // set begin to str
  969. if (str[0]!=' ') begin.pb(' ');
  970. fi (str.sz)
  971. begin.pb(str[i]);
  972. // start wit cursor
  973. int pos=text_restart;
  974. // look first in the map
  975. int c = KEY_TAB;
  976. if (last_completions.find(begin)!=last_completions.end()) {
  977. end = last_completions[begin];
  978. possibilities.insert(end);
  979. fi (end.sz) text_putchar(end[i]);
  980. c = text_getchar();
  981. }
  982. // first search backward
  983. // then from start...
  984. int from_start=0;
  985. // No match or user not happy,
  986. // look in the text
  987. while (c==KEY_TAB)
  988. {
  989. // remove current completion proposal
  990. if (end.sz>0) {
  991. fi (end.sz) str.pop_back();
  992. }
  993. // compute end
  994. // use possibilities to not propose the same thing twice
  995. do {
  996. end.clear();
  997. pos = search_next(begin, pos);
  998. if (pos>=0) {
  999. int i=pos;
  1000. while (isletter(text[i]) && i<text_end) {
  1001. end.pb(text[i]);
  1002. i++;
  1003. }
  1004. } else {
  1005. if (from_start) return;
  1006. from_start = 1;
  1007. pos = 0;
  1008. }
  1009. } while (possibilities.find(end)!=possibilities.end());
  1010. // use current end,
  1011. possibilities.insert(end);
  1012. fi (end.sz) str.push_back(end[i]);
  1013. c = text_getchar();
  1014. }
  1015. replay=c;
  1016. // save completed text in map
  1017. // except if it is empty
  1018. if (!end.empty())
  1019. last_completions[begin]=end;
  1020. return;
  1021. }
  1022. //******************************************
  1023. //** user search functions
  1024. //******************************************
  1025. // Pattern is the current search pattern
  1026. vector<int> pattern;
  1027. int display_pattern=0;
  1028. // get id at current cursor position
  1029. void get_id()
  1030. {
  1031. vector<int> old_pattern = pattern;
  1032. pattern.clear();
  1033. pattern.pb(' ');
  1034. int i=text_gap;
  1035. while (i>0 && isletter(text[i-1])) i--;
  1036. while (i<text_gap) {
  1037. pattern.pb(text[i]);
  1038. i++;
  1039. }
  1040. i = text_restart;
  1041. while (i<text_end && isletter(text[i])) {
  1042. pattern.pb(text[i]);
  1043. i++;
  1044. }
  1045. pattern.pb(' ');
  1046. if (pattern.sz==2) pattern=old_pattern;
  1047. }
  1048. // search next occurrence of [pattern] from text_restart
  1049. // [pattern] is the previous pattern if (search_highlight==1)
  1050. // else it is the one returned by get_id()
  1051. int text_search_next()
  1052. {
  1053. if (search_highlight==0) {
  1054. get_id();
  1055. search_highlight=1;
  1056. // move at end of id instead of looking
  1057. // for the next one
  1058. int i = text_restart;
  1059. while (i<text_end && isletter(text[i])) {
  1060. i++;
  1061. }
  1062. text_move(i);
  1063. return 1;
  1064. }
  1065. int t = search_next(pattern,text_restart);
  1066. if (t>0) text_move(t);
  1067. else {
  1068. text_message = "search restarted on top";
  1069. t = search_next(pattern,0);
  1070. if (t>0) text_move(t);
  1071. else {
  1072. text_message = "word not found";
  1073. return 0;
  1074. }
  1075. }
  1076. return 1;
  1077. }
  1078. // search first occurrence of [pattern]
  1079. // [pattern] is the previous pattern if (search_highlight==1)
  1080. // else it is the one returned by get_id()
  1081. void search_first()
  1082. {
  1083. if (search_highlight==0) get_id();
  1084. search_highlight=1;
  1085. // search for it
  1086. // starting at the beginning
  1087. text_move(0);
  1088. text_search_next();
  1089. }
  1090. void search_again()
  1091. {
  1092. search_highlight=1;
  1093. text_search_next();
  1094. }
  1095. // search previous occurrence of [pattern] from text_gap
  1096. // [pattern] is the previous pattern if (search_highlight==1)
  1097. // else it is the one returned by get_id()
  1098. int text_search_prev()
  1099. {
  1100. if (search_highlight==0) get_id();
  1101. search_highlight=1;
  1102. int size = pattern.sz;
  1103. if (pattern.sz>0 && pattern[0]==' ') size--;
  1104. if (pattern.sz>0 && pattern[pattern.sz-1]==' ') size--;
  1105. int t = search_prev(pattern,text_gap-size);
  1106. if (t>=0) {
  1107. t = text_index_from_absolute(t+size);
  1108. text_move(t);
  1109. } else {
  1110. text_message = "search restarted on bottom";
  1111. t = search_prev(pattern,text_end-2);
  1112. if (t>=0) {
  1113. t = text_compute_position(t,size);
  1114. text_move(t);
  1115. } else {
  1116. text_message = "word not found";
  1117. return 0;
  1118. }
  1119. }
  1120. return 1;
  1121. }
  1122. // Let the user enter a new pattern
  1123. // if empty use the old one...
  1124. void text_new_search()
  1125. {
  1126. search_highlight=1;
  1127. vector<int> old_pattern = pattern;
  1128. pattern.clear();
  1129. while (1) {
  1130. if (pattern.empty()) {
  1131. display_pattern=0;
  1132. text_message="<search>";
  1133. } else {
  1134. display_pattern=1;
  1135. }
  1136. int c = text_getchar();
  1137. if (isprint(c)) {
  1138. pattern.pb(c);
  1139. continue;
  1140. }
  1141. if (c==KEY_BACKSPACE && !pattern.empty()) {
  1142. pattern.pop_back();
  1143. continue;
  1144. }
  1145. if (c==KEY_TAB) {
  1146. text_search_complete(pattern);
  1147. continue;
  1148. }
  1149. if (c==KEY_ENTER) {
  1150. if (pattern.empty())
  1151. pattern = old_pattern;
  1152. text_search_next();
  1153. break;
  1154. }
  1155. replay = c;
  1156. break;
  1157. }
  1158. display_pattern=0;
  1159. }
  1160. /******************************************************************/
  1161. /******************************************************************/
  1162. void text_kill_word()
  1163. {
  1164. while (text_gap>0 && !isletter(text[text_gap-1])) text_backspace();
  1165. while (text_gap>0 && isletter(text[text_gap-1])) text_backspace();
  1166. }
  1167. void text_delete_to_end()
  1168. {
  1169. while (text[text_restart]!=EOL)
  1170. text_delete();
  1171. }
  1172. // TODO: really bad undowise
  1173. void text_change_case()
  1174. {
  1175. int i=text_restart;
  1176. int c;
  1177. if (text[i]>='a' && text[i]<='z') c = text[i]-'a'+'A';
  1178. else if (text[i]>='A' && text[i]<='Z') c = text[i]-'A'+'a';
  1179. else return;
  1180. text_delete();
  1181. text_putchar(c);
  1182. }
  1183. void text_tab()
  1184. {
  1185. if (is_indent())
  1186. insert_indent();
  1187. else
  1188. text_complete();
  1189. }
  1190. void space_tab()
  1191. {
  1192. if (is_indent())
  1193. insert_indent();
  1194. else
  1195. text_putchar(' ');
  1196. }
  1197. // This is what can be recorded in an
  1198. // automatic macro. Always stays
  1199. // on the same line.
  1200. // MAYBE : add some inline movements.
  1201. void insert()
  1202. {
  1203. while (1)
  1204. {
  1205. int c = text_getchar();
  1206. // if (c==' ') space_tab();else
  1207. if (isprint(c)) text_putchar(c);
  1208. else switch (c)
  1209. {
  1210. case KEY_INSERT :
  1211. text_message="<insert>";
  1212. c=text_getchar();
  1213. if (c==EOL) {
  1214. replay=KEY_ENTER;
  1215. return;
  1216. }
  1217. text_putchar(c);
  1218. break;
  1219. case KEY_BACKSPACE :
  1220. if (is_begin()) {
  1221. replay=c;
  1222. return;
  1223. }
  1224. smart_backspace();
  1225. break;
  1226. case KEY_DELETE :
  1227. if (is_end()) {
  1228. replay=c;
  1229. return;
  1230. }
  1231. smart_delete();
  1232. break;
  1233. case KEY_KWORD: text_kill_word();break;
  1234. case KEY_DEND: text_delete_to_end();break;
  1235. case KEY_CASE: text_change_case();break;
  1236. case KEY_TAB: text_tab();break;
  1237. default:
  1238. replay=c;
  1239. return;
  1240. }
  1241. }
  1242. }
  1243. /******************************************************************/
  1244. /******************************************************************/
  1245. void text_up()
  1246. {
  1247. text_move(line_begin());
  1248. text_move(text_gap-1);
  1249. line_goto(base_pos);
  1250. }
  1251. void text_down()
  1252. {
  1253. text_move(line_end()+1);
  1254. line_goto(base_pos);
  1255. }
  1256. void text_back_word()
  1257. {
  1258. int i=text_gap;
  1259. // while (i>0 && (!isletter(text[i-1]))) i--;
  1260. // while (i>0 && isletter(text[i-1])) i--;
  1261. while (i>0 && isletter(text[i-1])) i--;
  1262. while (i>0 && (!isletter(text[i-1]))) i--;
  1263. text_move(i);
  1264. }
  1265. void text_next_word()
  1266. {
  1267. int i=text_restart;
  1268. while (i<text_end && (!isletter(text[i]))) i++;
  1269. while (i<text_end && isletter(text[i])) i++;
  1270. text_move(i);
  1271. }
  1272. void text_next_letter()
  1273. {
  1274. int i=text_restart;
  1275. while (i<text_end && isletter(text[i])) i++;
  1276. while (i<text_end && (!isletter(text[i]))) i++;
  1277. text_move(i);
  1278. }
  1279. /******************************************************************************/
  1280. /******************************************************************************/
  1281. // Justify the whole paragraph where
  1282. // a paragraph is a set of line that start with a letter (no white)
  1283. // Why we need this ? it is because of our line wrap policy ...
  1284. // it is convenient to have it, like in nano. Also quite convenient
  1285. // to check if a line is > 80 char or not.
  1286. void justify()
  1287. {
  1288. int i=text_gap;
  1289. int last=-1;
  1290. char c=text[text_restart];
  1291. while (i>0 && (text[i-1]!=EOL || isletter(c))) {
  1292. if (text[i-1]==EOL) last=i;
  1293. i--;
  1294. c=text[i];
  1295. }
  1296. if (last<0 || i==0) last=i;
  1297. text_move(last);
  1298. i=text_restart;
  1299. while (i+1<text_end && (text[i]!=EOL || isletter(text[i+1]))) {
  1300. if (text[i]==EOL) {
  1301. text_move(i);
  1302. text_delete();
  1303. text_putchar(' ');
  1304. }
  1305. i++;
  1306. }
  1307. text_move(last);
  1308. i=text_restart;
  1309. int count=0;
  1310. int b=-1;
  1311. while (i<text_end && text[i]!=EOL) {
  1312. if (text[i]==' ') b=i;
  1313. count++;
  1314. // Line length without EOL <= JUSTIFY.
  1315. if (count > JUSTIFY) {
  1316. if (b>0) {
  1317. text_move(b);
  1318. text_delete();
  1319. } else {
  1320. text_move(i);
  1321. }
  1322. text_putchar(EOL);
  1323. count=0;
  1324. b=-1;
  1325. i = text_restart-1;
  1326. }
  1327. i++;
  1328. }
  1329. }
  1330. /******************************************************************/
  1331. /******************************************************************/
  1332. // we store there the last automatic macro
  1333. vector<int> macro_data;
  1334. // absolute position in the text of where the last
  1335. // operation was executed
  1336. int macro_end=-1;
  1337. // debug
  1338. void macro_display() {
  1339. SS yo;
  1340. fi (macro_data.sz) yo << macro_data[i];
  1341. yo << '#';
  1342. yo << macro_end;
  1343. text_message = yo.str();
  1344. }
  1345. // insert is called throught this
  1346. int macro_record()
  1347. {
  1348. start_record();
  1349. insert();
  1350. end_record();
  1351. if (record_data.sz > 1) {
  1352. record_data.erase(record_data.end()-1);
  1353. macro_data = record_data;
  1354. // to be sure it will exit insert()
  1355. // next time we execute it...
  1356. macro_data.pb(KEY_NULL);
  1357. macro_end = text_gap;
  1358. return 1;
  1359. }
  1360. return 0;
  1361. }
  1362. void macro_exec()
  1363. {
  1364. play_macro = macro_data;
  1365. insert();
  1366. macro_end=text_gap;
  1367. play_macro.clear();
  1368. replay=-1;
  1369. }
  1370. void replace()
  1371. {
  1372. int i=0;
  1373. int limit=text_gap;
  1374. text_move_to_absolute(macro_end);
  1375. int old=0;
  1376. int loop=1;
  1377. while (loop && text_search_next())
  1378. {
  1379. i++;
  1380. if (text_gap>=limit && (macro_end<limit || macro_end>=text_gap))
  1381. loop=0;
  1382. old=text_gap;
  1383. macro_exec();
  1384. // Beware !!
  1385. // limit change because we replace stuff...
  1386. if (old<limit) {
  1387. limit += text_gap-old;
  1388. }
  1389. }
  1390. SS m;
  1391. m << i << " operations";
  1392. text_message = m.str();
  1393. }
  1394. void macro_till()
  1395. {
  1396. if (search_highlight) return replace();
  1397. int limit=text_l;
  1398. text_move_to_absolute(macro_end);
  1399. while (text_l!=limit)
  1400. {
  1401. if (text_l>limit) text_up();
  1402. else text_down();
  1403. macro_exec();
  1404. };
  1405. }
  1406. /******************************************************************/
  1407. /******************************************************************/
  1408. int text_goto() {
  1409. char c;
  1410. int num=0;
  1411. int count=0;
  1412. string my_message="";
  1413. while (1) {
  1414. if (count)
  1415. text_message = my_message;
  1416. else
  1417. text_message = "<goto>";
  1418. c=text_getchar();
  1419. if (isnum(c)) {
  1420. num = 10*num + c-'0';
  1421. count++;
  1422. my_message.pb(c);
  1423. } else if (c==KEY_BACKSPACE && count>0) {
  1424. num /=10;
  1425. my_message.erase(my_message.end()-1);
  1426. count--;
  1427. } else if (c==KEY_ENTER) {
  1428. num = (num + text_lines - 1) % text_lines;
  1429. int i=text_line_begin(num);
  1430. text_move(i);
  1431. line_goto(base_pos);
  1432. break;
  1433. } else {
  1434. replay=c;
  1435. break;
  1436. }
  1437. }
  1438. }
  1439. // ********************************************************
  1440. // ********************************************************
  1441. void jump_interface()
  1442. {
  1443. // Remove bad positions.
  1444. update_jump_on_move(text_restart);
  1445. // Return if nothing to do.
  1446. if (jump_size == 0) {
  1447. return;
  1448. }
  1449. int i=0;
  1450. int c = KEY_JUMP;
  1451. int saved_pos = text_restart;
  1452. int saved_screen = screen_get_first_line();
  1453. while (c == KEY_JUMP) {
  1454. if (i == jump_size) {
  1455. i = 0;
  1456. text_move(saved_pos);
  1457. screen_set_first_line(saved_screen);
  1458. } else {
  1459. text_move(jump_pos[i]);
  1460. screen_set_first_line(jump_screen[i]);
  1461. i++;
  1462. }
  1463. c = text_getchar();
  1464. }
  1465. jump_pos[i] = saved_pos;
  1466. jump_screen[i] = saved_screen;
  1467. replay = c;
  1468. }
  1469. int move_command(uchar c)
  1470. {
  1471. int search=0;
  1472. int base=1;
  1473. switch (c)
  1474. {
  1475. // Search move
  1476. case KEY_AGAIN: search_again(); search=1; break;
  1477. case KEY_FIND: text_new_search(); search=1; break;
  1478. case KEY_FIRST: search_first(); search=1; break;
  1479. case KEY_NEXT: text_search_next(); search=1; break;
  1480. case KEY_PREV: text_search_prev(); search=1; break;
  1481. // Move that do not reset base pos
  1482. case KEY_UP: text_up();base=0;break;
  1483. case KEY_DOWN: text_down();base=0;break;
  1484. case KEY_GOTO: text_goto();base=0;break;
  1485. case PAGE_UP: screen_ppage();line_goto(base_pos);base=0;break;
  1486. case PAGE_DOWN: screen_npage();line_goto(base_pos);base=0;break;
  1487. // Inline move that reset base pos
  1488. case KEY_TAB: text_next_letter();break;
  1489. case KEY_BWORD: text_back_word();break;
  1490. case KEY_FWORD: text_next_word();break;
  1491. case KEY_BEGIN: text_move(line_begin());break;
  1492. case KEY_END: text_move(line_end());break;
  1493. case KEY_LEFT: text_move(text_gap-1);break;
  1494. case KEY_RIGHT: text_move(text_restart+1);break;
  1495. default : return 0;
  1496. }
  1497. if (!search) search_highlight=0;
  1498. if (base) base_pos = compute_pos();
  1499. return 1;
  1500. }
  1501. // ********************************************************
  1502. // ********************************************************
  1503. //void yank_select(int mark) {
  1504. // int b,e;
  1505. // if (mark<text_gap) {
  1506. // b=mark;
  1507. // e=text_gap;
  1508. // } else {
  1509. // b=text_restart;
  1510. // e=mark + text_restart-text_gap;
  1511. // }
  1512. // selection.clear();
  1513. // while (b<e) {
  1514. // selection.pb(text[b]);
  1515. // b++;
  1516. // }
  1517. //}
  1518. //
  1519. //void del_select(int mark) {
  1520. // if (mark<text_gap) {
  1521. // while (mark<text_gap)
  1522. // text_backspace();
  1523. // } else {
  1524. // int limit = mark + text_restart - text_gap;
  1525. // while (text_restart<limit)
  1526. // text_delete();
  1527. // }
  1528. //}
  1529. //
  1530. //void text_select()
  1531. //{
  1532. // int mark = text_gap;
  1533. // while (1) {
  1534. // int c = text_getchar();
  1535. // if (move_command(c)) continue;
  1536. // switch (c) {
  1537. // case KEY_COPY : yank_select(mark);
  1538. // return;
  1539. // case KEY_CUT : yank_select(mark);
  1540. // del_select(mark);
  1541. // return;
  1542. // case KEY_PRINT : del_select(mark);
  1543. // text_print();
  1544. // return;
  1545. // case KEY_ESC : text_move_to_absolute(mark);
  1546. // base_pos=compute_pos();
  1547. // return;
  1548. // }
  1549. // replay = c;
  1550. // return;
  1551. // }
  1552. //}
  1553. // save pos on any modification...
  1554. int mainloop()
  1555. {
  1556. int first_move=1;
  1557. int c=0;
  1558. while (c!=KEY_QUIT)
  1559. {
  1560. // insert? + record?
  1561. if (macro_record()) {
  1562. add_jump_pos();
  1563. }
  1564. // other command to process
  1565. int b=1;
  1566. c = text_getchar();
  1567. if (first_move) add_jump_pos();
  1568. if (move_command(c)) {
  1569. first_move=0;
  1570. continue;
  1571. }
  1572. first_move=1;
  1573. switch (c) {
  1574. case KEY_JUMP: jump_interface();break;
  1575. case KEY_AI : toggle_ai();break;
  1576. // case KEY_MARK: text_select();b=0;break;
  1577. case KEY_UNDO: text_undo();break;
  1578. case KEY_TILL: macro_till();b=0;break;
  1579. case KEY_REDO:
  1580. if (!text_redo()) {
  1581. macro_exec();
  1582. b=0;
  1583. }
  1584. break;
  1585. case KEY_COPY: yank_line();break;
  1586. case KEY_CUT: del_line();break;
  1587. case KEY_PRINT: text_print();break;
  1588. case KEY_OLINE: open_line_before();break;
  1589. case KEY_JUSTIFY: justify();break;
  1590. case KEY_SAVE: text_save();break;
  1591. case KEY_ENTER: smart_enter();break;
  1592. // here only on line boundary ...
  1593. case KEY_BACKSPACE: smart_backspace();break;
  1594. case KEY_DELETE: smart_delete();break;
  1595. default: b=0;
  1596. }
  1597. if (b) {
  1598. base_pos=compute_pos();
  1599. }
  1600. }
  1601. }
  1602. /******************************************************************/
  1603. /******************************************************************/
  1604. // Clean this : mouse control from screen.cpp
  1605. // we have to clean pgup/pgdown from screen.cpp too
  1606. const int X_buffer_size=1024;
  1607. uchar X_buffer[X_buffer_size];
  1608. void mouse_X_select(int b, int e)
  1609. {
  1610. FILE *fd = popen("xclip 2>/dev/null", "w");
  1611. if (fd==NULL) {
  1612. // even if the command is not there, fd is valid !
  1613. text_message = "Unable to copy to X selection. Is xclip installed?";
  1614. return;
  1615. }
  1616. while (b<=e) {
  1617. int n=0;
  1618. while (b<=e && n+4<X_buffer_size) {
  1619. if (b==text_gap) b=text_restart;
  1620. int temp = text[b];
  1621. b++;
  1622. do {
  1623. X_buffer[n++] = temp & 0xFF;
  1624. temp >>=8;
  1625. } while (temp);
  1626. }
  1627. fwrite(X_buffer, 1, n, fd);
  1628. }
  1629. pclose(fd);
  1630. }
  1631. void mouse_X_paste()
  1632. {
  1633. undo_flush();
  1634. undo_savepos();
  1635. FILE *fd = popen("xclip -o 2>/dev/null","r");
  1636. if (fd == NULL) {
  1637. // even if the command is not there, fd is valid !
  1638. text_message="Unable to get X selection. Is xclip installed?";
  1639. return;
  1640. }
  1641. int n;
  1642. int l=0;
  1643. int p=0;
  1644. int ch;
  1645. do {
  1646. n=fread(X_buffer,1,X_buffer_size,fd);
  1647. fi (n) {
  1648. uchar c=X_buffer[i];
  1649. if (p<l) {
  1650. ch ^= c << (8*(p+1));
  1651. p++;
  1652. if (p==l) {
  1653. text_putchar(ch);
  1654. p=l=0;
  1655. }
  1656. continue;
  1657. }
  1658. if ((c >> 7)&1) {
  1659. /* compute utf8 encoding length */
  1660. l=0;
  1661. while ((c >> (6-l))&1) l++;
  1662. ch = c;
  1663. continue;
  1664. }
  1665. text_putchar(c);
  1666. }
  1667. } while(n);
  1668. pclose(fd);
  1669. }
  1670. void mouse_select(int b, int e) {
  1671. mouse_X_select(b,e);
  1672. selection.clear();
  1673. while (b<=e) {
  1674. if (b==text_gap) b=text_restart;
  1675. selection.pb(text[b]);
  1676. b++;
  1677. }
  1678. }
  1679. // not used ...
  1680. void mouse_delete(int b, int e)
  1681. {
  1682. undo_flush();
  1683. undo_savepos();
  1684. // convert position to absolute
  1685. b = text_absolute_from_index(b);
  1686. e = text_absolute_from_index(e);
  1687. int save = text_gap;
  1688. if (save>b) {
  1689. if (save<e) save = b;
  1690. else save -= (e-b)+1;
  1691. }
  1692. text_move_to_absolute(b);
  1693. while(b<=e) {
  1694. b++;
  1695. text_delete();
  1696. }
  1697. text_move_to_absolute(save);
  1698. }
  1699. void mouse_paste() {
  1700. mouse_X_paste();
  1701. // undo_flush();
  1702. // undo_savepos();
  1703. // text_print();
  1704. }
  1705. /******************************************************************/
  1706. /* file handling */
  1707. /******************************************************************/
  1708. // copy file to file_dir/.fe/file_name
  1709. int text_backup()
  1710. {
  1711. if (access(file,F_OK)!=0) return 1;
  1712. strcpy(temp_name,file_dir);
  1713. strcat(temp_name,".fe/");
  1714. strcat(temp_name,file_name);
  1715. const int buf_size=1024;
  1716. uchar buf[buf_size];
  1717. int res=1;
  1718. FILE *src = fopen(file,"rb");
  1719. FILE *dst = fopen(temp_name,"wb");
  1720. int num=0;
  1721. if (src!=NULL && dst != NULL)
  1722. do {
  1723. num = fread(buf, sizeof(uchar), buf_size, src);
  1724. if (num==0 && ferror(src)) {
  1725. res = 0;
  1726. break;
  1727. }
  1728. if (fwrite(buf, sizeof(uchar), num, dst) < num) {
  1729. res = 0;
  1730. break;
  1731. }
  1732. } while (num>0);
  1733. if (src==NULL || fclose(src) == EOF) res=0;
  1734. if (dst==NULL || fclose(dst) == EOF) res=0;
  1735. return res;
  1736. }
  1737. // open file and write new content
  1738. int text_write(char* name)
  1739. {
  1740. ofstream s(name,ios_base::trunc);
  1741. if (!s) return 0;
  1742. int trailing_space = 0;
  1743. fi (text_end) {
  1744. // jump gap if needed
  1745. if (i == text_gap) i = text_restart;
  1746. if (i == text_end) {
  1747. break;
  1748. }
  1749. // Remove trailing space at write time.
  1750. if (text[i] == ' ') {
  1751. trailing_space++;
  1752. continue;
  1753. } else {
  1754. if (text[i] != EOL) {
  1755. while (trailing_space > 0) {
  1756. s.put(' ');
  1757. trailing_space--;
  1758. }
  1759. }
  1760. trailing_space = 0;
  1761. }
  1762. // convert to utf-8
  1763. unsigned int temp = text[i];
  1764. do {
  1765. s.put(temp & 0xFF);
  1766. temp>>=8;
  1767. } while (temp);
  1768. }
  1769. s.close();
  1770. return 1;
  1771. }
  1772. int utf_isvalid(unsigned int c)
  1773. {
  1774. unsigned int t = c & 0xFF;
  1775. unsigned int l = 0;
  1776. // over long encoding
  1777. if (t==192 || t==193) return 0;
  1778. // too high
  1779. if (t>=245) return 0;
  1780. while ((t >> (7-l)) & 1) l++;
  1781. if (l==0 && (c>>8)) return 0;
  1782. if (l==1) return 0;
  1783. for (int i=1; i<l; i++) {
  1784. c >>= 8;
  1785. t = c & 0xFF;
  1786. if (t>>6 != 2) return 0;
  1787. }
  1788. if (c>>8) return 0;
  1789. return 1;
  1790. }
  1791. int open_file()
  1792. {
  1793. /* Open file */
  1794. ifstream inputStream(file);
  1795. if (!inputStream) return 0;
  1796. /* Put file in gap buffer text */
  1797. char c;
  1798. int temp=0;
  1799. int l=0;
  1800. unsigned int ch;
  1801. unsigned int pending;
  1802. while (1)
  1803. {
  1804. if (l) {
  1805. ch = pending & 0xFF;
  1806. pending >>= 8;
  1807. l--;
  1808. } else {
  1809. if (!inputStream.get(c)) break;
  1810. ch = uchar(c);
  1811. if ((c >> 7)&1)
  1812. {
  1813. // compute utf8 encoding length
  1814. l=0;
  1815. while ((c >> (6-l))&1 && l<3) l++;
  1816. // compute corresponding int
  1817. fi (l) {
  1818. if (!inputStream.get(c)) break;
  1819. ch ^= uchar(c) << (8*(i+1));
  1820. }
  1821. // if not valid, just use individual char
  1822. if (utf_isvalid(ch)) {
  1823. l = 0;
  1824. } else {
  1825. pending = ch >> 8;
  1826. ch &= 0xFF;
  1827. }
  1828. }
  1829. }
  1830. // convert tab in space
  1831. if (ch == '\t') {
  1832. do {

Large files files are truncated, but you can click here to view the full file