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