|
|
|
@@ -2,6 +2,17 @@
|
|
|
|
#+AUTHOR: Camden Dixie O'Brien
|
|
|
|
#+AUTHOR: Camden Dixie O'Brien
|
|
|
|
#+ATTR_LATEX: :float t
|
|
|
|
#+ATTR_LATEX: :float t
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* Prelude
|
|
|
|
|
|
|
|
Before doing anything else, set a higher [[help:gc-cons-threshold][gc-cons-threshold]] (I'm
|
|
|
|
|
|
|
|
using 100 MiB here) so that we're not garbage collecting during
|
|
|
|
|
|
|
|
initialisation to make startup a little faster. We'll save the
|
|
|
|
|
|
|
|
original value to restore it at the end of initialisation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq original-gc-cons-threshold gc-cons-threshold)
|
|
|
|
|
|
|
|
(setq gc-cons-threshold (* 100 1024 1024))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
* Customize
|
|
|
|
* Customize
|
|
|
|
Hey, customize, leave my ~/.emacs.d/init.el alone!
|
|
|
|
Hey, customize, leave my ~/.emacs.d/init.el alone!
|
|
|
|
|
|
|
|
|
|
|
|
@@ -117,16 +128,17 @@
|
|
|
|
height based off the hostname.
|
|
|
|
height based off the hostname.
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(let ((font-height
|
|
|
|
(let ((font-height
|
|
|
|
(pcase hostname
|
|
|
|
(pcase hostname
|
|
|
|
("zora" 100)
|
|
|
|
("zora" 100)
|
|
|
|
("eddie" 100)
|
|
|
|
("eddie" 100)
|
|
|
|
("mandarax" 115)
|
|
|
|
("mandarax" 115)
|
|
|
|
("valis" 80)
|
|
|
|
("valis" 80)
|
|
|
|
(_ 110))))
|
|
|
|
("wipc23120423" 100)
|
|
|
|
(set-face-attribute 'default nil
|
|
|
|
(_ 110))))
|
|
|
|
:family "Courier 10 Pitch"
|
|
|
|
(set-face-attribute 'default nil
|
|
|
|
:height font-height))
|
|
|
|
:family "Courier 10 Pitch"
|
|
|
|
|
|
|
|
:height font-height))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
I also like a little more line spacing than default, again makes
|
|
|
|
I also like a little more line spacing than default, again makes
|
|
|
|
@@ -221,139 +233,35 @@
|
|
|
|
(yas-reload-all)
|
|
|
|
(yas-reload-all)
|
|
|
|
#+end_src
|
|
|
|
#+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
|
|
|
|
* Org
|
|
|
|
I use a couple non-standard bits and pieces, but not a whole
|
|
|
|
** Code and Quote block shortcuts
|
|
|
|
bunch. I really like the =<s= to insert a source block thing (which
|
|
|
|
I am a big fan of using =<s= for source blocks and =<q= for quotes;
|
|
|
|
was deprecated); =org-tempo= brings that back.
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defvar journal-directory
|
|
|
|
(use-package org :config (require 'org-tempo))
|
|
|
|
"~/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
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
I can't be bothered to handle [[help:journal-directory][journal-directory]] not existing in the
|
|
|
|
However, I have recently discovered, much to my despair, that these
|
|
|
|
main code, so I'm just going to make sure it exists here. The
|
|
|
|
shortcuts do not work if there are tabs in the line ahead of them!
|
|
|
|
second argument to [[help:make-directory][make-directory]] specifies to create parent
|
|
|
|
Quite ridiculous. Easily worked around, however; I am going to
|
|
|
|
directories too if necessary.
|
|
|
|
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
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(unless (file-directory-p journal-directory)
|
|
|
|
(add-hook 'org-mode-hook (lambda () (setq indent-tabs-mode nil)))
|
|
|
|
(make-directory journal-directory t))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
The next step is to define a function to determine the filename for
|
|
|
|
** Keybindings
|
|
|
|
today's journal entry. This is fairly straightforward, using
|
|
|
|
A keybinding to add a new heading is super useful
|
|
|
|
[[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
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defun journal-entry-filename ()
|
|
|
|
(add-hook 'org-mode-hook
|
|
|
|
"Returns the filename for today's journal entry."
|
|
|
|
(lambda ()
|
|
|
|
(let ((date-string (format-time-string journal-filename-format)))
|
|
|
|
(define-key org-mode-map
|
|
|
|
(concat journal-directory "/" date-string ".org")))
|
|
|
|
(kbd "<C-M-return>")
|
|
|
|
#+end_src
|
|
|
|
'org-insert-heading-after-current)))
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** Source Blocks
|
|
|
|
** Source Blocks
|
|
|
|
@@ -483,116 +391,7 @@
|
|
|
|
(auto-mode . emacs)))
|
|
|
|
(auto-mode . emacs)))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** Workflow States
|
|
|
|
** Indentation
|
|
|
|
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
|
|
|
|
Setting [[help:org-adapt-indentation][org-adapt-indentation]] to ~t~ ensures that Org will indent text
|
|
|
|
under a headline:
|
|
|
|
under a headline:
|
|
|
|
|
|
|
|
|
|
|
|
@@ -690,7 +489,7 @@
|
|
|
|
There's a lot of boilerplate in C, so I want YASnippet enabled.
|
|
|
|
There's a lot of boilerplate in C, so I want YASnippet enabled.
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook 'c-mode-hook (lambda () (yas-minor-mode)))
|
|
|
|
(add-hook 'c-mode-hook (lambda () (yas-minor-mode)))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** C++
|
|
|
|
** C++
|
|
|
|
@@ -815,25 +614,6 @@
|
|
|
|
(paredit-mode))))
|
|
|
|
(paredit-mode))))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
*** Scheme and Racket
|
|
|
|
|
|
|
|
[[https://www.nongnu.org/geiser/][Geiser]] is a pretty complete collection of Scheme things for
|
|
|
|
|
|
|
|
Emacs. Only change from the defaults is to open the REPL in the
|
|
|
|
|
|
|
|
current window instead of creating a new one.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(use-package geiser
|
|
|
|
|
|
|
|
:config
|
|
|
|
|
|
|
|
(setq geiser-repl-use-other-window nil))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I commonly use Chez, Guile and Racket so we want the packages for those:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(use-package geiser-chez)
|
|
|
|
|
|
|
|
(use-package geiser-guile)
|
|
|
|
|
|
|
|
(use-package geiser-racket)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
** YAML
|
|
|
|
** YAML
|
|
|
|
I don't really like YAML if I'm honest, but it's used a lot so...
|
|
|
|
I don't really like YAML if I'm honest, but it's used a lot so...
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1187,7 +967,7 @@
|
|
|
|
I want to indent with tabs (set to 4 characters wide):
|
|
|
|
I want to indent with tabs (set to 4 characters wide):
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq lua-indent-level 4)
|
|
|
|
(setq lua-indent-level 4)
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
I also want to be able to run =lua-format= on files with =C-c f=
|
|
|
|
I also want to be able to run =lua-format= on files with =C-c f=
|
|
|
|
@@ -1196,51 +976,77 @@
|
|
|
|
[[help:call-process-region][call-process-region]].
|
|
|
|
[[help:call-process-region][call-process-region]].
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(defvar lua-format-binary "lua-format")
|
|
|
|
(defvar lua-format-binary "lua-format")
|
|
|
|
|
|
|
|
|
|
|
|
(defun lua-format ()
|
|
|
|
(defun lua-format ()
|
|
|
|
(interactive)
|
|
|
|
(interactive)
|
|
|
|
(if (executable-find lua-format-binary)
|
|
|
|
(if (executable-find lua-format-binary)
|
|
|
|
(let ((start (if (region-active-p) (region-beginning) (point-min)))
|
|
|
|
(let ((start (if (region-active-p) (region-beginning) (point-min)))
|
|
|
|
(end (if (region-active-p) (region-end) (point-max))))
|
|
|
|
(end (if (region-active-p) (region-end) (point-max))))
|
|
|
|
(call-process-region start end lua-format-binary t '(t nil)))
|
|
|
|
(call-process-region start end lua-format-binary t '(t nil)))
|
|
|
|
(error "%s" (concat lua-format-binary " not found."))))
|
|
|
|
(error "%s" (concat lua-format-binary " not found."))))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
This then needs to be assigned to the keybinding:
|
|
|
|
This then needs to be assigned to the keybinding:
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-hook
|
|
|
|
(add-hook
|
|
|
|
'lua-mode-hook
|
|
|
|
'lua-mode-hook
|
|
|
|
(lambda () (define-key lua-mode-map (kbd "C-c f") 'lua-format)))
|
|
|
|
(lambda () (define-key lua-mode-map (kbd "C-c f") 'lua-format)))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** BASIC
|
|
|
|
** BASIC
|
|
|
|
=basic-mode= provides syntax highlighting and a few nice features:
|
|
|
|
=basic-mode= provides syntax highlighting and a few nice features:
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(use-package basic-mode)
|
|
|
|
(use-package basic-mode)
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
As well as =.bas= files, I want to open all =.bbc= files in
|
|
|
|
As well as =.bas= files, I want to open all =.bbc= files in
|
|
|
|
=basic-mode=:
|
|
|
|
=basic-mode=:
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(add-to-list 'auto-mode-alist '("\\.bbc\\'" . basic-mode))
|
|
|
|
(add-to-list 'auto-mode-alist '("\\.bbc\\'" . basic-mode))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** Nix
|
|
|
|
** Nix
|
|
|
|
Basic editing support comes from =nix-mode=:
|
|
|
|
Basic editing support comes from =nix-mode=:
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(use-package nix-mode)
|
|
|
|
(use-package nix-mode)
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
And =nix-update= provides a convenient way to update ~fetch~
|
|
|
|
And =nix-update= provides a convenient way to update ~fetch~
|
|
|
|
blocks:
|
|
|
|
blocks:
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(use-package nix-update)
|
|
|
|
(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
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
* Tool Integrations
|
|
|
|
* Tool Integrations
|
|
|
|
@@ -1318,6 +1124,13 @@
|
|
|
|
(add-hook 'rust-mode-hook 'cargo-minor-mode)
|
|
|
|
(add-hook 'rust-mode-hook 'cargo-minor-mode)
|
|
|
|
#+end_src
|
|
|
|
#+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
|
|
|
|
** clang-format
|
|
|
|
Most of the time, =lsp-mode= is fine for formatting, but sometimes
|
|
|
|
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
|
|
|
|
it doesn't work (mostly just because I haven't gone through the
|
|
|
|
@@ -1352,25 +1165,6 @@
|
|
|
|
'clang-format-region-or-buffer)))
|
|
|
|
'clang-format-region-or-buffer)))
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** ChatGPT
|
|
|
|
|
|
|
|
The =chatpt-shell= package provides a shell-like interface for
|
|
|
|
|
|
|
|
ChatGPT.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(use-package chatgpt-shell)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[help:chatgpt-shell-openai-key][chatgpt-shell-openai-key]] must also be set to a function that
|
|
|
|
|
|
|
|
returns an OpenAI API key. I have a key (made on [[https://platform.openai.com/account/api-keys][this page]]) stored
|
|
|
|
|
|
|
|
in =pass(1)= under =openai/api-key=, so this can be retrieved via
|
|
|
|
|
|
|
|
[[help:process-lines][process-lines]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq chatgpt-shell-openai-key
|
|
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
|
|
(car (process-lines "pass" "openai/api-key"))))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
** GraphViz
|
|
|
|
** GraphViz
|
|
|
|
The =graphviz-dot-mode= package provides some integration for the
|
|
|
|
The =graphviz-dot-mode= package provides some integration for the
|
|
|
|
GraphViz suite of tools, and a mode for the 'dot' language they
|
|
|
|
GraphViz suite of tools, and a mode for the 'dot' language they
|
|
|
|
@@ -1380,6 +1174,197 @@
|
|
|
|
(use-package graphviz-dot-mode)
|
|
|
|
(use-package graphviz-dot-mode)
|
|
|
|
#+end_src
|
|
|
|
#+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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
** Script-Fu
|
|
|
|
|
|
|
|
GIMP has a scheme-based language -- Script-Fu -- built into it that
|
|
|
|
|
|
|
|
you can use to script things (based). Sadly, the built-in console
|
|
|
|
|
|
|
|
is rather lackluster as a coding environment. Happily, there /is/
|
|
|
|
|
|
|
|
an option to run a server which listens for Script-Fu commands on a
|
|
|
|
|
|
|
|
TCP port, so I can use =comint= to make my own lil interface in
|
|
|
|
|
|
|
|
Emacs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It's things like this that make me really glad I switched to Emacs
|
|
|
|
|
|
|
|
because this is ridiculously cool. By my definition of "cool"
|
|
|
|
|
|
|
|
anyway (what can I say, I'm a massive nerd).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I should probably extract this and make a standalone package out of
|
|
|
|
|
|
|
|
it and stick it on Melpa at some point.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*** REPL
|
|
|
|
|
|
|
|
The Script-Fu server request format is very simple:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Bytes | Description |
|
|
|
|
|
|
|
|
|-------+-----------------------------------------------|
|
|
|
|
|
|
|
|
| 0 | 'G' magic byte (47h) |
|
|
|
|
|
|
|
|
| 1-2 | Length of expression (BE 32-bit unsigned int) |
|
|
|
|
|
|
|
|
| 3+ | Expression |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Writing an encoder for this is pretty trivial:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun script-fu-repl-encode-request (input)
|
|
|
|
|
|
|
|
(let* ((len (length input))
|
|
|
|
|
|
|
|
(hi (logand (lsh len -8) #xff))
|
|
|
|
|
|
|
|
(lo (logand len #xff))
|
|
|
|
|
|
|
|
(hdr (vector ?G hi lo)))
|
|
|
|
|
|
|
|
(vconcat hdr (encode-coding-string input 'utf-8))))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We then want a sender function to use with [[help:comint-mode][comint-mode]] that
|
|
|
|
|
|
|
|
applies this encoding. Unfortunately, it seems that there is no
|
|
|
|
|
|
|
|
=comint-send-bytes= or similar function to directly send a byte
|
|
|
|
|
|
|
|
vector to the comint process. I did try just sending the request
|
|
|
|
|
|
|
|
as a string with some invalid characters at the start but ran into
|
|
|
|
|
|
|
|
issues: Emacs would sometimes insert unicode control characters
|
|
|
|
|
|
|
|
into the data, which GIMP understandably didn't appreciate.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The method I ended up with is to create a temporary, unibyte
|
|
|
|
|
|
|
|
buffer, stick the data in there and then use [[help:comint-send-region][comint-send-region]] to
|
|
|
|
|
|
|
|
send the data. It's a bit of a kludge but it seems like it should
|
|
|
|
|
|
|
|
be reasonably robust.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun script-fu-repl-comint-send-bytes (proc bytes)
|
|
|
|
|
|
|
|
(let ((temp-buffer (generate-new-buffer "*script-fu-repl-tmp*")))
|
|
|
|
|
|
|
|
(unwind-protect
|
|
|
|
|
|
|
|
(with-current-buffer temp-buffer
|
|
|
|
|
|
|
|
(set-buffer-multibyte nil)
|
|
|
|
|
|
|
|
(insert (apply #'string (append bytes nil)))
|
|
|
|
|
|
|
|
(comint-send-region proc (point-min) (point-max)))
|
|
|
|
|
|
|
|
(kill-buffer temp-buffer))))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
With that handled, implementing the sender function itself is nice
|
|
|
|
|
|
|
|
and easy:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun script-fu-repl-send (proc input)
|
|
|
|
|
|
|
|
(let ((request (script-fu-repl-encode-request input)))
|
|
|
|
|
|
|
|
(script-fu-repl-comint-send-bytes proc request)))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The response format is similarly simple:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Bytes | Description |
|
|
|
|
|
|
|
|
|-------+-----------------------------------------|
|
|
|
|
|
|
|
|
| 0 | 'G' magic byte (47h) |
|
|
|
|
|
|
|
|
| 1 | Status code -- 0 on success, 1 on error |
|
|
|
|
|
|
|
|
| 2-3 | Length of response text |
|
|
|
|
|
|
|
|
| 4 | Response text |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For now, we only care about the response text, so all we need to do
|
|
|
|
|
|
|
|
is skip the first 4 bytes and add a trailing newline.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun script-fu-repl-decode-response (response)
|
|
|
|
|
|
|
|
(concat (substring response 4) "\n"))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Another thing is adding a prompt to the comint buffer -- the
|
|
|
|
|
|
|
|
server doesn't send one, so we have to add it ourselves.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defvar script-fu-repl-prompt "> ")
|
|
|
|
|
|
|
|
(defun script-fu-repl-insert-prompt (output)
|
|
|
|
|
|
|
|
(unless (string-blank-p output)
|
|
|
|
|
|
|
|
(let ((proc (get-buffer-process (current-buffer))))
|
|
|
|
|
|
|
|
(goto-char (process-mark proc))
|
|
|
|
|
|
|
|
(unless (looking-back script-fu-repl-prompt)
|
|
|
|
|
|
|
|
(insert script-fu-repl-prompt)
|
|
|
|
|
|
|
|
(set-marker (process-mark proc) (point)))))
|
|
|
|
|
|
|
|
output)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A mode for the client buffer can then be derived from [[help:comint-mode][comint-mode]].
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(define-derived-mode script-fu-repl-mode comint-mode "Script-Fu REPL"
|
|
|
|
|
|
|
|
(setq-local comint-prompt-read-only t)
|
|
|
|
|
|
|
|
(setq-local comint-prompt-regexp nil)
|
|
|
|
|
|
|
|
(setq-local comint-input-sender #'script-fu-repl-send)
|
|
|
|
|
|
|
|
(add-hook 'comint-preoutput-filter-functions
|
|
|
|
|
|
|
|
'script-fu-repl-decode-response nil t)
|
|
|
|
|
|
|
|
(add-hook 'comint-output-filter-functions
|
|
|
|
|
|
|
|
'script-fu-repl-insert-prompt nil t))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Now, to create a function to create or get the current REPL
|
|
|
|
|
|
|
|
buffer. The [[help:comint-check-proc][comint-check-proc]] function can be used to test
|
|
|
|
|
|
|
|
whether the buffer is already set up. Rather nicely,
|
|
|
|
|
|
|
|
[[help:make-comint-in-buffer][make-comint-in-buffer]] supports passing a ~(HOST . SERVICE)~ pair
|
|
|
|
|
|
|
|
to specify a TCP connection to open (via [[help:open-network-stream][open-network-stream]]) so
|
|
|
|
|
|
|
|
this is pretty simple. In both cases, we want to return the
|
|
|
|
|
|
|
|
client buffer for the caller to use.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defvar script-fu-repl-server '("localhost" . 10008))
|
|
|
|
|
|
|
|
(defun script-fu-repl ()
|
|
|
|
|
|
|
|
(interactive)
|
|
|
|
|
|
|
|
(let ((buffer (get-buffer-create "*Script-Fu REPL*")))
|
|
|
|
|
|
|
|
(when (not (comint-check-proc buffer))
|
|
|
|
|
|
|
|
(make-comint-in-buffer "Script-Fu REPL"
|
|
|
|
|
|
|
|
buffer script-fu-repl-server)
|
|
|
|
|
|
|
|
(with-current-buffer buffer (script-fu-repl-mode)))
|
|
|
|
|
|
|
|
(pop-to-buffer buffer '((display-buffer-in-direction)
|
|
|
|
|
|
|
|
(direction . below)
|
|
|
|
|
|
|
|
(window-height . 0.3)))
|
|
|
|
|
|
|
|
buffer))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*** Code Editing
|
|
|
|
|
|
|
|
With the client stuff done, we can define the code editing mode:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(define-derived-mode script-fu-mode scheme-mode "Script-Fu")
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Now to define something to send an expression or region to the
|
|
|
|
|
|
|
|
REPL. Since =script-fu-repl= returns the buffer we can use that
|
|
|
|
|
|
|
|
to transparently start a REPL or get the existing one if one's
|
|
|
|
|
|
|
|
already running.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun script-fu-mode-send-region-or-sexp ()
|
|
|
|
|
|
|
|
(interactive)
|
|
|
|
|
|
|
|
(let ((code (if (use-region-p)
|
|
|
|
|
|
|
|
(let ((start (region-beginning))
|
|
|
|
|
|
|
|
(end (region-end)))
|
|
|
|
|
|
|
|
(buffer-substring-no-properties start end))
|
|
|
|
|
|
|
|
(thing-at-point 'sexp t))))
|
|
|
|
|
|
|
|
(if (not code) (message "No code to send.")
|
|
|
|
|
|
|
|
(let* ((repl-proc (get-buffer-process (script-fu-repl))))
|
|
|
|
|
|
|
|
(script-fu-repl-send repl-proc code)))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(define-key script-fu-mode-map (kbd "C-c C-c")
|
|
|
|
|
|
|
|
'script-fu-mode-send-region-or-sexp)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
And finally, a similar thing for the whole file:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun script-fu-mode-send-file ()
|
|
|
|
|
|
|
|
(interactive)
|
|
|
|
|
|
|
|
(let* ((repl-proc (get-buffer-process (script-fu-repl)))
|
|
|
|
|
|
|
|
(buffer-contents
|
|
|
|
|
|
|
|
(buffer-substring-no-properties (point-min)
|
|
|
|
|
|
|
|
(point-max))))
|
|
|
|
|
|
|
|
(script-fu-repl-send repl-proc buffer-contents)))
|
|
|
|
|
|
|
|
(define-key script-fu-mode-map (kbd "C-c C-l")
|
|
|
|
|
|
|
|
'script-fu-mode-send-file)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I think that's all I need for now!
|
|
|
|
|
|
|
|
|
|
|
|
* Backup and Autosave
|
|
|
|
* Backup and Autosave
|
|
|
|
** Keep $PWD Tidy
|
|
|
|
** Keep $PWD Tidy
|
|
|
|
Emacs' default behaviour of dumping temporary files in the current
|
|
|
|
Emacs' default behaviour of dumping temporary files in the current
|
|
|
|
@@ -1422,83 +1407,6 @@
|
|
|
|
(setq delete-by-moving-to-trash t)
|
|
|
|
(setq delete-by-moving-to-trash t)
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
* Mail
|
|
|
|
|
|
|
|
Using Mu4e. It requires =mu(1)= tool be set up already. The mu4e
|
|
|
|
|
|
|
|
emacs package is bundled with the system package rather than
|
|
|
|
|
|
|
|
distributed seperately, so if it's been put somewhere on the
|
|
|
|
|
|
|
|
[[help:load-path][load-path]] we'd just need to ~require~ it — this is the case on the
|
|
|
|
|
|
|
|
linux distros that I've used. On OpenBSD, though, packages are
|
|
|
|
|
|
|
|
installed into =/usr/local/= and =/usr/local/share/emacs/site-lisp=
|
|
|
|
|
|
|
|
is not in =load-path=, so we need to add it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(when (string-match-p "OpenBSD" (shell-command-to-string "uname -a"))
|
|
|
|
|
|
|
|
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp"))
|
|
|
|
|
|
|
|
(require 'mu4e)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
On Bedrock linux, Mu4e can't the =mu= binary as it's not in a
|
|
|
|
|
|
|
|
standard location. The location can be specified explicitly by
|
|
|
|
|
|
|
|
setting [[help:mu4e-mu-binary][mu4e-mu-binary]]. To check whether we're on Bedrock, we'll see
|
|
|
|
|
|
|
|
if the =brl= program is available, as =uname= will just yield
|
|
|
|
|
|
|
|
whichever distro bedrock booted from.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(when (< 0 (length (shell-command-to-string "which brl")))
|
|
|
|
|
|
|
|
(setq mu4e-mu-binary "/bedrock/cross/bin/mu"))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The folder archived mail gets saved into is determined by
|
|
|
|
|
|
|
|
[[help:mu4e-refile-folder][mu4e-refile-folder]]. I prefer to have archived mail stored on the
|
|
|
|
|
|
|
|
remote since then it's accessible from every machine. The default is
|
|
|
|
|
|
|
|
=/archive=, though, which is outside of any remotes. Easiest thing to
|
|
|
|
|
|
|
|
do is hardcode =/wip/archive= as the refile folder.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq mu4e-refile-folder "/wip/archive")
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This only really works because I only have one email account I use
|
|
|
|
|
|
|
|
with Mu4e. It would archive mail from /all/ accounts onto the wip.sh
|
|
|
|
|
|
|
|
mail server.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The final general Mu4e thing is stopping it from prompting on exit:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq mu4e-confirm-quit nil)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
** Fetching
|
|
|
|
|
|
|
|
Use =offlineimap(1)= (also needs to be set up seperately) to sync the
|
|
|
|
|
|
|
|
maildir with the remote server:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq mu4e-get-mail-command "offlineimap")
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
** Sending
|
|
|
|
|
|
|
|
To send mail we first need to set the mail address:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq user-mail-address "cdo@wip.sh")
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Then set the sent and drafts folders to inside the remote folder:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq mu4e-sent-folder "/wip/sent"
|
|
|
|
|
|
|
|
mu4e-drafts-folder "/wip/drafts")
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
And finally configure SMTP:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(setq smtpmail-smtp-server "mail.wip.sh"
|
|
|
|
|
|
|
|
smtpmail-smtp-service 587
|
|
|
|
|
|
|
|
smtpmail-stream-type 'starttls
|
|
|
|
|
|
|
|
send-mail-function 'smtpmail-send-it)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* Remote Access
|
|
|
|
* Remote Access
|
|
|
|
** Sudo/doas on Remote Hosts
|
|
|
|
** Sudo/doas on Remote Hosts
|
|
|
|
To edit files as root on remote hosts with sudo or doas while also
|
|
|
|
To edit files as root on remote hosts with sudo or doas while also
|
|
|
|
@@ -1547,104 +1455,10 @@
|
|
|
|
(setq god-exempt-predicates nil)
|
|
|
|
(setq god-exempt-predicates nil)
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
** Case-flipping
|
|
|
|
* Coda
|
|
|
|
Want to be able to toggle the 'shiftedness' of a selected region,
|
|
|
|
Now that initialization is finished, [[help:gc-cons-threshold][gc-cons-threshold]] should be set
|
|
|
|
that is, map uppercase to lowercase and vice versa, but also map
|
|
|
|
back to its default value:
|
|
|
|
things like '1' to '!' and '[' to '{'.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I doubt there's anything in Emacs already that has that mapping for
|
|
|
|
|
|
|
|
non-alphabetic characters, so first thing to do is define that:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defvar non-letter-case-mapping
|
|
|
|
|
|
|
|
'((?1 . ?!) (?2 . ?\") (?3 . ?£) (?4 . ?$) (?5 . ?%)
|
|
|
|
|
|
|
|
(?6 . ?^) (?7 . ?&) (?8 . ?*) (?9 . ?\() (?0 . ?\))
|
|
|
|
|
|
|
|
(?- . ?_) (?= . ?+) (?\` . ?¬) (?\\ . ?\|) (?\[ . ?{)
|
|
|
|
|
|
|
|
(?\] . ?}) (?\; . ?:) (?\' . ?@) (?\# . ?~) (?\, . ?<)
|
|
|
|
|
|
|
|
(?\. . ?>) (?/ . ??)))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
And then, a function to toggle a non-letter character, using that
|
|
|
|
|
|
|
|
mapping, defaulting to the identity if there's no entry.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun flip-non-letter-case (c)
|
|
|
|
|
|
|
|
(let (value)
|
|
|
|
|
|
|
|
(dolist (case-pair non-letter-case-mapping value)
|
|
|
|
|
|
|
|
(cond ((eq (car case-pair) c)
|
|
|
|
|
|
|
|
(setq value (cdr case-pair)))
|
|
|
|
|
|
|
|
((eq (cdr case-pair) c)
|
|
|
|
|
|
|
|
(setq value (car case-pair)))))
|
|
|
|
|
|
|
|
(when (eq value nil)
|
|
|
|
|
|
|
|
(setq value c))
|
|
|
|
|
|
|
|
value))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A similar function for letters can be easily defined using [[help:upcase][upcase]]
|
|
|
|
|
|
|
|
and [[help:downcase][downcase]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun upper-case-p (c)
|
|
|
|
|
|
|
|
(eq (upcase c) c))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun flip-letter-case (c)
|
|
|
|
|
|
|
|
(if (upper-case-p c)
|
|
|
|
|
|
|
|
(downcase c)
|
|
|
|
|
|
|
|
(upcase c)))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
These can then be combined into a case-flipping function that will
|
|
|
|
|
|
|
|
work for both letters and non-letters:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun letter-p (c)
|
|
|
|
|
|
|
|
(and (characterp c)
|
|
|
|
|
|
|
|
(let ((uc (upcase c)))
|
|
|
|
|
|
|
|
(and (>= uc ?A) (<= uc ?Z)))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun flip-char-case (c)
|
|
|
|
|
|
|
|
(if (letter-p c)
|
|
|
|
|
|
|
|
(flip-letter-case c)
|
|
|
|
|
|
|
|
(flip-non-letter-case c)))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
~flip-char-case~ can then applied over a whole string:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun flip-string-case (s)
|
|
|
|
|
|
|
|
(let ((len (length s))
|
|
|
|
|
|
|
|
(i 0))
|
|
|
|
|
|
|
|
(while (< i len)
|
|
|
|
|
|
|
|
(aset s i (flip-char-case (aref s i)))
|
|
|
|
|
|
|
|
(setq i (1+ i)))
|
|
|
|
|
|
|
|
s))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Finally, this can then be applied to the region, if it's active:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(defun flip-region-case ()
|
|
|
|
|
|
|
|
(interactive)
|
|
|
|
|
|
|
|
(when (region-active-p)
|
|
|
|
|
|
|
|
(let* ((start (region-beginning))
|
|
|
|
|
|
|
|
(end (region-end))
|
|
|
|
|
|
|
|
(text (buffer-substring-no-properties start end)))
|
|
|
|
|
|
|
|
(delete-region start end)
|
|
|
|
|
|
|
|
(insert (flip-string-case text)))))
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
And of course, I need a keybinding for that:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
|
|
|
|
(global-set-key (kbd "C-~") 'flip-region-case)
|
|
|
|
|
|
|
|
#+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
|
|
|
|
#+begin_src emacs-lisp
|
|
|
|
(setq gc-cons-threshold 1000000)
|
|
|
|
(setq gc-cons-threshold original-gc-cons-threshold)
|
|
|
|
(setq gc-cons-percentage 0.2)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
#+end_src
|
|
|
|
|