Compare commits

..

28 Commits

Author SHA1 Message Date
33e63ccda2 Tweak Script-Fu config a little 2025-01-08 13:38:02 +00:00
20e426dc69 Remove journal files config
I don't use these anymore, instead I hand-write stuff on my e-ink
tablet like the techno-contrarian qípā that I am.
2025-01-08 13:13:11 +00:00
d855179951 Remove calendar and diary config 2025-01-08 13:11:45 +00:00
02f13ff739 Fix typo in subsection heading 2025-01-08 13:11:00 +00:00
60a0b1c247 Prune out unused org-mode features
It was indeed funny to read the bit in the agenda config.
2025-01-08 13:10:39 +00:00
68fc323efd Set GC threshold high at start of initialisation
I did have this in here before and the code to set it back was still
at the end of the file, so not sure what happened there.  I guess I
accidentally removed it at some point.
2025-01-08 13:04:24 +00:00
1f7d38e03f Add eln-cache to .gitignore 2025-01-08 12:39:39 +00:00
74af0b81b3 Implement Script-Fu mode 2025-01-08 12:39:39 +00:00
76aad81ca2 Remove case-flipping config
I never actually use it (I mostly wrote it to impress Ava) so time for
it to be relegated to the git history.
2025-01-08 12:39:39 +00:00
bfe6414f98 Remove chatgpt-shell config 2025-01-08 12:39:39 +00:00
d9d2042459 Remove Geiser config 2025-01-08 12:39:39 +00:00
Camden Dixie O'Brien
665edb56cd Remove mail config 2024-12-19 16:44:40 +00:00
Camden Dixie O'Brien
36975d2eac Configure bitbake package 2024-12-19 16:44:40 +00:00
Camden Dixie O'Brien
8ee2885405 Add font height for work laptop 2024-12-19 16:44:40 +00:00
1c277c96da Configure man pages to open in active window 2024-12-19 16:41:27 +00:00
6e4565cd90 Add go-mode config 2024-12-19 16:41:12 +00:00
6bc67c0c82 Tweak c-mode snippets 2024-12-19 16:40:54 +00:00
a1fea5bc99 Untabify config 2024-08-03 16:15:39 +01:00
a9265c931d Fix <s and <q org mode shortcuts 2024-08-03 16:15:17 +01:00
d54d16f98f Add SCAD config 2024-07-14 11:00:45 +01:00
91da886a69 Add Nix config 2023-12-08 11:36:06 +00:00
8b26d8b7de Add BASIC config 2023-12-08 11:35:34 +00:00
bf734ca1aa Get rid of LSP for Lua and add command to run lua-format 2023-12-08 11:35:09 +00:00
5cbae91478 Remove Java and C# configs 2023-12-08 11:34:40 +00:00
08ef40170c Switch to tabs as default 2023-12-08 11:34:27 +00:00
56bed6b44a Get rid of LSP for C 2023-12-08 11:33:32 +00:00
976cd7c6b0 Add font-size for zora and remove for eagle 2023-12-08 11:32:25 +00:00
e155779d24 Use package instead of submodule for chatgpt-shell 2023-12-08 11:31:57 +00:00
6 changed files with 342 additions and 504 deletions

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ session.*
eshell/* eshell/*
*.data *.data
auto-save-list/* auto-save-list/*
eln-cache/*

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "chatgpt-shell"]
path = chatgpt-shell
url = https://github.com/xenodium/chatgpt-shell

Submodule chatgpt-shell deleted from f7a53903eb

View File

@@ -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!
@@ -119,10 +130,11 @@
#+begin_src emacs-lisp #+begin_src emacs-lisp
(let ((font-height (let ((font-height
(pcase hostname (pcase hostname
("eagle" 100) ("zora" 100)
("eddie" 100) ("eddie" 100)
("mandarax" 115) ("mandarax" 115)
("valis" 80) ("valis" 80)
("wipc23120423" 100)
(_ 110)))) (_ 110))))
(set-face-attribute 'default nil (set-face-attribute 'default nil
:family "Courier 10 Pitch" :family "Courier 10 Pitch"
@@ -221,46 +233,27 @@
(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 #+begin_src emacs-lisp
(use-package org (use-package org :config (require 'org-tempo))
:config
(require 'org-tempo))
#+end_src #+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 A keybinding to add a new heading is super useful
#+begin_src emacs-lisp #+begin_src emacs-lisp
@@ -271,91 +264,6 @@
'org-insert-heading-after-current))) 'org-insert-heading-after-current)))
#+end_src #+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 ** Source Blocks
Pressing tab inside a source block should indent appropriately for its Pressing tab inside a source block should indent appropriately for its
language. language.
@@ -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:
@@ -609,11 +408,10 @@
(setq-default basic-offset 4) (setq-default basic-offset 4)
#+end_src #+end_src
And generally indenting with spaces is more common, so make that Use tabs as god intended:
the default:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(setq-default indent-tabs-mode nil) (setq-default indent-tabs-mode t)
#+end_src #+end_src
*** Language Server Protocol *** Language Server Protocol
@@ -674,15 +472,7 @@
#+end_src #+end_src
** C ** C
For C there is =clangd= implementing LSP. Assuming that's For indenting style, I like BSD-style but with 4-char-wide indents
installed and on the =PATH=, we can just hook =lsp-mode= into the
default mode and there will be much rejoicing.
#+begin_src emacs-lisp
(add-hook 'c-mode-hook #'lsp-deferred)
#+end_src
As for indenting style, I like BSD-style but with 4-char-wide indents
#+begin_src emacs-lisp #+begin_src emacs-lisp
(add-hook 'c-mode-hook (lambda () (add-hook 'c-mode-hook (lambda ()
@@ -699,8 +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 () (add-hook 'c-mode-hook (lambda () (yas-minor-mode)))
(yas-minor-mode)))
#+end_src #+end_src
** C++ ** C++
@@ -825,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...
@@ -929,26 +699,6 @@
(add-to-list 'auto-mode-alist '("\\.pl\\'" . prolog-mode)) (add-to-list 'auto-mode-alist '("\\.pl\\'" . prolog-mode))
#+end_src #+end_src
** Java
[[https://github.com/jdee-emacs/jdee][JDEE]] provides a whole bunch of tooling for Java. It requires the
[[https://github.com/jdee-emacs/jdee-server][JDEE server]] to work properly, which has to be built seperately
(this requires JDK and Maven):
#+begin_src shell :tangle no
cd ~/src
git clone https://github.com/jdee-emacs/jdee-server.git
cd jdee-server
mvn -Dmaven.test.skip=true package
#+end_src
With that built, we can install JDEE, pointing at the built server:
#+begin_src emacs-lisp
(use-package jdee
:config
(setq jdee-server-dir "~/src/jdee-server/target"))
#+end_src
** Dockerfiles ** Dockerfiles
Grab =dockerfile-mode= for syntax highlighting etc in Dockerfiles: Grab =dockerfile-mode= for syntax highlighting etc in Dockerfiles:
@@ -1048,22 +798,6 @@
(add-hook 'python-mode-hook #'lsp-deferred) (add-hook 'python-mode-hook #'lsp-deferred)
#+end_src #+end_src
** C#
I hate the .NET ecosystem quite a lot, but alas: sometimes I have
to use it at work.
Using =csharp-mode= for basic support:
#+begin_src emacs-lisp
(use-package csharp-mode)
#+end_src
And =lsp-mode= for the rest (using the omnisharp server):
#+begin_src emacs-lisp
(add-hook 'csharp-mode-hook #'lsp-deferred)
#+end_src
** Ada ** Ada
*** Old ada-mode *** Old ada-mode
Unfortunately, the =ada-mode= on ELPA is hot garbage. It requires Unfortunately, the =ada-mode= on ELPA is hot garbage. It requires
@@ -1224,11 +958,95 @@
#+end_src #+end_src
** Lua ** Lua
Using a pretty standard LSP setup for Lua: Just using basic =lua-mode= package:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package lua-mode) (use-package lua-mode)
(add-hook 'lua-mode-hook #'lsp-deferred) #+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 #+end_src
* Tool Integrations * Tool Integrations
@@ -1306,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
@@ -1340,28 +1165,6 @@
'clang-format-region-or-buffer))) 'clang-format-region-or-buffer)))
#+end_src #+end_src
** ChatGPT
The =chatpt-shell= package is in a git submodule, so this has to be
added to the [[help:load-path][load-path]] and ~require~'d:
#+begin_src emacs-lisp
(let ((path (concat (getenv "HOME")
"/.emacs.d/chatgpt-shell")))
(add-to-list 'load-path path))
(require '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
@@ -1371,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
@@ -1413,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
@@ -1530,94 +1447,18 @@
(global-set-key (kbd "C-.") #'god-mode-all) (global-set-key (kbd "C-.") #'god-mode-all)
#+end_src #+end_src
** Case-flipping I find it jarring and confusing to have it on and off in different
Want to be able to toggle the 'shiftedness' of a selected region, types of buffers, so will just disable the exemptions:
that is, map uppercase to lowercase and vice versa, but also map
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 #+begin_src emacs-lisp
(defvar non-letter-case-mapping (setq god-exempt-major-modes nil)
'((?1 . ?!) (?2 . ?\") (?3 . ) (?4 . ?$) (?5 . ?%) (setq god-exempt-predicates nil)
(?6 . ?^) (?7 . ?&) (?8 . ?*) (?9 . ?\() (?0 . ?\))
(?- . ?_) (?= . ?+) (?\` . ) (?\\ . ?\|) (?\[ . ?{)
(?\] . ?}) (?\; . ?:) (?\' . ?@) (?\# . ?~) (?\, . ?<)
(?\. . ?>) (?/ . ??)))
#+end_src #+end_src
And then, a function to toggle a non-letter character, using that * Coda
mapping, defaulting to the identity if there's no entry. Now that initialization is finished, [[help:gc-cons-threshold][gc-cons-threshold]] should be set
back to its default value:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun flip-non-letter-case (c) (setq gc-cons-threshold original-gc-cons-threshold)
(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 #+end_src

View File

@@ -1,6 +1,6 @@
# -*- mode: snippet -*- # -*- mode: snippet -*-
# name: header-guard # name: header-guard
# binding: C-c C-k C-l # binding: C-c C-k C-h
# key: hdr # key: hdr
# -- # --
#ifndef ${1:`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H} #ifndef ${1:`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H}

View File

@@ -4,8 +4,8 @@
# key: spdx # key: spdx
# -- # --
/* /*
* SPDX-License-Identifier: $1
* Copyright (c) Camden Dixie O'Brien * Copyright (c) Camden Dixie O'Brien
* SPDX-License-Identifier: $1
*/ */
$0 $0