changeset 53203:14b91211c987

New file.
author Luc Teirlinck <teirllm@auburn.edu>
date Sun, 30 Nov 2003 17:32:54 +0000
parents 8d8fae1e281a
children 0fd777e399e3
files lisp/help-at-pt.el
diffstat 1 files changed, 356 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/help-at-pt.el	Sun Nov 30 17:32:54 2003 +0000
@@ -0,0 +1,356 @@
+;;; help-at-pt.el --- local help through the keyboard
+
+;; Copyright (C) 2003 Free Software Foundation, Inc.
+
+;; Author: Luc Teirlinck <teirllm@auburn.edu>
+;; Keywords: help
+
+;; 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 2, 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; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; This file contains functionality to make the help provided by the
+;; help-echo text or overlay property available to the keyboard user.
+;; It also supports a more keyboard oriented alternative to
+;; `help-echo', namely a new text or overlay property `kbd-help'.
+;;
+;; It provides facilities to access the local help available at point
+;; either on demand, using the command `display-local-help', or
+;; automatically after a suitable idle time, through the customizable
+;; variable `help-at-pt-display-when-idle'.
+;;
+;; You can get a more global overview of the local help available in
+;; the buffer, using the commands `scan-buf-next-region' and
+;; `scan-buf-previous-region', which move to the start of the next or
+;; previous region with available local help and print the help found
+;; there.
+;;
+;; You do not have to do anything special to use the functionality
+;; provided by this file, because all important functions autoload.
+
+;;; Code:
+
+(defgroup help-at-pt nil
+  "Features for displaying local help."
+  :group 'help
+  :version "21.4")
+
+;;;###autoload
+(defun help-at-pt-string (&optional kbd)
+  "Return the help-echo string at point.
+Normally, the string produced by the `help-echo' text or overlay
+property, or nil, is returned.
+If KBD is non-nil, `kbd-help' is used instead, and any
+`help-echo' property is ignored.  In this case, the return value
+can also be t, if that is the value of the `kbd-help' property."
+  (let* ((prop (if kbd 'kbd-help 'help-echo))
+	 (pair (get-char-property-and-overlay (point) prop))
+	 (val (car pair))
+	 (ov (cdr pair)))
+    (if (functionp val)
+	(funcall val (selected-window) (if ov ov (current-buffer)) (point))
+      (eval val))))
+
+;;;###autoload
+(defun help-at-pt-kbd-string ()
+  "Return the keyboard help string at point.
+If the `kbd-help' text or overlay property at point produces a
+string, return it.  Otherwise, use the `help-echo' property.  If
+this produces no string either, return nil."
+  (let ((kbd (help-at-pt-string t))
+	(echo (help-at-pt-string)))
+    (if (and kbd (not (eq kbd t))) kbd echo)))
+
+;;;###autoload
+(defun display-local-help (&optional arg)
+  "Display local help in the echo area.
+This displays a short help message, namely the string produced by
+the `kbd-help' property at point.  If `kbd-help' does not produce
+a string, but the `help-echo' property does, then that string is
+printed instead.
+
+A numeric argument ARG prevents display of a message in case
+there is no help.  While ARG can be used interactively, it is
+mainly meant for use from Lisp."
+  (interactive "P")
+  (let ((help (help-at-pt-kbd-string)))
+    (if help
+	(message "%s" help)
+      (if (not arg) (message "No local help at point")))))
+
+(defcustom help-at-pt-timer-delay 1
+  "*Delay before displaying local help.
+This is used if `help-at-pt-display-when-idle' is enabled.
+The value may be an integer or floating point number.
+
+If a timer is already active, there are two ways to make the new
+value take effect immediately.  After setting the value, you can
+first call `help-at-pt-cancel-timer' and then set a new timer
+with `help-at-pt-set-timer' .  Alternatively, you can set this
+variable through Custom.  This will not set a timer if none is
+active, but if one is already active, Custom will make it use the
+new value."
+  :group 'help-at-pt
+  :type 'number
+  :set (lambda (variable value)
+	 (set-default variable value)
+	 (when (and (boundp 'help-at-pt-timer) help-at-pt-timer)
+	   (timer-set-idle-time help-at-pt-timer value t))))
+
+(defvar help-at-pt-timer nil
+  "Non-nil means that a timer is set that checks for local help.
+If non-nil, this is the value returned by the call of
+`run-with-idle-timer' that set that timer.  This variable is used
+internally to enable `help-at-pt-display-when-idle'.  Do not set it
+yourself.")
+
+;;;###autoload
+(defun help-at-pt-cancel-timer ()
+  "Cancel any timer set by `help-at-pt-set-timer'.
+This disables `help-at-pt-display-when-idle'."
+  (interactive)
+  (let ((inhibit-quit t))
+    (when help-at-pt-timer
+      (cancel-timer help-at-pt-timer)
+      (setq help-at-pt-timer nil))))
+
+;;;###autoload
+(defun help-at-pt-set-timer ()
+  "Enable `help-at-pt-display-when-idle'.
+This is done by setting a timer, if none is currently active."
+  (interactive)
+  (unless help-at-pt-timer
+    (setq help-at-pt-timer
+	  (run-with-idle-timer
+	   help-at-pt-timer-delay t #'help-at-pt-maybe-display))))
+
+;;;###autoload
+(defcustom help-at-pt-display-when-idle 'never
+  "*Automatically show local help on point-over.
+If the value is t, the string obtained from any `kbd-help' or
+`help-echo' property at point is automatically printed in the
+echo area, if nothing else is already displayed there, or after a
+quit.  If both `kbd-help' and `help-echo' produce help strings,
+`kbd-help' is used.  If the value is a list, the help only gets
+printed if there is a text or overlay property at point that is
+included in this list.  Suggested properties are `keymap',
+`local-map', `button' and `kbd-help'.  Any value other than t or
+a non-empty list disables the feature.
+
+This variable only takes effect after a call to
+`help-at-pt-set-timer'.  The help gets printed after Emacs has
+been idle for `help-at-pt-timer-delay' seconds.  You can call
+`help-at-pt-cancel-timer' to cancel the timer set by, and the
+effect of, `help-at-pt-set-timer'.
+
+When this variable is set through Custom, `help-at-pt-set-timer'
+is called automatically, unless the value is `never', in which
+case `help-at-pt-cancel-timer' is called.  Specifying an empty
+list of properties through Custom will set the timer, thus
+enabling buffer local values.  It sets the actual value to nil.
+Thus, Custom distinguishes between a nil value and other values
+that disable the feature, which Custom identifies with `never'.
+The default is `never'."
+  :group 'help-at-pt
+  :type '(choice  (const :tag "Always"
+			 :format "%t\n%h"
+			 :doc
+			 "This choice can get noisy.
+The text printed from the `help-echo' property is often only
+relevant when using the mouse.  If you mind about too many
+messages getting printed in the echo area, use \"In certain
+situations\".  See the documentation there for more information."
+			 t)
+		  (repeat :tag "In certain situations"
+			  ;; unless we specify 0 offset the doc string
+			  ;; for this choice gets indented very
+			  ;; differently than for the other two
+			  ;; choices, when "More" is selected.
+			  :offset 0
+			  :format "%{%t%}:\n%v%i\n%h"
+			  :doc
+			  "This choice lets you specify a list of \
+text properties.
+Presence of any of these properties will trigger display of
+available local help on point-over.
+If you use this alternative through Custom without listing any
+properties, a timer will be set anyway.  This will enable buffer
+local values.  Use \"Never\" if you do not want a timer to be set.
+
+Suggested properties:
+The `keymap' and `local-map' properties change keybindings in
+parts of the buffer.  Some of these keymaps are mode independent
+and are not mentioned in the mode documentation.  Hence, the help
+text is likely to be useful.
+Specifying `button' is relevant in Custom and similar buffers.
+In these buffers, most, but not all, of the text shown this way is
+available by default when using tab, but not on regular point-over.
+The presence of a `kbd-help' property guarantees that non mouse
+specific help is available."
+			  :value (keymap local-map button kbd-help)
+			  symbol)
+		  (other :tag "Never"
+			 :format "%t\n%h"
+			 :doc
+			 "This choice normally disables buffer local values.
+If you choose this value through Custom and a timer checking for
+local help is currently active, it will be canceled.  No new
+timer will be set.  Call `help-at-pt-set-timer' after choosing
+this option, or use \"In certain situations\" and specify no text
+properties, to enable buffer local values."
+			 never))
+  :initialize 'custom-initialize-default
+  :set #'(lambda (variable value)
+	   (set-default variable value)
+	   (if (eq value 'never)
+	       (help-at-pt-cancel-timer)
+	     (help-at-pt-set-timer)))
+  :set-after '(help-at-pt-timer-delay)
+  :require 'help-at-pt)
+
+;; Function for use in `help-at-pt-set-timer'.
+(defun help-at-pt-maybe-display ()
+  (and (or (eq help-at-pt-display-when-idle t)
+	   (and (consp help-at-pt-display-when-idle)
+		(catch 'found
+		  (dolist (prop help-at-pt-display-when-idle)
+		    (if (get-char-property (point) prop)
+			(throw 'found t))))))
+       (or (not (current-message))
+	   (string= (current-message) "Quit"))
+       (display-local-help t)))
+
+;;;###autoload
+(defun scan-buf-move-to-region (prop &optional arg hook)
+  "Go to the start of the next region with non-nil PROP property.
+Then run HOOK, which should be a quoted symbol that is a normal
+hook.variable, or an expression evaluating to such a symbol.
+Adjacent areas with different non-nil PROP properties are
+considered different regions.
+
+With numeric argument ARG, move to the start of the ARGth next
+such region, then run HOOK.  If ARG is negative, move backward.
+If point is already in a region, then that region does not count
+toward ARG.  If ARG is 0 and point is inside a region, move to
+the start of that region.  If ARG is 0 and point is not in a
+region, print a message to that effect, but do not move point and
+do not run HOOK.  If there are not enough regions to move over,
+an error results and the number of available regions is mentioned
+in the error message.  Point is not moved and HOOK is not run."
+  (cond ((> arg 0)
+	 (if (= (point) (point-max))
+	     (error "No further `%s' regions" prop))
+	 (let ((pos (point)))
+	   (dotimes (x arg)
+	     (setq pos (next-single-char-property-change pos prop))
+	     (unless (get-char-property pos prop)
+	       (setq pos (next-single-char-property-change pos prop))
+	       (unless (get-char-property pos prop)
+		 (cond ((= x 0)
+			(error "No further `%s' regions" prop))
+		       ((= x 1)
+			(error "There is only one further `%s' region" prop))
+		       (t
+			(error
+			 "There are only %d further `%s' regions"
+			 x prop))))))
+	   (goto-char pos)
+	   (run-hooks hook)))
+	((= arg 0)
+	 (let ((val (get-char-property (point) prop)))
+	   (cond ((not val)
+		  (message "Point is not in a `%s' region" prop))
+		 ((eq val (get-char-property (1- (point)) prop))
+		  (goto-char
+		   (previous-single-char-property-change (point) prop))
+		  (run-hooks hook))
+		 (t (run-hooks hook)))))
+	((< arg 0)
+	 (let ((pos (point)) (val (get-char-property (point) prop)))
+	   (and val
+		(eq val (get-char-property (1- pos) prop))
+		(setq pos
+		      (previous-single-char-property-change pos prop)))
+	   (if (= pos (point-min))
+	       (error "No prior `%s' regions" prop))
+	   (dotimes (x (- arg))
+	     (setq pos (previous-single-char-property-change pos prop))
+	     (unless (get-char-property pos prop)
+	       (setq pos (previous-single-char-property-change pos prop))
+	       (unless (get-char-property pos prop)
+		 (cond ((= x 0)
+			(error "No prior `%s' regions" prop))
+		       ((= x 1)
+			(error "There is only one prior `%s' region" prop))
+		       (t
+			(error "There are only %d prior `%s' regions"
+			       x prop))))))
+	   (goto-char pos)
+	   (run-hooks hook)))))
+
+;; To be moved to a different file and replaced by a defcustom in a
+;; future version.
+(defvar scan-buf-move-hook '(display-local-help)
+  "Normal hook run by `scan-buf-next-region'.
+Also used by `scan-buf-previous-region'.  The hook is run after
+positioning point.")
+
+;;;###autoload
+(defun scan-buf-next-region (&optional arg)
+  "Go to the start of the next region with non-nil help-echo.
+Print the help found there using `display-local-help'.  Adjacent
+areas with different non-nil help-echo properties are considered
+different regions.
+
+With numeric argument ARG, move to the start of the ARGth next
+help-echo region.  If ARG is negative, move backward.  If point
+is already in a help-echo region, then that region does not count
+toward ARG.  If ARG is 0 and point is inside a help-echo region,
+move to the start of that region.  If ARG is 0 and point is not
+in such a region, just print a message to that effect.  If there
+are not enough regions to move over, an error results and the
+number of available regions is mentioned in the error message.
+
+A potentially confusing subtlety is that point can be in a
+help-echo region without any local help being available.  This is
+because `help-echo' can be a function evaluating to nil.  This
+rarely happens in practice."
+  (interactive "p")
+  (scan-buf-move-to-region 'help-echo arg 'scan-buf-move-hook))
+
+;;;###autoload
+(defun scan-buf-previous-region (&optional arg)
+  "Go to the start of the previous region with non-nil help-echo.
+Print the help found there using `display-local-help'.  Adjacent
+areas with different non-nil help-echo properties are considered
+different regions.  With numeric argument ARG, behaves like
+`scan-buf-next-region' with argument -ARG.."
+  (interactive "p")
+  (scan-buf-move-to-region 'help-echo (- arg) 'scan-buf-move-hook))
+
+(defvar help-at-pt-unload-hook '(help-at-pt-cancel-timer)
+  "Normal hook run when `help-at-pt' is unloaded.")
+
+;; Suggested key bindings:
+;;
+;; (global-set-key [C-tab] 'scan-buf-next-region)
+;; (global-set-key [C-M-tab] 'scan-buf-previous-region)
+
+(provide 'help-at-pt)
+
+;;; help-at-pt.el ends here