% -*- slang -*- % This file provides a mode for viewing/editing diffs. Although this % mode do make a feeble attempt at supporting non-unified diffs, the % editing functionality will mostly break if you edit non-unified % diffs. % % Written by Abraham vd Merwe % Last updated: 24 June 2002 % % Credits % % Lots of thanks go to Dino Leonardo Sangoi without % whom this mode would have been a lot less useful. Many of the functions in % this mode is based on ideas I found in his diff mode. % % Keys % % Alt-9 Skip to previous block % Alt-0 Skip to next block % % Alt-, Skip to previous file % Alt-. Skip to next file % % Ctrl-F Mark file % Ctrl-B Mark block % % Ctrl-R Redo block % % Ctrl-C If you press this key, you get a menu % with keys to do all of the above and more % % Todo % % 1. Create buffer with all the diff headers so we can see what % file is involved % % 2. While in that buffer, if ENTER is pressed, go to that file % in the other buffer % % Changelog % % 2001-09-20 Abraham vd Merwe % - Initial mode for jed 0.99.12 % % 2001-09-20 Johann Botha % - Hacked to work with jed 0.99.14 % - Added regex for standard diff output % % 2001-09-21 Abraham vd Merwe % - Got rid of $1 (dangerous) % - Changed the predefined color keywords to language % language dependant ones. % % 2001-09-22 Abraham vd Merwe % - Added syntax highlighting for "Only in" % - Added loads of functions/keys % - Added the usual ^c way shortcut key % % 2001-09-24 Abraham vd Merwe % - Added function to remove stray headers (i.e. files % without any blocks in it) variable diff = "diff"; % Now create and initialize a syntax table. create_syntax_table (diff); set_syntax_flags (diff,0x01 | 0x04); !if (keymap_p (diff)) make_keymap (diff); variable bg = "black"; set_color ("keyword1","brown",bg); set_color ("keyword2","brightred",bg); set_color ("keyword3","white",bg); set_color ("keyword4","green",bg); set_color ("keyword5","gray",bg); set_color ("keyword6","cyan",bg); set_color ("keyword7","brightmagenta",bg); #ifdef HAS_DFA_SYNTAX %%% DFA_CACHE_BEGIN %%% static define setup_dfa_callback (name) { variable color_diff = "keyword1"; variable color_delete_file = "keyword2"; variable color_add_file = "keyword3"; variable color_position = "keyword4"; variable color_delete_line = "keyword5"; variable color_add_line = "keyword6"; variable color_only_in = "keyword7"; dfa_enable_highlight_cache ("diff.dfa",name); dfa_define_highlight_rule ("^diff .*$",color_diff,name); dfa_define_highlight_rule ("^\\-\\-\\- .*$",color_delete_file,name); dfa_define_highlight_rule ("^\\+\\+\\+ .*$",color_add_file,name); dfa_define_highlight_rule ("^@@.*@@",color_position,name); dfa_define_highlight_rule ("^[0-9].*$",color_position,name); dfa_define_highlight_rule ("^\\-.*$",color_delete_line,name); dfa_define_highlight_rule ("^\\+.*$",color_add_line,name); dfa_define_highlight_rule ("^> .*$",color_add_line,name); dfa_define_highlight_rule ("^< .*$",color_delete_line,name); dfa_define_highlight_rule ("^Only in .*$",color_only_in,name); dfa_build_highlight_table (name); } dfa_set_init_callback (&setup_dfa_callback,diff); %%% DFA_CACHE_END %%% #endif %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% static define diff_prev (marker,name) { push_mark (); bol (); !if (bol_bsearch (marker)) { if (looking_at (marker)) { pop_mark (1); error (sprintf ("Already at first %s",name)); } bob (); !if (bol_fsearch (marker)) { pop_mark (1); error (sprintf ("No %ss found",name)); } } pop_mark (0); } static define diff_next (marker,name) { push_mark (); bol (); go_right_1 (); !if (bol_fsearch (marker)) { eob (); !if (bol_bsearch (marker)) { pop_mark (1); error (sprintf ("No %ss found",name)); } else { pop_mark (1); error (sprintf ("Already at last %s",name)); } } pop_mark (0); } define diff_prev_file () { diff_prev ("diff ","file"); } define diff_next_file () { diff_next ("diff ","file"); } define diff_prev_block () { diff_prev ("@@ ","block"); } define diff_next_block () { diff_next ("@@ ","block"); } static define check_if_mark_is_lonely (marker,name) { push_mark (); bol (); go_right_1 (); !if (bol_fsearch (marker)) { eob (); !if (bol_bsearch (marker)) { smart_set_mark_cmd (); pop_mark (1); error (sprintf ("No %ss found",name)); } } pop_mark (1); } % this function is a mess. also, it doesn't display % an error when you've marked the last function and % try marking it again... define diff_mark_file () { variable marker = "diff "; variable name = "file"; !if (looking_at (marker)) diff_prev (marker,name); check_if_mark_is_lonely (marker,name); !if (markp ()) smart_set_mark_cmd (); bol (); go_right_1 (); !if (bol_fsearch (marker)) eob (); } static define find_next_marker () { while (not (looking_at ("diff ") or looking_at ("@@ ") or looking_at ("Only in "))) !if (down_1 ()) return (0); return (1); } % this function is an even bigger mess ): in addition % it has the same flaw as the diff_mark_file() function. % in addition it will also marker the next block even if % it's not in a block... define diff_mark_block () { variable marker = "@@ "; variable name = "block"; !if (looking_at (marker)) diff_prev (marker,name); check_if_mark_is_lonely (marker,name); !if (markp ()) smart_set_mark_cmd (); bol (); go_right_1 (); !if (find_next_marker ()) eob (); } define diff_narrow_to_file () { diff_mark_file (); if (down_1 ()) go_up (2); eol (); narrow (); } define diff_narrow_to_block () { variable str = ""; push_spot (); bol (); if (looking_at ("diff ") or bol_bsearch ("diff ")) { bol (); push_mark (); go_down (3); str = bufsubstr (); } pop_spot (); diff_mark_block (); if (down_1 ()) go_up (2); eol (); narrow (); push_spot (); bob(); insert (str); pop_spot (); } define diff_remove_file () { diff_mark_file (); call ("kill_region"); } define diff_remove_block () { diff_mark_block (); call ("kill_region"); push_spot (); if (looking_at ("diff ") or looking_at ("Only in ")) { go_up (3); bol (); if (looking_at ("diff ")) { push_mark (); go_down (3); call ("kill_region"); } } pop_spot (); } static define diff_parse_block_info (line) { variable pos,len; variable oldpos,oldlen,newpos,newlen; !if (string_match (line,"@@ \\-\\([0-9][0-9]\\)*,\\([0-9][0-9]*\\) \\+\\([0-9][0-9]\\)*,\\([0-9][0-9]*\\) @@",1)) { flush (line); error ("malformed block header"); } (pos,len) = string_match_nth (1); oldpos = integer (line[[pos:pos + len]]); (pos,len) = string_match_nth (2); oldlen = integer (line[[pos:pos + len]]); (pos,len) = string_match_nth (3); newpos = integer (line[[pos:pos + len]]); (pos,len) = string_match_nth (4); newlen = integer (line[[pos:pos + len]]); return (oldpos,oldlen,newpos,newlen); } static define diff_count_block () { variable countplus = 0; variable countminus = 0; variable countspace = 0; while (down_1 ()) { bol (); if (eobp ()) break; switch (what_char ()) { case '+': countplus++; } { case '-': countminus++; } { countspace++; } } return (countplus,countminus,countspace); } static define diff_redo_block (old_off,new_off) { variable countminus = 0; variable countplus = 0; variable countspace = 0; variable oldpos,oldsize,newpos,newsize; variable c; variable oldheader,newheader; push_spot (); diff_narrow_to_block (); bob (); oldheader = line_as_string (); (oldpos,oldsize,newpos,newsize) = diff_parse_block_info (oldheader); (countplus,countminus,countspace) = diff_count_block (); countplus += countspace; countminus += countspace; newheader = sprintf ("@@ -%d,%d +%d,%d @@",oldpos + old_off,countminus,newpos + new_off,countplus); flush (sprintf ("@@ -%d,%d +%d,%d @@ --> %s",oldpos,oldsize,newpos,newsize,newheader)); if (strcmp (oldheader,newheader)) { bob (); call ("kill_line"); insert (newheader + "\n"); } widen (); pop_spot (); return (old_off + countminus - oldsize,new_off + countplus - newsize); } %%static define diff_redo_from_here (oldoff,newoff) %%{ %% % Hack to avoid parsing two times the first block %% diff_top_of_block (); %% diff_end_of_block (); %% variable done = eolp (); %% %% forever %% { %% (oldoff,newoff) = diff_redo_block (oldoff,newoff); %% diff_end_of_block (); %% if (eolp ()) %% { %% if (done) break; %% done = 1; %% } %% } %%} define diff_redo_all_blocks () { %% push_spot (); %% diff_narrow_to_file (); %% bob (); %% diff_redo_from_here (0,0); %% widen (); %% pop_spot (); } % BUG: if there's a stray header right at the end of the file % and no newline after the +++ line, this function won't delete % it. i don't think that's likely to happen though, so I'm too % lazy to fix that... define diff_remove_stray_file_headers () { variable header_count = 0; push_spot (); bob (); while (bol_fsearch ("diff ") and down (3)) { bol (); if (looking_at ("diff ") or looking_at ("Only in ")) { header_count++; push_mark (); go_up (3); bol (); call ("kill_region"); } else !if (right (1)) { header_count++; push_mark (); go_up (3); bol (); call ("kill_region"); } } pop_spot (); !if (header_count) error ("There is no stray headers in this buffer"); } define diff_remove_only_in_lines () { variable only_count = 0; push_spot (); bob (); while (bol_fsearch ("Only in ")) { bol (); edt_ldel (); only_count++; } pop_spot (); !if (only_count) error ("There is no \"Only in\" tags in this buffer"); } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% static define diff_readkey (msg) { variable key; !if (input_pending (3)) flush (msg); tolower (getkey ()); } static define diff_file_commands () { switch (diff_readkey ("[file commands] Top:T End:E Mark:M Narrow:N Delete:D")) { case 't': diff_prev_file (); } { case 'e': diff_next_file (); } { case 'm': diff_mark_file (); } { case 'n': diff_narrow_to_file (); } { case 'd': diff_remove_file (); } { error ("undefined key"); } } static define diff_block_commands () { switch (diff_readkey ("[block commands] Top:T End:E Mark:M Narrow:N Delete:D Redo:R")) { case 't': diff_prev_block (); } { case 'e': diff_next_block (); } { case 'm': diff_mark_block (); } { case 'n': diff_narrow_to_block (); } { case 'd': diff_remove_block (); } { case 'r': diff_redo_all_blocks (); } { error ("undefined key"); } } static define diff_remove_junk_commands () { switch (diff_readkey ("[remove junk commands] \"Only In\" lines:O Stray File Headers:F")) { case 'o': diff_remove_only_in_lines (); } { case 'f': diff_remove_stray_file_headers (); } { error ("undefined key"); } } define diff_commands () { switch (diff_readkey ("--> File Commands:F Block Commands:B Remove Junk Commands:R")) { case 'f': diff_file_commands (); } { case 'b': diff_block_commands (); } { case 'r': diff_remove_junk_commands (); } { error ("undefined key"); } flush (""); } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% define diff_mode () { variable i; variable keys = [ [ "\e.", "diff_next_file" ], [ "\e,", "diff_prev_file" ], [ "\e0", "diff_next_block" ], [ "\e9", "diff_prev_block" ], [ "^f", "diff_mark_file" ], [ "^b", "diff_mark_block" ], [ "^r", "diff_redo_all_blocks" ], [ "^c", "diff_commands" ], [ "", "" ] ]; set_mode (diff,4); use_keymap (diff); use_syntax_table (diff); for (i = 0; keys[i,0] != ""; i++) { undefinekey (keys[i,0],diff); definekey (keys[i,1],keys[i,0],diff); } enable_dfa_syntax_for_mode (diff); }