I never actually use it (I mostly wrote it to impress Ava) so time for it to be relegated to the git history.
1499 lines
47 KiB
Org Mode
1499 lines
47 KiB
Org Mode
#+TITLE: Emacs Configuration
|
|
#+AUTHOR: Camden Dixie O'Brien
|
|
#+ATTR_LATEX: :float t
|
|
|
|
* Customize
|
|
Hey, customize, leave my ~/.emacs.d/init.el alone!
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq custom-file (concat user-emacs-directory "customize.el"))
|
|
(load custom-file t)
|
|
#+end_src
|
|
|
|
* Package Management
|
|
** MELPA
|
|
Let's be real here, all the good stuff's on MELPA.
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'package)
|
|
(add-to-list 'package-archives
|
|
'("melpa" . "https://melpa.org/packages/"))
|
|
#+end_src
|
|
|
|
** Initialization
|
|
We now need to run [[help:package-initialize][package-initialize]] to load and activate
|
|
packages. The documentation advises doing this early in
|
|
configuration.
|
|
|
|
#+begin_src emacs-lisp
|
|
(package-initialize)
|
|
#+end_src
|
|
|
|
We also need to fetch the package archives from ELPA and MELPA,
|
|
unless they have already been fetched:
|
|
|
|
#+begin_src emacs-lisp
|
|
(unless package-archive-contents
|
|
(package-refresh-contents))
|
|
#+end_src
|
|
|
|
** =use-package=
|
|
The rest of this config grabs packages via =use-package=, so that
|
|
needs to be installed:
|
|
|
|
#+begin_src emacs-lisp
|
|
(when (not (package-installed-p 'use-package))
|
|
(package-install 'use-package))
|
|
#+end_src
|
|
|
|
The wanted behaviour for =use-package= here is to ensure all used
|
|
packages are present.
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'use-package-ensure)
|
|
(setq use-package-always-ensure t)
|
|
#+end_src
|
|
|
|
** Hostname
|
|
It's useful to have the short hostname available in case we want to
|
|
configure something differently on certain machines. The full
|
|
hostname can be retrieved with [[help:system-name][system-name]], but this has the local
|
|
domain on the end; we want to cut that off.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun cut-at (delimeter string)
|
|
(substring string 0 (seq-position string delimeter)))
|
|
|
|
(defvar hostname (cut-at ?. (system-name)))
|
|
#+end_src
|
|
|
|
* Emacs Server
|
|
Start an Emacs server if one is not running already:
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'server)
|
|
(unless (server-running-p)
|
|
(server-start))
|
|
#+end_src
|
|
|
|
With this, files opened with =emacsclient= in a terminal will open
|
|
in Emacs. Most of the time I use Emacs itself for my file browsing
|
|
and terminal needs but =emacsclient= is still handy from
|
|
time-to-time.
|
|
|
|
* UI
|
|
The start-up message gets pretty annoying, so disable that.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq inhibit-startup-screen t)
|
|
#+end_src
|
|
|
|
Also, the menu-, tool- and scroll-bar are ugly, take up space and I
|
|
don't use them.
|
|
|
|
#+begin_src emacs-lisp
|
|
(menu-bar-mode -1)
|
|
(tool-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
#+end_src
|
|
|
|
It's nice to have an inverse of =C-x o= for switching between
|
|
windows. =other-window=, the function that =C-x o= is bound to,
|
|
takes an argument =COUNT= that determines how many windows it skips
|
|
forwards so we can simply pass -1 to =other-window= in a lambda and
|
|
bind to that:
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-x O")
|
|
(lambda ()
|
|
(interactive)
|
|
(other-window -1)))
|
|
#+end_src
|
|
|
|
** Font
|
|
I find serif fonts much nicer to read, so definitely change the
|
|
default face to a serif font. Font size seems inconsistent across
|
|
different systems for some reason, so need to determine the font
|
|
height based off the hostname.
|
|
|
|
#+begin_src emacs-lisp
|
|
(let ((font-height
|
|
(pcase hostname
|
|
("zora" 100)
|
|
("eddie" 100)
|
|
("mandarax" 115)
|
|
("valis" 80)
|
|
("wipc23120423" 100)
|
|
(_ 110))))
|
|
(set-face-attribute 'default nil
|
|
:family "Courier 10 Pitch"
|
|
:height font-height))
|
|
#+end_src
|
|
|
|
I also like a little more line spacing than default, again makes
|
|
code nicer to read.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default line-spacing 0.2)
|
|
#+end_src
|
|
|
|
** Colour Scheme
|
|
Currently using =spacemacs-theme='s light variant.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package spacemacs-theme
|
|
:defer t)
|
|
(load-theme 'spacemacs-light t)
|
|
#+end_src
|
|
|
|
The first tweak I make is changing the borders around the mode line
|
|
to be two pixels thick and the same colour as the background (of
|
|
the active mode line that is).
|
|
|
|
#+begin_src emacs-lisp
|
|
(set-face-attribute 'mode-line nil
|
|
:box '(:line-width 2 :color "#e7e5eb" :style nil))
|
|
(set-face-attribute 'mode-line-inactive nil
|
|
:box '(:line-width 2 :color "#e7e5eb" :style nil))
|
|
#+end_src
|
|
|
|
I also set the right window divider to the same colour as the
|
|
header background.
|
|
|
|
#+begin_src emacs-lisp
|
|
(set-face-attribute 'window-divider nil :foreground "#efeae9")
|
|
(setq initial-frame-alist '((right-divider-width . 1)))
|
|
#+end_src
|
|
|
|
** Opening buffers in current window
|
|
There are several places where buffers open in different windows to
|
|
the currently selected one. I find this behaviour annoying and I
|
|
don't understand why anyone would like it. [[help:display-buffer-alist][display-buffer-alist]]
|
|
provides a mechanism for preventing this where there isn't a better
|
|
way, as seems to be the case with shell and help buffers.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun open-in-same-window-p (buffer-name action)
|
|
(or (string= (upcase buffer-name) "*SHELL*")
|
|
(string= (upcase buffer-name) "*HELP*")))
|
|
|
|
(setq display-buffer-alist
|
|
'((open-in-same-window-p . (display-buffer-same-window . nil))))
|
|
#+end_src
|
|
|
|
* Autocompletion
|
|
Enable =company-mode= globally, and hook it into =completion-at-point-functions=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package company
|
|
:config
|
|
(add-hook 'after-init-hook 'global-company-mode)
|
|
(add-to-list 'company-backends 'company-capf))
|
|
#+end_src
|
|
|
|
And enable =ido-mode= everywhere, with flexible matching.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ido
|
|
:config
|
|
(setq ido-enable-flex-matching t)
|
|
(add-hook 'after-init-hook
|
|
(lambda ()
|
|
(ido-everywhere)
|
|
(ido-mode t))))
|
|
#+end_src
|
|
|
|
** Snippets
|
|
YASnippet is the de facto snippet engine. There are two packages
|
|
for it: one for the engine itself and one with a library of
|
|
snippets:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package yasnippet)
|
|
(use-package yasnippet-snippets)
|
|
#+end_src
|
|
|
|
I don't want it enabled all the time, only in certain modes. The
|
|
[[help:yas-minor-mode][yas-minor-mode]] function is provided for this, but it requires that
|
|
the snippet tables are loaded beforehand, so that has to be done
|
|
now.
|
|
|
|
#+begin_src emacs-lisp
|
|
(yas-reload-all)
|
|
#+end_src
|
|
|
|
* Calendar / Diary
|
|
Weeks start on Sunday by default, this can be changed to start on
|
|
Monday by setting [[help:calendar-week-start-day][calendar-week-start-day]] to 1:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq calendar-week-start-day 1)
|
|
#+end_src
|
|
|
|
Set latitute, longitude and location name to Bristol to get sunrise
|
|
and sunset times:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq calendar-latitude 51.45)
|
|
(setq calendar-longitude -2.58)
|
|
(setq calendar-location-name "Bristol, UK")
|
|
#+end_src
|
|
|
|
Emacs needs to be told where the diary file is, of course:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq diary-file "~/Documents/diary")
|
|
#+end_src
|
|
|
|
I want to use ISO-style dates in there:
|
|
|
|
#+begin_src emacs-lisp
|
|
(calendar-set-date-style 'iso)
|
|
#+end_src
|
|
|
|
* Org
|
|
** Code and Quote block shortcuts
|
|
I am a big fan of using =<s= for source blocks and =<q= for quotes;
|
|
these are enabled by the =org-tempo= module, which is included in
|
|
=org= but not loaded by default.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org :config (require 'org-tempo))
|
|
#+end_src
|
|
|
|
However, I have recently discovered, much to my despair, that these
|
|
shortcuts do not work if there are tabs in the line ahead of them!
|
|
Quite ridiculous. Easily worked around, however; I am going to
|
|
ensure that spaces are used for indentation when in org mode by
|
|
setting [[help:indent-tabs-mode][indent-tabs-mode]] to nil in a hook:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'org-mode-hook (lambda () (setq indent-tabs-mode nil)))
|
|
#+end_src
|
|
|
|
** Keybindings
|
|
A keybinding to add a new heading is super useful
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'org-mode-hook
|
|
(lambda ()
|
|
(define-key org-mode-map
|
|
(kbd "<C-M-return>")
|
|
'org-insert-heading-after-current)))
|
|
#+end_src
|
|
|
|
** Journal Files
|
|
Sometimes I like to make a todo list for a day if I've a lot to do,
|
|
or write a little bit about a day if it's been particularly
|
|
eventful. In both of these cases, it would be nice to just be able
|
|
to hit a particular keybinding and have the right buffer pop up. I
|
|
was using =org-roam='s daily notes feature for this, but I ended up
|
|
getting annoyed with =org-roam= (too many features for my taste
|
|
lol).
|
|
|
|
The convention I'm going for is for is having a particular
|
|
directory for these journal entries and then give each file a name
|
|
like "2022-10-30.org". With that in mind, there are two obvious
|
|
variables to define:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar journal-directory
|
|
"~/Documents/org/journal"
|
|
"Directory to store journal entries in.")
|
|
|
|
(defvar journal-filename-format
|
|
"%F"
|
|
"Date format to use for journal entries' filenames (not including
|
|
the \".org\" extension)")
|
|
#+end_src
|
|
|
|
I can't be bothered to handle [[help:journal-directory][journal-directory]] not existing in the
|
|
main code, so I'm just going to make sure it exists here. The
|
|
second argument to [[help:make-directory][make-directory]] specifies to create parent
|
|
directories too if necessary.
|
|
|
|
#+begin_src emacs-lisp
|
|
(unless (file-directory-p journal-directory)
|
|
(make-directory journal-directory t))
|
|
#+end_src
|
|
|
|
The next step is to define a function to determine the filename for
|
|
today's journal entry. This is fairly straightforward, using
|
|
[[help:format-time-string][format-time-string]] to get the current date in the right format,
|
|
then sticking that together with the directory, extension, etc.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun journal-entry-filename ()
|
|
"Returns the filename for today's journal entry."
|
|
(let ((date-string (format-time-string journal-filename-format)))
|
|
(concat journal-directory "/" date-string ".org")))
|
|
#+end_src
|
|
|
|
If the journal entry doesn't exist yet, I want it to be populated
|
|
with the long-form date as the title:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar journal-title-date-format
|
|
"%A, %-e %B %+4Y"
|
|
"The date format to use for journal entries' titles.")
|
|
|
|
(defun insert-default-journal-entry-contents ()
|
|
"Insert the default journal entry contents (currently this is
|
|
just today's long-form date as a title) into the current buffer."
|
|
(insert "#+TITLE: "
|
|
(format-time-string journal-title-date-format)
|
|
"\n\n"))
|
|
#+end_src
|
|
|
|
We can now make a function to open today's journal entry fairly
|
|
trivially using [[help:find-file][find-file]], and the above utilities. It should be
|
|
interactive, as this is what we'll be calling in the key binding.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun open-journal-entry ()
|
|
"Opens today's journal entry, populating it with the default
|
|
contents if it does not already exist."
|
|
(interactive)
|
|
(let* ((filename (journal-entry-filename))
|
|
(new-entry (not (file-exists-p filename))))
|
|
(find-file filename)
|
|
(when new-entry
|
|
(insert-default-journal-entry-contents))))
|
|
#+end_src
|
|
|
|
Finally, [[help:open-journal-entry][open-journal-entry]] can be bound to a key:
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-c t") 'open-journal-entry)
|
|
#+end_src
|
|
|
|
** Source Blocks
|
|
Pressing tab inside a source block should indent appropriately for its
|
|
language.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-src-tab-acts-natively t)
|
|
#+end_src
|
|
|
|
=babel= lets us evaluate Org documents containing source blocks!
|
|
I've left the enabling of this for most languages to the section
|
|
for that language, but I'll add Shell and Elisp here.
|
|
|
|
#+begin_src emacs-lisp
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
'((emacs-lisp . t)
|
|
(shell . t)))
|
|
#+end_src
|
|
|
|
The interface org-babel exposes is a little annoying as later in
|
|
this config we'll want to preserve the prior value of
|
|
[[help:org-babel-load-languages][org-babel-load-languages]] when enabling another language. However,
|
|
~org-babel-do-load-languages~ /sets the passed symbol to the passed
|
|
value/ with [[help:set-default][set-default]] before going through the pairs in
|
|
~org-babel-load-languages~. Don't ask me why, seems like an
|
|
obviously bad design. Workaround is to define a function here to
|
|
use later so at least it won't /look/ ugly.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun enable-org-babel-lang (lang)
|
|
"Enable executing source block in the passed language in
|
|
org-mode. Doesn't affect other enabled languages."
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
(append org-babel-load-languages `((,lang . t)))))
|
|
#+end_src
|
|
|
|
By default trying to execute a source block prompts you, which is
|
|
super annoying since I'm realistically not going to try to run any
|
|
code from Org documents I haven't written, so that needs
|
|
disabling. You can do that by setting [[help:org-confirm-babel-evaluate][org-confirm-babel-evaluate]] to
|
|
=nil=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-confirm-babel-evaluate nil)
|
|
#+end_src
|
|
|
|
Another annoying thing that happens by default is the clobbering of
|
|
the window layout when you open a source block. You can change that
|
|
by setting [[help:org-src-window-setup][org-src-window-setup]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-src-window-setup 'split-window-below)
|
|
#+end_src
|
|
|
|
*** Asyncronous Execution
|
|
=ob-async= makes source blocks with the ~:async~ keyword execute
|
|
asyncronously, super handy for long-running snippets etc.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ob-async)
|
|
#+end_src
|
|
|
|
** Exporting
|
|
I very rarely want a table of contents, as most of my org documents
|
|
are pretty short.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-export-with-toc nil)
|
|
#+end_src
|
|
|
|
Also don't want section numbering for similar reasons:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-export-with-section-numbers nil)
|
|
#+end_src
|
|
|
|
*** HTML
|
|
=htmlize= is needed for decent HTML exporting, but there is no need
|
|
for all that stuff at the bottom.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package htmlize)
|
|
(setq org-html-postamble nil)
|
|
#+end_src
|
|
|
|
I like margins and line height:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-html-style
|
|
"<style>
|
|
body {
|
|
margin: 3em auto;
|
|
max-width: 42em;
|
|
padding: 0 2em;
|
|
}
|
|
</style>")
|
|
#+end_src
|
|
|
|
*** LaTeX
|
|
Use =minted= (LaTeX package) to do syntax highlighting in code blocks:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'org-latex-packages-alist '("" "minted"))
|
|
(setq org-latex-listings 'minted)
|
|
#+end_src
|
|
|
|
=minted= actually calls =pygments= through the shell, which =pdflatex=
|
|
doesn't like; you have to tell it not to worry, and that everything is
|
|
going to be OK.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-latex-pdf-process
|
|
'("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
|
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
|
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
|
|
#+end_src
|
|
|
|
** Default Applications
|
|
It's all fun and games until =C-c C-e h o= opens the source code.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-file-apps
|
|
'(("html" . "firefox %s")
|
|
(auto-mode . emacs)))
|
|
#+end_src
|
|
|
|
** Workflow States
|
|
I like to have =IN-PROGRESS= and =CANCELLED= workflow states as
|
|
well as the standard =TODO= and =DONE=. Cancelled items also want a
|
|
note attached explaining why. All this can be added by setting
|
|
[[help:org-todo-keywords][org-todo-keywords]]:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-todo-keywords
|
|
'((sequence "TODO" "IN-PROGRESS" "|" "DONE" "CANCELLED(@)")))
|
|
#+end_src
|
|
|
|
The ="|"= separates /needs further action/ states (before it) from
|
|
/no further action needed/ states (after it).
|
|
|
|
I also want to log the date and time when a note is marked as done:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-log-done 'time)
|
|
#+end_src
|
|
|
|
** Agenda
|
|
Time to try org-mode's agenda feature again I think. Last time I
|
|
didn't end up using it much, but I am /much/ more of an Emacs
|
|
addict now so I do forsee it actually surviving (this will be funny
|
|
to read in the future if not).
|
|
|
|
I want to show all TODOs in =.org= files under my top-level
|
|
=~/Documents/org= directory and any in this config itself. This is
|
|
done by enumerating all files under =~/Documents/org= with
|
|
[[help:directory-files-recursively][directory-files-recursively]], then setting [[help:org-agenda-files][org-agenda-files]] to this,
|
|
along with this config's path.
|
|
|
|
#+begin_src emacs-lisp
|
|
(let ((org-docs
|
|
(directory-files-recursively "~/Documents/org" ".+\.org$")))
|
|
(setq org-agenda-files `("~/.emacs.d/config.org" ,@org-docs)))
|
|
#+end_src
|
|
|
|
Also I find it really very annoying that the the current window
|
|
layout is destroyed when you run =org-agenda=. That behaviour is
|
|
changed by setting [[help:org-agenda-window-setup][org-agenda-window-setup]]:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-window-setup 'current-window)
|
|
#+end_src
|
|
|
|
Include events from my diary:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-include-diary t)
|
|
#+end_src
|
|
|
|
Though I don't like the time grid being on by default.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-use-time-grid nil)
|
|
#+end_src
|
|
|
|
I primarily use the TODO list to keep track of un-scheduled tasks,
|
|
so I don't want those displayed in there:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-todo-ignore-scheduled t)
|
|
#+end_src
|
|
|
|
Finally, I want a keybinding for the weekly agenda and global TODO
|
|
list agenda view:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun org-weekly-agenda-and-todo-list ()
|
|
(interactive)
|
|
(org-agenda nil "n"))
|
|
|
|
(global-set-key (kbd "C-c a") 'org-weekly-agenda-and-todo-list)
|
|
#+end_src
|
|
|
|
** Habits
|
|
Habit tracking requires the habits org module to be loaded. This is
|
|
done by adding the symbol ~'habits~ to [[help:org-modules][org-modules]], if it's not in
|
|
there already. I originally didn't have the surrounding ~unless~,
|
|
but it causes problems when re-loading the config using
|
|
[[help:org-babel-load-file][org-babel-load-file]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(unless (member 'habits org-modules)
|
|
(add-to-list 'org-modules 'org-habit)
|
|
(org-load-modules-maybe t))
|
|
#+end_src
|
|
|
|
The [[help:org-load-modules-maybe][org-load-modules-maybe]] call forces org to load the modules in
|
|
[[help:org-modules][org-modules]]. Not sure it's needed, but I ran into some weird issues
|
|
and I think it fixed them.
|
|
|
|
The consistency graph is very nice but overlaps a lot of the habit
|
|
names, so I want to move it to the right a little:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-habit-graph-column 42)
|
|
#+end_src
|
|
|
|
Also it displays days that you did a habit in red if the habit was
|
|
overdue on that day, which makes a sort of sense, but always
|
|
showing days you did things in green makes more sense to me. The
|
|
variable [[help:org-habit-show-done-always-green][org-habit-show-done-always-green]] controls this.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-habit-show-done-always-green t)
|
|
#+end_src
|
|
|
|
** Identation
|
|
Setting [[help:org-adapt-indentation][org-adapt-indentation]] to ~t~ ensures that Org will indent text
|
|
under a headline:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-adapt-indentation t)
|
|
#+end_src
|
|
|
|
* Language Integrations
|
|
** Generic
|
|
Generally, 8-character-wide tabs are not my thing.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default tab-width 4)
|
|
(setq-default basic-offset 4)
|
|
#+end_src
|
|
|
|
Use tabs as god intended:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default indent-tabs-mode t)
|
|
#+end_src
|
|
|
|
*** Language Server Protocol
|
|
LSP seems to be the way forward in terms of IDE-like features in
|
|
Emacs; grab =lsp-mode= and enable =lsp-deferred=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package lsp-mode
|
|
:init (setq lsp-keymap-prefix "C-c l")
|
|
:commands (lsp lsp-deferred))
|
|
#+end_src
|
|
|
|
=lsp-deferred= means that the LSP server will only be started once
|
|
a buffer is actually opened, which makes more sense to me.
|
|
|
|
Also going to give =lsp-ui= a shot, which displays a bunch of
|
|
information from the language server in the buffer. It looks like
|
|
it could be a bit much but we'll see.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package lsp-ui :commands lsp-ui-mode)
|
|
#+end_src
|
|
|
|
For LSP's most excellent autocompletion to work properly
|
|
[[help:yas-minor-mode][yas-minor-mode]] must be enabled, so hook that into =lsp-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'lsp-mode-hook
|
|
(lambda ()
|
|
(yas-minor-mode)))
|
|
#+end_src
|
|
|
|
To enable Ido integration:
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'lsp-ido)
|
|
#+end_src
|
|
|
|
*** Smart Tabs
|
|
Indent with tabs and align with spaces. Installing the package
|
|
here but it's enabled on a per-language basis in the languages'
|
|
individual config sections.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package smart-tabs-mode)
|
|
#+end_src
|
|
|
|
The package has this really annoying behaviour that it turns
|
|
itself off if [[help:indent-tabs-mode][indent-tabs-mode]] is nil, even when you just
|
|
explicitly turned it on. The solution on the Emacs wiki is to set
|
|
indent-tabs-mode to t in a =c-mode-common= hook, which is a bit of
|
|
a hack, but I tried my own approach and it didn't work for no
|
|
apparent reason so I'm just going to do as I'm told.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'c-mode-common-hook
|
|
(lambda () (setq indent-tabs-mode t)))
|
|
#+end_src
|
|
|
|
** C
|
|
For indenting style, I like BSD-style but with 4-char-wide indents
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'c-mode-hook (lambda ()
|
|
(c-set-style "bsd")
|
|
(setq c-basic-offset 4)))
|
|
#+end_src
|
|
|
|
And we want to enable smart tabs:
|
|
|
|
#+begin_src emacs-lisp
|
|
(smart-tabs-insinuate 'c)
|
|
#+end_src
|
|
|
|
There's a lot of boilerplate in C, so I want YASnippet enabled.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'c-mode-hook (lambda () (yas-minor-mode)))
|
|
#+end_src
|
|
|
|
** C++
|
|
Essentially the same story as for C.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'c++-mode-hook #'lsp-deferred)
|
|
(add-hook 'c++-mode-hook (lambda ()
|
|
(c-set-style "bsd")
|
|
(setq c-basic-offset 4)))
|
|
(smart-tabs-insinuate 'c++)
|
|
#+end_src
|
|
|
|
** Haskell
|
|
My workflow with Haskell is very REPL-based, so I always want
|
|
=interactive-haskell-mode= on.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package haskell-mode)
|
|
(require 'haskell-interactive-mode)
|
|
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)
|
|
#+end_src
|
|
|
|
And, of course, that REPL needs to be taking advantage of parallelism!
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'haskell-process)
|
|
(set-variable 'haskell-process-args-ghci
|
|
'("-threaded" "+RTS" "-N8" "-RTS"))
|
|
#+end_src
|
|
|
|
** Idris
|
|
The only thing to change from the defaults here is to add a more
|
|
convenient way to case-split.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package idris-mode)
|
|
(add-hook 'idris-mode-hook
|
|
(lambda ()
|
|
(define-key idris-mode-map (kbd "C-c SPC")
|
|
'idris-case-split)))
|
|
#+end_src
|
|
|
|
** Rust
|
|
|
|
=rust-mode= provides basic support:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package rust-mode)
|
|
#+end_src
|
|
|
|
Then =rust-analyzer= via LSP does the rest :)
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'rust-mode-hook #'lsp-deferred)
|
|
#+end_src
|
|
|
|
** Lisps
|
|
*** Common Lisp
|
|
Use SLIME and Quicklisp for Common Lisp (SBCL).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package slime)
|
|
(setq inferior-lisp-program "sbcl")
|
|
(global-set-key (kbd "C-c s") 'slime-selector)
|
|
(let ((helper-filename "~/quicklisp/slime-helper.el"))
|
|
(when (file-exists-p helper-filename)
|
|
(load (expand-file-name helper-filename))))
|
|
#+end_src
|
|
|
|
This assumes that SBCL and Quicklisp, along with Quicklisp's SLIME
|
|
helper, are installed. Once SBCL is installed, Quicklisp and its
|
|
SLIME helper can be installed by grabbing [[https://beta.quicklisp.org/quicklisp.lisp][the installer]] and
|
|
loading it with SBCL.
|
|
|
|
#+begin_src shell :tangle no
|
|
curl -O https://beta.quicklisp.org/quicklisp.lisp
|
|
sbcl --load quicklisp.lisp
|
|
#+end_src
|
|
|
|
That will open a REPL with the =quicklisp-quickstart= system
|
|
loaded. At that REPL, run:
|
|
|
|
#+begin_src common-lisp :tangle no
|
|
(quicklisp-quickstart:install)
|
|
(ql:add-to-init-file)
|
|
(ql:quickload "quicklisp-slime-helper")
|
|
#+end_src
|
|
|
|
**** Source blocks
|
|
Enable execution of CL source blocks in Org mode:
|
|
|
|
#+begin_src emacs-lisp
|
|
(enable-org-babel-lang 'lisp)
|
|
#+end_src
|
|
|
|
**** ASDF
|
|
SLIME has a contrib for ASDF integration, =slime-asdf=. This is
|
|
enabled by adding it to [[help:slime-contribs][slime-contribs]]:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'slime-contribs 'slime-asdf)
|
|
#+end_src
|
|
|
|
*** Paredit
|
|
=paredit= is generally very useful for balancing parenthesis so we
|
|
want that turned on for all the lisps. Additionally, it's nice to have
|
|
an entire expression highlighted when the cursor is on one of its
|
|
enclosing parens.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package paredit)
|
|
(setq lispy-mode-hooks
|
|
'(emacs-lisp-mode-hook
|
|
lisp-mode-hook
|
|
racket-mode-hook
|
|
scheme-mode-hook
|
|
slime-repl-mode-hook))
|
|
(dolist (hook lispy-mode-hooks)
|
|
(add-hook hook (lambda ()
|
|
(setq show-paren-style 'expression)
|
|
(paredit-mode))))
|
|
#+end_src
|
|
|
|
** YAML
|
|
I don't really like YAML if I'm honest, but it's used a lot so...
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package yaml-mode)
|
|
#+end_src
|
|
|
|
** Javascript
|
|
The first bit of this setup (=js2-mode=, =js2-refactor= and
|
|
=xref-js2=) is essentially copied from [[https://emacs.cafe/emacs/javascript/setup/2017/04/23/emacs-setup-javascript.html][this Emacs cafe post]].
|
|
|
|
First of all we want to grab =js2-mode= and enable it for
|
|
javascript buffers. It extends the default =js-mode= and builds an
|
|
AST which can be used by other packages.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package js2-mode)
|
|
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
|
|
#+end_src
|
|
|
|
|
|
*** Refactoring
|
|
=js2-refactor= provides refactoring tools based of said AST, so
|
|
enable that and its keybindings:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package js2-refactor)
|
|
(add-hook 'js2-mode-hook #'js2-refactor-mode)
|
|
(js2r-add-keybindings-with-prefix "C-c C-r")
|
|
#+end_src
|
|
|
|
It provides a kill function with nice semantics for Javascript --
|
|
we definitely want that instead of the generic kill.
|
|
|
|
#+begin_src emacs-lisp
|
|
(define-key js2-mode-map (kbd "C-k") #'js2r-kill)
|
|
#+end_src
|
|
|
|
*** Find references / jump to definition
|
|
Then we get to =xref-js2=, which adds stuff for jumping to
|
|
references and definitions (uses the =ag= tool, so that must be
|
|
installed in the environment):
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package xref-js2)
|
|
#+end_src
|
|
|
|
=js-mode= binds =M-.=, which conflicts with =xref-js2= so we need to unbind that:
|
|
|
|
#+begin_src emacs-lisp
|
|
(define-key js-mode-map (kbd "M-.") nil)
|
|
#+end_src
|
|
|
|
And hook it up to =js2-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'js2-mode-hook (lambda ()
|
|
(add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t)))
|
|
#+end_src
|
|
|
|
*** Using local tools from NPM
|
|
To use tools locally by NPM, there is =add-node-modules-path=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package add-node-modules-path)
|
|
(eval-after-load 'js2-mode
|
|
'(add-hook 'js2-mode-hook #'add-node-modules-path))
|
|
#+end_src
|
|
|
|
*** Autoformatting
|
|
[[https://prettier.io/][Prettier]] seems low-effort to set up :D
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package prettier-js)
|
|
(add-hook 'js2-mode-hook 'prettier-js-mode)
|
|
#+end_src
|
|
|
|
** Prolog
|
|
=prolog-mode= comes with Emacs, but .pl files are assumed to be
|
|
Perl (which I never use, it scares me), so we need to change that.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'auto-mode-alist '("\\.pl\\'" . prolog-mode))
|
|
#+end_src
|
|
|
|
** Dockerfiles
|
|
Grab =dockerfile-mode= for syntax highlighting etc in Dockerfiles:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package dockerfile-mode)
|
|
#+end_src
|
|
|
|
** Zig
|
|
=zig-mode= provides basic language integration for Zig:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package zig-mode)
|
|
#+end_src
|
|
|
|
There's a language server implementation for Zig so we'll be using
|
|
that via =lsp-mode= alongside =zig-mode=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'zig-mode-hook #'lsp-deferred)
|
|
#+end_src
|
|
|
|
** GLSL
|
|
Firstly, =glsl-mode= provides basic support:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package glsl-mode)
|
|
#+end_src
|
|
|
|
It's a C-like language, so I want =bsd= code style and
|
|
=smart-tabs=. The former is easy:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'glsl-mode-hook
|
|
(lambda ()
|
|
(c-set-style "bsd")
|
|
(setq c-basic-offset 4)))
|
|
#+end_src
|
|
|
|
Since =smart-tabs= doesn't support GLSL out of the box, we need to
|
|
add support with [[help:smart-tabs-add-language-support][smart-tabs-add-language-support]]. There's an
|
|
example of how to use it on [[https://www.emacswiki.org/emacs/SmartTabs#h5o-5][Emacs Wiki]]:
|
|
|
|
#+begin_src emacs-lisp :tangle no
|
|
(smart-tabs-add-language-support c++ c++-mode-hook
|
|
((c-indent-line . c-basic-offset)
|
|
(c-indent-region . c-basic-offset)))
|
|
#+end_src
|
|
|
|
[[help:c-indent-line][c-indent-line]] et al will do fine for GLSL too since its syntax is
|
|
very similar to C's, so adding support for it looks very similar to
|
|
that example:
|
|
|
|
#+begin_src emacs-lisp
|
|
(smart-tabs-add-language-support glsl glsl-mode-hook
|
|
((c-indent-line . c-basic-offset)
|
|
(c-indent-region . c-basic-offset)))
|
|
#+end_src
|
|
|
|
Now that support is added, [[help:smart-tabs-insinuate][smart-tabs-insinuate]] should do its job:
|
|
|
|
#+begin_src emacs-lisp
|
|
(smart-tabs-insinuate 'glsl)
|
|
#+end_src
|
|
|
|
** Mermaid
|
|
Mermaid is a diagramming language. First of all we need syntax
|
|
highlighting etc. This is provided by =mermaid-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package mermaid-mode)
|
|
#+end_src
|
|
|
|
Also install =ob-mermaid= to add mermaid support to org-babel:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ob-mermaid)
|
|
#+end_src
|
|
|
|
And finally allow execution of mermaid source blocks (used to view the
|
|
diagrams):
|
|
|
|
#+begin_src emacs-lisp
|
|
(enable-org-babel-lang 'mermaid)
|
|
#+end_src
|
|
|
|
** crontab
|
|
Using =crontab-mode=, because it's called crontab-mode lol
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package crontab-mode)
|
|
#+end_src
|
|
|
|
** Python
|
|
Going to use LSP for Python:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'python-mode-hook #'lsp-deferred)
|
|
#+end_src
|
|
|
|
** Ada
|
|
*** Old ada-mode
|
|
Unfortunately, the =ada-mode= on ELPA is hot garbage. It requires
|
|
a custom parser to be built from its sources, and as far as I can
|
|
tell it's completely broken: every version I've tried to build has
|
|
produced multiple compile errors.
|
|
|
|
There was a more basic =ada-mode= built in to Emacs, and
|
|
thankfully someone else has already done the hard work of bundling
|
|
that up -- [[https://tkurtbond.github.io/posts/2022/07/09/using-the-old-version-of-ada-mode-for-emacs/][Using the old version of Ada Mode for Emacs]]. They've
|
|
provided a ZIP file with all the neccessary files bundled into it,
|
|
which can be grabbed with the following:
|
|
|
|
#+begin_src shell
|
|
cd ~/Downloads
|
|
curl -LO https://tkurtbond.github.io/emacs/old-ada-mode.zip
|
|
unzip -d ~/.emacs.d old-ada-mode.zip
|
|
#+end_src
|
|
|
|
The directory =~/.emacs.d/ada-mode= than has to be added to
|
|
[[help:load-path][load-path]] and autoloaded:
|
|
|
|
#+begin_src emacs-lisp
|
|
(let* ((home (getenv "HOME"))
|
|
(path (concat home "/.emacs.d/ada-mode")))
|
|
(add-to-list 'load-path path))
|
|
|
|
(autoload 'ada-mode "ada-mode")
|
|
#+end_src
|
|
|
|
*** LSP
|
|
=lsp-mode= with =ada_language_server= provides all the IDE-esque
|
|
niceties:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'ada-mode-hook #'lsp-deferred)
|
|
#+end_src
|
|
|
|
[[https://github.com/AdaCore/ada_language_server][ada_language_server]] has to be installed manually. There are linux
|
|
builds available on the [[https://github.com/AdaCore/ada_language_server/releases][GitHub releases page]].
|
|
|
|
*** Indentation
|
|
Set the indent width to 4:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq ada-indent 4)
|
|
#+end_src
|
|
|
|
*** GPRBuild files
|
|
GPRBuild files are pretty straightforward, but there doesn't seem
|
|
to be a major mode on ELPA for them so going to make a basic one.
|
|
|
|
**** Custom major mode
|
|
Start out by defining a list of keywords:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar gpr-keywords
|
|
'("case" "end" "external" "for" "is" "null"
|
|
"package" "project" "use" "when" "with"))
|
|
#+end_src
|
|
|
|
And a list of builtins:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar gpr-builtins
|
|
'("Compiler" "Default_Switches" "Exec_Dir" "Library_Dir"
|
|
"Library_Kind" "Library_Name" "Local_Configuration_Pragmas"
|
|
"Main" "Object_Dir" "Source_Dirs"))
|
|
#+end_src
|
|
|
|
Define some font lock regexes:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar gpr-font-lock-defaults
|
|
(let ((string-regex (rx (sequence "\"" (*? (not "\"")) "\"")))
|
|
(constant-regex (rx (or (+ digit) "True" "False"))))
|
|
`((,string-regex . font-lock-string-face)
|
|
(,constant-regex . font-lock-constant-face)
|
|
(,(regexp-opt gpr-builtins 'words) . font-lock-builtin-face)
|
|
(,(regexp-opt gpr-keywords 'words) . font-lock-keyword-face))))
|
|
#+end_src
|
|
|
|
Create a variable for the indent width:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar gpr-indent-width 4)
|
|
#+end_src
|
|
|
|
We then need to define a function for indentation, which is
|
|
non-trivial. A simple set of rules that gets us most of the way is:
|
|
|
|
1. Start at indentation level 0
|
|
2. Decrease indentation level if the line starts with "end"
|
|
3. Indent to same level as a previous "end" line
|
|
4. Increase indentation level if the previous line ends with "is"
|
|
5. Otherwise indent to level 0
|
|
|
|
#+begin_src emacs-lisp
|
|
(defconst gpr-block-start-regex
|
|
(rx (sequence line-start
|
|
(zero-or-more not-newline)
|
|
"is"
|
|
(zero-or-more blank)
|
|
line-end)))
|
|
|
|
(defconst gpr-block-end-regex
|
|
(rx (sequence line-start
|
|
(zero-or-more blank)
|
|
"end")))
|
|
|
|
(defun gpr-indent-line ()
|
|
"Indent the current line as GPRBuild code"
|
|
(interactive)
|
|
(beginning-of-line)
|
|
(indent-line-to (gpr-get-indent-level)))
|
|
|
|
(defun gpr-get-indent-level ()
|
|
(cond ((bobp) 0)
|
|
((looking-at-p gpr-block-end-regex)
|
|
(save-excursion
|
|
(forward-line -1)
|
|
(max (- (current-indentation) gpr-indent-width) 0)))
|
|
(t (gpr-get-indent-level-from-previous))))
|
|
|
|
(defun gpr-get-indent-level-from-previous ()
|
|
(save-excursion
|
|
(let (indent)
|
|
(while (not indent)
|
|
(forward-line -1)
|
|
(setq indent
|
|
(cond ((looking-at-p gpr-block-start-regex)
|
|
(+ (current-indentation) gpr-indent-width))
|
|
((looking-at-p gpr-block-end-regex)
|
|
(current-indentation))
|
|
((bobp) 0))))
|
|
indent)))
|
|
#+end_src
|
|
|
|
Define the mode, inheriting from =prog-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(define-derived-mode gpr-mode prog-mode "GPRBuild"
|
|
"GPR Mode is a major mode for editing GPRBuild files"
|
|
(set (make-local-variable 'comment-start) "--")
|
|
(set (make-local-variable 'comment-end) "")
|
|
|
|
(set (make-local-variable 'font-lock-defaults)
|
|
'(gpr-font-lock-defaults))
|
|
|
|
(set (make-local-variable 'indent-line-function)
|
|
'gpr-indent-line))
|
|
#+end_src
|
|
|
|
Finally, add an [[help:auto-load-alist][auto-load-alist]] entry for =.gpr= files:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'auto-mode-alist '("\\.gpr\\'" . gpr-mode))
|
|
#+end_src
|
|
|
|
** Lua
|
|
Just using basic =lua-mode= package:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package lua-mode)
|
|
#+end_src
|
|
|
|
I want to indent with tabs (set to 4 characters wide):
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq lua-indent-level 4)
|
|
#+end_src
|
|
|
|
I also want to be able to run =lua-format= on files with =C-c f=
|
|
like I have with =clang-format=. The first step for this is to make
|
|
an interactive function to run the formatter; this can be done with
|
|
[[help:call-process-region][call-process-region]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar lua-format-binary "lua-format")
|
|
|
|
(defun lua-format ()
|
|
(interactive)
|
|
(if (executable-find lua-format-binary)
|
|
(let ((start (if (region-active-p) (region-beginning) (point-min)))
|
|
(end (if (region-active-p) (region-end) (point-max))))
|
|
(call-process-region start end lua-format-binary t '(t nil)))
|
|
(error "%s" (concat lua-format-binary " not found."))))
|
|
#+end_src
|
|
|
|
This then needs to be assigned to the keybinding:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook
|
|
'lua-mode-hook
|
|
(lambda () (define-key lua-mode-map (kbd "C-c f") 'lua-format)))
|
|
#+end_src
|
|
|
|
** BASIC
|
|
=basic-mode= provides syntax highlighting and a few nice features:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package basic-mode)
|
|
#+end_src
|
|
|
|
As well as =.bas= files, I want to open all =.bbc= files in
|
|
=basic-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'auto-mode-alist '("\\.bbc\\'" . basic-mode))
|
|
#+end_src
|
|
|
|
** Nix
|
|
Basic editing support comes from =nix-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package nix-mode)
|
|
#+end_src
|
|
|
|
And =nix-update= provides a convenient way to update ~fetch~
|
|
blocks:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package nix-update)
|
|
#+end_src
|
|
|
|
** SCAD
|
|
There is a language server for OpenSCAD, but I think I'll just
|
|
stick to the basic mode:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package scad-mode)
|
|
#+end_src
|
|
|
|
** Go
|
|
First of all, of course, install =go-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package go-mode)
|
|
#+end_src
|
|
|
|
This package provides a convenient lil function to use gofmt to
|
|
format a buffer; I want to run this whenever I save a go source
|
|
file. This is pretty easily done by adding a =before-save-hook= in
|
|
a =go-mode-hook= (hey, I heard you like hooks...)
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'go-mode-hook
|
|
(lambda ()
|
|
(add-hook 'before-save-hook 'gofmt-before-save)))
|
|
#+end_src
|
|
|
|
* Tool Integrations
|
|
** Git
|
|
=magit= is truly a wonderful creation! Add keybinding for
|
|
=magit-status= and a maximum length for the summary line of commit
|
|
messages (after which the excess is highlighted).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package magit
|
|
:bind
|
|
("C-x g" . magit-status)
|
|
:config
|
|
(setq git-commit-summary-max-length 72))
|
|
#+end_src
|
|
|
|
By default, =magit-status= will open itself in a different window
|
|
to the one you open it in. I really don't understand how this is
|
|
useful but thankfully this is Emacs so the behaviour can be
|
|
tweaked. The default behaviour does make sense for other magit
|
|
windows, just not magit-status.
|
|
|
|
The behviour I want can be achieved by setting
|
|
[[help:magit-display-buffer-function][magit-display-buffer-function]] to something which will open the
|
|
buffer in the current window if and only if it's a
|
|
=magit-status-mode= window.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq magit-display-buffer-function
|
|
(lambda (buffer)
|
|
(display-buffer
|
|
buffer
|
|
(when (eq (with-current-buffer buffer major-mode)
|
|
'magit-status-mode)
|
|
'(display-buffer-same-window)))))
|
|
#+end_src
|
|
|
|
Now I'm thinking it I could customise this further as I often am
|
|
annoyed by diffs opening in a different window but I think I'll
|
|
leave it at that for now as I'm not sure precisely what behaviour
|
|
I'd want.
|
|
|
|
** Docker
|
|
I use docker quite a lot, unfortunately, so it's nice to be able to
|
|
spawn containers etc from Emacs. The =docker= package provides a
|
|
few nice bits and bobs.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package docker
|
|
:bind ("C-c d" . docker))
|
|
#+end_src
|
|
|
|
** Build systems
|
|
*** CMake
|
|
I hate it, but it's everywhere. =cmake-mode= provides basic syntax
|
|
highlighting etc.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package cmake-mode)
|
|
#+end_src
|
|
|
|
*** Meson
|
|
Use =meson-mode= for syntax highlighting etc in meson.build files.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package meson-mode)
|
|
#+end_src
|
|
|
|
*** Cargo
|
|
I never really use Rust without Cargo, so always turn on the minor
|
|
mode for Cargo in Rust buffers.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package cargo)
|
|
(add-hook 'rust-mode-hook 'cargo-minor-mode)
|
|
#+end_src
|
|
|
|
*** BitBake
|
|
I hate Yocto and its tools but alas, I must use it for work :(
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package bitbake)
|
|
#+end_src
|
|
|
|
** clang-format
|
|
Most of the time, =lsp-mode= is fine for formatting, but sometimes
|
|
it doesn't work (mostly just because I haven't gone through the
|
|
effort to set it up) but I still want to be able to auto-format
|
|
code easily (that is to say, with a convenient keybinding). The
|
|
=clang-format= package provides Elisp functions for invoking it.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package clang-format)
|
|
#+end_src
|
|
|
|
I want a keybinding that formats the region if its active, or the
|
|
whole buffer otherwise. It seems that there's no function which
|
|
does that out of the box, so that has to be defined first:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun clang-format-region-or-buffer ()
|
|
"Format the region if it's active, otherwise format the entire buffer."
|
|
(interactive)
|
|
(if (use-region-p)
|
|
(clang-format-region (region-beginning) (region-end))
|
|
(clang-format-buffer)))
|
|
#+end_src
|
|
|
|
With that defined, the keybinding can be added to C mode.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook
|
|
'c-mode-hook
|
|
(lambda ()
|
|
(define-key c-mode-map (kbd "C-c f")
|
|
'clang-format-region-or-buffer)))
|
|
#+end_src
|
|
|
|
** GraphViz
|
|
The =graphviz-dot-mode= package provides some integration for the
|
|
GraphViz suite of tools, and a mode for the 'dot' language they
|
|
use:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package graphviz-dot-mode)
|
|
#+end_src
|
|
|
|
** Man pages
|
|
Man page support is built in to Emacs but it's one of those
|
|
annoying things where it will open in the "other" window instead of
|
|
where you ran =M-x man= from. Thankfully, this behaviour can be
|
|
changed by setting [[help:Man-notify-method][Man-notify-method]]. The value ~'pushy~ makes the
|
|
man page open in the current window.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq Man-notify-method 'pushy)
|
|
#+end_src
|
|
|
|
* Backup and Autosave
|
|
** Keep $PWD Tidy
|
|
Emacs' default behaviour of dumping temporary files in the current
|
|
directory is quite a pain, so we want to get it to instead stick
|
|
them in a dedicated directory somewhere far away.
|
|
|
|
We can do that for auto-save files by setting the variable
|
|
[[help:auto-save-file-name-transforms][auto-save-file-name-transforms]]:
|
|
|
|
#+begin_src emacs-lisp
|
|
(make-directory "~/.emacs-tmp/auto-save" t)
|
|
(setq auto-save-file-name-transforms '((".*" "~/.emacs-tmp/auto-save" t)))
|
|
#+end_src
|
|
|
|
And for backup files there's [[help:backup-directory-alist][backup-directory-alist]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(make-directory "~/.emacs-tmp/backup" t)
|
|
(setq backup-directory-alist '(("." . "~/.emacs-tmp/backup")))
|
|
#+end_src
|
|
|
|
** Backup by Copying
|
|
By default Emacs moves a file to the backup location and then
|
|
creates a copy in the original location, which apart from being a
|
|
very strange thing to do also messes up hard links. Setting
|
|
[[help:backup-by-copying][backup-by-copying]] changes it to the more obvious behaviour of
|
|
simply copying the file to the backup location.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq backup-by-copying t)
|
|
#+end_src
|
|
|
|
** Use trash
|
|
Commands like [[help:delete-file][delete-file]] and [[help:delete-directory][delete-directory]], as well as deletion
|
|
commands in Dired can be made to move things to trash, instead of
|
|
permanently deleting them. This done by setting the
|
|
[[help:delete-by-moving-to-trash][delete-by-moving-to-trash]] variable to ~t~.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq delete-by-moving-to-trash t)
|
|
#+end_src
|
|
|
|
* Remote Access
|
|
** Sudo/doas on Remote Hosts
|
|
To edit files as root on remote hosts with sudo or doas while also
|
|
tunneling over SSH, you need to configure TRAMP to use SSH as a
|
|
proxy. This is done by adding to [[help:tramp-default-proxies-alist][tramp-default-proxies-alist]], as
|
|
detailed in [[info:tramp#Multi-hops][the TRAMP manual]]:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'tramp-default-proxies-alist
|
|
'(nil "\\`root\\'" "/ssh:%h:"))
|
|
(add-to-list 'tramp-default-proxies-alist
|
|
'((regexp-quote (system-name)) nil nil))
|
|
#+end_src
|
|
|
|
* Printing
|
|
#+begin_src emacs-lisp
|
|
(setq ps-paper-type 'a4
|
|
ps-font-size 10
|
|
ps-print-header nil)
|
|
#+end_src
|
|
|
|
* Misc
|
|
** God mode
|
|
God mode essentially makes Emacs a bit more VI-like by introducing
|
|
a mode where modifier keys are implicitly held down, thereby
|
|
reducing the amount of 'emacs claw' required for most commands. It
|
|
runs as a global minor mode.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package god-mode)
|
|
(god-mode)
|
|
#+end_src
|
|
|
|
In order to enter normal mode, [[help:god-mode-all][god-mode-all]] must be ran, so we'll
|
|
bind =C-.= to that:
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-.") #'god-mode-all)
|
|
#+end_src
|
|
|
|
I find it jarring and confusing to have it on and off in different
|
|
types of buffers, so will just disable the exemptions:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq god-exempt-major-modes nil)
|
|
(setq god-exempt-predicates nil)
|
|
#+end_src
|
|
|
|
* Fin
|
|
Now that start-up is finished, [[help:gc-cons-threshold][gc-cons-threshold]] and
|
|
[[help:gc-cons-percentage][gc-cons-percentage]] need to be set back to reasonable values to avoid
|
|
memory usage getting too high.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq gc-cons-threshold 1000000)
|
|
(setq gc-cons-percentage 0.2)
|
|
#+end_src
|