I showed you my source code, pls respond
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1052 lines
31 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #+TITLE: Dotfiles
  2. #+AUTHOR: Christopher James Hayward
  3. #+EMAIL: chris@chrishayward.xyz
  4. #+ROAM_KEY: https://github.com/chayward1/dotfiles
  5. #+begin_example
  6. ^^ @@@@@@@@@
  7. ^^ ^^ @@@@@@@@@@@@@@@
  8. @@@@@@@@@@@@@@@@@@ ^^
  9. @@@@@@@@@@@@@@@@@@@@
  10. ~~~~ ~~ ~~~~~ ~~~~~~~~ ~~ &&&&&&&&&&&&&&&&&&&& ~~~~~~~ ~~~~~~~~~~~ ~~~
  11. ~ ~~ ~ ~ ~~~~~~~~~~~~~~~~~~~~ ~ ~~ ~~ ~
  12. ~ ~~ ~~ ~~ ~~ ~~~~~~~~~~~~~ ~~~~ ~ ~~~ ~ ~~~ ~ ~~
  13. ~ ~~ ~ ~ ~~~~~~ ~~ ~~~ ~~ ~ ~~ ~~ ~
  14. ~ ~ ~ ~ ~ ~~ ~~~~~~ ~ ~~ ~ ~~
  15. ~ ~ ~ ~ ~~ ~ ~
  16. #+end_example
  17. Immutable GNU Emacs dotfiles, inspired by Doom, built for Liberty.
  18. + 100% Literate
  19. + 100% Immutable
  20. + 100% Reproducible
  21. #+begin_src emacs-lisp :tangle ~/.local/source/dotfiles/init.el :results silent
  22. (defun dotfiles/tangle (dir)
  23. "Recursively tangle the Org files within a directory."
  24. (interactive)
  25. (let ((org-files (directory-files-recursively dir "org")))
  26. (dolist (f org-files)
  27. (org-babel-tangle-file f))))
  28. #+end_src
  29. * Core
  30. :PROPERTIES:
  31. :header-args: :tangle ~/.local/source/dotfiles/init.el :results silent
  32. :END:
  33. Emacs creates a lot of files relative to ~user-emacs-directory~, these files are not part of this immutable configuration and do not belong in the emacs directory.
  34. #+begin_src emacs-lisp
  35. (defvar dotfiles/home user-emacs-directory)
  36. #+end_src
  37. How can we solve this issue?
  38. #+begin_src emacs-lisp
  39. (defvar dotfiles/cache "~/.cache/emacs")
  40. #+end_src
  41. Shortly after initialization, before most packages are loaded, we change the value to ~dotfiles/cache~. I elaborate more on the technique in my post [[https://chrishayward.xyz/posts/immutable_emacs/][Immutable Emacs]].
  42. #+begin_src emacs-lisp
  43. (setq user-emacs-directory dotfiles/cache)
  44. #+end_src
  45. Because this project uses version-control, we can disable more unwanted features:
  46. + Lock files
  47. + Backup files
  48. #+begin_src emacs-lisp
  49. (setq create-lockfiles nil
  50. make-backup-files nil)
  51. #+end_src
  52. ** Packages
  53. Download and install packages using [[https://github.com/raxod502/straight.el][straight.el]], a functional package manager that integrates with =use-package=, giving us more control over where packages are sourced from.
  54. + Use the development branch
  55. + Integrate with ~use-package~
  56. Apply the configurations prior to bootstrapping the package manager, by setting (writing) to the variables that =straight= will ultimately read from.
  57. #+begin_src emacs-lisp
  58. (setq straight-repository-branch "develop"
  59. straight-use-package-by-default t)
  60. #+end_src
  61. Bootstrap the package manager, downloading, installing, or configuring depending on the state of the configuration. All packages are downloaded and built from source, and can be pinned to specific git commit hashes.
  62. #+begin_src emacs-lisp
  63. (defvar bootstrap-version)
  64. (let ((bootstrap-file
  65. (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
  66. (bootstrap-version 5))
  67. (unless (file-exists-p bootstrap-file)
  68. (with-current-buffer
  69. (url-retrieve-synchronously
  70. "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
  71. 'silent 'inhibit-cookies)
  72. (goto-char (point-max))
  73. (eval-print-last-sexp)))
  74. (load bootstrap-file nil 'nomessage))
  75. #+end_src
  76. Complete the integration with ~use-package~ by installing it with =straight=.
  77. #+begin_src emacs-lisp
  78. (straight-use-package 'use-package)
  79. #+end_src
  80. ** Cleanup
  81. Despite having our *stateful* and *immutable* configurations seperate, it's good practice to make efforts to reduce the trash created by Emacs.
  82. Install [[https://github.com/emacscollective/no-littering][no-littering]] to reduce the files created by Emacs.
  83. #+begin_src emacs-lisp
  84. (use-package no-littering)
  85. #+end_src
  86. Emacs' default user interface is horrendous, but with less than 10 lines of code we can change that.
  87. #+begin_src emacs-lisp
  88. (setq inhibit-startup-message t)
  89. (global-prettify-symbols-mode)
  90. (scroll-bar-mode -1)
  91. (menu-bar-mode -1)
  92. (tool-bar-mode -1)
  93. (tooltip-mode -1)
  94. #+end_src
  95. ** Babel
  96. *Organize your plain life in plain text*
  97. [[https://orgmode.org][Org-mode]] is one of the hallmark features of Emacs, and provides the basis for my Literate Programming platform. It's essentially a markdown language with rich features for project management, scheduling, development, and writing. It's hard to convey everything within its capabilities.
  98. + [[https://orgmode.org/worg/org-contrib/babel/languages/index.html][Babel languages]]
  99. + [[https://orgmode.org/manual/Structure-Templates.html][Structure templates]]
  100. #+begin_src emacs-lisp
  101. (use-package org
  102. :hook
  103. (org-mode . (lambda ()
  104. (org-indent-mode)
  105. (visual-line-mode 1)
  106. (variable-pitch-mode 1)))
  107. :config
  108. (setq org-ellipsis " ▾"
  109. org-log-done 'time
  110. org-log-into-drawer t
  111. org-src-preserve-indentation t)
  112. (org-babel-do-load-languages
  113. 'org-babel-load-languages
  114. '((shell . t)
  115. (python . t)
  116. (emacs-lisp . t)))
  117. (require 'org-tempo)
  118. (add-to-list 'org-structure-template-alist '("s" . "src"))
  119. (add-to-list 'org-structure-template-alist '("q" . "quote"))
  120. (add-to-list 'org-structure-template-alist '("e" . "example"))
  121. (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
  122. (add-to-list 'org-structure-template-alist '("py" . "src python"))
  123. (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")))
  124. #+end_src
  125. ** Keys
  126. Make the =ESC= key quit (most) prompts, instead of the default =C-g=.
  127. #+begin_src emacs-lisp
  128. (global-set-key (kbd "<escape>") 'keyboard-escape-quit)
  129. #+end_src
  130. Functionality like =completion= and =hints= can be delayed to avoid popups for common manuevers. Adjust this value to your personal taste.
  131. #+begin_src emacs-lisp
  132. (defvar dotfiles/idle 0.0)
  133. #+end_src
  134. Download [[https://github.com/justbur/emacs-which-key][which-key]], a package that displays the current incomplete keybinding input in a mini-buffer, showing available completion options.
  135. #+begin_src emacs-lisp
  136. (use-package which-key
  137. :diminish which-key-mode
  138. :init (which-key-mode)
  139. :config (setq which-key-idle-delay dotfiles/idle))
  140. #+end_src
  141. Avoid the infamous *Emacs pinky* by binding =SPC= as a leader key, utilizing the thumb instead of the weaker pinky finger. You may change this value if you want to use something else.
  142. #+begin_src emacs-lisp
  143. (defvar dotfiles/leader-key "SPC")
  144. (defvar dotfiles/leader-key-global "C-SPC")
  145. #+end_src
  146. Implement the *leader* key mentioned above using [[https://github.com/noctuid/general.el][general.el]], letting us easily configure prefixed keybindings in a much cleaner manner than the default methods.
  147. #+begin_src emacs-lisp
  148. (use-package general
  149. :config
  150. (general-create-definer dotfiles/leader
  151. :states '(normal motion)
  152. :keymaps 'override
  153. :prefix dotfiles/leader-key
  154. :global-prefix dotfiles/leader-key-global))
  155. #+end_src
  156. Use [[https://github.com/abo-abo/hydra][hydra]] for transient keybindings sharing a common prefix.
  157. #+begin_src emacs-lisp
  158. (use-package hydra)
  159. #+end_src
  160. *** Evil
  161. After a few hour with =vim= I knew it was game over, I cannot even think of another way I would feel comfortable editing text. Luckily, there exist packages to emulate this within Emacs.
  162. https://evil.readthedocs.io/en/latest/index.html
  163. + Extendable VI layer for Emacs
  164. + Disable default keybindings
  165. #+begin_src emacs-lisp
  166. (use-package evil
  167. :init (setq evil-want-integration t
  168. evil-want-keybinding nil)
  169. :config (evil-mode 1))
  170. #+end_src
  171. https://github.com/emacs-evil/evil-collection
  172. + Community keybindings for =evil-mode=
  173. #+begin_src emacs-lisp
  174. (use-package evil-collection
  175. :after evil
  176. :config (evil-collection-init))
  177. #+end_src
  178. https://github.com/redguardtoo/evil-nerd-commenter
  179. + Toggle comments with =M-;=
  180. #+begin_src emacs-lisp
  181. (use-package evil-nerd-commenter
  182. :bind ("M-;" . evilnc-comment-or-uncomment-lines))
  183. #+end_src
  184. *** Shortcuts
  185. Again cherry picked from =Doom=, I want to continue utilizing the muscle memory I have developed from a year of mainlining the framework.
  186. + Close buffers with =SPC c=
  187. + Find files with =SPC . (period)=
  188. + Switch buffers with =SPC , (comma)=
  189. #+begin_src emacs-lisp
  190. (dotfiles/leader
  191. "." '(find-file :which-key "Files")
  192. "," '(switch-to-buffer :which-key "Buffers")
  193. "c" '(kill-buffer-and-window :which-key "Close"))
  194. #+end_src
  195. **** Help
  196. Run helper functions with =SPC h=.
  197. + Packages =p=
  198. + Variables =v=
  199. + Functions =f=
  200. #+begin_src emacs-lisp
  201. (dotfiles/leader
  202. "h" '(:ignore t :which-key "Help")
  203. "hp" '(describe-package :which-key "Package")
  204. "hv" '(describe-variable :which-key "Variable")
  205. "hf" '(describe-function :which-key "Function"))
  206. #+end_src
  207. **** Quit
  208. Quit emacs with =SPC q=.
  209. + Saving =q=
  210. + Without =w=
  211. + Frame (daemon) =f=
  212. #+begin_src emacs-lisp
  213. (dotfiles/leader
  214. "q" '(:ignore t :which-key "Quit")
  215. "qq" '(save-buffers-kill-emacs :which-key "Save")
  216. "qw" '(kill-emacs :which-key "Now")
  217. "qf" '(delete-frame :which-key "Frame"))
  218. #+end_src
  219. **** Windows
  220. Window management with =SPC w=.
  221. + Swap with =w=
  222. + Close with =c=
  223. + Motions with =h,j,k,l=
  224. + Split with =s + <MOTION>=
  225. #+begin_src emacs-lisp
  226. (dotfiles/leader
  227. "w" '(:ignore t :which-key "Window")
  228. "ww" '(window-swap-states :which-key "Swap")
  229. "wc" '(delete-window :which-key "Close")
  230. "wh" '(windmove-left :which-key "Left")
  231. "wj" '(windmove-down :which-key "Down")
  232. "wk" '(windmove-up :which-key "Up")
  233. "wl" '(windmove-right :which-key "Right")
  234. "ws" '(:ignore t :which-key "Split")
  235. "wsj" '(split-window-below :which-key "Down")
  236. "wsl" '(split-window-right :which-key "Right"))
  237. #+end_src
  238. * Editor
  239. :PROPERTIES:
  240. :header-args: :tangle ~/.local/source/dotfiles/init.el :results silent
  241. :END:
  242. *Bring Emacs out of the eighties*
  243. ** Git
  244. Another hallmark feature is [[https://github.com/magit/magit][Magit]], a complete git porcelain within Emacs.
  245. #+begin_src emacs-lisp
  246. (use-package magit
  247. :custom (magit-display-buffer-function
  248. #'magit-display-buffer-same-window-except-diff-v1))
  249. #+end_src
  250. Work directly with github issues / pull requests using [[https://github.com/magit/forge][Forge]].
  251. + Requires a valid ~$GITHUB_TOKEN~
  252. #+begin_src emacs-lisp
  253. (use-package forge)
  254. #+end_src
  255. Open the *status* page for the current repository with =SPC g=.
  256. #+begin_src emacs-lisp
  257. (dotfiles/leader
  258. "g" '(magit-status :which-key "Magit"))
  259. #+end_src
  260. ** Shell
  261. While not a traditional terminal emulator, =eshell= provides me with all of the functionality I expect and require from one. Some users may be left wanting more, I would recommend they look into =vterm=.
  262. https://github.com/zwild/eshell-prompt-extras
  263. + Enable lambda shell prompt
  264. #+begin_src emacs-lisp
  265. (use-package eshell-prompt-extras
  266. :config (setq eshell-highlight-prompt nil
  267. eshell-prompt-function 'epe-theme-lambda))
  268. #+end_src
  269. Open an =eshell= buffer with =SPC e=.
  270. #+begin_src emacs-lisp
  271. (dotfiles/leader
  272. "e" '(eshell :which-key "Shell"))
  273. #+end_src
  274. ** Files
  275. Emacs' can feel more modern when icon-fonts are installed and prioritized. I feel that this makes navigation of folders much faster, given that file types may be quickly identified by their corresponding icons.
  276. https://github.com/domtronn/all-the-icons.el
  277. + Collects various icon fonts
  278. #+begin_src emacs-lisp
  279. (use-package all-the-icons)
  280. #+end_src
  281. https://github.com/jtbm37/all-the-icons-dired
  282. + Integration with dired
  283. #+begin_src emacs-lisp
  284. (use-package all-the-icons-dired
  285. :hook (dired-mode . all-the-icons-dired-mode))
  286. #+end_src
  287. When opening =dired=, I don't want to have to press =RET= twice to navigate to the current directory. This can be avoided with ~dired-jump~, included in the =dired-x= package shipped with =dired=.
  288. #+begin_src emacs-lisp
  289. (require 'dired-x)
  290. #+end_src
  291. By default =dired= will create a new buffer everytime you press =RET= over a directory. In my workflow this leads to many unwanted =dired= buffers that have to be cleaned up manually. [[https://github.com/crocket/dired-single][Dired-single]] lets us reuse the same dired buffer.
  292. + Move up a directory with =h=
  293. + Open a single buffer with =l=
  294. #+begin_src emacs-lisp
  295. (use-package dired-single
  296. :config
  297. (evil-collection-define-key 'normal 'dired-mode-map
  298. "h" 'dired-single-up-directory
  299. "l" 'dired-single-buffer))
  300. #+end_src
  301. Open a dired buffer with =SPC d=.
  302. #+begin_src emacs-lisp
  303. (dotfiles/leader
  304. "d" '(dired-jump :which-key "Dired"))
  305. #+end_src
  306. ** Fonts
  307. Configure the system font with a single ~font-family~ and define the size, of which variations to the font size are relative to this value.
  308. #+begin_src emacs-lisp
  309. (defvar dotfiles/font "Fira Code")
  310. (defvar dotfiles/font-size 96)
  311. #+end_src
  312. Write out to all *3* of Emacs' default font faces.
  313. #+begin_src emacs-lisp
  314. (set-face-attribute 'default nil :font dotfiles/font :height dotfiles/font-size)
  315. (set-face-attribute 'fixed-pitch nil :font dotfiles/font :height dotfiles/font-size)
  316. (set-face-attribute 'variable-pitch nil :font dotfiles/font :height dotfiles/font-size)
  317. #+end_src
  318. Define a transient keybinding for scaling the text.
  319. #+begin_src emacs-lisp
  320. (defhydra hydra-text-scale (:timeout 4)
  321. "Scale"
  322. ("j" text-scale-increase "Increase")
  323. ("k" text-scale-decrease "Decrease")
  324. ("f" nil "Finished" :exit t))
  325. #+end_src
  326. Increase the font size in buffers with =SPC f=.
  327. + Increase =j=
  328. + Decrease =k=
  329. + Finish =f=
  330. #+begin_src emacs-lisp
  331. (dotfiles/leader
  332. "f" '(hydra-text-scale/body :which-key "Font"))
  333. #+end_src
  334. ** Lines
  335. Relative line numbers are important when using =VI= emulation keys. You can prefix most commands with a *number*, allowing you to jump up / down by a line count.
  336. #+begin_example
  337. 5:
  338. 4:
  339. 3:
  340. 2:
  341. 1:
  342. 156: << CURRENT LINE >>
  343. 1:
  344. 2:
  345. 3:
  346. 4:
  347. 5:
  348. #+end_example
  349. https://github.com/emacsmirror/linum-relative
  350. + Integrate with ~display-line-numbers-mode~ for performance
  351. #+begin_src emacs-lisp
  352. (use-package linum-relative
  353. :init (setq linum-relative-backend
  354. 'display-line-numbers-mode)
  355. :config (linum-relative-global-mode))
  356. #+end_src
  357. https://github.com/Fanael/rainbow-delimiters
  358. + Colourize nested parenthesis
  359. #+begin_src emacs-lisp
  360. (use-package rainbow-delimiters
  361. :hook (prog-mode . rainbow-delimiters-mode))
  362. #+end_src
  363. ** Themes
  364. Bring Emacs' out of the eighties by cherry picking a few modules from =Doom=.
  365. https://github.com/hlissner/emacs-doom-themes
  366. + Modern colour themes
  367. #+begin_src emacs-lisp
  368. (use-package doom-themes
  369. :init (load-theme 'doom-moonlight t))
  370. #+end_src
  371. #+begin_src emacs-lisp
  372. (dotfiles/leader
  373. "t" '(load-theme t nil :which-key "Themes"))
  374. #+end_src
  375. https://github.com/seagle0128/doom-modeline
  376. + Elegant status bar / modeline
  377. #+begin_src emacs-lisp
  378. (use-package doom-modeline
  379. :init (doom-modeline-mode 1)
  380. :custom ((doom-modeline-height 16)))
  381. #+end_src
  382. * Desktop
  383. :PROPERTIES:
  384. :header-args: :tangle ~/.local/source/dotfiles/init.el :results silent
  385. :END:
  386. I use Emacs as a Desktop Environment with the [[https://github.com/ch11ng/exwm][exwm]] package. It allows Emacs to function as a complete tiling window manager for =X11=.
  387. #+begin_src emacs-lisp
  388. (defun dotfiles/run (command)
  389. "Run an external process."
  390. (interactive (list (read-shell-command "λ ")))
  391. (start-process-shell-command command nil command))
  392. #+end_src
  393. Some methods must be called and applied to the current call process in order to function correctly with Emacs hooks.
  394. #+begin_src emacs-lisp
  395. (defun dotfiles/run-in-background (command)
  396. (let ((command-parts (split-string command "[ ]+")))
  397. (apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts)))))
  398. #+end_src
  399. Setting the wallpaper is one example; this must occur every time the screen change hook is called.
  400. #+begin_src emacs-lisp
  401. ;; (defun dotfiles/set-wallpaper (path)
  402. ;; (interactive)
  403. ;; (when (file-exists-p path)
  404. ;; (let ((command (concat "feh --bg-scale " path)))
  405. ;; (start-process-shell-command "feh" nil command))))
  406. #+end_src
  407. #+begin_src emacs-lisp
  408. (dotfiles/leader
  409. "r" '(dotfiles/run :which-key "Run"))
  410. #+end_src
  411. When the window manager first launches the ~init-hook~ will be called, this allows us to define some custom logic when it's initialized.
  412. + Display time and date
  413. + Display battery info (if available)
  414. In my personal configuration, I do not want the battery or time displayed within Emacs when it's not running as desktop environment because that information is typically already available.
  415. #+begin_src emacs-lisp
  416. (defun dotfiles/init-hook ()
  417. (exwm-workspace-switch-create 1)
  418. (setq display-time-and-date t)
  419. (display-battery-mode 1)
  420. (display-time-mode 1))
  421. #+end_src
  422. Using =autorandr= with pre configured profiles, switching screens (AKA hot plugging) is also handled through a hook.
  423. #+begin_src emacs-lisp
  424. (defun dotfiles/update-display ()
  425. (dotfiles/run-in-background "autorandr --change --force"))
  426. ;; (dotfiles/set-wallpaper "TODO"))
  427. #+end_src
  428. Finally we configure the window manager.
  429. + Enable =randr= support
  430. Connect our custom hooks and configure the input keys, a custom layer for defining which keys are captured by Emacs, and which are passed through to =X= applications.
  431. + Pass through to Emacs
  432. + =M-x= to Emacs
  433. + =C-g= to Emacs
  434. + =C-SPC= to Emacs
  435. + Bindings with =S= (Super / Win)
  436. + Reset =S-r=
  437. + Launch =S-&=
  438. + Workspace =S-[1..9]=
  439. #+begin_src emacs-lisp
  440. (use-package exwm
  441. :config
  442. (require 'exwm-randr)
  443. (exwm-randr-enable)
  444. (add-hook 'exwm-init-hook #'dotfiles/init-hook)
  445. (add-hook 'exwm-randr-screen-change-hook #'dotfiles/update-display)
  446. (dotfiles/update-display)
  447. (setq exwm-input-prefix-keys
  448. '(?\M-x
  449. ?\C-g
  450. ?\C-\ )
  451. exwm-input-global-keys
  452. `(([?\s-r] . exwm-reset)
  453. ([?\s-&] . dotfiles/run)
  454. ,@(mapcar (lambda (i)
  455. `(,(kbd (format "s-%d" i)) .
  456. (lambda ()
  457. (interactive)
  458. (exwm-workspace-switch-create ,i))))
  459. (number-sequence 1 9))))
  460. (exwm-enable))
  461. #+end_src
  462. * Writing
  463. :PROPERTIES:
  464. :header-args: :tangle ~/.local/source/dotfiles/init.el :results silent
  465. :END:
  466. I am using [[https://orgmode.org][Org-mode]] extensively for writing projects for different purposes. Improvements beyond what are required for my Literate Programming platform include:
  467. [[https://github.com/integral-dw/org-superstar-mode][Org-superstar-mode]] for making headline stars more *super*.
  468. #+begin_src emacs-lisp
  469. (use-package org-superstar
  470. :hook (org-mode . org-superstar-mode))
  471. #+end_src
  472. ** Mail
  473. #+begin_src emacs-lisp
  474. ;; (add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e")
  475. #+end_src
  476. #+begin_src emacs-lisp
  477. (use-package mu4e
  478. :load-path "/usr/share/emacs/site-lisp/mu4e"
  479. :config
  480. (setq mu4e-change-filenames-when-moving t
  481. mu4e-update-interval (* 5 60) ;; Every 5 minutes.
  482. mu4e-get-mail-command "mbsync -a"
  483. mu4e-maildir "~/.cache/mail"
  484. mu4e-compose-signature
  485. (concat "Chris Hayward\n"
  486. "https://chrishayward.xyz\n"))
  487. ;; Ensure plain text scales for all devices.
  488. (setq mu4e-compose-format-flowed t)
  489. ;; GPG signing key for outbound mail.
  490. (setq mml-secure-openpgp-signers '("37AB1CB72B741E478CA026D43025DCBD46F81C0F"))
  491. (add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime)
  492. (setq message-send-mail-function 'smtpmail-send-it)
  493. ;; Configure mail account(s).
  494. (setq mu4e-contexts
  495. (list
  496. ;; Main
  497. ;; chris@chrishayward.xyz
  498. (make-mu4e-context
  499. :name "Main"
  500. :match-func
  501. (lambda (msg)
  502. (when msg
  503. (string-prefix-p "/Main" (mu4e-message-field msg :maildir))))
  504. :vars
  505. '((user-full-name . "Christopher James Hayward")
  506. (user-mail-address . "chris@chrishayward.xyz")
  507. (smtpmail-smtp-server . "mail.chrishayward.xyz")
  508. (smtpmail-smtp-service . 587)
  509. (smtpmail-stream-type . starttls))))))
  510. #+end_src
  511. #+begin_src emacs-lisp
  512. (dotfiles/leader
  513. "m" '(mu4e :which-key "Mail"))
  514. #+end_src
  515. ** Brain
  516. [[https://github.com/org-roam/org-roam][Org-roam]] is a rudimentary roam replica built on =Org mode=.
  517. #+begin_src emacs-lisp
  518. (use-package org-roam
  519. :hook (after-init . org-roam-mode)
  520. :custom (org-roam-directory "~/.local/source/brain"))
  521. #+end_src
  522. [[https://github.com/org-roam/org-roam-server][Org-roam-server]] is a web application that visualizes the =Org roam= database, available when Emacs' running at [[http://localhost:8080][localhost:8080]].
  523. #+begin_src emacs-lisp
  524. (use-package org-roam-server
  525. :hook (org-roam-mode . org-roam-server-mode))
  526. #+end_src
  527. Configure keybindings behind =SPC b=.
  528. + Find with =f=
  529. + Buffer with =b=
  530. + Capture with =c=
  531. + Dailies with =d=
  532. #+begin_src emacs-lisp
  533. (dotfiles/leader
  534. "b" '(:ignore t :which-key "Roam")
  535. "bf" '(org-roam-find-file :which-key "Find")
  536. "bb" '(org-roam-buffer-toggle-display :which-key "Buffer")
  537. "bc" '(org-roam-capture :which-key "Capture")
  538. "bd" '(:ignore t :which-key "Dailies")
  539. "bdd" '(org-roam-dailies-find-date :which-key "Date")
  540. "bdt" '(org-roam-dailies-find-today :which-key "Today")
  541. "bdm" '(org-roam-dailies-find-tomorrow :which-key "Tomorrow")
  542. "bdy" '(org-roam-dailies-find-yesterday :which-key "Yesterday"))
  543. #+end_src
  544. Configure the default capture template for new topics.
  545. #+begin_src emacs-lisp
  546. (setq org-roam-capture-templates
  547. '(("d" "Default" plain (function org-roam-capture--get-point)
  548. "%?"
  549. :file-name "${slug}"
  550. :head "#+TITLE: ${title}\n"
  551. :unnarrowed t)))
  552. #+end_src
  553. Configure the default capture template for daily entries.
  554. #+begin_src emacs-lisp
  555. (setq org-roam-dailies-capture-templates
  556. '(("d" "Default" entry (function org-roam-capture--get-point)
  557. "* %?"
  558. :file-name "daily/%<%Y-%m-%d>"
  559. :head "#+TITLE: %<%Y-%m-%d>\n")))
  560. #+end_src
  561. ** Notes
  562. #+begin_src emacs-lisp
  563. (defvar dotfiles/bib "~/.local/source/brain/resources.bib")
  564. (defvar dotfiles/notes "~/.local/source/brain/notes/")
  565. #+end_src
  566. #+begin_src emacs-lisp
  567. (use-package org-noter
  568. :after org
  569. :config
  570. (setq org-noter-always-create-frame nil
  571. org-noter-notes-search-path dotfiles/notes))
  572. #+end_src
  573. #+begin_src emacs-lisp
  574. (use-package org-pdftools
  575. :hook (org-mode . org-pdftools-setup-link))
  576. #+end_src
  577. #+begin_src emacs-lisp
  578. (use-package org-noter-pdftools
  579. :after org-noter
  580. :config
  581. (with-eval-after-load 'pdf-annot
  582. (add-hook 'pdf-annot-active-handler-functions #'org-noter-pdftools-jump-to-note)))
  583. #+end_src
  584. #+begin_src emacs-lisp
  585. (setq bibtex-completion-notes-path dotfiles/notes
  586. bibtex-completion-bibliography dotfiles/bib
  587. bibtex-completion-pdf-field "file"
  588. bibtex-completion-notes-template-multiple-files
  589. (concat
  590. "#+TITLE: ${title}\n"
  591. "#+ROAM_KEY: cite:${=key=}\n"
  592. "#* TODO Notes\n"
  593. ":PROPERTIES:\n"
  594. ":CUSTOM_ID: ${=key}\n"
  595. ":NOTER_DOCUMENT: %(orb-process-file-field \"${=key=}\")\n"
  596. ":AUTHOR: ${author-abbrev}\n"
  597. ":JOURNAL: ${journaltitle}\n"
  598. ":DATE: ${date}\n"
  599. ":YEAR: ${year}\n"
  600. ":DOI: ${doi}\n"
  601. ":URL: ${url}\n"
  602. ":END:\n\n"))
  603. #+end_src
  604. #+begin_src emacs-lisp
  605. (use-package org-ref
  606. :config
  607. (setq org-ref-completion-library 'org-ref-helm-cite
  608. org-ref-get-pdf-filename-function 'org-refg-get-pdf-filename-helm-bibtex
  609. org-ref-default-bibliography dotfiles/bib
  610. org-ref-bibliography-notes dotfiles/notes
  611. org-ref-notes-directory dotfiles/notes
  612. org-ref-notes-function 'orb-edit-notes
  613. org-ref-note-title-format "* TODO %y - %t\n:PROPERTIES:\n:CUSTOM_ID: %k\n:NOTER_DOCUMENT: %F\n:ROAM_KEY: cite:%k\n:AUTHOR: %9a\n:JOURNAL: %j\n:YEAR: %y\n:VOLUME: %v\n:PAGES: %p\n:DOI: %D\n:URL: %U\n:END:\n\n"))
  614. #+end_src
  615. #+begin_src emacs-lisp
  616. (use-package org-roam-bibtex
  617. :after (org-roam)
  618. :hook (org-roam-mode . org-roam-bibtex-mode)
  619. :config
  620. (setq orb-preformat-keywords
  621. '("=key=" "title" "url" "file" "author-or-editor" "keywords")))
  622. #+end_src
  623. #+begin_src emacs-lisp
  624. (add-to-list 'org-roam-capture-templates
  625. '("n" "Notes" plain (function org-roam-capture--get-point)
  626. ""
  627. :file-name "notes/${slug}"
  628. :head "#+TITLE: ${=key=}: ${title}\n#+ROAM_KEY:${ref}\n\n* ${title} :PROPERTIES:\n:CUSTOM_ID: ${=key=}\n:URL: ${url}\n:AUTHOR: ${author-or-editor}\n:NOTER_DOCUMENT:%(orb-process-file-field \"${=key=}\")\n:NOTER_PAGE:\n:END:\n\n"))
  629. #+end_src
  630. ** Agenda
  631. Configure agenda sources.
  632. + Dailies ~~/.local/source/brain/daily/~
  633. + Secrets ~~/.local/source/secrets/org/~
  634. #+begin_src emacs-lisp
  635. (setq org-agenda-files '("~/.local/source/brain/daily/"
  636. "~/.local/source/secrets/org/"))
  637. #+end_src
  638. Open an agenda buffer with =SPC a=.
  639. #+begin_src emacs-lisp
  640. (dotfiles/leader
  641. "a" '(org-agenda :which-key "Agenda"))
  642. #+end_src
  643. ** Blogging
  644. I use [[https://gohugo.io][Hugo]] for my personal [[https://chrishayward.xyz][website]], which I write in =Org-mode= before compiling to =hugo-markdown=.
  645. [[https://github.com/kaushalmodi/ox-hugo][Ox-hugo]], configured for =one-post-per-file= is my technique for managing my blog.
  646. #+begin_src emacs-lisp
  647. (use-package ox-hugo
  648. :after ox)
  649. #+end_src
  650. Creaate a capture template for blog posts in the =posts= sub directory.
  651. #+begin_src emacs-lisp
  652. (add-to-list 'org-roam-capture-templates
  653. '("b" "Blogging" plain (function org-roam-capture--get-point)
  654. "%?"
  655. :file-name "posts/${slug}"
  656. :head "#+TITLE: ${title}\n#+HUGO_BASE_DIR: ~/.local/source/website\n#+HUGO_SECTION: posts\n"))
  657. #+end_src
  658. ** Screencasts
  659. Create screencasts with =one-frame-per-action= GIF recording via [[https://github.com/takaxp/emacs-gif-screencast][emacs-gif-screencast]].
  660. + Can be paused / resumed
  661. + High quality images
  662. + Optimized size
  663. It requires the installation of ~scrot~ and ~convert~ from the =ImageMagick= library.
  664. #+begin_src emacs-lisp
  665. (use-package gif-screencast
  666. :custom
  667. (gif-screencast-output-directory "~/.local/source/brain/screen/"))
  668. #+end_src
  669. Screencast controls behind =SPC s=.
  670. + Start / stop with =s=
  671. + Pause with =t=
  672. #+begin_src emacs-lisp
  673. (dotfiles/leader
  674. "s" '(:ignore t :which-key "Screencast")
  675. "ss" '(gif-screencast-start-or-stop :which-key "Start / Stop")
  676. "sp" '(gif-screencast-toggle-pause :which-key "Pause"))
  677. #+end_src
  678. ** Presentations
  679. Produce high quality presentations that work anywhere with =HTML/JS= and the [[https://revealjs.com][Reveal.js]] package.
  680. [[https://github.com/hexmode/ox-reveal][Ox-reveal]], configured to use a =cdn= allows us to produce ones that are not dependent on a local version of =Reveal.js=.
  681. #+begin_src emacs-lisp
  682. (use-package ox-reveal
  683. :after ox
  684. :custom (org-reveal-root "https://cdn.jsdelivr.net/reveal.js/3.9.2/"))
  685. #+end_src
  686. Create a capture template for presentations stored in the =slides= sub directory.
  687. #+begin_src emacs-lisp
  688. (add-to-list 'org-roam-capture-templates
  689. '("p" "Presentation" plain (function org-roam-capture--get-point)
  690. "%?"
  691. :file-name "slides/${slug}"
  692. :head "#+TITLE: ${title}\n"))
  693. #+end_src
  694. * Development
  695. :PROPERTIES:
  696. :header-args: :tangle ~/.local/source/dotfiles/init.el :results silent
  697. :END:
  698. An IDE like experience (or better) can be achieved in Emacs using two *Microsoft* open source initiatives.
  699. Turn Emacs into an *IDE* (or better) with the [[https://microsoft.github.io/language-server-protocol/][Language Server Protocol]], an open source initiative from *Microsoft* for the *VSCode* editor.
  700. [[https://emacs-lsp.github.io/lsp-mode/][Lsp-mode]] brings support for language servers into Emacs.
  701. #+begin_src emacs-lisp
  702. (use-package lsp-mode
  703. :custom (gc-cons-threshold 1000000000)
  704. (lsp-idle-delay 0.500))
  705. #+end_src
  706. https://emacs-lsp.github.io/lsp-ui/
  707. + UI improvements for =lsp-mode=
  708. #+begin_src emacs-lisp
  709. (use-package lsp-ui
  710. :custom (lsp-ui-doc-position 'at-point)
  711. (lsp-ui-doc-delay 0.500))
  712. #+end_src
  713. ** Passwords
  714. Pass makes managing passwords extremely easy, encrypring them in a file structure and providing easy commands for generating, modify, and copying passwords. =password-store.el= provides a wrapper for the functionality within Emacs.
  715. #+begin_src emacs-lisp
  716. (use-package password-store
  717. :custom (password-store-dir "~/.local/source/passwords"))
  718. #+end_src
  719. Configure keybindings behind =SPC p=.
  720. + Copy with =p=
  721. + Rename with =r=
  722. + Generate with =g=
  723. #+begin_src emacs-lisp
  724. (dotfiles/leader
  725. "p" '(:ignore t :which-key "Passwords")
  726. "pp" '(password-store-copy :which-key "Copy")
  727. "pr" '(password-store-rename :which-key "Rename")
  728. "pg" '(password-store-generate :which-key "Generate"))
  729. #+end_src
  730. ** Debugging
  731. Handled through the [[https://microsoft.github.io/debug-adapter-protocol/][Debug Adapter Protocol]], an open source initiative from *Microsoft* for the *VSCode* editor.
  732. [[https://emacs-lsp.github.io/dap-mode/][Dap-mode]] adds support for the protocol to Emacs.
  733. #+begin_src emacs-lisp
  734. (use-package dap-mode)
  735. #+end_src
  736. ** Completion
  737. Text completion framework via =company= aka *Complete Anything*.
  738. http://company-mode.github.io/
  739. + Integrate with =lsp-mode=
  740. #+begin_src emacs-lisp
  741. (use-package company)
  742. (use-package company-lsp)
  743. #+end_src
  744. ** Languages
  745. Support for individual languages are implemented here.
  746. *** C/C++
  747. Full *IDE* experience for Python within Emacs.
  748. + Completion, jumps via =lsp-mode=
  749. + Debugging via =dap-mode=
  750. Install the =ccls= language server.
  751. + https://github.com/MaskRay/ccls
  752. #+begin_src emacs-lisp
  753. (use-package ccls
  754. :hook ((c-mode c++-mode objc-mode cuda-mode) .
  755. (lambda () (require 'ccls) (lsp))))
  756. #+end_src
  757. *** Python
  758. Full *IDE* experience for Python within Emacs.
  759. + Completion, jumps via =lsp-mode=
  760. + Debugging via =dap-mode=
  761. Install the =pyls= language server.
  762. #+begin_src shell :tangle no
  763. pip install --user "python-language-server[all]"
  764. #+end_src
  765. https://www.emacswiki.org/emacs/PythonProgrammingInEmacs
  766. + Built in mode
  767. #+begin_src emacs-lisp
  768. (use-package python-mode
  769. :hook (python-mode . lsp)
  770. :config (require 'dap-python)
  771. :custom (python-shell-interpreter "python3") ;; Required if "python" is not python 3.
  772. (dap-python-executable "python3") ;; Same as above.
  773. (dap-python-debugger 'debugpy))
  774. #+end_src
  775. *** Rust
  776. Full *IDE* experience for Rust within Emacs.
  777. + Completion via =lsp-mode=
  778. + Debugging via =dap-mode=
  779. https://github.com/brotzeit/rustic
  780. + Install via ~lsp-install-server~
  781. #+begin_src shell :tangle no
  782. rustup default nightly
  783. #+end_src
  784. #+begin_src emacs-lisp
  785. (use-package rustic)
  786. #+end_src
  787. *** Go
  788. Full *IDE* experience for Rust within Emacs.
  789. + Completion via =lsp-mode=
  790. + Debugging via =dap-mode=
  791. Install the =gopls= language server.
  792. #+begin_src sh :tangle no
  793. GO111MODULE=on go get golang.org/x/tools/gopls@latest
  794. #+end_src
  795. #+begin_src emacs-lisp
  796. (use-package go-mode
  797. :hook (go-mode . lsp))
  798. #+end_src
  799. Apply some custom behaviour before saving:
  800. + Format buffer
  801. + Organize imports
  802. #+begin_src emacs-lisp
  803. (defun dotfiles/go-hook ()
  804. (add-hook 'before-save-hook #'lsp-format-buffer t t)
  805. (add-hook 'before-save-hook #'lsp-organize-imports t t))
  806. #+end_src
  807. #+begin_src emacs-lisp
  808. (add-hook 'go-mode-hook #'dotfiles/go-hook)
  809. #+end_src