about summary refs log tree commit diff
path: root/emacs/.emacs.d/wpc/prelude.el
blob: 6ef9e3ba7afbe7394c828810bb46682128efefe4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
;;; prelude.el --- My attempt at augmenting Elisp stdlib -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>

;;; Commentary:
;; Some of these ideas are scattered across other modules like `fs',
;; `string-functions', etc.  I'd like to keep everything modular.  I still don't
;; have an answer for which items belond in `misc'; I don't want that to become
;; a dumping grounds.  Ideally this file will `require' all other modules and
;; define just a handful of functions.

;; TODO: Consider removing all dependencies from prelude.el.

;;; Code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Third-party libraries
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 's)
(require 'dash)
(require 'f)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Libraries
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; TODO: Maybe don't globally import everything here.  Disable these and attepmt
;; to reload Emacs to assess damage.
(require 'string)
(require 'list)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utilities
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun prelude/to-string (x)
  "Convert X to a string."
  (format "%s" x))

(defun prelude/inspect (&rest args)
  "Message `ARGS' where ARGS are any type."
  (->> args
       (list/map #'prelude/to-string)
       (apply #'string/concat)
       message))

(defmacro prelude/call-process-to-string (cmd &rest args)
  "Return the string output of CMD called with ARGS."
  `(with-temp-buffer
     (call-process ,cmd nil (current-buffer) nil ,@args)
     (buffer-string)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Assertions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; TODO: Should I `throw' instead of `error' here?
(defmacro prelude/assert (x)
  "Errors unless X is t.
These are strict assertions and purposely do not rely on truthiness."
  (let ((as-string (prelude/to-string x)))
    `(unless (equal t ,x)
       (error (string/concat "Assertion failed: " ,as-string)))))

(defmacro prelude/refute (x)
  "Errors unless X is nil."
  (let ((as-string (prelude/to-string x)))
    `(unless (equal nil ,x)
       (error (string/concat "Refutation failed: " ,as-string)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Adapter functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun prelude/identity (x)
  "Return X unchanged."
  x)

(defun prelude/const (x)
  "Return a variadic lambda that will return X."
  (lambda (&rest _) x))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Miscellaneous
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; TODO: Consider packaging these into a linum-color.el package.
;; TODO: Generate the color used here from the theme.
(defvar linum/safe? nil
  "Flag indicating whether or not it is safe to work with `linum-mode'.")

(defvar linum/mru-color nil
  "Stores the color most recently attempted to be applied.")

(add-hook 'linum-mode-hook
          (lambda ()
            (setq linum/safe? t)
            (when (maybe/some? linum/mru-color)
              (set-face-foreground 'linum linum/mru-color))))

(defun prelude/set-line-number-color (color)
  "Safely set linum color to `COLOR'.

If this is called before Emacs initializes, the color will be stored in
`linum/mru-color' and applied once initialization completes.

Why is this safe?
If `(set-face-foreground 'linum)' is called before initialization completes,
Emacs will silently fail.  Without this function, it is easy to introduce
difficult to troubleshoot bugs in your init files."
  (if linum/safe?
      (set-face-foreground 'linum color)
    (setq linum/mru-color color)))

(defun prelude/prompt (prompt)
  "Read input from user with PROMPT."
  (read-string prompt))

(cl-defun prelude/start-process (&key name command)
  "Pass command string, COMMAND, and the function name, NAME.
This is a wrapper around `start-process' that has an API that resembles
`shell-command'."
  ;; TODO: Fix the bug with tokenizing here, since it will split any whitespace
  ;; character, even though it shouldn't in the case of quoted string in shell.
  ;; e.g. - "xmodmap -e 'one two three'" => '("xmodmap" "-e" "'one two three'")
  (prelude/refute (string/contains? "'" command))
  (let* ((tokens (string/split " " command))
         (program-name (list/head tokens))
         (program-args (list/tail tokens)))
    (apply #'start-process
           `(,(string/format "*%s<%s>*" program-name name)
             ,nil
             ,program-name
             ,@program-args))))

(defun prelude/executable-exists? (name)
  "Return t if CLI tool NAME exists according to `exec-path'."
  (let ((file (locate-file name exec-path)))
    (require 'maybe)
    (if (maybe/some? file)
        (f-exists? file)
      nil)))

(defmacro prelude/time (x)
  "Print the time it takes to evaluate X."
  `(benchmark 1 ',x))

(provide 'prelude)
;;; prelude.el ends here