summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbd <bdunahu@operationnull.com>2024-10-27 21:11:19 -0400
committerbd <bdunahu@operationnull.com>2024-10-27 21:11:19 -0400
commit05791ee8b63f37513fb1eaf1dcef77de5227994d (patch)
tree90c2c3838945b91f7ce26821b1f3d0d5a83db12c
parent70507e97d5a5523c2667638f09208d47fdbcf670 (diff)
read desc: browsing+bookmarking interface, remove popper, no work...
browsing+bookmarking interface, remove popper, no workspaces only tabs, custom password-store, exwm use window title, colonq selector, theme changes
-rw-r--r--.config/emacs/gnus.el4
-rw-r--r--.config/emacs/init.el2
-rw-r--r--.config/emacs/libraries/selector.el654
-rw-r--r--.config/emacs/modules/bd--browse.el58
-rw-r--r--.config/emacs/modules/bd--essentials.el15
-rw-r--r--.config/emacs/modules/bd--exwm-windowing.el92
-rw-r--r--.config/emacs/modules/bd--files.el21
-rw-r--r--.config/emacs/modules/bd--gpg.el73
-rw-r--r--.config/emacs/modules/bd--minibuffer.el8
-rw-r--r--.config/emacs/modules/bd--shells.el4
-rw-r--r--.config/emacs/modules/bd--tabs.el3
-rw-r--r--.config/emacs/modules/bd--themes.el11
-rw-r--r--.config/emacs/modules/bd--windows.el42
-rw-r--r--.config/guix/modules/base.scm2
-rw-r--r--.config/guix/modules/emacs.scm6
-rw-r--r--.gitignore2
16 files changed, 833 insertions, 164 deletions
diff --git a/.config/emacs/gnus.el b/.config/emacs/gnus.el
index 986d0d0..2be2cf9 100644
--- a/.config/emacs/gnus.el
+++ b/.config/emacs/gnus.el
@@ -47,9 +47,7 @@
"%s\n"))
(setopt message-from-style 'angles
- mml-secure-openpgp-encrypt-to-self t)
-
-(require 'bd--mail) ;; (gitignored)
+ mml-secure-openpgp-encrypt-to-self t)
;;; gnus.el ends here
diff --git a/.config/emacs/init.el b/.config/emacs/init.el
index 20767cd..baae521 100644
--- a/.config/emacs/init.el
+++ b/.config/emacs/init.el
@@ -62,6 +62,7 @@
(require 'bd--essentials)
(require 'bd--rss)
(require 'bd--browse)
+(require 'bd--gpg)
(require 'bd--tabs)
(require 'bd--files)
(require 'bd--image)
@@ -77,6 +78,7 @@
(require 'bd--modeline)
(require 'bd--themes)
(require 'bd--exwm-windowing)
+(require 'bd--secret) ;; (gitignored)
;;; init.el ends here
diff --git a/.config/emacs/libraries/selector.el b/.config/emacs/libraries/selector.el
new file mode 100644
index 0000000..8b16637
--- /dev/null
+++ b/.config/emacs/libraries/selector.el
@@ -0,0 +1,654 @@
+;;; -*- lexical-binding: t; -*-
+;;; Commentary:
+;; modified version of lcolonq: https://github.com/lcolonq/emacs
+;;; Code:
+
+
+(require 'dash)
+(require 'recentf)
+
+(defgroup selector nil
+ "Efficient selection and navigation."
+ :group 'convenience)
+
+(defgroup selector-faces nil
+ "Faces for `selector'."
+ :group 'selector
+ :group 'faces)
+
+(defface selector-source-name
+ '((default :underline t :inherit bold)
+ (((class color) (background dark)) :foreground "white"))
+ "Face used to highlight source names.")
+
+(defface selector-highlight
+ '((t :inherit highlight))
+ "Face used to highlight the current selection.")
+
+(defvar selector-minibuffer-lines 20
+ "Number of lines to display in the minibuffer.")
+
+(defvar selector-exit-hook nil
+ "Hook run when exiting minibuffer selection.")
+
+(defvar selector--sources '())
+(defvar selector--last nil)
+(defvar selector--matching '())
+(defvar selector--source 0)
+(defvar selector--index 0)
+(defvar selector--action nil)
+(defvar selector--result nil)
+(defvar selector--drawn-this-frame 0)
+
+(defvar selector-minibuffer-map (make-sparse-keymap))
+(define-key selector-minibuffer-map (kbd "\\") (lambda () (interactive) nil))
+(define-key selector-minibuffer-map (kbd "\\") (lambda () (interactive) nil))
+(define-key selector-minibuffer-map (kbd "C-g") 'selector-quit)
+(define-key selector-minibuffer-map (kbd "C-c") 'selector-quit)
+(define-key selector-minibuffer-map (kbd "<return>") 'selector-do)
+(define-key selector-minibuffer-map (kbd "<backtab>") 'selector-previous)
+(define-key selector-minibuffer-map (kbd "<tab>") 'selector-next)
+(define-key selector-minibuffer-map (kbd "<up>") 'selector-previous)
+(define-key selector-minibuffer-map (kbd "<down>") 'selector-next)
+(define-key selector-minibuffer-map (kbd "<left>") 'selector-previous-source)
+(define-key selector-minibuffer-map (kbd "<right>") 'selector-next-source)
+(define-key selector-minibuffer-map (kbd "C-p") 'selector-previous)
+(define-key selector-minibuffer-map (kbd "C-n") 'selector-next)
+(define-key selector-minibuffer-map (kbd "C-b") 'selector-previous-source)
+(define-key selector-minibuffer-map (kbd "C-f") 'selector-next-source)
+(defun selector-minibuffer-line (str)
+ "Write STR to the minibuffer."
+ (goto-char (point-max))
+ (insert (concat "\n" str)))
+
+(defun selector-minibuffer-line-face (str face)
+ "Write STR to the minibuffer in FACE."
+ (cl-incf selector--drawn-this-frame) ;; sort of an ugly hack,
+ ;; but this fixes the annoying "cursor jumping" glitch when multiple monitors
+ ;; are active. the real fix probably should live in selector-nearby?
+ (when (< selector--drawn-this-frame selector-minibuffer-lines)
+ (let ((before (point)))
+ (selector-minibuffer-line str)
+ (goto-char before)
+ (forward-line)
+ (put-text-property (line-beginning-position) (point-max) 'face face))))
+
+(defun selector-minibuffer-clear ()
+ "Clear minibuffer."
+ (save-excursion
+ (goto-char (minibuffer-prompt-end))
+ (delete-region (line-end-position) (point-max))))
+
+(defun selector-minibuffer-input ()
+ "Get current minibuffer input."
+ (buffer-substring-no-properties
+ (minibuffer-prompt-end)
+ (line-end-position)))
+
+(cl-defstruct (selector-candidate (:constructor selector-candidate--create)
+ (:copier nil)
+ (:conc-name selector-candidate--))
+ type (display nil :type string) face value action)
+
+(cl-defun selector-candidate-create
+ (display &key
+ value
+ (type 'normal)
+ (face 'default)
+ (action '()))
+ "Create a candidate.
+DISPLAY is the string to display (using FACE) / match against.
+VALUE is the value to pass to actions when the candidate is selected.
+TYPE is either normal or dummy - dummy candidates always appear in the
+results list regardless of the input pattern.
+ACTION is an alist mapping keybindings to candidate-specific actions."
+ (selector-candidate--create
+ :type type
+ :display display
+ :face face
+ :value (if value value display)
+ :action action))
+
+(defun selector-candidate-display (candidate)
+ "Return the display string for CANDIDATE."
+ (cond ((selector-candidate-p candidate) (selector-candidate--display candidate))
+ (t candidate)))
+
+(defun selector-candidate-value (candidate)
+ "Return the value of CANDIDATE."
+ (cond ((selector-candidate-p candidate) (selector-candidate--value candidate))
+ (t candidate)))
+
+(defun selector-candidate-type (candidate)
+ "Return the candidate type of CANDIDATE."
+ (cond ((selector-candidate-p candidate) (selector-candidate--type candidate))
+ (t 'normal)))
+
+(defun selector-candidate-face (candidate)
+ "Return the display face for CANDIDATE."
+ (cond ((selector-candidate-p candidate) (selector-candidate--face candidate))
+ (t 'default)))
+
+(defun selector-candidate-action (candidate)
+ "Return the actions for CANDIDATE."
+ (cond ((selector-candidate-p candidate) (selector-candidate--action candidate))
+ (t '())))
+
+(defun selector-candidate-display-string (candidate)
+ "Return the display of CANDIDATE as a string."
+ (let ((display (selector-candidate-display candidate)))
+ (cond ((stringp display) display)
+ (t (error "Invalid candidate display %s for candidate %s (of type %s)"
+ display candidate (type-of display))))))
+
+(defun selector-highlight-candidate (candidate)
+ "Return a copy of CANDIDATE with the face set to selector-highlight."
+ (selector-candidate--create
+ :type (selector-candidate-type candidate)
+ :display (selector-candidate-display candidate)
+ :face 'selector-highlight
+ :value (selector-candidate-value candidate)
+ :action (selector-candidate-action candidate)))
+
+(defun selector-match (candidate regex)
+ "Determine whether CANDIDATE is a match for REGEX."
+ (let ((type (selector-candidate-type candidate)))
+ (cond ((eq 'dummy type) t)
+ (t (string-match-p regex (selector-candidate-display-string candidate))))))
+
+(cl-defstruct (selector-source (:constructor selector-source--create)
+ (:copier nil))
+ name candidates actions keymap)
+
+(defun selector-action-function (action)
+ "Return the function associated with ACTION."
+ (if (functionp action)
+ action
+ (cdr action)))
+
+(defun selector-actions-keymap (actions)
+ "Return a keymap for ACTIONS."
+ (let ((keymap (make-sparse-keymap)))
+ (set-keymap-parent keymap selector-minibuffer-map)
+ (mapc
+ (lambda (a)
+ (unless (functionp a)
+ (define-key keymap (car a)
+ (lambda () (interactive)
+ (selector-do (cdr a))))))
+ actions)
+ keymap))
+
+(cl-defun selector-source-create (name &key candidates (actions '()))
+ "Create a new source named NAME with the given CANDIDATES and ACTIONS.
+
+CANDIDATES is either:
+- A list of candidates
+- A function returning a list of candidates given a regex
+ACTIONS is a list of actions, which can be:
+- functions taking candidate values as arguments
+- pairs of key strings and such functions"
+ (selector-source--create
+ :name name
+ :candidates
+ (if (functionp candidates) candidates
+ (--map (if (selector-candidate-p it) it (selector-candidate-create it))
+ candidates))
+ :actions actions
+ :keymap (selector-actions-keymap actions)))
+
+(defun selector-matching-candidates (candidates pattern)
+ "Return the candidates in CANDIDATES matching PATTERN."
+ (cond ((functionp candidates) (funcall candidates pattern))
+ (t (let ((regex (selector-pattern-regex pattern)))
+ (--filter (selector-match it regex) candidates)))))
+
+(defun selector-filter-source (source pattern)
+ "Return a copy of SOURCE including only the candidates matching PATTERN."
+ (selector-source--create
+ :name (selector-source-name source)
+ :candidates (selector-matching-candidates (selector-source-candidates source) pattern)
+ :actions (selector-source-actions source)
+ :keymap (selector-source-keymap source)))
+
+(defun selector-pattern-regex (pattern)
+ "Convert PATTERN into a regular expression."
+ (apply #'string-join
+ (--map (concat "\\(" it "\\)") (split-string pattern))
+ '(".*")))
+
+(defun selector-matching-sources (sources pattern)
+ "Return the sources in SOURCES matching PATTERN."
+ (let* ((matches (--map (selector-filter-source it pattern) sources)))
+ (-filter #'selector-source-candidates matches)))
+
+(defun selector-display-source (source)
+ "Display SOURCE."
+ (when source
+ (selector-minibuffer-line-face (selector-source-name source) 'selector-source-name)
+ (--map (selector-minibuffer-line-face
+ (selector-candidate-display-string it)
+ (selector-candidate-face it))
+ (selector-source-candidates source))))
+
+(defun selector-nearby (sources)
+ "Filter SOURCES to only include candidates close to the selected candidate."
+ (let* ((adjacent
+ (--map
+ (cond ((and (< (cdr it) (+ selector--source selector-minibuffer-lines))
+ (> (cdr it) selector--source))
+ (cons (car it) 'g))
+ ((= (cdr it) selector--source)
+ (cons (car it) 'e))
+ (t nil))
+ (-zip-pair sources (number-sequence 0 (length sources))))))
+ (--map
+ (when it
+ (let* ((candidates (selector-source-candidates (car it))))
+ (selector-source--create
+ :name (selector-source-name (car it))
+ :candidates
+ (cond ((eq (cdr it) 'g)
+ (-take selector-minibuffer-lines candidates))
+ (t
+ (cl-loop for i from (max (- selector--index
+ (- (/ selector-minibuffer-lines 2) 1))
+ 0)
+ for j in (-take
+ selector-minibuffer-lines
+ (-drop
+ (- selector--index
+ (- (/ selector-minibuffer-lines 2) 1))
+ candidates))
+ collect (if (= i selector--index)
+ (selector-highlight-candidate j)
+ j))))
+ :actions (selector-source-actions (car it))
+ :keymap (selector-source-keymap (car it)))))
+ adjacent)))
+
+(defun selector-update-transient-map ()
+ "Update the transient keymap to match the current source."
+ (let ((source (car (nthcdr selector--source selector--matching))))
+ (when source
+ (set-transient-map (selector-source-keymap source)))))
+
+(defun selector-minibuffer-render ()
+ "Draw matching candidates to minibuffer."
+ (setq selector--drawn-this-frame 0)
+ (save-excursion
+ (let ((pattern (selector-minibuffer-input)))
+ (unless (string= pattern selector--last)
+ (setq selector--last pattern
+ selector--index 0
+ selector--source 0
+ selector--matching (selector-matching-sources selector--sources pattern))))
+ (-map #'selector-display-source (selector-nearby selector--matching))
+ (goto-char (minibuffer-prompt-end))
+ (put-text-property (line-end-position) (point-max) 'readonly t))
+ (selector-update-transient-map))
+
+(defun selector-minibuffer-setup (initial)
+ "Ready minibuffer for completion with INITIAL as initial input."
+ (add-hook 'pre-command-hook 'selector-minibuffer-clear nil t)
+ (add-hook 'post-command-hook 'selector-minibuffer-render nil t)
+ (setq-local max-mini-window-height selector-minibuffer-lines)
+ (when initial
+ (save-excursion
+ (minibuffer-prompt-end)
+ (insert initial)))
+ (end-of-line)
+ (selector-update-transient-map))
+
+(defun selector-previous-source ()
+ "Move to the previous source."
+ (interactive)
+ (setq selector--index 0)
+ (setq selector--source (if (= selector--source 0)
+ (- (length selector--matching) 1)
+ (- selector--source 1))))
+
+(defun selector-next-source ()
+ "Move to the next source."
+ (interactive)
+ (setq selector--index 0)
+ (setq selector--source (% (+ selector--source 1) (length selector--matching))))
+
+(defun selector-previous ()
+ "Move to the previous candidate."
+ (interactive)
+ (let* ((new-source-index (if (= selector--source 0)
+ (- (length selector--matching) 1)
+ (- selector--source 1)))
+ (source (car (nthcdr new-source-index selector--matching))))
+ (setq selector--index (- selector--index 1))
+ (when (< selector--index 0)
+ (setq selector--index (- (length (selector-source-candidates source)) 1)
+ selector--source new-source-index))))
+
+(defun selector-next ()
+ "Move to the next candidate."
+ (interactive)
+ (let* ((source (car (nthcdr selector--source selector--matching))))
+ (setq selector--index (+ selector--index 1))
+ (when (= selector--index (length (selector-source-candidates source)))
+ (setq selector--index 0
+ selector--source (% (+ selector--source 1) (length selector--matching))))))
+
+(defun selector-quit ()
+ "Quit the selection interface without running an action."
+ (interactive)
+ (run-hooks 'selector-exit-hook)
+ (keyboard-escape-quit))
+
+(defun selector-do (&optional action-function)
+ "Act upon selected candidate.
+If ACTION-FUNCTION is given use it, otherwise use the first action for the candidate."
+ (interactive)
+ (if (null selector--matching)
+ (progn
+ (setq selector--action (lambda (x) x)
+ selector--result nil))
+ (progn
+ (let* ((source (car (nthcdr selector--source selector--matching)))
+ (candidate (car (nthcdr selector--index (selector-source-candidates source)))))
+ (setq selector--action (cond (action-function
+ action-function)
+ ((selector-candidate-action candidate)
+ (selector-candidate-action candidate))
+ (t
+ (let ((actions (selector-source-actions source)))
+ (if actions
+ (selector-action-function (car actions))
+ (lambda (x) x)))))
+ selector--result (selector-candidate-value candidate)))))
+ (run-hooks 'selector-exit-hook)
+ (exit-minibuffer))
+
+;;;###autoload
+(cl-defun selector (sources &key prompt initial)
+ "Select a candidate and run an action using SOURCES.
+Display PROMPT as the prompt, or \"pattern: \" if not given.
+Use INITIAL as the initial input."
+ (setq selector--sources sources
+ selector--last nil
+ selector--matching (selector-matching-sources sources "")
+ selector--source 0
+ selector--index 0
+ selector--action nil
+ selector--result nil)
+ (let ((inhibit-message t))
+ (minibuffer-with-setup-hook
+ (apply-partially 'selector-minibuffer-setup initial)
+ (read-from-minibuffer (or prompt "pattern: ") nil selector-minibuffer-map)))
+ (funcall selector--action selector--result))
+
+(defvar selector-completing-read-candidate-transformer (lambda (x) x))
+
+;;;###autoload
+(defun selector-completing-read (prompt collection &optional predicate require-match
+ initial-input hist def inherit-input-method)
+ "Replacement for `completing-read'.
+PROMPT, COLLECTION, PREDICATE, REQUIRE-MATCH, INITIAL-INPUT, HIST, DEF, and
+INHERIT-INPUT-METHOD have the same meaning as in `completing-read'."
+ (ignore predicate hist def inherit-input-method)
+ (let ((unspecified-source
+ (if require-match
+ '()
+ (list
+ (selector-source-create
+ "Other"
+ :candidates
+ (list (selector-candidate-create
+ "Specify"
+ :type 'dummy
+ :action (lambda (_) (selector-input)))))))))
+ (or
+ (cond ((functionp collection)
+ (selector
+ (cons
+ (selector-source-create
+ "Completions"
+ :candidates
+ (-non-nil
+ (--map
+ (when-let ((disp (funcall selector-completing-read-candidate-transformer it)))
+ (selector-candidate-create disp :value it))
+ (funcall collection "" nil t))))
+ unspecified-source)
+ :prompt prompt
+ :initial initial-input))
+ ((hash-table-p collection)
+ (selector
+ (cons
+ (selector-source-create
+ "Completions"
+ :candidates (hash-table-keys collection))
+ unspecified-source)
+ :prompt prompt
+ :initial initial-input))
+ ((obarrayp collection)
+ (let ((candidates (list)))
+ (mapatoms (lambda (x) (push (selector-candidate-create (symbol-name x)) candidates)) collection)
+ (selector
+ (cons (selector-source-create "Completions" :candidates candidates) unspecified-source)
+ :prompt prompt
+ :initial initial-input)))
+ (t (selector
+ (cons (selector-source-create
+ "Completions"
+ :candidates
+ (--map (if (consp it)
+ (selector-candidate-create (car it))
+ it)
+ collection))
+ unspecified-source)
+ :prompt prompt
+ :initial initial-input)))
+ (selector-input))))
+
+(defun selector-input () "Return last minibuffer input." selector--last)
+
+(defvar selector-extended-command-actions
+ (list (lambda (c)
+ (add-to-list 'extended-command-history c)
+ (command-execute (intern-soft c)))
+ (cons (kbd "C-h") (lambda (c) (describe-function (intern-soft c))))))
+
+;;;###autoload
+(defun selector-extended-commands-source ()
+ "Source for extended commands (`M-x')."
+ (selector-source-create
+ "Commands"
+ :candidates
+ (-map
+ #'selector-candidate-create
+ (all-completions "" obarray #'commandp))
+ :actions
+ selector-extended-command-actions))
+
+;;;###autoload
+(defun selector-extended-command-history-source ()
+ "Source for extended command history."
+ (selector-source-create
+ "Command History"
+ :candidates
+ (-map
+ #'selector-candidate-create
+ extended-command-history)
+ :actions
+ selector-extended-command-actions))
+
+;;;###autoload
+(defun selector-apropos-command-source ()
+ "Source for command lookup."
+ (selector-source-create
+ "Commands"
+ :candidates
+ (lambda (r) (-map #'selector-candidate-create (all-completions r obarray #'commandp)))
+ :actions
+ (list (lambda (c) (describe-function (intern-soft c))))))
+
+;;;###autoload
+(defun selector-apropos-function-source ()
+ "Source for function lookup."
+ (selector-source-create
+ "Functions"
+ :candidates
+ (lambda (r) (-map #'selector-candidate-create (all-completions r obarray #'fboundp)))
+ :actions
+ (list (lambda (c) (describe-function (intern-soft c))))))
+
+;;;###autoload
+(defun selector-apropos-variable-source ()
+ "Source for variable lookup."
+ (selector-source-create
+ "Variables"
+ :candidates
+ (lambda (r) (-map #'selector-candidate-create
+ (all-completions
+ r obarray
+ (lambda (x) (let ((sym (intern-soft x)))
+ (and (boundp sym) (not (keywordp sym))))))))
+ :actions
+ (list (lambda (c) (describe-variable (intern-soft c))))))
+
+(defvar selector-buffer-actions
+ (list 'switch-to-buffer
+ (cons (kbd "M-D") 'kill-buffer)))
+
+;;;###autoload
+(defun selector-buffers-source (&optional sort-pred)
+ "Source for open buffers.
+An optional SORT-PRED may be provided to sort the buffers (see `sort')."
+ (selector-source-create
+ "Buffers"
+ :candidates
+ (--map (selector-candidate-create (buffer-name it))
+ (if sort-pred (sort (buffer-list) sort-pred) (buffer-list)))
+ :actions
+ selector-buffer-actions))
+
+;;;###autoload
+(defun selector-create-buffer-source ()
+ "Dummy source to create a buffer."
+ (selector-source-create
+ "Other"
+ :candidates
+ (list (selector-candidate-create
+ "Create buffer"
+ :type 'dummy
+ :action (lambda (_) (switch-to-buffer (selector-input)))))))
+
+(defvar selector-file-actions
+ (list 'find-file
+ (cons (kbd "M-D") (lambda (f)
+ (when (y-or-n-p (concat "Delete file " f "? "))
+ (delete-file f))))))
+
+;;;###autoload
+(defun selector-files-source ()
+ "Source for files in current directory."
+ (selector-source-create
+ "Files"
+ :candidates
+ (-map #'selector-candidate-create (directory-files default-directory))
+ :actions
+ selector-file-actions))
+
+;;;###autoload
+(defun selector-create-file-source ()
+ "Dummy source to create a file."
+ (selector-source-create
+ "Other"
+ :candidates
+ (list (selector-candidate-create
+ "Create file"
+ :type 'dummy
+ :action (lambda (_) (find-file (selector-input)))))))
+
+;;;###autoload
+(defun selector-recentf-source ()
+ "Source for recentf."
+ (selector-source-create
+ "Recent Files"
+ :candidates
+ (-map #'selector-candidate-create recentf-list)
+ :actions
+ selector-file-actions))
+
+;;;###autoload
+(defun selector-M-x ()
+ "Preconfigured `selector' interface to replace `execute-external-command'."
+ (interactive)
+ (selector (list (selector-extended-command-history-source)
+ (selector-extended-commands-source))))
+
+;;;###autoload
+(defun selector-apropos (&optional initial)
+ "Preconfigured `selector' interface to replace `apropos'.
+INITIAL is the initial text to match."
+ (interactive)
+ (selector (list (selector-apropos-command-source)
+ (selector-apropos-function-source)
+ (selector-apropos-variable-source))
+ :initial (if initial initial (thing-at-point 'symbol t))))
+
+;;;###autoload
+(defun selector-for-buffers ()
+ "Preconfigured `selector' interface for open buffers and recentf."
+ (interactive)
+ (selector (list (selector-buffers-source)
+ (selector-recentf-source)
+ (selector-create-buffer-source))))
+
+;;;###autoload
+(defun selector-for-files ()
+ "Preconfigured `selector' interface for files in the current directory."
+ (interactive)
+ (selector (list (selector-files-source)
+ (selector-create-file-source))))
+
+;;;###autoload
+(defun selector-read-file-name (prompt &optional dir default-filename mustmatch initial predicate)
+ "Replacement for `read-file-name'.
+PROMPT, DIR, DEFAULT-FILENAME, MUSTMATCH, INITIAL and PREDICATE have the same
+meaning as in `read-file-name'."
+ (ignore default-filename mustmatch predicate)
+ (let ((d (if dir dir default-directory)))
+ (concat d (selector (list (selector-source-create
+ "Files"
+ :candidates (-map #'selector-candidate-create (directory-files d)))
+ (selector-source-create
+ "Other"
+ :candidates (list (selector-candidate-create
+ "New file"
+ :type 'dummy
+ :action (lambda (_) (selector-input))))))
+ :prompt prompt
+ :initial initial))))
+
+(defun selector-file-contents-actions (file)
+ "Actions for candidate values corresponding to lines in FILE."
+ (list
+ (lambda (index)
+ (find-file file)
+ (goto-char (point-min))
+ (forward-line index)
+ (pulse-momentary-highlight-one-line (point)))))
+
+(defun selector-file-contents-source (file)
+ "Source for lines in FILE."
+ (selector-source-create
+ file
+ :candidates
+ (-map-indexed
+ (lambda (index l)
+ (selector-candidate-create l :value index))
+ (split-string (f-read-text file) "\n"))
+ :actions
+ (selector-file-contents-actions file)))
+
+
+(provide 'selector)
+;;; selector ends here
diff --git a/.config/emacs/modules/bd--browse.el b/.config/emacs/modules/bd--browse.el
index 1dd5b4a..83bf0a2 100644
--- a/.config/emacs/modules/bd--browse.el
+++ b/.config/emacs/modules/bd--browse.el
@@ -2,10 +2,60 @@
;;; Commentary:
;;; Code:
+(require 'selector)
+(require 'dash)
-;; do not use an external browser
-(setopt browse-url-browser-function 'eww-browse-url
- shr-use-fonts t
+(defvar bd/bookmarks nil) ;; in secret file
+;;;; searching
+(defun bd/browse (url &optional pref &rest _)
+ "Given PREF, launches URL in one of librewolf, torbrowser,
+icecat, or eww."
+ (interactive)
+ (pcase pref
+ (0 (eww url))
+ (1 (start-process "icecat" nil "icecat" "--new-window" url))
+ (2 (start-process "torbrowser" nil "torbrowser" "--new-window" url))
+ (_ (start-process "librewolf" nil "librewolf" "--new-window" url))))
+(setopt browse-url-browser-function 'bd/browse)
+
+(defun bd/selector-bookmarks ()
+ (selector-source-create
+ "Bookmarks"
+ :candidates
+ (-map
+ (lambda (b) (selector-candidate-create (car b) :value (cdr b)))
+ bd/bookmarks)
+ :actions
+ (list (lambda (x) (apply #'bd/browse x)))))
+
+(defmacro bd/search-candidate (name url pref)
+ `(selector-candidate-create
+ ,(concat "Search " name)
+ :type 'dummy
+ :action (lambda (_) (browse-url (concat ,url (selector-input)) ,pref))))
+
+(defun bd/selector-search ()
+ (selector-source-create
+ "Browser"
+ :candidates
+ (list (bd/search-candidate "DuckDuckGo" "https://www.duckduckgo.com/?q=" 3)
+ (bd/search-candidate "Wikipedia" "https://en.wikipedia.org/w/index.php?search=" 3)
+ (bd/search-candidate "Invidious" "https://yewtu.be/search?q=" 0)
+ (bd/search-candidate "Urban Dictionary" "https://www.urbandictionary.com/define.php?term=" 1)
+ (bd/search-candidate "Archwiki" "https://wiki.archlinux.org/index.php?title=Special%3ASearch&search=" 1)
+ (bd/search-candidate "Web" "" 3))))
+
+(defun bd/visit-bookmark ()
+ "Select and `browse-url' a bookmark."
+ (interactive)
+ (unwind-protect
+ (selector
+ (list (bd/selector-bookmarks)
+ (bd/selector-search)))))
+
+
+;;;; eww
+(setopt shr-use-fonts t
shr-cookie-policy nil
shr-max-width 85
;; send only user agent
@@ -16,7 +66,7 @@
"youtube.com"
"youtu.be"))
browse-url-handlers
- `((,(regexp-opt'("youtube.com" "youtu.be")) .
+ `((,(regexp-opt '("youtube.com" "youtu.be")) .
(lambda (url &rest _)
(message "Ludu %s" url)
(start-process-shell-command "rip-video" nil (concat "rip-video " url))))))
diff --git a/.config/emacs/modules/bd--essentials.el b/.config/emacs/modules/bd--essentials.el
index 04fc3a2..e8ea502 100644
--- a/.config/emacs/modules/bd--essentials.el
+++ b/.config/emacs/modules/bd--essentials.el
@@ -30,21 +30,6 @@
(advice-add command :after #'pulse-line))
-;;;; insecure passwording
-(use-package pinentry
- :config
- (pinentry-start)
- :custom
- (pinentry-popup-prompt-window nil))
-
-(defun pinentry-toggle ()
- "Stops and starts Pinentry service. Workaround
-for a bug I've encountered."
- (interactive)
- (pinentry-stop)
- (pinentry-start))
-
-
;;;; defaults
;; basic editing
(setopt kill-whole-line t
diff --git a/.config/emacs/modules/bd--exwm-windowing.el b/.config/emacs/modules/bd--exwm-windowing.el
index 1dc5056..ab5b243 100644
--- a/.config/emacs/modules/bd--exwm-windowing.el
+++ b/.config/emacs/modules/bd--exwm-windowing.el
@@ -19,68 +19,20 @@
(desktop-environment-volume-increment-slowly "+2%")
(desktop-environment-volume-toggle-regexp nil))
-
-(defun bd/exwm-update-class ()
- "Changes the buffer name to reflect the class name for
-that buffer."
- (exwm-workspace-rename-buffer exwm-class-name))
-
-(defun bd/exwm-init-hook ()
- "Make workspace 1 the default startup workspace."
- (exwm-workspace-switch-create 1))
-
-
-;;;; bookmarks, passwords
-(defun insert-bookmark ()
- "Inserts a url or string from permanent
-bookmark file. If an x window, place in kill-ring."
- ;; note xdotool had issues with repeated keys when sending to windowid
- (interactive)
- (let ((url (car (last (split-string (choose-line-from-file "~/.local/bin/bookmarks.txt"))))))
- (if (exwm--buffer->id (current-buffer))
- (kill-new url)
- (insert url))))
-
-(defun choose-line-from-file (file)
- "Uses completion-framework to allow user to
-select a line from a file."
- (let* ((choices (read-file-into-list file)))
- (completing-read "Recall: " choices)))
-
-(defun read-file-into-list (file)
- (with-current-buffer
- (find-file-noselect file)
- (split-string
- (save-restriction
- (widen)
- (buffer-substring-no-properties
- (point-min)
- (point-max)))
- "\n" t)))
-
-(use-package password-store
- :custom
- (password-store-time-before-clipboard-restore 20))
-
(use-package exwm
:config
- (add-hook 'exwm-init-hook #'bd/exwm-init-hook)
- (add-hook 'exwm-update-class-hook #'bd/exwm-update-class)
- ;; order is important
- ;; (dolist (k '(("s-B" "icecat")))
- ;; (let ((f (lambda () (interactive)
- ;; (save-window-excursion
- ;; (start-process-shell-command (cadr k) nil (cadr k))))))
- ;; (exwm-input-set-key (kbd (car k)) f)
- ;; (define-key exwm-mode-map (kbd (car k)) f)))
+ (defun bd/exwm-update-title ()
+ "Changes the buffer name to reflect the class name for
+that buffer."
+ (exwm-workspace-rename-buffer exwm-title))
+ (add-hook 'exwm-update-title-hook #'bd/exwm-update-title)
(define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key)
(exwm-enable)
(server-start)
:custom
- (exwm-workspace-number 2) ;; two workspaces
(exwm-input-prefix-keys
- '(?\C-x
+ `(?\C-x
?\C-u
?\C-g
?\C-h
@@ -90,9 +42,11 @@ select a line from a file."
?\M-`
?\M-&
?\M-:
- ))
+ ,@(mapcar (lambda (i)
+ (kbd (concat "s-" (number-to-string i))))
+ (number-sequence 0 9))))
(exwm-input-global-keys
- `(([?\s-n] . other-window)
+ '(([?\s-n] . other-window)
([?\s-p] . (lambda ()
(interactive)
(other-window -1)))
@@ -107,20 +61,14 @@ select a line from a file."
([f10] . emms-next)
([print] . desktop-environment-screenshot-part)
([S-print] . desktop-environment-screenshot)
- ([?\s-O] . insert-bookmark)
- ([?\s-P] . password-store-copy)
+ ([?\s-O] . bd/visit-bookmark)
+ ([?\s-P] . bd/password)
([?\s-r] . exwm-reset)
([?\s-d] . toggle-current-window-dedication)
([?\s-q] . kill-current-buffer)
([?\s-x] . (lambda (command)
(interactive (list (read-shell-command "s-x ")))
- (start-process-shell-command command nil command)))
- ,@(mapcar (lambda (i)
- `(,(kbd (format "s-%d" i)) .
- (lambda ()
- (interactive)
- (exwm-workspace-switch-create ,i))))
- (number-sequence 0 9))))
+ (start-process-shell-command command nil command)))))
(exwm-input-simulation-keys
'(([?\C-b] . [left])
([?\C-f] . [right])
@@ -133,10 +81,12 @@ select a line from a file."
([?\C-d] . [delete])
([?\C-k] . [S-end delete])
([?\M-w] . [C-c])
- ([?\C-y] . [C-v])))
+ ([?\C-y] . [C-v])
+ ([?\M-d] . [C-delete])
+ ([?\M-b] . [C-left])
+ ([?\M-f] . [C-right]))))
- (exwm-workspace-show-all-buffers t)
- (exwm-layout-show-all-buffers t))
+(setopt tab-bar-select-tab-modifiers '(super))
(defvar new-mode-line nil)
(defun set-new-mode-line ()
@@ -145,9 +95,9 @@ select a line from a file."
(replace-regexp-in-string
"%" "%%"
(format " %s %s %s"
- (shell-command-to-string "mail-string")
- (shell-command-to-string "power-string")
- (shell-command-to-string "t1-string")))))
+ (shell-command-to-string "/home/bdunahu/.local/bin/mail-string 2>/dev/null")
+ (shell-command-to-string "/home/bdunahu/.local/bin/power-string 2>/dev/null")
+ (shell-command-to-string "/home/bdunahu/.local/bin/t1-string 2>/dev/null")))))
(defvar-local bd/external-mode-line
'(:eval (when new-mode-line
diff --git a/.config/emacs/modules/bd--files.el b/.config/emacs/modules/bd--files.el
index b8a5f4a..6ea121b 100644
--- a/.config/emacs/modules/bd--files.el
+++ b/.config/emacs/modules/bd--files.el
@@ -3,14 +3,15 @@
;;; Code:
+(require 'recentf)
(defmacro bd/defun-find-file-wrapper (name types open-f)
`(defun ,name (f &rest args)
"Wrapper around F (find-file), passing ARGS."
(if (string-match (regexp-opt ,types) (or (file-name-extension (car args)) ""))
- (progn
- (,(eval open-f) (car args))
- (recentf-add-file (car args))
- nil)
+ (progn
+ (,(eval open-f) (car args))
+ (recentf-add-file (car args))
+ nil)
(apply f args))))
;;;; dired
@@ -39,8 +40,8 @@
(start-process (concat "zathura (" (file-name-base file) ")") nil "zathura" (expand-file-name file)))
(bd/defun-find-file-wrapper bd/pdf-find-file-wrapper
- '("epub" "pdf")
- #'bd/zathura)
+ '("epub" "pdf")
+ #'bd/zathura)
(advice-add 'find-file :around #'bd/pdf-find-file-wrapper)
;; video
@@ -50,8 +51,8 @@
(bd/defun-find-file-wrapper bd/video-find-file-wrapper
- '("mkv" "mov" "mp4" "webm" "m4v" "wav" "mp3" "opus" "ogv" "flac")
- #'bd/mpv)
+ '("mkv" "mov" "mp4" "webm" "m4v" "wav" "mp3" "opus" "ogv" "flac")
+ #'bd/mpv)
(advice-add 'find-file :around 'bd/video-find-file-wrapper)
;; image
@@ -59,8 +60,8 @@
"Open FILE with nsxiv"
(start-process (concat "nsxiv (" (file-name-base file) ")") nil "nsxiv" (expand-file-name file)))
(bd/defun-find-file-wrapper bd/image-find-file-wrapper
- '("jpg" "jpeg" "png" "webp" "bmp" "ico" "gif" "JPG" "PNG")
- #'bd/nsxiv)
+ '("jpg" "jpeg" "png" "webp" "bmp" "ico" "gif" "JPG" "PNG")
+ #'bd/nsxiv)
(advice-add 'find-file :around 'bd/image-find-file-wrapper)
diff --git a/.config/emacs/modules/bd--gpg.el b/.config/emacs/modules/bd--gpg.el
new file mode 100644
index 0000000..f3d55cb
--- /dev/null
+++ b/.config/emacs/modules/bd--gpg.el
@@ -0,0 +1,73 @@
+;;; -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+
+(require 'f)
+
+(defvar bd/password-store-kill-ring-pointer nil
+ "The tail of the kill ring whose car is the password.")
+(defvar bd/password-store-time-before-clear 10
+ "The time before a killed password is cleared.")
+
+;;;; passwords
+(defun bd/password-store-list ()
+ "List password-store entries."
+ (mapcar (lambda (file)
+ (f-no-ext (f-relative file "~/.password-store/")))
+ (f-files "~/.password-store" (lambda (file) (equal (f-ext file) "gpg")) t)))
+
+(defun bd/password-store-clear (id)
+ "Clears the most recent password copied to the kill ring."
+ (when bd/password-store-kill-ring-pointer
+ (setcar bd/password-store-kill-ring-pointer nil)
+ (kill-new "")
+ (setq bd/password-store-kill-ring-pointer nil)
+ (message "Cleared password for %s from the kill ring and system clipboard." id)))
+
+(defun bd/read-password (id)
+ "Read the password-store entry corresponding to ID."
+ (bd/password-store-clear "id")
+ (let ((find-file-hook (remq 'recentf-track-opened-file find-file-hook)))
+ (find-file (concat "~/.password-store/" id ".gpg"))
+ (goto-char 1)
+ (kill-new (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
+ (setq bd/password-store-kill-ring-pointer kill-ring-yank-pointer)
+ (kill-buffer (current-buffer))
+ (run-at-time bd/password-store-time-before-clear nil
+ (lambda () (funcall #'bd/password-store-clear id)))
+ (message "Copied password for %s to the kill ring and system clipboard. Will clear in %s seconds."
+ id bd/password-store-time-before-clear)))
+
+(defun bd/selector-passwords ()
+ "Selector source for password-store passwords."
+ (selector-source-create
+ "Passwords"
+ :candidates
+ (bd/password-store-list)
+ :actions
+ (list #'bd/read-password)))
+
+(defun bd/password ()
+ "Interactively select a password-store password."
+ (interactive)
+ (selector (list (bd/selector-passwords))))
+
+;; pinentry
+(use-package pinentry
+ :config
+ (pinentry-start)
+ :custom
+ (enable-recursive-minibuffers t)
+ (pinentry-popup-prompt-window nil))
+
+(defun pinentry-toggle ()
+ "Stops and starts Pinentry service. Workaround
+for a bug I've encountered."
+ (interactive)
+ (pinentry-stop)
+ (pinentry-start))
+
+
+(provide 'bd--gpg)
+;;; bd-gpg ends here
diff --git a/.config/emacs/modules/bd--minibuffer.el b/.config/emacs/modules/bd--minibuffer.el
index 66dffa5..02451bb 100644
--- a/.config/emacs/modules/bd--minibuffer.el
+++ b/.config/emacs/modules/bd--minibuffer.el
@@ -6,13 +6,9 @@
(setopt use-short-answers t
vc-follow-symlinks t
completion-ignore-case t
- read-buffer-completion-ignore-case t
- ;; required for exwm (pass) but
- ;; always useful
- enable-recursive-minibuffers t)
+ read-buffer-completion-ignore-case t)
(minibuffer-depth-indicate-mode 1)
-
(use-package vertico
:init
(vertico-mode)
@@ -43,7 +39,7 @@
("H-s G" . consult-git-grep)
("H-s r" . consult-ripgrep)
("H-s l" . consult-line)
- ("H-s i" . consult-info)
+ ("H-s i" . consult-info)
("H-s L" . consult-line-multi)
("H-s k" . consult-keep-lines)
("H-s u" . consult-focus-lines)))
diff --git a/.config/emacs/modules/bd--shells.el b/.config/emacs/modules/bd--shells.el
index dbb2f25..0bc41e2 100644
--- a/.config/emacs/modules/bd--shells.el
+++ b/.config/emacs/modules/bd--shells.el
@@ -44,6 +44,10 @@ clear the scrollback contents. Outputs banner message."
(unless eshell-non-interactive-p
(eval eshell-banner-message)))))
+(defun eshell/open (file)
+ (interactive)
+ (find-file file))
+
;;;; shell
(require 'shell)
diff --git a/.config/emacs/modules/bd--tabs.el b/.config/emacs/modules/bd--tabs.el
index 3ef18d8..f8ceab1 100644
--- a/.config/emacs/modules/bd--tabs.el
+++ b/.config/emacs/modules/bd--tabs.el
@@ -32,8 +32,7 @@
tab-bar-auto-width-max nil)
;;;; add useless text elements
-(setopt tab-bar-tab-hints nil
- tab-bar-select-tab-modifiers '(meta))
+(setopt tab-bar-tab-hints nil)
(defun bd/tab-bar-name-function ()
(concat (tab-bar-tab-name-current)
diff --git a/.config/emacs/modules/bd--themes.el b/.config/emacs/modules/bd--themes.el
index 9f7f404..220b181 100644
--- a/.config/emacs/modules/bd--themes.el
+++ b/.config/emacs/modules/bd--themes.el
@@ -48,21 +48,18 @@ to.")
(ef-themes-to-toggle '(ef-autumn ef-frost))
(ef-themes-common-palette-overrides
- '((bg-mode-line bg-main)
- (fg-mode-line fg-main)
- (bg-dim bg-main)
+ '((bg-dim bg-main)
(bg-alt bg-main)
(bg-tab-bar bg-main)
- (bg-tab-current bg-main)
+ (bg-tab-current bg-mode-line)
(bg-tab-other bg-inactive)))
- (ef-winter-palette-overrides
- '((bg-main "#000000")))
(ef-bio-palette-overrides
'((bg-main "#000000")))
(ef-autumn-palette-overrides
'((bg-main "#000000"))))
-(load-theme 'ef-autumn :no-confirm)
+(load-theme 'ef-winter :no-confirm)
+
(provide 'bd--themes)
;;; bd-themes ends here
diff --git a/.config/emacs/modules/bd--windows.el b/.config/emacs/modules/bd--windows.el
index 5a338bf..282ca14 100644
--- a/.config/emacs/modules/bd--windows.el
+++ b/.config/emacs/modules/bd--windows.el
@@ -52,48 +52,6 @@ Due to a bug with guix-packaged emacs, only uses true-transparency on wayland."
(add-to-list 'default-frame-alist '(alpha . (82 . 82))))
-(require 'project)
-;;;; popper
-(use-package popper
- :demand t
- :bind (("C-`" . 'popper-toggle-latest)
- ("M-`" . 'popper-cycle)
- ("C-M-`" . popper-toggle-type))
- :config
- (popper-mode)
- (popper-echo-mode)
- :custom
- (popper-reference-buffers
- '(
- ;; native
- ("\\*Async Shell Command\\*.*" . hide)
- compilation-mode
- completion-list-mode
- messages-buffer-mode
- occur-mode
- ("Output\\*$" . hide)
- ;; info
- help-mode
- Info-mode
- "^\\*slime-description.*\\*$"
- ;; repls
- geiser-repl-mode
- slime-repl-mode
- ;; shells
- "^\\*eshell.*\\*$" eshell-mode
- "^\\*shell.*\\*$" shell-mode
- "^\\*term.*\\*$" term-mode
- ;; magit
- magit-diff-mode
- magit-status-mode
- ;; emms
- emms-playlist-mode
- ))
- (popper-display-control 'nil)
- (popper-mode-line nil)
- (popper-group-function #'popper-group-by-project))
-
-
;;;; shackle
(use-package shackle
:demand t
diff --git a/.config/guix/modules/base.scm b/.config/guix/modules/base.scm
index 13f25a3..9aa6d5d 100644
--- a/.config/guix/modules/base.scm
+++ b/.config/guix/modules/base.scm
@@ -30,6 +30,7 @@
admin ;; netcat
algebra ;; bc
bittorrent ;; transmission
+ commencement ;; gcc-toolchain
compression ;; unzip
cryptsetup ;; cryptsetup
curl ;; curl
@@ -81,6 +82,7 @@
bc
cryptsetup
curl
+ gcc-toolchain
git
jq
man-pages
diff --git a/.config/guix/modules/emacs.scm b/.config/guix/modules/emacs.scm
index b1f58d6..c6ff522 100644
--- a/.config/guix/modules/emacs.scm
+++ b/.config/guix/modules/emacs.scm
@@ -25,24 +25,24 @@
(list
emacs-auctex
emacs-consult
+ emacs-dash
emacs-denote
emacs-desktop-environment
emacs-ef-themes
emacs-emms
emacs-exwm
+ emacs-f
emacs-guix
emacs-lua-mode
emacs-magit
emacs-marginalia
emacs-orderless
emacs-paredit
- emacs-password-store
emacs-pinentry
- emacs-popper
emacs-rainbow-delimiters
emacs-rainbow-mode
emacs-shackle
- ;; emacs-transmission
+ emacs-transmission
emacs-vertico
emacs-visual-fill-column
diff --git a/.gitignore b/.gitignore
index 7e969a3..a79c9a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@
/.config/emacs/*.desktop
/.config/emacs/projects
/.config/emacs/eshell/
-/.config/emacs/modules/bd--mail.el
/.config/emacs/gnus-home/
/.local/bin/
/.config/guix/modules/mail.scm
+/.config/emacs/modules/bd--secret.el