command.c 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828
  1. /* command.c, Ait, BSD 3-Clause, Kevin Bloom, 2023-2024,
  2. Derived from: Atto January 2017
  3. Derived from: AnthonyEditor January 93
  4. */
  5. #include "header.h"
  6. #include "termbox.h"
  7. #include "util.h"
  8. void quit() { done = 1; }
  9. void up()
  10. {
  11. curbp->b_point = lncolumn(curbp, upup(curbp, curwp, curbp->b_point),curbp->b_pcol - curwp->w_left);
  12. }
  13. void down()
  14. {
  15. curbp->b_point = lncolumn(curbp, dndn(curbp, curwp, curbp->b_point),curbp->b_pcol - curwp->w_left);
  16. }
  17. void lnbegin()
  18. {
  19. char_t *p;
  20. if(curbp->b_point == 0)
  21. return;
  22. p = ptr(curbp, curbp->b_point);
  23. while(*(p = ptr(curbp, curbp->b_point-1)) != '\n' && p > curbp->b_buf)
  24. --curbp->b_point;
  25. if(curbp->b_point != 0 && p == curbp->b_buf)
  26. --curbp->b_point;
  27. curbp->b_pcol = 0 + curwp->w_left;
  28. }
  29. void version() { msg(VERSION); }
  30. void top()
  31. {
  32. shift_pmark(TRUE, NOMARK);
  33. curbp->b_point = 0;
  34. curbp->b_pcol = 0 + curwp->w_left;
  35. }
  36. void bottom()
  37. {
  38. shift_pmark(TRUE, NOMARK);
  39. curbp->b_point = pos(curbp, curbp->b_ebuf);
  40. if (curbp->b_epage < pos(curbp, curbp->b_ebuf))
  41. curbp->b_reframe = 1;
  42. curbp->b_pcol = 0 + curwp->w_left;
  43. }
  44. void block() { curbp->b_mark = curbp->b_point; }
  45. void copy() { copy_cut(FALSE, TRUE, FALSE); }
  46. void cut() {
  47. currentcommand = KBD_CUT;
  48. copy_cut(TRUE, TRUE, FALSE);
  49. }
  50. void resize_terminal()
  51. {
  52. LINES = tb_height();
  53. COLS = tb_width();
  54. MSGLINE = LINES-1;
  55. resize();
  56. }
  57. void print_to_msgline(const char *msg)
  58. {
  59. printf_tb(0, MSGLINE, TB_DEFAULT, TB_DEFAULT, msg);
  60. tb_set_cursor(strlen(msg), MSGLINE);
  61. }
  62. void quit_ask()
  63. {
  64. if (modified_buffers() > 0) {
  65. const char *msg = "Modified buffers exist; really exit (y/N) ?";
  66. print_to_msgline(msg);
  67. clrtoeol(msg, MSGLINE);
  68. if (!yesno(FALSE)) {
  69. clrtoeol("", MSGLINE);
  70. return;
  71. }
  72. }
  73. quit();
  74. }
  75. void redraw()
  76. {
  77. window_t *wp;
  78. for (wp=wheadp; wp != NULL; wp = wp->w_next)
  79. wp->w_update = TRUE;
  80. update_display();
  81. }
  82. void left()
  83. {
  84. int n = prev_utf8_char_size();
  85. if(curbp->b_point == 0)
  86. return;
  87. while (0 < curbp->b_point && n-- > 0)
  88. --curbp->b_point;
  89. }
  90. void right()
  91. {
  92. if(curbp->b_point == pos(curbp, curbp->b_ebuf))
  93. return;
  94. int n = utf8_size(*ptr(curbp,curbp->b_point));
  95. while ((curbp->b_point < pos(curbp, curbp->b_ebuf)) && n-- > 0)
  96. ++curbp->b_point;
  97. }
  98. /* work out number of bytes based on first byte */
  99. int utf8_size(char_t c)
  100. {
  101. return tb_utf8_char_length(c);
  102. }
  103. int prev_utf8_char_size()
  104. {
  105. int n;
  106. for (n=2;n<5;n++)
  107. if (-1 < curbp->b_point - n && (utf8_size(*(ptr(curbp, curbp->b_point - n))) == n))
  108. return n;
  109. return 1;
  110. }
  111. void lnend()
  112. {
  113. char_t *p;
  114. int cols = 0;
  115. lnbegin(); // reset the line so we get the right number for `cols`
  116. while(*(p = ptr(curbp, curbp->b_point)) != '\n' && curbp->b_ebuf > p) {
  117. ++curbp->b_point;
  118. }
  119. /* loop until we get to the correct column */
  120. while(cols > curwp->w_cols) {
  121. cols -= curwp->w_cols;
  122. }
  123. curbp->b_pcol = cols + curwp->w_left; // set it for column-memory
  124. }
  125. void wleft()
  126. {
  127. char_t *p = NULL;
  128. if ((curbp->b_buf < p || p == NULL) && (!isspace(*(p = ptr(curbp, curbp->b_point))) || !is_symbol(*p)))
  129. --curbp->b_point;
  130. while (curbp->b_buf < p && (isspace(*(p = ptr(curbp, curbp->b_point))) || is_symbol(*p)) && curbp->b_buf < p)
  131. --curbp->b_point;
  132. while (curbp->b_buf < p && !isspace(*(p = ptr(curbp, curbp->b_point))) && !is_symbol(*p) && curbp->b_buf < p)
  133. --curbp->b_point;
  134. if(curbp->b_buf < p && (isspace(*(p = ptr(curbp, curbp->b_point))) || is_symbol(*p)))
  135. ++curbp->b_point;
  136. }
  137. void wleftdelete()
  138. {
  139. currentcommand = KBD_DELETE_WORD;
  140. iblock();
  141. wleft();
  142. copy_cut(TRUE, TRUE, FALSE);
  143. }
  144. void pgdown()
  145. {
  146. curbp->b_page = curbp->b_point = upup(curbp, curwp, curbp->b_epage);
  147. while (0 < curwp->w_top - curbp->b_row) {
  148. down();
  149. curbp->b_row--;
  150. }
  151. curbp->b_epage = pos(curbp, curbp->b_ebuf);
  152. curbp->b_pcol = 0 + curwp->w_left;
  153. }
  154. void pgup()
  155. {
  156. int i = curwp->w_rows;
  157. while (0 < --i) {
  158. curbp->b_page = upup(curbp, curwp, curbp->b_page);
  159. up();
  160. }
  161. curbp->b_pcol = 0 + curwp->w_left;
  162. }
  163. void wright()
  164. {
  165. char_t *p = NULL;
  166. if (p < curbp->b_ebuf && (!isspace(*(p = ptr(curbp, curbp->b_point))) || !is_symbol(*p)))
  167. ++curbp->b_point;
  168. while (p < curbp->b_ebuf && (isspace(*(p = ptr(curbp, curbp->b_point))) || is_symbol(*p)))
  169. ++curbp->b_point;
  170. while (p < curbp->b_ebuf && !isspace(*(p = ptr(curbp, curbp->b_point))) && !is_symbol(*p))
  171. ++curbp->b_point;
  172. }
  173. void wrightdelete()
  174. {
  175. currentcommand = KBD_DELETE_WORD;
  176. iblock();
  177. wright();
  178. copy_cut(TRUE, TRUE, FALSE);
  179. }
  180. void insert()
  181. {
  182. assert(curbp->b_gap <= curbp->b_egap);
  183. if (curbp->b_gap == curbp->b_egap && !growgap(curbp, CHUNK))
  184. return;
  185. curbp->b_point = movegap(curbp, curbp->b_point);
  186. /* overwrite if mid line, not EOL or EOF, CR will insert as normal */
  187. if ((curbp->b_flags & B_OVERWRITE) && *input != '\r' && *(ptr(curbp, curbp->b_point)) != '\n' && curbp->b_point < pos(curbp,curbp->b_ebuf) ) {
  188. *(ptr(curbp, curbp->b_point)) = *input;
  189. if (curbp->b_point < pos(curbp, curbp->b_ebuf))
  190. ++curbp->b_point;
  191. } else {
  192. *curbp->b_gap++ = *input == '\r' ? '\n' : *input;
  193. curbp->b_point = pos(curbp, curbp->b_egap);
  194. // force reframe if scrolled off bottom of screen and at EOF
  195. if (curbp->b_point == pos(curbp, curbp->b_ebuf) && curbp->b_point >= curbp->b_epage &&
  196. curwp->w_rows == (curwp->w_row - curwp->w_top))
  197. curbp->b_reframe = 1;
  198. }
  199. curbp->b_flags |= B_MODIFIED;
  200. undoset_flag = TRUE;
  201. currentcommand = KBD_INSERT;
  202. }
  203. void insert_str()
  204. {
  205. int len = strlen((const char *)input);
  206. assert(curbp->b_gap <= curbp->b_egap);
  207. undoset(INSERT, FALSE);
  208. if (curbp->b_gap == curbp->b_egap && !growgap(curbp, CHUNK))
  209. return;
  210. curbp->b_point = movegap(curbp, curbp->b_point);
  211. /* overwrite if mid line, not EOL or EOF, CR will insert as normal */
  212. if ((curbp->b_flags & B_OVERWRITE) && input[0] != '\r' && *(ptr(curbp, curbp->b_point)) != '\n' && curbp->b_point < pos(curbp,curbp->b_ebuf) ) {
  213. *(ptr(curbp, curbp->b_point)) = *input;
  214. if (curbp->b_point < pos(curbp, curbp->b_ebuf))
  215. ++curbp->b_point;
  216. } else {
  217. for(int i = 0; i < len; i++) {
  218. *curbp->b_gap++ = input[i] == '\r' ? '\n' : input[i];
  219. // if(input[i] == '\n' || input[i] == '\r')
  220. // curbp->b_line++;
  221. }
  222. curbp->b_point = pos(curbp, curbp->b_egap);
  223. // force reframe if scrolled off bottom of screen and at EOF
  224. if (curbp->b_point == pos(curbp, curbp->b_ebuf) && curbp->b_point >= curbp->b_epage &&
  225. curwp->w_rows == (curwp->w_row - curwp->w_top))
  226. curbp->b_reframe = 1;
  227. }
  228. curbp->b_flags |= B_MODIFIED;
  229. undoset_flag = TRUE;
  230. }
  231. void insert_unicode()
  232. {
  233. int len = strlen((const char *)unicode_buf);
  234. assert(curbp->b_gap <= curbp->b_egap);
  235. undoset(INSERT, lastcommand == KBD_INSERT);
  236. if (curbp->b_gap == curbp->b_egap && !growgap(curbp, CHUNK))
  237. return;
  238. curbp->b_point = movegap(curbp, curbp->b_point);
  239. /* overwrite if mid line, not EOL or EOF, CR will insert as normal */
  240. for(int i = 0; i < len; i++) {
  241. *curbp->b_gap++ = unicode_buf[i];
  242. }
  243. curbp->b_point = pos(curbp, curbp->b_egap);
  244. // force reframe if scrolled off bottom of screen and at EOF
  245. if (curbp->b_point == pos(curbp, curbp->b_ebuf) && curbp->b_point >= curbp->b_epage &&
  246. curwp->w_rows == (curwp->w_row - curwp->w_top))
  247. curbp->b_reframe = 1;
  248. curbp->b_flags |= B_MODIFIED;
  249. undoset_flag = TRUE;
  250. unicode_buf[0] = '\0';
  251. currentcommand = KBD_INSERT;
  252. }
  253. void backsp()
  254. {
  255. undoset(BACKSP, lastcommand == KBD_DELETE_CHAR);
  256. if(curbp->b_point != 0 && *ptr(curbp, curbp->b_point - 1) == '\n')
  257. curbp->b_line--;
  258. curbp->b_point = movegap(curbp, curbp->b_point);
  259. if (curbp->b_buf < curbp->b_gap) {
  260. curbp->b_gap -= prev_utf8_char_size();
  261. curbp->b_flags |= B_MODIFIED;
  262. }
  263. curbp->b_point = pos(curbp, curbp->b_egap);
  264. currentcommand = KBD_DELETE_CHAR;
  265. }
  266. void delete()
  267. {
  268. undoset(DELETE, lastcommand == KBD_DELETE_CHAR);
  269. curbp->b_point = movegap(curbp, curbp->b_point);
  270. if (curbp->b_egap < curbp->b_ebuf) {
  271. curbp->b_egap += utf8_size(*curbp->b_egap);
  272. curbp->b_point = pos(curbp, curbp->b_egap);
  273. curbp->b_flags |= B_MODIFIED;
  274. }
  275. currentcommand = KBD_DELETE_CHAR;
  276. }
  277. void gotoline()
  278. {
  279. int line, showdefault = lastline > 0;
  280. point_t p;
  281. char *prompt;
  282. if(showdefault)
  283. asprintf(&prompt, "Goto line (default %d): ", lastline);
  284. else
  285. asprintf(&prompt, "Goto line: ");
  286. if (getinput(prompt, temp, STRBUF_S, F_CLEAR, showdefault)) {
  287. line = temp[0] == 0 && showdefault ? lastline : atoi(temp);
  288. p = line_to_point(line);
  289. if (p != -1) {
  290. shift_pmark(TRUE, NOMARK);
  291. curbp->b_point = p;
  292. curbp->b_pcol = 0 + curwp->w_left;
  293. if (curbp->b_epage < pos(curbp, curbp->b_ebuf)) curbp->b_reframe = 1;
  294. curwp->w_update = TRUE;
  295. curwp->w_recenter = TRUE;
  296. lastline = line;
  297. msg("Line %d", line);
  298. } else {
  299. msg("Line %d, not found", line);
  300. }
  301. }
  302. clrtoeol("", MSGLINE);
  303. free(prompt);
  304. prompt = NULL;
  305. }
  306. void gotocolumn()
  307. {
  308. int col, remainder = 0, showdefault = lastcol > 0;
  309. point_t opoint = curbp->b_point, end = pos(curbp, curbp->b_ebuf);
  310. char *prompt;
  311. if(showdefault)
  312. asprintf(&prompt, "Goto column (default %d): ", lastcol);
  313. else
  314. asprintf(&prompt, "Goto column: ");
  315. if (getinput(prompt, temp, STRBUF_S, F_CLEAR, showdefault)) {
  316. col = temp[0] == 0 && showdefault ? lastcol : atoi(temp);
  317. remainder = col - curbp->b_col - 1;
  318. if(remainder > 0) {
  319. shift_pmark(TRUE, NOMARK);
  320. if(*ptr(curbp, curbp->b_point) == '\n') {
  321. msg("Column %d, not found.", col);
  322. return;
  323. }
  324. for(; remainder > 0; remainder--) {
  325. right();
  326. if((*ptr(curbp, curbp->b_point) == '\n' ||
  327. curbp->b_point == end) &&
  328. remainder != 1) {
  329. curbp->b_point = opoint;
  330. msg("Column %d, not found.", col);
  331. return;
  332. }
  333. }
  334. } else if(remainder < 0) {
  335. shift_pmark(TRUE, NOMARK);
  336. remainder *= -1;
  337. if(curbp->b_point == 0 ||
  338. *ptr(curbp, curbp->b_point - 1) == '\n') {
  339. msg("Column %d, not found.", col);
  340. return;
  341. }
  342. for(; remainder > 0; remainder--) {
  343. left();
  344. if((*ptr(curbp, curbp->b_point - 1) == '\n' ||
  345. curbp->b_point == 0) &&
  346. remainder != 1) {
  347. curbp->b_point = opoint;
  348. msg("Column %d, not found.", col);
  349. return;
  350. }
  351. }
  352. }
  353. lastcol = col;
  354. }
  355. clrtoeol("", MSGLINE);
  356. free(prompt);
  357. prompt = NULL;
  358. }
  359. void jumptorow()
  360. {
  361. int line = -1, j = 0, i = 0, current, lastln, pageln;
  362. char num[3] = { 0, 0, 0 };
  363. struct tb_event ev;
  364. char *prompt = "Jump to line reference: ";
  365. int start_col = strlen(prompt), match = FALSE;
  366. char opts[10] = {'f','j','d','k','s','l','g','h', 'a', ';'};
  367. char chars[curwp->w_rows][2];
  368. point_t point;
  369. char_t *p;
  370. char f, s;
  371. int count = 0, fp = 0, sp = 0;
  372. int w_row = curwp->w_row - curwp->w_top;
  373. get_line_stats(&current, &lastln, curbp);
  374. pageln = current - w_row;
  375. point = curbp->b_page;
  376. p = ptr(curbp, point);
  377. for(int i = 0; i < curwp->w_rows && pageln <= lastln; i++) {
  378. f = opts[fp];
  379. s = opts[sp];
  380. chars[i][0] = f;
  381. chars[i][1] = s;
  382. printf_tb(curwp->w_left, curwp->w_top+i, TB_RED, TB_CYAN, "%c%c", f,s);
  383. sp++;
  384. count++;
  385. if(count > 7) {
  386. fp++;
  387. sp = 0;
  388. count = 0;
  389. }
  390. int c = 1;
  391. while(*(p = ptr(curbp, point)) != '\n' &&
  392. curbp->b_ebuf > p && c < curwp->w_cols) {
  393. ++point;
  394. c++;
  395. }
  396. if(*p == '\n' || pageln == lastln)
  397. pageln++;
  398. ++point;
  399. p = ptr(curbp, point);
  400. }
  401. display_prompt_and_response(prompt, num);
  402. tb_present();
  403. while(j < 2) {
  404. display_prompt_and_response(prompt, num);
  405. tb_present();
  406. if(execute_kbd_macro) {
  407. use_kbd_macro(&ev);
  408. } else if(tb_poll_event(&ev) != TB_OK)
  409. break;
  410. if(record_input) {
  411. record_buffer[record_buffer_index] = ev;
  412. record_buffer_index++;
  413. }
  414. if(ev.key == TB_KEY_CTRL_G) {
  415. clrtoeol("", MSGLINE);
  416. return;
  417. }
  418. if(j < 2) {
  419. num[j] = ev.ch;
  420. tb_set_cursor(start_col, MSGLINE);
  421. addstr(num);
  422. point = curbp->b_page;
  423. p = ptr(curbp, point);
  424. if(j == 0) {
  425. pageln = current - w_row;
  426. for(int i = 0; i < curwp->w_rows && pageln <= lastln; i++) {
  427. if(chars[i][0] == ev.ch) {
  428. match = TRUE;
  429. if(*p != '\n')
  430. p = ptr(curbp, point+1);
  431. if(*p == '\0')
  432. *p = ' ';
  433. printf_tb(curwp->w_left, curwp->w_top+i, TB_RED, TB_CYAN, "%c", chars[i][1]);
  434. printf_tb(curwp->w_left+1, curwp->w_top+i, TB_DEFAULT, TB_DEFAULT, "%c", *p == '\n' ? ' ' : *p);
  435. } else {
  436. printf_tb(curwp->w_left, curwp->w_top+i, TB_DEFAULT, TB_DEFAULT, "%c", *p == '\n' ? ' ' : *p);
  437. if(*p != '\n')
  438. p = ptr(curbp, point+1);
  439. if(*p == '\0')
  440. *p = ' ';
  441. printf_tb(curwp->w_left+1, curwp->w_top+i, TB_DEFAULT, TB_DEFAULT, "%c", *p == '\n' ? ' ' : *p);
  442. }
  443. int c = 1;
  444. while(*(p = ptr(curbp, point)) != '\n' &&
  445. curbp->b_ebuf > p && c < curwp->w_cols) {
  446. ++point;
  447. c++;
  448. }
  449. if(*p == '\n' || pageln == lastln)
  450. pageln++;
  451. ++point;
  452. p = ptr(curbp, point);
  453. }
  454. }
  455. j++;
  456. }
  457. if(!match) {
  458. clrtoeol("", MSGLINE);
  459. return;
  460. }
  461. }
  462. for(; i < curwp->w_rows; i++) {
  463. if(chars[i][0] == num[0] && chars[i][1] == num[1]) {
  464. line = w_row - i;
  465. break;
  466. }
  467. }
  468. if(i == curwp->w_rows) {
  469. msg("Out of bounds");
  470. return;
  471. }
  472. shift_pmark(TRUE, NOMARK);
  473. if(line > 0) {
  474. for(; line > 0; line--) {
  475. up();
  476. }
  477. } else {
  478. for(; line < 0; line++) {
  479. down();
  480. }
  481. }
  482. clrtoeol("", MSGLINE);
  483. }
  484. void jumpword()
  485. {
  486. point_t current = curbp->b_page;
  487. char num[3] = { 'f', 'f', 0 };
  488. int j = 0, match = FALSE;
  489. char starting[1];
  490. struct tb_event ev;
  491. char *prompt = "Jump to word starting with: ";
  492. int start_col = strlen(prompt);
  493. char opts[10] = {'f','j','d','k','s','l','g','h', 'a', ';'};
  494. int diff = curbp->b_epage - curbp->b_page;
  495. char chars[diff][2];
  496. point_t point = -1;
  497. int begin = TRUE, is_white = FALSE, is_symb = FALSE, charlen = 0;
  498. char_t *p, *tp;
  499. char f, s;
  500. int count = 0, fp = 0, sp = 0, x = 0, y = 0;
  501. display_prompt_and_response(prompt, starting);
  502. tb_present();
  503. if(execute_kbd_macro) {
  504. use_kbd_macro(&ev);
  505. } else if(tb_poll_event(&ev) != TB_OK)
  506. return;
  507. if(record_input) {
  508. record_buffer[record_buffer_index] = ev;
  509. record_buffer_index++;
  510. }
  511. if(ev.key == TB_KEY_CTRL_G) {
  512. clrtoeol("", MSGLINE);
  513. return;
  514. }
  515. starting[0] = (unsigned)ev.ch;
  516. for(; current < curbp->b_epage; current++) {
  517. p = ptr(curbp, current);
  518. is_white = isspace(*p);
  519. is_symb = is_symbol(*p);
  520. if(is_white || is_symb || current == 0)
  521. begin = TRUE;
  522. if(*p == (char_t)starting[0] && begin) {
  523. f = opts[fp];
  524. s = opts[sp];
  525. chars[current-curbp->b_page][0] = f;
  526. chars[current-curbp->b_page][1] = s;
  527. charlen++;
  528. printf_tb(curwp->w_left+x, curwp->w_top+y, TB_RED, TB_CYAN, "%c%c", f,s);
  529. sp++;
  530. count++;
  531. if(count > 7) {
  532. fp++;
  533. sp = 0;
  534. count = 0;
  535. }
  536. begin = FALSE;
  537. }
  538. if(!is_white && !is_symb)
  539. begin = FALSE;
  540. x++;
  541. if(*p == '\t')
  542. x += (TAB_SIZE - 2);
  543. if(*p < 31)
  544. x++;
  545. if(*p == '\n' || x >= curwp->w_cols) {
  546. x = 0;
  547. y++;
  548. }
  549. }
  550. tb_present();
  551. if(charlen > 1) {
  552. display_prompt_and_response(prompt, num);
  553. tb_present();
  554. while(j < 2) {
  555. display_prompt_and_response(prompt, num);
  556. tb_present();
  557. if(tb_poll_event(&ev) != TB_OK) break;
  558. if(ev.key == TB_KEY_CTRL_G) {
  559. clrtoeol("", MSGLINE);
  560. return;
  561. }
  562. if(j < 2) {
  563. num[j] = ev.ch;
  564. tb_set_cursor(start_col, MSGLINE);
  565. addstr(num);
  566. x = 0;
  567. y = 0;
  568. if(j == 0) {
  569. for(current = curbp->b_page; current < curbp->b_epage; current++) {
  570. p = ptr(curbp, current);
  571. tp = ptr(curbp, current);
  572. is_white = isspace(*p);
  573. is_symb = is_symbol(*p);
  574. if(is_white || is_symb || current == 0)
  575. begin = TRUE;
  576. if(*p == (char_t)starting[0] && begin) {
  577. point_t i = current-curbp->b_page;
  578. if(chars[i][0] == ev.ch) {
  579. match = TRUE;
  580. tp = ptr(curbp, current+1);
  581. printf_tb(curwp->w_left+x, curwp->w_top+y, TB_RED, TB_CYAN, "%c", chars[i][1]);
  582. printf_tb(curwp->w_left+x+1, curwp->w_top+y, TB_DEFAULT, TB_DEFAULT, "%c", *tp == '\n' ? ' ' : *tp);
  583. } else {
  584. printf_tb(curwp->w_left+x, curwp->w_top+y, TB_DEFAULT, TB_DEFAULT, "%c", *tp == '\n' ? ' ' : *tp);
  585. tp = ptr(curbp, current+1);
  586. printf_tb(curwp->w_left+x+1, curwp->w_top+y, TB_DEFAULT, TB_DEFAULT, "%c", *tp == '\n' ? ' ' : *tp);
  587. }
  588. begin = FALSE;
  589. }
  590. if(!is_white && !is_symb)
  591. begin = FALSE;
  592. x++;
  593. if(*p == '\t')
  594. x += (TAB_SIZE - 2);
  595. if(*p < 31)
  596. x++;
  597. if(*p == '\n' || x >= curwp->w_cols) {
  598. x = 0;
  599. y++;
  600. }
  601. }
  602. }
  603. j++;
  604. if(!match) {
  605. clrtoeol("", MSGLINE);
  606. return;
  607. }
  608. }
  609. }
  610. }
  611. for(point_t cur = 0; cur < diff; cur++) {
  612. if(chars[cur][0] == num[0] && chars[cur][1] == num[1]) {
  613. point = cur + curbp->b_page;
  614. break;
  615. }
  616. }
  617. if(point == -1) {
  618. msg("Out of bounds.");
  619. } else {
  620. shift_pmark(TRUE, NOMARK);
  621. curbp->b_point = point;
  622. int cols = 0;
  623. /* Calculate the pcol value */
  624. lnbegin(); // reset the line so we get the right number for `cols`
  625. while(curbp->b_point != point) {
  626. ++curbp->b_point;
  627. cols++;
  628. }
  629. /* loop until we get to the correct column */
  630. while(cols > curwp->w_cols) {
  631. cols -= curwp->w_cols;
  632. }
  633. curbp->b_pcol = cols + curwp->w_left; // set it for column-memory
  634. clrtoeol("", MSGLINE);
  635. }
  636. /* Clear out the chars array */
  637. for(int i = 0; i < diff; i++) {
  638. chars[i][0] = 0;
  639. chars[i][1] = 0;
  640. }
  641. /* TODO: figure out why this has to be here
  642. Without this printf, the chars array doesn't appear to get
  643. cleared entirely and you end up jumping to the wrong points.
  644. */
  645. printf("%s", chars[0]);
  646. }
  647. void get_current_path(char *cur_path)
  648. {
  649. int cutoff = 0;
  650. for(int i = strlen(curbp->b_fname) - 1; i > -1; i--) {
  651. if(curbp->b_fname[i] == '/') {
  652. cutoff = i;
  653. break;
  654. }
  655. }
  656. for(int i = 0; i <= cutoff; i++)
  657. cur_path[i] = curbp->b_fname[i];
  658. cur_path[cutoff+1] = '\0';
  659. }
  660. void insertfile()
  661. {
  662. char cur_path[PATH_MAX] = "\0";
  663. if(curbp->b_path) {
  664. get_current_path(cur_path);
  665. strcpy(temp, cur_path);
  666. }
  667. else
  668. strcpy(temp, editor_dir);
  669. if (getfilename("Insert file: ", temp, PATH_MAX))
  670. (void)insert_file(temp, TRUE);
  671. }
  672. void readfile()
  673. {
  674. buffer_t *bp;
  675. char cur_path[PATH_MAX];
  676. if(curbp->b_path) {
  677. get_current_path(cur_path);
  678. strcpy(temp, cur_path);
  679. }
  680. else
  681. strcpy(temp, editor_dir);
  682. int result = getfilename("Find file: ", (char*)temp, PATH_MAX);
  683. if (result) {
  684. bp = find_buffer(temp, TRUE);
  685. disassociate_b(curwp);
  686. curbp = bp;
  687. associate_b2w(curbp, curwp);
  688. if (!growgap(curbp, CHUNK))
  689. fatal("%s: Failed to allocate required memory.\n");
  690. movegap(curbp, 0);
  691. /* load the file if not already loaded */
  692. if (bp != NULL && bp->b_fname[0] == '\0') {
  693. if (!load_file(temp)) {
  694. msg("New file %s", temp);
  695. }
  696. strncpy(curbp->b_fname, temp, PATH_MAX);
  697. curbp->b_fname[PATH_MAX] = '\0'; /* truncate if required */
  698. }
  699. }
  700. }
  701. void savebuffer()
  702. {
  703. const char *message = "No newline at the end of file, add one (Y/n) ?";
  704. if(curbp->b_flags & B_MODIFIED) {
  705. /* move the gap to point 0 so that the ebuf is updated. */
  706. (void) movegap(curbp, 0);
  707. if(*(curbp->b_ebuf - 1) != '\n') {
  708. print_to_msgline(message);
  709. clrtoeol(message, MSGLINE);
  710. if (yesno(TRUE)) {
  711. clrtoeol("", MSGLINE);
  712. *curbp->b_ebuf++ = '\n';
  713. }
  714. }
  715. if (curbp->b_fname[0] != '\0') {
  716. save(curbp->b_fname);
  717. return;
  718. } else {
  719. writefile();
  720. }
  721. } else {
  722. msg("(No changes need to be saved.)");
  723. }
  724. }
  725. void writefile()
  726. {
  727. const char *message = "Write file: ";
  728. strncpy(temp, curbp->b_fname, PATH_MAX);
  729. if (getinput((char *)message, temp, PATH_MAX, F_NONE, FALSE))
  730. if (save(temp) == TRUE)
  731. strncpy(curbp->b_fname, temp, PATH_MAX);
  732. clrtoeol(message, MSGLINE);
  733. }
  734. void killbuffer()
  735. {
  736. buffer_t *kill_bp = curbp;
  737. buffer_t *bp;
  738. int bcount = count_buffers();
  739. const char *message = "Discard changes (y/N) ?";
  740. /* do nothing if only buffer left is the scratch buffer */
  741. if (bcount == 1 && 0 == strcmp(get_buffer_name(curbp), "*scratch*"))
  742. return;
  743. if (curbp->b_flags & B_MODIFIED) {
  744. print_to_msgline(message);
  745. clrtoeol(message, MSGLINE);
  746. if (!yesno(FALSE))
  747. return;
  748. }
  749. if (bcount == 1) {
  750. /* create a scratch buffer */
  751. bp = find_buffer("*scratch*", TRUE);
  752. strncpy(bp->b_bname, "*scratch*", STRBUF_S);
  753. bp->b_path = FALSE;
  754. }
  755. next_buffer();
  756. assert(kill_bp != curbp);
  757. delete_buffer(kill_bp);
  758. for(window_t *wp = wheadp; wp != NULL; wp = wp->w_next) {
  759. if(kill_bp == wp->w_bufp) {
  760. wp->w_bufp = curbp;
  761. }
  762. }
  763. }
  764. void iblock()
  765. {
  766. block();
  767. msg("Mark set");
  768. }
  769. void unmark()
  770. {
  771. shift_pmark(TRUE, NOMARK);
  772. curbp->b_mark = NOMARK;
  773. universal_argument = 0;
  774. msg("Mark removed");
  775. }
  776. void toggle_overwrite_mode() {
  777. if (curbp->b_flags & B_OVERWRITE)
  778. curbp->b_flags &= ~B_OVERWRITE;
  779. else
  780. curbp->b_flags |= B_OVERWRITE;
  781. }
  782. void killtoeol()
  783. {
  784. if (curbp->b_point == pos(curbp, curbp->b_ebuf))
  785. return; /* do nothing if at end of file */
  786. if (*(ptr(curbp, curbp->b_point)) == 0xa) {
  787. delete(); /* delete CR if at start of empty line */
  788. } else {
  789. curbp->b_mark = curbp->b_point;
  790. lnend();
  791. if (curbp->b_mark != curbp->b_point) {
  792. currentcommand = KBD_CUT;
  793. copy_cut(TRUE, TRUE, FALSE);
  794. }
  795. }
  796. }
  797. /* Since version 1.9, you can use back-word-delete and
  798. fwd-word-delete to delete (and cut) a string of text so long that
  799. you don't interrupt the use of that direction of deletion. In
  800. other words, if you had the follow text and performed two `M-d`,
  801. you'd get both words in the scrap:
  802. hello there
  803. ^
  804. |
  805. point is here
  806. This means I have to concat, in the correct order, what you've cut
  807. in the scrap. To do this, I need the original scrap size and value
  808. then do some stuff to merge them together.
  809. */
  810. void copy_cut(int cut, int displaymsg, int internal)
  811. {
  812. char_t *p, *os, *ns;
  813. int shouldconcat = FALSE, onscrap = scrap.len;
  814. /* if no mark or point == marker, nothing doing */
  815. if (curbp->b_mark == NOMARK || curbp->b_point == curbp->b_mark)
  816. return;
  817. if(cut && !internal) {
  818. /* We only concat if it's a word delete */
  819. shouldconcat = lastcommand == KBD_DELETE_WORD &&
  820. currentcommand == KBD_DELETE_WORD ?
  821. curbp->b_point - curbp->b_mark : FALSE;
  822. undoset(CUT, shouldconcat);
  823. }
  824. if(shouldconcat != FALSE && scrap.data != NULL) {
  825. os = (char_t *)strndup((char *)scrap.data, scrap.len);
  826. } else {
  827. os = NULL;
  828. onscrap = 0;
  829. for(int i = KILLRING_SIZE-1; i > 0; i--) {
  830. if(kill_ring[i].data != NULL)
  831. free(kill_ring[i].data);
  832. if(kill_ring[i-1].data != NULL) {
  833. kill_ring[i].data = (char_t *)strndup((char *)kill_ring[i-1].data, kill_ring[i-1].len);
  834. kill_ring[i].len = kill_ring[i-1].len;
  835. }
  836. }
  837. if(kill_ring[0].data != NULL)
  838. free(kill_ring[0].data);
  839. kill_ring[0].data = (char_t *)strndup((char *)scrap.data, scrap.len);
  840. kill_ring[0].len = scrap.len;
  841. }
  842. if (scrap.data != NULL) {
  843. free(scrap.data);
  844. scrap.data = NULL;
  845. }
  846. if (curbp->b_point < curbp->b_mark) {
  847. /* point above marker: move gap under point, region = marker - point */
  848. (void) movegap(curbp, curbp->b_point);
  849. p = ptr(curbp, curbp->b_point);
  850. scrap.len = curbp->b_mark - curbp->b_point;
  851. if(cut && currentcommand == KBD_DELETE_WORD)
  852. for(point_t pt = curbp->b_mark-1; pt > curbp->b_point; pt--) {
  853. if(*ptr(curbp, pt) == '\n')
  854. curbp->b_line--;
  855. }
  856. } else {
  857. /* if point below marker: move gap under marker, region = point - marker */
  858. (void) movegap(curbp, curbp->b_mark);
  859. p = ptr(curbp, curbp->b_mark);
  860. scrap.len = curbp->b_point - curbp->b_mark;
  861. if (cut && currentcommand != KBD_DELETE_WORD)
  862. for(point_t pt = curbp->b_mark; pt < curbp->b_point; pt++) {
  863. if(*ptr(curbp, pt) == '\n')
  864. curbp->b_line--;
  865. }
  866. }
  867. if ((scrap.data = (char_t*) malloc(scrap.len)) == NULL && displaymsg) {
  868. msg("No more memory available.");
  869. } else {
  870. if (shouldconcat != FALSE) {
  871. /* Free the scrap here because we needed it above to get the
  872. original value.
  873. */
  874. free(scrap.data);
  875. scrap.data = NULL;
  876. ns = (char_t*) malloc(scrap.len* sizeof (char_t));
  877. (void) memcpy(ns, p, scrap.len * sizeof (char_t));
  878. ns[scrap.len] = '\0';
  879. if(shouldconcat < 0 && os != NULL) { /* deleting with M-<backsp> */
  880. asprintf((char **)&scrap.data, "%s%s", ns, os);
  881. free(os);
  882. } else if(shouldconcat > 0 && os != NULL) { /* deleting with M-d */
  883. asprintf((char **)&scrap.data, "%s%s", os, ns);
  884. free(os);
  885. } else /* first time deleting */
  886. memccpy(scrap.data, ns, '\0', sizeof(ns));
  887. scrap.len = onscrap + scrap.len;
  888. free(ns);
  889. ns = NULL;
  890. os = NULL;
  891. } else {
  892. (void) memcpy(scrap.data, p, scrap.len * sizeof (char_t));
  893. scrap.data[scrap.len] = '\0';
  894. }
  895. if (cut) {
  896. /* note that we only need to expand the gap by the amount being
  897. concated
  898. */
  899. curbp->b_egap += scrap.len - onscrap; /* if cut expand gap down */
  900. curbp->b_point = pos(curbp, curbp->b_egap); /* set point to after region */
  901. curbp->b_flags |= B_MODIFIED;
  902. if(displaymsg)
  903. msg("%ld bytes cut.", scrap.len);
  904. // currentcommand = KBD_CUT;
  905. } else {
  906. if(displaymsg)
  907. msg("%ld bytes copied.", scrap.len);
  908. }
  909. curbp->b_mark = NOMARK; /* unmark */
  910. }
  911. }
  912. void paste_internal(int internal)
  913. {
  914. int new_rows = 0;
  915. int col = curwp->w_col - curwp->w_left + 1;
  916. point_t opoint = curbp->b_point;
  917. if(curbp->b_flags & B_OVERWRITE)
  918. return;
  919. if (scrap.len <= 0) {
  920. msg("Scrap is empty. Nothing to yank.");
  921. } else if (scrap.len < curbp->b_egap - curbp->b_gap || growgap(curbp, scrap.len)) {
  922. if(!internal)
  923. undoset(YANK, FALSE);
  924. curbp->b_point = movegap(curbp, curbp->b_point);
  925. memcpy(curbp->b_gap, scrap.data, scrap.len * sizeof (char_t));
  926. curbp->b_gap += scrap.len;
  927. curbp->b_point = pos(curbp, curbp->b_egap);
  928. curbp->b_flags |= B_MODIFIED;
  929. /* TODO: this assumes 1 char = 1 point (not always true) */
  930. col += curbp->b_point - opoint;
  931. for(int i = 0, cc = col; scrap.data[i] != '\0'; i++) {
  932. cc++;
  933. if(scrap.data[i] == '\n' || cc >= curwp->w_cols) {
  934. new_rows++;
  935. cc = 0;
  936. }
  937. curbp->b_pcol = cc + curwp->w_left;
  938. }
  939. if ((curbp->b_row - curwp->w_top) + new_rows >= curwp->w_rows)
  940. curbp->b_reframe = 1;
  941. }
  942. }
  943. void paste()
  944. {
  945. char_t *oscrap;
  946. int onscrap;
  947. currentcommand = KBD_YANK;
  948. if(universal_argument > 0 && universal_argument-1 < KILLRING_SIZE) {
  949. oscrap = (char_t *)strndup((char *)scrap.data, scrap.len);
  950. onscrap = scrap.len;
  951. free(scrap.data);
  952. scrap.len = kill_ring[universal_argument-1].len;
  953. scrap.data = (char_t*) malloc(scrap.len);
  954. memccpy(scrap.data, kill_ring[universal_argument-1].data, '\0', scrap.len);
  955. paste_internal(FALSE);
  956. free(scrap.data);
  957. scrap.len = onscrap;
  958. scrap.data = (char_t*) malloc(scrap.len);
  959. memcpy(scrap.data, oscrap, scrap.len);
  960. free(oscrap);
  961. return;
  962. }
  963. paste_internal(FALSE);
  964. }
  965. void clipboard()
  966. {
  967. int new_rows = 0;
  968. int ntemp = strlen(gtemp);
  969. if(curbp->b_flags & B_OVERWRITE)
  970. return;
  971. if (ntemp <= 0) {
  972. msg("Temp buffer is empty. Nothing to paste.");
  973. } else if (ntemp < curbp->b_egap - curbp->b_gap || growgap(curbp, ntemp)) {
  974. undoset(CLIPBOARD, FALSE);
  975. curbp->b_point = movegap(curbp, curbp->b_point);
  976. memcpy(curbp->b_gap, gtemp, ntemp * sizeof (char_t));
  977. curbp->b_gap += ntemp;
  978. curbp->b_point = pos(curbp, curbp->b_egap);
  979. curbp->b_flags |= B_MODIFIED;
  980. for(int i = 0; gtemp[i] != '\0'; i++) {
  981. if(gtemp[i] == '\n')
  982. new_rows++;
  983. }
  984. if ((curbp->b_row - curwp->w_top) + new_rows > curwp->w_rows &&
  985. curbp->b_point >= curbp->b_epage)
  986. curbp->b_reframe = 1;
  987. free(gtemp);
  988. gtemp = malloc(sizeof(char) * TEMPBUF);
  989. }
  990. }
  991. void showpos()
  992. {
  993. int current, lastln;
  994. point_t end_p = pos(curbp, curbp->b_ebuf);
  995. get_line_stats(&current, &lastln, curbp);
  996. if (curbp->b_point == end_p) {
  997. msg("[EOB] Line = %d/%d Point = %d/%d", current, lastln,
  998. curbp->b_point, ((curbp->b_ebuf - curbp->b_buf) - (curbp->b_egap - curbp->b_gap)));
  999. } else {
  1000. char c = unctrl(*(ptr(curbp, curbp->b_point)));
  1001. msg("Char = %c 0x%x Line = %d/%d Point = %d/%d", c, *(ptr(curbp, curbp->b_point)),
  1002. current, lastln,
  1003. curbp->b_point, ((curbp->b_ebuf - curbp->b_buf) - (curbp->b_egap - curbp->b_gap)));
  1004. }
  1005. }
  1006. /* Delete whitespace between non-whitespace */
  1007. void delete_between()
  1008. {
  1009. char_t *p, other;
  1010. struct tb_event ev;
  1011. char *prompt;
  1012. int c, is_start = FALSE;
  1013. /* Delete everything between brackets. */
  1014. if(universal_argument > 0) {
  1015. if(character[0] == '\0') {
  1016. if(lastsymb == 0)
  1017. asprintf(&prompt, "Bracket to Zap Between: ");
  1018. else
  1019. asprintf(&prompt, "Bracket to Zap Between (default %c): ", lastsymb);
  1020. display_prompt_and_response(prompt, character);
  1021. tb_present();
  1022. if(execute_kbd_macro) {
  1023. use_kbd_macro(&ev);
  1024. } else if(tb_poll_event(&ev) != TB_OK)
  1025. return;
  1026. if(!ev.mod)
  1027. c = ev.ch;
  1028. else
  1029. c = ev.key;
  1030. if(record_input) {
  1031. record_buffer[record_buffer_index] = ev;
  1032. record_buffer_index++;
  1033. }
  1034. /* Ignore all control keys other than C-g, ESC, and return */
  1035. if (c < 32 && c != TB_KEY_CTRL_G && c != TB_KEY_ESC && c != TB_KEY_ENTER)
  1036. return;
  1037. if(c == TB_KEY_CTRL_G || c == TB_KEY_ESC)
  1038. return;
  1039. else if(c == TB_KEY_ENTER)
  1040. character[0] = lastsymb;
  1041. else
  1042. character[0] = c;
  1043. display_prompt_and_response(prompt, character);
  1044. tb_present();
  1045. }
  1046. if(!(other = is_bracket(character[0], TRUE, &is_start))) {
  1047. return;
  1048. }
  1049. for(;universal_argument > 0; universal_argument--)
  1050. jumptochar();
  1051. adjust_bline();
  1052. universal_argument = 0;
  1053. lastcommand = KBD_DELETE_CHAR;
  1054. if(is_start) {
  1055. curbp->b_point++;
  1056. while (*(p = ptr(curbp, curbp->b_point)) != other && curbp->b_buf < p)
  1057. delete();
  1058. } else {
  1059. while (*(p = ptr(curbp, curbp->b_point - 1)) != other && curbp->b_buf < p)
  1060. backsp();
  1061. }
  1062. lastsymb = character[0];
  1063. character[0] = '\0';
  1064. return;
  1065. }
  1066. /* If in a word delete the word both directions.
  1067. This is the same as doing a `esc f` then `esc backsp`.
  1068. This does not delete the symbols, just the words.
  1069. */
  1070. if(!isspace(*ptr(curbp, curbp->b_point - 1)) &&
  1071. !isspace(*ptr(curbp, curbp->b_point)) &&
  1072. !isspace(*ptr(curbp, curbp->b_point + 1))) {
  1073. wright();
  1074. wleftdelete();
  1075. return;
  1076. }
  1077. lastcommand = KBD_DELETE_CHAR;
  1078. /* Otherwise just delete whitespace */
  1079. while (isspace(*(p = ptr(curbp, curbp->b_point - 1))) && curbp->b_buf < p && *p != '\n')
  1080. backsp();
  1081. while (isspace(*(p = ptr(curbp, curbp->b_point))) && curbp->b_buf <= p && *p != '\n')
  1082. delete();
  1083. }
  1084. void insertnewlinebelow()
  1085. {
  1086. char_t newline[2];
  1087. newline[0] = '\n';
  1088. newline[1] = '\0';
  1089. input = newline;
  1090. undoset(INSERT, FALSE);
  1091. insert();
  1092. curbp->b_point--;
  1093. currentcommand = KBD_DEFAULT;
  1094. }
  1095. void insertnewline()
  1096. {
  1097. point_t point;
  1098. char_t *p, *space = NULL, *str;
  1099. int spaces = 0, i;
  1100. point = segstart(curbp, curwp, lnstart(curbp, curbp->b_point), curbp->b_point);
  1101. while(point < pos(curbp, curbp->b_ebuf) &&
  1102. isspace(*(p = ptr(curbp, point))) &&
  1103. *p != '\n' &&
  1104. curwp->w_col != 0) {
  1105. if(spaces == 0) {
  1106. space = p;
  1107. }
  1108. if(*p != '\n') {
  1109. spaces++;
  1110. point++;
  1111. }
  1112. }
  1113. str = (char_t *) malloc(sizeof(char_t)*spaces+2);
  1114. str[0] = '\n';
  1115. for(i = 0; i < spaces; i++) {
  1116. str[i+1] = *space;
  1117. }
  1118. str[i+1] = '\0';
  1119. input = str;
  1120. insert_str();
  1121. curbp->b_pcol = spaces + curwp->w_left;
  1122. currentcommand = KBD_INSERT;
  1123. free(str);
  1124. if((curwp->w_row - curwp->w_top) == curwp->w_rows-1) {
  1125. curbp->b_reframe = TRUE;
  1126. }
  1127. }
  1128. void inserttab()
  1129. {
  1130. input = (char_t *)"\t";
  1131. undoset(INSERT, FALSE);
  1132. insert();
  1133. }
  1134. void inserttabasspace()
  1135. {
  1136. char_t spaces[TAB_SPACE_SIZE+1];
  1137. memset(spaces, ' ', sizeof(spaces));
  1138. spaces[TAB_SPACE_SIZE] = '\0';
  1139. input = spaces;
  1140. insert_str();
  1141. }
  1142. void suspend()
  1143. {
  1144. tb_shutdown();
  1145. raise(SIGTSTP);
  1146. }
  1147. void transpose()
  1148. {
  1149. char_t *cur = ptr(curbp, curbp->b_point);
  1150. char_t *prev = ptr(curbp, curbp->b_point-1);
  1151. char_t replace[3];
  1152. if(cur == curbp->b_ebuf) {
  1153. return;
  1154. }
  1155. point_t mark = curbp->b_mark;
  1156. replace[0] = *cur;
  1157. replace[1] = *prev;
  1158. replace[2] = '\0';
  1159. curbp->b_point--;
  1160. curbp->b_mark = curbp->b_point + 2;
  1161. undoset(REPLACE, 2);
  1162. curbp->b_mark = mark;
  1163. curbp->b_point++;
  1164. memcpy(ptr(curbp, curbp->b_point-1), replace, 2 * sizeof (char_t));
  1165. curbp->b_flags |= B_MODIFIED;
  1166. }
  1167. /* Transpose words and put scrap back to how it was. */
  1168. void transposeword()
  1169. {
  1170. char_t *current_scrap, *p;
  1171. int n_scrap = scrap.len, newlines = 0;
  1172. point_t mark = curbp->b_mark, epoint, point, npoint;
  1173. /* copy the current scrap */
  1174. current_scrap = (char_t*) malloc(scrap.len);
  1175. (void) memcpy(current_scrap, scrap.data, scrap.len * sizeof (char_t));
  1176. /* Find all the key points for the undo */
  1177. wright();
  1178. epoint = curbp->b_point;
  1179. wleft();
  1180. wleft();
  1181. curbp->b_mark = epoint;
  1182. point = curbp->b_point;
  1183. /* Adjust `b_line` to match the line you'll eventually
  1184. be on. This has to happen before the undo so that the
  1185. undo's line tracker keeps it right.
  1186. */
  1187. npoint = point;
  1188. while(npoint < epoint) {
  1189. p = ptr(curbp, npoint);
  1190. if(*p == '\n')
  1191. newlines++;
  1192. npoint++;
  1193. }
  1194. curbp->b_line -= newlines;
  1195. undoset(REPLACE, curbp->b_mark - point);
  1196. /* Cut the word to the left*/
  1197. curbp->b_mark = point;
  1198. curbp->b_point = point;
  1199. wright();
  1200. currentcommand = KBD_CUT;
  1201. copy_cut(TRUE, FALSE, TRUE);
  1202. /* paste the left word */
  1203. right();
  1204. paste_internal(TRUE);
  1205. /* cut the right word */
  1206. curbp->b_mark = curbp->b_point;
  1207. wright();
  1208. currentcommand = KBD_CUT;
  1209. copy_cut(TRUE, FALSE, TRUE);
  1210. wleft();
  1211. /* paste the right word */
  1212. left();
  1213. paste_internal(TRUE);
  1214. /* Put it all back together */
  1215. if (scrap.data != NULL) {
  1216. free(scrap.data);
  1217. scrap.data = NULL;
  1218. }
  1219. scrap.len = n_scrap;
  1220. scrap.data = (char_t*) malloc(scrap.len);
  1221. (void) memcpy(scrap.data, current_scrap, scrap.len * sizeof (char_t));
  1222. curbp->b_mark = mark;
  1223. }
  1224. void lowercaseword()
  1225. {
  1226. char_t *p, *word;
  1227. char_t c[2];
  1228. point_t sword, eword;
  1229. int olast = lastcommand;
  1230. while ((isspace(*(p = ptr(curbp, curbp->b_point))) || is_symbol(*p)) && p < curbp->b_ebuf)
  1231. ++curbp->b_point;
  1232. sword = curbp->b_point;
  1233. wright();
  1234. eword = curbp->b_point;
  1235. word = (char_t *) malloc(sizeof(char_t)*(eword - sword));
  1236. curbp->b_point = sword;
  1237. lastcommand = KBD_DELETE_CHAR;
  1238. for(int i = sword, k = 0; i < eword; i++, k++) {
  1239. word[k] = *ptr(curbp, curbp->b_point);
  1240. delete();
  1241. }
  1242. lastcommand = olast;
  1243. for(int i = sword, k = 0; i < eword; i++, k++) {
  1244. c[0] = tolower(word[k]);
  1245. c[1] = '\0';
  1246. input = c;
  1247. undoset(INSERT, i != 0);
  1248. insert();
  1249. }
  1250. free(word);
  1251. }
  1252. void capitalizeword()
  1253. {
  1254. char_t *p;
  1255. while (isspace(*(p = ptr(curbp, curbp->b_point))) && p < curbp->b_ebuf)
  1256. ++curbp->b_point;
  1257. p = ptr(curbp, curbp->b_point);
  1258. char_t c;
  1259. c = toupper(*p);
  1260. input[0] = (char)c;
  1261. input[1] = '\0';
  1262. delete();
  1263. undoset(INSERT, FALSE);
  1264. insert();
  1265. if(isspace(*(p = ptr(curbp, curbp->b_point+1))) || is_symbol(*p))
  1266. curbp->b_point++;
  1267. else
  1268. wright();
  1269. }
  1270. void uppercaseword()
  1271. {
  1272. char_t *p, *word;
  1273. char_t c[2];
  1274. point_t sword, eword;
  1275. int olast = lastcommand;
  1276. while ((isspace(*(p = ptr(curbp, curbp->b_point))) || is_symbol(*p)) && p < curbp->b_ebuf)
  1277. ++curbp->b_point;
  1278. sword = curbp->b_point;
  1279. wright();
  1280. eword = curbp->b_point;
  1281. word = (char_t *) malloc(sizeof(char_t)*(eword - sword));
  1282. curbp->b_point = sword;
  1283. lastcommand = KBD_DELETE_CHAR;
  1284. for(int i = sword, k = 0; i < eword; i++, k++) {
  1285. word[k] = *ptr(curbp, curbp->b_point);
  1286. delete();
  1287. }
  1288. lastcommand = olast;
  1289. for(int i = sword, k = 0; i < eword; i++, k++) {
  1290. c[0] = toupper(word[k]);
  1291. c[1] = '\0';
  1292. input = c;
  1293. undoset(INSERT, i != 0);
  1294. insert();
  1295. }
  1296. free(word);
  1297. }
  1298. /* type = 0, zap
  1299. type = 1, jump
  1300. */
  1301. /* TODO: Throw error when putting non-char in.
  1302. */
  1303. void gotochar(int type, int include_char)
  1304. {
  1305. char_t *p;
  1306. point_t opoint = curbp->b_point, eol;
  1307. int c, col = 0;
  1308. struct tb_event ev;
  1309. char *promptBeg = type == 0 ? "Zap to Char" : "Jump to Char";
  1310. char *prompt;
  1311. if(lastchar == 0)
  1312. asprintf(&prompt, "%s: ", promptBeg);
  1313. else
  1314. asprintf(&prompt, "%s (default %c): ", promptBeg, lastchar);
  1315. if(character[0] == '\0') {
  1316. display_prompt_and_response(prompt, character);
  1317. tb_present();
  1318. if(execute_kbd_macro) {
  1319. use_kbd_macro(&ev);
  1320. } else if(tb_poll_event(&ev) != TB_OK)
  1321. return;
  1322. if(!ev.mod)
  1323. c = ev.ch;
  1324. else
  1325. c = ev.key;
  1326. if(record_input) {
  1327. record_buffer[record_buffer_index] = ev;
  1328. record_buffer_index++;
  1329. }
  1330. /* Ignore all control keys other than C-g, ESC, and return */
  1331. if (c < 32 && c != TB_KEY_CTRL_G && c != TB_KEY_ESC && c != TB_KEY_ENTER)
  1332. return;
  1333. if(c == TB_KEY_CTRL_G || c == TB_KEY_ESC)
  1334. return;
  1335. else if (c == TB_KEY_ENTER)
  1336. character[0] = lastchar;
  1337. else
  1338. character[0] = c;
  1339. display_prompt_and_response(prompt, character);
  1340. tb_present();
  1341. }
  1342. if(type == 0) {
  1343. block();
  1344. }
  1345. if(*ptr(curbp, curbp->b_point) == character[0] || curbp->b_point == 0) {
  1346. if(negated)
  1347. left();
  1348. else
  1349. right();
  1350. }
  1351. while (*(p = ptr(curbp, curbp->b_point + (include_char ? 0 : (negated ? -1 : 1)))) != character[0] &&
  1352. p < curbp->b_ebuf && curbp->b_point > 0) {
  1353. if(negated)
  1354. left();
  1355. else
  1356. right();
  1357. }
  1358. if(type == 0 && !negated)
  1359. right();
  1360. if(type == 0) {
  1361. currentcommand = KBD_CUT;
  1362. copy_cut(TRUE, FALSE, FALSE);
  1363. }
  1364. tb_set_cursor(0, MSGLINE);
  1365. clrtoeol("", MSGLINE);
  1366. eol = lnstart(curbp, curbp->b_point);
  1367. for(point_t poi = curbp->b_point; poi > eol; poi -= utf8_size(*ptr(curbp,poi)))
  1368. col++;
  1369. curbp->b_pcol = col + curwp->w_left;
  1370. if(p >= ptr(curbp, curbp->b_epage)) {
  1371. curbp->b_reframe = TRUE;
  1372. }
  1373. if((!negated && p >= curbp->b_ebuf) || (negated && curbp->b_point <= 0)) {
  1374. msg("No match found.");
  1375. curbp->b_point = opoint;
  1376. }
  1377. negated = FALSE;
  1378. lastchar = character[0];
  1379. }
  1380. void zaptochar()
  1381. {
  1382. gotochar(0, universal_argument == 0);
  1383. universal_argument = 0;
  1384. }
  1385. void negated_zaptochar()
  1386. {
  1387. negated = TRUE;
  1388. gotochar(0, universal_argument == 0);
  1389. universal_argument = 0;
  1390. }
  1391. void jumptochar()
  1392. {
  1393. shift_pmark(TRUE, NOMARK);
  1394. gotochar(1, TRUE);
  1395. }
  1396. void negated_jumptochar()
  1397. {
  1398. shift_pmark(TRUE, NOMARK);
  1399. negated = TRUE;
  1400. gotochar(1, TRUE);
  1401. }
  1402. void poptomark()
  1403. {
  1404. if(curbp->b_mark != NOMARK)
  1405. curbp->b_point = curbp->b_mark;
  1406. else if(curbp->b_pmark[0] != NOMARK)
  1407. curbp->b_point = shift_pmark(FALSE, NOMARK);
  1408. else {
  1409. msg("No valid mark to pop to.");
  1410. return;
  1411. }
  1412. if(curbp->b_point < curbp->b_page || curbp->b_point > curbp->b_epage) {
  1413. curbp->b_reframe = TRUE;
  1414. curwp->w_recenter = TRUE;
  1415. }
  1416. }
  1417. void universal_argument_load()
  1418. {
  1419. universal_argument++;
  1420. msg("C-u %d", universal_argument);
  1421. }
  1422. void numeric_argument_load()
  1423. {
  1424. numeric_argument = (numeric_argument * 10) + atoi((const char *)&input_char);
  1425. msg("C-u %d", numeric_argument);
  1426. }
  1427. void back_to_indentation()
  1428. {
  1429. char_t *p;
  1430. while (isspace(*(p = ptr(curbp, curbp->b_point))) && p < curbp->b_ebuf)
  1431. ++curbp->b_point;
  1432. }
  1433. void negate()
  1434. {
  1435. negated = !negated;
  1436. msg("C-u -");
  1437. }
  1438. void forward_bracket()
  1439. {
  1440. point_t p, eol;
  1441. int col = 0;
  1442. if((p = find_matching_bracket(curbp, curwp, 1, FALSE)) >= 0)
  1443. curbp->b_point = curbp->b_mark == NOMARK ? p : p + 1;
  1444. /* Make sure the column memory updates to the new column */
  1445. eol = lnstart(curbp, curbp->b_point);
  1446. for(p = curbp->b_point; p > eol; p -= utf8_size(*ptr(curbp,p)))
  1447. col++;
  1448. curbp->b_pcol = col + curwp->w_left;
  1449. }
  1450. void backward_bracket()
  1451. {
  1452. point_t p, eol;
  1453. int col = 0;
  1454. if((p = find_matching_bracket(curbp, curwp, -1, FALSE)) >= 0) {
  1455. curbp->b_point = p;
  1456. if(curbp->b_mark != NOMARK)
  1457. curbp->b_mark++;
  1458. }
  1459. /* Make sure the column memory updates to the new column */
  1460. eol = lnstart(curbp, curbp->b_point);
  1461. for(p = curbp->b_point; p > eol; p -= utf8_size(*ptr(curbp,p)))
  1462. col++;
  1463. curbp->b_pcol = col + curwp->w_left;
  1464. }
  1465. void start_kbd_macro()
  1466. {
  1467. record_input = TRUE;
  1468. for(int i = 0; i < record_buffer_index; i++) {
  1469. memset(&record_buffer[i], 0, sizeof(record_buffer[i]));
  1470. }
  1471. record_buffer_index = 0;
  1472. msg("Started keyboard macro...");
  1473. }
  1474. void end_kbd_macro()
  1475. {
  1476. record_input = FALSE;
  1477. msg("Ended keyboard macro.");
  1478. }
  1479. void run_kbd_macro()
  1480. {
  1481. if(numeric_argument > 0)
  1482. numeric_argument--;
  1483. /* If you start_kbd_macro and immediately close it, you haven't
  1484. really recorded anything. This shows up as the second value
  1485. being C-x and then 0 in the 3rd.
  1486. */
  1487. if(record_buffer_index == 0 ||
  1488. (record_buffer[1].key == TB_KEY_CTRL_X && record_buffer[2].key == 0)) {
  1489. msg("No recorded keyboard macro.");
  1490. return;
  1491. }
  1492. if(record_input) {
  1493. msg("Currently recording keyboard macro.");
  1494. return;
  1495. }
  1496. execute_kbd_macro = TRUE;
  1497. }
  1498. void open_file_from_shell()
  1499. {
  1500. get_popen_data(1);
  1501. }
  1502. void insert_from_shell()
  1503. {
  1504. get_popen_data(0);
  1505. }
  1506. void insert_control_char()
  1507. {
  1508. struct tb_event ev;
  1509. char *prompt = "Insert Control Char: ";
  1510. display_prompt_and_response(prompt, character);
  1511. tb_present();
  1512. if(execute_kbd_macro) {
  1513. use_kbd_macro(&ev);
  1514. } else if(tb_poll_event(&ev) != TB_OK)
  1515. return;
  1516. if(record_input) {
  1517. record_buffer[record_buffer_index] = ev;
  1518. record_buffer_index++;
  1519. }
  1520. tb_set_cursor(0, MSGLINE);
  1521. clrtoeol("", MSGLINE);
  1522. if(ev.key > 0x1a) {
  1523. return;
  1524. }
  1525. input[0] = (char)ev.key;
  1526. input[1] = '\0';
  1527. undoset(INSERT, lastcommand == KBD_INSERT);
  1528. insert();
  1529. currentcommand = KBD_INSERT;
  1530. ignorenotbound = TRUE;
  1531. }
  1532. void comment_at_eol()
  1533. {
  1534. if(curbp->b_keywords == NULL || curbp->b_keywords->slc == NULL) {
  1535. return;
  1536. }
  1537. lnend();
  1538. inserttabasspace();
  1539. for(int c = 0; curbp->b_keywords->slc[c] != '\0'; c++)
  1540. input[c] = curbp->b_keywords->slc[c];
  1541. input[strlen(curbp->b_keywords->slc)] = ' ';
  1542. input[strlen(curbp->b_keywords->slc)+1] = '\0';
  1543. insert_str();
  1544. }
  1545. // TODO: Comment region with single-line comment
  1546. // TODO: Remove region comment with single-line
  1547. void comment()
  1548. {
  1549. point_t p = curbp->b_point, op = -1, mark = curbp->b_mark;
  1550. char_t *c;
  1551. int match = FALSE, i = 0, e = 0, j = 0, bline = 0;
  1552. int oline = curbp->b_line;
  1553. int newline = curbp->b_line;
  1554. if(curbp->b_keywords == NULL || curbp->b_keywords->slc == NULL) {
  1555. return;
  1556. }
  1557. /* multi-line */
  1558. if(mark != NOMARK && (curbp->b_keywords->mlc != NULL &&
  1559. curbp->b_keywords->emlc != NULL)) {
  1560. if(mark < curbp->b_point) {
  1561. while(curbp->b_point != mark) {
  1562. curbp->b_point--;
  1563. if(*(ptr(curbp, curbp->b_point)) == '\n') {
  1564. curbp->b_line--;
  1565. bline++;
  1566. }
  1567. }
  1568. }
  1569. j = 0;
  1570. for(; curbp->b_keywords->mlc[j] != '\0'; j++)
  1571. input[j] = curbp->b_keywords->mlc[j];
  1572. input[j+1] = '\0';
  1573. insert_str();
  1574. if(mark > curbp->b_point) {
  1575. op = p;
  1576. while(p != mark) {
  1577. p++;
  1578. if(*(ptr(curbp, p)) == '\n') {
  1579. curbp->b_line++;
  1580. bline++;
  1581. }
  1582. }
  1583. }
  1584. curbp->b_point = p + strlen(curbp->b_keywords->mlc);
  1585. if(op == -1)
  1586. curbp->b_line += bline;
  1587. else
  1588. curbp->b_line++;
  1589. j = 0;
  1590. for(; curbp->b_keywords->emlc[j] != '\0'; j++)
  1591. input[j] = curbp->b_keywords->emlc[j];
  1592. input[j+1] = '\0';
  1593. insert_str();
  1594. if(op > -1) {
  1595. curbp->b_point = op;
  1596. curbp->b_line -= bline;
  1597. curbp->b_line--;
  1598. }
  1599. curbp->b_opoint = curbp->b_point;
  1600. curbp->b_mark = NOMARK;
  1601. return;
  1602. }
  1603. /* Check to see if you're in a multi-line comment.
  1604. If you see the end of a multi-line comment, you know immediately
  1605. that you aren't in one.
  1606. */
  1607. if(curbp->b_keywords->mlc != NULL &&
  1608. curbp->b_keywords->emlc != NULL) {
  1609. op = p;
  1610. for(e = strlen(curbp->b_keywords->emlc) - 1, i = strlen(curbp->b_keywords->mlc) - 1;
  1611. p > 0 && i >= 0 && e >= 0;
  1612. p--) {
  1613. int smatch = *(c = ptr(curbp, p)) == curbp->b_keywords->mlc[i];
  1614. int ematch = *c == curbp->b_keywords->emlc[e];
  1615. if(*c == '\n')
  1616. newline--;
  1617. if(smatch) {
  1618. if(i == 0)
  1619. break;
  1620. i--;
  1621. }
  1622. if(ematch) {
  1623. if(e == 0)
  1624. break;
  1625. e--;
  1626. }
  1627. if(!smatch) {
  1628. i = strlen(curbp->b_keywords->mlc) - 1;
  1629. }
  1630. if(!ematch) {
  1631. e = strlen(curbp->b_keywords->emlc) - 1;
  1632. }
  1633. }
  1634. }
  1635. /* If you're in a multi-line comment, remove it. */
  1636. if(i <= 0 && e > 0) {
  1637. curbp->b_point = p;
  1638. curbp->b_line = newline;
  1639. for(i = strlen(curbp->b_keywords->mlc); i > 0; i--)
  1640. delete();
  1641. match = FALSE;
  1642. for(i = 0; p < pos(curbp, curbp->b_ebuf) && i < strlen(curbp->b_keywords->emlc); p++) {
  1643. if(*(c = ptr(curbp, p)) == curbp->b_keywords->emlc[i]) {
  1644. match = TRUE;
  1645. i++;
  1646. } else {
  1647. match = FALSE;
  1648. }
  1649. if(*c == '\n')
  1650. curbp->b_line++;
  1651. }
  1652. if(match) {
  1653. p -= i;
  1654. curbp->b_point = p;
  1655. for(; i > 0; i--)
  1656. delete();
  1657. }
  1658. curbp->b_point = op - strlen(curbp->b_keywords->mlc);
  1659. curbp->b_line = oline;
  1660. return;
  1661. }
  1662. /* single line */
  1663. if(op != -1)
  1664. p = op;
  1665. lnbegin();
  1666. for(i = 0; curbp->b_keywords->slc[i] != '\0'; i++) {
  1667. if(*(c = ptr(curbp, curbp->b_point)) == curbp->b_keywords->slc[i]) {
  1668. match = TRUE;
  1669. } else {
  1670. match = FALSE;
  1671. }
  1672. }
  1673. if(match) {
  1674. for(; i > 0; i--)
  1675. delete();
  1676. delete(); // don't forget the extra space
  1677. return;
  1678. }
  1679. for(int c = 0; curbp->b_keywords->slc[c] != '\0'; c++)
  1680. input[c] = curbp->b_keywords->slc[c];
  1681. input[strlen(curbp->b_keywords->slc)] = ' ';
  1682. input[strlen(curbp->b_keywords->slc)+1] = '\0';
  1683. insert_str();
  1684. curbp->b_point = p + strlen(curbp->b_keywords->slc) + 1;
  1685. }