emacs.org (29731B)
1 #+TITLE:Emacs config 2 #+AUTHOR: Crazazy 3 #+PROPERTY: header-args :tangle yes :noweb yes :results silent 4 #+INCLUDE: ./style.org 5 Welp, here it goes. This is a literal config for my emacs setup 6 I will prbably copy-paste what I have in my literal config, and provide small, fun explanations along the way 7 For now, prepare this to be not the best documented literal config you've seen. 8 Also, if you just stumbled accross this at random, there is an easy tangle button at [[file:readme.org][readme.org]] 9 * Before we start lisping 10 This emacs config's dependencies are mostly managed by [[https://nixos.org][nix]], therefore we have to make a nix file before we 11 do any emacs configuring by ourselves 12 We also add webkitgtk support for (eventual) web browsing support 13 #+begin_src nix :tangle emacs.nix 14 let 15 sources = import ./nix/sources.nix; 16 packageOverlay = final: old: { 17 emacs = old.emacs.override { 18 withXwidgets = true; 19 }; 20 emacsWithPackages = final.emacs.pkgs.withPackages; 21 }; 22 in 23 import sources.emacs { 24 # put 'packageOverlay' in the overlays array for XWidgets (whenever they are stable i guess...) 25 pkgs = import <nixpkgs> { overlays = [ ]; }; 26 configDir = ./emacsconfig; 27 } 28 #+end_src 29 * Emacs configuring 30 Now the real configuring begins! 31 for some backwards compat purposes I have decided to but different config subjects into different files 32 This won't matter too much as the nix framework is expecting multiple files anyway! 33 ** The bases 34 This is only better-defaults. It comes with ido-mode and some other defaults that I forgot to set in my 35 init file that I stole from [[https://github.com/editor-bootstrap/emacs-bootstrap][emacs-bootstrap]] months ago 36 #+begin_src emacs-lisp :mkdirp yes :tangle emacsconfig/base.el 37 (use-package better-defaults) 38 #+end_src 39 *** Multiple terminals 40 By default, =term= only supports 1 terminal. This version supports multiple terminals 41 #+begin_src emacs-lisp :tangle emacsconfig/base.el 42 (defun new-term (program) 43 "start a new terminal emulator in a new buffer" 44 (interactive (list (read-from-minibuffer "Run program: " 45 (or explicit-shell-file-name 46 (getenv "ESHELL") 47 shell-file-name)))) 48 (let* ((term-list (seq-filter 49 (lambda (s) (string-match-p "terminal" (buffer-name s))) 50 (buffer-list))) 51 (term-num (number-to-string (length term-list)))) 52 (set-buffer (make-term (concat "terminal-" term-num) program)) 53 (term-mode) 54 (term-char-mode) 55 (switch-to-buffer (concat "*terminal-" term-num "*")))) 56 #+end_src 57 ** Generally nice emacs tools 58 *** Magit 59 Simple. Nice. default config. (almost) 60 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 61 (use-package magit 62 :init 63 ;; import missing function 64 (defun seq-keep (function sequence) 65 "Apply FUNCTION to SEQUENCE and return the list of all the non-nil results" 66 (delq nil (seq-map function sequence))) 67 :config 68 (magit-add-section-hook 'magit-status-sections-hook 69 'magit-insert-modules 70 'magit-insert-stashes 71 'append) 72 :bind 73 ("C-x g" . magit-status)) 74 (use-package forge 75 :after magit) 76 #+end_src 77 *** IRC 78 I use IRC in emacs, with ERC. There is even a small macro so that I can easily join new servers 79 with my preferred nickname 80 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 81 (use-package erc 82 :custom 83 (erc-server-reconnect-attempts 10) 84 (erc-nick "crazazy") 85 (erc-autojoin-channels-alist '(("tilde.chat" 86 "#cafe" 87 "#meta") 88 ("libera.chat" 89 "#emacs" 90 "#haskell" 91 "#systemcrafters"))) 92 :config 93 (defmacro irc-quickjoin (servername url &optional nick) 94 "create a function to quickly join a server. Servers can be joined with M-x SERVERNAME-irc" 95 `(defun ,(intern (concat (symbol-name servername) "-irc")) (password) 96 (interactive (list (password-read "Password: "))) 97 (erc-tls :server ,url 98 :nick ,(if nick 99 nick 100 "crazazy") 101 :port 6697 102 :password password))) 103 104 (irc-quickjoin tilde "eu.tilde.chat") 105 (irc-quickjoin libera "irc.libera.chat")) 106 #+end_src 107 *** Dashboard 108 Gives me access to the most recent files I edited, and some other stuff that I don't really care about 109 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 110 (use-package dashboard 111 :config (dashboard-setup-startup-hook)) 112 #+end_src 113 *** Elfeed 114 I don't use elfeed in emacs that much anymore, as I have switched to [[https://www.seamonkey-project.org/][seamonkey]] for my RSS needs. 115 Here is my old config anyways. If you want to browse through some default news feel free to remove the last 116 =:custom= option 117 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 118 (use-package elfeed 119 :bind 120 ("C-x w" . elfeed) 121 (:map elfeed-search-mode-map 122 ("C-c a" . elfeed-add-feed) 123 ("C-c u" . elfeed-update) 124 ("C-c f" . elfeed-update-feed) 125 ("C-c r" . elfeed-mark-all-as-read)) 126 :config 127 (defun elfeed-mark-all-as-read () 128 (interactive) 129 (mark-whole-buffer) 130 (elfeed-search-untag-all-unread)) 131 :custom 132 (elfeed-search-filter "@6-months-ago +quality") 133 (elfeed-feeds '(("http://feeds.feedburner.com/tweakers/nieuws" NL tech exportable) 134 ("https://discourse.nixos.org/c/announcements/8.rss" nixos programming quality) 135 ("http://www.dnbradio.com/feeds" music podcasts) 136 ("https://codepen.io/spark/feed/" programming quality) 137 ("https://envs.net/~lucidiot/rsrsss/feed.xml" RSS quality) 138 ("https://falseknees.tumblr.com/rss" comics quality) 139 ("https://hackspace.raspberrypi.org/feed" tech programming) 140 ("https://lobste.rs/rss" programming) 141 ("https://news.rickcarlino.com/rss.rss" tech) 142 ("https://nu.nl/rss" NL news exportable) 143 ("https://planet.haskell.org/atom.xml" haskell programming) 144 ("https://planet.nixos.org/atom.xml" nixos programming quality) 145 ("https://reddit.com/r/dnb/.rss" reddit music) 146 ("https://reddit.com/r/programming/.rss" reddit programming) 147 ("https://reddit.com/r/realdubstep/.rss" reddit music) 148 ("https://reddit.com/r/thenetherlands/.rss" NL reddit) 149 ("https://sachachua.com/blog/category/emacs-news/feed" emacs quality programming) 150 ("https://webzine.puffy.cafe/atom.xml" openbsd tech quality programming) 151 ("https://www.fosskers.ca/en/rss" programming quality exportable) 152 ("https://xkcd.com/atom.xml" comics))) 153 (elfeed-feeds nil) 154 (elfeed-search-filter "")) 155 #+end_src 156 *** Org mode 157 While the brunt of this file is relatively vanilla, There are still some things that I want to customize about the org-mode experience 158 For now, I think it's best if I don't start depending on org-contrib, as it contains a lot of features and I would waste quite some time 159 figuring out what all those features are. 160 161 First of all, auto-indenting isn't enabled everywhere, so let's do that 162 #+begin_src emacs-lisp :tangle emacsconfig/org.el 163 (use-package org 164 :custom 165 (org-adapt-indentation t)) 166 #+end_src 167 **** Syntax highlighting for HTML exports 168 Normally, when I export my document to an html file for the website, syntax highlighting isn't automatically turned on. htmlize changes this 169 by putting some colorful spans in all my source code. No need to configure anything either 170 #+begin_src emacs-lisp :tangle emacsconfig/org.el 171 (use-package htmlize) 172 #+end_src 173 **** Org roam :noexport: 174 My parents are really urging me to take notes of stuff now. And I'm inclined to agree. I'll try and figure out what 175 I need from my note taking program down the line, but for now here is a basic config 176 Mostly stolen from [[https://systemcrafters.net/build-a-second-brain-in-emacs/][System crafters]] 177 I will have to figure out what most of this does /exactly/ but from the description this all seems like 178 stuff that I'd want for my note taking setup 179 #+begin_src emacs-lisp :tangle no 180 (use-package org-roam 181 :ensure t 182 :demand t ;; Ensure org-roam is loaded by default 183 :init 184 (setq org-roam-v2-ack t) 185 :custom 186 (org-roam-directory "~/Documents/notes") 187 (org-roam-completion-everywhere t) 188 :bind (("C-c n l" . org-roam-buffer-toggle) 189 ("C-c n f" . org-roam-node-find) 190 ("C-c n i" . org-roam-node-insert) 191 ("C-c n I" . org-roam-node-insert-immediate) 192 ("C-c n p" . my/org-roam-find-project) 193 ("C-c n t" . my/org-roam-capture-task) 194 ("C-c n b" . my/org-roam-capture-inbox) 195 :map org-mode-map 196 ("C-M-i" . completion-at-point) 197 :map org-roam-dailies-map 198 ("Y" . org-roam-dailies-capture-yesterday) 199 ("T" . org-roam-dailies-capture-tomorrow)) 200 :bind-keymap 201 ("C-c n d" . org-roam-dailies-map) 202 :config 203 (require 'org-roam-dailies) ;; Ensure the keymap is available 204 (org-roam-db-autosync-mode)) 205 206 (defun org-roam-node-insert-immediate (arg &rest args) 207 (interactive "P") 208 (let ((args (push arg args)) 209 (org-roam-capture-templates (list (append (car org-roam-capture-templates) 210 '(:immediate-finish t))))) 211 (apply #'org-roam-node-insert args))) 212 213 (defun my/org-roam-filter-by-tag (tag-name) 214 (lambda (node) 215 (member tag-name (org-roam-node-tags node)))) 216 217 (defun my/org-roam-list-notes-by-tag (tag-name) 218 (mapcar #'org-roam-node-file 219 (seq-filter 220 (my/org-roam-filter-by-tag tag-name) 221 (org-roam-node-list)))) 222 223 (defun my/org-roam-refresh-agenda-list () 224 (interactive) 225 (setq org-agenda-files (my/org-roam-list-notes-by-tag "Project"))) 226 227 ;; Build the agenda list the first time for the session 228 (my/org-roam-refresh-agenda-list) 229 230 (defun my/org-roam-project-finalize-hook () 231 "Adds the captured project file to `org-agenda-files' if the 232 capture was not aborted." 233 ;; Remove the hook since it was added temporarily 234 (remove-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook) 235 236 ;; Add project file to the agenda list if the capture was confirmed 237 (unless org-note-abort 238 (with-current-buffer (org-capture-get :buffer) 239 (add-to-list 'org-agenda-files (buffer-file-name))))) 240 241 (defun my/org-roam-find-project () 242 (interactive) 243 ;; Add the project file to the agenda after capture is finished 244 (add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook) 245 246 ;; Select a project file to open, creating it if necessary 247 (org-roam-node-find 248 nil 249 nil 250 (my/org-roam-filter-by-tag "Project") 251 :templates 252 '(("p" "project" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n" 253 :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Project") 254 :unnarrowed t)))) 255 256 (defun my/org-roam-capture-inbox () 257 (interactive) 258 (org-roam-capture- :node (org-roam-node-create) 259 :templates '(("i" "inbox" plain "* %?" 260 :if-new (file+head "Inbox.org" "#+title: Inbox\n"))))) 261 262 (defun my/org-roam-capture-task () 263 (interactive) 264 ;; Add the project file to the agenda after capture is finished 265 (add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook) 266 267 ;; Capture the new task, creating the project file if necessary 268 (org-roam-capture- :node (org-roam-node-read 269 nil 270 (my/org-roam-filter-by-tag "Project")) 271 :templates '(("p" "project" plain "** TODO %?" 272 :if-new (file+head+olp "%<%Y%m%d%H%M%S>-${slug}.org" 273 "#+title: ${title}\n#+category: ${title}\n#+filetags: Project" 274 ("Tasks")))))) 275 276 (defun my/org-roam-copy-todo-to-today () 277 (interactive) 278 (let ((org-refile-keep t) ;; Set this to nil to delete the original! 279 (org-roam-dailies-capture-templates 280 '(("t" "tasks" entry "%?" 281 :if-new (file+head+olp "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n" ("Tasks"))))) 282 (org-after-refile-insert-hook #'save-buffer) 283 today-file 284 pos) 285 (save-window-excursion 286 (org-roam-dailies--capture (current-time) t) 287 (setq today-file (buffer-file-name)) 288 (setq pos (point))) 289 290 ;; Only refile if the target file is different than the current file 291 (unless (equal (file-truename today-file) 292 (file-truename (buffer-file-name))) 293 (org-refile nil nil (list "Tasks" today-file nil pos))))) 294 295 (add-to-list 'org-after-todo-state-change-hook 296 (lambda () 297 (when (equal org-state "DONE") 298 (my/org-roam-copy-todo-to-today)))) 299 300 #+end_src 301 **** Denote 302 I used to want to try out org-roam, but nothing came of that. However, right now I do have notes in denote, 303 and since emacs-ng doesn't work with denote anymore (since its stuck at version 28.0.50) I have to move to 304 the default version of emacs 305 #+begin_src emacs-lisp :tangle emacsconfig/org.el 306 (use-package denote 307 :ensure t 308 :custom 309 (denote-directory "~/Documents/notes/") 310 (denote-file-type nil) 311 (denote-prompts '(title keywords)) 312 (denote-date-prompt-use-org-read-date t) 313 :bind 314 (("C-c n n" . denote) 315 ("C-c n N" . denote-type) 316 ("C-c n d" . denote-date) 317 ("C-c n s" . denote-subdirectory) 318 ("C-c n t" . denote-template) 319 ("C-c n r" . denote-rename-file) 320 :map org-mode-map 321 ("C-c n i" . denote-link) 322 ("C-c n I" . denote-link-add-links) 323 ("C-c n l" . denote-link-find-file) 324 ("C-c n b" . denote-link-backlinks))) 325 #+end_src 326 *** Evil mode 327 I am originally a vim user, and the standard vim bindings have not left my hands yet, 328 I am not a big configurer, so this is mostly just the standard configuration that [[https://github.com/emacs-evil/evil-collection#installation][evil-mode]] advices you to do 329 #+begin_src emacs-lisp :tangle emacsconfig/evil.el 330 (use-package evil 331 :init 332 (setq evil-want-integration t) ;; This is optional since it's already set to t by default. 333 (setq evil-want-keybinding nil) 334 :config 335 (evil-define-key 'normal global-map "," 'evil-execute-in-god-state) 336 (evil-mode 1)) 337 338 (use-package evil-collection 339 :after evil 340 :config 341 (evil-collection-init)) 342 #+end_src 343 **** God-mode 344 To further prove that I have no intentions at all to be busy with making evil bindings for eveything I find 345 I have also installed god-mode to help me do effortless keybinding for those programs that don't have evil 346 support 347 #+begin_src emacs-lisp :tangle emacsconfig/evil.el 348 (use-package evil-god-state 349 :bind 350 ("M-," . evil-god-state-bail)) 351 352 (use-package god-mode) 353 #+end_src 354 **** Tree-sitter 355 There is a pre-configured package that allows for easy tree-sitter support for a few languages. Nixos + tree-sitter 356 is surprisingly painless, so I figured I'd just add it 357 #+begin_src emacs-lisp :tangle emacsconfig/evil.el 358 (use-package evil-tree-edit 359 :hook 360 (java-mode . evil-tree-edit-mode) 361 (python-mode . evil-tree-edit-mode)) 362 #+end_src 363 *** Which-key 364 Has god-mode support 365 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 366 (use-package which-key 367 :after god-mode 368 :config 369 (which-key-enable-god-mode-support) 370 (which-key-mode)) 371 #+end_src 372 *** PDF-tools 373 I don't want to be at the mercy of my browser to figure out how to read a PDF 374 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 375 (use-package pdf-tools 376 :config 377 (add-to-list 'auto-mode-alist '("\\.pdf$" . pdf-view-mode))) 378 #+end_src 379 ** Programming-related emacs tools 380 I do computer science, so from now and then I tend to program a bit. Here are all the tools I use to accomplish 381 that stuff 382 *** Company 383 Generally nice for auto-completion anywhere. I think here is also the place to note that I am not a huge fan 384 of language-servers, as I've had bad experiences with them when I was still using vim 385 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 386 (use-package company 387 :hook 388 (prog-mode . company-mode) 389 :bind 390 ("C-SPC" . company-complete)) 391 #+end_src 392 *** Matching of parentheses and other stuff 393 "ERROR: Unmatched parenteses/braces/whatever" is really a thing I would prefer to never see again 394 It has nix and lisp support, but other langages are fairly standards so not much configuration needed there 395 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 396 (use-package smartparens 397 :hook 398 (emacs-lisp-mode . smartparens-mode) 399 (nix-mode . smartparens-mode) 400 (haskell-mode . smartparens-mode) 401 (lisp-mode . smartparens-mode) 402 :config 403 (sp-with-modes '(lisp-mode emacs-lisp-mode) 404 (sp-local-pair "'" nil :actions nil) 405 (sp-local-pair "`" nil :actions nil)) 406 407 (sp-with-modes '(haskell-mode) 408 (sp-local-pair "'" nil :actions nil)) 409 (sp-with-modes '(nix-mode) 410 (sp-local-pair "'" nil :actions nil) 411 (sp-local-pair "''" "''"))) 412 #+end_src 413 *** Languages 414 Other than that, there are some language-specific setups that I want to do,which are a bit more complicated 415 than just "editing a file" 416 **** Nix 417 Nix is a fairly essential language here. Not just because I run NixOS, but also because it provides 418 external packages (interpreters, compilers, tooling etc.) to the programs that need it 419 ***** Nix mode 420 This config is just part of editing nix config. It is supposed to come with company support, but 421 the auto-completion backend turns out to not be that great. We'll see what I do with it 422 #+begin_src emacs-lisp :tangle emacsconfig/nix.el 423 (use-package nix-mode 424 :mode "\\.nix\\'" 425 :config 426 ;; the company-nix backend is not available in melpa, but has no new dependencies 427 (unless (package-installed-p 'company-nix) 428 (with-temp-buffer 429 (url-insert-file-contents "https://github.com/NixOS/nix-mode/raw/master/nix-company.el") 430 (eval-buffer))) 431 (add-hook 'nix-mode-hook (lambda () 432 (set (make-local-variable 'company-backends) 433 '((company-nix)))))) 434 #+end_src 435 ***** Nix package management 436 I use nix-sandbox for managing nix package for other languages. 437 One of the functions doesn't work that well for me so I replaced it with something that does 438 #+begin_src emacs-lisp :tangle emacsconfig/nix.el 439 (use-package nix-sandbox 440 :demand 441 :config 442 (defun nix-executable-find (sandbox executable) 443 "finds an EXECUTABLE in SANDBOX" 444 (set (make-local-variable 'exec-path) (nix-exec-path sandbox)) 445 (executable-find executable)) 446 #+end_src 447 Beyond that, there is a helper function that makes it easy to quickly define environments with the required 448 packages for a programming language 449 #+begin_src emacs-lisp :tangle emacsconfig/nix.el 450 (defun intersperse (el ls) (if (cdr ls) `(,(car ls) ,el . ,(intersperse el (cdr ls))) ls)) 451 (defun nix-env-from-packages (name &rest packages) 452 "create a nix environment from nix packages. returns the location of the environment" 453 (interactive (append 454 (list (read-string "Environment name: " nil nil "nameless")) 455 (split-string (read-string "Packages: ")))) 456 (with-temp-buffer 457 (insert (format " 458 { pkgs ? import %s {}}: 459 pkgs.mkShell { 460 buildInputs = with pkgs;[ 461 %s 462 ]; 463 } 464 " (or nix-nixpkgs-path "<nixpkgs>") (apply 'concat (intersperse "\n" packages)))) 465 (write-file (concat temporary-file-directory name "-env/shell.nix")) 466 (nix-find-sandbox (concat temporary-file-directory name "-env"))))) 467 #+end_src 468 **** Python 469 Python is a bit weird. It had no intentions at all to do things the way I wanted it to do things with 470 use-package, so I had to find a weird work-arounds with add-hook and all that stuff 471 #+begin_src emacs-lisp :tangle emacsconfig/python.el :padline no :noweb no-export 472 (add-hook 'python-mode-hook (lambda () 473 <<el-python-config>>)) 474 475 #+end_src 476 First we set an interpreter with nix, it comes with all the python autocompletion tools we need 477 #+begin_src emacs-lisp :noweb-ref el-python-config :tangle no 478 (setq python-shell-interpreter 479 (nix-executable-find 480 (nix-env-from-packages "python" "(python3.withPackages (p: with p; [pygame virtualenvwrapper pip sqlite jedi flake8 yapf autopep8 black]))") 481 "python")) 482 483 #+end_src 484 Then we introduce elpy with so that it can use all the tools immidiately without downloading them 485 #+begin_src emacs-lisp :noweb-ref el-python-config :tangle no 486 (use-package elpy 487 :config 488 (elpy-enable) 489 (setq elpy-rpc-python-command python-shell-interpreter)) 490 491 #+end_src 492 **** Haskell 493 I have to use haskell in my new module, so there is now some haskell infra for that 494 first we have to set up a nix environment for our haskell tools 495 #+begin_src emacs-lisp :tangle emacsconfig/haskell.el 496 #+end_src 497 Then haskell-mode setup, which integrates company mode as well as the sandbox 498 #+begin_src emacs-lisp :tangle emacsconfig/haskell.el 499 (use-package haskell-mode 500 :mode "\\.hs\\'" 501 :after nix-sandbox 502 :hook 503 (haskell-mode . set-haskell-company-backends) 504 (haskell-mode . haskell-indentation-mode) 505 :config 506 (setq haskell-env (nix-env-from-packages "Haskell" 507 "ghc" 508 "cabal-install" 509 "haskellPackages.fourmolu")) 510 (setq haskell-process-wrapper-function 511 (lambda (args) 512 (cons 513 (nix-executable-find haskell-env (car args)) 514 (cdr args)))) 515 (defun set-haskell-company-backends () 516 (set (make-local-variable 'company-backends) 517 '((dante-company company-files))))) 518 #+end_src 519 ormolu formats my haskell files, though I use fourmolu since I prefer prefixing chained infix functions 520 #+begin_src emacs-lisp :tangle emacsconfig/haskell.el 521 (use-package ormolu 522 :after haskell-mode 523 :hook 524 (haskell-mode . ormolu-format-on-save-mode) 525 (haskell-mode . (lambda () 526 (set (make-local-variable 'ormolu-process-path) 527 (nix-executable-find haskell-env "fourmolu")))) 528 :bind 529 (:map haskell-mode-map 530 ("C-c r" . ormolu-format-buffer))) 531 #+end_src 532 Finally, dante runs as background interpreter of my haskell codes and checks stuff for errors 533 #+begin_src emacs-lisp :tangle emacsconfig/haskell.el 534 (use-package dante 535 :after haskell-mode 536 :hook 537 (haskell-mode . dante-mode) 538 :custom 539 ;; (dante-repl-command-line (list (nix-executable-find haskell-env "ghci"))) 540 (dante-methods '(nix-ghci bare-cabal bare-ghci))) 541 #+end_src 542 543 **** Web stuff 544 I also have made some basic webpages. I haven't gotten too deep into JS with emacs yet, so this section 545 might expand later, but so far this is what I have 546 ***** Web mode 547 General xml stuff editing. Fairly standard 548 #+begin_src emacs-lisp :tangle emacsconfig/web.el 549 (use-package web-mode 550 :mode (("\\.x?html?\\'" . web-mode) 551 ("\\.x[sm]l\\'" . web-mode) 552 ("\\.css\\'" . web-mode) 553 ("\\.jsx?\\'" . web-mode) 554 ("\\.tsx?\\'" . web-mode) 555 ("\\.json\\'" . web-mode)) 556 :custom 557 (web-mode-markup-indent-offset 2) ; HTML 558 (web-mode-css-indent-offset 2) ; CSS 559 (web-mode-code-indent-offset 2) ; JS/JSX/TS/TSX 560 (web-mode-content-types-alist '(("jsx" . "\\.js[x]?\\'")))) 561 (use-package elnode) 562 563 #+end_src 564 ***** Emmet mode 565 for quickly inserting a whole XML tree 566 #+begin_src emacs-lisp :tangle emacsconfig/web.el 567 (use-package emmet-mode 568 :hook 569 (sgml-mode . emmet-mode) 570 (web-mode . emmet-mode) 571 (css-mode . emmet-mode) 572 :bind 573 (:map emmet-mode-keymap 574 ("TAB" . emmet-dwim)) 575 :config 576 (defun emmet-dwim (prefix) 577 (interactive "p") 578 (or 579 (emmet-go-to-edit-point prefix) 580 (emmet-expand-line prefix) 581 (evil-jump-forward prefix)))) 582 #+end_src 583 ** Other, less useful stuff 584 *** Themes 585 I like to have a nice theme in my setup 586 #+begin_src emacs-lisp :tangle emacsconfig/misc.el 587 (load-theme 'leuven) 588 #+end_src 589 Also I want to theme my config exports 590 #+begin_src emacs-lisp :tangle emacsconfig/extras.el 591 (use-package htmlize) 592 #+end_src 593 *** Discord 594 My discord buddies /have/ to know I'm using emacs whenever I have my computer running 595 #+begin_src emacs-lisp :tangle emacsconfig/misc.el 596 (use-package elcord 597 :config 598 (elcord-mode)) 599 #+end_src 600 *** Emenu 601 Since I'm using emacs as a daemon, it can also be benefical to make an application launcer 602 #+begin_src emacs-lisp :tangle emacsconfig/misc.el 603 (defun all-commands () 604 "does a completing read of all the programs accessible to you in $PATH" 605 (let ((ls-output (mapcan (lambda (path) 606 (when (file-readable-p path) 607 (cl-remove-if (lambda (file) 608 (let ((fullpath (concat path "/" file))) 609 (or (file-directory-p fullpath) 610 (not (file-executable-p fullpath))))) 611 (directory-files path nil directory-files-no-dot-files-regexp nil)))) 612 (split-string (getenv "PATH") ":" t))) 613 (alias-output (split-string (shell-command-to-string "alias -p | sed -E 's/^alias ([^=]*)=.*$/\\1/'") "\n"))) 614 (append ls-output alias-output))) 615 616 (defun emenu (&optional command-list) 617 "A dmenu-inspired application launcher using a separate emacs frame" 618 (interactive) 619 (with-selected-frame (make-frame '((name . "emenu") 620 (minibuffer . only) 621 (width . 151) 622 (height . 1))) 623 (unwind-protect 624 (progn 625 (call-process 626 (ido-completing-read "Command: " (or 627 command-list 628 (all-commands))) 629 nil 630 0) 631 (keyboard-quit)) 632 (delete-frame)))) 633 #+end_src