summaryrefslogtreecommitdiff
path: root/extensions/corfu-echo.el
blob: 5e869e483cecb559ef8e42fb4ff657be58cbc6cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
;;; corfu-echo.el --- Show candidate documentation in echo area -*- lexical-binding: t -*-

;; Copyright (C) 2021-2023 Free Software Foundation, Inc.

;; Author: Daniel Mendler <mail@daniel-mendler.de>
;; Maintainer: Daniel Mendler <mail@daniel-mendler.de>
;; Created: 2022
;; Version: 0.1
;; Package-Requires: ((emacs "27.1") (corfu "0.34"))
;; Homepage: https://github.com/minad/corfu

;; This file is part of GNU Emacs.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Show candidate documentation in echo area.  Enable `corfu-echo-mode'.

;;; Code:

(require 'corfu)
(eval-when-compile
  (require 'subr-x))

(defface corfu-echo
  '((t :inherit completions-annotations))
  "Face used for echo area messages."
  :group 'corfu-faces)

(defcustom corfu-echo-delay '(2.0 . 1.0)
  "Show documentation string in the echo area after that number of seconds.
Set to t for an instant message.  The value can be a pair of two
floats to specify initial and subsequent delay."
  :type '(choice (const :tag "Never" nil)
                 (const :tag "Instant" t)
                 (number :tag "Delay in seconds")
                 (cons :tag "Two Delays"
                       (choice :tag "Initial   " number)
                       (choice :tag "Subsequent" number)))
  :group 'corfu)

(defvar-local corfu-echo--timer nil
  "Echo area message timer.")

(defvar-local corfu-echo--message nil
  "Last echo message.")

(defun corfu-echo--cancel (&optional msg)
  "Cancel echo timer and refresh MSG."
  (when corfu-echo--timer
    (cancel-timer corfu-echo--timer)
    (setq corfu-echo--timer nil))
  (corfu-echo--show msg)
  (unless corfu-echo--message
    (kill-local-variable 'corfu-echo--timer)
    (kill-local-variable 'corfu-echo--message)))

(defun corfu-echo--show (msg)
  "Show MSG in echo area."
  (when (or msg corfu-echo--message)
    (setq msg (or msg "")
          corfu-echo--message msg)
    (corfu--message "%s" (if (text-property-not-all 0 (length msg) 'face nil msg)
                             msg
                           (propertize msg 'face 'corfu-echo)))))

;;;###autoload
(define-minor-mode corfu-echo-mode
  "Show candidate documentation in echo area."
  :global t :group 'corfu)

(cl-defmethod corfu--exhibit :after (&context (corfu-echo-mode (eql t)) &optional _auto)
  (if-let ((delay (if (consp corfu-echo-delay)
                      (funcall (if corfu-echo--message #'cdr #'car)
                               corfu-echo-delay)
                    corfu-echo-delay))
           (fun (plist-get corfu--extra :company-docsig))
           (cand (and (>= corfu--index 0)
                      (nth corfu--index corfu--candidates))))
      (if (or (eq delay t) (<= delay 0))
          (corfu-echo--show (funcall fun cand))
        (corfu-echo--cancel)
        (setq corfu-echo--timer
              (run-at-time delay nil
                           (lambda ()
                             (corfu-echo--show (funcall fun cand))))))
    (corfu-echo--cancel)))

(cl-defmethod corfu--teardown :before (&context (corfu-echo-mode (eql t)))
  (corfu-echo--cancel))

(cl-defmethod corfu--prepare :before (&context (corfu-echo-mode (eql t)))
  ;; The refreshing is needed to prevent flicker if corfu-echo-delay=t.
  (corfu-echo--cancel corfu-echo--message))

(provide 'corfu-echo)
;;; corfu-echo.el ends here