The [[https://microsoft.github.io/language-server-protocol/][LSP]] is a way to connect /editors/ (like Emacs) to /languages/ (like Lisp) ... wait, no, it was originally designed for VS Code and probably Python, but we now abstract away [[https://github.com/davidhalter/jedi][Jedi]] and the [[http://tkf.github.io/emacs-jedi/latest/][Emacs integration to Jedi]] (and duplicate everything for Ruby, and Clojure, and...).
Instead, we install [[https://emacs-lsp.github.io/lsp-mode/][LSP Mode]] (and friends), which simplifies my configuration:
#+BEGIN_SRC emacs-lisp
(use-package lsp-mode
:init
;; Let's make lsp-doctor happy with these settings:
(setq gc-cons-threshold (* 100 1024 1024)
read-process-output-max (* 1024 1024)
company-idle-delay 0.0 ; Are thing fast enough to do this?
I will want to start adding commands under my =SPC m= mode-specific key sequence leader, but ... later.
The [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]] project offers much of the display and interface to LSP:
#+BEGIN_SRC emacs-lisp
(use-package lsp-ui
:commands lsp-ui-mode
:config
(setq lsp-ui-sideline-ignore-duplicate t
lsp-ui-sideline-show-hover t
lsp-ui-sideline-show-diagnostics t)
(add-hook 'lsp-mode-hook 'lsp-ui-mode))
#+END_SRC
The [[https://github.com/tigersoldier/company-lsp][company-lsp]] offers a [[http://company-mode.github.io/][company]] completion backend for [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]]:
#+BEGIN_SRC emacs-lisp
(use-package company-lsp
:config
(push 'company-lsp company-backends))
#+END_SRC
To options that might be interesting:
- =company-lsp-async=: When set to non-nil, fetch completion candidates asynchronously.
- =company-lsp-enable-snippet=: Set it to non-nil if you want to enable snippet expansion on completion. Set it to nil to disable this feature.
The [[https://github.com/emacs-lsp/lsp-ui/blob/master/lsp-ui-imenu.el][lsp-imenu]] offers a =lsp-ui-imenu= function for jumping to functions:
As I've mentioned [[http://www.howardism.org/Technical/Emacs/beep-for-emacs.html][on my website]], I've created a [[file:~/website/Technical/Emacs/beep-for-emacs.org][beep function]] that notifies when long running processes complete.
The idea of using math symbols for a programming languages keywords is /cute/, but confusing when working with other people, and they are looking at my screen:
#+BEGIN_SRC emacs-lisp :tangle no
(set-ligatures! 'python-mode nil)
#+END_SRC
The rest of the ligature system in Doom is nice.
** Task Runner
I've replaced my home-grown compilation list code with a more versatile [[https://github.com/emacs-taskrunner/emacs-taskrunner][Taskrunner project]].
Doom provides basic support, but we need more keybindings:
#+BEGIN_SRC emacs-lisp :tangle no
(map! :leader :prefix "p"
:desc "Project tasks" "Z" 'ivy-taskrunner
:desc "Reun last task" "z" 'ivy-taskrunner-rerun-last-command)
#+END_SRC
While my company is typically using =Rakefile= and =Makefile= in the top-level project, I want to have my personal tasks set per-project as well. For that, I thought about using [[https://pydoit.org/][doit]], where I would just create a =dodo.py= file that contains:
#+BEGIN_SRC python :tangle no
def hello():
"""This command greets you."""
return {
'actions': [ 'echo hello' ],
}
#+END_SRC
** Display Configuration
Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications:
#+BEGIN_SRC emacs-lisp
(setq doom-modeline-lsp t)
(setq doom-modeline-env-version t)
#+END_SRC
* Languages
Simple to configure languages go here. More advanced stuff will go in their own files...eventually.
** YAML/Ansible/Jinja
Doing a lot of [[https://github.com/yoshiki/yaml-mode][YAML work]], but this project needs a new maintainer.
#+BEGIN_SRC emacs-lisp
(use-package yaml-mode
:mode "\\.ya?ml\\'")
#+END_SRC
Ansible uses Jinja, so we install the [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]]:
#+BEGIN_SRC emacs-lisp
(use-package jinja2-mode
:mode (rx ".j2" eol))
#+END_SRC
And, finally, we install [[https://github.com/k1LoW/emacs-ansible][Ansible]] as well:
The [[https://github.com/luxbock/evil-cleverparens][evil-cleverparens]] solves having me create keybindings to the [[https://github.com/Fuco1/smartparens][smartparens]] project by updating the evil states with Lisp-specific bindings.
#+BEGIN_SRC emacs-lisp
(use-package evil-cleverparens
:after smartparens-mode
:custom
evil-cleverparens-use-additional-bindings t
evil-cleverparens-use-additional-movement-keys t
evil-cleverparens-use-s-and-S nil ; using evil-sniper
:init
(require 'evil-cleverparens-text-objects)
:hook (emacs-lisp-mode . evil-cleverparens-mode))
#+END_SRC
I would like to have a list of what keybindings do what:
- ~M-h~ / ~M-l~ move back/forward by functions
- ~H~ / ~L~ move back/forward by s-expression
- ~M-i~ insert at the beginning of the form
- ~M-a~ appends at the end of the form
- ~M-o~ new form after the current sexp
- ~M-O~ new form /before/ the current sexp
- ~M-j~ / ~M-k~ drags /thing at point/ and back and forth in the form
- ~>~ slurp forward if at the end of form, at beginning, it barfs backwards
- ~<~ slurp backward if at start of form, at the end, it barfs forwards
- ~M-(~ / ~M-)~ wraps next/previous form in parens (braces and brackets work too)
- ~x~ unwraps if the point is on the =(= of an expression.
- ~D~ deletes an entire s-expression, but this can depend on the position of the point.
The other advantage is moving around by s-expressions. This takes a little getting used to, for instance:
- ~[~ and ~]~ move from paren to paren, essentially, from s-expression.
- ~H~ and ~L~ act similarly to the above.
- ~(~ and ~)~ move up to the parent s-expression
Other nifty keybindings that I need to commit to muscle memory include:
| ~M-q~ | =sp-indent-defun= |
| ~M-J~ | =sp-join-sexp= |
| ~M-s~ | =sp-splice-sexp= |
| ~M-S~ | =sp-split-sexp= |
| ~M-t~ | =sp-transpose-sexp= |
| ~M-v~ | =sp-convolute-sexp= |
| ~M-r~ | =sp-raise-sexp= |
***** Eval Current Expression
A feature I enjoyed from Spacemacs is the ability to evaluate the s-expression currently containing the point. Not sure how how they made it, but cleverparens can help:
#+BEGIN_SRC emacs-lisp
(defun ha-eval-current-expression ()
"Evaluates the expression the point is currently 'in'.
It does this, by jumping to the end of the current
expression (using evil-cleverparens), and evaluating what it
finds at that point."
(interactive)
(save-excursion
(evil-cp-next-closing)
(evil-cp-forward-sexp)
(call-interactively 'eval-last-sexp)))
#+END_SRC
And we just need to bind it. The following is Doom-specific:
The [[https://github.com/tarsius/paren-face][paren-face]] project lowers the color level of parenthesis which I personally find better.
#+BEGIN_SRC emacs-lisp
(use-package paren-face
:hook (emacs-lisp-mode . paren-face-mode))
#+END_SRC
I'm going to play with the [[https://github.com/DogLooksGood/parinfer-mode][parinfer]] package.
*** Lispy
Sacha had an [[https://sachachua.com/blog/2021/04/emacs-making-a-hydra-cheatsheet-for-lispy/][interesting idea]] to /generate/ a Hydra from a mode map:
While I don't like writing them, I can't get away from them.
While filename extensions work fine most of the time, I don't like to pre-pend =.sh= to the few shell scripts I write, and instead, would like to associate =shell-mode= with all files in a =bin= directory:
*Note:* we make the script /executable/ by default. See [[https://emacsredux.com/blog/2021/09/29/make-script-files-executable-automatically/][this essay]] for details, but it appears that the executable bit is only turned on if the script has a shebang at the top of the file.