summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/cursor-sensor.el
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2015-04-13 15:51:15 -0400
committerStefan Monnier <monnier@iro.umontreal.ca>2015-04-13 15:51:15 -0400
commit84e0b7dad6f1a8e53261f9b96f5a9080fea681a4 (patch)
tree99dd57860e7fa4c01de3e4032388db3a1d1f0518 /lisp/emacs-lisp/cursor-sensor.el
parentb430d2a836f5a1bc8d8ae996893bb8338fc4780b (diff)
downloademacs-84e0b7dad6f1a8e53261f9b96f5a9080fea681a4.tar.gz
Deprecate `intangible' and `point-entered' properties
* lisp/emacs-lisp/cursor-sensor.el: New file. * lisp/simple.el (pre-redisplay-functions): New hook. (redisplay--pre-redisplay-functions): New function. (pre-redisplay-function): Use it. (minibuffer-avoid-prompt): Mark obsolete. (redisplay--update-region-highlight): Adapt it to work as a function on pre-redisplay-functions. * lisp/cus-start.el (minibuffer-prompt-properties--setter): New fun. (minibuffer-prompt-properties): Use it. Use cursor-intangible rather than point-entered to make the prompt intangible. * lisp/forms.el: Move `provide' calls to the end. (forms-mode): Don't use `run-hooks' on a local var. (forms--make-format, forms--make-format-elt-using-text-properties): Use cursor-intangible rather than `intangible'. (forms-mode): Enable cursor-intangible-mode. * lisp/isearch.el (isearch-mode): Use defvar-local. (cursor-sensor-inhibit): Declare. (isearch-mode): Set cursor-sensor-inhibit. (isearch-done): Set it back. (isearch-open-overlay-temporary, isearch-open-necessary-overlays) (isearch-close-unnecessary-overlays): Don't bother with `intangible' any more. * lisp/ses.el (ses-localvars): Remove `mode-line-process'. (ses-sym-rowcol, ses-cell-value, ses-col-width, ses-col-printer): Add Edebug spec. (ses-goto-print, ses-print-cell, ses-adjust-print-width) (ses-goto-data, ses-setup, ses-copy-region): Don't let-bind inhibit-point-motion-hooks any more. (ses--cell-at-pos, ses--curcell): New functions, extracted from ses-set-curcell. (ses-set-curcell): Use them. (ses-print-cell, ses-setup): Use cursor-intangible instead of `intangible'. Make sure cursor-intangible isn't sticky at BOB. (ses-print-cell-new-width, ses-reprint-all, ses-recalculate-all): Use ses--cell-at-pos. (ses--mode-line-process, ses--cursor-sensor-highlight): New functions, extracted from ses-command-hook. Make them work with multiple windows displaying the same buffer. (ses-mode): Use them via mode-line-process and pre-redisplay-functions. Enable cursor-intangible-mode. (ses-command-hook): Remove cell highlight and mode-line update code. (ses-forward-or-insert, ses-copy-region-helper, ses-sort-column): Update for new name of text-property holding the cell name. (ses-rename-cell): Don't mess with mode-line-process. * lisp/erc/erc-stamp.el (erc-add-timestamp): Use the new cursor-sensor-functions property instead of point-entered. (erc-insert-timestamp-right, erc-format-timestamp): Use cursor-intangible rather than `intangible'. (erc-munge-invisibility-spec): Use add-to-invisibility-spec and remove-from-invisibility-spec. Enable cursor-intangible-mode and cursor-sensor-mode if needed. (erc-echo-timestamp): Adapt to calling convention of cursor-sensor-functions. (erc-insert-timestamp-right): Remove unused vars `current-window' and `indent'. * lisp/gnus/gnus-group.el (gnus-tmp-*): Declare. (gnus-update-group-mark-positions): Remove unused `topic' var. (gnus-group-insert-group-line): Remove unused var `header'. (gnus-group--setup-tool-bar-update): New function. (gnus-group-insert-group-line): Use it. (gnus-group-update-eval-form): Declare local dynamically-bound variables. (gnus-group-unsubscribe-group): Use \` and \' to match string bounds. * lisp/gnus/gnus-topic.el (gnus-topic-jump-to-topic) (gnus-group-prepare-topics, gnus-topic-update-topic) (gnus-topic-change-level, gnus-topic-catchup-articles) (gnus-topic-remove-group, gnus-topic-delete, gnus-topic-indent): Use inhibit-read-only. (gnus-topic-prepare-topic): Use gnus-group--setup-tool-bar-update. (gnus-topic-mode): Use define-minor-mode and derived-mode-p. * lisp/textmodes/reftex-index.el (reftex-display-index): Use cursor-intangible-mode if available. (reftex-index-post-command-hook): Check cursor-intangible. * lisp/textmodes/reftex-toc.el (reftex-toc): Use cursor-intangible-mode if available. (reftex-toc-recenter, reftex-toc-post-command-hook): Check cursor-intangible. * lisp/textmodes/sgml-mode.el: Use lexical-binding. (sgml-tag): Use cursor-sensor-functions instead of point-entered. (sgml-tags-invisible): Use with-silent-modifications and inhibit-read-only. Enable cursor-sensor-mode. (sgml-cursor-sensor): Rename from sgml-point-entered and adjust to calling convention of cursor-sensor-functions. * lisp/textmodes/table.el (table-cell-map-hook, table-load-hook) (table-point-entered-cell-hook, table-point-left-cell-hook): Don't autoload. (table-cell-entered-state): Remove var. (table--put-cell-point-entered/left-property) (table--remove-cell-properties): Use cursor-sensor-functions rather than point-entered/left. (table--point-entered/left-cell-function): Merge table--point-entered-cell-function and table--point-left-cell-function and adjust to calling convention of cursor-sensor-functions.
Diffstat (limited to 'lisp/emacs-lisp/cursor-sensor.el')
-rw-r--r--lisp/emacs-lisp/cursor-sensor.el180
1 files changed, 180 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/cursor-sensor.el b/lisp/emacs-lisp/cursor-sensor.el
new file mode 100644
index 00000000000..1d1780baed0
--- /dev/null
+++ b/lisp/emacs-lisp/cursor-sensor.el
@@ -0,0 +1,180 @@
+;;; cursor-sensor.el --- React to cursor movement -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015 Free Software Foundation, Inc.
+
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+;; Keywords:
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package implements the `cursor-intangible' property, which is
+;; meant to replace the old `intangible' property. To use it, just enable the
+;; `cursor-intangible-mode', after which this package will move point away from
+;; any position that has a non-nil `cursor-intangible' property. This is only
+;; done just before redisplay happens, contrary to the old `intangible'
+;; property which was done at a much lower level.
+
+;;; Code:
+
+(defvar cursor-sensor-inhibit nil)
+
+(defun cursor-sensor--intangible-p (pos)
+ (let ((p (get-pos-property pos 'cursor-intangible)))
+ (if p
+ (let (a b)
+ (if (and (setq a (get-char-property pos 'cursor-intangible))
+ (setq b (if (> pos (point-min))
+ (get-char-property (1- pos) 'cursor-intangible)))
+ (not (eq a b)))
+ ;; If we're right between two different intangible thingies,
+ ;; we can stop here. This is not quite consistent with the
+ ;; interpretation of "if it's sticky, then this boundary is
+ ;; itself intangible", but it's convenient (and it better matches
+ ;; the behavior of `intangible', making it easier to port code).
+ nil p))
+ p)))
+
+(defun cursor-sensor-tangible-pos (curpos window &optional second-chance)
+ (let ((newpos curpos))
+ (when (cursor-sensor--intangible-p newpos)
+ (let ((oldpos (window-parameter window 'cursor-intangible--last-point)))
+ (cond
+ ((or (and (integerp oldpos) (< oldpos newpos))
+ (eq newpos (point-min)))
+ (while
+ (when (< newpos (point-max))
+ (setq newpos
+ (if (get-char-property newpos 'cursor-intangible)
+ (next-single-char-property-change
+ newpos 'cursor-intangible nil (point-max))
+ (1+ newpos)))
+ (cursor-sensor--intangible-p newpos))))
+ (t ;; (>= oldpos newpos)
+ (while
+ (when (> newpos (point-min))
+ (setq newpos
+ (if (get-char-property (1- newpos) 'cursor-intangible)
+ (previous-single-char-property-change
+ newpos 'cursor-intangible nil (point-min))
+ (1- newpos)))
+ (cursor-sensor--intangible-p newpos)))))
+ (if (not (and (or (eq newpos (point-min)) (eq newpos (point-max)))
+ (cursor-sensor--intangible-p newpos)))
+ ;; All clear, we're good to go.
+ newpos
+ ;; We're still on an intangible position because we bumped
+ ;; into an intangible BOB/EOB: try to move in the other direction.
+ (if second-chance
+ ;; Actually, we tried already and that failed!
+ curpos
+ (cursor-sensor-tangible-pos newpos window 'second-chance)))))))
+
+(defun cursor-sensor-move-to-tangible (window)
+ (let* ((curpos (window-point window))
+ (newpos (cursor-sensor-tangible-pos curpos window)))
+ (when newpos (set-window-point window newpos))
+ (set-window-parameter window 'cursor-intangible--last-point
+ (or newpos curpos))))
+
+(defun cursor-sensor--move-to-tangible (window)
+ (unless cursor-sensor-inhibit
+ (cursor-sensor-move-to-tangible window)))
+
+;;;###autoload
+(define-minor-mode cursor-intangible-mode
+ "Keep cursor outside of any `cursor-intangible' text property."
+ nil nil nil
+ (if cursor-intangible-mode
+ (add-hook 'pre-redisplay-functions #'cursor-sensor--move-to-tangible
+ nil t)
+ (remove-hook 'pre-redisplay-functions #'cursor-sensor--move-to-tangible t)))
+
+;;; Detect cursor movement.
+
+(defun cursor-sensor--detect (window)
+ (unless cursor-sensor-inhibit
+ (let* ((point (window-point window))
+ ;; It's often desirable to make the cursor-sensor-functions property
+ ;; non-sticky on both ends, but that means get-pos-property might
+ ;; never see it.
+ (new (or (get-char-property point 'cursor-sensor-functions)
+ (unless (bobp)
+ (get-char-property (1- point) 'cursor-sensor-functions))))
+ (old (window-parameter window 'cursor-sensor--last-state))
+ (oldposmark (car old))
+ (oldpos (or (if oldposmark (marker-position oldposmark))
+ (point-min)))
+ (start (min oldpos point))
+ (end (max oldpos point)))
+ (unless (or (null old) (eq (marker-buffer oldposmark) (current-buffer)))
+ ;; `window' does not display the same buffer any more!
+ (setcdr old nil))
+ (if (or (and (null new) (null (cdr old)))
+ (and (eq new (cdr old))
+ (eq (next-single-property-change
+ start 'cursor-sensor-functions nil end)
+ end)))
+ ;; Clearly nothing to do.
+ nil
+ ;; Maybe something to do. Let's see exactly what needs to run.
+ (let* ((missing-p
+ (lambda (f)
+ "Non-nil if F is missing somewhere between START and END."
+ (let ((pos start)
+ (missing nil))
+ (while (< pos end)
+ (setq pos (next-single-property-change
+ pos 'cursor-sensor-functions
+ nil end))
+ (unless (memq f (get-char-property
+ pos 'cursor-sensor-functions))
+ (setq missing t)))
+ missing))))
+ (dolist (f (cdr old))
+ (unless (and (memq f new) (not (funcall missing-p f)))
+ (funcall f window oldpos 'left)))
+ (dolist (f new)
+ (unless (and (memq f (cdr old)) (not (funcall missing-p f)))
+ (funcall f window oldpos 'entered)))))
+
+ ;; Remember current state for next time.
+ ;; Re-read cursor-sensor-functions since the functions may have moved
+ ;; window-point!
+ (if old
+ (progn (move-marker (car old) point)
+ (setcdr old new))
+ (set-window-parameter window 'cursor-sensor--last-state
+ (cons (copy-marker point) new))))))
+
+;;;###autoload
+(define-minor-mode cursor-sensor-mode
+ "Handle the `cursor-sensor-functions' text property.
+This property should hold a list of functions which react to the motion
+of the cursor. They're called with three arguments (WINDOW OLDPOS DIR)
+where WINDOW is the affected window, OLDPOS is the last known position of
+the cursor and DIR can be `left' or `entered' depending on whether the cursor is
+entering the area covered by the text-property property or leaving it."
+ nil nil nil
+ (if cursor-sensor-mode
+ (add-hook 'pre-redisplay-functions #'cursor-sensor--detect
+ nil t)
+ (remove-hook 'pre-redisplay-functions #'cursor-sensor--detect
+ t)))
+
+(provide 'cursor-sensor)
+;;; cursor-sensor.el ends here