From 8a8ac9ec56c1999fa650ed094e87ba2816b6a4b5 Mon Sep 17 00:00:00 2001
From: Eric Dallo <ericdallo06@hotmail.com>
Date: Sat, 1 Aug 2020 15:23:11 -0300
Subject: [PATCH 1/6] Add posframe integration to lsp-ui-doc

---
 Cask          |   1 +
 lsp-ui-doc.el | 183 +++++++++++++++++++++++++-------------------------
 lsp-ui.el     |   2 +-
 3 files changed, 92 insertions(+), 94 deletions(-)

diff --git a/Cask b/Cask
index 22f56301..d7354324 100644
--- a/Cask
+++ b/Cask
@@ -7,6 +7,7 @@
 (depends-on "lsp-mode")
 (depends-on "markdown-mode")
 (depends-on "rustic")
+(depends-on "posframe")
 
 (package-file "lsp-ui.el")
 (files "*.el" "lsp-ui-doc.html")
diff --git a/lsp-ui-doc.el b/lsp-ui-doc.el
index 66a9a383..a9c70034 100644
--- a/lsp-ui-doc.el
+++ b/lsp-ui-doc.el
@@ -37,6 +37,7 @@
 (require 'goto-addr)
 (require 'markdown-mode)
 (require 'cl-lib)
+(require 'posframe)
 
 (when (featurep 'xwidget-internal)
   (require 'xwidget))
@@ -144,29 +145,14 @@ Only the `background' is used in this face."
   :group 'lsp-ui-doc)
 
 (defvar lsp-ui-doc-frame-parameters
-  '((left . -1)
-    (no-focus-on-map . t)
-    (min-width  . 0)
-    (width  . 0)
-    (min-height  . 0)
-    (height  . 0)
-    (internal-border-width . 1)
+  '((no-focus-on-map . t)
     (vertical-scroll-bars . nil)
     (horizontal-scroll-bars . nil)
-    (right-fringe . 0)
-    (menu-bar-lines . 0)
-    (tool-bar-lines . 0)
-    (line-spacing . 0)
-    (unsplittable . t)
-    (undecorated . t)
-    (top . -1)
-    (visibility . nil)
     (mouse-wheel-frame . nil)
-    (no-other-frame . t)
+    (no-accept-focus . nil)
     (inhibit-double-buffering . t)
-    (drag-internal-border . t)
-    (no-special-glyphs . t)
-    (desktop-dont-save . t))
+    (cursor-type . box)
+    (drag-internal-border . t))
   "Frame parameters used to create the frame.")
 
 (defvar lsp-ui-doc-render-function nil
@@ -365,10 +351,7 @@ We don't extract the string that `lps-line' is already displaying."
   (when (overlayp lsp-ui-doc--inline-ov)
     (delete-overlay lsp-ui-doc--inline-ov))
   (when (lsp-ui-doc--get-frame)
-    (unless lsp-ui-doc-use-webkit
-      (lsp-ui-doc--with-buffer
-       (erase-buffer)))
-    (make-frame-invisible (lsp-ui-doc--get-frame))))
+    (posframe-hide (lsp-ui-doc--make-buffer-name))))
 
 (defun lsp-ui-doc--buffer-width ()
   "Calcul the max width of the buffer."
@@ -403,19 +386,6 @@ We don't extract the string that `lps-line' is already displaying."
     (xwidget-resize (lsp-ui-doc--webkit-get-xwidget) offset-width offset-height))
   (lsp-ui-doc--move-frame (lsp-ui-doc--get-frame)))
 
-(defun lsp-ui-doc--resize-buffer ()
-  "If the buffer's width is larger than the current frame, resize it."
-  (if lsp-ui-doc-use-webkit
-      (lsp-ui-doc--webkit-execute-script
-       "[document.querySelector('#lsp-ui-webkit').offsetWidth, document.querySelector('#lsp-ui-webkit').offsetHeight];"
-       'lsp-ui-doc--webkit-resize-callback)
-
-    (let* ((frame-width (frame-width))
-           (fill-column (min lsp-ui-doc-max-width (- frame-width 5))))
-      (when (> (lsp-ui-doc--buffer-width) (min lsp-ui-doc-max-width frame-width))
-        (lsp-ui-doc--with-buffer
-         (fill-region (point-min) (point-max)))))))
-
 (defun lsp-ui-doc--mv-at-point (frame width height start-x start-y)
   "Move FRAME to be where the point is.
 WIDTH is the child frame width.
@@ -459,12 +429,11 @@ FRAME just below the symbol at point."
     (if (eq lsp-ui-doc-position 'at-point)
         (lsp-ui-doc--mv-at-point frame width height left top)
       (set-frame-position frame
-                          (max (- frame-right width 10 (frame-char-width)) 10)
+                          (max (- frame-right width) 0)
                           (pcase lsp-ui-doc-position
-                            ('top (+ top 10))
+                            ('top top)
                             ('bottom (- (lsp-ui-doc--line-height 'mode-line)
-                                        height
-                                        10)))))))
+                                        height)))))))
 
 (defun lsp-ui-doc--visit-file (filename)
   "Visit FILENAME in the parent frame."
@@ -495,26 +464,31 @@ FN is the function to call on click."
         (lsp-ui-doc--put-click (match-beginning 0) (match-end 0)
                                'browse-url-at-mouse)))))
 
-(defun lsp-ui-doc--render-buffer (string symbol)
-  "Set the buffer with STRING."
+(defvar lsp-ui-doc--render-string nil
+  "The string to render in the documentation popup.")
+(defvar lsp-ui-doc--render-symbol nil
+  "The symbol to render documentation for.")
+
+(defun lsp-ui-doc--render-buffer ()
+  "Set the buffer with `lsp-ui-doc--render-string'."
   (lsp-ui-doc--with-buffer
    (if lsp-ui-doc-use-webkit
        (progn
          (lsp-ui-doc--webkit-execute-script
           (format
            "renderMarkdown('%s', '%s');"
-           symbol
-           (url-hexify-string string))
+           lsp-ui-doc--render-symbol
+           (url-hexify-string lsp-ui-doc--render-string))
           'lsp-ui-doc--webkit-resize-callback))
      (erase-buffer)
      (let ((inline-p (lsp-ui-doc--inline-p)))
        (insert (concat (unless inline-p (propertize "\n" 'face '(:height 0.2)))
-                       (s-trim string)
+                       (s-trim lsp-ui-doc--render-string)
                        (unless inline-p (propertize "\n\n" 'face '(:height 0.3))))))
      (lsp-ui-doc--make-clickable-link))
    (setq-local face-remapping-alist `((header-line lsp-ui-doc-header)))
    (setq-local window-min-height 1)
-   (setq header-line-format (when lsp-ui-doc-header (concat " " symbol))
+   (setq header-line-format (when lsp-ui-doc-header (concat " " lsp-ui-doc--render-symbol))
          mode-line-format nil
          cursor-type nil)))
 
@@ -619,49 +593,64 @@ HEIGHT is the documentation number of lines."
 (defun lsp-ui-doc--inline-p ()
   "Return non-nil when the documentation should be display without a child frame."
   (or (not lsp-ui-doc-use-childframe)
-      (not (display-graphic-p))
+      (not (posframe-workable-p))
       (not (fboundp 'display-buffer-in-child-frame))))
 
 (defun lsp-ui-doc--display (symbol string)
-  "Display the documentation."
+  "Display the documentation for SYMBOL and STRING."
+  (setq lsp-ui-doc--render-symbol symbol
+        lsp-ui-doc--render-string string)
   (when (and lsp-ui-doc-use-webkit (not (featurep 'xwidget-internal)))
     (setq lsp-ui-doc-use-webkit nil))
   (if (or (null string) (string-empty-p string))
       (lsp-ui-doc--hide-frame)
-    (lsp-ui-doc--render-buffer string symbol)
+    (lsp-ui-doc--render-buffer)
     (if (lsp-ui-doc--inline-p)
         (lsp-ui-doc--inline)
-      (unless (lsp-ui-doc--get-frame)
+      (when (or (not lsp-ui-doc-use-webkit)
+                (not (lsp-ui-doc--get-frame)))
         (lsp-ui-doc--set-frame (lsp-ui-doc--make-frame)))
-      (unless lsp-ui-doc-use-webkit
-        (lsp-ui-doc--resize-buffer)
-        (lsp-ui-doc--move-frame (lsp-ui-doc--get-frame)))
       (unless (frame-visible-p (lsp-ui-doc--get-frame))
         (make-frame-visible (lsp-ui-doc--get-frame))))))
 
+(defun lsp-ui-doc--posframe-poshandler-point-top-left-corner (info)
+  "Place the posframe at the top-left corner of the point without covering it.
+The structure of INFO is defined in the documentation of `posframe-show'."
+  (let* ((frame (plist-get info :posframe))
+         (height (frame-pixel-height frame)))
+    (posframe-poshandler-point-bottom-left-corner info (- height))))
+
 (defun lsp-ui-doc--make-frame ()
   "Create the child frame and return it."
   (lsp-ui-doc--delete-frame)
-  (let* ((after-make-frame-functions nil)
-         (before-make-frame-hook nil)
-         (name-buffer (lsp-ui-doc--make-buffer-name))
-         (buffer (get-buffer name-buffer))
+  (let* ((before-make-frame-hook nil)
+         (buffer-name (lsp-ui-doc--make-buffer-name))
+         (buffer (get-buffer-create buffer-name))
          (params (append lsp-ui-doc-frame-parameters
                          `((name . "")
                            (default-minibuffer-frame . ,(selected-frame))
-                           (minibuffer . ,(minibuffer-window))
-                           (left-fringe . ,(frame-char-width))
-                           (background-color . ,(face-background 'lsp-ui-doc-background nil t)))))
-         (window (display-buffer-in-child-frame
-                  buffer
-                  `((child-frame-parameters . ,params))))
-         (frame (window-frame window)))
+                           (minibuffer . ,(minibuffer-window)))))
+         (position (pcase (list lsp-ui-doc-position lsp-ui-doc-alignment)
+                     ('(top frame) #'posframe-poshandler-frame-top-right-corner)
+                     ('(top window) #'posframe-poshandler-window-top-right-corner)
+                     ('(bottom frame) #'posframe-poshandler-frame-bottom-right-corner)
+                     ('(bottom window) #'posframe-poshandler-window-bottom-right-corner)
+                     ('(at-point frame) #'lsp-ui-doc--posframe-poshandler-point-top-left-corner)
+                     ('(at-point window) #'lsp-ui-doc--posframe-poshandler-point-top-left-corner)))
+         (frame (posframe-show buffer
+                               :width lsp-ui-doc-max-width
+                               :height lsp-ui-doc-max-height
+                               :poshandler position
+                               :internal-border-width 1
+                               :internal-border-color lsp-ui-doc-border
+                               :left-fringe t
+                               :right-fringe t
+                               :background-color (face-background 'lsp-ui-doc-background nil t)
+                               :override-parameters params))
+         (window (frame-root-window frame)))
     (with-current-buffer buffer
-      (lsp-ui-doc-frame-mode 1))
-    (set-frame-parameter nil 'lsp-ui-doc-buffer buffer)
-    (set-window-dedicated-p window t)
-    (redirect-frame-focus frame (frame-parent frame))
-    (set-face-background 'internal-border lsp-ui-doc-border frame)
+      (lsp-ui-doc-frame-mode 1)
+      (visual-line-mode 1))
     (set-face-background 'fringe nil frame)
     (run-hook-with-args 'lsp-ui-doc-frame-hook frame window)
     (when lsp-ui-doc-use-webkit
@@ -670,6 +659,8 @@ HEIGHT is the documentation number of lines."
           (interactive)
 
           (let ((xwidget-event-type (nth 1 last-input-event)))
+            (when (eq xwidget-event-type 'load-changed)
+              (lsp-ui-doc--render-buffer))
             ;; (when (eq xwidget-event-type 'load-changed)
             ;;   (lsp-ui-doc--move-frame (lsp-ui-doc--get-frame)))
 
@@ -727,7 +718,7 @@ BUFFER is the buffer where the request has been made."
 (defun lsp-ui-doc--delete-frame ()
   "Delete the child frame if it exists."
   (-when-let (frame (lsp-ui-doc--get-frame))
-    (delete-frame frame)
+    (posframe-delete-frame (lsp-ui-doc--make-buffer-name))
     (lsp-ui-doc--set-frame nil)))
 
 (defun lsp-ui-doc--visible-p ()
@@ -766,6 +757,27 @@ before, or if the new window is the minibuffer."
     (and (buffer-live-p it) it)
     (kill-buffer it)))
 
+(define-minor-mode lsp-ui-doc-frame-mode
+  "Marker mode to add additional key bind for lsp-ui-doc-frame."
+  :init-value nil
+  :lighter ""
+  :group lsp-ui-doc
+  :keymap `(([?q] . lsp-ui-doc-unfocus-frame)))
+
+(defun lsp-ui-doc-focus-frame ()
+  "Focus into lsp-ui-doc-frame."
+  (interactive)
+  (when (lsp-ui-doc--frame-visible-p)
+    (lsp-ui-doc--with-buffer
+     (setq cursor-type t))
+    (select-frame-set-input-focus (lsp-ui-doc--get-frame))))
+
+(defun lsp-ui-doc-unfocus-frame ()
+  "Unfocus from lsp-ui-doc-frame."
+  (interactive)
+  (when-let ((frame (frame-parent (lsp-ui-doc--get-frame))))
+    (select-frame-set-input-focus frame)))
+
 (define-minor-mode lsp-ui-doc-mode
   "Minor mode for showing hover information in child frame."
   :init-value nil
@@ -783,11 +795,17 @@ before, or if the new window is the minibuffer."
         (cl-callf copy-tree frameset-filter-alist)
         (push '(lsp-ui-doc-frame . :never) frameset-filter-alist)))
     (add-hook 'post-command-hook 'lsp-ui-doc--make-request nil t)
-    (add-hook 'delete-frame-functions 'lsp-ui-doc--on-delete nil t))
+    (add-hook 'delete-frame-functions 'lsp-ui-doc--on-delete nil t)
+    (advice-add #'posframe--redirect-posframe-focus
+                :before-until (lambda (&rest _)
+                                lsp-ui-doc-frame-mode)
+                '((name . lsp-ui-doc--dont-redirect-posframe))))
    (t
     (lsp-ui-doc-hide)
     (remove-hook 'post-command-hook 'lsp-ui-doc--make-request t)
-    (remove-hook 'delete-frame-functions 'lsp-ui-doc--on-delete t))))
+    (remove-hook 'delete-frame-functions 'lsp-ui-doc--on-delete t)
+    (advice-remove #'posframe--redirect-posframe-focus
+                   'lsp-ui-doc--dont-redirect-posframe))))
 
 (defun lsp-ui-doc-enable (enable)
   "Enable/disable ‘lsp-ui-doc-mode’.
@@ -825,26 +843,5 @@ It is supposed to be called from `lsp-ui--toggle'"
     (cancel-timer lsp-ui-doc--unfocus-frame-timer))
   (add-hook 'post-command-hook 'lsp-ui-doc--glance-hide-frame))
 
-(define-minor-mode lsp-ui-doc-frame-mode
-  "Marker mode to add additional key bind for lsp-ui-doc-frame."
-  :init-value nil
-  :lighter ""
-  :group lsp-ui-doc
-  :keymap `(([?q] . lsp-ui-doc-unfocus-frame)))
-
-(defun lsp-ui-doc-focus-frame ()
-  "Focus into lsp-ui-doc-frame."
-  (interactive)
-  (when (lsp-ui-doc--frame-visible-p)
-    (lsp-ui-doc--with-buffer
-     (setq cursor-type t))
-    (select-frame-set-input-focus (lsp-ui-doc--get-frame))))
-
-(defun lsp-ui-doc-unfocus-frame ()
-  "Unfocus from lsp-ui-doc-frame."
-  (interactive)
-  (when-let ((frame (frame-parent (lsp-ui-doc--get-frame))))
-    (select-frame-set-input-focus frame)))
-
 (provide 'lsp-ui-doc)
 ;;; lsp-ui-doc.el ends here
diff --git a/lsp-ui.el b/lsp-ui.el
index b5488875..9a1e3a95 100644
--- a/lsp-ui.el
+++ b/lsp-ui.el
@@ -6,7 +6,7 @@
 ;; Author: Sebastien Chapuis <sebastien@chapu.is>, Fangrui Song <i@maskray.me>
 ;; Keywords: languages, tools
 ;; URL: https://github.com/emacs-lsp/lsp-ui
-;; Package-Requires: ((emacs "26.1") (dash "2.14") (dash-functional "1.2.0") (lsp-mode "6.0") (markdown-mode "2.3"))
+;; 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"))
 ;; Version: 7.0
 
 ;;; License

From ab97add65c2587a27491f83e119da712eb02ea14 Mon Sep 17 00:00:00 2001
From: Eric Dallo <ericdallo06@hotmail.com>
Date: Sat, 1 Aug 2020 17:28:36 -0300
Subject: [PATCH 2/6] Fix hide window when buffer didn't change

---
 lsp-ui-doc.el | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/lsp-ui-doc.el b/lsp-ui-doc.el
index a9c70034..f5ff52d2 100644
--- a/lsp-ui-doc.el
+++ b/lsp-ui-doc.el
@@ -184,6 +184,8 @@ Because some variables are buffer local.")
 (defvar-local lsp-ui-doc--bounds nil)
 (defvar-local lsp-ui-doc--timer nil)
 
+(defvar-local lsp-ui-doc--inline-width nil)
+
 (defconst lsp-ui-doc--buffer-prefix " *lsp-ui-doc-")
 
 (defmacro lsp-ui-doc--with-buffer (&rest body)
@@ -506,8 +508,6 @@ FN is the function to call on click."
       (setq start (text-property-not-all 0 (length string) 'invisible nil string)))
     string))
 
-(defvar-local lsp-ui-doc--inline-width nil)
-
 (defun lsp-ui-doc--inline-window-width nil
   (- (min (window-text-width)
           (window-body-width))
@@ -620,6 +620,8 @@ The structure of INFO is defined in the documentation of `posframe-show'."
          (height (frame-pixel-height frame)))
     (posframe-poshandler-point-bottom-left-corner info (- height))))
 
+(defvar-local lsp-ui-doc--original-buffer-with-frame nil)
+
 (defun lsp-ui-doc--make-frame ()
   "Create the child frame and return it."
   (lsp-ui-doc--delete-frame)
@@ -648,6 +650,7 @@ The structure of INFO is defined in the documentation of `posframe-show'."
                                :background-color (face-background 'lsp-ui-doc-background nil t)
                                :override-parameters params))
          (window (frame-root-window frame)))
+    (setq lsp-ui-doc--original-buffer-with-frame (current-buffer))
     (with-current-buffer buffer
       (lsp-ui-doc-frame-mode 1)
       (visual-line-mode 1))
@@ -746,7 +749,14 @@ before, or if the new window is the minibuffer."
 (advice-add #'select-window :around #'lsp-ui-doc-hide-frame-on-window-change)
 
 (advice-add 'load-theme :before (lambda (&rest _) (lsp-ui-doc--delete-frame)))
-(add-hook 'window-configuration-change-hook #'lsp-ui-doc--hide-frame)
+
+(defun lsp-ui-doc--hide-checking-buffer ()
+  "Check if buffer has changed before hide hook."
+  (when (or (null lsp-ui-doc--original-buffer-with-frame)
+            (not (eq (current-buffer) lsp-ui-doc--original-buffer-with-frame)))
+    (lsp-ui-doc--hide-frame)))
+
+(add-hook 'window-configuration-change-hook #'lsp-ui-doc--hide-checking-buffer)
 
 (advice-add #'keyboard-quit :before #'lsp-ui-doc--hide-frame)
 

From 9c5b01f995b72aeff43e75c376f17cd6211353e2 Mon Sep 17 00:00:00 2001
From: Eric Dallo <ericdallo06@hotmail.com>
Date: Sat, 1 Aug 2020 17:50:29 -0300
Subject: [PATCH 3/6] Avoid mouse focus

---
 lsp-ui-doc.el | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lsp-ui-doc.el b/lsp-ui-doc.el
index f5ff52d2..4d77394a 100644
--- a/lsp-ui-doc.el
+++ b/lsp-ui-doc.el
@@ -150,6 +150,8 @@ Only the `background' is used in this face."
     (horizontal-scroll-bars . nil)
     (mouse-wheel-frame . nil)
     (no-accept-focus . nil)
+    (focus-follows-mouse . nil)
+    (mouse-autoselect-window . nil)
     (inhibit-double-buffering . t)
     (cursor-type . box)
     (drag-internal-border . t))

From 0728d418610ff9e2c904f54665fe781e98fac046 Mon Sep 17 00:00:00 2001
From: Eric Dallo <ericdallo06@hotmail.com>
Date: Sat, 1 Aug 2020 17:51:07 -0300
Subject: [PATCH 4/6] Fix performace following
 https://github.com/emacs-lsp/lsp-ui/pull/465/files

---
 lsp-ui-doc.el | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/lsp-ui-doc.el b/lsp-ui-doc.el
index 4d77394a..61c106c4 100644
--- a/lsp-ui-doc.el
+++ b/lsp-ui-doc.el
@@ -609,8 +609,7 @@ HEIGHT is the documentation number of lines."
     (lsp-ui-doc--render-buffer)
     (if (lsp-ui-doc--inline-p)
         (lsp-ui-doc--inline)
-      (when (or (not lsp-ui-doc-use-webkit)
-                (not (lsp-ui-doc--get-frame)))
+      (unless lsp-ui-doc-use-webkit
         (lsp-ui-doc--set-frame (lsp-ui-doc--make-frame)))
       (unless (frame-visible-p (lsp-ui-doc--get-frame))
         (make-frame-visible (lsp-ui-doc--get-frame))))))
@@ -626,7 +625,6 @@ The structure of INFO is defined in the documentation of `posframe-show'."
 
 (defun lsp-ui-doc--make-frame ()
   "Create the child frame and return it."
-  (lsp-ui-doc--delete-frame)
   (let* ((before-make-frame-hook nil)
          (buffer-name (lsp-ui-doc--make-buffer-name))
          (buffer (get-buffer-create buffer-name))

From 96e79b3cbf1ae218224707e8b3710a2e8e7d63c7 Mon Sep 17 00:00:00 2001
From: Eric Dallo <ericdallo06@hotmail.com>
Date: Sat, 1 Aug 2020 17:58:13 -0300
Subject: [PATCH 5/6] Fix tests

---
 test/test-helper.el | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/test-helper.el b/test/test-helper.el
index 90a3d6fd..3e5ec44e 100644
--- a/test/test-helper.el
+++ b/test/test-helper.el
@@ -5,6 +5,7 @@
              (file-name-as-directory (f-parent (f-parent (f-this-file)))))
 
 (require 'lsp-mode)
+(require 'lsp-modeline)
 (require 'lsp-rust)
 (require 'lsp-ui)
 (require 'flycheck)

From 106816385e9273b6c591b7e57291070ef1fdcc5f Mon Sep 17 00:00:00 2001
From: Eric Dallo <ericdallo06@hotmail.com>
Date: Sat, 1 Aug 2020 22:43:42 -0300
Subject: [PATCH 6/6] Make variable global

---
 lsp-ui-doc.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lsp-ui-doc.el b/lsp-ui-doc.el
index 61c106c4..3f182989 100644
--- a/lsp-ui-doc.el
+++ b/lsp-ui-doc.el
@@ -621,7 +621,7 @@ The structure of INFO is defined in the documentation of `posframe-show'."
          (height (frame-pixel-height frame)))
     (posframe-poshandler-point-bottom-left-corner info (- height))))
 
-(defvar-local lsp-ui-doc--original-buffer-with-frame nil)
+(defvar lsp-ui-doc--original-buffer-with-frame nil)
 
 (defun lsp-ui-doc--make-frame ()
   "Create the child frame and return it."