]>
Commit | Line | Data |
---|---|---|
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.9.14" | |
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 |