2023-09-18 16:45:14 +00:00
|
|
|
|
#+title: Emacs — Packages — Autocompletion
|
|
|
|
|
#+setupfile: ../../headers
|
|
|
|
|
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
|
|
|
|
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/autocompletion.el
|
|
|
|
|
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
|
|
|
|
|
|
|
|
|
* Autocompletion
|
|
|
|
|
** Code Autocompletion
|
|
|
|
|
Company is, in my opinion, the best autocompleting engine for Emacs,
|
|
|
|
|
and it is one of the most popular if not /the/ most popular.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package company
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:defer t
|
|
|
|
|
:hook (company-mode . evil-normalize-keymaps)
|
|
|
|
|
:init (global-company-mode)
|
|
|
|
|
:config
|
|
|
|
|
(setq company-minimum-prefix-length 2
|
|
|
|
|
company-toolsip-limit 14
|
|
|
|
|
company-tooltip-align-annotations t
|
|
|
|
|
company-require-match 'never
|
|
|
|
|
company-global-modes '(not erc-mode message-mode help-mode gud-mode)
|
|
|
|
|
company-frontends
|
|
|
|
|
'(company-pseudo-tooltip-frontend ; always show candidates in overlay tooltip
|
|
|
|
|
company-echo-metadata-frontend) ; show selected candidate docs in echo area
|
|
|
|
|
company-backends '(company-capf)
|
|
|
|
|
company-auto-commit nil
|
|
|
|
|
company-auto-complete-chars nil
|
|
|
|
|
company-dabbrev-other-buffers nil
|
|
|
|
|
company-dabbrev-ignore-case nil
|
|
|
|
|
company-dabbrev-downcase nil))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
This package is a backend for company. It emulates
|
|
|
|
|
~ac-source-dictionary~ by proposing text related to the current
|
|
|
|
|
major-mode.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package company-dict
|
|
|
|
|
:after company
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:config
|
|
|
|
|
(setq company-dict-dir (expand-file-name "dicts" user-emacs-directory)))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
On the other hand, ~company-box~ is a Company front-end which offers
|
2023-12-10 14:09:07 +00:00
|
|
|
|
colours, icons, documentation and so on. Very nice.
|
2023-09-18 16:45:14 +00:00
|
|
|
|
|
|
|
|
|
Declaring all the icons for the variable
|
|
|
|
|
~company-box-icons-all-the-icons~ is quite verbose in Elisp, so I do it
|
|
|
|
|
with an org-table.
|
|
|
|
|
#+name: company-box-icons
|
|
|
|
|
| Type | Icon | Color |
|
|
|
|
|
|---------------+--------------------------+--------|
|
|
|
|
|
| Unknown | find_in_page | purple |
|
|
|
|
|
| Text | text_fields | green |
|
|
|
|
|
| Method | functions | red |
|
|
|
|
|
| Function | functions | red |
|
|
|
|
|
| Constructor | functions | red |
|
|
|
|
|
| Field | functions | red |
|
|
|
|
|
| Variable | adjust | blue |
|
|
|
|
|
| Class | class | red |
|
|
|
|
|
| Interface | settings_input_component | red |
|
|
|
|
|
| Module | view_module | red |
|
|
|
|
|
| Property | settings | red |
|
|
|
|
|
| Unit | straighten | red |
|
|
|
|
|
| Value | filter_1 | red |
|
|
|
|
|
| Enum | plus_one | red |
|
|
|
|
|
| Keyword | filter_center_focus | red |
|
|
|
|
|
| Snippet | short_text | red |
|
|
|
|
|
| Color | color_lens | red |
|
|
|
|
|
| File | insert_drive_file | red |
|
|
|
|
|
| Reference | collections_bookmark | red |
|
|
|
|
|
| Folder | folder | red |
|
|
|
|
|
| EnumMember | people | red |
|
|
|
|
|
| Constant | pause_circle_filled | red |
|
|
|
|
|
| Struct | streetview | red |
|
|
|
|
|
| Event | event | red |
|
|
|
|
|
| Operator | control_point | red |
|
|
|
|
|
| TypeParameter | class | red |
|
|
|
|
|
| Template | short_text | green |
|
|
|
|
|
| ElispFunction | functions | red |
|
|
|
|
|
| ElispVariable | check_circle | blue |
|
|
|
|
|
| ElispFeature | stars | orange |
|
|
|
|
|
| ElispFace | format_paint | pink |
|
|
|
|
|
|
|
|
|
|
#+name: gen-company-box-icons
|
|
|
|
|
#+headers: :tangle no :noweb yes :exports none :cache yes
|
|
|
|
|
#+header: :wrap "src emacs-lisp :exports none :tangle no"
|
|
|
|
|
#+begin_src emacs-lisp :var table=company-box-icons
|
|
|
|
|
(mapconcat (lambda (row)
|
|
|
|
|
(format "(%s . ,(all-the-icons-material \"%s\" :face 'all-the-icons-%s))"
|
|
|
|
|
(car row)
|
|
|
|
|
(cadr row)
|
|
|
|
|
(caddr row)))
|
|
|
|
|
table
|
|
|
|
|
"\n")
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
#+RESULTS[8ebf4bb3f7f354571a5d42cf58f8b9ba847ba028]: gen-company-box-icons
|
|
|
|
|
#+begin_src emacs-lisp :exports none :tangle no
|
|
|
|
|
(Unknown . ,(all-the-icons-material "find_in_page" :face 'all-the-icons-purple))
|
|
|
|
|
(Text . ,(all-the-icons-material "text_fields" :face 'all-the-icons-green))
|
|
|
|
|
(Method . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
|
|
|
|
(Function . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
|
|
|
|
(Constructor . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
|
|
|
|
(Field . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
|
|
|
|
(Variable . ,(all-the-icons-material "adjust" :face 'all-the-icons-blue))
|
|
|
|
|
(Class . ,(all-the-icons-material "class" :face 'all-the-icons-red))
|
|
|
|
|
(Interface . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red))
|
|
|
|
|
(Module . ,(all-the-icons-material "view_module" :face 'all-the-icons-red))
|
|
|
|
|
(Property . ,(all-the-icons-material "settings" :face 'all-the-icons-red))
|
|
|
|
|
(Unit . ,(all-the-icons-material "straighten" :face 'all-the-icons-red))
|
|
|
|
|
(Value . ,(all-the-icons-material "filter_1" :face 'all-the-icons-red))
|
|
|
|
|
(Enum . ,(all-the-icons-material "plus_one" :face 'all-the-icons-red))
|
|
|
|
|
(Keyword . ,(all-the-icons-material "filter_center_focus" :face 'all-the-icons-red))
|
|
|
|
|
(Snippet . ,(all-the-icons-material "short_text" :face 'all-the-icons-red))
|
|
|
|
|
(Color . ,(all-the-icons-material "color_lens" :face 'all-the-icons-red))
|
|
|
|
|
(File . ,(all-the-icons-material "insert_drive_file" :face 'all-the-icons-red))
|
|
|
|
|
(Reference . ,(all-the-icons-material "collections_bookmark" :face 'all-the-icons-red))
|
|
|
|
|
(Folder . ,(all-the-icons-material "folder" :face 'all-the-icons-red))
|
|
|
|
|
(EnumMember . ,(all-the-icons-material "people" :face 'all-the-icons-red))
|
|
|
|
|
(Constant . ,(all-the-icons-material "pause_circle_filled" :face 'all-the-icons-red))
|
|
|
|
|
(Struct . ,(all-the-icons-material "streetview" :face 'all-the-icons-red))
|
|
|
|
|
(Event . ,(all-the-icons-material "event" :face 'all-the-icons-red))
|
|
|
|
|
(Operator . ,(all-the-icons-material "control_point" :face 'all-the-icons-red))
|
|
|
|
|
(TypeParameter . ,(all-the-icons-material "class" :face 'all-the-icons-red))
|
|
|
|
|
(Template . ,(all-the-icons-material "short_text" :face 'all-the-icons-green))
|
|
|
|
|
(ElispFunction . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
|
|
|
|
(ElispVariable . ,(all-the-icons-material "check_circle" :face 'all-the-icons-blue))
|
|
|
|
|
(ElispFeature . ,(all-the-icons-material "stars" :face 'all-the-icons-orange))
|
|
|
|
|
(ElispFace . ,(all-the-icons-material "format_paint" :face 'all-the-icons-pink))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package company-box
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:after (company all-the-icons)
|
|
|
|
|
:config
|
|
|
|
|
(setq company-box-show-single-candidate t
|
|
|
|
|
company-box-backends-colors nil
|
|
|
|
|
company-box-max-candidates 50
|
|
|
|
|
company-box-icons-alist 'company-box-icons-all-the-icons
|
|
|
|
|
company-box-icons-all-the-icons
|
|
|
|
|
(let ((all-the-icons-scale-factor 0.8))
|
|
|
|
|
`(
|
|
|
|
|
<<gen-company-box-icons()>>))))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
** Ivy
|
|
|
|
|
My main menu package is =ivy= which I use as much as possible –I’ve
|
|
|
|
|
noticed =helm= can be slow, very slow in comparison to =ivy=, so I’ll use
|
|
|
|
|
the latter as much as possible. Actually, only =ivy= is installed for
|
|
|
|
|
now. I could have used =ido= too, but I find it to be a bit too
|
|
|
|
|
restricted in terms of features compared to =ivy=.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package ivy
|
|
|
|
|
:straight t
|
|
|
|
|
:defer t
|
|
|
|
|
:diminish
|
|
|
|
|
:bind (("C-s" . swiper)
|
|
|
|
|
:map ivy-minibuffer-map
|
|
|
|
|
("TAB" . ivy-alt-done)
|
|
|
|
|
("C-l" . ivy-alt-done)
|
|
|
|
|
("C-t" . ivy-next-line)
|
|
|
|
|
("C-s" . ivy-previous-line)
|
|
|
|
|
("C-u" . ivy-scroll-up-command)
|
|
|
|
|
("C-d" . ivy-scroll-down-command)
|
|
|
|
|
:map ivy-switch-buffer-map
|
|
|
|
|
("C-t" . ivy-next-line)
|
|
|
|
|
("C-s" . ivy-previous-line)
|
|
|
|
|
("C-l" . ivy-done)
|
|
|
|
|
("C-d" . ivy-switch-buffer-kill)
|
|
|
|
|
:map ivy-reverse-i-search-map
|
|
|
|
|
("C-t" . ivy-next-line)
|
|
|
|
|
("C-s" . ivy-previous-line)
|
|
|
|
|
("C-d" . ivy-reverse-i-search-kill))
|
|
|
|
|
:config
|
|
|
|
|
(ivy-mode 1)
|
|
|
|
|
(setq ivy-wrap t
|
|
|
|
|
ivy-height 17
|
|
|
|
|
ivy-sort-max-size 50000
|
|
|
|
|
ivy-fixed-height-minibuffer t
|
|
|
|
|
ivy-read-action-functions #'ivy-hydra-read-action
|
|
|
|
|
ivy-read-action-format-function #'ivy-read-action-format-columns
|
|
|
|
|
projectile-completion-system 'ivy
|
|
|
|
|
ivy-on-del-error-function #'ignore
|
|
|
|
|
ivy-use-selectable-prompt t))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
There is also [[https://github.com/raxod502/prescient.el][~prescient.el~]] that offers some nice features when
|
|
|
|
|
coupled with ~ivy~, guess what was born out of it? ~ivy-prescient~, of
|
|
|
|
|
course!
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package ivy-prescient
|
|
|
|
|
:after ivy
|
|
|
|
|
:straight (:build t))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
I warned you I’d use too much ~all-the-icons~, I did!
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package all-the-icons-ivy
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:after (ivy all-the-icons)
|
|
|
|
|
:init (all-the-icons-ivy-setup)
|
|
|
|
|
:hook (after-init . all-the-icons-ivy-setup))
|
|
|
|
|
(all-the-icons-ivy-setup)
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
A buffer popping at the bottom of the screen is nice and all, but have
|
2023-12-10 14:09:07 +00:00
|
|
|
|
you considered a floating buffer in the centre of your frame?
|
2023-09-18 16:45:14 +00:00
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package ivy-posframe
|
|
|
|
|
:defer t
|
|
|
|
|
:after (:any ivy helpful)
|
|
|
|
|
:hook (ivy-mode . ivy-posframe-mode)
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:init
|
|
|
|
|
(ivy-posframe-mode 1)
|
|
|
|
|
:config
|
|
|
|
|
(setq ivy-fixed-height-minibuffer nil
|
|
|
|
|
ivy-posframe-border-width 10
|
|
|
|
|
ivy-posframe-parameters
|
|
|
|
|
`((min-width . 90)
|
|
|
|
|
(min-height . ,ivy-height))))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
Something that can be missing sometimes in Ivy is the ability to
|
|
|
|
|
select multiple entries at once. For instance, when programming in
|
|
|
|
|
Java, LPS can offer you to automatically generate the methods ~equals~
|
|
|
|
|
and ~hashCode~ based on selected members, but with vanilla Ivy, you can
|
|
|
|
|
only select one. Not really useful. ~ivy-hydra~ is a package that offers
|
|
|
|
|
a Hydra interface when in Ivy which allows you to select multiple
|
|
|
|
|
choices among other things.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package ivy-hydra
|
|
|
|
|
:requires (ivy hydra)
|
|
|
|
|
:after ivy
|
|
|
|
|
:straight (:build t))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
Finally, let’s make ~ivy~ richer:
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package ivy-rich
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:after ivy
|
|
|
|
|
:init
|
|
|
|
|
(ivy-rich-mode 1))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
** Counsel
|
|
|
|
|
I could almost merge this chapter with the previous one since counsel
|
|
|
|
|
is a package that provides loads of completion functions for ivy. The
|
|
|
|
|
ones I find most useful are ~counsel-M-x~ and ~counsel-find-file~.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package counsel
|
|
|
|
|
:straight t
|
|
|
|
|
:after recentf
|
|
|
|
|
:after ivy
|
|
|
|
|
:bind (("M-x" . counsel-M-x)
|
|
|
|
|
("C-x b" . counsel-ibuffer)
|
|
|
|
|
("C-x C-f" . counsel-find-file)
|
|
|
|
|
:map minibuffer-local-map
|
|
|
|
|
("C-r" . 'counsel-minibuffer-history)))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
** Yasnippet
|
|
|
|
|
Yasnippet allows you to insert some pre-made code by just typing a few
|
|
|
|
|
characters. It can even generate some string with Elisp expressions
|
|
|
|
|
and ask the user for some input in some precise places.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package yasnippet
|
|
|
|
|
:defer t
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:init
|
|
|
|
|
(yas-global-mode)
|
|
|
|
|
:hook ((prog-mode . yas-minor-mode)
|
|
|
|
|
(text-mode . yas-minor-mode)))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
Of course, yasnippet wouldn’t be as awesome as it is without pre-made
|
|
|
|
|
snippets.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package yasnippet-snippets
|
|
|
|
|
:defer t
|
|
|
|
|
:after yasnippet
|
|
|
|
|
:straight (:build t))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
Similarly, yatemplate offers pre-made files rather than just strings.
|
|
|
|
|
That’s still yasnippet by the way.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package yatemplate
|
|
|
|
|
:defer t
|
|
|
|
|
:after yasnippet
|
|
|
|
|
:straight (:build t))
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
And finally, with ivy you can choose your snippets from a menu if
|
|
|
|
|
you’re not sure or if you don’t remember what your snippet is.
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
(use-package ivy-yasnippet
|
|
|
|
|
:defer t
|
|
|
|
|
:after (ivy yasnippet)
|
|
|
|
|
:straight (:build t)
|
|
|
|
|
:general
|
|
|
|
|
(phundrak/leader-key
|
|
|
|
|
:infix "i"
|
|
|
|
|
:packages 'ivy-yasnippet
|
|
|
|
|
"y" #'ivy-yasnippet))
|
|
|
|
|
#+end_src
|