More Elisp linting
This should cover most of the remaining linting errors. After this, I expect fewer than ten linting errors.
This commit is contained in:
		
							parent
							
								
									a638e15c0d
								
							
						
					
					
						commit
						fb5ec068dd
					
				
					 47 changed files with 1049 additions and 989 deletions
				
			
		|  | @ -2,4 +2,4 @@ | |||
| # name: redux-action | ||||
| # key: rax | ||||
| # -- | ||||
| export const ${1:$$(string-lower->caps yas-text)} = '`(downcase (buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' | ||||
| export const ${1:$$(string-lower->caps yas-text)} = '`(downcase (functions-buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' | ||||
|  | @ -2,4 +2,4 @@ | |||
| # name: typed-redux-action | ||||
| # key: trax | ||||
| # -- | ||||
| export const ${1:$$(string-lower->caps yas-text)}: '`(downcase (buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' = '`(downcase (buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' | ||||
| export const ${1:$$(string-lower->caps yas-text)}: '`(downcase (functions-buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' = '`(downcase (buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' | ||||
|  | @ -1,5 +1,9 @@ | |||
| ;;; alist.el --- Interface for working with associative lists -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "25.1")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Firstly, a rant: | ||||
|  | @ -89,7 +93,7 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst alist/enable-tests? t | ||||
| (defconst alist-enable-tests? t | ||||
|   "When t, run the test suite.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|  | @ -97,21 +101,21 @@ | |||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| ;; TODO: Support a variadic version of this to easily construct alists. | ||||
| (defun alist/new () | ||||
| (defun alist-new () | ||||
|   "Return a new, empty alist." | ||||
|   '()) | ||||
| 
 | ||||
| ;; Create | ||||
| ;; TODO: See if this mutates. | ||||
| (defun alist/set (k v xs) | ||||
| (defun alist-set (k v xs) | ||||
|   "Set K to V in XS." | ||||
|   (if (alist/has-key? k xs) | ||||
|   (if (alist-has-key? k xs) | ||||
|       (progn | ||||
|         (setf (alist-get k xs) v) | ||||
|         xs) | ||||
|     (list/cons `(,k . ,v) xs))) | ||||
|     (list-cons `(,k . ,v) xs))) | ||||
| 
 | ||||
| (defun alist/set! (k v xs) | ||||
| (defun alist-set! (k v xs) | ||||
|   "Set K to V in XS mutatively. | ||||
| Note that this doesn't append to the alist in the way that most alists handle | ||||
|   writing.  If the k already exists in XS, it is overwritten." | ||||
|  | @ -119,113 +123,113 @@ Note that this doesn't append to the alist in the way that most alists handle | |||
|   (map-put xs k v)) | ||||
| 
 | ||||
| ;; Read | ||||
| (defun alist/get (k xs) | ||||
| (defun alist-get (k xs) | ||||
|   "Return the value at K in XS; otherwise, return nil. | ||||
| Returns the first occurrence of K in XS since alists support multiple entries." | ||||
|   (cdr (assoc k xs))) | ||||
| 
 | ||||
| (defun alist/get-entry (k xs) | ||||
| (defun alist-get-entry (k xs) | ||||
|   "Return the first key-value pair at K in XS." | ||||
|   (assoc k xs)) | ||||
| 
 | ||||
| ;; Update | ||||
| ;; TODO: Add warning about only the first occurrence being updated in the | ||||
| ;; documentation. | ||||
| (defun alist/update (k f xs) | ||||
| (defun alist-update (k f xs) | ||||
|   "Apply F to the value stored at K in XS. | ||||
| If `K' is not in `XS', this function errors.  Use `alist/upsert' if you're | ||||
| If `K' is not in `XS', this function errors.  Use `alist-upsert' if you're | ||||
| interested in inserting a value when a key doesn't already exist." | ||||
|   (if (maybe-nil? (alist/get k xs)) | ||||
|   (if (maybe-nil? (alist-get k xs)) | ||||
|       (error "Refusing to update: key does not exist in alist") | ||||
|     (alist/set k (funcall f (alist/get k xs)) xs))) | ||||
|     (alist-set k (funcall f (alist-get k xs)) xs))) | ||||
| 
 | ||||
| (defun alist/update! (k f xs) | ||||
| (defun alist-update! (k f xs) | ||||
|   "Call F on the entry at K in XS. | ||||
| Mutative variant of `alist/update'." | ||||
|   (alist/set! k (funcall f (alist/get k xs))xs)) | ||||
| Mutative variant of `alist-update'." | ||||
|   (alist-set! k (funcall f (alist-get k xs))xs)) | ||||
| 
 | ||||
| ;; TODO: Support this. | ||||
| (defun alist/upsert (k v f xs) | ||||
| (defun alist-upsert (k v f xs) | ||||
|   "If K exists in `XS' call `F' on the value otherwise insert `V'." | ||||
|   (if (alist/get k xs) | ||||
|       (alist/update k f xs) | ||||
|     (alist/set k v xs))) | ||||
|   (if (alist-get k xs) | ||||
|       (alist-update k f xs) | ||||
|     (alist-set k v xs))) | ||||
| 
 | ||||
| ;; Delete | ||||
| ;; TODO: Make sure `delete' and `remove' behave as advertised in the Elisp docs. | ||||
| (defun alist/delete (k xs) | ||||
| (defun alist-delete (k xs) | ||||
|   "Deletes the entry of K from XS. | ||||
| This only removes the first occurrence of K, since alists support multiple | ||||
|   key-value entries.  See `alist/delete-all' and `alist/dedupe'." | ||||
|   key-value entries.  See `alist-delete-all' and `alist-dedupe'." | ||||
|   (remove (assoc k xs) xs)) | ||||
| 
 | ||||
| (defun alist/delete! (k xs) | ||||
| (defun alist-delete! (k xs) | ||||
|   "Delete the entry of K from XS. | ||||
| Mutative variant of `alist/delete'." | ||||
| Mutative variant of `alist-delete'." | ||||
|   (delete (assoc k xs) xs)) | ||||
| 
 | ||||
| ;; Additions to the CRUD API | ||||
| ;; TODO: Implement this function. | ||||
| (defun alist/dedupe-keys (xs) | ||||
| (defun alist-dedupe-keys (xs) | ||||
|   "Remove the entries in XS where the keys are `equal'.") | ||||
| 
 | ||||
| (defun alist/dedupe-entries (xs) | ||||
| (defun alist-dedupe-entries (xs) | ||||
|   "Remove the entries in XS where the key-value pair are `equal'." | ||||
|   (delete-dups xs)) | ||||
| 
 | ||||
| (defun alist/keys (xs) | ||||
| (defun alist-keys (xs) | ||||
|   "Return a list of the keys in XS." | ||||
|   (mapcar 'car xs)) | ||||
| 
 | ||||
| (defun alist/values (xs) | ||||
| (defun alist-values (xs) | ||||
|   "Return a list of the values in XS." | ||||
|   (mapcar 'cdr xs)) | ||||
| 
 | ||||
| (defun alist/has-key? (k xs) | ||||
| (defun alist-has-key? (k xs) | ||||
|   "Return t if XS has a key `equal' to K." | ||||
|   (maybe-some? (assoc k xs))) | ||||
| 
 | ||||
| (defun alist/has-value? (v xs) | ||||
| (defun alist-has-value? (v xs) | ||||
|   "Return t if XS has a value of V." | ||||
|   (maybe-some? (rassoc v xs))) | ||||
| 
 | ||||
| (defun alist/count (xs) | ||||
| (defun alist-count (xs) | ||||
|   "Return the number of entries in XS." | ||||
|   (length xs)) | ||||
| 
 | ||||
| ;; TODO: Should I support `alist/find-key' and `alist/find-value' variants? | ||||
| (defun alist/find (p xs) | ||||
| ;; TODO: Should I support `alist-find-key' and `alist-find-value' variants? | ||||
| (defun alist-find (p xs) | ||||
|   "Apply a predicate fn, P, to each key and value in XS and return the key of | ||||
|   the first element that returns t." | ||||
|   (let ((result (list/find (lambda (x) (funcall p (car x) (cdr x))) xs))) | ||||
|   (let ((result (list-find (lambda (x) (funcall p (car x) (cdr x))) xs))) | ||||
|     (if result | ||||
|         (car result) | ||||
|       nil))) | ||||
| 
 | ||||
| (defun alist/map-keys (f xs) | ||||
| (defun alist-map-keys (f xs) | ||||
|   "Call F on the values in XS, returning a new alist." | ||||
|   (list/map (lambda (x) | ||||
|   (list-map (lambda (x) | ||||
|               `(,(funcall f (car x)) . ,(cdr x))) | ||||
|             xs)) | ||||
| 
 | ||||
| (defun alist/map-values (f xs) | ||||
| (defun alist-map-values (f xs) | ||||
|   "Call F on the values in XS, returning a new alist." | ||||
|   (list/map (lambda (x) | ||||
|   (list-map (lambda (x) | ||||
|               `(,(car x) . ,(funcall f (cdr x)))) | ||||
|             xs)) | ||||
| 
 | ||||
| (defun alist/reduce (acc f xs) | ||||
| (defun alist-reduce (acc f xs) | ||||
|   "Return a new alist by calling F on k v and ACC from XS. | ||||
| F should return a tuple.  See tuple.el for more information." | ||||
|   (->> (alist/keys xs) | ||||
|        (list/reduce acc | ||||
|   (->> (alist-keys xs) | ||||
|        (list-reduce acc | ||||
|                     (lambda (k acc) | ||||
|                       (funcall f k (alist/get k xs) acc))))) | ||||
|                       (funcall f k (alist-get k xs) acc))))) | ||||
| 
 | ||||
| (defun alist/merge (a b) | ||||
| (defun alist-merge (a b) | ||||
|   "Return a new alist with a merge of alists, A and B. | ||||
| In this case, the last writer wins, which is B." | ||||
|   (alist/reduce a #'alist/set b)) | ||||
|   (alist-reduce a #'alist-set b)) | ||||
| 
 | ||||
| ;; TODO: Support `-all' variants like: | ||||
| ;; - get-all | ||||
|  | @ -239,34 +243,34 @@ In this case, the last writer wins, which is B." | |||
|                   (first-name . "William") | ||||
|                   (last-name  . "Carroll") | ||||
|                   (last-name  . "Another"))) | ||||
|    (alist/set 'last-name "Van Gogh" person) | ||||
|    (alist/get 'last-name person) | ||||
|    (alist/update 'last-name (lambda (x) "whoops") person) | ||||
|    (alist/delete 'first-name person) | ||||
|    (alist/keys person) | ||||
|    (alist/values person) | ||||
|    (alist/count person) | ||||
|    (alist/has-key? 'first-name person) | ||||
|    (alist/has-value? "William" person) | ||||
|    ;; (alist/dedupe-keys person) | ||||
|    (alist/dedupe-entries person) | ||||
|    (alist/count person))) | ||||
|    (alist-set 'last-name "Van Gogh" person) | ||||
|    (alist-get 'last-name person) | ||||
|    (alist-update 'last-name (lambda (x) "whoops") person) | ||||
|    (alist-delete 'first-name person) | ||||
|    (alist-keys person) | ||||
|    (alist-values person) | ||||
|    (alist-count person) | ||||
|    (alist-has-key? 'first-name person) | ||||
|    (alist-has-value? "William" person) | ||||
|    ;; (alist-dedupe-keys person) | ||||
|    (alist-dedupe-entries person) | ||||
|    (alist-count person))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when alist/enable-tests? | ||||
| (when alist-enable-tests? | ||||
|   (prelude-assert | ||||
|    (equal '((2 . one) | ||||
|             (3 . two)) | ||||
|           (alist/map-keys #'1+ | ||||
|           (alist-map-keys #'1+ | ||||
|                           '((1 . one) | ||||
|                             (2 . two))))) | ||||
|   (prelude-assert | ||||
|    (equal '((one . 2) | ||||
|             (two . 3)) | ||||
|           (alist/map-values #'1+ | ||||
|           (alist-map-values #'1+ | ||||
|                             '((one . 1) | ||||
|                               (two . 2)))))) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; bag.el --- Working with bags (aka multi-sets) -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; What is a bag?  A bag should be thought of as a frequency table.  It's a way | ||||
|  | @ -31,36 +35,36 @@ | |||
| 
 | ||||
| (cl-defstruct bag xs) | ||||
| 
 | ||||
| (defun bag/update (f xs) | ||||
| (defun bag-update (f xs) | ||||
|   "Call F on alist in XS." | ||||
|   (let ((ys (bag-xs xs))) | ||||
|     (setf (bag-xs xs) (funcall f ys)))) | ||||
| 
 | ||||
| (defun bag/new () | ||||
| (defun bag-new () | ||||
|   "Create an empty bag." | ||||
|   (make-bag :xs (alist/new))) | ||||
|   (make-bag :xs (alist-new))) | ||||
| 
 | ||||
| (defun bag/contains? (x xs) | ||||
| (defun bag-contains? (x xs) | ||||
|   "Return t if XS has X." | ||||
|   (alist/has-key? x (bag-xs xs))) | ||||
|   (alist-has-key? x (bag-xs xs))) | ||||
| 
 | ||||
| ;; TODO: Tabling this for now since working with structs seems to be | ||||
| ;; disappointingly difficult.  Where is `struct-update'? | ||||
| ;; (defun bag/add (x xs) | ||||
| ;; (defun bag-add (x xs) | ||||
| ;;   "Add X to XS.") | ||||
| 
 | ||||
| ;; TODO: What do we name delete vs. remove? | ||||
| ;; (defun bag/remove (x xs) | ||||
| ;; (defun bag-remove (x xs) | ||||
| ;;   "Remove X from XS. | ||||
| ;; This is a no-op is X doesn't exist in XS.") | ||||
| 
 | ||||
| (defun bag/from-list (xs) | ||||
| (defun bag-from-list (xs) | ||||
|   "Map a list of `XS' into a bag." | ||||
|   (->> xs | ||||
|        (list/reduce | ||||
|         (bag/new) | ||||
|        (list-reduce | ||||
|         (bag-new) | ||||
|         (lambda (x acc) | ||||
|           (bag/add x 1 #'number/inc acc))))) | ||||
|           (bag-add x 1 #'number-inc acc))))) | ||||
| 
 | ||||
| (provide 'bag) | ||||
| ;;; bag.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; bookmark.el --- Saved files and directories on my filesystem -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; After enjoying and relying on Emacs's builtin `jump-to-register' command, I'd | ||||
|  | @ -29,7 +33,7 @@ | |||
| 
 | ||||
| (cl-defstruct bookmark label path kbd) | ||||
| 
 | ||||
| (defconst bookmark/install-kbds? t | ||||
| (defconst bookmark-install-kbds? t | ||||
|   "When t, install keybindings.") | ||||
| 
 | ||||
| ;; TODO: Consider hosting this function somewhere other than here, since it | ||||
|  | @ -38,7 +42,7 @@ | |||
| ;; `counsel-projectile-switch-project-action'.  See the noise I made on GH for | ||||
| ;; more context: https://github.com/ericdanan/counsel-projectile/issues/137 | ||||
| 
 | ||||
| (defun bookmark/handle-directory-dwim (path) | ||||
| (defun bookmark-handle-directory-dwim (path) | ||||
|   "Open PATH as either a project directory or a regular directory. | ||||
| If PATH is `projectile-project-p', open with `counsel-projectile-find-file'. | ||||
| Otherwise, open with `counsel-find-file'." | ||||
|  | @ -49,19 +53,19 @@ Otherwise, open with `counsel-find-file'." | |||
|     (let ((ivy-extra-directories nil)) | ||||
|       (counsel-find-file path)))) | ||||
| 
 | ||||
| (defconst bookmark/handle-directory #'bookmark/handle-directory-dwim | ||||
| (defconst bookmark-handle-directory #'bookmark-handle-directory-dwim | ||||
|   "Function to call when a bookmark points to a directory.") | ||||
| 
 | ||||
| (defconst bookmark/handle-file #'counsel-find-file-action | ||||
| (defconst bookmark-handle-file #'counsel-find-file-action | ||||
|   "Function to call when a bookmark points to a file.") | ||||
| 
 | ||||
| (defconst bookmark/whitelist | ||||
| (defconst bookmark-whitelist | ||||
|   (list | ||||
|    (make-bookmark :label "briefcase" | ||||
|                   :path constants/briefcase | ||||
|                   :path constants-briefcase | ||||
|                   :kbd "b") | ||||
|    (make-bookmark :label "current project" | ||||
|                   :path constants/current-project | ||||
|                   :path constants-current-project | ||||
|                   :kbd "p")) | ||||
|   "List of registered bookmarks.") | ||||
| 
 | ||||
|  | @ -69,18 +73,18 @@ Otherwise, open with `counsel-find-file'." | |||
| ;; API | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun bookmark/open (b) | ||||
| (defun bookmark-open (b) | ||||
|   "Open bookmark, B, in a new buffer or an ivy minibuffer." | ||||
|   (let ((path (bookmark-path b))) | ||||
|     (cond | ||||
|      ((f-directory? path) | ||||
|       (funcall bookmark/handle-directory path)) | ||||
|       (funcall bookmark-handle-directory path)) | ||||
|      ((f-file? path) | ||||
|       (funcall bookmark/handle-file path))))) | ||||
|       (funcall bookmark-handle-file path))))) | ||||
| 
 | ||||
| (when bookmark/install-kbds? | ||||
|   (->> bookmark/whitelist | ||||
|        (list/map | ||||
| (when bookmark-install-kbds? | ||||
|   (->> bookmark-whitelist | ||||
|        (list-map | ||||
|         (lambda (b) | ||||
|           (general-define-key | ||||
|            :prefix "<SPC>" | ||||
|  | @ -88,7 +92,7 @@ Otherwise, open with `counsel-find-file'." | |||
|            (string-concat "j" (bookmark-kbd b)) | ||||
|            ;; TODO: Consider `cl-labels' so `which-key' minibuffer is more | ||||
|            ;; helpful. | ||||
|            (lambda () (interactive) (bookmark/open b))))))) | ||||
|            (lambda () (interactive) (bookmark-open b))))))) | ||||
| 
 | ||||
| (provide 'bookmark) | ||||
| ;;; bookmark.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; buffer.el --- Working with Emacs buffers -*- lexical-binding: t -*- | ||||
| ;;; buffer.el --- Working with buffers -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Utilities for CRUDing buffers in Emacs. | ||||
|  | @ -33,14 +37,14 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst buffer/enable-tests? t | ||||
| (defconst buffer-enable-tests? t | ||||
|   "When t, run the test suite.") | ||||
| 
 | ||||
| (defconst buffer/install-kbds? t | ||||
| (defconst buffer-install-kbds? t | ||||
|   "When t, install the keybindings defined herein.") | ||||
| 
 | ||||
| (defconst buffer/source-code-blacklist | ||||
|   (set/new 'dired-mode | ||||
| (defconst buffer-source-code-blacklist | ||||
|   (set-new 'dired-mode | ||||
|            'erc-mode | ||||
|            'vterm-mode | ||||
|            'magit-status-mode | ||||
|  | @ -51,140 +55,140 @@ | |||
|            'fundamental-mode) | ||||
|   "A blacklist of major-modes to ignore for listing source code buffers.") | ||||
| 
 | ||||
| (defconst buffer/source-code-timeout 2 | ||||
| (defconst buffer-source-code-timeout 2 | ||||
|   "Number of seconds to wait before invalidating the cycle.") | ||||
| 
 | ||||
| (cl-defstruct source-code-cycle cycle last-called) | ||||
| 
 | ||||
| (defun buffer/emacs-generated? (name) | ||||
| (defun buffer-emacs-generated? (name) | ||||
|   "Return t if buffer, NAME, is an Emacs-generated buffer. | ||||
| Some buffers are Emacs-generated but are surrounded by whitespace." | ||||
|   (let ((trimmed (s-trim name))) | ||||
|     (and (s-starts-with? "*" trimmed)))) | ||||
| 
 | ||||
| (defun buffer/find (buffer-or-name) | ||||
| (defun buffer-find (buffer-or-name) | ||||
|   "Find a buffer by its BUFFER-OR-NAME." | ||||
|   (get-buffer buffer-or-name)) | ||||
| 
 | ||||
| (defun buffer/major-mode (name) | ||||
| (defun buffer-major-mode (name) | ||||
|   "Return the active `major-mode' in buffer, NAME." | ||||
|   (with-current-buffer (buffer/find name) | ||||
|   (with-current-buffer (buffer-find name) | ||||
|     major-mode)) | ||||
| 
 | ||||
| (defun buffer/source-code-buffers () | ||||
| (defun buffer-source-code-buffers () | ||||
|   "Return a list of source code buffers. | ||||
| This will ignore Emacs-generated buffers, like *Messages*.  It will also ignore | ||||
|   any buffer whose major mode is defined in `buffer/source-code-blacklist'." | ||||
|   any buffer whose major mode is defined in `buffer-source-code-blacklist'." | ||||
|   (->> (buffer-list) | ||||
|        (list/map #'buffer-name) | ||||
|        (list/reject #'buffer/emacs-generated?) | ||||
|        (list/reject (lambda (name) | ||||
|                       (set/contains? (buffer/major-mode name) | ||||
|                                      buffer/source-code-blacklist))))) | ||||
|        (list-map #'buffer-name) | ||||
|        (list-reject #'buffer-emacs-generated?) | ||||
|        (list-reject (lambda (name) | ||||
|                       (set-contains? (buffer-major-mode name) | ||||
|                                      buffer-source-code-blacklist))))) | ||||
| 
 | ||||
| (defvar buffer/source-code-cycle-state | ||||
| (defvar buffer-source-code-cycle-state | ||||
|   (make-source-code-cycle | ||||
|    :cycle (cycle/from-list (buffer/source-code-buffers)) | ||||
|    :cycle (cycle-from-list (buffer-source-code-buffers)) | ||||
|    :last-called (ts-now)) | ||||
|   "State used to manage cycling between source code buffers.") | ||||
| 
 | ||||
| (defun buffer/exists? (name) | ||||
| (defun buffer-exists? (name) | ||||
|   "Return t if buffer, NAME, exists." | ||||
|   (maybe-some? (buffer/find name))) | ||||
|   (maybe-some? (buffer-find name))) | ||||
| 
 | ||||
| (defun buffer/new (name) | ||||
| (defun buffer-new (name) | ||||
|   "Return a newly created buffer NAME." | ||||
|   (generate-new-buffer name)) | ||||
| 
 | ||||
| (defun buffer/find-or-create (name) | ||||
| (defun buffer-find-or-create (name) | ||||
|   "Find or create buffer, NAME. | ||||
| Return a reference to that buffer." | ||||
|   (let ((x (buffer/find name))) | ||||
|   (let ((x (buffer-find name))) | ||||
|     (if (maybe-some? x) | ||||
|         x | ||||
|       (buffer/new name)))) | ||||
|       (buffer-new name)))) | ||||
| 
 | ||||
| ;; TODO: Should this consume: `display-buffer' or `switch-to-buffer'? | ||||
| (defun buffer/show (buffer-or-name) | ||||
| (defun buffer-show (buffer-or-name) | ||||
|   "Display the BUFFER-OR-NAME, which is either a buffer reference or its name." | ||||
|   (display-buffer buffer-or-name)) | ||||
| 
 | ||||
| ;; TODO: Move this and `buffer/cycle-prev' into a separate module that | ||||
| ;; TODO: Move this and `buffer-cycle-prev' into a separate module that | ||||
| ;; encapsulates all of this behavior. | ||||
| 
 | ||||
| (defun buffer/cycle (cycle-fn) | ||||
|   "Cycle forwards or backwards through `buffer/source-code-buffers'." | ||||
| (defun buffer-cycle (cycle-fn) | ||||
|   "Cycle forwards or backwards through `buffer-source-code-buffers'." | ||||
|   (let ((last-called (source-code-cycle-last-called | ||||
|                       buffer/source-code-cycle-state)) | ||||
|                       buffer-source-code-cycle-state)) | ||||
|         (cycle (source-code-cycle-cycle | ||||
|                 buffer/source-code-cycle-state))) | ||||
|                 buffer-source-code-cycle-state))) | ||||
|     (if (> (ts-diff (ts-now) last-called) | ||||
|            buffer/source-code-timeout) | ||||
|            buffer-source-code-timeout) | ||||
|         (progn | ||||
|           (struct-set! source-code-cycle | ||||
|                        cycle | ||||
|                        (cycle/from-list (buffer/source-code-buffers)) | ||||
|                        buffer/source-code-cycle-state) | ||||
|                        (cycle-from-list (buffer-source-code-buffers)) | ||||
|                        buffer-source-code-cycle-state) | ||||
|           (let ((cycle (source-code-cycle-cycle | ||||
|                         buffer/source-code-cycle-state))) | ||||
|                         buffer-source-code-cycle-state))) | ||||
|             (funcall cycle-fn cycle) | ||||
|             (switch-to-buffer (cycle/current cycle))) | ||||
|             (switch-to-buffer (cycle-current cycle))) | ||||
|           (struct-set! source-code-cycle | ||||
|                        last-called | ||||
|                        (ts-now) | ||||
|                        buffer/source-code-cycle-state)) | ||||
|                        buffer-source-code-cycle-state)) | ||||
|       (progn | ||||
|         (funcall cycle-fn cycle) | ||||
|         (switch-to-buffer (cycle/current cycle)))))) | ||||
|         (switch-to-buffer (cycle-current cycle)))))) | ||||
| 
 | ||||
| (defun buffer/cycle-next () | ||||
|   "Cycle forward through the `buffer/source-code-buffers'." | ||||
| (defun buffer-cycle-next () | ||||
|   "Cycle forward through the `buffer-source-code-buffers'." | ||||
|   (interactive) | ||||
|   (buffer/cycle #'cycle/next)) | ||||
|   (buffer-cycle #'cycle-next)) | ||||
| 
 | ||||
| (defun buffer/cycle-prev () | ||||
|   "Cycle backward through the `buffer/source-code-buffers'." | ||||
| (defun buffer-cycle-prev () | ||||
|   "Cycle backward through the `buffer-source-code-buffers'." | ||||
|   (interactive) | ||||
|   (buffer/cycle #'cycle/prev)) | ||||
|   (buffer-cycle #'cycle-prev)) | ||||
| 
 | ||||
| (defun buffer/ivy-source-code () | ||||
| (defun buffer-ivy-source-code () | ||||
|   "Use `ivy-read' to choose among all open source code buffers." | ||||
|   (interactive) | ||||
|   (ivy-read "Source code buffer: " | ||||
|             (-drop 1 (buffer/source-code-buffers)) | ||||
|             (-drop 1 (buffer-source-code-buffers)) | ||||
|             :sort nil | ||||
|             :action #'switch-to-buffer)) | ||||
| 
 | ||||
| (defun buffer/show-previous () | ||||
| (defun buffer-show-previous () | ||||
|   "Call `switch-to-buffer' on the previously visited buffer. | ||||
| This function ignores Emacs-generated buffers, i.e. the ones that look like | ||||
|   this: *Buffer*.  It also ignores buffers that are `dired-mode' or `erc-mode'. | ||||
|   This blacklist can easily be changed." | ||||
|   (interactive) | ||||
|   (let* ((xs (buffer/source-code-buffers)) | ||||
|          (candidate (list/get 1 xs))) | ||||
|   (let* ((xs (buffer-source-code-buffers)) | ||||
|          (candidate (list-get 1 xs))) | ||||
|     (prelude-assert (maybe-some? candidate)) | ||||
|     (switch-to-buffer candidate))) | ||||
| 
 | ||||
| (when buffer/install-kbds? | ||||
| (when buffer-install-kbds? | ||||
|   (general-define-key | ||||
|    :states '(normal) | ||||
|    "C-f" #'buffer/cycle-next | ||||
|    "C-b" #'buffer/cycle-prev) | ||||
|    "C-f" #'buffer-cycle-next | ||||
|    "C-b" #'buffer-cycle-prev) | ||||
|   (general-define-key | ||||
|    :prefix "<SPC>" | ||||
|    :states '(normal) | ||||
|    "b" #'buffer/ivy-source-code | ||||
|    "<SPC>" #'buffer/show-previous | ||||
|    "b" #'buffer-ivy-source-code | ||||
|    "<SPC>" #'buffer-show-previous | ||||
|    "k" #'kill-buffer)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when buffer/enable-tests? | ||||
| (when buffer-enable-tests? | ||||
|   (prelude-assert | ||||
|    (list/all? #'buffer/emacs-generated? | ||||
|    (list-all? #'buffer-emacs-generated? | ||||
|               '("*scratch*" | ||||
|                 "*Messages*" | ||||
|                 "*shell*" | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; bytes.el --- Working with byte values -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Functions to help with human-readable representations of byte values. | ||||
|  | @ -40,49 +44,49 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst bytes/kb (math/exp 2 10) | ||||
| (defconst bytes-kb (math-exp 2 10) | ||||
|   "Number of bytes in a kilobyte.") | ||||
| 
 | ||||
| (defconst bytes/mb (math/exp 2 20) | ||||
| (defconst bytes-mb (math-exp 2 20) | ||||
|   "Number of bytes in a megabytes.") | ||||
| 
 | ||||
| (defconst bytes/gb (math/exp 2 30) | ||||
| (defconst bytes-gb (math-exp 2 30) | ||||
|   "Number of bytes in a gigabyte.") | ||||
| 
 | ||||
| (defconst bytes/tb (math/exp 2 40) | ||||
| (defconst bytes-tb (math-exp 2 40) | ||||
|   "Number of bytes in a terabyte.") | ||||
| 
 | ||||
| (defconst bytes/pb (math/exp 2 50) | ||||
| (defconst bytes-pb (math-exp 2 50) | ||||
|   "Number of bytes in a petabyte.") | ||||
| 
 | ||||
| (defconst bytes/eb (math/exp 2 60) | ||||
| (defconst bytes-eb (math-exp 2 60) | ||||
|   "Number of bytes in an exabyte.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Functions | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun bytes/classify (x) | ||||
| (defun bytes-classify (x) | ||||
|   "Return unit that closest fits byte count, X." | ||||
|   (prelude-assert (number/whole? x)) | ||||
|   (prelude-assert (number-whole? x)) | ||||
|   (cond | ||||
|    ((and (>= x 0)        (< x bytes/kb))     'byte) | ||||
|    ((and (>= x bytes/kb) (< x bytes/mb)) 'kilobyte) | ||||
|    ((and (>= x bytes/mb) (< x bytes/gb)) 'megabyte) | ||||
|    ((and (>= x bytes/gb) (< x bytes/tb)) 'gigabyte) | ||||
|    ((and (>= x bytes/tb) (< x bytes/pb)) 'terabyte) | ||||
|    ((and (>= x bytes/pb) (< x bytes/eb)) 'petabyte))) | ||||
|    ((and (>= x 0)        (< x bytes-kb))     'byte) | ||||
|    ((and (>= x bytes-kb) (< x bytes-mb)) 'kilobyte) | ||||
|    ((and (>= x bytes-mb) (< x bytes-gb)) 'megabyte) | ||||
|    ((and (>= x bytes-gb) (< x bytes-tb)) 'gigabyte) | ||||
|    ((and (>= x bytes-tb) (< x bytes-pb)) 'terabyte) | ||||
|    ((and (>= x bytes-pb) (< x bytes-eb)) 'petabyte))) | ||||
| 
 | ||||
| (defun bytes/to-string (x) | ||||
| (defun bytes-to-string (x) | ||||
|   "Convert integer X into a human-readable string." | ||||
|   (let ((base-and-unit | ||||
|          (pcase (bytes/classify x) | ||||
|          (pcase (bytes-classify x) | ||||
|            ('byte     (tuple/from        1 "B")) | ||||
|            ('kilobyte (tuple/from bytes/kb "KB")) | ||||
|            ('megabyte (tuple/from bytes/mb "MB")) | ||||
|            ('gigabyte (tuple/from bytes/gb "GB")) | ||||
|            ('terabyte (tuple/from bytes/tb "TB")) | ||||
|            ('petabyte (tuple/from bytes/pb "PB"))))) | ||||
|            ('kilobyte (tuple/from bytes-kb "KB")) | ||||
|            ('megabyte (tuple/from bytes-mb "MB")) | ||||
|            ('gigabyte (tuple/from bytes-gb "GB")) | ||||
|            ('terabyte (tuple/from bytes-tb "TB")) | ||||
|            ('petabyte (tuple/from bytes-pb "PB"))))) | ||||
|     (string-format "%d%s" | ||||
|                    (round x (tuple/first base-and-unit)) | ||||
|                    (tuple/second base-and-unit)))) | ||||
|  | @ -93,17 +97,17 @@ | |||
| 
 | ||||
| (progn | ||||
|   (prelude-assert | ||||
|    (equal "1000B" (bytes/to-string 1000))) | ||||
|    (equal "1000B" (bytes-to-string 1000))) | ||||
|   (prelude-assert | ||||
|    (equal "2KB" (bytes/to-string (* 2 bytes/kb)))) | ||||
|    (equal "2KB" (bytes-to-string (* 2 bytes-kb)))) | ||||
|   (prelude-assert | ||||
|    (equal "17MB" (bytes/to-string (* 17 bytes/mb)))) | ||||
|    (equal "17MB" (bytes-to-string (* 17 bytes-mb)))) | ||||
|   (prelude-assert | ||||
|    (equal "419GB" (bytes/to-string (* 419 bytes/gb)))) | ||||
|    (equal "419GB" (bytes-to-string (* 419 bytes-gb)))) | ||||
|   (prelude-assert | ||||
|    (equal "999TB" (bytes/to-string (* 999 bytes/tb)))) | ||||
|    (equal "999TB" (bytes-to-string (* 999 bytes-tb)))) | ||||
|   (prelude-assert | ||||
|    (equal "2PB" (bytes/to-string (* 2 bytes/pb))))) | ||||
|    (equal "2PB" (bytes-to-string (* 2 bytes-pb))))) | ||||
| 
 | ||||
| (provide 'bytes) | ||||
| ;;; bytes.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; cache.el --- Caching things -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; An immutable cache data structure. | ||||
|  | @ -19,6 +23,10 @@ | |||
| 
 | ||||
| ;;; Code: | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Dependencies | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (require 'prelude) | ||||
| (require 'struct) | ||||
| 
 | ||||
|  | @ -31,24 +39,24 @@ | |||
| ;; TODO: Prefer another KBD for yasnippet form completion than company-mode's | ||||
| ;; current KBD. | ||||
| 
 | ||||
| (defun cache/from-list (xs) | ||||
| (defun cache-from-list (xs) | ||||
|   "Turn list, XS, into a cache." | ||||
|   (make-cache :xs xs)) | ||||
| 
 | ||||
| (defun cache/contains? (x xs) | ||||
| (defun cache-contains? (x xs) | ||||
|   "Return t if X in XS." | ||||
|   (->> xs | ||||
|        cache-xs | ||||
|        (list/contains? x))) | ||||
|        (list-contains? x))) | ||||
| 
 | ||||
| (defun cache/touch (x xs) | ||||
| (defun cache-touch (x xs) | ||||
|   "Ensure value X in cache, XS, is front of the list. | ||||
| If X isn't in XS (using `equal'), insert it at the front." | ||||
|   (struct-update | ||||
|    cache | ||||
|    xs | ||||
|    (>> (list/reject (lambda (y) (equal x y))) | ||||
|        (list/cons x)) | ||||
|    (>> (list-reject (lambda (y) (equal x y))) | ||||
|        (list-cons x)) | ||||
|    xs)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|  | @ -56,25 +64,25 @@ If X isn't in XS (using `equal'), insert it at the front." | |||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (progn | ||||
|   (let ((cache (cache/from-list '("chicken" "nugget")))) | ||||
|   (let ((cache (cache-from-list '("chicken" "nugget")))) | ||||
|     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|     ;; contains?/2 | ||||
|     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|     (prelude-refute | ||||
|      (cache/contains? "turkey" cache)) | ||||
|      (cache-contains? "turkey" cache)) | ||||
|     (prelude-assert | ||||
|      (cache/contains? "chicken" cache)) | ||||
|      (cache-contains? "chicken" cache)) | ||||
|     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|     ;; touch/2 | ||||
|     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|     (prelude-assert | ||||
|      (equal | ||||
|       (cache/touch "nugget" cache) | ||||
|       (cache/from-list '("nugget" "chicken")))) | ||||
|       (cache-touch "nugget" cache) | ||||
|       (cache-from-list '("nugget" "chicken")))) | ||||
|     (prelude-assert | ||||
|      (equal | ||||
|       (cache/touch "spicy" cache) | ||||
|       (cache/from-list '("spicy" "chicken" "nugget")))))) | ||||
|       (cache-touch "spicy" cache) | ||||
|       (cache-from-list '("spicy" "chicken" "nugget")))))) | ||||
| 
 | ||||
| (provide 'cache) | ||||
| ;;; cache.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; clipboard.el --- Working with X11's pasteboard -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Simple functions for copying and pasting. | ||||
|  | @ -23,17 +27,17 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (cl-defun clipboard/copy (x &key (message "[clipboard.el] Copied!")) | ||||
| (cl-defun clipboard-copy (x &key (message "[clipboard.el] Copied!")) | ||||
|   "Copy string, X, to X11's clipboard." | ||||
|   (kill-new x) | ||||
|   (message message)) | ||||
| 
 | ||||
| (cl-defun clipboard/paste (&key (message "[clipboard.el] Pasted!")) | ||||
| (cl-defun clipboard-paste (&key (message "[clipboard.el] Pasted!")) | ||||
|   "Paste contents of X11 clipboard." | ||||
|   (yank) | ||||
|   (message message)) | ||||
| 
 | ||||
| (defun clipboard/contents () | ||||
| (defun clipboard-contents () | ||||
|   "Return the contents of the clipboard as a string." | ||||
|   (substring-no-properties (current-kill 0))) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; colorscheme.el --- Syntax highlight and friends -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; | ||||
|  | @ -21,76 +25,76 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defcustom colorscheme/install-kbds? t | ||||
| (defcustom colorscheme-install-kbds? t | ||||
|   "If non-nil, enable the keybindings.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defcustom colorscheme/whitelist | ||||
|   (cycle/from-list | ||||
| (defcustom colorscheme-whitelist | ||||
|   (cycle-from-list | ||||
|    (->> (custom-available-themes) | ||||
|         (list/map #'symbol-name) | ||||
|         (list/filter (>> (s-starts-with? "doom-"))) | ||||
|         (list/map #'intern))) | ||||
|         (list-map #'symbol-name) | ||||
|         (list-filter (>> (s-starts-with? "doom-"))) | ||||
|         (list-map #'intern))) | ||||
|   "The whitelist of colorschemes through which to cycle.") | ||||
| 
 | ||||
| (defun colorscheme/current () | ||||
| (defun colorscheme-current () | ||||
|   "Return the currently enabled colorscheme." | ||||
|   (cycle/current colorscheme/whitelist)) | ||||
|   (cycle-current colorscheme-whitelist)) | ||||
| 
 | ||||
| (defun colorscheme/disable-all () | ||||
| (defun colorscheme-disable-all () | ||||
|   "Disable all currently enabled colorschemes." | ||||
|   (interactive) | ||||
|   (->> custom-enabled-themes | ||||
|        (list/map #'disable-theme))) | ||||
|        (list-map #'disable-theme))) | ||||
| 
 | ||||
| (defun colorscheme/set (theme) | ||||
| (defun colorscheme-set (theme) | ||||
|     "Call `load-theme' with `THEME', ensuring that the line numbers are bright. | ||||
| There is no hook that I'm aware of to handle this more elegantly." | ||||
|     (load-theme theme t) | ||||
|     (prelude-set-line-number-color "#da5468")) | ||||
| 
 | ||||
| (defun colorscheme/whitelist-set (colorscheme) | ||||
|   "Focus the COLORSCHEME in the `colorscheme/whitelist' cycle." | ||||
|   (cycle/focus (lambda (x) (equal x colorscheme)) colorscheme/whitelist) | ||||
|   (colorscheme/set (colorscheme/current))) | ||||
| (defun colorscheme-whitelist-set (colorscheme) | ||||
|   "Focus the COLORSCHEME in the `colorscheme-whitelist' cycle." | ||||
|   (cycle-focus (lambda (x) (equal x colorscheme)) colorscheme-whitelist) | ||||
|   (colorscheme-set (colorscheme-current))) | ||||
| 
 | ||||
| (defun colorscheme/ivy-select () | ||||
| (defun colorscheme-ivy-select () | ||||
|   "Load a colorscheme using ivy." | ||||
|   (interactive) | ||||
|   (let ((theme (ivy-read "Theme: " (cycle/to-list colorscheme/whitelist)))) | ||||
|     (colorscheme/disable-all) | ||||
|     (colorscheme/set (intern theme)))) | ||||
|   (let ((theme (ivy-read "Theme: " (cycle-to-list colorscheme-whitelist)))) | ||||
|     (colorscheme-disable-all) | ||||
|     (colorscheme-set (intern theme)))) | ||||
| 
 | ||||
| (cl-defun colorscheme/cycle (&key forward?) | ||||
| (cl-defun colorscheme-cycle (&key forward?) | ||||
|   "Cycle next if `FORWARD?' is non-nil. | ||||
| Cycle prev otherwise." | ||||
|   (disable-theme (cycle/current colorscheme/whitelist)) | ||||
|   (disable-theme (cycle-current colorscheme-whitelist)) | ||||
|   (let ((theme (if forward? | ||||
|                    (cycle/next colorscheme/whitelist) | ||||
|                  (cycle/prev colorscheme/whitelist)))) | ||||
|     (colorscheme/set theme) | ||||
|                    (cycle-next colorscheme-whitelist) | ||||
|                  (cycle-prev colorscheme-whitelist)))) | ||||
|     (colorscheme-set theme) | ||||
|     (message (s-concat "Active theme: " (symbol-to-string theme))))) | ||||
| 
 | ||||
| (defun colorscheme/next () | ||||
| (defun colorscheme-next () | ||||
|   "Disable the currently active theme and load the next theme." | ||||
|   (interactive) | ||||
|   (colorscheme/cycle :forward? t)) | ||||
|   (colorscheme-cycle :forward? t)) | ||||
| 
 | ||||
| (defun colorscheme/prev () | ||||
| (defun colorscheme-prev () | ||||
|   "Disable the currently active theme and load the previous theme." | ||||
|   (interactive) | ||||
|   (colorscheme/cycle :forward? nil)) | ||||
|   (colorscheme-cycle :forward? nil)) | ||||
| 
 | ||||
| ;; Keybindings | ||||
| (when colorscheme/install-kbds? | ||||
| (when colorscheme-install-kbds? | ||||
|   (general-define-key | ||||
|    :prefix "<SPC>" | ||||
|    :states '(normal) | ||||
|    "Ft" #'colorscheme/next | ||||
|    "Pt" #'colorscheme/prev)) | ||||
|    "Ft" #'colorscheme-next | ||||
|    "Pt" #'colorscheme-prev)) | ||||
| 
 | ||||
| (provide 'colorscheme) | ||||
| ;;; colorscheme.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; constants.el --- Constants for organizing my Emacs -*- lexical-binding: t -*- | ||||
| ;; Authpr: William Carroll <wpcarro@gmail.com> | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; This file contains constants that are shared across my configuration. | ||||
|  | @ -20,11 +24,11 @@ | |||
| ;; Configuration | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst constants/ci? | ||||
| (defconst constants-ci? | ||||
|   (maybe-some? (getenv "CI")) | ||||
|   "True when Emacs is running in CI.") | ||||
| 
 | ||||
| (defconst constants/briefcase | ||||
| (defconst constants-briefcase | ||||
|   (getenv "BRIEFCASE") | ||||
|   "Path to my monorepo, which various parts of my configuration rely on.") | ||||
| 
 | ||||
|  | @ -32,11 +36,11 @@ | |||
| ;; current consumers of these constants, and I'm unsure if the indirection that | ||||
| ;; globally defined constants introduces is worth it. | ||||
| 
 | ||||
| (defconst constants/current-project | ||||
|   constants/briefcase | ||||
| (defconst constants-current-project | ||||
|   constants-briefcase | ||||
|   "Variable holding the directory for my currently active project.") | ||||
| 
 | ||||
| (defconst constants/mouse-kbds | ||||
| (defconst constants-mouse-kbds | ||||
|   '([mouse-1] [down-mouse-1] [drag-mouse-1] [double-mouse-1] [triple-mouse-1] | ||||
|     [mouse-2] [down-mouse-2] [drag-mouse-2] [double-mouse-2] [triple-mouse-2] | ||||
|     [mouse-3] [down-mouse-3] [drag-mouse-3] [double-mouse-3] [triple-mouse-3] | ||||
|  | @ -44,7 +48,7 @@ | |||
|     [mouse-5] [down-mouse-5] [drag-mouse-5] [double-mouse-5] [triple-mouse-5]) | ||||
|   "All of the mouse-related keybindings that Emacs recognizes.") | ||||
| 
 | ||||
| (defconst constants/fill-column 80 | ||||
| (defconst constants-fill-column 80 | ||||
|   "Variable used to set the defaults for wrapping, highlighting, etc.") | ||||
| 
 | ||||
| (provide 'constants) | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; cycle.el --- Simple module for working with cycles. -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Something like this may already exist, but I'm having trouble finding it, and | ||||
|  | @ -21,7 +25,7 @@ | |||
| 
 | ||||
| ;; - TODO: Provide immutable variant. | ||||
| ;; - TODO: Replace mutable consumption with immutable variant. | ||||
| ;; - TODO: Replace indexing with (math/mod current cycle). | ||||
| ;; - TODO: Replace indexing with (math-mod current cycle). | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Library | ||||
|  | @ -31,10 +35,10 @@ | |||
| ;; `xs' is the original list | ||||
| (cl-defstruct cycle current-index previous-index xs) | ||||
| 
 | ||||
| (defconst cycle/enable-tests? t | ||||
| (defconst cycle-enable-tests? t | ||||
|   "When t, run the tests defined herein.") | ||||
| 
 | ||||
| (defun cycle/from-list (xs) | ||||
| (defun cycle-from-list (xs) | ||||
|   "Create a cycle from a list of `XS'." | ||||
|   (if (= 0 (length xs)) | ||||
|       (make-cycle :current-index nil | ||||
|  | @ -44,11 +48,11 @@ | |||
|                 :previous-index nil | ||||
|                 :xs xs))) | ||||
| 
 | ||||
| (defun cycle/new (&rest xs) | ||||
| (defun cycle-new (&rest xs) | ||||
|   "Create a cycle with XS as the values." | ||||
|   (cycle/from-list xs)) | ||||
|   (cycle-from-list xs)) | ||||
| 
 | ||||
| (defun cycle/to-list (xs) | ||||
| (defun cycle-to-list (xs) | ||||
|   "Return the list representation of a cycle, XS." | ||||
|   (cycle-xs xs)) | ||||
| 
 | ||||
|  | @ -70,7 +74,7 @@ | |||
|       lo | ||||
|     (+ 1 x))) | ||||
| 
 | ||||
| (defun cycle/previous-focus (cycle) | ||||
| (defun cycle-previous-focus (cycle) | ||||
|   "Return the previously focused entry in CYCLE." | ||||
|   (let ((i (cycle-previous-index cycle))) | ||||
|     (if (maybe-some? i) | ||||
|  | @ -79,81 +83,81 @@ | |||
| 
 | ||||
| ;; TODO: Consider adding "!" to the function name herein since many of them | ||||
| ;; mutate the collection, and the APIs are beginning to confuse me. | ||||
| (defun cycle/focus-previous! (xs) | ||||
| (defun cycle-focus-previous! (xs) | ||||
|   "Jump to the item in XS that was most recently focused; return the cycle. | ||||
| This will error when previous-index is nil.  This function mutates the | ||||
| underlying struct." | ||||
|   (let ((i (cycle-previous-index xs))) | ||||
|     (if (maybe-some? i) | ||||
|         (progn | ||||
|           (cycle/jump i xs) | ||||
|           (cycle/current xs)) | ||||
|           (cycle-jump i xs) | ||||
|           (cycle-current xs)) | ||||
|       (error "Cannot focus the previous element since cycle-previous-index is nil")))) | ||||
| 
 | ||||
| (defun cycle/next (xs) | ||||
| (defun cycle-next (xs) | ||||
|   "Return the next value in `XS' and update `current-index'." | ||||
|   (let* ((current-index (cycle-current-index xs)) | ||||
|          (next-index (next-index-> 0 (cycle/count xs) current-index))) | ||||
|          (next-index (next-index-> 0 (cycle-count xs) current-index))) | ||||
|     (struct-set! cycle previous-index current-index xs) | ||||
|     (struct-set! cycle current-index next-index xs) | ||||
|     (nth next-index (cycle-xs xs)))) | ||||
| 
 | ||||
| (defun cycle/prev (xs) | ||||
| (defun cycle-prev (xs) | ||||
|   "Return the previous value in `XS' and update `current-index'." | ||||
|   (let* ((current-index (cycle-current-index xs)) | ||||
|          (next-index (next-index<- 0 (cycle/count xs) current-index))) | ||||
|          (next-index (next-index<- 0 (cycle-count xs) current-index))) | ||||
|     (struct-set! cycle previous-index current-index xs) | ||||
|     (struct-set! cycle current-index next-index xs) | ||||
|     (nth next-index (cycle-xs xs)))) | ||||
| 
 | ||||
| (defun cycle/current (cycle) | ||||
| (defun cycle-current (cycle) | ||||
|   "Return the current value in `CYCLE'." | ||||
|   (nth (cycle-current-index cycle) (cycle-xs cycle))) | ||||
| 
 | ||||
| (defun cycle/count (cycle) | ||||
| (defun cycle-count (cycle) | ||||
|   "Return the length of `xs' in `CYCLE'." | ||||
|   (length (cycle-xs cycle))) | ||||
| 
 | ||||
| (defun cycle/jump (i xs) | ||||
| (defun cycle-jump (i xs) | ||||
|   "Jump to the I index of XS." | ||||
|   (let ((current-index (cycle-current-index xs)) | ||||
|         (next-index (math/mod i (cycle/count xs)))) | ||||
|         (next-index (math-mod i (cycle-count xs)))) | ||||
|     (struct-set! cycle previous-index current-index xs) | ||||
|     (struct-set! cycle current-index next-index xs)) | ||||
|   xs) | ||||
| 
 | ||||
| (defun cycle/focus (p cycle) | ||||
| (defun cycle-focus (p cycle) | ||||
|   "Focus the element in CYCLE for which predicate, P, is t." | ||||
|   (let ((i (->> cycle | ||||
|                 cycle-xs | ||||
|                 (-find-index p)))) | ||||
|     (if i | ||||
|         (cycle/jump i cycle) | ||||
|         (cycle-jump i cycle) | ||||
|       (error "No element in cycle matches predicate")))) | ||||
| 
 | ||||
| (defun cycle/focus-item (x xs) | ||||
| (defun cycle-focus-item (x xs) | ||||
|   "Focus ITEM in cycle XS. | ||||
| ITEM is the first item in XS that t for `equal'." | ||||
|   (cycle/focus (lambda (y) (equal x y)) xs)) | ||||
|   (cycle-focus (lambda (y) (equal x y)) xs)) | ||||
| 
 | ||||
| (defun cycle/contains? (x xs) | ||||
| (defun cycle-contains? (x xs) | ||||
|   "Return t if cycle, XS, has member X." | ||||
|   (->> xs | ||||
|        cycle-xs | ||||
|        (list/contains? x))) | ||||
|        (list-contains? x))) | ||||
| 
 | ||||
| (defun cycle/empty? (xs) | ||||
| (defun cycle-empty? (xs) | ||||
|   "Return t if cycle XS has no elements." | ||||
|   (= 0 (length (cycle-xs xs)))) | ||||
| 
 | ||||
| (defun cycle/focused? (xs) | ||||
| (defun cycle-focused? (xs) | ||||
|   "Return t if cycle XS has a non-nil value for current-index." | ||||
|   (maybe-some? (cycle-current-index xs))) | ||||
| 
 | ||||
| (defun cycle/append (x xs) | ||||
| (defun cycle-append (x xs) | ||||
|   "Add X to the left of the focused element in XS. | ||||
| If there is no currently focused item, add X to the beginning of XS." | ||||
|   (if (cycle/empty? xs) | ||||
|   (if (cycle-empty? xs) | ||||
|       (progn | ||||
|         (struct-set! cycle xs (list x) xs) | ||||
|         (struct-set! cycle current-index 0 xs) | ||||
|  | @ -170,7 +174,7 @@ If there is no currently focused item, add X to the beginning of XS." | |||
|           (when prev-i (struct-set! cycle previous-index (1+ prev-i) xs)))) | ||||
|       xs))) | ||||
| 
 | ||||
| (defun cycle/remove (x xs) | ||||
| (defun cycle-remove (x xs) | ||||
|   "Attempt to remove X from XS. | ||||
| 
 | ||||
| X is found using `equal'. | ||||
|  | @ -194,25 +198,25 @@ If X is the currently focused value, after it's deleted, current-index will be | |||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when cycle/enable-tests? | ||||
|   (let ((xs (cycle/new 1 2 3))) | ||||
|     (prelude-assert (maybe-nil? (cycle/previous-focus xs))) | ||||
|     (prelude-assert (= 1 (cycle/current xs))) | ||||
|     (prelude-assert (= 2 (cycle/next xs))) | ||||
|     (prelude-assert (= 1 (cycle/previous-focus xs))) | ||||
|     (prelude-assert (= 1 (->> xs (cycle/jump 0) cycle/current))) | ||||
|     (prelude-assert (= 2 (->> xs (cycle/jump 1) cycle/current))) | ||||
|     (prelude-assert (= 3 (->> xs (cycle/jump 2) cycle/current))) | ||||
|     (prelude-assert (= 2 (cycle/previous-focus xs))) | ||||
|     (prelude-assert (= 2 (cycle/focus-previous! xs))) | ||||
|     (prelude-assert (equal '(1 4 2 3) (cycle-xs (cycle/append 4 xs)))) | ||||
|     (prelude-assert (equal '(1 2 3) (cycle-xs (cycle/remove 4 xs)))) | ||||
| (when cycle-enable-tests? | ||||
|   (let ((xs (cycle-new 1 2 3))) | ||||
|     (prelude-assert (maybe-nil? (cycle-previous-focus xs))) | ||||
|     (prelude-assert (= 1 (cycle-current xs))) | ||||
|     (prelude-assert (= 2 (cycle-next xs))) | ||||
|     (prelude-assert (= 1 (cycle-previous-focus xs))) | ||||
|     (prelude-assert (= 1 (->> xs (cycle-jump 0) cycle-current))) | ||||
|     (prelude-assert (= 2 (->> xs (cycle-jump 1) cycle-current))) | ||||
|     (prelude-assert (= 3 (->> xs (cycle-jump 2) cycle-current))) | ||||
|     (prelude-assert (= 2 (cycle-previous-focus xs))) | ||||
|     (prelude-assert (= 2 (cycle-focus-previous! xs))) | ||||
|     (prelude-assert (equal '(1 4 2 3) (cycle-xs (cycle-append 4 xs)))) | ||||
|     (prelude-assert (equal '(1 2 3) (cycle-xs (cycle-remove 4 xs)))) | ||||
|     (progn | ||||
|       (cycle/focus-item 3 xs) | ||||
|       (cycle/focus-item 2 xs) | ||||
|       (cycle/remove 1 xs) | ||||
|       (prelude-assert (= 2 (cycle/current xs))) | ||||
|       (prelude-assert (= 3 (cycle/previous-focus xs)))))) | ||||
|       (cycle-focus-item 3 xs) | ||||
|       (cycle-focus-item 2 xs) | ||||
|       (cycle-remove 1 xs) | ||||
|       (prelude-assert (= 2 (cycle-current xs))) | ||||
|       (prelude-assert (= 3 (cycle-previous-focus xs)))))) | ||||
| 
 | ||||
| (provide 'cycle) | ||||
| ;;; cycle.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; device.el --- Physical device information -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Functions for querying device information. | ||||
|  | @ -13,30 +17,30 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst device/hostname->device | ||||
| (defconst device-hostname->device | ||||
|   '(("zeno.lon.corp.google.com" . work-desktop) | ||||
|     ("seneca" . work-laptop)) | ||||
|   "Mapping hostname to a device symbol.") | ||||
| 
 | ||||
| ;; TODO: Should I generate these predicates? | ||||
| 
 | ||||
| (defun device/classify () | ||||
| (defun device-classify () | ||||
|   "Return the device symbol for the current host or nil if not supported." | ||||
|   (alist/get system-name device/hostname->device)) | ||||
|   (alist-get system-name device-hostname->device)) | ||||
| 
 | ||||
| (defun device/work-laptop? () | ||||
| (defun device-work-laptop? () | ||||
|   "Return t if current device is work laptop." | ||||
|   (equal 'work-laptop | ||||
|          (device/classify))) | ||||
|          (device-classify))) | ||||
| 
 | ||||
| (defun device/work-desktop? () | ||||
| (defun device-work-desktop? () | ||||
|   "Return t if current device is work desktop." | ||||
|   (equal 'work-desktop | ||||
|          (device/classify))) | ||||
|          (device-classify))) | ||||
| 
 | ||||
| (defun device/corporate? () | ||||
| (defun device-corporate? () | ||||
|   "Return t if the current device is owned by my company." | ||||
|   (or (device/work-laptop?) (device/work-desktop?))) | ||||
|   (or (device-work-laptop?) (device-work-desktop?))) | ||||
| 
 | ||||
| (provide 'device) | ||||
| ;;; device.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; display.el --- Working with single or multiple displays -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Mostly wrappers around xrandr. | ||||
|  | @ -24,15 +28,15 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| ;; TODO: Consider if this logic should be conditioned by `device/work-laptop?'. | ||||
| (defconst display/laptop-monitor "eDP1" | ||||
| ;; TODO: Consider if this logic should be conditioned by `device-work-laptop?'. | ||||
| (defconst display-laptop-monitor "eDP1" | ||||
|   "The xrandr identifier for my primary screen (on work laptop).") | ||||
| 
 | ||||
| ;; TODO: Why is HDMI-1, eDP-1 sometimes and HDMI1, eDP1 other times. | ||||
| (defconst display/4k-monitor "HDMI1" | ||||
| (defconst display-4k-monitor "HDMI1" | ||||
|   "The xrandr identifer for my 4K monitor.") | ||||
| 
 | ||||
| (defconst display/display-states (cycle/from-list '((t . nil) (nil . t))) | ||||
| (defconst display-display-states (cycle-from-list '((t . nil) (nil . t))) | ||||
|   "A list of cons cells modelling enabled and disabled states for my displays. | ||||
| The car models the enabled state of my laptop display; the cdr models the | ||||
|   enabled state of my external monitor.") | ||||
|  | @ -43,50 +47,50 @@ The car models the enabled state of my laptop display; the cdr models the | |||
| 
 | ||||
| ;; TODO: Debug why something this scales to 4k appropriately and other times it | ||||
| ;; doesn't. | ||||
| (defun display/enable-4k () | ||||
| (defun display-enable-4k () | ||||
|   "Attempt to connect to my 4K monitor." | ||||
|   (interactive) | ||||
|   (prelude-start-process | ||||
|    :name "display/enable-4k" | ||||
|    :name "display-enable-4k" | ||||
|    :command (string-format | ||||
|              "xrandr --output %s --above %s --primary --auto --size 3840x2160 --rate 30.00 --dpi 144" | ||||
|              display/4k-monitor | ||||
|              display/laptop-monitor))) | ||||
|              display-4k-monitor | ||||
|              display-laptop-monitor))) | ||||
| 
 | ||||
| (defun display/disable-4k () | ||||
| (defun display-disable-4k () | ||||
|   "Disconnect from the 4K monitor." | ||||
|   (interactive) | ||||
|   (prelude-start-process | ||||
|    :name "display/disable-4k" | ||||
|    :name "display-disable-4k" | ||||
|    :command (string-format "xrandr --output %s --off" | ||||
|                            display/4k-monitor))) | ||||
|                            display-4k-monitor))) | ||||
| 
 | ||||
| (defun display/enable-laptop () | ||||
| (defun display-enable-laptop () | ||||
|   "Turn the laptop monitor off. | ||||
| Sometimes this is useful when I'm sharing my screen in a Google Hangout and I | ||||
|   only want to present one of my monitors." | ||||
|   (interactive) | ||||
|   (prelude-start-process | ||||
|    :name "display/disable-laptop" | ||||
|    :name "display-disable-laptop" | ||||
|    :command (string-format "xrandr --output %s --auto" | ||||
|                            display/laptop-monitor))) | ||||
|                            display-laptop-monitor))) | ||||
| 
 | ||||
| (defun display/disable-laptop () | ||||
| (defun display-disable-laptop () | ||||
|   "Turn the laptop monitor off. | ||||
| Sometimes this is useful when I'm sharing my screen in a Google Hangout and I | ||||
|   only want to present one of my monitors." | ||||
|   (interactive) | ||||
|   (prelude-start-process | ||||
|    :name "display/disable-laptop" | ||||
|    :name "display-disable-laptop" | ||||
|    :command (string-format "xrandr --output %s --off" | ||||
|                            display/laptop-monitor))) | ||||
|                            display-laptop-monitor))) | ||||
| 
 | ||||
| (defun display/cycle-display-states () | ||||
|   "Cycle through `display/display-states' enabling and disabling displays." | ||||
| (defun display-cycle-display-states () | ||||
|   "Cycle through `display-display-states' enabling and disabling displays." | ||||
|   (interactive) | ||||
|   (let ((state (cycle/next display/display-states))) | ||||
|     (if (car state) (display/enable-laptop) (display/disable-laptop)) | ||||
|     (if (cdr state) (display/enable-4k) (display/disable-4k)))) | ||||
|   (let ((state (cycle-next display-display-states))) | ||||
|     (if (car state) (display-enable-laptop) (display-disable-laptop)) | ||||
|     (if (cdr state) (display-enable-4k) (display-disable-4k)))) | ||||
| 
 | ||||
| (provide 'display) | ||||
| ;;; display.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; dotted.el --- Working with dotted pairs in Elisp -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Part of my primitives library extensions in Elisp.  Contrast my primitives | ||||
|  | @ -9,6 +13,10 @@ | |||
| 
 | ||||
| ;;; Code: | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Dependencies | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (require 'prelude) | ||||
| (require 'macros) | ||||
| 
 | ||||
|  | @ -16,20 +24,20 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (cl-defun dotted/new (&optional a b) | ||||
| (cl-defun dotted-new (&optional a b) | ||||
|   "Create a new dotted pair (i.e. cons cell)." | ||||
|   (cons a b)) | ||||
| 
 | ||||
| (defun dotted/instance? (x) | ||||
| (defun dotted-instance? (x) | ||||
|   "Return t if X is a dotted pair." | ||||
|   (let ((b (cdr x))) | ||||
|     (and b (atom b)))) | ||||
| 
 | ||||
| (defun dotted/first (x) | ||||
| (defun dotted-first (x) | ||||
|   "Return the first element of X." | ||||
|   (car x)) | ||||
| 
 | ||||
| (defun dotted/second (x) | ||||
| (defun dotted-second (x) | ||||
|   "Return the second element of X." | ||||
|   (cdr x)) | ||||
| 
 | ||||
|  | @ -39,11 +47,11 @@ | |||
| 
 | ||||
| (progn | ||||
|   (prelude-assert | ||||
|    (equal '(fname . "Bob") (dotted/new 'fname "Bob"))) | ||||
|    (equal '(fname . "Bob") (dotted-new 'fname "Bob"))) | ||||
|   (prelude-assert | ||||
|    (dotted/instance? '(one . two))) | ||||
|    (dotted-instance? '(one . two))) | ||||
|   (prelude-refute | ||||
|    (dotted/instance? '(1 2 3)))) | ||||
|    (dotted-instance? '(1 2 3)))) | ||||
| 
 | ||||
| (provide 'dotted) | ||||
| ;;; dotted.el ends here | ||||
|  |  | |||
|  | @ -23,7 +23,9 @@ | |||
| 
 | ||||
| (setq notmuch-saved-searches | ||||
|       '((:name "inbox" :query "tag:inbox" :key "i") | ||||
|         (:name "direct" :query "tag:direct and tag:unread and not tag:sent" :key "d") | ||||
|         (:name "direct" | ||||
|          :query "tag:direct and tag:unread and not tag:sent" | ||||
|          :key "d") | ||||
|         (:name "action" :query "tag:action" :key "a") | ||||
|         (:name "review" :query "tag:review" :key "r") | ||||
|         (:name "waiting" :query "tag:waiting" :key "w") | ||||
|  | @ -69,7 +71,7 @@ | |||
| 
 | ||||
| ;; Assert that no two saved searches share share a KBD | ||||
| (prelude-assert | ||||
|  (list/xs-distinct-by? (lambda (x) (plist-get x :key)) notmuch-saved-searches)) | ||||
|  (list-xs-distinct-by? (lambda (x) (plist-get x :key)) notmuch-saved-searches)) | ||||
| 
 | ||||
| (provide 'email) | ||||
| ;;; email.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; fonts.el --- Font preferences -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Control my font preferences with ELisp. | ||||
|  | @ -8,7 +12,6 @@ | |||
| 
 | ||||
| ;; TODO: `defcustom' font-size. | ||||
| ;; TODO: `defcustom' fonts. | ||||
| ;; TODO: Remove wpc/ namespace. | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Dependencies | ||||
|  | @ -27,16 +30,16 @@ | |||
| 
 | ||||
| ;; TODO: Consider having a different font size when I'm using my 4K monitor. | ||||
| 
 | ||||
| (defconst fonts/size | ||||
|   (pcase (device/classify) | ||||
| (defconst fonts-size | ||||
|   (pcase (device-classify) | ||||
|     ('work-laptop "10") | ||||
|     ('work-desktop "8")) | ||||
|   "My preferred default font-size, which is device specific.") | ||||
| 
 | ||||
| (defconst fonts/size-step 10 | ||||
| (defconst fonts-size-step 10 | ||||
|   "The amount (%) by which to increase or decrease a font.") | ||||
| 
 | ||||
| (defconst fonts/hacker-news-recommendations | ||||
| (defconst fonts-hacker-news-recommendations | ||||
|   '("APL385 Unicode" | ||||
|     "Go Mono" | ||||
|     "Sudo" | ||||
|  | @ -45,10 +48,10 @@ | |||
|     ) | ||||
|   "List of fonts optimized for programming I found in a HN article.") | ||||
| 
 | ||||
| (defconst fonts/whitelist | ||||
|   (cycle/from-list | ||||
|    (list/concat | ||||
|     fonts/hacker-news-recommendations | ||||
| (defconst fonts-whitelist | ||||
|   (cycle-from-list | ||||
|    (list-concat | ||||
|     fonts-hacker-news-recommendations | ||||
|     '("JetBrainsMono" | ||||
|       "Mononoki Medium" | ||||
|       "Monospace" | ||||
|  | @ -63,75 +66,75 @@ | |||
| ;; Functions | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| ;; TODO: fonts and fonts/whitelist make it difficult to name functions like | ||||
| ;; fonts/set as a generic Emacs function vs choosing a font from the whitelist. | ||||
| ;; TODO: fonts and fonts-whitelist make it difficult to name functions like | ||||
| ;; fonts-set as a generic Emacs function vs choosing a font from the whitelist. | ||||
| 
 | ||||
| (cl-defun fonts/cycle (&key forward?) | ||||
| (cl-defun fonts-cycle (&key forward?) | ||||
|   "Cycle forwards when `FORWARD?' non-nil." | ||||
|   (let ((font (if forward? | ||||
|                   (cycle/next fonts/whitelist) | ||||
|                 (cycle/prev fonts/whitelist)))) | ||||
|                   (cycle-next fonts-whitelist) | ||||
|                 (cycle-prev fonts-whitelist)))) | ||||
|     (message (s-concat "Active font: " font)) | ||||
|     (fonts/set font))) | ||||
|     (fonts-set font))) | ||||
| 
 | ||||
| (defun fonts/next () | ||||
| (defun fonts-next () | ||||
|   "Quickly cycle through preferred fonts." | ||||
|   (interactive) | ||||
|   (fonts/cycle :forward? t)) | ||||
|   (fonts-cycle :forward? t)) | ||||
| 
 | ||||
| (defun fonts/prev () | ||||
| (defun fonts-prev () | ||||
|   "Quickly cycle through preferred fonts." | ||||
|   (interactive) | ||||
|   (fonts/cycle :forward? nil)) | ||||
|   (fonts-cycle :forward? nil)) | ||||
| 
 | ||||
| (defun fonts/set (font &optional size) | ||||
| (defun fonts-set (font &optional size) | ||||
|   "Change the font to `FONT' with option integer, SIZE, in pixels." | ||||
|   (if (maybe-some? size) | ||||
|       (set-frame-font (string-format "%s %s" font size) nil t) | ||||
|     (set-frame-font font nil t))) | ||||
| 
 | ||||
| (defun fonts/whitelist-set (font) | ||||
|   "Focuses the FONT in the `fonts/whitelist' cycle. | ||||
| The size of the font is determined by `fonts/size'." | ||||
|   (prelude-assert (cycle/contains? font fonts/whitelist)) | ||||
|   (cycle/focus (lambda (x) (equal x font)) fonts/whitelist) | ||||
|   (fonts/set (fonts/current) fonts/size)) | ||||
| (defun fonts-whitelist-set (font) | ||||
|   "Focuses the FONT in the `fonts-whitelist' cycle. | ||||
| The size of the font is determined by `fonts-size'." | ||||
|   (prelude-assert (cycle-contains? font fonts-whitelist)) | ||||
|   (cycle-focus (lambda (x) (equal x font)) fonts-whitelist) | ||||
|   (fonts-set (fonts-current) fonts-size)) | ||||
| 
 | ||||
| (defun fonts/ivy-select () | ||||
| (defun fonts-ivy-select () | ||||
|   "Select a font from an ivy prompt." | ||||
|   (interactive) | ||||
|   (fonts/whitelist-set | ||||
|    (ivy-read "Font: " (cycle/to-list fonts/whitelist)))) | ||||
|   (fonts-whitelist-set | ||||
|    (ivy-read "Font: " (cycle-to-list fonts-whitelist)))) | ||||
| 
 | ||||
| (defun fonts/print-current () | ||||
| (defun fonts-print-current () | ||||
|   "Message the currently enabled font." | ||||
|   (interactive) | ||||
|   (message | ||||
|    (string-format "[fonts] Current font: \"%s\"" | ||||
|                   (fonts/current)))) | ||||
|                   (fonts-current)))) | ||||
| 
 | ||||
| (defun fonts/current () | ||||
| (defun fonts-current () | ||||
|   "Return the currently enabled font." | ||||
|   (cycle/current fonts/whitelist)) | ||||
|   (cycle-current fonts-whitelist)) | ||||
| 
 | ||||
| (defun fonts/increase-size () | ||||
| (defun fonts-increase-size () | ||||
|   "Increase font size." | ||||
|   (interactive) | ||||
|   (->> (face-attribute 'default :height) | ||||
|        (+ fonts/size-step) | ||||
|        (+ fonts-size-step) | ||||
|        (set-face-attribute 'default (selected-frame) :height))) | ||||
| 
 | ||||
| (defun fonts/decrease-size () | ||||
| (defun fonts-decrease-size () | ||||
|   "Decrease font size." | ||||
|   (interactive) | ||||
|   (->> (face-attribute 'default :height) | ||||
|        (+ (- fonts/size-step)) | ||||
|        (+ (- fonts-size-step)) | ||||
|        (set-face-attribute 'default (selected-frame) :height))) | ||||
| 
 | ||||
| (defun fonts/reset-size () | ||||
| (defun fonts-reset-size () | ||||
|   "Restore font size to its default value." | ||||
|   (interactive) | ||||
|   (fonts/whitelist-set (fonts/current))) | ||||
|   (fonts-whitelist-set (fonts-current))) | ||||
| 
 | ||||
| (provide 'fonts) | ||||
| ;;; fonts.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; fs.el --- Make working with the filesystem easier -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.1")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Ergonomic alternatives for working with the filesystem. | ||||
|  | @ -10,31 +14,33 @@ | |||
| ;; Dependencies | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (require 'dash) | ||||
| (require 'f) | ||||
| (require 's) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun fs/ensure-file (path) | ||||
| (defun fs-ensure-file (path) | ||||
|   "Ensure that a file and its directories in `PATH' exist. | ||||
| Will error for inputs with a trailing slash." | ||||
|   (when (s-ends-with? "/" path) | ||||
|     (error (format "Input path has trailing slash: %s" path))) | ||||
|   (->> path | ||||
|        f-dirname | ||||
|        fs/ensure-dir) | ||||
|        fs-ensure-dir) | ||||
|   (f-touch path)) | ||||
| 
 | ||||
| (f-dirname "/tmp/a/b/file.txt") | ||||
| 
 | ||||
| (defun fs/ensure-dir (path) | ||||
| (defun fs-ensure-dir (path) | ||||
|   "Ensure that a directory and its ancestor directories in `PATH' exist." | ||||
|   (->> path | ||||
|        f-split | ||||
|        (apply #'f-mkdir))) | ||||
| 
 | ||||
| (defun fs/ls (dir &optional full-path?) | ||||
| (defun fs-ls (dir &optional full-path?) | ||||
|   "List the files in `DIR' one-level deep. | ||||
| Should behave similarly in spirit to the Unix command, ls. | ||||
| If `FULL-PATH?' is set, return the full-path of the files." | ||||
|  | @ -44,20 +50,19 @@ If `FULL-PATH?' is set, return the full-path of the files." | |||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| ;; TODO: Support `refute' function / macro. | ||||
| (ert-deftest fs/test/ensure-file () | ||||
| (ert-deftest fs-test-ensure-file () | ||||
|   (let ((file "/tmp/file/a/b/c/file.txt")) | ||||
|     ;; Ensure this file doesn't exist first to prevent false-positives. | ||||
|     (f-delete file t) | ||||
|     (fs/ensure-file file) | ||||
|     (fs-ensure-file file) | ||||
|     (should (and (f-exists? file) | ||||
|                  (f-file? file))))) | ||||
| 
 | ||||
| (ert-deftest fs/test/ensure-dir () | ||||
| (ert-deftest fs-test-ensure-dir () | ||||
|   (let ((dir "/tmp/dir/a/b/c")) | ||||
|     ;; Ensure the directory doesn't exist. | ||||
|     (f-delete dir t) | ||||
|     (fs/ensure-dir dir) | ||||
|     (fs-ensure-dir dir) | ||||
|     (should (and (f-exists? dir) | ||||
|                  (f-dir? dir))))) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;; functions.el --- Helper functions for my Emacs development -*- lexical-binding: t -*- | ||||
| ;;; functions.el --- Helper functions -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; This file hopefully contains friendly APIs that making ELisp development more | ||||
|  | @ -8,114 +12,30 @@ | |||
| ;; TODO: Break these out into separate modules. | ||||
| 
 | ||||
| ;;; Code: | ||||
| (defun wpc/evil-window-vsplit-right () | ||||
| (defun functions-evil-window-vsplit-right () | ||||
|   (interactive) | ||||
|   (evil-window-vsplit) | ||||
|   (windmove-right)) | ||||
| 
 | ||||
| (defun wpc/evil-window-split-down () | ||||
| (defun functions-evil-window-split-down () | ||||
|   (interactive) | ||||
|   (evil-window-split) | ||||
|   (windmove-down)) | ||||
| 
 | ||||
| (defun wpc/reindent-defun-and-align-clojure-map () | ||||
|   (interactive) | ||||
|   (call-interactively #'paredit-reindent-defun) | ||||
|   (call-interactively #'clojure-align)) | ||||
| 
 | ||||
| (defun wpc/find-file-split (filename) | ||||
|   "Creates a window split and then edits `filename'." | ||||
|   (interactive) | ||||
|   (evil-window-vsplit) | ||||
|   (find-file filename)) | ||||
| 
 | ||||
| (defun wpc/find-or-create-js-test () | ||||
|   (->> buffer-file-name | ||||
|        (s-chop-suffix ".js") | ||||
|        (s-append ".test.js") | ||||
|        (find-file))) | ||||
| 
 | ||||
| (defun wpc/find-or-create-js-module () | ||||
|   (->> buffer-file-name | ||||
|        (s-chop-suffix ".test.js") | ||||
|        (s-append ".js") | ||||
|        (find-file))) | ||||
| 
 | ||||
| (defun wpc/find-or-create-js-store () | ||||
|   (->> buffer-file-name | ||||
|        (s-replace "index.js" "store.js") | ||||
|        (find-file))) | ||||
| 
 | ||||
| (defun wpc/find-or-create-js-component () | ||||
|   (->> buffer-file-name | ||||
|        (s-replace "store.js" "index.js") | ||||
|        (find-file))) | ||||
| 
 | ||||
| (defun wpc/toggle-between-js-test-and-module () | ||||
|   "Toggle between a Javascript test or module." | ||||
|   (interactive) | ||||
|   (if (s-ends-with? ".test.js" buffer-file-name) | ||||
|       (wpc/find-or-create-js-module) | ||||
|     (if (s-ends-with? ".js" buffer-file-name) | ||||
|         (wpc/find-or-create-js-test) | ||||
|       (message "Not in a Javascript file. Exiting...")))) | ||||
| 
 | ||||
| (defun wpc/toggle-between-js-component-and-store () | ||||
|   "Toggle between a React component and its Redux store." | ||||
|   (interactive) | ||||
|   (if (s-ends-with? "index.js" buffer-file-name) | ||||
|       (wpc/find-or-create-js-store) | ||||
|     (if (or (s-ends-with? "store.js" buffer-file-name) | ||||
|             (s-ends-with? "store.test.js" buffer-file-name)) | ||||
|         (wpc/find-or-create-js-component) | ||||
|       (message "Not in a React/Redux file. Exiting...")))) | ||||
| 
 | ||||
| (defun wpc/read-file-as-string (filename) | ||||
|   (with-temp-buffer | ||||
|     (insert-file-contents filename) | ||||
|     (s-trim (buffer-string)))) | ||||
| 
 | ||||
| (defun wpc/create-snippet () | ||||
| (defun functions-create-snippet () | ||||
|   "Creates a window split and then opens the Yasnippet editor." | ||||
|   (interactive) | ||||
|   (evil-window-vsplit) | ||||
|   (call-interactively #'yas-new-snippet)) | ||||
| 
 | ||||
| (defun wpc/jump-to-parent-file () | ||||
|   "Jumps to a React store or component's parent file. Useful for store or index file." | ||||
|   (interactive) | ||||
|   (-> buffer-file-name | ||||
|       f-dirname | ||||
|       (f-join "..") | ||||
|       (f-join (f-filename buffer-file-name)) | ||||
|       find-file)) | ||||
| 
 | ||||
| (defun wpc/add-earmuffs (x) | ||||
|   "Returns X surrounded by asterisks." | ||||
|   (format "*%s*" x)) | ||||
| 
 | ||||
| (defun wpc/put-file-name-on-clipboard () | ||||
|   "Put the current file name on the clipboard" | ||||
|   (interactive) | ||||
|   (let ((filename (if (equal major-mode 'dired-mode) | ||||
|                       default-directory | ||||
|                     (buffer-file-name)))) | ||||
|     (when filename | ||||
|       (with-temp-buffer | ||||
|         (insert filename) | ||||
|         (clipboard-kill-region (point-min) (point-max))) | ||||
|       (message filename)))) | ||||
| 
 | ||||
| (s-replace "/" "x" "a/b/c") | ||||
| 
 | ||||
| (defun wpc/evil-replace-under-point () | ||||
| (defun functions-evil-replace-under-point () | ||||
|   "Faster than typing %s//thing/g." | ||||
|   (interactive) | ||||
|   (let ((term (s-replace "/" "\\/" (symbol-to-string (symbol-at-point))))) | ||||
|     (save-excursion | ||||
|       (evil-ex (concat "%s/\\b" term "\\b/"))))) | ||||
| 
 | ||||
| (defun buffer-dirname () | ||||
| (defun functions-buffer-dirname () | ||||
|   "Return the directory name of the current buffer as a string." | ||||
|   (->> buffer-file-name | ||||
|        f-dirname | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; graph.el --- Working with in-memory graphs -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; | ||||
|  | @ -42,44 +46,44 @@ | |||
| (cl-defstruct graph neighbors edges) | ||||
| 
 | ||||
| ;; TODO: How do you find the starting point for a topo sort? | ||||
| (defun graph/sort (xs) | ||||
| (defun graph-sort (xs) | ||||
|   "Return a topological sort of XS.") | ||||
| 
 | ||||
| (defun graph/from-edges (xs) | ||||
| (defun graph-from-edges (xs) | ||||
|   "Create a graph struct from the Edge List, XS. | ||||
| The user must pass in a valid Edge List since asserting on the shape of XS might | ||||
|   be expensive." | ||||
|   (make-graph :edges xs)) | ||||
| 
 | ||||
| (defun graph/from-neighbors (xs) | ||||
| (defun graph-from-neighbors (xs) | ||||
|   "Create a graph struct from a Neighbors Table, XS. | ||||
| The user must pass in a valid Neighbors Table since asserting on the shape of | ||||
|   XS might be expensive." | ||||
|   (make-graph :neighbors xs)) | ||||
| 
 | ||||
| (defun graph/instance? (xs) | ||||
| (defun graph-instance? (xs) | ||||
|   "Return t if XS is a graph struct." | ||||
|   (graph-p xs)) | ||||
| 
 | ||||
| ;; TODO: Model each of the mapping functions into an isomorphism. | ||||
| (defun graph/edges->neighbors (xs) | ||||
| (defun graph-edges->neighbors (xs) | ||||
|   "Map Edge List, XS, into a Neighbors Table." | ||||
|   (prelude-assert (graph/instance? xs))) | ||||
|   (prelude-assert (graph-instance? xs))) | ||||
| 
 | ||||
| (defun graph/neighbors->edges (xs) | ||||
| (defun graph-neighbors->edges (xs) | ||||
|   "Map Neighbors Table, XS, into an Edge List." | ||||
|   (prelude-assert (graph/instance? xs))) | ||||
|   (prelude-assert (graph-instance? xs))) | ||||
| 
 | ||||
| ;; Below are three different models of the same unweighted, directed graph. | ||||
| 
 | ||||
| (defvar graph/edges | ||||
| (defvar graph-edges | ||||
|   '((a . b) (a . c) (a . e) | ||||
|     (b . c) (b . d) | ||||
|     (c . e) | ||||
|     (d . f) | ||||
|     (e . d) (e . f))) | ||||
| 
 | ||||
| (defvar graph/neighbors | ||||
| (defvar graph-neighbors | ||||
|   ((a b c e) | ||||
|    (b c d) | ||||
|    (c e) | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; irc.el --- Configuration for IRC chat -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Need to decide which client I will use for IRC. | ||||
|  | @ -24,47 +28,47 @@ | |||
| ;; Configuration | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst irc/enable-tests? t | ||||
| (defconst irc-enable-tests? t | ||||
|   "When t, run the tests defined herein.") | ||||
| 
 | ||||
| (setq erc-rename-buffers t) | ||||
| 
 | ||||
| ;; TODO: Find a way to avoid putting "freenode" and "#freenode" as channels | ||||
| ;; here.  I'm doing it because when erc first connects, it's `(buffer-name)' is | ||||
| ;; "freenode", so when `irc/next-channel' is called, it 404s on the | ||||
| ;; `cycle/contains?' call in `irc/channel->cycle" unless "freenode" is there. To | ||||
| ;; "freenode", so when `irc-next-channel' is called, it 404s on the | ||||
| ;; `cycle-contains?' call in `irc-channel->cycle" unless "freenode" is there. To | ||||
| ;; make matters even uglier, when `erc-join-channel' is called with "freenode" | ||||
| ;; as the value, it connects to the "#freenode" channel, so unless "#freenode" | ||||
| ;; exists in this cycle also, `irc/next-channel' breaks again.  This doesn't | ||||
| ;; exists in this cycle also, `irc-next-channel' breaks again.  This doesn't | ||||
| ;; pass my smell test. | ||||
| (defconst irc/server->channels | ||||
|   `(("irc.freenode.net"    . ,(cycle/new "freenode" "#freenode" "#nixos" "#emacs" "#pass")) | ||||
|     ("irc.corp.google.com" . ,(cycle/new "#omg" "#london" "#panic" "#prod-team"))) | ||||
| (defconst irc-server->channels | ||||
|   `(("irc.freenode.net"    . ,(cycle-new "freenode" "#freenode" "#nixos" "#emacs" "#pass")) | ||||
|     ("irc.corp.google.com" . ,(cycle-new "#omg" "#london" "#panic" "#prod-team"))) | ||||
|   "Mapping of IRC servers to a cycle of my preferred channels.") | ||||
| 
 | ||||
| ;; TODO: Assert that no two servers have a channel with the same name. We need | ||||
| ;; this because that's the assumption that underpins the `irc/channel->server' | ||||
| ;; this because that's the assumption that underpins the `irc-channel->server' | ||||
| ;; function. This will probably be an O(n^2) operation. | ||||
| (prelude-assert | ||||
|  (set/distinct? (set/from-list | ||||
|                  (cycle/to-list | ||||
|                   (alist/get "irc.freenode.net" | ||||
|                              irc/server->channels))) | ||||
|                 (set/from-list | ||||
|                  (cycle/to-list | ||||
|                   (alist/get "irc.corp.google.com" | ||||
|                              irc/server->channels))))) | ||||
|  (set-distinct? (set-from-list | ||||
|                  (cycle-to-list | ||||
|                   (alist-get "irc.freenode.net" | ||||
|                              irc-server->channels))) | ||||
|                 (set-from-list | ||||
|                  (cycle-to-list | ||||
|                   (alist-get "irc.corp.google.com" | ||||
|                              irc-server->channels))))) | ||||
| 
 | ||||
| (defun irc/channel->server (server->channels channel) | ||||
| (defun irc-channel->server (server->channels channel) | ||||
|   "Resolve an IRC server from a given CHANNEL." | ||||
|   (let ((result (alist/find (lambda (k v) (cycle/contains? channel v)) | ||||
|   (let ((result (alist-find (lambda (k v) (cycle-contains? channel v)) | ||||
|                             server->channels))) | ||||
|     (prelude-assert (maybe-some? result)) | ||||
|     result)) | ||||
| 
 | ||||
| (defun irc/channel->cycle (server->channels channel) | ||||
| (defun irc-channel->cycle (server->channels channel) | ||||
|   "Resolve an IRC's channels cycle from a given CHANNEL." | ||||
|   (alist/get (irc/channel->server server->channels channel) | ||||
|   (alist-get (irc-channel->server server->channels channel) | ||||
|              server->channels)) | ||||
| 
 | ||||
| ;; Setting `erc-join-buffer' to 'bury prevents erc from stealing focus of the | ||||
|  | @ -73,19 +77,19 @@ | |||
| 
 | ||||
| ;; TODO: Here is another horrible hack that should be revisted. | ||||
| (setq erc-autojoin-channels-alist | ||||
|       (->> irc/server->channels | ||||
|            (alist/map-values #'cycle/to-list) | ||||
|            (alist/map-keys (>> (s-chop-prefix "irc.") | ||||
|       (->> irc-server->channels | ||||
|            (alist-map-values #'cycle-to-list) | ||||
|            (alist-map-keys (>> (s-chop-prefix "irc.") | ||||
|                                (s-chop-suffix ".net"))))) | ||||
| 
 | ||||
| (defcustom irc/install-kbds? t | ||||
| (defcustom irc-install-kbds? t | ||||
|   "When t, install the keybindings defined herein.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun irc/message (x) | ||||
| (defun irc-message (x) | ||||
|   "Print message X in a structured way." | ||||
|   (message (string-format "[irc.el] %s" x))) | ||||
| 
 | ||||
|  | @ -93,31 +97,31 @@ | |||
| 
 | ||||
| ;; TODO: Support function or KBD for switching to an ERC buffer. | ||||
| 
 | ||||
| (defun irc/kill-all-erc-processes () | ||||
| (defun irc-kill-all-erc-processes () | ||||
|   "Kills all ERC buffers and processes." | ||||
|   (interactive) | ||||
|   (->> (erc-buffer-list) | ||||
|        (-map #'kill-buffer))) | ||||
| 
 | ||||
| (defun irc/switch-to-erc-buffer () | ||||
| (defun irc-switch-to-erc-buffer () | ||||
|   "Switch to an ERC buffer." | ||||
|   (interactive) | ||||
|   (let ((buffers (erc-buffer-list))) | ||||
|     (if (list/empty? buffers) | ||||
|     (if (list-empty? buffers) | ||||
|         (error "[irc.el] No ERC buffers available") | ||||
|       (switch-to-buffer (list/head (erc-buffer-list)))))) | ||||
|       (switch-to-buffer (list-head (erc-buffer-list)))))) | ||||
| 
 | ||||
| (defun irc/connect-to-freenode () | ||||
| (defun irc-connect-to-freenode () | ||||
|   "Connect to Freenode IRC." | ||||
|   (interactive) | ||||
|   (erc-ssl :server "irc.freenode.net" | ||||
|            :port 6697 | ||||
|            :nick "wpcarro" | ||||
|            :password (password-store-get "programming/irc/freenode") | ||||
|            :password (password-store-get "programming/irc-freenode") | ||||
|            :full-name "William Carroll")) | ||||
| 
 | ||||
| ;; TODO: Handle failed connections. | ||||
| (defun irc/connect-to-google () | ||||
| (defun irc-connect-to-google () | ||||
|   "Connect to Google's Corp IRC using ERC." | ||||
|   (interactive) | ||||
|   (erc-ssl :server "irc.corp.google.com" | ||||
|  | @ -127,26 +131,26 @@ | |||
| 
 | ||||
| ;; TODO: Prefer defining these with a less homespun solution. There is a | ||||
| ;; function call `erc-buffer-filter' that would be more appropriate for the | ||||
| ;; implementation of `irc/next-channel' and `irc/prev-channel'. | ||||
| (defun irc/next-channel () | ||||
| ;; implementation of `irc-next-channel' and `irc-prev-channel'. | ||||
| (defun irc-next-channel () | ||||
|   "Join the next channel for the active server." | ||||
|   (interactive) | ||||
|   (with-current-buffer (current-buffer) | ||||
|     (let ((cycle (irc/channel->cycle irc/server->channels (buffer-name)))) | ||||
|     (let ((cycle (irc-channel->cycle irc-server->channels (buffer-name)))) | ||||
|       (erc-join-channel | ||||
|        (cycle/next cycle)) | ||||
|       (irc/message | ||||
|        (string-format "Current IRC channel: %s" (cycle/current cycle)))))) | ||||
|        (cycle-next cycle)) | ||||
|       (irc-message | ||||
|        (string-format "Current IRC channel: %s" (cycle-current cycle)))))) | ||||
| 
 | ||||
| (defun irc/prev-channel () | ||||
| (defun irc-prev-channel () | ||||
|   "Join the previous channel for the active server." | ||||
|   (interactive) | ||||
|   (with-current-buffer (current-buffer) | ||||
|     (let ((cycle (irc/channel->cycle irc/server->channels (buffer-name)))) | ||||
|     (let ((cycle (irc-channel->cycle irc-server->channels (buffer-name)))) | ||||
|       (erc-join-channel | ||||
|        (cycle/prev cycle)) | ||||
|       (irc/message | ||||
|        (string-format "Current IRC channel: %s" (cycle/current cycle)))))) | ||||
|        (cycle-prev cycle)) | ||||
|       (irc-message | ||||
|        (string-format "Current IRC channel: %s" (cycle-current cycle)))))) | ||||
| 
 | ||||
| (add-hook 'erc-mode-hook (macros-disable auto-fill-mode)) | ||||
| (add-hook 'erc-mode-hook (macros-disable company-mode)) | ||||
|  | @ -155,21 +159,21 @@ | |||
| ;; Keybindings | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when irc/install-kbds? | ||||
| (when irc-install-kbds? | ||||
|   (general-define-key | ||||
|    :keymaps 'erc-mode-map | ||||
|    "<C-tab>" #'irc/next-channel | ||||
|    "<C-S-iso-lefttab>" #'irc/prev-channel)) | ||||
|    "<C-tab>" #'irc-next-channel | ||||
|    "<C-S-iso-lefttab>" #'irc-prev-channel)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when irc/enable-tests? | ||||
| (when irc-enable-tests? | ||||
|   (prelude-assert | ||||
|    (equal | ||||
|     (irc/channel->server `(("irc.dairy.com" . ,(cycle/new "#cheese" "#milk")) | ||||
|                            ("irc.color.com" . ,(cycle/new "#red" "#blue"))) | ||||
|     (irc-channel->server `(("irc.dairy.com" . ,(cycle-new "#cheese" "#milk")) | ||||
|                            ("irc.color.com" . ,(cycle-new "#red" "#blue"))) | ||||
|                          "#cheese") | ||||
|     "irc.dairy.com"))) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; ivy-clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "25.1")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Ivy integration with the clipboard manager, clipmenu.  Essentially, clipmenu | ||||
|  | @ -11,7 +15,7 @@ | |||
| ;; | ||||
| ;; This module intentionally does not define any keybindings since I'd prefer | ||||
| ;; not to presume my users' preferences.  Personally, I use EXWM as my window | ||||
| ;; manager, so I call `exwm-input-set-key' and map it to `ivy-clipmenu/copy'. | ||||
| ;; manager, so I call `exwm-input-set-key' and map it to `ivy-clipmenu-copy'. | ||||
| ;; | ||||
| ;; Usually clipmenu integrates with rofi or dmenu.  This Emacs module integrates | ||||
| ;; with ivy.  Launch this when you want to select a clip. | ||||
|  | @ -44,7 +48,7 @@ | |||
|   "Ivy integration for clipmenu." | ||||
|   :group 'ivy) | ||||
| 
 | ||||
| (defcustom ivy-clipmenu/directory | ||||
| (defcustom ivy-clipmenu-directory | ||||
|   (or (getenv "XDG_RUNTIME_DIR") | ||||
|       (getenv "TMPDIR") | ||||
|       "/tmp") | ||||
|  | @ -52,52 +56,52 @@ | |||
|   :type 'string | ||||
|   :group 'ivy-clipmenu) | ||||
| 
 | ||||
| (defconst ivy-clipmenu/executable-version 5 | ||||
| (defconst ivy-clipmenu-executable-version 5 | ||||
|    "The major version number for the clipmenu executable.") | ||||
| 
 | ||||
| (defconst ivy-clipmenu/cache-directory | ||||
|   (f-join ivy-clipmenu/directory | ||||
| (defconst ivy-clipmenu-cache-directory | ||||
|   (f-join ivy-clipmenu-directory | ||||
|           (format "clipmenu.%s.%s" | ||||
|                   ivy-clipmenu/executable-version | ||||
|                   ivy-clipmenu-executable-version | ||||
|                   (getenv "USER"))) | ||||
|   "Directory where the clips are stored.") | ||||
| 
 | ||||
| (defconst ivy-clipmenu/cache-file-pattern | ||||
|   (f-join ivy-clipmenu/cache-directory "line_cache_*") | ||||
| (defconst ivy-clipmenu-cache-file-pattern | ||||
|   (f-join ivy-clipmenu-cache-directory "line_cache_*") | ||||
|   "Glob pattern matching the locations on disk for clipmenu's labels.") | ||||
| 
 | ||||
| (defcustom ivy-clipmenu/history-length | ||||
| (defcustom ivy-clipmenu-history-length | ||||
|   (or (getenv "CM_HISTLENGTH") 25) | ||||
|   "Limit the number of clips in the history. | ||||
| This value defaults to 25.") | ||||
| 
 | ||||
| (defvar ivy-clipmenu/history nil | ||||
|   "History for `ivy-clipmenu/copy'.") | ||||
| (defvar ivy-clipmenu-history nil | ||||
|   "History for `ivy-clipmenu-copy'.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Functions | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun ivy-clipmenu/parse-content (x) | ||||
| (defun ivy-clipmenu-parse-content (x) | ||||
|   "Parse the label from the entry in clipmenu's line-cache." | ||||
|   (->> (s-split " " x) | ||||
|        (-drop 1) | ||||
|        (s-join " "))) | ||||
| 
 | ||||
| (defun ivy-clipmenu/list-clips () | ||||
| (defun ivy-clipmenu-list-clips () | ||||
|   "Return a list of the content of all of the clips." | ||||
|   (->> ivy-clipmenu/cache-file-pattern | ||||
|   (->> ivy-clipmenu-cache-file-pattern | ||||
|        f-glob | ||||
|        (-map (lambda (path) | ||||
|                (s-split "\n" (f-read path) t))) | ||||
|        -flatten | ||||
|        (-reject #'s-blank?) | ||||
|        (-sort #'string>) | ||||
|        (-map #'ivy-clipmenu/parse-content) | ||||
|        (-map #'ivy-clipmenu-parse-content) | ||||
|        delete-dups | ||||
|        (-take ivy-clipmenu/history-length))) | ||||
|        (-take ivy-clipmenu-history-length))) | ||||
| 
 | ||||
| (defun ivy-clipmenu/checksum (content) | ||||
| (defun ivy-clipmenu-checksum (content) | ||||
|   "Return the CRC checksum of CONTENT." | ||||
|   (s-trim-right | ||||
|    (with-temp-buffer | ||||
|  | @ -105,30 +109,30 @@ This value defaults to 25.") | |||
|                    (format "cksum <<<'%s'" content)) | ||||
|      (buffer-string)))) | ||||
| 
 | ||||
| (defun ivy-clipmenu/line-to-content (line) | ||||
| (defun ivy-clipmenu-line-to-content (line) | ||||
|   "Map the chosen LINE from the line cache its content from disk." | ||||
|   (->> line | ||||
|        ivy-clipmenu/checksum | ||||
|        (f-join ivy-clipmenu/cache-directory) | ||||
|        ivy-clipmenu-checksum | ||||
|        (f-join ivy-clipmenu-cache-directory) | ||||
|        f-read)) | ||||
| 
 | ||||
| (defun ivy-clipmenu/do-copy (x) | ||||
| (defun ivy-clipmenu-do-copy (x) | ||||
|   "Copy string, X, to the system clipboard." | ||||
|   (kill-new x) | ||||
|   (message "[ivy-clipmenu.el] Copied!")) | ||||
| 
 | ||||
| (defun ivy-clipmenu/copy () | ||||
| (defun ivy-clipmenu-copy () | ||||
|   "Use `ivy-read' to select and copy a clip. | ||||
| It's recommended to bind this function to a globally available keymap." | ||||
|   (interactive) | ||||
|   (let ((ivy-sort-functions-alist nil)) | ||||
|     (ivy-read "Clipmenu: " | ||||
|               (ivy-clipmenu/list-clips) | ||||
|               :history 'ivy-clipmenu/history | ||||
|               (ivy-clipmenu-list-clips) | ||||
|               :history 'ivy-clipmenu-history | ||||
|               :action (lambda (line) | ||||
|                         (->> line | ||||
|                              ivy-clipmenu/line-to-content | ||||
|                              ivy-clipmenu/do-copy))))) | ||||
|                              ivy-clipmenu-line-to-content | ||||
|                              ivy-clipmenu-do-copy))))) | ||||
| 
 | ||||
| (provide 'ivy-clipmenu) | ||||
| ;;; ivy-clipmenu.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; ivy-helpers.el --- More interfaces to ivy -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Hopefully to improve my workflows. | ||||
|  | @ -16,7 +20,7 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (cl-defun ivy-helpers/kv (prompt kv f) | ||||
| (cl-defun ivy-helpers-kv (prompt kv f) | ||||
|   "PROMPT users with the keys in KV and return its corresponding value.  Calls F | ||||
| with the key and value from KV." | ||||
|   (ivy-read | ||||
|  | @ -26,7 +30,7 @@ with the key and value from KV." | |||
|    :action (lambda (entry) | ||||
|              (funcall f (car entry) (cdr entry))))) | ||||
| 
 | ||||
| (defun ivy-helpers/do-run-external-command (cmd) | ||||
| (defun ivy-helpers-do-run-external-command (cmd) | ||||
|   "Execute the specified CMD and notify the user when it finishes." | ||||
|   (message "Starting %s..." cmd) | ||||
|   (set-process-sentinel | ||||
|  | @ -35,7 +39,7 @@ with the key and value from KV." | |||
|      (when (string= event "finished\n") | ||||
|        (message "%s process finished." process))))) | ||||
| 
 | ||||
| (defun ivy-helpers/list-external-commands () | ||||
| (defun ivy-helpers-list-external-commands () | ||||
|   "Creates a list of all external commands available on $PATH while filtering | ||||
| NixOS wrappers." | ||||
|   (cl-loop | ||||
|  | @ -51,14 +55,14 @@ NixOS wrappers." | |||
|    append lsdir into completions | ||||
|    finally return (sort completions 'string-lessp))) | ||||
| 
 | ||||
| (defun ivy-helpers/run-external-command () | ||||
| (defun ivy-helpers-run-external-command () | ||||
|   "Prompts the user with a list of all installed applications and | ||||
| lets them select one to launch." | ||||
|   (interactive) | ||||
|   (let ((external-commands-list (ivy-helpers/list-external-commands))) | ||||
|   (let ((external-commands-list (ivy-helpers-list-external-commands))) | ||||
|     (ivy-read "Command:" external-commands-list | ||||
|               :require-match t | ||||
|               :action #'ivy-helpers/do-run-external-command))) | ||||
|               :action #'ivy-helpers-do-run-external-command))) | ||||
| 
 | ||||
| ;;; Code: | ||||
| (provide 'ivy-helpers) | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; kbd.el --- Elisp keybinding -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; In order to stay organized, I'm attempting to dedicate KBD prefixes to | ||||
|  | @ -27,52 +31,52 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst kbd/prefixes | ||||
| (defconst kbd-prefixes | ||||
|   '((workspace . "s") | ||||
|     (x11 . "C-s")) | ||||
|   "Mapping of functions to designated keybinding prefixes to stay organized.") | ||||
| 
 | ||||
| ;; Assert that no keybindings are colliding. | ||||
| (prelude-assert | ||||
|  (= (alist/count kbd/prefixes) | ||||
|     (->> kbd/prefixes | ||||
|          alist/values | ||||
|          set/from-list | ||||
|          set/count))) | ||||
|  (= (alist-count kbd-prefixes) | ||||
|     (->> kbd-prefixes | ||||
|          alist-values | ||||
|          set-from-list | ||||
|          set-count))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun kbd/raw (f x) | ||||
| (defun kbd-raw (f x) | ||||
|   "Return the string keybinding for function F and appendage X. | ||||
| Values for F include: | ||||
| - workspace | ||||
| - x11" | ||||
|   (prelude-assert (alist/has-key? f kbd/prefixes)) | ||||
|   (prelude-assert (alist-has-key? f kbd-prefixes)) | ||||
|   (string-format | ||||
|    "%s-%s" | ||||
|    (alist/get f kbd/prefixes) | ||||
|    (alist-get f kbd-prefixes) | ||||
|    x)) | ||||
| 
 | ||||
| (defun kbd/for (f x) | ||||
| (defun kbd-for (f x) | ||||
|   "Return the `kbd' for function F and appendage X. | ||||
| Values for F include: | ||||
| - workspace | ||||
| - x11" | ||||
|   (kbd (kbd/raw f x))) | ||||
|   (kbd (kbd-raw f x))) | ||||
| 
 | ||||
| ;; TODO: Prefer copying human-readable versions to the clipboard.  Right now | ||||
| ;; this isn't too useful. | ||||
| (defun kbd/copy-keycode () | ||||
| (defun kbd-copy-keycode () | ||||
|   "Copy the pressed key to the system clipboard." | ||||
|   (interactive) | ||||
|   (message "[kbd] Awaiting keypress...") | ||||
|   (let ((key (read-key))) | ||||
|     (clipboard/copy (string-format "%s" key)) | ||||
|     (clipboard-copy (string-format "%s" key)) | ||||
|     (message (string-format "[kbd] \"%s\" copied!" key)))) | ||||
| 
 | ||||
| (defun kbd/print-keycode () | ||||
| (defun kbd-print-keycode () | ||||
|   "Prints the pressed keybinding." | ||||
|   (interactive) | ||||
|   (message "[kbd] Awaiting keypress...") | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; keybindings.el --- Centralizing my keybindings -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "25.1")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Attempting to centralize my keybindings to simplify my configuration. | ||||
|  | @ -63,10 +67,10 @@ | |||
|   "L"   #'evil-end-of-line | ||||
|   "_"   #'ranger | ||||
|   "-"   #'dired-jump | ||||
|   "sl"  #'wpc/evil-window-vsplit-right | ||||
|   "sl"  #'functions-evil-window-vsplit-right | ||||
|   "sh"  #'evil-window-vsplit | ||||
|   "sk"  #'evil-window-split | ||||
|   "sj"  #'wpc/evil-window-split-down) | ||||
|   "sj"  #'functions-evil-window-split-down) | ||||
| 
 | ||||
| (general-nmap | ||||
|   :keymaps 'override | ||||
|  | @ -114,19 +118,19 @@ | |||
| ;; have to bound to the readline function that deletes the entire line. | ||||
| (general-unbind "C-u") | ||||
| 
 | ||||
| (defmacro keybinding/exwm (c fn) | ||||
| (defmacro keybindings-exwm (c fn) | ||||
|   "Bind C to FN using `exwm-input-set-key' with `kbd' applied to C." | ||||
|   `(exwm-input-set-key (kbd ,c) ,fn)) | ||||
| 
 | ||||
| (keybinding/exwm "C-M-v" #'ivy-clipmenu/copy) | ||||
| (keybinding/exwm "<XF86MonBrightnessUp>" #'screen-brightness/increase) | ||||
| (keybinding/exwm "<XF86MonBrightnessDown>" #'screen-brightness/decrease) | ||||
| (keybinding/exwm "<XF86AudioMute>" #'pulse-audio/toggle-mute) | ||||
| (keybinding/exwm "<XF86AudioLowerVolume>" #'pulse-audio/decrease-volume) | ||||
| (keybinding/exwm "<XF86AudioRaiseVolume>" #'pulse-audio/increase-volume) | ||||
| (keybinding/exwm "<XF86AudioMicMute>" #'pulse-audio/toggle-microphone) | ||||
| (keybinding/exwm (kbd/raw 'x11 "s") #'scrot/select) | ||||
| (keybinding/exwm "<C-M-tab>" #'window-manager-switch-to-exwm-buffer) | ||||
| (keybindings-exwm "C-M-v" #'ivy-clipmenu-copy) | ||||
| (keybindings-exwm "<XF86MonBrightnessUp>" #'screen-brightness/increase) | ||||
| (keybindings-exwm "<XF86MonBrightnessDown>" #'screen-brightness/decrease) | ||||
| (keybindings-exwm "<XF86AudioMute>" #'pulse-audio/toggle-mute) | ||||
| (keybindings-exwm "<XF86AudioLowerVolume>" #'pulse-audio/decrease-volume) | ||||
| (keybindings-exwm "<XF86AudioRaiseVolume>" #'pulse-audio/increase-volume) | ||||
| (keybindings-exwm "<XF86AudioMicMute>" #'pulse-audio/toggle-microphone) | ||||
| (keybindings-exwm (kbd-raw 'x11 "s") #'scrot-select) | ||||
| (keybindings-exwm "<C-M-tab>" #'window-manager-switch-to-exwm-buffer) | ||||
| 
 | ||||
| (general-define-key | ||||
|  :keymaps 'override | ||||
|  | @ -168,11 +172,11 @@ | |||
|  "W" #'balance-windows | ||||
|  "gs" #'magit-status | ||||
|  "E" #'refine | ||||
|  "es" #'wpc/create-snippet | ||||
|  "es" #'functions-create-snippet | ||||
|  "l" #'linum-mode | ||||
|  "B" #'magit-blame | ||||
|  "w" #'save-buffer | ||||
|  "r" #'wpc/evil-replace-under-point | ||||
|  "r" #'functions-evil-replace-under-point | ||||
|  "R" #'deadgrep) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|  | @ -180,13 +184,13 @@ | |||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| ;; Show or hide a vterm buffer.  I'm intentionally not defining this in | ||||
| ;; vterm-mgt.el because it consumes `buffer/show-previous', and I'd like to | ||||
| ;; vterm-mgt.el because it consumes `buffer-show-previous', and I'd like to | ||||
| ;; avoid bloating vterm-mgt.el with dependencies that others may not want. | ||||
| (general-define-key (kbd/raw 'x11 "t") | ||||
| (general-define-key (kbd-raw 'x11 "t") | ||||
|                     (lambda () | ||||
|                       (interactive) | ||||
|                       (if (vterm-mgt--instance? (current-buffer)) | ||||
|                           (switch-to-buffer (first (buffer/source-code-buffers))) | ||||
|                           (switch-to-buffer (first (buffer-source-code-buffers))) | ||||
|                         (call-interactively #'vterm-mgt-find-or-create)))) | ||||
| 
 | ||||
| (general-define-key | ||||
|  | @ -201,15 +205,15 @@ | |||
| ;; Displays | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when (device/work-laptop?) | ||||
|   (keybinding/exwm "<XF86Display>" #'display/cycle-display-states) | ||||
| (when (device-work-laptop?) | ||||
|   (keybindings-exwm "<XF86Display>" #'display-cycle-display-states) | ||||
|   (general-define-key | ||||
|    :prefix "<SPC>" | ||||
|    :states '(normal) | ||||
|    "d0" #'display/disable-laptop | ||||
|    "d1" #'display/enable-laptop | ||||
|    "D0" #'display/disable-4k | ||||
|    "D1" #'display/enable-4k)) | ||||
|    "d0" #'display-disable-laptop | ||||
|    "d1" #'display-enable-laptop | ||||
|    "D0" #'display-disable-4k | ||||
|    "D1" #'display-enable-4k)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; notmuch | ||||
|  | @ -227,7 +231,7 @@ | |||
|  "e" #'notmuch-show-archive-message-then-next-or-next-thread) | ||||
| 
 | ||||
| ;; TODO(wpcarro): Consider moving this to a separate module | ||||
| (defun evil-ex-define-cmd-local (cmd f) | ||||
| (defun keybindings--evil-ex-define-cmd-local (cmd f) | ||||
|   "Define CMD to F locally to a buffer." | ||||
|   (unless (local-variable-p 'evil-ex-commands) | ||||
|     (setq-local evil-ex-commands (copy-alist evil-ex-commands))) | ||||
|  | @ -241,7 +245,7 @@ | |||
| 
 | ||||
| (add-hook 'notmuch-message-mode-hook | ||||
|           (lambda () | ||||
|             (evil-ex-define-cmd-local "x" #'notmuch-mua-send-and-exit))) | ||||
|             (keybindings--evil-ex-define-cmd-local "x" #'notmuch-mua-send-and-exit))) | ||||
| 
 | ||||
| ;; For now, I'm mimmicking Gmail KBDs that I have memorized and enjoy | ||||
| (general-define-key | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; keyboard.el --- Managing keyboard preferences with Elisp -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Setting key repeat and other values. | ||||
|  | @ -21,38 +25,38 @@ | |||
| 
 | ||||
| ;; TODO: Support clamping functions for repeat-{rate,delay} to ensure only valid | ||||
| ;; values are sent to xset. | ||||
| (defcustom keyboard/repeat-rate 80 | ||||
| (defcustom keyboard-repeat-rate 80 | ||||
|   "The number of key repeat signals sent per second.") | ||||
| 
 | ||||
| (defcustom keyboard/repeat-delay 170 | ||||
| (defcustom keyboard-repeat-delay 170 | ||||
|   "The number of milliseconds before autorepeat starts.") | ||||
| 
 | ||||
| (defconst keyboard/repeat-rate-copy keyboard/repeat-rate | ||||
|   "Copy of `keyboard/repeat-rate' to support `keyboard/reset-key-repeat'.") | ||||
| (defconst keyboard-repeat-rate-copy keyboard-repeat-rate | ||||
|   "Copy of `keyboard-repeat-rate' to support `keyboard-reset-key-repeat'.") | ||||
| 
 | ||||
| (defconst keyboard/repeat-delay-copy keyboard/repeat-delay | ||||
|   "Copy of `keyboard/repeat-delay' to support `keyboard/reset-key-repeat'.") | ||||
| (defconst keyboard-repeat-delay-copy keyboard-repeat-delay | ||||
|   "Copy of `keyboard-repeat-delay' to support `keyboard-reset-key-repeat'.") | ||||
| 
 | ||||
| (defcustom keyboard/install-preferences? t | ||||
| (defcustom keyboard-install-preferences? t | ||||
|   "When t, install keyboard preferences.") | ||||
| 
 | ||||
| (defcustom keyboard/install-kbds? nil | ||||
| (defcustom keyboard-install-kbds? nil | ||||
|   "When t, install keybindings.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Functions | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun keyboard/message (x) | ||||
| (defun keyboard-message (x) | ||||
|   "Message X in a structured way." | ||||
|   (message (string-format "[keyboard.el] %s" x))) | ||||
| 
 | ||||
| (cl-defun keyboard/set-key-repeat (&key | ||||
|                                    (rate keyboard/repeat-rate) | ||||
|                                    (delay keyboard/repeat-delay)) | ||||
| (cl-defun keyboard-set-key-repeat (&key | ||||
|                                    (rate keyboard-repeat-rate) | ||||
|                                    (delay keyboard-repeat-delay)) | ||||
|   "Use xset to set the key-repeat RATE and DELAY." | ||||
|   (prelude-start-process | ||||
|    :name "keyboard/set-key-repeat" | ||||
|    :name "keyboard-set-key-repeat" | ||||
|    :command (string-format "xset r rate %s %s" delay rate))) | ||||
| 
 | ||||
| ;; NOTE: Settings like this are machine-dependent. For instance I only need to | ||||
|  | @ -62,91 +66,91 @@ | |||
| ;; than once, xmodmap will start to error about non-existent Caps_Lock symbol. | ||||
| ;; For more information see here: | ||||
| ;; https://unix.stackexchange.com/questions/108207/how-to-map-caps-lock-as-the-compose-key-using-xmodmap-portably-and-idempotently | ||||
| (defun keyboard/swap-caps-lock-and-escape () | ||||
| (defun keyboard-swap-caps-lock-and-escape () | ||||
|   "Swaps the caps lock and escape keys using xmodmap." | ||||
|   (interactive) | ||||
|   ;; TODO: Ensure these work once the tokenizing in prelude-start-process works | ||||
|   ;; as expected. | ||||
|   (start-process "keyboard/swap-caps-lock-and-escape" nil "/usr/bin/xmodmap" "-e" | ||||
|   (start-process "keyboard-swap-caps-lock-and-escape" nil "/usr/bin/xmodmap" "-e" | ||||
|                  "remove Lock = Caps_Lock") | ||||
|   (start-process "keyboard/swap-caps-lock-and-escape" nil "/usr/bin/xmodmap" "-e" | ||||
|   (start-process "keyboard-swap-caps-lock-and-escape" nil "/usr/bin/xmodmap" "-e" | ||||
|                  "keysym Caps_Lock = Escape")) | ||||
| 
 | ||||
| (defun keyboard/inc-repeat-rate () | ||||
|   "Increment `keyboard/repeat-rate'." | ||||
| (defun keyboard-inc-repeat-rate () | ||||
|   "Increment `keyboard-repeat-rate'." | ||||
|   (interactive) | ||||
|   (setq keyboard/repeat-rate (number/inc keyboard/repeat-rate)) | ||||
|   (keyboard/set-key-repeat :rate keyboard/repeat-rate) | ||||
|   (keyboard/message | ||||
|    (string-format "Rate: %s" keyboard/repeat-rate))) | ||||
|   (setq keyboard-repeat-rate (number-inc keyboard-repeat-rate)) | ||||
|   (keyboard-set-key-repeat :rate keyboard-repeat-rate) | ||||
|   (keyboard-message | ||||
|    (string-format "Rate: %s" keyboard-repeat-rate))) | ||||
| 
 | ||||
| (defun keyboard/dec-repeat-rate () | ||||
|   "Decrement `keyboard/repeat-rate'." | ||||
| (defun keyboard-dec-repeat-rate () | ||||
|   "Decrement `keyboard-repeat-rate'." | ||||
|   (interactive) | ||||
|   (setq keyboard/repeat-rate (number/dec keyboard/repeat-rate)) | ||||
|   (keyboard/set-key-repeat :rate keyboard/repeat-rate) | ||||
|   (keyboard/message | ||||
|    (string-format "Rate: %s" keyboard/repeat-rate))) | ||||
|   (setq keyboard-repeat-rate (number-dec keyboard-repeat-rate)) | ||||
|   (keyboard-set-key-repeat :rate keyboard-repeat-rate) | ||||
|   (keyboard-message | ||||
|    (string-format "Rate: %s" keyboard-repeat-rate))) | ||||
| 
 | ||||
| (defun keyboard/inc-repeat-delay () | ||||
|   "Increment `keyboard/repeat-delay'." | ||||
| (defun keyboard-inc-repeat-delay () | ||||
|   "Increment `keyboard-repeat-delay'." | ||||
|   (interactive) | ||||
|   (setq keyboard/repeat-delay (number/inc keyboard/repeat-delay)) | ||||
|   (keyboard/set-key-repeat :delay keyboard/repeat-delay) | ||||
|   (keyboard/message | ||||
|    (string-format "Delay: %s" keyboard/repeat-delay))) | ||||
|   (setq keyboard-repeat-delay (number-inc keyboard-repeat-delay)) | ||||
|   (keyboard-set-key-repeat :delay keyboard-repeat-delay) | ||||
|   (keyboard-message | ||||
|    (string-format "Delay: %s" keyboard-repeat-delay))) | ||||
| 
 | ||||
| (defun keyboard/dec-repeat-delay () | ||||
|   "Decrement `keyboard/repeat-delay'." | ||||
| (defun keyboard-dec-repeat-delay () | ||||
|   "Decrement `keyboard-repeat-delay'." | ||||
|   (interactive) | ||||
|   (setq keyboard/repeat-delay (number/dec keyboard/repeat-delay)) | ||||
|   (keyboard/set-key-repeat :delay keyboard/repeat-delay) | ||||
|   (keyboard/message | ||||
|    (string-format "Delay: %s" keyboard/repeat-delay))) | ||||
|   (setq keyboard-repeat-delay (number-dec keyboard-repeat-delay)) | ||||
|   (keyboard-set-key-repeat :delay keyboard-repeat-delay) | ||||
|   (keyboard-message | ||||
|    (string-format "Delay: %s" keyboard-repeat-delay))) | ||||
| 
 | ||||
| (defun keyboard/print-key-repeat () | ||||
| (defun keyboard-print-key-repeat () | ||||
|   "Print the currently set values for key repeat." | ||||
|   (interactive) | ||||
|   (keyboard/message | ||||
|   (keyboard-message | ||||
|    (string-format "Rate: %s. Delay: %s" | ||||
|                   keyboard/repeat-rate | ||||
|                   keyboard/repeat-delay))) | ||||
|                   keyboard-repeat-rate | ||||
|                   keyboard-repeat-delay))) | ||||
| 
 | ||||
| (defun keyboard/set-preferences () | ||||
| (defun keyboard-set-preferences () | ||||
|   "Reset the keyboard preferences to their default values. | ||||
| NOTE: This function exists because occasionally I unplug and re-plug in a | ||||
|   keyboard and all of the preferences that I set using xset disappear." | ||||
|   (interactive) | ||||
|   (keyboard/swap-caps-lock-and-escape) | ||||
|   (keyboard/set-key-repeat :rate keyboard/repeat-rate | ||||
|                            :delay keyboard/repeat-delay) | ||||
|   (keyboard-swap-caps-lock-and-escape) | ||||
|   (keyboard-set-key-repeat :rate keyboard-repeat-rate | ||||
|                            :delay keyboard-repeat-delay) | ||||
|   ;; TODO: Implement this message function as a macro that pulls the current | ||||
|   ;; file name. | ||||
|   (keyboard/message "Keyboard preferences set!")) | ||||
|   (keyboard-message "Keyboard preferences set!")) | ||||
| 
 | ||||
| (defun keyboard/reset-key-repeat () | ||||
| (defun keyboard-reset-key-repeat () | ||||
|   "Set key repeat rate and delay to original values." | ||||
|   (interactive) | ||||
|   (keyboard/set-key-repeat :rate keyboard/repeat-rate-copy | ||||
|                            :delay keyboard/repeat-delay-copy) | ||||
|   (keyboard/message "Key repeat preferences reset.")) | ||||
|   (keyboard-set-key-repeat :rate keyboard-repeat-rate-copy | ||||
|                            :delay keyboard-repeat-delay-copy) | ||||
|   (keyboard-message "Key repeat preferences reset.")) | ||||
| 
 | ||||
| (when keyboard/install-preferences? | ||||
|   (keyboard/set-preferences)) | ||||
| (when keyboard-install-preferences? | ||||
|   (keyboard-set-preferences)) | ||||
| 
 | ||||
| ;; TODO: Define minor-mode for this. | ||||
| (when keyboard/install-kbds? | ||||
| (when keyboard-install-kbds? | ||||
|   (general-unbind 'motion "C-i" "C-y") | ||||
|   (general-define-key | ||||
|    ;; TODO: Choose better KBDs for these that don't interfere with useful evil | ||||
|    ;; ones. | ||||
|    ;; Use C-y when you accidentally send the key-repeat too high or too low to | ||||
|    ;; be meaningful. | ||||
|    "C-y" #'keyboard/reset-key-repeat | ||||
|    "C-i" #'keyboard/inc-repeat-rate | ||||
|    "C-u" #'keyboard/dec-repeat-rate | ||||
|    "C-S-i" #'keyboard/inc-repeat-delay | ||||
|    "C-S-u" #'keyboard/dec-repeat-delay)) | ||||
|    "C-y" #'keyboard-reset-key-repeat | ||||
|    "C-i" #'keyboard-inc-repeat-rate | ||||
|    "C-u" #'keyboard-dec-repeat-rate | ||||
|    "C-S-i" #'keyboard-inc-repeat-delay | ||||
|    "C-S-u" #'keyboard-dec-repeat-delay)) | ||||
| 
 | ||||
| (provide 'keyboard) | ||||
| ;;; keyboard.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; laptop-battery.el --- Display laptop battery information -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Some wrappers to obtain battery information. | ||||
|  | @ -30,28 +34,28 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun laptop-battery/available? () | ||||
| (defun laptop-battery-available? () | ||||
|   "Return t if battery information is available." | ||||
|   (maybe-some? battery-status-function)) | ||||
| 
 | ||||
| (defun laptop-battery/percentage () | ||||
| (defun laptop-battery-percentage () | ||||
|   "Return the current percentage of the battery." | ||||
|   (->> battery-status-function | ||||
|        funcall | ||||
|        (alist/get 112))) | ||||
|        (alist-get 112))) | ||||
| 
 | ||||
| (defun laptop-battery/print-percentage () | ||||
| (defun laptop-battery-print-percentage () | ||||
|   "Return the current percentage of the battery." | ||||
|   (interactive) | ||||
|   (->> (laptop-battery/percentage) | ||||
|   (->> (laptop-battery-percentage) | ||||
|        message)) | ||||
| 
 | ||||
| (defun laptop-battery/display () | ||||
| (defun laptop-battery-display () | ||||
|   "Display laptop battery percentage in the modeline." | ||||
|   (interactive) | ||||
|   (display-battery-mode 1)) | ||||
| 
 | ||||
| (defun laptop-battery/hide () | ||||
| (defun laptop-battery-hide () | ||||
|   "Hide laptop battery percentage in the modeline." | ||||
|   (interactive) | ||||
|   (display-battery-mode -1)) | ||||
|  |  | |||
|  | @ -1,8 +1,12 @@ | |||
| ;;; list.el --- Functions for working with lists. -*- lexical-binding: t -*- | ||||
| ;;; list.el --- Functions for working with lists -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Since I prefer having the `list/' namespace, I wrote this module to wrap many | ||||
| ;; Since I prefer having the `list-' namespace, I wrote this module to wrap many | ||||
| ;; of the functions that are defined in the the global namespace in ELisp.  I | ||||
| ;; sometimes forget the names of these functions, so it's nice for them to be | ||||
| ;; organized like this. | ||||
|  | @ -58,56 +62,56 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst list/tests? t | ||||
| (defconst list-tests? t | ||||
|   "When t, run the test suite.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun list/new () | ||||
| (defun list-new () | ||||
|   "Return a new, empty list." | ||||
|   '()) | ||||
| 
 | ||||
| (defun list/concat (&rest lists) | ||||
| (defun list-concat (&rest lists) | ||||
|   "Joins `LISTS' into on list." | ||||
|   (apply #'-concat lists)) | ||||
| 
 | ||||
| (defun list/join (joint xs) | ||||
| (defun list-join (joint xs) | ||||
|   "Join a list of strings, XS, with JOINT." | ||||
|   (if (list/empty? xs) | ||||
|   (if (list-empty? xs) | ||||
|       "" | ||||
|     (list/reduce (list/first xs) | ||||
|     (list-reduce (list-first xs) | ||||
|                  (lambda (x acc) | ||||
|                    (string-concat acc joint x)) | ||||
|                  (list/tail xs)))) | ||||
|                  (list-tail xs)))) | ||||
| 
 | ||||
| (defun list/length (xs) | ||||
| (defun list-length (xs) | ||||
|   "Return the number of elements in `XS'." | ||||
|   (length xs)) | ||||
| 
 | ||||
| (defun list/get (i xs) | ||||
| (defun list-get (i xs) | ||||
|   "Return the value in `XS' at `I', or nil." | ||||
|   (nth i xs)) | ||||
| 
 | ||||
| (defun list/head (xs) | ||||
| (defun list-head (xs) | ||||
|   "Return the head of `XS'." | ||||
|   (car xs)) | ||||
| 
 | ||||
| ;; TODO: Learn how to write proper function aliases. | ||||
| (defun list/first (xs) | ||||
|   "Alias for `list/head' for `XS'." | ||||
|   (list/head xs)) | ||||
| (defun list-first (xs) | ||||
|   "Alias for `list-head' for `XS'." | ||||
|   (list-head xs)) | ||||
| 
 | ||||
| (defun list/tail (xs) | ||||
| (defun list-tail (xs) | ||||
|   "Return the tail of `XS'." | ||||
|   (cdr xs)) | ||||
| 
 | ||||
| (defun list/reverse (xs) | ||||
| (defun list-reverse (xs) | ||||
|   "Reverses `XS'." | ||||
|   (reverse xs)) | ||||
| 
 | ||||
| (defun list/cons (x xs) | ||||
| (defun list-cons (x xs) | ||||
|   "Add `X' to the head of `XS'." | ||||
|   (cons x xs)) | ||||
| 
 | ||||
|  | @ -120,56 +124,56 @@ | |||
| ;;     (funcall f b a))) | ||||
| 
 | ||||
| ;; TODO: Make this function work. | ||||
| (defun list/reduce (acc f xs) | ||||
| (defun list-reduce (acc f xs) | ||||
|   "Return over `XS' calling `F' on an element in `XS'and `ACC'." | ||||
|   (-reduce-from (lambda (acc x) (funcall f x acc)) acc xs)) | ||||
| 
 | ||||
| ;; TODO: Support this. It seems like `alist/set' is not working as I expected it | ||||
| ;; TODO: Support this. It seems like `alist-set' is not working as I expected it | ||||
| ;; to. Perhaps we should add some tests to confirm the expected behavior. | ||||
| ;; (cl-defun list/index (f xs &key (transform (lambda (x) x))) | ||||
| ;; (cl-defun list-index (f xs &key (transform (lambda (x) x))) | ||||
| ;;   "Return a mapping of F applied to each x in XS to TRANSFORM applied to x. | ||||
| ;; The TRANSFORM function defaults to the identity function." | ||||
| ;;   (->> xs | ||||
| ;;        (list/reduce (alist/new) | ||||
| ;;        (list-reduce (alist-new) | ||||
| ;;                     (lambda (x acc) | ||||
| ;;                       (let ((k (funcall f x)) | ||||
| ;;                             (v (funcall transform x))) | ||||
| ;;                         (if (alist/has-key? k acc) | ||||
| ;;                         (if (alist-has-key? k acc) | ||||
| ;;                             (setf (alist-get k acc) (list v)) | ||||
| ;;                           (setf (alist-get k acc) (list v)))))))) | ||||
| ;; (prelude-assert | ||||
| ;;  (equal '(("John" . ("Cleese" "Malkovich")) | ||||
| ;;           ("Thomas" . ("Aquinas"))) | ||||
| ;;         (list/index (lambda (x) (plist-get x :first-name)) | ||||
| ;;         (list-index (lambda (x) (plist-get x :first-name)) | ||||
| ;;                     '((:first-name "John" :last-name "Cleese") | ||||
| ;;                       (:first-name "John" :last-name "Malkovich") | ||||
| ;;                       (:first-name "Thomas" :last-name "Aquinas")) | ||||
| ;;                     :transform (lambda (x) (plist-get x :last-name))))) | ||||
| 
 | ||||
| (defun list/map (f xs) | ||||
| (defun list-map (f xs) | ||||
|   "Call `F' on each element of `XS'." | ||||
|   (-map f xs)) | ||||
| 
 | ||||
| (defun list/map-indexed (f xs) | ||||
| (defun list-map-indexed (f xs) | ||||
|   "Call `F' on each element of `XS' along with its index." | ||||
|   (-map-indexed (lambda (i x) (funcall f x i)) xs)) | ||||
| 
 | ||||
| (defun list/filter (p xs) | ||||
| (defun list-filter (p xs) | ||||
|   "Return a subset of XS where predicate P returned t." | ||||
|   (list/reverse | ||||
|    (list/reduce | ||||
|   (list-reverse | ||||
|    (list-reduce | ||||
|     '() | ||||
|     (lambda (x acc) | ||||
|       (if (funcall p x) | ||||
|           (list/cons x acc) | ||||
|           (list-cons x acc) | ||||
|         acc)) | ||||
|     xs))) | ||||
| 
 | ||||
| (defun list/reject (p xs) | ||||
| (defun list-reject (p xs) | ||||
|   "Return a subset of XS where predicate of P return nil." | ||||
|   (list/filter (lambda (x) (not (funcall p x))) xs)) | ||||
|   (list-filter (lambda (x) (not (funcall p x))) xs)) | ||||
| 
 | ||||
| (defun list/find (p xs) | ||||
| (defun list-find (p xs) | ||||
|   "Return the first x in XS that passes P or nil." | ||||
|   (-find p xs)) | ||||
| 
 | ||||
|  | @ -177,64 +181,64 @@ | |||
| ;; Predicates | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun list/instance? (xs) | ||||
| (defun list-instance? (xs) | ||||
|   "Return t if `XS' is a list. | ||||
| Be leery of using this with things like alists.  Many data structures in Elisp | ||||
|   are implemented using linked lists." | ||||
|   (listp xs)) | ||||
| 
 | ||||
| (defun list/empty? (xs) | ||||
| (defun list-empty? (xs) | ||||
|   "Return t if XS are empty." | ||||
|   (= 0 (list/length xs))) | ||||
|   (= 0 (list-length xs))) | ||||
| 
 | ||||
| (defun list/all? (p xs) | ||||
| (defun list-all? (p xs) | ||||
|   "Return t if all `XS' pass the predicate, `P'." | ||||
|   (-all? p xs)) | ||||
| 
 | ||||
| (defun list/any? (p xs) | ||||
| (defun list-any? (p xs) | ||||
|   "Return t if any `XS' pass the predicate, `P'." | ||||
|   (-any? p xs)) | ||||
| 
 | ||||
| (defun list/contains? (x xs) | ||||
| (defun list-contains? (x xs) | ||||
|   "Return t if X is in XS using `equal'." | ||||
|   (-contains? xs x)) | ||||
| 
 | ||||
| (defun list/xs-distinct-by? (f xs) | ||||
| (defun list-xs-distinct-by? (f xs) | ||||
|   "Return t if all elements in XS are distinct after applying F to each." | ||||
|   (= (length xs) | ||||
|      (->> xs (-map f) set/from-list set/count))) | ||||
|      (->> xs (-map f) set-from-list set-count))) | ||||
| 
 | ||||
| ;; TODO: Support dedupe. | ||||
| ;; TODO: Should we call this unique? Or distinct? | ||||
| 
 | ||||
| ;; TODO: Add tests. | ||||
| (defun list/dedupe-adjacent (xs) | ||||
| (defun list-dedupe-adjacent (xs) | ||||
|   "Return XS without adjacent duplicates." | ||||
|   (prelude-assert (not (list/empty? xs))) | ||||
|   (list/reduce (list (list/first xs)) | ||||
|   (prelude-assert (not (list-empty? xs))) | ||||
|   (list-reduce (list (list-first xs)) | ||||
|     (lambda (x acc) | ||||
|       (if (equal x (list/first acc)) | ||||
|       (if (equal x (list-first acc)) | ||||
|           acc | ||||
|         (list/cons x acc))) | ||||
|         (list-cons x acc))) | ||||
|     xs)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| ;; (when list/tests? | ||||
| ;; (when list-tests? | ||||
| ;;   (prelude-assert | ||||
| ;;    (= 0 | ||||
| ;;       (list/length '()))) | ||||
| ;;       (list-length '()))) | ||||
| ;;   (prelude-assert | ||||
| ;;    (= 5 | ||||
| ;;       (list/length '(1 2 3 4 5)))) | ||||
| ;;       (list-length '(1 2 3 4 5)))) | ||||
| ;;   (prelude-assert | ||||
| ;;    (= 16 | ||||
| ;;       (list/reduce 1 (lambda (x acc) (+ x acc)) '(1 2 3 4 5)))) | ||||
| ;;       (list-reduce 1 (lambda (x acc) (+ x acc)) '(1 2 3 4 5)))) | ||||
| ;;   (prelude-assert | ||||
| ;;    (equal '(2 4 6 8 10) | ||||
| ;;           (list/map (lambda (x) (* x 2)) '(1 2 3 4 5))))) | ||||
| ;;           (list-map (lambda (x) (* x 2)) '(1 2 3 4 5))))) | ||||
| 
 | ||||
| (provide 'list) | ||||
| ;;; list.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; math.el --- Math stuffs -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| ;; Homepage: https://user.git.corp.google.com/wpcarro/briefcase | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Containing some useful mathematical functions. | ||||
|  | @ -16,7 +20,7 @@ | |||
| ;; Constants | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst math/pi pi | ||||
| (defconst math-pi pi | ||||
|   "The number pi.") | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|  | @ -25,7 +29,7 @@ | |||
| 
 | ||||
| ;; TODO: Support all three arguments. | ||||
| ;; Int -> Int -> Int -> Boolean | ||||
| (cl-defun math/triangle-of-power (&key base power result) | ||||
| (cl-defun math-triangle-of-power (&key base power result) | ||||
|   ;; TODO: Assert two of three are set. | ||||
|   (cond | ||||
|    ((maybe-somes? base power result) | ||||
|  | @ -39,19 +43,19 @@ | |||
|    (t | ||||
|     (error "Two of the three arguments must be set")))) | ||||
| 
 | ||||
| (defun math/mod (x y) | ||||
| (defun math-mod (x y) | ||||
|   "Return X mod Y." | ||||
|   (mod x y)) | ||||
| 
 | ||||
| (defun math/exp (x y) | ||||
| (defun math-exp (x y) | ||||
|   "Return X raised to the Y." | ||||
|   (expt x y)) | ||||
| 
 | ||||
| (defun math/round (x) | ||||
| (defun math-round (x) | ||||
|   "Round X to nearest ones digit." | ||||
|   (round x)) | ||||
| 
 | ||||
| (defun math/floor (x) | ||||
| (defun math-floor (x) | ||||
|   "Floor value X." | ||||
|   (floor x)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -60,11 +60,11 @@ | |||
| 
 | ||||
| (defun maybe-nils? (&rest xs) | ||||
|   "Return t if all XS are nil." | ||||
|   (list/all? #'maybe-nil? xs)) | ||||
|   (list-all? #'maybe-nil? xs)) | ||||
| 
 | ||||
| (defun maybe-somes? (&rest xs) | ||||
|   "Return t if all XS are non-nil." | ||||
|   (list/all? #'maybe-some? xs)) | ||||
|   (list-all? #'maybe-some? xs)) | ||||
| 
 | ||||
| (defun maybe-default (default x) | ||||
|   "Return DEFAULT when X is nil." | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; modeline.el --- Customize my Emacs mode-line -*- lexical-binding: t -*- | ||||
| ;;; modeline.el --- Customize my mode-line -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; Package-Requires: ((emacs "25.1")) | ||||
| ;; Homepage: https://user.git.corp.google.com/wpcarro/briefcase | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Because I use EXWM, I treat my Emacs mode-line like my system bar: I need to | ||||
|  | @ -13,7 +17,7 @@ | |||
| 
 | ||||
| (use-package telephone-line) | ||||
| 
 | ||||
| (defun modeline/bottom-right-window? () | ||||
| (defun modeline-bottom-right-window? () | ||||
|   "Determines whether the last (i.e. bottom-right) window of the | ||||
|   active frame is showing the buffer in which this function is | ||||
|   executed." | ||||
|  | @ -23,23 +27,23 @@ | |||
|          (last-window (car (seq-intersection right-windows bottom-windows)))) | ||||
|     (eq (current-buffer) (window-buffer last-window)))) | ||||
| 
 | ||||
| (defun modeline/maybe-render-time () | ||||
| (defun modeline-maybe-render-time () | ||||
|   "Renders the mode-line-misc-info string for display in the | ||||
|   mode-line if the currently active window is the last one in the | ||||
|   frame. | ||||
| 
 | ||||
|   The idea is to not display information like the current time, | ||||
|   load, battery levels on all buffers." | ||||
|   (when (modeline/bottom-right-window?) | ||||
|   (when (modeline-bottom-right-window?) | ||||
|     (telephone-line-raw mode-line-misc-info t))) | ||||
| 
 | ||||
| (defun modeline/setup () | ||||
| (defun modeline-setup () | ||||
|   "Render my custom modeline." | ||||
|   (telephone-line-defsegment telephone-line-last-window-segment () | ||||
|     (modeline/maybe-render-time)) | ||||
|     (modeline-maybe-render-time)) | ||||
|   ;; Display the current EXWM workspace index in the mode-line | ||||
|   (telephone-line-defsegment telephone-line-exwm-workspace-index () | ||||
|     (when (modeline/bottom-right-window?) | ||||
|     (when (modeline-bottom-right-window?) | ||||
|       (format "[%s]" exwm-workspace-current-index))) | ||||
|   ;; Define a highlight font for ~ important ~ information in the last | ||||
|   ;; window. | ||||
|  | @ -61,4 +65,4 @@ | |||
|   (telephone-line-mode 1)) | ||||
| 
 | ||||
| (provide 'modeline) | ||||
| ;; modeline.el ends here | ||||
| ;;; modeline.el ends here | ||||
|  |  | |||
|  | @ -40,40 +40,40 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst number/test? t | ||||
| (defconst number-test? t | ||||
|   "When t, run the test suite defined herein.") | ||||
| 
 | ||||
| ;; TODO: What about int.el? | ||||
| 
 | ||||
| ;; TODO: How do we handle a number typeclass? | ||||
| 
 | ||||
| (defun number/positive? (x) | ||||
| (defun number-positive? (x) | ||||
|   "Return t if `X' is a positive number." | ||||
|   (> x 0)) | ||||
| 
 | ||||
| (defun number/negative? (x) | ||||
| (defun number-negative? (x) | ||||
|   "Return t if `X' is a positive number." | ||||
|   (< x 0)) | ||||
| 
 | ||||
| ;; TODO: Don't rely on this. Need to have 10.0 and 10 behave similarly. | ||||
| (defun number/float? (x) | ||||
| (defun number-float? (x) | ||||
|   "Return t if `X' is a floating point number." | ||||
|   (floatp x)) | ||||
| 
 | ||||
| (defun number/natural? (x) | ||||
| (defun number-natural? (x) | ||||
|   "Return t if `X' is a natural number." | ||||
|   (and (number/positive? x) | ||||
|        (not (number/float? x)))) | ||||
|   (and (number-positive? x) | ||||
|        (not (number-float? x)))) | ||||
| 
 | ||||
| (defun number/whole? (x) | ||||
| (defun number-whole? (x) | ||||
|   "Return t if `X' is a whole number." | ||||
|   (or (= 0 x) | ||||
|       (number/natural? x))) | ||||
|       (number-natural? x))) | ||||
| 
 | ||||
| (defun number/integer? (x) | ||||
| (defun number-integer? (x) | ||||
|   "Return t if `X' is an integer." | ||||
|   (or (number/whole? x) | ||||
|       (number/natural? (- x)))) | ||||
|   (or (number-whole? x) | ||||
|       (number-natural? (- x)))) | ||||
| 
 | ||||
| ;; TODO: How defensive should these guards be?  Should we assert that the inputs | ||||
| ;; are integers before checking evenness or oddness? | ||||
|  | @ -83,28 +83,28 @@ | |||
| ;; TODO: How should rational numbers be handled? Lisp is supposedly famous for | ||||
| ;; its handling of rational numbers. | ||||
| ;; TODO: `calc-mode' supports rational numbers as "1:2" meaning "1/2" | ||||
| ;; (defun number/rational? (x)) | ||||
| ;; (defun number-rational? (x)) | ||||
| 
 | ||||
| ;; TODO: Can or should I support real numbers? | ||||
| ;; (defun number/real? (x)) | ||||
| ;; (defun number-real? (x)) | ||||
| 
 | ||||
| (defun number/even? (x) | ||||
| (defun number-even? (x) | ||||
|   "Return t if `X' is an even number." | ||||
|   (or (= 0 x) | ||||
|       (= 0 (mod x 2)))) | ||||
| 
 | ||||
| (defun number/odd? (x) | ||||
| (defun number-odd? (x) | ||||
|   "Return t if `X' is an odd number." | ||||
|   (not (number/even? x))) | ||||
|   (not (number-even? x))) | ||||
| 
 | ||||
| (defun number/dec (x) | ||||
| (defun number-dec (x) | ||||
|   "Subtract one from `X'. | ||||
| While this function is undeniably trivial, I have unintentionally done (- 1 x) | ||||
|   when in fact I meant to do (- x 1) that I figure it's better for this function | ||||
|   to exist, and for me to train myself to reach for it and its inc counterpart." | ||||
|   (- x 1)) | ||||
| 
 | ||||
| (defun number/inc (x) | ||||
| (defun number-inc (x) | ||||
|   "Add one to `X'." | ||||
|   (+ x 1)) | ||||
| 
 | ||||
|  | @ -112,46 +112,46 @@ While this function is undeniably trivial, I have unintentionally done (- 1 x) | |||
| ;; too vague? | ||||
| ;; TODO: Resolve the circular dependency that this introduces with series.el, | ||||
| ;; and then re-enable this function and its tests below. | ||||
| ;; (defun number/factorial (x) | ||||
| ;; (defun number-factorial (x) | ||||
| ;;   "Return factorial of `X'." | ||||
| ;;   (cond | ||||
| ;;    ((number/negative? x) (error "Will not take factorial of negative numbers")) | ||||
| ;;    ((number-negative? x) (error "Will not take factorial of negative numbers")) | ||||
| ;;    ((= 0 x) 1) | ||||
| ;;    ;; NOTE: Using `series/range' introduces a circular dependency because: | ||||
| ;;    ;; series -> number -> series.  Conceptually, however, this should be | ||||
| ;;    ;; perfectly acceptable. | ||||
| ;;    (t (->> (series/range 1 x) | ||||
| ;;            (list/reduce 1 #'*))))) | ||||
| ;;            (list-reduce 1 #'*))))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when number/test? | ||||
| (when number-test? | ||||
|   (prelude-assert | ||||
|    (number/positive? 10)) | ||||
|    (number-positive? 10)) | ||||
|   (prelude-assert | ||||
|    (number/natural? 10)) | ||||
|    (number-natural? 10)) | ||||
|   (prelude-assert | ||||
|    (number/whole? 10)) | ||||
|    (number-whole? 10)) | ||||
|   (prelude-assert | ||||
|    (number/whole? 0)) | ||||
|    (number-whole? 0)) | ||||
|   (prelude-assert | ||||
|    (number/integer? 10)) | ||||
|    (number-integer? 10)) | ||||
|   ;; (prelude-assert | ||||
|   ;;  (= 120 (number/factorial 5))) | ||||
|   ;;  (= 120 (number-factorial 5))) | ||||
|   (prelude-assert | ||||
|    (number/even? 6)) | ||||
|    (number-even? 6)) | ||||
|   (prelude-refute | ||||
|    (number/odd? 6)) | ||||
|    (number-odd? 6)) | ||||
|   (prelude-refute | ||||
|    (number/positive? -10)) | ||||
|    (number-positive? -10)) | ||||
|   (prelude-refute | ||||
|    (number/natural? 10.0)) | ||||
|    (number-natural? 10.0)) | ||||
|   (prelude-refute | ||||
|    (number/natural? -10)) | ||||
|    (number-natural? -10)) | ||||
|   (prelude-refute | ||||
|    (number/natural? -10.0))) | ||||
|    (number-natural? -10.0))) | ||||
| 
 | ||||
| (provide 'number) | ||||
| ;;; number.el ends here | ||||
|  |  | |||
|  | @ -33,8 +33,8 @@ | |||
| ;; TODO: Make this work with sequences instead of lists. | ||||
| (defun random-choice (xs) | ||||
|   "Return a random element of `XS'." | ||||
|   (let ((ct (list/length xs))) | ||||
|     (list/get | ||||
|   (let ((ct (list-length xs))) | ||||
|     (list-get | ||||
|      (random-int ct) | ||||
|      xs))) | ||||
| 
 | ||||
|  | @ -45,9 +45,9 @@ | |||
| ;; TODO: This may not work if any of these generate numbers like 0, 1, etc. | ||||
| (defun random-uuid () | ||||
|   "Return a generated UUID string." | ||||
|   (let ((eight  (number/dec (math/triangle-of-power :base 16 :power 8))) | ||||
|         (four   (number/dec (math/triangle-of-power :base 16 :power 4))) | ||||
|         (twelve (number/dec (math/triangle-of-power :base 16 :power 12)))) | ||||
|   (let ((eight  (number-dec (math-triangle-of-power :base 16 :power 8))) | ||||
|         (four   (number-dec (math-triangle-of-power :base 16 :power 4))) | ||||
|         (twelve (number-dec (math-triangle-of-power :base 16 :power 12)))) | ||||
|     (format "%x-%x-%x-%x-%x" | ||||
|             (random-int eight) | ||||
|             (random-int four) | ||||
|  | @ -57,25 +57,25 @@ | |||
| 
 | ||||
| (defun random-token (length) | ||||
|   "Return a randomly generated hexadecimal string of LENGTH." | ||||
|   (->> (series/range 0 (number/dec length)) | ||||
|        (list/map (lambda (_) (format "%x" (random-int 15)))) | ||||
|        (list/join ""))) | ||||
|   (->> (series/range 0 (number-dec length)) | ||||
|        (list-map (lambda (_) (format "%x" (random-int 15)))) | ||||
|        (list-join ""))) | ||||
| 
 | ||||
| ;; TODO: Support random-sample | ||||
| ;; (defun random-sample (n xs) | ||||
| ;;   "Return a randomly sample of list XS of size N." | ||||
| ;;   (prelude-assert (and (>= n 0) (< n (list/length xs)))) | ||||
| ;;   (prelude-assert (and (>= n 0) (< n (list-length xs)))) | ||||
| ;;   (cl-labels ((do-sample | ||||
| ;;                (n xs y ys) | ||||
| ;;                (if (= n (set/count ys)) | ||||
| ;;                (if (= n (set-count ys)) | ||||
| ;;                    (->> ys | ||||
| ;;                         set/to-list | ||||
| ;;                         (list/map (lambda (i) | ||||
| ;;                                     (list/get i xs)))) | ||||
| ;;                  (if (set/contains? y ys) | ||||
| ;;                      (do-sample n xs (random-int (list/length xs)) ys) | ||||
| ;;                    (do-sample n xs y (set/add y ys)))))) | ||||
| ;;     (do-sample n xs (random-int (list/length xs)) (set/new)))) | ||||
| ;;                         set-to-list | ||||
| ;;                         (list-map (lambda (i) | ||||
| ;;                                     (list-get i xs)))) | ||||
| ;;                  (if (set-contains? y ys) | ||||
| ;;                      (do-sample n xs (random-int (list-length xs)) ys) | ||||
| ;;                    (do-sample n xs y (set-add y ys)))))) | ||||
| ;;     (do-sample n xs (random-int (list-length xs)) (set-new)))) | ||||
| 
 | ||||
| (provide 'random) | ||||
| ;;; random.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; scope.el --- Work with a scope data structure -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Exposing an API for working with a scope data structure in a non-mutative | ||||
|  | @ -9,89 +13,93 @@ | |||
| 
 | ||||
| ;;; Code: | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Dependencies | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (require 'alist) | ||||
| (require 'stack) | ||||
| (require 'struct) | ||||
| (require 'macros) | ||||
| 
 | ||||
| (cl-defstruct scope scopes) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Create | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun scope/new () | ||||
|   "Return an empty scope." | ||||
|   (make-scope :scopes (->> (stack/new) | ||||
|                            (stack/push (alist/new))))) | ||||
| (cl-defstruct scope scopes) | ||||
| 
 | ||||
| (defun scope/flatten (xs) | ||||
| (defun scope-new () | ||||
|   "Return an empty scope." | ||||
|   (make-scope :scopes (->> (stack-new) | ||||
|                            (stack-push (alist-new))))) | ||||
| 
 | ||||
| (defun scope-flatten (xs) | ||||
|   "Return a flattened representation of the scope, XS. | ||||
| The newest bindings eclipse the oldest." | ||||
|   (->> xs | ||||
|        scope-scopes | ||||
|        stack/to-list | ||||
|        (list/reduce (alist/new) | ||||
|        stack-to-list | ||||
|        (list-reduce (alist-new) | ||||
|                     (lambda (scope acc) | ||||
|                       (alist/merge acc scope))))) | ||||
|                       (alist-merge acc scope))))) | ||||
| 
 | ||||
| (defun scope/push-new (xs) | ||||
| (defun scope-push-new (xs) | ||||
|   "Push a new, empty scope onto XS." | ||||
|   (struct-update scope | ||||
|                  scopes | ||||
|                  (>> (stack/push (alist/new))) | ||||
|                  (>> (stack-push (alist-new))) | ||||
|                  xs)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Read | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun scope/get (k xs) | ||||
| (defun scope-get (k xs) | ||||
|   "Return K from XS if it's in scope." | ||||
|   (->> xs | ||||
|        scope/flatten | ||||
|        (alist/get k))) | ||||
|        scope-flatten | ||||
|        (alist-get k))) | ||||
| 
 | ||||
| (defun scope/current (xs) | ||||
| (defun scope-current (xs) | ||||
|   "Return the newest scope from XS." | ||||
|   (let ((xs-copy (copy-scope xs))) | ||||
|     (->> xs-copy | ||||
|          scope-scopes | ||||
|          stack/peek))) | ||||
|          stack-peek))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Update | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun scope/set (k v xs) | ||||
| (defun scope-set (k v xs) | ||||
|   "Set value, V, at key, K, in XS for the current scope." | ||||
|   (struct-update scope | ||||
|                  scopes | ||||
|                  (>> (stack/map-top (>> (alist/set k v)))) | ||||
|                  (>> (stack-map-top (>> (alist-set k v)))) | ||||
|                  xs)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Delete | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun scope/pop (xs) | ||||
| (defun scope-pop (xs) | ||||
|   "Return a new scope without the top element from XS." | ||||
|   (->> xs | ||||
|        scope-scopes | ||||
|        stack/pop)) | ||||
|        stack-pop)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Predicates | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun scope/defined? (k xs) | ||||
| (defun scope-defined? (k xs) | ||||
|   "Return t if K is in scope of XS." | ||||
|   (->> xs | ||||
|        scope/flatten | ||||
|        (alist/has-key? k))) | ||||
|        scope-flatten | ||||
|        (alist-has-key? k))) | ||||
| 
 | ||||
| ;; TODO: Find a faster way to write aliases like this. | ||||
| (defun scope/instance? (xs) | ||||
| (defun scope-instance? (xs) | ||||
|   "Return t if XS is a scope struct." | ||||
|   (scope-p xs)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| ;;; screen-brightness.el --- Control laptop screen brightness -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| 
 | ||||
| ;;; Commentary: | ||||
|  |  | |||
|  | @ -1,4 +1,9 @@ | |||
| ;;; scrot.el --- Screenshot functions -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; scrot is a Linux utility for taking screenshots. | ||||
|  | @ -19,43 +24,43 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst scrot/screenshot-directory "~/Downloads" | ||||
| (defconst scrot-screenshot-directory "~/Downloads" | ||||
|   "The default directory for screenshot outputs.") | ||||
| 
 | ||||
| (defconst scrot/path-to-executable "/usr/bin/scrot" | ||||
| (defconst scrot-path-to-executable "/usr/bin/scrot" | ||||
|   "Path to the scrot executable.") | ||||
| 
 | ||||
| (defconst scrot/output-format "screenshot_%H:%M:%S_%Y-%m-%d.png" | ||||
| (defconst scrot-output-format "screenshot_%H:%M:%S_%Y-%m-%d.png" | ||||
|   "The format string for the output screenshot file. | ||||
| See scrot's man page for more information.") | ||||
| 
 | ||||
| (defun scrot/copy-image (path) | ||||
| (defun scrot-copy-image (path) | ||||
|   "Use xclip to copy the image at PATH to the clipboard. | ||||
| This currently only works for PNG files because that's what I'm outputting" | ||||
|   (call-process "xclip" nil nil nil | ||||
|                 "-selection" "clipboard" "-t" "image/png" path) | ||||
|   (message (string-format "[scrot.el] Image copied to clipboard!"))) | ||||
| 
 | ||||
| (defmacro scrot/call (&rest args) | ||||
| (defmacro scrot-call (&rest args) | ||||
|   "Call scrot with ARGS." | ||||
|   `(call-process ,scrot/path-to-executable nil nil nil ,@args)) | ||||
|   `(call-process ,scrot-path-to-executable nil nil nil ,@args)) | ||||
| 
 | ||||
| (defun scrot/fullscreen () | ||||
| (defun scrot-fullscreen () | ||||
|   "Screenshot the entire screen." | ||||
|   (interactive) | ||||
|   (let ((screenshot-path (f-join scrot/screenshot-directory | ||||
|                                  (ts-format scrot/output-format (ts-now))))) | ||||
|     (scrot/call screenshot-path) | ||||
|     (scrot/copy-image screenshot-path))) | ||||
|   (let ((screenshot-path (f-join scrot-screenshot-directory | ||||
|                                  (ts-format scrot-output-format (ts-now))))) | ||||
|     (scrot-call screenshot-path) | ||||
|     (scrot-copy-image screenshot-path))) | ||||
| 
 | ||||
| (defun scrot/select () | ||||
| (defun scrot-select () | ||||
|   "Click-and-drag to screenshot a region. | ||||
| The output path is copied to the user's clipboard." | ||||
|   (interactive) | ||||
|   (let ((screenshot-path (f-join scrot/screenshot-directory | ||||
|                                  (ts-format scrot/output-format (ts-now))))) | ||||
|     (scrot/call "--select" screenshot-path) | ||||
|     (scrot/copy-image screenshot-path))) | ||||
|   (let ((screenshot-path (f-join scrot-screenshot-directory | ||||
|                                  (ts-format scrot-output-format (ts-now))))) | ||||
|     (scrot-call "--select" screenshot-path) | ||||
|     (scrot-copy-image screenshot-path))) | ||||
| 
 | ||||
| (provide 'scrot) | ||||
| ;;; scrot.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; sequence.el --- Working with the "sequence" types -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "25.1")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Elisp supports a typeclass none as "sequence" which covers the following | ||||
|  | @ -66,22 +70,22 @@ | |||
| ;; (defprotocol sequence | ||||
| ;;   :functions (reduce)) | ||||
| ;; (definstance sequence list | ||||
| ;;   :reduce #'list/reduce | ||||
| ;;   :filter #'list/filter | ||||
| ;;   :map    #'list/map) | ||||
| ;;   :reduce #'list-reduce | ||||
| ;;   :filter #'list-filter | ||||
| ;;   :map    #'list-map) | ||||
| ;; (definstance sequence vector | ||||
| ;;   :reduce #'vector/reduce) | ||||
| ;; (definstance sequence string | ||||
| ;;   :reduce #'string) | ||||
| 
 | ||||
| (defun sequence/classify (xs) | ||||
| (defun sequence-classify (xs) | ||||
|   "Return the type of `XS'." | ||||
|   (cond | ||||
|    ((listp xs) 'list) | ||||
|    ((vectorp xs) 'vector) | ||||
|    ((stringp xs) 'string))) | ||||
| 
 | ||||
| (defun sequence/reduce (acc f xs) | ||||
| (defun sequence-reduce (acc f xs) | ||||
|   "Reduce of `XS' calling `F' on x and `ACC'." | ||||
|   (seq-reduce | ||||
|    (lambda (acc x) | ||||
|  | @ -91,12 +95,12 @@ | |||
| 
 | ||||
| ;; Elixir also turned everything into a list for efficiecy reasons. | ||||
| 
 | ||||
| (defun sequence/filter (p xs) | ||||
| (defun sequence-filter (p xs) | ||||
|   "Filter `XS' with predicate, `P'. | ||||
| Returns a list regardless of the type of `XS'." | ||||
|   (seq-filter p xs)) | ||||
| 
 | ||||
| (defun sequence/map (f xs) | ||||
| (defun sequence-map (f xs) | ||||
|   "Maps `XS' calling `F' on each element. | ||||
| Returns a list regardless of the type of `XS'." | ||||
|   (seq-map f xs)) | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; series.el --- Hosting common series of numbers -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Encoding number series as I learn about them. | ||||
|  | @ -28,62 +32,62 @@ | |||
| ;; Library | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun series/range (beg end) | ||||
| (defun series-range (beg end) | ||||
|   "Create a list of numbers from `BEG' to `END'. | ||||
| This is an inclusive number range." | ||||
|   (if (< end beg) | ||||
|       (list/reverse | ||||
|       (list-reverse | ||||
|        (number-sequence end beg)) | ||||
|     (number-sequence beg end))) | ||||
| 
 | ||||
| (defun series/fibonacci-number (i) | ||||
| (defun series-fibonacci-number (i) | ||||
|   "Return the number in the fibonacci series at `I'." | ||||
|   (cond | ||||
|    ((= 0 i) 0) | ||||
|    ((= 1 i) 1) | ||||
|    (t (+ (series/fibonacci-number (- i 1)) | ||||
|          (series/fibonacci-number (- i 2)))))) | ||||
|    (t (+ (series-fibonacci-number (- i 1)) | ||||
|          (series-fibonacci-number (- i 2)))))) | ||||
| 
 | ||||
| (defun series/fibonacci (n) | ||||
| (defun series-fibonacci (n) | ||||
|   "Return the first `N' numbers of the fibonaccci series starting at zero." | ||||
|   (if (= 0 n) | ||||
|       '() | ||||
|     (list/reverse | ||||
|      (list/cons (series/fibonacci-number (number/dec n)) | ||||
|                 (list/reverse | ||||
|                  (series/fibonacci (number/dec n))))))) | ||||
|     (list-reverse | ||||
|      (list-cons (series-fibonacci-number (number-dec n)) | ||||
|                 (list-reverse | ||||
|                  (series-fibonacci (number-dec n))))))) | ||||
| 
 | ||||
| ;; TODO: Consider memoization. | ||||
| (defun series/triangular-number (i) | ||||
| (defun series-triangular-number (i) | ||||
|   "Return the number in the triangular series at `I'." | ||||
|   (if (= 0 i) | ||||
|       0 | ||||
|     (+ i (series/triangular-number (number/dec i))))) | ||||
|     (+ i (series-triangular-number (number-dec i))))) | ||||
| 
 | ||||
| ;; TODO: Improve performance. | ||||
| ;; TODO: Consider creating a stream protocol with `stream/next' and implement | ||||
| ;; this using that. | ||||
| (defun series/triangular (n) | ||||
| (defun series-triangular (n) | ||||
|   "Return the first `N' numbers of a triangular series starting at 0." | ||||
|   (if (= 0 n) | ||||
|       '() | ||||
|     (list/reverse | ||||
|      (list/cons (series/triangular-number (number/dec n)) | ||||
|                 (list/reverse | ||||
|                  (series/triangular (number/dec n))))))) | ||||
|     (list-reverse | ||||
|      (list-cons (series-triangular-number (number-dec n)) | ||||
|                 (list-reverse | ||||
|                  (series-triangular (number-dec n))))))) | ||||
| 
 | ||||
| (defun series/catalan-number (i) | ||||
| (defun series-catalan-number (i) | ||||
|   "Return the catalan number in the series at `I'." | ||||
|   (if (= 0 i) | ||||
|       1 | ||||
|     (/ (number/factorial (* 2 i)) | ||||
|        (* (number/factorial (number/inc i)) | ||||
|           (number/factorial i))))) | ||||
|     (/ (number-factorial (* 2 i)) | ||||
|        (* (number-factorial (number-inc i)) | ||||
|           (number-factorial i))))) | ||||
| 
 | ||||
| (defun series/catalan (n) | ||||
| (defun series-catalan (n) | ||||
|   "Return the first `N' numbers in a catalan series." | ||||
|   (->> (series/range 0 (number/dec n)) | ||||
|        (list/map #'series/catalan-number))) | ||||
|   (->> (series-range 0 (number-dec n)) | ||||
|        (list-map #'series-catalan-number))) | ||||
| 
 | ||||
| (provide 'series) | ||||
| ;;; series.el ends here | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| ;;; set.el --- Working with mathematical sets -*- lexical-binding: t -*- | ||||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24.3")) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; The set data structure is a collection that deduplicates its elements. | ||||
|  | @ -24,26 +28,26 @@ | |||
| 
 | ||||
| (cl-defstruct set xs) | ||||
| 
 | ||||
| (defconst set/enable-testing? t | ||||
| (defconst set-enable-testing? t | ||||
|   "Run tests when t.") | ||||
| 
 | ||||
| (defun set/from-list (xs) | ||||
| (defun set-from-list (xs) | ||||
|   "Create a new set from the list XS." | ||||
|   (make-set :xs (->> xs | ||||
|                      (list/map #'dotted/new) | ||||
|                      (list-map #'dotted-new) | ||||
|                      ht-from-alist))) | ||||
| 
 | ||||
| (defun set/new (&rest args) | ||||
| (defun set-new (&rest args) | ||||
|   "Create a new set from ARGS." | ||||
|   (set/from-list args)) | ||||
|   (set-from-list args)) | ||||
| 
 | ||||
| (defun set/to-list (xs) | ||||
| (defun set-to-list (xs) | ||||
|   "Map set XS into a list." | ||||
|   (->> xs | ||||
|        set-xs | ||||
|        ht-keys)) | ||||
| 
 | ||||
| (defun set/add (x xs) | ||||
| (defun set-add (x xs) | ||||
|   "Add X to set XS." | ||||
|   (struct-update set | ||||
|                  xs | ||||
|  | @ -54,22 +58,22 @@ | |||
|                  xs)) | ||||
| 
 | ||||
| ;; TODO: Ensure all `*/reduce' functions share the same API. | ||||
| (defun set/reduce (acc f xs) | ||||
| (defun set-reduce (acc f xs) | ||||
|   "Return a new set by calling F on each element of XS and ACC." | ||||
|   (->> xs | ||||
|        set/to-list | ||||
|        (list/reduce acc f))) | ||||
|        set-to-list | ||||
|        (list-reduce acc f))) | ||||
| 
 | ||||
| (defun set/intersection (a b) | ||||
| (defun set-intersection (a b) | ||||
|   "Return the set intersection between sets A and B." | ||||
|   (set/reduce (set/new) | ||||
|   (set-reduce (set-new) | ||||
|               (lambda (x acc) | ||||
|                 (if (set/contains? x b) | ||||
|                     (set/add x acc) | ||||
|                 (if (set-contains? x b) | ||||
|                     (set-add x acc) | ||||
|                   acc)) | ||||
|               a)) | ||||
| 
 | ||||
| (defun set/count (xs) | ||||
| (defun set-count (xs) | ||||
|   "Return the number of elements in XS." | ||||
|   (->> xs | ||||
|        set-xs | ||||
|  | @ -79,93 +83,93 @@ | |||
| ;; Predicates | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun set/empty? (xs) | ||||
| (defun set-empty? (xs) | ||||
|   "Return t if XS has no elements in it." | ||||
|   (= 0 (set/count xs))) | ||||
|   (= 0 (set-count xs))) | ||||
| 
 | ||||
| (defun set/contains? (x xs) | ||||
| (defun set-contains? (x xs) | ||||
|   "Return t if set XS has X." | ||||
|   (ht-contains? (set-xs xs) x)) | ||||
| 
 | ||||
| ;; TODO: Prefer using `ht.el' functions for this. | ||||
| (defun set/equal? (a b) | ||||
| (defun set-equal? (a b) | ||||
|   "Return t if A and B share the name members." | ||||
|   (ht-equal? (set-xs a) | ||||
|              (set-xs b))) | ||||
| 
 | ||||
| (defun set/distinct? (a b) | ||||
| (defun set-distinct? (a b) | ||||
|   "Return t if sets A and B have no shared members." | ||||
|   (set/empty? (set/intersection a b))) | ||||
|   (set-empty? (set-intersection a b))) | ||||
| 
 | ||||
| (defun set/superset? (a b) | ||||
| (defun set-superset? (a b) | ||||
|   "Return t if set A contains all of the members of set B." | ||||
|   (->> b | ||||
|        set/to-list | ||||
|        (list/all? (lambda (x) (set/contains? x a))))) | ||||
|        set-to-list | ||||
|        (list-all? (lambda (x) (set-contains? x a))))) | ||||
| 
 | ||||
| (defun set/subset? (a b) | ||||
| (defun set-subset? (a b) | ||||
|   "Return t if each member of set A is present in set B." | ||||
|   (set/superset? b a)) | ||||
|   (set-superset? b a)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (when set/enable-testing? | ||||
|   ;; set/distinct? | ||||
| (when set-enable-testing? | ||||
|   ;; set-distinct? | ||||
|   (prelude-assert | ||||
|    (set/distinct? (set/new 'one 'two 'three) | ||||
|                   (set/new 'a 'b 'c))) | ||||
|    (set-distinct? (set-new 'one 'two 'three) | ||||
|                   (set-new 'a 'b 'c))) | ||||
|   (prelude-refute | ||||
|    (set/distinct? (set/new 1 2 3) | ||||
|                   (set/new 3 4 5))) | ||||
|    (set-distinct? (set-new 1 2 3) | ||||
|                   (set-new 3 4 5))) | ||||
|   (prelude-refute | ||||
|    (set/distinct? (set/new 1 2 3) | ||||
|                   (set/new 1 2 3))) | ||||
|   ;; set/equal? | ||||
|    (set-distinct? (set-new 1 2 3) | ||||
|                   (set-new 1 2 3))) | ||||
|   ;; set-equal? | ||||
|   (prelude-refute | ||||
|    (set/equal? (set/new 'a 'b 'c) | ||||
|                (set/new 'x 'y 'z))) | ||||
|    (set-equal? (set-new 'a 'b 'c) | ||||
|                (set-new 'x 'y 'z))) | ||||
|   (prelude-refute | ||||
|    (set/equal? (set/new 'a 'b 'c) | ||||
|                (set/new 'a 'b))) | ||||
|    (set-equal? (set-new 'a 'b 'c) | ||||
|                (set-new 'a 'b))) | ||||
|   (prelude-assert | ||||
|    (set/equal? (set/new 'a 'b 'c) | ||||
|                (set/new 'a 'b 'c))) | ||||
|   ;; set/intersection | ||||
|    (set-equal? (set-new 'a 'b 'c) | ||||
|                (set-new 'a 'b 'c))) | ||||
|   ;; set-intersection | ||||
|   (prelude-assert | ||||
|    (set/equal? (set/new 2 3) | ||||
|                (set/intersection (set/new 1 2 3) | ||||
|                                  (set/new 2 3 4)))) | ||||
|   ;; set/{from,to}-list | ||||
|    (set-equal? (set-new 2 3) | ||||
|                (set-intersection (set-new 1 2 3) | ||||
|                                  (set-new 2 3 4)))) | ||||
|   ;; set-{from,to}-list | ||||
|   (prelude-assert (equal '(1 2 3) | ||||
|                          (->> '(1 1 2 2 3 3) | ||||
|                               set/from-list | ||||
|                               set/to-list))) | ||||
|   (let ((primary-colors (set/new "red" "green" "blue"))) | ||||
|     ;; set/subset? | ||||
|                               set-from-list | ||||
|                               set-to-list))) | ||||
|   (let ((primary-colors (set-new "red" "green" "blue"))) | ||||
|     ;; set-subset? | ||||
|     (prelude-refute | ||||
|      (set/subset? (set/new "black" "grey") | ||||
|      (set-subset? (set-new "black" "grey") | ||||
|                   primary-colors)) | ||||
|     (prelude-assert | ||||
|      (set/subset? (set/new "red") | ||||
|      (set-subset? (set-new "red") | ||||
|                   primary-colors)) | ||||
|     ;; set/superset? | ||||
|     ;; set-superset? | ||||
|     (prelude-refute | ||||
|      (set/superset? primary-colors | ||||
|                     (set/new "black" "grey"))) | ||||
|      (set-superset? primary-colors | ||||
|                     (set-new "black" "grey"))) | ||||
|     (prelude-assert | ||||
|      (set/superset? primary-colors | ||||
|                     (set/new "red" "green" "blue"))) | ||||
|      (set-superset? primary-colors | ||||
|                     (set-new "red" "green" "blue"))) | ||||
|     (prelude-assert | ||||
|      (set/superset? primary-colors | ||||
|                     (set/new "red" "blue")))) | ||||
|   ;; set/empty? | ||||
|   (prelude-assert (set/empty? (set/new))) | ||||
|   (prelude-refute (set/empty? (set/new 1 2 3))) | ||||
|   ;; set/count | ||||
|   (prelude-assert (= 0 (set/count (set/new)))) | ||||
|   (prelude-assert (= 2 (set/count (set/new 1 1 2 2))))) | ||||
|      (set-superset? primary-colors | ||||
|                     (set-new "red" "blue")))) | ||||
|   ;; set-empty? | ||||
|   (prelude-assert (set-empty? (set-new))) | ||||
|   (prelude-refute (set-empty? (set-new 1 2 3))) | ||||
|   ;; set-count | ||||
|   (prelude-assert (= 0 (set-count (set-new)))) | ||||
|   (prelude-assert (= 2 (set-count (set-new 1 1 2 2))))) | ||||
| 
 | ||||
| (provide 'set) | ||||
| ;;; set.el ends here | ||||
|  |  | |||
|  | @ -37,23 +37,23 @@ | |||
| ;; Maximizes the tramp debugging noisiness while I'm still learning about tramp. | ||||
| (setq tramp-verbose 10) | ||||
| 
 | ||||
| (defcustom ssh/hosts '("desktop" "socrates") | ||||
| (defcustom ssh-hosts '("desktop" "socrates") | ||||
|   "List of hosts to which I commonly connect. | ||||
| Note: It could be interesting to read these values from ~/.ssh/config, but | ||||
| Note: It could be interesting to read these values from ~/.ssh-config, but | ||||
|   that's more than I need at the moment.") | ||||
| 
 | ||||
| (defun ssh/sudo-buffer () | ||||
| (defun ssh-sudo-buffer () | ||||
|   "Open the current buffer with sudo rights." | ||||
|   (interactive) | ||||
|   (with-current-buffer (current-buffer) | ||||
|     (if (s-starts-with? "/ssh:" buffer-file-name) | ||||
|         (message "[ssh.el] calling ssh/sudo-buffer for remote files isn't currently supported") | ||||
|         (message "[ssh.el] calling ssh-sudo-buffer for remote files isn't currently supported") | ||||
|       (find-file (format "/sudo::%s" buffer-file-name))))) | ||||
| 
 | ||||
| (defun ssh/cd-home () | ||||
| (defun ssh-cd-home () | ||||
|   "Prompt for an SSH host and open a dired buffer for wpcarro on that machine." | ||||
|   (interactive) | ||||
|   (let ((machine (completing-read "Machine: " ssh/hosts))) | ||||
|   (let ((machine (completing-read "Machine: " ssh-hosts))) | ||||
|     (find-file (format "/ssh:wpcarro@%s:~" machine)))) | ||||
| 
 | ||||
| (provide 'ssh) | ||||
|  |  | |||
|  | @ -26,62 +26,62 @@ | |||
| ;; Create | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun stack/new () | ||||
| (defun stack-new () | ||||
|   "Create an empty stack." | ||||
|   (make-stack :xs '())) | ||||
| 
 | ||||
| (defun stack/from-list (xs) | ||||
| (defun stack-from-list (xs) | ||||
|   "Create a new stack from the list, `XS'." | ||||
|   (list/reduce (stack/new) #'stack/push xs)) | ||||
|   (list-reduce (stack-new) #'stack-push xs)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Read | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun stack/peek (xs) | ||||
| (defun stack-peek (xs) | ||||
|   "Look at the top element of `XS' without popping it off." | ||||
|   (->> xs | ||||
|        stack-xs | ||||
|        list/head)) | ||||
|        list-head)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Update | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun stack/push (x xs) | ||||
| (defun stack-push (x xs) | ||||
|   "Push `X' on `XS'." | ||||
|   (struct-update stack | ||||
|                  xs | ||||
|                  (>> (list/cons x)) | ||||
|                  (>> (list-cons x)) | ||||
|                  xs)) | ||||
| 
 | ||||
| ;; TODO: How to return something like {(list/head xs), (list/tail xs)} in Elixir | ||||
| ;; TODO: How to return something like {(list-head xs), (list-tail xs)} in Elixir | ||||
| ;; TODO: How to handle popping from empty stacks? | ||||
| (defun stack/pop (xs) | ||||
| (defun stack-pop (xs) | ||||
|   "Return the stack, `XS', without the top element. | ||||
| Since I cannot figure out a nice way of return tuples in Elisp, if you want to | ||||
| look at the first element, use `stack/peek' before running `stack/pop'." | ||||
| look at the first element, use `stack-peek' before running `stack-pop'." | ||||
|   (struct-update stack | ||||
|                  xs | ||||
|                  (>> list/tail) | ||||
|                  (>> list-tail) | ||||
|                  xs)) | ||||
| 
 | ||||
| (defun stack/map-top (f xs) | ||||
| (defun stack-map-top (f xs) | ||||
|   "Apply F to the top element of XS." | ||||
|   (->> xs | ||||
|        stack/pop | ||||
|        (stack/push (funcall f (stack/peek xs))))) | ||||
|        stack-pop | ||||
|        (stack-push (funcall f (stack-peek xs))))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Miscellaneous | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defun stack/to-list (xs) | ||||
| (defun stack-to-list (xs) | ||||
|   "Return XS as a list. | ||||
| The round-trip property of `stack/from-list' and `stack/to-list' should hold." | ||||
| The round-trip property of `stack-from-list' and `stack-to-list' should hold." | ||||
|   (->> xs | ||||
|        stack-xs | ||||
|        list/reverse)) | ||||
|        list-reverse)) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Predicates | ||||
|  | @ -89,7 +89,7 @@ The round-trip property of `stack/from-list' and `stack/to-list' should hold." | |||
| 
 | ||||
| ;; TODO: Create a macro that wraps `cl-defstruct' that automatically creates | ||||
| ;; things like `new', `instance?'. | ||||
| (defun stack/instance? (xs) | ||||
| (defun stack-instance? (xs) | ||||
|   "Return t if XS is a stack." | ||||
|   (stack-p xs)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -72,14 +72,14 @@ Depth-first traversals have the advantage of typically consuming less memory | |||
|                  (if (or (maybe-nil? node) | ||||
|                          (tree-leaf? node)) | ||||
|                      acc-new | ||||
|                    (list/reduce | ||||
|                    (list-reduce | ||||
|                     acc-new | ||||
|                     (lambda (node acc) | ||||
|                       (tree-do-reduce-depth | ||||
|                        acc | ||||
|                        f | ||||
|                        node | ||||
|                        (number/inc depth))) | ||||
|                        (number-inc depth))) | ||||
|                     (node-children node)))))) | ||||
|     (do-reduce-depth acc f node 0))) | ||||
| 
 | ||||
|  | @ -94,13 +94,13 @@ Depth-first traversals have the advantage of typically consuming less memory | |||
| ;; above. | ||||
| (defun tree-leaf-depths (xs) | ||||
|   "Return a list of all of the depths of the leaf nodes in XS." | ||||
|   (list/reverse | ||||
|   (list-reverse | ||||
|    (tree-reduce-depth | ||||
|     '() | ||||
|     (lambda (node acc depth) | ||||
|       (if (or (maybe-nil? node) | ||||
|               (tree-leaf? node)) | ||||
|           (list/cons depth acc) | ||||
|           (list-cons depth acc) | ||||
|         acc)) | ||||
|     xs))) | ||||
| 
 | ||||
|  | @ -122,8 +122,8 @@ generating test data.  Warning this function can overflow the stack." | |||
|                (d vf bf) | ||||
|                (make-node | ||||
|                 :value (funcall vf d) | ||||
|                 :children (->> (series/range 0 (number/dec bf)) | ||||
|                                (list/map | ||||
|                 :children (->> (series/range 0 (number-dec bf)) | ||||
|                                (list-map | ||||
|                                 (lambda (_) | ||||
|                                   (when (random-boolean?) | ||||
|                                     (do-random d vf bf)))))))) | ||||
|  | @ -147,9 +147,9 @@ A tree is balanced if none of the differences between any two depths of two leaf | |||
|   nodes in XS is greater than N." | ||||
|   (> n (->> xs | ||||
|             tree-leaf-depths | ||||
|             set/from-list | ||||
|             set/count | ||||
|             number/dec))) | ||||
|             set-from-list | ||||
|             set-count | ||||
|             number-dec))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;; Tests | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ | |||
| ;; Configuration | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (defconst vterm-mgt--instances (cycle/new) | ||||
| (defconst vterm-mgt--instances (cycle-new) | ||||
|   "A cycle tracking all of my vterm instances.") | ||||
| 
 | ||||
| (defcustom vterm-mgt-scroll-on-focus nil | ||||
|  | @ -50,8 +50,8 @@ | |||
| This function should be called from a buffer running vterm." | ||||
|   (interactive) | ||||
|   (vterm-mgt--assert-vterm-buffer) | ||||
|   (cycle/focus-item (current-buffer) vterm-mgt--instances) | ||||
|   (switch-to-buffer (cycle/next vterm-mgt--instances)) | ||||
|   (cycle-focus-item (current-buffer) vterm-mgt--instances) | ||||
|   (switch-to-buffer (cycle-next vterm-mgt--instances)) | ||||
|   (when vterm-mgt-scroll-on-focus (end-of-buffer))) | ||||
| 
 | ||||
| (defun vterm-mgt-prev () | ||||
|  | @ -59,8 +59,8 @@ This function should be called from a buffer running vterm." | |||
| This function should be called from a buffer running vterm." | ||||
|   (interactive) | ||||
|   (vterm-mgt--assert-vterm-buffer) | ||||
|   (cycle/focus-item (current-buffer) vterm-mgt--instances) | ||||
|   (switch-to-buffer (cycle/prev vterm-mgt--instances)) | ||||
|   (cycle-focus-item (current-buffer) vterm-mgt--instances) | ||||
|   (switch-to-buffer (cycle-prev vterm-mgt--instances)) | ||||
|   (when vterm-mgt-scroll-on-focus (end-of-buffer))) | ||||
| 
 | ||||
| (defun vterm-mgt-instantiate () | ||||
|  | @ -74,8 +74,8 @@ If however you must call `vterm', if you'd like to cycle through vterm | |||
|   collect any untracked vterm instances." | ||||
|   (interactive) | ||||
|   (let ((buffer (vterm))) | ||||
|     (cycle/append buffer vterm-mgt--instances) | ||||
|     (cycle/focus-item buffer vterm-mgt--instances))) | ||||
|     (cycle-append buffer vterm-mgt--instances) | ||||
|     (cycle-focus-item buffer vterm-mgt--instances))) | ||||
| 
 | ||||
| (defun vterm-mgt-kill () | ||||
|   "Kill the current buffer and remove it from `vterm-mgt--instances'. | ||||
|  | @ -83,23 +83,23 @@ This function should be called from a buffer running vterm." | |||
|   (interactive) | ||||
|   (vterm-mgt--assert-vterm-buffer) | ||||
|   (let ((buffer (current-buffer))) | ||||
|     (cycle/remove buffer vterm-mgt--instances) | ||||
|     (cycle-remove buffer vterm-mgt--instances) | ||||
|     (kill-buffer buffer))) | ||||
| 
 | ||||
| (defun vterm-mgt-find-or-create () | ||||
|   "Call `switch-to-buffer' on a focused vterm instance if there is one. | ||||
| 
 | ||||
| When `cycle/focused?' returns nil, focus the first item in the cycle.  When | ||||
| When `cycle-focused?' returns nil, focus the first item in the cycle.  When | ||||
| there are no items in the cycle, call `vterm-mgt-instantiate' to create a vterm | ||||
| instance." | ||||
|   (interactive) | ||||
|   (if (cycle/empty? vterm-mgt--instances) | ||||
|   (if (cycle-empty? vterm-mgt--instances) | ||||
|       (vterm-mgt-instantiate) | ||||
|     (if (cycle/focused? vterm-mgt--instances) | ||||
|         (switch-to-buffer (cycle/current vterm-mgt--instances)) | ||||
|     (if (cycle-focused? vterm-mgt--instances) | ||||
|         (switch-to-buffer (cycle-current vterm-mgt--instances)) | ||||
|       (progn | ||||
|         (cycle/jump 0 vterm-mgt--instances) | ||||
|         (switch-to-buffer (cycle/current vterm-mgt--instances)))))) | ||||
|         (cycle-jump 0 vterm-mgt--instances) | ||||
|         (switch-to-buffer (cycle-current vterm-mgt--instances)))))) | ||||
| 
 | ||||
| (defun vterm-mgt-rename-buffer (name) | ||||
|   "Rename the current buffer ensuring that its NAME is wrapped in *vterm*<...>. | ||||
|  | @ -118,7 +118,7 @@ If for whatever reason, the state of `vterm-mgt--instances' is corrupted and | |||
|   (setq vterm-mgt--instances | ||||
|         (->> (buffer-list) | ||||
|              (-filter #'vterm-mgt--instance?) | ||||
|              cycle/from-list))) | ||||
|              cycle-from-list))) | ||||
| 
 | ||||
| (provide 'vterm-mgt) | ||||
| ;;; vterm-mgt.el ends here | ||||
|  |  | |||
|  | @ -43,11 +43,11 @@ | |||
| 
 | ||||
| ;; TODO: Decide between window-manager, exwm, or some other namespace. | ||||
| 
 | ||||
| ;; TODO: Support (cycle/from-list '(current previous)) to toggle back and forth | ||||
| ;; TODO: Support (cycle-from-list '(current previous)) to toggle back and forth | ||||
| ;; between most recent workspace. | ||||
| 
 | ||||
| ;; TODO: Support ad hoc cycle for loading a few workspaces that can be cycled | ||||
| ;; between. (cycle/from-list '("Project" "Workspace")) | ||||
| ;; between. (cycle-from-list '("Project" "Workspace")) | ||||
| 
 | ||||
| ;; TODO: Consider supporting a workspace for Racket, Clojure, Common Lisp, | ||||
| ;; Haskell, Elixir, and a few other languages. These could behave very similarly | ||||
|  | @ -80,11 +80,11 @@ | |||
|   "List of `window-manager--named-workspace' structs.") | ||||
| 
 | ||||
| ;; Assert that no two workspaces share KBDs. | ||||
| (prelude-assert (= (list/length window-manager--named-workspaces) | ||||
| (prelude-assert (= (list-length window-manager--named-workspaces) | ||||
|                    (->> window-manager--named-workspaces | ||||
|                         (list/map #'window-manager--named-workspace-kbd) | ||||
|                         set/from-list | ||||
|                         set/count))) | ||||
|                         (list-map #'window-manager--named-workspace-kbd) | ||||
|                         set-from-list | ||||
|                         set-count))) | ||||
| 
 | ||||
| (defun window-manager--alert (x) | ||||
|   "Message X with a structured format." | ||||
|  | @ -101,12 +101,12 @@ | |||
|   (require 'exwm-randr) | ||||
|   (exwm-randr-enable) | ||||
|   (setq exwm-randr-workspace-monitor-plist | ||||
|         (list 0 display/4k-monitor | ||||
|               1 display/laptop-monitor)) | ||||
|         (list 0 display-4k-monitor | ||||
|               1 display-laptop-monitor)) | ||||
| 
 | ||||
|   (evil-set-initial-state 'exwm-mode 'emacs) | ||||
|   (setq exwm-workspace-number | ||||
|         (list/length window-manager--named-workspaces)) | ||||
|         (list-length window-manager--named-workspaces)) | ||||
|   (let ((kbds `( | ||||
|                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|                 ;; Window sizing | ||||
|  | @ -146,7 +146,7 @@ | |||
|                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
|                 (:key "M-:"               :fn eval-expression) | ||||
|                 (:key "M-SPC"             :fn ivy-helpers/run-external-command) | ||||
|                 (:key "M-SPC"             :fn ivy-helpers-run-external-command) | ||||
|                 (:key "M-x"               :fn counsel-M-x) | ||||
|                 (:key "<M-tab>"           :fn window-manager-next-workspace) | ||||
|                 (:key "<M-S-iso-lefttab>" :fn window-manager-prev-workspace) | ||||
|  | @ -157,7 +157,7 @@ | |||
|                 ;; Workspaces | ||||
|                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
|                 (:key ,(kbd/raw 'workspace "l") :fn window-manager-logout)))) | ||||
|                 (:key ,(kbd-raw 'workspace "l") :fn window-manager-logout)))) | ||||
|     (setq exwm-input-global-keys | ||||
|           (->> kbds | ||||
|                (-map (lambda (plist) | ||||
|  | @ -184,22 +184,22 @@ | |||
| ;; Here is the code required to allow EXWM to cycle workspaces. | ||||
| (defconst window-manager--workspaces | ||||
|   (->> window-manager--named-workspaces | ||||
|        cycle/from-list) | ||||
|        cycle-from-list) | ||||
|   "Cycle of the my EXWM workspaces.") | ||||
| 
 | ||||
| (prelude-assert | ||||
|  (= exwm-workspace-number | ||||
|     (list/length window-manager--named-workspaces))) | ||||
|     (list-length window-manager--named-workspaces))) | ||||
| 
 | ||||
| (defun window-manager-next-workspace () | ||||
|   "Cycle forwards to the next workspace." | ||||
|   (interactive) | ||||
|   (window-manager--change-workspace (cycle/next window-manager--workspaces))) | ||||
|   (window-manager--change-workspace (cycle-next window-manager--workspaces))) | ||||
| 
 | ||||
| (defun window-manager-prev-workspace () | ||||
|   "Cycle backwards to the previous workspace." | ||||
|   (interactive) | ||||
|   (window-manager--change-workspace (cycle/prev window-manager--workspaces))) | ||||
|   (window-manager--change-workspace (cycle-prev window-manager--workspaces))) | ||||
| 
 | ||||
| ;; TODO: Create friendlier API for working with EXWM. | ||||
| 
 | ||||
|  | @ -215,7 +215,7 @@ | |||
|   (window-manager--alert "Switched to char-mode")) | ||||
| 
 | ||||
| (defconst window-manager--modes | ||||
|   (cycle/from-list (list #'window-manager--char-mode | ||||
|   (cycle-from-list (list #'window-manager--char-mode | ||||
|                          #'window-manager--line-mode)) | ||||
|   "Functions to switch exwm modes.") | ||||
| 
 | ||||
|  | @ -224,7 +224,7 @@ | |||
|   (interactive) | ||||
|   (with-current-buffer (window-buffer) | ||||
|     (when (eq major-mode 'exwm-mode) | ||||
|       (funcall (cycle/next window-manager--modes))))) | ||||
|       (funcall (cycle-next window-manager--modes))))) | ||||
| 
 | ||||
| ;; Ensure exwm apps open in char-mode. | ||||
| (add-hook 'exwm-manage-finish-hook #'window-manager--char-mode) | ||||
|  | @ -285,7 +285,7 @@ Ivy is used to capture the user's input." | |||
|     (funcall | ||||
|      (lambda () | ||||
|        (shell-command | ||||
|         (alist/get (ivy-read "System: " (alist/keys name->cmd)) | ||||
|         (alist-get (ivy-read "System: " (alist-keys name->cmd)) | ||||
|                    name->cmd)))))) | ||||
| 
 | ||||
| (defun window-manager--label->index (label workspaces) | ||||
|  | @ -303,7 +303,7 @@ Currently using super- as the prefix for switching workspaces." | |||
|                     (window-manager--named-workspace-label workspace)))) | ||||
|         (key (window-manager--named-workspace-kbd workspace))) | ||||
|     (exwm-input-set-key | ||||
|      (kbd/for 'workspace key) | ||||
|      (kbd-for 'workspace key) | ||||
|      handler))) | ||||
| 
 | ||||
| (defun window-manager--change-workspace (workspace) | ||||
|  | @ -318,11 +318,11 @@ Currently using super- as the prefix for switching workspaces." | |||
| 
 | ||||
| (defun window-manager--switch (label) | ||||
|   "Switch to a named workspaces using LABEL." | ||||
|   (cycle/focus (lambda (x) | ||||
|   (cycle-focus (lambda (x) | ||||
|                  (equal label | ||||
|                         (window-manager--named-workspace-label x))) | ||||
|                window-manager--workspaces) | ||||
|   (window-manager--change-workspace (cycle/current window-manager--workspaces))) | ||||
|   (window-manager--change-workspace (cycle-current window-manager--workspaces))) | ||||
| 
 | ||||
| (exwm-input-set-key (kbd "C-S-f") #'window-manager-toggle-previous) | ||||
| 
 | ||||
|  | @ -330,7 +330,7 @@ Currently using super- as the prefix for switching workspaces." | |||
|   "Focus the previously active EXWM workspace." | ||||
|   (interactive) | ||||
|   (window-manager--change-workspace | ||||
|    (cycle/focus-previous! window-manager--workspaces))) | ||||
|    (cycle-focus-previous! window-manager--workspaces))) | ||||
| 
 | ||||
| (defun window-manager--exwm-buffer? (x) | ||||
|   "Return t if buffer X is an EXWM buffer." | ||||
|  | @ -361,7 +361,7 @@ predicate." | |||
| (when window-manager--install-kbds? | ||||
|   (progn | ||||
|     (->> window-manager--named-workspaces | ||||
|          (list/map #'window-manager--register-kbd)) | ||||
|          (list-map #'window-manager--register-kbd)) | ||||
|     (window-manager--alert "Registered workspace KBDs!"))) | ||||
| 
 | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
|  |  | |||
|  | @ -179,7 +179,7 @@ | |||
|       create-lockfiles nil) | ||||
| 
 | ||||
| ;; ensure code wraps at 80 characters by default | ||||
| (setq-default fill-column constants/fill-column) | ||||
| (setq-default fill-column constants-fill-column) | ||||
| 
 | ||||
| (put 'narrow-to-region 'disabled nil) | ||||
| 
 | ||||
|  | @ -190,7 +190,7 @@ | |||
| (add-hook 'after-save-hook | ||||
|           (lambda () | ||||
|             (when (f-equal? (buffer-file-name) | ||||
|                             (f-join constants/briefcase "secrets.json")) | ||||
|                             (f-join constants-briefcase "secrets.json")) | ||||
|               (shell-command "git secret hide")))) | ||||
| 
 | ||||
| ;; use tabs instead of spaces | ||||
|  | @ -214,7 +214,7 @@ | |||
| ;; TODO: Consider moving this into a briefcase.el module. | ||||
| (defun wpc-misc--briefcase-find (dir) | ||||
|   "Find the default.nix nearest to DIR." | ||||
|   (when (s-starts-with? constants/briefcase (f-expand dir)) | ||||
|   (when (s-starts-with? constants-briefcase (f-expand dir)) | ||||
|     (if (f-exists? (f-join dir "default.nix")) | ||||
|         (cons 'transient dir) | ||||
|       (wpc-misc--briefcase-find (f-parent dir))))) | ||||
|  |  | |||
|  | @ -28,12 +28,12 @@ | |||
| (defun wpc-nix-rebuild-emacs () | ||||
|   "Use nix-env to rebuild wpcarros-emacs." | ||||
|   (interactive) | ||||
|   (let* ((emacs (if (device/corporate?) "emacs.glinux" "emacs.nixos")) | ||||
|   (let* ((emacs (if (device-corporate?) "emacs.glinux" "emacs.nixos")) | ||||
|          (pname (format "nix-build <briefcase/%s>" emacs)) | ||||
|          (bname (format "*%s*" pname))) | ||||
|     (start-process pname bname | ||||
|                    "nix-env" | ||||
|                    "-I" (format "briefcase=%s" constants/briefcase) | ||||
|                    "-I" (format "briefcase=%s" constants-briefcase) | ||||
|                    "-f" "<briefcase>" "-iA" emacs) | ||||
|     (display-buffer bname))) | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ | |||
| 
 | ||||
| ;; Author: William Carroll <wpcarro@gmail.com> | ||||
| ;; Version: 0.0.1 | ||||
| ;; URL: https://git.wpcarro.dev/wpcarro/briefcase | ||||
| ;; Package-Requires: ((emacs "24")) | ||||
| ;; Homepage: https://user.git.corp.google.com/wpcarro/briefcase | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;; Hosts font settings, scrolling, color schemes. | ||||
|  | @ -70,7 +70,7 @@ | |||
| (tool-bar-mode -1) | ||||
| 
 | ||||
| ;; set default buffer for Emacs | ||||
| (setq initial-buffer-choice constants/current-project) | ||||
| (setq initial-buffer-choice constants-current-project) | ||||
| 
 | ||||
| ;; premium Emacs themes | ||||
| (use-package doom-themes | ||||
|  | @ -91,7 +91,7 @@ | |||
|   :config | ||||
|   (counsel-mode t) | ||||
|   (ivy-mode t) | ||||
|   (alist/set! #'counsel-M-x "" ivy-initial-inputs-alist) | ||||
|   (alist-set! #'counsel-M-x "" ivy-initial-inputs-alist) | ||||
|   ;; prefer using `helpful' variants | ||||
|   (progn | ||||
|     (setq counsel-describe-function-function #'helpful-callable) | ||||
|  | @ -113,7 +113,7 @@ | |||
| ;; all-the-icons | ||||
| (use-package all-the-icons | ||||
|   :config | ||||
|   (when (not constants/ci?) | ||||
|   (when (not constants-ci?) | ||||
|     (unless (f-exists? "~/.local/share/fonts/all-the-icons.ttf") | ||||
|       (all-the-icons-install-fonts t)))) | ||||
| 
 | ||||
|  | @ -129,13 +129,13 @@ | |||
| ;; reduce noisiness of auto-revert-mode | ||||
| (setq auto-revert-verbose nil) | ||||
| 
 | ||||
| ;; highlight lines that are over `constants/fill-column' characters long | ||||
| ;; highlight lines that are over `constants-fill-column' characters long | ||||
| (use-package whitespace | ||||
|   :config | ||||
|   ;; TODO: This should change depending on the language and project. For | ||||
|   ;; example, Google Java projects prefer 100 character width instead of 80 | ||||
|   ;; character width. | ||||
|   (setq whitespace-line-column constants/fill-column) | ||||
|   (setq whitespace-line-column constants-fill-column) | ||||
|   (setq whitespace-style '(face lines-tail)) | ||||
|   (add-hook 'prog-mode-hook #'whitespace-mode)) | ||||
| 
 | ||||
|  | @ -156,15 +156,15 @@ | |||
|   :config | ||||
|   (setq alert-default-style 'notifier)) | ||||
| 
 | ||||
| ;; TODO: Should `device/work-laptop?' be a function or a constant that gets set | ||||
| ;; TODO: Should `device-work-laptop?' be a function or a constant that gets set | ||||
| ;; during initialization? | ||||
| (when (device/work-laptop?) | ||||
|   (laptop-battery/display)) | ||||
| (when (device-work-laptop?) | ||||
|   (laptop-battery-display)) | ||||
| 
 | ||||
| (fonts/whitelist-set "JetBrainsMono") | ||||
| (colorscheme/whitelist-set 'doom-solarized-light) | ||||
| (fonts-whitelist-set "JetBrainsMono") | ||||
| (colorscheme-whitelist-set 'doom-solarized-light) | ||||
| 
 | ||||
| (modeline/setup) | ||||
| (modeline-setup) | ||||
| 
 | ||||
| (provide 'wpc-ui) | ||||
| ;;; wpc-ui.el ends here | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue