| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- ;;; flashparen.el --- flash matching parens a la Zmacs
- ;; Copyright (C) 1995 Noah S. Friedman
- ;; Author: Noah Friedman <[email protected]>
- ;; Maintainer: [email protected]
- ;; Keywords: extensions
- ;; Status: Works in Emacs 19
- ;; Created: 1995-03-03
- ;; LCD Archive Entry:
- ;; flashparen|Noah Friedman|[email protected]|
- ;; flash matching parens a la Zmacs|
- ;; 12-Nov-1995|1.8|~/misc/flashparen.el.gz|
- ;; $Id$
- ;; This program 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.
- ;;
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;;
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program; if not, you can either send email to this
- ;; program's maintainer or write to: The Free Software Foundation,
- ;; Inc.; 675 Massachusetts Avenue; Cambridge, MA 02139, USA.
- ;;; Commentary:
- ;; Loading this makes emacs's paren blinking behavior more closely
- ;; approximate the behavior of Zmacs. It should work under X or on ascii
- ;; terminals.
- ;; Note that in XEmacs, blink-paren.el implements this functionality in a
- ;; more reliable manner, so use that instead of this program.
- ;; To use this program, load this file and do
- ;;
- ;; (flash-matching-mode 1)
- ;;
- ;; It is vitally important that flash-matching-char be the *last* hook on
- ;; post-command-hook. If anything comes after it, it won't get run until
- ;; flash-matching-char is interrupted by user input, which is almost
- ;; certainly undesirable. As a consequence, the function
- ;; flash-matching-mode will make sure this is the case whenever it is run.
- ;; The real solution is to get the flashing function off the command hook
- ;; entirely, but since emacs has no builtin timers there seems to be no
- ;; readily apparent way to accomplish this efficiently.
- ;;; Code:
- (defvar flash-matching-mode nil
- "*If non-nil, then flash corresponding matching character on display.
- It's best to call the function of the same name, since there are other
- things to be done by side effect when enabling this feature.")
- (defvar flash-matching-delay
- (cond (window-system 0.2)
- ((> baud-rate 19200) 0.2)
- ((>= baud-rate 9600) 0.5)
- (t 1))
- "Interval (in seconds) for flash delay.
- This number may be a floating-point number in instances of emacs that
- support floating point arguments to `sit-for'.")
- ;;;###autoload
- (defun flash-matching-mode (&optional prefix)
- "*If non-nil, then flash corresponding matching character on display."
- (interactive "P")
- ;; Make sure flash-matching-char is last on post-command-hook or
- ;; post-command-idle-hook. The latter is defined in Emacs 19.30 and later.
- (let* ((hook (if (boundp 'post-command-idle-hook)
- 'post-command-idle-hook
- 'post-command-hook))
- (h (memq 'flash-matching-char (symbol-value hook))))
- (cond ((null h)
- (add-hook hook 'flash-matching-char 'append))
- ((cdr h)
- (remove-hook hook 'flash-matching-char)
- (add-hook hook 'flash-matching-char 'append))))
- (setq flash-matching-mode
- (>= (prefix-numeric-value prefix) 0))
- (and (interactive-p)
- (if flash-matching-mode
- (message "flash-matching-mode is enabled")
- (message "flash-matching-mode is disabled")))
- flash-matching-mode)
- ;; Verify that an even number of quoting characters precede char at point.
- (defsubst flash-matching-even-quoting-p (point)
- (let ((p (point)))
- (if (= point (point-min))
- t
- (= 1 (logand 1 (- point
- (progn
- (goto-char point)
- (forward-char -1)
- (skip-syntax-backward "/\\" (point-min))
- (prog1
- (point)
- (goto-char p)))))))))
- (defun flash-matching-char ()
- (and flash-matching-mode
- ;; prefix args do strange things with commands; it seems that
- ;; running post-command-hook after invoking one of these is delayed
- ;; until the command is finished, then the hook is run twice.
- ;; It's undesirable to wait for user input twice before returning to
- ;; the top command loop, so skip this the first time.
- (not (memq this-command '(digit-argument universal-argument)))
- ;; keyboard macros run a sequence of interactive commands, each one
- ;; of which will cause a call to post-command-hook; so as long as
- ;; the keyboard macro is still executing, do nothing.
- (null executing-macro)
- (let* ((saved-point (point))
- (cho (char-after saved-point))
- (chc (char-after (1- saved-point)))
- ch)
- (cond
- ((or (and (numberp cho)
- (= (char-syntax cho) ?\()
- (< saved-point (window-end))
- (flash-matching-even-quoting-p saved-point)
- (setq ch cho))
- (and (numberp chc)
- (= (char-syntax chc) ?\))
- (> saved-point (window-start))
- (flash-matching-even-quoting-p saved-point)
- (setq ch chc)))
- (let ((parse-sexp-ignore-comments t)
- ;; this beginning of line is not necessarily the same as
- ;; the one of the matching char `line-beg', below.
- (bol-point (progn
- (beginning-of-line)
- (point)))
- match-point)
- ;; should be at bol now
- ;; If we're inside a comment already, turn off ignoring comments.
- (and comment-start
- (looking-at (concat "^[ \t]*" (regexp-quote comment-start)))
- (setq parse-sexp-ignore-comments nil))
- ;; Find matching paren position, but don't search any further
- ;; than the visible window.
- (save-restriction
- (condition-case ()
- (progn
- (narrow-to-region (window-start) (window-end))
- (cond
- ((= (char-syntax ch) ?\()
- (setq match-point (1- (scan-sexps saved-point 1))))
- (t
- (setq match-point (scan-sexps saved-point -1)))))
- (error nil)))
- ;; Matched char must be the corresponding character for the
- ;; char at the saved point, not just another paired delimiter.
- ;; This can happen when parens and brackets are mismatched,
- ;; for example. Also don't be fooled by things in an
- ;; open/close syntax class but with no defined matching
- ;; character.
- (and match-point
- (flashparen-matching-paren ch)
- (not (= (char-after match-point)
- (flashparen-matching-paren ch)))
- (setq match-point nil))
- ;; match char must be horizontally visible on display.
- ;; Unfortunately we cannot just use pos-visible-in-window-p
- ;; since that returns t for things that are actually off the
- ;; display horizontally.
- (and truncate-lines
- match-point
- (let ((window-hstart (window-hscroll))
- (match-column (progn
- (goto-char match-point)
- (current-column))))
- (if (or (< match-column window-hstart)
- (> match-column (+ window-hstart (window-width))))
- (setq match-point nil))))
- (cond (match-point
- ;; I added this to remove messages left over from
- ;; blink-matching-open, but it also causes messages
- ;; returned by eval-expression, etc. not to appear if
- ;; point is right after a sexp, which is too annoying.
- ;;(message nil)
- (flash-matching-do-flash saved-point match-point))
- (t
- (goto-char saved-point)
- (and chc
- (= (char-syntax chc) ?\))
- ;; blink-matching-open can sometimes signal an
- ;; error if the function name is outside of a
- ;; narrowed region---this can happen in C, perl,
- ;; and other languages where the function label is
- ;; outside the starting block character, depending
- ;; on how one's narrow-to-defun function is defined.
- (condition-case ()
- (blink-matching-open)
- (error nil)))))))))))
- (defun flash-matching-do-flash (flash-matching-opoint flash-matching-mpoint)
- ;; Deactivate the mark now if deactivate-mark is set in transient mark
- ;; mode. Normally the command loop does this itself, but because this
- ;; function is on post-command-hook, deactivation is delayed and causes
- ;; noticable, undesirable effects on the display.
- ;; The only time I've noticed this to be of consequence is when point is
- ;; right before a sexp and you insert a character. Otherwise, this
- ;; function doesn't get called again because after modifying the buffer,
- ;; point is no longer at the beginning or end of a sexp.
- (and transient-mark-mode
- deactivate-mark
- (deactivate-mark))
- (let ((modp (buffer-modified-p))
- (buffer-file-name buffer-file-name)
- (buffer-auto-save-file-name buffer-auto-save-file-name)
- (auto-save-hook (and (boundp 'auto-save-hook)
- auto-save-hook))
- ;; Don't make any undo records while flashing.
- ;; If this is nil, new undo records are appended.
- ;; Setting it to t avoids consing any records at all.
- (buffer-undo-list t)
- (before-change-function nil)
- (after-change-function nil)
- ;; buffer modification messes with transient mark mode.
- (deactivate-mark nil)
- ;; These variables have long names because they may be referenced
- ;; by a function in the auto-save-hook even if the current buffer
- ;; isn't this one (e.g. because a process filter was running at the
- ;; time).
- (flash-matching-buffer (current-buffer))
- (flash-matching-char (char-after flash-matching-mpoint))
- (flash-matching-visible-p t))
- (cond
- ((null buffer-file-name))
- (modp
- ;; If buffer is already modified, do not try to disable locking or
- ;; autosaving, but make sure flashed char is in the buffer exactly
- ;; when autosaving occurs.
- (add-hook 'auto-save-hook
- (function
- (lambda ()
- (or flash-matching-visible-p
- (save-excursion
- (set-buffer flash-matching-buffer)
- (let ((buffer-read-only nil))
- (goto-char flash-matching-mpoint)
- (insert-before-markers-and-inherit
- flash-matching-char)
- (goto-char flash-matching-mpoint)
- (delete-char -1)
- (setq flash-matching-visible-p t)
- (goto-char flash-matching-opoint))))))))
- (t
- ;; Defeat file locking. Don't try this at home, kids!
- (setq buffer-file-name nil)
- (setq buffer-auto-save-file-name nil)))
- ;; We insert-before-markers-and-inherit one char after the one to
- ;; delete, just in case things like window-start, process-mark,
- ;; etc. are at the point of interest.
- (setq flash-matching-mpoint (1+ flash-matching-mpoint))
- (goto-char flash-matching-opoint)
- (unwind-protect
- (while (sit-for flash-matching-delay)
- (let ((buffer-read-only nil))
- (goto-char flash-matching-mpoint)
- ;; Insert char before deleting existing one, to avoid
- ;; complications having to do with overlays and text
- ;; properties on a region.
- (if flash-matching-visible-p
- (insert-before-markers-and-inherit 32)
- (insert-before-markers-and-inherit flash-matching-char))
- (goto-char flash-matching-mpoint)
- (delete-char -1)
- (setq flash-matching-visible-p
- (not flash-matching-visible-p))
- ;; Hide fact of temporary modification during redisplay, if
- ;; buffer was unmodified originally.
- (or modp
- (set-buffer-modified-p modp))
- (goto-char flash-matching-opoint)))
- (or flash-matching-visible-p
- (let ((buffer-read-only nil))
- (goto-char flash-matching-mpoint)
- (insert-before-markers-and-inherit flash-matching-char)
- (goto-char flash-matching-mpoint)
- (delete-char -1)
- (or modp
- (set-buffer-modified-p modp))
- (goto-char flash-matching-opoint))))))
- ;; matching-paren wasn't defined in emacs until version 19.26.
- (if (fboundp 'matching-paren)
- (defalias 'flashparen-matching-paren 'matching-paren)
- (defun flashparen-matching-paren (c)
- (and (memq (char-syntax c) '(?\( ?\)))
- (lsh (aref (syntax-table) c) -8))))
- (provide 'flashparen)
- ;; local variables:
- ;; vc-make-backup-files: t
- ;; end:
- ;;; flashparen.el ends here
|