From 107eab7ef6311a5b5f9c85c179ae83b2e3204094 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Sun, 8 Jan 2023 18:19:04 -0600 Subject: myeahhhhhh --- basics.el | 315 ++++++++++++++++++++++++++++++++++------ early-init.el | 15 +- init.el | 451 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- lisp/acdw.el | 39 +++++ 4 files changed, 768 insertions(+), 52 deletions(-) create mode 100644 lisp/acdw.el diff --git a/basics.el b/basics.el index 06f5ece..4e9e0a9 100644 --- a/basics.el +++ b/basics.el @@ -8,41 +8,15 @@ ;;; Code: -;;; Directories +(push (locate-user-emacs-file "lisp/") load-path) +(require 'acdw) -(defmacro defdir (name directory &optional docstring makedir) - "Define a variable and a function NAME expanding to DIRECTORY. -DOCSTRING is applied to the variable; its default is DIRECTORY's -path. If MAKEDIR is non-nil, the directory and its parents will -be created." - (declare (indent 2) (doc-string 3)) - `(progn - (defvar ,name (expand-file-name ,directory) - ,(concat (or docstring (format "%s" directory)) "\n" - "Defined by `defdir'.")) - (defun ,name (file &optional mkdir) - ,(concat "Expand FILE relative to variable `" (symbol-name name) "'.\n" - "If MKDIR is non-nil, parent directories are created.\n" - "Defined by `defdir'.") - (let ((file-name (expand-file-name - (convert-standard-filename file) ,name))) - (when mkdir - (make-directory (file-name-directory file-name) :parents)) - file-name)) - ,(if makedir - `(make-directory ,directory :parents) - `(unless (file-exists-p ,directory) - (warn "Directory `%s' doesn't exist." ,directory))))) +;;; Directories (defdir etc/ (locate-user-emacs-file "etc/") "Where various Emacs files are placed." :makedir) -(defdir lisp/ (locate-user-emacs-file "lisp/") - "My bespoke elisp files." - :makedir) -(push lisp/ load-path) - (defdir sync/ "~/Sync/" "My Syncthing directory." :makedir) @@ -50,7 +24,6 @@ be created." (defdir private/ (sync/ "emacs/private/") "Private files and stuff." :makedir) -(push private/ load-path) (use-package no-littering :ensure t :demand t @@ -114,6 +87,15 @@ be created." (file-name-shadow-mode) (minibuffer-electric-default-mode) +(define-minor-mode truncate-lines-local-mode + "Truncate lines locally in a buffer." + :lighter " ..." + :group 'display + (setq-local truncate-lines truncate-lines-local-mode)) + +(add-hook 'minibuffer-setup-hook #'truncate-lines-local-mode) + + (require 'savehist) (setq-default history-length 1024 history-delete-duplicates t @@ -122,13 +104,16 @@ be created." savehist-autosave-interval 30) (savehist-mode) -;; Undo -(setq-default undo-limit (* 10 1024 1024)) - ;; Killing and yanking (setq-default kill-do-not-save-duplicates t kill-read-only-ok t - save-interprogram-paste-before-kill t + ;; XXX: This setting causes an error message the first time it's + ;; called: "Selection owner couldn't convert: TIMESTAMP". I have + ;; absolutely no idea why I get this error, but it's generated in + ;; `x_get_foreign_selection'. I also can't inhibit the message or + ;; do anything else with it, so for now, I'll just live with the + ;; message. + save-interprogram-paste-before-kill t yank-pop-change-selection t) (delete-selection-mode) @@ -147,14 +132,20 @@ be created." initial-buffer-choice t initial-scratch-message nil) -;; (menu-bar-mode -1) +(define-advice startup-echo-area-message (:override ()) + (if (get-buffer "*Warnings*") + ";_;" + "^_^")) + +(menu-bar-mode -1) (tool-bar-mode -1) (tooltip-mode -1) ;; Text editing (setq-default fill-column 80 sentence-end-double-space t - tab-width 8) + tab-width 8 + tab-always-indent 'complete) (global-so-long-mode) (setq-default show-paren-delay 0.01 @@ -202,7 +193,8 @@ be created." auto-save-interval 1 auto-save-no-message t auto-save-timeout 1 - auto-save-visited-interval 1) + auto-save-visited-interval 1 + remote-file-name-inhibit-auto-save-visited t) (add-to-list 'auto-save-file-name-transforms `(".*" ,(etc/ "auto-save/" t) t)) (auto-save-visited-mode) @@ -215,9 +207,9 @@ be created." (require 'recentf) (setq-default ;; recentf-save-file (etc/ "recentf" t) - recentf-max-menu-items 500 - recentf-max-saved-items nil ; Save the whole list - recentf-auto-cleanup 'mode) + recentf-max-menu-items 500 + recentf-max-saved-items nil ; Save the whole list + recentf-auto-cleanup 'mode) (add-to-list 'recentf-exclude etc/) (add-to-list 'recentf-exclude "-autoloads.el\\'") (add-hook 'buffer-list-update-hook #'recentf-track-opened-file) @@ -225,8 +217,8 @@ be created." (require 'saveplace) (setq-default ;; save-place-file (etc/ "places.el") - save-place-forget-unreadable-files (eq system-type - 'gnu/linux)) + save-place-forget-unreadable-files (eq system-type + 'gnu/linux)) (save-place-mode) (require 'uniquify) @@ -247,10 +239,12 @@ be created." (startup-redirect-eln-cache native-compile-target-directory)) ;; Custom file -(setq-default custom-file (sync/ "emacs/custom.el")) -(define-advice package--save-selected-packages (:around (orig &rest args) no-custom) +(setq-default custom-file (private/ "custom.el")) +(define-advice package--save-selected-packages + (:around (orig &rest args) no-custom) "Don't save `package-selected-packages' to `custom-file'." - (let ((custom-file null-device)) + (let ((custom-file (expand-file-name "custom.el" + temporary-file-directory))) (apply orig args))) ;; Goto Address @@ -293,22 +287,56 @@ N spaces." (global-set-key (kbd "C-x 0") #'delete-window|bury-buffer) (global-set-key (kbd "M-SPC") #'+cycle-spacing) (global-set-key (kbd "C-x C-k") #'kill-this-buffer) +(global-set-key (kbd "C-/") #'undo-only) +(global-set-key (kbd "C-?") #'undo-redo) + +(define-key emacs-lisp-mode-map (kbd "C-c C-c") + #'eval-defun) +(define-key emacs-lisp-mode-map (kbd "C-c C-k") + #'elisp-eval-region-or-buffer) +(define-key lisp-interaction-mode-map (kbd "C-c C-c") + #'eval-defun) +(define-key lisp-interaction-mode-map (kbd "C-c C-k") + #'elisp-eval-region-or-buffer) +(define-advice eval-region (:around (orig start end &rest args) pulse) + (apply orig start end args) + (pulse-momentary-highlight-region start end)) + +(global-set-key (kbd "C-x C-b") #'ibuffer) ;;; Hooks (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) + (add-hook 'find-file-not-found-functions (defun create-missing-directories () "Automatically create missing directories." (let ((target-dir (file-name-directory buffer-file-name))) (unless (file-exists-p target-dir) (make-directory target-dir :parents))))) + (add-hook 'find-file-hook (defun vc-remote-off () "Turn VC off when remote." (when (file-remote-p (buffer-file-name)) (setq-local vc-handled-backends nil)))) +(add-hook 'after-init-hook + (defun after-init@emoji-font-setup () + "Set up emoji fonts after init." + (run-with-idle-timer + 1 nil (defun emoji-font-setup () + "Set up emoji fonts." + (let ((ffl (font-family-list))) + (dolist (font '("Noto Emoji" "Noto Color Emoji" + "Segoe UI Emoji" "Apple Color Emoji" + "FreeSans" "FreeMono" "FreeSerif" + "Unifont" "Symbola")) + (when (member font (font-family-list)) + (set-fontset-font t 'symbol + (font-spec :family font) + nil :add)))))))) + ;;; Advice (define-advice switch-to-buffer (:after (&rest _) normal-mode) @@ -373,4 +401,201 @@ See also `with-region-or-to-eol'." (use-package _acdw :load-path private/) +(use-package custom-allowed + :load-path "/home/case/src/emacs/custom-allowed/" + :config + (add-to-list 'custom-allowed-variables 'safe-local-variable-values) + (add-to-list 'custom-allowed-variables 'ispell-buffer-session-localwords) + (add-to-list 'custom-allowed-variables 'warning-suppress-types) + (add-to-list 'custom-allowed-variables 'calendar-latitude) + (add-to-list 'custom-allowed-variables 'calendar-longitude) + (add-to-list 'custom-allowed-variables 'user-full-name) + (add-to-list 'custom-allowed-variables 'user-mail-address) + :hook + (after-init-hook . custom-allowed-load-custom-file)) + +(use-package sophomore + :load-path "/home/case/src/emacs/sophomore/" + :config + (sophomore-enable-all) + (sophomore-disable 'view-hello-file + 'describe-gnu-project + 'suspend-frame) + (sophomore-mode)) + +(use-package compat + ;; This shouldn't be necessary, but sadly I believe that it is. + :ensure t) + +(use-package vertico + :ensure t :demand t + :config + (setq vertico-cycle t) + (vertico-mode)) + +(use-package vertico-directory + :after vertico + :bind (:map vertico-map + ("RET" . vertico-directory-enter) + ("DEL" . vertico-directory-delete-char) + ("M-DEL" . vertico-directory-delete-word)) + :hook (rfn-shadow-update-overlay-hook . vertico-directory-tidy)) + +(use-package vertico-mouse + :after vertico + :config (vertico-mouse-mode)) + +;; Example configuration for Consult +(use-package consult + :ensure t + ;; Replace bindings. Lazily loaded due by `use-package'. + :bind (;; C-c bindings (mode-specific-map) + ("C-c h" . consult-history) + ("C-c m" . consult-mode-command) + ("C-c k" . consult-kmacro) + ;; C-x bindings (ctl-x-map) + ("C-x M-:" . consult-complex-command) + ("C-x b" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ("C-x r b" . consult-bookmark) + ("C-x p b" . consult-project-buffer) + ;; Custom M-# bindings for fast register access + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) + ("C-M-#" . consult-register) + ;; Other custom bindings + ("M-y" . consult-yank-pop) + ;; M-g bindings (goto-map) + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) + ("M-g g" . consult-goto-line) + ("M-g M-g" . consult-goto-line) + ("M-g o" . consult-outline) + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ("M-g i" . consult-imenu) + ("M-g I" . consult-imenu-multi) + ;; M-s bindings (search-map) + ("M-s d" . consult-find) + ("M-s D" . consult-locate) + ("M-s g" . consult-grep) + ("M-s G" . consult-git-grep) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s k" . consult-keep-lines) + ("M-s u" . consult-focus-lines) + ;; Isearch integration + ("M-s e" . consult-isearch-history) + :map isearch-mode-map + ("M-e" . consult-isearch-history) + ("M-s e" . consult-isearch-history) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ;; Minibuffer history + :map minibuffer-local-map + ("M-s" . consult-history) + ("M-r" . consult-history)) + + ;; Enable automatic preview at point in the *Completions* buffer. This is + ;; relevant when you use the default completion UI. + :hook (completion-list-mode . consult-preview-at-point-mode) + + ;; The :init configuration is always executed (Not lazy) + :init + + ;; Optionally configure the register formatting. This improves the register + ;; preview for `consult-register', `consult-register-load', + ;; `consult-register-store' and the Emacs built-ins. + (setq register-preview-delay 0.5 + register-preview-function #'consult-register-format) + + ;; Optionally tweak the register preview window. + ;; This adds thin lines, sorting and hides the mode line of the window. + (advice-add #'register-preview :override #'consult-register-window) + + (define-advice completing-read-multiple (:filter-args (args) indicator) + (cons (format "[CRM%s] %s" + (replace-regexp-in-string + "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" + crm-separator) + (car args)) + (cdr args))) + + ;; Use Consult to select xref locations with preview + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + + (setq completion-in-region-function #'consult-completion-in-region) + + ;; Configure other variables and modes in the :config section, + ;; after lazily loading the package. + :config + + ;; Optionally configure preview. The default value + ;; is 'any, such that any key triggers the preview. + ;; (setq consult-preview-key 'any) + ;; (setq consult-preview-key (kbd "M-.")) + ;; (setq consult-preview-key (list (kbd "") (kbd ""))) + ;; For some commands and buffer sources it is useful to configure the + ;; :preview-key on a per-command basis using the `consult-customize' macro. + (consult-customize + consult-theme :preview-key '(:debounce 0.2 any) + consult-ripgrep consult-git-grep consult-grep + consult-bookmark consult-recent-file consult-xref + consult--source-bookmark consult--source-file-register + consult--source-recent-file consult--source-project-recent-file + ;; :preview-key (kbd "M-.") + :preview-key '(:debounce 0.4 any)) + + ;; Optionally configure the narrowing key. + ;; Both < and C-+ work reasonably well. + (setq consult-narrow-key "<") ;; (kbd "C-+") + + ;; Optionally make narrowing help available in the minibuffer. + ;; You may want to use `embark-prefix-help-command' or which-key instead. + (define-key consult-narrow-map (vconcat consult-narrow-key "?") + #'consult-narrow-help)) + +(use-package orderless + :ensure t :demand t + :init + (setopt completion-styles '(substring orderless basic) + completion-category-defaults nil + completion-category-overrides + '((file (styles basic partial-completion))))) + +(use-package marginalia + :ensure t :demand t + :config + (marginalia-mode)) + +(use-package embark + :ensure t + :bind + (("C-." . embark-act) + ("M-." . embark-dwim) + ("C-h B" . embark-bindings)) + :init + (setopt prefix-help-command #'embark-prefix-help-command) + :config + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +(use-package embark-consult + :ensure t + :hook + (embark-collect-mode . consult-preview-at-point-mode)) + +(use-package undo-fu-session + :ensure t + :config + (setq undo-fu-session-incompatible-files + '("/COMMIT_EDITMSG\\'" + "/git-rebase-todo\\'")) + (global-undo-fu-session-mode)) + ;;; basics.el ends here diff --git a/early-init.el b/early-init.el index 3dd74e0..d59fe64 100644 --- a/early-init.el +++ b/early-init.el @@ -5,10 +5,11 @@ ;; Debugging shit (setq debug-on-error t use-package-verbose t) +;; (setq debug-on-message "Selection owner couldn’t convert: TIMESTAMP") ;; Frames (setq default-frame-alist '((tool-bar-lines . 0) - ;; (menu-bar-lines . 0) + (menu-bar-lines . 0) (vertical-scroll-bars . nil) (horizontal-scroll-bars . nil)) frame-inhibit-implied-resize t @@ -21,11 +22,13 @@ ;; Packages (require 'package) (add-to-list 'package-archives - '("melpa" . "https://melpa.org/packages/") - :append) -(setq package-priorities '(("melpa" . 2) - ("nongnu" . 1) - ("gnu" . 0))) + '("melpa" . "https://melpa.org/packages/") :append) +(add-to-list 'package-archives + '("melpa-stable" . "https://stable.melpa.org/packages/") :append) +(setq package-priorities '(("melpa" . 3) + ("nongnu" . 2) + ("gnu" . 1) + ("melpa-stable" . 0))) (package-initialize) (unless package-archive-contents (package-refresh-contents)) diff --git a/init.el b/init.el index 369641c..c3c2f49 100644 --- a/init.el +++ b/init.el @@ -1,7 +1,456 @@ ;;; init.el --- An Emacs of one's own -*- lexical-binding: t -*- - ;; Bankruptcy: 9.4 ;;; Code: (load (locate-user-emacs-file "basics")) ; super basic stuff + + +;;; Built-ins + +(use-package emacs + :custom-face + (default ((t :family "Comic Code" + :height 100))) + (variable-pitch ((t :family "Comic Code"))) + :config + (setopt tab-bar-show 1)) + +(use-package text-mode + :config + (add-hook 'text-mode-hook #'abbrev-mode)) + +(use-package prog-mode + :config + (add-hook 'prog-mode-hook #'auto-fill-mode) + (add-hook 'prog-mode-hook + (defun prog@indent-tabs-maybe () + (indent-tabs-mode + (if (derived-mode-p 'emacs-lisp-mode + 'python-mode + 'haskell-mode) + -1 1))))) + +(use-package eshell + :preface + ;; TODO: Break this out into its own package (eshell-pop?). This may not work + ;; the way I want it to sometimes .. but then, I don't know how I want it to + ;; work sometimes either. More testing, clearly, is needed. + (defvar eshell-buffer-format "*eshell:%s*" + "Format for eshell buffer names.") + (defun eshell-rename-pwd () + (rename-buffer (format eshell-buffer-format default-directory) t)) + (defun eshell-last-dir () + (goto-char (point-max)) + (insert "cd -") + (eshell-send-input)) + (defun eshellp (buffer-or-name) + (with-current-buffer buffer-or-name + (derived-mode-p 'eshell-mode))) + (defun +eshell (&optional new) + (interactive "P") + (let ((dir default-directory) + (bname (format eshell-buffer-format default-directory)) + (display-comint-buffer-action 'pop-to-buffer)) + (if-let ((buf (and (not new) + (or (get-buffer bname) + (seq-find #'eshellp + (reverse (buffer-list))))))) + (pop-to-buffer buf) + (eshell new)) + (eshell-rename-pwd) + (unless (equal default-directory dir) + (goto-char (point-max)) + (insert (format "cd %s" dir)) + (eshell-send-input)))) + (defun +eshell-quit (&optional choose) + (interactive "P") + (if choose + (let* ((bufs (mapcar #'buffer-name + (seq-filter #'eshellp + (buffer-list)))) + (buf (get-buffer + (completing-read "Eshell: " + bufs nil t nil nil (car bufs))))) + (quit-window) + (pop-to-buffer buf)) + (quit-window))) + :init + (add-hook 'eshell-post-command-hook #'eshell-rename-pwd) + :commands eshell + :bind (("C-z" . +eshell) + :map eshell-mode-map + ("C-z" . +eshell-quit) + ("C-o" . eshell-last-dir)) + :config + (add-hook 'eshell-mode-hook + (defun eshell-setup () + (setq-local imenu-generic-expression + '(("Prompt" " $ \\(.*\\)" 1)))))) + +(use-package auth-source + :config + (setopt auth-sources '(default "secrets:passwords")) + (add-hook 'auth-info-hook #'truncate-lines-local-mode)) + +(use-package fringe + :config + (fringe-mode '(nil . 0))) + + +;;; Locally-developed packages + +(use-package dawn + :load-path "~/src/emacs/dawn/" + :after custom-allowed + :config + (add-hook 'custom-allowed-after-load-hook + (defun dawn-modus () + (dawn-schedule-themes 'modus-operandi + 'modus-vivendi)))) + +(use-package electric-cursor + :load-path "~/src/emacs/electric-cursor/" + :config + (setopt electric-cursor-alist '((overwrite-mode . box) + (t . bar))) + (electric-cursor-mode)) + +(use-package mode-line-bell + :load-path "~/src/emacs/mode-line-bell/" + :config + (setopt mode-line-bell-flash-time 0.25) + (mode-line-bell-mode)) + +(use-package titlecase + :load-path "~/src/emacs/titlecase.el/" + :after scule + :bind (:map scule-map + ("M-t" . titlecase-dwim))) + +(use-package scule + :load-path "~/src/emacs/scule/" + :config + (defvar-keymap scule-map + :doc "Keymap to twiddle scules." + :repeat t ; TODO: doesn't work + "M-u" #'scule-upcase + "M-l" #'scule-downcase + "M-c" #'scule-capitalize) + (keymap-global-set "M-c" scule-map) + ;; Use M-u for prefix keys + (keymap-global-set "M-u" #'universal-argument) + (keymap-set universal-argument-map "M-u" #'universal-argument-more)) + +(use-package filldent + :load-path "~/src/emacs/filldent/" + :bind ("M-q" . filldent-dwim)) + +(use-package frowny + :load-path "~/src/emacs/frowny/" + :config + (global-frowny-mode)) + +(use-package jabber + :load-path "~/src/emacs/emacs-jabber/" + :preface + (defun jabber-ui-setup () + "Setup the `jabber' user interface." + (visual-fill-column-mode) + (electric-pair-local-mode -1) + (auto-fill-mode -1)) + :custom-face + (jabber-activity-face ((t :inherit jabber-chat-prompt-foreign + :foreground unspecified + :weight normal))) + (jabber-activity-personal-face ((t :inherit font-lock-warning-face + :foreground unspecified + :weight bold))) + (jabber-chat-prompt-local ((t :inherit font-lock-warning-face + :foreground unspecified))) + (jabber-chat-prompt-foreign ((t :inherit font-lock-constant-face + :foreground unspecified))) + (jabber-chat-prompt-system ((t :inherit font-lock-doc-face + :foreground unspecified))) + (jabber-rare-time-face ((t :inherit font-lock-comment-face + :foreground unspecified + :underline nil))) + :bind-keymap ("C-c j" . jabber-global-keymap) + :bind (("C-c C-SPC" . jabber-activity-switch-to)) + :config + (setopt jabber-account-list '(("acdw@hmm.st")) + jabber-auto-reconnect t + jabber-last-read-marker (make-string 40 ?-) + jabber-muc-presence-patterns + '(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$") + ("." . jabber-muc-presence-dim)) + jabber-activity-make-strings #'jabber-activity-make-strings-shorten) + (add-hook 'jabber-chat-mode-hook #'jabber-ui-setup) + (keymap-global-set "C-x C-j" #'dired-jump) ; Extremely annoying fix + (require 'jabber-httpupload nil t) + (add-hook 'jabber-post-connect-hooks #'jabber-enable-carbons) + (remove-hook 'jabber-alert-muc-hooks 'jabber-muc-echo) + (remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo) + (add-hook 'jabber-alert-muc-hooks + (defun jabber@highlight-acdw (&optional _ _ buf _ _) + (when buf + (with-current-buffer buf + (let ((regexp (rx word-boundary + "acdw" ; maybe get from the config? + word-boundary))) + (hi-lock-unface-buffer regexp) + (highlight-regexp regexp 'font-lock-warning-face)))))) + (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus)) + +(use-package keepassxc-shim + :load-path "~/src/emacs/keepassxc-shim/" + :config + (keepassxc-shim-activate)) + + +;;; External packages + +(use-package minions + :ensure t + :config (minions-mode)) + +(use-package visual-fill-column + :ensure t + :init + (setopt visual-fill-column-center-text t + visual-fill-column-extra-text-width '(1 . 1)) + :config + (add-hook 'visual-fill-column-mode-hook #'visual-line-mode) + (add-hook 'eww-mode-hook #'visual-fill-column-mode) + (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) + +(use-package mlscroll + :ensure t :defer 1 + :after modus-themes + :preface + (define-advice load-theme (:after (&rest _) mlscroll) + (mlscroll-mode -1) + (when (seq-intersection '(modus-vivendi modus-operandi) + custom-enabled-themes) + (modus-themes-with-colors + (setq mlscroll-in-color fg-dim + mlscroll-out-color bg-inactive))) + (run-with-idle-timer 1 nil #'mlscroll-mode)) + :config + (load-theme@mlscroll)) + +(use-package cape + :ensure t + :config + (add-hook 'completion-at-point-functions #'cape-file 90) + (add-hook 'completion-at-point-functions #'cape-dabbrev 91) + (advice-add 'emacs-completion-at-point + :around #'cape-wrap-nonexclusive)) + +(use-package ws-butler + :ensure t + :config + (setopt ws-butler-trim-predicate + (lambda (begin end) + (not (eq 'font-lock-string-face + (get-text-property end 'face))))) + (ws-butler-global-mode)) + +(use-package wgrep + :ensure t + :config + (setopt wgrep-enable-key (kbd "C-x C-q")) + :bind (:map grep-mode-map + ("C-x C-q" . wgrep-change-to-wgrep-mode))) + +(use-package avy + :ensure t + :init + (setopt avy-background t + avy-keys (string-to-list "asdfghjklqwertyuiopzxcvbnm")) + :bind (("M-j" . avy-goto-char-timer) + :map isearch-mode-map + ("M-j" . avy-isearch))) + +(use-package zzz-to-char + :ensure t + :bind (("M-z" . zzz-to-char))) + +(use-package anzu + :ensure t + :bind (("M-%" . anzu-query-replace-regexp) + ("C-M-%" . anzu-query-replace))) + +(use-package isearch-mb + :ensure t + :config + (setopt isearch-lazy-count t + isearch-regexp-lax-whitespace t + search-whitespace-regexp "\\W+" + search-default-mode t ; Search regexp by default + isearch-wrap-pause 'no) + (define-advice isearch-cancel (:before (&rest _) add-search-to-history) + "Add search string to history when canceling." + (unless (equal "" isearch-string) + (isearch-update-ring isearch-string isearch-regexp))) + (define-advice perform-replace (:around (orig &rest r) no-anykey-exit) + "Don't exit replace for any key that's not in `query-replace-map'." + (save-window-excursion + (cl-letf* ((lookup-key-orig (symbol-function 'lookup-key)) + ((symbol-function 'lookup-key) + (lambda (map key &optional accept-default) + (or (apply lookup-key-orig map key accept-default) + (when (eq map query-replace-map) 'help))))) + (apply orig r)))) + ;; Consult + (autoload 'consult-line "consult" nil t) + (autoload 'consult-isearch-history "consult" nil t) + (add-to-list 'isearch-mb--after-exit #'consult-line) + (add-to-list 'isearch-mb--with-buffer #'consult-isearch-history) + (keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line) + (keymap-set isearch-mb-minibuffer-map "M-r" #'consult-isearch-history) + ;; Anzu + (autoload 'anzu-isearch-query-replace "anzu" nil t) + (autoload 'anzu-isearch-query-replace-regexp "anzu" nil t) + (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace) + (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace-regexp) + (keymap-set isearch-mb-minibuffer-map + "M-%" #'anzu-isearch-query-replace-regexp) + (keymap-set isearch-mb-minibuffer-map + "C-M-%" #'anzu-isearch-query-replace) + (isearch-mb-mode)) + +(use-package paredit + :ensure t + :hook ( emacs-lisp-mode-hook ielm-mode-hook + eval-expression-minibuffer-setup-hook + lisp-interaction-mode-hook + lisp-mode-hook scheme-mode-hook + fennel-mode-hook fennel-repl-mode-hook + geiser-mode-hook geiser-repl-mode-hook) + :config + (keymap-set paredit-mode-map "C-j" + (defun +paredit-newline + (interactive) + (call-interactively + (if (derived-mode-p 'lisp-interaction-mode) + #'eval-print-last-sexp #'paredit-newline)))) + (keymap-set paredit-mode-map "RET" nil) + (keymap-set paredit-mode-map "M-s" nil) + (add-to-list 'paredit-space-for-delimiter-predicates + (defun paredit@dont-space-@ (endp delimiter) + "Don't add a space after @ in `paredit-mode'." + (let ((point (point))) + (or endp + (seq-every-p + (lambda (prefix) + (and (> point (length prefix)) + (let ((start (- point (length prefix))) + (end point)) + (not (string= (buffer-substring start end) + prefix))))) + ;; Add strings to this list to inhibit adding a space + ;; after them. + '(",@")))))) + (with-eval-after-load 'eldoc + (eldoc-add-command #'paredit-backward-delete #'paredit-close-round))) + +(use-package hungry-delete + :ensure t + :config + (setopt hungry-delete-chars-to-skip " \t" + hungry-delete-skip-regexp (format "[%s]" hungry-delete-chars-to-skip) + hungry-delete-join-reluctantly nil) + (add-to-list 'hungry-delete-except-modes 'eshell-mode) + (add-to-list 'hungry-delete-except-modes 'nim-mode) + (add-to-list 'hungry-delete-except-modes 'python-mode) + ;; Keys + (with-eval-after-load 'paredit + (define-key paredit-mode-map [remap paredit-backward-delete] + (defun paredit/hungry-delete-backward (arg) + (interactive "*p") + (if (looking-back hungry-delete-skip-regexp) + (hungry-delete-backward (or arg 1)) + (paredit-backward-delete arg)))) + (define-key paredit-mode-map [remap paredit-forward-delete] + (defun paredit/hungry-delete-forward (arg) + (interactive "*p") + (if (looking-at hungry-delete-skip-regexp) + (hungry-delete-forward (or arg 1)) + (paredit-forward-delete arg)))) + ;; Mode + (global-hungry-delete-mode))) + +(use-package macrostep + :ensure t + :after elisp-mode + :bind ( :map emacs-lisp-mode-map + ("C-c e" . macrostep-expand) + :map lisp-interaction-mode-map + ("C-c e" . macrostep-expand))) + +(use-package package-lint + :ensure t) + +(use-package sly + :ensure t + :preface + (setopt inferior-lisp-program (choose-executable "sbcl")) + :when inferior-lisp-program + :bind (:map sly-mode-map + ("C-c C-z" . sly-mrepl)) + :config + (setopt sly-net-coding-system 'utf-8-unix) + (sly-symbol-completion-mode -1)) + +(use-package eat + :ensure t + :hook (eshell-load-hook . eat-eshell-mode)) + +(use-package pdf-tools + :ensure t + :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode) + :magic ("%PDF" . pdf-view-mode) + :config + (pdf-tools-install)) + +(use-package keychain-environment + :ensure t + :when (executable-find "keychain") + :hook (after-init-hook . keychain-refresh-environment)) + +(use-package web-mode + :ensure t + :mode ("\\.phtml\\'" + "\\.tpl\\.php\\'" + "\\.[agj]sp\\'" + "\\.as[cp]x\\'" + "\\.erb\\'" + "\\.mustache\\'" + "\\.djhtml\\'" + "\\.html?\\'")) + +(use-package nginx-mode + :ensure t + :mode "/nginx/sites-\\(?:available\\|enabled\\)/") + +(use-package markdown-mode + :ensure t + :mode "\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" + :config + (setopt markdown-command (choose-executable + '("pandoc" "--from=markdown" "--to=html5") + "markdown")) + (add-hook 'markdown-mode-hook #'visual-fill-column-mode)) + +(use-package transpose-frame + :ensure t + :bind (("C-x 5 t" . transpose-frame) + ("C-x 5 h" . flop-frame) ; horizontal + ("C-x 5 v" . flip-frame) ; vertical + )) + +(use-package magit + :pin melpa-stable + :ensure t + :bind ("C-x g" . magit)) diff --git a/lisp/acdw.el b/lisp/acdw.el new file mode 100644 index 0000000..f16a679 --- /dev/null +++ b/lisp/acdw.el @@ -0,0 +1,39 @@ +;;; acdw.el --- My Emacs extras -*- lexical-binding: t; -*- + +;;; Code: + +(defmacro defdir (name directory &optional docstring makedir) + "Define a variable and a function NAME expanding to DIRECTORY. +DOCSTRING is applied to the variable; its default is DIRECTORY's +path. If MAKEDIR is non-nil, the directory and its parents will +be created." + (declare (indent 2) (doc-string 3)) + `(progn + (defvar ,name (expand-file-name ,directory) + ,(concat (or docstring (format "%s" directory)) "\n" + "Defined by `defdir'.")) + (defun ,name (file &optional mkdir) + ,(concat "Expand FILE relative to variable `" (symbol-name name) "'.\n" + "If MKDIR is non-nil, parent directories are created.\n" + "Defined by `defdir'.") + (let ((file-name (expand-file-name + (convert-standard-filename file) ,name))) + (when mkdir + (make-directory (file-name-directory file-name) :parents)) + file-name)) + ,(if makedir + `(make-directory ,directory :parents) + `(unless (file-exists-p ,directory) + (warn "Directory `%s' doesn't exist." ,directory))))) + +(defun choose-executable (&rest programs) + "Return the first of PROGRAMS that exists in the system's $PATH. +Each of PROGRAMS can be a single string, or a list. If it's a list then its car +will be tested with `executable-find', and the entire list returned. This +enables passing arguments to a calling function." + (seq-find (lambda (x) + (executable-find (car (ensure-list x)))) + programs)) + +(provide 'acdw) +;;; acdw.el ends here -- cgit 1.4.1-21-gabe81