Skip to content

Commit 1ee113c

Browse files
Sorixellebrotzeit
authored andcommitted
Use posframe library for lsp-ui-doc
Closes #331, fixes #334.
1 parent ce997c4 commit 1ee113c

File tree

2 files changed

+95
-96
lines changed

2 files changed

+95
-96
lines changed

lsp-ui-doc.el

Lines changed: 94 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
(require 'goto-addr)
3838
(require 'markdown-mode)
3939
(require 'cl-lib)
40+
(require 'posframe)
4041

4142
(when (featurep 'xwidget-internal)
4243
(require 'xwidget))
@@ -144,29 +145,14 @@ Only the `background' is used in this face."
144145
:group 'lsp-ui-doc)
145146

146147
(defvar lsp-ui-doc-frame-parameters
147-
'((left . -1)
148-
(no-focus-on-map . t)
149-
(min-width . 0)
150-
(width . 0)
151-
(min-height . 0)
152-
(height . 0)
153-
(internal-border-width . 1)
148+
'((no-focus-on-map . t)
154149
(vertical-scroll-bars . nil)
155150
(horizontal-scroll-bars . nil)
156-
(right-fringe . 0)
157-
(menu-bar-lines . 0)
158-
(tool-bar-lines . 0)
159-
(line-spacing . 0)
160-
(unsplittable . t)
161-
(undecorated . t)
162-
(top . -1)
163-
(visibility . nil)
164151
(mouse-wheel-frame . nil)
165-
(no-other-frame . t)
152+
(no-accept-focus . nil)
166153
(inhibit-double-buffering . t)
167-
(drag-internal-border . t)
168-
(no-special-glyphs . t)
169-
(desktop-dont-save . t))
154+
(cursor-type . box)
155+
(drag-internal-border . t))
170156
"Frame parameters used to create the frame.")
171157

172158
(defvar lsp-ui-doc-render-function nil
@@ -365,10 +351,7 @@ We don't extract the string that `lps-line' is already displaying."
365351
(when (overlayp lsp-ui-doc--inline-ov)
366352
(delete-overlay lsp-ui-doc--inline-ov))
367353
(when (lsp-ui-doc--get-frame)
368-
(unless lsp-ui-doc-use-webkit
369-
(lsp-ui-doc--with-buffer
370-
(erase-buffer)))
371-
(make-frame-invisible (lsp-ui-doc--get-frame))))
354+
(posframe-hide (lsp-ui-doc--make-buffer-name))))
372355

373356
(defun lsp-ui-doc--buffer-width ()
374357
"Calcul the max width of the buffer."
@@ -403,19 +386,6 @@ We don't extract the string that `lps-line' is already displaying."
403386
(xwidget-resize (lsp-ui-doc--webkit-get-xwidget) offset-width offset-height))
404387
(lsp-ui-doc--move-frame (lsp-ui-doc--get-frame)))
405388

406-
(defun lsp-ui-doc--resize-buffer ()
407-
"If the buffer's width is larger than the current frame, resize it."
408-
(if lsp-ui-doc-use-webkit
409-
(lsp-ui-doc--webkit-execute-script
410-
"[document.querySelector('#lsp-ui-webkit').offsetWidth, document.querySelector('#lsp-ui-webkit').offsetHeight];"
411-
'lsp-ui-doc--webkit-resize-callback)
412-
413-
(let* ((frame-width (frame-width))
414-
(fill-column (min lsp-ui-doc-max-width (- frame-width 5))))
415-
(when (> (lsp-ui-doc--buffer-width) (min lsp-ui-doc-max-width frame-width))
416-
(lsp-ui-doc--with-buffer
417-
(fill-region (point-min) (point-max)))))))
418-
419389
(defun lsp-ui-doc--mv-at-point (frame width height start-x start-y)
420390
"Move FRAME to be where the point is.
421391
WIDTH is the child frame width.
@@ -459,12 +429,11 @@ FRAME just below the symbol at point."
459429
(if (eq lsp-ui-doc-position 'at-point)
460430
(lsp-ui-doc--mv-at-point frame width height left top)
461431
(set-frame-position frame
462-
(max (- frame-right width 10 (frame-char-width)) 10)
432+
(max (- frame-right width ) 0)
463433
(pcase lsp-ui-doc-position
464-
('top (+ top 10))
434+
('top top)
465435
('bottom (- (lsp-ui-doc--line-height 'mode-line)
466-
height
467-
10)))))))
436+
height)))))))
468437

469438
(defun lsp-ui-doc--visit-file (filename)
470439
"Visit FILENAME in the parent frame."
@@ -495,26 +464,31 @@ FN is the function to call on click."
495464
(lsp-ui-doc--put-click (match-beginning 0) (match-end 0)
496465
'browse-url-at-mouse)))))
497466

498-
(defun lsp-ui-doc--render-buffer (string symbol)
499-
"Set the buffer with STRING."
467+
(defvar lsp-ui-doc--render-string nil
468+
"The string to render in the documentation popup.")
469+
(defvar lsp-ui-doc--render-symbol nil
470+
"The symbol to render documentation for.")
471+
472+
(defun lsp-ui-doc--render-buffer ()
473+
"Set the buffer with `lsp-ui-doc--render-string'."
500474
(lsp-ui-doc--with-buffer
501475
(if lsp-ui-doc-use-webkit
502476
(progn
503477
(lsp-ui-doc--webkit-execute-script
504478
(format
505479
"renderMarkdown('%s', '%s');"
506-
symbol
507-
(url-hexify-string string))
480+
lsp-ui-doc--render-symbol
481+
(url-hexify-string lsp-ui-doc--render-string))
508482
'lsp-ui-doc--webkit-resize-callback))
509483
(erase-buffer)
510484
(let ((inline-p (lsp-ui-doc--inline-p)))
511485
(insert (concat (unless inline-p (propertize "\n" 'face '(:height 0.2)))
512-
(s-trim string)
486+
(s-trim lsp-ui-doc--render-string)
513487
(unless inline-p (propertize "\n\n" 'face '(:height 0.3))))))
514488
(lsp-ui-doc--make-clickable-link))
515489
(setq-local face-remapping-alist `((header-line lsp-ui-doc-header)))
516490
(setq-local window-min-height 1)
517-
(setq header-line-format (when lsp-ui-doc-header (concat " " symbol))
491+
(setq header-line-format (when lsp-ui-doc-header (concat " " lsp-ui-doc--render-symbol))
518492
mode-line-format nil
519493
cursor-type nil)))
520494

@@ -619,49 +593,66 @@ HEIGHT is the documentation number of lines."
619593
(defun lsp-ui-doc--inline-p ()
620594
"Return non-nil when the documentation should be display without a child frame."
621595
(or (not lsp-ui-doc-use-childframe)
622-
(not (display-graphic-p))
596+
(not (posframe-workable-p))
623597
(not (fboundp 'display-buffer-in-child-frame))))
624598

625599
(defun lsp-ui-doc--display (symbol string)
626600
"Display the documentation."
601+
(setq lsp-ui-doc--render-symbol symbol
602+
lsp-ui-doc--render-string string)
627603
(when (and lsp-ui-doc-use-webkit (not (featurep 'xwidget-internal)))
628604
(setq lsp-ui-doc-use-webkit nil))
629605
(if (or (null string) (string-empty-p string))
630606
(lsp-ui-doc--hide-frame)
631-
(lsp-ui-doc--render-buffer string symbol)
607+
(lsp-ui-doc--render-buffer)
632608
(if (lsp-ui-doc--inline-p)
633609
(lsp-ui-doc--inline)
634-
(unless (lsp-ui-doc--get-frame)
610+
(when (or (not lsp-ui-doc-use-webkit)
611+
(not (lsp-ui-doc--get-frame)))
635612
(lsp-ui-doc--set-frame (lsp-ui-doc--make-frame)))
636-
(unless lsp-ui-doc-use-webkit
637-
(lsp-ui-doc--resize-buffer)
638-
(lsp-ui-doc--move-frame (lsp-ui-doc--get-frame)))
639613
(unless (frame-visible-p (lsp-ui-doc--get-frame))
640614
(make-frame-visible (lsp-ui-doc--get-frame))))))
641615

616+
(defun lsp-ui-doc--posframe-poshandler-point-top-left-corner (info)
617+
"Place the posframe at the top-left corner of the point without covering the
618+
point.
619+
620+
The structure of INFO is defined in the documentation of `posframe-show'."
621+
(let* ((frame (plist-get info :posframe))
622+
(height (frame-pixel-height frame)))
623+
(posframe-poshandler-point-bottom-left-corner info (- height))))
624+
642625
(defun lsp-ui-doc--make-frame ()
643626
"Create the child frame and return it."
644627
(lsp-ui-doc--delete-frame)
645-
(let* ((after-make-frame-functions nil)
646-
(before-make-frame-hook nil)
647-
(name-buffer (lsp-ui-doc--make-buffer-name))
648-
(buffer (get-buffer name-buffer))
649-
(params (append lsp-ui-doc-frame-parameters
650-
`((name . "")
651-
(default-minibuffer-frame . ,(selected-frame))
652-
(minibuffer . ,(minibuffer-window))
653-
(left-fringe . ,(frame-char-width))
654-
(background-color . ,(face-background 'lsp-ui-doc-background nil t)))))
655-
(window (display-buffer-in-child-frame
656-
buffer
657-
`((child-frame-parameters . ,params))))
658-
(frame (window-frame window)))
628+
(let* ((before-make-frame-hook nil)
629+
(buffer-name (lsp-ui-doc--make-buffer-name))
630+
(buffer (get-buffer-create buffer-name))
631+
(params (append lsp-ui-doc-frame-parameters
632+
`((name . "")
633+
(default-minibuffer-frame . ,(selected-frame))
634+
(minibuffer . ,(minibuffer-window)))))
635+
(position (pcase (list lsp-ui-doc-position lsp-ui-doc-alignment)
636+
('(top frame) #'posframe-poshandler-frame-top-right-corner)
637+
('(top window) #'posframe-poshandler-window-top-right-corner)
638+
('(bottom frame) #'posframe-poshandler-frame-bottom-right-corner)
639+
('(bottom window) #'posframe-poshandler-window-bottom-right-corner)
640+
('(at-point frame) #'lsp-ui-doc--posframe-poshandler-point-top-left-corner)
641+
('(at-point window) #'lsp-ui-doc--posframe-poshandler-point-top-left-corner)))
642+
(frame (posframe-show buffer
643+
:width lsp-ui-doc-max-width
644+
:height lsp-ui-doc-max-height
645+
:poshandler position
646+
:internal-border-width 1
647+
:internal-border-color lsp-ui-doc-border
648+
:left-fringe t
649+
:right-fringe t
650+
:background-color (face-background 'lsp-ui-doc-background nil t)
651+
:override-parameters params))
652+
(window (frame-root-window frame)))
659653
(with-current-buffer buffer
660-
(lsp-ui-doc-frame-mode 1))
661-
(set-frame-parameter nil 'lsp-ui-doc-buffer buffer)
662-
(set-window-dedicated-p window t)
663-
(redirect-frame-focus frame (frame-parent frame))
664-
(set-face-background 'internal-border lsp-ui-doc-border frame)
654+
(lsp-ui-doc-frame-mode 1)
655+
(visual-line-mode 1))
665656
(set-face-background 'fringe nil frame)
666657
(run-hook-with-args 'lsp-ui-doc-frame-hook frame window)
667658
(when lsp-ui-doc-use-webkit
@@ -670,6 +661,8 @@ HEIGHT is the documentation number of lines."
670661
(interactive)
671662

672663
(let ((xwidget-event-type (nth 1 last-input-event)))
664+
(when (eq xwidget-event-type 'load-changed)
665+
(lsp-ui-doc--render-buffer))
673666
;; (when (eq xwidget-event-type 'load-changed)
674667
;; (lsp-ui-doc--move-frame (lsp-ui-doc--get-frame)))
675668

@@ -727,7 +720,7 @@ BUFFER is the buffer where the request has been made."
727720
(defun lsp-ui-doc--delete-frame ()
728721
"Delete the child frame if it exists."
729722
(-when-let (frame (lsp-ui-doc--get-frame))
730-
(delete-frame frame)
723+
(posframe-delete-frame (lsp-ui-doc--make-buffer-name))
731724
(lsp-ui-doc--set-frame nil)))
732725

733726
(defun lsp-ui-doc--visible-p ()
@@ -766,6 +759,27 @@ before, or if the new window is the minibuffer."
766759
(and (buffer-live-p it) it)
767760
(kill-buffer it)))
768761

762+
(define-minor-mode lsp-ui-doc-frame-mode
763+
"Marker mode to add additional key bind for lsp-ui-doc-frame."
764+
:init-value nil
765+
:lighter ""
766+
:group lsp-ui-doc
767+
:keymap `(([?q] . lsp-ui-doc-unfocus-frame)))
768+
769+
(defun lsp-ui-doc-focus-frame ()
770+
"Focus into lsp-ui-doc-frame."
771+
(interactive)
772+
(when (lsp-ui-doc--frame-visible-p)
773+
(lsp-ui-doc--with-buffer
774+
(setq cursor-type t))
775+
(select-frame-set-input-focus (lsp-ui-doc--get-frame))))
776+
777+
(defun lsp-ui-doc-unfocus-frame ()
778+
"Unfocus from lsp-ui-doc-frame."
779+
(interactive)
780+
(when-let ((frame (frame-parent (lsp-ui-doc--get-frame))))
781+
(select-frame-set-input-focus frame)))
782+
769783
(define-minor-mode lsp-ui-doc-mode
770784
"Minor mode for showing hover information in child frame."
771785
:init-value nil
@@ -783,11 +797,17 @@ before, or if the new window is the minibuffer."
783797
(cl-callf copy-tree frameset-filter-alist)
784798
(push '(lsp-ui-doc-frame . :never) frameset-filter-alist)))
785799
(add-hook 'post-command-hook 'lsp-ui-doc--make-request nil t)
786-
(add-hook 'delete-frame-functions 'lsp-ui-doc--on-delete nil t))
800+
(add-hook 'delete-frame-functions 'lsp-ui-doc--on-delete nil t)
801+
(advice-add #'posframe--redirect-posframe-focus
802+
:before-until (lambda (&rest _)
803+
lsp-ui-doc-frame-mode)
804+
'((name . lsp-ui-doc--dont-redirect-posframe))))
787805
(t
788806
(lsp-ui-doc-hide)
789807
(remove-hook 'post-command-hook 'lsp-ui-doc--make-request t)
790-
(remove-hook 'delete-frame-functions 'lsp-ui-doc--on-delete t))))
808+
(remove-hook 'delete-frame-functions 'lsp-ui-doc--on-delete t)
809+
(advice-remove #'posframe--redirect-posframe-focus
810+
'lsp-ui-doc--dont-redirect-posframe))))
791811

792812
(defun lsp-ui-doc-enable (enable)
793813
"Enable/disable ‘lsp-ui-doc-mode’.
@@ -825,26 +845,5 @@ It is supposed to be called from `lsp-ui--toggle'"
825845
(cancel-timer lsp-ui-doc--unfocus-frame-timer))
826846
(add-hook 'post-command-hook 'lsp-ui-doc--glance-hide-frame))
827847

828-
(define-minor-mode lsp-ui-doc-frame-mode
829-
"Marker mode to add additional key bind for lsp-ui-doc-frame."
830-
:init-value nil
831-
:lighter ""
832-
:group lsp-ui-doc
833-
:keymap `(([?q] . lsp-ui-doc-unfocus-frame)))
834-
835-
(defun lsp-ui-doc-focus-frame ()
836-
"Focus into lsp-ui-doc-frame."
837-
(interactive)
838-
(when (lsp-ui-doc--frame-visible-p)
839-
(lsp-ui-doc--with-buffer
840-
(setq cursor-type t))
841-
(select-frame-set-input-focus (lsp-ui-doc--get-frame))))
842-
843-
(defun lsp-ui-doc-unfocus-frame ()
844-
"Unfocus from lsp-ui-doc-frame."
845-
(interactive)
846-
(when-let ((frame (frame-parent (lsp-ui-doc--get-frame))))
847-
(select-frame-set-input-focus frame)))
848-
849848
(provide 'lsp-ui-doc)
850849
;;; lsp-ui-doc.el ends here

lsp-ui.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
;; Author: Sebastien Chapuis <[email protected]>, Fangrui Song <[email protected]>
77
;; Keywords: languages, tools
88
;; URL: https://github.com/emacs-lsp/lsp-ui
9-
;; Package-Requires: ((emacs "26.1") (dash "2.14") (dash-functional "1.2.0") (lsp-mode "6.0") (markdown-mode "2.3"))
9+
;; Package-Requires: ((emacs "26.1") (dash "2.14") (dash-functional "1.2.0") (lsp-mode "6.0") (markdown-mode "2.3") (posframe "0.7.0"))
1010
;; Version: 6.3
1111

1212
;;; License

0 commit comments

Comments
 (0)