config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

corfu-terminal.el (8856B)


      1 ;;; corfu-terminal.el --- Corfu popup on terminal -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2022 Akib Azmain Turja.
      4 
      5 ;; Author: Akib Azmain Turja <akib@disroot.org>
      6 ;; Created: 2022-04-11
      7 ;; Version: 0.7
      8 ;; Package-Requires: ((emacs "26.1") (corfu "0.36") (popon "0.13"))
      9 ;; Keywords: convenience
     10 ;; Homepage: https://codeberg.org/akib/emacs-corfu-terminal
     11 
     12 ;; This file is not part of GNU Emacs.
     13 
     14 ;; This file is free software; you can redistribute it and/or modify
     15 ;; it under the terms of the GNU General Public License as published by
     16 ;; the Free Software Foundation; either version 3, or (at your option)
     17 ;; any later version.
     18 
     19 ;; This program is distributed in the hope that it will be useful,
     20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     22 ;; GNU General Public License for more details.
     23 
     24 ;; For a full copy of the GNU General Public License
     25 ;; see <https://www.gnu.org/licenses/>.
     26 
     27 ;;; Commentary:
     28 
     29 ;; Corfu uses child frames to display candidates.  This makes Corfu
     30 ;; unusable on terminal.  This package replaces that with popup/popon,
     31 ;; which works everywhere.  Use M-x corfu-terminal-mode to enable.
     32 ;; You'll probably want to enable it only on terminal.  In that case,
     33 ;; put the following in your init file:
     34 
     35 ;;   (unless (display-graphic-p)
     36 ;;     (corfu-terminal-mode +1))
     37 
     38 ;;; Code:
     39 
     40 (require 'subr-x)
     41 (require 'corfu)
     42 (require 'popon)
     43 (require 'cl-lib)
     44 
     45 (defgroup corfu-terminal nil
     46   "Corfu popup on terminal."
     47   :group 'convenience
     48   :link '(url-link "https://codeberg.org/akib/emacs-corfu-terminal")
     49   :prefix "corfu-terminal-")
     50 
     51 (defcustom corfu-terminal-enable-on-minibuffer t
     52   "Non-nil means enable corfu-terminal on minibuffer."
     53   :type 'boolean)
     54 
     55 (defcustom corfu-terminal-resize-minibuffer t
     56   "Non-nil means resize minibuffer to show popup."
     57   :type 'boolean)
     58 
     59 (defcustom corfu-terminal-position-right-margin 0
     60   "Number of columns of margin at the right of window.
     61 
     62 Always keep the popup this many columns away from the right edge of
     63 the window.
     64 
     65 Note: If the popup breaks or crosses the right edge of window, you may
     66 set this variable to warkaround it.  But remember, that's a *bug*, so
     67 if that ever happens to you please report the issue at
     68 https://codeberg.org/akib/emacs-corfu-terminal/issues."
     69   :type 'integer)
     70 
     71 (defcustom corfu-terminal-disable-on-gui t
     72   "Don't use popon UI on GUI."
     73   :type '(choice (const :tag "Yes" t)
     74                  (const :tag "No" nil)))
     75 
     76 (defvar corfu-terminal--popon nil
     77   "Popon object.")
     78 
     79 (defvar corfu-terminal--last-position nil
     80   "Position of last popon, and some data to make sure that's valid.")
     81 
     82 (cl-defmethod corfu--popup-support-p (&context (corfu-terminal-mode
     83                                                 (eql t)))
     84   "Return whether corfu-terminal supports showing popon now."
     85   (or (not (minibufferp))
     86       corfu-terminal-enable-on-minibuffer
     87       (and corfu-terminal-disable-on-gui
     88            (display-graphic-p))))
     89 
     90 (cl-defmethod corfu--popup-hide (&context (corfu-terminal-mode
     91                                            (eql t)))
     92   "Hide popup.
     93 
     94 If `corfu-terminal-disable-on-gui' is non-nil and  `display-graphic-p'
     95 returns non-nil then call FN instead, where FN should be the original
     96 definition in Corfu."
     97   (if (and corfu-terminal-disable-on-gui
     98            (display-graphic-p))
     99       (cl-call-next-method)
    100     (when corfu-terminal--popon
    101       (setq corfu-terminal--popon
    102             (popon-kill corfu-terminal--popon)))))
    103 
    104 (cl-defmethod corfu--popup-show ( pos off width lines
    105                                   &context (corfu-terminal-mode
    106                                             (eql t))
    107                                   &optional curr lo bar)
    108   "Show popup at OFF columns before POS.
    109 
    110 Show LINES, a list of lines.  Highlight CURRth line as current
    111 selection.  Show a vertical scroll bar of size BAR + 1 from LOth line.
    112 
    113 If `corfu-terminal-disable-on-gui' is non-nil and  `display-graphic-p'
    114 returns non-nil then call FN instead, where FN should be the original
    115 definition in Corfu."
    116   (if (and corfu-terminal-disable-on-gui
    117            (display-graphic-p))
    118       (cl-call-next-method)
    119     (corfu--popup-hide) ; Hide the popup first.
    120     (when (and (window-minibuffer-p)
    121                (< (/ (window-body-height nil 'pixelwise)
    122                      (default-font-height))
    123                   (1+ (length lines)))
    124                corfu-terminal-resize-minibuffer
    125                (not (frame-root-window-p (selected-window))))
    126       (window-resize nil (- (1+ (length lines))
    127                             (/ (window-body-height nil 'pixelwise)
    128                                (default-font-height)))))
    129     (let* ((bar-width (ceiling (* (default-font-width)
    130                                   corfu-bar-width)))
    131            (margin-left-width (ceiling (* (default-font-width)
    132                                           corfu-left-margin-width)))
    133            (margin-right-width (max (ceiling
    134                                      (* (default-font-width)
    135                                         corfu-right-margin-width))
    136                                     bar-width))
    137            (scroll-bar
    138             (when (< 0 bar-width)
    139               (if (display-graphic-p)
    140                   (concat
    141                    (propertize
    142                     " " 'display
    143                     `(space
    144                       :width (,(- margin-right-width bar-width))))
    145                    (propertize " " 'display
    146                                `(space :width (,bar-width))
    147                                'face 'corfu-bar))
    148                 (concat
    149                  (make-string (- margin-right-width bar-width) ?\ )
    150                  (propertize (make-string bar-width ?\ ) 'face
    151                              'corfu-bar)))))
    152            (margin-left
    153             (when (> margin-left-width 0)
    154               (if (display-graphic-p)
    155                   (propertize
    156                    " " 'display `(space :width (,margin-left-width)))
    157                 (make-string margin-left-width ?\ ))))
    158            (margin-right
    159             (when (> margin-right-width 0)
    160               (if (display-graphic-p)
    161                   (propertize
    162                    " " 'display `(space :width (,margin-right-width)))
    163                 (make-string margin-right-width ?\ ))))
    164            (popon-width
    165             (if (display-graphic-p)
    166                 (+ width (round (/ (+ margin-left-width
    167                                       margin-right-width)
    168                                    (default-font-width))))
    169               (+ width margin-left-width margin-right-width)))
    170            (popon-pos
    171             (if (equal (cdr corfu-terminal--last-position)
    172                        (list (posn-point pos) popon-width
    173                              (window-start) (buffer-modified-tick)))
    174                 (car corfu-terminal--last-position)
    175               (let ((x-y (popon-x-y-at-posn pos)))
    176                 (cons
    177                  (max
    178                   (min (- (car x-y) (+ off margin-left-width))
    179                        (- (window-max-chars-per-line)
    180                           corfu-terminal-position-right-margin
    181                           popon-width))
    182                   0)
    183                  (if (and (< (/ (window-body-height nil 'pixelwise)
    184                                 (default-font-height))
    185                              (+ (1+ (cdr x-y)) (length lines)))
    186                           (>= (cdr x-y) (length lines)))
    187                      (- (cdr x-y) (length lines))
    188                    (1+ (cdr x-y))))))))
    189       (setq corfu-terminal--last-position
    190             (list popon-pos (posn-point pos) popon-width
    191                   (window-start) (buffer-modified-tick)))
    192       (setq corfu-terminal--popon
    193             (popon-create
    194              (cons
    195               (string-join
    196                (seq-map-indexed
    197                 (lambda (line line-number)
    198                   (let ((str
    199                          (concat
    200                           margin-left line
    201                           (make-string (- width (string-width line))
    202                                        ?\ )
    203                           (if (and lo (<= lo line-number (+ lo bar)))
    204                               scroll-bar
    205                             margin-right))))
    206                     (add-face-text-property 0 (length str)
    207                                             (if (eq line-number curr)
    208                                                 'corfu-current
    209                                               'corfu-default)
    210                                             t str)
    211                     str))
    212                 lines)
    213                "\n")
    214               popon-width)
    215              popon-pos))
    216       nil)))
    217 
    218 ;;;###autoload
    219 (define-minor-mode corfu-terminal-mode
    220   "Corfu popup on terminal."
    221   :global t
    222   :group 'corfu-terminal)
    223 
    224 (provide 'corfu-terminal)
    225 ;;; corfu-terminal.el ends here