Compare commits

..
4 Commits
Author SHA1 Message Date
jasonhilder 1c4f950e3f Emacs config wip, split to better fit modules. 2026-10-26 08:06:00 +02:00
jasonhilder 2e6bbc77fd Cleanups. 2026-10-26 08:05:45 +02:00
jasonhilder 60c8d863d3 Cleaned up gitignore. 2026-10-26 08:05:31 +02:00
jasonhilder 30c87a36af Steam install script. 2026-10-26 08:05:19 +02:00
10 changed files with 582 additions and 93 deletions
+7 -2
View File
@@ -1,13 +1,18 @@
# emacs
config/emacs/.ignore
config/emacs/var/
config/emacs/elfeed/
config/emacs/eshell/
config/emacs/auto-save-list/
# fish
config/fish/conf.d
config/fish/functions
config/fish/fish_variables
config/fish/generated_completions/
# vim/nvim
config/vim/autoload/
config/vim/pack/
config/vim/.netrwhist
config/nvim/nvim-pack-lock.json
config/awesome/bling
scripts/bin/Obsidian
# other
scripts/bin/ols
+27 -39
View File
@@ -4,9 +4,9 @@
;; C-h works too, except in normal/motion state where it moves
;; to the window on the left.
;;; Code:
;;;; 1. Emac setup, files, packages and paths
(let ((bin (expand-file-name "~/.local/bin")))
(dolist (bin (list (expand-file-name "~/.local/bin")
(expand-file-name "~/.go/bin")))
(add-to-list 'exec-path bin)
(setenv "PATH" (concat bin path-separator (getenv "PATH"))))
@@ -63,16 +63,25 @@
(which-key-mode 1)
(add-to-list 'warning-suppress-types '(files))
(use-package jbeans-theme
:config
(load-theme 'jbeans t))
(use-package doom-themes :ensure t)
(load-theme 'doom-one t)
(doom-themes-set-faces 'user
'(default :background "#131313")
'(fringe :background "#131313")
'(line-number :background "#131313")
'(line-number-current-line :background "#131313")
'(mode-line :background "#1a1a1a")
'(mode-line-inactive :background "#131313"))
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font" :height 120)
(setq-default line-spacing '(4 . 4))
(use-package doom-modeline
:ensure t
:init (doom-modeline-mode 1))
;; set mode line at the top of each window
(setq-default header-line-format mode-line-format mode-line-format nil)
(set-face-attribute 'header-line nil :inherit 'mode-line :background "#131313")
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font" :height 120)
(setq-default line-spacing '(4 . 4))
;; Command launcher.
(keymap-global-set "C-S-p" #'execute-extended-command)
@@ -234,41 +243,20 @@
"." #'dired-omit-mode
(kbd "RET") #'dired-find-file))
;;;; 8. Build
(setq compile-command "./build.sh"
compilation-read-command nil
compilation-ask-about-save nil
compilation-scroll-output t)
;;;; 8. Modules - additional functionality outside of the core init.el
;; Setup for orgmode
(load (locate-user-emacs-file "modules/org.el") nil 'nomessage)
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)
;; Setup for rss feed
(load (locate-user-emacs-file "modules/rss.el") nil 'nomessage)
;; Compilation buffer always at the bottom, full frame width.
(add-to-list 'display-buffer-alist
'((derived-mode . compilation-mode)
(display-buffer-reuse-window display-buffer-at-bottom)
(window-height . 0.45)))
;; C-c C-c builds from the project root.
;; C-c b asks first: the prompt is pre-filled with ./build.sh, so type the args after it.
(defun my/project-compile-prompt ()
"Run `project-compile', prompting to edit the command."
(interactive)
(let ((compilation-read-command t))
(call-interactively #'project-compile)))
(keymap-global-set "C-c C-c" #'project-compile)
(keymap-global-set "C-c b" #'my/project-compile-prompt)
;; Focus the compilation window when a build starts.
(add-hook 'compilation-start-hook
(lambda (proc)
(when-let* ((win (get-buffer-window (process-buffer proc))))
(select-window win))))
;;;; 9. Modules - additional functionality outside of the core init.el
;; Magit and the :git command live in modules/git.el.
(load (locate-user-emacs-file "modules/git.el") nil 'nomessage)
;; Setup for personal build commands and system.
(load (locate-user-emacs-file "modules/build.el") nil 'nomessage)
;; Setup for dev languages, modes, lsp and lsp keybinds.
(load (locate-user-emacs-file "modules/development.el") nil 'nomessage)
(load (locate-user-emacs-file "modules/dev.el") nil 'nomessage)
;;; init.el ends here
+41
View File
@@ -0,0 +1,41 @@
;;; build.el --- Project build, set up for Evil -*- lexical-binding: t; -*-
;;; Commentary:
;; Loaded from init.el after Evil.
;;
;; :build Run ./build.sh from the project root.
;; :build ARGS Run ./build.sh ARGS, e.g. :build release -v
;;
;; The compilation buffer opens at the bottom (45% height, full width) and
;; takes focus when the build starts. ANSI colours are rendered.
;; Kill a running build with M-x kill-compilation.
;;; Code:
(setq compile-command "./build.sh"
compilation-read-command nil ; run without prompting
compilation-ask-about-save nil
compilation-scroll-output t)
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)
;; Compilation buffer at the bottom, full frame width.
(add-to-list 'display-buffer-alist
'((derived-mode . compilation-mode)
(display-buffer-reuse-window display-buffer-at-bottom)
(window-height . 0.45)))
;; Focus the compilation window when a build starts.
(add-hook 'compilation-start-hook
(lambda (proc)
(when-let* ((win (get-buffer-window (process-buffer proc))))
(select-window win))))
;; :build runs ./build.sh; :build ARGS appends ARGS.
(evil-define-command my/ex-build (args)
"Run `./build.sh ARGS' from the project root."
(interactive "<a>")
(let ((compile-command (concat "./build.sh " args))) ; nil args -> ""
(project-compile)))
(evil-ex-define-cmd "build" #'my/ex-build)
;;; build.el ends here
+99
View File
@@ -0,0 +1,99 @@
;;; development.el --- Languages, Eglot and code navigation -*- lexical-binding: t; -*-
;;; Commentary:
;; Loaded from init.el. Requires Evil.
;;
;; Languages:
;; Markdown markdown-mode.
;; PHP php-mode, PSR-2 style, 4 spaces.
;; Odin odin-mode from git (needs Emacs 30+ for :vc), Eglot + OLS.
;; Go go-mode, Eglot + gopls; organize imports and format on save.
;;
;; Normal/motion state:
;; gd definitions gD references K eldoc
;; C-t jump back ESC closes any open eldoc window.
;;; Code:
;;;; Markdown
(use-package markdown-mode)
;;;; PHP
(use-package php-mode
:mode "\\.php\\'"
:hook
(php-mode . (lambda ()
(setq-local indent-tabs-mode nil)
(setq-local tab-width 4)
(setq-local c-basic-offset 4)))
:custom
(php-mode-coding-style 'psr2))
;;;; Odin
(use-package odin-mode
:vc (:url "https://github.com/mattt-b/odin-mode"
:rev :newest)
:mode "\\.odin\\'"
:hook (odin-mode . eglot-ensure))
;;;; Go
;; Emacs started from the desktop doesn't inherit fish env.
(dolist (d '("/usr/local/go/bin" "/home/jason/.go/bin"))
(add-to-list 'exec-path d)
(setenv "PATH" (concat d ":" (getenv "PATH"))))
(setenv "GOPATH" "/home/jason/.go")
(setenv "GOCACHE" "/home/jason/.go/cache")
(defun my/go-before-save ()
"Organize imports and format the buffer through Eglot."
(when (eglot-managed-p)
(eglot-code-actions nil nil "source.organizeImports" t)
(eglot-format-buffer)))
(use-package go-mode
:mode "\\.go\\'"
:hook ((go-mode . eglot-ensure)
(go-mode . (lambda ()
(setq-local tab-width 4)
(add-hook 'before-save-hook #'my/go-before-save nil t)))))
;;;; Eglot
(use-package eglot
:ensure nil
:config
(setq eglot-autoshutdown t)
(add-to-list 'eglot-ignored-server-capabilities :signatureHelpProvider)
;; Explicit OLS configuration.
(add-to-list
'eglot-server-programs
'(odin-mode
. ("/home/jason/.local/bin/ols"
:initializationOptions
(:odin_command "/home/jason/.odin/odin"
:enable_document_symbols t
:enable_format t
:collections
[(:name "core"
:path "/home/jason/.odin/core")
(:name "vendor"
:path "/home/jason/.odin/vendor")])))))
;; OLS built-in definitions.
(setenv "OLS_BUILTIN_FOLDER" "/home/jason/ThirdParty/ols/builtin")
;;;; Code navigation
;; Vim-style code navigation.
(evil-define-key '(normal motion) 'global
"gd" #'xref-find-definitions
"gD" #'xref-find-references
"K" #'eldoc
(kbd "C-t") #'xref-go-back)
;; ESC closes any open eldoc window.
(defun my/eldoc-close (&rest _)
"Quit every window showing an *eldoc* buffer."
(dolist (w (window-list))
(when (string-prefix-p "*eldoc" (buffer-name (window-buffer w)))
(quit-window nil w))))
(advice-add 'evil-force-normal-state :after #'my/eldoc-close)
;;; development.el ends here
-48
View File
@@ -1,48 +0,0 @@
;;;; Markdown
(use-package markdown-mode)
;;;; Odin
(use-package odin-mode
:vc (:url "https://github.com/mattt-b/odin-mode"
:rev :newest)
:mode "\\.odin\\'"
:hook (odin-mode . eglot-ensure))
(use-package eglot
:ensure nil
:config
(setq eglot-autoshutdown t)
(add-to-list 'eglot-ignored-server-capabilities :signatureHelpProvider)
;; Explicit OLS configuration.
(add-to-list
'eglot-server-programs
'(odin-mode
. ("/home/jason/.local/bin/ols"
:initializationOptions
(:odin_command "/home/jason/.odin/odin"
:enable_document_symbols t
:enable_format t
:collections
[(:name "core"
:path "/home/jason/.odin/core")
(:name "vendor"
:path "/home/jason/.odin/vendor")])))))
;; OLS built-in definitions.
(setenv "OLS_BUILTIN_FOLDER" "/home/jason/ThirdParty/ols/builtin")
;;;; Code navigation
;; Vim-style code navigation.
(evil-define-key '(normal motion) 'global
"gd" #'xref-find-definitions
"gD" #'xref-find-references
"K" #'eldoc
(kbd "C-t") #'xref-go-back)
;; ESC closes any open eldoc window.
(defun my/eldoc-close (&rest _)
(dolist (w (window-list))
(when (string-prefix-p "*eldoc" (buffer-name (window-buffer w)))
(quit-window nil w))))
(advice-add 'evil-force-normal-state :after #'my/eldoc-close)
+58
View File
@@ -0,0 +1,58 @@
;;; org.el --- Org appearance -*- lexical-binding: t; -*-
;;; Code:
;; Styled stars, bullets, tags, tables and blocks.
(use-package org-modern
:hook (org-mode . org-modern-mode))
(setq org-directory "~/Projects/notebook")
(setq org-startup-indented t ; visual indent per level, no real spaces
org-hide-emphasis-markers t ; *bold* shows bold, markers hidden
org-pretty-entities t ; \rarr, \alpha etc. as symbols
org-ellipsis " ▾" ; folded-heading marker
org-startup-folded 'show2levels ; open with headings only
org-fontify-whole-heading-line t
org-fontify-done-headline t
org-fontify-quote-and-verse-blocks t
org-src-fontify-natively t ; highlight src blocks with their own mode
org-src-tab-acts-natively t
org-edit-src-content-indentation 0
org-return-follows-link t)
;; org-modern's recommended settings.
(setq org-auto-align-tags nil
org-tags-column 0
org-catch-invisible-edits 'show-and-error
org-special-ctrl-a/e t
org-insert-heading-respect-content t)
(add-hook 'org-mode-hook #'visual-line-mode) ; wrap at window edge
(add-hook 'org-mode-hook (lambda () (display-line-numbers-mode -1))) ; prose reads better without
;; doom-themes' own Org fontification fixes (blocks, headings).
(doom-themes-org-config)
;; Heading sizes; 'user survives theme reloads, same as in init.el.
(doom-themes-set-faces 'user
'(org-document-title :height 1.3 :weight 'bold)
'(org-level-1 :height 1.2 :weight 'bold)
'(org-level-2 :height 1.15 :weight 'bold)
'(org-level-3 :height 1.1 :weight 'bold)
'(org-level-4 :height 1.0 :weight 'bold))
(defun my/notes-index ()
"Open the notes index."
(interactive)
(find-file (expand-file-name "index.org" org-directory)))
(keymap-global-set "C-c n" #'my/notes-index)
;; Evil's TAB (= C-i, jump-forward) shadows Org's. Rebind for Org buffers only.
(with-eval-after-load 'org
(evil-define-key 'normal org-mode-map
(kbd "TAB") #'org-cycle ; fold/unfold the heading at point
(kbd "<tab>") #'org-cycle ; GUI emits <tab>, so bind both
(kbd "RET") #'org-open-at-point
(kbd "<backtab>") #'org-shifttab)) ; S-TAB: cycle all headings
;;; org.el ends here
+278
View File
@@ -0,0 +1,278 @@
;;; rss.el --- Elfeed for YouTube feeds, played in mpv -*- lexical-binding: t; -*-
;;; Commentary:
;; Elfeed with a little glue for Evil. Needs the `elfeed' package (MELPA).
;;
;; :rss / :rss get open elfeed / also fetch all feeds now
;;
;; Search buffer RET show the entry
;; o play in mpv and mark read
;; r/u mark read / unread
;; A mark everything listed as read
;; c pick a channel/feed by name and show only it
;; s change the filter; TAB completes +tags
;; (type "+seb" TAB -> "+Sebastian_Lague")
;; gr fetch all feeds
;; q quit
;; Entry buffer o play in mpv
;; q close
;;
;; Every feed is tagged with its category (+youtube, +news) and with its
;; own channel tag: its name with spaces replaced by "_". So
;; "gingerBill" is +gingerBill and "Sebastian Lague" is +Sebastian_Lague.
;; Tags match exactly, but in the `s' prompt TAB completes them, ignoring
;; case. Give a feed a short tag of your own with a third argument:
;; (my/rss-yt "Jonathan Blow highlights" "UC..." 'blow).
;;
;; Everything else is your normal Evil keys. Nothing is fetched
;; automatically; fetching is always manual (:rss get / gr). Fetch errors
;; are logged in the *elfeed-log* buffer.
;;
;; Feeds live in `my/rss-categories' below, grouped by tag. Edit the list
;; and restart Emacs.
;;; Code:
(use-package elfeed
:ensure t
:defer t
:custom
(elfeed-search-filter "@1-month-ago +unread")
;; Be gentle with YouTube: few simultaneous connections.
(elfeed-curl-max-connections 4)
:config
;; Not in :custom: `my/rss-feeds' is defined below, and :custom runs
;; immediately, while :config waits until elfeed loads.
(setq elfeed-feeds (my/rss-feeds))
(my/rss-apply-titles)
(my/rss-retag))
(defun my/rss-yt (name channel-id &optional tag)
"Feed entry for a YouTube channel, uploads only (no Shorts).
TAG, a symbol, overrides the channel tag made from NAME."
(let ((url (concat "https://www.youtube.com/feeds/videos.xml?playlist_id=UULF"
(substring channel-id 2))))
(if tag (list name url tag) (list name url))))
(defvar my/rss-categories
(list
(list 'youtube
(my/rss-yt "Awesome" "UCXzw-OdotBUcNA9yhuYQBwA")
(my/rss-yt "Coding with Sphere" "UCpwl7jNE9PJc-lBTShNs5TQ")
(my/rss-yt "Computerphile" "UC9-y-6csu5WGm29I7JiwpnA")
(my/rss-yt "gingerBill" "UCUSck1dOH7VKmG4lRW7tZXg")
(my/rss-yt "Jonathan Blow highlights" "UCavhLwXoB9tkqd-KsXpovOQ")
(my/rss-yt "Karl Zylinski" "UC5YnADU2rChK7598A9gKygQ")
(my/rss-yt "Nic Barker" "UCz-yrxeZYIYdpEZgHGvIydA")
(my/rss-yt "Nir Lichtman" "UCAMu6Dso0ENoNm3sKpQsy0g")
(my/rss-yt "Ryan Fleury" "UCCsdwE2z_kRL3vfVsd5OGyA")
(my/rss-yt "Tariq10x" "UCXyisNI9ezbv7fvGMoSaFKg")
(my/rss-yt "The Coding Gopher" "UC-mTIBh__DzAqW495JHXy5A")
(my/rss-yt "Tsoding Daily" "UCrqM0Ym_NbK1fqeQG2VIohg")
(my/rss-yt "zyapguy" "UCc4IizbM0q_JySK3XUY0Drw")
(my/rss-yt "Dylan Falconer" "UCZzs3Umh6sRiYS_IQIQDThw")
(my/rss-yt "Gamefromscratch" "UCr-5TdGkKszdbboXXsFZJTQ")
(my/rss-yt "Jonas Tyroller" "UC_p_9arduPuxM8DHTGIuSOg")
(my/rss-yt "Miziziziz" "UCaoqVlqPTH78_xjTjTOMcmQ")
(my/rss-yt "Penusbmic" "UCjU-rielI7RsX1hqsCQdfhw")
(my/rss-yt "randy" "UCUmLRMERmJrmUtgnbFfknAg")
(my/rss-yt "randy uncut" "UCgXdyySqz7qH-lDiVG-7KwA")
(my/rss-yt "raylib" "UC8WIBkhYb5sBNqXO1mZ7WSQ")
(my/rss-yt "Sebastian Lague" "UCmtyQOKKmrMVaKuRXz02jbQ")
(my/rss-yt "Tantan" "UChl_NKOs1qqh_x7yJfaDpDw")
(my/rss-yt "ThinMatrix" "UCUkRj4qoT1bsWpE_C8lZYoQ")
(my/rss-yt "Brodie Robertson" "UCld68syR8Wi-GY_n4CaoJGA")
(my/rss-yt "ETA PRIME" "UC_0CVCfC_3iuHqmyClu59Uw")
(my/rss-yt "Benny Surge" "UCkih_7PNYICxAZi1omr2_Og")
(my/rss-yt "bizlychannel" "UCMPGiQ8gwDXFYpwQhX6kK9A")
(my/rss-yt "Daz Games" "UCDBCCMlFD-hArPbWv38FINA")
(my/rss-yt "Deep Dive Dev" "UC1yGsLABIDAqLVtPnoHbpEA")
(my/rss-yt "Flo Woelki" "UC18qytfIhR9cNEjUcgGLl3A")
(my/rss-yt "Gohjoe" "UCac9YlOpTLy_wToRb5Bo-DQ")
(my/rss-yt "Herbcode" "UC79nyXj9gujAj5CySiy2FAQ")
(my/rss-yt "hoff._world" "UCaezB99bVg9xlMS6Jn_ka0A")
(my/rss-yt "Inbound Shovel" "UCdYwjLVP-98bptdlQFO_5zQ")
(my/rss-yt "Jimmy Oakes" "UChp08hcL-qAJD4LK51ZZZvw")
(my/rss-yt "Juby Philosophy" "UCLrpO7U1lnmbRtVxji843-Q")
(my/rss-yt "Lance Hedrick" "UCR2_8uHyWcTSV6iIw6BN4rg")
(my/rss-yt "Mental Moshpit" "UC5a4hb7i2UAT6DxmGz72hdg")
(my/rss-yt "NIGHTRIDE" "UCuYLDxZWv-aal88czKkFvKA")
(my/rss-yt "Rockers Games" "UCeq6focWSVKJy5DR1JxDGyw")
(my/rss-yt "Slax" "UCc0V8-3xYIT6aNOAreAjRcw")
(my/rss-yt "StayAtHomeDev" "UCDshKIMtFjYreCzApleuV8w")
(my/rss-yt "Turkey Tom" "UCDKJdFer1phQI95UinPZehw")
(my/rss-yt "Zei" "UCywwEsnSmADUyhtSaq5dKNg")
(my/rss-yt "Zip Tie Tech" "UCUpt_5O1vYpWAGg77iNMQXA"))
(list 'news
'("Hacker News" "https://hnrss.org/frontpage?points=100")))
"Feeds by tag: each entry is (TAG FEED...), a feed is (NAME URL [CHANNEL-TAG]).
The tag is what you filter on in elfeed, e.g. \"+youtube\". Each feed
also gets its own channel tag, see `my/rss-channel-tag'.")
(defun my/rss-slug (name)
"Tag symbol made from NAME, with spaces replaced by \"_\"."
(intern (replace-regexp-in-string " +" "_" (string-trim name) t t)))
(defun my/rss-channel-tag (feed)
"Channel tag for FEED, (NAME URL [TAG]): TAG if given, else NAME slugged."
(or (nth 2 feed) (my/rss-slug (car feed))))
(defun my/rss-feeds ()
"Elfeed feed list, (URL CATEGORY CHANNEL-TAG), from `my/rss-categories'."
(mapcan (lambda (cat)
(mapcar (lambda (feed)
(list (cadr feed) (car cat) (my/rss-channel-tag feed)))
(cdr cat)))
my/rss-categories))
(defun my/rss-all-feeds ()
"Every (NAME URL) in `my/rss-categories', across all tags."
(mapcan (lambda (cat) (copy-sequence (cdr cat))) my/rss-categories))
(defun my/rss-apply-titles ()
"Give each feed the name from `my/rss-categories' as its display title.
YouTube playlist feeds all call themselves \"Videos\"; this overrides
that, so the list shows the channel and \"=Name\" filters work. The
title is stored as feed metadata, which fetching never overwrites."
(dolist (feed (my/rss-all-feeds))
(setf (elfeed-meta (elfeed-db-get-feed (cadr feed)) :title)
(car feed))))
(defun my/rss-retag ()
"Give already-fetched entries their feed's tags from `elfeed-feeds'.
Elfeed tags entries only as they arrive, so this catches up entries
fetched before a feed's tags changed. Safe to run any time."
(interactive)
(elfeed-db-ensure)
(let ((tags-by-url (make-hash-table :test #'equal))
(n 0))
(dolist (f elfeed-feeds)
(puthash (car f) (cdr f) tags-by-url))
(maphash
(lambda (_id entry)
(let* ((tags (gethash (elfeed-entry-feed-id entry) tags-by-url))
(missing (cl-set-difference tags (elfeed-entry-tags entry))))
(when missing
(apply #'elfeed-tag entry missing)
(cl-incf n))))
elfeed-db-entries)
(when (or (> n 0) (called-interactively-p 'any))
(message "rss: retagged %d entries" n))))
;;;; Playing videos
(defun my/rss-play (url &rest _)
"Play URL in mpv. Its output goes to the *mpv* buffer."
(message "mpv: starting %s (can take a few seconds)..." url)
(set-process-sentinel
(start-process "mpv" "*mpv*" "mpv" url)
(lambda (proc _event)
(when (and (eq (process-status proc) 'exit)
(/= (process-exit-status proc) 0))
(message "mpv failed (exit %d), see the *mpv* buffer"
(process-exit-status proc))
(display-buffer "*mpv*")))))
;; YouTube links go to mpv, everywhere in Emacs.
(with-eval-after-load 'browse-url
(add-to-list 'browse-url-handlers
'("\\`https?://\\(?:www\\.\\|m\\.\\)?\\(?:youtube\\.com\\|youtu\\.be\\)/"
. my/rss-play)))
;;;; Elfeed
(defvar my/rss-stagger 4
"Seconds between feed fetches. YouTube answers with 404/500 when hit fast.")
(defun my/rss-fetch ()
"Fetch every feed, one at a time, `my/rss-stagger' seconds apart.
Run it again to fill in feeds that failed; entries are not duplicated."
(interactive)
(seq-do-indexed
(lambda (feed i)
(run-with-timer (* my/rss-stagger i) nil #'elfeed-update-feed (car feed)))
elfeed-feeds)
(message "rss: fetching %d feeds, about %ds"
(length elfeed-feeds) (* my/rss-stagger (length elfeed-feeds))))
(defun my/rss-all-tags ()
"Every tag name: the ones in `elfeed-feeds' plus any in the database."
(delete-dups
(mapcar #'symbol-name
(append (mapcan (lambda (f) (copy-sequence (cdr f))) elfeed-feeds)
(elfeed-db-get-all-tags)))))
(defun my/rss-filter-capf ()
"Complete the +tag or -tag at point in the filter prompt."
(when (looking-back "\\(?:^\\| \\)[+-]\\([^ ]*\\)" (line-beginning-position))
(list (match-beginning 1) (match-end 1)
(completion-table-case-fold (my/rss-all-tags))
:exclusive 'no)))
(defvar my/rss-filter-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "TAB") #'completion-at-point)
map)
"Extra keys for the filter prompt.")
(defun my/rss-filter ()
"Elfeed's live filter, with TAB completing +tags and -tags."
(interactive)
(minibuffer-with-setup-hook
(lambda ()
(add-hook 'completion-at-point-functions #'my/rss-filter-capf nil t)
(use-local-map (make-composed-keymap my/rss-filter-map
(current-local-map))))
(elfeed-search-live-filter)))
(defun my/rss-mark-all-read ()
"Mark every entry the current filter lists as read.
Works on elfeed's list directly, so it doesn't depend on the region or
on Evil's visual state. Clear the filter first to mark everything."
(interactive)
(let ((n (length elfeed-search-entries)))
(elfeed-untag elfeed-search-entries 'unread)
(elfeed-search-update :force)
(message "rss: marked %d entries read" n)))
(defun my/rss-channel ()
"Pick a feed by name and show only its entries (read and unread).
Filters on the feed URL, so it works whatever the feed calls itself.
Press `s' and clear the filter to go back."
(interactive)
(let* ((feeds (my/rss-all-feeds))
(name (completing-read "Channel: " feeds nil t))
(url (cadr (assoc name feeds))))
(elfeed-search-set-filter
(concat "@6-months-ago =" (regexp-quote url)))))
;; Elfeed's buffers start in motion state: Evil movement, no editing keys.
(evil-set-initial-state 'elfeed-search-mode 'motion)
(evil-set-initial-state 'elfeed-show-mode 'motion)
(with-eval-after-load 'elfeed-search
(evil-define-key 'motion elfeed-search-mode-map
(kbd "RET") #'elfeed-search-show-entry
"o" #'elfeed-search-browse-url ; plays via `browse-url-handlers', marks read
"r" #'elfeed-search-untag-all-unread
"u" #'elfeed-search-tag-all-unread
"A" #'my/rss-mark-all-read
"c" #'my/rss-channel
"s" #'my/rss-filter
"gr" #'my/rss-fetch
"q" #'elfeed-search-quit-window))
(with-eval-after-load 'elfeed-show
(evil-define-key 'motion elfeed-show-mode-map
"o" #'elfeed-show-visit
"q" #'elfeed-kill-buffer))
;; :rss opens elfeed; :rss get also fetches every feed now.
(evil-define-command my/rss (args)
"Open elfeed, or fetch all feeds too with `:rss get'."
(interactive "<a>")
(elfeed)
(when (equal (string-trim (or args "")) "get")
(my/rss-fetch)))
(evil-ex-define-cmd "rss" #'my/rss)
;;; rss.el ends here
+4 -1
View File
@@ -21,7 +21,6 @@ set -gx COLORTERM "truecolor"
set -gx DEBUG 1
set -gx GOPATH "$HOME/.go"
set -gx GOCACHE "$HOME/.go/cache"
set -gx GOROOT /usr/local/go
set -gx OLS_BUILTIN_FOLDER "/home/jason/ThirdParty/ols/builtin"
# Direnv
@@ -82,6 +81,10 @@ alias bat 'bat --theme="ansi" --paging=never'
alias :q 'exit'
alias files 'thunar .'
alias fetch 'pfetch'
# Map 'php' directly to FrankenPHP's bundled PHP CLI runtime
alias php="frankenphp php-cli"
# Optional: A shorthand to spin up your local dev server instantly using your Caddyfile
alias phpserve="frankenphp php-server --config Caddyfile"
# Custom shortcuts
alias reload 'source ~/.config/fish/config.fish'
+3 -2
View File
@@ -151,9 +151,10 @@ if [ "$DO_INSTALL" = true ]; then
# core utilities & CLI tools
git fd jq fzf ripgrep tree direnv make
bat unzip 7zip tar gzip bzip2 xz ark
mpv xfce4-screenshooter xkill xwininfo
xdotool wmctrl
# build & dev tools
emacs-x11 alacritty base-devel valgrind
xfce4-screenshooter xkill xwininfo xdotool wmctrl
emacs-x11 alacritty base-devel valgrind go
)
MISSING_PACKAGES=()
+64
View File
@@ -0,0 +1,64 @@
#!/bin/bash
# ---------------------------------------------------------------------------------
# Steam install recipe
# Target: Void Linux (x86_64 glibc only)
# Enables nonfree + multilib repos, installs Steam, 32-bit runtime libs, GPU drivers
# Usage: GPU=amd|nvidia|intel ./steam.sh (default: amd)
# ---------------------------------------------------------------------------------
set -e
GPU="${GPU:-amd}"
if [ "$(xbps-uhelper arch)" != "x86_64" ]; then
echo "Steam requires x86_64 glibc. Aborting." >&2
exit 1
fi
echo "Enabling nonfree and multilib repositories..."
sudo xbps-install -Sy void-repo-nonfree void-repo-multilib void-repo-multilib-nonfree
sudo xbps-install -Sy # sync the newly added repos
echo "Installing Steam and 32-bit runtime libraries..."
sudo xbps-install -y \
steam \
steam-udev-rules \
libgcc-32bit \
libstdc++-32bit \
libdrm-32bit \
libglvnd-32bit \
mesa-dri \
mesa-dri-32bit \
vulkan-loader \
vulkan-loader-32bit \
alsa-lib-32bit \
libpulseaudio-32bit
echo "Installing GPU drivers ($GPU)..."
case "$GPU" in
amd)
sudo xbps-install -y \
mesa-vulkan-radeon mesa-vulkan-radeon-32bit \
mesa-vaapi
;;
nvidia)
sudo xbps-install -y nvidia nvidia-libs nvidia-libs-32bit
;;
intel)
sudo xbps-install -y \
mesa-vulkan-intel mesa-vulkan-intel-32bit \
intel-video-accel
;;
*)
echo "Unknown GPU '$GPU' (amd|nvidia|intel)" >&2
exit 1
;;
esac
echo "Reloading udev rules..."
sudo udevadm control --reload
sudo udevadm trigger
echo ""
echo "✓ Steam installed. Log out/in (or reboot) before first launch."
echo ""