Browse Source

redo emacs from scratch

Hazel Levine 5 months ago
Signed by: hazel
GPG Key ID: 1884029A28789A62
  1. 191
  2. 567
  3. 90
  4. 729
  5. 19
  6. 9
  7. BIN
  8. 4
  9. 3
  10. 562
  11. 35
  12. 53
  13. 77
  14. 57
  15. 26
  16. 76
  17. 12
  18. 33
  19. 24
  20. 146
  21. 49
  22. 29
  23. 36
  24. 46
  25. 37
  26. 65
  27. 82
  28. 24
  29. 24
  30. 21
  31. 164
  32. 31
  33. 17
  34. 15
  35. 64
  36. 12
  37. 5
  38. 9
  39. 95
  40. 11
  41. 1
  42. 4
  43. 2
  44. 20
  45. 31


@ -1,191 +0,0 @@
#+TITLE: The Blag
#+AUTHOR: Hazel Levine
#+STARTUP: nofold
This is the source code powering my blog, available at [[][]]. It sure
does exist. More info can be found on the blog itself [[][here]], but basically this
uses pure Org mode with no external dependencies.
* Global variables
These are defined for later configuration and to avoid repetition, as well as to
modularize things such that if I ever change my home directory structure it
doesn't break everything.
We define where we want to store the files:
#+BEGIN_SRC emacs-lisp
(setq blog/site-project-path "/home/hazel/usr/doc/blog/")
Where we want the exported HTML to go:
#+BEGIN_SRC emacs-lisp
(setq blog/site-publish-path (concat blog/site-project-path "build/"))
As well as extra content to put in the header and footer. Note that setting
these as variables means =blog.el= has to be reloaded whenever you change the
contents of these files, which has bitten me in the butt /multiple/ times.
#+BEGIN_SRC emacs-lisp
(setq blog/site-extra-head-file (concat blog/site-project-path "templates/headext.html"))
(setq blog/site-header-file (concat blog/site-project-path "templates/header.html"))
(setq blog/site-footer-file (concat blog/site-project-path "templates/footer.html"))
Finally, we host MathJax locally, to avoid using CDNs, as well as the fact that
Org doesn't tango nice with MathJax 3.x for some reason. We use the Neo-Euler
font because I think it looks cool. Note that when deploying this, you probably
don't want to =scp= MathJax; it'll take forever.
#+BEGIN_SRC emacs-lisp
(setq blog/mathjax-options
'((path "./static/mj/MathJax.js?config=TeX-AMS-MML_HTMLorMML")
(scale "100") (align "left") (indent "2em") (tagside "right")
(mathml nil) (font "Neo-Euler")))
* Functions
We define a means to add HTML files as part of a file, such that we can add our
custom headers and footers:
#+BEGIN_SRC emacs-lisp
(defun blog/add-html-file (arg)
(insert-file-contents arg)
We then take our previous variables defining the header and footer of the site
and convert them into raw strings for usage in Org's exporter:
#+BEGIN_SRC emacs-lisp
(setq blog/site-extra-head-raw (blog/add-html-file blog/site-extra-head-file))
(setq blog/site-header-raw (blog/add-html-file blog/site-header-file))
(setq blog/site-footer-raw (blog/add-html-file blog/site-footer-file))
Additionally, we define a way to format a site entry in our table of contents:
#+BEGIN_SRC emacs-lisp
(defun blog/site-format-entry (entry style project)
(format "[[file:%s][%s]] --- %s"
(org-publish-find-title entry project)
(format-time-string "%Y-%m-%d" (org-publish-find-date entry project))))
* Org-publish
This is the meat of it; what tells Org that we're actually looking to make a
website out of miscalleneous files scattered in my documents folder. It's one
giant call to =setq=.
We tell Org that the site is a concatenation of all of its components, that we
want to export HTML, and to look recursively. We also tell Org to put stuff in
that's defined later.
#+BEGIN_SRC emacs-lisp :noweb yes
(setq org-publish-project-alist
:components ("site-static", "site-images", "site-articles", "site-dl", "site-rss"))
:publishing-function org-html-publish-to-html
:recursive t)))
** Static
Static resources, such as MathJax and CSS, should be copied directly to their
respective folders. This also applies for images and public files (=dl=). We
send these files to =blog/site-project-path=. The built-in publishing function
=org-publish-attachment= copies the file directly, and we also tell Org to
search recursively in case there are any subfolders.
#+NAME: static
#+BEGIN_SRC emacs-lisp :tangle no
:base-directory ,(concat blog/site-project-path "static/")
:base-extension ".*"
:publishing-directory ,(concat blog/site-publish-path "static/")
:publishing-function org-publish-attachment
:recursive t)
:base-directory ,(concat blog/site-project-path "img")
:base-extension ".*"
:publishing-directory ,(concat blog/site-publish-path "img/")
:publishing-function org-publish-attachment
:recursive t)
:base-directory ,(concat blog/site-project-path "dl")
:base-extension ".*"
:publishing-directory ,(concat blog/site-publish-path "dl/")
:publishing-function org-publish-attachment
:recursive t)
** RSS
I still like RSS. Also, one person asked for it, so I'm happy to oblige. For
some reason, Org likes to repeat the XML header three times (???) at the
beginning of the resulting XML file, so I go back and manually delete it every
We tell it to look over all the articles, tell it where the blog is, and tell it
to look over (which is our table of contents) and nothing else. We
also use =org-rss-publish-to-rss=, which pretty much does what it says it does.
#+NAME: rss
#+BEGIN_SRC emacs-lisp :tangle no
:base-directory ,(concat blog/site-project-path "articles/")
:base-extension "org"
:publishing-directory ,blog/site-publish-path
:publishing-function org-rss-publish-to-rss
:html-link-home ""
:html-link-use-abs-url t
:title "Ziodyne"
:section-numbers nil
:exclude ".*"
:include ("")
:table-of-contents nil)
** Articles (HTML)
We tell Org to look over our articles directory, only search through Org files,
and put everything in the site's publish path:
#+NAME: html1
#+BEGIN_SRC emacs-lisp :tangle no
:base-directory ,(concat blog/site-project-path "articles/")
:base-extension "org"
:publishing-directory ,blog/site-publish-path
We tell it that we're using HTML5, to include the extra headers, preamble, and
postamble, and that we're operating on the root of the subdomain, as well as
define MathJax to be itself:
#+NAME: html2
#+BEGIN_SRC emacs-lisp :tangle no
:html-doctype "html5"
:html-link-home "/"
:html-head nil
:html-head-extra ,blog/site-extra-head-raw
:html-head-include-default-style nil
:html-head-include-scripts nil
:html-home/up-format ""
:html-preamble ,blog/site-header-raw
:html-postamble ,blog/site-footer-raw
:html-mathjax-options ,blog/mathjax-options
We generate the sitemap, tell it to save the result to, and to list
anti-chronologically. We also use the previously defined function
=blog/site-format-entry= to... format entries.
#+NAME: html3
#+BEGIN_SRC emacs-lisp :tangle no
:makeindex nil
:auto-sitemap t
:sitemap-filename ""
:sitemap-title "Ramblings"
:sitemap-style list
:sitemap-sort-files anti-chronologically
:sitemap-format-entry blog/site-format-entry
:with-toc nil
:section-numbers nil


@ -1,567 +0,0 @@
#+TITLE: Hazel's DOOM Emacs configuration
#+AUTHOR: Hazel Levine
#+STARTUP: nofold
Whee, literate programming, whoo. I get to type words here. These are words on a
screen. Typing. With /haaaaaaands/.
In this file, we tangle the following:
#+BEGIN_SRC emacs-lisp
;;; config.el -*- lexical-binding: t -*-
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
;;; packages.el -*- lexical-binding: t -*-
Any calls to =package!= generally get tangled to =packages.el=, whereas
everything else goes to =config.el=.
* Introduction
** =whoami=
I'm a bunny.
#+BEGIN_SRC emacs-lisp
(setq user-full-name "Hazel Levine"
user-mail-address "")
* UI
** Fonts
I've had font rendering issues with Emacs for /years/, ever since I picked it
up. Namely, Emacs' font always looked just different enough from that of
whatever terminal emulator I'm using to be annoying. Thankfully, [[][rocx's emacs
config]] held the solution by forcing Emacs to use XFT.
I first define a function to create an XFT font string from whatever font, size,
and options I want:
#+BEGIN_SRC emacs-lisp
(defun hzl/xft-font-string (name size &optional properties)
(concat (format "%s-%d" name size)
(when properties
(apply #'concat (mapcar (lambda (prop)
(format ":%s=%s" (car prop) (cdr prop)))
As my university education dragged on and I began to do things with formal logic,
I needed a font with good support for unicode characters. I use Source Code Pro,
but with a bunch of fallbacks.
I've also disabled bold fonts because I cannot get them to render properly in
#+BEGIN_SRC emacs-lisp
(defvar hzl/monospace-font
(hzl/xft-font-string "Source Code Pro" 10
'((hintstyle . 3)
(hinting . true)
(lcdfilter . 3)
(antialias . true))))
(defvar hzl/variable-pitch-font
(hzl/xft-font-string "IBM Plex Sans" 10
'((hintstyle . 3)
(autohint . true)
(lcdfilter . 3)
(antialias . true))))
(defvar hzl/unicode-font
(hzl/xft-font-string "DejaVu Sans Mono" 10
'((hintstyle . 3)
(autohint . true)
(lcdfilter . 3)
(antialias . true))))
(setq doom-font hzl/monospace-font)
(setq doom-variable-pitch-font hzl/variable-pitch-font)
(setq doom-unicode-font hzl/monospace-font)
Set up Unicode fallback fonts, for the weird characters that I can't pick up:
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! unicode-fonts)
#+BEGIN_SRC emacs-lisp
(use-package! unicode-fonts
:config (unicode-fonts-setup))
** Colorscheme
I use light themes. After years and years of dark themes, I came to the realization that:
+ Anti-aliasing algorithms were better with black text on a white background
+ I can read light themes without glasses
+ "Burning your eyes" is not a thing that actually happens
Right now I'm using the Leuven theme.
#+BEGIN_SRC emacs-lisp
(setq doom-theme 'doom-earl-grey)
** Everything else
Enable line numbers, because those are nice, and make 'em relative:
#+BEGIN_SRC emacs-lisp
(setq display-line-numbers-type 'relative)
Copy Tecosaur's splash screen because I'm lazy:
#+BEGIN_SRC emacs-lisp
(defvar fancy-splash-image-template
(expand-file-name "splash/emacs-e-template.svg" doom-private-dir)
"Default template svg used for the splash image, with substitutions from ")
(defvar fancy-splash-sizes
`((:height 300 :min-height 50 :padding (0 . 2))
(:height 250 :min-height 42 :padding (2 . 4))
(:height 200 :min-height 35 :padding (3 . 3))
(:height 150 :min-height 28 :padding (3 . 3))
(:height 100 :min-height 20 :padding (2 . 2))
(:height 75 :min-height 15 :padding (2 . 1))
(:height 50 :min-height 10 :padding (1 . 0))
(:height 1 :min-height 0 :padding (0 . 0)))
"list of plists with the following properties
:height the height of the image
:min-height minimum `frame-height' for image
:padding `+doom-dashboard-banner-padding' (top . bottom) to apply
:template non-default template file
:file file to use instead of template")
;; (defvar fancy-splash-template-colours
;; '(("$colour1" . "#335ea8"))
;; "list of colour-replacement alists of the form (\"$placeholder\" . 'theme-colour) which applied the template")
(defvar fancy-splash-template-colours
'(("$colour1" . keywords) ("$colour2" . type) ("$colour3" . base5) ("$colour4" . base8))
"list of colour-replacement alists of the form (\"$placeholder\" . 'theme-colour) which applied the template")
(unless (file-exists-p (expand-file-name "theme-splashes" doom-cache-dir))
(make-directory (expand-file-name "theme-splashes" doom-cache-dir) t))
(defun fancy-splash-filename (theme-name height)
(expand-file-name (concat (file-name-as-directory "theme-splashes")
"-" (number-to-string height) ".svg")
(defun fancy-splash-clear-cache ()
"Delete all cached fancy splash images"
(delete-directory (expand-file-name "theme-splashes" doom-cache-dir) t)
(message "Cache cleared!"))
(defun fancy-splash-generate-image (template height)
"Read TEMPLATE and create an image if HEIGHT with colour substitutions as
described by `fancy-splash-template-colours' for the current theme"
(insert-file-contents template)
(re-search-forward "$height" nil t)
(replace-match (number-to-string height) nil nil)
(dolist (substitution fancy-splash-template-colours)
(goto-char (point-min))
(while (re-search-forward (car substitution) nil t)
(replace-match (doom-color (cdr substitution)) nil nil)))
(write-region nil nil
(fancy-splash-filename (symbol-name doom-theme) height) nil nil)))
(defun fancy-splash-generate-images ()
"Perform `fancy-splash-generate-image' in bulk"
(dolist (size fancy-splash-sizes)
(unless (plist-get size :file)
(fancy-splash-generate-image (or (plist-get size :template)
(plist-get size :height)))))
(defun ensure-theme-splash-images-exist (&optional height)
(unless (file-exists-p (fancy-splash-filename
(symbol-name doom-theme)
(or height
(plist-get (car fancy-splash-sizes) :height))))
(defun get-appropriate-splash ()
(let ((height (frame-height)))
(cl-some (lambda (size) (when (>= height (plist-get size :min-height)) size))
(setq fancy-splash-last-size nil)
(setq fancy-splash-last-theme nil)
(defun set-appropriate-splash (&rest _)
(let ((appropriate-image (get-appropriate-splash)))
(unless (and (equal appropriate-image fancy-splash-last-size)
(equal doom-theme fancy-splash-last-theme)))
(unless (plist-get appropriate-image :file)
(ensure-theme-splash-images-exist (plist-get appropriate-image :height)))
(setq fancy-splash-image
(or (plist-get appropriate-image :file)
(fancy-splash-filename (symbol-name doom-theme) (plist-get appropriate-image :height))))
(setq +doom-dashboard-banner-padding (plist-get appropriate-image :padding))
(setq fancy-splash-last-size appropriate-image)
(setq fancy-splash-last-theme doom-theme)
(add-hook 'window-size-change-functions #'set-appropriate-splash)
(add-hook 'doom-load-theme-hook #'set-appropriate-splash)
Also copy quotes, because I'm just a complete damned ripoff:
#+BEGIN_SRC emacs-lisp
(defvar phrase-api-url
(nth (random 3)
'(("" :phrase)
("" :data)
("" :text))))
(defmacro phrase-generate-callback (token &optional format-fn ignore-read-only callback buffer-name)
`(lambda (status)
(unless (plist-get status :error)
(goto-char url-http-end-of-headers)
(let ((phrase (plist-get (json-parse-buffer :object-type 'plist) (cadr phrase-api-url)))
(inhibit-read-only ,(when (eval ignore-read-only) t)))
(setq phrase-last (cons phrase (float-time)))
(with-current-buffer ,(or (eval buffer-name) (buffer-name (current-buffer)))
(goto-char (point-min))
(when (search-forward ,token nil t)
(replace-match "")
(insert ,(if format-fn format-fn 'phrase)))))
(defvar phrase-last nil)
(defvar phrase-timeout 5)
(defmacro phrase-insert-async (&optional format-fn token ignore-read-only callback buffer-name)
`(let ((inhibit-message t))
(if (and phrase-last
(> phrase-timeout (- (float-time) (cdr phrase-last))))
(let ((phrase (car phrase-last)))
,(if format-fn format-fn 'phrase))
(url-retrieve (car phrase-api-url)
(phrase-generate-callback ,(or token "\ufeff") ,format-fn ,ignore-read-only ,callback ,buffer-name))
;; For reference, \ufeff = Zero-width no-break space / BOM
,(or token "\ufeff"))))
(defun doom-dashboard-phrase ()
(setq-local phrase-position (point))
(lambda (line)
(lambda (_)
(setq phrase-last nil)
(+doom-dashboard-reload t))
'face 'doom-dashboard-menu-title
'mouse-face 'doom-dashboard-menu-title
'help-echo "Random phrase"
'follow-link t)
(insert phrase)
(setq fill-column (min 70 (/ (* 2 (window-width)) 3)))
(fill-region (point-min) (point-max))
nil t
(goto-char phrase-position)
(forward-whitespace 1))
(defadvice! doom-dashboard-widget-loaded-with-phrase ()
:override #'doom-dashboard-widget-loaded
(setq line-spacing 0.2)
(doom-display-benchmark-h 'return))
'face 'doom-dashboard-loaded)
(remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-shortmenu)
(add-hook! '+doom-dashboard-mode-hook (hide-mode-line-mode 1) (hl-line-mode -1))
(setq-hook! '+doom-dashboard-mode-hook evil-normal-state-cursor (list nil))
* Functionality
For the most part, DOOM Emacs handles 99% of the things I want to do in a text
editor and more, which is why this section is pretty brief.
Pretty much all of the functions I write end up under the "namespace"
=hzl/whatever=, on the merit that I want to avoid any clashes anywhere.
** MPDel music player
I usually use =ncmpcpp= for music, but sometimes when I'm working I use this.
It's all MPD, so it's not like they conflict.
We grab both MPDel and its Ivy interface:
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! mpdel)
(package! ivy-mpdel)
We also disable evil in MPDel because its keybindings are contingent on doing
#+BEGIN_SRC emacs-lisp
(use-package! mpdel
(set-evil-initial-state! '(mpdel-playlist-mode
** Arbitrary Unicode input
While I like Agda's input mode for Unicode input, it's not practical everywhere,
and I occasionally need to put Unicode into non-Agda files (namely Racket when
writing DSLs using Unicode characters). DrRacket's approach is pretty good, so I
stole a package to emulate it.
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! dr-racket-like-unicode
:recipe (:host github :repo "david-christiansen/dr-racket-like-unicode"))
Bind it to C-\ everywhere, because I haven't found any situation where this
breaks anything:
#+BEGIN_SRC emacs-lisp
(use-package! dr-racket-like-unicode
:config (map! :i "C-\\" #'dr-racket-like-unicode-char))
** LSP
Mostly handled by DOOM modules, but this causes Emacs to not fucking crash:
#+BEGIN_SRC emacs-lisp
(after! lsp-mode (setq lsp-enable-file-watchers nil))
* Productivity, papers, etc
For the boring stuff that's not /quite/ programming.
** TeXcount
This is a binding to a Perl script installed via =tlmgr= that... counts words.
Considering most of the papers I write have hard minimum/maximum limits, this
comes in pretty useful pretty often.
#+BEGIN_SRC emacs-lisp
(defun hzl/texcount ()
;; Counts words in a TeX file.
((this-file (buffer-file-name))
(enc-str (symbol-name buffer-file-coding-system))
((string-match "utf-8" enc-str) "-utf8")
((string-match "latin" enc-str) "-latin1")
(with-current-buffer standard-output
(call-process "texcount" nil t nil "-0" enc-opt this-file)))))
(message word-count)))
...and then, actually bind it to =C-c w= in LaTeX mode.
#+BEGIN_SRC emacs-lisp
(add-hook 'LaTeX-mode-hook (lambda () (define-key LaTeX-mode-map "\C-cw" 'hzl/texcount)))
** AucTeX =latexmk=
I use =latexmk= to build my LaTeX work because I use external files for my
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! auctex-latexmk)
We tell it to run the auto-setup function and use PDFs:
#+BEGIN_SRC emacs-lisp
(use-package! auctex-latexmk
(setq auctex-latexmk-inherit-TeX-pdf-mode t))
** Org-mode
Set the bullets to pretty stuff:
#+BEGIN_SRC emacs-lisp
(setq org-bullets-bullet-list '("☯" "☰" "☱" "☲" "☳" "☴" "☵" "☶" "☷"))
(setq org-ellipsis "↝")
Make sure that Org doesn't try to clutter my home directory, and put stuff where
it's supposed to be:
#+BEGIN_SRC emacs-lisp
(setq org-directory "~/usr/doc/org/")
Set DOOM's scratch buffer, available at any point with =SPC x=, to Org, which I
find useful for taking quick notes:
#+BEGIN_SRC emacs-lisp
(setq doom-scratch-buffer-major-mode 'org-mode)
We also grab =emacs-org=dnd=, for my character sheet:
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! ox-dnd
:recipe (:host github :repo "xeals/emacs-org-dnd"))
#+BEGIN_SRC emacs-lisp
(use-package! ox-dnd)
** PDF Tools
While editing LaTeX documents, this is my PDF viewer of choice. Otherwise, I use
#+BEGIN_SRC emacs-lisp
(setq TeX-view-program-selection '((output-pdf "PDF Tools")))
(add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)
* Programming languages
The cool stuff. Unless it's Java.
** Agda
The Doom module is kinda broken.
Grab the executable if we can find the =agda-mode= binary:
#+BEGIN_SRC emacs-lisp
(when (executable-find "agda-mode")
(let ((coding-system-for-read 'utf-8))
(shell-command-to-string "agda-mode locate"))))
Then copy straight from the Doom Agda module:
#+BEGIN_SRC emacs-lisp
(map! :after agda2-mode
:map agda2-mode-map
"?" #'agda2-show-goals
"." #'agda2-goal-and-context-and-inferred
"," #'agda2-goal-and-context
"=" #'agda2-show-constraints
"SPC" #'agda2-give
"a" #'agda2-auto-maybe-all
"b" #'agda2-previous-goal
"c" #'agda2-make-case
"d" #'agda2-infer-type-maybe-toplevel
"e" #'agda2-show-context
"f" #'agda2-next-goal
"gG" #'agda2-go-back
"h" #'agda2-helper-function-type
"l" #'agda2-load
"n" #'agda2-compute-normalised-maybe-toplevel
"p" #'agda2-module-contents-maybe-toplevel
"r" #'agda2-refine
"s" #'agda2-solveAll
"t" #'agda2-goal-type
"w" #'agda2-why-in-scope-maybe-toplevel
(:prefix "x"
"c" #'agda2-compile
"d" #'agda2-remove-annotations
"h" #'agda2-display-implicit-arguments
"q" #'agda2-quit
"r" #'agda2-restart))
** cooltt
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! cooltt
:recipe (:host github
:repo "RedPRL/cooltt"
:files ("emacs/*.el")))
#+BEGIN_SRC emacs-lisp
(use-package! cooltt
:config (setq cooltt-command "dune exec cooltt --"))
(add-hook! cooltt-mode
(activate-input-method "TeX"))
(map! :after cooltt-mode
:map cooltt-mode-map
"l" #'cooltt-compile-buffer
"d" #'cooltt-toggle-debug)
** FRC Mode
This is a =gradlew= wrapper I hacked together really fast while sitting in my
physics class not paying attention. The officially sanctioned IDE for FIRST
Robotics is Visual Studio Code, which I hate with a burning passion for numerous
Note that I'm no longer a FRC student, so if this ever goes out of date, sucks.
I'm planning on mentoring though, so it probably won't.
We grab it directly from my Git, since it's not in ELPA (and probably never will
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! frc-mode
:recipe (:host nil :repo "" :branch "canon"))
And tell it to run with all Java files. I'd never willingly write Java outside
of FRC, so it's fine.
#+BEGIN_SRC emacs-lisp
(use-package! frc-mode
:hook (java-mode . frc-mode))
** Lisp/symex.el
I used to use parinfer. I had a lot of issues with it. It stopped working.
Then, I used paredit, and had similar issues. It also stopped working.
Parinfer-rust is really broken for me.
Lisp is good, but when I copy-paste stuff in I have to disable it to re-match parentheses.
This is my final hope.
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! symex)
#+BEGIN_SRC emacs-lisp
(use-package! symex
(setq symex--user-evil-keyspec
'(("j" . symex-go-up)
("k" . symex-go-down)
("C-j" . symex-climb-branch)
("C-k" . symex-descend-branch)
("M-j" . symex-goto-highest)
("M-k" . symex-goto-lowest)))
(map! :leader
:desc "Symex mode"
"~" #'symex-mode-interface)
(symex-modal-backend 'evil))
** =rust-analyzer=
I have tons of issues with RLS -- it just does NOT behave. While I have to pull
=rust-analyzer= from unstable nixpkgs, and it's marked as unstable all over the
place, it's /still/ miles ahead of RLS.
#+BEGIN_SRC emacs-lisp
(after! rustic
(setq rustic-lsp-server 'rust-analyzer))
** Sage
The only calculator useful enough for the math classes I'm taking.
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! sage-shell-mode)
#+BEGIN_SRC emacs-lisp
(use-package! sage-shell-mode
:config (sage-shell:define-alias))
** Scribble
The format for Racket documentation. Grab it from GitHub:
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el
(package! scribble-mode
:recipe (:host github :repo "emacs-pe/scribble-mode"))
Then enable it. It takes care of the file extensions itself.
#+BEGIN_SRC emacs-lisp
(use-package! scribble-mode)
* What
Misc scraps. Most of these I don't remember the original purpose of.
#+BEGIN_SRC emacs-lisp
(map! "C-;" nil)


@ -1,90 +0,0 @@
;; init.el -*- lexical-binding: t; -*-
;; :: _ ;;
;; `:_|_;'
;; !
(doom! :completion
(vertico +icons)
(popup +all +defaults)
(evil +everywhere)
;; lispy
(dired +icons +ranger)
(spell +flyspell) ; this annoying? yes! do i need it for editing latex papers?
; also yes!
(eval +overlay)
(lookup +docsets)
(haskell +lsp)
(java +lsp)
(org +dragndrop
(racket +xp)
(rust +lsp)
(scala +lsp)
(rss +org)
(default +bindings +smartparens))


@ -1,729 +0,0 @@
;;; qi-mode-el -- Major mode for editing Qi files
;;; qi-inferior-mode provided below
;; Author: Michael Ilseman
;; Created: 12 May 2007
;; Keywords: Qi major-mode
;; Copyright (C) 2007 Michael Ilseman
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2 of
;; the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be
;; useful, but WITHOUT ANY WARRANTY; without even the implied
;; PURPOSE. See the GNU General Public License for more details.
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;;; Commentary:
;; based on the information from the tutorial below
;; and the scheme major mode
;; Contributers: Jt Gleason <>
;;; Code:
(defvar qi-mode-hook nil)
(defvar qi-mode-map nil)
(unless qi-mode-map
(let ((map (make-sparse-keymap "Qi")))
(setq qi-mode-map (make-sparse-keymap))
(define-key qi-mode-map "\C-c\C-c" 'run-qi)
(set-keymap-parent qi-mode-map lisp-mode-shared-map)
(define-key qi-mode-map [menu-bar] (make-sparse-keymap))
(define-key qi-mode-map [menu-bar qi]
(cons "Qi" map))
(define-key map [run-qi] '("Run Inferior Qi" . run-qi))
(define-key map [indent-region] '("Indent Region" . indent-region))
(define-key map [indent-line] '("Indent Line" . lisp-indent-line))
(put 'comment-region 'menu-enable 'mark-active)
(put 'uncomment-region 'menu-enable 'mark-active)
(put 'indent-region 'menu-enable 'mark-active)))
(add-to-list 'auto-mode-alist '("\\.qi\\'" . qi-mode))
(defconst qi-font-lock-keywords-1
'("\\<\\(define\\|datatype\\)\\>" . font-lock-keyword-face)
'("\\('\\w*'\\)" . font-lock-variable-name-face))
"Minimal highlighting expressions for Qi mode.")
(defconst qi-font-lock-keywords-2
(append qi-font-lock-keywords-1
'("^error: .*$" . font-lock-warning-face)
'("\\<\\(if\\|tc\\)\\>" . font-lock-function-name-face)
'("\\W/\\.\\W" . font-lock-function-name-face)
'("\\<\\([A-Z]\\w*\\)\\>" . font-lock-variable-name-face)
'("\\<where\\>" . font-lock-type-face)
'("\\W@p\\W" . font-lock-type-face)
'("\\<\\(boolean[?]\\|character[?]\\|complex[?]\\|congruent[?]\\|cons[?]\\|element[?]\\|empty[?]\\|float[?]\\|integer[?]\\|number[?]\\|provable[?]\\|rational[?]\\|solved[?]\\|string[?]\\|symbol[?]\\|tuple[?]\\|variable[?]\\)\\W" . font-lock-builtin-face)
'("\\<\\(and\\|append\\|apply\\|atp-credits\\|atp-prompt\\|cd\\|collect\\|concat\\|cons\\|delete-file\\|destroy\\|debug\\|difference\\|display-mode\\|do\\|dump\\|dump-proof\\|eval\\|explode\\|error\\|fix\\|from-goals\\|fst\\|fst-ass\\|fst-conc\\|fst-goal\\|gensym\\|get-array\\|get-prop\\|get-rule\\|head\\|if-with-checking\\|if-without-checking\\|include\\|include-all-but\\|inferences\\|input\\|length\\|lineread\\|map\\|macroexpand\\|make-string\\|maxinferences\\|newfuntype\\|notes-in\\|nth\\|occurrences\\|output\\|preclude\\|preclude-all-but\\|prf\\|profile\\|profile-results\\|prooftool\\|put-array\\|put-prop\\|quit\\|random\\|read-char\\|read-file\\|read-file-as-charlist\\|read-chars-as-stringlist\\|refine\\|reserve\\|reverse\\|round\\|save\\|snd\\|spy\\|sqrt\\|step\\|strong-warning\\|tail\\|theory-size\\|thm-intro\\|to-goals\\|time\\|time-proof\\|track\\|undebug\\|union\\|unprf\\|unprofile\\|unreserve\\|unspecialise\\|untrack\\|value\\|version\\|warn\\|write-to-file\\)\\>" . font-lock-builtin-face)
; '("\\<test\\>" . font-lock-comment-face)
; '("\\<test1\\>" . font-lock-keyword-face)
; '("\\<test3\\>" . font-lock-variable-name-face)
; '("\\<test4\\>" . font-lock-type-face)
; '("\\<test5\\>" . font-lock-constant-face)
; '("\\<test6\\>" . font-lock-warning-face)
"more highlighting in Qi mode.")
(defconst qi-font-lock-keywords-3
(append qi-font-lock-keywords-2
'("\\<\\(true\\|false\\|character\\|string\\|symbol\\|number\\|list\\|boolean\\)\\>" . font-lock-constant-face)))
"Additional Keywords to highlight in Qi mode.")
(defvar qi-font-lock-keywords qi-font-lock-keywords-3
"Default highlighting expressions for Qi mode.")
;; Copied from scheme-mode, which in turn is from lisp-mode
(defun qi-indent-function (indent-point state)
(let ((normal-indent (current-column)))
(goto-char (1+ (elt state 1)))
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
(if (and (elt state 2)
(not (looking-at "\\sw\\|\\s_")))
;; car of form doesn't seem to be a symbol
(if (not (> (save-excursion (forward-line 1) (point))
(progn (goto-char calculate-lisp-indent-last-sexp)
(parse-partial-sexp (point)
calculate-lisp-indent-last-sexp 0 t)))
;; Indent under the list or under the first sexp on the same
;; line as calculate-lisp-indent-last-sexp. Note that first
;; thing on that line has to be complete sexp since we are
;; inside the innermost containing sexp.
(let ((function (buffer-substring (point)
(progn (forward-sexp 1) (point))))
(setq method (or (get (intern-soft function) 'qi-indent-function)
(get (intern-soft function) 'qi-indent-hook)))
(cond ((or (eq method 'defun)
(and (null method)
(> (length function) 3)
(string-match "\\`def" function)))
(lisp-indent-defform state indent-point))
((integerp method)
(lisp-indent-specform method state
indent-point normal-indent))
(funcall method state indent-point normal-indent)))))))
(defun qi-let-indent (state indent-point normal-indent)
(skip-chars-forward " \t")
(if (looking-at "[-a-zA-Z0-9+*/?!@$%^&_:~]")
(lisp-indent-specform 2 state indent-point normal-indent)
(lisp-indent-specform 1 state indent-point normal-indent)))
;; (put 'begin 'qi-indent-function 0), say, causes begin to be indented
;; like defun if the first form is placed on the next line, otherwise
;; it is indented like any other form (i.e. forms line up under first).
;(put (make-symbol "/.") 'qi-indent-function 1)
(put 'datatype 'qi-indent-function 1)
(put 'let 'qi-indent-function 'qi-let-indent)
;(put (make-symbol "->") 'qi-indent-function 'qi-let-indent)
;; Qi uses the \ character as the comment delimiter
;; Qi follows lisp in using # as the character escape, this overrides the comment type for #\Space sequences
(defvar qi-mode-syntax-table
(let ((qi-mode-syntax-table (make-syntax-table)))
(modify-syntax-entry ?\\ "!" qi-mode-syntax-table)
(modify-syntax-entry ?# "\\" qi-mode-syntax-table)
(modify-syntax-entry ?- "w" qi-mode-syntax-table)
"Syntax table for qi-mode")
(defun qi-mode ()
(use-local-map qi-mode-map)
(set-syntax-table qi-mode-syntax-table)
;; Set up font-lock
(set (make-local-variable 'font-lock-defaults) '(qi-font-lock-keywords))
;; Register our indentation function
(make-local-variable 'indent-line-function)
(setq indent-line-function 'lisp-indent-line)
(make-local-variable 'comment-indent-function)
(setq comment-indent-function 'lisp-comment-indent)
(make-local-variable 'lisp-indent-function)
(set lisp-indent-function 'qi-indent-function)
(setq major-mode 'qi-mode)
(setq mode-name "Qi")
(run-hooks 'qi-mode-hook))
(provide 'qi-mode)
;;; inferior-qi-mode --- an inferior-qi mode
;; Copyright (C) 2007
;; Author: Michael Ilseman
;; Keywords: processes, qi
;;; Commentary:
;; Mostly taken from inf-lisp.el. Pretty much a copy/paste,
;; search/replace with syntax highlighting added.
;; This file defines a qi-in-a-buffer package (inferior-qi mode)
;; built on top of comint mode.
;; Since this mode is built on top of the general command-interpreter-in-
;; a-buffer mode (comint mode), it shares a common base functionality,
;; and a common set of bindings, with all modes derived from comint mode.
;; This makes these modes easier to use.
;; For documentation on the functionality provided by comint mode, and
;; the hooks available for customising it, see the file comint.el.
;; For further information on inferior-qi mode, see the comments below.
;; Needs fixin:
;;; Code:
(require 'comint)
(require 'qi-mode)
(defvar inferior-qi-filter-regexp "\\`\\s *\\(:\\(\\w\\|\\s_\\)\\)?\\s *\\'"
"*What not to save on inferior Qi's input history.
Input matching this regexp is not saved on the input history in Inferior Qi
mode. Default is whitespace followed by 0 or 1 single-letter colon-keyword
\(as in :a, :c, etc.)")
(defvar inferior-qi-mode-map nil)
(unless inferior-qi-mode-map
(setq inferior-qi-mode-map (copy-keymap comint-mode-map))
; (set-keymap-parent inferior-qi-mode-map qi-mode-shared-map)
(define-key inferior-qi-mode-map "\C-x\C-e" 'qi-eval-last-sexp)
(define-key inferior-qi-mode-map "\C-c\C-l" 'qi-load-file)
(define-key inferior-qi-mode-map "\C-c\C-k" 'qi-compile-file)
(define-key inferior-qi-mode-map "\C-c\C-a" 'qi-show-arglist)
(define-key inferior-qi-mode-map "\C-c\C-d" 'qi-describe-sym)
(define-key inferior-qi-mode-map "\C-c\C-f"
(define-key inferior-qi-mode-map "\C-c\C-v"
;;; These commands augment Qi mode, so you can process Qi code in
;;; the source files.
(define-key qi-mode-map "\M-\C-x" 'qi-eval-defun) ; Gnu convention
(define-key qi-mode-map "\C-x\C-e" 'qi-eval-last-sexp) ; Gnu convention
(define-key qi-mode-map "\C-c\C-e" 'qi-eval-defun)
(define-key qi-mode-map "\C-c\C-r" 'qi-eval-region)
(define-key qi-mode-map "\C-c\C-c" 'qi-compile-defun)
(define-key qi-mode-map "\C-c\C-z" 'switch-to-qi)
(define-key qi-mode-map "\C-c\C-l" 'qi-load-file)
(define-key qi-mode-map "\C-c\C-k" 'qi-compile-file) ; "kompile" file
(define-key qi-mode-map "\C-c\C-a" 'qi-show-arglist)
(define-key qi-mode-map "\C-c\C-d" 'qi-describe-sym)
(define-key qi-mode-map "\C-c\C-f" 'qi-show-function-documentation)
(define-key qi-mode-map "\C-c\C-v" 'qi-show-variable-documentation)
;;; This function exists for backwards compatibility.
;;; Previous versions of this package bound commands to C-c <letter>
;;; bindings, which is not allowed by the gnumacs standard.
;;; "This function binds many inferior-qi commands to C-c <letter> bindings,
;;;where they are more accessible. C-c <letter> bindings are reserved for the
;;;user, so these bindings are non-standard. If you want them, you should
;;;have this function called by the inferior-qi-load-hook:
;;; (setq inferior-qi-load-hook '(inferior-qi-install-letter-bindings))
;;;You can modify this function to install just the bindings you want."
(defun inferior-qi-install-letter-bindings ()
(define-key qi-mode-map "\C-ce" 'qi-eval-defun-and-go)
(define-key qi-mode-map "\C-cr" 'qi-eval-region-and-go)
(define-key qi-mode-map "\C-cc" 'qi-compile-defun-and-go)
(define-key qi-mode-map "\C-cz" 'switch-to-qi)
(define-key qi-mode-map "\C-cl" 'qi-load-file)
(define-key qi-mode-map "\C-ck" 'qi-compile-file)
(define-key qi-mode-map "\C-ca" 'qi-show-arglist)
(define-key qi-mode-map "\C-cd" 'qi-describe-sym)
(define-key qi-mode-map "\C-cf" 'qi-show-function-documentation)
(define-key qi-mode-map "\C-cv" 'qi-show-variable-documentation)
(define-key inferior-qi-mode-map "\C-cl" 'qi-load-file)
(define-key inferior-qi-mode-map "\C-ck" 'qi-compile-file)
(define-key inferior-qi-mode-map "\C-ca" 'qi-show-arglist)
(define-key inferior-qi-mode-map "\C-cd" 'qi-describe-sym)
(define-key inferior-qi-mode-map "\C-cf" 'qi-show-function-documentation)
(define-key inferior-qi-mode-map "\C-cv"
(defvar inferior-qi-program "Qi"
"*Program name for invoking an inferior Qi with for Inferior Qi mode.")
(defvar inferior-qi-load-command "(load \"%s\")\n"
"*Format-string for building a Qi expression to load a file.
This format string should use `%s' to substitute a file name
and should result in a Qi expression that will command the inferior Qi
to load that file. The default works acceptably on most Qis.
The string \"(progn (load \\\"%s\\\" :verbose nil :print t) (values))\\n\"
produces cosmetically superior output for this application,
but it works only in Common Qi.")
(defvar inferior-qi-prompt "^[^> \n]*>+:? *"
"Regexp to recognise prompts in the Inferior Qi mode.
Defaults to \"^[^> \\n]*>+:? *\", which works pretty good for Lucid, kcl,
and franz. This variable is used to initialize `comint-prompt-regexp' in the
Inferior Qi buffer.
This variable is only used if the variable
`comint-use-prompt-regexp-instead-of-fields' is non-nil.
More precise choices:
Lucid Common Qi: \"^\\\\(>\\\\|\\\\(->\\\\)+\\\\) *\"
franz: \"^\\\\(->\\\\|<[0-9]*>:\\\\) *\"
kcl: \"^>+ *\"
This is a fine thing to set in your .emacs file.")
(defvar inferior-qi-buffer nil "*The current inferior-qi process buffer.
To run multiple Qi processes, you start the first up
with \\[inferior-qi]. It will be in a buffer named `*inferior-qi*'.
Rename this buffer with \\[rename-buffer]. You may now start up a new
process with another \\[inferior-qi]. It will be in a new buffer,
named `*inferior-qi*'. You can switch between the different process
buffers with \\[switch-to-buffer].
Commands that send text from source buffers to Qi processes --
like `qi-eval-defun' or `qi-show-arglist' -- have to choose a process
to send to, when you have more than one Qi process around. This
is determined by the global variable `inferior-qi-buffer'. Suppose you
have three inferior Qis running:
Buffer Process
foo inferior-qi
bar inferior-qi<2>
*inferior-qi* inferior-qi<3>
If you do a \\[qi-eval-defun] command on some Qi source code,
what process do you send it to?
- If you're in a process buffer (foo, bar, or *inferior-qi*),
you send it to that process.
- If you're in some other buffer (e.g., a source file), you
send it to the process attached to buffer `inferior-qi-buffer'.
This process selection is performed by function `inferior-qi-proc'.
Whenever \\[inferior-qi] fires up a new process, it resets
`inferior-qi-buffer' to be the new process's buffer. If you only run
one process, this does the right thing. If you run multiple
processes, you can change `inferior-qi-buffer' to another process
buffer with \\[set-variable].")
(defvar inferior-qi-mode-hook '()
"*Hook for customising Inferior Qi mode.")
(put 'inferior-qi-mode 'mode-class 'special)
(defun inferior-qi-mode ()
"Major mode for interacting with an inferior Qi process.
Runs a Qi interpreter as a subprocess of Emacs, with Qi I/O through an
Emacs buffer. Variable `inferior-qi-program' controls which Qi interpreter
is run. Variables `inferior-qi-prompt', `inferior-qi-filter-regexp' and
`inferior-qi-load-command' can customize this mode for different Qi
For information on running multiple processes in multiple buffers, see
documentation for variable `inferior-qi-buffer'.
Customisation: Entry to this mode runs the hooks on `comint-mode-hook' and
`inferior-qi-mode-hook' (in that order).
You can send text to the inferior Qi process from other buffers containing
Qi source.
switch-to-qi switches the current buffer to the Qi process buffer.
qi-eval-defun sends the current defun to the Qi process.
qi-compile-defun compiles the current defun.
qi-eval-region sends the current region to the Qi process.
qi-compile-region compiles the current region.
Prefixing the qi-eval/compile-defun/region commands with
a \\[universal-argument] causes a switch to the Qi process buffer after sending
the text.
Return after the end of the process' output sends the text from the
end of process to point.
Return before the end of the process' output copies the sexp ending at point
to the end of the process' output, and sends it.
Delete converts tabs to spaces as it moves back.
Tab indents for Qi; with argument, shifts rest
of expression rigidly with the current line.
C-M-q does Tab on each line starting within following expression.
Paragraphs are separated only by blank lines. Semicolons start comments.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
(set (make-local-variable 'font-lock-defaults) '(qi-font-lock-keywords))
(setq comint-prompt-regexp inferior-qi-prompt)
(setq major-mode 'inferior-qi-mode)
(setq mode-name "Inferior Qi")
(setq mode-line-process '(":%s"))
(use-local-map inferior-qi-mode-map) ;c-c c-k for "kompile" file
(setq comint-get-old-input (function qi-get-old-input))
(setq comint-input-filter (function qi-input-filter))
(run-hooks 'inferior-qi-mode-hook))
(defun qi-get-old-input ()
"Return a string containing the sexp ending at point."
(let ((end (point)))
(buffer-substring (point) end))))
(defun qi-input-filter (str)
"t if STR does not match `inferior-qi-filter-regexp'."
(not (string-match inferior-qi-filter-regexp str)))
(defun inferior-qi (cmd)
"Run an inferior Qi process, input and output via buffer `*inferior-qi*'.
If there is a process already running in `*inferior-qi*', just switch
to that buffer.
With argument, allows you to edit the command line (default is value
of `inferior-qi-program'). Runs the hooks from
`inferior-qi-mode-hook' (after the `comint-mode-hook' is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
(interactive (list (if current-prefix-arg
(read-string "Run qi: " inferior-qi-program)
(if (not (comint-check-proc "*inferior-qi*"))
(let ((cmdlist (split-string cmd)))
(set-buffer (apply (function make-comint)
"inferior-qi" (car cmdlist) nil (cdr cmdlist)))
(setq inferior-qi-buffer "*inferior-qi*")
(pop-to-buffer "*inferior-qi*"))
;;;###autoload (add-hook 'same-window-buffer-names "*inferior-qi*")
(defalias 'run-qi 'inferior-qi)
(defun qi-eval-region (start end &optional and-go)
"Send the current region to the inferior Qi process.
Prefix argument means switch to the Qi buffer afterwards."
(interactive "r\nP")
(comint-send-region (inferior-qi-proc) start end)
(comint-send-string (inferior-qi-proc) "\n")
(if and-go (switch-to-qi t)))
(defun qi-eval-defun (&optional and-go)
"Send the current defun to the inferior Qi process.
Prefix argument means switch to the Qi buffer afterwards."
(interactive "P")
(skip-chars-backward " \t\n\r\f") ; Makes allegro happy
(let ((end (point)))
(qi-eval-region (point) end)))
(if and-go (switch-to-qi t)))
(defun qi-eval-last-sexp (&optional and-go)
"Send the previous sexp to the inferior Qi process.
Prefix argument means switch to the Qi buffer afterwards."
(interactive "P")
(qi-eval-region (save-excursion (backward-sexp) (point)) (point) and-go))
;;; Common Qi COMPILE sux.
(defun qi-compile-region (start end &optional and-go)
"Compile the current region in the inferior Qi process.
Prefix argument means switch to the Qi buffer afterwards."
(interactive "r\nP")
(format "(funcall (compile nil `(lambda () (progn 'compile %s))))\n"
(buffer-substring start end)))
(if and-go (switch-to-qi t)))
(defun qi-compile-defun (&optional and-go)
"Compile the current defun in the inferior Qi process.
Prefix argument means switch to the Qi buffer afterwards."
(interactive "P")
(skip-chars-backward " \t\n\r\f") ; Makes allegro happy
(let ((e (point)))
(qi-compile-region (point) e)))
(if and-go (switch-to-qi t)))
(defun switch-to-qi (eob-p)
"Switch to the inferior Qi process buffer.
With argument, positions cursor at end of buffer."
(interactive "P")
(if (get-buffer-process inferior-qi-buffer)
(let ((pop-up-frames
;; Be willing to use another frame
;; that already has the window in it.
(or pop-up-frames
(get-buffer-window inferior-qi-buffer t))))
(pop-to-buffer inferior-qi-buffer))
(run-qi inferior-qi-program))
(when eob-p
(goto-char (point-max))))
;;; Now that qi-compile/eval-defun/region takes an optional prefix arg,
;;; these commands are redundant. But they are kept around for the user
;;; to bind if he wishes, for backwards functionality, and because it's
;;; easier to type C-c e than C-u C-c C-e.
(defun qi-eval-region-and-go (start end)
"Send the current region to the inferior Qi, and switch to its buffer."
(interactive "r")
(qi-eval-region start end t))
(defun qi-eval-defun-and-go ()
"Send the current defun to the inferior Qi, and switch to its buffer."
(qi-eval-defun t))
(defun qi-compile-region-and-go (start end)
"Compile the current region in the inferior Qi, and switch to its buffer."
(interactive "r")
(qi-compile-region start end t))
(defun qi-compile-defun-and-go ()
"Compile the current defun in the inferior Qi, and switch to its buffer."
(qi-compile-defun t))
;;; A version of the form in H. Shevis' soar-mode.el package. Less robust.
;;; (defun qi-compile-sexp (start end)
;;; "Compile the s-expression bounded by START and END in the inferior qi.
;;; If the sexp isn't a DEFUN form, it is evaluated instead."
;;; (cond ((looking-at "(defun\\s +")
;;; (goto-char (match-end 0))
;;; (let ((name-start (point)))
;;; (forward-sexp 1)
;;; (process-send-string "inferior-qi"
;;; (format "(compile '%s #'(lambda "
;;; (buffer-substring name-start
;;; (point)))))
;;; (let ((body-start (point)))
;;; (goto-char start) (forward-sexp 1) ; Can't use end-of-defun.
;;; (process-send-region "inferior-qi"
;;; (buffer-substring body-start (point))))
;;; (process-send-string "inferior-qi" ")\n"))
;;; (t (qi-eval-region start end)))))
;;; (defun qi-compile-region (start end)
;;; "Each s-expression in the current region is compiled (if a DEFUN)
;;; or evaluated (if not) in the inferior qi."
;;; (interactive "r")
;;; (save-excursion
;;; (goto-char start) (end-of-defun) (beginning-of-defun) ; error check
;;; (if (< (point) start) (error "region begins in middle of defun"))
;;; (goto-char start)
;;; (let ((s start))
;;; (end-of-defun)
;;; (while (<= (point) end) ; Zip through
;;; (qi-compile-sexp s (point)) ; compiling up defun-sized chunks.
;;; (setq s (point))
;;; (end-of-defun))
;;; (if (< s end) (qi-compile-sexp s end)))))
;;; End of HS-style code
(defvar qi-prev-l/c-dir/file nil
"Record last directory and file used in loading or compiling.
This holds a cons cell of the form `(DIRECTORY . FILE)'
describing the last `qi-load-file' or `qi-compile-file' command.")
(defvar qi-source-modes '(qi-mode)
"*Used to determine if a buffer contains Qi source code.
If it's loaded into a buffer that is in one of these major modes, it's
considered a Qi source file by `qi-load-file' and `qi-compile-file'.
Used by these commands to determine defaults.")
(defun qi-load-file (file-name)
"Load a Qi file into the inferior Qi process."
(interactive (comint-get-source "Load Qi file: " qi-prev-l/c-dir/file
qi-source-modes nil)) ; NIL because LOAD
; doesn't need an exact name
(comint-check-source file-name) ; Check to see if buffer needs saved.
(setq qi-prev-l/c-dir/file (cons (file-name-directory file-name)
(file-name-nondirectory file-name)))
(comint-send-string (inferior-qi-proc)
(format inferior-qi-load-command file-name))
(switch-to-qi t))
(defun qi-compile-file (file-name)
"Compile a Qi file in the inferior Qi process."
(interactive (comint-get-source "Compile Qi file: " qi-prev-l/c-dir/file
qi-source-modes nil)) ; NIL = don't need
; suffix .qi
(comint-check-source file-name) ; Check to see if buffer needs saved.
(setq qi-prev-l/c-dir/file (cons (file-name-directory file-name)
(file-name-nondirectory file-name)))
(comint-send-string (inferior-qi-proc) (concat "(compile-file \""
(switch-to-qi t))
;;; Documentation functions: function doc, var doc, arglist, and
;;; describe symbol.
;;; ===========================================================================
;;; Command strings
;;; ===============
(defvar qi-function-doc-command
"(let ((fn '%s))
(format t \"Documentation for ~a:~&~a\"
fn (documentation fn 'function))
"Command to query inferior Qi for a function's documentation.")
(defvar qi-var-doc-command
"(let ((v '%s))
(format t \"Documentation for ~a:~&~a\"
v (documentation v 'variable))
"Command to query inferior Qi for a variable's documentation.")
(defvar qi-arglist-command
"(let ((fn '%s))
(format t \"Arglist for ~a: ~a\" fn (arglist fn))
"Command to query inferior Qi for a function's arglist.")
(defvar qi-describe-sym-command
"(describe '%s)\n"
"Command to query inferior Qi for a variable's documentation.")
;;; Ancillary functions
;;; ===================
;;; Reads a string from the user.
(defun qi-symprompt (prompt default)
(list (let* ((prompt (if default
(format "%s (default %s): " prompt default)
(concat prompt ": ")))
(ans (read-string prompt)))
(if (zerop (length ans)) default ans))))