summaryrefslogtreecommitdiff
path: root/elpa/company-20220326.48/company-tng.el
blob: 55124a309f74d82bce3929ba84961a23733b949e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
;;; company-tng.el --- company-mode configuration for single-button interaction

;; Copyright (C) 2017-2021  Free Software Foundation, Inc.

;; Author: Nikita Leshenko

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.


;;; Commentary:
;;
;; company-tng (Tab and Go) allows you to perform completion using just TAB.
;; Pressing it will both select the next completion candidate in the list and
;; insert it into the buffer (or make it look like it's inserted, in fact).
;;
;; It cycles the candidates like `yank-pop' or `dabbrev-expand' or Vim:
;; Pressing TAB selects the first item in the completion menu and inserts it in
;; the buffer. Pressing TAB again selects the second item and replaces the
;; "inserted" item with the second one. This can continue as long as the user
;; wishes to cycle through the menu. You can also press S-TAB to select the
;; previous candidate, of course.
;;
;; The benefits are that you only have to use one shortcut key and there is no
;; need to confirm the entry.
;;
;; Usage:
;;
;; Enable `company-tng-mode' with:
;;
;;   (add-hook 'after-init-hook 'company-tng-mode)
;;
;; in your init script. It will set up the required frontend, as well as make a
;; number of recommended configuration changes described below.
;;
;; To avoid these changes, if you want to tweak everything yourself, customize
;;`company-tng-auto-configure' to nil.
;;
;; We recommend to bind TAB to `company-select-next', S-TAB to
;; `company-select-previous', and unbind RET and other now-unnecessary
;; keys from `company-active-map':
;;
;;   (define-key company-active-map (kbd "TAB") 'company-select-next)
;;   (define-key company-active-map (kbd "<backtab>") 'company-select-previous)
;;   (define-key company-active-map (kbd "RET") nil)
;;
;; Note that it's not necessary to rebind keys to use this frontend,
;; you can use the arrow keys or M-n/M-p to select and insert
;; candidates. You also need to decide which keys to unbind, depending
;; on whether you want them to do the Company action or the default
;; Emacs action (for example C-s or C-w).
;;
;; We recommend to disable `company-require-match' to allow free typing at any
;; point.
;;
;; By default, company-tng doesn't work well with backends that insert function
;; arguments into the buffer and (optionally) expand them into a snippet
;; (usually performed in `post-completion' using yasnippet or company-template).
;; In company-tng, completion candidates
;; are inserted into the buffer as the user selects them and the completion is
;; finished implicitly when the user continues typing after selecting a
;; candidate. Modifying the buffer (by expanding a snippet) when the user
;; continues typing would be surprising and undesirable, since the candidate was
;; already inserted into the buffer.
;;
;; For this reason `company-tng-mode' by default disables arguments insertion
;; for a number of popular backends. If the backend you are using is not among
;; them, you might have to configure it not to do that yourself.
;;
;; YASnippet and company-tng both use TAB, which causes conflicts. The
;; recommended way to use YASnippet with company-tng is to choose a different
;; key for expanding a snippet and moving to the next snippet field:
;;
;;   (define-key yas-minor-mode-map "\C-j" 'yas-expand)
;;   (define-key yas-keymap "\C-j" 'yas-next-field-or-maybe-expand)
;;   (dolist (keymap (list yas-minor-mode-map yas-keymap))
;;     (define-key keymap (kbd "TAB") nil)
;;     (define-key keymap [(tab)] nil))

;;; Code:

(require 'company)
(require 'cl-lib)

(defvar-local company-tng--overlay nil)

;;;###autoload
(defun company-tng-frontend (command)
  "When the user changes the selection at least once, this
frontend will display the candidate in the buffer as if it's
already there and any key outside of `company-active-map' will
confirm the selection and finish the completion."
  (cl-case command
    (show
     (let ((ov (make-overlay (point) (point))))
       (setq company-tng--overlay ov)
       (overlay-put ov 'priority 2)))
    (update
     (let* ((ov company-tng--overlay)
            (selected (and company-selection
                           (nth company-selection company-candidates)))
            (prefix (length company-prefix)))
       (move-overlay ov (- (point) prefix) (point))
       (overlay-put ov
                    (if (= prefix 0) 'after-string 'display)
                    selected)))
    (hide
     (when company-tng--overlay
       (delete-overlay company-tng--overlay)
       (kill-local-variable 'company-tng--overlay)))
    (pre-command
     (when (and company-selection
                (not (company--company-command-p (this-command-keys))))
       (company--unread-this-command-keys)
       (setq this-command 'company-complete-selection)))))

(defvar company-clang-insert-arguments)
(defvar company-semantic-insert-arguments)
(defvar company-rtags-insert-arguments)
(defvar lsp-enable-snippet)

(defgroup company-tng nil
  "Company Tab and Go."
  :group 'company)

(defcustom company-tng-auto-configure t
  "Automatically apply default configure when enable `company-tng-mode'."
  :type 'boolean)

;;;###autoload
(define-obsolete-function-alias 'company-tng-configure-default 'company-tng-mode "0.9.14"
  "Applies the default configuration to enable company-tng.")

(declare-function eglot--snippet-expansion-fn "eglot")

(defvar company-tng-map
  (let ((keymap (make-sparse-keymap)))
    (set-keymap-parent keymap company-active-map)
    (define-key keymap [return] nil)
    (define-key keymap (kbd "RET") nil)
    (define-key keymap [tab] 'company-select-next)
    (define-key keymap (kbd "TAB") 'company-select-next)
    (define-key keymap [backtab] 'company-select-previous)
    (define-key keymap (kbd "S-TAB") 'company-select-previous)
    keymap))

;;;###autoload
(define-minor-mode company-tng-mode
 "This minor mode enables `company-tng-frontend'."
  :init-value nil
  :global t
  (cond
   (company-tng-mode
    (setq company-frontends
          (add-to-list 'company-frontends 'company-tng-frontend))
    (when company-tng-auto-configure
      (setq company-frontends '(company-tng-frontend
                                company-pseudo-tooltip-frontend
                                company-echo-metadata-frontend))
      (setq company-require-match nil
            company-clang-insert-arguments nil
            company-semantic-insert-arguments nil
            company-rtags-insert-arguments nil
            lsp-enable-snippet nil)
      (advice-add #'eglot--snippet-expansion-fn :override #'ignore)
      (setq company-active-map company-tng-map))
    (setq company-selection-default nil))
   (t
    (setq company-frontends
          '(company-pseudo-tooltip-unless-just-one-frontend
            company-preview-if-just-one-frontend
            company-echo-metadata-frontend))
    (when company-tng-auto-configure
      (setq company-require-match 'company-explicit-action-p
            company-clang-insert-arguments t
            company-semantic-insert-arguments t
            company-rtags-insert-arguments t
            lsp-enable-snippet t)
      (advice-remove #'eglot--snippet-expansion-fn #'ignore)
      (setq company-active-map (keymap-parent company-tng-map)))
    (setq company-selection-default 0))))

(provide 'company-tng)
;;; company-tng.el ends here