1 ;;; tinydiff.el --- Diff and patch minor mode. Browsing, patching.
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1996-2007 Jari Aalto
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program use ident(1) or call M-x
13 ;; tinydiff-version Look at the code with folding.el
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into your
39 ;; ~/.emacs startup file
41 ;; Here is the very basic setup. You don't necessarily need to set
42 ;; these variables, because they are determined at startup.
44 ;; (setq tinydiff-:diff-program "gdiff") ;; GNU diff
45 ;; (setq tinydiff-:patch-program "gpatch -t") ;; GNU patch
47 ;; (require 'tinydiff)
49 ;; OR use this; your .emacs will load much quicker
51 ;; (autoload 'tinydiff-mode "tinydiff" "" t)
52 ;; (autoload 'turn-on-tinydiff-mode "tinydiff" "" t)
53 ;; (autoload 'tinydiff-diff "tinydiff" "" t)
54 ;; (autoload 'tinydiff-diff-show "tinydiff" "" t)
55 ;; (autoload 'tinydiff-diff-show-noask "tinydiff" "" t)
57 ;; Here are some suggested key bindings. Reserve The "d" for diff map.
59 ;; (global-set-key "\C-cmd" nil) ;; Initialize prefix key
60 ;; (global-set-key "\C-cmdd" 'tinydiff-mode)
61 ;; (global-set-key "\C-cmdd" 'tinydiff-diff-show)
62 ;; (global-set-key "\C-cmdp" 'tinydiff-patch)
64 ;; If you have any questions, use these functions
66 ;; M-x tinydiff-debug-toggle turn on debug
67 ;; <... do as you did ...>
68 ;; M-x tinydiff-submit-bug-report send bug report
73 ;; ..................................................... &t-commentary ...
79 ;; Long ago there was set of simple functions lying around to generate
80 ;; instant diffs for the file that was being edited, before it was
81 ;; checked in with RCS. At the time *vc.el* was not in the Emacs
82 ;; distribution. Looking at diffs and using "goto-line" command in
83 ;; other buffer gave an idea to make a separate diff mode. The project
84 ;; turned out to be a bit bigger than just taking simple diff. You may
85 ;; wonder, why would you use this utility over ediff.el? If you like
86 ;; working with "command line" diff interface, then you may want to
87 ;; use this utility over *ediff.el*. There is a command prompt when
88 ;; various diff options can be manipulated with key bindings. Lik:
89 ;; Change rcsdiff to diff command, copy previous argument etc.
91 ;; Overview of features
95 ;; o Buffer based simple diff/rcsdiff/patch package.
96 ;; o You can diff current buffer against last saved backup copy.
97 ;; o Give and manipulate diff command in echo-area: file name
98 ;; completion; switching between rcsdiff/diff, guess rcs version
99 ;; for buffer, various diff switches...
103 ;; o Supported: normal diff, context diff, gnu diff -u, gnu diff -n
104 ;; o When you have diff output in buffer, turning on `tinydiff-mode'
105 ;; allows you to browse source buffer by jumping diff blocks fwd/back
106 ;; and showing the source location.
107 ;; o You can kill single diff block with `tinydiff-block-kill'
108 ;; o You can apply only the current diff block (patch) with
109 ;; `tinydiff-block-apply-patch'
111 ;; Sending or saving diff
113 ;; o In diff buffer, you can save the diff to a file with "W"; write
114 ;; o In diff buffer, you can attach the content as MIME diff to
115 ;; the open mail buffer. Or if you don't have MIME-edit active,
116 ;; the diff is added without MIME tags. Command "M" for Mime.
120 ;; o Easy patching. Finds file to patch (along user defined paths)
121 ;; and applies the diff. You can receive patches by email
122 ;; and apply them with one or two keystrokes.
123 ;; o Finds the file to patch through pre defined paths.
124 ;; o Can also patch compresses .gz file.
125 ;; o loads patch rejection file, if patch didn't succeed 100%
126 ;; o Re-evaluates patched lisp file if the file was used by Emacs
127 ;; o If you don't want to apply whole diff, use
128 ;; `tinydiff-block-apply-patch' for individual sections.
130 ;; Genrating diff -- parsing diff
132 ;; Be in buffer where you have diff file and just turn on the
136 ;; Then take a look at the bindings you have available:
140 ;; If you want to generate [rcs]diff for current buffer, call function
142 ;; M-x tinydiff-diff-show
144 ;; And it generates diff and puts you on `tinydiff-mode'. X window users and
145 ;; those that have the highlighting capabitities can enjoy more about
146 ;; this mode, because it marks line numbers in buffer with
147 ;; `mouse-face'. You just click the point to jump to diff position
151 ;; The main purpose of this module is to help you taking "diff shots",
152 ;; inside emacs. This means that the file must be loaded into
153 ;; emacs and your cursor must be in the buffers, before you execute
155 ;; M-x tinydiff-diff-show
157 ;; o If the file is not rcs controlled you're offered regular diff
158 ;; o if file is rcs controlled, your're offered rcsdiff prompt
159 ;; o if the buffer has changed, you're offered to diff against
160 ;; last saved file to see recent changes you have done since you
165 ;; The help key is on `?', press it to get summary of command while
166 ;; you're in minibuffer prompt. The command prompt in minibuffer looks
167 ;; like this for rcs controlled file.
169 ;; > cd /users/foo/dir1/dir2; rcsdiff -c -r1.21 test.txt
171 ;; You can edit this command as much as you like, but please leave `cd'
172 ;; `XXX' alone because the minibuffer commands expect it it be
173 ;; present. The hotkey command won't work without it.
175 ;; Command prompt: rcsdiff and diff toggle
177 ;; To conveniently construct diff command against another file, say
178 ;; test2.txt, you can hit key `C-z' to chage the prompt immediately to
180 ;; > cd /users/foo/dir1/dir2; diff -c test.txt
182 ;; And add the `test2.txt' to the end of line. If you want to restore
183 ;; the previous rcsdiff form, just hit `C-z' again. This `C-z'
184 ;; facility works only if the initial command was rcsdiff. There is no
185 ;; point of converting initial diff command to rcsdiff command though.
187 ;; Command prompt: tab completes file name
189 ;; While your're editing the command you can also use the TAB key to
190 ;; complete filename in the 'cd' command directory. If you specify any
191 ;; directories for the file, the directory's files are completed.
192 ;; That feature should allow you to get filenames into the prompt
195 ;; Command prompt: diffing between two Rcs revisions
197 ;; There is also more commands, like `C-r' which changes
199 ;; > cd /users/foo/dir1/dir2; rcsdiff -c -r1.21 test.txt
201 ;; prompt so that it has now two -r commands. You can take diffs
202 ;; between two versions easily with it. The `C-r' key is a toggle.
204 ;; > cd /users/foo/dir1/dir2; rcsdiff -c -r1.21 -r1.21 test.txt
208 ;; You see nice package on the net. You download it ;; and notice that
209 ;; it needs some fixes. You put the original version ;; to your
210 ;; private rcstree with the same version number as what ;; the package
211 ;; had; say 2.2. Then you CheckOut the original, make ;; changes, and
212 ;; put it back to tree with version 2.2.1.1. You dont't ;; put it back
213 ;; with 2.3, because that's not your file. You made the ;; correction
214 ;; to 2.2, so you must make a branch.
216 ;; Okay. You have the original 2.2 and you have the fixed version
217 ;; 2.2.1.1 and you want to send the diff to the author. Here is how
220 ;; o Be on the file buffer 2.2.1.1 and hit M-x tinydiff-dif
221 ;; o Hit `C-r' toggle second revision (previous) and edit the line
222 ;; to look "-r2.2 -r2.2.1.1". You are usually comparing _old_ and
224 ;; o Hit `C-s' to toggle between `-u' or `-c'. You normally want
225 ;; to send `-u' gnu unified diff, because it is more readable.
226 ;; Provided that the receiver has gnu patch to understand it.
227 ;; o Hit `C-f' to add option `tinydiff-:cl-user-option' which by
228 ;; default is `-kk'. From the rcsdiff(1) man pages you will
229 ;; see that it roughly means: "When you take diff between versions,
230 ;; ignore the rcs tag differencies". Confused? It means that
231 ;; the keywords that changed, like version, author, log ..
232 ;; when you deposited 2.2 and 2.2.1.1 are ignored.
234 ;; And hit enter. Then you get clean diff that you can send to author.
235 ;; And when he responds back or sends you new version, say 2.5,
236 ;; you repeat the whole process again if you intend to make more
237 ;; changes 8put original 2.5 on ice and make branch 2.5.1.1 for your
240 ;; Command prompt: autosave and backup file diff
242 ;; Other helpfull commands insert he #autosaved# and backup~ filenames
243 ;; into the current point. Remember to put the # or ~ file to the left
244 ;; and not to the right. you do want to diff current file against the
245 ;; saved one; right? The first one is original prompt. That second is
246 ;; after `C-r' and latter after `C-v'
248 ;; > cd /users/foo/dir1/dir2; diff -c test.txt
251 ;; > cd /users/foo/dir1/dir2; diff -c #test.txt# test.txt
252 ;; > cd /users/foo/dir1/dir2; diff -c ~/backup/test.txt~ test.txt
254 ;; Notice that your backup file may not reside int he same directory.
255 ;; The backupfilename is returned by function `make-backup-file'.
257 ;; Generated diff: the Prereq tag
259 ;; It is important that when you send diff, it is diff between two
260 ;; rcs versions if possible (if you're author of program). In those
261 ;; cases where revision information can be found, the diff data
262 ;; is preceeded with this line:
264 ;; Prereq: N.NN e.g. Prereq: 1.76
266 ;; If the receiving end has GNU patch, the patch program first checks
267 ;; if the version that person has is exactly N.NN and aborts if
268 ;; he had some other version. This prevent applying diffs that
269 ;; are meant to other versions. Regular Unix *patch* program
270 ;; does not notice the *Prereq:* tag, so consider getting more
271 ;; safer GNU version as soon as possible.
275 ;; There is also included little patching function.
277 ;; M-x tinydiff-patch non verbose
278 ;; C-u M-x tinydiff-patch verbose
280 ;; For elisp (.el) files the `load-path' is automatically searched
281 ;; for possible destination of the patch. You can set variable
283 ;; tinydiff-:patch-list
285 ;; To match files and their associated patch directories if you
286 ;; receive patches for other files regularly. This function is most
287 ;; useful for RCS diffs, because they can be easily detected and the
288 ;; file information is also included in the diff.
290 ;; Patch: general notes
292 ;; Note: normally when `patch' program is called it always makes
293 ;; backup with the suffix .orig. So if you have applied a patch,
294 ;; then there is two file in the directory.
296 ;; FILE.txt -- patched file
297 ;; FILE.txt.orig -- original file, before the patch
299 ;; It also creates rejections file if all dind't go as planned.
303 ;; Patch: success or failure
305 ;; When the patch has been applied, This package checks if all went
306 ;; well. If rejection file was created; then the patch process's
307 ;; output is shown and the rejection file is loaded so that you can
308 ;; see what possibly went wrong and if you should be concerned.
310 ;; If you get this rejection file, then there propably is potential
311 ;; trouble. Please contact the sender of patch immediately and tell
312 ;; about your troubles. There are few common reasons why patch failure
315 ;; o Sender forgot `-kk' switch when he run rcsdiff to the file
316 ;; that was not owned by him (See RCS for details about `-kk')
317 ;; o Too few context, Sender should try increasing context with
318 ;; `-C' switch (like `-C7')
319 ;; o The patch were modified during email transit. Ask
320 ;; to send the patch with some encoded format: uuencode, base64,
321 ;; PGP encrypted or PGP base64 signed (clearsig off) format.
323 ;; Patch: what happens after success
325 ;; When the patch succeeds, there is a bit special handling for Emacs
326 ;; elisp packages. Say we recieve correction to the following module
327 ;; and you have it loaded in emacs: (feature 'foo) returns true.
331 ;; After patch is applied, you're asked if you want to reload the
332 ;; new release of *foo* module (just patched). You should answer
333 ;; `Yes' to get the newest one running in your Emacs immediately.
335 ;; Patch: after success, returning to original version
337 ;; If the patched version, which is usually new version of the progrmam
338 ;; doesn't work as it is supposed to, you can go back to the original
339 ;; version by appluing the same patch again. You should report what
340 ;; problems you had to the maintainer and inform that you wnet back
341 ;; to previous version.
343 ;; *IMPORTANT* If you did get the rejection file, you can't use that
344 ;; patch to go back to original!! See next chapter how to go to
345 ;; original version in that case
347 ;; Patch: rejection file created -- what to do?
349 ;; If you want to go back to original version, apply the same diff
350 ;; again; this reverses just applied patch. Just call `M-x'
351 ;; `tinydiff-patch' in the buffer where you have the diff.
353 ;; When you do that, the function detects that there is already a
354 ;; .orig file and prompts you to choose an appropriate action.
355 ;; Here is the explanation what they do and what should you choose
359 ;; Go back to (o)riginal. This copies the FILE.txt.orig over the
360 ;; FILE.txt and deletes FILE.txt.orig and doesn't do _anything_
361 ;; else (stops the patching process). You're back to starting
362 ;; point as if you never patched anything.
366 ;; (R)etry means that the FILE.txt.orig is copied over FILE.txt and the
367 ;; pach is tried again for FILE.txt. You may have asked the author to
368 ;; send more context with using the -C10 switch and after you received
369 ;; this new patch you want to try if it now goes ok. The FILE.txt.orig
370 ;; still remains as a backup
374 ;; (G)o says that we should apply the diff again to FILE.txt. Do this
375 ;; only if you did not get rejections last time. The intention
376 ;; is that you apply the patch again, and this reverses the situation.
377 ;; I mean 1) you patch; you get new version 2) you patch again: you
378 ;; degrade to the version before patch (original file before patch)
382 ;; There is `ediff.el', which is much more complete package than
383 ;; this is. The aim was to develop a simple but handy package for
384 ;; everyday diff'ing and easy package patching.
388 ;; The "f" key, which shows the function identifier in diff browse
389 ;; mode `tinydiff-mode', can handle buffers which are narrowed, but if the
390 ;; buffer is using folding.el or similar package where goto-line does
391 ;; not work properly, the returned message shown to user is not
394 ;; Please unfold the buffer and you get the correct result.
398 ;; This hook setup turns on the view mode for easy scrolling
401 ;; (add-hook 'tinydiff-:diff-hook 'my-tinydiff-diff-hook)
403 ;; (defun my-tinydiff-diff-hook ()
404 ;; "Turn on view-mode in diff buffer."
405 ;; ;; See tinydiff-:diff-buffer.
408 ;; Sending good bug reports
410 ;; If you find anything funny happening in the command line prompt
411 ;; while you use the tdi minibuffer commands. Immediately do
414 ;; o Turn on debug: `M-x' `tinydiff-debug-toggle'
415 ;; o Turn on emacs debug: (setq debug-on-error t)
416 ;; o Clear the debug buffer *tinydiff-debug* if it exists
417 ;; o Start from original situation
418 ;; o Do what you did and when the weird condition is met
419 ;; immediately go to *tinydiff-debug* buffer and save the
420 ;; content and send it to the maintainer.
430 ;;; ......................................................... &require ...
432 ;; make sure this is loaded so that the `tinydiff-mode' map can redefine
438 (ti::package-use-dynamic-compilation)
439 (ti::package-require-view)
440 (defvar diff-command) ;; Byte compiler silencer
441 (defvar ediff-diff-program) ;; Byte compiler silencer
442 (autoload 'dired-get-filename "dired" "" t))
444 (ti::package-defgroup-tiny TinyDiff tinydiff-: tools
445 "Take buffer diffs easily, browse diff and apply patch.
446 Overview of features.
448 o Buffer based simple diff/rcsdiff/patch package.
449 o You cab diff current buffer against last saved backup copy.
450 o Give and manipulate diff command in echo-area: file name
451 completion; switching between rcsdiff/diff, guess rcs version
453 o When you have diff output in buffer, turning on `tinydiff-mode'
454 allows you to browse source buffer by jumping diff blocks fwd/back
455 and showing the source location.
456 o Supported: normal diff, context diff, gnu diff -u, gnu diff -n
457 o Easy patching. Finds file to patch (along user defined paths)
458 and applies the diff. You can receive patches by email
459 and apply them with one or two keystrokes.
460 o loads patch rejection file, if patch didn't succeed 100%")
465 ;;; ......................................................... &v-hooks ...
467 (defcustom tinydiff-:load-hook nil
468 "*Hook that is run when package is loaded."
472 (defcustom tinydiff-:diff-hook nil
473 "*Hooks that run after successful diff run."
477 (defcustom tinydiff-:mode-define-keys-minibuffer-hook
478 'tinydiff-mode-define-keys-minibuffer-default
479 "*Function to define the keys for the minibuffer."
483 (defcustom tinydiff-:parse-buffer-hook nil
484 "Function called when diff buffer has been parsed. (highight)."
489 ;;{{{ setup: variables
491 ;;; ........................................................ &v-public ...
492 ;;; User configurable
494 (defcustom tinydiff-:auto-mode-alist
495 '(("\\.diff\\'" . turn-on-tinydiff-mode)
496 ("\\.patch\\'" . turn-on-tinydiff-mode))
497 "Items to add to `auto-mode-alist' to activate `turn-on-tinydiff-mode'."
500 (string :tag "File Regexp")
501 (const turn-on-tinydiff-mode)))
504 ;; We need this function in defvar; so instantiate it for compiler
509 (defun tinydiff-find-program (program-list default opt seek-option)
510 "Try to use GNU program. Return program name.
513 PROGRAM-LIST list of program binaries to try (gnu)
514 DEFAULT if no gnu binary found, use DEFAULT binary
515 OPT additional option for binary
516 SEEK-OPTION When GNU product, it must know this option."
517 (let* ((exec-path exec-path)
521 ;; Give precedence to GNU diff programs
522 (dolist (path '("/opt/local/bin/" "/usr/local/bin/"))
523 (if (file-directory-p path)
524 (push path exec-path )))
526 (setq path t)) ;XEmacs ByteCom silencer, no-op
528 ;; Select gnu if possible
529 (dolist (prg program-list)
530 (message "TinyDiff: Please wait. Searching for binary `%s'" prg)
531 (when (setq path (executable-find prg))
537 (when (ti::re-search-check seek-option)
542 (message "TinyDiff: Hm, no GNU %s, but using it anyway" default)
544 (set-buffer-modified-p nil)
547 ;; Different users may want to set the keys differently.
550 ;; (setq tinydiff-:diff-program (progn (my-diff-program-select)))
552 ;; Which would return appropriate diff program: maybe you want to use
553 ;; GNU diff for some files and normal diff other times.
554 ;; GNU diff offers line exlude options that you may want
555 ;; to set for RCS files.
557 (defcustom tinydiff-:diff-program
559 ((and (boundp 'diff-command)
562 ((and (boundp 'ediff-diff-program)
565 ((ti::os-check-gnu-support-p)
568 (or (tinydiff-find-program '("gdiff" "diff") "diff" nil "--help")
570 "*Program to generate diff.
571 It should print 'filename: FILE.XXX' tag which is read by function
572 `tinydiff-get-buffer-name'. The variable can contain a Lisp expression
573 which returns program name."
574 :type '(string :tag "Shell command")
577 (defcustom tinydiff-:rcsdiff-program "rcsdiff"
578 "*Shell program to print RCS diff.
579 It should output the 'filename: FILE' which is read by `tinydiff-get-buffer-name'.
580 This variable is evaled to get the program name."
581 :type '(string :tag "Shell program")
584 (defcustom tinydiff-:cvsdiff-program "cvs diff"
585 "*Shell command to print CVS diff."
586 :type '(string :tag "Command")
589 ;; The diff-switches is defined at least in vc.el
591 (defcustom tinydiff-:diff-option
592 '(or (and (boundp 'diff-switches)
593 (stringp diff-switches)
595 (and (ti::os-check-gnu-support-p)
598 "*Diff options as STRING.
599 This variable is evaled to get the options, so it can contain Lisp
600 FORM that returns option string."
602 :type '(string :tag "Options")
605 (defcustom tinydiff-:cl-user-option "-kk"
606 "The option inserted or removed when user presses C - s in command line.
607 The default option -kk is only meaningful on rcsdiff command where
608 it excludes the rcs tags from diff."
612 (defcustom tinydiff-:diff-tmp-file
613 (or (let ((temp (or (getenv "TEMPDIR")
615 (file "tinydiff.diff"))
620 temp ;; this may be nil
625 (when (and (stringp dir)
626 (file-directory-p dir))
627 (return (concat (file-name-as-directory dir) file)))))
628 (error "TinyDiff: Please set tinydiff-:diff-tmp-file"))
629 "*Temporary file where the diff is stored for patching."
633 (defcustom tinydiff-:patch-program
634 (if (ti::os-check-gnu-support-p)
635 ;; We Know this is GNU patch, do not search alternatives
636 ;; gnu patch : -t, --batch
637 ;; similar to -f, in that it suppresses questions,
638 ;; skip patches for which a file to patch can't be found
640 ;; Note: OLD patch command doesn not know -t switch!
642 (tinydiff-find-program
647 "*Patch command and its options.
648 This variable is evaluated to get the program name and switches."
649 :type '(string :tag "shell command")
652 (defcustom tinydiff-:patch-list
653 '(( "[.]el$" load-path)
654 ( "." '("~/txt" "~/elisp")))
655 "*List of item that control how patching is applied.
658 '((REGEXP EVAL-FORM) (REGEXP EVAL-FORM) ..)
660 Where REGEXP is tried against the filename that is found from the patch
661 itself. EVAL-FORM can be any form that return list of pathnames that can
662 be searched for the filename. The first file that is found from the path
666 (string :tag "Regexp")
670 (defcustom tinydiff-:font-lock-keywords
675 ("RCS file: +\\(.*\\)" 1 'highlight)
676 ("retrieving revision +\\(.*\\)" 1 'highlight)
678 ;; Lisp: "defun NAME" etc.
680 ("(def[^(\n]+" 0 font-lock-reference-face)
681 ("(interactive.*)" 0 font-lock-type-face)
685 ("(\\(let\\|cond\\|when\\|unless\\|save-.*\\|with-.*\\)"
686 1 font-lock-keyword-face))
687 "Font lock keywords."
691 ;;; .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . misc . .
693 (defcustom tinydiff-:register-function-name ?f
694 "*Register used to store the function name.
695 Only used if `tinydiff-:function-name-handle-function' is set to
696 'tinydiff-function-name-store."
700 ;; The diff data is inserted into register automatically, because
701 ;; many time the diff data is needed elswhere.
703 (defcustom tinydiff-:register-diff ?d
704 "*Register name where diff is inserted.
705 If this variable is nil, then no data is inserted into register.
707 Variable is evaled to get the register name."
711 ;;; ......................................................... &v-funcs ...
713 (defcustom tinydiff-:mail-buffer-function 'ti::mail-get-buffer
714 "Return some mail buffer for function `tinydiff-mime-compose'.
715 Default value is `ti::mail-get-buffer'."
719 (defcustom tinydiff-:find-ref-function 'beginning-of-defun
720 "*Elisp Function to find underlying code's function name around point.
721 The cursor is positioned in the source buffer and on the referenced
722 line before calling with no arguments. Function should move the point
723 in the line below where the associated reference is located.
725 If no reference is found, function _must_ call 'error'."
729 ;; - It's great to paste the function name into buffer.
730 ;; - C-x g REG put the name into current buffer...
732 (defcustom tinydiff-:function-name-handle-function 'tinydiff-function-name-store
733 "*Function which find the code's function name string.
735 Input args to function:
737 POINT where the `tinydiff-:find-ref-function' positioned the defun.
738 This function should store the function name into register
739 `tinydiff-:register-function-name'."
743 (defcustom tinydiff-:source-buffer-function 'tinydiff-get-buffer-name
744 "*Function which return filename for the diff buffer.
745 You shouldn't touch this function unless you're coping with very
746 strange diff format. Default function is `tinydiff-get-buffer-name'."
753 ;;; ....................................................... &v-private ...
755 (defvar tinydiff-:patch-global-option nil
756 "Path options in effect. See `tinydiff-patch-set-option'.")
758 (defvar tinydiff-:patch-reject-buffer "*tinydiff-patch-rejects"
759 "Buffer where to display rejected patch parts.")
761 (defvar tinydiff-:diff-source-buffer nil
762 "Private. Source buffer for diff. See `tinydiff-:source-buffer-function'.")
763 (make-variable-buffer-local 'tinydiff-:diff-source-buffer)
765 (defvar tinydiff-:last-data nil
766 "Private. Data storage eg in `tinydiff-minibuffer--change-diff-command'.")
768 (defvar tinydiff-:version-list nil
769 "All Version for file. Updated when diff command is being run.")
771 (defvar tinydiff-:version-branch-list nil
772 "Branches. Updated when diff command is being run.")
774 (defvar tinydiff-:diff-tmp-buffer "*tinydiff-tmp*"
775 "Temporary work buffer, patch shell results.")
777 (defvar tinydiff-:patch-tmp-buffer " *tinydiff-tmp-patch*"
778 "Temporary work buffer, patch.")
780 (defvar tinydiff-:package-exist-tinymy (locate-library "tinymy.el")
781 "Private. Has load path for package tinymy.el if it exist.
782 It has some functions we may use from there.")
784 (defvar tinydiff-:diff-buffer "*diff*"
785 "Buffer where diff is inserted.")
787 (defvar tinydiff-:patch-to-file nil
788 "Name of the file to patch.
789 This variable is made local to current patch/diff buffer.")
791 (defvar tinydiff-:patch-hunk-count nil
792 "Counter how many patch hunks hvae been applied
793 This variable is made local to current patch/diff buffer.")
798 ;;; ....................................................... &v-version ...
800 ;;;###autoload (autoload 'tinydiff-version "tinydiff" "Display commentary." t)
803 (ti::macrof-version-bug-report
807 "$Id: tinydiff.el,v 2.83 2007/05/01 17:20:43 jaalto Exp $"
808 '(tinydiff-:version-id
814 tinydiff-:diff-program
815 tinydiff-:rcsdiff-program
816 tinydiff-:cvsdiff-program
817 tinydiff-:source-buffer-function
818 tinydiff-:function-name-handle-function
819 tinydiff-:register-function-name
820 tinydiff-:find-ref-function
821 tinydiff-:source-buffer-function
822 tinydiff-:mode-define-keys-hook
823 tinydiff-:register-diff
824 tinydiff-:diff-buffer
825 tinydiff-:diff-option
826 tinydiff-:diff-tmp-file
827 tinydiff-:diff-tmp-buffer
828 tinydiff-:patch-global-option
829 tinydiff-:patch-program)
830 '(tinydiff-:debug-buffer)))
833 ;;{{{ code: minor mode definition
835 ;;; ............................................................ &mode ...
837 (defvar tinydiff-:minibuffer-map nil
838 "Minibuffer key map when asked for the right diff command.")
840 ;;;###autoload (autoload 'tinydiff-mode "tinydiff" "" t)
841 ;;;###autoload (autoload 'turn-on-tinydiff-mode "tinydiff" "" t)
842 ;;;###autoload (autoload 'turn-off-tinydiff-mode "tinydiff" "" t)
843 ;;;###autoload (autoload 'tinydiff-commentary "tinydiff" "" t)
846 (ti::macrof-minor-mode-wizard
847 "tinydiff-" " Tdi" nil " Tdiff" 'TinyDiff "tinydiff-:" ;1-6
849 "Diff browsing minor mode.
853 \\{tinydiff-:mode-map}"
862 tinydiff-:mode-easymenu-name
863 ["goto current point" tinydiff-goto-kbd t]
864 ["block forward" tinydiff-goto-next t]
865 ["block forward, no update" tinydiff-goto-next-no-update t]
866 ["block backward" tinydiff-goto-prev t]
867 ["block backward, no update" tinydiff-goto-prev-no-update t]
868 ["Set patch option" tinydiff-patch-set-option t]
870 ["Parse buffer" tinydiff-parse-buffer t]
871 ["Set source buffer for diff" tinydiff-set-source-buffer t]
872 ["Show function name" tinydiff-show-function-name t]
874 ;; ["Keyboard menu" tinydiff-menu-main t]
875 ["Package version" tinydiff-version t]
876 ["Package commentary" tinydiff-commentary t]
877 ["Mode help" tinydiff-mode-help t]
878 ["Mode off" tinydiff-mode t])
882 ;; I first thought to put goto line into " " or "\C-m"
883 ;; That is: SPACE or RETURN, but later realized that they were
884 ;; used in view-mode to scroll buffer up-down.
886 ;; Eg. in my setup whenever I turn on the read-only
887 ;; with C-x C-q it also automatically turns on view-mode for
891 ;; This happens to be unsifted in HP-UX, a top-leftmost button.
892 ;; Select something that suit you more...
894 (define-key root-map "\C-m" 'tinydiff-goto-kbd)
896 (define-key root-map "e" 'tinydiff-parse-buffer)
897 (define-key root-map "!" 'tinydiff-set-source-buffer)
898 (define-key root-map "n" 'tinydiff-goto-next)
899 (define-key root-map "p" 'tinydiff-goto-prev)
901 ;; These are borrewd from unix more(1) and less(1)
903 (define-key root-map "y" 'tinydiff-goto-prev-no-update)
904 (define-key root-map "b" 'tinydiff-goto-next-no-update)
906 ;; But perhaps user feels more comfortable with these.
908 (define-key root-map "P" 'tinydiff-goto-prev-no-update)
909 (define-key root-map "B" 'tinydiff-goto-next-no-update)
911 (define-key root-map "f" 'tinydiff-show-function-name)
913 (define-key root-map "W" 'tinydiff-write-file)
914 (define-key root-map "M" 'tinydiff-mime-compose)
915 (define-key root-map "O" 'tinydiff-patch-set-option)
917 (define-key root-map "-k" 'tinydiff-block-kill)
918 (define-key root-map "-\C-?" 'tinydiff-block-kill) ;; backspace
919 (define-key root-map "--" 'tinydiff-block-apply-patch)
921 (define-key map "?" 'tinydiff-mode-help)
922 (define-key map "Hm" 'tinydiff-mode-help)
923 (define-key map "Hc" 'tinydiff-commentary)
924 (define-key map "Hv" 'tinydiff-version)
927 (define-key root-map [mouse-2] 'tinydiff-goto-mouse)
928 (define-key root-map [(button2)] 'tinydiff-goto-mouse)))))
930 ;;; ----------------------------------------------------------------------
932 (defun tinydiff-install (&optional uninstall)
933 "Install TinyDiff package, or optionally UNINSTALL.
934 A .diff or .patch file invokes `tinydiff-mode' in `automode-alist'."
938 (ti::assoc-replace-maybe-add
939 'auto-mode-alist tinydiff-:auto-mode-alist 'remove))
941 (ti::assoc-replace-maybe-add
942 'auto-mode-alist tinydiff-:auto-mode-alist)
944 (message "TinyDiff installed")))))
949 ;;; ----------------------------------------------------------------------
951 (defun tinydiff-mode-define-keys-minibuffer-default ()
952 "This function defines some extra bindings to minibuffer.
953 Eg. TAB that completes current filename."
954 (setq tinydiff-:minibuffer-map (copy-keymap minibuffer-local-map))
956 ;; Here we define nice tab filename completion inside minibuffer
957 ;; This may be superflous, but what the heck :-)
959 (define-key tinydiff-:minibuffer-map "\t"
960 'tinydiff-minibuffer--complete-filename)
961 (define-key tinydiff-:minibuffer-map [(kp-tab)]
962 'tinydiff-minibuffer--complete-filename)
964 ;; More command line handling
966 ;; There is no logic in naming the key settings other than
967 ;; simple rule: they must me as lower left as possible
968 ;; to be reached quickly.
970 ;; You won't need any of these in echo area prompt
972 (define-key tinydiff-:minibuffer-map "\C-z"
973 'tinydiff-minibuffer--change-diff-command)
974 (define-key tinydiff-:minibuffer-map "\C-r"
975 'tinydiff-minibuffer--rev-add-command)
976 (define-key tinydiff-:minibuffer-map "\C-c"
977 'tinydiff-minibuffer--insert-file-autosave)
978 (define-key tinydiff-:minibuffer-map "\C-v"
979 'tinydiff-minibuffer--insert-file-backup)
980 (define-key tinydiff-:minibuffer-map "\C-s"
981 'tinydiff-minibuffer--toggle-diff-type)
982 (define-key tinydiff-:minibuffer-map "\C-f"
983 'tinydiff-minibuffer--user-option)
984 (define-key tinydiff-:minibuffer-map "\C-p"
985 'tinydiff-minibuffer--insert-previous-word)
986 (define-key tinydiff-:minibuffer-map "?"
987 'tinydiff-minibuffer--minibuffer-help))
992 ;;; ............................................................ &misc ...
994 ;;;###autoload (autoload 'tinydiff-debug-toggle "tinydiff" "" t)
996 (eval-and-compile (ti::macrof-debug-standard "tinydiff" "-:"))
998 ;;; ----------------------------------------------------------------------
1000 (defsubst tinydiff-kill-revision-list ()
1001 "Deletes private version lists."
1002 (setq tinydiff-:version-list nil
1003 tinydiff-:version-branch-list nil))
1005 ;;; ----------------------------------------------------------------------
1007 (defun tinydiff-splice-command (string)
1008 "Splice off directory from string and return '(DIR CMD REST)"
1011 "cd[ \t]+\\([^;]+\\);" ;; dir
1012 "[ \t]*\\([^ \t\r\n]+\\)" ;; cmd
1013 "[ \t]+\\(.+\\)") ;; rest
1016 ;; Delete trailing whitespace
1017 (replace-regexp-in-string "[ \t\r\n]+$" ""
1018 (match-string 1 string))
1019 (match-string 2 string)
1020 (match-string 3 string))))
1022 ;;; ----------------------------------------------------------------------
1024 (defun tinydiff-shell-command (cmd buffer)
1025 "Run CMD and output to BUFFER.
1026 The passed CMD must be in the format:
1028 cd DIRECTORY; BINARY option option option ...."
1029 (let* ((fid "tinydiff-shell-command"))
1030 (multiple-value-bind (dir cmd rest)
1031 (tinydiff-splice-command cmd)
1033 (not (file-directory-p dir)))
1034 (error "Tinydiff: No directory found `%s'" dir))
1035 (let ((default-directory (file-name-as-directory dir)))
1037 'default-directory default-directory
1041 (setq buffer (get-buffer-create buffer))
1043 (rargs (reverse (split-string rest)))
1044 (file2 (expand-file-name (pop rargs)))
1045 (file1 (expand-file-name (pop rargs))))
1048 (setq args (reverse rargs))
1049 (tinydiff-debug fid 'CMD cmd 'FILE1 file1 'FILE2 file2)
1050 (apply 'call-process
1056 (when (or (null buffer)
1057 (null (get-buffer buffer))
1058 (not (buffer-live-p buffer)))
1059 (error "TinyDiff: Shell dind't return results [ %s ]" cmd))))))
1061 ;;; ----------------------------------------------------------------------
1063 (defun tinydiff-update-revision-list (file &optional version)
1064 "Read all revision numbers for FILE starting from VERSION."
1065 (let* ((fid "tinydiff-update-revision-list: ")
1066 (ver (or version "1.1"))
1067 (dots (count-char-in-string ?. ver)))
1068 (unless fid ;; XEmacs byte compiler silencer
1070 (when (null tinydiff-:version-list)
1071 (tinydiff-debug fid ver dots)
1072 (setq tinydiff-:version-list (ti::vc-rcs-all-versions file)))
1073 (when (or (null tinydiff-:version-branch-list)
1074 ;; Chck that there is right brach list
1075 ;; "1.1" -- "1.1.1.1" ?
1077 (count-char-in-string
1079 (car tinydiff-:version-branch-list)))))
1080 (setq tinydiff-:version-branch-list
1081 (ti::vc-rcs-get-all-branches
1082 ver tinydiff-:version-list))
1083 (tinydiff-debug fid "ver" version))
1084 tinydiff-:version-list))
1086 ;;; ----------------------------------------------------------------------
1087 ;;; - Doing the version getting is SLOW with lisp. Use some external
1088 ;;; shell program to do the job for you. It is MUCH quicker.
1090 (defun tinydiff-rcs-diff-between-versions (dir file)
1091 "Return VC diff command that diffs current version and previous version.
1092 DIR and FILE is passed to function."
1094 (let* ((ver (ti::vc-rcs-buffer-version))
1099 (error "TinyDiff: Cannot find version number."))
1100 (setq v-list (tinydiff-update-revision-list (concat dir file)))
1101 (setq prev (ti::vc-rcs-previous-version ver v-list))
1103 (error "TinyDiff: Cannot find previous version number."))
1105 (format "cd %s; %s -r%s -r%s %s %s "
1107 (if (ti::vc-rcs-file-exists-p file)
1108 tinydiff-:rcsdiff-program
1109 tinydiff-:cvsdiff-program)
1111 (or (eval tinydiff-:diff-option)
1116 ;;; ----------------------------------------------------------------------
1118 (defsubst tinydiff-source ()
1119 "Return source buffer of diff."
1120 (if (and tinydiff-:diff-source-buffer
1121 (buffer-live-p (get-buffer tinydiff-:diff-source-buffer)))
1122 tinydiff-:diff-source-buffer
1123 (setq tinydiff-:diff-source-buffer
1124 (funcall tinydiff-:source-buffer-function))))
1126 ;;; ----------------------------------------------------------------------
1128 (defun tinydiff-set-source-buffer (buffer)
1129 "Set source BUFFER for current diff."
1130 (interactive "bSource buffer: ")
1131 (if (not (get-buffer buffer))
1132 (error "TinyDiff: Buffer does not exist")
1133 (setq tinydiff-:diff-source-buffer buffer)))
1135 ;;; ----------------------------------------------------------------------
1137 (defun tinydiff-function-name-store (line)
1138 "Store found function name from LINE into `tinydiff-:register-function-name'.
1139 Currently works well only Lisp functions."
1140 (let* ((reg tinydiff-:register-function-name)
1141 (mode (symbol-name major-mode))
1143 (when (string-match "lisp" mode)
1144 ;; try to extract symbol-name
1145 ;; see tinydiff-get-function-name, because point sits on DEFUN already
1146 ;; and the ti::buffer-defun-function-name searches it again..
1148 (setq txt (ti::buffer-defun-function-name))
1151 (set-register reg txt)
1153 (set-register reg "")))))
1155 ;;; ----------------------------------------------------------------------
1157 (defun tinydiff-get-function-name (buffer line)
1158 "Return function or variable name at current point.
1159 Switches to BUFFER and go to LINE and calls `beginning-of-defun'"
1160 (let* ((func tinydiff-:function-name-handle-function)
1161 (find-func tinydiff-:find-ref-function)
1162 (max-leap 100) ;lines
1165 (with-current-buffer buffer
1169 (setq point (point))
1170 (funcall find-func) ;exit , if error generated
1171 ;; Let's be little intelligent
1172 (if (< (count-lines (point) point) max-leap)
1173 ;; Okay, we believe that function was found
1174 (setq ret (ti::read-current-line))
1175 ;; Can't be that far away... reset the pointer
1178 (setq ret (funcall func (point))))) ;; ignore-errors
1181 ;;; ----------------------------------------------------------------------
1183 (defun tinydiff-show-function-name (&rest args)
1184 "Show possible function name in mode line on the current diff point. ARGS."
1186 (let* ((line (tinydiff-get-line-number))
1187 (buffer (tinydiff-source))
1190 ((not (and line buffer))
1191 (message "Tinydiff: Sorry, No line and buffer info."))
1193 (setq desc (tinydiff-get-function-name buffer line))
1195 ;; Moving mouse wipes this away...
1197 (message "Tinydiff: Can't find reference."))))))
1199 ;;; ----------------------------------------------------------------------
1201 (defun tinydiff-turn-on-view-mode ()
1202 "Turn on view mode and read-only status."
1204 (unless buffer-read-only
1205 (setq buffer-read-only t)))
1208 ;;{{{ code: command line
1210 ;;; ..................................................... &commandLine ...
1212 ;;; ----------------------------------------------------------------------
1214 (defun tinydiff-minibuffer--replace-text (beg end text)
1215 "Command line. Replace region between BEG and END with TEXT."
1216 (ti::save-line-column-macro
1218 (delete-region beg end)
1222 ;;; ----------------------------------------------------------------------
1224 (defun tinydiff-minibuffer--read-revision ()
1225 "Command line. Read revision.
1228 (nbr . pos) ,where pos is point at line.
1231 (let* ((fid "tinydiff-minibuffer--read-revision:")
1234 (unless fid ;; XEmacs byte compiler silencer
1237 ;; a) in the top of REV number
1238 ;; b) pick REV forward
1239 (or (eq 0 (skip-chars-backward "0-9."))
1240 (eq 0 (skip-chars-forward "^0-9.")))
1241 ;; cd /users/foo/elisp/; rcsdiff -c -r1.25 -r1.8 tinydiff.el
1243 ((setq nbr (ti::buffer-match "[0-9.]+" 0))
1244 (setq pos (match-beginning 0)))
1247 (setq nbr (ti::buffer-match ".*-[rul]\\([0-9.]+\\)" 1))
1248 (setq pos (match-beginning 1)))))
1249 (tinydiff-debug fid "col"(current-column) "nbr" nbr "pos" pos )
1253 ;;; ----------------------------------------------------------------------
1255 (defun tinydiff-minibuffer--directory ()
1256 "Return directory name from 'cd' command."
1259 (ti::buffer-match "cd +\\([^;]+\\)" 1)))
1261 ;;; ----------------------------------------------------------------------
1263 (defun tinydiff-minibuffer--read-rcs-file-name (&optional line or-diff-name)
1264 "Read RCS filename from LINE.
1265 If OR-DIFF-NAME is non-nil, look for 'diff' command instead."
1266 (let* ((fid "tinydiff-minibuffer--read-rcs-file-name:")
1267 (line (or line (ti::read-current-line)))
1268 (rcsdiff tinydiff-:rcsdiff-program)
1269 (diff tinydiff-:diff-program)
1271 "cd[ \t]+\\([^;]+\\);[ \t]*"
1272 (if or-diff-name diff rcsdiff)
1273 ".* \\([^ \t]+\\)"))
1277 (unless fid ;; XEmacs byte compiler silencer
1279 ;; The directory name is gotten after 'cd' command
1280 (tinydiff-debug fid line re)
1281 (when (and (string-match re line)
1282 (setq dir (match-string 1 line))
1283 (setq file (match-string 2 line)))
1284 (setq ret (concat dir file)))
1287 ;;; ----------------------------------------------------------------------
1288 ;;; Don't ask: This function should be rewritten someday, someday...
1290 (defun tinydiff-minibuffer--rev-add-command ()
1291 "Adding two -rX.x string to the command line.
1292 This is only done if there is rcsdiff command and less the 2 -rX.x
1296 rcsdiff -r1.3 file.cc
1297 --> rcsdiff -r1.2 -r1.3 file.cc
1299 rcsdiff -r1.1 -r1.1 file.cc
1300 --> Do nothing, since there is already two -r switches."
1302 (let* ((fid "tinydiff-minibuffer--rev-add-command:")
1303 (line (ti::remove-properties (ti::read-current-line)))
1304 ;; (rcsdiff tinydiff-:rcsdiff-program)
1305 (re (concat "^.*;[ \t]*"
1307 tinydiff-:rcsdiff-program
1310 tinydiff-:cvsdiff-program
1322 (unless fid ;; XEmacs byte compiler silencer
1324 (tinydiff-debug "IN:\n\n" fid line re)
1325 (when (string-match re line)
1326 (setq args (match-string 1 line)
1327 list (split-string line)
1328 copy (copy-list list))
1329 (tinydiff-debug fid "ARGS" args "LIST" list)
1331 (while (setq elt (pop copy))
1332 (if (not (string-match "^-r" elt)) ;Find this
1333 (ti::nconc prev-list elt)
1334 ;; Here we make list
1335 ;; '((NTH-POS CAR-LIST CDR-LIST) (N CA CD) ..)
1336 ;; -- all-the-elements before
1339 (copy-list prev-list)
1342 (tinydiff-debug fid "R-LIST>>" r-list))
1344 (tinydiff-debug fid "r-list NOW:" (length r-list) r-list)
1346 ((eq (length r-list) 0) ;no -r --> add it
1347 ;; looks complicated? The idea is to
1348 ;; -- get last element, but only if it's NOT an option
1349 ;; -- get all, but not the last element.
1350 (setq r-list (nreverse args))
1351 (setq elt (car r-list)) ;This is the added -rN.N
1352 (tinydiff-debug fid "ELT" elt r-list)
1353 (if (not (string-match "^-" elt))
1354 (setq args (reverse (cdr (reverse args))))
1355 (setq elt nil)) ;it was an option
1356 (ti::nconc args "-r" )
1357 (if elt ;file name set ?
1358 (ti::nconc args elt )))
1359 ((eq (length r-list) 2) ;-r -r remove first
1360 (setq elt (car r-list))
1361 (setq args (nth 2 elt)) ;Revision and rest of the args
1362 (setq args ;car-list to the beginning
1363 (reverse (union (reverse (nth 1 elt)) args))))
1364 ((eq (length r-list) 1) ;Only one -r
1365 (setq elt (car r-list))
1366 (setq args (nth 2 elt)) ;Revision and rest of the args
1367 ;; elt = (POS LIST-UNTIL-R LIST-INClUDING-R-AND-AFTER)
1368 (setq tmp (car (nth 2 elt)) ;copy the -rN.N
1369 i (count-char-in-string ?. tmp)) ;; How many dots?
1370 (tinydiff-debug fid "1>" elt "ARGS" args "I=" i "TMP" tmp)
1371 ;; ........................................... Change revision ...
1372 (when (string-match "\\.\\([0-9]+\\)$" tmp)
1373 (setq revision (string-to-int (match-string 1 tmp))
1375 (ti::string-right (match-string 1 tmp) 1)))
1376 (tinydiff-debug fid "1>REV" revision "last NBR" nbr)
1378 ((or (eq i 1) ; Like 2.2, one dot
1379 (and (> i 1) ; Can't make 1.1.1.1 --> 1.1.1.0
1382 (setq revision (1- revision))
1383 (setq tmp (ti::replace-match 1 (int-to-string revision) tmp))
1384 (tinydiff-debug fid "1>CHANGED" revision tmp))
1387 ;; Remove last 1.1.N.N --> 1.1
1388 (if (string-match "\\(.*\\)\\.[0-9]+\\.[0-9]+$" tmp)
1389 (setq tmp (match-string 1 tmp)))
1390 (tinydiff-debug fid "2>CHANGED" tmp))))
1391 (push tmp args) ;--> '(-rN.N -rN.N FILE)
1392 (setq args ;car-list to the beginning
1393 (reverse (union (reverse (nth 1 elt)) args)))))
1394 (setq args (ti::list-to-string args))
1395 (tinydiff-debug fid "args" args )
1396 (delete-region (line-beginning-position) (line-end-position))
1399 ;; Go after last revision number, so that user can change it easily
1401 (if (re-search-forward "-r" nil t)
1402 (skip-chars-forward "^ \t")
1405 ;;; ----------------------------------------------------------------------
1407 (defun tinydiff-minibuffer--toggle-diff-type ()
1408 "Toggle -c context or -u unified diff option in command line."
1410 (let* ((line (ti::remove-properties (ti::read-current-line)))
1413 ((string-match "^\\(.+ -[^ \t]*\\)c\\(.+\\)" line)
1414 (setq ret (format "%su%s"
1415 (match-string 1 line)
1416 (match-string 2 line))))
1417 ((string-match "^\\(.+ -[^ \t]*\\)u\\(.+\\)" line)
1418 (setq ret (format "%sc%s"
1419 (match-string 1 line)
1420 (match-string 2 line)))))
1425 ;; Preserve approx point.
1426 (goto-char (min (point-max) (point))))))
1428 ;;; ----------------------------------------------------------------------
1430 (defun tinydiff-minibuffer--user-option ()
1431 "Add or remove tinydiff-:cl-user-option from the line."
1433 (let* ((line (ti::remove-properties (ti::read-current-line)))
1434 (rcsdiff tinydiff-:rcsdiff-program)
1435 (diff tinydiff-:diff-program)
1436 (opt tinydiff-:cl-user-option)
1438 (when (not (ti::nil-p opt))
1440 ((string-match (regexp-quote opt) line)
1441 (setq ret (ti::replace-match 0 "" line)))
1443 ((or (string-match (concat rcsdiff "\\( +\\)") line)
1444 (string-match (concat diff "\\( +\\)") line))
1445 (setq ret (ti::replace-match 1 (concat " " opt " ") line)))))
1447 (beginning-of-line) (kill-line)
1450 ;;; ----------------------------------------------------------------------
1452 (defun tinydiff-minibuffer--change-diff-command ()
1453 "Change diff command in minibuffer."
1455 (let* ((fid "tinydiff-minibuffer--change-diff-command: ")
1456 (rcsdiff tinydiff-:rcsdiff-program)
1457 (diff tinydiff-:diff-program)
1462 (unless fid ;; XEmacs byte compiler silencer
1464 (tinydiff-debug fid "in")
1468 ;; Make sure it's our command.
1469 ((and (re-search-forward "; *" nil t)
1470 (setq word (ti::buffer-read-word)))
1471 (setq line (buffer-substring-no-properties
1472 (point) (line-end-position)))
1473 (setq list (split-string line))
1475 "\n\nLINE " line "\n"
1477 "1 string= word rcsdiff " word rcsdiff "\n"
1478 "2 string= word diff " word diff "\n"
1479 "2 tinydiff-:last-data " tinydiff-:last-data "\n"
1480 "3 " (and (string-match word diff)
1482 (not (stringp tinydiff-:last-data))))
1485 ;; ................................................... case-1 ...
1486 ((or (string= word rcsdiff)
1487 (string= word "cvs"))
1488 (tinydiff-debug fid "rcsdiff INPUT:" list)
1489 (setq tinydiff-:last-data line)
1490 ;; Remove some elements from list
1491 (setq list (ti::list-find
1493 (concat "^-r\\|" rcsdiff "\\|cvs\\|diff" )
1496 (not (string-match arg elt))))
1498 (tinydiff-debug fid "1 FILTERED:" list)
1500 (insert diff " " (ti::list-to-string list))
1502 ;; ................................................... case-2 ...
1503 ;; Did the last command have the same filename ? --> if not
1504 ;; then we cannot use C-z
1505 ((and (string-match word diff)
1507 (stringp tinydiff-:last-data)
1508 (ti::string-match-case
1509 (regexp-quote (nth (1- (length list)) list) )
1510 tinydiff-:last-data))
1512 (insert tinydiff-:last-data)
1514 ;; ................................................... case-3 ...
1515 ;; (3) diff --> rcs diff thing
1516 ((and (string-match word diff)
1518 (not (stringp tinydiff-:last-data)))
1523 (ti::list-to-string (cdr list))))
1526 ;; Move after the 'cd' and 'diff commands to add/change options easily
1528 (re-search-forward "; *" nil t)
1531 ;;; ----------------------------------------------------------------------
1533 (defun tinydiff-minibuffer--sweep-unix () ;; win32
1534 "Change all backslashes to forward slashes."
1535 (let* ((line (ti::remove-properties (ti::read-current-line))))
1536 (when (string-match "[\\]" line)
1537 (setq line (ti::file-name-forward-slashes line))
1538 (beginning-of-line) (kill-line)
1540 ;; Preserve approx point.
1541 (goto-char (min (point-max) (point))))))
1543 ;;; ----------------------------------------------------------------------
1545 (defun tinydiff-minibuffer--complete-filename ()
1546 "Complete filename."
1548 (tinydiff-minibuffer--sweep-unix)
1549 (let* ((fid "tinydiff-minibuffer--complete-filename")
1550 (word (save-excursion
1552 (ti::buffer-read-space-word)))
1553 (dir default-directory))
1554 (unless fid ;; XEmacs byte compiler silencer
1556 (tinydiff-debug fid "BEGIN" word dir)
1557 (unless (ti::nil-p word)
1558 ;; The directory name we get from the 'CD' prompt if the
1559 ;; filename lacks dir part.
1560 (let ((cddir (tinydiff-minibuffer--directory)))
1562 ((not (string-match "/" word))
1564 ((string-match "^\\.\\." word) ;; relative path
1565 (setq dir (expand-file-name (concat
1566 (file-name-as-directory cddir)
1567 (file-name-directory word)))
1568 word (file-name-nondirectory word)))))
1569 (tinydiff-debug fid "END" word dir)
1570 (let ((default-directory (or dir
1571 default-directory)))
1572 (ti::file-complete-file-name-word word)))))
1574 ;;; ----------------------------------------------------------------------
1576 (defun tinydiff-minibuffer--insert-file-autosave (&optional backup)
1577 "Insert auto-save filename into current point if it exists.
1578 Prefix arg says to insert BACKUP filename instead."
1580 (let* ((line (ti::read-current-line))
1581 (rcsdiff tinydiff-:rcsdiff-program)
1582 (fid "tinydiff-minibuffer--insert-file-autosave: ") ;function id
1586 (unless fid ;; XEmacs byte compiler silencer
1588 (setq file (tinydiff-minibuffer--read-rcs-file-name line 'diff))
1589 (tinydiff-debug fid "arg" backup "file" file)
1591 ((string-match rcsdiff line)
1593 (substitute-command-keys
1595 "Tinydiff: Don't use rcsdiff command; Change command with "
1596 "\\[tinydiff-minibuffer--change-diff-command] "))))
1598 (message "Tinydiff: Can't read file from prompt. Include 'cd'"))
1599 ;; .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . process file ..
1603 (setq file2 (make-backup-file-name file))
1604 (setq stat (file-exists-p file2)))
1606 ;; The autosave fiel must be surrounded with '' because
1607 ;; # is shell comment
1610 (file-name-nondirectory file)))
1612 (file-exists-p (concat (file-name-directory file) file2)))))
1614 (message "Tinydiff: Not found %s " file2 )
1615 (setq file2 (concat "'" file2 "'"))
1616 ;; put spaces around filename if needed
1619 (if (ti::space-p (preceding-char))
1623 (if (ti::space-p (following-char))
1628 ;;; ----------------------------------------------------------------------
1630 (defun tinydiff-minibuffer--insert-file-backup ()
1631 "Command line. Inset backup filename if it exists."
1633 (tinydiff-minibuffer--insert-file-autosave 'backup))
1635 ;;; ----------------------------------------------------------------------
1637 (defun tinydiff-minibuffer--minibuffer-help ()
1640 (with-output-to-temp-buffer "*Help*"
1642 (substitute-command-keys
1643 "TinyDiff minibuffer command line keys\n\n \\{tinydiff-:minibuffer-map}")))
1644 (with-current-buffer "*Help*"
1647 (delete-non-matching-lines "tinydiff-minibuffer-")))
1649 ;;; ----------------------------------------------------------------------
1651 (defun tinydiff-minibuffer--insert-previous-word ()
1652 "Insert previous word: like filename or rcs switch."
1655 ;;; (char (if (line-end-position)
1656 ;;; (preceding-char)
1657 ;;; (or (following-char)
1658 ;;; (preceding-char))))
1660 (word (save-excursion
1661 (when (re-search-backward "[^ \t]" nil t)
1662 (ti::buffer-read-space-word)))))
1668 ;;{{{ code: diff type, patch
1670 ;;; ........................................................... &patch ...
1672 ;;; ----------------------------------------------------------------------
1674 (defsubst tinydiff-patch-get-dir-from-cmd (cmd)
1675 "Return FILE from CMD."
1677 (setq cmd (ti::string-match "cd +\\([^ \t;]+\\)" 1 cmd))
1678 (ti::file-make-path cmd)))
1680 ;;; ----------------------------------------------------------------------
1682 (defsubst tinydiff-patch-get-file-from-cmd (cmd)
1683 "Return FILE from CMD."
1685 (setq cmd (ti::string-match
1686 (format "cd .*%s +\\([^ \t;]+\\)" tinydiff-:patch-program)
1690 ;;; ----------------------------------------------------------------------
1692 (defun tinydiff-patch-minibuffer-cleanup ()
1693 "Check if we can use the patch as is, PGP change must be restored."
1695 ;; PGP breaks the dashed lines:
1696 ;; - --- 1.2.1.1 1996/06/11 11:36:03
1697 ;; - --- 212,219 ----
1699 ;; Correct the lines back.
1702 (while (re-search-forward "^\\(- \\)\\(--- [0-9]+.*\\)" nil t)
1703 (message "Tinydiff: Correcting patch: PGP's broken lines...")
1705 (ti::replace-match 1 (match-string 2)))
1707 (when (re-search-forward "^--+BEGIN +PGP +SIGNATURE" nil t)
1708 (message "Tinydiff: Correcting patch: removing PGP tags...")
1710 (delete-region (line-beginning-position) (point-max)))
1712 (message "Tinydiff: Correcting patch: done.")))))
1714 ;;; ----------------------------------------------------------------------
1716 (defun tinydiff-get-file-name (&optional arg)
1717 "Try to get file name for the diff.
1718 Optionally read line \"RCS file: xxx.el\"
1720 ARG says which to look:
1722 1 pick line *** file.xx
1723 2 pick line --- file.xx
1724 3 pick line +++ file.xx GNU unified diff.
1729 (let* ((stat (ti::buffer-diff-type-p))
1730 (re1 "^[ \t]*[*][*][*] +\\([^ \t\n]+\\)")
1731 (re2 "^[ \t]*--- +\\([^ \t\n]+\\)")
1732 (re3 "^[ \t]*[+][+][+] +\\([^ \t\n]+\\)")
1736 (when stat ;only if diff found
1737 (if arg ;set search regexp
1739 ((eq 1 arg) (setq re re1))
1740 ((eq 2 arg) (setq re re2))
1741 ((eq 3 arg) (setq re re3))))
1744 (goto-char (cdr stat))
1748 (and (or (re-search-backward
1749 "RCS +file: +\\([^/]+\\),v" nil t)
1751 "RCS +file: +.*/\\\(.*\\),v" nil t))
1752 (setq file (match-string 1)))))
1754 (when (re-search-backward "diff -c.*" nil t)
1755 (setq list (split-string (ti::read-current-line)))
1757 (setq file (nth (length list) list)))))
1759 (when (or (re-search-backward re nil t)
1760 (re-search-forward re nil t))
1761 (setq file (match-string 1))))))))
1763 (if file ;pick the return values
1764 (list (ti::remove-properties file) stat)
1767 ;;; ----------------------------------------------------------------------
1769 (defun tinydiff-patch-check-if-load (file buffer &optional flag)
1770 "See if we want to load FILE by looking results in BUFFER.
1771 If file was RCS controlled and not in Emacs, ask to load it.
1772 If file is active in Emacs ad to do `load-file' to refresh current Emacs.
1777 BUFFER The patch(1) command output buffer
1778 FLAG If 'hunk, this is only partial diff."
1779 (let* (case-fold-search ;case sensitive matches
1780 (fid "tinydiff-patch-check-if-load")
1781 (fbuffer (find-buffer-visiting file))
1782 (modified (if fbuffer (ti::buffer-modified-p fbuffer)))
1783 (was-rcs (ti::re-search-check "RCS/"))
1785 ;; Is package active in Emacs? Was patched file .el?
1790 (unless fid ;; XEmacs byte compiler silencer
1793 (setq el-file (ti::string-match "\\(.*\\)\\.el$" 1 file))
1794 (setq sym (intern-soft (file-name-nondirectory el-file)))
1795 (setq feature (featurep sym)))
1797 (tinydiff-debug fid "in:" file buffer el-file sym feature
1798 "FBUFFER" fbuffer "MODIFIED" modified
1800 ;; Be sure about success
1801 (when (with-current-buffer buffer (ti::re-search-check "[Hh]unk.*succeed"))
1805 ;; If autorevert.el is turned on with
1806 ;; M-x global-auto-revert-mode, do not ask from user
1807 (or (not (boundp 'global-auto-revert-mode))
1808 (null (symbol-value 'global-auto-revert-mode)))
1809 (or (y-or-n-p "Buffer for the file exist, revert? ")
1812 ;; Set flag and Stop case
1814 (pop-to-buffer fbuffer)
1816 ((and fbuffer modified)
1817 (display-buffer buffer)
1818 (message "Tinydiff: file's buffer in Emacs is modified.."))
1820 (null no-ask) ;Maybe set in 1st cond case
1821 (y-or-n-p (format "RCS file patched, find-file %s " file "? ")))
1824 (not (eq flag 'hunk))
1825 (y-or-n-p (format "%s is running in your Emacs, reload it? "
1829 "Tinydiff: %s reloaded. Your Emacs is now running the latest patch."
1832 ;;; ----------------------------------------------------------------------
1834 (defun tinydiff-patch-check-failure (&optional buffer)
1835 "Check patch failure messages from BUFFER.
1837 filename rejection file if failure happened.
1838 '(\"\") some unknown failure happened; rejection file not available
1839 String holds matched failure condition.
1840 nil Patch succeeded ok."
1842 (let* ((fid "tinydiff-patch-check-failure")
1843 (re "failed.*saving +rejects +to +\\([^ \t\n]+\\)")
1844 (case-fold-search t)
1846 (unless fid ;; XEmacs byte compiler silencer
1848 (with-current-buffer (or buffer (current-buffer))
1850 ;; 1 out of 2 hunks failed--saving rejects to test.el.rej
1851 (setq file (ti::re-search-check re 1 nil 'get-matched))
1852 ;; patch: **** this file doesn't appear to be the 1.7 version--aborting
1854 (ti::re-search-check
1855 "^patch:.*--aborting" 1 nil 'get-matched-text))
1856 (setq file (list file)))
1858 ;; In SunOS it simply prints the following on success.
1860 ;; Looks like a new-style context diff.
1862 (and (save-excursion ;Check second line first
1863 (ti::pmin) ;If we care to dig deeper then...
1865 (looking-at ".*done\\.?"))
1867 (let (stat1 stat2 stat3 str)
1869 (setq stat1 (looking-at ".*Looks like.*"))
1871 (setq stat2 (looking-at ".*done\\.?"))
1873 (setq str (buffer-substring-no-properties
1874 (point) (point-max)))
1875 (setq stat3 (string-match "^[\n\r]*\\'" str))
1876 (or (and stat1 stat2 stat3)
1879 "TinyDiff: Hm. Can't tell if patch succeeded."))))))
1880 ;; Maybe shell error has terminated the command
1881 ;; "Unrecognized switch: -t"
1882 (and (not (ti::re-search-check "hunk.*succeed"))
1886 "TinyDiff: Hm. No 'hunk succeed' message found."))))))
1887 (tinydiff-debug fid file)
1890 ;;; ----------------------------------------------------------------------
1892 (defun tinydiff-patch-check-rejections (cmd buffer)
1893 "After CMD, check rejections from BUFFER.
1894 If the the patch command says in this buffer:
1896 1 out of 1 hunks failed--saving rejects to file.rej
1898 Then loading the rejection file.
1900 CMD is the original patch command used.
1904 `tinydiff-:patch-reject-buffer'"
1905 (let* ((fid "tinydiff-patch-check-rejections: ")
1911 (unless fid ;; XEmacs byte compiler silencer
1913 (tinydiff-debug fid "in:" cmd buffer)
1914 (setq stat (tinydiff-patch-check-failure buffer))
1915 ;;; (pop-to-buffer (current-buffer)) (ti::d! "rejections" stat cmd)
1918 (message (car stat)))
1922 (setq dir (tinydiff-patch-get-dir-from-cmd cmd))
1923 (setq dir default-directory))
1924 (setq file-load (concat dir file))
1925 (tinydiff-debug fid file-load)
1927 ((file-exists-p file-load)
1928 (setq tmp (ti::temp-buffer tinydiff-:patch-reject-buffer 'clear))
1929 (with-current-buffer tmp (insert-file-contents file-load))
1930 (display-buffer tmp)
1933 (message "TinyDiff: Can't find DIR for file...") (sit-for 1)
1934 (call-interactively 'find-file)
1937 ;;; ----------------------------------------------------------------------
1939 (defun tinydiff-patch-with-diff-1 (file beg end &optional interactive type)
1940 "Apply diff to file i.e. patch a FILE.
1944 FILE absolute file name where to store diff.
1945 BEG diff start point in buffer
1947 INTERACTIVE User interaction, allow editing the patch command etc.
1948 TYPE Type of patch: 'hunk means partial diff."
1949 (let* ((fid "tinydiff-patch-with-diff-1")
1950 (file (expand-file-name file))
1951 (opt-global (or tinydiff-:patch-global-option
1953 (diff-tmp (if (ti::win32-shell-p)
1954 ;; Must use DOS paths
1955 (expand-file-name tinydiff-:diff-tmp-file)
1956 ;; Otherwise take it as it is
1957 tinydiff-:diff-tmp-file))
1958 (prg (eval tinydiff-:patch-program))
1959 (map tinydiff-:minibuffer-map)
1960 (source-buffer (current-buffer))
1961 (file-buffer (find-buffer-visiting file))
1962 buffer ;shell messages
1967 (unless fid ;; XEmacs byte compiler silencer
1969 (tinydiff-debug fid "in:" file beg end interactive)
1970 (tinydiff-debug fid "vars:" diff-tmp prg source-buffer file-buffer)
1973 (ti::buffer-modified-p file-buffer)
1977 "TinyDiff: buffer %s not saved, continue ?" file-buffer))))
1978 (error "TinyDiff: Aborted."))
1979 (if (not (file-exists-p file))
1980 (error "Tinydiff: file not found: %s" file ))
1981 (setq dir (file-name-directory file)
1982 ff (file-name-nondirectory file)
1983 buffer (ti::temp-buffer tinydiff-:diff-tmp-buffer 'clear)
1984 data-buffer (ti::temp-buffer tinydiff-:patch-tmp-buffer 'clear))
1985 ;; Sometimes user's UMASK is not ok. PErhaps the file is
1986 ;; not readable after the write.
1987 (when (and (file-exists-p diff-tmp)
1988 (not (file-writable-p diff-tmp)))
1989 (error "TinyDiff: [ERROR] Not writable. Check UMASK or permissions %s"
1991 (ti::write-file-as-is-macro
1992 (write-region (point-min) (point-max) diff-tmp))
1993 (unless (file-readable-p diff-tmp)
1994 (set-file-modes diff-tmp 384)) ;; -rw-------
1995 (with-current-buffer data-buffer
1996 (insert-buffer-substring source-buffer beg end)
1997 (tinydiff-patch-minibuffer-cleanup)
1998 ;;; (pop-to-buffer (current-buffer)) (ti::d! "ok")
1999 (setq cmd (format "cd %s ; %s %s %s %s" dir prg opt-global ff diff-tmp))
2000 (tinydiff-debug fid "cmd:" cmd)
2002 (let* (tinyef-mode ;Electric file mode OFF
2003 tinycompile-mode) ;Make sure this is off too
2005 (setq tinyef-mode nil)) ;No-op, bytecomp silencer
2006 (if tinycompile-mode
2007 (setq tinycompile-mode nil)) ;No-op
2008 (setq cmd (ti::remove-properties
2009 (read-from-minibuffer "> " cmd map)))
2010 ;; Record command line prompt to *Messages* buffer
2011 (message (concat "TinyDiff: RUN " cmd))))
2012 (if (ti::nil-p cmd) ;user cleared the line ?
2013 (message "Tinydiff: Patching cancelled.")
2014 (tinydiff-shell-command cmd buffer)
2016 (display-buffer buffer)
2017 (or (tinydiff-patch-check-rejections cmd buffer)
2019 (tinydiff-patch-check-if-load
2024 ;;; ----------------------------------------------------------------------
2026 (defun tinydiff-file-to-patch ()
2027 "Suggest possible filename to patch.
2031 `tinydiff-:patch-list'
2035 string suggested file to patch
2036 list list of filenames read from diff."
2037 (let* ((fid "tinydiff-get-file-name-list")
2038 (list tinydiff-:patch-list)
2045 (unless fid ;; XEmacs byte compiler silencer
2047 (ti::dotimes counter 1 4
2048 (setq stat (tinydiff-get-file-name counter))
2050 (setq file (nth 0 stat)
2052 ;; Try to find also gzipped files
2053 (push file file-list)
2054 (pushnew (concat file ".gz") file-list :test 'string=)
2055 (if (string-match "/" file)
2056 (push (file-name-nondirectory file) file-list))))
2057 ;; Preserve the order. Try to find the original file first.
2058 (setq file-list (nreverse file-list))
2059 (tinydiff-debug fid "file-list:" file-list)
2061 ;; Normally the directory part cannot be used, because people have
2062 ;; files in different places, search it anyway in case the
2063 ;; file structure is the same..
2065 ;; 1. "/dir/dir/file.el"
2069 ;; *** /work/jackr/Emacs/folding.el@@/main/5 Wed Mar 13 09:39:36 1996
2070 ;; --- /work/jackr/Emacs/folding.el Wed Mar 13 13:50:26 1996
2072 (dolist (file file-list)
2074 ((file-exists-p file)
2075 (setq dest-file file)
2078 (setq file (file-name-nondirectory file))
2079 (dolist (elt list) ;; User '((REGEXP DIR) ..)
2080 ;; Get next element from patch table
2081 (setq re (nth 0 elt)
2082 search-path (eval (nth 1 elt)))
2083 (when (and (string-match re file)
2085 (ti::file-get-load-path file search-path))
2086 (and (string-match "\\.el$" file)
2089 (ti::file-get-load-path
2092 (throw 'done t))))))))
2096 ;;; ----------------------------------------------------------- &patch ---
2098 (defun tinydiff-patch
2099 (arg &optional beg end dest-file verb type orig-buffer)
2100 "Try to guess diff type and region in the buffer.
2101 If automatic detection fails user must select diff region by hand.
2105 PREFIX ARG lets user to edit the diff command before executing.
2106 This is enabled by default for interactive calls.
2107 BEG END diff region; defaults to whole buffer if nil.
2108 DEST-FILE file to patch
2110 TYPE Type of diff: 'hunk or nil (whole diff)
2111 ORIG-BUFFER Original buffer where the whole patch is."
2114 ;;; This is not a good idea if you get many patches; to
2115 ;;; ask every time...
2117 ;;; (if (and tinydiff-:package-exist-tinymy
2118 ;;; (y-or-n-p "Do you want to make a safe copy? "))
2119 ;;; (call-interactively 'tinymy-copy-file))
2124 (region-beginning) ;avoids region active check
2126 (tinydiff-file-to-patch)))
2132 (tinydiff-file-to-patch))))))
2133 (let* ((fid "tinydiff-patch")
2134 (go-status t) ;should we proceed patching?
2140 (setq orig-buffer (current-buffer)))
2141 (let* ((patch-buffer-file-name
2142 (with-current-buffer orig-buffer
2144 (guess-dir (if patch-buffer-file-name
2145 (file-name-directory
2146 patch-buffer-file-name)))
2147 (guess-file (if patch-buffer-file-name
2148 (file-name-nondirectory
2149 patch-buffer-file-name)))
2150 (guess-path patch-buffer-file-name))
2152 (string-match "\\.rej$" guess-file))
2153 (setq guess-file (file-name-sans-extension guess-file)
2154 guess-path (concat guess-dir guess-file)))
2155 (unless fid ;; XEmacs byte compiler silencer
2157 (setq verb (or arg verb (interactive-p)))
2158 (tinydiff-debug fid "in:" arg beg end)
2160 (setq orig-buffer (current-buffer)))
2162 (with-current-buffer orig-buffer
2163 (setq dest-file tinydiff-:patch-to-file)))
2164 (tinydiff-debug fid "dest-file:" dest-file)
2165 ;; ......................................................... patch ...
2167 (unless (stringp dest-file)
2169 "Tinydiff: Can't detect file along paths in tinydiff-:patch-list.")
2171 (let ((default-directory default-directory))
2172 ;; The ask prompt will start from HOME in that case.
2173 (unless patch-buffer-file-name
2174 (setq default-directory "~"))
2175 (setq dest-file (read-file-name
2181 (if (or (not (stringp dest-file))
2182 (not (file-exists-p dest-file)))
2183 (error "TinyDiff: Cannot patch non-existing file. Aborted."))
2184 (with-current-buffer orig-buffer
2185 (set (make-local-variable 'tinydiff-:patch-to-file) dest-file))
2186 ;; ........................................................... zip ...
2187 (when (string-match "\\(.*\\)\\.gz$" dest-file)
2188 (setq dest-file (match-string 1 dest-file))
2190 (message "TinyDiff: Uncompressing gzip file...")
2191 (ti::temp-buffer tinydiff-:diff-tmp-buffer 'clear)
2195 tinydiff-:patch-tmp-buffer ;; Output buffer
2198 (format "%s.gz" dest-file))
2199 (message "TinyDiff: Uncompressing %s file...done." dest-file))
2200 ;; ....................................................... rejects ...
2201 (when (file-exists-p (concat dest-file ".rej"))
2203 (when (and (not (or (null tinydiff-:patch-hunk-count)
2204 (eq 0 tinydiff-:patch-hunk-count))))
2207 (file-exists-p (concat dest-file ".orig"))
2211 (ti::read-char-safe-until
2213 .orig found: r = retry patch, o = back to .orig, g = go and patch"
2217 '(?o ?r ?g ?\e ?q ?\b ?\C-g))))))
2219 (message "Tinydiff: Hm... rejection file found.")
2221 ;; .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .orig exist ..
2223 ((char= char ?r) ;Retry patch to original
2224 (delete-file dest-file) ;copy-file won't work otw
2225 (copy-file (concat dest-file ".orig") dest-file))
2226 ((char= char ?g) ;Go ahead
2229 (delete-file dest-file)
2230 (copy-file (concat dest-file ".orig") dest-file)
2231 (delete-file (concat dest-file ".orig"))
2232 (setq go-status nil)
2234 "Tinydiff: Original file restored: .orig copied over %s"
2237 (setq go-status nil)
2238 (message "Tinydiff: Cancelled patching.")))))
2240 (with-current-buffer orig-buffer
2241 (set (make-local-variable 'tinydiff-:patch-hunk-count)
2242 (1+ (or tinydiff-:patch-hunk-count 0))))
2243 (tinydiff-patch-with-diff-1
2244 dest-file (point-min) (point-max) verb type))
2246 (message "TinyDiff: Compressing gzip file...")
2247 (call-process "gzip"
2249 nil ; (current-buffer)
2252 (format "%s.gz" dest-file))
2253 (message "TinyDiff: Compressing %s file...done" dest-file)))))
2256 ;;{{{ code: command generate
2258 ;;; ----------------------------------------------------------------------
2260 (defun tinydiff-diff-command-generate (&optional no-ask)
2261 "Return diff command as string; optionally NO-ASK."
2262 (if (null tinydiff-:minibuffer-map)
2263 (run-hooks 'tinydiff-:mode-define-keys-minibuffer-hook))
2264 (let* ((map tinydiff-:minibuffer-map)
2265 (tmp-file (expand-file-name
2266 tinydiff-:diff-tmp-file))
2267 (diff-prg (or (eval tinydiff-:diff-program)
2268 (error "TinyDiff: tinydiff-:diff-program is nil")))
2269 (rcsdiff-prg (eval tinydiff-:rcsdiff-program))
2270 (cvsdiff-prg (eval tinydiff-:cvsdiff-program))
2271 (options (or (eval tinydiff-:diff-option) ""))
2272 (ange (and (stringp default-directory)
2273 (string-match "@" default-directory)))
2274 (dired (and (null ange)
2275 (string-match "dired" (symbol-name major-mode))))
2279 ;; If point is out of file listing dired flags
2280 ;; error, don't mind it.
2282 (ignore-errors (dired-get-filename)))
2283 ((buffer-file-name))))
2291 ;; This flag is used to to signal that buffer should not be
2292 ;; saved, because the underlying file has changed.
2293 (put 'tinydiff-diff-command-generate 'buffer-not-modified nil)
2294 (tinydiff-debug default-directory bf "DIRED-ANG" dired ange "OPT" options)
2295 (tinydiff-kill-revision-list)
2297 (setq dir (file-name-directory bf)
2298 file (file-name-nondirectory bf)))
2300 ;; .............................................. buffer change ...
2301 ;; - The buffer has changed. Or file has changed.
2305 (or (buffer-modified-p)
2306 ;; someone else edited or changed the same file
2307 (null (verify-visited-file-modtime (current-buffer))))
2308 (y-or-n-p "Diff between buffer and file on disk? "))
2309 (put 'tinydiff-diff-command-generate 'buffer-not-modified bf)
2310 (ti::widen-safe ;make sure whole buffer is saved
2311 (write-region (point-min) (point-max) tmp-file))
2312 (setq file2 tmp-file))
2313 ;; ........................................................ rcs ...
2314 ;; - Buffer is RCS controlled.
2317 (ti::vc-rcs-file-exists-p bf))
2318 ;; - if the revision information cannot be found, then the
2319 ;; '-rX.x' switch is not used.
2320 (setq rev (ti::vc-rcs-buffer-version))
2322 (message "Tinydiff: RCS Revision not detected.") (sit-for 1))
2327 (concat "-r" rev " ")
2331 (error "TinyDiff: tinydiff-:rcsdiff-program is nil"))))
2332 ;; .......................................................... cvs ...
2335 (or (boundp 'tinydiff-cvs-flagged)
2336 (setq cvs-info (ti::vc-cvs-file-exists-p bf))))
2337 ;; It's too expensive to call `ti::vc-cvs-file-exists-p' every time,
2338 ;; so we create intermediate variable to flag this buffer as CVS
2340 (make-local-variable 'tinydiff-cvs-flagged)
2341 ;; (setq rev (ti::vc-rcs-buffer-version))
2342 (setq rev (car (ti::vc-cvs-entry-split-info
2343 (ti::vc-cvs-entry-split cvs-info)
2346 (message "Tinydiff: CVS Revision not detected.") (sit-for 1))
2351 (concat "-r" rev " ")
2355 (error "TinyDiff: tinydiff-:cvsdiff-program is nil"))))
2356 ;; .................................................... default ...
2357 ;; General diff prompt
2359 (setq dir default-directory))
2361 (setq dir (expand-file-name "~"))))
2362 ;; ... ... ... ... ... ... ... ... ... ... command generated . .
2363 (setq prompt (format
2364 "cd %s; %s %s %s %s "
2368 "TinyDiff: No diff program available. Check PATH."))
2374 (let* (tinyef-:mode) ;Electric file mode OFF
2376 (setq tief-mode nil)) ;No-op, bytecompier silencer
2377 ;; Record command line prompt to *Messages* buffer
2378 (message (concat "TinyDiff: " prompt))
2379 (setq ans (read-from-minibuffer ">" prompt map))))
2382 (ti::remove-properties ans)))
2385 ;;{{{ code: generating, parsing diff
2387 ;;; .................................................... &diff-parsing ...
2389 ;;; ----------------------------------------------------------------------
2391 (defun tinydiff-parse-buffer (&optional verb)
2392 "Prepare diff buffer for `tinydiff-mode'. VERB.
2393 Mark diff lines for special handling."
2395 (let* ((diff-type (car-safe (ti::buffer-diff-type-p)))
2397 ;; In GNU diff , there is option --initial-tab
2398 ;; which adds tab before each diff line to make the
2399 ;; text look "as it was originally"
2401 ;; That's why allowed whitespace at the beginning.
2403 (re-c2 "^[ \t]*[*][*][*] \\([0-9]+\\)")
2404 (re-c3 "^[ \t]*[-][-][-] \\([0-9]+\\)")
2405 (re-normal "^\\([0-9]+\\)\\(,[0-9]+\\)?+[acd][0-9]")
2407 (re-gnu-u "^@@[ \t]+[-+][0-9]+,[0-9]+[ \t]+[-+]+\\([0-9]+\\)")
2408 (re-gnu-n "^[dac]\\([0-9]+\\) [0-9]+$")
2410 (prop-list '(mouse-face highlight
2413 (let ((sym 'font-lock-keywords))
2414 (set sym tinydiff-:font-lock-keywords))
2415 (ti::text-clear-region-properties
2416 (point-min) (point-max) '(owner tinydiff))
2420 ((eq diff-type 'context)
2421 (ti::text-re-search re-c2 nil 1 nil prop-list)
2423 (ti::text-re-search re-c3 nil 1 nil prop-list))
2424 ((eq diff-type 'normal)
2425 (ti::text-re-search re-normal nil 1 nil prop-list))
2426 ((eq diff-type 'gnu-u)
2427 (ti::text-re-search re-gnu-u nil 1 nil prop-list))
2428 ((eq diff-type 'gnu-n)
2429 (ti::text-re-search re-gnu-n nil 1 nil prop-list))))
2432 (message (concat "Tinydiff: Diff parsed, type: "
2433 (prin1-to-string diff-type))))
2435 (message "Tinydiff: Diff not recognized.")))
2436 (run-hooks 'tinydiff-:parse-buffer-hook)))
2438 ;;; ......................................................... &diff-do ...
2440 ;;; ----------------------------------------------------------------------
2443 (defun tinydiff-diff-show (cmd)
2444 "Generate diff CMD for the buffer and show it in the other window.
2445 Lets user to edit option in the command line."
2453 (file-name-directory (buffer-file-name))
2454 (file-name-nondirectory (buffer-file-name)))))
2456 "Tinydiff: There is autosave file, use minibuffer %s binding"
2457 (ti::keymap-function-bind-info
2458 'tinydiff-minibuffer--insert-file-autosave
2459 tinydiff-:minibuffer-map)
2462 (tinydiff-diff-command-generate))))
2463 (if (and (stringp cmd)
2466 ;; If this is in the commend, we're diffing buffer against
2468 (not (or (string-match (regexp-quote tinydiff-:diff-tmp-file) cmd)
2470 (regexp-quote (expand-file-name tinydiff-:diff-tmp-file))
2472 (y-or-n-p "Save buffer before running diff? "))
2475 (tinydiff-diff cmd 'show)))
2477 ;;; ----------------------------------------------------------------------
2480 (defun tinydiff-diff-show-noask (cmd)
2481 "Generate diff CMD for the buffer. Guess all parameters."
2484 ;; - The first one runs fine for rcsdiff (ie. buffer is in RCS)
2485 ;; but if it fails, we have to ask paramters from user.
2486 (or (tinydiff-diff-command-generate 'no-ask)
2487 (tinydiff-diff-command-generate))))
2488 ;; the `get' is set in `tinydiff-diff-command-generate'
2489 ;; to indicate that buffer and file content are no in synch, user
2490 ;; does not want to save modified buffer, but check against the copy
2492 (if (and (null (get 'tinydiff-diff-command-generate 'buffer-not-modified))
2494 (y-or-n-p "Save buffer before running diff? "))
2496 (if (not (ti::nil-p cmd))
2497 (tinydiff-diff cmd 'show)))
2499 ;;; ------------------------------------------------------------- &cmd ---
2500 ;;; - The diff data is inserted into register automatically, because
2501 ;;; many time the diff data is pasted to somewhere else. Eg. by sending it
2502 ;;; via mail to someone else in projects.
2506 (defun tinydiff-diff (cmd &optional show verb)
2507 "Run diff on buffer, possibly using rcsdiff if file is version controlled.
2508 Inserts contents into register.
2510 The version control is determined by searching RCS strings 'Id' or 'Log'
2515 SHOW show the results
2516 NO-ASK run diff without asking any questions.
2517 VERB enable verbose messages
2521 `tinydiff-:extra-diff-program'
2522 `tinydiff-:diff-buffer'
2523 `tinydiff-:diff-options'
2527 nil ,the no-ask parameter could not determine right diff.
2528 buffer ,the shell output buffer. Note, that the diff may have
2529 failed, in that case the buffer does not hold valid output."
2530 (let* ((fid "tinydiff-diff")
2531 (bf (buffer-file-name))
2532 (buffer (ti::temp-buffer tinydiff-:diff-buffer 'clear))
2533 (reg (eval tinydiff-:register-diff)) ;where to put the diff content
2536 (tinydiff-debug fid 'cmd cmd 'show show 'verb verb)
2538 ;; It the command is rcsdiff, then we must include
2539 ;; Prereq: tag to the beginning of diff. Thhat tells patch
2540 ;; that when applying the diff, it must find that string
2541 ;; from the file before it can prosess applying the patch.
2543 ;; It prevents patching wrong versions.
2547 ;; Or we use this which is more stricter. It supposes you have
2548 ;; rcs 'id' string in a file.
2550 ;; Prereq: tinylib.el,v 1.10
2552 ;; Nov 8 1996: Hm the latter isn't supported, if picks only
2553 ;; "tinylib.el,v" and not whole string in the linew
2554 ;; I'm going to request imprevement to GNU patch....
2556 ((setq tmp (ti::string-match "-r\\([0-9.]+\\)" 1 cmd))
2557 ;; Add file name too
2559 (null (tinydiff-minibuffer--read-rcs-file-name cmd)))
2560 (setq prereq (format "Prereq: %s\n" tmp))
2562 ;;; (setq tmp2 (file-name-nondirectory tmp2))
2563 ;;; (setq prereq (format "Prereq: %s,v %s" tmp2 tmp))
2567 (message "Tinydiff: Sorry, this is not a file buffer.")
2569 ;; if the NO-ASK parameter is set, then we can't ask anything
2570 ;; from the user. What if the file is not RCS file? Then what we
2571 ;; diff against? --> give up and return nil pointer
2573 ;; ... ... ... ... ... ... ... ... ... ... ... ... shell command . .
2575 (tinydiff-shell-command cmd buffer)
2576 (with-current-buffer buffer
2579 (insert prereq "\n"))
2581 (setq tmp (current-buffer))
2582 ;; Unless it's already visible in some frame.
2583 (if (setq tmp (get-buffer-window buffer t))
2584 (raise-frame (window-frame tmp))
2585 (display-buffer buffer)))
2587 (set-register reg (buffer-string))
2590 (concat "Tinydiff: Diff stored in register ..."
2591 (char-to-string reg)))))
2592 (run-hooks 'tinydiff-:diff-hook))))
2596 ;;{{{ code: Misc; mime write
2598 ;;; ----------------------------------------------------------------------
2600 (defun tinydiff-compose-diff-filename ()
2601 "Compose filename by reading the original filename from diff buffer.
2602 Filename is composed like this: ~/tmp + FILE + .diff suffix."
2605 ((file-directory-p "~/tmp/") "~/tmp/")
2606 ((file-directory-p "/tmp/") "/tmp/")
2608 default-directory)))
2612 (ti::dotimes counter 1 4 ;; #todo: remove ti::dotimes
2613 (setq stat (tinydiff-get-file-name counter))
2615 (setq file (nth 0 stat)
2617 ;; If the name is not .diff or .patch, then it will do
2618 (when (not (string-match "\\.diff\\|\\.patch" file))
2619 (setq ret file counter 6))))
2622 (if (ti::win32-shell-p)
2623 (expand-file-name dir)
2625 (concat file ".patch")))))
2627 ;;; ----------------------------------------------------------------------
2629 (defun tinydiff-write-file (file)
2630 "Write current diff to temporary file.
2631 This is purely an interactive function.
2632 The suggested to be written is named like this: ~/tmp + FILE + .diff suffix."
2634 (let* ((file-name (tinydiff-compose-diff-filename))
2635 (file (and file-name
2636 (file-name-nondirectory file-name)))
2637 (default-directory (if file-name
2638 (file-name-as-directory
2639 (file-name-directory file-name))
2640 default-directory)))
2641 (list (read-file-name "Write diff to: " nil nil nil file))))
2642 (unless (ti::nil-p file)
2643 (write-region (point-min) (point-max) file)))
2645 ;;; ----------------------------------------------------------------------
2647 (defun tinydiff-mime-compose (&optional insert-to-mail verb)
2648 "Read current buffer and make TM MIME attachement.
2649 Save attachement to `tinydiff-:register-diff' or to a mail buffer,
2650 which must have MIME-edit mode active.
2652 The TM MIME spec is used: 7bit, type=patch.
2653 If you need other specifications, insert diff via TM's insert file.
2657 INSERT-TO-MAIL Flag, if non-nil, add MIME block to the end of
2658 buffer pointed by `tinydiff-:mail-buffer-function'.
2661 (let* ((file (file-name-nondirectory (tinydiff-compose-diff-filename)))
2665 "--[[application/octet-stream; type=patch\n"
2666 "Content-Disposition: attachment; "
2667 "filename=\"%s\"][7bit]]\n")
2669 (obuffer (current-buffer))
2675 (unless (setq mail-buffer (funcall tinydiff-:mail-buffer-function))
2676 (error "Tinydiff: Can't find mail buffer where to insert to"))
2677 (with-current-buffer mail-buffer
2678 (when (not (or (ti::mail-mime-tm-edit-p)
2679 (ti::mail-mime-semi-edit-p)))
2681 ;; Do not add the mime tag, if there is noTM mime edit mode
2683 (ti::append-to-buffer mail-buffer mime-tag))
2684 (append-to-buffer mail-buffer (point-min) (point-max))
2686 (message "Tinydiff: %sdiff appended to buffer: %s"
2687 (if mime-p "Mime " "")
2688 (buffer-name mail-buffer))))
2692 (insert-buffer obuffer)
2693 (set-register tinydiff-:register-diff (buffer-string))
2695 (message "TinyDiff: MIME diff in register `%c'"
2696 tinydiff-:register-diff)))))))
2700 ;;{{{ code: Line functions
2702 ;;; ----------------------------------------------------------------------
2704 (defun tinydiff-get-buffer-name ()
2705 "Return buffer name of the current diff."
2712 ;;; (ti::d! "RCS file")
2713 (re-search-forward "^RCS file: .*/\\(.*\\),v" nil t))
2715 ;; RCS file: RCS/folding.el,v
2716 ;; retrieving revision 1.18
2717 ;; retrieving revision 1.19
2718 (setq file (match-string 1)))
2721 (re-search-forward "^---[ \t]+\\([^\t ]+\\)" nil t))
2722 ;; --- copy/tinydiff.el Wed Jan 24 16:08:05 1996
2723 ;; +++ tinydiff.el Wed Jan 24 16:29:07 1996
2725 (setq file (match-string 1))
2726 (or (or (setq ret (and (string-match "/" file)
2727 (find-buffer-visiting file)))
2728 (setq ret (get-buffer (file-name-nondirectory file))))
2729 ;; Hmm; the "---" file was not found; try "***" file
2734 (re-search-forward "^\\*\\*\\*[ \t]+\\([^\t ]+\\)" nil t)
2735 ;; *** copy/tinydiff.el Wed Jan 24 16:08:05 1996
2736 ;; --- /tmp/tdi.diff Wed Jan 24 16:29:07 1996
2737 (setq file (match-string 1)))
2738 (or (setq ret (and (string-match "/" file)
2739 (find-buffer-visiting file)))
2740 (setq ret (get-buffer (file-name-nondirectory file)))))))
2742 (re-search-forward "^filename: \\([^\t ]+\\)$" nil t))
2743 ;; User tag, e.g. output from shell script that generates the
2744 ;; 'filename' tag + runs the diff program.
2745 (setq file (match-string 1)))
2747 ;; other diff formats .. Still open
2749 ;; ............................................. examine results ...
2750 (when (and (not ret)
2752 (if file ;remove directory part
2753 (setq file ;; returns nil if no "/" found
2754 (file-name-nondirectory file)))
2757 (setq ret file)) ;; - buffer name == file name
2758 ((setq list (ti::dolist-buffer-list (string-match file (buffer-name))))
2759 ;; - Eg name tinydiff.el may be in name tinydiff.el<2>
2760 ;; - just pick the first from list
2761 (setq ret (car list)))))
2765 ;;; ----------------------------------------------------------------------
2767 (defun tinydiff-get-line-number ()
2768 "Return diff line number if line has one."
2769 (let* ((diff-type (car-safe (ti::buffer-diff-type-p)))
2770 (re-c2 "^[*][*][*] \\([0-9]+\\)")
2771 (re-c3 "^[-][-][-] \\([0-9]+\\)")
2772 (re-normal "^\\([0-9]+\\)\\(,[0-9]+\\)?+[acd][0-9]")
2773 ;; Wrong: this returned left hand number
2774 ;;; (re-gnu-u "^@@ [-+]\\([0-9]+\\),[0-9]+[ \t]+[-+]+")
2775 (re-gnu-u "^@@ [-+][0-9]+,[0-9]+[ \t]+[-+]+\\([0-9]+\\)")
2776 (re-gnu-n "^[dac]\\([0-9]+\\) [0-9]+$")
2781 ((eq diff-type 'context)
2782 (or (setq ret (ti::buffer-match re-c2 1))
2783 (setq ret (ti::buffer-match re-c3 1))))
2784 ((eq diff-type 'normal)
2785 (setq ret (ti::buffer-match re-normal 1)))
2786 ((eq diff-type 'gnu-n)
2787 (setq ret (ti::buffer-match re-gnu-n 1)))
2788 ((eq diff-type 'gnu-u)
2789 (setq ret (ti::buffer-match re-gnu-u 1)))))
2790 ;;; (ti::d! (match-string 0) diff-type)
2792 (setq ret (string-to-int ret)))
2795 ;;; ----------------------------------------------------------------------
2797 (defun tinydiff-goto (buffer line)
2798 "Show BUFFER and put cursor at LINE in other window."
2799 (let* ((ob (current-buffer)) ;original buffer
2801 (switch-to-buffer-other-window buffer)
2803 ;; Flash the cursor and go back to diff buffer
2806 (pop-to-buffer ob)))
2808 ;;; ----------------------------------------------------------------------
2810 (defun tinydiff-goto-next (&optional back verb no-update)
2811 "Search next position, or BACKWARD.
2815 BACK if non-nil then search backward
2816 VERB enable verbose messages
2817 NO-UPDATE do not update diff source buffer
2824 (let* ((diff-type (car-safe (ti::buffer-diff-type-p)))
2825 (re-c2 "^[*][*][*] \\([0-9]+\\)")
2826 ;;; (re-c3 "^[-][-][-] \\([0-9]+\\)")
2827 (re-normal "^[0-9]+\\(,[0-9]+\\)?+[acd][0-9]")
2829 ;;; (re-gnu-u "^@@ [-+]\\([0-9]+\\),[0-9]+[ \t]+[-+]+")
2830 (re-gnu-u "^@@ [-+][0-9]+,[0-9]+[ \t]+[-+]+\\([0-9]+\\)")
2831 (re-gnu-n "^[dac]\\([0-9]+\\) [0-9]+$")
2842 (func (if back 're-search-backward 're-search-forward))
2847 (setq re (cdr-safe (assoc diff-type type-list)))
2849 (when (setq buffer (funcall tinydiff-:source-buffer-function))
2850 (setq tinydiff-:diff-source-buffer buffer) ))
2855 (if (null (funcall func re nil t))
2856 (if verb (message "Tinydiff: No more hits."))
2857 ;; Adjust the display to middle of the screen
2859 (goto-char (match-beginning 0))
2860 (recenter '(4)) ;show it
2861 (tinydiff-goto-kbd 'verb))
2865 ;;; ----------------------------------------------------------------------
2867 (defun tinydiff-goto-prev ()
2868 "Search diff position backward."
2871 (tinydiff-goto-next 'back 'verb))
2873 ;;; ----------------------------------------------------------------------
2875 (defun tinydiff-goto-prev-no-update ()
2876 "Search diff position backward."
2879 (tinydiff-goto-next 'back 'verb 'no-update))
2881 ;;; ----------------------------------------------------------------------
2883 (defun tinydiff-goto-next-no-update ()
2884 "Search next position."
2887 (tinydiff-goto-next nil 'verb 'no-update))
2889 ;;; ----------------------------------------------------------------------
2891 (defun tinydiff-goto-kbd (&optional verb)
2892 "Show the diff source in another window. VERB."
2894 (let* ((line (tinydiff-get-line-number))
2895 (buffer (tinydiff-source)))
2898 ;; nothing to do, not valid line
2900 "Tinydiff: Can't find source line reference."))
2902 (if verb (message "Tinydiff: Cannot find buffer name for diff."))
2903 (tinydiff-goto buffer line)))))
2905 ;;; ----------------------------------------------------------------------
2907 (defun tinydiff-goto-mouse (event)
2908 "Show current line in other window. Use mouse EVENT.
2909 Activate only if point underneath has 'mouse-property."
2911 (let* ((buffer (tinydiff-source))
2913 (when (ti::text-get-mouse-property)
2914 (setq line (ti::remove-properties (ti::buffer-read-word "[0-9]+")))
2916 (not (ti::nil-p line)))
2917 (tinydiff-goto buffer (string-to-int line))
2918 (message "Tinydiff: Sorry, missing Line Number or filenname.")))))
2921 ;;{{{ code: patch block handling
2923 ;;; ----------------------------------------------------------------------
2925 (defun tinydiff-header ()
2926 "Return the diff header."
2929 ;; --- foo-2.4.orig/config.guess
2930 ;; +++ foo-2.4/config.guess
2931 (when (looking-at "^--- ")
2933 (buffer-substring (point-min) (point)))))
2935 ;;; ----------------------------------------------------------------------
2937 (defun tinydiff-block-region ()
2938 "Return (beg . end) of diff block around current point or nil."
2939 (let* ( ;; (type (ti::buffer-diff-type-p))
2944 (or (tinydiff-goto-next nil nil 'no-update) ;; *** 2720,2727 ****
2945 (goto-char (point-max)))
2946 ;; If the point moved to somewhere.
2947 (if (eq end (point))
2949 ;; We need the previous line too
2952 ;; *** 4687,4692 ****
2955 (setq end (line-beginning-position)))
2957 (tinydiff-goto-next 'back nil 'no-update)
2958 (if (eq beg (point))
2960 ;; Take the whole hunk
2961 (if (looking-at "^[*][*][*] ")
2963 (setq beg (line-beginning-position)))
2967 ;;; ----------------------------------------------------------------------
2969 (defun tinydiff-block-kill ()
2970 "Kill Diff block around point."
2972 (let* ((region (tinydiff-block-region))
2974 (if (and (interactive-p)
2976 (message "TinyDiff: can't determine diff block bounds.")
2977 (delete-region (car region) (cdr region)))))
2979 ;;; ----------------------------------------------------------------------
2981 (defun tinydiff-block-apply-patch ()
2982 "Apply diff block around point.
2984 `tinydiff-patch-set-option'."
2986 (let* ((add-opt tinydiff-:patch-global-option)
2987 (region (save-excursion
2988 (or (tinydiff-block-region)
2989 (error "TinyDiff: Hunk region not found."))))
2990 (header (or (tinydiff-header)
2992 (message "TinyDiff: [WARN] Patch header not found.")
2994 (buffer (current-buffer))
2996 (if (and (interactive-p)
2998 (message "TinyDiff: can't determine diff block bounds.")
2999 (setq file (tinydiff-file-to-patch))
3002 (insert-buffer-substring buffer (car region) (cdr region))
3003 (tinydiff-patch nil nil nil file 'verb 'hunk buffer)))))
3005 ;;; ----------------------------------------------------------------------
3007 (defun tinydiff-patch-set-option (opt-string)
3008 "Set `tinydiff-:patch-global-option' to OPT-STRING.
3009 E.g. to apply revese diff, you may want to set the option to: -R"
3014 "Tinydiff patch option%s: "
3015 (if (and (stringp tinydiff-:patch-global-option)
3016 (not (string-match "^[ \t]*$"
3017 tinydiff-:patch-global-option)))
3019 tinydiff-:patch-global-option)
3021 (setq tinydiff-:patch-global-option opt-string))
3024 ;;{{{ setup: Install
3026 (defadvice cvs-mode-diff (after tinydiff-turn-on-diff-mode act)
3027 "Call `turn-on-tinydiff-mode'."
3028 (when (boundp 'cvs-diff-buffer-name)
3029 (with-current-buffer (symbol-value 'cvs-diff-buffer-name)
3030 (unless (fboundp 'diff-mode)
3031 (turn-on-tinydiff-mode)))))
3033 (add-hook 'tinydiff-:mode-define-keys-hook 'tinydiff-mode-define-keys)
3034 (add-hook 'tinydiff-:parse-buffer-hook 'turn-on-font-lock-mode)
3036 ;; These have to be here, because when someone says
3037 ;; (add-hook 'tinydiff-:diff-hook 'my-tinydiff-:diff-hook)
3039 ;; The variable gets defined immediately. --> following does nothing...
3040 ;; (defvar tinydiff-:diff-hook '(tinydiff-parse-buffer tinydiff-mode))
3042 (ti::add-hooks 'tinydiff-:diff-hook
3043 '(tinydiff-parse-buffer
3044 turn-on-tinydiff-mode
3045 tinydiff-turn-on-view-mode))
3052 (run-hooks 'tinydiff-:load-hook)
3054 ;;; tinydiff.el ends here