changelog shortlog graph tags branches files raw help

Mercurial > core / changeset: add corfu-terminal

changeset 633: 88a3f078c185
parent 632: bbd9024f2fe2
child 634: 8eef7df3242d
author: Richard Westhaver <ellis@rwest.io>
date: Sun, 01 Sep 2024 21:00:12 -0400
files: emacs/lib/corfu-terminal.el
description: add corfu-terminal
     1.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2+++ b/emacs/lib/corfu-terminal.el	Sun Sep 01 21:00:12 2024 -0400
     1.3@@ -0,0 +1,225 @@
     1.4+;;; corfu-terminal.el --- Corfu popup on terminal -*- lexical-binding: t; -*-
     1.5+
     1.6+;; Copyright (C) 2022 Akib Azmain Turja.
     1.7+
     1.8+;; Author: Akib Azmain Turja <akib@disroot.org>
     1.9+;; Created: 2022-04-11
    1.10+;; Version: 0.7
    1.11+;; Package-Requires: ((emacs "26.1") (corfu "0.36") (popon "0.13"))
    1.12+;; Keywords: convenience
    1.13+;; Homepage: https://codeberg.org/akib/emacs-corfu-terminal
    1.14+
    1.15+;; This file is not part of GNU Emacs.
    1.16+
    1.17+;; This file is free software; you can redistribute it and/or modify
    1.18+;; it under the terms of the GNU General Public License as published by
    1.19+;; the Free Software Foundation; either version 3, or (at your option)
    1.20+;; any later version.
    1.21+
    1.22+;; This program is distributed in the hope that it will be useful,
    1.23+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.24+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.25+;; GNU General Public License for more details.
    1.26+
    1.27+;; For a full copy of the GNU General Public License
    1.28+;; see <https://www.gnu.org/licenses/>.
    1.29+
    1.30+;;; Commentary:
    1.31+
    1.32+;; Corfu uses child frames to display candidates.  This makes Corfu
    1.33+;; unusable on terminal.  This package replaces that with popup/popon,
    1.34+;; which works everywhere.  Use M-x corfu-terminal-mode to enable.
    1.35+;; You'll probably want to enable it only on terminal.  In that case,
    1.36+;; put the following in your init file:
    1.37+
    1.38+;;   (unless (display-graphic-p)
    1.39+;;     (corfu-terminal-mode +1))
    1.40+
    1.41+;;; Code:
    1.42+
    1.43+(require 'subr-x)
    1.44+(require 'corfu)
    1.45+(require 'popon)
    1.46+(require 'cl-lib)
    1.47+
    1.48+(defgroup corfu-terminal nil
    1.49+  "Corfu popup on terminal."
    1.50+  :group 'convenience
    1.51+  :link '(url-link "https://codeberg.org/akib/emacs-corfu-terminal")
    1.52+  :prefix "corfu-terminal-")
    1.53+
    1.54+(defcustom corfu-terminal-enable-on-minibuffer t
    1.55+  "Non-nil means enable corfu-terminal on minibuffer."
    1.56+  :type 'boolean)
    1.57+
    1.58+(defcustom corfu-terminal-resize-minibuffer t
    1.59+  "Non-nil means resize minibuffer to show popup."
    1.60+  :type 'boolean)
    1.61+
    1.62+(defcustom corfu-terminal-position-right-margin 0
    1.63+  "Number of columns of margin at the right of window.
    1.64+
    1.65+Always keep the popup this many columns away from the right edge of
    1.66+the window.
    1.67+
    1.68+Note: If the popup breaks or crosses the right edge of window, you may
    1.69+set this variable to warkaround it.  But remember, that's a *bug*, so
    1.70+if that ever happens to you please report the issue at
    1.71+https://codeberg.org/akib/emacs-corfu-terminal/issues."
    1.72+  :type 'integer)
    1.73+
    1.74+(defcustom corfu-terminal-disable-on-gui t
    1.75+  "Don't use popon UI on GUI."
    1.76+  :type '(choice (const :tag "Yes" t)
    1.77+                 (const :tag "No" nil)))
    1.78+
    1.79+(defvar corfu-terminal--popon nil
    1.80+  "Popon object.")
    1.81+
    1.82+(defvar corfu-terminal--last-position nil
    1.83+  "Position of last popon, and some data to make sure that's valid.")
    1.84+
    1.85+(cl-defmethod corfu--popup-support-p (&context (corfu-terminal-mode
    1.86+                                                (eql t)))
    1.87+  "Return whether corfu-terminal supports showing popon now."
    1.88+  (or (not (minibufferp))
    1.89+      corfu-terminal-enable-on-minibuffer
    1.90+      (and corfu-terminal-disable-on-gui
    1.91+           (display-graphic-p))))
    1.92+
    1.93+(cl-defmethod corfu--popup-hide (&context (corfu-terminal-mode
    1.94+                                           (eql t)))
    1.95+  "Hide popup.
    1.96+
    1.97+If `corfu-terminal-disable-on-gui' is non-nil and  `display-graphic-p'
    1.98+returns non-nil then call FN instead, where FN should be the original
    1.99+definition in Corfu."
   1.100+  (if (and corfu-terminal-disable-on-gui
   1.101+           (display-graphic-p))
   1.102+      (cl-call-next-method)
   1.103+    (when corfu-terminal--popon
   1.104+      (setq corfu-terminal--popon
   1.105+            (popon-kill corfu-terminal--popon)))))
   1.106+
   1.107+(cl-defmethod corfu--popup-show ( pos off width lines
   1.108+                                  &context (corfu-terminal-mode
   1.109+                                            (eql t))
   1.110+                                  &optional curr lo bar)
   1.111+  "Show popup at OFF columns before POS.
   1.112+
   1.113+Show LINES, a list of lines.  Highlight CURRth line as current
   1.114+selection.  Show a vertical scroll bar of size BAR + 1 from LOth line.
   1.115+
   1.116+If `corfu-terminal-disable-on-gui' is non-nil and  `display-graphic-p'
   1.117+returns non-nil then call FN instead, where FN should be the original
   1.118+definition in Corfu."
   1.119+  (if (and corfu-terminal-disable-on-gui
   1.120+           (display-graphic-p))
   1.121+      (cl-call-next-method)
   1.122+    (corfu--popup-hide) ; Hide the popup first.
   1.123+    (when (and (window-minibuffer-p)
   1.124+               (< (/ (window-body-height nil 'pixelwise)
   1.125+                     (default-font-height))
   1.126+                  (1+ (length lines)))
   1.127+               corfu-terminal-resize-minibuffer
   1.128+               (not (frame-root-window-p (selected-window))))
   1.129+      (window-resize nil (- (1+ (length lines))
   1.130+                            (/ (window-body-height nil 'pixelwise)
   1.131+                               (default-font-height)))))
   1.132+    (let* ((bar-width (ceiling (* (default-font-width)
   1.133+                                  corfu-bar-width)))
   1.134+           (margin-left-width (ceiling (* (default-font-width)
   1.135+                                          corfu-left-margin-width)))
   1.136+           (margin-right-width (max (ceiling
   1.137+                                     (* (default-font-width)
   1.138+                                        corfu-right-margin-width))
   1.139+                                    bar-width))
   1.140+           (scroll-bar
   1.141+            (when (< 0 bar-width)
   1.142+              (if (display-graphic-p)
   1.143+                  (concat
   1.144+                   (propertize
   1.145+                    " " 'display
   1.146+                    `(space
   1.147+                      :width (,(- margin-right-width bar-width))))
   1.148+                   (propertize " " 'display
   1.149+                               `(space :width (,bar-width))
   1.150+                               'face 'corfu-bar))
   1.151+                (concat
   1.152+                 (make-string (- margin-right-width bar-width) ?\ )
   1.153+                 (propertize (make-string bar-width ?\ ) 'face
   1.154+                             'corfu-bar)))))
   1.155+           (margin-left
   1.156+            (when (> margin-left-width 0)
   1.157+              (if (display-graphic-p)
   1.158+                  (propertize
   1.159+                   " " 'display `(space :width (,margin-left-width)))
   1.160+                (make-string margin-left-width ?\ ))))
   1.161+           (margin-right
   1.162+            (when (> margin-right-width 0)
   1.163+              (if (display-graphic-p)
   1.164+                  (propertize
   1.165+                   " " 'display `(space :width (,margin-right-width)))
   1.166+                (make-string margin-right-width ?\ ))))
   1.167+           (popon-width
   1.168+            (if (display-graphic-p)
   1.169+                (+ width (round (/ (+ margin-left-width
   1.170+                                      margin-right-width)
   1.171+                                   (default-font-width))))
   1.172+              (+ width margin-left-width margin-right-width)))
   1.173+           (popon-pos
   1.174+            (if (equal (cdr corfu-terminal--last-position)
   1.175+                       (list (posn-point pos) popon-width
   1.176+                             (window-start) (buffer-modified-tick)))
   1.177+                (car corfu-terminal--last-position)
   1.178+              (let ((x-y (popon-x-y-at-posn pos)))
   1.179+                (cons
   1.180+                 (max
   1.181+                  (min (- (car x-y) (+ off margin-left-width))
   1.182+                       (- (window-max-chars-per-line)
   1.183+                          corfu-terminal-position-right-margin
   1.184+                          popon-width))
   1.185+                  0)
   1.186+                 (if (and (< (/ (window-body-height nil 'pixelwise)
   1.187+                                (default-font-height))
   1.188+                             (+ (1+ (cdr x-y)) (length lines)))
   1.189+                          (>= (cdr x-y) (length lines)))
   1.190+                     (- (cdr x-y) (length lines))
   1.191+                   (1+ (cdr x-y))))))))
   1.192+      (setq corfu-terminal--last-position
   1.193+            (list popon-pos (posn-point pos) popon-width
   1.194+                  (window-start) (buffer-modified-tick)))
   1.195+      (setq corfu-terminal--popon
   1.196+            (popon-create
   1.197+             (cons
   1.198+              (string-join
   1.199+               (seq-map-indexed
   1.200+                (lambda (line line-number)
   1.201+                  (let ((str
   1.202+                         (concat
   1.203+                          margin-left line
   1.204+                          (make-string (- width (string-width line))
   1.205+                                       ?\ )
   1.206+                          (if (and lo (<= lo line-number (+ lo bar)))
   1.207+                              scroll-bar
   1.208+                            margin-right))))
   1.209+                    (add-face-text-property 0 (length str)
   1.210+                                            (if (eq line-number curr)
   1.211+                                                'corfu-current
   1.212+                                              'corfu-default)
   1.213+                                            t str)
   1.214+                    str))
   1.215+                lines)
   1.216+               "\n")
   1.217+              popon-width)
   1.218+             popon-pos))
   1.219+      nil)))
   1.220+
   1.221+;;;###autoload
   1.222+(define-minor-mode corfu-terminal-mode
   1.223+  "Corfu popup on terminal."
   1.224+  :global t
   1.225+  :group 'corfu-terminal)
   1.226+
   1.227+(provide 'corfu-terminal)
   1.228+;;; corfu-terminal.el ends here