dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

company-tng.el (7935B)


      1 ;;; company-tng.el --- company-mode configuration for single-button interaction
      2 
      3 ;; Copyright (C) 2017-2021  Free Software Foundation, Inc.
      4 
      5 ;; Author: Nikita Leshenko
      6 
      7 ;; This file is part of GNU Emacs.
      8 
      9 ;; GNU Emacs is free software: you can redistribute it and/or modify
     10 ;; it under the terms of the GNU General Public License as published by
     11 ;; the Free Software Foundation, either version 3 of the License, or
     12 ;; (at your option) any later version.
     13 
     14 ;; GNU Emacs is distributed in the hope that it will be useful,
     15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 ;; GNU General Public License for more details.
     18 
     19 ;; You should have received a copy of the GNU General Public License
     20 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     21 
     22 
     23 ;;; Commentary:
     24 ;;
     25 ;; company-tng (Tab and Go) allows you to perform completion using just TAB.
     26 ;; Pressing it will both select the next completion candidate in the list and
     27 ;; insert it into the buffer (or make it look like it's inserted, in fact).
     28 ;;
     29 ;; It cycles the candidates like `yank-pop' or `dabbrev-expand' or Vim:
     30 ;; Pressing TAB selects the first item in the completion menu and inserts it in
     31 ;; the buffer. Pressing TAB again selects the second item and replaces the
     32 ;; "inserted" item with the second one. This can continue as long as the user
     33 ;; wishes to cycle through the menu. You can also press S-TAB to select the
     34 ;; previous candidate, of course.
     35 ;;
     36 ;; The benefits are that you only have to use one shortcut key and there is no
     37 ;; need to confirm the entry.
     38 ;;
     39 ;; Usage:
     40 ;;
     41 ;; Enable `company-tng-mode' with:
     42 ;;
     43 ;;   (add-hook 'after-init-hook 'company-tng-mode)
     44 ;;
     45 ;; in your init script. It will set up the required frontend, as well as make a
     46 ;; number of recommended configuration changes described below.
     47 ;;
     48 ;; To avoid these changes, if you want to tweak everything yourself, customize
     49 ;;`company-tng-auto-configure' to nil.
     50 ;;
     51 ;; We recommend to bind TAB to `company-select-next', S-TAB to
     52 ;; `company-select-previous', and unbind RET and other now-unnecessary
     53 ;; keys from `company-active-map':
     54 ;;
     55 ;;   (define-key company-active-map (kbd "TAB") 'company-select-next)
     56 ;;   (define-key company-active-map (kbd "<backtab>") 'company-select-previous)
     57 ;;   (define-key company-active-map (kbd "RET") nil)
     58 ;;
     59 ;; Note that it's not necessary to rebind keys to use this frontend,
     60 ;; you can use the arrow keys or M-n/M-p to select and insert
     61 ;; candidates. You also need to decide which keys to unbind, depending
     62 ;; on whether you want them to do the Company action or the default
     63 ;; Emacs action (for example C-s or C-w).
     64 ;;
     65 ;; We recommend to disable `company-require-match' to allow free typing at any
     66 ;; point.
     67 ;;
     68 ;; By default, company-tng doesn't work well with backends that insert function
     69 ;; arguments into the buffer and (optionally) expand them into a snippet
     70 ;; (usually performed in `post-completion' using yasnippet or company-template).
     71 ;; In company-tng, completion candidates
     72 ;; are inserted into the buffer as the user selects them and the completion is
     73 ;; finished implicitly when the user continues typing after selecting a
     74 ;; candidate. Modifying the buffer (by expanding a snippet) when the user
     75 ;; continues typing would be surprising and undesirable, since the candidate was
     76 ;; already inserted into the buffer.
     77 ;;
     78 ;; For this reason `company-tng-mode' by default disables arguments insertion
     79 ;; for a number of popular backends. If the backend you are using is not among
     80 ;; them, you might have to configure it not to do that yourself.
     81 ;;
     82 ;; YASnippet and company-tng both use TAB, which causes conflicts. The
     83 ;; recommended way to use YASnippet with company-tng is to choose a different
     84 ;; key for expanding a snippet and moving to the next snippet field:
     85 ;;
     86 ;;   (define-key yas-minor-mode-map "\C-j" 'yas-expand)
     87 ;;   (define-key yas-keymap "\C-j" 'yas-next-field-or-maybe-expand)
     88 ;;   (dolist (keymap (list yas-minor-mode-map yas-keymap))
     89 ;;     (define-key keymap (kbd "TAB") nil)
     90 ;;     (define-key keymap [(tab)] nil))
     91 
     92 ;;; Code:
     93 
     94 (require 'company)
     95 (require 'cl-lib)
     96 
     97 (defvar-local company-tng--overlay nil)
     98 
     99 ;;;###autoload
    100 (defun company-tng-frontend (command)
    101   "When the user changes the selection at least once, this
    102 frontend will display the candidate in the buffer as if it's
    103 already there and any key outside of `company-active-map' will
    104 confirm the selection and finish the completion."
    105   (cl-case command
    106     (show
    107      (let ((ov (make-overlay (point) (point))))
    108        (setq company-tng--overlay ov)
    109        (overlay-put ov 'priority 2)))
    110     (update
    111      (let* ((ov company-tng--overlay)
    112             (selected (and company-selection
    113                            (nth company-selection company-candidates)))
    114             (prefix (length company-prefix)))
    115        (move-overlay ov (- (point) prefix) (point))
    116        (overlay-put ov
    117                     (if (= prefix 0) 'after-string 'display)
    118                     selected)))
    119     (hide
    120      (when company-tng--overlay
    121        (delete-overlay company-tng--overlay)
    122        (kill-local-variable 'company-tng--overlay)))
    123     (pre-command
    124      (when (and company-selection
    125                 (not (company--company-command-p (this-command-keys))))
    126        (company--unread-this-command-keys)
    127        (setq this-command 'company-complete-selection)))))
    128 
    129 (defvar company-clang-insert-arguments)
    130 (defvar company-semantic-insert-arguments)
    131 (defvar company-rtags-insert-arguments)
    132 (defvar lsp-enable-snippet)
    133 
    134 (defgroup company-tng nil
    135   "Company Tab and Go."
    136   :group 'company)
    137 
    138 (defcustom company-tng-auto-configure t
    139   "Automatically apply default configure when enable `company-tng-mode'."
    140   :type 'boolean)
    141 
    142 ;;;###autoload
    143 (define-obsolete-function-alias 'company-tng-configure-default 'company-tng-mode "0.10.0"
    144   "Applies the default configuration to enable company-tng.")
    145 
    146 (declare-function eglot--snippet-expansion-fn "eglot")
    147 
    148 (defvar company-tng-map
    149   (let ((keymap (make-sparse-keymap)))
    150     (set-keymap-parent keymap company-active-map)
    151     (define-key keymap [return] nil)
    152     (define-key keymap (kbd "RET") nil)
    153     (define-key keymap [tab] 'company-select-next)
    154     (define-key keymap (kbd "TAB") 'company-select-next)
    155     (define-key keymap [backtab] 'company-select-previous)
    156     (define-key keymap (kbd "S-TAB") 'company-select-previous)
    157     keymap))
    158 
    159 ;;;###autoload
    160 (define-minor-mode company-tng-mode
    161  "This minor mode enables `company-tng-frontend'."
    162   :init-value nil
    163   :global t
    164   (cond
    165    (company-tng-mode
    166     (setq company-frontends
    167           (add-to-list 'company-frontends 'company-tng-frontend))
    168     (when company-tng-auto-configure
    169       (setq company-frontends '(company-tng-frontend
    170                                 company-pseudo-tooltip-frontend
    171                                 company-echo-metadata-frontend))
    172       (setq company-require-match nil
    173             company-clang-insert-arguments nil
    174             company-semantic-insert-arguments nil
    175             company-rtags-insert-arguments nil
    176             lsp-enable-snippet nil)
    177       (advice-add #'eglot--snippet-expansion-fn :override #'ignore)
    178       (setq company-active-map company-tng-map))
    179     (setq company-selection-default nil))
    180    (t
    181     (setq company-frontends
    182           '(company-pseudo-tooltip-unless-just-one-frontend
    183             company-preview-if-just-one-frontend
    184             company-echo-metadata-frontend))
    185     (when company-tng-auto-configure
    186       (setq company-require-match 'company-explicit-action-p
    187             company-clang-insert-arguments t
    188             company-semantic-insert-arguments t
    189             company-rtags-insert-arguments t
    190             lsp-enable-snippet t)
    191       (advice-remove #'eglot--snippet-expansion-fn #'ignore)
    192       (setq company-active-map (keymap-parent company-tng-map)))
    193     (setq company-selection-default 0))))
    194 
    195 (provide 'company-tng)
    196 ;;; company-tng.el ends here