Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 lines
8.0 KiB

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