;;; init.el --- Emacs configuration -*- lexical-binding: t; -*- (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (package-initialize) (unless package-archive-contents (package-refresh-contents)) (require 'use-package) (setq use-package-always-ensure t) (scroll-bar-mode -1) (setq display-line-numbers-type 'relative) (global-display-line-numbers-mode 1) (setq-default indent-tabs-mode nil) (setq custom-file (make-temp-file "emacs-custom-")) ;; move temp files out of the cwd (let ((auto-save-dir (concat user-emacs-directory "auto-save/")) (backup-dir (concat user-emacs-directory "backups/"))) (make-directory auto-save-dir t) (make-directory backup-dir t) (setq auto-save-file-name-transforms `((".*" ,auto-save-dir t))) (setq backup-directory-alist `(("." . ,backup-dir)))) (use-package evil :init (setq evil-want-keybinding nil) (setq evil-want-C-u-scroll t) :config (evil-mode 1)) (use-package evil-collection :after evil :config (evil-collection-init)) (add-hook 'eshell-mode-hook (lambda () (evil-insert-state))) (use-package vs-dark-theme :config (load-theme 'vs-dark t)) (use-package apheleia :config (add-to-list 'apheleia-mode-alist '(scheme-mode . lisp-indent)) (apheleia-global-mode +1)) (use-package geiser-guile) (use-package markdown-mode :ensure t :mode ("\\.md\\'" . gfm-mode) :hook (markdown-mode . flyspell-mode)) (use-package nix-ts-mode :mode "\\.nix\\'") (use-package rainbow-delimiters :hook (scheme-mode . rainbow-delimiters-mode)) (add-hook 'scheme-mode-hook #'electric-pair-local-mode) (setq ispell-program-name "aspell" ispell-dictionary "en_US") (add-hook 'markdown-mode-hook #'flyspell-mode) (use-package magit :bind ("C-x g" . magit-status)) (use-package agent-shell ;; Talks to LLM coding agents over the Agent Client Protocol (ACP). ;; Pi needs the external `pi-acp` adapter on PATH (provided via nixos-config). ;; Pi authenticates via OAuth -- run `/login` inside the shell once; no API key. :bind ("C-c a p" . agent-shell-pi-start-agent) :config (setq agent-shell-pi-acp-command '("pi-acp")) ;; Start ready to type instead of in evil normal state. (add-hook 'agent-shell-mode-hook #'evil-insert-state) ;; Same GUI-Enter gotcha as mpdel: shell-maker binds submit via a ;; comint-send-input remap on RET/C-m, but a GUI frame's Enter sends ;; -- which evil-collection shadows -- so input never gets sent. ;; Point (and keypad Enter) straight at submit in insert state. (defun my/agent-shell-restore-ret () (evil-local-set-key 'insert (kbd "") #'shell-maker-submit) (evil-local-set-key 'insert (kbd "") #'shell-maker-submit)) (add-hook 'agent-shell-mode-hook #'my/agent-shell-restore-ret)) (use-package mpdel :after evil :config (mpdel-mode) ;; In a GUI frame the Enter key sends , which evil-collection binds to ;; play/pause -- shadowing tablist's "descend into entry" command (that command ;; only sits on RET / C-m). Point (and the keypad Enter) at descend ;; in all mpdel list buffers (browser, playlists, ...). (defun my/mpdel-restore-ret () (evil-local-set-key 'normal (kbd "") #'tablist-find-entry) (evil-local-set-key 'normal (kbd "") #'tablist-find-entry)) (add-hook 'mpdel-tablist-mode-hook #'my/mpdel-restore-ret) ;; "Recently added" view. MPD has no such entity, so subclass mpdel's filter ;; (which already renders a browsable, addable song list) and issue a search ;; sorted by modification time, newest first. (require 'cl-lib) (defvar my/mpdel-recent-count 50 "How many recently added songs `my/mpdel-recent' lists.") (cl-defstruct (my/mpdel-recent-entity (:include libmpdel-filter))) (cl-defmethod libmpdel-entity-name ((_e my/mpdel-recent-entity)) "Recently added") (cl-defmethod libmpdel-list-songs ((_e my/mpdel-recent-entity) function) (libmpdel-send-command (format "search \"(file != \\\"\\\")\" sort -Last-Modified window 0:%d" my/mpdel-recent-count) (lambda (data) (funcall function (libmpdel--create-songs-from-data data))))) (defun my/mpdel-recent () "Browse the most recently added songs, newest first." (interactive) (mpdel-core-open (make-my/mpdel-recent-entity :text "recently added"))) (defun my/mpdel-recent-replace () "Replace the current playlist with the recently added songs." (interactive) (libmpdel-list-songs (make-my/mpdel-recent-entity :text "recently added") (lambda (songs) (libmpdel-current-playlist-replace songs)))) (global-set-key (kbd "C-c m r") #'my/mpdel-recent) (global-set-key (kbd "C-c m R") #'my/mpdel-recent-replace))