From 1738ccae4c54cfa8e7ff60ef07b053d9ae75b359 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 09:27:01 -0500 Subject: [PATCH 01/12] Support max width/height to be functions --- eldoc-box.el | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 579fbe8..62b9958 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -90,11 +90,15 @@ you moved the point to somewhere else (that doesn't have a doc to show)") (defvar eldoc-box-max-pixel-width 800 "Maximum width of doc childframw in pixel. -Consider your machine's screen's resolution when setting this variable.") +Consider your machine's screen's resolution when setting this variable. +Set it to a function with no argument +if you want to dynamically change the maximum width.") (defvar eldoc-box-max-pixel-height 700 "Maximum height of doc childframw in pixel. -Consider your machine's screen's resolution when setting this variable.") +Consider your machine's screen's resolution when setting this variable. +Set it to a function with no argument +if you want to dynamically change the maximum height.") ;;;;; Function (defvar eldoc-box--frame nil ;; A backstage variable @@ -172,8 +176,9 @@ Checkout `lsp-ui-doc--make-frame', `lsp-ui-doc--move-frame'." (let* ((size (window-text-pixel-size window nil nil - eldoc-box-max-pixel-width - eldoc-box-max-pixel-height t)) + (if (functionp eldoc-box-max-pixel-width) (funcall eldoc-box-max-pixel-width) eldoc-box-max-pixel-width) + (if (functionp eldoc-box-max-pixel-height) (funcall eldoc-box-max-pixel-height) eldoc-box-max-pixel-height) + t)) (width (car size)) (height (cdr size)) (width (+ width (frame-char-width frame))) ; add margin From c4af2c7e23587291c03e2fa54c494813429d628f Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 09:57:30 -0500 Subject: [PATCH 02/12] Support childframe at point --- eldoc-box.el | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 62b9958..b533c0e 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -100,6 +100,11 @@ Consider your machine's screen's resolution when setting this variable. Set it to a function with no argument if you want to dynamically change the maximum height.") +(defvar eldoc-box-position-function #'eldoc-box--default-upper-corner-position-function + "Eldoc-box uses this function to set childframe's position. +This should be a function that returns a (X . Y) cons cell. +It will be passes with two arguments: WIDTH and HEIGHT of the childframe.") + ;;;;; Function (defvar eldoc-box--frame nil ;; A backstage variable "The frame to display doc.") @@ -152,6 +157,26 @@ if you want to dynamically change the maximum height.") 'left 'right))) +(defun eldoc-box--default-upper-corner-position-function (width _) + "The default function to set childframe position. +Used by `eldoc-box-position-function'. +Position is calculated base on WIDTH and HEIGHT of chilframe text window" + (cons (pcase (eldoc-box--window-side) ; x position + a little padding (16) + ;; display doc on right + ('left (- (frame-outer-width (selected-frame)) width 16)) + ;; display doc on left + ('right 16)) + ;; y position + a little padding (16) + 16)) + +(defun eldoc-box--default-at-point-position-function (width _) + "Set `eldoc-box-position-function' to this function to have childframe appear under point. +Position is calculated base on WIDTH and HEIGHT of chilframe text window" + (let ((point-pos (window-absolute-pixel-position))) + (cons (+ 16 (car point-pos)) (- (min (cdr point-pos) + (frame-outer-width (selected-frame))) + 16)))) + (defun eldoc-box--get-frame (buffer) "Return a childframe displaying BUFFER. Checkout `lsp-ui-doc--make-frame', `lsp-ui-doc--move-frame'." @@ -182,18 +207,14 @@ Checkout `lsp-ui-doc--make-frame', `lsp-ui-doc--move-frame'." (width (car size)) (height (cdr size)) (width (+ width (frame-char-width frame))) ; add margin - (frame-resize-pixelwise t)) + (frame-resize-pixelwise t) + (pos (funcall eldoc-box-position-function width height))) (set-frame-size frame width height t) ;; move position - (set-frame-position frame (pcase (eldoc-box--window-side) ; x position + a little padding (16) - ;; display doc on right - ('left (- (frame-outer-width main-frame) width 16)) - ;; display doc on left - ('right 16)) - ;; y position + a little padding (16) - 16)) + (set-frame-position frame (car pos) (cdr pos))) (setq eldoc-box--frame frame))) + ;;;;; ElDoc (defvar eldoc-box--cleanup-timer nil From 8f71b3c65e2a2acd219668fefb00e09960a2c25b Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 10:18:49 -0500 Subject: [PATCH 03/12] Fix at point position function --- eldoc-box.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index b533c0e..25d4530 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -169,13 +169,16 @@ Position is calculated base on WIDTH and HEIGHT of chilframe text window" ;; y position + a little padding (16) 16)) -(defun eldoc-box--default-at-point-position-function (width _) +(defun eldoc-box--default-at-point-position-function (width height) "Set `eldoc-box-position-function' to this function to have childframe appear under point. Position is calculated base on WIDTH and HEIGHT of chilframe text window" (let ((point-pos (window-absolute-pixel-position))) - (cons (+ 16 (car point-pos)) (- (min (cdr point-pos) - (frame-outer-width (selected-frame))) - 16)))) + (cons (- (min (car point-pos) + (- (frame-outer-width (selected-frame)) width)) + 50) + (if (< (- (frame-outer-height (selected-frame)) height) (cdr point-pos)) + (- (cdr point-pos) height) + (- (cdr point-pos) 20))))) (defun eldoc-box--get-frame (buffer) "Return a childframe displaying BUFFER. From 1733e35085684ffb11147ce8380bacf195297200 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 10:26:03 -0500 Subject: [PATCH 04/12] Add eldoc-box-clear-with-C-g, update README --- README.org | 7 +++++++ eldoc-box.el | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 52d2d90..a37f494 100644 --- a/README.org +++ b/README.org @@ -19,6 +19,13 @@ Get the file, add to load path, and - =eldoc-box-max-pixel-width= & =eldoc-box-max-pixel-height= :: Set them according to the screen resolution of your machine. - =eldoc-box-only-multi-line= :: Set this to non-nil and eldoc-box only display multi-line message in childframe. One line messages are left in minibuffer. - =eldoc-box-cleanup-interval= :: After this amount of seconds will eldoc-box attempt to cleanup the childframe. E.g. if it is set to 1, the childframe is cleared 1 second after you moved the point to somewhere else (that doesn't have a doc to show). + +** Pop at point instead of on the upper corner +I don't use this, but if you do: +1. set =eldoc-box-position-function= to =eldoc-box--default-at-point-position-function=. +2. Set =eldoc-box-clear-with-C-g= to =t=, so =C-g= will clear the childframe. +3. Maybe set =eldoc-box-max-pixel-width= & =eldoc-box-max-pixel-height= to some custom functions. + ** Use with eglot As of writing this README, eglot doesn't have a public mode hook, use this hook: #+BEGIN_SRC emacs-lisp diff --git a/eldoc-box.el b/eldoc-box.el index 25d4530..7e5e2a9 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -59,6 +59,9 @@ E.g. if it is set to 1, the childframe is cleared 1 second after you moved the point to somewhere else (that doesn't have a doc to show)") +(defvar eldoc-box-clear-with-C-g nil + "If set to non-nil, eldoc-box clears cildframe when you hit \C-g.") + (defvar eldoc-box-frame-parameters '( ;; (left . -1) @@ -120,9 +123,12 @@ It will be passes with two arguments: WIDTH and HEIGHT of the childframe.") "Displays hover documentations in a childframe. This mode is buffer local." :lighter " ELDOC-BOX" (if eldoc-box-hover-mode - (add-function :before-until (local 'eldoc-message-function) - #'eldoc-box--eldoc-message-function) + (progn (add-function :before-until (local 'eldoc-message-function) + #'eldoc-box--eldoc-message-function) + (when eldoc-box-clear-with-C-g + (advice-add #'keyboard-quit :before #'eldoc-box-quit-frame))) (remove-function (local 'eldoc-message-function) #'eldoc-box--eldoc-message-function) + (advice-remove #'keyboard-quit #'eldoc-box-quit-frame) ;; if minor mode is turned off when childframe is visible ;; hide it (when eldoc-box--frame From f7b4806694e004a6235af4e247d1fbded35b8ca5 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 10:31:14 -0500 Subject: [PATCH 05/12] Tweak the pop at point position --- eldoc-box.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eldoc-box.el b/eldoc-box.el index 7e5e2a9..3e14034 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -183,7 +183,7 @@ Position is calculated base on WIDTH and HEIGHT of chilframe text window" (- (frame-outer-width (selected-frame)) width)) 50) (if (< (- (frame-outer-height (selected-frame)) height) (cdr point-pos)) - (- (cdr point-pos) height) + (- (cdr point-pos) height 60) (- (cdr point-pos) 20))))) (defun eldoc-box--get-frame (buffer) From bbc1139e95affa988dd5426c98c34b5d3f2191cd Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 21:56:36 -0500 Subject: [PATCH 06/12] Fix at-point position calculation --- eldoc-box.el | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 3e14034..48c968b 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -178,13 +178,24 @@ Position is calculated base on WIDTH and HEIGHT of chilframe text window" (defun eldoc-box--default-at-point-position-function (width height) "Set `eldoc-box-position-function' to this function to have childframe appear under point. Position is calculated base on WIDTH and HEIGHT of chilframe text window" - (let ((point-pos (window-absolute-pixel-position))) - (cons (- (min (car point-pos) - (- (frame-outer-width (selected-frame)) width)) - 50) - (if (< (- (frame-outer-height (selected-frame)) height) (cdr point-pos)) - (- (cdr point-pos) height 60) - (- (cdr point-pos) 20))))) + ;; (window-absolute-pixel-position) + ;; (posn-x-y (posn-at-point)) + (let* ((point-pos (window-absolute-pixel-position)) + (frame-pos (frame-edges nil 'native-edges)) + (x (- (car point-pos) (car frame-pos))) ; relative to native frame + (y (- (cdr point-pos) (nth 1 frame-pos))) + (en (frame-char-width)) + (em (frame-char-height))) + (cons (if (< (- (frame-inner-width) width) x) + ;; space on the right of the pos is not enough + ;; put to left + (- x width) + (+ x en)) + (if (< (- (frame-inner-height) height) y) + ;; space under the pos is not enough + ;; put above + (- y height) + (+ y em))))) (defun eldoc-box--get-frame (buffer) "Return a childframe displaying BUFFER. From 28eb1d7f8ac1532c8a81d7b339e3dc905014c98d Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 21:56:48 -0500 Subject: [PATCH 07/12] Add convenient minor mode for at-point mode --- eldoc-box.el | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/eldoc-box.el b/eldoc-box.el index 48c968b..38ac6cf 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -135,6 +135,23 @@ It will be passes with two arguments: WIDTH and HEIGHT of the childframe.") (delete-frame eldoc-box--frame) (setq eldoc-box--frame nil)))) +;;;###autoload +(define-minor-mode eldoc-box-hover-at-point-mode + "A convenient minor mode to display doc at point. +You can use C-g to hide the doc." + :lighter "" + (if eldoc-box-hover-at-point-mode + (progn (setq-local + eldoc-box-position-function + #'eldoc-box--default-at-point-position-function) + (setq-local eldoc-box-clear-with-C-g t) + (add-hook 'pre-command-hook #'eldoc-box-quit-frame t t) + (eldoc-box-hover-mode)) + (eldoc-box-hover-mode -1) + (remove-hook 'pre-command-hook #'eldoc-box-quit-frame t) + (kill-local-variable 'eldoc-box-position-function) + (kill-local-variable 'eldoc-box-clear-with-C-g))) + ;;;; Backstage ;;;;; Variable (defvar eldoc-box--buffer " *eldoc-box*" From 615e8bce86f17904404199bad8601971a73dc620 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 21:58:48 -0500 Subject: [PATCH 08/12] Update README for new minor mode --- README.org | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.org b/README.org index a37f494..8b76fda 100644 --- a/README.org +++ b/README.org @@ -12,6 +12,7 @@ Get the file, add to load path, and * Usage ** Function - =eldoc-box-hover-mode= :: Show documentation upon hover. Note that you need to enable ElDoc mode for this to work. +- =eldoc-box-hover-at-point-mode= :: This minor mode shows doc at point. Don't try to enable both minor mode: you can only enable one in the same time. ** Face - =eldoc-box-border= :: Adjust =:background= of this face for border color. - =eldoc-box-body= :: Adjust =:background= of this face for background color of childframe. @@ -20,12 +21,6 @@ Get the file, add to load path, and - =eldoc-box-only-multi-line= :: Set this to non-nil and eldoc-box only display multi-line message in childframe. One line messages are left in minibuffer. - =eldoc-box-cleanup-interval= :: After this amount of seconds will eldoc-box attempt to cleanup the childframe. E.g. if it is set to 1, the childframe is cleared 1 second after you moved the point to somewhere else (that doesn't have a doc to show). -** Pop at point instead of on the upper corner -I don't use this, but if you do: -1. set =eldoc-box-position-function= to =eldoc-box--default-at-point-position-function=. -2. Set =eldoc-box-clear-with-C-g= to =t=, so =C-g= will clear the childframe. -3. Maybe set =eldoc-box-max-pixel-width= & =eldoc-box-max-pixel-height= to some custom functions. - ** Use with eglot As of writing this README, eglot doesn't have a public mode hook, use this hook: #+BEGIN_SRC emacs-lisp From cf5ef5510c74226fa5d04da8aceabc3c4574b482 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 22:17:04 -0500 Subject: [PATCH 09/12] Fix: local-variable eldoc-box-position-function doesn't work --- eldoc-box.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 38ac6cf..ab7135f 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -168,8 +168,8 @@ You can use C-g to hide the doc." ;; and `eldoc-box--maybe-cleanup' in `eldoc-box--cleanup-timer' will clear the childframe (setq eldoc-box-hover-mode t) (erase-buffer) - (insert str) - (eldoc-box--get-frame doc-buffer)))) + (insert str)) + (eldoc-box--get-frame doc-buffer))) (defun eldoc-box--window-side () From c7fea8278e2c0ab27412dbe22a63502c01ac3759 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 22:19:58 -0500 Subject: [PATCH 10/12] Update README --- README.org | 2 +- eldoc-box.el | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 8b76fda..41248d0 100644 --- a/README.org +++ b/README.org @@ -19,7 +19,7 @@ Get the file, add to load path, and ** Variable - =eldoc-box-max-pixel-width= & =eldoc-box-max-pixel-height= :: Set them according to the screen resolution of your machine. - =eldoc-box-only-multi-line= :: Set this to non-nil and eldoc-box only display multi-line message in childframe. One line messages are left in minibuffer. -- =eldoc-box-cleanup-interval= :: After this amount of seconds will eldoc-box attempt to cleanup the childframe. E.g. if it is set to 1, the childframe is cleared 1 second after you moved the point to somewhere else (that doesn't have a doc to show). +- =eldoc-box-cleanup-interval= :: After this amount of seconds will eldoc-box attempt to cleanup the childframe. E.g. if it is set to 1, the childframe is cleared 1 second after you moved the point to somewhere else (that doesn't have a doc to show). This doesn't apply to =eldoc-box-hover-at-point-mode=, in that mode the childframe is cleared as soon as point moves. ** Use with eglot As of writing this README, eglot doesn't have a public mode hook, use this hook: diff --git a/eldoc-box.el b/eldoc-box.el index ab7135f..d66a61a 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -57,7 +57,9 @@ (defvar eldoc-box-cleanup-interval 1 "After this amount of seconds will eldoc-box attempt to cleanup the childframe. E.g. if it is set to 1, the childframe is cleared 1 second after -you moved the point to somewhere else (that doesn't have a doc to show)") +you moved the point to somewhere else (that doesn't have a doc to show). +This doesn't apply to =eldoc-box-hover-at-point-mode=, +in that mode the childframe is cleared as soon as point moves.") (defvar eldoc-box-clear-with-C-g nil "If set to non-nil, eldoc-box clears cildframe when you hit \C-g.") From 108403d5f317a51b07679c05f4b0c9641cee9bcf Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 20 Dec 2018 22:54:58 -0500 Subject: [PATCH 11/12] Add hack --- README.org | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index 41248d0..41bda7e 100644 --- a/README.org +++ b/README.org @@ -20,9 +20,24 @@ Get the file, add to load path, and - =eldoc-box-max-pixel-width= & =eldoc-box-max-pixel-height= :: Set them according to the screen resolution of your machine. - =eldoc-box-only-multi-line= :: Set this to non-nil and eldoc-box only display multi-line message in childframe. One line messages are left in minibuffer. - =eldoc-box-cleanup-interval= :: After this amount of seconds will eldoc-box attempt to cleanup the childframe. E.g. if it is set to 1, the childframe is cleared 1 second after you moved the point to somewhere else (that doesn't have a doc to show). This doesn't apply to =eldoc-box-hover-at-point-mode=, in that mode the childframe is cleared as soon as point moves. - ** Use with eglot As of writing this README, eglot doesn't have a public mode hook, use this hook: #+BEGIN_SRC emacs-lisp (add-hook 'eglot--managed-mode-hook #'eldoc-box-hover-mode t) #+END_SRC +** Help at point hack +If all you need is a "help at point" popup to be used with eglot, here is my hack: +#+BEGIN_SRC emacs-lisp +(defun moon-help-at-point () + (interactive) + (when eglot--managed-mode + (require 'eldoc-box) + (let ((eldoc-box-position-function #'eldoc-box--default-at-point-position-function)) + (eldoc-box--display + (eglot--dbind ((Hover) contents range) + (jsonrpc-request (eglot--current-server-or-lose) :textDocument/hover + (eglot--TextDocumentPositionParams)) + (when (seq-empty-p contents) (eglot--error "No hover info here")) + (eglot--hover-info contents range)))) + (add-hook 'pre-command-hook #'eldoc-box-quit-frame t t))) +#+END_SRC From 291e5ac97c1fc6eba606d33f8bd317d35edbe447 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Fri, 21 Dec 2018 02:16:02 -0500 Subject: [PATCH 12/12] Fix: tool-bar-mode mess up popup position --- eldoc-box.el | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index d66a61a..d41bf5e 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -204,17 +204,22 @@ Position is calculated base on WIDTH and HEIGHT of chilframe text window" (x (- (car point-pos) (car frame-pos))) ; relative to native frame (y (- (cdr point-pos) (nth 1 frame-pos))) (en (frame-char-width)) - (em (frame-char-height))) + (em (frame-char-height)) + (frame-geometry (frame-geometry)) + (tool-bar (if (and tool-bar-mode + (alist-get 'tool-bar-external frame-geometry)) + (cdr (alist-get 'tool-bar-size frame-geometry)) + 0))) (cons (if (< (- (frame-inner-width) width) x) ;; space on the right of the pos is not enough ;; put to left - (- x width) + (max 0 (- x width)) (+ x en)) (if (< (- (frame-inner-height) height) y) ;; space under the pos is not enough ;; put above - (- y height) - (+ y em))))) + (max 0 (- y height)) + (+ y em tool-bar))))) (defun eldoc-box--get-frame (buffer) "Return a childframe displaying BUFFER.