X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/c1587aff5527edb84ff15694c318aaabe9cc6d8d..01fcc3a532872b29784a4d888ab9cc1aef0eed01:/lisp/hilit-chg.el diff --git a/lisp/hilit-chg.el b/lisp/hilit-chg.el index 19cecb1c8b..17b91245d6 100644 --- a/lisp/hilit-chg.el +++ b/lisp/hilit-chg.el @@ -1,17 +1,16 @@ ;;; hilit-chg.el --- minor mode displaying buffer changes with special face -;; Copyright (C) 1998, 2000, 2001, 2002, 2003, 2004, -;; 2005, 2006, 2007 Free Software Foundation, Inc. +;; Copyright (C) 1998, 2000-2013 Free Software Foundation, Inc. ;; Author: Richard Sharman ;; Keywords: faces ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,142 +18,112 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: ;; A minor mode: "Highlight Changes mode". -;; -;; Highlight Changes mode has 2 submodes: active and passive. -;; When active, changes to the buffer are displayed in a different face. -;; When passive, any existing displayed changes are saved and new ones -;; recorded but are not displayed differently. -;; Why active and passive? Having the changes visible can be handy when you -;; want the information but very distracting otherwise. So, you can keep -;; Highlight Changes mode in passive state while you make your changes, toggle -;; it on to active mode to see them, then toggle it back off to avoid -;; distraction. -;; -;; When active, changes are displayed in the `highlight-changes' face. -;; When text is deleted, the following character is displayed in the -;; `highlight-changes-delete' face. -;; +;; When Highlight Changes mode is enabled changes to the buffer are +;; recorded with a text property. Normally these ranges of text are +;; displayed in a distinctive face. However, sometimes it is +;; desirable to temporarily not see these changes. Instead of +;; disabling Highlight Changes mode (which would remove the text property) +;; use the command highlight-changes-visible-mode. + +;; Two faces are supported: one for changed or inserted text and +;; another for the first character after text has been deleted. + +;; When Highlight Changes mode is on (even if changes are not visible) +;; you can go to the next or previous change with +;; `highlight-changes-next-change' or `highlight-changes-previous-change'. + +;; Command highlight-compare-with-file shows changes in this file +;; compared with another file (by default the previous version of the +;; file). ;; +;; The command highlight-compare-buffers compares two buffers by +;; highlighting their differences. + ;; You can "age" different sets of changes by using ;; `highlight-changes-rotate-faces'. This rotates through a series ;; of different faces, so you can distinguish "new" changes from "older" ;; changes. You can customize these "rotated" faces in two ways. You can ;; either explicitly define each face by customizing ;; `highlight-changes-face-list'. If, however, the faces differ from -;; the `highlight-changes' face only in the foreground color, you can simply set +;; `highlight-changes-face' only in the foreground color, you can simply set ;; `highlight-changes-colors'. If `highlight-changes-face-list' is nil when ;; the faces are required they will be constructed from ;; `highlight-changes-colors'. -;; -;; -;; When a Highlight Changes mode is on (either active or passive) you can go -;; to the next or previous change with `highlight-changes-next-change' and -;; `highlight-changes-previous-change'. -;; -;; -;; You can also use the command highlight-compare-with-file to show changes -;; in this file compared with another file (typically the previous version -;; of the file). The command highlight-compare-buffers can be used to -;; compare two buffers. -;; -;; -;; There are currently three hooks run by `highlight-changes-mode': -;; `highlight-changes-enable-hook' - is run when Highlight Changes mode -;; is initially enabled for a buffer. -;; `highlight-changes-disable-hook' - is run when Highlight Changes mode -;; is turned off. -;; `highlight-changes-toggle-hook' - is run each time `highlight-changes-mode' -;; is called. Typically this is when -;; toggling between active and passive -;; modes. The variable -;; `highlight-changes-mode' contains the new -;; state (`active' or `passive'.) -;; -;; + +;; You can automatically rotate faces when the buffer is saved; +;; see function `highlight-changes-rotate-faces' for how to do this. + +;; The hook `highlight-changes-mode-hook' is called when +;; Highlight Changes mode is turned on or off. +;; When it called, variable `highlight-changes-mode' has been updated +;; to the new value. ;; ;; Example usage: -;; (defun my-highlight-changes-enable-hook () -;; (add-hook 'write-file-functions 'highlight-changes-rotate-faces nil t) -;; ) -;; -;; (defun my-highlight-changes-disable-hook () -;; (remove-hook 'write-file-functions 'highlight-changes-rotate-faces t) -;; ) -;; -;; (add-hook 'highlight-changes-enable-hook 'my-highlight-changes-enable-hook) -;; (add-hook 'highlight-changes-disable-hook -;; 'my-highlight-changes-disable-hook) +;; (defun my-highlight-changes-mode-hook () +;; (if highlight-changes-mode +;; (add-hook 'write-file-functions 'highlight-changes-rotate-faces nil t) +;; (remove-hook 'write-file-functions 'highlight-changes-rotate-faces t) +;; )) -;; Explicit vs. Implicit +;; Automatically enabling Highlight Changes mode ;; ;; Normally, Highlight Changes mode is turned on explicitly in a buffer. ;; ;; If you prefer to have it automatically invoked you can do it as ;; follows. -;; + ;; 1. Most modes have a major-hook, typically called MODE-hook. You ;; can use `add-hook' to call `highlight-changes-mode'. -;; + ;; Example: ;; (add-hook 'c-mode-hook 'highlight-changes-mode) -;; -;; If you want to make it start up in passive mode (regardless of the -;; setting of highlight-changes-initial-state): -;; (add-hook 'emacs-lisp-mode-hook -;; (lambda () -;; (highlight-changes-mode 'passive))) -;; + ;; However, this cannot be done for Fundamental mode for there is no ;; such hook. -;; -;; 2. You can use the function `global-highlight-changes' + +;; 2. You can use the function `global-highlight-changes-mode' ;; ;; This function, which is fashioned after the way `global-font-lock' works, ;; toggles on or off global Highlight Changes mode. When activated, it turns ;; on Highlight Changes mode in all "suitable" existing buffers and will turn ;; it on in new "suitable" buffers to be created. -;; + ;; A buffer's "suitability" is determined by variable -;; `highlight-changes-global-modes', as follows. If the variable is +;; `highlight-changes-global-modes', as follows. If it is ;; * nil -- then no buffers are suitable; ;; * a function -- this function is called and the result is used. As ;; an example, if the value is `buffer-file-name' then all buffers ;; who are visiting files are suitable, but others (like dired ;; buffers) are not; -;; * a list -- then the buffer is suitable iff its mode is in the +;; * a list -- then the buffer is suitable if and only if its mode is in the ;; list, except if the first element is `not', in which case the test ;; is reversed (i.e. it is a list of unsuitable modes). ;; * Otherwise, the buffer is suitable if its name does not begin with ;; ` ' or `*' and if `buffer-file-name' returns true. -;; +;; To enable it for future sessions put this in your ~/.emacs file: +;; (global-highlight-changes-mode t) ;; Possible bindings: ;; (global-set-key '[C-right] 'highlight-changes-next-change) ;; (global-set-key '[C-left] 'highlight-changes-previous-change) ;; -;; Other interactive functions (which could be bound if desired): +;; Other interactive functions (that could be bound if desired): ;; highlight-changes-mode +;; highlight-changes-toggle-visibility ;; highlight-changes-remove-highlight -;; highlight-changes-rotate-faces ;; highlight-compare-with-file ;; highlight-compare-buffers - -;; -;; You can automatically rotate faces when the buffer is saved; -;; see function `highlight-changes-rotate-faces' for how to do this. -;; +;; highlight-changes-rotate-faces ;;; Bugs: @@ -168,11 +137,8 @@ ;; - having different faces for deletion and non-deletion: is it ;; really worth the hassle? -;; - should have better hooks: when should they be run? ;; - highlight-compare-with-file should allow RCS files - e.g. nice to be ;; able to say show changes compared with version 2.1. -;; - Maybe we should have compare-with-buffer as well. (When I tried -;; a while back I ran into a problem with ediff-buffers-internal.) ;;; History: @@ -193,6 +159,15 @@ ;; Dec 2003 ;; - Use require for ediff stuff ;; - Added highlight-compare-buffers +;; Mar 2008 +;; - Made highlight-changes-mode like other modes (toggle on/off) +;; - Added new command highlight-changes-visible-mode to replace the +;; previous active/passive aspect of highlight-changes-mode. +;; - Removed highlight-changes-toggle-hook +;; - Put back eval-and-compile inadvertently dropped +;; May 2008 +;; - Removed highlight-changes-disable-hook and highlight-changes-enable-hook +;; because highlight-changes-mode-hook can do both. ;;; Code: @@ -219,8 +194,8 @@ (t (:inverse-video t))) "Face used for highlighting changes." :group 'highlight-changes) -;; backward-compatibility alias -(put 'highlight-changes-face 'face-alias 'highlight-changes) +(define-obsolete-face-alias 'highlight-changes-face + 'highlight-changes "22.1") ;; This looks pretty ugly, actually. Maybe the underline should be removed. (defface highlight-changes-delete @@ -229,20 +204,21 @@ (t (:inverse-video t))) "Face used for highlighting deletions." :group 'highlight-changes) -;; backward-compatibility alias -(put 'highlight-changes-delete-face 'face-alias 'highlight-changes-delete) - +(define-obsolete-face-alias 'highlight-changes-delete-face + 'highlight-changes-delete "22.1") ;; A (not very good) default list of colors to rotate through. -;; +(define-obsolete-variable-alias 'highlight-changes-colours + 'highlight-changes-colors "22.1") + (defcustom highlight-changes-colors (if (eq (frame-parameter nil 'background-mode) 'light) ;; defaults for light background: '( "magenta" "blue" "darkgreen" "chocolate" "sienna4" "NavyBlue") ;; defaults for dark background: '("yellow" "magenta" "blue" "maroon" "firebrick" "green4" "DarkOrchid")) - "*Colors used by `highlight-changes-rotate-faces'. + "Colors used by `highlight-changes-rotate-faces'. The newest rotated change will be displayed in the first element of this list, the next older will be in the second element etc. @@ -253,40 +229,42 @@ colors then use this, if you want fancier faces then set :type '(repeat color) :group 'highlight-changes) -(define-obsolete-variable-alias 'highlight-changes-colours - 'highlight-changes-colors "22.1") +;; When you invoke highlight-changes-mode, should highlight-changes-visible-mode +;; be on or off? +(define-obsolete-variable-alias 'highlight-changes-initial-state + 'highlight-changes-visibility-initial-state "23.1") -;; If you invoke highlight-changes-mode with no argument, should it start in -;; active or passive mode? -;; -(defcustom highlight-changes-initial-state 'active - "*What state (active or passive) Highlight Changes mode should start in. -This is used when `highlight-changes-mode' is called with no argument. -This variable must be set to one of the symbols `active' or `passive'." - :type '(choice (const :tag "Active" active) - (const :tag "Passive" passive)) - :group 'highlight-changes) +(defcustom highlight-changes-visibility-initial-state t + "Controls whether changes are initially visible in Highlight Changes mode. -(defcustom highlight-changes-global-initial-state 'passive - "*What state global Highlight Changes mode should start in. -This is used if `global-highlight-changes' is called with no argument. -This variable must be set to either `active' or `passive'." - :type '(choice (const :tag "Active" active) - (const :tag "Passive" passive)) +This controls the initial value of `highlight-changes-visible-mode'. +When a buffer is in Highlight Changes mode the function +`highlight-changes-visible-mode' is used to toggle the mode on or off." + :type 'boolean :group 'highlight-changes) -;; The strings displayed in the mode-line for the minor mode: -(defcustom highlight-changes-active-string " +Chg" - "*The string used when Highlight Changes mode is in the active state. +;; highlight-changes-global-initial-state has been removed + + + +;; These are the strings displayed in the mode-line for the minor mode: +(define-obsolete-variable-alias 'highlight-changes-active-string + 'highlight-changes-visible-string "23.1") + +(defcustom highlight-changes-visible-string " +Chg" + "The string used when in Highlight Changes mode and changes are visible. This should be set to nil if no indication is desired, or to a string with a leading space." :type '(choice string (const :tag "None" nil)) :group 'highlight-changes) -(defcustom highlight-changes-passive-string " -Chg" - "*The string used when Highlight Changes mode is in the passive state. +(define-obsolete-variable-alias 'highlight-changes-passive-string + 'highlight-changes-invisible-string "23.1") + +(defcustom highlight-changes-invisible-string " -Chg" + "The string used when in Highlight Changes mode and changes are hidden. This should be set to nil if no indication is desired, or to a string with a leading space." :type '(choice string @@ -294,7 +272,7 @@ a string with a leading space." :group 'highlight-changes) (defcustom highlight-changes-global-modes t - "*Determine whether a buffer is suitable for global Highlight Changes mode. + "Determine whether a buffer is suitable for global Highlight Changes mode. A function means call that function to decide: if it returns non-nil, the buffer is suitable. @@ -306,11 +284,11 @@ modes which are not suitable. A value of t means the buffer is suitable if it is visiting a file and its name does not begin with ` ' or `*'. -A value of nil means no buffers are suitable for `global-highlight-changes' +A value of nil means no buffers are suitable for `global-highlight-changes-mode' \(effectively disabling the mode). Example: - (c-mode c++-mode) + (c-mode c++-mode) means that Highlight Changes mode is turned on for buffers in C and C++ modes only." :type '(choice @@ -325,10 +303,8 @@ modes only." ) :group 'highlight-changes) -(defvar global-highlight-changes nil) - (defcustom highlight-changes-global-changes-existing-buffers nil - "*If non-nil, toggling global Highlight Changes mode affects existing buffers. + "If non-nil, toggling global Highlight Changes mode affects existing buffers. Normally, `global-highlight-changes' affects only new buffers (to be created). However, if `highlight-changes-global-changes-existing-buffers' is non-nil, then turning on `global-highlight-changes' will turn on @@ -337,7 +313,83 @@ remove it from existing buffers." :type 'boolean :group 'highlight-changes) -(defun hilit-chg-cust-fix-changes-face-list (w wc &optional event) +;; These are for internal use. + +(defvar hilit-chg-list nil) +(defvar hilit-chg-string " ??") + +(make-variable-buffer-local 'hilit-chg-string) + + + +;;; Functions... + +;;;###autoload +(define-minor-mode highlight-changes-mode + "Toggle highlighting changes in this buffer (Highlight Changes mode). +With a prefix argument ARG, enable Highlight Changes mode if ARG +is positive, and disable it otherwise. If called from Lisp, +enable the mode if ARG is omitted or nil. + +When Highlight Changes is enabled, changes are marked with a text +property. Normally they are displayed in a distinctive face, but +command \\[highlight-changes-visible-mode] can be used to toggles +this on and off. + +Other functions for buffers in this mode include: +\\[highlight-changes-next-change] - move point to beginning of next change +\\[highlight-changes-previous-change] - move to beginning of previous change +\\[highlight-changes-remove-highlight] - remove the change face from the region +\\[highlight-changes-rotate-faces] - rotate different \"ages\" of changes +through various faces. +\\[highlight-compare-with-file] - mark text as changed by comparing this +buffer with the contents of a file +\\[highlight-compare-buffers] highlights differences between two buffers." + nil ;; init-value + hilit-chg-string ;; lighter + nil ;; keymap + (if (or (display-color-p) + (and (fboundp 'x-display-grayscale-p) (x-display-grayscale-p))) + (progn + (if (and (eq this-command 'global-highlight-changes-mode) + (not highlight-changes-global-changes-existing-buffers)) + ;; The global mode has toggled the value of the mode variable, + ;; but not other changes have been mode, so we are safe + ;; to retoggle it. + (setq highlight-changes-mode (not highlight-changes-mode))) + (if highlight-changes-mode + ;; it is being turned on + (hilit-chg-set) + ;; mode is turned off + (hilit-chg-clear))) + (message "Highlight Changes mode requires color or grayscale display"))) + + +;;;###autoload +(define-minor-mode highlight-changes-visible-mode + "Toggle visibility of highlighting due to Highlight Changes mode. +With a prefix argument ARG, enable Highlight Changes Visible mode +if ARG is positive, and disable it otherwise. If called from +Lisp, enable the mode if ARG is omitted or nil. + +Highlight Changes Visible mode only has an effect when Highlight +Changes mode is on. When enabled, the changed text is displayed +in a distinctive face. + +The default value can be customized with variable +`highlight-changes-visibility-initial-state'. + +This command does not itself set highlight-changes mode." + + t ;; init-value + nil ;; lighter + nil ;; keymap + + (hilit-chg-update) + ) + + +(defun hilit-chg-cust-fix-changes-face-list (w _wc &optional event) ;; When customization function `highlight-changes-face-list' inserts a new ;; face it uses the default face. We don't want the user to modify this ;; face, so we rename the faces in the list on an insert. The rename is @@ -383,7 +435,7 @@ remove it from existing buffers." (defcustom highlight-changes-face-list nil - "*A list of faces used when rotating changes. + "A list of faces used when rotating changes. Normally the variable is initialized to nil and the list is created from `highlight-changes-colors' when needed. However, you can set this variable to any list of faces. You will have to do this if you want faces which @@ -398,32 +450,12 @@ Otherwise, this list will be constructed when needed from ) :group 'highlight-changes) -;; ======================================================================== - -;; These shouldn't be changed! - -(defvar highlight-changes-mode nil) -(defvar hilit-chg-list nil) -(defvar hilit-chg-string " ??") -(or (assq 'highlight-changes-mode minor-mode-alist) - (setq minor-mode-alist - (cons '(highlight-changes-mode hilit-chg-string) minor-mode-alist) - )) -(make-variable-buffer-local 'highlight-changes-mode) -(make-variable-buffer-local 'hilit-chg-string) - - -(require 'ediff-init) -(require 'ediff-util) - - -;;; Functions... (defun hilit-chg-map-changes (func &optional start-position end-position) - "Call function FUNC for each region used by Highlight Changes mode." - ;; if start-position is nil, (point-min) is used - ;; if end-position is nil, (point-max) is used - ;; FUNC is called with 3 params: property start stop + "Call function FUNC for each region used by Highlight Changes mode. +If START-POSITION is nil, (point-min) is used. +If END-POSITION is nil, (point-max) is used. +FUNC is called with 3 params: PROPERTY START STOP." (let ((start (or start-position (point-min))) (limit (or end-position (point-max))) prop end) @@ -438,8 +470,8 @@ Otherwise, this list will be constructed when needed from (defun hilit-chg-display-changes (&optional beg end) "Display face information for Highlight Changes mode. -An overlay containing a change face is added from the information -in the text property of type `hilit-chg'. +An overlay from BEG to END containing a change face is added from the +information in the text property of type `hilit-chg'. This is the opposite of `hilit-chg-hide-changes'." (hilit-chg-map-changes 'hilit-chg-make-ov beg end)) @@ -448,16 +480,15 @@ This is the opposite of `hilit-chg-hide-changes'." (defun hilit-chg-make-ov (prop start end) (or prop (error "hilit-chg-make-ov: prop is nil")) - ;; for the region make change overlays corresponding to - ;; the text property 'hilit-chg + ;; For the region create overlays with a distinctive face + ;; and the text property 'hilit-chg. (let ((ov (make-overlay start end)) - face) - (if (eq prop 'hilit-chg-delete) - (setq face 'highlight-changes-delete) - (setq face (nth 1 (member prop hilit-chg-list)))) + (face (if (eq prop 'hilit-chg-delete) + 'highlight-changes-delete + (nth 1 (member prop hilit-chg-list))))) (if face (progn - ;; We must mark the face, that is the purpose of the overlay + ;; We must mark the face, that is the purpose of the overlay. (overlay-put ov 'face face) ;; I don't think we need to set evaporate since we should ;; be controlling them! @@ -476,14 +507,12 @@ containing the change information is retained. This is the opposite of `hilit-chg-display-changes'." (let ((start (or beg (point-min))) - (limit (or end (point-max))) - p ov) - (setq p (overlays-in start limit)) - (while p + (limit (or end (point-max)))) + (dolist (p (overlays-in start limit)) ;; don't delete the overlay if it isn't ours! - (if (overlay-get (car p) 'hilit-chg) - (delete-overlay (car p))) - (setq p (cdr p))))) + (if (overlay-get p 'hilit-chg) + (delete-overlay p))))) + (defun hilit-chg-fixup (beg end) "Fix change overlays in region between BEG and END. @@ -491,36 +520,15 @@ This is the opposite of `hilit-chg-display-changes'." Ensure the overlays agree with the changes as determined from the text properties of type `hilit-chg'." ;; Remove or alter overlays in region beg..end - (let (ov-start ov-end props q) - ;; temp for debugging: - ;; (or (eq highlight-changes-mode 'active) - ;; (error "hilit-chg-fixup called but Highlight Changes mode not active")) - (dolist (ov (overlays-in beg end)) - ;; Don't alter overlays that are not ours. - (when (overlay-get ov 'hilit-chg) - (let ((ov-start (overlay-start ov)) - (ov-end (overlay-end ov))) - (if (< ov-start beg) - (progn - (move-overlay ov ov-start beg) - (if (> ov-end end) - (progn - (setq props (overlay-properties ov)) - (setq ov (make-overlay end ov-end)) - (while props - (overlay-put ov (car props)(car (cdr props))) - (setq props (cdr (cdr props))))))) - (if (> ov-end end) - (move-overlay ov end ov-end) - (delete-overlay ov)))))) - (hilit-chg-display-changes beg end))) + (remove-overlays beg end 'hilit-chg t) + (hilit-chg-display-changes beg end)) ;;;###autoload (defun highlight-changes-remove-highlight (beg end) "Remove the change face from the region between BEG and END. This allows you to manually remove highlighting from uninteresting changes." (interactive "r") - (let ((after-change-functions nil)) + (with-silent-modifications (remove-text-properties beg end '(hilit-chg nil)) (hilit-chg-fixup beg end))) @@ -537,59 +545,68 @@ This allows you to manually remove highlighting from uninteresting changes." ;; otherwise an undone change shows up as changed. While the properties ;; are automatically restored by undo, we must fix up the overlay. (save-match-data - (let ((beg-decr 1) (end-incr 1) - (type 'hilit-chg) - old) + (let (;;(beg-decr 1) + (end-incr 1) + (type 'hilit-chg)) (if undo-in-progress - (if (eq highlight-changes-mode 'active) + (if (and highlight-changes-mode + highlight-changes-visible-mode) (hilit-chg-fixup beg end)) - (if (and (= beg end) (> leng-before 0)) - ;; deletion - (progn - ;; The eolp and bolp tests are a kludge! But they prevent - ;; rather nasty looking displays when deleting text at the end - ;; of line, such as normal corrections as one is typing and - ;; immediately makes a correction, and when deleting first - ;; character of a line. -;;; (if (= leng-before 1) -;;; (if (eolp) -;;; (setq beg-decr 0 end-incr 0) -;;; (if (bolp) -;;; (setq beg-decr 0)))) -;;; (setq beg (max (- beg beg-decr) (point-min))) - (setq end (min (+ end end-incr) (point-max))) - (setq type 'hilit-chg-delete)) - ;; Not a deletion. - ;; Most of the time the following is not necessary, but - ;; if the current text was marked as a deletion then - ;; the old overlay is still in effect, so if we add some - ;; text then remove the deletion marking, but set it to - ;; changed otherwise its highlighting disappears. - (if (eq (get-text-property end 'hilit-chg) 'hilit-chg-delete) - (progn - (remove-text-properties end (+ end 1) '(hilit-chg nil)) - (put-text-property end (+ end 1) 'hilit-chg 'hilit-chg) - (if (eq highlight-changes-mode 'active) - (hilit-chg-fixup beg (+ end 1)))))) - (unless no-property-change - (put-text-property beg end 'hilit-chg type)) - (if (or (eq highlight-changes-mode 'active) no-property-change) - (hilit-chg-make-ov type beg end)))))) - -(defun hilit-chg-set (value) - "Turn on Highlight Changes mode for this buffer." - (setq highlight-changes-mode value) - (remove-hook 'after-change-functions 'hilit-chg-set-face-on-change t) - (hilit-chg-make-list) - (if (eq highlight-changes-mode 'active) + (with-silent-modifications + (if (and (= beg end) (> leng-before 0)) + ;; deletion + (progn + ;; The eolp and bolp tests are a kludge! But they prevent + ;; rather nasty looking displays when deleting text at the end + ;; of line, such as normal corrections as one is typing and + ;; immediately makes a correction, and when deleting first + ;; character of a line. + ;; (if (= leng-before 1) + ;; (if (eolp) + ;; (setq beg-decr 0 end-incr 0) + ;; (if (bolp) + ;; (setq beg-decr 0)))) + ;; (setq beg (max (- beg beg-decr) (point-min))) + (setq end (min (+ end end-incr) (point-max))) + (setq type 'hilit-chg-delete)) + ;; Not a deletion. + ;; Most of the time the following is not necessary, but + ;; if the current text was marked as a deletion then + ;; the old overlay is still in effect. So if the user adds some + ;; text where she earlier deleted text, we have to remove the + ;; deletion marking, and replace it explicitly with a `changed' + ;; marking, otherwise its highlighting would disappear. + (if (eq (get-text-property end 'hilit-chg) 'hilit-chg-delete) + (save-restriction + (widen) + (put-text-property end (+ end 1) 'hilit-chg 'hilit-chg) + (if highlight-changes-visible-mode + (hilit-chg-fixup end (+ end 1)))))) + (unless no-property-change + (put-text-property beg end 'hilit-chg type)) + (if (or highlight-changes-visible-mode no-property-change) + (hilit-chg-make-ov type beg end))))))) + +(defun hilit-chg-update () + "Update a buffer's highlight changes when visibility changed." + (if highlight-changes-visible-mode + ;; changes are visible (progn - (setq hilit-chg-string highlight-changes-active-string) + (setq hilit-chg-string highlight-changes-visible-string) (or buffer-read-only (hilit-chg-display-changes))) - ;; mode is passive - (setq hilit-chg-string highlight-changes-passive-string) + ;; changes are invisible + (setq hilit-chg-string highlight-changes-invisible-string) (or buffer-read-only - (hilit-chg-hide-changes))) + (hilit-chg-hide-changes)))) + +(defun hilit-chg-set () + "Turn on Highlight Changes mode for this buffer." + (remove-hook 'after-change-functions 'hilit-chg-set-face-on-change t) + (hilit-chg-make-list) + (setq highlight-changes-mode t) + (setq highlight-changes-visible-mode highlight-changes-visibility-initial-state) + (hilit-chg-update) (force-mode-line-update) (add-hook 'after-change-functions 'hilit-chg-set-face-on-change nil t)) @@ -598,90 +615,18 @@ This allows you to manually remove highlighting from uninteresting changes." This removes all saved change information." (if buffer-read-only ;; We print the buffer name because this function could be called - ;; on many buffers from `global-highlight-changes'. + ;; on many buffers from `global-highlight-changes-mode'. (message "Cannot remove highlighting from read-only mode buffer %s" (buffer-name)) (remove-hook 'after-change-functions 'hilit-chg-set-face-on-change t) - (let ((after-change-functions nil)) + (with-silent-modifications (hilit-chg-hide-changes) (hilit-chg-map-changes - '(lambda (prop start stop) - (remove-text-properties start stop '(hilit-chg nil)))) - ) + (lambda (_prop start stop) + (remove-text-properties start stop '(hilit-chg nil))))) (setq highlight-changes-mode nil) - (force-mode-line-update) - ;; If we type: C-u -1 M-x highlight-changes-mode - ;; we want to turn it off, but hilit-chg-post-command-hook - ;; runs and that turns it back on! - (remove-hook 'post-command-hook 'hilit-chg-post-command-hook))) + (force-mode-line-update))) -;;;###autoload -(defun highlight-changes-mode (&optional arg) - "Toggle (or initially set) Highlight Changes mode. - -Without an argument: - If Highlight Changes mode is not enabled, then enable it (in either active - or passive state as determined by the variable - `highlight-changes-initial-state'); otherwise, toggle between active - and passive state. - -With an argument ARG: - If ARG is positive, set state to active; - If ARG is zero, set state to passive; - If ARG is negative, disable Highlight Changes mode completely. - -Active state - means changes are shown in a distinctive face. -Passive state - means changes are kept and new ones recorded but are - not displayed in a different face. - -Functions: -\\[highlight-changes-next-change] - move point to beginning of next change -\\[highlight-changes-previous-change] - move to beginning of previous change -\\[highlight-compare-with-file] - mark text as changed by comparing this - buffer with the contents of a file -\\[highlight-changes-remove-highlight] - remove the change face from the region -\\[highlight-changes-rotate-faces] - rotate different \"ages\" of changes \ -through - various faces - -Hook variables: -`highlight-changes-enable-hook' - when enabling Highlight Changes mode -`highlight-changes-toggle-hook' - when entering active or passive state -`highlight-changes-disable-hook' - when turning off Highlight Changes mode" - (interactive "P") - (if (or (display-color-p) - (and (fboundp 'x-display-grayscale-p) (x-display-grayscale-p))) - (let ((was-on highlight-changes-mode) - (new-highlight-changes-mode - (cond - ((null arg) - ;; no arg => toggle (or set to active initially) - (if highlight-changes-mode - (if (eq highlight-changes-mode 'active) 'passive 'active) - highlight-changes-initial-state)) - ;; an argument is given - ((eq arg 'active) - 'active) - ((eq arg 'passive) - 'passive) - ((> (prefix-numeric-value arg) 0) - 'active) - ((< (prefix-numeric-value arg) 0) - nil) - (t - 'passive)))) - (if new-highlight-changes-mode - ;; mode is turned on -- but may be passive - (progn - (hilit-chg-set new-highlight-changes-mode) - (or was-on - ;; run highlight-changes-enable-hook once - (run-hooks 'highlight-changes-enable-hook)) - (run-hooks 'highlight-changes-toggle-hook)) - ;; mode is turned off - (run-hooks 'highlight-changes-disable-hook) - (hilit-chg-clear))) - (message "Highlight Changes mode requires color or grayscale display"))) ;;;###autoload (defun highlight-changes-next-change () @@ -775,7 +720,7 @@ Hook variables: ;;;###autoload (defun highlight-changes-rotate-faces () - "Rotate the faces used by Highlight Changes mode. + "Rotate the faces if in Highlight Changes mode and the changes are visible. Current changes are displayed in the face described by the first element of `highlight-changes-face-list', one level older changes are shown in @@ -788,9 +733,7 @@ this, eval the following in the buffer to be saved: \(add-hook 'write-file-functions 'highlight-changes-rotate-faces nil t)" (interactive) - ;; If not in active mode do nothing but don't complain because this - ;; may be bound to a hook. - (when (eq highlight-changes-mode 'active) + (when (and highlight-changes-mode highlight-changes-visible-mode) (let ((modified (buffer-modified-p)) (inhibit-modification-hooks t)) ;; The `modified' related code tries to combine two goals: (1) Record the @@ -798,11 +741,12 @@ this, eval the following in the buffer to be saved: ;; of the current buffer due to the rotation. We do this by inserting (in ;; `buffer-undo-list') entries restoring buffer-modified-p to nil before ;; and after the entry for the rotation. - (unless modified - ;; Install the "before" entry. - (setq buffer-undo-list - (cons '(apply restore-buffer-modified-p nil) - buffer-undo-list))) + ;; FIXME: this is no good: we need to test the `modified' state at the + ;; time of the undo, not at the time of the "do", otherwise the undo + ;; may erroneously clear the modified flag. --Stef + ;; (unless modified + ;; ;; Install the "before" entry. + ;; (push '(apply restore-buffer-modified-p nil) buffer-undo-list)) (unwind-protect (progn ;; ensure hilit-chg-list is made and up to date @@ -811,14 +755,11 @@ this, eval the following in the buffer to be saved: (hilit-chg-hide-changes) ;; for each change text property, increment it (hilit-chg-map-changes 'hilit-chg-bump-change) - ;; and display them all if active - (if (eq highlight-changes-mode 'active) - (hilit-chg-display-changes))) + ;; and display them + (hilit-chg-display-changes)) (unless modified - ;; Install the "after" entry. - (setq buffer-undo-list - (cons '(apply restore-buffer-modified-p nil) - buffer-undo-list)) + ;; Install the "after" entry. FIXME: See above. + ;; (push '(apply restore-buffer-modified-p nil) buffer-undo-list) (restore-buffer-modified-p nil))))) ;; This always returns nil so it is safe to use in write-file-functions @@ -833,6 +774,8 @@ this, eval the following in the buffer to be saved: "Get differences between two buffers and set highlight changes. Both buffers are done unless optional parameter MARKUP-A-ONLY is non-nil." + (eval-and-compile + (require 'ediff-util)) (save-window-excursion (let* (change-info change-a change-b @@ -861,9 +804,9 @@ is non-nil." (or file-b (setq temp-b (setq file-b (ediff-make-temp-file buf-b nil)))) (set-buffer buf-a) - (highlight-changes-mode 'active) + (highlight-changes-mode 1) (or markup-a-only (with-current-buffer buf-b - (highlight-changes-mode 'active))) + (highlight-changes-mode 1))) (setq change-info (hilit-chg-get-diff-info buf-a file-a buf-b file-b)) @@ -937,26 +880,28 @@ read in temporarily but the buffer is deleted. If the buffer is read-only, differences will be highlighted but no property changes are made, so \\[highlight-changes-next-change] and \\[highlight-changes-previous-change] will not work." - (interactive (list - (read-file-name - "File to compare with? " ;; prompt - "" ;; directory - nil ;; default - 'yes ;; must exist - (let ((f (buffer-file-name (current-buffer)))) - (if f - (progn - (setq f (make-backup-file-name f)) - (or (file-exists-p f) - (setq f nil))) - ) - f)))) + (interactive + (let ((file buffer-file-name) + (file-name nil) + (file-dir nil)) + (and file + (setq file-name (file-name-nondirectory file) + file-dir (file-name-directory file))) + (setq file-name (make-backup-file-name file-name)) + (unless (file-exists-p file-name) + (setq file-name nil)) + (list (read-file-name + "Find to compare with: " ;; prompt + file-dir ;; directory + nil ;; default + nil ;; existing + file-name) ;; initial + ))) (let* ((buf-a (current-buffer)) (file-a (buffer-file-name)) (existing-buf (get-file-buffer file-b)) (buf-b (or existing-buf - (find-file-noselect file-b))) - (buf-b-read-only (with-current-buffer buf-b buffer-read-only))) + (find-file-noselect file-b)))) (highlight-markup-buffers buf-a file-a buf-b file-b (not existing-buf)) (unless existing-buf (kill-buffer buf-b)) @@ -964,24 +909,26 @@ changes are made, so \\[highlight-changes-next-change] and (defun hilit-chg-get-diff-info (buf-a file-a buf-b file-b) - (let ((e nil) x y) ;; e is set by function hilit-chg-get-diff-list-hk + ;; hilit-e,x,y are set by function hilit-chg-get-diff-list-hk. + (let (hilit-e hilit-x hilit-y) (ediff-setup buf-a file-a buf-b file-b nil nil ; buf-c file-C 'hilit-chg-get-diff-list-hk (list (cons 'ediff-job-name 'something)) ) - (ediff-with-current-buffer e (ediff-really-quit nil)) - (list x y))) + (ediff-with-current-buffer hilit-e (ediff-really-quit nil)) + (list hilit-x hilit-y))) (defun hilit-chg-get-diff-list-hk () - ;; x and y are dynamically bound by hilit-chg-get-diff-info - ;; which calls this function as a hook - (defvar x) ;; placate the byte-compiler - (defvar y) - (setq e (current-buffer)) + ;; hilit-e/x/y are dynamically bound by hilit-chg-get-diff-info + ;; which calls this function as a hook. + (defvar hilit-x) ; placate the byte-compiler + (defvar hilit-y) + (defvar hilit-e) + (setq hilit-e (current-buffer)) (let ((n 0) extent p va vb a b) - (setq x nil y nil) ;; x and y are bound by hilit-chg-get-diff-info + (setq hilit-x nil hilit-y nil) (while (< n ediff-number-of-differences) (ediff-make-fine-diffs n) (setq va (ediff-get-fine-diff-vector n 'A)) @@ -997,7 +944,7 @@ changes are made, so \\[highlight-changes-next-change] and (setq extent (list (overlay-start (car p)) (overlay-end (car p)))) (setq p (cdr p)) - (setq x (append x (list extent) )));; while p + (setq hilit-x (append hilit-x (list extent) )));; while p ;; (setq vb (ediff-get-fine-diff-vector n 'B)) ;; vb is a vector @@ -1012,131 +959,25 @@ changes are made, so \\[highlight-changes-next-change] and (setq extent (list (overlay-start (car p)) (overlay-end (car p)))) (setq p (cdr p)) - (setq y (append y (list extent) ))) + (setq hilit-y (append hilit-y (list extent) ))) (setq n (1+ n)));; while ;; ediff-quit doesn't work here. ;; No point in returning a value, since this is a hook function. )) -;; ======================= automatic stuff ============== - -;; Global Highlight Changes mode is modeled after Global Font-lock mode. -;; Three hooks are used to gain control. When Global Changes Mode is -;; enabled, `find-file-hook' and `change-major-mode-hook' are set. -;; `find-file-hook' is called when visiting a file, the new mode is -;; known at this time. -;; `change-major-mode-hook' is called when a buffer is changing mode. -;; This could be because of finding a file in which case -;; `find-file-hook' has already been called and has done its work. -;; However, it also catches the case where a new mode is being set by -;; the user. However, it is called from `kill-all-variables' and at -;; this time the mode is the old mode, which is not what we want. -;; So, our function temporarily sets `post-command-hook' which will -;; be called after the buffer has been completely set up (with the new -;; mode). It then removes the `post-command-hook'. -;; One other wrinkle - every M-x command runs the `change-major-mode-hook' -;; so we ignore this by examining the buffer name. - - -(defun hilit-chg-major-mode-hook () - (add-hook 'post-command-hook 'hilit-chg-post-command-hook)) - -(defun hilit-chg-post-command-hook () - ;; This is called after changing a major mode, but also after each - ;; M-x command, in which case the current buffer is a minibuffer. - ;; In that case, do not act on it here, but don't turn it off - ;; either, we will get called here again soon-after. - ;; Also, don't enable it for other special buffers. - (if (string-match "^[ *]" (buffer-name)) - nil ;; (message "ignoring this post-command-hook") - (remove-hook 'post-command-hook 'hilit-chg-post-command-hook) - ;; The following check isn't necessary, since - ;; hilit-chg-turn-on-maybe makes this check too. - (or highlight-changes-mode ;; don't turn it on if it already is - (hilit-chg-turn-on-maybe highlight-changes-global-initial-state)))) - -(defun hilit-chg-check-global () - ;; This is called from the find file hook. - (hilit-chg-turn-on-maybe highlight-changes-global-initial-state)) - +;; ======================= global-highlight-changes-mode ============== ;;;###autoload -(defun global-highlight-changes (&optional arg) - "Turn on or off global Highlight Changes mode. - -When called interactively: -- if no prefix, toggle global Highlight Changes mode on or off -- if called with a positive prefix (or just C-u) turn it on in active mode -- if called with a zero prefix turn it on in passive mode -- if called with a negative prefix turn it off - -When called from a program: -- if ARG is nil or omitted, turn it off -- if ARG is `active', turn it on in active mode -- if ARG is `passive', turn it on in passive mode -- otherwise just turn it on - -When global Highlight Changes mode is enabled, Highlight Changes mode is turned -on for future \"suitable\" buffers (and for \"suitable\" existing buffers if -variable `highlight-changes-global-changes-existing-buffers' is non-nil). -\"Suitability\" is determined by variable `highlight-changes-global-modes'." +(define-globalized-minor-mode global-highlight-changes-mode + highlight-changes-mode highlight-changes-mode-turn-on) - (interactive - (list - (cond - ((null current-prefix-arg) - ;; no arg => toggle it on/off - (setq global-highlight-changes (not global-highlight-changes))) - ;; positive interactive arg - turn it on as active - ((> (prefix-numeric-value current-prefix-arg) 0) - (setq global-highlight-changes t) - 'active) - ;; zero interactive arg - turn it on as passive - ((= (prefix-numeric-value current-prefix-arg) 0) - (setq global-highlight-changes t) - 'passive) - ;; negative interactive arg - turn it off - (t - (setq global-highlight-changes nil) - nil)))) - - (if arg - (progn - (if (eq arg 'active) - (setq highlight-changes-global-initial-state 'active) - (if (eq arg 'passive) - (setq highlight-changes-global-initial-state 'passive))) - (setq global-highlight-changes t) - (message "Turning ON Global Highlight Changes mode in %s state" - highlight-changes-global-initial-state) - ;; FIXME: Not sure what this was intended to do. --Stef - ;; (add-hook 'hilit-chg-major-mode-hook 'hilit-chg-major-mode-hook) - (add-hook 'find-file-hook 'hilit-chg-check-global) - (if highlight-changes-global-changes-existing-buffers - (hilit-chg-update-all-buffers - highlight-changes-global-initial-state))) - - (message "Turning OFF global Highlight Changes mode") - ;; FIXME: Not sure what this was intended to do. --Stef - ;; (remove-hook 'hilit-chg-major-mode-hook 'hilit-chg-major-mode-hook) - (remove-hook 'post-command-hook 'hilit-chg-post-command-hook) - (remove-hook 'find-file-hook 'hilit-chg-check-global) - (if highlight-changes-global-changes-existing-buffers - (hilit-chg-update-all-buffers nil)))) - - -(defun hilit-chg-turn-on-maybe (value) - "Turn on Highlight Changes mode if it is appropriate for this buffer. - -A buffer is appropriate for Highlight Changes mode if all these are true: -- the buffer is not a special buffer (one whose name begins with - `*' or ` '), -- the buffer's mode is suitable as per variable - `highlight-changes-global-modes', -- Highlight Changes mode is not already on for this buffer. - -This function is called from `hilit-chg-update-all-buffers' or -from `global-highlight-changes' when turning on global Highlight Changes mode." +(define-obsolete-function-alias + 'global-highlight-changes + 'global-highlight-changes-mode "23.1") + +(defun highlight-changes-mode-turn-on () + "See if Highlight Changes mode should be turned on for this buffer. +This is called when `global-highlight-changes-mode' is turned on." (or highlight-changes-mode ; do nothing if already on (if (cond @@ -1152,29 +993,10 @@ from `global-highlight-changes' when turning on global Highlight Changes mode." (and (not (string-match "^[ *]" (buffer-name))) (buffer-file-name)))) - (progn - (hilit-chg-set value) - (run-hooks 'highlight-changes-enable-hook))))) + (highlight-changes-mode 1)) + )) -(defun hilit-chg-turn-off-maybe () - (if highlight-changes-mode - (progn - (run-hooks 'highlight-changes-disable-hook) - (hilit-chg-clear)))) - - -(defun hilit-chg-update-all-buffers (value) - (mapc - (function (lambda (buffer) - (with-current-buffer buffer - (if value - (hilit-chg-turn-on-maybe value) - (hilit-chg-turn-off-maybe)) - ))) - (buffer-list)) - nil) - ;;;; Desktop support. ;; Called by `desktop-create-buffer' to restore `highlight-changes-mode'. @@ -1193,9 +1015,8 @@ from `global-highlight-changes' when turning on global Highlight Changes mode." ;; (defun hilit-chg-debug-show (&optional beg end) ;; (interactive) ;; (message "--- hilit-chg-debug-show ---") -;; (hilit-chg-map-changes '(lambda (prop start end) -;; (message "%d-%d: %s" start end prop) -;; ) +;; (hilit-chg-map-changes (lambda (prop start end) +;; (message "%d-%d: %s" start end prop)) ;; beg end ;; )) ;; @@ -1203,5 +1024,4 @@ from `global-highlight-changes' when turning on global Highlight Changes mode." (provide 'hilit-chg) -;; arch-tag: de00301d-5bad-44da-aa82-e0e010b0c463 ;;; hilit-chg.el ends here