├── .gitattributes
├── .github
└── workflows
│ ├── Dockerfile
│ ├── image-trigger.yml
│ ├── image.yml
│ └── publish.yml
├── .gitignore
├── LICENSE
├── README.org
├── archive.org
├── cli.el
├── config.org
├── eshell
└── aliases
├── exwm
├── picom.conf
├── start-debug.sh
└── start.sh
├── images
├── background.png
├── banner.png
├── kill-process.png
├── overview.png
└── tray.png
├── init.el
├── modules
├── checkers
│ └── jinx
│ │ ├── config.el
│ │ └── packages.el
├── completion
│ └── corfu
│ │ ├── autoload
│ │ ├── corfu.el
│ │ ├── extra.el
│ │ └── minad-capfs.el
│ │ ├── config.el
│ │ └── packages.el
├── lang
│ └── sql
│ │ ├── autoload
│ │ └── sql.el
│ │ ├── config.el
│ │ ├── doctor.el
│ │ └── packages.el
└── ui
│ ├── exwm
│ ├── README.org
│ ├── cli.el
│ ├── config.el
│ ├── doctor.el
│ ├── init.el
│ └── packages.el
│ ├── perspective
│ ├── README.org
│ ├── autoload
│ │ └── session.el
│ ├── config.el
│ └── packages.el
│ └── tab-bar
│ ├── README.org
│ └── config.el
├── org
├── static
├── index.css
└── index.js
└── templates
└── roam-default.org
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.org linguist-detectable=true
2 |
--------------------------------------------------------------------------------
/.github/workflows/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM archlinux:base
2 |
3 | RUN pacman -Sy --noconfirm emacs tectonic ripgrep git gcc sqlite rsync
4 |
5 | RUN rm -rfv /var/lib/pacman/sync/* &&\
6 | rm -rfv /usr/share/man/* &&\
7 | rm -rfv /usr/share/doc/*
8 |
9 | RUN mkdir -p ~/.config
10 | RUN git clone https://github.com/doomemacs/doomemacs ~/.config/emacs
11 | RUN git clone https://github.com/elken/doom ~/.config/doom
12 | RUN ~/.config/emacs/bin/doom sync
13 |
--------------------------------------------------------------------------------
/.github/workflows/image-trigger.yml:
--------------------------------------------------------------------------------
1 | name: Check for new Doom commits
2 |
3 | on:
4 | schedule:
5 | - cron: "30 5 * * *"
6 |
7 | workflow_dispatch:
8 | jobs:
9 | check:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 | - run: exit $(curl -s https://api.github.com/repos/doomemacs/doomemacs/commits/master | jq -r "((now - (.commit.committer.date | fromdateiso8601) ) / (60*60*24) | trunc)")
15 |
--------------------------------------------------------------------------------
/.github/workflows/image.yml:
--------------------------------------------------------------------------------
1 | name: "Build Image"
2 | on:
3 | workflow_run:
4 | workflows: ["Check for new Doom commits"]
5 | types: [completed]
6 |
7 | workflow_dispatch:
8 | inputs:
9 | override:
10 | description: "Manually build image?"
11 | type: "boolean"
12 | jobs:
13 | build-image:
14 | runs-on: ubuntu-20.04
15 | if: ${{ github.event.workflow_run.conclusion == 'success' || github.event.inputs.override }}
16 | steps:
17 | - name: Check out the repo
18 | uses: actions/checkout@v2
19 |
20 | - name: Set up Docker Buildx
21 | uses: docker/setup-buildx-action@v1
22 |
23 | - name: Login to the GitHub Container Registry
24 | uses: docker/login-action@v1.14.1
25 | with:
26 | registry: ghcr.io
27 | username: ${{ github.repository_owner }}
28 | password: ${{ secrets.GITHUB_TOKEN }}
29 |
30 | - name: Push to Docker Hub
31 | uses: docker/build-push-action@v2
32 | with:
33 | context: .github/workflows
34 | tags: ghcr.io/elken/doom:latest
35 | push: true
36 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: "Publish"
2 | on:
3 | push:
4 | branches: master
5 |
6 | permissions:
7 | contents: write
8 | pages: write
9 | id-token: write
10 |
11 | concurrency:
12 | group: "pages"
13 | cancel-in-progress: true
14 |
15 | jobs:
16 | deploy:
17 | runs-on: ubuntu-20.04 # change to -latest when possible
18 | container:
19 | image: ghcr.io/elken/doom:latest
20 | credentials:
21 | username: ${{ github.actor }}
22 | password: ${{ secrets.GITHUB_TOKEN }}
23 |
24 | steps:
25 | - uses: actions/checkout@v2
26 |
27 | - name: Get Doom's version
28 | id: doom-version
29 | run: |
30 | git config --global --add safe.directory /__w/doom/doom
31 | echo "doom_hash=$(git log -1 | head -1 | awk '"'"'{print substr($2,1,8)}'"'"')" >> $GITHUB_ENV
32 |
33 | - name: Cache Doom's Install
34 | id: doomcache
35 | uses: actions/cache@v2
36 | with:
37 | path: /root/.config/emacs
38 | key: ${{ runner.os }}-doom@${{ env.doom_hash }}
39 |
40 | - name: Export config
41 | env:
42 | DOOMDIR: /__w/doom/doom
43 | run: |
44 | /root/.config/emacs/bin/doom sync -u
45 | /root/.config/emacs/bin/doom publish
46 |
47 | - name: 🚀 Deploy
48 | uses: JamesIves/github-pages-deploy-action@4.1.6
49 | with:
50 | branch: gh-pages
51 | folder: out
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled
2 | *.elc
3 |
4 | # Packaging
5 | .cask
6 |
7 | # Backup files
8 | *~
9 |
10 | # Undo-tree save-files
11 | *.~undo-tree
12 |
13 | # Ignore tangled files
14 | /config.el
15 | /packages.el
16 |
17 | # Random emacs files
18 | custom.el
19 |
20 | /README.org
21 |
22 | .env*
23 | .DS_Store
24 | *.tex
25 | *.pdf
26 | /autoloads.el
27 | /*.html
28 | /snippets/
29 |
30 | config-local.el
31 | /.dict
32 | out/
33 | /profiles/
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ellis Kenyő
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | config.org
--------------------------------------------------------------------------------
/archive.org:
--------------------------------------------------------------------------------
1 | #+title: Archive
2 |
3 | A listing of various snippets no longer in use but maintain for historical significance.
4 |
5 | * Archive
6 | ** EXWM Setup
7 | One of the former usecases for this config is =exwm= which was pulled from a number of files in the current environment. Loading =exwm= is a simple case of enabling the exwm module, although it's /wildly/ untested and is basically hardcoded to just work for me. Any input is welcome, although unlikely to make it into /this/ module (until I stop being lazy and attempt to make it better for upstream....)
8 |
9 | Due to a number of issues with Emacs itself though (mostly around the single-threaded nature and the ease of which you can lock up your whole WM), this has been abandoned in favour of Awesomewm.
10 |
11 | *** modules/ui/exwm
12 | The primary lisp file where the bulk of the configuration is held, with everything from my process manager to a now-playing segment. Below are some usage screenshots.
13 | Standard doom module layout, nothing fishy going on. For those unfamiliar,
14 |
15 | - =init.el= is loaded before anything else really, which is important to properly check if the flag exists to load the exwm code as early as possible
16 | - =config.el= is the main bread and butter, all the config lives here (surprisingly)
17 | - =doctor.el= is currently just used for detecting missing exe's, by plugging into =doom doctor=
18 | - =packages.el= is a list of extra packages to be installed by doom's package manager
19 |
20 | [[file:images/kill-process.png]]
21 |
22 | [[file:images/tray.png]]
23 |
24 | Transparency is handled both through [[*GUI/Frame][Doom]] and via [[file:exwm/picom.conf][picom]].
25 |
26 | *** Session
27 | For the sake of simplicity, I use a slightly modified version of [[https://github.com/WJCFerguson/exwm-gnome-flashback][GNOME Flashback]] to run the startup scripts. It also gives me ootb access to things like =pinentry=, the various password stores, =gnome-screensaver= lock screen and the useful screenshot tool.
28 |
29 | As such, everything is themed around [[https://nordtheme.com][Nord]].
30 |
31 | Over time and due to various issues, I have been migrating to a plain =exwm= session but I haven't yet settled on the best approach.
32 |
33 | *** start.sh / start-debug.sh
34 | The scripts responsible for starting up exwm in the right way, including env variables and picom.
35 | ** Tab-bar-mode
36 | Acting as a global modeline (of sorts), this could end up being quite useful but
37 | for now this is just testing.
38 |
39 | Due to issues with window managers and no longer needing the extra info, the
40 | tab-bar-mode setup has been archived.
41 |
42 | #+begin_src emacs-lisp
43 | (defun reaper-get-running-timer-duration ()
44 | "Return duration of current running timer."
45 | (reaper-with-buffer
46 | (when reaper-running-timer
47 | (reaper--hours-to-time
48 | (let ((entry (assoc reaper-running-timer reaper-timeentries)))
49 | (cdr (assoc :hours entry)))))))
50 |
51 | (defun reaper-modeline-vanilla ()
52 | "Return modeline string for vanilla emacs."
53 | (when (and (string= "" reaper-api-key)
54 | (string= "" reaper-account-id))
55 | ;; TODO Make this a function
56 | (dotenv-update-project-env doom-user-dir)
57 | (customize-set-variable 'reaper-api-key (getenv "REAPER_API_KEY"))
58 | (customize-set-variable 'reaper-account-id (getenv "REAPER_ACCOUNT_ID")))
59 | (when-let ((note (reaper-get-running-timer-note))
60 | (timer (reaper-get-running-timer-duration)))
61 | (format " [ %s | %s] " (string-trim note) timer)))
62 |
63 | (defvar reaper-modeline--timer nil)
64 | (defun reaper-modeline-timer ()
65 | "Start/stop the time for updating reaper doom-modeline segment."
66 | (if (timerp reaper-modeline--timer)
67 | (cancel-timer reaper-modeline--timer))
68 | (setq reaper-modeline--timer
69 | (run-with-timer
70 | 1
71 | 60
72 | (lambda ()
73 | (reaper-with-buffer
74 | (setq reaper-timeentries nil)
75 | (reaper-refresh-entries))))))
76 |
77 | (after! (reaper dotenv)
78 | (unless (and (or (null reaper-api-key) (string= "" reaper-api-key))
79 | (or (null reaper-account-id) (string= "" reaper-account-id)))
80 | (reaper-modeline-timer)
81 | (add-to-list 'global-mode-string '(:eval (reaper-modeline-vanilla)))))
82 |
83 | ;; (customize-set-variable 'tab-bar-format '(tab-bar-format-global))
84 | ;; (customize-set-variable 'tab-bar-mode t)
85 | ;; (when tab-bar-mode
86 | ;; (setq display-time-format " [ %H:%M %d/%m/%y]"
87 | ;; display-time-default-load-average nil)
88 | ;; (display-time-mode 1))
89 | #+end_src
90 |
91 |
92 | ** Languages
93 | *** Dart
94 | Simple hook/project mode to update translations on save.
95 |
96 | #+begin_src emacs-lisp
97 | (after! lsp-dart
98 | (customize-set-variable 'lsp-dart-dap-extension-version "3.46.1")
99 |
100 | (defun lsp-dart--intl-update ()
101 | "Update translations on save."
102 | (when (and +dart-intl-mode
103 | buffer-file-name
104 | (string-search "lib/l10n" buffer-file-name))
105 | (lsp-dart--run-command (lsp-dart-flutter-command) "pub run intl_utils:generate")))
106 |
107 | (def-project-mode! +dart-intl-mode
108 | :modes '(json-mode)
109 | :files (and "pubspec.yaml" "lib/l10n")
110 | :on-enter (add-hook 'after-save-hook #'lsp-dart--intl-update)
111 | :on-exit (remove-hook 'after-save-hook #'lsp-dart--intl-update))
112 |
113 | (setq lsp-dart-dap-flutter-hot-reload-on-save t))
114 | #+end_src
115 |
116 | *** Rust
117 | Rust is the hot new thing, guess I better jump on the bandwagon if I want to stay cool.
118 |
119 | #+begin_src emacs-lisp
120 | (after! lsp-mode
121 | (setq lsp-rust-analyzer-display-parameter-hints t
122 | lsp-rust-analyzer-server-display-inlay-hints t))
123 | #+end_src
124 |
125 | *** C#
126 | Projectile doesn't recognise these projects properly, so we have to fix that
127 |
128 | #+begin_src emacs-lisp
129 | (after! projectile
130 | (defun projectile-dotnet-project-p ()
131 | "Check if a project contains a *.sln file at the project root, or either
132 | a .csproj or .fsproj file at either the project root or within src/*/."
133 | (or (projectile-verify-file-wildcard "?*.sln")
134 | (projectile-verify-file-wildcard "?*.csproj")
135 | (projectile-verify-file-wildcard "src/*/?*.csproj")
136 | (projectile-verify-file-wildcard "?*.fsproj")
137 | (projectile-verify-file-wildcard "src/*/?*.fsproj"))))
138 | #+end_src
139 |
140 | *** Ignore files in xref
141 | PHP is a dumb language (don't get me started...), as such we need extra files to
142 | get decent completion & documentation. 0% of the time will we want to use them
143 | as references, so we won't.
144 |
145 | #+begin_src emacs-lisp
146 | (defvar xref-ignored-files '("_ide_helper_models.php" "_ide_helper.php")
147 | "List of files to be ignored by `xref'.")
148 |
149 | (defun xref-ignored-file-p (item)
150 | "Return t if `item' should be ignored."
151 | (seq-some
152 | (lambda (cand)
153 | (string-suffix-p cand (oref (xref-item-location item) file))) xref-ignored-files))
154 |
155 | (defadvice! +lsp--ignored-locations-to-xref-items-a (items)
156 | "Remove ignored files from list of xref-items."
157 | :filter-return #'lsp--locations-to-xref-items
158 | (cl-remove-if #'xref-ignored-file-p items))
159 |
160 | (defadvice! +lsp-ui-peek--ignored-locations-a (items)
161 | "Remove ignored files from list of xref-items."
162 | :filter-return #'lsp-ui-peek--get-references
163 | (cl-remove-if #'xref-ignored-file-p items))
164 | #+end_src
165 |
166 | *** Ignore directories
167 | Add some extra ignored directories for =+lsp=.
168 |
169 | #+begin_src emacs-lisp
170 | (after! lsp-mode
171 | (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\vendor"))
172 | #+end_src
173 |
174 | And some more for projectile
175 |
176 | #+begin_src emacs-lisp
177 | (after! projectile
178 | (add-to-list 'projectile-globally-ignored-directories "vendor"))
179 | #+end_src
180 |
181 | *** PHP
182 | **** Web-mode setup
183 | #+begin_src emacs-lisp
184 | (after! web-mode
185 | (pushnew! web-mode-engines-alist '(("blade" . "\\.blade\\."))))
186 | #+end_src
187 |
188 | **** Intelephense
189 | Because I'm a massive sellout who likes features
190 |
191 | #+begin_src emacs-lisp
192 | (after! eglot
193 | (setq lsp-intelephense-licence-key (or (ignore-errors (fetch-auth-source :user "intelephense") nil))))
194 | #+end_src
195 |
196 | **** Eglot
197 | Trying out this eglot thing for a bit, let's see how it goes.
198 |
199 | Make sure it's loaded in php-mode
200 |
201 | #+begin_src emacs-lisp
202 | (after! eglot
203 | (add-hook 'php-mode-hook 'eglot-ensure)
204 | (add-hook 'dart-mode-hook 'eglot-ensure))
205 | #+end_src
206 |
207 | Set some config needed for the server
208 |
209 | #+begin_src emacs-lisp
210 | (when (modulep! :tools lsp +eglot)
211 | (defvar php-intelephense-storage-path (expand-file-name "lsp-intelephense" doom-etc-dir))
212 | (defvar php-intelephense-command (expand-file-name "lsp/npm/intelephense/bin/intelephense" doom-etc-dir)))
213 | #+end_src
214 |
215 | And set the server to be loaded
216 |
217 | #+begin_src emacs-lisp
218 | (after! eglot
219 | (defclass eglot-php (eglot-lsp-server) () :documentation "PHP's Intelephense")
220 | (cl-defmethod eglot-initialization-options ((server eglot-php))
221 | "Passes through required intelephense options"
222 | `(:storagePath ,php-intelephense-storage-path
223 | :licenceKey ,lsp-intelephense-licence-key
224 | :clearCache t))
225 | (add-to-list 'eglot-server-programs `((php-mode phps-mode) . (eglot-php . (,php-intelephense-command "--stdio")))))
226 | #+end_src
227 |
228 |
229 | ** Snippets
230 | *** PHP-Mode
231 | **** function
232 | #+begin_src snippet
233 | # -*- mode: snippet -*-
234 | # name: function
235 | # key: fu
236 | # uuid: fu
237 | # expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
238 | # --
239 | ${1:$$(yas-auto-next (yas-completing-read "Visibility (public): " '("public" "private" "protected") nil nil nil nil "public"))} function ${2:name}($3)
240 | {
241 | $0
242 | }
243 | #+end_src
244 |
245 | **** php
246 | #+begin_src snippet
247 | # -*- mode: snippet -*-
248 | # name: setContext('$2', [
339 | $3
340 | ]);
341 |
342 | `%`$0
343 | });
344 | #+end_src
345 |
346 | *** laravel-mode
347 | Not yet fit for human consumption, but fit for mine because I'm +sub+ super-human
348 |
349 | #+begin_src elisp
350 | ;; (package! laravel-mode
351 | ;; :recipe (:local-repo "~/build/elisp/laravel-mode"
352 | ;; :build (:not compile)))
353 | #+end_src
354 |
355 | #+begin_src emacs-lisp
356 | (use-package! laravel-tinker
357 | :after php-mode
358 | :init
359 | (set-popup-rule! "^\\tinker:" :vslot -5 :size 0.35 :select t :modeline nil :ttl nil)
360 | (map! :localleader
361 | :map php-mode-map
362 | :desc "Toggle a project-local Tinker REPL" "o t" #'laravel-tinker-toggle))
363 | #+end_src
364 |
365 | ** Slack
366 | Includes a couple of niceties locally, these /may/ end up in a module one day.
367 |
368 | #+begin_src elisp
369 | (package! slack)
370 | #+end_src
371 |
372 | #+begin_src emacs-lisp
373 | (use-package! slack
374 | :commands (slack-start)
375 | :custom
376 | (slack-buffer-emojify t)
377 | (slack-prefer-current-team t)
378 | (slack-enable-global-mode-string t)
379 | (slack-modeline-count-only-subscribed-channel nil)
380 | :init
381 | (require 'auth-source)
382 | (require 'slack-util)
383 |
384 | (defun +slack/register-team (&rest plist)
385 | "Given an email, get the `:token' and `:cookie' for the given EMAIL."
386 | (let ((token (auth-source-pick-first-password
387 | :host (plist-get plist :host)
388 | :user (plist-get plist :email)))
389 | (cookie (auth-source-pick-first-password
390 | :host (plist-get plist :host)
391 | :user (format "%s^cookie" (plist-get plist :email)))))
392 | (apply 'slack-register-team (slack-merge-plist `(:token ,token :cookie ,cookie) plist))))
393 |
394 | (defun +slack/post-standup-message ()
395 | "Create a standup message to send to a room."
396 | (interactive)
397 | (let* ((team (slack-team-select))
398 | (channel (slack-room-select
399 | (cl-loop for team in (list team)
400 | for channels = (append (slack-team-channels team) (slack-team-ims team))
401 | nconc channels)
402 | team))
403 | (buf (slack-create-room-message-compose-buffer channel team)))
404 |
405 | (slack-buffer-display buf)
406 | (yas-expand-snippet (yas-lookup-snippet "Standup template" 'slack-message-compose-buffer-mode)))))
407 | #+end_src
408 |
409 | ** Reaper
410 | Emacs client for Harvest, the time tracker we use at =$DAYJOB=. Over time, this
411 | will likely become more config than just a dump of macros.
412 |
413 | #+begin_src elisp
414 | (package! reaper)
415 | #+end_src
416 |
417 | #+begin_src emacs-lisp
418 | (use-package! reaper
419 | :init
420 | (map! :leader :n :desc "Track time" "t t" #'reaper))
421 | #+end_src
422 |
423 | ** Jira
424 | Config related to setting up Jira.
425 |
426 | *** org-jira
427 | Used for accessing Jira tasks through org-mode. Jira's interface is /quite/ a mess
428 | so I'd rather not use it as much.
429 |
430 | You know what we love? =org-mode=.
431 |
432 | #+begin_src elisp
433 | (package! org-jira)
434 | #+end_src
435 |
436 | #+begin_src emacs-lisp
437 | (use-package! org-jira
438 | :commands (org-jira-get-issues org-jira-get-issues-from-custom-jql)
439 | :init
440 | (let ((dir (expand-file-name ".org-jira"
441 | (or (getenv "XDG_CONFIG_HOME")
442 | (getenv "HOME")))))
443 | (unless (file-directory-p dir)
444 | (make-directory dir))
445 | (setq org-jira-working-dir dir)))
446 | #+end_src
447 |
448 | *** jira-workflow
449 | Custom package I've written to handle some of my jira workflow usage to get over
450 | some of the gripes I have. Still some features left to work on (eg automatic
451 | ticket movement) but for the most part it does a decent job
452 |
453 | #+begin_src elisp
454 | (package! jira-workflow
455 | :recipe (:host github :repo "elken/jira-workflow"))
456 | #+end_src
457 |
458 | #+begin_src emacs-lisp
459 | (use-package! jira-workflow
460 | :after dotenv
461 | :commands (jira-workflow-start-ticket)
462 | :init
463 | (map!
464 | :desc "Start working on a ticket"
465 | :leader "g c B"
466 | #'jira-workflow-start-ticket))
467 | #+end_src
468 |
469 | ** Translate
470 | I do a decent amount of copy/paste translating stuff for work, so let's make this /easier/.
471 |
472 | #+begin_src elisp
473 | (package! google-translate)
474 | #+end_src
475 |
476 | #+begin_src emacs-lisp
477 | (use-package! google-translate
478 | :config
479 | (defun google-translate--search-tkk ()
480 | "Search TKK."
481 | (list 430675 2721866130))
482 | (setf (alist-get "Kinyarwanda" google-translate-supported-languages-alist) "rw")
483 | (setq google-translate-output-destination 'kill-ring)
484 | (setq google-translate-translation-directions-alist
485 | '(("en" . "fr")
486 | ("en" . "rw"))))
487 | #+end_src
488 |
489 |
--------------------------------------------------------------------------------
/cli.el:
--------------------------------------------------------------------------------
1 | ;;; cli.el -*- lexical-binding: t; -*-
2 |
3 | (defcli! (publish) ()
4 | "Publish my doom config as a html page."
5 | (require 'doom-start)
6 | (require 'ox-publish)
7 | (require 'htmlize)
8 | (require 'engrave-faces)
9 |
10 | ;; Set theme needed for ox-chameleon
11 | (setq custom-enabled-themes `(,doom-theme))
12 | (print! (green "Starting publish\n"))
13 |
14 | ;; UI setup to properly load colours and include fontified minor modes
15 | (run-hooks 'doom-init-ui-hook)
16 | (add-hook 'htmlize-before-hook (lambda ()
17 | (rainbow-delimiters-mode-enable)
18 | (highlight-numbers--turn-on)
19 | (highlight-quoted--turn-on)
20 | (font-lock-flush)
21 | (font-lock-ensure)))
22 |
23 | ;; TODO Remove this and make engrave-faces work properly
24 | (after! engrave-faces
25 |
26 | (add-to-list 'engrave-faces-themes
27 | '(doom-nord
28 | (default :short "default" :slug "D" :family "Iosevka Nerd Font" :foreground "#ECEFF4" :background "#2E3440" :slant normal :weight regular)
29 | (variable-pitch :short "var-pitch" :slug "vp" :family "Montserrat")
30 | (shadow :short "shadow" :slug "h" :foreground "#4C566A")
31 | (success :short "success" :slug "sc" :foreground "#A3BE8C")
32 | (warning :short "warning" :slug "w" :foreground "#EBCB8B")
33 | (error :short "error" :slug "e" :foreground "#BF616A")
34 | (link :short "link" :slug "l" :foreground "#81A1C1" :weight bold)
35 | (link-visited :short "link" :slug "lv" :foreground "#ee82ee" :weight bold)
36 | (highlight :short "link" :slug "hi" :foreground "#191C25" :background "#81A1C1")
37 | (font-lock-comment-face :short "fl-comment" :slug "c" :foreground "#6f7787")
38 | (font-lock-comment-delimiter-face :short "fl-comment-delim" :slug "cd" :foreground "#6f7787")
39 | (font-lock-string-face :short "fl-string" :slug "s" :foreground "#A3BE8C")
40 | (font-lock-doc-face :short "fl-doc" :slug "d" :foreground "#78808f")
41 | (font-lock-doc-markup-face :short "fl-doc-markup" :slug "m" :foreground "#81A1C1")
42 | (font-lock-keyword-face :short "fl-keyword" :slug "k" :foreground "#81A1C1")
43 | (font-lock-builtin-face :short "fl-builtin" :slug "b" :foreground "#81A1C1")
44 | (font-lock-function-name-face :short "fl-function" :slug "f" :foreground "#88C0D0")
45 | (font-lock-variable-name-face :short "fl-variable" :slug "v" :foreground "#D8DEE9")
46 | (font-lock-type-face :short "fl-type" :slug "t" :foreground "#8FBCBB")
47 | (font-lock-constant-face :short "fl-constant" :slug "o" :foreground "#81A1C1")
48 | (font-lock-warning-face :short "fl-warning" :slug "wr" :foreground "#EBCB8B")
49 | (font-lock-negation-char-face :short "fl-neg-char" :slug "nc" :foreground "#81A1C1" :weight bold)
50 | (font-lock-preprocessor-face :short "fl-preprocessor" :slug "pp" :foreground "#81A1C1" :weight bold)
51 | (font-lock-regexp-grouping-construct :short "fl-regexp" :slug "rc" :foreground "#81A1C1" :weight bold)
52 | (font-lock-regexp-grouping-backslash :short "fl-regexp-backslash" :slug "rb" :foreground "#81A1C1" :weight bold)
53 | (org-block :short "org-block" :slug "ob" :background "#373E4C")
54 | (org-block-begin-line :short "org-block-begin" :slug "obb" :foreground "#6f7787" :background "#373E4C")
55 | (org-block-end-line :short "org-block-end" :slug "obe" :foreground "#6f7787" :background "#373E4C")
56 | (outline-1 :short "outline-1" :slug "Oa" :foreground "#81A1C1" :weight bold)
57 | (outline-2 :short "outline-2" :slug "Ob" :foreground "#B48EAD" :weight bold)
58 | (outline-3 :short "outline-3" :slug "Oc" :foreground "#5D80AE" :weight bold)
59 | (outline-4 :short "outline-4" :slug "Od" :foreground "#a0b8d0" :weight bold)
60 | (outline-5 :short "outline-5" :slug "Oe" :foreground "#c6aac1" :weight bold)
61 | (outline-6 :short "outline-6" :slug "Of" :foreground "#c0d0e0" :weight bold)
62 | (outline-7 :short "outline-7" :slug "Og" :foreground "#d9c6d6" :weight bold)
63 | (outline-8 :short "outline-8" :slug "Oh" :foreground "#e5ecf2" :weight bold)
64 | (highlight-numbers-number :short "hl-number" :slug "hn" :foreground "#B48EAD" :weight bold)
65 | (highlight-quoted-quote :short "hl-qquote" :slug "hq" :foreground "#81A1C1")
66 | (highlight-quoted-symbol :short "hl-qsymbol" :slug "hs" :foreground "#8FBCBB")
67 | (rainbow-delimiters-depth-1-face :short "rd-1" :slug "rda" :foreground "#81A1C1")
68 | (rainbow-delimiters-depth-2-face :short "rd-2" :slug "rdb" :foreground "#B48EAD")
69 | (rainbow-delimiters-depth-3-face :short "rd-3" :slug "rdc" :foreground "#A3BE8C")
70 | (rainbow-delimiters-depth-4-face :short "rd-4" :slug "rdd" :foreground "#5D80AE")
71 | (rainbow-delimiters-depth-5-face :short "rd-5" :slug "rde" :foreground "#8FBCBB")
72 | (rainbow-delimiters-depth-6-face :short "rd-6" :slug "rdf" :foreground "#81A1C1")
73 | (rainbow-delimiters-depth-7-face :short "rd-7" :slug "rdg" :foreground "#B48EAD")
74 | (rainbow-delimiters-depth-8-face :short "rd-8" :slug "rdh" :foreground "#A3BE8C")
75 | (rainbow-delimiters-depth-9-face :short "rd-9" :slug "rdi" :foreground "#5D80AE")
76 | (ansi-color-yellow :short "ansi-yellow" :slug "any" :foreground "#EBCB8B" :background "#EBCB8B")
77 | (ansi-color-red :short "ansi-red" :slug "anr" :foreground "#BF616A" :background "#BF616A")
78 | (ansi-color-black :short "ansi-black" :slug "anb" :foreground "#2E3440")
79 | (ansi-color-green :short "ansi-green" :slug "ang" :foreground "#A3BE8C" :background "#A3BE8C")
80 | (ansi-color-blue :short "ansi-blue" :slug "anB" :foreground "#81A1C1" :background "#81A1C1")
81 | (ansi-color-cyan :short "ansi-cyan" :slug "anc" :foreground "#88C0D0" :background "#88C0D0")
82 | (ansi-color-white :short "ansi-white" :slug "anw" :background "#ECEFF4")
83 | (ansi-color-magenta :short "ansi-magenta" :slug "anm" :foreground "#B48EAD" :background "#B48EAD")
84 | (ansi-color-bright-yellow :short "ansi-bright-yellow" :slug "ANy" :foreground "#edd29c" :background "#edd29c")
85 | (ansi-color-bright-red :short "ansi-bright-red" :slug "ANr" :foreground "#c87880" :background "#c87880")
86 | (ansi-color-bright-black :short "ansi-bright-black" :slug "ANb" :foreground "#191C25" :background "#2C333F")
87 | (ansi-color-bright-green :short "ansi-bright-green" :slug "ANg" :foreground "#b0c79d" :background "#b0c79d")
88 | (ansi-color-bright-blue :short "ansi-bright-blue" :slug "ANB" :foreground "#93afca" :background "#93afca")
89 | (ansi-color-bright-cyan :short "ansi-bright-cyan" :slug "ANc" :foreground "#99c9d7" :background "#99c9d7")
90 | (ansi-color-bright-white :short "ansi-bright-white" :slug "ANw" :foreground "#F0F4FC" :background "#F0F4FC")
91 | (ansi-color-bright-magenta :short "ansi-bright-magenta" :slug "ANm" :foreground "#bf9eb9" :background "#bf9eb9"))))
92 |
93 | (defun +org-publish-rename (props)
94 | "Things to do after the project finishes."
95 | (let ((out-dir (plist-get props :publishing-directory)))
96 | (when (file-exists-p (expand-file-name "config.html" out-dir))
97 | (rename-file
98 | (expand-file-name "config.html" out-dir)
99 | (expand-file-name "index.html" out-dir) t))))
100 |
101 | (setq org-html-validation-link nil
102 | org-html-head-include-scripts t
103 | org-html-head-include-default-style nil
104 | org-html-divs '((preamble "header" "top")
105 | (content "main" "content")
106 | (postamble "footer" "postamble"))
107 | org-html-container-element "section"
108 | org-html-metadata-timestamp-format "%Y-%m-%d"
109 | org-html-checkbox-type 'html
110 | org-html-html5-fancy t
111 | org-html-htmlize-output-type 'css
112 | org-html-doctype "html5"
113 | org-html-home/up-format "%s\n%s\n"
114 | org-export-with-broken-links t
115 | org-html-head "\n"
116 | org-html-scripts ""
117 | org-export-headline-levels 8
118 | org-publish-project-alist
119 | `(("dotfiles"
120 | :base-directory ,doom-user-dir
121 | :base-extension "org"
122 | :publishing-directory "out"
123 | :exclude "README.org"
124 | :publishing-function org-html-publish-to-html
125 | :completion-function +org-publish-rename
126 | :with-creator t
127 | :section-numbers nil)
128 |
129 | ("images"
130 | :base-directory "./images"
131 | :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
132 | :publishing-directory "out/images"
133 | :recursive t
134 | :publishing-function org-publish-attachment)
135 |
136 | ("static"
137 | :base-directory "./static"
138 | :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
139 | :publishing-directory "out/static"
140 | :recursive t
141 | :publishing-function org-publish-attachment)
142 |
143 | ("site" :components ("dotfiles" "static" "images"))))
144 |
145 | (when (file-exists-p "publish.el")
146 | (print! (blue "Loading extra config from %S" (expand-file-name "publish.el")))
147 | (load! (expand-file-name "publish.el")))
148 |
149 | (org-publish-project "site" t)
150 | (print! (green "Done! ")))
151 |
--------------------------------------------------------------------------------
/config.org:
--------------------------------------------------------------------------------
1 | #+title: Config
2 | #+author: Ellis Kenyő
3 | #+property: header-args:emacs-lisp :tangle yes :comments link
4 | #+property: header-args:elisp :tangle packages.el :comments link
5 | #+property: header-args :tangle no :results silent :eval no-export
6 | #+caption: Banner
7 | #+latex_class: chameleon
8 | #+html_content_class: chameleon
9 | [[file:images/banner.png]]
10 |
11 | (icon courtesy of https://github.com/eccentric-j/doom-icon)
12 |
13 | Below is my [[https://github.com/hlissner/doom-emacs][doom-emacs]] config. Most of it isn't particularly original; snippets
14 | from stackoverflow, modernemacs, [[https://github.com/daviwil][David Wilson]] and [[https://github.com/tecosaur][Tecosaur]]. Everything else will
15 | be commented to the best of /my/ ability.
16 |
17 | Anything that's missing from here might also be located in [[file:archive.org][the archive]], so it's
18 | worth skimming from there too.
19 |
20 | Most of this cobbled mess is now mine, and if you do intend to use any of it it
21 | would be nice if you mentioned me when doing so. It's just polite :)
22 |
23 | Depends on Emacs 29 because of some super cool stuff, so if you're not using it
24 | _please_ don't open any issues about missing variables.
25 |
26 | #+name: file-name
27 | #+begin_src emacs-lisp :tangle no
28 | (buffer-file-name)
29 | #+end_src
30 |
31 | * Table of Contents :TOC_5_gh:noexport:
32 | - [[#faq][FAQ]]
33 | - [[#globals][Globals]]
34 | - [[#constants-and-variables][Constants and Variables]]
35 | - [[#lexical-binding][Lexical binding]]
36 | - [[#frame-title-format][Frame title format]]
37 | - [[#default-projectile-path][Default projectile path]]
38 | - [[#lookup-provider-urls][Lookup provider URLs]]
39 | - [[#subword-mode][Subword-mode]]
40 | - [[#auto-revert-mode][Auto-revert-mode]]
41 | - [[#prevent-flickering][Prevent flickering]]
42 | - [[#clear-snippets-before-loading][Clear snippets before loading]]
43 | - [[#load-env-after-reload][Load env after reload]]
44 | - [[#reload-config-without-sync][Reload config without sync]]
45 | - [[#bury-compile-buffer][Bury compile buffer]]
46 | - [[#evil][Evil]]
47 | - [[#splits][Splits]]
48 | - [[#fine-undo][Fine undo]]
49 | - [[#global-substitute][Global substitute]]
50 | - [[#ignore-visual-text-in-the-kill-ring][Ignore visual text in the kill ring]]
51 | - [[#use-emacs-binds-in-insert-mode][Use emacs binds in insert mode]]
52 | - [[#lispyville][Lispyville]]
53 | - [[#default-scratch-mode][Default scratch mode]]
54 | - [[#auth-info][Auth info]]
55 | - [[#fetch-auth-source][fetch-auth-source]]
56 | - [[#magit][Magit]]
57 | - [[#forge][Forge]]
58 | - [[#eshell][EShell]]
59 | - [[#prompt][Prompt]]
60 | - [[#settings][Settings]]
61 | - [[#user-setup][User setup]]
62 | - [[#vterm][vterm]]
63 | - [[#always-compile][Always compile]]
64 | - [[#kill-buffer][Kill buffer]]
65 | - [[#fix-c-backspace][Fix =c-backspace=]]
66 | - [[#functions][Functions]]
67 | - [[#multi-vterm][Multi-vterm]]
68 | - [[#ensure-the-shell-is-zsh][Ensure the shell is ZSH]]
69 | - [[#default-modes][Default modes]]
70 | - [[#convert-a-url-to-a-valid-package-recipe][Convert a URL to a valid package recipe]]
71 | - [[#keybindings][Keybindings]]
72 | - [[#save][Save]]
73 | - [[#search][Search]]
74 | - [[#dired][Dired]]
75 | - [[#journal][Journal]]
76 | - [[#graphical-setup][Graphical setup]]
77 | - [[#pixel-precision-scrolling][Pixel-precision scrolling]]
78 | - [[#which-key][which-key]]
79 | - [[#marginalia][Marginalia]]
80 | - [[#files][Files]]
81 | - [[#info-pages][Info pages]]
82 | - [[#dashboard][Dashboard]]
83 | - [[#modeline][Modeline]]
84 | - [[#fonts][Fonts]]
85 | - [[#defaults][Defaults]]
86 | - [[#ligatures][Ligatures]]
87 | - [[#theme][Theme]]
88 | - [[#banner][Banner]]
89 | - [[#line-numbers][Line Numbers]]
90 | - [[#guiframe][GUI/Frame]]
91 | - [[#org-mode][Org Mode]]
92 | - [[#complete-ids-when-inserting-links][Complete IDs when inserting links]]
93 | - [[#fill-column][fill-column]]
94 | - [[#hook-setup][Hook setup]]
95 | - [[#org-directory][org-directory]]
96 | - [[#font-setup][Font setup]]
97 | - [[#properties][Properties]]
98 | - [[#allow-property-inheritance][Allow property inheritance]]
99 | - [[#characters][Characters]]
100 | - [[#headline-bullets][Headline bullets]]
101 | - [[#item-bullets][Item bullets]]
102 | - [[#dropdown-icon][Dropdown icon]]
103 | - [[#keywords][Keywords]]
104 | - [[#agendalog][Agenda/Log]]
105 | - [[#show-done-tasks-in-agenda][Show =DONE= tasks in agenda]]
106 | - [[#timestamp-done-items][Timestamp done items]]
107 | - [[#log-items-in-the-drawer][Log items in the drawer]]
108 | - [[#cycle][Cycle]]
109 | - [[#folding][Folding]]
110 | - [[#org-appear][Org-appear]]
111 | - [[#mixed-pitch][Mixed pitch]]
112 | - [[#archivecleanup][Archive/Cleanup]]
113 | - [[#archive-done-tasks][Archive =DONE= tasks]]
114 | - [[#remove-kill-tasks][Remove =KILL= tasks]]
115 | - [[#show-images][Show images]]
116 | - [[#autoexecute-tangled-shell-files][Autoexecute tangled shell files]]
117 | - [[#variable-setup][Variable setup]]
118 | - [[#better-snippets][Better snippets]]
119 | - [[#roam][Roam]]
120 | - [[#templates][Templates]]
121 | - [[#capture][Capture]]
122 | - [[#prettify][Prettify]]
123 | - [[#templates-1][Templates]]
124 | - [[#export][Export]]
125 | - [[#latex][LaTeX]]
126 | - [[#preambles][Preambles]]
127 | - [[#conditional-features][Conditional features]]
128 | - [[#tectonic][Tectonic]]
129 | - [[#classes][Classes]]
130 | - [[#packages][Packages]]
131 | - [[#pretty-code-blocks][Pretty code blocks]]
132 | - [[#ox-chameleon][ox-chameleon]]
133 | - [[#beamer][Beamer]]
134 | - [[#subsuperscript-characters][(sub|super)script characters]]
135 | - [[#auto-export][Auto-export]]
136 | - [[#org-protocol][org-protocol]]
137 | - [[#languages][Languages]]
138 | - [[#clojure][Clojure]]
139 | - [[#epithet][Epithet]]
140 | - [[#portal][Portal]]
141 | - [[#lua][Lua]]
142 | - [[#ruby][Ruby]]
143 | - [[#enable-rbenv][Enable rbenv]]
144 | - [[#force-disable-rvm][Force disable rvm]]
145 | - [[#disable-other-language-servers][Disable other language servers]]
146 | - [[#enable-rainbow-parens][Enable rainbow-parens]]
147 | - [[#lspdap][LSP/DAP]]
148 | - [[#increase-variable-line-length][Increase variable line length]]
149 | - [[#improve-completions][Improve completions]]
150 | - [[#completion][Completion]]
151 | - [[#projectile-completion-fn][Projectile completion fn]]
152 | - [[#jump-to-heading][Jump to heading]]
153 | - [[#snippets][Snippets]]
154 | - [[#snippet-definitions][Snippet definitions]]
155 | - [[#org-mode-1][Org-mode]]
156 | - [[#__][__]]
157 | - [[#slack-message-compose-buffer-mode][slack-message-compose-buffer-mode]]
158 | - [[#standup][standup]]
159 | - [[#packages-1][Packages]]
160 | - [[#disabledunpin][Disabled/unpin]]
161 | - [[#embark-vc][embark-vc]]
162 | - [[#prescient][prescient]]
163 | - [[#rainbow-identifiers][Rainbow Identifiers]]
164 | - [[#fix-in-web-mode][Fix in web-mode]]
165 | - [[#cucumber][Cucumber]]
166 | - [[#systemd][Systemd]]
167 | - [[#rpm-spec][RPM Spec]]
168 | - [[#autothemer][Autothemer]]
169 | - [[#bamboo][Bamboo]]
170 | - [[#yadm][YADM]]
171 | - [[#tramp-yadm][tramp-yadm]]
172 | - [[#keychain][Keychain]]
173 | - [[#asciidoc][Asciidoc]]
174 | - [[#graphviz][Graphviz]]
175 | - [[#exercism][Exercism]]
176 | - [[#evil-cleverparens][evil-cleverparens]]
177 | - [[#litable][litable]]
178 | - [[#magit-file-icons][magit-file-icons]]
179 | - [[#spelling][Spelling]]
180 | - [[#local-settings][Local settings]]
181 | - [[#dotenv][dotenv]]
182 |
183 | * FAQ
184 | None yet because luckily nobody else has seen this spaghetti junction
185 |
186 | * Globals
187 | ** Constants and Variables
188 | I could make a Bioshock Infinite joke here but I can't think of one. Wouldn't
189 | think of one? Would have thought of one.
190 |
191 | *** Lexical binding
192 |
193 | #+begin_src emacs-lisp
194 | ;;; -*- lexical-binding: t; -*-
195 | #+end_src
196 |
197 | *** Frame title format
198 | Making a /marginally/ more useful title format, since we often times might not necessarily running Emacs through the host.
199 |
200 | #+begin_src emacs-lisp
201 | (setq-default
202 | frame-title-format
203 | '(:eval (format "[%%b%s] - %s"
204 | (if (buffer-modified-p)
205 | " •"
206 | "")
207 | system-name)))
208 | #+end_src
209 |
210 | *** Default projectile path
211 | I stick to the same convention for projects on every OS, so it makes sense to
212 | tell projectile about it.
213 |
214 | #+begin_src emacs-lisp
215 | (setq projectile-project-search-path '("~/build"))
216 | #+end_src
217 |
218 | *** Lookup provider URLs
219 | Majority of the default lookup providers are useless (to me) so let's trim the fat, adjust the order and add in some of our own!
220 |
221 | #+begin_src emacs-lisp
222 | (setq +lookup-provider-url-alist
223 | '(("Doom Emacs issues" "https://github.com/hlissner/doom-emacs/issues?q=is%%3Aissue+%s")
224 | ("DuckDuckGo" +lookup--online-backend-duckduckgo "https://duckduckgo.com/?q=%s")
225 | ("StackOverflow" "https://stackoverflow.com/search?q=%s")
226 | ("Github" "https://github.com/search?ref=simplesearch&q=%s")
227 | ("Youtube" "https://youtube.com/results?aq=f&oq=&search_query=%s")
228 | ("MDN" "https://developer.mozilla.org/en-US/search?q=%s")
229 | ("Arch Wiki" "https://wiki.archlinux.org/index.php?search=%s&title=Special%3ASearch&wprov=acrw1")
230 | ("AUR" "https://aur.archlinux.org/packages?O=0&K=%s")))
231 | #+end_src
232 |
233 | *** Subword-mode
234 | Subword mode is a good start because some languages use a lot of CamelCase and
235 | it makes refactoring slightly easier
236 |
237 | #+begin_src emacs-lisp
238 | (global-subword-mode 1)
239 | #+end_src
240 |
241 | *** Auto-revert-mode
242 | Testing having auto-revert-mode on for text-mode buffers (should just be log
243 | files mostly)
244 |
245 | #+begin_src emacs-lisp
246 | (add-hook! 'text-mode (lambda () (auto-revert-mode 1)))
247 | #+end_src
248 |
249 | *** Prevent flickering
250 | Noticed some odd flickering here and there, apparently this should resolve it
251 |
252 | #+begin_src emacs-lisp
253 | (add-to-list 'default-frame-alist '(inhibit-double-buffering . t))
254 | #+end_src
255 |
256 | *** Clear snippets before loading
257 | Some attempt to make them reproducible.
258 |
259 | #+begin_src emacs-lisp
260 | (add-hook! 'org-babel-pre-tangle-hook
261 | (when (file-directory-p (expand-file-name "snippets" doom-user-dir))
262 | (require 'async)
263 | (async-start
264 | (lambda ()
265 | (delete-directory (expand-file-name "snippets" doom-user-dir) t (not (null delete-by-moving-to-trash))))
266 | (lambda (result)
267 | (print! "Delete snippets dir got: " result)))))
268 | #+end_src
269 |
270 | *** Load env after reload
271 | Most of the time, reloading breaks. So, let's not break.
272 |
273 | #+begin_src emacs-lisp
274 | (add-hook! 'doom-after-reload-hook (doom-load-envvars-file (expand-file-name "env" doom-local-dir) t))
275 | #+end_src
276 |
277 | *** Reload config without sync
278 | Seems there's a bug I'm too lazy to look into atm on this, so let's just define
279 | another reload command for me to use that doesn't depend on =doom sync=.
280 |
281 | #+begin_src emacs-lisp
282 | (defun doom/reload-without-sync ()
283 | (interactive)
284 | (mapc #'require (cdr doom-incremental-packages))
285 | (doom-context-with '(reload modules)
286 | (doom-run-hooks 'doom-before-reload-hook)
287 | (doom-load (file-name-concat doom-user-dir doom-module-init-file) t)
288 | (with-demoted-errors "PRIVATE CONFIG ERROR: %s"
289 | (general-auto-unbind-keys)
290 | (unwind-protect
291 | (startup--load-user-init-file nil)
292 | (general-auto-unbind-keys t)))
293 | (doom-run-hooks 'doom-after-reload-hook)
294 | (message "Config successfully reloaded!")))
295 |
296 | (define-key! help-map "rc" #'doom/reload-without-sync)
297 | #+end_src
298 |
299 | *** Bury compile buffer
300 | Assuming the buffer finishes successfully, close after 1 second.
301 |
302 | #+begin_src emacs-lisp
303 | (defun bury-compile-buffer-if-successful (buffer string)
304 | "Bury a compilation buffer if succeeded without warnings "
305 | (when (and (eq major-mode 'comint-mode)
306 | (string-match "finished" string)
307 | (not
308 | (with-current-buffer buffer
309 | (search-forward "warning" nil t))))
310 | (run-with-timer 1 nil
311 | (lambda (buf)
312 | (let ((window (get-buffer-window buf)))
313 | (when (and (window-live-p window)
314 | (eq buf (window-buffer window)))
315 | (delete-window window))))
316 | buffer)))
317 |
318 | (add-hook 'compilation-finish-functions #'bury-compile-buffer-if-successful)
319 | #+end_src
320 |
321 | *** Evil
322 | **** Splits
323 | I make a lot of splits, and it finally got annoying having to swap to them all
324 | the time. So, let's change that
325 |
326 | #+begin_src emacs-lisp
327 | (setq evil-split-window-below t
328 | evil-vsplit-window-right t)
329 | #+end_src
330 |
331 | **** Fine undo
332 | I don't need this because I, like all programmers, make 0 mistaeks.
333 |
334 | #+begin_src emacs-lisp
335 | (setq evil-want-fine-undo t)
336 | #+end_src
337 |
338 | **** Global substitute
339 | More often than not, I'd argue always, I want ~s/~ on my ex commands, so let's
340 | sort that out.
341 |
342 | #+begin_src emacs-lisp
343 | (setq evil-ex-substitute-global t)
344 | #+end_src
345 |
346 | **** Ignore visual text in the kill ring
347 | When we overwrite text in visual mode, say =vip=, don't add to the kill ring.
348 |
349 | #+begin_src emacs-lisp
350 | (setq evil-kill-on-visual-paste nil)
351 | #+end_src
352 |
353 | **** Use emacs binds in insert mode
354 | Some of them are quite useful, and I normally use them in the DE.
355 |
356 | #+begin_src emacs-lisp
357 | (setq evil-disable-insert-state-bindings t)
358 | #+end_src
359 | *** Lispyville
360 | This structured-editing thing is apparently really neat, so let's see how we go
361 |
362 | #+begin_src emacs-lisp
363 | (after! lispy
364 | (setq lispyville-key-theme
365 | '((operators normal)
366 | c-w
367 | (prettify insert)
368 | (atom-movement normal visual)
369 | (additional-movement normal)
370 | slurp/barf-lispy
371 | additional)))
372 | #+end_src
373 |
374 | *** Default scratch mode
375 | Make the scratch buffer start in lisp mode
376 |
377 | #+begin_src emacs-lisp
378 | (setq doom-scratch-initial-major-mode 'lisp-interaction-mode)
379 | #+end_src
380 |
381 | *** Auth info
382 | Add plaintext authinfo file to the list of sources. I /know/ I should use a GPG
383 | file but I'll get around to it damn it.
384 |
385 | #+begin_src emacs-lisp
386 | (add-to-list 'auth-sources "~/.authinfo")
387 | #+end_src
388 |
389 | *** fetch-auth-source
390 | Useful function to retrieve passwords from auth-sources
391 |
392 | #+begin_src emacs-lisp
393 | (defun fetch-auth-source (&rest params)
394 | (require 'auth-source)
395 | (let ((match (car (apply #'auth-source-search params))))
396 | (if match
397 | (let ((secret (plist-get match :secret)))
398 | (if (functionp secret)
399 | (funcall secret)
400 | secret))
401 | (error "Password not found for %S" params))))
402 | #+end_src
403 |
404 | *** Magit
405 | **** Forge
406 | Allow forge to create repos under my name
407 |
408 | #+begin_src emacs-lisp
409 | (setq forge-owned-accounts '(("elken")))
410 | #+end_src
411 |
412 | *** EShell
413 | **** Prompt
414 | Eshell is a beautiful thing but ootb experience is a tad dated. Custom prompt
415 | based on a combination of the famous p10k and eshell-git-prompt. I only /really/
416 | need the minimum out of a prompt:
417 |
418 | + =cwd=; almost impossible to work without knowing the current working directory
419 | + =git= info; current branch, dirty/clean status, etc
420 | + prompt number: useful for jumping up and down for fast history in a given
421 | session
422 |
423 | Can't get enough out of the default powerline theme, and removing a dependancy
424 | we're rolling our own prompt called =eshell-p10kline=
425 |
426 | #+begin_src elisp
427 | (package! eshell-p10k
428 | :recipe (:host github :repo "elken/eshell-p10k"))
429 | #+end_src
430 |
431 | #+begin_src emacs-lisp
432 | (use-package! eshell-p10k
433 | :after eshell
434 | :config
435 | (setq eshell-prompt-function #'eshell-p10k-prompt-function
436 | eshell-prompt-regexp eshell-p10k-prompt-string))
437 | #+end_src
438 |
439 | **** Settings
440 | We use eshell in a cross platform world, so we should prefer the lisp version of
441 | things to ensure a more consistent experience.
442 |
443 | #+begin_src emacs-lisp
444 | (setq eshell-prefer-lisp-functions t)
445 | #+end_src
446 |
447 | *** User setup
448 | Use my name and emails for things like GPG, snippets, mail, magit, etc. Differs
449 | based on which OS I'm on.
450 |
451 | #+BEGIN_SRC emacs-lisp
452 | (setq user-full-name "Ellis Kenyő"
453 | user-mail-address "me@elken.dev")
454 | #+END_SRC
455 |
456 | *** vterm
457 | Vterm clearly wins the terminal war. Also doesn't need much configuration out of
458 | the box, although the shell integration does. That currently exists in my
459 | [[https://github.com/elken/.files][dotfiles]]
460 |
461 | **** Always compile
462 | Fixes a weird bug with native-comp, and I don't use guix anymore.
463 |
464 | #+begin_src emacs-lisp
465 | (setq vterm-always-compile-module t)
466 | #+end_src
467 |
468 | **** Kill buffer
469 | If the process exits, kill the =vterm= buffer
470 |
471 | #+begin_src emacs-lisp
472 | (setq vterm-kill-buffer-on-exit t)
473 | #+end_src
474 |
475 | **** Fix =c-backspace=
476 | I've picked this up in muscle memory now and I'm fed up with it not working. Not
477 | anymore!
478 |
479 | #+begin_src emacs-lisp
480 | (after! vterm
481 | (define-key vterm-mode-map (kbd "") (lambda () (interactive) (vterm-send-key (kbd "C-w")))))
482 | #+end_src
483 |
484 | **** Functions
485 | Useful functions for the shell-side integration provided by vterm.
486 |
487 | #+begin_src emacs-lisp
488 | (after! vterm
489 | (setf (alist-get "woman" vterm-eval-cmds nil nil #'equal)
490 | '((lambda (topic)
491 | (woman topic))))
492 | (setf (alist-get "magit-status" vterm-eval-cmds nil nil #'equal)
493 | '((lambda (path)
494 | (magit-status path))))
495 | (setf (alist-get "dired" vterm-eval-cmds nil nil #'equal)
496 | '((lambda (dir)
497 | (dired dir)))))
498 | #+end_src
499 |
500 | **** Multi-vterm
501 | #+begin_src elisp
502 | (package! multi-vterm)
503 | #+end_src
504 |
505 | #+begin_src emacs-lisp
506 | (use-package! multi-vterm
507 | :after vterm)
508 | #+end_src
509 |
510 | **** Ensure the shell is ZSH
511 | Noticed a few weird cases where =chsh= doesn't /quite/ apply, so let's force that to be the case instead.
512 |
513 | #+begin_src emacs-lisp
514 | (setq vterm-shell "/bin/zsh")
515 | #+end_src
516 |
517 | *** Default modes
518 | Ensuring that correct modes are loaded for given file extensions
519 |
520 | #+begin_src emacs-lisp
521 | (add-to-list 'auto-mode-alist '("\\.jsonc\\'" . jsonc-mode))
522 |
523 | (after! nerd-icons
524 | (setf (alist-get "yuck" nerd-icons-extension-icon-alist)
525 | '(nerd-icons-fileicon "lisp" :face nerd-icons-orange))
526 | (setf (alist-get 'yuck-mode nerd-icons-mode-icon-alist)
527 | '(nerd-icons-fileicon "lisp" :face nerd-icons-orange))
528 |
529 | (setf (alist-get "jsonc" nerd-icons-extension-icon-alist)
530 | '(nerd-icons-fileicon "config-js" :v-adjust -0.05 :face nerd-icons-orange))
531 | (setf (alist-get 'jsonc-mode nerd-icons-mode-icon-alist)
532 | '(nerd-icons-fileicon "config-js" :v-adjust -0.05 :face nerd-icons-orange)))
533 | #+end_src
534 |
535 | ** Convert a URL to a valid package recipe
536 | Useful when copying links to try new packages. Attempt to create one using
537 | =straight-hosts=, but fall back if one can't be found.
538 |
539 | #+begin_src emacs-lisp
540 | (defun lkn/url->package (string &optional arg)
541 | "Interactively select a URL from the kill-ring and create a package! block."
542 | (interactive (list (consult--read-from-kill-ring) current-prefix-arg))
543 | (require 'consult)
544 | (require 'straight)
545 | (let ((url (thread-first
546 | string
547 | substring-no-properties
548 | (substring 1 (length string))
549 | string-trim
550 | url-generic-parse-url)))
551 | (if-let ((host
552 | (cl-find-if (lambda (cell)
553 | (member (url-host url) cell))
554 | straight-hosts)))
555 | (insert
556 | (concat
557 | "(package! "
558 | (car (last (string-split (url-filename url) "/")))
559 | "\n:recipe (:host "
560 | (symbol-name (car host))
561 | " :repo \""
562 | (substring (url-filename url) 1)
563 | "\"))"))
564 | (insert
565 | (concat
566 | "(package! "
567 | (car (last (string-split (url-filename url) "/")))
568 | "\n:recipe (:host nil :repo \""
569 | string
570 | "\"))")))
571 | (call-interactively #'indent-region)))
572 | #+end_src
573 |
574 | * Keybindings
575 | It's not a custom config without some fancy keybinds
576 |
577 | ** Save
578 | Back to a simpler time...
579 |
580 | #+begin_src emacs-lisp
581 | (map! :g "C-s" #'save-buffer)
582 | #+end_src
583 |
584 | ** Search
585 | +Swiper+ Consult is /much/ better than isearch
586 |
587 | #+begin_src emacs-lisp
588 | (map! :after evil :gnvi "C-f" #'consult-line)
589 | #+end_src
590 |
591 | ** Dired
592 | Dired should behave better with evil mappings
593 |
594 | #+begin_src emacs-lisp
595 | (map! :map dired-mode-map
596 | :n "h" #'dired-up-directory
597 | :n "l" #'dired-find-alternate-file)
598 | #+end_src
599 |
600 | ** Journal
601 | This is something I'm likely to use quite often, especially with an easy
602 | convenience binding
603 |
604 | #+begin_src emacs-lisp
605 | (after! org-journal
606 | (setq org-journal-find-file #'find-file-other-window)
607 |
608 | (map! :leader :desc "Open today's journal" "j" #'org-journal-open-current-journal-file))
609 | #+end_src
610 |
611 | * Graphical setup
612 | ** Pixel-precision scrolling
613 | Emacs 29 has some new hotness, including a cool new scrolling thing.
614 |
615 | #+begin_src emacs-lisp
616 | (when (version< "29.0.50" emacs-version)
617 | (pixel-scroll-precision-mode))
618 | #+end_src
619 | ** which-key
620 | Remove some of the useless =evil-= prefixes from which-key commands.
621 |
622 | #+begin_src emacs-lisp
623 | (setq which-key-allow-multiple-replacements t)
624 | (after! which-key
625 | (pushnew!
626 | which-key-replacement-alist
627 | '(("" . "\\`+?evil[-:]?\\(?:a-\\)?\\(.*\\)") . (nil . " \\1"))
628 | '(("\\`g s" . "\\`evilem--?motion-\\(.*\\)") . (nil . " \\1"))))
629 | #+end_src
630 |
631 | ** Marginalia
632 | Marginalia is part of the Vertico stack, and is responsible for all the fancy
633 | faces and extra information.
634 | *** Files
635 | The doom module out of the box includes a number of customizations, but the
636 | below from Teco gives a much better experience for files.
637 |
638 | #+begin_src emacs-lisp
639 | (after! marginalia
640 | (setq marginalia-censor-variables nil)
641 |
642 | (defadvice! +marginalia--anotate-local-file-colorful (cand)
643 | "Just a more colourful version of `marginalia--anotate-local-file'."
644 | :override #'marginalia--annotate-local-file
645 | (when-let (attrs (file-attributes (substitute-in-file-name
646 | (marginalia--full-candidate cand))
647 | 'integer))
648 | (marginalia--fields
649 | ((marginalia--file-owner attrs)
650 | :width 12 :face 'marginalia-file-owner)
651 | ((marginalia--file-modes attrs))
652 | ((+marginalia-file-size-colorful (file-attribute-size attrs))
653 | :width 7)
654 | ((+marginalia--time-colorful (file-attribute-modification-time attrs))
655 | :width 12))))
656 |
657 | (defun +marginalia--time-colorful (time)
658 | (let* ((seconds (float-time (time-subtract (current-time) time)))
659 | (color (doom-blend
660 | (face-attribute 'marginalia-date :foreground nil t)
661 | (face-attribute 'marginalia-documentation :foreground nil t)
662 | (/ 1.0 (log (+ 3 (/ (+ 1 seconds) 345600.0)))))))
663 | ;; 1 - log(3 + 1/(days + 1)) % grey
664 | (propertize (marginalia--time time) 'face (list :foreground color))))
665 |
666 | (defun +marginalia-file-size-colorful (size)
667 | (let* ((size-index (/ (log10 (+ 1 size)) 7.0))
668 | (color (if (< size-index 10000000) ; 10m
669 | (doom-blend 'orange 'green size-index)
670 | (doom-blend 'red 'orange (- size-index 1)))))
671 | (propertize (file-size-human-readable size) 'face (list :foreground color)))))
672 | #+end_src
673 |
674 | ** Info pages
675 | Slightly improve the look and feel of Info pages, might actually encourage me to /read/ them.
676 |
677 | #+begin_src elisp
678 | (package! info-colors)
679 | #+end_src
680 |
681 | #+begin_src emacs-lisp
682 | (use-package! info-colors
683 | :after info
684 | :commands (info-colors-fontify-node)
685 | :hook (Info-selection . info-colors-fontify-node))
686 | #+end_src
687 |
688 | ** Dashboard
689 | Inhibit the menu to improve things slightly
690 |
691 | #+begin_src emacs-lisp
692 | (remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-shortmenu)
693 | (remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-footer)
694 | #+end_src
695 |
696 | ** Modeline
697 | Default modeline is a tad cluttered, and because I don't use exwm anymore the
698 | modeline from that module isn't in use. So, it's duplicated here and tweaked.
699 |
700 | #+begin_src emacs-lisp
701 | (after! doom-modeline
702 | (setq auto-revert-check-vc-info t
703 | doom-modeline-major-mode-icon t
704 | doom-modeline-buffer-file-name-style 'relative-to-project
705 | doom-modeline-github nil
706 | doom-modeline-vcs-max-length 60)
707 | (remove-hook 'doom-modeline-mode-hook #'size-indication-mode)
708 | (doom-modeline-def-modeline 'main
709 | '(matches bar modals workspace-name window-number persp-name selection-info buffer-info remote-host debug vcs matches)
710 | '(github mu4e grip gnus check misc-info repl lsp " ")))
711 | #+end_src
712 |
713 | ** Fonts
714 | *** Defaults
715 | Configure the fonts across all used platforms (slightly different names).
716 |
717 | #+BEGIN_SRC emacs-lisp
718 | (setq doom-font (font-spec :family "Iosevka Nerd Font" :size 16)
719 | doom-variable-pitch-font (font-spec :family "Montserrat" :size 16)
720 | doom-unicode-font (font-spec :family "Symbols Nerd Font Mono" :size 16))
721 | #+END_SRC
722 |
723 | *** Ligatures
724 | Ligatures are a mess in programming languages, however they make org documents
725 | quite nice so let's just use them here until a good fix is found.
726 |
727 | #+begin_src emacs-lisp
728 | (setq-hook! org-mode
729 | prettify-symbols-alist '(("#+end_quote" . "”")
730 | ("#+END_QUOTE" . "”")
731 | ("#+begin_quote" . "“")
732 | ("#+BEGIN_QUOTE" . "“")
733 | ("#+end_src" . "«")
734 | ("#+END_SRC" . "«")
735 | ("#+begin_src" . "»")
736 | ("#+BEGIN_SRC" . "»")
737 | ("#+name:" . "»")
738 | ("#+NAME:" . "»")))
739 | #+end_src
740 |
741 | ** Theme
742 | Load my current flavour-of-the-month colour scheme.
743 |
744 | #+BEGIN_SRC emacs-lisp
745 | (setq doom-theme 'doom-nord)
746 | #+END_SRC
747 |
748 | Along with a few face overrides (thought about merging upstream but it would
749 | have sparked a discussion, maybe later)
750 |
751 | #+begin_src emacs-lisp
752 | (custom-theme-set-faces! 'doom-nord
753 | `(tree-sitter-hl-face:constructor :foreground ,(doom-color 'blue))
754 | `(tree-sitter-hl-face:number :foreground ,(doom-color 'orange))
755 | `(tree-sitter-hl-face:attribute :foreground ,(doom-color 'magenta) :weight bold)
756 | `(tree-sitter-hl-face:variable :foreground ,(doom-color 'base7) :weight bold)
757 | `(tree-sitter-hl-face:variable.builtin :foreground ,(doom-color 'red))
758 | `(tree-sitter-hl-face:constant.builtin :foreground ,(doom-color 'magenta) :weight bold)
759 | `(tree-sitter-hl-face:constant :foreground ,(doom-color 'blue) :weight bold)
760 | `(tree-sitter-hl-face:function.macro :foreground ,(doom-color 'teal))
761 | `(tree-sitter-hl-face:label :foreground ,(doom-color 'magenta))
762 | `(tree-sitter-hl-face:operator :foreground ,(doom-color 'blue))
763 | `(tree-sitter-hl-face:variable.parameter :foreground ,(doom-color 'cyan))
764 | `(tree-sitter-hl-face:punctuation.delimiter :foreground ,(doom-color 'cyan))
765 | `(tree-sitter-hl-face:punctuation.bracket :foreground ,(doom-color 'cyan))
766 | `(tree-sitter-hl-face:punctuation.special :foreground ,(doom-color 'cyan))
767 | `(tree-sitter-hl-face:type :foreground ,(doom-color 'yellow))
768 | `(tree-sitter-hl-face:type.builtin :foreground ,(doom-color 'blue))
769 | `(tree-sitter-hl-face:tag :foreground ,(doom-color 'base7))
770 | `(tree-sitter-hl-face:string :foreground ,(doom-color 'green))
771 | `(tree-sitter-hl-face:comment :foreground ,(doom-color 'base6))
772 | `(tree-sitter-hl-face:function :foreground ,(doom-color 'cyan))
773 | `(tree-sitter-hl-face:method :foreground ,(doom-color 'blue))
774 | `(tree-sitter-hl-face:function.builtin :foreground ,(doom-color 'cyan))
775 | `(tree-sitter-hl-face:property :foreground ,(doom-color 'blue))
776 | `(tree-sitter-hl-face:keyword :foreground ,(doom-color 'magenta))
777 | `(corfu-default :font "Iosevka Nerd Font Mono" :background ,(doom-color 'bg-alt) :foreground ,(doom-color 'fg))
778 | `(adoc-title-0-face :foreground ,(doom-color 'blue) :height 1.2)
779 | `(adoc-title-1-face :foreground ,(doom-color 'magenta) :height 1.1)
780 | `(adoc-title-2-face :foreground ,(doom-color 'violet) :height 1.05)
781 | `(adoc-title-3-face :foreground ,(doom-lighten (doom-color 'blue) 0.25) :height 1.0)
782 | `(adoc-title-4-face :foreground ,(doom-lighten (doom-color 'magenta) 0.25) :height 1.1)
783 | `(adoc-verbatim-face :background nil)
784 | `(adoc-list-face :background nil)
785 | `(adoc-internal-reference-face :foreground ,(face-attribute 'font-lock-comment-face :foreground)))
786 | #+end_src
787 |
788 | ** Banner
789 | Change the default banner (need to add the ASCII banner at some point)
790 |
791 | #+BEGIN_SRC emacs-lisp
792 | (setq +doom-dashboard-banner-file (expand-file-name "images/banner.png" doom-private-dir))
793 | #+END_SRC
794 |
795 | ** Line Numbers
796 | Set the default line number format to be relative and disable line numbers for
797 | specific modes
798 |
799 | #+BEGIN_SRC emacs-lisp
800 | (setq display-line-numbers-type 'relative)
801 |
802 | (dolist (mode '(org-mode-hook
803 | term-mode-hook
804 | shell-mode-hook
805 | eshell-mode-hook))
806 | (add-hook mode (lambda () (display-line-numbers-mode 0))))
807 | #+END_SRC
808 |
809 | ** GUI/Frame
810 | Maximise emacs on startup when not running under awesome
811 |
812 | #+BEGIN_SRC emacs-lisp
813 | (when (string= "" (shell-command-to-string "pgrep awesome"))
814 | (add-to-list 'default-frame-alist '(fullscreen . maximized)))
815 | #+END_SRC
816 |
817 | Add some transparency when running under awesome
818 |
819 | #+begin_src emacs-lisp
820 | (unless (string= "" (shell-command-to-string "pgrep awesome"))
821 | (set-frame-parameter (selected-frame) 'alpha-background 90)
822 | (add-to-list 'default-frame-alist '(alpha-background . 90)))
823 | #+end_src
824 |
825 | * Org Mode
826 | ** Complete IDs when inserting links
827 | This /definitely/ feels like something that should be on ootb, but hey ho.
828 |
829 | #+begin_src emacs-lisp
830 | (defun org-id-complete-link (&optional arg)
831 | "Create an id: link using completion"
832 | (concat "id:" (org-id-get-with-outline-path-completion)))
833 |
834 | (after! org
835 | (org-link-set-parameters "id" :complete 'org-id-complete-link))
836 | #+end_src
837 |
838 | ** fill-column
839 | Keep the content centered on the page when writing org documents
840 |
841 | #+begin_src elisp
842 | (package! visual-fill-column)
843 | #+end_src
844 |
845 | #+begin_src emacs-lisp
846 | (use-package! visual-fill-column
847 | :custom
848 | (visual-fill-column-width 300)
849 | (visual-fill-column-center-text t)
850 | :hook (org-mode . visual-fill-column-mode))
851 | #+end_src
852 |
853 | ** Hook setup
854 | =org-mode= is a wonderful thing, and far too complex to bury in another section.
855 | The more I use it, the more I will add to this area but for now it's mostly used
856 | for documentation and organisation.
857 |
858 | #+begin_src emacs-lisp
859 | (defun elken/org-setup-hook ()
860 | "Modes to enable on org-mode start"
861 | (org-indent-mode)
862 | (visual-line-mode 1)
863 | (+org-pretty-mode)
864 | (elken/org-font-setup))
865 |
866 | (add-hook! org-mode #'elken/org-setup-hook)
867 | #+end_src
868 |
869 | ** org-directory
870 | Let's set a sane default directory based on where I am
871 |
872 | #+begin_src emacs-lisp
873 | (setq org-directory "~/Nextcloud/org"
874 | org-agenda-files '("~/Nextcloud/org/Home.org" "~/Nextcloud/org/Work.org" "~/Nextcloud/org/Notes.org"))
875 | #+end_src
876 |
877 | ** Font setup
878 | Font setup to prettify the fonts. Uses Montserrat in most places except where
879 | it makes sense to use the defined fixed width font.
880 |
881 | #+BEGIN_SRC emacs-lisp
882 | (defun elken/org-font-setup ()
883 | ;; Set faces for heading levels
884 | (dolist (face '((org-level-1 . 1.2)
885 | (org-level-2 . 1.1)
886 | (org-level-3 . 1.05)
887 | (org-level-4 . 1.0)
888 | (org-level-5 . 1.1)
889 | (org-level-6 . 1.1)
890 | (org-level-7 . 1.1)
891 | (org-level-8 . 1.1)))
892 | (set-face-attribute (car face) nil :font "Montserrat" :weight 'regular :height (cdr face) :slant 'unspecified))
893 |
894 | ;; Ensure that anything that should be fixed-pitch in Org files appears that way
895 | (set-face-attribute 'org-tag nil :foreground nil :inherit '(shadow fixed-pitch) :weight 'bold)
896 | (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch)
897 | (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch))
898 | (set-face-attribute 'org-table nil :inherit '(shadow fixed-pitch))
899 | (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
900 | (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
901 | (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
902 | (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch))
903 | #+END_SRC
904 |
905 | ** Properties
906 | *** Allow property inheritance
907 | This may be the solution to /so/ many weird issues with src blocks.
908 |
909 | #+begin_src emacs-lisp
910 | (setq org-use-property-inheritance t)
911 | #+end_src
912 |
913 | ** Characters
914 | Tried out org-modern recently, it is /very/ nice but also detracts away from some
915 | of the org markup and makes editing it too hard, so back to =(:lang org +pretty)=
916 | we go.
917 |
918 | *** Headline bullets
919 | I don't feel the need for fancy characters to discern depth, I found this on
920 | someone else's config and I actually quite like the minimal look.
921 |
922 | #+begin_src emacs-lisp
923 | (setq org-superstar-headline-bullets-list '("› "))
924 | #+end_src
925 |
926 | *** Item bullets
927 | Barely any adjustment here, just make them look a /bit/ nicer.
928 |
929 | #+begin_src emacs-lisp
930 | (setq org-superstar-item-bullet-alist '((?* . ?⋆)
931 | (?+ . ?‣)
932 | (?- . ?•)))
933 | #+end_src
934 |
935 | *** Dropdown icon
936 | When a drawer is collapsed, show a nice dropdown arrow.
937 |
938 | #+begin_src emacs-lisp
939 | (setq org-ellipsis " ▾")
940 | #+end_src
941 |
942 | ** Keywords
943 | Default keywords are /far/ too minimal. This will need further tweaking as I start
944 | using org mode for organisation more.
945 |
946 | Some tasks we want to file an action for, eg =DONE=, =KILL= and =WAIT= occur and we want to list a reason why. =org-todo-keywords= handles this natively by simply adding ~@/!~ after the shortcut key.
947 |
948 | The below is courtesy of [[https://git.sr.ht/~gagbo/doom-config/tree/eb615417ca0cc01df89bc9a9aea06e5c99f97540/item/config-org.el#L57-62][gagbo]].
949 |
950 | #+begin_src emacs-lisp
951 | (after! org
952 | (setq org-todo-keywords
953 | '((sequence "TODO(t)" "INPROG(i)" "PROJ(p)" "STORY(s)" "WAIT(w@/!)" "|" "DONE(d@/!)" "KILL(k@/!)")
954 | (sequence "[ ](T)" "[-](S)" "[?](W)" "|" "[X](D)"))
955 | ;; The triggers break down to the following rules:
956 |
957 | ;; - Moving a task to =KILLED= adds a =killed= tag
958 | ;; - Moving a task to =WAIT= adds a =waiting= tag
959 | ;; - Moving a task to a done state removes =WAIT= and =HOLD= tags
960 | ;; - Moving a task to =TODO= removes all tags
961 | ;; - Moving a task to =NEXT= removes all tags
962 | ;; - Moving a task to =DONE= removes all tags
963 | org-todo-state-tags-triggers
964 | '(("KILL" ("killed" . t))
965 | ("HOLD" ("hold" . t))
966 | ("WAIT" ("waiting" . t))
967 | (done ("waiting") ("hold"))
968 | ("TODO" ("waiting") ("cancelled") ("hold"))
969 | ("NEXT" ("waiting") ("cancelled") ("hold"))
970 | ("DONE" ("waiting") ("cancelled") ("hold")))
971 |
972 | ;; This settings allows to fixup the state of a todo item without
973 | ;; triggering notes or log.
974 | org-treat-S-cursor-todo-selection-as-state-change nil))
975 | #+end_src
976 |
977 | ** Agenda/Log
978 | *** Show =DONE= tasks in agenda
979 |
980 | #+begin_src emacs-lisp
981 | (setq org-agenda-start-with-log-mode t)
982 | #+end_src
983 |
984 | *** Timestamp done items
985 |
986 | #+begin_src emacs-lisp
987 | (setq org-log-done 'time)
988 | #+end_src
989 |
990 | *** Log items in the drawer
991 |
992 | #+begin_src emacs-lisp
993 | (setq org-log-into-drawer t)
994 | #+end_src
995 |
996 | ** Cycle
997 | Cycle by default (no idea why this isn't default)
998 |
999 | #+begin_src emacs-lisp
1000 | (setq org-cycle-emulate-tab nil)
1001 | #+end_src
1002 |
1003 | ** Folding
1004 | Default folding is very noisy, I /rarely/ need to see everything expanded
1005 |
1006 | #+begin_src emacs-lisp
1007 | (setq org-startup-folded 'content)
1008 | #+end_src
1009 |
1010 | ** Org-appear
1011 | Defines a minor mode to allow special forms such as /italics/, *bold*, _underline_ and
1012 | =literal= to be editable when the cursor is over them, otherwise display the
1013 | proper value.
1014 |
1015 | #+begin_src elisp
1016 | (package! org-appear
1017 | :recipe (:host github :repo "awth13/org-appear"))
1018 | #+end_src
1019 |
1020 | #+begin_src emacs-lisp
1021 | (use-package! org-appear
1022 | :after org
1023 | :hook (org-mode . org-appear-mode)
1024 | :config
1025 | (setq org-appear-autoemphasis t
1026 | org-appear-autolinks t
1027 | org-appear-autosubmarkers t))
1028 | #+end_src
1029 |
1030 | ** Mixed pitch
1031 | Enable =mixed-pitch-mode= to enable the more readable fonts where it makes sense.
1032 |
1033 | #+begin_src elisp
1034 | (package! mixed-pitch)
1035 | #+end_src
1036 |
1037 | #+begin_src emacs-lisp
1038 | (setq +zen-mixed-pitch-modes '(org-mode LaTeX-mode markdown-mode gfm-mode Info-mode rst-mode adoc-mode))
1039 |
1040 | (dolist (hook +zen-mixed-pitch-modes)
1041 | (add-hook (intern (concat (symbol-name hook) "-hook")) #'mixed-pitch-mode))
1042 | #+end_src
1043 |
1044 | ** Archive/Cleanup
1045 | Adjust the format of archived org files (so they don't show up in orgzly)
1046 |
1047 | #+begin_src emacs-lisp
1048 | (setq org-archive-location "archive/Archive_%s::")
1049 | #+end_src
1050 |
1051 | *** Archive =DONE= tasks
1052 |
1053 | Enables archiving of tasks. Replaces the in-built version which only works for single tasks.
1054 |
1055 | #+BEGIN_SRC emacs-lisp
1056 | (defun elken/org-archive-done-tasks ()
1057 | "Attempt to archive all done tasks in file"
1058 | (interactive)
1059 | (org-map-entries
1060 | (lambda ()
1061 | (org-archive-subtree)
1062 | (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
1063 | "/DONE" 'file))
1064 |
1065 | (map! :map org-mode-map :desc "Archive tasks marked DONE" "C-c DEL a" #'elken/org-archive-done-tasks)
1066 | #+END_SRC
1067 |
1068 | *** Remove =KILL= tasks
1069 |
1070 | Enables removal of killed tasks. I'm not /yet/ interested in tracking this long-term.
1071 |
1072 | #+BEGIN_SRC emacs-lisp
1073 | (defun elken/org-remove-kill-tasks ()
1074 | (interactive)
1075 | (org-map-entries
1076 | (lambda ()
1077 | (org-cut-subtree)
1078 | (pop kill-ring)
1079 | (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
1080 | "/KILL" 'file))
1081 |
1082 | (map! :map org-mode-map :desc "Remove tasks marked as KILL" "C-c DEL k" #'elken/org-remove-kill-tasks)
1083 | #+END_SRC
1084 |
1085 | ** Show images
1086 | Show images inline by default
1087 |
1088 | #+BEGIN_SRC emacs-lisp
1089 | (setq org-startup-with-inline-images t)
1090 | #+END_SRC
1091 |
1092 | But also, adjust them to an appropriate size. This should be adjusted to handle better resolutions.
1093 |
1094 | #+begin_src emacs-lisp
1095 | (setq org-image-actual-width 600)
1096 | #+end_src
1097 |
1098 | ** Autoexecute tangled shell files
1099 | Make tangled shell files executable (I trust myself, ish...)
1100 |
1101 | #+begin_src emacs-lisp
1102 | (defun elken/make-tangled-shell-executable ()
1103 | "Ensure that tangled shell files are executable"
1104 | (set-file-modes (buffer-file-name) #o755))
1105 |
1106 | (add-hook 'org-babel-post-tangle-hook 'elken/make-tangled-shell-executable)
1107 | #+end_src
1108 |
1109 | ** Variable setup
1110 | Useful settings and functions for maintaining modified dates in org files
1111 |
1112 | #+begin_src emacs-lisp
1113 | (setq enable-dir-local-variables t)
1114 | (defun elken/find-time-property (property)
1115 | "Find the PROPETY in the current buffer."
1116 | (save-excursion
1117 | (goto-char (point-min))
1118 | (let ((first-heading
1119 | (save-excursion
1120 | (re-search-forward org-outline-regexp-bol nil t))))
1121 | (when (re-search-forward (format "^#\\+%s:" property) nil t)
1122 | (point)))))
1123 |
1124 | (defun elken/has-time-property-p (property)
1125 | "Gets the position of PROPETY if it exists, nil if not and empty string if it's undefined."
1126 | (when-let ((pos (elken/find-time-property property)))
1127 | (save-excursion
1128 | (goto-char pos)
1129 | (if (and (looking-at-p " ")
1130 | (progn (forward-char)
1131 | (org-at-timestamp-p 'lax)))
1132 | pos
1133 | ""))))
1134 |
1135 | (defun elken/set-time-property (property &optional pos)
1136 | "Set the PROPERTY in the current buffer.
1137 | Can pass the position as POS if already computed."
1138 | (when-let ((pos (or pos (elken/find-time-property property))))
1139 | (save-excursion
1140 | (goto-char pos)
1141 | (if (looking-at-p " ")
1142 | (forward-char)
1143 | (insert " "))
1144 | (delete-region (point) (line-end-position))
1145 | (let* ((now (format-time-string "<%Y-%m-%d %H:%M>")))
1146 | (insert now)))))
1147 |
1148 | (add-hook! 'before-save-hook (when (derived-mode-p 'org-mode)
1149 | (elken/set-time-property "LAST_MODIFIED")
1150 | (elken/set-time-property "DATE_UPDATED")))
1151 | #+end_src
1152 |
1153 | ** Better snippets
1154 | Programmers are, by design, lazy
1155 |
1156 | #+begin_src emacs-lisp
1157 | (use-package! org-tempo
1158 | :after org
1159 | :init
1160 | (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
1161 | (add-to-list 'org-structure-template-alist '("els" . "src elisp"))
1162 | (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")))
1163 | #+end_src
1164 |
1165 | ** Roam
1166 | Let's jump on the bandwagon and start taking useful notes.
1167 |
1168 | #+begin_src emacs-lisp
1169 | (setq org-roam-directory (expand-file-name "roam" org-directory))
1170 | #+end_src
1171 |
1172 | *** Templates
1173 | #+begin_src emacs-lisp
1174 | (after! org-roam
1175 | (setq org-roam-capture-templates
1176 | `(("d" "default" plain
1177 | (file ,(expand-file-name "templates/roam-default.org" doom-private-dir))
1178 | :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "")
1179 | :unnarrowed t))))
1180 | #+end_src
1181 |
1182 | ** Capture
1183 | It's about time I start using =org-capture=, but because I'm a developer I'm inhernetly lazy so time to steal from other people.
1184 |
1185 | Useful wrapper package for creating more declarative templates
1186 | #+begin_src elisp
1187 | (package! doct)
1188 | #+end_src
1189 |
1190 | #+begin_src emacs-lisp
1191 | (use-package! doct
1192 | :defer t
1193 | :commands (doct))
1194 | #+end_src
1195 |
1196 | *** Prettify
1197 | Improve the look of the capture dialog (idea borrowed from [[https://github.com/tecosaur][tecosaur]])
1198 | #+begin_src emacs-lisp
1199 | (defun org-capture-select-template-prettier (&optional keys)
1200 | "Select a capture template, in a prettier way than default
1201 | Lisp programs can force the template by setting KEYS to a string."
1202 | (let ((org-capture-templates
1203 | (or (org-contextualize-keys
1204 | (org-capture-upgrade-templates org-capture-templates)
1205 | org-capture-templates-contexts)
1206 | '(("t" "Task" entry (file+headline "" "Tasks")
1207 | "* TODO %?\n %u\n %a")))))
1208 | (if keys
1209 | (or (assoc keys org-capture-templates)
1210 | (error "No capture template referred to by \"%s\" keys" keys))
1211 | (org-mks org-capture-templates
1212 | "Select a capture template\n━━━━━━━━━━━━━━━━━━━━━━━━━"
1213 | "Template key: "
1214 | `(("q" ,(concat (nerd-icons-octicon "nf-oct-stop" :face 'nerd-icons-red :v-adjust 0.01) "\tAbort")))))))
1215 | (advice-add 'org-capture-select-template :override #'org-capture-select-template-prettier)
1216 |
1217 | (defun org-mks-pretty (table title &optional prompt specials)
1218 | "Select a member of an alist with multiple keys. Prettified.
1219 |
1220 | TABLE is the alist which should contain entries where the car is a string.
1221 | There should be two types of entries.
1222 |
1223 | 1. prefix descriptions like (\"a\" \"Description\")
1224 | This indicates that `a' is a prefix key for multi-letter selection, and
1225 | that there are entries following with keys like \"ab\", \"ax\"…
1226 |
1227 | 2. Select-able members must have more than two elements, with the first
1228 | being the string of keys that lead to selecting it, and the second a
1229 | short description string of the item.
1230 |
1231 | The command will then make a temporary buffer listing all entries
1232 | that can be selected with a single key, and all the single key
1233 | prefixes. When you press the key for a single-letter entry, it is selected.
1234 | When you press a prefix key, the commands (and maybe further prefixes)
1235 | under this key will be shown and offered for selection.
1236 |
1237 | TITLE will be placed over the selection in the temporary buffer,
1238 | PROMPT will be used when prompting for a key. SPECIALS is an
1239 | alist with (\"key\" \"description\") entries. When one of these
1240 | is selected, only the bare key is returned."
1241 | (save-window-excursion
1242 | (let ((inhibit-quit t)
1243 | (buffer (org-switch-to-buffer-other-window "*Org Select*"))
1244 | (prompt (or prompt "Select: "))
1245 | case-fold-search
1246 | current)
1247 | (unwind-protect
1248 | (catch 'exit
1249 | (while t
1250 | (setq-local evil-normal-state-cursor (list nil))
1251 | (erase-buffer)
1252 | (insert title "\n\n")
1253 | (let ((des-keys nil)
1254 | (allowed-keys '("\C-g"))
1255 | (tab-alternatives '("\s" "\t" "\r"))
1256 | (cursor-type nil))
1257 | ;; Populate allowed keys and descriptions keys
1258 | ;; available with CURRENT selector.
1259 | (let ((re (format "\\`%s\\(.\\)\\'"
1260 | (if current (regexp-quote current) "")))
1261 | (prefix (if current (concat current " ") "")))
1262 | (dolist (entry table)
1263 | (pcase entry
1264 | ;; Description.
1265 | (`(,(and key (pred (string-match re))) ,desc)
1266 | (let ((k (match-string 1 key)))
1267 | (push k des-keys)
1268 | ;; Keys ending in tab, space or RET are equivalent.
1269 | (if (member k tab-alternatives)
1270 | (push "\t" allowed-keys)
1271 | (push k allowed-keys))
1272 | (insert (propertize prefix 'face 'font-lock-comment-face) (propertize k 'face 'bold) (propertize "›" 'face 'font-lock-comment-face) " " desc "…" "\n")))
1273 | ;; Usable entry.
1274 | (`(,(and key (pred (string-match re))) ,desc . ,_)
1275 | (let ((k (match-string 1 key)))
1276 | (insert (propertize prefix 'face 'font-lock-comment-face) (propertize k 'face 'bold) " " desc "\n")
1277 | (push k allowed-keys)))
1278 | (_ nil))))
1279 | ;; Insert special entries, if any.
1280 | (when specials
1281 | (insert "─────────────────────────\n")
1282 | (pcase-dolist (`(,key ,description) specials)
1283 | (insert (format "%s %s\n" (propertize key 'face '(bold nerd-icons-red)) description))
1284 | (push key allowed-keys)))
1285 | ;; Display UI and let user select an entry or
1286 | ;; a sub-level prefix.
1287 | (goto-char (point-min))
1288 | (unless (pos-visible-in-window-p (point-max))
1289 | (org-fit-window-to-buffer))
1290 | (let ((pressed (org--mks-read-key allowed-keys prompt nil)))
1291 | (setq current (concat current pressed))
1292 | (cond
1293 | ((equal pressed "\C-g") (user-error "Abort"))
1294 | ((equal pressed "ESC") (user-error "Abort"))
1295 | ;; Selection is a prefix: open a new menu.
1296 | ((member pressed des-keys))
1297 | ;; Selection matches an association: return it.
1298 | ((let ((entry (assoc current table)))
1299 | (and entry (throw 'exit entry))))
1300 | ;; Selection matches a special entry: return the
1301 | ;; selection prefix.
1302 | ((assoc current specials) (throw 'exit current))
1303 | (t (error "No entry available")))))))
1304 | (when buffer (kill-buffer buffer))))))
1305 | (advice-add 'org-mks :override #'org-mks-pretty)
1306 | #+end_src
1307 |
1308 | The [[file:~/.config/emacs/bin/org-capture][doom org-capture bin]] is rather nice, but I'd be nicer with a smaller frame, and
1309 | no modeline.
1310 |
1311 | #+begin_src emacs-lisp
1312 | (setf (alist-get 'height +org-capture-frame-parameters) 15)
1313 | ;; (alist-get 'name +org-capture-frame-parameters) "❖ Capture") ;; ATM hardcoded in other places, so changing breaks stuff
1314 | (setq +org-capture-fn
1315 | (lambda ()
1316 | (interactive)
1317 | (set-window-parameter nil 'mode-line-format 'none)
1318 | (org-capture)))
1319 | #+end_src
1320 |
1321 | Sprinkle in some =doct= utility functions
1322 | #+begin_src emacs-lisp
1323 | (defun +doct-icon-declaration-to-icon (declaration)
1324 | "Convert :icon declaration to icon"
1325 | (let ((name (pop declaration))
1326 | (set (intern (concat "nerd-icons-" (plist-get declaration :set))))
1327 | (face (intern (concat "nerd-icons-" (plist-get declaration :color))))
1328 | (v-adjust (or (plist-get declaration :v-adjust) 0.01)))
1329 | (apply set `(,name :face ,face :v-adjust ,v-adjust))))
1330 |
1331 | (defun +doct-iconify-capture-templates (groups)
1332 | "Add declaration's :icon to each template group in GROUPS."
1333 | (let ((templates (doct-flatten-lists-in groups)))
1334 | (setq doct-templates (mapcar (lambda (template)
1335 | (when-let* ((props (nthcdr (if (= (length template) 4) 2 5) template))
1336 | (spec (plist-get (plist-get props :doct) :icon)))
1337 | (setf (nth 1 template) (concat (+doct-icon-declaration-to-icon spec)
1338 | "\t"
1339 | (nth 1 template))))
1340 | template)
1341 | templates))))
1342 |
1343 | (setq doct-after-conversion-functions '(+doct-iconify-capture-templates))
1344 | #+end_src
1345 |
1346 | *** Templates
1347 |
1348 | And we can now add some templates! This isn't even remotely set in stone, I wouldn't even describe them as set in /jelly/ really.
1349 | #+begin_src emacs-lisp
1350 | (after! org-capture
1351 | (defun +org-capture/replace-brackets (link)
1352 | (mapconcat
1353 | (lambda (c)
1354 | (pcase (key-description (vector c))
1355 | ("[" "(")
1356 | ("]" ")")
1357 | (_ (key-description (vector c)))))
1358 | link))
1359 |
1360 | (setq org-capture-templates
1361 | (doct `(("Home" :keys "h"
1362 | :icon ("nf-fa-home" :set "faicon" :color "cyan")
1363 | :file "Home.org"
1364 | :prepend t
1365 | :headline "Inbox"
1366 | :template ("* TODO %?"
1367 | "%i %a"))
1368 | ("Work" :keys "w"
1369 | :icon ("nf-fa-building" :set "faicon" :color "yellow")
1370 | :file "Work.org"
1371 | :prepend t
1372 | :headline "Inbox"
1373 | :template ("* TODO %?"
1374 | "SCHEDULED: %^{Schedule:}t"
1375 | "DEADLINE: %^{Deadline:}t"
1376 | "%i %a"))
1377 | ("Note" :keys "n"
1378 | :icon ("nf-fa-sticky_note" :set "faicon" :color "yellow")
1379 | :file "Notes.org"
1380 | :template ("* %?"
1381 | "%i %a"))
1382 | ("Journal" :keys "j"
1383 | :icon ("nf-fa-calendar" :set "faicon" :color "pink")
1384 | :type plain
1385 | :function (lambda ()
1386 | (org-journal-new-entry t)
1387 | (unless (eq org-journal-file-type 'daily)
1388 | (org-narrow-to-subtree))
1389 | (goto-char (point-max)))
1390 | :template "** %(format-time-string org-journal-time-format)%^{Title}\n%i%?"
1391 | :jump-to-captured t
1392 | :immediate-finish t)
1393 | ("Protocol" :keys "P"
1394 | :icon ("nf-fa-link" :set "faicon" :color "blue")
1395 | :file "Notes.org"
1396 | :template ("* TODO %^{Title}"
1397 | "Source: %u"
1398 | "#+BEGIN_QUOTE"
1399 | "%i"
1400 | "#+END_QUOTE"
1401 | "%?"))
1402 | ("Protocol link" :keys "L"
1403 | :icon ("nf-fa-link" :set "faicon" :color "blue")
1404 | :file "Notes.org"
1405 | :template ("* TODO %?"
1406 | "[[%:link][%:description]]"
1407 | "Captured on: %U"))
1408 | ("Project" :keys "p"
1409 | :icon ("nf-oct-repo" :set "octicon" :color "silver")
1410 | :prepend t
1411 | :type entry
1412 | :headline "Inbox"
1413 | :template ("* %{keyword} %?"
1414 | "%i"
1415 | "%a")
1416 | :file ""
1417 | :custom (:keyword "")
1418 | :children (("Task" :keys "t"
1419 | :icon ("nf-cod-checklist" :set "codicon" :color "green")
1420 | :keyword "TODO"
1421 | :file +org-capture-project-todo-file)
1422 | ("Note" :keys "n"
1423 | :icon ("nf-fa-sticky_note" :set "faicon" :color "yellow")
1424 | :keyword "%U"
1425 | :file +org-capture-project-notes-file)))))))
1426 | #+end_src
1427 |
1428 | ** Export
1429 | *** LaTeX
1430 | A necessary evil. I hate it, it hates me, but it makes my PDF documents look nice.
1431 |
1432 | **** Preambles
1433 | Various preamble setups to improve the overall look of several items
1434 |
1435 | #+begin_src emacs-lisp
1436 | (defvar org-latex-caption-preamble "
1437 | \\usepackage{subcaption}
1438 | \\usepackage[hypcap=true]{caption}
1439 | \\setkomafont{caption}{\\sffamily\\small}
1440 | \\setkomafont{captionlabel}{\\upshape\\bfseries}
1441 | \\captionsetup{justification=raggedright,singlelinecheck=true}
1442 | \\usepackage{capt-of} % required by Org
1443 | "
1444 | "Preamble that improves captions.")
1445 |
1446 | (defvar org-latex-checkbox-preamble "
1447 | \\newcommand{\\checkboxUnchecked}{$\\square$}
1448 | \\newcommand{\\checkboxTransitive}{\\rlap{\\raisebox{-0.1ex}{\\hspace{0.35ex}\\Large\\textbf -}}$\\square$}
1449 | \\newcommand{\\checkboxChecked}{\\rlap{\\raisebox{0.2ex}{\\hspace{0.35ex}\\scriptsize \\ding{52}}}$\\square$}
1450 | "
1451 | "Preamble that improves checkboxes.")
1452 |
1453 | (defvar org-latex-box-preamble "
1454 | % args = #1 Name, #2 Colour, #3 Ding, #4 Label
1455 | \\newcommand{\\defsimplebox}[4]{%
1456 | \\definecolor{#1}{HTML}{#2}
1457 | \\newenvironment{#1}[1][]
1458 | {%
1459 | \\par\\vspace{-0.7\\baselineskip}%
1460 | \\textcolor{#1}{#3} \\textcolor{#1}{\\textbf{\\def\\temp{##1}\\ifx\\temp\\empty#4\\else##1\\fi}}%
1461 | \\vspace{-0.8\\baselineskip}
1462 | \\begin{addmargin}[1em]{1em}
1463 | }{%
1464 | \\end{addmargin}
1465 | \\vspace{-0.5\\baselineskip}
1466 | }%
1467 | }
1468 | "
1469 | "Preamble that provides a macro for custom boxes.")
1470 | #+end_src
1471 |
1472 | **** Conditional features
1473 | Don't always need everything in LaTeX, so only add it what we need when we need it.
1474 |
1475 | #+begin_src emacs-lisp
1476 | (defvar org-latex-italic-quotes t
1477 | "Make \"quote\" environments italic.")
1478 | (defvar org-latex-par-sep t
1479 | "Vertically seperate paragraphs, and remove indentation.")
1480 |
1481 | (defvar org-latex-conditional-features
1482 | '(("\\[\\[\\(?:file\\|https?\\):\\(?:[^]]\\|\\\\\\]\\)+?\\.\\(?:eps\\|pdf\\|png\\|jpeg\\|jpg\\|jbig2\\)\\]\\]" . image)
1483 | ("\\[\\[\\(?:file\\|https?\\):\\(?:[^]]+?\\|\\\\\\]\\)\\.svg\\]\\]\\|\\\\includesvg" . svg)
1484 | ("^[ \t]*|" . table)
1485 | ("cref:\\|\\cref{\\|\\[\\[[^\\]]+\\]\\]" . cleveref)
1486 | ("[;\\\\]?\\b[A-Z][A-Z]+s?[^A-Za-z]" . acronym)
1487 | ("\\+[^ ].*[^ ]\\+\\|_[^ ].*[^ ]_\\|\\\\uu?line\\|\\\\uwave\\|\\\\sout\\|\\\\xout\\|\\\\dashuline\\|\\dotuline\\|\\markoverwith" . underline)
1488 | (":float wrap" . float-wrap)
1489 | (":float sideways" . rotate)
1490 | ("^[ \t]*#\\+caption:\\|\\\\caption" . caption)
1491 | ("\\[\\[xkcd:" . (image caption))
1492 | ((and org-latex-italic-quotes "^[ \t]*#\\+begin_quote\\|\\\\begin{quote}") . italic-quotes)
1493 | (org-latex-par-sep . par-sep)
1494 | ("^[ \t]*\\(?:[-+*]\\|[0-9]+[.)]\\|[A-Za-z]+[.)]\\) \\[[ -X]\\]" . checkbox)
1495 | ("^[ \t]*#\\+begin_warning\\|\\\\begin{warning}" . box-warning)
1496 | ("^[ \t]*#\\+begin_info\\|\\\\begin{info}" . box-info)
1497 | ("^[ \t]*#\\+begin_success\\|\\\\begin{success}" . box-success)
1498 | ("^[ \t]*#\\+begin_error\\|\\\\begin{error}" . box-error))
1499 | "Org feature tests and associated LaTeX feature flags.
1500 |
1501 | Alist where the car is a test for the presense of the feature,
1502 | and the cdr is either a single feature symbol or list of feature symbols.
1503 |
1504 | When a string, it is used as a regex search in the buffer.
1505 | The feature is registered as present when there is a match.
1506 |
1507 | The car can also be a
1508 | - symbol, the value of which is fetched
1509 | - function, which is called with info as an argument
1510 | - list, which is `eval'uated
1511 |
1512 | If the symbol, function, or list produces a string: that is used as a regex
1513 | search in the buffer. Otherwise any non-nil return value will indicate the
1514 | existance of the feature.")
1515 |
1516 | (defvar org-latex-feature-implementations
1517 | '((image :snippet "\\usepackage{graphicx}" :order 2)
1518 | (svg :snippet "\\usepackage{svg}" :order 2)
1519 | (table :snippet "\\usepackage{longtable}\n\\usepackage{booktabs}" :order 2)
1520 | (cleveref :snippet "\\usepackage[capitalize]{cleveref}" :order 1)
1521 | (underline :snippet "\\usepackage[normalem]{ulem}" :order 0.5)
1522 | (float-wrap :snippet "\\usepackage{wrapfig}" :order 2)
1523 | (rotate :snippet "\\usepackage{rotating}" :order 2)
1524 | (caption :snippet org-latex-caption-preamble :order 2.1)
1525 | (acronym :snippet "\\newcommand{\\acr}[1]{\\protect\\textls*[110]{\\scshape #1}}\n\\newcommand{\\acrs}{\\protect\\scalebox{.91}[.84]{\\hspace{0.15ex}s}}" :order 0.4)
1526 | (italic-quotes :snippet "\\renewcommand{\\quote}{\\list{}{\\rightmargin\\leftmargin}\\item\\relax\\em}\n" :order 0.5)
1527 | (par-sep :snippet "\\setlength{\\parskip}{\\baselineskip}\n\\setlength{\\parindent}{0pt}\n" :order 0.5)
1528 | (.pifont :snippet "\\usepackage{pifont}")
1529 | (checkbox :requires .pifont :order 3
1530 | :snippet (concat (unless (memq 'maths features)
1531 | "\\usepackage{amssymb} % provides \\square")
1532 | org-latex-checkbox-preamble))
1533 | (.fancy-box :requires .pifont :snippet org-latex-box-preamble :order 3.9)
1534 | (box-warning :requires .fancy-box :snippet "\\defsimplebox{warning}{e66100}{\\ding{68}}{Warning}" :order 4)
1535 | (box-info :requires .fancy-box :snippet "\\defsimplebox{info}{3584e4}{\\ding{68}}{Information}" :order 4)
1536 | (box-success :requires .fancy-box :snippet "\\defsimplebox{success}{26a269}{\\ding{68}}{\\vspace{-\\baselineskip}}" :order 4)
1537 | (box-error :requires .fancy-box :snippet "\\defsimplebox{error}{c01c28}{\\ding{68}}{Important}" :order 4))
1538 | "LaTeX features and details required to implement them.
1539 |
1540 | List where the car is the feature symbol, and the rest forms a plist with the
1541 | following keys:
1542 | - :snippet, which may be either
1543 | - a string which should be included in the preamble
1544 | - a symbol, the value of which is included in the preamble
1545 | - a function, which is evaluated with the list of feature flags as its
1546 | single argument. The result of which is included in the preamble
1547 | - a list, which is passed to `eval', with a list of feature flags available
1548 | as \"features\"
1549 |
1550 | - :requires, a feature or list of features that must be available
1551 | - :when, a feature or list of features that when all available should cause this
1552 | to be automatically enabled.
1553 | - :prevents, a feature or list of features that should be masked
1554 | - :order, for when ordering is important. Lower values appear first.
1555 | The default is 0.
1556 |
1557 | Features that start with ! will be eagerly loaded, i.e. without being detected.")
1558 | #+end_src
1559 |
1560 | First, we need to detect which features we actually need
1561 |
1562 | #+begin_src emacs-lisp
1563 | (defun org-latex-detect-features (&optional buffer info)
1564 | "List features from `org-latex-conditional-features' detected in BUFFER."
1565 | (let ((case-fold-search nil))
1566 | (with-current-buffer (or buffer (current-buffer))
1567 | (delete-dups
1568 | (mapcan (lambda (construct-feature)
1569 | (when (let ((out (pcase (car construct-feature)
1570 | ((pred stringp) (car construct-feature))
1571 | ((pred functionp) (funcall (car construct-feature) info))
1572 | ((pred listp) (eval (car construct-feature)))
1573 | ((pred symbolp) (symbol-value (car construct-feature)))
1574 | (_ (user-error "org-latex-conditional-features key %s unable to be used" (car construct-feature))))))
1575 | (if (stringp out)
1576 | (save-excursion
1577 | (goto-char (point-min))
1578 | (re-search-forward out nil t))
1579 | out))
1580 | (if (listp (cdr construct-feature)) (cdr construct-feature) (list (cdr construct-feature)))))
1581 | org-latex-conditional-features)))))
1582 | #+end_src
1583 |
1584 | Then we need to expand them and sort them according to the above definitions
1585 |
1586 | #+begin_src emacs-lisp
1587 | (defun org-latex-expand-features (features)
1588 | "For each feature in FEATURES process :requires, :when, and :prevents keywords and sort according to :order."
1589 | (dolist (feature features)
1590 | (unless (assoc feature org-latex-feature-implementations)
1591 | (error "Feature %s not provided in org-latex-feature-implementations" feature)))
1592 | (setq current features)
1593 | (while current
1594 | (when-let ((requirements (plist-get (cdr (assq (car current) org-latex-feature-implementations)) :requires)))
1595 | (setcdr current (if (listp requirements)
1596 | (append requirements (cdr current))
1597 | (cons requirements (cdr current)))))
1598 | (setq current (cdr current)))
1599 | (dolist (potential-feature
1600 | (append features (delq nil (mapcar (lambda (feat)
1601 | (when (plist-get (cdr feat) :eager)
1602 | (car feat)))
1603 | org-latex-feature-implementations))))
1604 | (when-let ((prerequisites (plist-get (cdr (assoc potential-feature org-latex-feature-implementations)) :when)))
1605 | (setf features (if (if (listp prerequisites)
1606 | (cl-every (lambda (preq) (memq preq features)) prerequisites)
1607 | (memq prerequisites features))
1608 | (append (list potential-feature) features)
1609 | (delq potential-feature features)))))
1610 | (dolist (feature features)
1611 | (when-let ((prevents (plist-get (cdr (assoc feature org-latex-feature-implementations)) :prevents)))
1612 | (setf features (cl-set-difference features (if (listp prevents) prevents (list prevents))))))
1613 | (sort (delete-dups features)
1614 | (lambda (feat1 feat2)
1615 | (if (< (or (plist-get (cdr (assoc feat1 org-latex-feature-implementations)) :order) 1)
1616 | (or (plist-get (cdr (assoc feat2 org-latex-feature-implementations)) :order) 1))
1617 | t nil))))
1618 | #+end_src
1619 |
1620 | Finally, we can create the preamble to be inserted
1621 |
1622 | #+begin_src emacs-lisp
1623 | (defun org-latex-generate-features-preamble (features)
1624 | "Generate the LaTeX preamble content required to provide FEATURES.
1625 | This is done according to `org-latex-feature-implementations'"
1626 | (let ((expanded-features (org-latex-expand-features features)))
1627 | (concat
1628 | (format "\n%% features: %s\n" expanded-features)
1629 | (mapconcat (lambda (feature)
1630 | (when-let ((snippet (plist-get (cdr (assoc feature org-latex-feature-implementations)) :snippet)))
1631 | (concat
1632 | (pcase snippet
1633 | ((pred stringp) snippet)
1634 | ((pred functionp) (funcall snippet features))
1635 | ((pred listp) (eval `(let ((features ',features)) (,@snippet))))
1636 | ((pred symbolp) (symbol-value snippet))
1637 | (_ (user-error "org-latex-feature-implementations :snippet value %s unable to be used" snippet)))
1638 | "\n")))
1639 | expanded-features
1640 | "")
1641 | "% end features\n")))
1642 | #+end_src
1643 |
1644 | Last step, some advice to hook in all of the above to work
1645 |
1646 | #+begin_src emacs-lisp
1647 | (defvar info--tmp nil)
1648 |
1649 | (defadvice! org-latex-save-info (info &optional t_ s_)
1650 | :before #'org-latex-make-preamble
1651 | (setq info--tmp info))
1652 |
1653 | (defadvice! org-splice-latex-header-and-generated-preamble-a (orig-fn tpl def-pkg pkg snippets-p &optional extra)
1654 | "Dynamically insert preamble content based on `org-latex-conditional-preambles'."
1655 | :around #'org-splice-latex-header
1656 | (let ((header (funcall orig-fn tpl def-pkg pkg snippets-p extra)))
1657 | (if snippets-p header
1658 | (concat header
1659 | (org-latex-generate-features-preamble (org-latex-detect-features nil info--tmp))
1660 | "\n"))))
1661 | #+end_src
1662 |
1663 | **** Tectonic
1664 | Tectonic is the hot new thing, which also means I can get rid of my tex installation.
1665 |
1666 | #+begin_src emacs-lisp
1667 | (setq-default org-latex-pdf-process '("tectonic -Z shell-escape --outdir=%o %f"))
1668 | #+end_src
1669 |
1670 | **** Classes
1671 | Simple base header shared by all defines classes
1672 |
1673 | #+name: base-template
1674 | #+begin_src latex
1675 | \\documentclass[10pt]{scrartcl}
1676 | [PACKAGES]
1677 | [DEFAULT-PACKAGES]
1678 | [EXTRA]
1679 | \\setmainfont[Ligatures=TeX]{Montserrat}
1680 | \\setmonofont[Ligatures=TeX]{Iosevka Nerd Font Mono}
1681 | #+end_src
1682 |
1683 | #+name: chameleon-template
1684 | #+begin_src latex :noweb yes
1685 | % Using chameleon
1686 | <>
1687 | #+end_src
1688 |
1689 | #+name: work-template
1690 | #+begin_src latex :noweb yes
1691 | % Using work
1692 | <>
1693 | \\usepackage{fontawesome5}
1694 | \\usepackage{tcolorbox}
1695 | \\usepackage{fancyhdr}
1696 | \\usepackage{lastpage}
1697 | \\pagestyle{fancy}
1698 | \\fancyhead{}
1699 | \\fancyhead[RO, LE]{}
1700 | #+end_src
1701 |
1702 | Now for some class setup (likely to change over time)
1703 |
1704 | #+begin_src emacs-lisp :noweb no-export
1705 | (after! ox-latex
1706 | (add-to-list 'org-latex-classes
1707 | '("chameleon" "
1708 | <>
1709 | "
1710 | ("\\section{%s}" . "\\section*{%s}")
1711 | ("\\subsection{%s}" . "\\subsection*{%s}")
1712 | ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
1713 | ("\\paragraph{%s}" . "\\paragraph*{%s}")
1714 | ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
1715 | #+end_src
1716 |
1717 | And some saner defaults for them
1718 |
1719 | #+begin_src emacs-lisp
1720 | (after! ox-latex
1721 | (setq org-latex-tables-booktabs t
1722 | org-latex-default-class "chameleon"
1723 | org-latex-hyperref-template "\\colorlet{greenyblue}{blue!70!green}
1724 | \\colorlet{blueygreen}{blue!40!green}
1725 | \\providecolor{link}{named}{greenyblue}
1726 | \\providecolor{cite}{named}{blueygreen}
1727 | \\hypersetup{
1728 | pdfauthor={%a},
1729 | pdftitle={%t},
1730 | pdfkeywords={%k},
1731 | pdfsubject={%d},
1732 | pdfcreator={%c},
1733 | pdflang={%L},
1734 | breaklinks=true,
1735 | colorlinks=true,
1736 | linkcolor=,
1737 | urlcolor=link,
1738 | citecolor=cite\n}
1739 | \\urlstyle{same}
1740 | "
1741 | org-latex-reference-command "\\cref{%s}"))
1742 | #+end_src
1743 |
1744 | **** Packages
1745 | Add some packages (also very likely to change)
1746 |
1747 | #+begin_src emacs-lisp
1748 | (setq org-latex-default-packages-alist
1749 | `(("AUTO" "inputenc" t ("pdflatex"))
1750 | ("T1" "fontenc" t ("pdflatex"))
1751 | ("" "fontspec" t)
1752 | ("" "xcolor" nil)
1753 | ("" "hyperref" nil)
1754 | ("" "cleveref" nil)))
1755 | #+end_src
1756 |
1757 | **** Pretty code blocks
1758 | Teco is the goto for this, so basically just ripping off him.
1759 |
1760 | #+begin_src elisp
1761 | (package! engrave-faces
1762 | :recipe (:host github :repo "tecosaur/engrave-faces"))
1763 | #+end_src
1764 |
1765 | #+begin_src emacs-lisp
1766 | (use-package! engrave-faces-latex
1767 | :after ox-latex
1768 | :config
1769 | (setq org-latex-listings 'engraved))
1770 | #+end_src
1771 |
1772 | #+begin_src emacs-lisp
1773 | (use-package! engrave-faces-html
1774 | :after ox-html
1775 | :config
1776 | (setq org-latex-listings 'engraved))
1777 | #+end_src
1778 |
1779 | #+begin_src emacs-lisp
1780 | (defvar-local org-export-has-code-p nil)
1781 |
1782 | (defadvice! org-export-expect-no-code (&rest _)
1783 | :before #'org-export-as
1784 | (setq org-export-has-code-p nil))
1785 |
1786 | (defadvice! org-export-register-code (&rest _)
1787 | :after #'org-latex-src-block
1788 | :after #'org-latex-inline-src-block-engraved
1789 | (setq org-export-has-code-p t))
1790 |
1791 | (defadvice! org-latex-example-block-engraved (orig-fn example-block contents info)
1792 | "Like `org-latex-example-block', but supporting an engraved backend"
1793 | :around #'org-latex-example-block
1794 | (let ((output-block (funcall orig-fn example-block contents info)))
1795 | (if (eq 'engraved (plist-get info :latex-listings))
1796 | (format "\\begin{Code}[alt]\n%s\n\\end{Code}" output-block)
1797 | output-block)))
1798 | #+end_src
1799 |
1800 | **** ox-chameleon
1801 | Chameleons are cool, not having to touches faces is cooler (not the COVID kind)
1802 |
1803 | #+begin_src elisp
1804 | (package! ox-chameleon
1805 | :recipe (:host github :repo "tecosaur/ox-chameleon"))
1806 | #+end_src
1807 |
1808 | #+begin_src emacs-lisp
1809 | (use-package! ox-chameleon
1810 | :after ox)
1811 | #+end_src
1812 |
1813 | **** Beamer
1814 | Starting to look into beamer for creating presentations, seems like we need to +steal+ borrow more config from Tecosaur.
1815 |
1816 | Metropolis is a nice theme, with a tiny adjustment it might be the best.
1817 |
1818 | #+begin_src emacs-lisp
1819 | (setq org-beamer-theme "[progressbar=foot]metropolis")
1820 | #+end_src
1821 |
1822 | #+begin_src emacs-lisp :noweb yes
1823 | (defun org-beamer-p (info)
1824 | (eq 'beamer (and (plist-get info :back-end)
1825 | (org-export-backend-name (plist-get info :back-end)))))
1826 |
1827 | (add-to-list 'org-latex-conditional-features '(org-beamer-p . beamer) t)
1828 | (add-to-list 'org-latex-feature-implementations '(beamer :requires .missing-koma :prevents (italic-quotes condensed-lists)) t)
1829 | (add-to-list 'org-latex-feature-implementations '(.missing-koma :snippet "\\usepackage{scrextend}" :order 2) t)
1830 | #+end_src
1831 |
1832 | And lastly, a small tweak to improve how sections are divided
1833 |
1834 | #+begin_src emacs-lisp
1835 | (setq org-beamer-frame-level 2)
1836 | #+end_src
1837 |
1838 | *** (sub|super)script characters
1839 | Annoying having to gate these, so let's fix that
1840 |
1841 | #+begin_src emacs-lisp
1842 | (setq org-export-with-sub-superscripts '{})
1843 | #+end_src
1844 |
1845 | *** Auto-export
1846 | Defines a minor mode I can use to automatically export a PDF on save.
1847 |
1848 | #+begin_src emacs-lisp
1849 | (defun +org-auto-export ()
1850 | (org-beamer-export-to-pdf t))
1851 |
1852 | (define-minor-mode org-auto-export-mode
1853 | "Toggle auto exporting the Org file."
1854 | :global nil
1855 | :lighter ""
1856 | (if org-auto-export-mode
1857 | ;; When the mode is enabled
1858 | (progn
1859 | (add-hook 'after-save-hook #'+org-auto-export :append :local))
1860 | ;; When the mode is disabled
1861 | (remove-hook 'after-save-hook #'+org-auto-export :local)))
1862 | #+end_src
1863 |
1864 | ** org-protocol
1865 | Interact with org-mode from other applications, /including my web browser/. Being
1866 | able to create things like tasks and other org items from anywhere sounds ideal.
1867 |
1868 | #+begin_src emacs-lisp
1869 | (use-package! org-protocol
1870 | :defer t)
1871 | #+end_src
1872 |
1873 | #+begin_src conf :tangle ~/.local/share/applications/org-protocol.desktop :mkdirp yes :noweb yes
1874 | # DO NOT EDIT THIS
1875 | # I have been generated from <>
1876 | [Desktop Entry]
1877 | Name=org-protocol
1878 | Comment=Intercept calls from emacsclient to trigger custom actions
1879 | Categories=Other;
1880 | Keywords=org-protocol;
1881 | Icon=emacs
1882 | Type=Application
1883 | Exec=emacsclient -- %u
1884 | Terminal=false
1885 | StartupWMClass=Emacs
1886 | MimeType=x-scheme-handler/org-protocol;
1887 | #+end_src
1888 |
1889 | * Languages
1890 | Configuration for various programming languages.
1891 |
1892 | ** Clojure
1893 |
1894 | *** Epithet
1895 | Buffers are pretty great, but sometimes they can be named ... less usefully.
1896 |
1897 | #+begin_src elisp
1898 | (package! epithet
1899 | :recipe (:host github :repo "oantolin/epithet"))
1900 | #+end_src
1901 |
1902 | #+begin_src emacs-lisp
1903 | (use-package! epithet
1904 | :hook (clojure-mode . epithet-rename-buffer)
1905 | :init
1906 | ;; (setq-hook! 'clojure-mode-hook doom-modeline-buffer-file-name-style 'buffer-name)
1907 | (defun epithet-for-clojure ()
1908 | "Suggest a name for a `clojure-mode' buffer."
1909 | (when (and (require 'cider nil t)
1910 | (derived-mode-p 'clojure-mode))
1911 | (after! doom-modeline
1912 | (setq-local doom-modeline-buffer-file-name-style 'buffer-name))
1913 | (format
1914 | "%s <%s>"
1915 | (substring-no-properties (cider-current-ns))
1916 | (projectile-project-name))))
1917 |
1918 | :config
1919 | (add-to-list 'epithet-suggesters #'epithet-for-clojure)
1920 | (after! doom-modeline
1921 | (advice-add #'epithet-rename-buffer :after #'doom-modeline-update-buffer-file-name)))
1922 | #+end_src
1923 |
1924 | *** Portal
1925 | This portal thing looks pretty cool yanno.
1926 |
1927 | #+begin_src emacs-lisp
1928 | (defvar lkn/clj-portal-viewers '(":portal.viewer/inspector"
1929 | ":portal.viewer/pprint"
1930 | ":portal.viewer/table"
1931 | ":portal.viewer/tree"
1932 | ":portal.viewer/hiccup"))
1933 |
1934 | (defun lkn/clj-start-of-sexp ()
1935 | (save-excursion
1936 | (paredit-backward-up)
1937 | (point)))
1938 |
1939 | (defun lkn/clj-end-of-sexp ()
1940 | (save-excursion
1941 | (paredit-forward-up)
1942 | (point)))
1943 |
1944 | (defun lkn/portal-open ()
1945 | (interactive)
1946 | (cider-nrepl-sync-request:eval
1947 | "(do (ns dev) (def portal ((requiring-resolve 'portal.api/open) {:launcher :emacs})) (add-tap (requiring-resolve 'portal.api/submit)))"))
1948 |
1949 | (defun lkn/portal-clear ()
1950 | (interactive)
1951 | (cider-nrepl-sync-request:eval "(portal.api/clear)"))
1952 |
1953 | (defun lkn/portal-close ()
1954 | (interactive)
1955 | (cider-nrepl-sync-request:eval "portal.api/close"))
1956 |
1957 | (defun lkn/portal-tap-contained (&optional viewer)
1958 | (interactive (list (when (consp current-prefix-arg)
1959 | (completing-read "Default Viewer: " lkn/clj-portal-viewers))))
1960 | (let ((beg (lkn/clj-start-of-sexp))
1961 | (end (lkn/clj-end-of-sexp)))
1962 | (cider-interactive-eval
1963 | (format "(tap> ^{:portal.viewer/default %s} %s)" (or viewer (car lkn/clj-portal-viewers)) (buffer-substring-no-properties beg end)))))
1964 |
1965 | (defun lkn/portal-tap-last (&optional viewer)
1966 | (interactive (list (when (consp current-prefix-arg)
1967 | (completing-read "Default Viewer: " lkn/clj-portal-viewers))))
1968 | (cider-interactive-eval
1969 | (format "(tap> ^{:portal.viewer/default %s} %s)" (or viewer (car lkn/clj-portal-viewers)) (cider-last-sexp))))
1970 |
1971 | (map! :map (clojure-mode-map clojurescript-mode-map clojurec-mode-map)
1972 | :localleader
1973 | (:prefix ("p p" . "Portal")
1974 | :desc "Open Portal"
1975 | "o" #'lkn/portal-open
1976 | :desc "Clear layers"
1977 | "c" #'lkn/portal-clear
1978 | :desc "Close current session"
1979 | "q" #'lkn/portal-close
1980 | :desc "Send sexp around point"
1981 | "s" #'lkn/portal-tap-contained
1982 | :desc "Send sexp next to point"
1983 | "S" #'lkn/portal-tap-last))
1984 | #+end_src
1985 |
1986 | ** Lua
1987 | First things first; we need a project mode for my awesomewm config.
1988 |
1989 | #+begin_src emacs-lisp
1990 | (def-project-mode! +awesome-config-mode
1991 | :modes '(lua-mode)
1992 | :files ("rc.lua")
1993 | :when (string-prefix-p (expand-file-name "awesome" (xdg-config-home)) default-directory))
1994 |
1995 | (add-hook '+awesome-config-mode-hook #'rainbow-mode)
1996 | #+end_src
1997 |
1998 | ** Ruby
1999 | New year new me. This year, it's Ruby.
2000 |
2001 | *** Enable rbenv
2002 | I don't yet see a case for /not/ having this on all the time, so for now we just
2003 | always have this on.
2004 |
2005 | Easier to manage things with rbenv.
2006 |
2007 | #+begin_src emacs-lisp
2008 | (global-rbenv-mode)
2009 | #+end_src
2010 |
2011 | *** Force disable rvm
2012 | Seems that even just having some remnant of rvm around is enough to trigger it,
2013 | and that breaks a lot.
2014 |
2015 | So, I'd rather just have it always be off.
2016 |
2017 | #+begin_src emacs-lisp
2018 | (setq rspec-use-rvm nil)
2019 | #+end_src
2020 |
2021 | *** Disable other language servers
2022 | Not yet sure which of these is best, so for now until I get a compelling reason
2023 | I'd rather stick with solargraph.
2024 |
2025 | #+begin_src emacs-lisp
2026 | (after! lsp-mode
2027 | (add-to-list 'lsp-disabled-clients 'rubocop-ls)
2028 | (add-to-list 'lsp-disabled-clients 'solargraph)
2029 | (add-to-list 'lsp-disabled-clients 'typeprof-ls))
2030 | #+end_src
2031 |
2032 | *** Enable rainbow-parens
2033 | Another language that's missing this...
2034 |
2035 | #+begin_src emacs-lisp
2036 | (add-hook 'ruby-mode-hook #'rainbow-delimiters-mode)
2037 | #+end_src
2038 |
2039 | ** LSP/DAP
2040 | *** Increase variable line length
2041 | By default this is /way/ too short.
2042 |
2043 | #+begin_src emacs-lisp
2044 | (setq dap-ui-variable-length 200)
2045 | #+end_src
2046 |
2047 | *** Improve completions
2048 | The default completions are quite bad
2049 |
2050 | #+begin_src emacs-lisp
2051 | (after! lsp-mode
2052 | (setq +lsp-company-backends
2053 | '(:separate company-capf company-yasnippet company-dabbrev)))
2054 | #+end_src
2055 |
2056 | * Completion
2057 | Completion is handled by the amazing VERTICO stack, most of which we can just rely on stock Doom setup.
2058 |
2059 | There are however a few minor changes we want...
2060 |
2061 | ** Projectile completion fn
2062 | In order to get a slightly nicer UI, we can set this manually rather than relying on the default =completing-read=.
2063 |
2064 | #+begin_src emacs-lisp
2065 | (autoload #'consult--read "consult")
2066 |
2067 | ;;;###autoload
2068 | (defun +vertico/projectile-completion-fn (prompt choices)
2069 | "Given a PROMPT and a list of CHOICES, filter a list of files for
2070 | `projectile-find-file'."
2071 | (interactive)
2072 | (consult--read
2073 | choices
2074 | :prompt prompt
2075 | :sort nil
2076 | :add-history (thing-at-point 'filename)
2077 | :category 'file
2078 | :history '(:input +vertico/find-file-in--history)))
2079 |
2080 | (setq projectile-completion-system '+vertico/projectile-completion-fn)
2081 | #+end_src
2082 |
2083 |
2084 | ** Jump to heading
2085 | #+begin_src emacs-lisp
2086 | (defun flatten-imenu-index (index &optional prefix)
2087 | "Flatten an org-mode imenu index."
2088 | (let ((flattened '()))
2089 | (dolist (item index flattened)
2090 | (let* ((name (propertize (car item) 'face (intern (format "org-level-%d" (if prefix (+ 2 (cl-count ?/ prefix)) 1)))))
2091 | (prefix (if prefix (concat prefix "/" name) name)))
2092 | (if (imenu--subalist-p item)
2093 | (setq flattened (append flattened (flatten-imenu-index (cdr item) prefix)))
2094 | (push (cons prefix (cdr item)) flattened))))
2095 | (nreverse flattened)))
2096 |
2097 | ;;;###autoload
2098 | (defun +literate-jump-heading ()
2099 | "Jump to a heading in the literate org file."
2100 | (interactive)
2101 | (let* ((+literate-config-file (file-name-concat doom-user-dir "config.org"))
2102 | (buffer (or (find-buffer-visiting +literate-config-file)
2103 | (find-file-noselect +literate-config-file t))))
2104 | (with-current-buffer buffer
2105 | (let* ((imenu-auto-rescan t)
2106 | (org-imenu-depth 8)
2107 | (index (flatten-imenu-index (imenu--make-index-alist))))
2108 | (let ((c (current-window-configuration))
2109 | (result nil))
2110 | (unwind-protect
2111 | (progn
2112 | (switch-to-buffer buffer)
2113 | (cond
2114 | ((modulep! :completion vertico)
2115 | (setq result (consult-org-heading)))
2116 | (t
2117 | (let ((entry (assoc (completing-read "Go to heading: " index nil t) index)))
2118 | (setq result entry)
2119 | (imenu entry)))))
2120 | (unless result
2121 | (set-window-configuration c))))))))
2122 |
2123 | (map! :leader :n :desc "Open heading in literate config" "f o" #'+literate-jump-heading)
2124 | #+end_src
2125 | * Snippets
2126 | I constantly find myself complaining I don't have snippets setup, and yet I
2127 | always forget to set snippets up. [[https://www.youtube.com/watch?v=sc5iTNVEOAg][My own worst enemy]]? Probably. But who's
2128 | keeping score...
2129 |
2130 | ** Snippet definitions
2131 | :PROPERTIES:
2132 | :header-args:snippet: :mkdirp yes :tangle (expand-file-name (downcase (car (last (org-get-outline-path t)))) (expand-file-name (downcase (car (last (butlast (org-get-outline-path t))))) "snippets"))
2133 | :END:
2134 |
2135 | A collection of snippets tangled using clever magic.
2136 |
2137 | *** Org-mode
2138 | **** __
2139 | #+begin_src snippet
2140 | # -*- mode: snippet -*-
2141 | # name: Org template
2142 | # --
2143 | ,#+title: ${1:`(s-titleized-words (replace-regexp-in-string "^[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9]-" "" (file-name-base (or buffer-file-name "new buffer"))))`}
2144 | ,#+author: ${2:`(user-full-name)`}
2145 | ,#+date: \today
2146 | ,#+latex_class: chameleon
2147 |
2148 | $0
2149 | #+end_src
2150 |
2151 | *** slack-message-compose-buffer-mode
2152 | **** standup
2153 | #+begin_src snippet
2154 | # -*- mode: snippet -*-
2155 | # name: Standup template
2156 | # --
2157 | *Standup*
2158 |
2159 | $1: $2
2160 | `(format-time-string "%d/%m/%y" (current-time))`: $3
2161 |
2162 | *Blocked*: $4
2163 | #+end_src
2164 |
2165 | * Packages
2166 | Place to put packages that don't have a guaranteed home yet.
2167 |
2168 | ** Disabled/unpin
2169 | Packages to be unpinned or just completely disabled
2170 |
2171 | #+begin_src elisp
2172 | (disable-packages! evil-escape org-yt)
2173 | (unpin! evil-collection)
2174 | (package! apheleia
2175 | :recipe (:local-repo "~/build/elisp/apheleia"))
2176 | (package! engrave-faces
2177 | :recipe (:host github :repo "elken/engrave-faces"))
2178 | (package! ox-chameleon
2179 | :recipe (:host github :repo "elken/ox-chameleon"))
2180 | #+end_src
2181 |
2182 | ** embark-vc
2183 | Embark additions to improve various vc operations
2184 |
2185 | #+begin_src elisp
2186 | (package! embark-vc)
2187 | #+end_src
2188 |
2189 | #+begin_src emacs-lisp
2190 | (use-package! embark-vc
2191 | :after embark)
2192 | #+end_src
2193 |
2194 | ** prescient
2195 | Need to add this into company module when I've tested
2196 |
2197 | #+begin_src elisp
2198 | (when (modulep! :completion company)
2199 | (package! company-prescient))
2200 | #+end_src
2201 |
2202 | #+begin_src emacs-lisp
2203 | (when (modulep! :completion company)
2204 | (use-package! company-prescient
2205 | :after company
2206 | :hook (company-mode . company-prescient-mode)
2207 | :hook (company-prescient-mode . prescient-persist-mode)
2208 | :config
2209 | (setq prescient-save-file (concat doom-cache-dir "prescient-save.el")
2210 | history-length 1000)))
2211 | #+end_src
2212 |
2213 | ** Rainbow Identifiers
2214 | *** TODO Fix in web-mode
2215 | Web-mode has normal text which should be ignored.
2216 |
2217 | #+begin_src elisp
2218 | (package! rainbow-identifiers)
2219 | #+end_src
2220 |
2221 | #+begin_src emacs-lisp
2222 | (use-package! rainbow-identifiers
2223 | ;; :hook (php-mode . rainbow-identifiers-mode)
2224 | ;; :hook (org-mode . (lambda () (rainbow-identifiers-mode -1)))
2225 | ;; :hook (web-mode . (lambda () (rainbow-identifiers-mode -1)))
2226 | :config
2227 | (setq rainbow-identifiers-faces-to-override
2228 | '(php-variable-name
2229 | php-property-name
2230 | php-variable-sigil
2231 | web-mode-variable-name-face)))
2232 | #+end_src
2233 |
2234 | ** Cucumber
2235 | Needed for feature test files
2236 |
2237 | #+begin_src elisp
2238 | (package! feature-mode)
2239 | #+end_src
2240 |
2241 | #+begin_src emacs-lisp
2242 | (use-package! feature-mode
2243 | :mode "\\.feature$")
2244 | #+end_src
2245 |
2246 | ** Systemd
2247 | Starting to actually write more of these now, so this is an easy sell
2248 |
2249 | #+begin_src elisp
2250 | (package! systemd)
2251 | #+end_src
2252 |
2253 | #+begin_src emacs-lisp
2254 | (use-package! systemd
2255 | :mode "\\.service$")
2256 | #+end_src
2257 |
2258 | ** RPM Spec
2259 | Needed for rpm files to not be treated poorly (there, there)
2260 |
2261 | #+begin_src elisp
2262 | (package! rpm-spec-mode
2263 | :recipe (:host github :repo "bhavin192/rpm-spec-mode"))
2264 | #+end_src
2265 |
2266 | #+begin_src emacs-lisp
2267 | (use-package! rpm-spec-mode
2268 | :mode "\\.spec\\(\\.in\\)?$")
2269 | #+end_src
2270 |
2271 | ** Autothemer
2272 | Needed for a very WIP theme, otherwise not needed.
2273 |
2274 | #+begin_src elisp
2275 | (package! autothemer)
2276 | #+end_src
2277 |
2278 | ** Bamboo
2279 | Setup for my package for integrating with Bamboo HR
2280 |
2281 | #+begin_src elisp
2282 | (package! bhr
2283 | :recipe (:host github :repo "elken/bhr.el"))
2284 | #+end_src
2285 |
2286 | #+begin_src emacs-lisp
2287 | (use-package! bhr
2288 | :commands (bhr-view-timesheet bhr-submit-multiple))
2289 | #+end_src
2290 |
2291 | ** YADM
2292 | [[https://yadm.io][yadm]] is my preferred dotfile manager of choice, but by default because of the nature of how the repo is handled; it's quite a pain to manage from Emacs.
2293 |
2294 | *** tramp-yadm
2295 | tramp-yadm to the rescue! This lets me use magit & projectile as expected on the repo; allowing me to manage dotfile changes with the superior git client.
2296 |
2297 | #+begin_src elisp
2298 | (package! tramp-yadm
2299 | :recipe (:host github :repo "seanfarley/tramp-yadm"))
2300 | #+end_src
2301 |
2302 | #+begin_src emacs-lisp
2303 | (use-package! tramp-yadm
2304 | :defer t
2305 | :init
2306 | (defun yadm-status ()
2307 | "Invoke magit on the yadm repo"
2308 | (interactive)
2309 | (magit-status "/yadm::~")
2310 | (setq-local magit-git-executable (executable-find "yadm"))
2311 | (setq-local magit-remote-git-executable (executable-find "yadm")))
2312 |
2313 | (after! magit
2314 | (tramp-yadm-register)
2315 | (map! :leader :desc "Open yadm status" "g p" #'yadm-status)))
2316 | #+end_src
2317 |
2318 | ** Keychain
2319 | [[http://www.funtoo.org/wiki/Keychain][Keychain]] is /amazing/. It wraps ssh-agent and gpg-agent so I never have to.
2320 |
2321 | The /problem/ is Emacs doesn't always detect it nicely ... until now!
2322 |
2323 | #+begin_src emacs-lisp
2324 | (defun +keychain-startup-hook ()
2325 | "Load keychain env after emacs"
2326 | (let* ((ssh (shell-command-to-string "keychain -q --noask --agents ssh --eval"))
2327 | (gpg (shell-command-to-string "keychain -q --noask --agents gpg --eval")))
2328 | (list (and ssh
2329 | (string-match "SSH_AUTH_SOCK[=\s]\\([^\s;\n]*\\)" ssh)
2330 | (setenv "SSH_AUTH_SOCK" (match-string 1 ssh)))
2331 | (and ssh
2332 | (string-match "SSH_AGENT_PID[=\s]\\([0-9]*\\)?" ssh)
2333 | (setenv "SSH_AGENT_PID" (match-string 1 ssh)))
2334 | (and gpg
2335 | (string-match "GPG_AGENT_INFO[=\s]\\([^\s;\n]*\\)" gpg)
2336 | (setenv "GPG_AGENT_INFO" (match-string 1 gpg))))))
2337 |
2338 | (add-hook 'after-init-hook #'+keychain-startup-hook)
2339 | #+end_src
2340 |
2341 | ** Asciidoc
2342 | #+begin_src elisp
2343 | (package! adoc-mode)
2344 | #+end_src
2345 |
2346 | ** Graphviz
2347 | Some config to help with graphviz
2348 |
2349 | #+begin_src elisp
2350 | (package! graphviz-dot-mode)
2351 | #+end_src
2352 |
2353 | #+begin_src emacs-lisp
2354 | (use-package! graphviz-dot-mode
2355 | :init
2356 | (after! company
2357 | (require 'company-graphviz-dot)))
2358 | #+end_src
2359 |
2360 | ** Exercism
2361 | Exercism is a useful site for learning a programming language by performing
2362 | various exercises. You can opt to use either an in-browser editor or your own
2363 | via a local CLI.
2364 |
2365 | Which do you think I want?
2366 |
2367 | #+begin_src elisp
2368 | (package! exercism-modern
2369 | :recipe (:files (:defaults "icons")
2370 | :host github :repo "elken/exercism-modern"))
2371 | #+end_src
2372 |
2373 | #+begin_src emacs-lisp
2374 | (use-package! exercism-modern
2375 | :commands (exercism-modern-jump exercism-modern-view-tracks))
2376 | #+end_src
2377 |
2378 | ** evil-cleverparens
2379 | Trying to find a decent structural editor I like...
2380 |
2381 | #+begin_src elisp
2382 | (package! evil-cleverparens
2383 | :recipe (:host github :repo "tomdl89/evil-cleverparens" :branch "fix/delete-escaped-parens"))
2384 |
2385 | (package! paredit)
2386 | #+end_src
2387 |
2388 | #+begin_src emacs-lisp
2389 | (use-package! paredit
2390 | :hook (emacs-lisp-mode . paredit-mode)
2391 | :hook (clojure-mode . paredit-mode))
2392 |
2393 | (use-package! evil-cleverparens
2394 | :when (modulep! :editor evil +everywhere)
2395 | :hook (paredit-mode . evil-cleverparens-mode))
2396 | #+end_src
2397 |
2398 | ** litable
2399 | This is literally the coolest package ever...
2400 |
2401 | #+begin_src elisp
2402 | (package! litable
2403 | :recipe (:host github :repo "Fuco1/litable"))
2404 | #+end_src
2405 |
2406 | #+begin_src emacs-lisp
2407 | (use-package! litable
2408 | :custom
2409 | (litable-list-file (expand-file-name "litable-lists.el" doom-cache-dir))
2410 | :init
2411 | (map! :localleader
2412 | :map emacs-lisp-mode-map
2413 | (:prefix ("t" . "toggle")
2414 | "l" #'litable-mode)))
2415 | #+end_src
2416 |
2417 | ** magit-file-icons
2418 | A simple package to add file icons in magit views.
2419 |
2420 | #+begin_src elisp
2421 | (package! magit-file-icons
2422 | :pin "0006e243b0e7aa005f6e8900b4b2e3a92c2d0532"
2423 | :recipe (:host github :repo "gekoke/magit-file-icons"))
2424 | #+end_src
2425 |
2426 | #+begin_src emacs-lisp
2427 | (use-package! magit-file-icons
2428 | :after magit
2429 | :init
2430 | (magit-file-icons-mode 1))
2431 | #+end_src
2432 |
2433 | * Spelling
2434 |
2435 | #+begin_src emacs-lisp
2436 | (setq ispell-program-name "aspell"
2437 | ispell-extra-args '("--sug-mode=ultra" "--lang=en_GB")
2438 | ispell-dictionary "en"
2439 | ispell-personal-dictionary "~/Nextcloud/dict")
2440 |
2441 | (after! cape
2442 | (setq cape-dict-file (if (file-exists-p ispell-personal-dictionary) ispell-personal-dictionary cape-dict-file)))
2443 | #+end_src
2444 |
2445 | * Local settings
2446 | Needed some way to manage settings for a local machine, so let's be lazy with it
2447 |
2448 | #+begin_src emacs-lisp
2449 | (when (file-exists-p! "config-local.el" doom-private-dir)
2450 | (load! "config-local.el" doom-private-dir))
2451 | #+end_src
2452 |
2453 | ** dotenv
2454 | Better handle setting of environment variables needed for various tools
2455 | #+begin_src elisp
2456 | (package! dotenv
2457 | :recipe (:host github :repo "pkulev/dotenv.el"))
2458 | #+end_src
2459 |
2460 | #+begin_src emacs-lisp
2461 | (use-package! dotenv
2462 | :init
2463 | (when (file-exists-p (expand-file-name ".env" doom-user-dir))
2464 | (add-hook! 'doom-init-ui-hook
2465 | (defun +dotenv-startup-hook ()
2466 | "Load .env after starting emacs"
2467 | (dotenv-update-project-env doom-user-dir))))
2468 | :config
2469 | (add-hook! 'projectile-after-switch-project-hook
2470 | (defun +dotenv-projectile-hook ()
2471 | "Load .env after changing projects."
2472 | (dotenv-update-project-env (projectile-project-root)))))
2473 | #+end_src
2474 |
--------------------------------------------------------------------------------
/eshell/aliases:
--------------------------------------------------------------------------------
1 | alias _ sudo $*
2 | alias q exit
3 | alias c recenter 0
4 | alias e for i in ${eshell-flatten-list $*} {find-file $i}
5 | alias o for i in ${eshell-flatten-list $*} {consult-file-externally $i}
6 | alias l ls -1A $*
7 | alias ll ls -lh $*
8 | alias lt ll -tr $*
9 | alias la ll -A $*
10 | alias lc lt -c $*
11 | alias lk ll -Sr $*
12 | alias lm la $* | "$PAGER"
13 | alias lr ll -R $*
14 | alias lu lt -u $*
15 | alias lx ll -XB $*
16 |
--------------------------------------------------------------------------------
/exwm/picom.conf:
--------------------------------------------------------------------------------
1 | # ▀ ▄▀▀
2 | # ▄▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄ ▄▄▄ ▄▄▄ ▄ ▄▄ ▄▄█▄▄
3 | # █▀ ▀█ █ █▀ ▀ █▀ ▀█ █ █ █ █▀ ▀ █▀ ▀█ █▀ █ █
4 | # █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
5 | # ██▄█▀ ▄▄█▄▄ ▀█▄▄▀ ▀█▄█▀ █ █ █ █ ▀█▄▄▀ ▀█▄█▀ █ █ █
6 | # █
7 | # ▀
8 |
9 |
10 |
11 |
12 | # Shadow
13 |
14 | shadow = true;
15 | shadow-radius = 12;
16 | shadow-offset-x = -12;
17 | shadow-offset-y = -12;
18 | shadow-opacity = 0.7;
19 |
20 | # shadow-red = 0.0;
21 | # shadow-green = 0.0;
22 | # shadow-blue = 0.0;
23 |
24 | # shadow-exclude-reg = "x10+0+0";
25 | xinerama-shadow-crop = true;
26 |
27 | shadow-exclude = [
28 | "name = 'Notification'",
29 | "class_g = 'Conky'",
30 | "class_g ?= 'Notify-osd'",
31 | "class_g = 'Cairo-clock'",
32 | "class_g = 'slop'",
33 | "class_g = 'Firefox' && argb",
34 | "class_g = 'Rofi'",
35 | "_GTK_FRAME_EXTENTS@:c",
36 | "_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'"
37 | ];
38 |
39 | # Logs
40 | log-level = "ERROR";
41 | log-file = "~/.cache/picom-log.log";
42 |
43 |
44 | # Opacity
45 |
46 | # inactive-opacity = 0.8;
47 | # active-opacity = 0.8;
48 | # frame-opacity = 0.7;
49 | inactive-opacity-override = false;
50 |
51 | opacity-rule = [
52 | "80:class_g = 'URxvt'",
53 | "80:class_g = 'UXTerm'",
54 | "80:class_g = 'XTerm'"
55 | ];
56 |
57 | # inactive-dim = 0.2;
58 | # inactive-dim-fixed = true;
59 |
60 | # Blur
61 |
62 | blur: {
63 | method = "dual_kawase";
64 | strength = 2.0;
65 | # deviation = 1.0;
66 | # kernel = "11x11gaussian";
67 | }
68 |
69 | # blur-background = true;
70 | blur-background-frame = true;
71 | # blur-kern = "3x3box";
72 | # blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
73 | # blur-background-fixed = true;
74 |
75 | blur-background-exclude = [
76 | "window_type = 'desktop'",
77 | "window_type = 'utility'",
78 | "window_type = 'notification'",
79 | "class_g = 'slop'",
80 | "class_g = 'Firefox' && argb",
81 | "name = 'rofi - Search'",
82 | "_GTK_FRAME_EXTENTS@:c"
83 | ];
84 |
85 | # max-brightness = 0.66
86 |
87 | # Fading
88 |
89 | fading = true;
90 | fade-delta = 3;
91 | fade-in-step = 0.03;
92 | fade-out-step = 0.03;
93 | # no-fading-openclose = true;
94 | # no-fading-destroyed-argb = true;
95 | fade-exclude = [ ];
96 |
97 | # Other
98 |
99 | backend = "glx";
100 | mark-wmwin-focused = true;
101 | mark-ovredir-focused = true;
102 | # use-ewmh-active-win = true;
103 | detect-rounded-corners = true;
104 | detect-client-opacity = true;
105 | refresh-rate = 144;
106 | vsync = true;
107 | # sw-opti = true;
108 | unredir-if-possible = false;
109 | # unredir-if-possible-delay = 5000;
110 | # unredir-if-possible-exclude = [ ];
111 | # focus-exclude = [ "class_g = 'Cairo-clock'" ];
112 |
113 | focus-exclude = [
114 | "class_g = 'Cairo-clock'",
115 | "class_g ?= 'rofi'",
116 | "class_g ?= 'slop'",
117 | "class_g ?= 'Steam'"
118 | ];
119 |
120 |
121 | detect-transient = true;
122 | detect-client-leader = true;
123 | invert-color-include = [ ];
124 | # resize-damage = 1;
125 |
126 | # GLX backend
127 |
128 | glx-no-stencil = true;
129 | # glx-no-rebind-pixmap = true;
130 | # xrender-sync-fence = true;
131 | use-damage = true;
132 |
133 | # Rounded corner stuff
134 | corner-radius = 8;
135 | round-borders = 1;
136 | experimental-backends = true;
137 |
138 |
139 | # Window type settings
140 |
141 | wintypes:
142 | {
143 | tooltip = { fade = true; shadow = true; focus = false; };
144 | normal = { shadow = false; };
145 | dnd = { shadow = false; };
146 | popup_menu = { shadow = true; focus = false; opacity = 0.90; };
147 | dropdown_menu = { shadow = true; focus = false; };
148 | above = { shadow = true; };
149 | splash = { shadow = false; };
150 | utility = { focus = false; shadow = false; };
151 | notification = { shadow = false; };
152 | desktop = { shadow = false };
153 | menu = { focus = false };
154 | dialog = { shadow = true; };
155 | };
156 |
--------------------------------------------------------------------------------
/exwm/start-debug.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | exec >~/.logs/xsession 2>&1
4 | export LANG="en_GB.UTF-8"
5 | export LANGUAGE="en_GB.UTF-8"
6 | export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
7 | xset -dpms
8 | xset s off
9 | xhost +SI:localuser:$USER
10 | picom -b --experimental-backends --dbus --config ~/.doom.d/exwm/picom.conf
11 | emacs -mm --with-exwm --debug-init
12 |
--------------------------------------------------------------------------------
/exwm/start.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | exec >~/.logs/xsession 2>&1
4 | export LANG="en_GB.UTF-8"
5 | export LANGUAGE="en_GB.UTF-8"
6 | export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
7 | export _JAVA_AWT_WM_NONREPARENTING=1
8 | wmname LG3D
9 | xset -dpms
10 | xset s off
11 | # xss-lock -- gnome-screensaver-command -l &
12 | xhost +SI:localuser:$USER
13 | # picom -b --experimental-backends --dbus --config ~/.doom.d/exwm/picom.conf
14 | emacs -mm --with-exwm
15 |
--------------------------------------------------------------------------------
/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elken/doom/c39cd56615355a0c16fa69462d6b3ded39ca1a42/images/background.png
--------------------------------------------------------------------------------
/images/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elken/doom/c39cd56615355a0c16fa69462d6b3ded39ca1a42/images/banner.png
--------------------------------------------------------------------------------
/images/kill-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elken/doom/c39cd56615355a0c16fa69462d6b3ded39ca1a42/images/kill-process.png
--------------------------------------------------------------------------------
/images/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elken/doom/c39cd56615355a0c16fa69462d6b3ded39ca1a42/images/overview.png
--------------------------------------------------------------------------------
/images/tray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elken/doom/c39cd56615355a0c16fa69462d6b3ded39ca1a42/images/tray.png
--------------------------------------------------------------------------------
/init.el:
--------------------------------------------------------------------------------
1 | ;;; init.el -*- lexical-binding: t; -*-
2 |
3 | ;; This file controls what Doom modules are enabled and what order they load
4 | ;; in. Remember to run 'doom sync' after modifying it!
5 |
6 | ;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
7 | ;; documentation. There you'll find a "Module Index" link where you'll find
8 | ;; a comprehensive list of Doom's modules and what flags they support.
9 |
10 | ;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
11 | ;; 'C-c c k' for non-vim users) to view its documentation. This works on
12 | ;; flags as well (those symbols that start with a plus).
13 | ;;
14 | ;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
15 | ;; directory (for easy access to its source code).
16 |
17 | ;; Loading before emacs
18 | (setq treemacs-git-mode 'deferred
19 | doom-themes-treemacs-theme "doom-colors")
20 |
21 | ;; Make line endings work
22 | (setq evil-respect-visual-line-mode t)
23 |
24 | (setq doom-localleader-key ",")
25 |
26 | ;; LSP prefers plists
27 | (setenv "LSP_USE_PLISTS" "1")
28 |
29 | (setq process-connection-type nil)
30 |
31 | (doom! :input
32 | ;;chinese
33 | ;;japanese
34 | ;;layout ; auie,ctsrnm is the superior home row
35 |
36 | :completion
37 | ;; (company +childframe); the ultimate code completion backend
38 |
39 | (corfu +icons
40 | +orderless)
41 | (vertico +icons)
42 | ;;helm ; the *other* search engine for love and life
43 | ;;ido ; the other *other* search engine...
44 | ;;(ivy +childframe
45 | ;; +prescient
46 | ;; +icons) ; a search engine for love and life
47 |
48 | :ui
49 | ;; deft ; notational velocity for Emacs
50 | doom ; what makes DOOM look the way it does
51 | doom-dashboard ; a nifty splash screen for Emacs
52 | doom-quit ; DOOM quit-message prompts when you quit Emacs
53 | (emoji +unicode)
54 | ;; exwm
55 | ;;fill-column ; a `fill-column' indicator
56 | hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
57 | ;;hydra
58 | ;; indent-guides ; highlighted indent columns
59 | ligatures ; ligatures and symbols to make your code pretty again
60 | ;; minimap ; show a map of the code on the side
61 | modeline ; snazzy, Atom-inspired modeline, plus API
62 | ;;nav-flash ; blink cursor line after big motions
63 | ;;neotree ; a project drawer, like NERDTree for vim
64 | ophints ; highlight the region an operation acts on
65 | ;; perspective
66 | (popup +all
67 | +defaults) ; tame sudden yet inevitable temporary windows
68 | ;; tabs ; a tab bar for Emacs
69 | ;; tab-bar
70 | ;; (treemacs +lsp) ; a project drawer, like neotree but cooler
71 | ;; unicode ; extended unicode support for various languages
72 | (vc-gutter +pretty) ; vcs diff in the fringe
73 | ;; vi-tilde-fringe ; fringe tildes to mark beyond EOB
74 | (window-select +numbers) ; visually switch windows
75 | workspaces ; tab emulation, persistence & separate workspaces
76 | ;; zen ; distraction-free coding or writing
77 |
78 | :editor
79 | (evil +everywhere) ; come to the dark side, we have cookies
80 | file-templates ; auto-snippets for empty files
81 | fold ; (nigh) universal code folding
82 | (format +onsave) ; automated prettiness
83 | ;;god ; run Emacs commands without modifier keys
84 | ;; lispy ; vim for lisp, for people who don't like vim
85 | multiple-cursors ; editing in many places at once
86 | ;;objed ; text object editing for the innocent
87 | ;;parinfer ; turn lisp into python, sort of
88 | ;;rotate-text ; cycle region at point between text candidates
89 | snippets ; my elves. They type so I don't have to
90 | ;;word-wrap ; soft wrapping with language-aware indent
91 |
92 | :emacs
93 | (dired +icons) ; making dired pretty [functional]
94 | electric ; smarter, keyword-based electric-indent
95 | ;; (ibuffer +icons) ; interactive buffer management
96 | (undo +tree) ; persistent, smarter undo for your inevitable mistakes
97 | vc ; version-control and Emacs, sitting in a tree
98 |
99 | :term
100 | ;; eshell ; the elisp shell that works everywhere
101 | ;;shell ; simple shell REPL for Emacs
102 | ;;term ; basic terminal emulator for Emacs
103 | vterm ; the best terminal emulation in Emacs
104 |
105 | :checkers
106 | syntax ; tasing you for every semicolon you forget
107 | ;; (:if (executable-find "aspell") (spell +aspell)) ; tasing you for misspelling mispelling
108 | ;; grammar ; tasing grammar mistake every you make
109 | jinx
110 |
111 | :tools
112 | ;;ansible
113 | (debugger +lsp) ; FIXME stepping through code, to help you add bugs
114 | direnv
115 | docker
116 | editorconfig ; let someone else argue about tabs vs spaces
117 | ;;ein ; tame Jupyter notebooks with emacs
118 | (eval +overlay) ; run code, run (also, repls)
119 | (lookup +docsets) ; navigate your code and its documentation
120 | (lsp +peek)
121 | (magit +forge) ; a git porcelain for Emacs
122 | ;;make ; run make tasks from Emacs
123 | ;;pass ; password manager for nerds
124 | pdf ; pdf enhancements
125 | ;;prodigy ; FIXME managing external services & code builders
126 | terraform ; infrastructure as code
127 | tree-sitter
128 | ;;tmux ; an API for interacting with tmux
129 | ;;upload ; map local to remote projects via ssh/ftp
130 |
131 | :os
132 | (:if IS-MAC macos) ; improve compatibility with macOS
133 | tty ; improve the terminal Emacs experience
134 |
135 | :lang
136 | ;;agda ; types of types of types of types...
137 | ;;cc ; C/C++/Obj-C madness
138 | (clojure +lsp) ; java with a lisp
139 | ;; common-lisp ; if you've seen one lisp, you've seen them all
140 | ;;coq ; proofs-as-programs
141 | ;;crystal ; ruby at the speed of c
142 | ;; (csharp +lsp
143 | ;; +sharper
144 | ;; +tree-sitter) ; unity, .NET, and mono shenanigans
145 | data ; config/data formats
146 | ;; (dart +lsp
147 | ;; +flutter) ; paint ui and not much else
148 | (elixir +lsp
149 | +tree-sitter) ; erlang done right
150 | ;;elm ; care for a cup of TEA?
151 | (emacs-lisp +tree-sitter) ; drown in parentheses
152 | ;;erlang ; an elegant language for a more civilized age
153 | ;;ess ; emacs speaks statistics
154 | ;;faust ; dsp, but you get to keep your soul
155 | ;; (fsharp
156 | ;; +tree-sitter
157 | ;; +lsp) ; ML stands for Microsoft's Language
158 | ;;fstar ; (dependent) types and (monadic) effects and Z3
159 | (graphql +lsp) ; Give it a REST
160 | ;;gdscript ; the language you waited for
161 | ;;(go +lsp) ; the hipster dialect
162 | ;; (haskell +dante) ; a language that's lazier than I am
163 | ;;hy ; readability of scheme w/ speed of python
164 | ;;idris ; a language you can depend on
165 | (json +lsp
166 | +tree-sitter) ; At least it ain't XML
167 | ;; (java +lsp
168 | ;; +tree-sitter) ; the poster child for carpal tunnel syndrome
169 | (javascript +lsp
170 | +tree-sitter) ; all(hope(abandon(ye(who(enter(here))))))
171 | ;;julia ; a better, faster MATLAB
172 | ;; (kotlin +lsp) ; a better, slicker Java(Script)
173 | ;;latex ; writing papers in Emacs has never been so fun
174 | ;;lean
175 | ;;factor
176 | ;;ledger ; an accounting system in Emacs
177 | (lua +lsp) ; one-based indices? one-based indices
178 | (markdown +grip) ; writing docs for people to ignore
179 | ;;nim ; python + lisp at the speed of c
180 | ;; nix ; I hereby declare "nix geht mehr!"
181 | ;; (ocaml +lsp) ; an objective camel
182 | (org +gnuplot
183 | +dragndrop
184 | +journal
185 | +hugo
186 | +noter
187 | +pandoc
188 | +present
189 | +pretty
190 | +roam2) ; organize your plain life in plain text
191 | ;; (php +lsp) ; perl's insecure younger brother
192 | ;;plantuml ; diagrams for confusing people more
193 | ;;purescript ; javascript, but functional
194 | ;; (python +lsp
195 | ;; +pyright
196 | ;; +tree-sitter
197 | ;; +poetry) ; beautiful is better than ugly
198 | ;;qt ; the 'cutest' gui framework ever
199 | ;;racket ; a DSL for DSLs
200 | ;;raku ; the artist formerly known as perl6
201 | rest ; Emacs as a REST client
202 | ;;rst ; ReST in peace
203 | (ruby +lsp
204 | +tree-sitter
205 | +rbenv
206 | +tree-sitter
207 | +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
208 | ;; (rust +lsp
209 | ;; +tree-sitter) ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
210 | ;;scala ; java, but good
211 | ;; scheme ; a fully conniving family of lisps
212 | (sh +lsp) ; she sells {ba,z,fi}sh shells on the C xor
213 | ;;sml
214 | ;;solidity ; do you need a blockchain? No.
215 | ;;swift ; who asked for emoji variables?
216 | ;;terra ; Earth and Moon in alignment for performance.
217 | (web +html
218 | +lsp
219 | +css) ; the tubes
220 | (yaml +lsp) ; JSON, but readable
221 |
222 | :email
223 | ;; (mu4e +gmail
224 | ;; +org)
225 | ;;notmuch
226 | ;;(wanderlust +gmail)
227 |
228 | :app
229 | ;;calendar
230 | ;;irc ; how neckbeards socialize
231 | ;;(rss +org) ; emacs as an RSS reader
232 | ;;twitter ; twitter client https://twitter.com/vnought
233 | ;; everywhere
234 |
235 | :config
236 | literate
237 | (default +bindings +smartparens))
238 |
--------------------------------------------------------------------------------
/modules/checkers/jinx/config.el:
--------------------------------------------------------------------------------
1 | ;;; checkers/jinx/config.el -*- lexical-binding: t; -*-
2 |
3 | (use-package! jinx
4 | :init (global-jinx-mode)
5 | :custom
6 | (jinx-languages "en_GB")
7 | (jinx-include-modes '(text-mode prog-mode))
8 | (jinx-include-faces
9 | '((prog-mode font-lock-doc-face)
10 | (conf-mode font-lock-comment-face)))
11 | (jinx-exclude-regexps
12 | '((t "[A-Z]+\\>"
13 | "\\<[[:upper:]][[:lower:]]+\\>"
14 | "\\w*?[0-9\.'\"-]\\w*"
15 | "[a-z]+://\\S-+"
16 | "[-+_.~a-zA-Z][-+_.~:a-zA-Z0-9]*@[-.a-zA-Z0-9]+>?")))
17 | :bind
18 | (("M-$" . jinx-correct)))
19 |
--------------------------------------------------------------------------------
/modules/checkers/jinx/packages.el:
--------------------------------------------------------------------------------
1 | ;; -*- no-byte-compile: t; -*-
2 | ;;; checkers/jinx/packages.el
3 |
4 | (package! jinx)
5 |
--------------------------------------------------------------------------------
/modules/completion/corfu/autoload/corfu.el:
--------------------------------------------------------------------------------
1 | ;;; completion/corfu/autoload/corfu.el -*- lexical-binding: t; -*-
2 | ;;;###if (modulep! :completion corfu +minibuffer)
3 |
4 | ;;;###autoload
5 | (defun +corfu--enable-in-minibuffer ()
6 | (unless (or (bound-and-true-p mct--active)
7 | (bound-and-true-p vertico--input)
8 | (memq this-command '(evil-ex
9 | evil-ex-search-forward
10 | evil-ex-search-backward))
11 | (and (modulep! :completion helm)
12 | (helm--alive-p))
13 | (corfu-mode +1))))
14 |
--------------------------------------------------------------------------------
/modules/completion/corfu/autoload/extra.el:
--------------------------------------------------------------------------------
1 | ;;; completion/corfu/autoload/extra.el -*- lexical-binding: t; -*-
2 |
3 | ;;;###autoload
4 | (defun +corfu-complete-file-at-point ()
5 | "Complete a file path from scratch at point"
6 | (interactive)
7 | (completion-in-region (point) (point) #'read-file-name-internal))
8 |
9 | ;;;###autoload
10 | (defun +corfu-files ()
11 | "Complete using files source"
12 | (interactive)
13 | (let ((completion-at-point-functions (list #'+file-completion-at-point-function)))
14 | (completion-at-point)))
15 |
16 | ;;;###autoload
17 | (defun +corfu-dabbrev ()
18 | "Complete using dabbrev source"
19 | (interactive)
20 | (let ((completion-at-point-functions (list #'+dabbrev-completion-at-point-function)))
21 | (completion-at-point)))
22 |
23 | ;;;###autoload
24 | (defun +corfu-ispell ()
25 | "Complete using ispell source.
26 |
27 | See `ispell-lookup-words' for more info"
28 | (interactive)
29 | (let ((completion-at-point-functions (list #'+ispell-completion-at-point-function)))
30 | (completion-at-point)))
31 |
32 | ;;;###autoload
33 | (defun +corfu-dict ()
34 | "Complete using dict source.
35 |
36 | See `+dict--words' for extra words, and `+dict-file' for a wordslist source "
37 | (interactive)
38 | (let ((completion-at-point-functions (list #'+dict-completion-at-point-function)))
39 | (completion-at-point)))
40 |
--------------------------------------------------------------------------------
/modules/completion/corfu/autoload/minad-capfs.el:
--------------------------------------------------------------------------------
1 | ;; Daniel "minad" Mendler extra capfs -*- lexical-binding: t -*-
2 | ;; Source : https://github.com/minad/corfu/issues/9#issuecomment-945090516
3 |
4 | (require 'dabbrev)
5 |
6 | ;;;###autoload
7 | (defun +file-completion-at-point-function ()
8 | "File name completion-at-point-function."
9 | (when-let (bounds (bounds-of-thing-at-point 'filename))
10 | (list (car bounds) (cdr bounds)
11 | 'read-file-name-internal
12 | :exclusive 'no
13 | :annotation-function (lambda (_) " (File)"))))
14 |
15 | ;;;###autoload
16 | (defun +dabbrev-completion-at-point-function ()
17 | (let ((dabbrev-check-all-buffers nil)
18 | (dabbrev-check-other-buffers nil))
19 | (dabbrev--reset-global-variables))
20 | (let ((abbrev (ignore-errors (dabbrev--abbrev-at-point))))
21 | (when (and abbrev (not (string-match-p "[ \t]" abbrev)))
22 | (pcase ;; Interruptible scanning
23 | (while-no-input
24 | (let ((inhibit-message t)
25 | (message-log-max nil))
26 | (or (dabbrev--find-all-expansions
27 | abbrev (dabbrev--ignore-case-p abbrev))
28 | t)))
29 | ('nil (keyboard-quit))
30 | ('t nil)
31 | (words
32 | ;; Ignore completions which are too short
33 | (let ((min-len (+ 4 (length abbrev))))
34 | (setq words (seq-remove (lambda (x) (< (length x) min-len)) words)))
35 | (when words
36 | (let ((beg (progn (search-backward abbrev) (point)))
37 | (end (progn (search-forward abbrev) (point))))
38 | (unless (string-match-p "\n" (buffer-substring beg end))
39 | (list beg end words
40 | :exclusive 'no
41 | :annotation-function (lambda (_) " (Dabbrev)"))))))))))
42 |
43 | (autoload 'ispell-lookup-words "ispell")
44 |
45 | ;;;###autoload
46 | (defun +ispell-completion-at-point-function ()
47 | (when-let* ((bounds (bounds-of-thing-at-point 'word))
48 | (table (with-demoted-errors
49 | (let ((message-log-max nil)
50 | (inhibit-message t))
51 | (ispell-lookup-words
52 | (format "*%s*"
53 | (buffer-substring-no-properties (car bounds) (cdr bounds))))))))
54 | (list (car bounds) (cdr bounds) table
55 | :exclusive 'no
56 | :annotation-function (lambda (_) " (Ispell)"))))
57 |
58 | (defun +word-completion-at-point-function (words)
59 | (when-let (bounds (bounds-of-thing-at-point 'word))
60 | (list (car bounds) (cdr bounds) words
61 | :exclusive 'no
62 | :annotation-function (lambda (_) " (Words)"))))
63 |
64 | (defvar +dict--words nil)
65 | (defvar +dict-file "/etc/dictionaries-common/words")
66 |
67 | ;;;###autoload
68 | (defun +dict-completion-at-point-function ()
69 | (+word-completion-at-point-function
70 | (or +dict--words
71 | (setq +dict--words
72 | (split-string (with-temp-buffer
73 | (insert-file-contents-literally +dict-file)
74 | (buffer-string))
75 | "\n")))))
76 |
--------------------------------------------------------------------------------
/modules/completion/corfu/config.el:
--------------------------------------------------------------------------------
1 | ;;; completion/corfu/config.el -*- lexical-binding: t; -*-
2 |
3 | (defvar +corfu-global-capes
4 | '(:completion)
5 | "A list of global capes to be available at all times.
6 | The key :completion is used to specify where completion candidates should be
7 | placed, otherwise they come first.")
8 |
9 | (defvar +corfu-capf-hosts
10 | '(lsp-completion-at-point
11 | eglot-completion-at-point
12 | elisp-completion-at-point
13 | tags-completion-at-point-function)
14 | "A prioritised list of host capfs to create a super cape onto from
15 | `+corfu-global-capes'.")
16 |
17 | (use-package! corfu
18 | :custom
19 | (corfu-auto t)
20 | (corfu-on-exact-match nil)
21 | (corfu-cycle t)
22 | (corfu-auto-prefix 2)
23 | (completion-cycle-threshold 1)
24 | (tab-always-indent 'complete)
25 | (corfu-min-width 50)
26 | :hook
27 | (doom-first-buffer . global-corfu-mode)
28 | :config
29 | (when (modulep! +minibuffer)
30 | (add-hook! 'minibuffer-setup-hook
31 | (defun corfu-move-to-minibuffer ()
32 | "Move current completions to the minibuffer"
33 | (interactive)
34 | (let ((completion-extra-properties corfu--extra)
35 | completion-cycle-threshold completion-cycling)
36 | (apply #'consult-completion-in-region completion-in-region--data)))))
37 |
38 | ;; Dirty hack to get c completion running
39 | ;; Discussion in https://github.com/minad/corfu/issues/34
40 | (when (and (modulep! :lang cc)
41 | (equal tab-always-indent 'complete))
42 | (map! :map c-mode-base-map
43 | :i [remap c-indent-line-or-region] #'completion-at-point))
44 |
45 | ;; Reset lsp-completion provider
46 | (after! lsp-mode
47 | (setq lsp-completion-provider :none))
48 |
49 | (add-hook! '(lsp-mode-hook eglot-mode-hook after-change-major-mode-hook)
50 | (defun +corfu--load-capes ()
51 | "Load all capes specified in `+corfu-global-capes'."
52 | (interactive)
53 | (when-let ((host (cl-intersection +corfu-capf-hosts completion-at-point-functions)))
54 | (setq-local
55 | completion-at-point-functions
56 | (cl-substitute
57 | (apply #'cape-capf-super (cl-substitute (car host) :completion (cl-pushnew :completion +corfu-global-capes)))
58 | (car host)
59 | completion-at-point-functions)))))
60 |
61 | (map! :map corfu-map
62 | "C-SPC" #'corfu-insert-separator
63 | "C-n" #'corfu-next
64 | "C-p" #'corfu-previous
65 | "M-m" #'corfu-move-to-minibuffer
66 | (:prefix "C-x"
67 | "C-k" #'cape-dict
68 | "C-f" #'cape-file))
69 |
70 | (after! evil
71 | (advice-add 'corfu--setup :after 'evil-normalize-keymaps)
72 | (advice-add 'corfu--teardown :after 'evil-normalize-keymaps)
73 | (evil-make-overriding-map corfu-map))
74 |
75 | (defadvice! +corfu--org-return (orig) :around '+org/return
76 | (if (and (modulep! :completion corfu)
77 | corfu-mode
78 | (>= corfu--index 0))
79 | (corfu-insert)
80 | (funcall orig)))
81 |
82 | (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible)
83 | (unless (display-graphic-p)
84 | (corfu-doc-terminal-mode)
85 | (corfu-terminal-mode)))
86 |
87 |
88 | (use-package! orderless
89 | :when (modulep! +orderless)
90 | :hook (lsp-completion-mode . +corfu-lsp-mode-setup)
91 | :init
92 | (defun +corfu-lsp-mode-setup ()
93 | (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
94 | '(orderless)))
95 | (setq completion-styles '(orderless partial-completion flex)
96 | completion-category-overrides '((file (styles . (partial-completion))))))
97 |
98 |
99 | (use-package! kind-icon
100 | :after corfu
101 | :when (modulep! +icons)
102 | :commands (kind-icon-margin-formatter)
103 | :init
104 | (setq kind-icon-default-face 'corfu-default
105 | kind-icon-use-icons nil
106 | kind-icon-mapping
107 | `((array ,(nerd-icons-codicon "nf-cod-symbol_array") :face font-lock-type-face)
108 | (boolean ,(nerd-icons-codicon "nf-cod-symbol_boolean") :face font-lock-builtin-face)
109 | (class ,(nerd-icons-codicon "nf-cod-symbol_class") :face font-lock-type-face)
110 | (color ,(nerd-icons-codicon "nf-cod-symbol_color") :face success)
111 | (command ,(nerd-icons-codicon "nf-cod-terminal") :face default)
112 | (constant ,(nerd-icons-codicon "nf-cod-symbol_constant") :face font-lock-constant-face)
113 | (constructor ,(nerd-icons-codicon "nf-cod-triangle_right") :face font-lock-function-name-face)
114 | (enummember ,(nerd-icons-codicon "nf-cod-symbol_enum_member") :face font-lock-builtin-face)
115 | (enum-member ,(nerd-icons-codicon "nf-cod-symbol_enum_member") :face font-lock-builtin-face)
116 | (enum ,(nerd-icons-codicon "nf-cod-symbol_enum") :face font-lock-builtin-face)
117 | (event ,(nerd-icons-codicon "nf-cod-symbol_event") :face font-lock-warning-face)
118 | (field ,(nerd-icons-codicon "nf-cod-symbol_field") :face font-lock-variable-name-face)
119 | (file ,(nerd-icons-codicon "nf-cod-symbol_file") :face font-lock-string-face)
120 | (folder ,(nerd-icons-codicon "nf-cod-folder") :face font-lock-doc-face)
121 | (interface ,(nerd-icons-codicon "nf-cod-symbol_interface") :face font-lock-type-face)
122 | (keyword ,(nerd-icons-codicon "nf-cod-symbol_keyword") :face font-lock-keyword-face)
123 | (macro ,(nerd-icons-codicon "nf-cod-symbol_misc") :face font-lock-keyword-face)
124 | (magic ,(nerd-icons-codicon "nf-cod-wand") :face font-lock-builtin-face)
125 | (method ,(nerd-icons-codicon "nf-cod-symbol_method") :face font-lock-function-name-face)
126 | (function ,(nerd-icons-codicon "nf-cod-symbol_method") :face font-lock-function-name-face)
127 | (module ,(nerd-icons-codicon "nf-cod-file_submodule") :face font-lock-preprocessor-face)
128 | (numeric ,(nerd-icons-codicon "nf-cod-symbol_numeric") :face font-lock-builtin-face)
129 | (operator ,(nerd-icons-codicon "nf-cod-symbol_operator") :face font-lock-comment-delimiter-face)
130 | (param ,(nerd-icons-codicon "nf-cod-symbol_parameter") :face default)
131 | (property ,(nerd-icons-codicon "nf-cod-symbol_property") :face font-lock-variable-name-face)
132 | (reference ,(nerd-icons-codicon "nf-cod-references") :face font-lock-variable-name-face)
133 | (snippet ,(nerd-icons-codicon "nf-cod-symbol_snippet") :face font-lock-string-face)
134 | (string ,(nerd-icons-codicon "nf-cod-symbol_string") :face font-lock-string-face)
135 | (struct ,(nerd-icons-codicon "nf-cod-symbol_structure") :face font-lock-variable-name-face)
136 | (text ,(nerd-icons-codicon "nf-cod-text_size") :face font-lock-doc-face)
137 | (typeparameter ,(nerd-icons-codicon "nf-cod-list_unordered") :face font-lock-type-face)
138 | (type-parameter ,(nerd-icons-codicon "nf-cod-list_unordered") :face font-lock-type-face)
139 | (unit ,(nerd-icons-codicon "nf-cod-symbol_ruler") :face font-lock-constant-face)
140 | (value ,(nerd-icons-codicon "nf-cod-symbol_field") :face font-lock-builtin-face)
141 | (variable ,(nerd-icons-codicon "nf-cod-symbol_variable") :face font-lock-variable-name-face)
142 | (t ,(nerd-icons-codicon "nf-cod-code") :face font-lock-warning-face)))
143 | (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
144 |
145 |
146 | (use-package! cape
147 | :defer t
148 | :init
149 | (map!
150 | [remap dabbrev-expand] 'cape-dabbrev)
151 |
152 | (add-hook! 'latex-mode-hook
153 | (defun +corfu--latex-set-capfs ()
154 | (make-local-variable '+corfu-global-capes)
155 | (add-to-list '+corfu-global-capes #'cape-tex)))
156 |
157 | (add-to-list '+corfu-global-capes #'cape-file)
158 | (add-to-list '+corfu-global-capes #'cape-dabbrev t))
159 |
160 |
161 | (use-package! corfu-history
162 | :after corfu
163 | :init
164 | (after! savehist
165 | (add-to-list 'savehist-additional-variables 'corfu-history))
166 | :hook (corfu-mode . corfu-history-mode))
167 |
168 |
169 | (use-package! corfu-quick
170 | :after corfu
171 | :bind (:map corfu-map
172 | ("C-q" . corfu-quick-insert)))
173 |
174 |
175 | (use-package! corfu-echo
176 | :after corfu
177 | :hook (corfu-mode . corfu-echo-mode))
178 |
179 |
180 | (use-package! corfu-info
181 | :after corfu)
182 |
183 |
184 | (use-package! corfu-popupinfo
185 | :after corfu
186 | :hook (corfu-mode . corfu-popupinfo-mode))
187 |
188 |
189 | (use-package! yasnippet-capf
190 | :after corfu
191 | :init
192 | (add-to-list '+corfu-global-capes #'yasnippet-capf))
193 |
194 |
195 | (use-package! package-capf
196 | :after corfu
197 | :init
198 | (add-hook! 'emacs-lisp-mode-hook
199 | (defun +corfu--emacs-lisp-set-capfs ()
200 | (make-local-variable '+corfu-global-capes)
201 | (add-to-list '+corfu-global-capes #'package-capf)
202 | (+corfu--load-capes))))
203 |
204 |
205 | (use-package! evil-collection-corfu
206 | :when (modulep! :editor evil +everywhere)
207 | :defer t
208 | :init (setq evil-collection-corfu-key-themes '(default magic-return))
209 | :config
210 | (evil-collection-corfu-setup))
211 |
212 |
213 | (use-package! corfu-prescient
214 | :after corfu
215 | :hook (corfu-mode . corfu-prescient-mode))
216 |
--------------------------------------------------------------------------------
/modules/completion/corfu/packages.el:
--------------------------------------------------------------------------------
1 | ;; -*- no-byte-compile: t; -*-
2 | ;;; completion/corfu/packages.el
3 |
4 | (package! corfu
5 | :pin "24dccafeea114b1aec7118f2a8405b46aa0051e0"
6 | :recipe (:files (:defaults "extensions/*.el")))
7 | (when (modulep! +icons)
8 | (package! kind-icon))
9 | (when (modulep! +orderless)
10 | (package! orderless :pin "b24748093b00b37c3a572c4909f61c08fa27504f"))
11 | (package! corfu-doc
12 | :recipe (:host github :repo "galeo/corfu-doc"))
13 | (package! cape)
14 | (package! yasnippet-capf
15 | :recipe (:host github :repo "elken/yasnippet-capf"))
16 | (package! package-capf
17 | :recipe (:host github :repo "elken/package-capf"))
18 | (package! corfu-prescient)
19 |
20 | (when (modulep! :os tty)
21 | (package! popon
22 | :recipe (:type git :repo "https://codeberg.org/akib/emacs-popon"))
23 | (package! corfu-terminal
24 | :recipe (:type git :repo "https://codeberg.org/akib/emacs-corfu-terminal.git"))
25 | (package! corfu-doc-terminal
26 | :recipe (:type git :repo "https://codeberg.org/akib/emacs-corfu-doc-terminal.git")))
27 |
--------------------------------------------------------------------------------
/modules/lang/sql/autoload/sql.el:
--------------------------------------------------------------------------------
1 | ;;; lang/sql/autoload/sql.el -*- lexical-binding: t; -*-
2 |
3 | ;;;###autoload
4 | (defun +sql-connect (name)
5 | "Wrapper for `sql-connect' which also handles setting buffer
6 | name."
7 | (interactive (list (sql-read-connection "Connection: " nil '(nil))))
8 | (let* ((sql-product (or (cadadr (assoc 'sql-product (cdr (assoc name sql-connection-alist))))
9 | sql-product)))
10 | (sql-connect name name)))
11 |
--------------------------------------------------------------------------------
/modules/lang/sql/config.el:
--------------------------------------------------------------------------------
1 | ;;; lang/sql/config.el -*- lexical-binding: t; -*-
2 |
3 | (defvar +sql/capitalize-keywords nil
4 | "Whether or not to capitalize keywords by default. Possible
5 | options:
6 |
7 | - nil for never
8 | - 'interactive for only during interactive sessions
9 | - 'editor for only during the editor
10 | - t for everywhere")
11 |
12 | (defvar +sql/capitalize-ignore '("name")
13 | "List of keywords to not capitalize, for example 'name' is
14 | commonly used.")
15 |
16 |
17 | (add-hook! sql-mode
18 | (when (modulep! +lsp)
19 | (add-hook 'sql-mode-local-vars-hook #'lsp!))
20 | (unless (file-directory-p (expand-file-name "sql/" doom-cache-dir))
21 | (mkdir (expand-file-name "sql/" doom-cache-dir) t))
22 | (setq-hook! 'sql-interactive-mode-hook
23 | sql-input-ring-file-name (expand-file-name (format "%s-history.sql" (symbol-name (symbol-value 'sql-product))) (expand-file-name "sql/" doom-cache-dir))))
24 |
25 |
26 | (use-package! sqllint
27 | :when (modulep! :checkers syntax)
28 | :unless (modulep! +lsp)
29 | :after sql-mode)
30 |
--------------------------------------------------------------------------------
/modules/lang/sql/doctor.el:
--------------------------------------------------------------------------------
1 | ;;; lang/sql/doctor.el -*- lexical-binding: t; -*-
2 |
3 | (when (modulep! :checkers syntax)
4 | (unless (executable-find "sqlint")
5 | (warn! "Couldn't find sqlint. Syntax checking will not work")))
6 |
7 | (when (modulep! +lsp)
8 | (unless (executable-find "sqls")
9 | (error! "Couldn't find sqls. Needed for LSP")))
10 |
11 | (dolist (app '(("psql" . "PostgreSQL")
12 | ("mysql" . "MySQL")
13 | ("sqlite" . "SQLite")
14 | ("solsql" . "Solid")
15 | ("sqlplus" . "SQL*Plus")
16 | ("dbaccess" . "Informix")
17 | ("isql" . "SyBase or Interbase")
18 | ("sql" . "Ingres")
19 | ("osql" . "MS SQL Server")
20 | ("db2" . "DB2")
21 | ("inl" . "RELEX")))
22 | (unless (executable-find (car app))
23 | (warn! "Couldn't find %s. %s won't work" (car app) (cdr app))))
24 |
--------------------------------------------------------------------------------
/modules/lang/sql/packages.el:
--------------------------------------------------------------------------------
1 | ;; -*- no-byte-compile: t; -*-
2 | ;;; lang/sql/packages.el
3 |
4 | (package! sqlup-mode :pin "3f9df9c88d6a7f9b1ae907e401cad8d3d7d63bbf")
5 | (when (modulep! +mysql)
6 | (package! mysql-to-org-mode :pin "c5eefc71200f2e1d0d67a13ed897b3cdfa835117"))
7 |
--------------------------------------------------------------------------------
/modules/ui/exwm/README.org:
--------------------------------------------------------------------------------
1 | #+TITLE: ui/exwm
2 | #+DATE: February 18, 2021
3 | #+SINCE: v2.0.9
4 | #+STARTUP: inlineimages nofold
5 |
6 | * Table of Contents :TOC_3:noexport:
7 | - [[#description][Description]]
8 | - [[#maintainers][Maintainers]]
9 | - [[#module-flags][Module Flags]]
10 | - [[#plugins][Plugins]]
11 | - [[#prerequisites][Prerequisites]]
12 | - [[#features][Features]]
13 | - [[#configuration][Configuration]]
14 | - [[#background][Background]]
15 | - [[#monitor-layout][Monitor Layout]]
16 | - [[#exwm-startup][EXWM Startup]]
17 | - [[#window-titles][Window Titles]]
18 | - [[#window-behaviour][Window Behaviour]]
19 | - [[#troubleshooting][Troubleshooting]]
20 |
21 | * Description
22 | /Don't be a chump, don't use stump./
23 |
24 | [[file:../../../images/overview.png]]
25 |
26 | EXWM is the best thing since sliced bread. And this config proves that. It's
27 | time to give in and use emacs for everything.
28 |
29 | EXWM (Emacs X Windows Manager) is a package for Emacs that lets us manage X
30 | Windows from the safety of emacs, with everything you'd expect from a tiling
31 | window manager.
32 |
33 | The features include:
34 |
35 | + Fully keyboard-driven operations
36 | + Hybrid layout modes (tiling & stacking)
37 | + Dynamic workspace support
38 | + ICCCM/EWMH compliance
39 | + (Optional) RandR (multi-monitor) support
40 | + (Optional) Builtin system tray
41 | + (Optional) Builtin input method
42 |
43 | This module also includes a number of niceties from other packages, providing a
44 | quite complete desktop experience; such as media key and brightness key support,
45 | a /tray/, a built-in primitive daemon process manager
46 |
47 | ** Maintainers
48 | + [[https://github.com/elken][elken]] (Author)
49 |
50 | ** Module Flags
51 | + =+modeline-panel= Includes various segments to make the modeline function more like a panel
52 | + =+tray= Embed a tray in the minibufer of the current window to show tray icons
53 |
54 | ** Plugins
55 | + [[https://github.com/ch11ng/exwm][exwm]]
56 | + [[https://gitea.petton.fr/DamienCassou/desktop-environment][desktop-environment]]
57 | + [[https://github.com/elken/doom-modeline-now-playing][doom-modeline-now-playing]] (=+modeline-panel=)
58 |
59 | * Prerequisites
60 | Should work out of the box, but a few extra packages are /recommended/
61 |
62 | + =picom=
63 | Fancy transparency and /rounded windows/
64 |
65 | + =flameshot=
66 | Best screenshot tool out there imo, supports area selection and
67 | drawing on the image
68 |
69 | * Features
70 | # An in-depth list of features, how to use them, and their dependencies.
71 |
72 | * Configuration
73 | This (surprisingly) is tweaked for my preference, but that doesn't mean it's
74 | untouchable! Change this as needed, below are the more obvious areas I can think
75 | of
76 |
77 | ** Background
78 | Replace the =images/background.png= in the root doom directory
79 |
80 | ** Monitor Layout
81 | Replace the =xrandr= shell command line with the configuration of your monitors
82 |
83 | ** EXWM Startup
84 | Modify the =elken/exwm-init-hook= function to add any startup processes
85 |
86 | ** Window Titles
87 | =elken/exwm-update-title= is the (trivial) function for controlling window titles
88 |
89 | ** Window Behaviour
90 | =elken/configure-window-by-class= is the function that controls the default
91 | behaviour for applications.
92 |
93 | *NOTE*
94 | Spotify doesn't work out of the box and requires [[https://github.com/dasJ/spotifywm][=spotifywm=]] to be installed separately.
95 |
96 | * Troubleshooting
97 | Running in debug mode (=--debug-init=) will also enable =exwm-debug= and make the
98 | log more active. If this doesn't help, check the troubleshooting steps on the [[https://github.com/ch11ng/exwm/wiki#how-to-report-a-bug][exwm wiki]].
99 |
--------------------------------------------------------------------------------
/modules/ui/exwm/cli.el:
--------------------------------------------------------------------------------
1 | ;;; ui/exwm/cli.el -*- lexical-binding: t; -*-
2 |
3 | (defun desktop-template (&optional debug-p)
4 | (let ((script (if debug-p "start-debug.sh"
5 | "start.sh"))
6 | (title (if debug-p "(Debug)"
7 | "")))
8 | (format "[Desktop Entry]
9 | Name=Doom EXWM %s
10 | Comment=Doom-flavoured Emacs Window Manager
11 | Exec=%s
12 | TryExec=sh
13 | Type=Application
14 | DesktopNames=exwm
15 | " title (doom-dir doom-private-dir "exwm" script))))
16 |
17 | (defun script-template (&optional debug-p)
18 | (format "#!/usr/bin/env bash
19 | export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
20 | export _JAVA_AWT_WM_NOREPARENTING=1
21 | wmname LG3D
22 | emacs -mm --with-exwm %s"
23 | (if debug-p
24 | "--debug-init"
25 | "")))
26 |
27 | (defcligroup! "EXWM"
28 | "For managing EXWM"
29 | (defcli! exwm-install ()
30 | "Install relevant files for EXWM"
31 | (let ((tramp-verbose-old 'tramp-verbose)
32 | (desktop (desktop-template))
33 | (desktop-debug (desktop-template t))
34 | (start-script (script-template))
35 | (start-debug-script (script-template t)))
36 | (setq tramp-verbose 0)
37 |
38 | (print! (start "Login manager setup"))
39 | (print-group!
40 | (unless (file-readable-p "/usr/share/xsessions/doom-exwm.desktop")
41 | (print! "Creating primary EXWM session")
42 | (with-current-buffer (find-file-noselect (doom--sudo-file-path "/usr/share/xsessions/doom-exwm.desktop"))
43 | (goto-char (point-max))
44 | (insert desktop)
45 | (save-buffer)))
46 | (print! (success "Primary session setup!"))
47 |
48 | (unless (file-readable-p "/usr/share/xsessions/doom-exwm-debug.desktop")
49 | (print! "Creating debug EXWM session")
50 | (with-current-buffer (find-file-noselect (doom--sudo-file-path "/usr/share/xsessions/doom-exwm-debug.desktop"))
51 | (goto-char (point-max))
52 | (insert desktop-debug)
53 | (save-buffer)))
54 | (print! (success "Debug session setup!")))
55 | (setq tramp-verbose tramp-verbose-old))
56 |
57 |
58 | (print! (start "Local script setup"))
59 | (when (not (file-directory-p (doom-dir doom-private-dir "exwm")))
60 | (print! "Creating %s" (doom-dir "exwm")))
61 | (make-directory (doom-dir doom-private-dir "exwm") 'parents)
62 | (mapc (lambda (file)
63 | (let ((filename (car file))
64 | (body (cdr file)))
65 | (if (file-exists-p! filename (doom-dir doom-private-dir "exwm"))
66 | (print! (warn "%s already exists, skipping") filename))))
67 | `(("start.sh" . ,(script-template))
68 | ("start-debug.sh" . ,(script-template t))))))
69 |
--------------------------------------------------------------------------------
/modules/ui/exwm/config.el:
--------------------------------------------------------------------------------
1 | ;;; ui/exwm/config.el -*- lexical-binding: t; -*-
2 |
3 | ;; Make the launcher only show app names
4 | (use-package! counsel
5 | :custom
6 | (counsel-linux-app-format-function #'counsel-linux-app-format-function-name-only))
7 |
8 | (defun elken/playerctl-format (function format)
9 | "Invoke playerctl for FUNCTION using FORMAT to present output"
10 | (string-trim (shell-command-to-string (format "playerctl %s --format '%s'" function format))))
11 |
12 | (defun elken/exwm-get-index (index)
13 | "Get the correct index from the passed index"
14 | (- index 1))
15 |
16 | (defun elken/run-application (command)
17 | "Run the specified command as an application"
18 | (call-process "gtk-launch" nil 0 nil command))
19 |
20 | (defun elken/exwm-update-class ()
21 | "Update the buffer to be the name of the window"
22 | (exwm-workspace-rename-buffer exwm-class-name))
23 |
24 | (defun elken/run-in-background (command &optional args)
25 | "Run the specified command as a daemon"
26 | (elken/kill-process--action (assoc command elken/process-alist))
27 | (setq elken/process-alist
28 | (cons `(,command . ,(start-process-shell-command command nil (format "%s %s" command (or args "")))) elken/process-alist)))
29 |
30 | (defun elken/reload-tray ()
31 | "Restart the systemtray"
32 | (interactive)
33 | (exwm-systemtray--exit)
34 | (exwm-systemtray--init))
35 |
36 | (defun elken/set-wallpaper (file)
37 | "Set the DE wallpaper to be $FILE"
38 | (interactive)
39 | (start-process-shell-command "feh" nil (format "feh --bg-scale %s" file)))
40 |
41 | (defun elken/exwm-update-title ()
42 | "Update the window title when needed"
43 | (pcase (downcase (or exwm-class-name ""))
44 | ("spotify" (exwm-workspace-rename-buffer (format "Spotify: %s" (elken/playerctl-format "--player=spotify metadata" "{{ artist }} - {{ title }}"))))
45 | (_ (exwm-workspace-rename-buffer (format "%s" exwm-title)))))
46 |
47 | (defun elken/configure-window-by-class()
48 | "Configuration for windows (grouped by WM_CLASS)"
49 | (interactive)
50 | (pcase (downcase (or exwm-class-name ""))
51 | ("mpv" (progn
52 | (exwm-floating-toggle-floating)
53 | (exwm-layout-toggle-mode-line)))
54 | ("discord" (progn
55 | (exwm-workspace-move-window (elken/exwm-get-index 3))
56 | (elken/reload-tray)))
57 | ("megasync" (progn
58 | (exwm-floating-toggle-floating)
59 | (exwm-layout-toggle-mode-line)))
60 | ("spotify" (exwm-workspace-move-window (elken/exwm-get-index 4)))
61 | ("firefox" (exwm-workspace-move-window (elken/exwm-get-index 2)))))
62 |
63 | (defun elken/exwm-init-hook ()
64 | "Various init processes for exwm"
65 | ;; Daemon applications
66 | (elken/run-in-background "pasystray")
67 | (elken/run-in-background "megasync")
68 | (elken/run-in-background "nm-applet")
69 |
70 | ;; Startup applications
71 | (elken/run-application "spotify")
72 | (elken/run-application "discord")
73 | (elken/run-application "firefox")
74 |
75 | ;; Default emacs behaviours
76 | ;; TODO Take this out of emacs
77 | (mu4e t))
78 |
79 | (defvar elken/process-alist '())
80 |
81 | (defun elken/kill-process--action (process)
82 | "Do the actual process killing"
83 | (when process
84 | (ignore-errors
85 | (kill-process (cdr process))))
86 | (setq elken/process-alist (remove process elken/process-alist)))
87 |
88 | (defun elken/kill-process ()
89 | "Kill a background process"
90 | (interactive)
91 | (ivy-read "Kill process: " elken/process-alist
92 | :action #'elken/kill-process--action
93 | :caller 'elken/kill-process))
94 |
95 | (after! (exwm doom-modeline)
96 | (setq all-the-icons-scale-factor 1.1)
97 | (use-package! doom-modeline-exwm)
98 | (use-package! doom-modeline-now-playing
99 | :config
100 | (doom-modeline-now-playing-timer))
101 | (doom-modeline-def-segment exwm-buffer-info
102 | (concat
103 | (let ((face (if (doom-modeline--active)
104 | 'doom-modeline-buffer-file
105 | 'mode-line-inactive)))
106 | (doom-modeline-icon 'octicon "browser" "" ""
107 | :face face :v-adjust -0.05))
108 | (doom-modeline-spc)
109 | (doom-modeline--buffer-name)))
110 | (setf (alist-get 'exwm-mode all-the-icons-mode-icon-alist)
111 | '(all-the-icons-octicon "browser" :v-adjust -0.05))
112 | (doom-modeline-def-modeline 'exwm
113 | '(bar workspace-name exwm-workspaces debug exwm-buffer-info)
114 | '(now-playing objed-state misc-info persp-name grip mu4e gnus github repl lsp major-mode process " "))
115 | (defun doom-modeline-set-exwm-modeline ()
116 | "Set exwm mode-line"
117 | (doom-modeline-set-modeline 'exwm))
118 | (add-hook 'exwm-mode-hook #'doom-modeline-set-exwm-modeline)
119 | (doom-modeline-def-modeline 'main
120 | '(bar workspace-name exwm-workspaces debug modals matches buffer-info remote-host parrot selection-info)
121 | '(now-playing objed-state misc-info persp-name grip mu4e gnus github repl lsp major-mode process vcs checker " ")))
122 |
123 | ;; Used to handle screen locking (currently unused), media keys and screenshotting
124 | (use-package! desktop-environment
125 | :after exwm
126 | :config
127 | (setq desktop-environment-screenshot-command "flameshot gui")
128 | (desktop-environment-mode))
129 |
130 | ;; The meat and potatoes as they say
131 | (use-package! exwm
132 | :commands (exwm-enable)
133 | :config
134 | ;; Enabled debugging when doom is in debug mode
135 | (when doom-debug-p
136 | (advice-add #'exwm-input--on-buffer-list-update :before
137 | (lambda (&rest r)
138 | (exwm--log "CALL STACK: %s" (cddr (reverse (xcb-debug:-call-stack))))))
139 | (exwm-debug))
140 |
141 | ;; Show all buffers for switching
142 | (setq exwm-workspace-show-all-buffers t)
143 |
144 | ;; Set a sane number of default workspaces
145 | (setq exwm-workspace-number 5)
146 |
147 | ;; Define workspace setup for monitors
148 | (setq exwm-randr-workspace-monitor-plist `(,(elken/exwm-get-index 2) "DP-0" ,(elken/exwm-get-index 3) "DP-0"))
149 |
150 | (setq exwm-workspace-index-map
151 | (lambda (index) (number-to-string (+ 1 index))))
152 |
153 | ;; Set the buffer to the name of the window class
154 | (add-hook 'exwm-update-class-hook #'elken/exwm-update-class)
155 |
156 | ;; Init hook
157 | (add-hook 'exwm-init-hook #'elken/exwm-init-hook)
158 |
159 | ;; Update window title
160 | (add-hook 'exwm-update-title-hook #'elken/exwm-update-title)
161 |
162 | ;; Configure windows as created
163 | (add-hook 'exwm-manage-finish-hook #'elken/configure-window-by-class)
164 |
165 | ;; /Always/ pass these to emacs
166 | (setq exwm-input-prefix-keys
167 | '(?\C-x
168 | ?\C-u
169 | ?\C-h
170 | ?\M-x
171 | ?\C-w
172 | ?\M-`
173 | ?\M-&
174 | ?\M-:
175 | ?\M-\
176 | ?\C-g
177 | ?\C-\M-j
178 | ?\C-\ ))
179 |
180 | ;; Shortcut to passthrough next keys
181 | (map! :map exwm-mode-map [?\C-q] 'exwm-input-send-next-key)
182 |
183 | ;; Setup screen layout
184 | (require 'exwm-randr)
185 | (exwm-randr-enable)
186 | (start-process-shell-command "xrandr" nil "xrandr --output HDMI-0 --primary --mode 2560x1440 --pos 0x1080 --output DP-0 --mode 2560x1080 --pos 0x0")
187 |
188 | ;; Set the wallpaper
189 | (elken/set-wallpaper (expand-file-name "images/background.png" doom-private-dir))
190 |
191 | ;; Setup tray
192 | (require 'exwm-systemtray)
193 | (setq exwm-systemtray-height 16)
194 | (exwm-systemtray-enable)
195 |
196 | ;; Date/time
197 | (setq display-time-format " [ %H:%M %d/%m/%y]")
198 | (setq display-time-default-load-average nil)
199 | (display-time-mode 1)
200 |
201 | (exwm-input-set-key (kbd "") '+eshell/toggle)
202 |
203 | (setq exwm-input-global-keys
204 | '(
205 | ([?\s- ] . counsel-linux-app)
206 | ([?\s-r] . exwm-reset)
207 | ([s-left] . windmove-left)
208 | ([s-right] . windmove-right)
209 | ([s-up] . windmove-up)
210 | ([s-down] . windmove-down)
211 |
212 | ([?\s-&] . (lambda (command) (interactive (list (read-shell-command "[command] $ ")))
213 | (start-process-shell-command command nil command)))
214 |
215 | ([?\s-e] . (lambda () (interactive) (dired "~")))
216 |
217 | ([?\s-w] . exwm-workspace-switch)
218 |
219 | ([?\s-Q] . (lambda () (interactive) (kill-buffer)))
220 | ([?\s-1] . (lambda ()
221 | (interactive)
222 | (exwm-workspace-switch-create (elken/exwm-get-index 1))))
223 | ([?\s-2] . (lambda ()
224 | (interactive)
225 | (exwm-workspace-switch-create (elken/exwm-get-index 2))))
226 | ([?\s-3] . (lambda ()
227 | (interactive)
228 | (exwm-workspace-switch-create (elken/exwm-get-index 3))))
229 | ([?\s-4] . (lambda ()
230 | (interactive)
231 | (exwm-workspace-switch-create (elken/exwm-get-index 4))))
232 | ([?\s-5] . (lambda ()
233 | (interactive)
234 | (exwm-workspace-switch-create (elken/exwm-get-index 5))))
235 | ([?\s-6] . (lambda ()
236 | (interactive)
237 | (exwm-workspace-switch-create (elken/exwm-get-index 6))))
238 | ([?\s-7] . (lambda ()
239 | (interactive)
240 | (exwm-workspace-switch-create (elken/exwm-get-index 7))))
241 | ([?\s-8] . (lambda ()
242 | (interactive)
243 | (exwm-workspace-switch-create (elken/exwm-get-index 8))))
244 | ([?\s-9] . (lambda ()
245 | (interactive)
246 | (exwm-workspace-switch-create (elken/exwm-get-index 9)))))))
247 |
--------------------------------------------------------------------------------
/modules/ui/exwm/doctor.el:
--------------------------------------------------------------------------------
1 | ;;; ui/exwm/doctor.el -*- lexical-binding: t; -*-
2 |
3 | (dolist (app '(("flameshot" . "screenshots")
4 | ("playerctl" . "song information")
5 | ("wmname" . "some apps")
6 | ("picom" . "transparency")))
7 | (when (not (executable-find (car app)))
8 | (warn! (format "%s is missing, %s won't work" (car app) (cdr app)))))
9 |
--------------------------------------------------------------------------------
/modules/ui/exwm/init.el:
--------------------------------------------------------------------------------
1 | ;;; ui/exwm/init.el -*- lexical-binding: t; -*-
2 |
3 |
4 | (defconst IS-EXWM (not (null (member "---with-exwm" command-line-args))))
5 | (add-to-list 'command-switch-alist '("--with-exwm" . (lambda (_) (pop command-line-args-left))))
6 |
7 | (when (and doom-interactive-p
8 | (not doom-reloading-p)
9 | IS-EXWM)
10 | (require 'exwm)
11 | (exwm-enable))
12 |
--------------------------------------------------------------------------------
/modules/ui/exwm/packages.el:
--------------------------------------------------------------------------------
1 | ;; -*- no-byte-compile: t; -*-
2 | ;;; ui/exwm/packages.el
3 |
4 | (package! exwm)
5 | (package! desktop-environment)
6 | (package! doom-modeline-now-playing)
7 | (package! doom-modeline-exwm
8 | :recipe (:local-repo "~/build/elisp/doom-modeline-exwm"
9 | :build (:not compile)))
10 |
--------------------------------------------------------------------------------
/modules/ui/perspective/README.org:
--------------------------------------------------------------------------------
1 | #+title: Readme
2 | #+author: Ellis Kenyő
3 | #+date: \today
4 | #+latex_class: chameleon
5 |
6 | Simple setup for [[https://github.com/nex3/perspective-el][perspective.el]] copied from my private vanilla config. Seems to
7 | be much more stable and buggy than persp-mode without any of the custom advice,
8 | so I'm sticking with it for now.
9 |
10 | If [[https://debbugs.gnu.org/cgi/bugreport.cgi?bug=65719][this patch]] ever gets merged, that'll also give this a big win.
11 |
--------------------------------------------------------------------------------
/modules/ui/perspective/autoload/session.el:
--------------------------------------------------------------------------------
1 | ;;; ui/perspective/autoload/session.el -*- lexical-binding: t; -*-
2 |
3 | (defvar desktop-base-file-name)
4 | (defvar desktop-dirname)
5 | (defvar desktop-restore-eager)
6 | (defvar desktop-file-modtime)
7 |
8 | ;;;###autoload
9 | (defun doom-session-file (&optional name)
10 | "TODO"
11 | (cond ((require 'persp-mode nil t)
12 | (expand-file-name (or name persp-auto-save-fname) persp-save-dir))
13 | ((require 'perspective nil t)
14 | (or name persp-state-default-file))
15 | ((require 'desktop nil t)
16 | (if name
17 | (expand-file-name name (file-name-directory (desktop-full-file-name)))
18 | (desktop-full-file-name)))
19 | ((error "No session backend available"))))
20 |
21 | ;;;###autoload
22 | (defun doom-save-session (&optional file)
23 | "TODO"
24 | (setq file (expand-file-name (or file (doom-session-file))))
25 | (cond ((require 'persp-mode nil t)
26 | (unless persp-mode (persp-mode +1))
27 | (setq persp-auto-save-opt 0)
28 | (persp-save-state-to-file file))
29 | ((require 'perspective nil t)
30 | (unless persp-mode (persp-mode +1))
31 | (let ((persp-state-default-file (or file persp-state-default-file)))
32 | (persp-save)))
33 | ((and (require 'frameset nil t)
34 | (require 'restart-emacs nil t))
35 | (let ((frameset-filter-alist (append '((client . restart-emacs--record-tty-file))
36 | frameset-filter-alist))
37 | (desktop-base-file-name (file-name-nondirectory file))
38 | (desktop-dirname (file-name-directory file))
39 | (desktop-restore-eager t)
40 | desktop-file-modtime)
41 | (make-directory desktop-dirname t)
42 | ;; Prevents confirmation prompts
43 | (let ((desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))))
44 | (desktop-save desktop-dirname t))))
45 | ((error "No session backend to save session with"))))
46 |
47 | ;;;###autoload
48 | (defun doom-load-session (&optional file)
49 | "TODO"
50 | (setq file (expand-file-name (or file (doom-session-file))))
51 | (message "Attempting to load %s" file)
52 | (cond ((not (file-readable-p file))
53 | (message "No session file at %S to read from" file))
54 | ((require 'persp-mode nil t)
55 | (unless persp-mode
56 | (persp-mode +1))
57 | (let ((allowed (persp-list-persp-names-in-file file)))
58 | (cl-loop for name being the hash-keys of *persp-hash*
59 | unless (member name allowed)
60 | do (persp-kill name))
61 | (persp-load-state-from-file file)))
62 | ((require 'perspective nil t)
63 | (unless persp-mode (persp-mode +1))
64 | (persp-state-load file))
65 | ((and (require 'frameset nil t)
66 | (require 'restart-emacs nil t))
67 | (restart-emacs--restore-frames-using-desktop file))
68 | ((error "No session backend to load session with"))))
69 |
--------------------------------------------------------------------------------
/modules/ui/perspective/config.el:
--------------------------------------------------------------------------------
1 | ;;; ui/perspective/config.el -*- lexical-binding: t; -*-
2 |
3 | (use-package! perspective
4 | :init (persp-mode)
5 | :hook (kill-emacs . persp-state-save)
6 | :custom
7 | (persp-sort 'created)
8 | (persp-suppress-no-prefix-key-warning t)
9 | (persp-show-modestring nil)
10 | (persp-state-default-file (expand-file-name "perspectives" doom-cache-dir))
11 | :config
12 | (defun +doom-dashboard--persp-detect-project-h ())
13 |
14 | (when (modulep! :completion vertico)
15 | (after! consult
16 | (consult-customize consult--source-buffer :hidden t :default nil)
17 | (add-to-list 'consult-buffer-sources persp-consult-source)))
18 |
19 | (defun persp-generate-id ()
20 | (format "#%d"
21 | (cl-loop for name in (persp-names)
22 | when (string-match-p "^#[0-9]+$" name)
23 | maximize (string-to-number (substring name 1)) into max
24 | finally return (if max (1+ max) 1))))
25 |
26 | (defun persp-names (&optional asc)
27 | "Return a list of the names of all perspectives on the `selected frame'.
28 |
29 | Optionall sort ASCending."
30 | (let ((persps (hash-table-values (perspectives-hash))))
31 | (cond ((eq persp-sort 'name)
32 | (sort (mapcar 'persp-name persps) (if asc #'string> #'string<)))
33 | ((eq persp-sort 'access)
34 | (mapcar 'persp-name
35 | (sort persps (lambda (a b)
36 | (time-less-p (persp-last-switch-time (if asc b a))
37 | (persp-last-switch-time (if asc a b)))))))
38 | ((eq persp-sort 'created)
39 | (mapcar 'persp-name
40 | (sort persps (lambda (a b)
41 | (time-less-p (persp-created-time (if asc b a))
42 | (persp-created-time (if asc a b))))))))))
43 |
44 | (map!
45 | :leader
46 | :desc "Switch buffer" "," #'persp-switch-to-buffer*
47 | (:prefix-map ("TAB" . "Workspaces")
48 | :desc "Switch to last workspace" "`" (cmd! (persp-last))
49 | :desc "New workspace" "n" (cmd! (if current-prefix-arg
50 | (persp-switch nil)
51 | (persp-switch (persp-generate-id))))
52 | :desc "Load workspace from file" "l" #'persp-state-load
53 | :desc "Save workspace to file" "s" #'persp-state-save
54 | :desc "Delete this workspace" "d" (cmd! (persp-kill (persp-current-name)))
55 | :desc "Rename workspace" "r" #'persp-rename
56 | :desc "Next workspace" "]" #'persp-next
57 | :desc "Previous workspace" "[" #'persp-prev
58 | :desc "Switch to 1st workspace" "1" (cmd! (persp-switch-by-number 1))
59 | :desc "Switch to 2nd workspace" "2" (cmd! (persp-switch-by-number 2))
60 | :desc "Switch to 3rd workspace" "3" (cmd! (persp-switch-by-number 3))
61 | :desc "Switch to 4th workspace" "4" (cmd! (persp-switch-by-number 4))
62 | :desc "Switch to 5th workspace" "5" (cmd! (persp-switch-by-number 5))
63 | :desc "Switch to 6th workspace" "6" (cmd! (persp-switch-by-number 6))
64 | :desc "Switch to 7th workspace" "7" (cmd! (persp-switch-by-number 7))
65 | :desc "Switch to 8th workspace" "8" (cmd! (persp-switch-by-number 8))
66 | :desc "Switch to 9th workspace" "9" (cmd! (persp-switch-by-number 9))
67 | :desc "Switch to 10th workspace" "0" (cmd! (persp-switch-by-number 10))))
68 |
69 | ;; Inspired by
70 | (defadvice project-switch-project (around project-persp-switch-project (project) activate)
71 | "Switch to perspective for project."
72 | (interactive (list (project-prompt-project-dir)))
73 | (let* ((name (project-name (project-current nil project)))
74 | (persp (gethash name (perspectives-hash)))
75 | (command (if (symbolp project-switch-commands)
76 | project-switch-commands
77 | (project--switch-project-command)))
78 | (project-current-directory-override project))
79 | (persp-switch name)
80 | (unless (equal persp (persp-curr))
81 | (call-interactively command))))
82 |
83 | (defadvice persp-init-frame (after project-persp-init-frame activate)
84 | "Rename initial perspective to the project name when a new frame
85 | is created in a known project."
86 | (with-selected-frame frame
87 | (when (projectile-project-p)
88 | (persp-rename (project-name (project-current)))))))
89 |
--------------------------------------------------------------------------------
/modules/ui/perspective/packages.el:
--------------------------------------------------------------------------------
1 | ;; -*- no-byte-compile: t; -*-
2 | ;;; ui/perspective/packages.el
3 |
4 | (package! perspective)
5 |
--------------------------------------------------------------------------------
/modules/ui/tab-bar/README.org:
--------------------------------------------------------------------------------
1 | #+title: Readme
2 | #+author: Ellis Kenyő
3 | #+date: \today
4 | #+latex_class: chameleon
5 |
6 | A simple module to customize tab-bar-mode. Currently just shows workspaces from [[file:~/.config/doom/modules/ui/perspective/][perspectives]].
7 |
--------------------------------------------------------------------------------
/modules/ui/tab-bar/config.el:
--------------------------------------------------------------------------------
1 | ;;; ui/tab-bar/config.el -*- lexical-binding: t; -*-
2 |
3 | (declare-function "persp-names" 'perspective)
4 |
5 | (defgroup lkn-tab-bar nil
6 | "Options related to my custom tab-bar."
7 | :group 'tab-bar)
8 |
9 | (defgroup lkn-tab-bar-faces nil
10 | "Faces for the tab-bar."
11 | :group 'lkn-tab-bar)
12 |
13 | (defface lkn-tab-bar-workspace-tab
14 | `((t :inherit default :family ,(face-attribute 'default :family)))
15 | "Face for a workspace tab."
16 | :group 'lkn-tab-bar-faces)
17 |
18 | (defface lkn-tab-bar-selected-workspace-tab
19 | '((t :inherit (highlight lkn-tab-bar-workspace-tab)))
20 | "Face for a selected workspace tab."
21 | :group 'lkn-tab-bar-faces)
22 |
23 | (defun lkn-tab-bar--workspaces ()
24 | "Return a list of the current workspaces."
25 | (nreverse
26 | (let ((show-help-function nil)
27 | (persps (persp-names))
28 | (persp (persp-current-name)))
29 | (when (< 1 (length persps))
30 | (seq-reduce
31 | (lambda (acc elm)
32 | (let* ((face (if (equal persp elm)
33 | 'lkn-tab-bar-selected-workspace-tab
34 | 'lkn-tab-bar-workspace-tab))
35 | (pos (1+ (cl-position elm persps)))
36 | (edge-x (get-text-property 0 'edge-x (car acc)))
37 | (tab-id (format " %d" pos))
38 | (tab-name (format " %s " elm)))
39 | (push
40 | (concat
41 | (propertize tab-id
42 | 'id pos
43 | 'name elm
44 | 'edge-x (+ edge-x (string-pixel-width tab-name) (string-pixel-width tab-id))
45 | 'face
46 | `(:inherit ,face
47 | :weight bold))
48 | (propertize tab-name 'face `,face))
49 | acc)
50 | acc))
51 | persps
52 | `(,(propertize (persp-current-name) 'edge-x 0 'invisible t)))))))
53 |
54 | (customize-set-variable 'global-mode-string '((:eval (lkn-tab-bar--workspaces)) " "))
55 | (customize-set-variable 'tab-bar-format '(tab-bar-format-global))
56 | (customize-set-variable 'tab-bar-mode t)
57 |
58 | ;; These two things combined prevents the tab list to be printed either as a
59 | ;; tooltip or in the echo area
60 | (defun tooltip-help-tips (_event)
61 | "Hook function to display a help tooltip.
62 | This is installed on the hook `tooltip-functions', which
63 | is run when the timer with id `tooltip-timeout-id' fires.
64 | Value is non-nil if this function handled the tip."
65 | (let ((xf (lambda (str) (string-trim (substring-no-properties str)))))
66 | (when (and
67 | (stringp tooltip-help-message)
68 | (not (string= (funcall xf tooltip-help-message) (funcall xf (format-mode-line (lkn-tab-bar--workspaces))))))
69 | (tooltip-show tooltip-help-message (not tooltip-mode))
70 | t)))
71 |
72 | (tooltip-mode)
73 |
74 | (defun lkn-tab-bar--event-to-item (event)
75 | "Given a click EVENT, translate to a tab.
76 |
77 | We handle this by using `string-pixel-width' to calculate how
78 | long the tab would be in pixels and use that in the reduction in
79 | `lkn-tab-bar--workspaces' to determine which tab has been
80 | clicked."
81 | (let* ((posn (event-start event))
82 | (workspaces (lkn-tab-bar--workspaces))
83 | (x (car (posn-x-y posn))))
84 | (car (cl-remove-if (lambda (workspace)
85 | (>= x (get-text-property 0 'edge-x workspace)))
86 | workspaces))))
87 |
88 | (defun lkn-tab-bar-mouse-1 (ws)
89 | "Switch to tabs by left clicking."
90 | (when-let ((id (get-text-property 0 'id ws)))
91 | (persp-switch-by-number id)))
92 |
93 | (defun lkn-tab-bar-mouse-2 (ws)
94 | "Close tabs by clicking the mouse wheel."
95 | (when-let ((name (get-text-property 0 'name ws)))
96 | (persp-kill name)))
97 |
98 | (defun lkn-tab-bar-click-handler (evt)
99 | "Function to handle clicks on the custom tab."
100 | (interactive "e")
101 | (when-let ((ws (lkn-tab-bar--event-to-item evt)))
102 | (pcase (car evt)
103 | ('mouse-1 (lkn-tab-bar-mouse-1 ws))
104 | ('mouse-2 (lkn-tab-bar-mouse-2 ws)))))
105 |
106 | (keymap-set tab-bar-map "" #'lkn-tab-bar-click-handler)
107 | (keymap-set tab-bar-map "" #'lkn-tab-bar-click-handler)
108 | (keymap-set tab-bar-map "" #'persp-prev)
109 | (keymap-set tab-bar-map "" #'persp-next)
110 |
111 | (provide 'lkn-tab-bar)
112 |
--------------------------------------------------------------------------------
/org:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | function build() {
4 | doom publish
5 | }
6 |
7 | function dev() {
8 | mkdir -p out
9 |
10 | npx live-server out &
11 | build
12 |
13 | while true; do
14 | changes=()
15 | while read -r -t 1 change; do
16 | changes+=("$change")
17 | done
18 |
19 | if [[ "${#changes[@]}" -ne 0 ]]; then
20 | for change in "${changes[@]}"; do
21 | echo $change
22 | done
23 | build &
24 | fi
25 | done < <(inotifywait --monitor \
26 | --recursive \
27 | --event modify \
28 | --event move \
29 | --event create \
30 | --event delete \
31 | --event attrib \
32 | --exclude "(/\..+)|(out)|(flycheck*)" \
33 | --quiet \
34 | .)
35 | }
36 |
37 | function main() {
38 | trap 'exit' TERM INT
39 | trap 'kill 0' EXIT
40 |
41 | command="$1"
42 | case $command in
43 | "" | "-h" | "--help")
44 | echo "Usage: org build|dev"
45 | echo " build: performs a one time build"
46 | echo " dev: builds, starts a file server, and watches file changes to trigger rebuilds"
47 | ;;
48 | build)
49 | echo "=> Starting a one off build..."
50 | build
51 | ;;
52 | dev)
53 | echo "=> Starting development..."
54 | dev
55 | ;;
56 | *)
57 | echo "Error: '$command' is not a supported command." >&2
58 | echo " Run '$0 --help' for a list of commands ." >&2
59 | exit 1
60 | ;;
61 | esac
62 | }
63 |
64 | main "$@"
65 |
--------------------------------------------------------------------------------
/static/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --sidebar-width: 25em;
3 | --body-width: 70vw;
4 | }
5 |
6 | html {
7 | overflow-x: hidden;
8 | scroll-behavior: smooth;
9 | }
10 |
11 | body {
12 | width: var(--body-width);
13 | overflow: auto;
14 | padding-left: var(--sidebar-width);
15 | padding-right: 1em;
16 | margin: 0 auto;
17 | line-height: 1.6;
18 | font-size: 1rem;
19 | }
20 |
21 | .content {
22 | padding: 0 2em 30em;
23 | }
24 |
25 | #table-of-contents {
26 | position: fixed;
27 | top: 0;
28 | left: 0;
29 | width: var(--sidebar-width);
30 | height: 100vh;
31 | overflow: auto;
32 | padding: 10px;
33 | }
34 |
35 | img {
36 | width: 100%;
37 | max-width: 40vw;
38 | height: 100%;
39 | object-fit: cover;
40 | }
41 |
42 | nav h2 {
43 | margin: 0;
44 | }
45 |
46 | nav ul {
47 | list-style: none;
48 | padding: 5px 10px;
49 | }
50 |
51 | nav a.active,
52 | nav li.active > a {
53 | background: #3b4252;
54 | padding: 5px;
55 | box-shadow: 3px 3px 11px -3px rgba(61,69,85,0.75);
56 | }
57 |
58 | nav li {
59 | margin-bottom: 10px;
60 | }
61 |
62 | nav li a {
63 | text-decoration: none;
64 | }
65 |
66 | nav a:hover {
67 | padding: 5px;
68 | }
69 |
--------------------------------------------------------------------------------
/static/index.js:
--------------------------------------------------------------------------------
1 | const getParentsUntil = (el, selector) => {
2 | let parents = [], _el = el.parentNode;
3 | while (_el && typeof _el.matches === 'function') {
4 | parents.unshift(_el);
5 | if (_el.matches(selector)) {
6 | return parents;
7 | } else {
8 | _el = _el.parentNode;
9 | }
10 | }
11 | return [];
12 | };
13 |
14 | function canSeeElement(element) {
15 | const rect = element.getBoundingClientRect();
16 | const top = rect.top;
17 | const bottom = rect.bottom;
18 | const height = window.innerHeight;
19 |
20 | return height - top > 0 && bottom > 0;
21 | }
22 |
23 | function setActive() {
24 | const sections = document.querySelectorAll("div[class^=org-src-container]");
25 | document.querySelectorAll("nav .active").forEach(activeElem => activeElem.classList.remove("active"));
26 |
27 | sections.forEach(current => {
28 | const sectionId = current.parentNode.getAttribute("id");
29 | const navId = sectionId.split("-").reverse()[0];
30 | const elem = document.querySelector("nav a[href*=" + navId + "]");
31 |
32 | if (navId.startsWith("org") && elem) {
33 | const parents = [...getParentsUntil(elem, 'div').slice(2), elem];
34 |
35 | if (canSeeElement(current)) {
36 | parents.forEach((activeElem) => activeElem.classList.add("active"));
37 | if (!canSeeElement(elem)) {
38 | elem.scrollIntoView({
39 | behavior: 'smooth',
40 | block: 'center',
41 | inline: 'center'
42 | });
43 | }
44 | }
45 | }
46 | });
47 | }
48 |
49 | window.onload = function () {
50 | document.addEventListener("scroll", setActive);
51 |
52 | setActive()
53 | }
54 |
--------------------------------------------------------------------------------
/templates/roam-default.org:
--------------------------------------------------------------------------------
1 | #+title: ${title}
2 | #+author: Ellis Kenyő
3 | #+created: %U
4 | #+last_modified: <2022-01-13 16:16>
5 | #+latex_class: chameleon
6 |
7 | %?
8 |
--------------------------------------------------------------------------------