72cd815902
LSP is mostly code jumping with the xref, and I wanted to have some keybinding consistency. This also loads the LSP map only when eglot is turned on, making the menu more consistent as well.
560 lines
25 KiB
Org Mode
560 lines
25 KiB
Org Mode
#+TITLE: General Programming Configuration
|
||
#+AUTHOR: Howard X. Abrams
|
||
#+DATE: 2020-10-26
|
||
|
||
A literate programming file for helping me program.
|
||
|
||
#+begin_src emacs-lisp :exports none
|
||
;;; general-programming --- Configuration for general languages. -*- lexical-binding: t; -*-
|
||
;;
|
||
;; © 2020-2022 Howard X. Abrams
|
||
;; Licensed under a Creative Commons Attribution 4.0 International License.
|
||
;; See http://creativecommons.org/licenses/by/4.0/
|
||
;;
|
||
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
|
||
;; Maintainer: Howard X. Abrams
|
||
;; Created: October 26, 2020
|
||
;;
|
||
;; This file is not part of GNU Emacs.
|
||
;;
|
||
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
|
||
;; ~/other/hamacs/general-programming.org
|
||
;; And tangle the file to recreate this one.
|
||
;;
|
||
;;; Code:
|
||
#+end_src
|
||
* Introduction
|
||
Seems that all programming interfaces and workflows behave similarly. One other helper routine is a =general= macro for org-mode files:
|
||
#+begin_src emacs-lisp
|
||
(general-create-definer ha-prog-leader
|
||
:states '(normal visual motion)
|
||
:keymaps 'prog-mode-map
|
||
:prefix "SPC m"
|
||
:global-prefix "<f17>"
|
||
:non-normal-prefix "S-SPC")
|
||
#+end_src
|
||
* General
|
||
The following work for all programming languages.
|
||
** direnv
|
||
Farm off commands into /virtual environments/:
|
||
#+begin_src emacs-lisp
|
||
(use-package direnv
|
||
:init
|
||
(setq direnv--executable "/usr/local/bin/direnv"
|
||
direnv-always-show-summary t
|
||
direnv-show-paths-in-summary t)
|
||
:config
|
||
(direnv-mode))
|
||
#+end_src
|
||
** Spell Checking Comments
|
||
The [[https://www.emacswiki.org/emacs/FlySpell#h5o-2][flyspell-prog-mode]] checks for misspellings in comments.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package flyspell
|
||
:hook (prog-mode . flyspell-prog-mode))
|
||
#+end_src
|
||
** Flycheck
|
||
Why use [[https://www.flycheck.org/][flycheck]] over the built-in =flymake=? Speed used to be the advantage, but I’m now pushing much of this to LSP, so speed is less of an issue. What about when I am not using LSP? Also, since I’ve hooked grammar checkers, I need this with global keybindings.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package flycheck
|
||
:init
|
||
(setq next-error-message-highlight t)
|
||
:bind (:map flycheck-error-list-mode-map
|
||
("C-n" . 'flycheck-error-list-next-error)
|
||
("C-p" . 'flycheck-error-list-previous-error)
|
||
("j" . 'flycheck-error-list-next-error)
|
||
("k" . 'flycheck-error-list-previous-error))
|
||
:config
|
||
(flymake-mode -1)
|
||
(global-flycheck-mode)
|
||
(ha-leader "t c" 'flycheck-mode)
|
||
|
||
(ha-leader
|
||
">" '("next problem" . flycheck-next-error)
|
||
"<" '("previous problem" . flycheck-previous-error)
|
||
|
||
"P" '(:ignore t :which-key "problems")
|
||
"P b" '("error buffer" . flycheck-buffer)
|
||
"P c" '("clear" . flycheck-clear)
|
||
"P n" '("next" . flycheck-next-error)
|
||
"P N" '("next" . flycheck-next-error)
|
||
"P p" '("previous" . flycheck-previous-error)
|
||
"P P" '("previous" . flycheck-previous-error)
|
||
"P l" '("list all" . flycheck-list-errors)
|
||
"P y" '("copy errors" . flycheck-copy-errors-as-kill)
|
||
"P s" '("select checker" . flycheck-select-checker)
|
||
"P ?" '("describe checker" . flycheck-describe-checker)
|
||
"P h" '("display error" . flycheck-display-error-at-point)
|
||
"P e" '("explain error" . flycheck-explain-error-at-point)
|
||
"P H" '("help" . display-local-help)
|
||
"P i" '("manual" . flycheck-manual)
|
||
"P V" '("version" . flycheck-version)
|
||
"P v" '("verify-setup" . flycheck-verify-setup)
|
||
"P x" '("disable-checker" . flycheck-disable-checker)
|
||
"P t" '("toggle flycheck" . flycheck-mode)))
|
||
#+end_src
|
||
** Documentation
|
||
I’m interested in using [[https://devdocs.io/][devdocs]] instead, which is similar, but keeps it all /inside/ Emacs (and works on my Linux system). Two Emacs projects compete for this position. The Emacs [[https://github.com/astoff/devdocs.el][devdocs]] project is active, and seems to work well. Its advantage is a special mode for moving around the documentation.
|
||
#+begin_src emacs-lisp
|
||
(use-package devdocs
|
||
:general (:states 'normal "gD" 'devdocs-lookup)
|
||
|
||
:config
|
||
(ha-prog-leader
|
||
"d" '(:ignore t :which-key "docs")
|
||
"d e" '("eldoc" . eldoc)
|
||
"d d" '("open" . devdocs-lookup)
|
||
"d p" '("peruse" . devdocs-peruse)
|
||
"d i" '("install" . devdocs-install)
|
||
"d u" '("update" . devdocs-update-all)
|
||
"d x" '("uninstall" . devdocs-delete)
|
||
"d s" '("search" . devdocs-search)))
|
||
#+end_src
|
||
|
||
The [[https://github.com/blahgeek/emacs-devdocs-browser][devdocs-browser]] project acts similar, but with slightly different command names. Its advantage is that it allows for downloading docs and having it available offline, in fact, you can’t search for a function, until you download its pack. This is slightly faster because of this.
|
||
#+begin_src emacs-lisp :tangle no
|
||
(use-package devdocs-browser
|
||
:general (:states 'normal "gD" 'devdocs-browser-open)
|
||
|
||
:config
|
||
(ha-prog-leader
|
||
"d" '(:ignore t :which-key "docs")
|
||
"d d" '("open" . devdocs-browser-open)
|
||
"d D" '("open in" . devdocs-browser-open-in)
|
||
"d l" '("list" . devdocs-browser-list-docs)
|
||
"d u" '("update" . devdocs-browser-update-docs)
|
||
"d i" '("install" . devdocs-browser-install-doc)
|
||
"d x" '("uninstall" . devdocs-browser-uninstall-doc)
|
||
"d U" '("upgrade" . devdocs-browser-upgrade-doc)
|
||
"d o" '("download" . devdocs-browser-download-offline-data)
|
||
"d O" '("remove download" . devdocs-browser-remove-offline-data)))
|
||
#+end_src
|
||
** Code Folding
|
||
While Emacs has options for viewing and moving around code, sometimes, we could /collapse/ all functions, and then start to expand them one at a time. For this, we could enable the built-in [[https://www.emacswiki.org/emacs/HideShow][hide-show feature]]:
|
||
#+begin_src emacs-lisp :tangle no
|
||
(use-package hide-show
|
||
:straight (:type built-in)
|
||
:init
|
||
(setq hs-hide-comments t
|
||
hs-hide-initial-comment-block t
|
||
hs-isearch-open t)
|
||
:hook (prog-mode . hs-minor-mode))
|
||
#+end_src
|
||
Note that =hide-show= doesn’t work with complex YAML files. The [[https://github.com/gregsexton/origami.el][origami]] mode works better /out-of-the-box/, as it works with Python and Lisp, but falls back to indents as the format, which works well.
|
||
#+begin_src emacs-lisp
|
||
(use-package origami
|
||
:init
|
||
(setq origami-fold-replacement "⤵")
|
||
:hook (prog-mode . origami-mode))
|
||
#+end_src
|
||
To take advantage of this, type:
|
||
- ~z m~ :: To collapse everything
|
||
- ~z r~ :: To open everything
|
||
- ~z o~ :: To open a particular section
|
||
- ~z c~ :: To collapse a /section/ (like a function)
|
||
- ~z a~ :: Toggles open to close
|
||
|
||
Note: Yes, we could use [[https://github.com/mrkkrp/vimish-fold][vimish-fold]] (and its cousin, [[https://github.com/alexmurray/evil-vimish-fold][evil-vimish-fold]]) and we’ll see if I need those.
|
||
** Navigation with dumb-jump
|
||
Once upon a time, we use to create a =TAGS= file that contained the database for navigating code bases, but with new faster versions of grep, e.g. [[https://beyondgrep.com][ack]], [[https://github.com/ggreer/the_silver_searcher][ag]] (aka, the Silver Searcher), [[https://github.com/Genivia/ugrep][ugrep]] and [[https://github.com/BurntSushi/ripgrep][ripgrep]], we should be able to use them. but I want to:
|
||
- Be in a function, and see its callers. For this, the [[help:rg-dwim][rg-dwim]] function is my bread-and-butter.
|
||
- Be on a function, and jump to the definition. For this, I use [[https://github.com/jacktasia/dumb-jump][dumb-jump]], which uses the above utilities.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package dumb-jump
|
||
:init
|
||
(setq dumb-jump-prefer-searcher 'rg)
|
||
|
||
:config
|
||
(setq xref-show-definitions-function #'xref-show-definitions-completing-read)
|
||
|
||
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
|
||
;; (add-to-list 'evil-goto-definition-functions #'dumb-jump)
|
||
|
||
;; Remove this now that https://github.com/jacktasia/dumb-jump/issues/338
|
||
;; (defun evil-set-jump-args (&rest ns) (evil-set-jump))
|
||
;; (advice-add 'dumb-jump-goto-file-line :before #'evil-set-jump-args)
|
||
|
||
(ha-prog-leader
|
||
"s" '(:ignore t :which-key "search")
|
||
"s s" '("search" . xref-find-apropos)
|
||
"s d" '("definitions" . xref-find-definitions)
|
||
"s o" '("other window" . xref-find-definitions-other-window)
|
||
"s r" '("references" . xref-find-references)
|
||
"s b" '("back" . xref-go-back)
|
||
"s f" '("forward" . xref-go-forward))
|
||
|
||
:general (:states 'normal
|
||
"g." 'xref-find-definitions
|
||
"g>" 'xref-find-definitions-other-window
|
||
"g," 'xref-go-back
|
||
"g<" 'xref-go-forward
|
||
"g/" 'xref-find-references
|
||
"g?" 'xref-find-references-and-replace
|
||
"gh" 'xref-find-apropos
|
||
"gb" 'xref-go-back))
|
||
#+end_src
|
||
|
||
I have two different /jumping/ systems, the [[info:emacs#Xref][Xref interface]] and Evil’s. While comparable goals, they are behave different. Let’s compare evil keybindings:
|
||
| ~M-.~ | ~g .~ | [[help:xref-find-definitions][xref-find-definitions]] (also ~g d~ for [[help:evil-goto-definition][evil-goto-definition]])† |
|
||
| | ~g >~ | =xref-find-definitions-other-window= |
|
||
| ~M-,~ | ~g ,~ | [[help:xref-go-back][xref-go-back]] (see [[help:xref-pop-marker-stack][xref-pop-marker-stack]]) |
|
||
| ~C-M-,~ | ~g <~ | [[help:xref-go-forward][xref-go-forward]] (kinda like =xref-find-definitions=) |
|
||
| ~M-?~ | ~g /~ | [[help:xref-find-references][xref-find-references]] to go from definition to code calls‡ |
|
||
| | ~g ?~ | [[help:xref-find-references-and-replace][xref-find-references-and-replace]] could be more accurate than [[*iEdit][iEdit]]. |
|
||
| ~C-M-.~ | ~g h~ | [[help:xref-find-apropos][xref-find-apropos]] … doesn’t work well without LSP |
|
||
| ~C-TAB~ | | perform completion around point (also ~M-TAB~), see [[file:ha-config.org::*Auto Completion][Auto Completion]]. |
|
||
|
||
† Prefix to prompt for the term \
|
||
‡ If it finds more than one definition, Emacs displays the [[info:emacs#Xref Commands][*xref* buffer]], allowing you to select the definition.
|
||
** Language Server Protocol (LSP) Integration
|
||
The [[https://microsoft.github.io/language-server-protocol/][LSP]] is a way to connect /editors/ (like Emacs) to /languages/ (like Lisp)… wait, no. While originally designed for VS Code and probably Python, we can 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…).
|
||
|
||
Emacs has two LSP projects, and while I have used [[LSP Mode]], but since I don’t have heavy IDE requirements, I am finding that [[eglot]] to be simpler.
|
||
*** eglot
|
||
The [[https://github.com/joaotavora/eglot][eglot]] package connects to Emacs’ standard command interface, so the eglot-specific code is connects the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html][xref interface]] to controlling backend servers. That said, it has a couple of =eglot-= commands that I want easy access to:
|
||
#+begin_src emacs-lisp
|
||
(use-package eglot
|
||
:init
|
||
(setq eglot-connect-timeout 10
|
||
eglot-autoshutdown t)
|
||
|
||
:config
|
||
(ha-prog-leader
|
||
"w" '(:ignore t :which-key "eglot")
|
||
"ws" '("start" . eglot))
|
||
|
||
;; The following leader-like keys, are only available when I have started LSP:
|
||
:general
|
||
(:states 'normal :keymaps 'eglot-mode-map
|
||
"SPC m w r" '("restart" . eglot-reconnect)
|
||
"SPC m w b" '("events" . eglot-events-buffer)
|
||
"SPC m w e" '("errors" . eglot-stderr-buffer)
|
||
"SPC m w q" '("quit" . eglot-shutdown)
|
||
"SPC m w Q" '("quit all" . eglot-shutdown-all)
|
||
|
||
"SPC m l" '(:ignore t :which-key "lsp")
|
||
"SPC m l r" '("rename" . eglot-rename)
|
||
"SPC m l f" '("format" . eglot-format)
|
||
"SPC m l a" '("actions" . eglot-code-actions)
|
||
"SPC m l i" '("imports" . eglot-code-action-organize-imports)
|
||
"SPC m l d" '("doc" . eglot-lookup-documentation)))
|
||
#+end_src
|
||
|
||
The following was stolen from Doom’s configuration:
|
||
#+begin_src emacs-lisp
|
||
(defvar eglot--help-buffer nil)
|
||
|
||
(defun eglot-lookup-documentation ()
|
||
"Request documentation for the thing at point."
|
||
(interactive)
|
||
(eglot--dbind ((Hover) contents range)
|
||
(jsonrpc-request (eglot--current-server-or-lose) :textDocument/hover
|
||
(eglot--TextDocumentPositionParams))
|
||
(let ((blurb (and (not (seq-empty-p contents))
|
||
(eglot--hover-info contents range)))
|
||
(hint (thing-at-point 'symbol)))
|
||
(if blurb
|
||
(with-current-buffer
|
||
(or (and (buffer-live-p eglot--help-buffer)
|
||
eglot--help-buffer)
|
||
(setq eglot--help-buffer (generate-new-buffer "*eglot-help*")))
|
||
(with-help-window (current-buffer)
|
||
(rename-buffer (format "*eglot-help for %s*" hint))
|
||
(with-current-buffer standard-output (insert blurb))
|
||
(setq-local nobreak-char-display nil)))
|
||
(display-local-help))))
|
||
'deferred)
|
||
#+end_src
|
||
*** eglot with Consult
|
||
The [[https://github.com/mohkale/consult-eglot][consult-eglot]] project adds a [[file:ha-config.org::*Consult][Consult]] interface to lookup symbols from the LSP server.
|
||
#+begin_src emacs-lisp
|
||
(use-package consult-eglot
|
||
:general
|
||
(:states 'normal :keymaps 'eglot-mode-map
|
||
"g h" '("find apropos" . consult-eglot-symbols)
|
||
"SPC m l s" '("find symbol" . consult-eglot-symbols)))
|
||
#+end_src
|
||
*** Display Configuration
|
||
Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications:
|
||
#+begin_src emacs-lisp
|
||
(use-package doom-modeline
|
||
:config
|
||
(setq doom-modeline-lsp t
|
||
doom-modeline-env-version t))
|
||
#+end_src
|
||
** Function Call Notifications
|
||
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.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package alert
|
||
:init
|
||
(setq alert-default-style
|
||
(if (ha-running-on-macos?)
|
||
'osx-notifier
|
||
'libnotify)))
|
||
|
||
(use-package beep
|
||
:straight nil ; Already in the load-path
|
||
:hook (after-init . (lambda () (beep--when-finished "Emacs has started")))
|
||
:config
|
||
(dolist (func '(org-publish
|
||
org-publish-all
|
||
org-publish-project
|
||
compile
|
||
shell-command))
|
||
(advice-add func :around #'beep-when-runs-too-long)))
|
||
#+end_src
|
||
While that code /advices/ the publishing and compile commands, I may want to add more.
|
||
** iEdit
|
||
While there are language-specific ways to rename variables and functions, [[https://github.com/victorhge/iedit][iedit]] is often sufficient.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package iedit
|
||
:config
|
||
(ha-leader "s e" '("iedit" . iedit-mode)))
|
||
#+end_src
|
||
** Commenting
|
||
I like =comment-dwim= (~M-;~), and I like =comment-box=, but I have an odd personal style that I like to codify:
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun ha-comment-line (&optional start end)
|
||
"Comment a line or region with a block-level format.
|
||
Calls `comment-region' with START and END set to the region or
|
||
the start and end of the line."
|
||
(interactive)
|
||
(when (or (null start) (not (region-active-p)))
|
||
(setq start (line-beginning-position))
|
||
(setq end (line-end-position)))
|
||
(save-excursion
|
||
(narrow-to-region start end)
|
||
(upcase-region start end)
|
||
(goto-char (point-min))
|
||
(insert "------------------------------------------------------------------------\n")
|
||
(goto-char (point-max))
|
||
(insert "\n------------------------------------------------------------------------")
|
||
(comment-region (point-min) (point-max))
|
||
(widen)))
|
||
#+end_src
|
||
And a keybinding:
|
||
#+begin_src emacs-lisp
|
||
(ha-prog-leader "c" '("comment line" . ha-comment-line))
|
||
#+end_src
|
||
** Evaluation
|
||
While I like [[help:eval-print-last-sexp][eval-print-last-sexp]], I would like a bit of formatting in order to /keep the results/ in the file.
|
||
#+begin_src emacs-lisp
|
||
(defun ha-eval-print-last-sexp (&optional internal-arg)
|
||
"Evaluate the expression located before the point.
|
||
Insert results back into the buffer at the end of the line after
|
||
a comment."
|
||
(interactive)
|
||
(save-excursion
|
||
(eval-print-last-sexp internal-arg))
|
||
(end-of-line)
|
||
(insert " ")
|
||
(insert comment-start)
|
||
(insert "⟹ ")
|
||
(dotimes (i 2)
|
||
(next-line)
|
||
(join-line)))
|
||
#+end_src
|
||
|
||
Typical keybindings for all programming modes:
|
||
#+begin_src emacs-lisp
|
||
(ha-prog-leader
|
||
"e" '(:ignore t :which-key "eval")
|
||
"e ;" '("expression" . eval-expression)
|
||
"e b" '("buffer" . eval-buffer)
|
||
"e f" '("function" . eval-defun)
|
||
"e r" '("region" . eval-region)
|
||
"e e" '("last s-exp" . eval-last-sexp)
|
||
"e p" '("print s-exp" . ha-eval-print-last-sexp))
|
||
#+end_src
|
||
** Ligatures
|
||
The idea of using math symbols for a programming languages keywords is /cute/, but can be confusing, so I use it sparingly:
|
||
#+begin_src emacs-lisp
|
||
(defun ha-prettify-prog ()
|
||
"Extends the `prettify-symbols-alist' for programming."
|
||
(mapc (lambda (pair) (push pair prettify-symbols-alist))
|
||
'(("lambda" . "𝝀")
|
||
(">=" . "≥")
|
||
("<=" . "≤")
|
||
("!=" . "≠")))
|
||
(prettify-symbols-mode))
|
||
|
||
(add-hook 'prog-mode-hook 'ha-prettify-prog)
|
||
#+end_src
|
||
Hopefully I can follow [[https://www.masteringemacs.org/article/unicode-ligatures-color-emoji][Mickey Petersen's essay]] on getting full ligatures working, but right now, they don’t work on the Mac, and that is my current workhorse.
|
||
** Task Runner
|
||
I've replaced my home-grown compilation list code with a more versatile [[https://github.com/emacs-taskrunner/emacs-taskrunner][Taskrunner project]].
|
||
#+begin_src emacs-lisp :tangle no
|
||
(setq ivy-taskrunner-notifications-on t
|
||
ivy-taskrunner-doit-bin-path "/usr/local/bin/doit")
|
||
#+end_src
|
||
|
||
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 create a =dodo.py= file that contains:
|
||
|
||
#+begin_src python :tangle no
|
||
def hello():
|
||
"""This command greets you."""
|
||
return {
|
||
'actions': [ 'echo hello' ],
|
||
}
|
||
#+end_src
|
||
|
||
* Languages
|
||
Simple to configure languages go here. More advanced languages go into their own files… eventually.
|
||
** Markdown
|
||
All the READMEs and other documentation use [[https://jblevins.org/projects/markdown-mode/][markdown-mode]].
|
||
#+begin_src emacs-lisp
|
||
(use-package markdown-mode
|
||
:mode ("README\\.md\\'" . gfm-mode)
|
||
:init (setq markdown-command "multimarkdown")
|
||
:general
|
||
(:states 'normal :no-autoload t :keymaps 'markdown-mode-map
|
||
"SPC m l" '("insert link" . markdown-insert-link)
|
||
;; SPC u 3 SPC m h for a third-level header:
|
||
"SPC m h" '("insert header" . markdown-insert-header-dwim)
|
||
"SPC m e" '("export" . markdown-export)
|
||
"SPC m p" '("preview" . markdown-export-and-preview)))
|
||
#+end_src
|
||
Note that the markdown-specific commands use the ~C-c C-c~ and ~C-c C-s~ prefixes.
|
||
** Ansible
|
||
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 (rx ".y" (optional "a") "ml" string-end))
|
||
#+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" string-end))
|
||
#+end_src
|
||
|
||
Do I consider all YAML files an Ansible file needing [[https://github.com/k1LoW/emacs-ansible][ansible-mode]]?
|
||
#+begin_src emacs-lisp
|
||
(use-package ansible
|
||
:init
|
||
(setq ansible-vault-password-file "~/.ansible-vault-passfile")
|
||
;; :hook (yaml-mode . ansible-mode)
|
||
:config
|
||
(ha-leader "t y" 'ansible))
|
||
#+end_src
|
||
The [[help:ansible-vault-password-file][ansible-vault-password-file]] variable needs to change /per project/, so let’s use the =.dir-locals.el= file, for instance:
|
||
#+begin_src emacs-lisp :tangle no
|
||
((nil . ((ansible-vault-password-file . "playbooks/.vault-password"))))
|
||
|
||
#+end_src
|
||
|
||
The YAML files get access Ansible’s documentation using the [[https://github.com/emacsorphanage/ansible-doc][ansible-doc]] project:
|
||
#+begin_src emacs-lisp
|
||
(use-package ansible-doc
|
||
:hook (yaml-mode . ansible-doc-mode)
|
||
:config
|
||
(ha-local-leader :keymaps 'yaml-mode-map
|
||
"d" '(:ignore t :which-key "docs")
|
||
"d d" 'ansible-doc))
|
||
#+end_src
|
||
|
||
The [[https://github.com/emacsmirror/poly-ansible][poly-ansible]] project uses [[https://polymode.github.io/][polymode]], gluing [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]] into [[https://github.com/yoshiki/yaml-mode][yaml-mode]].
|
||
#+begin_src emacs-lisp
|
||
(use-package polymode)
|
||
|
||
(use-package poly-ansible
|
||
:after polymode
|
||
:straight (:host github :repo "emacsmirror/poly-ansible")
|
||
:hook ((yaml-mode . poly-ansible-mode)
|
||
(poly-ansible-mode . font-lock-update)))
|
||
#+end_src
|
||
|
||
Can we integrate Ansible with LSP using [[https://github.com/ansible/ansible-language-server][ansible-language-server]] project (see [[https://emacs-lsp.github.io/lsp-mode/page/lsp-ansible/][this documentation]])?
|
||
|
||
First, use =npm= to install the program:
|
||
#+begin_src sh
|
||
npm installl -g @ansible/ansible-language-server
|
||
#+end_src
|
||
|
||
Let’s assume that all YAML files can have access to this:
|
||
#+begin_src emacs-lisp
|
||
(use-package eglot
|
||
:config
|
||
(add-to-list 'eglot-server-programs '(yaml-mode "ansible-language-server" "--stdio")))
|
||
#+end_src
|
||
** Shell Scripts
|
||
While I don't like writing them, I can't get away from them. Check out the goodies in [[https://www.youtube.com/watch?v=LTC6SP7R1hA&t=5s][this video]].
|
||
|
||
While filename extensions work fine most of the time, I don't like to pre-pend =.sh= to the shell scripts I write, and instead, would like to associate =shell-mode= with all files in a =bin= directory:
|
||
#+begin_src emacs-lisp
|
||
(use-package sh-mode
|
||
:straight (:type built-in)
|
||
:mode (rx (or (seq ".sh" eol)
|
||
"/bin/"))
|
||
:init
|
||
(setq sh-basic-offset 2
|
||
sh-indentation 2)
|
||
:config
|
||
(ha-auto-insert-file (rx (or (seq ".sh" eol)
|
||
"/bin/"))
|
||
"sh-mode.sh")
|
||
:hook
|
||
(after-save . executable-make-buffer-file-executable-if-script-p))
|
||
#+end_src
|
||
*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 turns on the executable bit if the script has a shebang at the top of the file.
|
||
|
||
The [[https://www.shellcheck.net/][shellcheck]] project integrates with [[Flycheck]]. First, install the executable into the system, for instance, on a Mac:
|
||
#+begin_src sh
|
||
brew install shellcheck
|
||
#+end_src
|
||
And we can enable it:
|
||
#+begin_src emacs-lisp
|
||
(flycheck-may-enable-checker 'sh-shellcheck)
|
||
#+end_src
|
||
Place the following /on a line/ before a shell script warning to ignore it:
|
||
#+begin_src sh
|
||
# shellcheck disable=SC2116,SC2086
|
||
#+end_src
|
||
See [[https://github.com/koalaman/shellcheck/wiki/Ignore][this page]] for details.
|
||
|
||
Integration with the [[https://github.com/bash-lsp/bash-language-server][Bash LSP implementation]]. First, install that too:
|
||
#+begin_src sh
|
||
brew install bash-language-server
|
||
#+end_src
|
||
** Fish Shell
|
||
I think the [[https://fishshell.com/][fish shell]] is an interesting experiment (and I appreciate the basics that come with [[https://github.com/emacsmirror/fish-mode][fish-mode]]).
|
||
#+begin_src emacs-lisp
|
||
(use-package fish-mode
|
||
:mode (rx ".fish" eol)
|
||
:config
|
||
(ha-auto-insert-file (rx ".fish") "fish-mode.sh")
|
||
:hook
|
||
(fish-mode . (lambda () (add-hook 'before-save-hook 'fish_indent-before-save))))
|
||
#+end_src
|
||
* Technical Artifacts :noexport:
|
||
Provide a name to =require= this code.
|
||
#+begin_src emacs-lisp :exports none
|
||
(provide 'ha-programming)
|
||
;;; ha-programming.el ends here
|
||
#+end_src
|
||
|
||
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
|
||
|
||
#+DESCRIPTION: A literate programming file for helping me program.
|
||
|
||
#+PROPERTY: header-args:sh :tangle no
|
||
#+PROPERTY: header-args:emacs-lisp yes
|
||
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
|
||
|
||
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
|
||
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
|
||
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
|