summary refs log tree commit diff stats
path: root/lisp/acdw.el
blob: 67297594a3c83afdb5fbb7fd856c33aa88913656 (plain)
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
;;; acdw.el --- My Emacs extras  -*- lexical-binding: t; -*-

;;; Code:

(defmacro defdir (name directory &optional docstring makedir)
  "Define a variable and a function NAME expanding to DIRECTORY.
DOCSTRING is applied to the variable; its default is DIRECTORY's
path.  If MAKEDIR is non-nil, the directory and its parents will
be created."
  (declare (indent 2) (doc-string 3))
  `(progn
     (defvar ,name (expand-file-name ,directory)
       ,(concat (or docstring (format "%s" directory)) "\n"
                "Defined by `defdir'."))
     (defun ,name (file &optional mkdir)
       ,(concat "Expand FILE relative to variable `" (symbol-name name) "'.\n"
                "If MKDIR is non-nil, parent directories are created.\n"
                "Defined by `defdir'.")
       (let ((file-name (expand-file-name
                         (convert-standard-filename file) ,name)))
         (when mkdir
           (make-directory (file-name-directory file-name) :parents))
         file-name))
     ,(if makedir
	  `(make-directory ,directory :parents)
	`(unless (file-exists-p ,directory)
	   (warn "Directory `%s' doesn't exist." ,directory)))))

(defun choose-executable (&rest programs)
  "Return the first of PROGRAMS that exists in the system's $PATH.
Each of PROGRAMS can be a single string, or a list.  If it's a list then its car
will be tested with `executable-find', and the entire list returned.  This
enables passing arguments to a calling function."
  (seq-find (lambda (x)
              (executable-find (car (ensure-list x))))
            programs))

(defun file-string (file)
  "Return the contents of FILE as a string."
  (with-current-buffer (find-file-noselect file)
    (buffer-string)))

(defun unsmartify-region (begin end)
  "Replace \"smart\" punctuation with \"dumb\" counterparts."
  (interactive "*r")
  (save-excursion
    (goto-char begin)
    (while (re-search-forward  "[“”‘’–—]" end t)
      (let ((replace (pcase (match-string 0)
                       ((or "“" "”") "\"")
                       ((or "‘" "’") "'")
                       ("–" "--")
                       ("—" "---"))))
        (replace-match replace nil nil)))))

(defun ++concat (func strings)
  "Concat STRINGS processed by FUNC.
Each of STRINGS can be a bare string or a list.  Strings are
passed through as-is, but lists are passed to FUNC first as
arguments.  Finally, all the resulting strings are `mapconcat'-ed
together.

As a special case, if `:separator' is the first of STRINGS, the
string following will be used as a separator.  Otherwise, a
newline will be used."
  (let (separator)
    (when (eq (car strings) :separator)
      (setq separator (cadr strings)
            strings (cddr strings)))
    (mapconcat (lambda (s)
                 (cond
                  ((listp s) (apply func s))
                  ((stringp s) s)
                  (t (user-error "Bad argument: %S" s))))
               strings
               (or separator "\n"))))

(defun format-concat (&rest strings)
  "Concatenate formatted STRINGS.
Each of STRINGS can be a bare string or a list.  Bare strings are passed as-is
to `mapconcat' for concatenation and separation.  Lists, however, are passed to
`format' first.

If `:separator' is the first of STRINGS, the next string will be
used as a separator."
  (++concat #'format strings))

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