Compare commits

...

7 Commits

Author SHA1 Message Date
TEC 9dbd13f4d2
Switch from ispell to jinx 2024-03-22 14:16:31 +08:00
TEC 8740abb117
Custom analogue clock was upstreamed
It's now part of doom-modeline 🥳
2024-03-22 14:16:31 +08:00
TEC ae995ad221
I no longer use IRC at all really 2024-03-21 23:56:47 +08:00
TEC d2100c3b3f
Highlight "julia>" in julia-mode
It's good when using julia-mode for Julia repl examples.
2024-03-21 23:48:53 +08:00
TEC 58562718ee
Note ambition to have upright LaTeX parentheses 2024-03-21 23:48:29 +08:00
TEC aa91fbf545
LaTeX link style hiding in toc is no longer needed
Unfortunately, I forget why/the details.
2024-03-21 23:48:00 +08:00
TEC 8020bd1052
Hide the mode name in the dashboard 2024-03-21 23:46:50 +08:00
1 changed files with 76 additions and 431 deletions

View File

@ -1631,7 +1631,7 @@ vterm ; the best terminal emulation in Emacs
#+name: doom-checkers
#+begin_src emacs-lisp
syntax ; tasing you for every semicolon you forget
(:if (executable-find "aspell") spell) ; tasing you for misspelling mispelling
;; spell ; tasing you for misspelling mispelling
grammar ; tasing grammar mistake every you make
#+end_src
@ -2939,6 +2939,7 @@ dashboard.
(with-current-buffer (get-buffer +doom-dashboard-name)
(setq-local line-spacing 0.2
mode-line-format nil
mode-name ""
evil-normal-state-cursor (list nil))))
#+end_src
@ -3986,7 +3987,47 @@ add package src directories to projectile. This isn't desirable in my opinion.
(or (mapcar (lambda (p) (s-starts-with-p p filepath)) projectile-ignored-projects)))
#+end_src
*** Ispell
*** Jinx
#+call: confpkg()
Minad's Jinx spell-checker looks pretty nifty. When Henrik and I (or someone
else) have some more bandwidth, I think it would be good to incorporate with
Doom.
In the meantime, let's use it here.
#+begin_src emacs-lisp :tangle packages.el :noweb-ref none
(package! jinx)
#+end_src
**** Configuration
Jinx has some pretty lovely defaults out of the box, we'll just be making a few
tweaks.
#+begin_src emacs-lisp
(use-package! jinx
:defer t
:init
(add-hook 'doom-init-ui-hook #'global-jinx-mode)
:config
;; Use my custom dictionary
(setq jinx-languages "en-custom")
;; Extra face(s) to ignore
(push 'org-inline-src-block
(alist-get 'org-mode jinx-exclude-faces))
;; Take over the relevant bindings.
(after! ispell
(global-set-key [remap ispell-word] #'jinx-correct))
(after! evil-commands
(global-set-key [remap evil-next-flyspell-error] #'jinx-next)
(global-set-key [remap evil-prev-flyspell-error] #'jinx-previous))
;; I prefer for `point' to end up at the start of the word,
;; not just after the end.
(advice-add 'jinx-next :after (lambda (_) (left-word))))
#+end_src
**** Downloading dictionaries
Let's get a nice big dictionary from [[http://app.aspell.net/create][SCOWL Custom List/Dictionary Creator]] with
@ -4005,15 +4046,20 @@ curl -o "hunspell-en-custom.zip" 'http://app.aspell.net/create?max_size=80&spell
unzip "hunspell-en-custom.zip"
sudo chown root:root en-custom.*
sudo mv en-custom.{aff,dic} /usr/share/myspell/
DESTDIR1="$HOME/.local/share/hunspell"
DESTDIR2="$HOME/.config/enchant/hunspell"
mkdir -p "$DESTDIR1"
mkdir -p "$DESTDIR2"
sudo mv en-custom.{aff,dic} "$DESTDIR1"
sudo mv en-custom.{aff,dic} "$DESTDIR2"
#+end_src
We will also add an acompanying =doctor= warning.
We will also add an accompanying =doctor= warning.
#+begin_src emacs-lisp :noweb-ref doctor
(unless (executable-find "hunspell")
(warn! "Couldn't find hunspell executable."))
(unless (file-exists-p "/usr/share/myspell/en-custom.dic")
(unless (file-exists-p "~/.local/share/hunspell/en-custom.dic")
(warn! "Custom hunspell dictionary is not present."))
#+end_src
@ -4025,39 +4071,21 @@ curl -o "aspell6-en-custom.tar.bz2" 'http://app.aspell.net/create?max_size=80&sp
tar -xjf "aspell6-en-custom.tar.bz2"
cd aspell6-en-custom
./configure && make && sudo make install
DESTDIR="$HOME/.config/enchant/" ./configure
sed -i 's/dictdir = .*/dictdir = "aspell"/' Makefile
sed -i 's/datadir = .*/datadir = "aspell"/' Makefile
make && make install
#+end_src
We will also add an acompanying =doctor= warning.
We will also add an accompanying =doctor= warning.
#+begin_src emacs-lisp :noweb-ref doctor
(unless (executable-find "ispell")
(warn! "Couldn't find ispell executable."))
(unless (file-expand-wildcards "/usr/lib64/aspell*/en-custom.multi")
(unless (executable-find "aspell")
(warn! "Couldn't find aspell executable."))
(unless (file-exists-p "~/.config/enchant/aspell/en-custom.multi")
(warn! "Custom aspell dictionary is not present."))
#+end_src
**** Configuration
#+call: confpkg("!Pkg Ispell")
#+begin_src emacs-lisp
(setq ispell-dictionary "en-custom")
#+end_src
Oh, and by the way, if ~company-ispell-dictionary~ is ~nil~, then
~ispell-complete-word-dict~ is used instead, which once again when ~nil~ is
~ispell-alternate-dictionary~, which at the moment maps to a plaintext version of
the above.
It seems reasonable to want to keep an eye on my personal dict, let's have it
nearby (also means that if I change the 'main' dictionary I keep my addition).
#+begin_src emacs-lisp
(setq ispell-personal-dictionary
(expand-file-name "misc/ispell_personal" doom-private-dir))
#+end_src
*** TRAMP
#+call: confpkg("TRAMP")
@ -4400,111 +4428,13 @@ hide it?
(add-hook 'after-change-major-mode-hook #'doom-modeline-conditional-buffer-encoding)
#+end_src
**** Time
**** Analogue clock
Moving onto the modeline segments, there's a calendar icon showed next to the
current time, which I'm not a fan of. Let's replace that by redefining the
segment. Instead of the calendar, it would be much nicer to have an updating
analog clock, so let's use that idea as an excuse to try out the Emacs svg library.
First, we'll need a way to produce svg clocks on-demand.
Now that my code for an analogue clock icon has been upstreamed, all I do here
is adjust the size slightly 🙂.
#+begin_src emacs-lisp
(defvar micro-clock-hour-hand-ratio 0.45
"Length of the hour hand as a proportion of the radius.")
(defvar micro-clock-minute-hand-ratio 0.7
"Length of the minute hand as a proportion of the radius.")
(defun micro-clock-svg (hour minute radius color)
"Construct an SVG clock showing the time HOUR:MINUTE.
The clock will be of the specified RADIUS and COLOR."
(let ((hour-x (* radius (sin (* (- 6 hour (/ minute 60.0)) (/ float-pi 6)))
micro-clock-hour-hand-ratio))
(hour-y (* radius (cos (* (- 6 hour (/ minute 60.0)) (/ float-pi 6)))
micro-clock-hour-hand-ratio))
(minute-x (* radius (sin (* (- 30 minute) (/ float-pi 30)))
micro-clock-minute-hand-ratio))
(minute-y (* radius (cos (* (- 30 minute) (/ float-pi 30)))
micro-clock-minute-hand-ratio))
(svg (svg-create (* 2 radius) (* 2 radius) :stroke color)))
(svg-circle svg radius radius (1- radius) :fill "none" :stroke-width 2)
(svg-circle svg radius radius 1 :fill color :stroke "none")
(svg-line svg radius radius (+ radius hour-x) (+ radius hour-y)
:stroke-width 2)
(svg-line svg radius radius (+ radius minute-x) (+ radius minute-y)
:stroke-width 1.5)
svg))
#+end_src
With that out the way, we need to figure out how to integrate this into the
modeline. For the most part this was fairly easy, the tricky part was getting
the alignment right. I tried using the =(raise FACTOR)= display property, but that
doesn't appear to combine with images. Looking at the documentation on [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Image-Descriptors.html][image
descriptors]] though, I came across the =:ascent= property, which looked rather
promising. By setting =:ascent center= the image is centred relative to the text,
which is exactly what we need. With that sorted, we just add some caching and
the obvious customisations and we've got modeline-appropriate clock generation!
#+begin_src emacs-lisp
(require 'svg)
(defvar +doom-modeline-micro-clock-minute-resolution 1
"The clock will be updated every this many minutes, truncating.")
(defvar +doom-modeline-micro-clock-inverse-size 4.8
"The size of the clock, as an inverse proportion to the mode line height.")
(defvar +doom-modeline-micro-clock--cache nil)
(defvar +doom-modeline-clock-text-format "%c")
(defun +doom-modeline--clock-text (&optional _window _object _pos)
(format-time-string +doom-modeline-clock-text-format))
(defun +doom-modeline-micro-clock ()
"Return a string containing an current analogue clock."
(cdr
(if (equal (truncate (float-time)
(* +doom-modeline-micro-clock-minute-resolution 60))
(car +doom-modeline-micro-clock--cache))
+doom-modeline-micro-clock--cache
(setq +doom-modeline-micro-clock--cache
(cons (truncate (float-time)
(* +doom-modeline-micro-clock-minute-resolution 60))
(with-temp-buffer
(svg-insert-image
(micro-clock-svg
(string-to-number (format-time-string "%-I")) ; hour
(* (truncate (string-to-number (format-time-string "%-M"))
+doom-modeline-micro-clock-minute-resolution)
+doom-modeline-micro-clock-minute-resolution) ; minute
(/ doom-modeline-height +doom-modeline-micro-clock-inverse-size) ; radius
"currentColor"))
(propertize
" "
'display
(append (get-text-property 0 'display (buffer-string))
'(:ascent center))
'face 'doom-modeline-time
'help-echo #'+doom-modeline--clock-text)))))))
#+end_src
With clock generation sorted out, all that's to be done is replacing the =time=
modeline segment with our design.
#+begin_src emacs-lisp
(doom-modeline-def-segment time
(when (and doom-modeline-time
(bound-and-true-p display-time-mode)
(not doom-modeline--limited-width-p))
(concat
(doom-modeline-spc)
(when doom-modeline-time-icon
(concat
(+doom-modeline-micro-clock)
(and (or doom-modeline-icon doom-modeline-unicode-fallback)
(doom-modeline-spc))))
(propertize display-time-string
'face (doom-modeline-face 'doom-modeline-time)))))
(setq doom-modeline-time-clock-size 0.65)
#+end_src
**** Media player
@ -5808,301 +5738,6 @@ panel.
(select-window main-window))))
#+end_src
** IRC
#+call: confpkg()
=circe= is a client for IRC in Emacs (hey, isn't that a nice project
name+acronym), and a greek enchantress who turned humans into animals.
Let's use the former to chat to +recluses+ discerning individuals online.
[[xkcd:1782]]
Before we start seeing and sending messages, we need to authenticate with our
IRC servers. The circe manual provided a snippet for putting some of the auth
details in =.authinfo.gpg= --- but I think we should go further than that: have
the entire server info in our authinfo.
First, a reasonable format by which we can specify:
+ server
+ port
+ SASL username
+ SASL password
+ channels to join
We can have these stored like so
#+begin_src authinfo
machine chat.freenode.net login USERNAME password PASSWORD port PORT for irc channels emacs,org-mode
#+end_src
The ~for irc~ bit is used so we can uniquely identify all IRC auth info. By
omitting the =#= in channel names we can have a list of channels comma-separated
(no space!) which the secrets API will return as a single string.
#+name: irc-authinfo-reader
#+begin_src emacs-lisp :tangle no
(defun auth-server-pass (server)
(if-let ((secret (plist-get (car (auth-source-search :host server)) :secret)))
(if (functionp secret)
(funcall secret) secret)
(error "Could not fetch password for host %s" server)))
(defun register-irc-auths ()
(require 'circe)
(require 'dash)
(let ((accounts (-filter (lambda (a) (string= "irc" (plist-get a :for)))
(auth-source-search :require '(:for) :max 10))))
(appendq! circe-network-options
(mapcar (lambda (entry)
(let* ((host (plist-get entry :host))
(label (or (plist-get entry :label) host))
(ports (mapcar #'string-to-number
(s-split "," (plist-get entry :port))))
(port (if (= 1 (length ports)) (car ports) ports))
(user (plist-get entry :user))
(nick (or (plist-get entry :nick) user))
(channels (mapcar (lambda (c) (concat "#" c))
(s-split "," (plist-get entry :channels)))))
`(,label
:host ,host :port ,port :nick ,nick
:sasl-username ,user :sasl-password auth-server-pass
:channels ,channels)))
accounts))))
#+end_src
We'll just call src_elisp{(register-irc-auths)} on a hook when we start Circe
up.
Now we're ready to go, let's actually wire-up Circe, with one or two
configuration tweaks.
#+begin_src emacs-lisp :noweb no-export
(after! circe
(setq-default circe-use-tls t)
(setq circe-notifications-alert-icon "/usr/share/icons/breeze/actions/24/network-connect.svg"
lui-logging-directory (expand-file-name "irc" doom-etc-dir)
lui-logging-file-format "{buffer}/%Y/%m-%d.txt"
circe-format-self-say "{nick:+13s} ┃ {body}")
(custom-set-faces!
'(circe-my-message-face :weight unspecified))
(enable-lui-logging-globally)
(enable-circe-display-images)
<<org-emph-to-irc>>
<<circe-emojis>>
<<circe-emoji-alists>>
(defun named-circe-prompt ()
(lui-set-prompt
(concat (propertize (format "%13s > " (circe-nick))
'face 'circe-prompt-face)
"")))
(add-hook 'circe-chat-mode-hook #'named-circe-prompt)
(appendq! nerd-icons-mode-icon-alist
'((circe-channel-mode nerd-icons-mdicon "nf-md-message" :face nerd-icons-lblue)
(circe-server-mode nerd-icons-mdicon "nf-md-chat_outline" :face nerd-icons-purple))))
<<irc-authinfo-reader>>
(add-transient-hook! #'=irc (register-irc-auths))
#+end_src
*** Org-style emphasis
Let's do our *bold*, /italic/, and _underline_ in org-syntax, using IRC control characters.
#+name: org-emph-to-irc
#+begin_src emacs-lisp
(defun lui-org-to-irc ()
"Examine a buffer with simple org-mode formatting, and converts the empasis:
,*bold*, /italic/, and _underline_ to IRC semi-standard escape codes.
=code= is converted to inverse (highlighted) text."
(goto-char (point-min))
(while (re-search-forward "\\_<\\(?1:[*/_=]\\)\\(?2:[^[:space:]]\\(?:.*?[^[:space:]]\\)?\\)\\1\\_>" nil t)
(replace-match
(concat (pcase (match-string 1)
("*" "")
("/" "")
("_" "")
("=" ""))
(match-string 2)
"") nil nil)))
(add-hook 'lui-pre-input-hook #'lui-org-to-irc)
#+end_src
*** Emojis
Let's setup Circe to use some emojis
#+name: circe-emojis
#+begin_src emacs-lisp :tangle no
(defun lui-ascii-to-emoji ()
(goto-char (point-min))
(while (re-search-forward "\\( \\)?::?\\([^[:space:]:]+\\):\\( \\)?" nil t)
(replace-match
(concat
(match-string 1)
(or (cdr (assoc (match-string 2) lui-emojis-alist))
(concat ":" (match-string 2) ":"))
(match-string 3))
nil nil)))
(defun lui-emoticon-to-emoji ()
(dolist (emoticon lui-emoticons-alist)
(goto-char (point-min))
(while (re-search-forward (concat " " (car emoticon) "\\( \\)?") nil t)
(replace-match (concat " "
(cdr (assoc (cdr emoticon) lui-emojis-alist))
(match-string 1))))))
(define-minor-mode lui-emojify
"Replace :emojis: and ;) emoticons with unicode emoji chars."
:global t
:init-value t
(if lui-emojify
(add-hook! lui-pre-input #'lui-ascii-to-emoji #'lui-emoticon-to-emoji)
(remove-hook! lui-pre-input #'lui-ascii-to-emoji #'lui-emoticon-to-emoji)))
#+end_src
Now, some actual emojis to use.
#+name: circe-emoji-alists
#+begin_src emacs-lisp :tangle no
(defvar lui-emojis-alist
'(("grinning" . "😀")
("smiley" . "😃")
("smile" . "😄")
("grin" . "😁")
("laughing" . "😆")
("sweat_smile" . "😅")
("joy" . "😂")
("rofl" . "🤣")
("relaxed" . "☺️")
("blush" . "😊")
("innocent" . "😇")
("slight_smile" . "🙂")
("upside_down" . "🙃")
("wink" . "😉")
("relieved" . "😌")
("heart_eyes" . "😍")
("yum" . "😋")
("stuck_out_tongue" . "😛")
("stuck_out_tongue_closed_eyes" . "😝")
("stuck_out_tongue_wink" . "😜")
("zanzy" . "🤪")
("raised_eyebrow" . "🤨")
("monocle" . "🧐")
("nerd" . "🤓")
("cool" . "😎")
("star_struck" . "🤩")
("party" . "🥳")
("smirk" . "😏")
("unamused" . "😒")
("disapointed" . "😞")
("pensive" . "😔")
("worried" . "😟")
("confused" . "😕")
("slight_frown" . "🙁")
("frown" . "☹️")
("persevere" . "😣")
("confounded" . "😖")
("tired" . "😫")
("weary" . "😩")
("pleading" . "🥺")
("tear" . "😢")
("cry" . "😢")
("sob" . "😭")
("triumph" . "😤")
("angry" . "😠")
("rage" . "😡")
("exploding_head" . "🤯")
("flushed" . "😳")
("hot" . "🥵")
("cold" . "🥶")
("scream" . "😱")
("fearful" . "😨")
("disapointed" . "😰")
("relieved" . "😥")
("sweat" . "😓")
("thinking" . "🤔")
("shush" . "🤫")
("liar" . "🤥")
("blank_face" . "😶")
("neutral" . "😐")
("expressionless" . "😑")
("grimace" . "😬")
("rolling_eyes" . "🙄")
("hushed" . "😯")
("frowning" . "😦")
("anguished" . "😧")
("wow" . "😮")
("astonished" . "😲")
("sleeping" . "😴")
("drooling" . "🤤")
("sleepy" . "😪")
("dizzy" . "😵")
("zipper_mouth" . "🤐")
("woozy" . "🥴")
("sick" . "🤢")
("vomiting" . "🤮")
("sneeze" . "🤧")
("mask" . "😷")
("bandaged_head" . "🤕")
("money_face" . "🤑")
("cowboy" . "🤠")
("imp" . "😈")
("ghost" . "👻")
("alien" . "👽")
("robot" . "🤖")
("clap" . "👏")
("thumpup" . "👍")
("+1" . "👍")
("thumbdown" . "👎")
("-1" . "👎")
("ok" . "👌")
("pinch" . "🤏")
("left" . "👈")
("right" . "👉")
("down" . "👇")
("wave" . "👋")
("pray" . "🙏")
("eyes" . "👀")
("brain" . "🧠")
("facepalm" . "🤦")
("tada" . "🎉")
("fire" . "🔥")
("flying_money" . "💸")
("lighbulb" . "💡")
("heart" . "❤️")
("sparkling_heart" . "💖")
("heartbreak" . "💔")
("100" . "💯")))
(defvar lui-emoticons-alist
'((":)" . "slight_smile")
(";)" . "wink")
(":D" . "smile")
("=D" . "grin")
("xD" . "laughing")
(";(" . "joy")
(":P" . "stuck_out_tongue")
(";D" . "stuck_out_tongue_wink")
("xP" . "stuck_out_tongue_closed_eyes")
(":(" . "slight_frown")
(";(" . "cry")
(";'(" . "sob")
(">:(" . "angry")
(">>:(" . "rage")
(":o" . "wow")
(":O" . "astonished")
(":/" . "confused")
(":-/" . "thinking")
(":|" . "neutral")
(":-|" . "expressionless")))
#+end_src
** Newsfeed
#+call: confpkg()
@ -11246,9 +10881,6 @@ The =hyperref= setup needs to be handled separately however.
citecolor=cite
}
\urlstyle{same}
%% hide links styles in toc
\NewCommandCopy{\oldtoc}{\tableofcontents}
\renewcommand{\tableofcontents}{\begingroup\hypersetup{hidelinks}\oldtoc\endgroup}
#+end_src
**** A cleverer preamble
@ -12261,6 +11893,10 @@ Then we can just hook this in with our clever preamble.
:order 0.7))
#+end_src
**** Upright parentheses in italic text
TODO, see https://tex.stackexchange.com/a/13057/167605
**** Pretty code blocks
We could just use minted for syntax highlighting --- however, we can do better!
@ -13896,6 +13532,15 @@ Lastly, whenever Emacs is non-graphical (i.e. a TUI), we want to use this by def
#+call: confpkg(after="julia-mode")
It would be nice if =julia-mode= also highlighted the =julia>= prompt when writing
REPL examples.
#+begin_src emacs-lisp
(add-to-list
'julia-font-lock-keywords
'("^julia>" 0 '(font-lock-string-face bold) prepend))
#+end_src
As mentioned in [[https://github.com/non-Jedi/lsp-julia/issues/35][lsp-julia#35]], =lsp-mode= seems to serve an invalid response to the
Julia server. The pseudo-fix is rather simple at least
#+begin_src emacs-lisp