From c51d2e9354a8fe84a589af6c4d617e6f8457928d Mon Sep 17 00:00:00 2001 From: starr-dusT Date: Mon, 27 Dec 2021 14:43:36 -0800 Subject: [PATCH] Many changes before working on scratch. --- .../qtile/{settings => custom}/traverse.py | 2 +- .config/qtile/{settings => custom}/wal.py | 0 .config/qtile/custom/windowname.py | 87 + .config/qtile/settings/keys.py | 12 +- .config/qtile/settings/layouts.py | 2 +- .config/qtile/settings/screens.py | 5 +- .config/qtile/settings/widgets.py | 8 +- .config/scratch/Scratch.org | 2931 +++++++++++++++++ .config/scratch/readme.md | 4 + .config/xmobar/xmobarrc | 2 +- .doom.d/config.el | 92 + .doom.d/doomed.org | 100 + .doom.d/packages.el | 4 + .doom.d/scripts/update-screens | 27 + .xinitrc | 5 +- .xmonad/xmonad.hs | 12 +- 16 files changed, 3272 insertions(+), 21 deletions(-) rename .config/qtile/{settings => custom}/traverse.py (96%) rename .config/qtile/{settings => custom}/wal.py (100%) create mode 100644 .config/qtile/custom/windowname.py create mode 100755 .config/scratch/Scratch.org create mode 100644 .config/scratch/readme.md create mode 100755 .doom.d/scripts/update-screens diff --git a/.config/qtile/settings/traverse.py b/.config/qtile/custom/traverse.py similarity index 96% rename from .config/qtile/settings/traverse.py rename to .config/qtile/custom/traverse.py index 55e263d4..7afd147d 100644 --- a/.config/qtile/settings/traverse.py +++ b/.config/qtile/custom/traverse.py @@ -1,6 +1,6 @@ """ This plugin exports four functions - up, down, left and right - that when called will -move window focus to the first window in that general direction. Focussing is based + move window focus to the first window in that general direction. Focussing is based entirely on position and geometry, so is independent of screens, layouts and whether windows are floating or tiled. It can also move focus to and from empty screens. Example usage: diff --git a/.config/qtile/settings/wal.py b/.config/qtile/custom/wal.py similarity index 100% rename from .config/qtile/settings/wal.py rename to .config/qtile/custom/wal.py diff --git a/.config/qtile/custom/windowname.py b/.config/qtile/custom/windowname.py new file mode 100644 index 00000000..3a55008c --- /dev/null +++ b/.config/qtile/custom/windowname.py @@ -0,0 +1,87 @@ +# Copyright (c) 2008, 2010 Aldo Cortesi +# Copyright (c) 2010 matt +# Copyright (c) 2011 Mounier Florian +# Copyright (c) 2012 Tim Neumann +# Copyright (c) 2013 Craig Barnes +# Copyright (c) 2014 Sean Vig +# Copyright (c) 2014 Tycho Andersen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from libqtile import bar, hook, pangocffi +from libqtile.log_utils import logger +from libqtile.widget import base + + +class WindowName(base._TextBox): + """Displays the name of the window that currently has focus""" + orientations = base.ORIENTATION_HORIZONTAL + defaults = [ + ('for_current_screen', False, 'instead of this bars screen use currently active screen'), + ('empty_group_string', ' ', 'string to display when no windows are focused on current group'), + ('format', '{state}{name}', 'format of the text'), + ('parse_text', None, 'Function to parse and modify window names. ' + 'e.g. function in config that removes excess ' + 'strings from window name: ' + 'def my_func(text)' + ' for string in [\" - Chromium\", \" - Firefox\"]:' + ' text = text.replace(string, \"\")' + ' return text' + 'then set option parse_text=my_func'), + ] + + def __init__(self, width=bar.STRETCH, **config): + base._TextBox.__init__(self, width=width, **config) + self.add_defaults(WindowName.defaults) + + def _configure(self, qtile, bar): + base._TextBox._configure(self, qtile, bar) + hook.subscribe.client_name_updated(self.hook_response) + hook.subscribe.focus_change(self.hook_response) + hook.subscribe.float_change(self.hook_response) + + @hook.subscribe.current_screen_change + def on_screen_changed(): + if self.for_current_screen: + self.hook_response() + + def hook_response(self, *args): + w = self.qtile.current_screen.group.current_window + state = '' + if w: + if w.maximized: + state = '[] ' + elif w.minimized: + state = '_ ' + elif w.floating: + state = 'V ' + var = {} + var["state"] = state + var["name"] = w.name + if callable(self.parse_text): + try: + var["name"] = self.parse_text(var["name"]) + except: # noqa: E722 + logger.exception("parse_text function failed:") + wm_class = w.get_wm_class() + var["class"] = wm_class[0] if wm_class else "" + unescaped = self.format.format(**var) + else: + unescaped = self.empty_group_string + self.update(pangocffi.markup_escape_text(unescaped)) diff --git a/.config/qtile/settings/keys.py b/.config/qtile/settings/keys.py index 78bc101e..c6c1c0a6 100644 --- a/.config/qtile/settings/keys.py +++ b/.config/qtile/settings/keys.py @@ -1,7 +1,7 @@ from libqtile.config import EzKey from libqtile.command import lazy from libqtile import qtile -import settings.traverse as traverse +from custom.traverse import * import os # Set mod key to the "windows" key @@ -35,10 +35,10 @@ keys = [EzKey(k[0], *k[1:]) for k in [ # ------ Movement ------ # # Navigate between windows - ("M-h", lazy.function(traverse.left)), - ("M-j", lazy.function(traverse.down)), - ("M-k", lazy.function(traverse.up)), - ("M-l", lazy.function(traverse.right)), + ("M-h", lazy.function(left)), + ("M-j", lazy.function(down)), + ("M-k", lazy.function(up)), + ("M-l", lazy.function(right)), # Switch windows ("M-S-", lazy.function(switch_screens)), # Switch focus between two screens @@ -95,7 +95,7 @@ keys = [EzKey(k[0], *k[1:]) for k in [ ("M-s", lazy.spawn("flameshot gui")), # Gamemode ("M-S-g", lazy.spawn('toggle_gamemode')), - # Manage computer audio + #Manage computer audio ("", lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ -2%")), ("", diff --git a/.config/qtile/settings/layouts.py b/.config/qtile/settings/layouts.py index c9ada0c0..ee33ee60 100644 --- a/.config/qtile/settings/layouts.py +++ b/.config/qtile/settings/layouts.py @@ -1,5 +1,5 @@ from libqtile import layout -from settings.wal import wal +from custom.wal import wal from libqtile.config import Match # Layout configs diff --git a/.config/qtile/settings/screens.py b/.config/qtile/settings/screens.py index 20bfcd1a..35042a5f 100644 --- a/.config/qtile/settings/screens.py +++ b/.config/qtile/settings/screens.py @@ -4,9 +4,6 @@ from settings.widgets import primary_widgets # Define the screens (and bars) screens = [ + Screen(), Screen(top=bar.Bar(widgets=primary_widgets, size=20)), - Screen(top=bar.Bar([ - widget.GroupBox(), - widget.WindowName() - ], 20),) ] diff --git a/.config/qtile/settings/widgets.py b/.config/qtile/settings/widgets.py index 75773652..86b271dc 100644 --- a/.config/qtile/settings/widgets.py +++ b/.config/qtile/settings/widgets.py @@ -1,5 +1,6 @@ from libqtile import widget -from settings.wal import wal +from custom.windowname import WindowName +from custom.wal import wal spacer_len = 3 wal_color = wal["colors"] @@ -51,7 +52,8 @@ primary_widgets = [ ), # Window Name widget.Spacer(length=spacer_len), - widget.WindowName(foreground=wal_color["color2"]), + WindowName(foreground=wal_color["color2"]), + #widget.WindowName(foreground=wal_color["color2"]), # System Tray widget.Systray(background=wal_color["color0"], padding=0), widget.Spacer(length=spacer_len, background=wal_color["color0"]), @@ -79,7 +81,7 @@ primary_widgets = [ background=wal_color["color2"], ), widget.Net( - interface="enp4s0", + interface="enp3s0", format="{down} ↓↑ {up}", foreground=wal_color["color0"], background=wal_color["color1"], diff --git a/.config/scratch/Scratch.org b/.config/scratch/Scratch.org new file mode 100755 index 00000000..de9082c1 --- /dev/null +++ b/.config/scratch/Scratch.org @@ -0,0 +1,2931 @@ ++TITLE: Scratch Emacs Config +#+PROPERTY: header-args:emacs-lisp + +* Table of Contents +:PROPERTIES: +:TOC: :include all :depth 3 +:END: +:CONTENTS: +- [[#table-of-contents][Table of Contents]] +- [[#system-configuration][System Configuration]] + - [[#lexical-binding][Lexical Binding]] + - [[#this-system][This-System]] + - [[#package-management][Package Management]] + - [[#startup][Startup]] + - [[#runtime-performance][Runtime Performance]] + - [[#clean-emacsd][Clean emacs.d]] + - [[#buffers][Buffers]] + - [[#server-mode][Server Mode]] + - [[#ido][IDO]] +- [[#general-configuration][General Configuration]] + - [[#completions][Completions]] + - [[#ui][UI]] + - [[#ui-elements][UI Elements]] + - [[#scrolling][Scrolling]] + - [[#line-numbers][Line Numbers]] + - [[#file-warnings][File Warnings]] + - [[#hilight-matching-braces][Hilight Matching Braces]] + - [[#fix-annoying-buffers][Fix Annoying Buffers]] + - [[#theme][Theme]] + - [[#fonts][Fonts]] + - [[#mode-line][Mode Line]] + - [[#time-format][Time Format]] + - [[#diminishing][Diminishing]] + - [[#doom-modeline][Doom Modeline]] + - [[#saving-tweaks][Saving Tweaks]] + - [[#keybinds][Keybinds]] + - [[#evil][Evil!]] + - [[#generalel-for-moar-evil][General.el for Moar Evil!]] + - [[#which-key][Which-key]] + - [[#keycord-binds][Keycord Binds]] + - [[#hydra][Hydra]] +- [[#editing][Editing]] + - [[#general][General]] + - [[#the-mighty-org-mode][The Mighty Org-mode]] + - [[#start][Start]] + - [[#tangle-on-save][Tangle on Save]] + - [[#update-table-of-contents-on-save][Update Table of Contents on Save]] + - [[#fonts-and-bullets][Fonts and Bullets]] + - [[#org-file-paths][Org File Paths]] + - [[#tasks][Tasks]] + - [[#capture][Capture]] + - [[#refile][Refile]] + - [[#super-org-agenda][Super-org-agenda]] + - [[#tags][Tags]] + - [[#searching][Searching]] + - [[#end][End]] + - [[#bindings][Bindings]] + - [[#beancount][Beancount]] + - [[#avy][Avy]] + - [[#expand-region][Expand Region]] + - [[#scratch][Scratch]] +- [[#window-management][Window Management]] + - [[#frame-scaling][Frame Scaling]] + - [[#window-selection][Window Selection]] + - [[#window-history][Window History]] + - [[#burly][Burly]] +- [[#file-browsing][File Browsing]] + - [[#dired][Dired]] + - [[#open-files-externally][Open Files Externally]] + - [[#open-videos-externally][Open Videos Externally]] + - [[#keybinds][Keybinds]] +- [[#devel][Devel]] + - [[#git][Git]] + - [[#magit][Magit]] + - [[#forge][Forge]] + - [[#magit-todos][magit-todos]] + - [[#git-link][git-link]] + - [[#projectile][Projectile]] + - [[#languages][Languages]] + - [[#typescript-and-javascript][TypeScript and JavaScript]] + - [[#cc][C/C++]] + - [[#haskell][Haskell]] + - [[#rust][Rust]] + - [[#emacs-lisp][Emacs Lisp]] + - [[#html][HTML]] + - [[#yaml][YAML]] + - [[#beancount][beancount]] + - [[#http][HTTP]] + - [[#python][Python]] + - [[#clojure][Clojure]] + - [[#productivity][Productivity]] + - [[#syntax-checking-with-flycheck][Syntax checking with Flycheck]] + - [[#snippets][Snippets]] + - [[#smart-parens][Smart Parens]] + - [[#rainbow-delimiters][Rainbow Delimiters]] + - [[#rainbow-mode][Rainbow Mode]] +- [[#project-specific][Project Specific]] + - [[#website-management][Website Management]] + - [[#tstarrus][tstarr.us]] +- [[#applications][Applications]] + - [[#elfeed][Elfeed]] + - [[#general][General]] + - [[#keybinds][Keybinds]] + - [[#download-youtube-videoaudio][Download youtube video/audio]] + - [[#format-elfeed-entries-for-youtube][Format elfeed entries for Youtube]] + - [[#mu4e][Mu4e]] +- [[#keybinds][Keybinds]] + - [[#general][General]] + - [[#main][Main]] + - [[#open][Open]] + - [[#window][Window]] + - [[#project][Project]] + - [[#git][Git]] + - [[#util][Util]] + - [[#help][Help]] + - [[#buffer][Buffer]] + - [[#dired][Dired]] +:END: + +* System Configuration +** Lexical Binding + +Add lexical binding tag to top of config file. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; -*- lexical-binding: t -*- + +#+end_src + +** This-System + +This section aims to allow users to select which portions of the configuration are used on different systems by defining this-system and the possible categories for systems. This system is simply a name identifying the current system the config is being used on. Then the categories (system-category-1, system-category-2, ...) define the possible different combinations of different systems that use that portion of the config. An example of a tangle tag follows: + +:tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +The above statement indicates that for following portion of the config, if the current system is a member of category-1 then it will be tangled and thus effectively added to the final config. As such, we can define different categories for the different permutations of systems that may need different portions of the config. Hopefully that all makes sense :) + +#+begin_src emacs-lisp :tangle ".emacs.d/init.el" + +(setq this-system "main") +(setq system-category-1 '("main" "work" "termux")) +(setq system-category-2 '("main")) + +#+end_src + +** Package Management + +Setup package management. Some lines can be uncommented for fresh installs. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Use package and add archives to list +(require 'package) + +(setq package-enable-at-startup nil) +(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t) + +(package-initialize) + +; Uncomment for fresh install +;(package-refresh-contents) +;(package-install 'use-package) + +(require 'use-package) +(require 'use-package-ensure) + +; Uncomment for fresh install +;(setq use-package-always-ensure t) + +#+end_src + +** Startup + +Make startup faster by reducing the frequency of garbage collection and then use a hook to measure Emacs startup time. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; The default is 800 kilobytes. Measured in bytes. +(setq gc-cons-threshold (* 50 1000 1000)) + +;; Profile emacs startup +(add-hook 'emacs-startup-hook + (lambda () + (message "*** Emacs loaded in %s with %d garbage collections." + (format "%.2f seconds" + (float-time + (time-subtract after-init-time before-init-time))) + gcs-done))) + +(setq package-quickstart t) + +#+end_src + +** Runtime Performance + +Dial the GC threshold back down so that garbage collection happens more frequently but in less time. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; Make gc pauses faster by decreasing the threshold. +(setq gc-cons-threshold (* 2 1000 1000)) + +(use-package benchmark-init + :ensure t + :config + ;; To disable collection of benchmark data after init is done. + (add-hook 'after-init-hook 'benchmark-init/deactivate)) + +#+end_src + +** Clean emacs.d + +I don't want a bunch of transient files showing up as untracked in the Git repo so I move them all to another location. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; Keep transient cruft out of ~/.emacs.d/ +(setq user-emacs-directory "~/.cache/emacs/" + backup-directory-alist `(("." . ,(expand-file-name "backups" user-emacs-directory))) + url-history-file (expand-file-name "url/history" user-emacs-directory) + auto-save-list-file-prefix (expand-file-name "auto-save-list/.saves-" user-emacs-directory) + projectile-known-projects-file (expand-file-name "projectile-bookmarks.eld" user-emacs-directory)) + +;; Keep customization settings in a temporary file (thanks Ambrevar!) +(setq custom-file + (if (boundp 'server-socket-dir) + (expand-file-name "custom.el" server-socket-dir) + (expand-file-name (format "emacs-custom-%s.el" (user-uid)) temporary-file-directory))) +(load custom-file t) + +#+end_src + +** Buffers + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(global-auto-revert-mode t) ; Allow buffers to update from disk contents + +#+end_src + +** Server Mode + + Start the Emacs server from this instance so that all =emacsclient= calls are routed here. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(server-start) + +#+end_src + +** IDO + +IDO provides interactive bits and bobs for buffers and files. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(ido-mode 1) +(ido-everywhere 1) + +(use-package ido-completing-read+ + :init + (ido-ubiquitous-mode 1)) + +#+end_src + +* General Configuration +** Completions + +Stolen from https://github.com/MatthewZMD + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package ivy + :diminish + :init + (use-package amx :defer t) + (use-package counsel :diminish :config (counsel-mode 1)) + (use-package swiper :defer t) + (ivy-mode 1) + :bind + (("C-s" . swiper-isearch) + ("C-c s" . counsel-rg) + ("C-c b" . counsel-buffer-or-recentf) + ("C-c C-b" . counsel-ibuffer) + (:map ivy-minibuffer-map + ("C-r" . ivy-previous-line-or-history) + ("M-RET" . ivy-immediate-done)) + (:map counsel-find-file-map + ("C-~" . counsel-goto-local-home))) + :custom + (ivy-use-virtual-buffers t) + (ivy-height 10) + (ivy-on-del-error-function nil) + (ivy-magic-slash-non-match-action 'ivy-magic-slash-non-match-create) + (ivy-count-format "【%d/%d】") + (ivy-wrap t) + :config + (defun counsel-goto-local-home () + "Go to the $HOME of the local machine." + (interactive) + (ivy--cd "~/"))) + +#+end_src +** UI +*** UI Elements + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (setq inhibit-startup-message t) + (scroll-bar-mode -1) ; Disable visible scrollbar + (tool-bar-mode -1) ; Disable the toolbar + (tooltip-mode -1) ; Disable tooltips + (set-fringe-mode 10) ; Give some breathing room + (menu-bar-mode -1) ; Disable the menu bar + +#+end_src + +*** Scrolling + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (setq mouse-wheel-scroll-amount '(5 ((shift) . 5))) ; start out scrolling 1 line at a time + (setq mouse-wheel-progressive-speed nil) ; accelerate scrolling + (setq mouse-wheel-follow-mouse 't) ; scroll window under mouse + (setq scroll-step 5) ; keyboard scroll one line at a timesetq use-dialog-box nil + +#+end_src + +*** Line Numbers + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (column-number-mode) + (global-display-line-numbers-mode t) + + ;; Disable line numbers for some modes + (dolist (mode '(org-mode-hook + eshell-mode-hook)) + (add-hook mode (lambda () (display-line-numbers-mode 0)))) + +#+end_src + +*** File Warnings +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (setq large-file-warning-threshold nil) ; Don't warn for large files + (setq vc-follow-symlinks t) ; Don't warn for following symlinked files + (setq ad-redefinition-action 'accept) ; Don't warn when advice is added for functions + +#+end_src + +*** Hilight Matching Braces + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package paren + :config + (set-face-attribute 'show-paren-match-expression nil :background "#363e4a") + (show-paren-mode 1)) + +#+end_src + +*** Fix Annoying Buffers + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package popwin + :config + (popwin-mode 1)) + +#+end_src + +** Theme + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package doom-themes :defer t) + (load-theme 'doom-gruvbox t) + +#+end_src + +** Fonts +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + ;; Set the font face based on platform + (set-face-attribute 'default nil :font "JetBrains Mono Nerd Font" :height 80) + ;; Set the fixed pitch face + (set-face-attribute 'fixed-pitch nil :font "JetBrains Mono Nerd Font" :height 80) + ;; Set the variable pitch face + (set-face-attribute 'variable-pitch nil :font "JetBrains Mono Nerd Font" :height 80 :weight 'regular) + +#+end_src +** Mode Line +*** Time Format +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (setq display-time-format "%l:%M %p %b %y" + display-time-default-load-average nil) + +#+end_src +*** Diminishing + +The [[https://github.com/myrjola/diminish.el][diminish]] package hides pesky minor modes from the modelines. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package diminish) + +#+end_src + +*** Doom Modeline +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + ;; You must run (all-the-icons-install-fonts) one time after + ;; installing this package! + + (use-package minions + :hook (doom-modeline-mode . minions-mode) + :custom + (minions-mode-line-lighter "")) + + (use-package doom-modeline + ;:after eshell ;; Make sure it gets hooked after eshell + :hook (after-init . doom-modeline-init) + :custom-face + (mode-line ((t (:height 0.85)))) + (mode-line-inactive ((t (:height 0.85)))) + :custom + (doom-modeline-height 20) + (doom-modeline-bar-width 6) + (doom-modeline-lsp t) + (doom-modeline-github nil) + (doom-modeline-mu4e nil) + (doom-modeline-irc nil) + (doom-modeline-minor-modes t) + (doom-modeline-persp-name nil) + (doom-modeline-buffer-file-name-style 'truncate-except-project) + (doom-modeline-major-mode-icon nil)) + +#+end_src + +*** Saving Tweaks +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + ; Auto-save changed files + (use-package super-save + :ensure t + :defer 1 + :diminish super-save-mode + :config + (super-save-mode +1) + (setq super-save-auto-save-when-idle t)) + + ; Auto revert changed files + (global-auto-revert-mode 1) + +#+end_src + +** Keybinds +*** Evil! +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (defun dw/evil-hook () + (dolist (mode '(custom-mode + eshell-mode + git-rebase-mode + erc-mode + circe-server-mode + circe-chat-mode + circe-query-mode + sauron-mode + term-mode)) + (add-to-list 'evil-emacs-state-modes mode))) + + (defun dw/dont-arrow-me-bro () + (interactive) + (message "Arrow keys are bad, you know?")) + + (use-package evil + :init + (setq evil-want-integration t) + (setq evil-want-keybinding nil) + (setq evil-want-C-u-scroll t) + (setq evil-want-C-i-jump nil) + (setq evil-respect-visual-line-mode t) + :config + (add-hook 'evil-mode-hook 'dw/evil-hook) + (evil-mode 1) + (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state) + (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join) + + ;; Use visual line motions even outside of visual-line-mode buffers + (evil-global-set-key 'motion "j" 'evil-next-visual-line) + (evil-global-set-key 'motion "k" 'evil-previous-visual-line) + + ;; Disable arrow keys in normal and visual modes + (define-key evil-normal-state-map (kbd "") 'dw/dont-arrow-me-bro) + (define-key evil-normal-state-map (kbd "") 'dw/dont-arrow-me-bro) + (define-key evil-normal-state-map (kbd "") 'dw/dont-arrow-me-bro) + (define-key evil-normal-state-map (kbd "") 'dw/dont-arrow-me-bro) + (evil-global-set-key 'motion (kbd "") 'dw/dont-arrow-me-bro) + (evil-global-set-key 'motion (kbd "") 'dw/dont-arrow-me-bro) + (evil-global-set-key 'motion (kbd "") 'dw/dont-arrow-me-bro) + (evil-global-set-key 'motion (kbd "") 'dw/dont-arrow-me-bro) + + (evil-set-initial-state 'messages-buffer-mode 'normal) + (evil-set-initial-state 'dashboard-mode 'normal)) + + (use-package evil-collection + :after evil + :custom + (evil-collection-outline-bind-tab-p nil) + :config + (evil-collection-init)) + +#+end_src +*** General.el for Moar Evil! +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package general + :ensure t + :config + (general-evil-setup t)) + + (general-create-definer dw/leader-key-def + :keymaps '(normal insert visual emacs) + :prefix "SPC" + :global-prefix "C-SPC") + +#+end_src +*** Which-key + +[[https://github.com/justbur/emacs-which-key][which-key]] is great for getting an overview of what keybindings are available +based on the prefix keys you entered. Learned about this one from Spacemacs. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package which-key + :init (which-key-mode) + :diminish which-key-mode + :config + (setq which-key-idle-delay 0.3) + (setq which-key-min-display-lines 6)) + +#+end_src + +*** Keycord Binds + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package use-package-chords + :disabled + :config (key-chord-mode 1)) + +#+end_src + +*** Hydra +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package hydra + :defer 1) + +#+end_src +* Editing +** General +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Set Default indentation to 2 characters +(setq-default tab-width 2) +(setq-default evil-shift-width tab-width) + +; Use spaces instead of tabs for indents +(setq-default indent-tabs-mode nil) + +; Automatic comment/uncomment lines +(use-package evil-nerd-commenter + :bind ("M-/" . evilnc-comment-or-uncomment-lines)) + +; Use Parinfer for Lispy languages +(use-package parinfer + :hook ((clojure-mode . parinfer-mode) + (emacs-lisp-mode . parinfer-mode) + (common-lisp-mode . parinfer-mode) + (scheme-mode . parinfer-mode) + (lisp-mode . parinfer-mode)) + :config + (setq parinfer-extensions + '(defaults ; should be included. + pretty-parens ; different paren styles for different modes. + evil ; If you use Evil. + smart-tab ; C-b & C-f jump positions and smart shift with tab & S-tab. + smart-yank))) ; Yank behavior depend on mode. + +;(dw/leader-key-def +; "tp" 'parinfer-toggle-mode) + +#+end_src +** The Mighty Org-mode +*** Start + +Set up Org Mode with a baseline configuration. The following sections will add more things to it. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; TODO: Mode this to another section +(setq-default fill-column 80) + +;; Turn on indentation and auto-fill mode for Org files +(defun dw/org-mode-setup () + (org-indent-mode) + (variable-pitch-mode 1) + (auto-fill-mode 0) + (visual-line-mode 1) + (setq evil-auto-indent nil) + (diminish org-indent-mode)) + +(use-package org + :defer t + :hook (org-mode . dw/org-mode-setup) + :config + (setq org-ellipsis " ▾" + org-hide-emphasis-markers t + org-src-fontify-natively t + org-src-tab-acts-natively t + org-edit-src-content-indentation 0 + org-hide-block-startup nil + org-src-preserve-indentation nil + org-startup-folded 'content + org-cycle-separator-lines 2) + + (setq org-modules + '(org-crypt + org-habit)) + + (setq org-refile-targets '((nil :maxlevel . 3) + (org-agenda-files :maxlevel . 3))) + (setq org-outline-path-complete-in-steps nil) + (setq org-refile-use-outline-path t) + + (evil-define-key '(normal insert visual) org-mode-map (kbd "C-j") 'org-next-visible-heading) + (evil-define-key '(normal insert visual) org-mode-map (kbd "C-k") 'org-previous-visible-heading) + + (evil-define-key '(normal insert visual) org-mode-map (kbd "M-j") 'org-metadown) + (evil-define-key '(normal insert visual) org-mode-map (kbd "M-k") 'org-metaup) + + (org-babel-do-load-languages + 'org-babel-load-languages + '((emacs-lisp . t) + (ledger . t))) + + (push '("conf-unix" . conf-unix) org-src-lang-modes) + + ;; NOTE: Subsequent sections are still part of this use-package block! + +#+end_src + +*** Tangle on Save +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + ;; Since we don't want to disable org-confirm-babel-evaluate all + ;; of the time, do it around the after-save-hook + (defun dw/org-babel-tangle-dont-ask () + ;; Dynamic scoping to the rescue + (let ((org-confirm-babel-evaluate nil)) + (org-babel-tangle))) + + (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'dw/org-babel-tangle-dont-ask + 'run-at-end 'only-in-org-mode))) + +#+end_src + +*** Update Table of Contents on Save + +It’s nice to have a table of contents section for long literate configuration files (like this one!) so I use org-make-toc to automatically update the ToC in any header with a property named TOC. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package org-make-toc + :hook (org-mode . org-make-toc-mode)) + +#+end_src + +*** Fonts and Bullets + +Use bullet characters instead of asterisks, plus set the header font sizes to something more palatable. A fair amount of inspiration has been taken from [[https://zzamboni.org/post/beautifying-org-mode-in-emacs/][this blog post]]. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package org-bullets + :after org + :hook (org-mode . org-bullets-mode) + :custom + (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●"))) + +;; Replace list hyphen with dot +(font-lock-add-keywords 'org-mode + '(("^ *\\([-]\\) " + (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))) + +(dolist (face '((org-level-1 . 1.2) + (org-level-2 . 1.1) + (org-level-3 . 1.05) + (org-level-4 . 1.0) + (org-level-5 . 1.1) + (org-level-6 . 1.1) + (org-level-7 . 1.1) + (org-level-8 . 1.1))) + (set-face-attribute (car face) nil :font "JetBrains Mono Nerd Font" :weight 'regular :height (cdr face))) + +;; Make sure org-indent face is available +(require 'org-indent) + +;; Ensure that anything that should be fixed-pitch in Org files appears that way +(set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) +(set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch)) +(set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch)) +(set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch)) +(set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch)) +(set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch)) +(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch) + +#+end_src + +*** Org File Paths +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;;; Directory Options +;; Set default working directory for org files +(setq org-directory "~/documents/org") +;; Set default locations to store notes +(setq org-default-notes-file "~/documents/org/capture/refile.org") +;; Set agenda files +(setq org-agenda-files (quote ("~/documents/org/capture" + "~/documents/org/capture/agendas" + "~/documents/org/capture/bookmarks" + "~/documents/org/capture/notes"))) + +#+end_src +*** Tasks +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;;; Set Todo Options +;; Set keywords for todo items +(setq org-todo-keywords + (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") + (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" )))) +;; Set colors for todo items +(setq org-todo-keyword-faces + (quote (("TODO" :foreground "red" :weight bold) + ("NEXT" :foreground "blue" :weight bold) + ("DONE" :foreground "forest green" :weight bold) + ("WAITING" :foreground "orange" :weight bold) + ("HOLD" :foreground "magenta" :weight bold) + ("CANCELLED" :foreground "forest green" :weight bold)))) +;; Set tags based on todo changes +(setq org-todo-state-tags-triggers + (quote (("CANCELLED" ("CANCELLED" . t)) + ("WAITING" ("WAITING" . t)) + ("HOLD" ("WAITING") ("HOLD" . t)) + (done ("WAITING") ("HOLD")) + ("TODO" ("WAITING") ("CANCELLED") ("HOLD")) + ("NEXT" ("WAITING") ("CANCELLED") ("HOLD")) + ("DONE" ("WAITING") ("CANCELLED") ("HOLD"))))) + +#+end_src +*** Capture +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; open org-capture +(global-set-key (kbd "C-c c") 'org-capture) + +;;; Set Org-Capture Options +;; Capture templates for: TODO tasks, Notes, appointments, and meetings +(setq org-capture-templates + (quote (("t" "todo" entry (file "~/documents/org/capture/refile.org") + "* TODO %?\n%U\n%a\n") + ("r" "respond" entry (file "~/documents/org/capture/refile.org") + "* TODO Respond to %:from on %:subject\nSCHEDULED: %t\n%U\n%a\n") + ("n" "note" entry (file "~/documents/org/capture/refile.org") + "* %? :NOTE:\n%U\n%a\n") + ("m" "Meeting" entry (file "~/documents/org/capture/refile.org") + "* MEETING with %? :MEETING:\n%U") + ("h" "Habit" entry (file "~/documents/org/capture/refile.org") + "* NEXT %?\n%U\n%a\nSCHEDULED: %(format-time-string \"%<<%Y-%m-%d %a .+1d/3d>>\")\n:PROPERTIES:\n:STYLE: habit\n:REPEAT_TO_STATE: NEXT\n:END:\n")))) + +#+end_src +*** Refile +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;;; Set Task Refiling Options +;; Targets include this file and any file contributing to the agenda - up to 9 levels deep +(setq org-refile-targets (quote ((nil :maxlevel . 9) + (org-agenda-files :maxlevel . 9)))) +;; Use full outline paths for refile targets - we file directly with IDO +(setq org-refile-use-outline-path t) +;; Targets complete directly with IDO +(setq org-outline-path-complete-in-steps nil) +;; Allow refile to create parent tasks with confirmation +(setq org-refile-allow-creating-parent-nodes (quote confirm)) +;; Use IDO for both buffer and file completion and ido-everywhere to t +(setq org-completion-use-ido t) +(setq ido-everywhere t) +(setq ido-max-directory-size 100000) +(ido-mode (quote both)) +;; Use the current window when visiting files and buffers with ido +(setq ido-default-file-method 'selected-window) +(setq ido-default-buffer-method 'selected-window) +;; Use the current window for indirect buffer display +(setq org-indirect-buffer-display 'current-window) +;; Exclude DONE state tasks from refile targets +(defun bh/verify-refile-target () + "Exclude todo keywords with a done state from refile targets" + (not (member (nth 2 (org-heading-components)) org-done-keywords))) +(setq org-refile-target-verify-function 'bh/verify-refile-target) + +#+end_src +*** Super-org-agenda + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package org-super-agenda + :after org-agenda + :init + (setq org-super-agenda-header-map (make-sparse-keymap)) + + (setq org-agenda-custom-commands + '(("c" "Custom Agenda" + ((agenda "" ((org-agenda-span 'day) + (org-super-agenda-groups + '((:name "--- LATE ---" + :face (:underline t) + :deadline past + :order 1) + (:name "--- DUE TODAY ---" + :time-grid t + :deadline today + :order 2) + (:name "--- SCHEDULED TODAY ---" + :time-grid t + :date today + :scheduled today + :order 3) + (:name "" + :discard (:anything) + :order 99)) + ))))))) + + :config + (org-super-agenda-mode)) + +#+end_src + +*** Tags + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + ;; Configure common tags + (setq org-tag-alist + '((:startgroup) + ; Put mutually exclusive tags here + (:endgroup) + ("@errand" . ?E) + ("@home" . ?H) + ("@work" . ?W) + ("agenda" . ?a) + ("planning" . ?p) + ("publish" . ?P) + ("batch" . ?b) + ("note" . ?n) + ("idea" . ?i) + ("thinking" . ?t) + ("recurring" . ?r))) + +#+end_src + +*** Searching + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(defun dw/search-org-files () + (interactive) + (counsel-rg "" "~/documents/org/capture/notes" nil "Search Notes: ")) + +#+end_src + +*** End + + #+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + ;; This ends the use-package org-mode block +) + + #+end_src + +*** Bindings + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + + +(use-package evil-org + :after org + :hook ((org-mode . evil-org-mode) + (org-agenda-mode . evil-org-mode) + (evil-org-mode . (lambda () (evil-org-set-key-theme '(navigation todo insert textobjects additional))))) + :config + (require 'evil-org-agenda) + (evil-org-agenda-set-keys)) + +;(dw/leader-key-def +; "o" '(:ignore t :which-key "org mode") +; "oi" '(:ignore t :which-key "insert") +; "oil" '(org-insert-link :which-key "insert link") +; "on" '(org-toggle-narrow-to-subtree :which-key "toggle narrow") +; "os" '(dw/counsel-rg-org-files :which-key "search notes") +; "oa" '(org-agenda :which-key "status") +; "oc" '(org-capture t :which-key "capture") +; "ox" '(org-export-dispatch t :which-key "export")) + +#+end_src + +** Beancount + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;(use-package Beancount +; :straight (beancount +; :type git +; :host github +; :repo "cnsunyour/beancount.el") +; :bind +; ("C-M-b" . (lambda () +; (interactive) +; (find-file "~/Dropbox/beancount/main.bean"))) +; :mode +; ("\\.bean\\(?:count\\)?\\'" . beancount-mode) +; :config +; (setq beancount-accounts-files +; (directory-files "~/Dropbox/beancount/accounts/" +; 'full +; (rx ".bean" eos)))) + +#+end_src + +** Avy +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package avy + :commands (avy-goto-char avy-goto-word-0 avy-goto-line)) + + (dw/leader-key-def + "j" '(:ignore t :which-key "jump") + "jj" '(avy-goto-char :which-key "jump to char") + "jw" '(avy-goto-word-0 :which-key "jump to word") + "jl" '(avy-goto-line :which-key "jump to line")) + +#+end_src + +** Expand Region + +This module is absolutely necessary for working inside of Emacs Lisp files, +especially when trying to some parent of an expression (like a =setq=). Makes +tweaking Org agenda views much less annoying. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package expand-region + :bind (("M-[" . er/expand-region) + ("C-(" . er/mark-outside-pairs))) + +#+end_src + +** Scratch + +Since the =*scratch*= buffer is pretty hard-wired into Emacs (see +=buffer.c=), the least we could do is getting rid of its initial +message. No, it's using its own mode instead of ~emacs-lisp-mode~ for +the questionable benefit of having a function inserting evaluation +values after a newline. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") +(setq initial-scratch-message "") +(setq initial-major-mode 'emacs-lisp-mode) +#+END_SRC + +* Window Management +** Frame Scaling + +The keybindings for this are =C+M+-= and =C+M+==. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package default-text-scale + :defer 1 + :config + (default-text-scale-mode)) + +#+end_src + +** Window Selection + +Use ace-window for selecting windows quickly. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package ace-window + :config + (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))) + +#+end_src + +** Window History + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(winner-mode) +(define-key evil-window-map "u" 'winner-undo) + +#+end_src + +** Burly + +Use burly to bookmark layouts and Emacs state. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package burly) + +#+end_src + +* File Browsing +** Dired + +Stolen from [[https://github.com/daviwil][the_dev_aspect]]. I have edited to fit my needs including removing the termux logic. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (setq dired-listing-switches "-agho --group-directories-first" + dired-omit-files "^\\.[^.].*" + dired-omit-verbose nil) + + (autoload 'dired-omit-mode "dired-x") + + (add-hook 'dired-load-hook + (lambda () + (interactive) + (dired-collapse))) + + (add-hook 'dired-mode-hook + (lambda () + (interactive) + (dired-omit-mode 1) + (hl-line-mode 1))) + + (use-package dired-rainbow + :defer 2 + :config + (dired-rainbow-define-chmod directory "#6cb2eb" "d.*") + (dired-rainbow-define html "#eb5286" ("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht" "eml" "mustache" "xhtml")) + (dired-rainbow-define xml "#f2d024" ("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg" "pgn" "rss" "yaml" "yml" "rdata")) + (dired-rainbow-define document "#9561e2" ("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub" "odp" "ppt" "pptx")) + (dired-rainbow-define markdown "#ffed4a" ("org" "etx" "info" "markdown" "md" "mkd" "nfo" "pod" "rst" "tex" "textfile" "txt")) + (dired-rainbow-define database "#6574cd" ("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc")) + (dired-rainbow-define media "#de751f" ("mp3" "mp4" "mkv" "MP3" "MP4" "avi" "mpeg" "mpg" "flv" "ogg" "mov" "mid" "midi" "wav" "aiff" "flac")) + (dired-rainbow-define image "#f66d9b" ("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png" "psd" "eps" "svg")) + (dired-rainbow-define log "#c17d11" ("log")) + (dired-rainbow-define shell "#f6993f" ("awk" "bash" "bat" "sed" "sh" "zsh" "vim")) + (dired-rainbow-define interpreted "#38c172" ("py" "ipynb" "rb" "pl" "t" "msql" "mysql" "pgsql" "sql" "r" "clj" "cljs" "scala" "js")) + (dired-rainbow-define compiled "#4dc0b5" ("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp" "hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn" "f90" "f95" "f03" "f08" "s" "rs" "hi" "hs" "pyc" ".java")) + (dired-rainbow-define executable "#8cc4ff" ("exe" "msi")) + (dired-rainbow-define compressed "#51d88a" ("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar")) + (dired-rainbow-define packaged "#faad63" ("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf" "vpk" "bsp")) + (dired-rainbow-define encrypted "#ffed4a" ("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12" "pem")) + (dired-rainbow-define fonts "#6cb2eb" ("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf")) + (dired-rainbow-define partition "#e3342f" ("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk" "bak")) + (dired-rainbow-define vc "#0074d9" ("git" "gitignore" "gitattributes" "gitmodules")) + (dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*")) + + (use-package dired-single + :ensure t + :defer t) + + (use-package dired-ranger + :defer t) + + (use-package dired-collapse + :defer t) + +#+end_src +** Open Files Externally + +Stolen from [[https://github.com/daviwil][the_dev_aspect]]. Didn't even bother to change it ;) + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package openwith + :config + (setq openwith-associations + (list + (list (openwith-make-extension-regexp + '("mpg" "mpeg" "mp3" "mp4" + "avi" "wmv" "wav" "mov" "flv" + "ogm" "ogg" "mkv")) + "mpv" + '(file)) + (list (openwith-make-extension-regexp + '("xbm" "pbm" "pgm" "ppm" "pnm" + "png" "gif" "bmp" "tif" "jpeg")) ;; Removed jpg because Telega was + ;; causing feh to be opened... + "feh" + '(file)) + (list (openwith-make-extension-regexp + '("pdf")) + "zathura" + '(file)))) + (openwith-mode 1)) + +#+end_src + +** Open Videos Externally + +Can't remember where I stole this from. Might edit in future to integrate audio and video playing with Dired instead of using ncmpcpp for audio. I had to expand the file-name in the mpv-dir function to make it work with mpv. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (defun start-mpv (path &optional playlist-p) + + "Start mpv with specified arguments" + (let* ((default-cmd "mpv --force-window") + (cmd (if playlist-p + (s-append " --loop-playlist --playlist=" default-cmd) + (s-append " --loop " default-cmd)))) + (call-process-shell-command (s-concat cmd (shell-quote-argument path)) nil 0))) + + (defun mpv () + "Play a file in current line" + (interactive) + (start-mpv (dired-get-filename))) + + (defun mpv-dir () + "Play all multimedia files in current directory" + (interactive) + (start-mpv (expand-file-name default-directory))) + + (defun mpv-playlist () + "Play a playlist in current line" + (interactive) + (start-mpv (dired-get-filename) t)) + +#+end_src + +** Keybinds + +Stolen from [[https://github.com/daviwil][the_dev_aspect]]. I have edited to fit my needs including changing/adding keybinds. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (setq which-key-sort-order 'which-key-prefix-then-key-order) + + (evil-collection-define-key 'normal 'dired-mode-map + "h" 'dired-single-up-directory + "H" 'dired-omit-mode + "l" 'dired-single-buffer + "y" 'dired-ranger-copy + "X" 'dired-ranger-move + "p" 'dired-ranger-paste) + + (require 'cl) + + (defun dw/dired-link (path) + (lexical-let ((target path)) + (lambda () (interactive) (message "Path: %s" target) (dired target)))) + +#+end_src + +* Devel +** Git +*** Magit + +https://magit.vc/manual/magit/ + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package magit + :commands (magit-status magit-get-current-branch) + :custom + (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) + + (use-package evil-magit + :after magit) + +#+end_src + +*** Forge + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package forge + :disabled) + +#+end_src + +*** magit-todos + +This is an interesting extension to Magit that shows a TODOs section in your +git status buffer containing all lines with TODO (or other similar words) in +files contained within the repo. More information at the [[https://github.com/alphapapa/magit-todos][GitHub repo]]. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package magit-todos + :defer t) + +#+end_src + +*** git-link + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package git-link + :commands git-link + :config + (setq git-link-open-in-browser t)) + +#+end_src + +** Projectile + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package projectile + :diminish projectile-mode + :config (projectile-mode) + :bind-keymap + ("C-c p" . projectile-command-map) + :init + (when (file-directory-p "~/devel") + (setq projectile-project-search-path '("~/devel"))) + (setq projectile-switch-project-action #'projectile-dired)) + + (use-package counsel-projectile + :after projectile) + +#+end_src + +** Languages +*** TypeScript and JavaScript + +Set up nvm so that we can manage Node versions + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package nvm + :defer t) + +#+end_src + +Configure TypeScript and JavaScript language modes + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package typescript-mode + :mode "\\.ts\\'" + :config + (setq typescript-indent-level 2)) + + (defun dw/set-js-indentation () + (setq js-indent-level 2) + (setq evil-shift-width js-indent-level) + (setq-default tab-width 2)) + + (use-package js2-mode + :mode "\\.jsx?\\'" + :config + ;; Use js2-mode for Node scripts + (add-to-list 'magic-mode-alist '("#!/usr/bin/env node" . js2-mode)) + + ;; Don't use built-in syntax checking + (setq js2-mode-show-strict-warnings nil) + + ;; Set up proper indentation in JavaScript and JSON files + (add-hook 'js2-mode-hook #'dw/set-js-indentation) + (add-hook 'json-mode-hook #'dw/set-js-indentation)) + + (use-package prettier-js + :hook ((js2-mode . prettier-js-mode) + (typescript-mode . prettier-js-mode)) + :config + (setq prettier-js-show-errors nil)) + +#+end_src + +*** C/C++ + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package ccls + :hook ((c-mode c++-mode objc-mode cuda-mode) . + (lambda () (quire 'ccls) (lsp)))) + +#+end_src + +*** Haskell + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package haskell-mode) + +#+end_src + +*** Rust + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package rust-mode + :mode "\\.rs\\'" + :init (setq rust-format-on-save t)) + + (use-package cargo + :ensure t + :defer t) + +#+end_src + +*** Emacs Lisp + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (add-hook 'emacs-lisp-mode-hook #'flycheck-mode) + + (use-package helpful + :ensure t + :custom + (counsel-describe-function-function #'helpful-callable) + (counsel-describe-variable-function #'helpful-variable) + :bind + ([remap describe-function] . counsel-describe-function) + ([remap describe-command] . helpful-command) + ([remap describe-variable] . counsel-describe-variable) + ([remap describe-key] . helpful-key)) + +#+end_src + + +*** HTML + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package web-mode + :mode "(\\.\\(html?\\|ejs\\|tsx\\|jsx\\)\\'" + :config + (setq-default web-mode-code-indent-offset 2) + (setq-default web-mode-markup-indent-offset 2) + (setq-default web-mode-attribute-indent-offset 2)) + +;; 1. Start the server with `httpd-start' +;; 2. Use `impatient-mode' on any buffer +(use-package impatient-mode + :ensure t) + +(use-package skewer-mode + :ensure t) + +#+end_src + +*** YAML + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package yaml-mode + :mode "\\.ya?ml\\'") + +#+end_src + +*** beancount +**** Github stolen beancount functions +Provide beancount major mode for personal ledger. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(autoload 'ido-completing-read "ido") +(require 'subr-x) +(require 'outline) + +(defgroup beancount () + "Editing mode for Beancount files." + :group 'beancount) + +(defcustom beancount-transaction-indent 2 + "Transaction indent." + :type 'integer) + +(defcustom beancount-number-alignment-column 52 + "Column to which align numbers in postinng definitions. Set to +0 to automatically determine the minimum column that will allow +to align all amounts." + :type 'integer) + +(defcustom beancount-highlight-transaction-at-point nil + "If t highlight transaction under point." + :type 'boolean) + +(defcustom beancount-use-ido t + "If non-nil, use ido-style completion rather than the standard." + :type 'boolean) + +(defcustom beancount-electric-currency nil + "If non-nil, make `newline' try to add missing currency to +complete the posting at point. The correct currency is determined +from the open directive for the relevant account." + :type 'boolean) + +(defgroup beancount-faces nil "Beancount mode highlighting" :group 'beancount) + +(defface beancount-directive + `((t :inherit font-lock-keyword-face)) + "Face for Beancount directives.") + +(defface beancount-tag + `((t :inherit font-lock-type-face)) + "Face for Beancount tags.") + +(defface beancount-link + `((t :inherit font-lock-type-face)) + "Face for Beancount links.") + +(defface beancount-date + `((t :inherit font-lock-constant-face)) + "Face for Beancount dates.") + +(defface beancount-account + `((t :inherit font-lock-builtin-face)) + "Face for Beancount account names.") + +(defface beancount-amount + `((t :inherit font-lock-default-face)) + "Face for Beancount amounts.") + +(defface beancount-narrative + `((t :inherit font-lock-builtin-face)) + "Face for Beancount transactions narrative.") + +(defface beancount-narrative-cleared + `((t :inherit font-lock-string-face)) + "Face for Beancount cleared transactions narrative.") + +(defface beancount-narrative-pending + `((t :inherit font-lock-keyword-face)) + "Face for Beancount pending transactions narrative.") + +(defface beancount-metadata + `((t :inherit font-lock-type-face)) + "Face for Beancount metadata.") + +(defface beancount-highlight + `((t :inherit highlight)) + "Face to highlight Beancount transaction at point.") + +(defconst beancount-account-directive-names + '("balance" + "close" + "document" + "note" + "open" + "pad") + "Directive bames that can appear after a date and are followd by an account.") + +(defconst beancount-no-account-directive-names + '("commodity" + "event" + "price" + "query" + "txn") + "Directive names that can appear after a date and are _not_ followed by an account.") + +(defconst beancount-timestamped-directive-names + (append beancount-account-directive-names + beancount-no-account-directive-names) + "Directive names that can appear after a date.") + +(defconst beancount-directive-names + '("include" + "option" + "plugin" + "poptag" + "pushtag") + "Directive names that can appear at the beginning of a line.") + +(defconst beancount-account-categories + '("Assets" "Liabilities" "Equity" "Income" "Expenses")) + +(defconst beancount-tag-chars "[:alnum:]-_/.") + +(defconst beancount-account-chars "[:alnum:]-_:") + +(defconst beancount-option-names + ;; This list is kept in sync with the options defined in + ;; beancount/parser/options.py. + '("account_current_conversions" + "account_current_earnings" + "account_previous_balances" + "account_previous_conversions" + "account_previous_earnings" + "account_rounding" + "allow_deprecated_none_for_tags_and_links" + "allow_pipe_separator" + "booking_method" + "conversion_currency" + "documents" + "infer_tolerance_from_cost" + "inferred_tolerance_default" + "inferred_tolerance_multiplier" + "insert_pythonpath" + "long_string_maxlines" + "name_assets" + "name_equity" + "name_expenses" + "name_income" + "name_liabilities" + "operating_currency" + "plugin_processing_mode" + "render_commas" + "title")) + +(defconst beancount-date-regexp "[0-9]\\{4\\}[-/][0-9]\\{2\\}[-/][0-9]\\{2\\}" + "A regular expression to match dates.") + +(defconst beancount-account-regexp + (concat (regexp-opt beancount-account-categories) + "\\(?::[[:upper:]][[:alnum:]-_]+\\)+") + "A regular expression to match account names.") + +(defconst beancount-number-regexp "[-+]?[0-9]+\\(?:,[0-9]\\{3\\}\\)*\\(?:\\.[0-9]*\\)?" + "A regular expression to match decimal numbers.") + +(defconst beancount-currency-regexp "[A-Z][A-Z-_'.]*" + "A regular expression to match currencies.") + +(defconst beancount-flag-regexp + ;; Single char that is neither a space nor a lower-case letter. + "[^ a-z]") + +(defconst beancount-transaction-regexp + (concat "^\\(" beancount-date-regexp "\\) +" + "\\(?:txn +\\)?" + "\\(" beancount-flag-regexp "\\) +" + "\\(\".*\"\\)")) + +(defconst beancount-posting-regexp + (concat "^\\s-+" + "\\(" beancount-account-regexp "\\)" + "\\(?:\\s-+\\(\\(" beancount-number-regexp "\\)" + "\\s-+\\(" beancount-currency-regexp "\\)\\)\\)?")) + +(defconst beancount-directive-regexp + (concat "^\\(" (regexp-opt beancount-directive-names) "\\) +")) + +(defconst beancount-timestamped-directive-regexp + (concat "^\\(" beancount-date-regexp "\\) +" + "\\(" (regexp-opt beancount-timestamped-directive-names) "\\) +")) + +(defconst beancount-metadata-regexp + "^\\s-+\\([a-z][A-Za-z0-9_-]+:\\)\\s-+\\(.+\\)") + +;; This is a grouping regular expression because the subexpression is +;; used in determining the outline level in `beancount-outline-level'. +(defvar beancount-outline-regexp "\\(;;;+\\|\\*+\\)") + +(defun beancount-outline-level () + (let ((len (- (match-end 1) (match-beginning 1)))) + (if (equal (substring (match-string 1) 0 1) ";") + (- len 2) + len))) + +(defun beancount-face-by-state (state) + (cond ((string-equal state "*") 'beancount-narrative-cleared) + ((string-equal state "!") 'beancount-narrative-pending) + (t 'beancount-narrative))) + +(defun beancount-outline-face () + (if outline-minor-mode + (cl-case (funcall outline-level) + (1 'org-level-1) + (2 'org-level-2) + (3 'org-level-3) + (4 'org-level-4) + (5 'org-level-5) + (6 'org-level-6) + (otherwise nil)) + nil)) + +(defvar beancount-font-lock-keywords + `((,beancount-transaction-regexp (1 'beancount-date) + (2 (beancount-face-by-state (match-string 2)) t) + (3 (beancount-face-by-state (match-string 2)) t)) + (,beancount-posting-regexp (1 'beancount-account) + (2 'beancount-amount nil :lax)) + (,beancount-metadata-regexp (1 'beancount-metadata) + (2 'beancount-metadata t)) + (,beancount-directive-regexp (1 'beancount-directive)) + (,beancount-timestamped-directive-regexp (1 'beancount-date) + (2 'beancount-directive)) + ;; Fontify section headers when composed with outline-minor-mode. + (,(concat "^\\(" beancount-outline-regexp "\\).*") . (0 (beancount-outline-face))) + ;; Tags and links. + (,(concat "\\#[" beancount-tag-chars "]*") . 'beancount-tag) + (,(concat "\\^[" beancount-tag-chars "]*") . 'beancount-link) + ;; Number followed by currency not covered by previous rules. + (,(concat beancount-number-regexp "\\s-+" beancount-currency-regexp) . 'beancount-amount) + ;; Accounts not covered by previous rules. + (,beancount-account-regexp . 'beancount-account) + )) + +(defun beancount-tab-dwim (&optional arg) + (interactive "P") + (if (and outline-minor-mode + (or arg (outline-on-heading-p))) + (beancount-outline-cycle arg) + (indent-for-tab-command))) + +(defvar beancount-mode-map-prefix [(control c)] + "The prefix key used to bind Beancount commands in Emacs") + +(defvar beancount-mode-map + (let ((map (make-sparse-keymap)) + (p beancount-mode-map-prefix)) + (define-key map (kbd "TAB") #'beancount-tab-dwim) + (define-key map (kbd "M-RET") #'beancount-insert-date) + (define-key map (vconcat p [(\')]) #'beancount-insert-account) + (define-key map (vconcat p [(control g)]) #'beancount-transaction-clear) + (define-key map (vconcat p [(l)]) #'beancount-check) + (define-key map (vconcat p [(q)]) #'beancount-query) + (define-key map (vconcat p [(x)]) #'beancount-context) + (define-key map (vconcat p [(k)]) #'beancount-linked) + (define-key map (vconcat p [(p)]) #'beancount-insert-prices) + (define-key map (vconcat p [(\;)]) #'beancount-align-to-previous-number) + (define-key map (vconcat p [(\:)]) #'beancount-align-numbers) + map)) + +(defvar beancount-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?\" "\"\"" st) + (modify-syntax-entry ?\; "<" st) + (modify-syntax-entry ?\n ">" st) + st)) + +;;;###autoload +(define-derived-mode beancount-mode fundamental-mode "Beancount" + "A mode for Beancount files. +\\{beancount-mode-map}" + :group 'beancount + :syntax-table beancount-mode-syntax-table + + (setq-local paragraph-ignore-fill-prefix t) + (setq-local fill-paragraph-function #'beancount-indent-transaction) + + (setq-local comment-start ";") + (setq-local comment-start-skip ";+\\s-*") + (setq-local comment-add 1) + + (setq-local indent-line-function #'beancount-indent-line) + (setq-local indent-region-function #'beancount-indent-region) + (setq-local indent-tabs-mode nil) + + (setq-local tab-always-indent 'complete) + (setq-local completion-ignore-case t) + + (add-hook 'completion-at-point-functions #'beancount-completion-at-point nil t) + (add-hook 'post-command-hook #'beancount-highlight-transaction-at-point nil t) + (add-hook 'post-self-insert-hook #'beancount--electric-currency nil t) + + (setq-local font-lock-defaults '(beancount-font-lock-keywords)) + (setq-local font-lock-syntax-table t) + + (setq-local outline-regexp beancount-outline-regexp) + (setq-local outline-level #'beancount-outline-level) + + (setq imenu-generic-expression + (list (list nil (concat "^" beancount-outline-regexp "\\s-+\\(.*\\)$") 2)))) + +(defun beancount-collect-pushed-tags (begin end) + "Return list of all pushed (and not popped) tags in the region." + (goto-char begin) + (let ((tags (make-hash-table :test 'equal))) + (while (re-search-forward + (concat "^\\(push\\|pop\\)tag\\s-+\\(#[" beancount-tag-chars "]+\\)") end t) + (if (string-equal (match-string 1) "push") + (puthash (match-string-no-properties 2) nil tags) + (remhash (match-string-no-properties 2) tags))) + (hash-table-keys tags))) + +(defun beancount-goto-transaction-begin () + "Move the cursor to the first line of the transaction definition." + (interactive) + (beginning-of-line) + ;; everything that is indented with at lest one space or tab is part + ;; of the transaction definition + (while (looking-at-p "[ \t]+") + (forward-line -1)) + (point)) + +(defun beancount-goto-transaction-end () + "Move the cursor to the line after the transaction definition." + (interactive) + (beginning-of-line) + (if (looking-at-p beancount-transaction-regexp) + (forward-line)) + ;; everything that is indented with at least one space or tab as part + ;; of the transaction definition + (while (looking-at-p "[ \t]+") + (forward-line)) + (point)) + +(defun beancount-goto-next-transaction (&optional arg) + "Move to the next transaction. +With an argument move to the next non cleared transaction." + (interactive "P") + (beancount-goto-transaction-end) + (let ((done nil)) + (while (and (not done) + (re-search-forward beancount-transaction-regexp nil t)) + (if (and arg (string-equal (match-string 2) "*")) + (goto-char (match-end 0)) + (goto-char (match-beginning 0)) + (setq done t))) + (if (not done) (goto-char (point-max))))) + +(defun beancount-find-transaction-extents (p) + (save-excursion + (goto-char p) + (list (beancount-goto-transaction-begin) + (beancount-goto-transaction-end)))) + +(defun beancount-inside-transaction-p () + (let ((bounds (beancount-find-transaction-extents (point)))) + (> (- (cadr bounds) (car bounds)) 0))) + +(defun beancount-looking-at (regexp n pos) + (and (looking-at regexp) + (>= pos (match-beginning n)) + (<= pos (match-end n)))) + +(defvar beancount-accounts nil + "A list of the accounts available in this buffer.") +(make-variable-buffer-local 'beancount-accounts) + +(defun beancount-completion-at-point () + "Return the completion data relevant for the text at point." + (save-excursion + (save-match-data + (let ((pos (point))) + (beginning-of-line) + (cond + ;; non timestamped directive + ((beancount-looking-at "[a-z]*" 0 pos) + (list (match-beginning 0) (match-end 0) + (mapcar (lambda (s) (concat s " ")) beancount-directive-names))) + + ;; poptag + ((beancount-looking-at + (concat "poptag\\s-+\\(\\(?:#[" beancount-tag-chars "]*\\)\\)") 1 pos) + (list (match-beginning 1) (match-end 1) + (beancount-collect-pushed-tags (point-min) (point)))) + + ;; option + ((beancount-looking-at + (concat "^option\\s-+\\(\"[a-z_]*\\)") 1 pos) + (list (match-beginning 1) (match-end 1) + (mapcar (lambda (s) (concat "\"" s "\" ")) beancount-option-names))) + + ;; timestamped directive + ((beancount-looking-at + (concat beancount-date-regexp "\\s-+\\([[:alpha:]]*\\)") 1 pos) + (list (match-beginning 1) (match-end 1) + (mapcar (lambda (s) (concat s " ")) beancount-timestamped-directive-names))) + + ;; timestamped directives followed by account + ((beancount-looking-at + (concat "^" beancount-date-regexp + "\\s-+" (regexp-opt beancount-account-directive-names) + "\\s-+\\([" beancount-account-chars "]*\\)") 1 pos) + (setq beancount-accounts nil) + (list (match-beginning 1) (match-end 1) #'beancount-account-completion-table)) + + ;; posting + ((and (beancount-looking-at + (concat "[ \t]+\\([" beancount-account-chars "]*\\)") 1 pos) + ;; Do not force the account name to start with a + ;; capital, so that it is possible to use substring + ;; completion and we can rely on completion to fix + ;; capitalization thanks to completion-ignore-case. + (beancount-inside-transaction-p)) + (setq beancount-accounts nil) + (list (match-beginning 1) (match-end 1) #'beancount-account-completion-table)) + + ;; tags + ((beancount-looking-at + (concat "[ \t]+#\\([" beancount-tag-chars "]*\\)") 1 pos) + (let* ((candidates nil) + (regexp (concat "\\#\\([" beancount-tag-chars "]+\\)")) + (completion-table + (lambda (string pred action) + (if (null candidates) + (setq candidates + (sort (beancount-collect regexp 1) #'string<))) + (complete-with-action action candidates string pred)))) + (list (match-beginning 1) (match-end 1) completion-table))) + + ;; links + ((beancount-looking-at + (concat "[ \t]+\\^\\([" beancount-tag-chars "]*\\)") 1 pos) + (let* ((candidates nil) + (regexp (concat "\\^\\([" beancount-tag-chars "]+\\)")) + (completion-table + (lambda (string pred action) + (if (null candidates) + (setq candidates + (sort (beancount-collect regexp 1) #'string<))) + (complete-with-action action candidates string pred)))) + (list (match-beginning 1) (match-end 1) completion-table)))))))) + +(defun beancount-collect (regexp n) + "Return an unique list of REGEXP group N in the current buffer." + (let ((pos (point))) + (save-excursion + (save-match-data + (let ((hash (make-hash-table :test 'equal))) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + ;; Ignore matches around `pos' (the point position when + ;; entering this funcyion) since that's presumably what + ;; we're currently trying to complete. + (unless (<= (match-beginning 0) pos (match-end 0)) + (puthash (match-string-no-properties n) nil hash))) + (hash-table-keys hash)))))) + +(defun beancount-account-completion-table (string pred action) + (if (eq action 'metadata) '(metadata (category . beancount-account)) + (if (null beancount-accounts) + (setq beancount-accounts + (sort (beancount-collect beancount-account-regexp 0) #'string<))) + (complete-with-action action beancount-accounts string pred))) + +;; Default to substring completion for beancount accounts. +(defconst beancount--completion-overrides + '(beancount-account (styles basic partial-completion substring))) +(add-to-list 'completion-category-defaults beancount--completion-overrides) + +(defun beancount-number-alignment-column () + "Return the column to which postings amounts should be aligned to. +Returns `beancount-number-alignment-column' unless it is 0. In +that case, scan the buffer to determine the minimum column that +will allow to align all numbers." + (if (> beancount-number-alignment-column 0) + beancount-number-alignment-column + (save-excursion + (save-match-data + (let ((account-width 0) + (number-width 0)) + (goto-char (point-min)) + (while (re-search-forward beancount-posting-regexp nil t) + (if (match-string 2) + (let ((accw (- (match-end 1) (line-beginning-position))) + (numw (- (match-end 3) (match-beginning 3)))) + (setq account-width (max account-width accw) + number-width (max number-width numw))))) + (+ account-width 2 number-width)))))) + +(defun beancount-compute-indentation () + "Return the column to which the current line should be indented." + (save-excursion + (beginning-of-line) + (cond + ;; Only timestamped directives start with a digit. + ((looking-at-p "[0-9]") 0) + ;; Otherwise look at the previous line. + ((and (= (forward-line -1) 0) + (or (looking-at-p "[ \t].+") + (looking-at-p beancount-timestamped-directive-regexp) + (looking-at-p beancount-transaction-regexp))) + beancount-transaction-indent) + ;; Default. + (t 0)))) + +(defun beancount-align-number (target-column) + (save-excursion + (beginning-of-line) + ;; Check if the current line is a posting with a number to align. + (when (and (looking-at beancount-posting-regexp) + (match-string 2)) + (let* ((account-end-column (- (match-end 1) (line-beginning-position))) + (number-width (- (match-end 3) (match-beginning 3))) + (account-end (match-end 1)) + (number-beginning (match-beginning 3)) + (spaces (max 2 (- target-column account-end-column number-width)))) + (unless (eq spaces (- number-beginning account-end)) + (goto-char account-end) + (delete-region account-end number-beginning) + (insert (make-string spaces ? ))))))) + +(defun beancount-indent-line () + (let ((indent (beancount-compute-indentation)) + (savep (> (current-column) (current-indentation)))) + (unless (eq indent (current-indentation)) + (if savep (save-excursion (indent-line-to indent)) + (indent-line-to indent))) + (unless (eq this-command 'beancount-tab-dwim) + (beancount-align-number (beancount-number-alignment-column))))) + +(defun beancount-indent-region (start end) + "Indent a region automagically. START and END specify the region to indent." + (let ((deactivate-mark nil) + (beancount-number-alignment-column (beancount-number-alignment-column))) + (save-excursion + (setq end (copy-marker end)) + (goto-char start) + (or (bolp) (forward-line 1)) + (while (< (point) end) + (unless (looking-at-p "\\s-*$") + (beancount-indent-line)) + (forward-line 1)) + (move-marker end nil)))) + +(defun beancount-indent-transaction (&optional _justify _region) + "Indent Beancount transaction at point." + (interactive) + (save-excursion + (let ((bounds (beancount-find-transaction-extents (point)))) + (beancount-indent-region (car bounds) (cadr bounds))))) + +(defun beancount-transaction-clear (&optional arg) + "Clear transaction at point. With a prefix argument set the +transaction as pending." + (interactive "P") + (save-excursion + (save-match-data + (let ((flag (if arg "!" "*"))) + (beancount-goto-transaction-begin) + (if (looking-at beancount-transaction-regexp) + (replace-match flag t t nil 2)))))) + +(defun beancount-insert-account (account-name) + "Insert one of the valid account names in this file. +Uses ido niceness according to `beancount-use-ido'." + (interactive + (list + (if beancount-use-ido + ;; `ido-completing-read' does not understand functional + ;; completion tables thus directly build a list of the + ;; accounts in the buffer + (let ((beancount-accounts + (sort (beancount-collect beancount-account-regexp 0) #'string<))) + (ido-completing-read "Account: " beancount-accounts + nil nil (thing-at-point 'word))) + (completing-read "Account: " #'beancount-account-completion-table + nil t (thing-at-point 'word))))) + (let ((bounds (bounds-of-thing-at-point 'word))) + (when bounds + (delete-region (car bounds) (cdr bounds)))) + (insert account-name)) + +(defmacro beancount-for-line-in-region (begin end &rest exprs) + "Iterate over each line in region until an empty line is encountered." + `(save-excursion + (let ((end-marker (copy-marker ,end))) + (goto-char ,begin) + (beginning-of-line) + (while (and (not (eobp)) (< (point) end-marker)) + (beginning-of-line) + (progn ,@exprs) + (forward-line 1) + )))) + +(defun beancount-align-numbers (begin end &optional requested-currency-column) + "Align all numbers in the given region. CURRENCY-COLUMN is the character +at which to align the beginning of the amount's currency. If not specified, use +the smallest columns that will align all the numbers. With a prefix argument, +align with the fill-column." + (interactive "r") + + ;; With a prefix argument, align with the fill-column. + (when current-prefix-arg + (setq requested-currency-column fill-column)) + + ;; Loop once in the region to find the length of the longest string before the + ;; number. + (let (prefix-widths + number-widths + (number-padding " ")) + (beancount-for-line-in-region + begin end + (let ((line (thing-at-point 'line))) + (when (string-match (concat "\\(.*?\\)" + "[ \t]+" + "\\(" beancount-number-regexp "\\)" + "[ \t]+" + beancount-currency-regexp) + line) + (push (length (match-string 1 line)) prefix-widths) + (push (length (match-string 2 line)) number-widths) + ))) + + (when prefix-widths + ;; Loop again to make the adjustments to the numbers. + (let* ((number-width (apply 'max number-widths)) + (number-format (format "%%%ss" number-width)) + ;; Compute rightmost column of prefix. + (max-prefix-width (apply 'max prefix-widths)) + (max-prefix-width + (if requested-currency-column + (max (- requested-currency-column (length number-padding) number-width 1) + max-prefix-width) + max-prefix-width)) + (prefix-format (format "%%-%ss" max-prefix-width)) + ) + + (beancount-for-line-in-region + begin end + (let ((line (thing-at-point 'line))) + (when (string-match (concat "^\\([^\"]*?\\)" + "[ \t]+" + "\\(" beancount-number-regexp "\\)" + "[ \t]+" + "\\(.*\\)$") + line) + (delete-region (line-beginning-position) (line-end-position)) + (let* ((prefix (match-string 1 line)) + (number (match-string 2 line)) + (rest (match-string 3 line)) ) + (insert (format prefix-format prefix)) + (insert number-padding) + (insert (format number-format number)) + (insert " ") + (insert rest))))))))) + +(defun beancount-align-to-previous-number () + "Align postings under the point's paragraph. +This function looks for a posting in the previous transaction to +determine the column at which to align the transaction, or otherwise +the fill column, and align all the postings of this transaction to +this column." + (interactive) + (let* ((begin (save-excursion + (beancount-beginning-of-directive) + (point))) + (end (save-excursion + (goto-char begin) + (forward-paragraph 1) + (point))) + (currency-column (or (beancount-find-previous-alignment-column) + fill-column))) + (beancount-align-numbers begin end currency-column))) + + +(defun beancount-beginning-of-directive () + "Move point to the beginning of the enclosed or preceding directive." + (beginning-of-line) + (while (and (> (point) (point-min)) + (not (looking-at + "[0-9][0-9][0-9][0-9][\-/][0-9][0-9][\-/][0-9][0-9]"))) + (forward-line -1))) + + +(defun beancount-find-previous-alignment-column () + "Find the preceding column to align amounts with. +This is used to align transactions at the same column as that of +the previous transaction in the file. This function merely finds +what that column is and returns it (an integer)." + ;; Go hunting for the last column with a suitable posting. + (let (column) + (save-excursion + ;; Go to the beginning of the enclosing directive. + (beancount-beginning-of-directive) + (forward-line -1) + + ;; Find the last posting with an amount and a currency on it. + (let ((posting-regexp (concat + "\\s-+" + beancount-account-regexp "\\s-+" + beancount-number-regexp "\\s-+" + "\\(" beancount-currency-regexp "\\)")) + (balance-regexp (concat + beancount-date-regexp "\\s-+" + "balance" "\\s-+" + beancount-account-regexp "\\s-+" + beancount-number-regexp "\\s-+" + "\\(" beancount-currency-regexp "\\)"))) + (while (and (> (point) (point-min)) + (not (or (looking-at posting-regexp) + (looking-at balance-regexp)))) + (forward-line -1)) + (when (or (looking-at posting-regexp) + (looking-at balance-regexp)) + (setq column (- (match-beginning 1) (point)))) + )) + column)) + +(defun beancount--account-currency (account) + ;; Build a regexp that matches an open directive that specifies a + ;; single account currencydaaee. The currency is match group 1. + (let ((re (concat "^" beancount-date-regexp " +open" + "\\s-+" (regexp-quote account) + "\\s-+\\(" beancount-currency-regexp "\\)\\s-+"))) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward re nil t) + ;; The account has declared a single currency, so we can fill it in. + (match-string-no-properties 1))))) + +(defun beancount--electric-currency () + (when (and beancount-electric-currency (eq last-command-event ?\n)) + (save-excursion + (forward-line -1) + (when (and (beancount-inside-transaction-p) + (looking-at (concat "\\s-+\\(" beancount-account-regexp "\\)" + "\\s-+\\(" beancount-number-regexp "\\)\\s-*$"))) + ;; Last line is a posting without currency. + (let* ((account (match-string 1)) + (pos (match-end 0)) + (currency (beancount--account-currency account))) + (when currency + (save-excursion + (goto-char pos) + (insert " " currency)))))))) + +(defun beancount-insert-date () + "Start a new timestamped directive." + (interactive) + (unless (bolp) (newline)) + (insert (format-time-string "%Y-%m-%d") " ")) + +(defvar beancount-install-dir nil + "Directory in which Beancount's source is located. +Only useful if you have not installed Beancount properly in your PATH.") + +(defvar beancount-check-program "bean-check" + "Program to run to run just the parser and validator on an + input file.") + +(defvar compilation-read-command) + +(defun beancount--run (prog &rest args) + (let ((process-environment + (if beancount-install-dir + `(,(concat "PYTHONPATH=" beancount-install-dir) + ,(concat "PATH=" + (expand-file-name "bin" beancount-install-dir) + ":" + (getenv "PATH")) + ,@process-environment) + process-environment)) + (compile-command (mapconcat (lambda (arg) + (if (stringp arg) + (shell-quote-argument arg) "")) + (cons prog args) + " "))) + (call-interactively 'compile))) + +(defun beancount-check () + "Run `beancount-check-program'." + (interactive) + (let ((compilation-read-command nil)) + (beancount--run beancount-check-program + (file-relative-name buffer-file-name)))) + +(defvar beancount-query-program "bean-query" + "Program to run to run just the parser and validator on an + input file.") + +(defun beancount-query () + "Run bean-query." + (interactive) + ;; Don't let-bind compilation-read-command this time, since the default + ;; command is incomplete. + (beancount--run beancount-query-program + (file-relative-name buffer-file-name) t)) + +(defvar beancount-doctor-program "bean-doctor" + "Program to run the doctor commands.") + +(defun beancount-context () + "Get the \"context\" from `beancount-doctor-program'." + (interactive) + (let ((compilation-read-command nil)) + (beancount--run beancount-doctor-program "context" + (file-relative-name buffer-file-name) + (number-to-string (line-number-at-pos))))) + + +(defun beancount-linked () + "Get the \"linked\" info from `beancount-doctor-program'." + (interactive) + (let ((compilation-read-command nil)) + (beancount--run beancount-doctor-program "linked" + (file-relative-name buffer-file-name) + (number-to-string (line-number-at-pos))))) + +(defvar beancount-price-program "bean-price" + "Program to run the price fetching commands.") + +(defun beancount-insert-prices () + "Run bean-price on the current file and insert the output inline." + (interactive) + (call-process beancount-price-program nil t nil + (file-relative-name buffer-file-name))) + +;;; Transaction highligh + +(defvar beancount-highlight-overlay (list)) +(make-variable-buffer-local 'beancount-highlight-overlay) + +(defun beancount-highlight-overlay-make () + (let ((overlay (make-overlay 1 1))) + (overlay-put overlay 'face 'beancount-highlight) + (overlay-put overlay 'priority '(nil . 99)) + overlay)) + +(defun beancount-highlight-transaction-at-point () + "Move the highlight overlay to the current transaction." + (when beancount-highlight-transaction-at-point + (unless beancount-highlight-overlay + (setq beancount-highlight-overlay (beancount-highlight-overlay-make))) + (let* ((bounds (beancount-find-transaction-extents (point))) + (begin (car bounds)) + (end (cadr bounds))) + (if (> (- end begin) 0) + (move-overlay beancount-highlight-overlay begin end) + (move-overlay beancount-highlight-overlay 1 1))))) + +;;; Outline minor mode support. + +(defun beancount-outline-cycle (&optional arg) + "Implement visibility cycling a la `org-mode'. +The behavior of this command is determined by the first matching +condition among the following: + 1. When point is at the beginning of the buffer, or when called + with a `\\[universal-argument]' universal argument, rotate the entire buffer + through 3 states: + - OVERVIEW: Show only top-level headlines. + - CONTENTS: Show all headlines of all levels, but no body text. + - SHOW ALL: Show everything. + 2. When point is at the beginning of a headline, rotate the + subtree starting at this line through 3 different states: + - FOLDED: Only the main headline is shown. + - CHILDREN: The main headline and its direct children are shown. + From this state, you can move to one of the children + and zoom in further. + - SUBTREE: Show the entire subtree, including body text." + (interactive "P") + (setq deactivate-mark t) + (cond + ;; Beginning of buffer or called with C-u: Global cycling + ((or (equal arg '(4)) + (and (bobp) + ;; org-mode style behaviour - only cycle if not on a heading + (not (outline-on-heading-p)))) + (beancount-cycle-buffer)) + + ;; At a heading: rotate between three different views + ((save-excursion (beginning-of-line 1) (looking-at outline-regexp)) + (outline-back-to-heading) + (let ((goal-column 0) eoh eol eos) + ;; First, some boundaries + (save-excursion + (save-excursion (beancount-next-line) (setq eol (point))) + (outline-end-of-heading) (setq eoh (point)) + (outline-end-of-subtree) (setq eos (point))) + ;; Find out what to do next and set `this-command' + (cond + ((= eos eoh) + ;; Nothing is hidden behind this heading + (beancount-message "EMPTY ENTRY")) + ((>= eol eos) + ;; Entire subtree is hidden in one line: open it + (outline-show-entry) + (outline-show-children) + (beancount-message "CHILDREN") + (setq + this-command 'beancount-cycle-children)) + ((eq last-command 'beancount-cycle-children) + ;; We just showed the children, now show everything. + (outline-show-subtree) + (beancount-message "SUBTREE")) + (t + ;; Default action: hide the subtree. + (outline-hide-subtree) + (beancount-message "FOLDED"))))))) + +(defvar beancount-current-buffer-visibility-state nil + "Current visibility state of buffer.") +(make-variable-buffer-local 'beancount-current-buffer-visibility-state) + +(defvar beancount-current-buffer-visibility-state) + +(defun beancount-cycle-buffer (&optional arg) + "Rotate the visibility state of the buffer through 3 states: + - OVERVIEW: Show only top-level headlines. + - CONTENTS: Show all headlines of all levels, but no body text. + - SHOW ALL: Show everything. +With a numeric prefix ARG, show all headlines up to that level." + (interactive "P") + (save-excursion + (cond + ((integerp arg) + (outline-show-all) + (outline-hide-sublevels arg)) + ((eq last-command 'beancount-cycle-overview) + ;; We just created the overview - now do table of contents + ;; This can be slow in very large buffers, so indicate action + ;; Visit all headings and show their offspring + (goto-char (point-max)) + (while (not (bobp)) + (condition-case nil + (progn + (outline-previous-visible-heading 1) + (outline-show-branches)) + (error (goto-char (point-min))))) + (beancount-message "CONTENTS") + (setq this-command 'beancount-cycle-toc + beancount-current-buffer-visibility-state 'contents)) + ((eq last-command 'beancount-cycle-toc) + ;; We just showed the table of contents - now show everything + (outline-show-all) + (beancount-message "SHOW ALL") + (setq this-command 'beancount-cycle-showall + beancount-current-buffer-visibility-state 'all)) + (t + ;; Default action: go to overview + (let ((toplevel + (cond + (current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + ((save-excursion + (beginning-of-line) + (looking-at outline-regexp)) + (max 1 (funcall outline-level))) + (t 1)))) + (outline-hide-sublevels toplevel)) + (beancount-message "OVERVIEW") + (setq this-command 'beancount-cycle-overview + beancount-current-buffer-visibility-state 'overview))))) + +(defun beancount-message (msg) + "Display MSG, but avoid logging it in the *Messages* buffer." + (let ((message-log-max nil)) + (message msg))) + +(defun beancount-next-line () + "Forward line, but mover over invisible line ends. +Essentially a much simplified version of `next-line'." + (interactive) + (beginning-of-line 2) + (while (and (not (eobp)) + (get-char-property (1- (point)) 'invisible)) + (beginning-of-line 2))) + +(provide 'beancount) + +#+end_src + +**** My beancount functions + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(defun beancount-fixme-replace () + "Search for next FIXME in ledger and insert account." + (interactive) + (if (search-forward "FIXME") + (progn + (replace-match "" nil nil) + (call-interactively 'beancount-insert-account)))) + +#+end_src + +**** Keybinds + +Define Evil keybinds for editing Beancount ledger files. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Custom keybinds for the entry view +(evil-define-key 'normal beancount-mode-map + (kbd "C-c C-n") 'beancount-fixme-replace) + +#+end_src + +*** HTTP + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package know-your-http-well + :defer t) + +#+end_src + +*** Python + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package elpy + :ensure t + :init + (elpy-enable)) + +(setq python-shell-interpreter "jupyter" + python-shell-interpreter-args "console --simple-prompt" + python-shell-prompt-detect-failure-warning nil) +(add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter") + +#+end_src + +*** Clojure + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package cider) + +#+end_src + +** Productivity +*** Syntax checking with Flycheck + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package flycheck + :defer t + :hook (lsp-mode . flycheck-mode)) + +#+end_src + +*** Snippets + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package yasnippet + :hook (prog-mode . yas-minor-mode) + :config + (yas-reload-all)) + +#+end_src + +*** Smart Parens + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package smartparens + :hook (prog-mode . smartparens-mode)) + +#+end_src + +*** Rainbow Delimiters + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + + (use-package rainbow-delimiters + :hook (prog-mode . rainbow-delimiters-mode)) + +#+end_src + +*** Rainbow Mode + +Sets the background of HTML color strings in buffers to be the color mentioned. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(use-package rainbow-mode + :defer t + :hook (org-mode + emacs-lisp-mode + js2-mode)) + +#+end_src + +* Project Specific +** Website Management +*** tstarr.us + +Functions and keybinds for publishing my org-based personal website + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +;; Example from the_dev_aspect's config + +; (defun dw/generate-site () +; (interactive) +; (start-process-shell-command "emacs" nil "emacs --batch -l ~/Projects/Writing/Blog/publish.el --funcall dw/publish")) + +;; Add keybinds here to + +#+end_src + +* Applications +** Elfeed +*** General + +Initial setup for elfeed. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-2) ".emacs.d/init.el" "no") + +; Use elfeed +(use-package elfeed + :commands elfeed) + +; Set default filter +(setq-default elfeed-search-filter "@1-week-ago") + +; Use elfeed-org +(use-package elfeed-org) +(elfeed-org) + +; Set default org file for feed +(setq rmh-elfeed-org-files (list "~/devel/elisp/scratch/scratch-config/elfeed.org")) + +; Update and open elfeed at the same time +(defun elfeed-open-and-update () + (interactive) + (elfeed-update) + (elfeed)) + +#+end_src + +*** Keybinds + +Define elfeed only keybinds and fixes using Evil. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-2) ".emacs.d/init.el" "no") + +; Custom keybinds for the entry view +(evil-define-key 'normal elfeed-show-mode-map + "v" 'elfeed-download-yt-video + "a" 'elfeed-download-yt-audio) + +; Custom keybinds for the feed view +(evil-define-key 'normal elfeed-search-mode-map + "r" 'elfeed-update) + +; Fix keybinds for evil modes in elfeed +(add-to-list 'evil-emacs-state-modes 'elfeed-search-mode) +(add-to-list 'evil-emacs-state-modes 'elfeed-show-mode) + +#+end_src + +*** Download youtube video/audio + +Use youtube-dl to download audio/video from elfeed entries. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-2) ".emacs.d/init.el" "no") + +; Set executable path for the most illegal youtube-dl ;) +(setq youtube-dl-path "/usr/bin/youtube-dl") +; Set video/audio storage path +(setq youtube-dl-video-dir "~/media/video/youtube/") +(setq youtube-dl-audio-dir "~/media/audio/youtube/") + +; Function to download youtube video while suppressing the pop-up +(defun elfeed-download-yt-video () + "Download a video using youtube-dl." + (interactive) + (message "Youtube video download started...") + (call-process-shell-command (format "%s -o \"%s%s\" -f bestvideo+bestaudio --add-metadata %s > /dev/null 2>&1" + youtube-dl-path + youtube-dl-video-dir + "%(title)s.%(ext)s" + (elfeed-entry-link elfeed-show-entry)) nil 0)) + +; Function to download youtube audio while suppressing the pop-up +(defun elfeed-download-yt-audio () + "Download a video using youtube-dl." + (interactive) + (message "Youtube audio download started...") + (call-process-shell-command (format "%s -o \"%s%s\" -f bestaudio --add-metadata --extract-audio --audio-format mp3 %s > /dev/null 2>&1" + youtube-dl-path + youtube-dl-audio-dir + "%(title)s.%(ext)s" + (elfeed-entry-link elfeed-show-entry)) nil 0)) + +; Function to download youtube video without suppressing the pop-up for debug purposes +(defun elfeed-download-yt-video-debug () + "Download a video using youtube-dl." + (interactive) + (async-shell-command (format "%s -o \"%s%s\" -f bestvideo+bestaudio --add-metadata %s" + youtube-dl-path + youtube-dl-video-dir + "%(title)s.%(ext)s" + (elfeed-entry-link elfeed-show-entry)))) + +; Function to download youtube audio without suppressing the pop-up for debug purposes +(defun elfeed-download-yt-audio-debug () + "Download a video using youtube-dl." + (interactive) + (async-shell-command (format "%s -o \"%s%s\" -f bestaudio --add-metadata --extract-audio --audio-format mp3 %s" + youtube-dl-path + youtube-dl-audio-dir + "%(title)s.%(ext)s" + (elfeed-entry-link elfeed-show-entry)))) + +; Commented this out for now I don't think I need this +; Add `youtube` tag to all videos +;(add-hook 'elfeed-new-entry-hook +; (elfeed-make-tagger :feed-url "youtube\\.com" +; :add '(video youtube))) + +#+end_src + +*** Format elfeed entries for Youtube + +Adds a nice formatted youtube entry. Stolen from https://github.com/xFA25E. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-2) ".emacs.d/init.el" "no") + +(require 'elfeed-db) +(require 'xml-query) + +(defgroup elfeed-youtube-parser nil + "Parse video thumbnails and descriptions from youtube feeds." + :group 'multimedia) + +(defcustom elfeed-youtube-parser-tag 'youtube + "The tag to use when looking for youtube entry in a feed." + :type 'symbol + :group 'elfeed-youtube-parser) + +(defun elfeed-youtube-parser--make-html (thumbnail description) + "Transforms `THUMBNAIL' link and `DESCRIPTION' string into html string." + (format "
%s
" + thumbnail description)) + +;;;###autoload +(defun elfeed-youtube-parser-parse-youtube (_type xml-entry db-entry) + "Parse elfeed youtube entry. +If `XML-ENTRY' contains `YOUTUBE' tag, read `DB-ENTRY' and search +for thumbnail and video description. Then associate new +information with future `DB-ENTRY'. `TYPE' is ignored." + + (when (member elfeed-youtube-parser-tag (elfeed-entry-tags db-entry)) + (let ((thumbnail (xml-query* (group thumbnail :url) xml-entry)) + (description (xml-query* (group description *) xml-entry))) + (setf (elfeed-entry-content-type db-entry) 'html) + (setf (elfeed-entry-content db-entry) + (elfeed-youtube-parser--make-html thumbnail description))))) + +(provide 'elfeed-youtube-parser) +;;; elfeed-youtube-parser.el ends here + +(add-hook 'elfeed-new-entry-parse-hook 'elfeed-youtube-parser-parse-youtube) + +#+end_src + +** Mu4e + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-2) ".emacs.d/init.el" "no") + +(use-package mu4e + :defer t + :config + ;; use mu4e for e-mail in emacs + (setq mail-user-agent 'mu4e-user-agent) + + ;; default + (setq mu4e-root-maildir "/home/tstarr/media/email/starrtyler88") + + (setq mu4e-drafts-folder "/[Gmail].Drafts") + (setq mu4e-sent-folder "/[Gmail].Sent Mail") + (setq mu4e-trash-folder "/[Gmail].Trash") + + ;; don't save message to Sent Messages, Gmail/IMAP takes care of this + (setq mu4e-sent-messages-behavior 'delete) + + ;; (See the documentation for `mu4e-sent-messages-behavior' if you have + ;; additional non-Gmail addresses and want assign them different + ;; behavior.) + + ;; setup some handy shortcuts + ;; you can quickly switch to your Inbox -- press ``ji'' + ;; then, when you want archive some messages, move them to + ;; the 'All Mail' folder by pressing ``ma''. + + (setq mu4e-maildir-shortcuts + '( (:maildir "/INBOX" :key ?i) + (:maildir "/[Gmail].Sent Mail" :key ?s) + (:maildir "/[Gmail].Trash" :key ?t) + (:maildir "/[Gmail].All Mail" :key ?a))) + + ;; allow for updating mail using 'U' in the main view: + (setq mu4e-get-mail-command "offlineimap") + + ;; something about ourselves + (setq + user-mail-address "starrtyler88@gmail.com" + user-full-name "Tyler Starr" + mu4e-compose-signature + (concat + "Tyler Starr\n" + "http://tstarr.us\n")) + + ;; sending mail -- replace USERNAME with your gmail username + ;; also, make sure the gnutls command line utils are installed + ;; package 'gnutls-bin' in Debian/Ubuntu + + (use-package smtpmail) + (setq message-send-mail-function 'smtpmail-send-it + starttls-use-gnutls t + smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)) + smtpmail-auth-credentials + '(("smtp.gmail.com" 587 "starrtyler88@gmail.com" nil)) + smtpmail-default-smtp-server "smtp.gmail.com" + smtpmail-smtp-server "smtp.gmail.com" + smtpmail-smtp-service 587) + + ;; alternatively, for emacs-24 you can use: + ;;(setq message-send-mail-function 'smtpmail-send-it + ;; smtpmail-stream-type 'starttls + ;; smtpmail-default-smtp-server "smtp.gmail.com" + ;; smtpmail-smtp-server "smtp.gmail.com" + ;; smtpmail-smtp-service 587) + + ;; don't keep message buffers around + (setq message-kill-buffer-on-exit t)) + +#+end_src + +* Keybinds +** General + +Define base bindings that aren't SPC lead. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Make ESC cancel all mini-buffer menus +(global-set-key (kbd "") 'keyboard-escape-quit) + +#+end_src + +** Main + +Define SPC lead bindings that don't have submenus. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Make ESC cancel all mini-buffer menus +(global-set-key (kbd "") 'keyboard-escape-quit) + +; Commands without sub-menus +(dw/leader-key-def + "RET" '(bookmark-jump :which-key "Jump to Bookmark") + "SPC" '(project-find-file :which-key "Find file in project") + "'" '(ivy-resume :which-key "Resume last search") + "." '(find-file :which-key "Find file") + ":" '(counsel-M-x :which-key "M-x") + ";" '(eval-expression :which-key "Eval expression") + "<" '(switch-to-buffer :which-key "Switch buffer") + "`" '(switch-to-prev-buffer :which-key "Switch to last buffer") + "C" '(org-capture :which-key "Org Capture") + "a" '(ace-window :which-key "Select window")) + ;"TAB" '(:ignore t :which-key "workspace") + ;"b" '(:ignore t :which-key "buffer") + ;"c" '(:ignore t :which-key "code") + ;"f" '(:ignore t :which-key "file") + ;"h" '(:ignore t :which-key "help") + ;"i" '(:ignore t :which-key "insert") + ;"n" '(:ignore t :which-key "notes") + ;"o" '(:ignore t :which-key "open") + ;"q" '(:ignore t :which-key "quit/session") + ;"s" '(:ignore t :which-key "search") + ;"t" '(:ignore t :which-key "toggle")) + +#+end_src + +** Open + +Define SPC lead bindings that open Emacs programs. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Menu for opening applications +(dw/leader-key-def + "o" '(:ignore t :which-key "open") + "om" '(mu4e :which-key "Mu4e") + "oe" '(elfeed :which-key "Elfeed") + "oa" '(org-agenda :which-key "Agenda")) + +#+end_src + +** Window + +Define SPC lead bindings that manipulate Emacs windows. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") +; Open evil-window keybind menus +(dw/leader-key-def + "w" '(evil-window-map :which-key "window")) + +#+end_src + +** Project + +Define SPC lead bindings for projectile. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Commands for projectile projects +(dw/leader-key-def + "p" '(:ignore t :which-key "project") + "p." '(projectile-find-file :which-key "Find file in project") + "p>" '(projectile-find-file-in-known-projects :which-key "Find file in know projects") + "p!" '(projectile-run-shell-command-in-root :which-key "Run cmd in project root") + "pa" '(projectile-add-known-project :which-key "Add new project") + "pb" '(projectile-switch-to-buffer :which-key "Switch to project buffer") + "pc" '(projectile-compile-project :which-key "Compile in project") + "pC" '(projectile-repeat-last-command :which-key "Repeat last command") + "pd" '(projectile-remove-known-project :which-key "Remove known project") + "pe" '(projectile-edit-dir-locals :which-key "Edit project .dir-locals") + "pg" '(projectile-configure-project :which-key "Configure project") + "pi" '(projectile-invalidate-cache :which-key "Invalidate project cache") + "pk" '(projectile-kill-buffers :which-key "Kill project buffers") + "po" '(projectile-find-other-file :which-key "Find other file") + "pp" '(projectile-switch-project :which-key "Switch project") + "pr" '(projectile-recentf :which-key "Find recent project files") + "pR" '(projectile-run-project :which-key "Run project") + "ps" '(projectile-save-project-buffers :which-key "Save project files") + "pt" '(magit-todos-list :which-key "List project todos") + "pT" '(projectile-test-project :which-key "Test project")) + +#+end_src + +** Git + +Define SPC lead bindings for magit. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Commands for magit +(dw/leader-key-def + "g" '(:ignore t :which-key "git") + "gs" '(magit-status :which-key "Status") + "gd" '(magit-diff-unstaged :which-key "Diff Unstaged") + "gc" '(magit-branch-or-checkout :which-key "Branch or Checkout") + "gb" '(magit-branch :which-key "Branch") + "gP" '(magit-push-current :which-key "Push Current") + "gp" '(magit-pull-branch :which-key "Pull Branch") + "gf" '(magit-fetch :which-key "Fetch") + "gF" '(magit-fetch-all :which-key "Fetch All") + "gr" '(magit-rebase :which-key "Rebase") + "gl" '(:ignore t :which-key "log") + "glc" '(magit-log-current :which-key "Log Current") + "glf" '(magit-log-buffer-file :which-key "Log Buffer File")) + +#+end_src + +** Util + +Define SPC lead bindings for various Emacs and general system utilites. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Keybinds +(dw/leader-key-def + "u" '(:ignore t :which-key "util")) + +#+end_src + +** Help + +Define SPC lead bindings for finding documentation. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Help +(dw/leader-key-def + "h" '(:ignore t :which-key "help") + "hw" '(:ignore t :which-key "which-key") + "hwm" '(which-key-show-major-mode :which-key "Major binds")) + +#+end_src + +** Buffer + +Define SPC lead bindings that manipulate Emacs buffers and bookmarks. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +; Buffer +(dw/leader-key-def + "b" '(:ignore t :which-key "buffer") + "b[" '(previous-buffer :which-key "Previous buffer") + "b]" '(next-buffer :which-key "Next buffer") + "bb" '(persp-switch-to-buffer :which-key "Switch workspace buffer") + "bB" '(switch-to-buffer :which-key "Switch buffer") + "bb" '(switch-to-buffer :which-key "Switch buffer") + "bd" '(kill-current-buffer :which-key "Kill buffer") + "bi" '(ibuffer :which-key "ibuffer") + "bk" '(kill-current-buffer :which-key "Kill buffer") + "bl" '(evil-switch-to-windows-last-buffer :which-key "Switch to last buffer") + "bm" '(bookmark-set :which-key "Set bookmark") + "bM" '(bookmark-delete :which-key "Delete bookmark") + "bn" '(next-buffer :which-key "Next buffer") + "bN" '(evil-buffer-new :which-key "New empty buffer") + "bp" '(previous-buffer :which-key "Previous buffer") + "br" '(revert-buffer :which-key "Revert buffer") + "bs" '(basic-save-buffer :which-key "Save buffer") + "bS" '(evil-write-all :which-key "Save all buffers") + "bw" '(burly-bookmark-windows :which-key "Bookmark windows") + "bz" '(bury-buffer :which-key "Bury buffer")) + +#+end_src + +** Dired + +Define SPC lead bindings for dired. + +#+begin_src emacs-lisp :tangle (if (member this-system system-category-1) ".emacs.d/init.el" "no") + +(dw/leader-key-def + "d" '(:ignore t :which-key "dired") + "dd" '(dired :which-key "here") + "dh" `(,(dw/dired-link "~") :which-key "home") + "dn" `(,(dw/dired-link "~/documents/org") :which-key "org") + "do" `(,(dw/dired-link "~/downloads") :which-key "downloads") + "df" `(,(dw/dired-link "~/media/pictures") :which-key "pictures") + "dv" `(,(dw/dired-link "~/netdrive/media/video") :which-key "video") + "da" `(,(dw/dired-link "~/netdrive/media/audio") :which-key "audio") + "dx" `(,(dw/dired-link "~/.xmonad") :which-key "xmonad") + "dc" `(,(dw/dired-link "~/.config") :which-key "configs") + "de" `(,(dw/dired-link "~/devel/elisp/scratch") :which-key "emacs") + "dp" '(:ignore t :which-key "play") + "dpf" '(mpv :which-key "file") + "dpd" '(mpv-dir :which-key "directory") + "dpp" '(mpv-playlist :which-key "playlist")) + +#+end_src diff --git a/.config/scratch/readme.md b/.config/scratch/readme.md new file mode 100644 index 00000000..bb907ecd --- /dev/null +++ b/.config/scratch/readme.md @@ -0,0 +1,4 @@ +# Scatch +> When Doom can't scratch the itch. + +My personal config of [Emacs](https://www.gnu.org/software/emacs/). My config is written in a Literate style so checkout [Scratch.org](https://github.com/starr-dusT/scratch/Scratch.org). diff --git a/.config/xmobar/xmobarrc b/.config/xmobar/xmobarrc index dd59b6ad..a69b0016 100755 --- a/.config/xmobar/xmobarrc +++ b/.config/xmobar/xmobarrc @@ -1,7 +1,7 @@ Config { font = "xft:Mononoki Nerd Font:pixelsize=12:antialias=true:hinting=true" , bgColor = "#282828" , fgColor = "#ebdbb2" - , position = Static {xpos = 0, ypos = 0, width = 2560, height = 20} + , position = Static {xpos = 2560, ypos = 0, width = 2560, height = 20} , iconRoot = "X" , allDesktops = True , commands = [ Run Cpu ["-t", " %","-H", "2"] 10 diff --git a/.doom.d/config.el b/.doom.d/config.el index a23f8ba0..5534a8e1 100644 --- a/.doom.d/config.el +++ b/.doom.d/config.el @@ -307,3 +307,95 @@ (beancount-mode) (define-key beancount-mode-map (kbd "C-c F") #'ts/next-fixme) (define-key beancount-mode-map (kbd "C-c f") #'ts/next-fixme-replace) + +(setq ts/exwm-enabled t) + +(defun ts/update-screen-layout () + (interactive) + (let ((layout-script "~/.doom.d/scripts/update-screens")) + (message "Running screen layout script: %s" layout-script) + (start-process-shell-command "xrandr" nil layout-script))) + +(defun ts/set-wallpaper () + (interactive) + (start-process-shell-command + "feh" nil "feh --bg-scale /home/tstarr/media/pictures/wallpapers/random_wallpapers/halfdome-2560x1440.jpg")) + +(defun ts/configure-desktop () + (interactive) + ;;(dw/run-xmodmap) + (ts/update-screen-layout) + (ts/set-wallpaper)) + ;;(run-at-time "2 sec" nil (lambda () (dw/update-wallpapers)))) + + +(when ts/exwm-enabled + (set-frame-parameter (selected-frame) 'alpha '(95 . 95)) + (add-to-list 'default-frame-alist '(alpha . (95 . 95)))) + +(defun ts/on-exwm-init () + (ts/configure-desktop)) + +(when ts/exwm-enabled + ;; Configure the desktop for first load + (add-hook 'exwm-init-hook #'ts/on-exwm-init)) + +;; Enable exwm-randr before exwm-init gets called +(require 'exwm-randr) +(exwm-randr-enable) + +;; Set the default number of workspaces +(setq exwm-workspace-number 5) + +(defun efs/exwm-update-class () + (exwm-workspace-rename-buffer exwm-class-name)) +;; When window "class" updates, use it to set the buffer name +(add-hook 'exwm-update-class-hook #'efs/exwm-update-class) + +;; These keys should always pass through to Emacs +(setq exwm-input-prefix-keys + '(?\C-x + ?\C-u + ?\C-h + ?\M-x + ?\M-` + ?\M-& + ?\M-: + ?\C-w + ?\C-\M-j ;; Buffer list + ?\C-\ )) ;; Ctrl+Space + +;; Ctrl+Q will enable the next key to be sent directly +(define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key) + +;; Set up global key bindings. These always work, no matter the input state! +;; Keep in mind that changing this list after EXWM initializes has no effect. +(setq exwm-input-global-keys + `( + ;; Reset to line-mode (C-c C-k switches to char-mode via exwm-input-release-keyboard) + ([?\s-r] . exwm-reset) + + ;; Move between windows + ([s-left] . windmove-left) + ([s-right] . windmove-right) + ([s-up] . windmove-up) + ([s-down] . windmove-down) + + ;; Launch applications via shell command + ([?\s-o] . (lambda (command) + (interactive (list (read-shell-command "$ "))) + (start-process-shell-command command nil command))) + + ;; Switch workspace + ([?\s-w] . exwm-workspace-switch) + + ;; 's-N': Switch to certain workspace with Super (Win) plus a number key (0 - 9) + ,@(mapcar (lambda (i) + `(,(kbd (format "s-%d" i)) . + (lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) + (number-sequence 0 9)))) + +(setq exwm-randr-workspace-monitor-plist '(2 "HDMI-0" 4 "HDMI-0")) +(exwm-enable) diff --git a/.doom.d/doomed.org b/.doom.d/doomed.org index b71f38ba..4f374db8 100644 --- a/.doom.d/doomed.org +++ b/.doom.d/doomed.org @@ -375,6 +375,10 @@ We start by simply defining the standard headers used by the three files. These (package! org-chef) +(package! simple-httpd) + +(package! exwm) + #+end_src * General Configuration @@ -736,3 +740,99 @@ Following config items will utilizes the awesome [[https://github.com/alphapapa/ (define-key beancount-mode-map (kbd "C-c F") #'ts/next-fixme) (define-key beancount-mode-map (kbd "C-c f") #'ts/next-fixme-replace) #+end_src +* Desktop +#+begin_src emacs-lisp :tangle (if (member this-system all-systems) "config.el" "no") + +(setq ts/exwm-enabled t) + +(defun ts/update-screen-layout () + (interactive) + (let ((layout-script "~/.doom.d/scripts/update-screens")) + (message "Running screen layout script: %s" layout-script) + (start-process-shell-command "xrandr" nil layout-script))) + +(defun ts/set-wallpaper () + (interactive) + (start-process-shell-command + "feh" nil "feh --bg-scale /home/tstarr/media/pictures/wallpapers/random_wallpapers/halfdome-2560x1440.jpg")) + +(defun ts/configure-desktop () + (interactive) + ;;(dw/run-xmodmap) + (ts/update-screen-layout) + (ts/set-wallpaper)) + ;;(run-at-time "2 sec" nil (lambda () (dw/update-wallpapers)))) + + +(when ts/exwm-enabled + (set-frame-parameter (selected-frame) 'alpha '(95 . 95)) + (add-to-list 'default-frame-alist '(alpha . (95 . 95)))) + +(defun ts/on-exwm-init () + (ts/configure-desktop)) + +(when ts/exwm-enabled + ;; Configure the desktop for first load + (add-hook 'exwm-init-hook #'ts/on-exwm-init)) + +;; Enable exwm-randr before exwm-init gets called +(require 'exwm-randr) +(exwm-randr-enable) + +;; Set the default number of workspaces +(setq exwm-workspace-number 5) + +(defun efs/exwm-update-class () + (exwm-workspace-rename-buffer exwm-class-name)) +;; When window "class" updates, use it to set the buffer name +(add-hook 'exwm-update-class-hook #'efs/exwm-update-class) + +;; These keys should always pass through to Emacs +(setq exwm-input-prefix-keys + '(?\C-x + ?\C-u + ?\C-h + ?\M-x + ?\M-` + ?\M-& + ?\M-: + ?\C-w + ?\C-\M-j ;; Buffer list + ?\C-\ )) ;; Ctrl+Space + +;; Ctrl+Q will enable the next key to be sent directly +(define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key) + +;; Set up global key bindings. These always work, no matter the input state! +;; Keep in mind that changing this list after EXWM initializes has no effect. +(setq exwm-input-global-keys + `( + ;; Reset to line-mode (C-c C-k switches to char-mode via exwm-input-release-keyboard) + ([?\s-r] . exwm-reset) + + ;; Move between windows + ([s-left] . windmove-left) + ([s-right] . windmove-right) + ([s-up] . windmove-up) + ([s-down] . windmove-down) + + ;; Launch applications via shell command + ([?\s-o] . (lambda (command) + (interactive (list (read-shell-command "$ "))) + (start-process-shell-command command nil command))) + + ;; Switch workspace + ([?\s-w] . exwm-workspace-switch) + + ;; 's-N': Switch to certain workspace with Super (Win) plus a number key (0 - 9) + ,@(mapcar (lambda (i) + `(,(kbd (format "s-%d" i)) . + (lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) + (number-sequence 0 9)))) + +(setq exwm-randr-workspace-monitor-plist '(2 "HDMI-0" 4 "HDMI-0")) +(exwm-enable) + +#+end_src diff --git a/.doom.d/packages.el b/.doom.d/packages.el index 4014c0ee..c536ae7e 100644 --- a/.doom.d/packages.el +++ b/.doom.d/packages.el @@ -77,3 +77,7 @@ :branch "main")) (package! org-chef) + +(package! simple-httpd) + +(package! exwm) diff --git a/.doom.d/scripts/update-screens b/.doom.d/scripts/update-screens new file mode 100755 index 00000000..5d5540f6 --- /dev/null +++ b/.doom.d/scripts/update-screens @@ -0,0 +1,27 @@ +#!/bin/sh + +# Get current IP address +addr=$(ip -4 addr show $1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}') +# The the relevant number (X) 192.168.X.YYY +num=($(echo "$addr" | tr '.' '\n')) +# Based on number set xrandr +case ${num[2]} in + 2) + # Primary monitor on left + xrandr --output DP-4 --primary \ + --mode 2560x1440 --rate 144 \ + --pos 0x0 --rotate normal \ + --output HDMI-0 \ + --mode 2560x1440 --rate 144 \ + --pos 2560x0 --rotate normal + ;; + 1) + # Primary monitor on right + xrandr --output HDMI-0 \ + --mode 2560x1440 --rate 144 \ + --pos 0x0 --rotate normal \ + --output DP-4 --primary \ + --mode 2560x1440 --rate 144 \ + --pos 2560x0 --rotate normal + ;; +esac diff --git a/.xinitrc b/.xinitrc index 5b3bbc59..7d255890 100644 --- a/.xinitrc +++ b/.xinitrc @@ -1,2 +1,5 @@ -exec dbus-launch xmonad +#exec dbus-launch xmonad #exec qtile start +picom & +exec dbus-launch --exit-with-session emacs -mm --debug-init +#exec stumpwm start diff --git a/.xmonad/xmonad.hs b/.xmonad/xmonad.hs index e3a5a1f8..870a842e 100644 --- a/.xmonad/xmonad.hs +++ b/.xmonad/xmonad.hs @@ -12,6 +12,7 @@ import XMonad.Hooks.ManageHelpers import XMonad.Hooks.WorkspaceHistory import XMonad.Hooks.DynamicLog (dynamicLogWithPP, wrap, xmobarPP, xmobarColor, shorten, PP(..)) import XMonad.Hooks.DynamicProperty + -- Layouts import XMonad.Layout.NoBorders import XMonad.Layout.Spacing @@ -19,6 +20,8 @@ import XMonad.Layout.Tabbed import XMonad.Layout.WindowNavigation import XMonad.Layout.SimpleFloat import XMonad.Layout.HintedTile +import XMonad.Layout.Grid + --Utilities import XMonad.Util.Run (spawnPipe) import XMonad.Util.SpawnOnce @@ -42,9 +45,9 @@ myFocusFollowsMouse = True myModMask :: KeyMask myModMask = mod4Mask -- Define volume keys and commands -lowerVolumeCmd = "pulseaudio-ctl down 2" -raiseVolumeCmd = "pulseaudio-ctl up 2" -muteVolumeCmd = "pulseaudio-ctl mute" +lowerVolumeCmd = "pactl set-sink-volume @DEFAULT_SINK@ -2%" +raiseVolumeCmd = "pactl set-sink-volume @DEFAULT_SINK@ +2%" +muteVolumeCmd = "pactl set-sink-mute @DEFAULT_SINK@ toggle" -- Count windows windowCount :: X (Maybe String) windowCount = gets $ Just . show . length . W.integrate' . W.stack . W.workspace . W.current . windowset @@ -87,7 +90,7 @@ projects = } ] -myLayout = windowNavigation $ spacing 2 $ smartBorders (tiled Tall ||| tiled Wide ||| Full ||| simpleFloat) +myLayout = windowNavigation $ spacing 2 $ smartBorders (tiled Tall ||| tiled Wide ||| Full ||| simpleFloat ||| Grid) where -- default tiling algorithm partitions the screen into two panes --tiled = Tall nmaster delta ratio @@ -160,6 +163,7 @@ myManageHook = composeAll className =? "mpv" --> doRectFloat (W.RationalRect 0.55 0.05 0.4 0.4), className =? "Steam" --> doFullFloat, className =? "Superslicer" --> doFullFloat, + isInProperty "WM_WINDOW_ROLE" "pop-up" --> doRectFloat (W.RationalRect 0.55 0.05 0.4 0.4), namedScratchpadManageHook myScratchPads] -- Set dynamic display modes myEventHook :: Event -> X All