From 1cb8f409e0435d5c8cd90d5c52b3ea9cc7173926 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Wed, 2 Oct 2019 23:28:35 -0400 Subject: [PATCH 1/4] Separate hover-mode and at-point-mode (#21) at-point-mode no longer stack on top of hover-mode. Instead, they stands as two exclusive modes, enabling any one of them disables the other. --- eldoc-box.el | 74 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 57086b2..891662b 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -134,46 +134,58 @@ Each function runs inside the new frame and receives the main frame as argument. (when eldoc-box--frame (make-frame-invisible eldoc-box--frame t))) +(defun eldoc-box--enable () + "Enable eldoc-box hover. +Intended for internal use." + (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))) + +(defun eldoc-box--disable () + "Disable eldoc-box hover. +Intended for internal use." + (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 + (delete-frame eldoc-box--frame) + (setq eldoc-box--frame nil))) + +;; please compiler +(defvar eldoc-box-hover-at-point-mode) +(declare-function eldoc-box-hover-at-point-mode "eldoc-box.el") + ;;;###autoload (define-minor-mode eldoc-box-hover-mode "Displays hover documentations in a childframe. This mode is buffer local." :lighter " ELDOC-BOX" (if eldoc-box-hover-mode - (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))) - (when eldoc-box-hover-at-point-mode - (eldoc-box-hover-at-point-mode -1)) - (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 - (delete-frame eldoc-box--frame) - (setq eldoc-box--frame nil)))) + (progn (when eldoc-box-hover-at-point-mode + (eldoc-box-hover-at-point-mode -1)) + (eldoc-box--enable)) + (eldoc-box--disable))) ;;;###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-mode - (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) - ;; always kill frame instead of using maybe-cleanup - (remove-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area t) - ;; (add-hook 'pre-command-hook #'eldoc-box-quit-frame t t) - (add-hook 'post-command-hook #'eldoc-box--follow-cursor t t)) - (add-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area t) - ;; (remove-hook 'pre-command-hook #'eldoc-box-quit-frame t) - (remove-hook 'post-command-hook #'eldoc-box--follow-cursor t) - (kill-local-variable 'eldoc-box-position-function) - (kill-local-variable 'eldoc-box-clear-with-C-g)) - (message "Enable eldoc-box-hover-mode first"))) + :lighter " ELDOC-BOX" + (if eldoc-box-hover-at-point-mode + (progn (when eldoc-box-hover-mode + (eldoc-box-hover-mode -1)) + (setq-local eldoc-box-position-function + #'eldoc-box--default-at-point-position-function) + (setq-local eldoc-box-clear-with-C-g t) + (remove-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area t) + (add-hook 'post-command-hook #'eldoc-box--follow-cursor t t) + (eldoc-box--enable)) + (eldoc-box--disable) + (add-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area t) + (remove-hook 'post-command-hook #'eldoc-box--follow-cursor t) + (kill-local-variable 'eldoc-box-position-function) + (kill-local-variable 'eldoc-box-clear-with-C-g))) ;;;; Backstage ;;;;; Variable @@ -336,7 +348,7 @@ Checkout `lsp-ui-doc--make-frame', `lsp-ui-doc--move-frame'." (or (and (not eldoc-last-message) ; 1 (not (eq (point) eldoc-box--last-point)) ; 2 (not (eq (current-buffer) (get-buffer eldoc-box--buffer)))) ; 3 - (not eldoc-box-hover-mode))) ; 4 + (not (or eldoc-box-hover-mode eldoc-box-hover-at-point-mode)))) ; 4 ;; 1. Obviously, last-message nil means we are not on a valid symbol anymore. ;; 2. Or are we? If you scroll the childframe with mouse wheel ;; `eldoc-pre-command-refresh-echo-area' will set `eldoc-last-message' to nil. From 6c5c9d57d8dde6c7bc127bb95ceffe2b797c761e Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 3 Oct 2019 00:38:32 -0400 Subject: [PATCH 2/4] Inhibit childframe display when moving point (#21) (only in at-point mode) --- eldoc-box.el | 96 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 891662b..6ac16e6 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -124,7 +124,14 @@ Run inside the new buffer.") "Hook run after doc frame is setup but just before it is made visible. Each function runs inside the new frame and receives the main frame as argument.") +(defvar eldoc-box-self-insert-command-list '(self-insert-command outshine-self-insert-command) + "Commands in this list are considered self-insert-command by eldoc-box. +See `eldoc-box-inhibit-display-when-moving'.") + ;;;;; Function +(defvar eldoc-box--inhibit-childframe nil + "If non-nil, inhibit display of childframe.") + (defvar eldoc-box--frame nil ;; A backstage variable "The frame to display doc.") @@ -159,7 +166,8 @@ Intended for internal use." ;;;###autoload (define-minor-mode eldoc-box-hover-mode - "Displays hover documentations in a childframe. This mode is buffer local." + "Displays hover documentations in a childframe. +The default position of childframe is upper corner." :lighter " ELDOC-BOX" (if eldoc-box-hover-mode (progn (when eldoc-box-hover-at-point-mode @@ -169,8 +177,9 @@ Intended for internal use." ;;;###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." + "A convenient minor mode to display doc at point +instead of at upper corner. You can use C-g to hide the doc. +The childframe will follow you when you type." :lighter " ELDOC-BOX" (if eldoc-box-hover-at-point-mode (progn (when eldoc-box-hover-mode @@ -293,42 +302,63 @@ FRAME is the childframe, WINDOW is the primary window." ;; move position (set-frame-position frame (car pos) (cdr pos)))) +(defvar eldoc-box--inhibit-childframe-timer nil + "When this timer is on, inhibit childframe display. +Intended for follow-cursor to disable display when moving cursor.") + +(defun eldoc-box--inhibit-childframe-for (sec) + "Inhibit display of childframe for SEC seconds." + (when eldoc-box--inhibit-childframe-timer + (cancel-timer eldoc-box--inhibit-childframe-timer)) + (eldoc-box-quit-frame) + (setq eldoc-box--inhibit-childframe t + eldoc-box--inhibit-childframe-timer + (run-with-timer sec nil + (lambda () + (setq eldoc-box--inhibit-childframe nil))))) + (defun eldoc-box--follow-cursor () "Make childframe follow cursor in at-point mode." - (when (frame-live-p eldoc-box--frame) - (eldoc-box--update-childframe-geometry - eldoc-box--frame (frame-selected-window eldoc-box--frame)))) + (if (member this-command eldoc-box-self-insert-command-list) + (progn (when (frame-live-p eldoc-box--frame) + (eldoc-box--update-childframe-geometry + eldoc-box--frame (frame-selected-window eldoc-box--frame)))) + ;; if not typing, inhibit display + (eldoc-box--inhibit-childframe-for 0.2))) (defun eldoc-box--get-frame (buffer) "Return a childframe displaying BUFFER. Checkout `lsp-ui-doc--make-frame', `lsp-ui-doc--move-frame'." - (let* ((after-make-frame-functions nil) - (before-make-frame-hook nil) - (parameter (append eldoc-box-frame-parameters - `((default-minibuffer-frame . ,(selected-frame)) - (minibuffer . ,(minibuffer-window)) - (left-fringe . ,(frame-char-width))))) - window frame - (main-frame (selected-frame))) - (if (and eldoc-box--frame (frame-live-p eldoc-box--frame)) - (progn (setq frame eldoc-box--frame) - (setq window (frame-selected-window frame)) - ;; in case the main frame changed - (set-frame-parameter frame 'parent-frame main-frame)) - (setq window (display-buffer-in-child-frame - buffer - `((child-frame-parameters . ,parameter)))) - (setq frame (window-frame window))) - (set-face-attribute 'fringe frame :background nil :inherit 'eldoc-box-body) - (fringe-mode 3) - (set-window-dedicated-p window t) - (redirect-frame-focus frame (frame-parent frame)) - (set-face-attribute 'internal-border frame :inherit 'eldoc-box-border) - ;; set size - (eldoc-box--update-childframe-geometry frame window) - (setq eldoc-box--frame frame) - (run-hook-with-args 'eldoc-box-frame-hook main-frame) - (make-frame-visible frame))) + (if eldoc-box--inhibit-childframe + ;; if inhibit display, do nothing + eldoc-box--frame + (let* ((after-make-frame-functions nil) + (before-make-frame-hook nil) + (parameter (append eldoc-box-frame-parameters + `((default-minibuffer-frame . ,(selected-frame)) + (minibuffer . ,(minibuffer-window)) + (left-fringe . ,(frame-char-width))))) + window frame + (main-frame (selected-frame))) + (if (and eldoc-box--frame (frame-live-p eldoc-box--frame)) + (progn (setq frame eldoc-box--frame) + (setq window (frame-selected-window frame)) + ;; in case the main frame changed + (set-frame-parameter frame 'parent-frame main-frame)) + (setq window (display-buffer-in-child-frame + buffer + `((child-frame-parameters . ,parameter)))) + (setq frame (window-frame window))) + (set-face-attribute 'fringe frame :background nil :inherit 'eldoc-box-body) + (fringe-mode 3) + (set-window-dedicated-p window t) + (redirect-frame-focus frame (frame-parent frame)) + (set-face-attribute 'internal-border frame :inherit 'eldoc-box-border) + ;; set size + (eldoc-box--update-childframe-geometry frame window) + (setq eldoc-box--frame frame) + (run-hook-with-args 'eldoc-box-frame-hook main-frame) + (make-frame-visible frame)))) ;;;;; ElDoc From 5ca8a2cef5d057aeeaf6185880a7c1ac15e54402 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 3 Oct 2019 01:06:55 -0400 Subject: [PATCH 3/4] Fix fringe --- eldoc-box.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 6ac16e6..6fffd63 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -80,7 +80,8 @@ in that mode the childframe is cleared as soon as point moves.") (internal-border-width . 1) (vertical-scroll-bars . nil) (horizontal-scroll-bars . nil) - (right-fringe . 0) + (right-fringe . 3) + (left-fringe . 3) (menu-bar-lines . 0) (tool-bar-lines . 0) (line-spacing . 0) @@ -349,8 +350,11 @@ Checkout `lsp-ui-doc--make-frame', `lsp-ui-doc--move-frame'." buffer `((child-frame-parameters . ,parameter)))) (setq frame (window-frame window))) + ;; workaround + ;; (set-frame-parameter frame 'left-fringe (alist-get 'left-fringe eldoc-box-frame-parameters)) + ;; (set-frame-parameter frame 'right-fringe (alist-get 'right-fringe eldoc-box-frame-parameters)) + (set-face-attribute 'fringe frame :background nil :inherit 'eldoc-box-body) - (fringe-mode 3) (set-window-dedicated-p window t) (redirect-frame-focus frame (frame-parent frame)) (set-face-attribute 'internal-border frame :inherit 'eldoc-box-border) From 9196bfe85810a8555cceafee7ac194e922e5130c Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 3 Oct 2019 09:39:20 -0400 Subject: [PATCH 4/4] Take header line height into account in position calculation (#22) --- eldoc-box.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eldoc-box.el b/eldoc-box.el index 6fffd63..a5d4a14 100644 --- a/eldoc-box.el +++ b/eldoc-box.el @@ -253,8 +253,9 @@ WINDOW nil means use selected window." (when pos-in-window ;; change absolute to relative to native frame (let ((edges (window-edges window t nil t))) - (cons (+ (nth 0 edges) (nth 0 pos-in-window)) - (+ (nth 1 edges) (nth 1 pos-in-window))))))) + (cons (+ (nth 0 edges) (nth 0 pos-in-window)) ; x + (+ (nth 1 edges) (nth 1 pos-in-window) + (- (window-header-line-height window)))))))) ; y (defun eldoc-box--default-at-point-position-function-1 (width height) "See `eldoc-box--default-at-point-position-function'."