#+TITLE: Doom Emacs configuration #+PROPERTY: header-args:emacs-lisp :tangle config.el Hello! This is my literate configuration for Doom Emacs. Why literate? because org is cool! Made with org-babel (the little begin_src blocks) * Splash screen This is my custom splash screen for Emacs. #+begin_src emacs-lisp (load! "splash") (defun +splash-initial-buffer () (splash-screen) (get-buffer "*splash*")) (setq doom-fallback-buffer-name "*splash*" initial-buffer-choice #'+splash-initial-buffer) (defadvice! +splash-regenerate-if-missing-a (&rest _) :before #'doom-fallback-buffer (unless (get-buffer "*splash*") (splash-screen))) #+end_src The splash screen doesnt do much, it prints the emacs version and a warranty notice and whatnot. i like it tho! * 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 ** 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 The emacs mood line, It's nice and simple which i adore. I might have to try out the default modeline from Emacs sometime though. #+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 * 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 ** TODO make this a ~SPC~-based binding Hook the function up up to f5 #+begin_src emacs-lisp (add-hook 'haskell-mode-hook (lambda () (local-set-key (kbd "") #'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 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). Declare the package block and set up the commands. ** TODO put the following prose in an org-roam file ~: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) #+end_src Configure the mappings for EMMS ** TODO put the following prose in an org-roam file ~: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-browser :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" "s" #'majutsu ; default majutsu buffer :desc "Describe" "c" #'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-indented t org-pretty-entities t org-hide-emphasis-markers t org-capture-templates-contexts nil #+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 ** 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 :desc "Graph" "g" #'org-roam-graph ; graph of every note :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 * Eww ~Eww... What browser is that girl using?~ ~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!) emms 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 ** 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 * TODO Elpher ~Elpher Leaves Protocols Hanging, Eternally Relevant~ * Packages ~The ones that dont belong~ 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 *** 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 ** aggressive-indent ~aggressive-indent~ is a package that makes sure every line is indented properly. It follows its own indentation, and forces (very aggressively) for lines to stay on there, live, while editing. #+begin_src emacs-lisp (use-package! aggressive-indent :config (global-aggressive-indent-mode 1)) ; use aggressive-indent everywhere #+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~ Because i use Helm, im using the pre-configured Helm version #+begin_src emacs-lisp (map! :leader :prefix "o" :desc "Woman" "w" #'helm-man-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. * 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 ** 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