├── .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 | "?"))) 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 | --------------------------------------------------------------------------------