diff options
| author | Magdalena Szweda <hi@roughlynamed.net> | 2026-09-27 12:45:33 +0200 |
|---|---|---|
| committer | Magdalena Szweda <hi@roughlynamed.net> | 2026-09-27 12:45:33 +0200 |
| commit | 30a274f48e1f0746a2aaff34eed09df80a47ec1f (patch) | |
| tree | 306124848ec84a428e9ba10881b191135b2ffa2c /dots.org | |
| parent | 568475983d15f81dff7b1d70276c77b1b994ce6e (diff) | |
backup
Diffstat (limited to 'dots.org')
| -rw-r--r-- | dots.org | 1061 |
1 files changed, 1061 insertions, 0 deletions
diff --git a/dots.org b/dots.org new file mode 100644 index 0000000..2fcd355 --- /dev/null +++ b/dots.org @@ -0,0 +1,1061 @@ +#+title: Dots +My literate system configuration +* TODO Foreword +* Emacs +** UI +~How Emacs looks!~ +*** Theme +Load the directory with additional, local themes +This currently mostly loads the emacs port of the Evergarden theme (made by me!) +at [[https://codeberg.org/roughlynamed/emacs-evergarden][roughlynamed/emacs-evergarden]], also the fortune-gold theme. +The reason i use modus-vivendi instead of my evergarden port is because i dont like evergarden (dont tell the devs though!), seeing it not have an emacs port just made me sad. +I also use tango from time-to-time, thats the comment. +#+begin_src emacs-lisp +(add-to-list 'custom-theme-load-path + "~/.config/doom/extra-themes/") +(setq doom-theme 'modus-vivendi) +;; (setq doom-theme 'tango) +#+end_src + +By default, Emacs prompts 2 times when loading a theme, this is annoying so im getting rid of it. +#+begin_src emacs-lisp + (setq custom-safe-themes t) +#+end_src +*** Font +I use the completely not pirated ~Berkeley Mono~ font. I really like it. +Sometimes i will use the Commit mono font (sometimes!) +#+begin_src emacs-lisp + (setq doom-font (font-spec :family "Berkeley Mono" :size 12.0)) +#+end_src + +Line numbers! And not relative! +I dont like rln, i just dont +#+begin_src emacs-lisp + (setq display-line-numbers-type t) +#+end_src +*** Modeline + +mood-line is a nice & simple modeline for emacs! +#+begin_src emacs-lisp + (use-package! mood-line + :config + (mood-line-mode) + :custom + (mood-line-glyph-alist mood-line-glyphs-fira-code)) +#+end_src + +*** Custom window management +I didnt like how in emacs, split windows under ~SPC w s~ just stack, so i made this function to make it dynamic in a fibonnaci-like layout. +#+begin_src emacs-lisp + (defun my/split-window-dynamically () + "Split the selected window, choosing orientation by its shape." + (interactive) + (select-window + (if (> (window-total-width) (* 2 (window-total-height))) + (split-window-right) + (split-window-below)))) +#+end_src + +Map the function to override ~SPC w s~ +#+begin_src emacs-lisp + (map! :leader "w s" #'my/split-window-dynamically) +#+end_src + +*** UX ~ UI +Scroll margin - when the cursor reaches 3 lines above/below the window bottom/top, the window goes down (this happens in helix automatically, but the margin is 5) +#+begin_src emacs-lisp + (setq scroll-margin 3) +#+end_src + +*** Breadcrumb +Breadcrumb is a package that shows you where you are in a file at the very top of the window. Editing this section of this file, the breadcrumb shows: +~stow/d/.config/doom/config.org : UI~, +this is because we are in the UI section of the file. +#+begin_src emacs-lisp + (use-package! breadcrumb + :defer t + :hook + (prog-mode . breadcrumb-mode) ;; make sure its in programming modes + :config + (breadcrumb-mode 1)) +#+end_src + +*** Zen +the ~zen~ option in Doom Emacs enables ~writeroom~ mode on toggle. +~writeroom~ centers the buffer, makes it bigger and lets you type away without distractions. +By default, entering ~writeroom~ mode (which i will continue to call ~zen~ mode) changes the typeface in the buffer. +I don't like that, especially when im editing source blocks (e.g here). Let's turn it off. +#+begin_src emacs-lisp + (setq +zen-mixed-pitch-modes '()) +#+end_src +** Languages & LSP +~M-x vscode~ +*** Haskell +A custom function that runs the current Haskell file with runghc (interpreter instead of a compiler). +#+begin_src emacs-lisp + (defun my/hs-eval () + (interactive) + (let* ((src (buffer-file-name)) + (out (file-name-sans-extension src))) + (when (zerop (call-process-shell-command + (format "runghc %s" src) nil "*runghc-output*")) + (let ((buf (make-comint "hs-run" out))) + (display-buffer buf))))) +#+end_src + +Hook the function up up to f5 +#+begin_src emacs-lisp + (add-hook 'haskell-mode-hook + (lambda () + (local-set-key (kbd "<f5>") #'my/hs-eval))) +#+end_src + +*** Eglot +Eglot is, well, LSP for Emacs! +Here i hook eglot-ensure into prog-mode. +eglot-ensure makes sure that eglot is enabled and working when opening a prog-mode buffer now. +#+begin_src emacs-lisp + (use-package! eglot + :hook + (prog-mode . eglot-ensure)) +#+end_src + +I copied this block from somewhere and have no idea what it does. +#+begin_src emacs-lisp + (after! eglot + (cl-defmethod eglot-register-capability + (server (method (eql workspace/didChangeWatchedFiles)) id &key watchers) + nil) +#+end_src + + +Here we add a custom LSP for typescript and javascript mode. +Oxlint is a rewrite of ESLint in Rust, Not sure why oxlsp isnt its own tool, but oh well. +#+begin_src emacs-lisp + (add-to-list 'eglot-server-programs + '((typescript-ts-mode javascript-mode) + . ("oxlint" "--lsp")))) +#+end_src + +~eglot-sync-connect~ makes it so that eglot has to catch up and connect before a buffer can be worked on, thats why i set it to nil. +~eglot-connect-timeout~ just stops trying to connect to eglot after a minute. +#+begin_src emacs-lisp + (setq eglot-sync-connect nil + eglot-connect-timeout 60) +#+end_src + +*** Flycheck +Flycheck is a linter engine for Emacs. + +Here i define a new checker for flycheck, htmlhint (which is the linter i most enjoy using). +Then i hook it into ~web-mode~ +#+begin_src emacs-lisp + (after! flycheck + (flycheck-define-checker htmlhint + "htmlhint for flycheck" + :command ("htmlhint" "--format" "unix" source) + :error-patterns + ((error + line-start + (file-name) ":" line ":" column ": " + (message) line-end)) + :modes (web-mode)) +#+end_src + +I then add the new checker to the flycheck checkers (say that 3 times fast) +#+begin_src emacs-lisp + (add-to-list 'flycheck-checkers 'htmlhint) +#+end_src + +Here we add additional checkers to flycheck. +We add ESLint and oxlint, and then another block for html-tidy and the new htmlhint. +The reason this cant be all in one ~flycheck-add-next-checker~ block is because ~flyckech-add-next-checker~ only takes 2 variables. +#+begin_src emacs-lisp + (flycheck-add-next-checker + 'javascript-eslint + 'javascript-oxlint) +(flycheck-add-next-checker + 'html-tidy + 'htmlhint) +#+end_src + +We then make flycheck check the file after saving. +#+begin_src emacs-lisp + (setq flycheck-check-syntax-automatically + '(save mode-enabled))) +#+end_src + +*** Apheleia + +Apheleia is an easy way to format your files in Emacs. +First we hook apheleia into prog-mode. +#+begin_src emacs-lisp +(use-package! apheleia + :hook (prog-mode . apheleia-mode) +#+end_src + +We can then add a new apheleia formatter, in this case oxfmt. +(why is oxfmt its own tool but oxlsp isnt? why cant it just be ~oxlint --lsp~ and ~oxlint --format~, this is dumb) +#+begin_src emacs-lisp + :config + (setf (alist-get 'oxfmt apheleia-formatters) + '("oxfmt" "--config=.oxfmtrc.json" "--stdin-filepath" filepath)) +#+end_src + +Here we hook the new oxfmt formatter into: ++ html-mode ++ mhtml-mode ++ web-mode ++ css-mode +#+begin_src emacs-lisp + (dolist (mode '(html-mode mhtml-mode web-mode css-mode)) + (setf (alist-get mode apheleia-mode-alist) 'oxfmt))) +#+end_src + +** Email +~Emacs - prover of Zawinki's law~ +*** Auth +To send mail, we need to log into everything. +Instead of typing out passwords every time, we use the ~~.authinfo.gpg~ file (can also be without the gpg encryption). +To read the file: +#+begin_src emacs-lisp + (setq auth-sources '("~/.authinfo.gpg")) +#+end_src +Emacs automatically decrypts the file with ~epa~ and ~epg~. +To make this not expire, we set +#+begin_src emacs-lisp + (setq auth-source-cache-expiry nil) +#+end_src + +*** Gnus +Gnus is Emacs's built in Email reader, sender, news viewer and RSS reader. +But right now we're setting up the Email part. + +This makes it so that all subscribed groups are visible, no matter if theres no new messages. +#+begin_src emacs-lisp + (setq gnus-permanently-visible-groups ".") +#+end_src + +When browsing gnus, make the mode in evil standard emacs. +#+begin_src emacs-lisp + (evil-set-initial-state 'gnus-browse-mode 'emacs) +#+end_src + +Now we configure our user of gnus. +#+begin_src emacs-lisp + (setq user-mail-address "hi@roughlynamed.net" ; my email + user-full-name "Magdalena Szweda") ; my name +#+end_src + +While we're at it, lets point Emacs at my LibrePGP key. +#+begin_src emacs-lisp + (setq mml-secure-openpgp-signers + '("3258B28C124AB0891D6C9E929B72909EEF00CBD0") + mml-secure-openpgp-sign-with-sender t) +#+end_src + +We can also set up the signature via the ~.signature~ file instead of encoding it raw into the config. +#+begin_src emacs-lisp + (setq message-signature-file "~/.signature") ; note no trailing slash +#+end_src + +Now we can set up the actual mail part. +I use migadu for my email hosting, provided to me by ~@bas.sh~ +Here we set up IMAP for receiving mail. +#+begin_src emacs-lisp + (setq gnus-select-method + '(nnimap "migadu" + (nnimap-address "imap.migadu.com") + (nnimap-server-port 993) + (nnimap-stream ssl))) +#+end_src + +Once that's done, we can configure the sending (smtp) part. +#+begin_src emacs-lisp + (setq smtpmail-smtp-server "smtp.migadu.com" + smtpmail-smtp-service 465 ; port 465 + smtpmail-stream-type 'ssl + message-send-mail-function #'smtpmail-send-it + ) +#+end_src + +After that, a little bit more config for UX +#+begin_src emacs-lisp + (setq gnus-message-archive-method '(nnimap "migadu") ; use migadu for archiving email + gnus-message-archive-group "Sent") ; where to put sent mail + +(setq gnus-large-newsgroup nil + gnus-fetch-old-headers t + nnmail-expiry-target "Trash") +#+end_src + +By default, Gnus puts entries newest last, let's reverse it. +#+begin_src emacs-lisp +(setq gnus-thread-sort-functions + '((not gnus-thread-sort-by-date))) + +(setq gnus-subthread-sort-functions + '((not gnus-thread-sort-by-date))) +(setq gnus-thread-sort-most-recent-first t) +#+end_src + +What Gnus also does by default is ask to read the .newsrc file, this file is basically an auto-save, 90% of the time you want to read it. +Let's just disable the prompt on every Gnus startup. +#+begin_src emacs-lisp + (setq gnus-always-read-dribble-file nil) +#+end_src + +I like to sign my message with pgp as a way to confirm that its really me sending the email. +We can do this with the following: +#+begin_src emacs-lisp + (add-hook 'message-send-hook #'mml-secure-message-sign-pgpmime) +#+end_src +We use pgpmime to put the signature in the MIME header instead of inline PGP blocks. + +I'm not a fan of html in my emails as i find it rendering bad and being hard to navigate, so i set it to discourage html and prefer plain text emails. +#+begin_src emacs-lisp + (with-eval-after-load "mm-decode" + (add-to-list 'mm-discouraged-alternatives "text/html") + (add-to-list 'mm-discouraged-alternatives "text/richtext")) +#+end_src +** Jabber +~Jabber? I hardly know her!~ + +Excuse the bad pun, jabber.el is a way to use XMPP in Emacs. +#+begin_src emacs-lisp + (use-package! jabber + :config +#+end_src +We point jabber at my XMPP account: +#+begin_src emacs-lisp + (setq jabber-account-list + '(("roughlynamed@yax.im"))) +#+end_src +And let jabber use the modeline +#+begin_src emacs-lisp + (jabber-modeline-mode 1) +#+end_src +Now, we can hook jabber to kill itself when emacs dies. +#+begin_src emacs-lisp + :hook + (kill-emacs . jabber-disconnect)) +#+end_src +As to not rely on ~M-x~ commands, define a binding as so +#+begin_src emacs-lisp + (map! :leader + :prefix "j" + :desc "Connect" "c" #'jabber-connect-all + :desc "Disconnect" "q" #'jabber-disconnect + :desc "Dash" "f" #'jabber-roster + :desc "Reply" "r" #'jabber-chat-reply + :desc "New chat" "n" #'jabber-chat-with + :desc "Set status" "s" #'jabber-send-presence) +#+end_src + +** EMMS +~Music to my ears~ +EMMS, the Emacs MultiMedia System (but mostly its just used for music). + +Now, emms has this issue where mpd and the emms cache fall out of sync. This is a documented issue so lets make them catch up by default. +#+BEGIN_SRC emacs-lisp + (defun emms-full-resync () + "Update MPD's db, then pull it into EMMS's cache, then rebuild the browser." + (interactive) + (emms-player-mpd-update-all) + (run-with-timer 2 nil + (lambda () + (emms-cache-set-from-mpd-all) + (when (get-buffer "Browsing by: artist") + (with-current-buffer "Browsing by: artist" (revert-buffer))))) + (emms-browser)) +#+END_SRC + +Declare the package block and set up the commands. +~:commands~ in elisp is used to eagerly load functions even when a package is lazy loaded. +The following makes the commands ~emms~ and ~emms-browser~ available even before emms itself is loaded. +#+begin_src emacs-lisp + (use-package! emms + :commands (emms emms-browser emms-player-mpd-update-all emms-cache-set-from-mpd-all) +#+end_src + +Configure the mappings for EMMS +~:init~ in elisp runs the following code =before= a package is loaded. +In this case, it makes the emms mappings available right when emacs starts up. +#+begin_src emacs-lisp + :init + (map! :leader + (:prefix ("e" . "emms") + :desc "Browser" "b" #'emms-full-resync + :desc "Playlist" "m" #'emms-playlist-mode-go + :desc "Play/pause" "SPC" #'emms-pause + :desc "Next" "n" #'emms-next + :desc "Previous" "p" #'emms-previous + :desc "Stop" "s" #'emms-stop + :desc "Clear" "c" #'emms-playlist-clear)) +#+end_src + +Here we tell EMMS to use the preset configuration loader, this makes it so that i dont have to wire up a bunch of emms submodules myself. +#+begin_src emacs-lisp + :config +(require 'emms-setup) +#+end_src + +We then use ~emms-all~ to load every emms submodule. Possible variations include: +| Name | What it does | +|---------------+----------------------------------------------------------------| +| ~emms-all~ | Loads every EMMS submodule. (browser, cover art, lyrics, etc.) | +|---------------+----------------------------------------------------------------| +| ~emms-standard~ | Smaller and more curated subset of features | +|---------------+----------------------------------------------------------------| +| ~emms-minimal~ | Very barebones EMMS | + +#+begin_src emacs-lisp + (emms-all) +#+end_src + +Here we tell EMMS to use MPD instead of MPRIS. +#+begin_src emacs-lisp + (require 'emms-player-mpd) +(setq emms-player-list '(emms-player-mpd) + emms-info-functions '(emms-info-mpd) +#+end_src + + +Here we point EMMS/MPD at the default server and port for MPD +#+begin_src emacs-lisp + emms-player-mpd-server-name "localhost" +emms-player-mpd-server-port "6600" +#+end_src + +Tell EMMS where MPD's music dir lives, and where its own music dir lives. +#+begin_src emacs-lisp + emms-player-mpd-music-directory "~/Music" +emms-source-file-default-directory (expand-file-name "~/Music/") +#+end_src +Make playlists loop instead of stopping at the last track. +#+begin_src emacs-lisp + emms-repeat-playlist t +#+end_src + +Make EMMS use Doom Emacs' cache instead of its own. +#+begin_src emacs-lisp + emms-cache-file (concat doom-cache-dir "emms-cache")) +#+end_src + +Clear the playlist both on EMMS and MPD +#+begin_src emacs-lisp + (add-hook 'emms-playlist-cleared-hook #'emms-player-mpd-clear) +#+end_src + +Connect to MPD as soon as this config loads. +#+begin_src emacs-lisp + (emms-player-mpd-connect) +#+end_src + +Turn on the little [ARTIST - ALBUM - TITLE] indicator in the modeline. +#+begin_src emacs-lisp + (emms-mode-line-mode 1) +#+end_src + +Show lyrics in the buffer whenever its free +#+begin_src emacs-lisp + (emms-lyrics-toggle-display-on-minibuffer) +#+end_src + +LISTENED/LENGTH indicator in the modeline + +#+begin_src emacs-lisp + (emms-playing-time-mode 1) +#+end_src + +This makes it so that presing RET on a song in normal mode adds the tracks to the playlist and plays it. +This currently doesnt work. +#+begin_src emacs-lisp + (map! :map emms-browser-mode-map + :n "RET" #'emms-browser-add-tracks-and-play)) +#+end_src + +** Git +~LEAVE Emacs? You gotta be kidding.~ +*** Majutsu +I use the JuJutsu version control system. +I am an old user of ~magit~. Why not use magit for JJ? + +Define mappings for the already-installed ~majutsu~ +Open the main majutsu buffer. +#+begin_src emacs-lisp + (map! + :leader + :prefix "g m" + :desc "Status" "m" #'majutsu ; default majutsu buffer + :desc "Describe" "d" #'majutsu-describe ; equivalent of 'jj describe' + :desc "New" "n" #'majutsu-new ; new jj rev + :desc "Push" "p" #'majutsu-git-push) ; push to git +#+end_src + +** Org +~Protesting for Org README support.~ +Org is Emacs's version of markdown, and is what this file is written in. + +Tell org what directory to use +#+begin_src emacs-lisp + (setq org-directory "~/org/") +#+end_src +Now it's time to get to the guts; the main org config. +But first, some good 'ol self-documenting code: +#+begin_src emacs-lisp + (after! org + (setq + org-startup-folded t ; fold headers on startup + org-pretty-entities t + org-hide-emphasis-markers t + org-auto-align-tags nil + org-tags-column 0 + org-catch-invisible-edits 'show-and-error + org-agenda-tags-column 0 + org-capture-templates-contexts nil + org-insert-heading-respect-content t + org-special-ctrl-a/e t + org-return-follows-link t +#+end_src +*** Org Babel +Org babel is Org's version of code blocks. +Every code block defined above and below is made via org-babel and the following config. +#+begin_src emacs-lisp + org-confirm-babel-evaluate nil) +(org-babel-do-load-languages +'org-babel-load-languages +'((emacs-lisp . t) + (python . t) + (C . t) + (lisp . t)))) +#+end_src +*** poly-org +~poly-org~ lets you use LSP and autocompletion in org code blocks without pressing ~C-c '~ +By default, org lets you 'enter' code blocks with ~C-c '~ and have actual autocompletion/LSP features. +But, typing that every block is annoying (especially with a literate config), that's why ~poly-org~ does the same thing* as ~C-c '~, but it happens when +the cursor hovers over a block. +#+begin_src emacs-lisp + (use-package! poly-org + :hook (org-mode . poly-org-mode)) +#+end_src + +After that, we still need to add Eglot integration. +#+begin_src emacs-lisp + (add-hook 'org-src-mode-hook #'eglot-ensure) +#+end_src +*** Org UX +This makes org capturing much better. +When capturing a new org note, all windows are deleted, and the capture window is made a dedicated window. +#+begin_src emacs-lisp + (add-hook 'org-capture-mode-hook #'delete-other-windows) + (add-hook 'org-capture-mode-hook + (lambda () (set-window-dedicated-p (selected-window) nil))) +#+end_src + +*** TODO Org roam +As much as i'd love to put it under its own higher header, that would be nonsensical. +Org roam is org's way of a Zettelkasten style knowledge place. Something like obsidian, logseq or ~zk~. Another plus is everything lives in a ~SQLite~ database. + +Point org-roam at the org directory (declared seperately). +#+begin_src emacs-lisp + (after! org-roam + (setq org-roam-directory (file-truename "~/org/")) +#+end_src + +Make roam sync it's database automatically. +#+begin_src emacs-lisp + (org-roam-db-autosync-mode) +#+end_src + +This is a custom function, i made it to mimic how the ~zk~ cli works when making a new note, which is usually a random 4 character string. for example ~zqy2.md~ could be a note. +#+begin_src emacs-lisp + (defun my/org-roam-short-id () + (let ((chars "abcdefghijklmnopqrstuvwxyz0123456789")) + (apply #'string + (cl-loop repeat 4 + collect (aref chars (random (length chars))))))) + + +#+end_src + +The template for new org notes. +#+begin_src emacs-lisp + (setq org-roam-capture-templates + '(("d" "default" plain + "* %?" + :if-new +#+end_src + +Here we use the function defined above to name the captured org file +#+begin_src emacs-lisp + (file+head + (lambda () (concat (my/org-roam-short-id) ".org")) + "#+TITLE: %^{title|Untitled}\n#+FILETAGS: :%^{tags}:\n\n") ; title&tags +:unnarrowed t))) + +(cl-defmethod org-roam-node-file-stem ((node org-roam-node)) + (file-name-base (org-roam-node-file node))) + +(setq org-roam-node-display-template + (concat "${title:*} " (propertize "${file-stem}" 'face 'org-tag))) + + +#+end_src + +Define sections in roam. +#+begin_src emacs-lisp + (setq org-roam-mode-sections + '(org-roam-backlinks-section + org-roam-reflinks-section + org-roam-unlinked-references-section)) +#+end_src + +**** Link checking +Here we have custom functions, mimicking the ~zk~ cli. +This functions check if an org file has an inserted link to itself. +#+begin_src emacs-lisp + (defun my/org-roam-check-self-link () + (when-let* ((node (org-roam-node-at-point)) + (id (org-roam-node-id node))) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward org-link-any-re nil t) + (when (string-match-p (regexp-quote id) (match-string 0)) + (message "Warning: self-link detected")))))) + + +#+end_src + +Check links that dont point to an actual file +#+begin_src emacs-lisp + (defun my/org-roam-check-dead-links () + (dolist (link (org-roam-db-query + [:select [dest] :from links + :where (= source $s1)] (org-roam-id-at-point))) + (unless (org-roam-db-query [:select id :from nodes :where (= id $s1)] (car link)) + (message "Error: dead link to %s" (car link))))) +#+end_src +**** Hooks +Run the defined above functions before every org file save +#+begin_src emacs-lisp + (add-hook 'before-save-hook + (lambda () + (when (org-roam-file-p) + (my/org-roam-check-self-link) + (my/org-roam-check-dead-links))) + nil t) +#+end_src +**** Maps +Define mappings +#+begin_src emacs-lisp + (map! + :leader + :prefix ("n r" . "roam") + :desc "Find node" "f" #'org-roam-node-find ; 'zk edit -i' equivalent + :desc "Insert node" "i" #'org-roam-node-insert ; link a note + :desc "Backlinks" "b" #'org-roam-buffer-toggle + :desc "Capture" "c" #'org-roam-capture ; make a new note +#+end_src +The org graph requires Graphviz and the ~dot~ executable +#+begin_src emacs-lisp + :desc "Graph" "g" #'org-roam-graph ; graph of every note +#+end_src +#+begin_src emacs-lisp + :desc "Random node" "r" #'org-roam-node-random ; random node +:desc "Open link" "o" #'org-open-at-point ; open linked note under cursor +:desc "Jump back" "O" #'org-mark-ring-goto)) ; jump back to previous note after SPC n r o + +#+end_src +*** ox-html +~ox-html~ is a way to convert org files into html. +#+begin_src emacs-lisp + (with-eval-after-load 'ox-html + (setq org-html-xml-declaration nil + org-html-doctype "html5" + org-html-html5-fancy t +#+end_src + +Dont use ox's default html style +#+begin_src emacs-lisp + org-html-head-include-default-style nil +#+end_src + +Dont postamble in generated html +#+begin_src emacs-lisp + org-html-postamble nil)) +#+end_src + +*** org-roam-ui +~org-roam-ui~ makes a Obsidian-like graph available in the browser. +It makes a pretty graph of all your notes and puts it at http://localhost:35901. + +Required websocket +#+begin_src emacs-lisp + (use-package! websocket :after org-roam) +#+end_src + +org-roam config +#+begin_src emacs-lisp + (use-package! org-roam-ui + :after org-roam + :config + (setq org-roam-ui-sync-theme t ; use emacs theme + org-roam-ui-follow t + org-roam-ui-update-on-save t ; update when a org roam file is saved + )) +#+end_src +*** org-appear +Org appear makes it so that inline code blocks (=~~=), words marked verbatim (====) and (hyper)link syntax hide when viewing the file and appear when hovered on for easy editing. +for example =[[id:xqyz][emacs]]= will become =emacs= when viewing the file, but turn into the original when the cursor is over the text. +#+begin_src emacs-lisp + (use-package! org-appear + :hook (org-mode . org-appear-mode) + :config + (setq org-appear-autoemphasis t + org-appear-autolinks t + org-appear-autosubmarkers t + org-appear-autoentities t)) +#+end_src +*** org-modern-indent +Because doom's org ~+pretty~ flag turns on org modern, and org-modern is incompatible with ~org-startup-indented~, we use the ~org-indent-modern~ package +#+begin_src emacs-lisp +(use-package! org-modern-indent + :defer t +:hook (org-mode . org-modern-indent-mode)) +#+end_src +** Eww +#+begin_quote +Eww... What browser is that girl using? +#+end_quote + +~eww~ (Emacs Web WOWser) is, well, a web browser in Emacs. +It only renders HTML and tries its best to render CSS, so javascript is out of the question (hooray!) + +~eww~ is fine on its own, but i have a config to make the experience better. +Here we define what to search with, for me this is Ecosia. +#+begin_src emacs-lisp + (setq eww-search-prefix "https://ecosia.org/search?q=") +#+end_src + +Where eww downloads files to. +#+begin_src emacs-lisp + (setq eww-download-directory (expand-file-name "~/Downloads/")) +#+end_src +*** Functions +Open a link in mpv +#+begin_src emacs-lisp + (defun my-browse-url-mpv (url &rest _args) + "Open URL in mpv." + (start-process "mpv" nil "mpv" url)) +#+end_src + +Open a url in pdf-tools +#+begin_src emacs-lisp + (defun my-browse-url-pdf (url &rest _args) + "Fetch remote PDF and open in pdf-tools within Emacs." + (let ((tmp (make-temp-file "emacs-pdf-" nil ".pdf"))) + (url-copy-file url tmp t) + (find-file-other-window tmp) + (pdf-view-mode))) +#+end_src + +Download an image and put it into ~eww-download-directory~ +#+begin_src emacs-lisp + (defun my/eww-download-image-at-point () + (interactive) + (let ((url (or (get-text-property (point) 'image-url) + (get-text-property (point) 'shr-url)))) + (if (not url) + (message "No image at point") + (let* ((filename (file-name-nondirectory (url-filename (url-generic-parse-url url)))) + (dest (expand-file-name filename eww-download-directory))) + (url-copy-file url dest t) + (message "Saved: %s" dest))))) +#+end_src + +*** Config :: Misc +Use the functions defined above when opening specific links +#+begin_src emacs-lisp + (setq browse-url-handlers + '(("\\(youtube\\.com\\|youtu\\.be\\|vimeo\\.com\\|twitch\\.tv\\)" . my-browse-url-mpv) + ("\\.mp4$" . my-browse-url-mpv) + ("\\.pdf$" . my-browse-url-pdf) + ("^gemini://" . elpher-browse-url-elpher) + ("^gopher://" . elpher-browse-url-elpher) + ("." . eww-browse-url))) + +#+end_src + +Open a url in a secondary program, in my case ~Vivaldi~ +#+begin_src emacs-lisp + (setq browse-url-secondary-browser-function 'browse-url-generic + browse-url-generic-program "vivaldi") +#+end_src + +*** UI/UX settings +#+begin_src emacs-lisp + (setq shr-width 100) +(setq shr-max-width 120) +(setq shr-indentation 4) +(setq shr-use-fonts nil) +(setq shr-max-image-size '(800 . 600)) +(setq shr-image-animate t) +#+end_src + +Let's make ~eww~ go into readable mode on page enter. +#+begin_src emacs-lisp + (add-hook 'eww-after-render-hook #'eww-readable) +#+end_src +*** Keybindings +#+begin_src emacs-lisp +(with-eval-after-load 'eww + (define-key eww-mode-map (kbd "=") #'text-scale-increase) + (define-key eww-mode-map (kbd "-") #'text-scale-decrease) + (define-key eww-mode-map (kbd "0") #'text-scale-adjust) + (define-key eww-mode-map (kbd "b") #'eww-back-url) + (define-key eww-mode-map (kbd "a") #'eww-add-bookmark) + (define-key eww-mode-map (kbd "U") #'shr-copy-url) + (define-key eww-mode-map (kbd "D") #'my/eww-download-image-at-point) + (evil-define-key 'normal eww-mode-map + "b" #'eww-back-url + "a" #'eww-add-bookmark + "U" #'shr-copy-url + "D" #'my/eww-download-image-at-point)) + +(provide 'browser) +#+end_src + +** Elpher +#+begin_quote +Elpher? I hardly know her! +#+end_quote + +Elpher is a way to use the Gemini and Gopher protocols in Emacs. +#+begin_src emacs-lisp +(use-package! elpher) +#+end_src +There's not much config needed except 1 (one) binding +#+begin_src emacs-lisp + (map! + :leader + :prefix "o g" + :desc "Elpher" "e" #'elpher + :desc "Browse Elpher" "b" #'elpher-browse-url-elpher) +#+end_src +** Packages +#+BEGIN_QUOTE +The ones that dont belong +#+END_QUOTE +These packages are (mostly) defined in ~packages.el~ and configured here. +*** TODO mosey +Mosey lets you ~Mosey down~ (get it?) to different points in a code block. +#+begin_src emacs-lisp +(use-package! mosey + :commands (mosey-forward mosey-backward + mosey-forward-bounce mosey-backward-bounce + mosey-forward-cycle mosey-backward-cycle)) +#+end_src +TODO: make this work properly and not just go to the start/end of line +**** Keybinds +Keybinds for mosey, under K because m was taken by localleader bindings +#+begin_src emacs-lisp +(map! :leader + (:prefix ("k" . "mosey") + :desc "Mosey backward" "a" #'mosey-backward-bounce + :desc "Mosey forward" "e" #'mosey-forward-bounce)) +#+end_src + +*** Woman +~woman~ is a way to view man pages straight in Emacs, without shelling out to the actual ~man~ binary, it stands for ~without man~ +Let's map this to a mnemonic binding, in this case ~Open Woman~ + +#+begin_src emacs-lisp + (map! + :leader + :prefix "o" + :desc "Woman" "w" #'woman) +#+end_src +*** Dired +~Dired~ (Directory editor) is a way to, well, edit directories (and files!) in Emacs. +This is similar to ~oil.nvim~ +These are only bindings as dired is pre-installed on Emacs. +#+begin_src emacs-lisp + (map! + :leader + :prefix "d" + :desc "Dired" "d" #'dired + :desc "Dired here" "h" #'dired-jump) +#+end_src +~dired~ prompts for the directory to open first, while ~dired-jump~ opens dired in the current directory, pretty neat. +*** eat +~eat~ (emulate a terminal) is a terminal emulator, in elisp, for emacs! +It is not available as a doom emacs module, so we declare it manually. +#+begin_src emacs-lisp + (use-package! eat + :defer t) +#+end_src + +Then we add bindings for ~eat~ as to not have to run ~M-x~ everytime. +#+begin_src emacs-lisp + (map! + :leader + :prefix "o" + :desc "Open Eat" "e" #'eat-other-window) ; dont steal the current window +#+end_src + +** Evil +~Mapping at its finest~ +EVIl mode emulates VIM bindings in Emacs. + +Remove ~evil-snipe~ as it conflicts with other bindings +#+begin_src emacs-lisp + (remove-hook 'doom-first-input-hook #'evil-snipe-mode) +#+end_src + +*** Lispy +~lispy~[fn:1] is a way to run vim motions in a S-exp aware way. +As an example , running ~D~ on +~(message "Get rid of |everything" a b (+ 1 2))~, where ~|~ is the point would ruin the S-exp, but with ~lispy~, this turns into: +~(message "Get rid of ")~ +Pretty neat, huh? +#+begin_src emacs-lisp + (use-package lispyville + :init + (general-add-hook '(emacs-lisp-mode-hook lisp-mode-hook) #'lispyville-mode) ;; lispy lisps + :config + (lispyville-set-key-theme '(operators c-w additional))) +#+end_src +*** Colemak +The biggest block for last. +This is a custom mapping i made for EVIl mode that takes the bindings from [[https://github.com/kalbasit/vim-colemak][kalbasit/vim-colemak]] and puts them into Emacs. +I plan on packaging this to [[https://melpa.org][MELPA]] soon. + +Make ~o~ go right in org mode (previously ~inserted on newline~) +#+begin_src emacs-lisp + (after! evil-org + (evil-define-key 'normal evil-org-mode-map + "o" #'evil-forward-char)) +#+end_src + +**** Main bindings +#+begin_src emacs-lisp + (evil-define-key '(normal visual operator) 'global + "n" 'evil-backward-char + "e" 'evil-next-line + "i" 'evil-previous-line + "o" 'evil-forward-char) +#+end_src +**** Motion +#+begin_src emacs-lisp + (evil-define-key '(normal visual operator) 'global + "l" 'evil-backward-word-begin + "L" 'evil-backward-WORD-begin + "u" 'evil-forward-word-end + "U" 'evil-forward-WORD-end + "y" 'evil-forward-word-begin + "Y" 'evil-forward-word-begin) + +(with-eval-after-load 'evil-ex + (define-key evil-ex-completion-map (kbd "C-l") 'left-word) + (define-key evil-ex-completion-map (kbd "C-y") 'right-word)) + + +#+end_src +**** Inserting +#+begin_src emacs-lisp + (evil-define-key 'normal 'global + "s" 'evil-insert + "S" 'evil-insert-line + "t" 'evil-append + "T" 'evil-append-line + "h" 'evil-open-below + "H" 'evil-open-above) +#+end_src +**** Changing +#+begin_src emacs-lisp + (evil-define-key '(normal visual) 'global + "w" 'evil-change + "W" 'evil-change-line) +#+end_src +**** Editing +#+begin_src emacs-lisp + (evil-define-key 'normal 'global + "x" 'evil-delete-char + "c" 'evil-yank + "v" 'evil-paste-after + "X" 'evil-delete-line + "C" 'evil-yank-line + "V" 'evil-paste-before) + +(evil-define-key 'visual 'global + "x" 'evil-delete + "c" 'evil-yank + "v" 'evil-paste-after) +#+end_src +**** TODO Undo +Doesnt work in org files +#+begin_src emacs-lisp +(evil-define-key 'normal 'global + "z" 'evil-undo + "Z" 'evil-redo) + + +#+end_src +**** Visual +#+begin_src emacs-lisp +(evil-define-key 'normal 'global + "a" 'evil-visual-char + "A" 'evil-visual-line + "ga" 'evil-visual-restore) + + +#+end_src +**** Find/Search +#+begin_src emacs-lisp +(evil-define-key '(normal visual operator) 'global + "p" 'evil-find-char-to + "P" 'evil-find-char-to-backward + "b" 'evil-repeat-find-char + "B" 'evil-repeat-find-char-reverse + "k" 'evil-search-next + "K" 'evil-search-previous) + + +#+end_src +**** Inner change +#+begin_src emacs-lisp +(with-eval-after-load 'evil + (evil-define-key '(operator visual) 'global "r" evil-inner-text-objects-map)) + +#+end_src +**** Scroll +#+begin_src emacs-lisp +(define-prefix-command 'my-colemak-fold-map) +(evil-define-key '(normal visual) 'global "j" 'my-colemak-fold-map) +(define-key my-colemak-fold-map "j" 'evil-scroll-line-to-center) +(define-key my-colemak-fold-map "n" 'evil-scroll-line-to-top) +(define-key my-colemak-fold-map "e" 'evil-scroll-line-to-bottom) + + +#+end_src +**** Lookup +#+begin_src emacs-lisp +(evil-define-key 'normal 'global + "gK" 'evil-lookup) + + +#+end_src +**** Window management +#+begin_src emacs-lisp +(map! :n "SPC w n" #'evil-window-left + :n "SPC w e" #'evil-window-down + :n "SPC w i" #'evil-window-up + :n "SPC w o" #'evil-window-right) + +#+end_src + +** Footnotes + +[fn:1] +The actual vim motion part is provided by ~lispyville~, which adds evil support into |
