;; completion-settings.el --- Completion settings  -*- lexical-binding: t -*-

;;; Commentary:
;;  <M-tab> alias of C-M-i
;;    in general `completion-at-point' (`minibuffer.el')
;;    in python-mode `anaconde-mode-complete'

;; manual https://oremacs.com/swiper/
;; org:
;; - org-insert-structure-template is bound to C-c C-,
;; - for easy-template require org-tempo or add to org-modules
;;   https://orgmode.org/manual/Easy-templates.html#Easy-templates

;; Requirements:
;;   amx https://melpa.org/#/?q=amx
;;   ivy (instead of ido) https://elpa.gnu.org/packages/ivy.html
;;     counsel    https://github.com/abo-abo/swiper
;;       swiper   https://github.com/abo-abo/swiper
;;   company https://elpa.gnu.org/packages/company.html
;;     config for R see ess-settings.el
;;   company-quickhelp https://github.com/company-mode/company-quickhelp
;;     pos-tip
;;   yasnippet
;;     popup
;;   yasnippet-snippets https://melpa.org/#/yasnippet-snippets

;;; Code:
(use-package amx
  :defer t
  :config
  (setq amx-save-file (concat user-cache-directory "amx-items")))

;; in an ivy action minibuffer use M-o `ivy-dispatching-done' to see
;; valid actions.
(use-package ivy
  :delight (ivy-mode "Ivy")  ;; \u24BE i
  :defer 0.1
  ;; Ivy-based interface to standard commands
  :bind (("C-s" . swiper)
         ("C-c C-r" . ivy-resume)     ;;  resumes the last Ivy-based completion.
         ("<f6>" . ivy-resume)
         ("C-c v" . ivy-push-view)
         ("C-c V" . ivy-pop-view))
  :config
  (ivy-mode 1)  ;; also with `ivy-rich-mode' (see below). `ivy-mode' still active for views not defined by `ivy-rich-mode'.
  (setq ivy-use-virtual-buffers t)    ;; Add recent files and bookmarks to the ivy-switch-buffer
  (setq ivy-count-format "%d/%d ")    ;; Displays the current and total number in the collection in the prompt
  (setq enable-recursive-minibuffers t)
  (setq ivy-extra-directories nil)    ;; remove ./ and ../ from list, because <tab> on them would open dird mode. default: '("../" "./")
  (setq ivy-use-selectable-prompt t)  ;; make the prompt line selectable. Example: create a file bar when a file barricade exists in the current directory. Also C-M-j usable
  ;; enable this if you want `swiper' to use it
  ;; (setq search-default-mode #'char-fold-to-regexp)
  )

(use-package ivy-rich
  :after (ivy counsel)
  :config
  (setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line)
  (ivy-rich-mode 1))

(use-package swiper
  :after ivy)

(use-package counsel
  :after ivy
  :bind (("M-x" . counsel-M-x)
         ("M-y" . counsel-yank-pop)
         ;; ("C-x b" . counsel-switch-buffer)
         ;; ("C-x b" . counsel-ibuffer)
         ("C-x b" . counsel-buffer-or-recentf)
         ("C-x C-f" . counsel-find-file)
         ("C-x C-r" . counsel-recentf)
         ;; ("<f1> f" . describe-function)  ;; counsel-describe-function, not set bc of helpful.el
         ;; ("<f1> v" . describe-variable)  ;; counsel-describe-variable, net set bc of helpful.el
         ("<f1> l" . find-library)  ;; find-library works better with counsel as counsel-find-library
         ("<f2> i" . counsel-info-lookup-symbol)
         ("<f2> u" . counsel-unicode-char)
         ("<f2> j" . counsel-set-variable)
         ;; Ivy-based interface to shell and system tools
         ("C-c c" . counsel-compile)
         ("C-c g" . counsel-git)
         ("C-c j" . counsel-git-grep)
         ("C-c L" . counsel-git-log)
         ("C-c k" . counsel-rg)  ;; 'counsel-ag 'counsel-rg
         ("C-c m" . counsel-linux-app)
         ("C-c n" . counsel-fzf)
         ("C-x l" . counsel-locate)
         ("C-c J" . counsel-file-jump)
         ("C-S-o" . counsel-rhythmbox)
         ("C-c w" . counsel-wmctrl)
         ;; other commands
         ("C-c b" . counsel-bookmark)
         ("C-c d" . counsel-descbinds)
         ("C-c o" . counsel-outline)
         ("C-c t" . counsel-load-theme)
         ("C-c F" . counsel-org-file)
         :map minibuffer-local-map
         ("C-r" . counsel-minibuffer-history))
  :config
  ;; Do not open dired for directories when using counsel-find-file
  ;; https://emacs.stackexchange.com/questions/33701/do-not-open-dired-for-directories-when-using-counsel-find-file
  (let ((done (where-is-internal #'ivy-done     ivy-minibuffer-map t))
        (alt  (where-is-internal #'ivy-alt-done ivy-minibuffer-map t)))
    (define-key counsel-find-file-map done #'ivy-alt-done)
    (define-key counsel-find-file-map alt  #'ivy-done)
    (define-key counsel-find-file-map (kbd "TAB") 'ivy-alt-done)
    (define-key counsel-find-file-map (kbd "<tab>") 'ivy-alt-done)
    ;; <left> and <right> not used to be able to navigate inside the text
    ;; https://oremacs.com/swiper/#file-name-completion
    ;; ivy-done            completion for file, but opens dird mode for directories
    ;; ivy-alt-done        complete dir or open file, but without entering any character it opens dird mode
    ;; ivy-immediate-done  no completion
    )

  ;; previously defined in tmm.el (removed by commit on Oct 24, 2020)
  ;; counsel-tmm uses `tmm-get-keybind'
  (defun tmm-get-keybind (keyseq)
    "Return the current binding of KEYSEQ, merging prefix definitions.
If KEYSEQ is a prefix key that has local and global bindings,
we merge them into a single keymap which shows the proper order of the menu.
However, for the menu bar itself, the value does not take account
of `menu-bar-final-items'."
    (lookup-key (cons 'keymap (nreverse (current-active-maps))) keyseq))
  )

;;(setq tab-always-indent 'complete)  ;; use 'complete when auto-complete is disabled

(use-package company
  :delight (company-mode "Co")  ;; \u24B8 c
  ;;:bind (("C-M-i" . company-complete))
  :bind (([remap completion-at-point] . company-complete)
         :map lisp-interaction-mode-map
         ("M-?" . (lambda () (interactive) (describe-symbol (symbol-at-point))))
         :map emacs-lisp-mode-map
         ("M-?" . (lambda () (interactive) (describe-symbol (symbol-at-point)))))
         ;; see org bind below
  :hook ((after-init . global-company-mode)
         (minibuffer-setup . company-mode)) ;; minibuffer-setup-hook
  :config
  (setq company-idle-delay nil)   ;; no idle completion, invoke with <M-tab>
  (setq company-minimum-prefix-length 2)
  (setq company-require-match nil)
  (setq company-dabbrev-ignore-case nil)
  (setq company-dabbrev-downcase nil)

  ;; https://github.com/syl20bnr/spacemacs/tree/develop/layers/%2Bcompletion/auto-completion
  (defvar my-completion-menu-tab-key-behavior 'cycle
    "What the TAB key should do when completion menu is active.
Possible values are `complete', `cycle' or nil.")
  (setq my-completion-tab-key-behavior 'complete)
  (defun my-completion-menu-set-tab-key-behavior (package)
    "Bind TAB key appropriately for the given PACKAGE and value of
`my-completion-menu-tab-key-behavior'.
Possible PACKAGE values are `company'."
    (cond
     ((eq 'company package)
      (let ((map company-active-map))
        (cond
         ((eq 'complete my-completion-menu-tab-key-behavior)
          (define-key map (kbd "TAB") 'company-complete-selection)
          (define-key map (kbd "<tab>") 'company-complete-selection))
         ((eq 'cycle my-completion-menu-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>")
            (lambda () (company-complete-common-or-cycle -1)))
          (define-key map (kbd "<backtab>")
            (lambda () (company-complete-common-or-cycle -1)))
          )
         (t
          (define-key map (kbd "TAB") nil)
          (define-key map (kbd "<tab>") nil)))))
     (t (message "Not yet implemented for package %S" package))))
  (my-completion-menu-set-tab-key-behavior 'company)

  (with-eval-after-load 'org
    (define-key org-mode-map [remap complete-symbol] 'company-complete)))

;; org completion with pcomplete company-capf
(defun my-org-mode-completion-hook ()
  (add-hook 'completion-at-point-functions 'pcomplete-completions-at-point nil t))
(add-hook 'org-mode-hook #'my-org-mode-completion-hook)

(use-package company-quickhelp
  :after company
  :bind (:map company-active-map
         ("C-c h" . company-quickhelp-manual-begin))  ;; manually trigger the help popup, but only when company is doing its thing.
  ;;:config
  ;;(company-quickhelp-mode 1)  ;; deactivated bc/ auto show is not good
  )

(use-package yasnippet   ;; https://elpa.gnu.org/packages/yasnippet.html
  :delight (yas-minor-mode "Ys")  ;; \u24CE y
  :hook ((prog-mode . yas-minor-mode)
         (org-mode . yas-minor-mode))
  ;; see also variable yas-snippet-dirs, the yasnippet-snippets
  ;; collection dir is added via require 'yasnippet-snipets
  :config
  (define-key yas-minor-mode-map (kbd "C-c & C-s") nil)
  (define-key yas-minor-mode-map (kbd "C-c & C-n") nil)
  (define-key yas-minor-mode-map (kbd "C-c & C-v") nil)
  (define-key yas-minor-mode-map (kbd "C-c &") nil)
  (setq yas-snippet-dirs (list (concat config-dir "snippets")))
  ;; global
  ;;(yas-global-mode 1)
  ;; on a per-buffer basis
  (yas-reload-all))

;; use popup menu for yas-choose-value
(use-package popup
  :after yasnippet
  :bind (:map popup-menu-keymap  ;; add some shotcuts in popup menu mode
              ("M-n" . popup-next)
              ("TAB" . popup-next)
              ("<tab>" . popup-next)
              ("<backtab>" . popup-previous)
              ("M-p" . popup-previous))
  :config
  (defun yas-popup-isearch-prompt (prompt choices &optional display-fn)
    (when (featurep 'popup)
      (popup-menu*
       (mapcar
        (lambda (choice)
          (popup-make-item
           (or (and display-fn (funcall display-fn choice))
               choice)
           :value choice))
        choices)
       :prompt prompt
       ;; start isearch mode immediately
       :isearch t
       )))
  (setq yas-prompt-functions
        '(yas-popup-isearch-prompt yas-maybe-ido-prompt yas-completing-prompt yas-no-prompt)))

(use-package yasnippet-snippets
  :after (yasnippet)
  :defer 1)

(provide 'completion-settings)
;;; completion-settings.el ends here
