flashparen.el 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. ;;; flashparen.el --- flash matching parens a la Zmacs
  2. ;; Copyright (C) 1995 Noah S. Friedman
  3. ;; Author: Noah Friedman <[email protected]>
  4. ;; Maintainer: [email protected]
  5. ;; Keywords: extensions
  6. ;; Status: Works in Emacs 19
  7. ;; Created: 1995-03-03
  8. ;; LCD Archive Entry:
  9. ;; flashparen|Noah Friedman|[email protected]|
  10. ;; flash matching parens a la Zmacs|
  11. ;; 12-Nov-1995|1.8|~/misc/flashparen.el.gz|
  12. ;; $Id$
  13. ;; This program is free software; you can redistribute it and/or modify
  14. ;; it under the terms of the GNU General Public License as published by
  15. ;; the Free Software Foundation; either version 2, or (at your option)
  16. ;; any later version.
  17. ;;
  18. ;; This program is distributed in the hope that it will be useful,
  19. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. ;; GNU General Public License for more details.
  22. ;;
  23. ;; You should have received a copy of the GNU General Public License
  24. ;; along with this program; if not, you can either send email to this
  25. ;; program's maintainer or write to: The Free Software Foundation,
  26. ;; Inc.; 675 Massachusetts Avenue; Cambridge, MA 02139, USA.
  27. ;;; Commentary:
  28. ;; Loading this makes emacs's paren blinking behavior more closely
  29. ;; approximate the behavior of Zmacs. It should work under X or on ascii
  30. ;; terminals.
  31. ;; Note that in XEmacs, blink-paren.el implements this functionality in a
  32. ;; more reliable manner, so use that instead of this program.
  33. ;; To use this program, load this file and do
  34. ;;
  35. ;; (flash-matching-mode 1)
  36. ;;
  37. ;; It is vitally important that flash-matching-char be the *last* hook on
  38. ;; post-command-hook. If anything comes after it, it won't get run until
  39. ;; flash-matching-char is interrupted by user input, which is almost
  40. ;; certainly undesirable. As a consequence, the function
  41. ;; flash-matching-mode will make sure this is the case whenever it is run.
  42. ;; The real solution is to get the flashing function off the command hook
  43. ;; entirely, but since emacs has no builtin timers there seems to be no
  44. ;; readily apparent way to accomplish this efficiently.
  45. ;;; Code:
  46. (defvar flash-matching-mode nil
  47. "*If non-nil, then flash corresponding matching character on display.
  48. It's best to call the function of the same name, since there are other
  49. things to be done by side effect when enabling this feature.")
  50. (defvar flash-matching-delay
  51. (cond (window-system 0.2)
  52. ((> baud-rate 19200) 0.2)
  53. ((>= baud-rate 9600) 0.5)
  54. (t 1))
  55. "Interval (in seconds) for flash delay.
  56. This number may be a floating-point number in instances of emacs that
  57. support floating point arguments to `sit-for'.")
  58. ;;;###autoload
  59. (defun flash-matching-mode (&optional prefix)
  60. "*If non-nil, then flash corresponding matching character on display."
  61. (interactive "P")
  62. ;; Make sure flash-matching-char is last on post-command-hook or
  63. ;; post-command-idle-hook. The latter is defined in Emacs 19.30 and later.
  64. (let* ((hook (if (boundp 'post-command-idle-hook)
  65. 'post-command-idle-hook
  66. 'post-command-hook))
  67. (h (memq 'flash-matching-char (symbol-value hook))))
  68. (cond ((null h)
  69. (add-hook hook 'flash-matching-char 'append))
  70. ((cdr h)
  71. (remove-hook hook 'flash-matching-char)
  72. (add-hook hook 'flash-matching-char 'append))))
  73. (setq flash-matching-mode
  74. (>= (prefix-numeric-value prefix) 0))
  75. (and (interactive-p)
  76. (if flash-matching-mode
  77. (message "flash-matching-mode is enabled")
  78. (message "flash-matching-mode is disabled")))
  79. flash-matching-mode)
  80. ;; Verify that an even number of quoting characters precede char at point.
  81. (defsubst flash-matching-even-quoting-p (point)
  82. (let ((p (point)))
  83. (if (= point (point-min))
  84. t
  85. (= 1 (logand 1 (- point
  86. (progn
  87. (goto-char point)
  88. (forward-char -1)
  89. (skip-syntax-backward "/\\" (point-min))
  90. (prog1
  91. (point)
  92. (goto-char p)))))))))
  93. (defun flash-matching-char ()
  94. (and flash-matching-mode
  95. ;; prefix args do strange things with commands; it seems that
  96. ;; running post-command-hook after invoking one of these is delayed
  97. ;; until the command is finished, then the hook is run twice.
  98. ;; It's undesirable to wait for user input twice before returning to
  99. ;; the top command loop, so skip this the first time.
  100. (not (memq this-command '(digit-argument universal-argument)))
  101. ;; keyboard macros run a sequence of interactive commands, each one
  102. ;; of which will cause a call to post-command-hook; so as long as
  103. ;; the keyboard macro is still executing, do nothing.
  104. (null executing-macro)
  105. (let* ((saved-point (point))
  106. (cho (char-after saved-point))
  107. (chc (char-after (1- saved-point)))
  108. ch)
  109. (cond
  110. ((or (and (numberp cho)
  111. (= (char-syntax cho) ?\()
  112. (< saved-point (window-end))
  113. (flash-matching-even-quoting-p saved-point)
  114. (setq ch cho))
  115. (and (numberp chc)
  116. (= (char-syntax chc) ?\))
  117. (> saved-point (window-start))
  118. (flash-matching-even-quoting-p saved-point)
  119. (setq ch chc)))
  120. (let ((parse-sexp-ignore-comments t)
  121. ;; this beginning of line is not necessarily the same as
  122. ;; the one of the matching char `line-beg', below.
  123. (bol-point (progn
  124. (beginning-of-line)
  125. (point)))
  126. match-point)
  127. ;; should be at bol now
  128. ;; If we're inside a comment already, turn off ignoring comments.
  129. (and comment-start
  130. (looking-at (concat "^[ \t]*" (regexp-quote comment-start)))
  131. (setq parse-sexp-ignore-comments nil))
  132. ;; Find matching paren position, but don't search any further
  133. ;; than the visible window.
  134. (save-restriction
  135. (condition-case ()
  136. (progn
  137. (narrow-to-region (window-start) (window-end))
  138. (cond
  139. ((= (char-syntax ch) ?\()
  140. (setq match-point (1- (scan-sexps saved-point 1))))
  141. (t
  142. (setq match-point (scan-sexps saved-point -1)))))
  143. (error nil)))
  144. ;; Matched char must be the corresponding character for the
  145. ;; char at the saved point, not just another paired delimiter.
  146. ;; This can happen when parens and brackets are mismatched,
  147. ;; for example. Also don't be fooled by things in an
  148. ;; open/close syntax class but with no defined matching
  149. ;; character.
  150. (and match-point
  151. (flashparen-matching-paren ch)
  152. (not (= (char-after match-point)
  153. (flashparen-matching-paren ch)))
  154. (setq match-point nil))
  155. ;; match char must be horizontally visible on display.
  156. ;; Unfortunately we cannot just use pos-visible-in-window-p
  157. ;; since that returns t for things that are actually off the
  158. ;; display horizontally.
  159. (and truncate-lines
  160. match-point
  161. (let ((window-hstart (window-hscroll))
  162. (match-column (progn
  163. (goto-char match-point)
  164. (current-column))))
  165. (if (or (< match-column window-hstart)
  166. (> match-column (+ window-hstart (window-width))))
  167. (setq match-point nil))))
  168. (cond (match-point
  169. ;; I added this to remove messages left over from
  170. ;; blink-matching-open, but it also causes messages
  171. ;; returned by eval-expression, etc. not to appear if
  172. ;; point is right after a sexp, which is too annoying.
  173. ;;(message nil)
  174. (flash-matching-do-flash saved-point match-point))
  175. (t
  176. (goto-char saved-point)
  177. (and chc
  178. (= (char-syntax chc) ?\))
  179. ;; blink-matching-open can sometimes signal an
  180. ;; error if the function name is outside of a
  181. ;; narrowed region---this can happen in C, perl,
  182. ;; and other languages where the function label is
  183. ;; outside the starting block character, depending
  184. ;; on how one's narrow-to-defun function is defined.
  185. (condition-case ()
  186. (blink-matching-open)
  187. (error nil)))))))))))
  188. (defun flash-matching-do-flash (flash-matching-opoint flash-matching-mpoint)
  189. ;; Deactivate the mark now if deactivate-mark is set in transient mark
  190. ;; mode. Normally the command loop does this itself, but because this
  191. ;; function is on post-command-hook, deactivation is delayed and causes
  192. ;; noticable, undesirable effects on the display.
  193. ;; The only time I've noticed this to be of consequence is when point is
  194. ;; right before a sexp and you insert a character. Otherwise, this
  195. ;; function doesn't get called again because after modifying the buffer,
  196. ;; point is no longer at the beginning or end of a sexp.
  197. (and transient-mark-mode
  198. deactivate-mark
  199. (deactivate-mark))
  200. (let ((modp (buffer-modified-p))
  201. (buffer-file-name buffer-file-name)
  202. (buffer-auto-save-file-name buffer-auto-save-file-name)
  203. (auto-save-hook (and (boundp 'auto-save-hook)
  204. auto-save-hook))
  205. ;; Don't make any undo records while flashing.
  206. ;; If this is nil, new undo records are appended.
  207. ;; Setting it to t avoids consing any records at all.
  208. (buffer-undo-list t)
  209. (before-change-function nil)
  210. (after-change-function nil)
  211. ;; buffer modification messes with transient mark mode.
  212. (deactivate-mark nil)
  213. ;; These variables have long names because they may be referenced
  214. ;; by a function in the auto-save-hook even if the current buffer
  215. ;; isn't this one (e.g. because a process filter was running at the
  216. ;; time).
  217. (flash-matching-buffer (current-buffer))
  218. (flash-matching-char (char-after flash-matching-mpoint))
  219. (flash-matching-visible-p t))
  220. (cond
  221. ((null buffer-file-name))
  222. (modp
  223. ;; If buffer is already modified, do not try to disable locking or
  224. ;; autosaving, but make sure flashed char is in the buffer exactly
  225. ;; when autosaving occurs.
  226. (add-hook 'auto-save-hook
  227. (function
  228. (lambda ()
  229. (or flash-matching-visible-p
  230. (save-excursion
  231. (set-buffer flash-matching-buffer)
  232. (let ((buffer-read-only nil))
  233. (goto-char flash-matching-mpoint)
  234. (insert-before-markers-and-inherit
  235. flash-matching-char)
  236. (goto-char flash-matching-mpoint)
  237. (delete-char -1)
  238. (setq flash-matching-visible-p t)
  239. (goto-char flash-matching-opoint))))))))
  240. (t
  241. ;; Defeat file locking. Don't try this at home, kids!
  242. (setq buffer-file-name nil)
  243. (setq buffer-auto-save-file-name nil)))
  244. ;; We insert-before-markers-and-inherit one char after the one to
  245. ;; delete, just in case things like window-start, process-mark,
  246. ;; etc. are at the point of interest.
  247. (setq flash-matching-mpoint (1+ flash-matching-mpoint))
  248. (goto-char flash-matching-opoint)
  249. (unwind-protect
  250. (while (sit-for flash-matching-delay)
  251. (let ((buffer-read-only nil))
  252. (goto-char flash-matching-mpoint)
  253. ;; Insert char before deleting existing one, to avoid
  254. ;; complications having to do with overlays and text
  255. ;; properties on a region.
  256. (if flash-matching-visible-p
  257. (insert-before-markers-and-inherit 32)
  258. (insert-before-markers-and-inherit flash-matching-char))
  259. (goto-char flash-matching-mpoint)
  260. (delete-char -1)
  261. (setq flash-matching-visible-p
  262. (not flash-matching-visible-p))
  263. ;; Hide fact of temporary modification during redisplay, if
  264. ;; buffer was unmodified originally.
  265. (or modp
  266. (set-buffer-modified-p modp))
  267. (goto-char flash-matching-opoint)))
  268. (or flash-matching-visible-p
  269. (let ((buffer-read-only nil))
  270. (goto-char flash-matching-mpoint)
  271. (insert-before-markers-and-inherit flash-matching-char)
  272. (goto-char flash-matching-mpoint)
  273. (delete-char -1)
  274. (or modp
  275. (set-buffer-modified-p modp))
  276. (goto-char flash-matching-opoint))))))
  277. ;; matching-paren wasn't defined in emacs until version 19.26.
  278. (if (fboundp 'matching-paren)
  279. (defalias 'flashparen-matching-paren 'matching-paren)
  280. (defun flashparen-matching-paren (c)
  281. (and (memq (char-syntax c) '(?\( ?\)))
  282. (lsh (aref (syntax-table) c) -8))))
  283. (provide 'flashparen)
  284. ;; local variables:
  285. ;; vc-make-backup-files: t
  286. ;; end:
  287. ;;; flashparen.el ends here