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:
William Carroll 2020-09-01 10:17:43 +01:00
parent a638e15c0d
commit fb5ec068dd
47 changed files with 1049 additions and 989 deletions

View file

@ -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)}'

View file

@ -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)}'

View file

@ -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))))))

View file

@ -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

View file

@ -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

View file

@ -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*"

View file

@ -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

View file

@ -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

View file

@ -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)))

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)))))

View file

@ -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

View file

@ -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)

View file

@ -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")))

View file

@ -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

View file

@ -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)

View file

@ -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...")

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -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))

View file

@ -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."

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -1,4 +1,5 @@
;;; screen-brightness.el --- Control laptop screen brightness -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>
;;; Commentary:

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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!")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -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)))))

View file

@ -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)))

View file

@ -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