eldoc-frame/eldoc-box.el

187 lines
6.2 KiB
EmacsLisp
Raw Normal View History

2018-12-11 14:15:00 +00:00
;;; eldoc-box.el --- Display documentation in childframe -*- lexical-binding: t; -*-
2018-12-11 01:26:08 +00:00
;; Copyright (C) 2018 Yuan Fu
2018-12-11 17:24:52 +00:00
;; Version: 1.0
2018-12-11 01:26:08 +00:00
;; Author: Yuan Fu <casouri@gmail.com>
;; Not really because I copied code from lsp-ui-doc.el
;; which is written by Sebastien Chapuis <sebastien@chapu.is>
;; And I removed the license so the previous commits with license
;; are not really with license.
;; I hope I don't get in troubles because of this.
2018-12-11 17:24:52 +00:00
;; URL: https://github.com/casouri/eldoc-box
;; Package-Requires: ((emacs "26.1"))
2018-12-11 01:26:08 +00:00
;;; This file is NOT part of GNU Emacs
;;; Commentary:
;;
;;; Code:
;;
2018-12-11 16:27:54 +00:00
(require 'cl-lib)
2018-12-11 01:26:08 +00:00
;;;; Userland
;;;;; Variable
2018-12-11 14:15:00 +00:00
(defface eldoc-box-border '((((background dark)) . (:background "white"))
2018-12-11 21:34:37 +00:00
(((background light)) . (:background "black")))
"The border color used in childframe.")
2018-12-11 14:15:00 +00:00
(defface eldoc-box-body '((t . (:background nil)))
2018-12-11 05:20:21 +00:00
"Body face used in eglot doc childframe. Only :background is used.")
(defvar eldoc-box-only-multi-line nil
"If non-nil, only use childframe when there are more than one line.")
2018-12-11 14:15:00 +00:00
(defvar eldoc-box-frame-parameters
2018-12-11 01:26:08 +00:00
'(
;; (left . -1)
(no-accept-focus . t)
(no-focus-on-map . t)
(min-width . 0)
;; (width . 0)
(min-height . 0)
;; (height . 0)
(internal-border-width . 1)
(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)
2018-12-11 01:26:08 +00:00
(mouse-wheel-frame . nil)
(no-other-frame . t)
(cursor-type . nil)
(inhibit-double-buffering . t)
(drag-internal-border . t)
(no-special-glyphs . t)
(desktop-dont-save . t))
"Frame parameters used to create the frame.")
2018-12-11 14:15:00 +00:00
(defvar eldoc-box-max-pixel-width 800
2018-12-11 01:26:08 +00:00
"Maximum width of doc childframw in pixel.")
2018-12-11 14:15:00 +00:00
(defvar eldoc-box-max-pixel-height 1400
2018-12-11 01:26:08 +00:00
"Maximum height of doc childframw in pixel.")
;;;;; Function
2018-12-11 14:15:00 +00:00
(defun eldoc-box-help-at-point ()
2018-12-11 01:26:08 +00:00
"Display hover info at point in a childframe."
(interactive)
(let ((doc (funcall eldoc-documentation-function)))
(when doc
(eldoc-box--display doc)
(eldoc-box--inject-quit-func))))
2018-12-11 01:26:08 +00:00
2018-12-11 14:15:00 +00:00
(defun eldoc-box-quit-frame ()
2018-12-11 01:26:08 +00:00
"Hide childframe used by eglot doc."
(interactive)
;; TODO
2018-12-11 14:15:00 +00:00
(when eldoc-box--frame
(make-frame-invisible eldoc-box--frame t)))
2018-12-11 01:26:08 +00:00
;;;; Backstage
;;;;; Variable
2018-12-11 14:15:00 +00:00
(defvar eldoc-box--frame nil
2018-12-11 01:26:08 +00:00
"The frame to display doc.")
2018-12-11 14:15:00 +00:00
(defvar eldoc-box--buffer "*eldoc-box*"
2018-12-11 01:26:08 +00:00
"The buffer used to display eglot doc.")
;;;;; Function
2018-12-11 14:15:00 +00:00
(defun eldoc-box--display (str)
2018-12-11 01:26:08 +00:00
"Display STR in childframe."
2018-12-11 14:15:00 +00:00
(let ((doc-buffer (get-buffer-create eldoc-box--buffer)))
2018-12-11 01:26:08 +00:00
(with-current-buffer doc-buffer
(setq mode-line-format nil)
(erase-buffer)
(insert str)
(eldoc-box--get-frame doc-buffer))))
2018-12-11 01:26:08 +00:00
2018-12-11 14:15:00 +00:00
(defun eldoc-box-quit-hook ()
2018-12-11 01:26:08 +00:00
"Quit eglot doc childframe and remove self from hook."
2018-12-11 14:15:00 +00:00
(eldoc-box-quit-frame)
(remove-hook 'pre-command-hook #'eldoc-box-quit-hook t))
2018-12-11 01:26:08 +00:00
2018-12-11 14:15:00 +00:00
(defun eldoc-box--inject-quit-func ()
2018-12-11 01:26:08 +00:00
"Inject quit function into `pre-command-hook' so doing anything will quit eglot doc childframe."
2018-12-11 14:15:00 +00:00
(add-hook 'pre-command-hook #'eldoc-box-quit-hook t t))
2018-12-11 01:26:08 +00:00
2018-12-11 14:15:00 +00:00
(defun eldoc-box--window-side ()
2018-12-11 01:26:08 +00:00
"Return 'left if the selected window is on the left,
'right if on the right. Return 'left if there is only one window."
(let ((left-window(window-at 0 0)))
(if (eq left-window (selected-window))
'left
'right)))
2018-12-11 14:15:00 +00:00
(defun eldoc-box--get-frame (buffer)
2018-12-11 01:26:08 +00:00
"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)
2018-12-11 14:15:00 +00:00
(parameter (append eldoc-box-frame-parameters
2018-12-11 01:26:08 +00:00
`((default-minibuffer-frame . ,(selected-frame))
(minibuffer . ,(minibuffer-window))
(left-fringe . ,(frame-char-width)))))
(window (or (and eldoc-box--frame (frame-selected-window eldoc-box--frame))
(display-buffer-in-child-frame
buffer
`((child-frame-parameters . ,parameter)))))
2018-12-11 01:26:08 +00:00
(frame (window-frame window))
(main-frame (selected-frame)))
(make-frame-visible frame)
2018-12-11 01:26:08 +00:00
(set-window-dedicated-p window t)
(redirect-frame-focus frame (frame-parent frame))
2018-12-11 14:15:00 +00:00
(set-face-attribute 'internal-border frame :inherit 'eldoc-box-border)
(set-face-attribute 'default frame :background (face-attribute 'eldoc-box-body :background main-frame))
2018-12-11 01:26:08 +00:00
;; set size
(let* ((size
(window-text-pixel-size
window nil nil
2018-12-11 14:15:00 +00:00
eldoc-box-max-pixel-width
eldoc-box-max-pixel-height t))
2018-12-11 01:26:08 +00:00
(width (car size))
(height (cdr size))
(width (+ width (frame-char-width frame))) ; add margin
(frame-resize-pixelwise t))
(set-frame-size frame width height t)
;; move position
2018-12-11 14:15:00 +00:00
(set-frame-position frame (pcase (eldoc-box--window-side) ; x position + a little padding (16)
2018-12-11 01:26:08 +00:00
;; display doc on right
('left (- (frame-outer-width main-frame) width 16))
2018-12-11 01:26:08 +00:00
;; display doc on left
('right 16))
;; y position + a little padding (16)
16))
2018-12-11 14:15:00 +00:00
(setq eldoc-box--frame frame)))
2018-12-11 01:26:08 +00:00
;;;;; ElDoc
2018-12-11 14:15:00 +00:00
(defun eldoc-box--eldoc-message-function (str &rest args)
2018-12-11 01:26:08 +00:00
"Frontend for eldoc. Display STR in childframe and ARGS works like `message'."
(if (stringp str)
(let ((doc (apply #'format str args)))
(if (and eldoc-box-only-multi-line (eq (cl-count ?\n doc) 0))
(apply #'eldoc-minibuffer-message str args)
(eldoc-box--display (apply #'format str args))))
(eldoc-box-quit-frame)))
2018-12-11 01:26:08 +00:00
2018-12-11 14:15:00 +00:00
(define-minor-mode eldoc-box-hover-mode
2018-12-11 01:26:08 +00:00
"Displays hover documentations in a childframe. This mode is buffer local."
2018-12-11 14:15:00 +00:00
:lighter " ELDOC-BOX"
(if eldoc-box-hover-mode
(setq-local eldoc-message-function #'eldoc-box--eldoc-message-function)
2018-12-11 01:26:08 +00:00
(setq-local eldoc-message-function #'eldoc-minibuffer-message)))
2018-12-11 14:15:00 +00:00
(provide 'eldoc-box)
2018-12-11 01:26:08 +00:00
2018-12-11 14:15:00 +00:00
;;; eldoc-box.el ends here