elog.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <draw.h>
  12. #include <thread.h>
  13. #include <cursor.h>
  14. #include <mouse.h>
  15. #include <keyboard.h>
  16. #include <frame.h>
  17. #include <fcall.h>
  18. #include <plumb.h>
  19. #include "dat.h"
  20. #include "fns.h"
  21. #include "edit.h"
  22. static char Wsequence[] = "warning: changes out of sequence\n";
  23. static int warned = FALSE;
  24. /*
  25. * Log of changes made by editing commands. Three reasons for this:
  26. * 1) We want addresses in commands to apply to old file, not file-in-change.
  27. * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
  28. * 3) This gives an opportunity to optimize by merging adjacent changes.
  29. * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
  30. * separate implementation. To do this well, we use Replace as well as
  31. * Insert and Delete
  32. */
  33. typedef struct Buflog Buflog;
  34. struct Buflog
  35. {
  36. i16 type; /* Replace, Filename */
  37. u32 q0; /* location of change (unused in f) */
  38. u32 nd; /* # runes to delete */
  39. u32 nr; /* # runes in string or file name */
  40. };
  41. enum
  42. {
  43. Buflogsize = sizeof(Buflog)/sizeof(Rune),
  44. };
  45. /*
  46. * Minstring shouldn't be very big or we will do lots of I/O for small changes.
  47. * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
  48. */
  49. enum
  50. {
  51. Minstring = 16, /* distance beneath which we merge changes */
  52. Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */
  53. };
  54. void
  55. eloginit(File *f)
  56. {
  57. if(f->elog.type != Empty)
  58. return;
  59. f->elog.type = Null;
  60. if(f->elogbuf == nil)
  61. f->elogbuf = emalloc(sizeof(Buffer));
  62. if(f->elog.r == nil)
  63. f->elog.r = fbufalloc();
  64. bufreset(f->elogbuf);
  65. }
  66. void
  67. elogclose(File *f)
  68. {
  69. if(f->elogbuf){
  70. bufclose(f->elogbuf);
  71. free(f->elogbuf);
  72. f->elogbuf = nil;
  73. }
  74. }
  75. void
  76. elogreset(File *f)
  77. {
  78. f->elog.type = Null;
  79. f->elog.nd = 0;
  80. f->elog.nr = 0;
  81. }
  82. void
  83. elogterm(File *f)
  84. {
  85. elogreset(f);
  86. if(f->elogbuf)
  87. bufreset(f->elogbuf);
  88. f->elog.type = Empty;
  89. fbuffree(f->elog.r);
  90. f->elog.r = nil;
  91. warned = FALSE;
  92. }
  93. void
  94. elogflush(File *f)
  95. {
  96. Buflog b;
  97. b.type = f->elog.type;
  98. b.q0 = f->elog.q0;
  99. b.nd = f->elog.nd;
  100. b.nr = f->elog.nr;
  101. switch(f->elog.type){
  102. default:
  103. warning(nil, "unknown elog type 0x%x\n", f->elog.type);
  104. break;
  105. case Null:
  106. break;
  107. case Insert:
  108. case Replace:
  109. if(f->elog.nr > 0)
  110. bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
  111. /* fall through */
  112. case Delete:
  113. bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
  114. break;
  115. }
  116. elogreset(f);
  117. }
  118. void
  119. elogreplace(File *f, int q0, int q1, Rune *r, int nr)
  120. {
  121. u32 gap;
  122. if(q0==q1 && nr==0)
  123. return;
  124. eloginit(f);
  125. if(f->elog.type!=Null && q0<f->elog.q0){
  126. if(warned++ == 0)
  127. warning(nil, Wsequence);
  128. elogflush(f);
  129. }
  130. /* try to merge with previous */
  131. gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */
  132. if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
  133. if(gap < Minstring){
  134. if(gap > 0){
  135. bufread(&f->Buffer, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
  136. f->elog.nr += gap;
  137. }
  138. f->elog.nd += gap + q1-q0;
  139. runemove(f->elog.r+f->elog.nr, r, nr);
  140. f->elog.nr += nr;
  141. return;
  142. }
  143. }
  144. elogflush(f);
  145. f->elog.type = Replace;
  146. f->elog.q0 = q0;
  147. f->elog.nd = q1-q0;
  148. f->elog.nr = nr;
  149. if(nr > RBUFSIZE)
  150. editerror("internal error: replacement string too large(%d)", nr);
  151. runemove(f->elog.r, r, nr);
  152. }
  153. void
  154. eloginsert(File *f, int q0, Rune *r, int nr)
  155. {
  156. int n;
  157. if(nr == 0)
  158. return;
  159. eloginit(f);
  160. if(f->elog.type!=Null && q0<f->elog.q0){
  161. if(warned++ == 0)
  162. warning(nil, Wsequence);
  163. elogflush(f);
  164. }
  165. /* try to merge with previous */
  166. if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
  167. runemove(f->elog.r+f->elog.nr, r, nr);
  168. f->elog.nr += nr;
  169. return;
  170. }
  171. while(nr > 0){
  172. elogflush(f);
  173. f->elog.type = Insert;
  174. f->elog.q0 = q0;
  175. n = nr;
  176. if(n > RBUFSIZE)
  177. n = RBUFSIZE;
  178. f->elog.nr = n;
  179. runemove(f->elog.r, r, n);
  180. r += n;
  181. nr -= n;
  182. }
  183. }
  184. void
  185. elogdelete(File *f, int q0, int q1)
  186. {
  187. if(q0 == q1)
  188. return;
  189. eloginit(f);
  190. if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
  191. if(warned++ == 0)
  192. warning(nil, Wsequence);
  193. elogflush(f);
  194. }
  195. /* try to merge with previous */
  196. if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
  197. f->elog.nd += q1-q0;
  198. return;
  199. }
  200. elogflush(f);
  201. f->elog.type = Delete;
  202. f->elog.q0 = q0;
  203. f->elog.nd = q1-q0;
  204. }
  205. #define tracelog 0
  206. void
  207. elogapply(File *f)
  208. {
  209. Buflog b;
  210. Rune *buf;
  211. u32 i, n, up, mod;
  212. u32 tq0, tq1;
  213. Buffer *log;
  214. Text *t;
  215. int owner;
  216. elogflush(f);
  217. log = f->elogbuf;
  218. t = f->curtext;
  219. buf = fbufalloc();
  220. mod = FALSE;
  221. owner = 0;
  222. if(t->w){
  223. owner = t->w->owner;
  224. if(owner == 0)
  225. t->w->owner = 'E';
  226. }
  227. /*
  228. * The edit commands have already updated the selection in t->q0, t->q1,
  229. * but using coordinates relative to the unmodified buffer. As we apply the log,
  230. * we have to update the coordinates to be relative to the modified buffer.
  231. * Textinsert and textdelete will do this for us; our only work is to apply the
  232. * convention that an insertion at t->q0==t->q1 is intended to select the
  233. * inserted text.
  234. */
  235. /*
  236. * We constrain the addresses in here (with textconstrain()) because
  237. * overlapping changes will generate bogus addresses. We will warn
  238. * about changes out of sequence but proceed anyway; here we must
  239. * keep things in range.
  240. */
  241. while(log->nc > 0){
  242. up = log->nc-Buflogsize;
  243. bufread(log, up, (Rune*)&b, Buflogsize);
  244. switch(b.type){
  245. default:
  246. fprint(2, "elogapply: 0x%x\n", b.type);
  247. abort();
  248. break;
  249. case Replace:
  250. if(tracelog)
  251. warning(nil, "elog replace %d %d (%d %d)\n",
  252. b.q0, b.q0+b.nd, t->q0, t->q1);
  253. if(!mod){
  254. mod = TRUE;
  255. filemark(f);
  256. }
  257. textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
  258. textdelete(t, tq0, tq1, TRUE);
  259. up -= b.nr;
  260. for(i=0; i<b.nr; i+=n){
  261. n = b.nr - i;
  262. if(n > RBUFSIZE)
  263. n = RBUFSIZE;
  264. bufread(log, up+i, buf, n);
  265. textinsert(t, tq0+i, buf, n, TRUE);
  266. }
  267. if(t->q0 == b.q0 && t->q1 == b.q0)
  268. t->q1 += b.nr;
  269. break;
  270. case Delete:
  271. if(tracelog)
  272. warning(nil, "elog delete %d %d (%d %d)\n",
  273. b.q0, b.q0+b.nd, t->q0, t->q1);
  274. if(!mod){
  275. mod = TRUE;
  276. filemark(f);
  277. }
  278. textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
  279. textdelete(t, tq0, tq1, TRUE);
  280. break;
  281. case Insert:
  282. if(tracelog)
  283. warning(nil, "elog insert %d %d (%d %d)\n",
  284. b.q0, b.q0+b.nr, t->q0, t->q1);
  285. if(!mod){
  286. mod = TRUE;
  287. filemark(f);
  288. }
  289. textconstrain(t, b.q0, b.q0, &tq0, &tq1);
  290. up -= b.nr;
  291. for(i=0; i<b.nr; i+=n){
  292. n = b.nr - i;
  293. if(n > RBUFSIZE)
  294. n = RBUFSIZE;
  295. bufread(log, up+i, buf, n);
  296. textinsert(t, tq0+i, buf, n, TRUE);
  297. }
  298. if(t->q0 == b.q0 && t->q1 == b.q0)
  299. t->q1 += b.nr;
  300. break;
  301. /* case Filename:
  302. f->seq = u.seq;
  303. fileunsetname(f, epsilon);
  304. f->mod = u.mod;
  305. up -= u.n;
  306. free(f->name);
  307. if(u.n == 0)
  308. f->name = nil;
  309. else
  310. f->name = runemalloc(u.n);
  311. bufread(delta, up, f->name, u.n);
  312. f->nname = u.n;
  313. break;
  314. */
  315. }
  316. bufdelete(log, up, log->nc);
  317. }
  318. fbuffree(buf);
  319. elogterm(f);
  320. /*
  321. * Bad addresses will cause bufload to crash, so double check.
  322. * If changes were out of order, we expect problems so don't complain further.
  323. */
  324. if(t->q0 > f->Buffer.nc || t->q1 > f->Buffer.nc || t->q0 > t->q1){
  325. if(!warned)
  326. warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->Buffer.nc);
  327. t->q1 = min(t->q1, f->Buffer.nc);
  328. t->q0 = min(t->q0, t->q1);
  329. }
  330. if(t->w)
  331. t->w->owner = owner;
  332. }