spacemacs/layers/+completion/auto-completion/funcs.el

413 lines
16 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; funcs.el --- Auto-completion functions File -*- lexical-binding: t; -*-
;;
;; Copyright (c) 2012-2022 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;; This program 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.
;;
;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
(spacemacs|add-toggle auto-completion
:status
(if (eq 'company auto-completion-front-end)
(bound-and-true-p company-mode)
(bound-and-true-p auto-complete-mode))
:on
(progn
(if (eq 'company auto-completion-front-end)
(company-mode)
(auto-complete-mode))
(message "Enabled auto-completion (using %S)."
auto-completion-front-end))
:off
(progn
(if (eq 'company auto-completion-front-end)
(company-mode -1)
(auto-complete-mode -1))
(message "Disabled auto-completion."))
:documentation "Enable auto-completion."
:evil-leader "ta")
;; company backends declaration macro
(defmacro spacemacs|add-company-backends (&rest props)
"Add and enable company backends.
This function should be called exclusively in `post-init-company' functions or
`init-company-xxx' function where xxx is company backend package.
Available PROPS:
`:backends BACKENDS'
One or several symbols or lists representing a company backend or a list of
company backends.
`:modes MODES'
One or several modes where BACKENDS will be added.
`:variables VAR VALUE'
One or several VAR VALUE pairs (similar to layer variables).
These variables are made buffer local so their values are set only for
the given MODES.
`:from SYMBOL'
Advanced property aimed at avoiding hook function name conflicts when
`:variables' property is used in several calls to this macro for the same
MODES.
`:append-hook BOOLEAN'
Advanced property to control whether hooks functions are hooked or not,
if non-nil hook functions are appended to modes hooks passed as `:modes'.
`:call-hooks BOOLEAN'
if non-nil then hooked functions are called right away."
(declare (indent 0))
(let* ((backends (spacemacs/mplist-get-values props :backends))
(modes (spacemacs/mplist-get-values props :modes))
(variables (spacemacs/mplist-get-values props :variables))
(from (spacemacs/mplist-get-value props :from))
(hooks (if (memq :append-hooks props)
(spacemacs/mplist-get-value props :append-hooks)
t))
(call-hooks (when (memq :call-hooks props)
(spacemacs/mplist-get-value props :call-hooks)))
(result '(progn)))
(dolist (mode modes)
(let ((backends-var-name (intern (format "company-backends-%S" mode)))
(raw-backends-var-name (intern (format "company-backends-%S-raw"
mode)))
(init-func-name (intern (format "spacemacs//init-company-%S" mode)))
(vars-func-name (intern
(format "spacemacs//init-company-vars-%S%s" mode
(if from (format "-%S" from) ""))))
(mode-hook-name (intern (format "%S-hook" mode))))
;; declare buffer local company-backends variable
(push `(defvar ,raw-backends-var-name
spacemacs-default-company-backends
,(format "Company backend list for %S." mode)) result)
(push `(defvar ,backends-var-name ,raw-backends-var-name
,(format "Company backend list for %S." mode)) result)
;; add backends
(dolist (backend backends)
(push `(add-to-list ',raw-backends-var-name ',backend) result))
;; define initialization hook function
(push `(defun ,init-func-name ()
,(format "Initialize company for %S." mode)
(if auto-completion-enable-snippets-in-popup
(setq ,backends-var-name
(mapcar 'spacemacs//show-snippets-in-company
,raw-backends-var-name))
(setq ,backends-var-name ,raw-backends-var-name))
(set (make-variable-buffer-local 'auto-completion-front-end)
'company)
(set (make-variable-buffer-local 'company-backends)
,backends-var-name)) result)
(when call-hooks
(push `(,init-func-name) result))
(when hooks
(push `(add-hook ',mode-hook-name ',init-func-name t) result))
;; define variables hook function
(when variables
(let ((variables-copy variables)
(vars-func `(defun ,vars-func-name ()
,(format "Define company local variables for %S."
mode)))
vars)
(while variables-copy
(let* ((var (pop variables-copy))
(forms
(when (consp variables-copy)
`(set (make-variable-buffer-local ',var)
,(eval (pop variables-copy))))))
(when forms (push forms vars))))
(push (append vars-func vars) result))
(when call-hooks
(push `(,vars-func-name) result))
(when hooks
(push `(add-hook ',mode-hook-name ',vars-func-name t) result)))
(when hooks
(push `(add-hook ',mode-hook-name 'company-mode t) result))))
;; return the expanded macro in correct order
(reverse result)))
(defmacro spacemacs|disable-company (mode)
"Disable company for the given MODE.
MODE parameter must match the :modes values used in the call to
`spacemacs|add-company-backends'."
(let ((mode-hook-name (intern (format "%S-hook" mode)))
(func (intern (format "spacemacs//init-company-%S" mode))))
`(progn
(remove-hook ',mode-hook-name ',func)
(remove-hook ',mode-hook-name 'company-mode))))
(defun spacemacs//show-snippets-in-company (backend)
(if (or (not auto-completion-enable-snippets-in-popup)
(and (listp backend) (member 'company-yasnippet backend)))
backend
(append (if (consp backend) backend (list backend))
'(:with company-yasnippet))))
;; auto-completion key bindings functions
(defun spacemacs//auto-completion-set-RET-key-behavior (package)
"Bind RET key appropriately for the given PACKAGE and value of
`auto-completion-return-key-behavior'."
(cond
((eq 'company package)
(let ((map company-active-map))
(cond
((eq 'complete auto-completion-return-key-behavior)
(define-key map [return] 'company-complete-selection)
(define-key map (kbd "RET") 'company-complete-selection))
(t
(define-key map [return] 'nil)
(define-key map (kbd "RET") 'nil)))))
(t (message "Not yet implemented for package %S" package))))
(defun spacemacs//auto-completion-set-TAB-key-behavior (package)
"Bind TAB key appropriately for the given PACKAGE and value of
`auto-completion-tab-key-behavior'."
(cond
((eq 'company package)
(let ((map company-active-map))
(cond
((eq 'complete auto-completion-tab-key-behavior)
(define-key map (kbd "TAB") 'company-complete-selection)
(define-key map (kbd "<tab>") 'company-complete-selection))
((eq 'cycle auto-completion-tab-key-behavior)
(define-key map (kbd "TAB") 'company-complete-common-or-cycle)
(define-key map (kbd "<tab>") 'company-complete-common-or-cycle)
(define-key map (kbd "<S-tab>")
'spacemacs//company-complete-common-or-cycle-backward)
(define-key map (kbd "<backtab>")
'spacemacs//company-complete-common-or-cycle-backward))
(t
(define-key map (kbd "TAB") nil)
(define-key map (kbd "<tab>") nil)))))
(t (message "Not yet implemented for package %S" package))))
(defun spacemacs//auto-completion-setup-key-sequence (package)
"Setup the key sequence to complete current selection."
(when auto-completion-complete-with-key-sequence
(let ((first-key (elt auto-completion-complete-with-key-sequence 0)))
(cond ((eq 'company package)
(define-key company-active-map (kbd (char-to-string first-key))
'spacemacs//auto-completion-key-sequence-start))
(t (message "Not yet implemented for package %S" package))))))
;; key sequence to complete selection
(defvar spacemacs--auto-completion-time nil)
(defvar spacemacs--auto-completion-complete-last-candidate nil)
(defvar spacemacs--auto-completion-shadowed-insert-binding nil)
(defvar spacemacs--auto-completion-shadowed-emacs-binding nil)
(defvar spacemacs--auto-completion-shadowed-hybrid-binding nil)
(defun spacemacs//auto-completion-key-sequence-start ()
"Initiate auto-completion sequence."
(interactive)
(self-insert-command 1)
(setq spacemacs--auto-completion-complete-last-candidate
(cond
((bound-and-true-p company-mode)
(nth company-selection company-candidates))))
;; enable second key of the sequence
(let ((second-key (kbd (char-to-string
(elt auto-completion-complete-with-key-sequence 1)))))
(setq spacemacs--auto-completion-shadowed-insert-binding
(lookup-key evil-insert-state-map second-key))
(setq spacemacs--auto-completion-shadowed-emacs-binding
(lookup-key evil-emacs-state-map second-key))
(setq spacemacs--auto-completion-shadowed-hybrid-binding
(lookup-key evil-hybrid-state-map second-key))
(define-key
evil-insert-state-map
second-key
'spacemacs//auto-completion-key-sequence-end)
(define-key
evil-emacs-state-map
second-key
'spacemacs//auto-completion-key-sequence-end)
(define-key
evil-hybrid-state-map
second-key
'spacemacs//auto-completion-key-sequence-end))
;; set a timer to restore the old bindings
(run-at-time auto-completion-complete-with-key-sequence-delay
nil
'spacemacs//auto-completion-key-sequence-restore)
(when spacemacs--auto-completion-complete-last-candidate
(setq spacemacs--auto-completion-time (current-time))))
(defun spacemacs//auto-completion-key-sequence-end ()
"Check if the auto-completion key sequence has been entered."
(interactive)
(if (or (null spacemacs--auto-completion-time)
(< auto-completion-complete-with-key-sequence-delay
(float-time (time-since spacemacs--auto-completion-time))))
(self-insert-command 1)
(cond
((bound-and-true-p company-mode)
(unless company-candidates
;; if the auto-completion menu is still active then we don't need to
;; delete the last inserted first key of the sequence
(delete-char -1))
(let ((company-idle-delay))
(company-auto-begin)
(company-finish spacemacs--auto-completion-complete-last-candidate)))))
(spacemacs//auto-completion-key-sequence-restore)
(setq spacemacs--auto-completion-time nil))
(defun spacemacs//auto-completion-key-sequence-restore ()
"Restore the shadowed key bindings used to auto-complete."
(let ((second-key (kbd (char-to-string
(elt auto-completion-complete-with-key-sequence 1)))))
(define-key
evil-insert-state-map
second-key
spacemacs--auto-completion-shadowed-insert-binding)
(define-key
evil-emacs-state-map
second-key
spacemacs--auto-completion-shadowed-emacs-binding)
(define-key
evil-hybrid-state-map
second-key
spacemacs--auto-completion-shadowed-hybrid-binding)))
;; Editing style
(defun spacemacs//company-active-navigation (style)
"Set navigation for the given editing STYLE."
(cond
((or (eq 'vim style)
(and (eq 'hybrid style)
hybrid-style-enable-hjkl-bindings))
(dolist (map (list company-active-map company-search-map))
(define-key map (kbd "C-j") 'company-select-next)
(define-key map (kbd "C-k") 'company-select-previous)
(define-key map (kbd "C-l") 'company-complete-selection))
;; Fix company-quickhelp Evil C-k
(let ((prev nil))
(defun spacemacs//set-C-k-company-select-previous (&rest args)
(setf prev (lookup-key evil-insert-state-map (kbd "C-k")))
(define-key evil-insert-state-map (kbd "C-k") 'company-select-previous))
(defun spacemacs//restore-C-k-evil-insert-digraph (&rest args)
(define-key evil-insert-state-map (kbd "C-k") prev)))
(add-hook 'company-completion-started-hook 'spacemacs//set-C-k-company-select-previous)
(add-hook 'company-completion-finished-hook 'spacemacs//restore-C-k-evil-insert-digraph)
(add-hook 'company-completion-cancelled-hook 'spacemacs//restore-C-k-evil-insert-digraph))
(t
(dolist (map (list company-active-map company-search-map))
(define-key map (kbd "C-n") 'company-select-next)
(define-key map (kbd "C-p") 'company-select-previous)))))
(defvar-local company-fci-mode-on-p nil)
(defun company-turn-off-fci (&rest ignore)
(when (boundp 'fci-mode)
(setq company-fci-mode-on-p fci-mode)
(when fci-mode (fci-mode -1))))
(defun company-maybe-turn-on-fci (&rest ignore)
(when company-fci-mode-on-p (fci-mode 1)))
;; helm-yas
(defun spacemacs/helm-yas ()
"Properly lazy load helm-c-yasnipper."
(interactive)
(spacemacs/load-yasnippet)
(require 'helm-c-yasnippet)
(call-interactively 'helm-yas-complete))
;; ivy-yas
(defun spacemacs/ivy-yas ()
"Lazy load ivy-yasnippet"
(interactive)
(spacemacs/load-yasnippet)
(require 'ivy-yasnippet)
(call-interactively 'ivy-yasnippet))
;; Yasnippet
(defun spacemacs/load-yasnippet ()
(unless yas-global-mode (yas-global-mode 1))
(yas-minor-mode 1))
(defun spacemacs/force-yasnippet-off ()
(yas-minor-mode -1)
(setq yas-dont-activate t))
;; Auto-Yasnippet
(defun spacemacs/auto-yasnippet-expand ()
"Call `yas-expand' and switch to `insert state'"
(interactive)
(call-interactively 'aya-expand)
(evil-insert-state))
;; Yasnippet and Smartparens
;; If enabled, smartparens will mess snippets expanded by `hippie-expand`.
;; We want to temporarily disable Smartparens during the snippet expansion and
;; switch it back to the initial state when done.
;;
;; However, there is an asymmetry in Yasnippet's hooks:
;; * `yas-before-expand-snippet-hook' is called for all snippet expansions,
;; including the nested ones.
;; * `yas-after-exit-snippet-hook' is called only for the top level snippet,
;; but NOT for the nested ones.
;;
;; That's why we introduce `spacemacs--yasnippet-expanding' below.
;;
;; MWO 2021-03-16
;; I have removed spacemacs--yasnippet-expanding as it prevents
;; default yasnippet expansions from seeing the value of smartparens-mode.
;; This will effectively disable smartparens with the first yasnippet expand.
;; As `hippie-expand' is less frequently used than yasnippet I think it is
;; better to have smartparens state preserved with the default case.
(defvar spacemacs--smartparens-enabled-initially t
"Stored whether smartparens is originally enabled or not.")
(defun spacemacs//smartparens-disable-before-expand-snippet ()
"Handler for `yas-before-expand-snippet-hook'.
Disable smartparens and remember its initial state."
;; Remember the initial smartparens state only once, when expanding a top-level snippet.
(setq spacemacs--smartparens-enabled-initially (or smartparens-mode smartparens-strict-mode))
(spacemacs//deactivate-smartparens))
(defun spacemacs//smartparens-restore-after-exit-snippet ()
"Handler for `yas-after-exit-snippet-hook'.
Restore the initial state of smartparens."
(when spacemacs--smartparens-enabled-initially
(spacemacs//activate-smartparens)))