unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: martin rudalics <rudalics@gmx.at>
To: "João Távora" <joaotavora@gmail.com>
Cc: 43609@debbugs.gnu.org
Subject: bug#43609: 28.0.50; eldoc-documentation-function
Date: Wed, 30 Sep 2020 19:33:16 +0200	[thread overview]
Message-ID: <3fa6b315-7fc0-06ee-81e9-b68d164aec1b@gmx.at> (raw)
In-Reply-To: <87r1qjjppu.fsf@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 7645 bytes --]

 >> M-: (funcall eldoc-documentation-function)
 >
 > You're not supposed to call the that function in your programs and
 > neither were you in Emacs 27, much in the same way you normally don't
 > call, say, adaptive-fill-function, add-log-file-name-function or many
 > others.  That you _could_ do it in your special circumstances was
 > incidental, though apparently useful for your third-party extension,
 > which I wasn't aware of.
 >
 > Furthermore, calling eldoc-documentation-function directly in Emacs 27
 > simply doesn't work with a lot of major-modes/extensions that use
 > `eldoc-mode': ElPy, SLIME, SLY, Eldoc, and probably many others. If you
 > call the function directly in those modes, it will quite likely return
 > something other than the desired string, which will potentially appear
 > in the echo area only some time after.

Citing again the Emacs 27 doc-string for 'eldoc-documentation-function'

   Function to call to return doc string.
   The function of no args should return a one-line string for displaying
   doc about a function etc.  appropriate to the context around point.
   It should return nil if there’s no doc appropriate for the context.
   Typically doc is returned if point is on a function-like name or in its
   arg list.

   The result is used as is, so the function must explicitly handle
   the variables ‘eldoc-argument-case’ and ‘eldoc-echo-area-use-multiline-p’,
   and the face ‘eldoc-highlight-function-argument’, if they are to have any
   effect.

so if it did not work for the modes you cite, the doc-string should have
warned about that fact earlier.  While this is certainly not your fault,
changing the semantics of that variable for elisp-mode with the argument
that it did not work for other modes was not a user-friendly step.

Personally I use eldoc for two major modes only: 'emacs-lisp-mode' and
'c-mode' (in a crippled way via 'c-eldoc' but here nobody ever bothered
to write an 'eldoc-documentation-function' function for one of the two
major languages used to implement Emacs).  And as far as the former is
concerned, I now cannot use even 'elisp-eldoc-documentation-function'
directly either because you changed its return value too.

 > That is becasue these modes (which all work in Emacs 26, 27 and master)
 > set eldoc-documentation-function to fetch their docstrings from
 > asynchronous sources.  Therefore, calling the function won't return the
 > immediate value you expect, since ElDoc in Emacs 27 doesn't have any
 > concept of "async".  It is true that in Elisp mode (and maybe some other
 > modes) you get away with it becasue it doesn't have asynchronous sources
 > (by default, at least, and mostly becasue it doesn't need to).  This is
 > why your technique worked, under very special conditions.
 >
 > This is to clarify that the "direct call" protocol of Emacs 27's
 > eldoc.el was _never_ "a thing".  At any rate it was never something you
 > could rely on generally.

At the time "el" in eldoc stood for elisp it was.  But many things
changed since then, admittedly.

 > Anyway, eldoc-documentation-function is consulted by the Eldoc framework
 > to provide documentation of the thing at point.  The default value of
 > that variable has changed and it follows the "new" protocol.  The
 > documentation is telling you how to craft values that you assign to the
 > varible, not how your application should interpret them.
 >
 > If you need a direct call, "give me the string" entry point to the
 > eldoc.el library, one can be added.

I think we should do that and provide it as compatibility layer for
people like me who need it.

 > However, its semantics depends on
 > your use case:
 >
 > - Do you want to have a return value handy for the docstrings that are
 >    immediately available from the sources that respond synchronously?

Definitely.

 > - Do you want to wait actively until all sources have reported back and
 >    then get a return value?  In this case, do you need a timeout?

If we can provide an option for that, yes.

 > - Independent of your choice above, or do you want to get the return
 >    value as list of strings?  Or as a single string, maybe concatenated
 >    and propertized, exactly the way it is display

I'd prefer a list of strings.

 > But maybe we are putting the cart ahead of ourselves?  Would you mind
 > explaining exactly what you are trying to do?  I suppose it's:
 >
 >> I am using a package that displays the string produced by that
 >> function in a tooltip near point.
 >
 > Is this supposed to work only for Elisp mode or in general for every
 > mode that uses Eldoc, such as the ones I listed above?

I explained that above.  I attach a copy of eldoc-tooltip.el here (I
have two other packages to display tooltips either in a separate window
or in the header line but I have not used them for years).  As mentioned
above, I use eldoc for elisp and c only.  For the former I currently use

     (cond
      ((fboundp 'eldoc-tooltip-mode)
       (when (fboundp 'global-eldoc-mode)
	(global-eldoc-mode -1))
       (eldoc-tooltip-mode 1))

because eldoc-tooltip-mode was written some time before
'global-eldoc-mode' was added.

 > If the former, you can probably do this (or some variation):
 >
 > (defun martin ()
 >    "CAUTION: Only works in default Emacs Lisp mode or modes with all-sync
 > docstring generating functions.  If some functions calls the
 > callback afterwards, that result is discarded."
 >    (let (res)
 >      (run-hook-with-args 'eldoc-documentation-functions
 >                          (cl-function
 >                           (lambda (doc &key thing face &allow-other-keys)
 >                             (push (format "%s: %s"
 >                                           (propertize
 >                                            (format "%s" thing)
 >                                            'face face)
 >                                           doc)
 >                                   res))))
 >      (mapconcat #'identity res "\n")))
 >

Works without problems for elisp-mode.  Many thanks.

Now for c-mode I use

       (set (make-local-variable 'eldoc-documentation-function)
	   'c-eldoc-print-current-symbol-info)

What can I do here? The same?

 > If the latter, I suggest you look at the code I have in the
 > scratch/eldoc-display-functions branch, which seems to match your
 > intentions/use case very closely.  The docstring of the new
 > eldoc-display-functions variable could be of use (let me know if it's
 > insuficcient).  This means your advanced tooltip-displaying technology
 > could now work with every Eldoc mode that uses the "new" protocol
 > (including, of course, Elisp mode).

OK.  I will look into that later.

 > As a side note, I would take the opinions of your other interlocutor
 > here so far with a grain of salt or two.  They're not always grounded in
 > reality.

My other interlocutor here was Dmitry Gutov.  Please refer to him with
his name.  And I highly appreciate the fact that he was the only person
to answer my bug report within a day or two.

 > Finally, I understand the documentation for the new ElDoc framework is
 > not very good yet: I will update it soon.  Again, I apologize for the
 > delay in answering this bug report, but I am extremely busy as of late.
 > Next time, if you can remember, please X-Debbugs-CC: me in your fresh
 > bug report on this matter, so that the message reaches me immediately.

I'll do that.

Many thanks, martin

[-- Attachment #2: eldoc-tooltip.el --]
[-- Type: text/x-emacs-lisp, Size: 23083 bytes --]

;;; eldoc-tooltip.el --- show ElDoc as tooltips  -*- lexical-binding:t -*-

;; Copyright (C) 2015  Free Software Foundation, Inc.

;; Time-stamp: "2016-01-11 08:55:41 martin"
;; Author: Martin Rudalics <rudalics@gmx.at>
;; Keywords: ElDoc, tooltips
;; Version: 1.0

;; eldoc-tooltip.el 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, or (at
;; your option) any later version.

;; eldoc-tooltip.el 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, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; ElDoc tooltip mode is a minor mode to show the function arglist or
;; variable docstring provided by `eldoc-documentation-function' in a
;; tooltip window.

;; To turn on this mode automatically (and simultaneously turn off
;; showing tooltips in the echo area) put
;;
;;      (eldoc-tooltip-mode 1)
;;	(global-eldoc-mode -1)
;;
;; into your .emacs.

;; Eldoc tooltip mode requires Emacs 25 or higher for its handling of
;; toolbar window positioning.  It requires `eldoc' only to (1) get
;; access to `eldoc-documentation-function' and (2) override the option
;; `eldoc-echo-area-use-multiline-p' in invocations of the former.

;;; Code:
(defgroup eldoc-tooltip nil
  "Show ElDoc in tooltips."
  :version "25.1"
  :group 'extensions)

;;;###autoload
(define-minor-mode eldoc-tooltip-mode
  "Toggle ElDoc tooltip mode on or off."
  :group 'eldoc-tooltip
  (if eldoc-tooltip-mode
      (progn
	(set-frame-parameter nil 'eldoc-focus t)
	(add-hook 'focus-in-hook 'eldoc-tooltip--focus-in)
	(add-hook 'focus-out-hook 'eldoc-tooltip--focus-out)
	(add-hook 'window-configuration-change-hook 'eldoc-tooltip--hide-tip)
	(add-hook 'window-size-change-functions 'eldoc-tooltip--hide-tip)
	(unless (memq eldoc-tooltip-idle-timer timer-idle-list)
	  (setq eldoc-tooltip-idle-timer
		(run-with-idle-timer
		 eldoc-tooltip-delay t
		 (if (fboundp 'window-absolute-body-pixel-edges)
		     'eldoc-tooltip--make-2
		 'eldoc-tooltip--make)))))
    (when eldoc-tooltip-idle-timer
      (cancel-timer eldoc-tooltip-idle-timer)
      (setq eldoc-tooltip-idle-timer nil))
    (remove-hook 'focus-in-hook 'eldoc-tooltip--focus-in)
    (remove-hook 'focus-out-hook 'eldoc-tooltip--focus-out)
    (remove-hook 'window-configuration-change-hook 'eldoc-tooltip--hide-tip)
    (remove-hook 'window-size-change-functions 'eldoc-tooltip--hide-tip)))

(defgroup eldoc-tooltip nil
  "Show function arglist or variable docstring in tooltip."
  :group 'eldoc)

(defcustom eldoc-tooltip-delay 1.0
  "Number of seconds of idle time to wait before showing eldoc tooltip.
If user input arrives before this interval of time has elapsed after the
last input, no documentation will be shown.

If this variable is set to 0, no idle time is required."
  :type 'number
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-duration 60
  "Number of seconds to show eldoc tooltip.
If user input arrives before this time has elapsed, the tooltip will be
hidden."
  :type 'number
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-location 'eols
  "Where to show ElDoc tooltip.
Options are:

`above' - over text on line above point

`below' - over text on line below point

`eols' - at ends of lines near point

`top' on top of selected window

`bottom' - on bottom of selected window"
  :type '(choice (const :tag "on line above point" above)
                 (const :tag "on line below point" below)
                 (const :tag "at line ends near point" eols)
                 (const :tag "on top of selected window" top)
                 (const :tag "on bottom of selected window" bottom))
  :group 'eldoc-tooltip)


(defcustom eldoc-tooltip-x-offset-alist
  '((above . 2) (current . 2) (below . 2) (top . 2) (bottom . 2))
  "Offsets to adjust x-position of tooltips.
These pixel offsets are added to the x-position of tooltips that
respectively appear on the line above the current line (`above'),
the current line (`current'), the line below the current
line (`below'), the top edge of the window (`top') and the bottom
edge of the window (`bottom')."
  :type '(alist :key-type (symbol :tag "Location")
		:value-type integer)
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-indent-above-below t
  "Non-nil means to show tooltips at indent of current line.
This options takes affect only when tooltips shown at the
beginning of a line, that is when `eldoc-tooltip-location' equals
`above' or `below'.  When this option is nil, such tooltips start
at the visual beginning of the respective line."
  :type 'boolean
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-current-eol-extra-x-offset t
  "Extra offset for tooltip at end of current line.
If non-nil this adds an extra offset of one character's width to
avoid that the tooltip window overlays a block cursor at the end
of the current line.  If nil the offset is entirely determined by
the value of `eldoc-tooltip-x-offsets'.  Tooltips appearing
above or below the current line are not affected by this option."
  :type 'boolean
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-y-offset-alist
  '((above . 0) (current . -2) (below . -2) (top . 2) (bottom . -2))
  "Alist of offsets to adjust y-position of tooltips.
These pixel offsets are added to the y-position of tooltips that
respectively appear on the line above the current line (`above'),
the current line (`current'), the line below the current
line (`below'), the top edge of the window (`top') and the bottom
edge of the window (`bottom')."
  :type '(alist :key-type (symbol :tag "Location")
		:value-type integer)
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-max-size '(120 . 1)
  "Maximimum size of ElDoc tooltips.
A cons of rows and columns."
  :type '(cons (integer :tag "Columns")
	       (integer :tag "Lines"))
  :group 'eldoc-tooltip)

(defcustom eldoc-tooltip-frame-parameters
  '((background-color . "honeydew")
    (alpha . 60))
  "Frame parameter alist used for ElDoc tooltips.
The list of frame parameters passed to `x-show-tip' is built by
appending to this a list built from the `left' and `top'/`bottom'
parameters as produced by `eldoc-tooltip-make' and the value of
`tooltip-frame-parameters'."
  :type '(alist :key-type symbol
		:value-type sexp)
  :group 'tooltip)

(defvar eldoc-tooltip-idle-timer nil
  "ElDoc tooltip timer.")

(defvar eldoc-tooltip-debug nil
  "Non-nil means display message about position of tooltip window.")

(defun eldoc-tooltip--focus-in ()
  "ElDoc tooltip mode function when selected frame gains focus."
  (set-frame-parameter nil 'eldoc-focus t))

;; In the following two we should check that we own the tooltip.
(defun eldoc-tooltip--focus-out ()
  "ElDoc tooltip mode function when selected frame loses focus."
  (set-frame-parameter nil 'eldoc-focus nil)
  (x-hide-tip))

(defun eldoc-tooltip--hide-tip (&optional _ignore)
  "Hide ElDoc tooltip."
  (x-hide-tip))

(defun eldoc-tooltip--text ()
  "Return text string for ElDoc tooltip, nil if there's none."
  (save-excursion
    (goto-char (window-point))
    (when (boundp 'eldoc-documentation-function)
      ;; Alwasy try to get full string.
      (let ((eldoc-echo-area-use-multiline-p t))
	(funcall eldoc-documentation-function)))))

(defun eldoc-tooltip--current-line-wrapped-p ()
  "Return non-nil when current line is wrapped."
  (save-excursion
    (< (progn
	 (beginning-of-line)
	 (vertical-motion 1)
	 (point))
       (progn
	 (forward-line)
	 (point)))))

(defun eldoc-tooltip--make ()
  "Make ElDoc tooltip."
  (let* ((pos-point (pos-visible-in-window-p (point) nil t))
	 ;; We make a tooltip iff `point' is visible in the selected
	 ;; window, the frame has focus and we get a suitable text from
	 ;; the corresponding eldoc function.  Remember that
	 ;; `pos-visible-in-window-p' may return non-nil even when POS
	 ;; is scrolled off horizontally.
	 (text (and eldoc-tooltip-mode pos-point
		    (frame-parameter nil 'eldoc-focus)
		    (eldoc-tooltip--text))))
    (when text
      (let* ((edges (window-inside-pixel-edges))
	     (frame-geometry (frame-geometry))
	     ;; 	     (x-frame-geometry)
	     (frame-left-position
	      (or (cadr (assq 'frame-position frame-geometry))
		  (cadr (assq 'outer-position frame-geometry))
		  (nth 1 (assq 'outer-edges frame-geometry))))
	     (frame-top-position
	      (or (cddr (assq 'frame-position frame-geometry))
		  (cddr (assq 'outer-position frame-geometry))
		  (nth 2 (assq 'outer-edges frame-geometry))))
	     (frame-left
	      (let ((edge (frame-parameter nil 'left)))
		(if (numberp edge) edge 0)))
	     (left (+ frame-left
		      ;; Count left border iff frame's left edge is on
		      ;; screen (this should catch frame maximation by
		      ;; moving the border off-display tricks).
		      (if (< frame-left-position 0)
			  0
			(cadr (assq 'external-border-size frame-geometry)))
		      (nth 0 edges)
		      ;; Count tool bar when it's on the left.
		      (or (and (cdr (assq 'tool-bar-external frame-geometry))
			       (eq (cdr (assq 'tool-bar-position frame-geometry))
				   'left)
			       (cddr (assq 'tool-bar-size frame-geometry)))
			  0)
		      ;; Count vertical scroll bar when it's on the
		      ;; left.  Don't care about fringes or margins yet.
		      (if (eq (car (window-current-scroll-bars)) 'left)
			  (window-scroll-bar-width)
			0)))
	     (frame-top
	      (let ((edge (frame-parameter nil 'top)))
		(if (numberp edge) edge 0)))
	     (top
	      (+ frame-top
		 ;; Count top border iff frame's top edge is on screen
		 ;; (to catch frame maximation by moving the border
		 ;; off-display tricks).  Also, apparently ns counts the
		 ;; top border in the title height already.
		 (if (or (< frame-top-position 0)
			 (eq (window-system) 'ns))
		     0
		   (cddr (assq 'external-border-size frame-geometry)))
		 ;; Add heights of title, menu and tool bar but only if
		 ;; they are external (an "internal" tool or menu bar is
		 ;; already counted by `window-pixel-edges').
		 (or (cdr (assq 'title-height frame-geometry))
		     (cddr (assq 'title-bar-size frame-geometry))
		     0)
		 (or (and (cdr (assq 'menu-bar-external frame-geometry))
			  (cddr (assq 'menu-bar-size frame-geometry)))
		     0)
		 (or (and (cdr (assq 'tool-bar-external frame-geometry))
			  (eq (cdr (assq 'tool-bar-position frame-geometry))
			      'top)
			  (cddr (assq 'tool-bar-size frame-geometry)))
		     0)
		 (if (eq eldoc-tooltip-location 'bottom)
		     ;; Don't obscure mode line.
		     (- (nth 3 edges)
			(window-mode-line-height)
			(window-bottom-divider-width))
		   (nth 1 edges))))
	     (x-max-tooltip-size eldoc-tooltip-max-size)
	     parameters)
	(pcase eldoc-tooltip-location
	  ((or `above `below)
	   (let ((x-adjust (or (cdr (assq eldoc-tooltip-location
					  eldoc-tooltip-x-offset-alist))
			       0))
		 (y-adjust (or (cdr (assq eldoc-tooltip-location
					  eldoc-tooltip-y-offset-alist))
			       0))
		 (pos-x
		  (if eldoc-tooltip-indent-above-below
		      (save-excursion
			(beginning-of-line)
			(skip-chars-forward " \t")
			(nth 0 (pos-visible-in-window-p (point) nil t)))
		    0))
		 (pos-y (+ (nth 1 pos-point)
			   (if (eq eldoc-tooltip-location 'above)
			       0
			     ;; `window-line-height' returns nil when
			     ;; display is not up-to-date.
			     (or (car (window-line-height))
				 (frame-char-height))))))
	     (when eldoc-tooltip-debug
	       (message "%s %s %s %s [x: %s %s %s] [y: %s %s %s] -> %s"

			(if (or (< frame-top 0) (eq (window-system) 'ns))
			    0
			  (cddr (assq 'external-border-size frame-geometry)))
			(or (cdr (assq 'title-height frame-geometry))
			    (cddr (assq 'title-bar-size frame-geometry))
			    0)
			(or (and (cdr (assq 'menu-bar-external frame-geometry))
				 (cddr (assq 'menu-bar-size frame-geometry)))
			    0)
			(or (and (cdr (assq 'tool-bar-external frame-geometry))
				 (eq (cdr (assq 'tool-bar-position frame-geometry))
				     'top)
				 (cddr (assq 'tool-bar-size frame-geometry)))
			    0)

			left pos-x x-adjust top pos-y y-adjust
			(cons (+ left 0 x-adjust)
			      (+ top pos-y y-adjust))))
	     (setq parameters
		   (list
		    (cons 'left (+ left pos-x x-adjust))
		    (if (eq eldoc-tooltip-location 'above)
			(cons 'bottom (+ top pos-y y-adjust))
		      (cons 'top (+ top pos-y y-adjust)))))))
	  (`eols
	   (let (where pos-above pos-this pos-below pos-x pos-y x-adjust y-adjust)
	     (setq pos-this (pos-visible-in-window-p (line-end-position) nil t))

	     ;; Try current line first.
	     (when pos-this
	       (setq where 'current)
	       (setq pos-x (nth 0 pos-this))
	       (setq pos-y (nth 1 pos-this)))

	     ;; Try line above next.
	     (when (and (setq pos-above
			      (and (/= (line-beginning-position) (point-min))
				   (pos-visible-in-window-p
				    (line-end-position 0) nil t)))
			(or (not pos-x) (< (nth 0 pos-above) pos-x)))
	       (setq where 'above)
	       (setq pos-x (nth 0 pos-above))
	       (setq pos-y (if truncate-lines
			       ;; When lines are truncated use top
			       ;; of the current line.
			       (nth 1 pos-point)
			     ;; Otherwise use top of beginning of
			     ;; current line.
			     (or (nth 1 (pos-visible-in-window-p
					 (line-beginning-position) nil t))
				 0))))

	     ;; Try line below last.
	     (when (and (/= (line-end-position) (point-max))
			(or truncate-lines
			    (save-excursion
			      (forward-line)
			      (not (eldoc-tooltip--current-line-wrapped-p))))
			(setq pos-below (pos-visible-in-window-p
					 (line-end-position 2) nil t))
			(or (not pos-x) (< (nth 0 pos-below) pos-x)))
		 (setq where 'below)
		 (setq pos-x (nth 0 pos-below))
		 (setq pos-y (nth 1 pos-below)))

	     ;; If we didn't get a result till now simulate `above'.
	     (unless pos-x
	       (setq where 'above)
	       (setq pos-x 0)
	       (setq pos-y (nth 1 pos-point)))

	     ;; If the window is scrolled horizontally, make sure the
	     ;; tooltip doesn't start on the left of it.
	     (setq pos-x (max 0 pos-x))

	     ;; Adjust offsets now.
	     (setq x-adjust
		   (or (cdr (assq where eldoc-tooltip-x-offset-alist)) 0))
	     (setq y-adjust
		   (or (cdr (assq where eldoc-tooltip-y-offset-alist)) 0))

	     (when (and eldoc-tooltip-current-eol-extra-x-offset
			(or (= (point) (point-max)) (looking-at "\n")))
	       ;; Don't obscure block pointer at EOL.
	       (setq x-adjust (+ x-adjust (frame-char-width))))

	     (when eldoc-tooltip-debug
	       (message "%s %s %s %s - %s / %s / %s / %s -> %s [x: %s %s %s] [y: %s %s %s] -> %s"

			(if (or (< frame-top 0) (eq (window-system) 'ns))
			    0
			  (cddr (assq 'external-border-size frame-geometry)))
			(or (cdr (assq 'title-height frame-geometry))
			    (cddr (assq 'title-bar-size frame-geometry))
			    0)
			(or (and (cdr (assq 'menu-bar-external frame-geometry))
				 (cddr (assq 'menu-bar-size frame-geometry)))
			    0)
			(or (and (cdr (assq 'tool-bar-external frame-geometry))
				 (eq (cdr (assq 'tool-bar-position frame-geometry))
				     'top)
				 (cddr (assq 'tool-bar-size frame-geometry)))
			    0)

			pos-above pos-point pos-this pos-below where
			left pos-x x-adjust top pos-y y-adjust
			(cons (+ left pos-x x-adjust)
			      (+ top pos-y y-adjust))))

	     (setq parameters
		   (list
		    (cons 'left (+ left pos-x x-adjust))
		    (if (eq where 'above)
			(cons 'bottom (+ top pos-y y-adjust))
		      (cons 'top (+ top pos-y y-adjust)))))))
	  (`top
	   (let* ((x-adjust (cdr (assq 'top eldoc-tooltip-x-offset-alist)))
		  (y-adjust (cdr (assq 'top eldoc-tooltip-y-offset-alist))))
	     (when eldoc-tooltip-debug
	       (message "[x: %s %s] [y: %s %s] -> %s"
			left x-adjust top y-adjust
			(cons (+ left x-adjust) (+ top y-adjust))))
	     (setq parameters
		   (list
		    (cons 'left (+ left x-adjust))
		    (cons 'bottom (+ top y-adjust))))))
	  (`bottom
	   (let* ((x-adjust (cdr (assq 'top eldoc-tooltip-x-offset-alist)))
		  (y-adjust (cdr (assq 'top eldoc-tooltip-y-offset-alist))))
	     (when eldoc-tooltip-debug
	       (message "[x: %s %s] [y: %s %s] -> %s"
			left x-adjust top y-adjust
			(cons (+ left x-adjust) (+ top y-adjust))))
	     (setq parameters
		   (list
		    (cons 'left (+ left x-adjust))
		    (cons 'bottom (+ top y-adjust)))))))
	;; Show tip.
	(when parameters
	  (x-show-tip
	   text (selected-frame)
	   (append
	    eldoc-tooltip-frame-parameters parameters tooltip-frame-parameters)
	   eldoc-tooltip-duration 0 0))))))

(defun eldoc-tooltip--make-2 ()
  "Make ElDoc tooltip.
`window-absolute-body-pixel-edges' is needed for this."
  (let* ((pos-point (pos-visible-in-window-p (point) nil t))
	 ;; We make a tooltip iff `point' is visible in the selected
	 ;; window, the frame has focus and we get a suitable text from
	 ;; the corresponding eldoc function.  Remember that
	 ;; `pos-visible-in-window-p' may return non-nil even when POS
	 ;; is scrolled off horizontally.
	 (text (and eldoc-tooltip-mode pos-point
		    (frame-parameter nil 'eldoc-focus)
		    (eldoc-tooltip--text)))
	 old-consed)
    ;; Optional.
    (when (boundp 'mode-line-operation-consed)
      (setq old-consed cons-cells-consed))
    (when text
      (let* ((edges (and (fboundp 'window-absolute-body-pixel-edges)
			 (window-absolute-body-pixel-edges)))
	     (left (nth 0 edges))
	     (top (nth 1 edges))
	     (x-max-tooltip-size eldoc-tooltip-max-size)
	     parameters)
	(pcase eldoc-tooltip-location
	  ((or `above `below)
	   (let ((x-adjust (or (cdr (assq eldoc-tooltip-location
					  eldoc-tooltip-x-offset-alist))
			       0))
		 (y-adjust (or (cdr (assq eldoc-tooltip-location
					  eldoc-tooltip-y-offset-alist))
			       0))
		 (pos-x
		  (if eldoc-tooltip-indent-above-below
		      (save-excursion
			(beginning-of-line)
			(skip-chars-forward " \t")
			(nth 0 (pos-visible-in-window-p (point) nil t)))
		    0))
		 (pos-y (+ (nth 1 pos-point)
			   (if (eq eldoc-tooltip-location 'above)
			       0
			     ;; `window-line-height' returns nil when
			     ;; display is not up-to-date.
			     (or (car (window-line-height))
				 (frame-char-height))))))
	     (setq parameters
		   (list
		    (cons 'left (+ left pos-x x-adjust))
		    (if (eq eldoc-tooltip-location 'above)
			(cons 'bottom (+ top pos-y y-adjust))
		      (cons 'top (+ top pos-y y-adjust)))))))
	  (`eols
	   (let (where pos-above pos-this pos-below pos-x pos-y x-adjust y-adjust)
	     (setq pos-this (pos-visible-in-window-p (line-end-position) nil t))

	     ;; Try current line first.
	     (when pos-this
	       (setq where 'current)
	       (setq pos-x (nth 0 pos-this))
	       (setq pos-y (nth 1 pos-this)))

	     ;; Try line above next.
	     (when (and (setq pos-above
			      (and (/= (line-beginning-position) (point-min))
				   (pos-visible-in-window-p
				    (line-end-position 0) nil t)))
			(or (not pos-x) (< (nth 0 pos-above) pos-x)))
	       (setq where 'above)
	       (setq pos-x (nth 0 pos-above))
	       (setq pos-y (if truncate-lines
			       ;; When lines are truncated use top
			       ;; of the current line.
			       (nth 1 pos-point)
			     ;; Otherwise use top of beginning of
			     ;; current line.
			     (or (nth 1 (pos-visible-in-window-p
					 (line-beginning-position) nil t))
				 0))))

	     ;; Try line below last.
	     (when (and (/= (line-end-position) (point-max))
			(or truncate-lines
			    (save-excursion
			      (forward-line)
			      (not (eldoc-tooltip--current-line-wrapped-p))))
			(setq pos-below (pos-visible-in-window-p
					 (line-end-position 2) nil t))
			(or (not pos-x) (< (nth 0 pos-below) pos-x)))
	       (setq where 'below)
	       (setq pos-x (nth 0 pos-below))
	       (setq pos-y (nth 1 pos-below)))

	     ;; If we didn't get a result till now simulate `above'.
	     (unless pos-x
	       (setq where 'above)
	       (setq pos-x 0)
	       (setq pos-y (nth 1 pos-point)))

	     ;; If the window is scrolled horizontally, make sure the
	     ;; tooltip doesn't start on the left of it.
	     (setq pos-x (max 0 pos-x))

	     ;; Adjust offsets now.
	     (setq x-adjust
		   (or (cdr (assq where eldoc-tooltip-x-offset-alist)) 0))
	     (setq y-adjust
		   (or (cdr (assq where eldoc-tooltip-y-offset-alist)) 0))

	     (when (and eldoc-tooltip-current-eol-extra-x-offset
			(eq where 'current)
			(or (= (point) (point-max)) (looking-at "\n")))
	       ;; Don't obscure block pointer at EOL.
	       (setq x-adjust (+ x-adjust (frame-char-width))))

	     (setq parameters
		   (list
		    (cons 'left (+ left pos-x x-adjust))
		    (if (eq where 'above)
			(cons 'bottom (+ top pos-y y-adjust))
		      (cons 'top (+ top pos-y y-adjust)))))))
	  (`top
	   (let* ((x-adjust (cdr (assq 'top eldoc-tooltip-x-offset-alist)))
		  (y-adjust (cdr (assq 'top eldoc-tooltip-y-offset-alist))))
	     (setq parameters
		   (list
		    (cons 'left (+ left x-adjust))
		    (cons 'top (+ top y-adjust))))))
	  (`bottom
	   (let* ((x-adjust (cdr (assq 'bottom eldoc-tooltip-x-offset-alist)))
		  (y-adjust (cdr (assq 'bottom eldoc-tooltip-y-offset-alist))))
	     (setq parameters
		   (list
		    (cons 'left (+ left x-adjust))
		    (cons 'bottom (+ (nth 3 edges) y-adjust)))))))
	(when parameters
	  ;; Show tip first.
	  (x-show-tip
	   text (selected-frame)
	   (append
	    eldoc-tooltip-frame-parameters parameters tooltip-frame-parameters)
	   eldoc-tooltip-duration 0 0)
	  ;; If necessary, move mouse pointer away afterwards.
	  (let* ((buffer (get-buffer " *tip*"))
		 (window (and buffer (get-buffer-window buffer t)))
		 (frame (and window (window-frame window)))
		 (tip-edges (and frame (frame-edges frame 'outer)))
		 (tip-left (nth 0 tip-edges))
		 (tip-top (nth 1 tip-edges))
		 (tip-right (nth 2 tip-edges))
		 (tip-bottom (nth 3 tip-edges))
		 (mouse-position (mouse-absolute-pixel-position))
		 (mouse-x (car mouse-position))
		 (mouse-y (cdr mouse-position)))
	    (when (and tip-left tip-right mouse-x (<= tip-left mouse-x) (<= mouse-x tip-right)
		       tip-top tip-bottom mouse-y (<= tip-top mouse-y) (<= mouse-y tip-bottom))
	      (let* ((window-edges (window-edges nil t t t))
		     (window-top (nth 1 window-edges)))
;; 		(message "frame: %s  tip-left: %s  tip-top: %s  tip-right: %s  tip-bottom: %s"
;; 			 frame tip-left tip-top tip-right tip-bottom)
		(set-mouse-absolute-pixel-position
		 mouse-x
		 ;; If there's enough space within the seelcted window,
		 ;; move mouse pointer two pixels above top of tooltip,
		 ;; otherwise move it one pixel below bottom of tooltip.
		 (if (< (+ window-top 2) tip-top)
		     (- tip-top 2)
		   (1+ tip-bottom)))))))))
    ;; Optional.
    (when (boundp 'mode-line-operation-consed)
      (setq mode-line-operation-consed (- cons-cells-consed old-consed)))
    ))

(provide 'eldoc-tooltip)
;;; eldoc-tooltip.el ends here

  reply	other threads:[~2020-09-30 17:33 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-25  8:46 bug#43609: 28.0.50; eldoc-documentation-function martin rudalics
2020-09-26 18:34 ` Dmitry Gutov
2020-09-27  8:30   ` martin rudalics
2020-09-29 11:20     ` Dmitry Gutov
2020-09-29 15:09       ` martin rudalics
2020-09-29 15:23         ` Dmitry Gutov
2020-09-30  8:14           ` martin rudalics
2020-09-30  8:50             ` Dmitry Gutov
2020-09-30 14:37 ` João Távora
2020-09-30 17:33   ` martin rudalics [this message]
2020-09-30 18:22     ` João Távora
2020-10-01  8:40       ` martin rudalics
2020-10-01  9:23         ` João Távora
2020-10-03 19:15         ` bug#43609: 28.0.50; eldoc-documentation-function [vs new eldoc-display-functions] João Távora
2020-10-05  8:35           ` martin rudalics
2020-10-05  9:29             ` João Távora
2020-10-06  8:23               ` martin rudalics
2020-10-06  9:29                 ` João Távora
2020-10-07  8:36                   ` martin rudalics
2020-10-07  8:40                     ` João Távora
2020-10-07  9:36                     ` João Távora
2020-10-08  8:22                       ` martin rudalics
2020-10-08  8:27                         ` João Távora
2020-10-09  8:03                           ` martin rudalics
2020-10-24 15:18                             ` João Távora
2020-10-26 14:12                               ` João Távora
2020-10-27  9:59                                 ` martin rudalics
2020-10-27  9:58                               ` martin rudalics
2020-10-27 15:11                                 ` João Távora
2020-10-27 18:05                                   ` martin rudalics
2020-10-27 19:56                                     ` João Távora
2020-10-28  8:39                                       ` martin rudalics
2020-10-28  9:38                                         ` João Távora
2020-10-31  8:01                                           ` martin rudalics
2020-10-28  9:17                                       ` Lars Ingebrigtsen
2020-10-28  9:54                                         ` João Távora
2020-10-30 22:51                                           ` João Távora
2020-10-31  8:02                                             ` martin rudalics
2020-10-23  2:37           ` Yuan Fu
2020-10-24 17:09             ` João Távora
2020-10-31 13:07               ` Basil L. Contovounesios

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3fa6b315-7fc0-06ee-81e9-b68d164aec1b@gmx.at \
    --to=rudalics@gmx.at \
    --cc=43609@debbugs.gnu.org \
    --cc=joaotavora@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).