
* 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.
180 lines
7.8 KiB
EmacsLisp
180 lines
7.8 KiB
EmacsLisp
;;; 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
|