Download
jcs
/amend
/committer.c
(View History)
jcs committer+editor: Update log scrollbar after cut or paste | Latest amendment: 114 on 2023-04-17 |
1 | /* |
2 | * Copyright (c) 2021 joshua stein <jcs@jcs.org> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <stdarg.h> |
18 | #include <stdio.h> |
19 | #include <string.h> |
20 | |
21 | #include "amend.h" |
22 | #include "browser.h" |
23 | #include "committer.h" |
24 | #include "diff.h" |
25 | #include "focusable.h" |
26 | #include "repo.h" |
27 | #include "settings.h" |
28 | #include "tetab.h" |
29 | #include "util.h" |
30 | |
31 | #define LABEL_FONT geneva |
32 | #define LABEL_FONT_SIZE 10 |
33 | |
34 | #define PADDING 10 |
35 | |
36 | /* needed by diffreg */ |
37 | struct stat stb1, stb2; |
38 | long diff_format, diff_context, status = 0; |
39 | char *ifdefname, *diffargs, *label[2], *ignore_pats; |
40 | |
41 | struct committer *committer_diffing = NULL; |
42 | |
43 | bool committer_close(struct focusable *focusable); |
44 | void committer_idle(struct focusable *focusable, EventRecord *event); |
45 | void committer_update(struct focusable *focusable, EventRecord *event); |
46 | void committer_suspend(struct focusable *focusable); |
47 | void committer_resume(struct focusable *focusable); |
48 | void committer_key_down(struct focusable *focusable, EventRecord *event); |
49 | void committer_mouse_down(struct focusable *focusable, EventRecord *event); |
50 | bool committer_handle_menu(struct focusable *focusable, short menu, |
51 | short item); |
52 | |
53 | void committer_generate_diff(struct committer *committer); |
54 | void committer_update_menu(struct committer *committer); |
55 | void committer_commit(struct committer *committer); |
56 | void diff_append_line(char *str, size_t len, bool flush); |
57 | void diff_chunk_write(void); |
58 | void diff_finish(void); |
59 | |
60 | void |
61 | committer_init(struct browser *browser) |
62 | { |
63 | Str255 title, filename; |
64 | struct committer *committer; |
65 | struct focusable *focusable; |
66 | Rect bounds = { 0 }, te_bounds = { 0 }; |
67 | TextStyle style; |
68 | short fh; |
69 | |
70 | committer = xmalloczero(sizeof(struct committer), "committer_init"); |
71 | committer->browser = browser; |
72 | browser->committer = committer; |
73 | |
74 | /* main window, centered in its browser */ |
75 | bounds = (*(((WindowPeek)browser->win)->strucRgn))->rgnBBox; |
76 | bounds.left += (PADDING / 2); |
77 | bounds.top += ((PADDING / 2) + MBarHeight); |
78 | bounds.right -= (PADDING / 2); |
79 | bounds.bottom -= (PADDING / 2); |
80 | |
81 | memcpy(&filename, browser->repo->bile->filename, sizeof(filename)); |
82 | PtoCstr(filename); |
83 | snprintf((char *)&title, sizeof(title), "%s: %s: diff", |
84 | PROGRAM_NAME, (browser->repo ? (char *)filename : "No repo open")); |
85 | |
86 | committer->win = NewWindow(0L, &bounds, CtoPstr(title), false, |
87 | noGrowDocProc, (WindowPtr)-1L, true, 0); |
88 | if (!committer) |
89 | err(1, "Can't create committer window"); |
90 | SetPort(committer->win); |
91 | |
92 | /* log message */ |
93 | bounds.top = PADDING; |
94 | bounds.left = 50; |
95 | fh = FontHeight(monaco, 9); |
96 | bounds.bottom = bounds.top + (fh * 5) + 2; |
97 | bounds.right = committer->win->portRect.right - SCROLLBAR_WIDTH - |
98 | PADDING; |
99 | te_bounds = bounds; |
100 | InsetRect(&te_bounds, 2, 2); |
101 | TextFont(monaco); |
102 | TextSize(9); |
103 | committer->log_te = TEStylNew(&te_bounds, &bounds); |
104 | style.tsFont = monaco; |
105 | style.tsSize = 9; |
106 | TESetStyle(doFont | doSize, &style, false, committer->log_te); |
107 | TEAutoView(true, committer->log_te); |
108 | TETabEnable(committer->log_te); |
109 | TEActivate(committer->log_te); |
110 | |
111 | /* scrollbar for log message */ |
112 | bounds.left = bounds.right; |
113 | bounds.right += SCROLLBAR_WIDTH; |
114 | bounds.bottom++; |
115 | bounds.top--; |
116 | committer->log_scroller = NewControl(committer->win, &bounds, "\p", |
117 | true, 1, 1, 1, scrollBarProc, 0L); |
118 | |
119 | /* diff */ |
120 | bounds.top = bounds.bottom + PADDING; |
121 | bounds.left = PADDING; |
122 | bounds.bottom = committer->win->portRect.bottom - 20 - PADDING - |
123 | PADDING; |
124 | bounds.right = committer->win->portRect.right - SCROLLBAR_WIDTH - |
125 | PADDING; |
126 | te_bounds = bounds; |
127 | InsetRect(&te_bounds, 2, 2); |
128 | committer->diff_te = TEStylNew(&te_bounds, &bounds); |
129 | TEAutoView(true, committer->diff_te); |
130 | TETabEnable(committer->diff_te); |
131 | (*(committer->diff_te))->caretHook = NullCaretHook; |
132 | TEActivate(committer->diff_te); |
133 | |
134 | /* scrollbar for diff */ |
135 | bounds.left = bounds.right; |
136 | bounds.right += SCROLLBAR_WIDTH; |
137 | bounds.bottom++; |
138 | bounds.top--; |
139 | committer->diff_scroller = NewControl(committer->win, &bounds, "\p", |
140 | true, 1, 1, 1, scrollBarProc, 0L); |
141 | |
142 | /* commit button */ |
143 | bounds.left = committer->win->portRect.right - PADDING - 100; |
144 | bounds.right = bounds.left + 100; |
145 | bounds.bottom = committer->win->portRect.bottom - PADDING; |
146 | bounds.top = bounds.bottom - 20; |
147 | committer->commit_button = NewControl(committer->win, &bounds, |
148 | "\pCommit", true, 1, 1, 1, pushButProc, 0L); |
149 | |
150 | committer->last_te = committer->log_te; |
151 | |
152 | focusable = xmalloczero(sizeof(struct focusable), "committer focusable"); |
153 | focusable->cookie = committer; |
154 | focusable->win = committer->win; |
155 | focusable->modal = true; |
156 | focusable->idle = committer_idle; |
157 | focusable->update = committer_update; |
158 | focusable->mouse_down = committer_mouse_down; |
159 | focusable->key_down = committer_key_down; |
160 | focusable->menu = committer_handle_menu; |
161 | focusable->close = committer_close; |
162 | focusable_add(focusable); |
163 | |
164 | committer->state = COMMITTER_STATE_DO_DIFF; |
165 | } |
166 | |
167 | bool |
168 | committer_close(struct focusable *focusable) |
169 | { |
170 | struct committer *committer = (struct committer *)focusable->cookie; |
171 | |
172 | committer->browser->committer = NULL; |
173 | |
174 | if (committer->diff_line != NULL) { |
175 | DisposHandle(committer->diff_line); |
176 | committer->diff_line = NULL; |
177 | } |
178 | |
179 | if (committer->diffed_files != NULL) |
180 | xfree(&committer->diffed_files); |
181 | |
182 | TEDispose(committer->log_te); |
183 | TEDispose(committer->diff_te); |
184 | DisposeWindow(committer->win); |
185 | |
186 | xfree(&committer); |
187 | |
188 | return true; |
189 | } |
190 | |
191 | void |
192 | committer_idle(struct focusable *focusable, EventRecord *event) |
193 | { |
194 | struct committer *committer = (struct committer *)focusable->cookie; |
195 | |
196 | switch (committer->state) { |
197 | case COMMITTER_STATE_IDLE: |
198 | if (committer->last_te == committer->log_te) |
199 | TEIdle(committer->log_te); |
200 | break; |
201 | case COMMITTER_STATE_DO_DIFF: |
202 | committer_generate_diff(committer); |
203 | committer->state = COMMITTER_STATE_IDLE; |
204 | break; |
205 | case COMMITTER_STATE_DO_COMMIT: |
206 | break; |
207 | } |
208 | } |
209 | |
210 | void |
211 | committer_update(struct focusable *focusable, EventRecord *event) |
212 | { |
213 | Str255 buf; |
214 | Rect r; |
215 | short what = -1, len; |
216 | struct committer *committer = (struct committer *)focusable->cookie; |
217 | |
218 | if (event != NULL) |
219 | what = event->what; |
220 | |
221 | switch (what) { |
222 | case -1: |
223 | case updateEvt: |
224 | r = (*(committer->log_te))->viewRect; |
225 | MoveTo(r.top, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2); |
226 | TextFont(LABEL_FONT); |
227 | TextSize(LABEL_FONT_SIZE); |
228 | DrawText("Log:", 0, 4); |
229 | |
230 | r = (*(committer->log_te))->viewRect; |
231 | TEUpdate(&r, committer->log_te); |
232 | InsetRect(&r, -1, -1); |
233 | FrameRect(&r); |
234 | |
235 | r = (*(committer->diff_te))->viewRect; |
236 | TEUpdate(&r, committer->diff_te); |
237 | InsetRect(&r, -1, -1); |
238 | FrameRect(&r); |
239 | |
240 | if ((*(committer->diff_te))->nLines > 0) { |
241 | r = (*(committer->diff_te))->viewRect; |
242 | MoveTo(r.left, r.bottom + FontHeight(monaco, 9) + PADDING); |
243 | TextFont(monaco); |
244 | TextSize(9); |
245 | len = snprintf((char *)buf, sizeof(buf), "%d (+), %d (-)", |
246 | committer->diff_adds, committer->diff_subs); |
247 | DrawText(buf, 0, len); |
248 | } |
249 | |
250 | committer_update_menu(committer); |
251 | UpdtControl(committer->win, committer->win->visRgn); |
252 | |
253 | break; |
254 | case activateEvt: |
255 | if (event->modifiers & activeFlag) { |
256 | TEActivate(committer->log_te); |
257 | TEActivate(committer->diff_te); |
258 | } else { |
259 | TEDeactivate(committer->log_te); |
260 | TEDeactivate(committer->diff_te); |
261 | } |
262 | break; |
263 | } |
264 | } |
265 | |
266 | void |
267 | committer_suspend(struct focusable *focusable) |
268 | { |
269 | struct committer *committer = (struct committer *)focusable->cookie; |
270 | |
271 | TEDeactivate(committer->log_te); |
272 | TEDeactivate(committer->diff_te); |
273 | } |
274 | |
275 | void |
276 | committer_resume(struct focusable *focusable) |
277 | { |
278 | struct committer *committer = (struct committer *)focusable->cookie; |
279 | |
280 | TEActivate(committer->log_te); |
281 | TEActivate(committer->diff_te); |
282 | } |
283 | |
284 | void |
285 | committer_key_down(struct focusable *focusable, EventRecord *event) |
286 | { |
287 | struct committer *committer = (struct committer *)focusable->cookie; |
288 | char k; |
289 | |
290 | k = event->message & charCodeMask; |
291 | if ((event->modifiers & cmdKey) != 0) { |
292 | if (k == 'w') |
293 | focusable_close(focusable); |
294 | return; |
295 | } |
296 | |
297 | TEKey(k, committer->log_te); |
298 | UpdateScrollbarForTE(committer->win, committer->log_scroller, |
299 | committer->log_te, false); |
300 | committer_update_menu(committer); |
301 | } |
302 | |
303 | void |
304 | committer_mouse_down(struct focusable *focusable, EventRecord *event) |
305 | { |
306 | struct committer *committer = (struct committer *)focusable->cookie; |
307 | Point p; |
308 | ControlHandle control; |
309 | Rect r; |
310 | short val, adj; |
311 | |
312 | p = event->where; |
313 | GlobalToLocal(&p); |
314 | |
315 | r = (*(committer->diff_te))->viewRect; |
316 | if (PtInRect(p, &r)) { |
317 | TEClick(p, ((event->modifiers & shiftKey) != 0), |
318 | committer->diff_te); |
319 | committer->last_te = committer->diff_te; |
320 | committer_update_menu(committer); |
321 | return; |
322 | } |
323 | |
324 | r = (*(committer->log_te))->viewRect; |
325 | if (PtInRect(p, &r)) { |
326 | TEClick(p, ((event->modifiers & shiftKey) != 0), committer->log_te); |
327 | committer->last_te = committer->log_te; |
328 | committer_update_menu(committer); |
329 | return; |
330 | } |
331 | |
332 | switch (FindControl(p, committer->win, &control)) { |
333 | case inButton: |
334 | if (TrackControl(control, p, 0L) && |
335 | control == committer->commit_button) |
336 | committer_commit(committer); |
337 | break; |
338 | case inUpButton: |
339 | case inDownButton: |
340 | case inPageUp: |
341 | case inPageDown: |
342 | if (control == committer->diff_scroller) |
343 | SetTrackControlTE(committer->diff_te); |
344 | else if (control == committer->log_scroller) |
345 | SetTrackControlTE(committer->log_te); |
346 | else |
347 | break; |
348 | TrackControl(control, p, TrackMouseDownInControl); |
349 | break; |
350 | case inThumb: |
351 | val = GetCtlValue(control); |
352 | if (TrackControl(control, p, 0L) == 0) |
353 | break; |
354 | adj = val - GetCtlValue(control); |
355 | if (adj != 0) { |
356 | val -= adj; |
357 | if (control == committer->diff_scroller) |
358 | TEScroll(0, adj * TEGetHeight(0, 0, committer->diff_te), |
359 | committer->diff_te); |
360 | else if (control == committer->log_scroller) |
361 | TEScroll(0, adj * TEGetHeight(0, 0, committer->log_te), |
362 | committer->log_te); |
363 | SetCtlValue(control, val); |
364 | } |
365 | break; |
366 | } |
367 | } |
368 | |
369 | void |
370 | committer_update_menu(struct committer *committer) |
371 | { |
372 | HLock(committer->diff_te); |
373 | HLock(committer->log_te); |
374 | |
375 | if (committer->last_te == committer->diff_te) { |
376 | DisableItem(edit_menu, EDIT_MENU_CUT_ID); |
377 | if ((*(committer->diff_te))->selStart == |
378 | (*(committer->diff_te))->selEnd) |
379 | DisableItem(edit_menu, EDIT_MENU_COPY_ID); |
380 | else |
381 | EnableItem(edit_menu, EDIT_MENU_COPY_ID); |
382 | if ((*(committer->diff_te))->nLines > 0) |
383 | EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
384 | else |
385 | DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
386 | DisableItem(edit_menu, EDIT_MENU_PASTE_ID); |
387 | } else if (committer->last_te == committer->log_te) { |
388 | if ((*(committer->log_te))->selStart == |
389 | (*(committer->log_te))->selEnd) { |
390 | DisableItem(edit_menu, EDIT_MENU_CUT_ID); |
391 | DisableItem(edit_menu, EDIT_MENU_COPY_ID); |
392 | } else { |
393 | EnableItem(edit_menu, EDIT_MENU_CUT_ID); |
394 | EnableItem(edit_menu, EDIT_MENU_COPY_ID); |
395 | } |
396 | if ((*(committer->log_te))->nLines > 0) |
397 | EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
398 | else |
399 | DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
400 | EnableItem(edit_menu, EDIT_MENU_PASTE_ID); |
401 | } |
402 | |
403 | DisableItem(repo_menu, REPO_MENU_ADD_FILE_ID); |
404 | DisableItem(repo_menu, REPO_MENU_DISCARD_CHANGES_ID); |
405 | DisableItem(repo_menu, REPO_MENU_APPLY_DIFF_ID); |
406 | |
407 | DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID); |
408 | DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID); |
409 | |
410 | HUnlock(committer->log_te); |
411 | HUnlock(committer->diff_te); |
412 | |
413 | EnableItem(repo_menu, 0); |
414 | |
415 | if ((*(committer->log_te))->nLines > 0 && committer->allow_commit) |
416 | HiliteControl(committer->commit_button, 0); |
417 | else |
418 | HiliteControl(committer->commit_button, 255); |
419 | } |
420 | |
421 | void |
422 | committer_generate_diff(struct committer *committer) |
423 | { |
424 | struct repo_file *file; |
425 | short i, all_files; |
426 | short *selected_files = NULL; |
427 | short nselected_files = 0; |
428 | TextStyle style; |
429 | |
430 | SetCursor(*(GetCursor(watchCursor))); |
431 | |
432 | nselected_files = browser_selected_file_ids(committer->browser, |
433 | &selected_files); |
434 | |
435 | /* default to unified diffs (should this be a setting?) */ |
436 | diff_format = D_UNIFIED; |
437 | diff_context = 3; |
438 | |
439 | committer->diff_adds = 0; |
440 | committer->diff_subs = 0; |
441 | committer->ndiffed_files = 0; |
442 | committer->allow_commit = false; |
443 | committer->diffed_files = xcalloc(sizeof(struct diffed_file), |
444 | nselected_files, "committer diffed_files"); |
445 | committer->diff_too_big = false; |
446 | |
447 | HLock(committer->diff_te); |
448 | |
449 | style.tsFont = monaco; |
450 | style.tsSize = 9; |
451 | TESetStyle(doFont | doSize, &style, false, committer->diff_te); |
452 | |
453 | all_files = browser_is_all_files_selected(committer->browser); |
454 | |
455 | committer_diffing = committer; |
456 | |
457 | for (i = 0; i < nselected_files; i++) { |
458 | file = repo_file_with_id(committer->browser->repo, |
459 | selected_files[i]); |
460 | if (file == NULL) |
461 | err(1, "Failed to find file in repo with id %d", |
462 | selected_files[i]); |
463 | |
464 | if (all_files && !repo_file_changed(committer->browser->repo, |
465 | file)) { |
466 | progress("Skipping unchanged %s...", file->filename); |
467 | continue; |
468 | } |
469 | |
470 | committer->diffed_files[committer->ndiffed_files].file = file; |
471 | committer->diffed_files[committer->ndiffed_files].flags = |
472 | DIFFED_FILE_METADATA; |
473 | |
474 | progress("Diffing %s...", file->filename); |
475 | if (repo_diff_file(committer->browser->repo, file)) { |
476 | committer->diffed_files[committer->ndiffed_files].flags |= |
477 | DIFFED_FILE_TEXT; |
478 | committer->allow_commit = true; |
479 | } |
480 | |
481 | diff_finish(); |
482 | committer->ndiffed_files++; |
483 | } |
484 | |
485 | committer_diffing = NULL; |
486 | |
487 | HUnlock(committer->diff_te); |
488 | InvalRect(&committer->win->portRect); |
489 | UpdateScrollbarForTE(committer->win, committer->diff_scroller, |
490 | committer->diff_te, true); |
491 | |
492 | progress(NULL); |
493 | |
494 | done_diffing: |
495 | if (selected_files != NULL) |
496 | xfree(&selected_files); |
497 | SetCursor(&arrow); |
498 | |
499 | if (!committer->allow_commit) { |
500 | warnx("No changes detected"); |
501 | browser_close_committer(committer->browser); |
502 | } |
503 | } |
504 | |
505 | void |
506 | committer_commit(struct committer *committer) |
507 | { |
508 | struct browser *browser; |
509 | short loglen; |
510 | |
511 | HLock(committer->log_te); |
512 | HLock(committer->diff_te); |
513 | |
514 | loglen = (*(committer->log_te))->teLength; |
515 | |
516 | SetCursor(*(GetCursor(watchCursor))); |
517 | |
518 | progress("Committing changes..."); |
519 | |
520 | repo_amend(committer->browser->repo, committer->diffed_files, |
521 | committer->ndiffed_files, committer->diff_adds, committer->diff_subs, |
522 | settings.author, (*(committer->log_te))->hText, loglen, |
523 | (*(committer->diff_te))->hText, committer->diff_te_len); |
524 | |
525 | HUnlock(committer->diff_te); |
526 | HUnlock(committer->log_te); |
527 | |
528 | progress(NULL); |
529 | SetCursor(&arrow); |
530 | |
531 | browser = committer->browser; |
532 | browser_close_committer(committer->browser); |
533 | browser->state = BROWSER_STATE_UPDATE_AMENDMENT_LIST; |
534 | } |
535 | |
536 | bool |
537 | committer_handle_menu(struct focusable *focusable, short menu, short item) |
538 | { |
539 | struct committer *committer = (struct committer *)focusable->cookie; |
540 | |
541 | switch (menu) { |
542 | case EDIT_MENU_ID: |
543 | switch (item) { |
544 | case EDIT_MENU_CUT_ID: |
545 | if (committer->last_te == committer->log_te) { |
546 | TECut(committer->log_te); |
547 | UpdateScrollbarForTE(committer->win, |
548 | committer->log_scroller, committer->log_te, false); |
549 | committer_update_menu(committer); |
550 | } |
551 | return true; |
552 | case EDIT_MENU_COPY_ID: |
553 | if (committer->last_te) |
554 | TECopy(committer->last_te); |
555 | committer_update_menu(committer); |
556 | return true; |
557 | case EDIT_MENU_PASTE_ID: |
558 | if (committer->last_te == committer->log_te) { |
559 | TEPaste(committer->log_te); |
560 | UpdateScrollbarForTE(committer->win, |
561 | committer->log_scroller, committer->log_te, false); |
562 | committer_update_menu(committer); |
563 | } |
564 | return true; |
565 | case EDIT_MENU_SELECT_ALL_ID: |
566 | if (committer->last_te) |
567 | TESetSelect(0, 1024 * 32, committer->last_te); |
568 | committer_update_menu(committer); |
569 | return true; |
570 | } |
571 | break; |
572 | } |
573 | |
574 | return false; |
575 | } |
576 | |
577 | size_t |
578 | diff_output(const char *format, ...) |
579 | { |
580 | va_list argptr; |
581 | size_t len, last_pos, last_line, i; |
582 | |
583 | if (committer_diffing == NULL) |
584 | panic("diff_output without committer_diffing"); |
585 | |
586 | if (committer_diffing->diff_line == NULL) { |
587 | committer_diffing->diff_line = xNewHandle(DIFF_LINE_SIZE); |
588 | committer_diffing->diff_line_pos = 0; |
589 | HLock(committer_diffing->diff_line); |
590 | } |
591 | |
592 | last_pos = committer_diffing->diff_line_pos; |
593 | last_line = 0; |
594 | |
595 | va_start(argptr, format); |
596 | |
597 | if (format[0] == '%' && format[1] == 'c' && format[2] == '\0') { |
598 | /* avoid having to vsprintf just to append 1 character */ |
599 | (*(committer_diffing->diff_line))[last_pos] = va_arg(argptr, int); |
600 | len = 1; |
601 | } else |
602 | len = vsprintf(*(committer_diffing->diff_line) + last_pos, format, |
603 | argptr); |
604 | |
605 | va_end(argptr); |
606 | |
607 | committer_diffing->diff_line_pos += len; |
608 | if (committer_diffing->diff_line_pos >= DIFF_LINE_SIZE) |
609 | err(1, "diff line overflow!"); |
610 | |
611 | if (len == 1 && (*(committer_diffing->diff_line))[last_pos] != '\r') |
612 | return 1; |
613 | |
614 | for (i = last_pos; i < committer_diffing->diff_line_pos; i++) { |
615 | if (((char *)*(committer_diffing->diff_line))[i] == '\r') { |
616 | diff_append_line(*(committer_diffing->diff_line) + last_line, |
617 | i - last_line + 1, false); |
618 | last_line = i + 1; |
619 | } |
620 | } |
621 | |
622 | if (last_line == committer_diffing->diff_line_pos) { |
623 | committer_diffing->diff_line_pos = 0; |
624 | } else if (last_line > 0) { |
625 | memmove(*(committer_diffing->diff_line), |
626 | *(committer_diffing->diff_line) + last_line, |
627 | committer_diffing->diff_line_pos - last_line); |
628 | committer_diffing->diff_line_pos -= last_line; |
629 | } |
630 | |
631 | return len; |
632 | } |
633 | |
634 | void |
635 | diff_append_line(char *str, size_t len, bool flush) |
636 | { |
637 | if (committer_diffing == NULL) |
638 | panic("diff_append_line without committer_diffing"); |
639 | |
640 | if (committer_diffing->diff_chunk == NULL) { |
641 | committer_diffing->diff_chunk = xNewHandle(DIFF_CHUNK_SIZE); |
642 | committer_diffing->diff_chunk_pos = 0; |
643 | } |
644 | |
645 | if (str[0] == '-' && str[1] != '-') |
646 | committer_diffing->diff_subs++; |
647 | else if (str[0] == '+' && str[1] != '+') |
648 | committer_diffing->diff_adds++; |
649 | |
650 | if (committer_diffing->diff_chunk_pos + len >= DIFF_CHUNK_SIZE) |
651 | diff_chunk_write(); |
652 | |
653 | HLock(committer_diffing->diff_chunk); |
654 | memcpy(*(committer_diffing->diff_chunk) + |
655 | committer_diffing->diff_chunk_pos, str, len); |
656 | HUnlock(committer_diffing->diff_chunk); |
657 | committer_diffing->diff_chunk_pos += len; |
658 | |
659 | if (flush) |
660 | diff_chunk_write(); |
661 | } |
662 | |
663 | void |
664 | diff_chunk_write(void) |
665 | { |
666 | if (committer_diffing == NULL) |
667 | panic("diff_chunk_write without committer_diffing"); |
668 | |
669 | HLock(committer_diffing->diff_chunk); |
670 | |
671 | if (committer_diffing->diff_te_len + |
672 | committer_diffing->diff_chunk_pos > MAX_TEXTEDIT_SIZE) { |
673 | HUnlock((*(committer_diffing->diff_te))->hText); |
674 | SetHandleSize((*(committer_diffing->diff_te))->hText, |
675 | committer_diffing->diff_te_len + |
676 | committer_diffing->diff_chunk_pos); |
677 | if (MemError()) |
678 | err(1, "Out of memory! Can't expand diff TE by %lu bytes.", |
679 | committer_diffing->diff_chunk_pos); |
680 | HLock((*(committer_diffing->diff_te))->hText); |
681 | memcpy(*(*(committer_diffing->diff_te))->hText + |
682 | committer_diffing->diff_te_len, *(committer_diffing->diff_chunk), |
683 | committer_diffing->diff_chunk_pos); |
684 | HUnlock((*(committer_diffing->diff_te))->hText); |
685 | } else { |
686 | TEStylInsert(*(committer_diffing->diff_chunk), |
687 | committer_diffing->diff_chunk_pos, 0, |
688 | committer_diffing->diff_te); |
689 | } |
690 | HUnlock(committer_diffing->diff_chunk); |
691 | |
692 | committer_diffing->diff_te_len += committer_diffing->diff_chunk_pos; |
693 | committer_diffing->diff_chunk_pos = 0; |
694 | } |
695 | |
696 | void |
697 | diff_finish(void) |
698 | { |
699 | if (committer_diffing == NULL) |
700 | panic("diff_finish without committer_diffing"); |
701 | |
702 | if (committer_diffing->diff_line != NULL) { |
703 | if (committer_diffing->diff_line_pos) |
704 | diff_append_line(*(committer_diffing->diff_line), |
705 | committer_diffing->diff_line_pos, true); |
706 | |
707 | DisposHandle(committer_diffing->diff_line); |
708 | committer_diffing->diff_line = NULL; |
709 | } |
710 | |
711 | if (committer_diffing->diff_chunk != NULL) { |
712 | if (committer_diffing->diff_chunk_pos) |
713 | diff_chunk_write(); |
714 | DisposHandle(committer_diffing->diff_chunk); |
715 | committer_diffing->diff_chunk = NULL; |
716 | } |
717 | |
718 | HUnlock((*(committer_diffing->diff_te))->hText); |
719 | SetHandleSize((*(committer_diffing->diff_te))->hText, |
720 | committer_diffing->diff_te_len); |
721 | TECalText(committer_diffing->diff_te); |
722 | } |