This repository has been archived by the owner on Mar 26, 2020. It is now read-only.
/
mk-utils.el
177 lines (147 loc) · 5.39 KB
/
mk-utils.el
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
;;; mk-utils.el --- -*- lexical-binding: t; -*-
;;; Commentary:
;; I've collected various auxiliary functions here to avoid cluttering other
;; files.
;;; Code:
(eval-when-compile
(require 'dired))
(require 'cl-lib)
(require 'f)
(require 'subr-x)
(defvar mk-dir (f-expand "mk" user-emacs-directory)
"This is directory where all the configuration files are kept.")
(defmacro mk-translate-kbd (from to)
"Translate combinations of keys FROM to TO combination."
`(define-key key-translation-map (kbd ,from) (kbd ,to)))
(defun mk-set-key (key fnc)
"Set global key binding that binds KEY to FNC."
(global-set-key (kbd key) fnc))
(defmacro mk-iwrap (fnc &rest args)
"Interactively invoke function FNC with arguments ARGS."
`(lambda (&rest rest)
(interactive)
(apply ,fnc ,@args rest)))
(defun mk-visit-file (filename)
"Visit specified file FILENAME.
If the file does not exist, print a message about the fact, but
don't create new empty buffer."
(let ((filename (f-full filename)))
(if (f-exists? filename)
(find-file filename)
(message "%s does not exist" filename))))
(defun mk-switch-to-messages ()
"Switch to the *Messages* buffer."
(interactive)
(switch-to-buffer "*Messages*"))
(defun mk-switch-to-scratch ()
"Switch to the *scratch* buffer."
(interactive)
(switch-to-buffer "*scratch*"))
(defun mk-double-buffer ()
"Show current buffer in other window and switch to that window."
(interactive)
(if (> (length (window-list)) 1)
(let ((original-buffer (buffer-name)))
(other-window 1)
(switch-to-buffer original-buffer))
(split-window-sensibly)
(other-window 1)))
(defun mk-shell-quote-arg (arg)
"Quote ARG for using in the shell.
This function is different from ‘shell-quote-argument’ in that it
can be used for text transformations in Yasnippet without
backslash flood."
(replace-regexp-in-string "\\W" "\\\\\\&" (remove ?\\ arg)))
(defun mk-grab-input (prompt &optional initial-input add-space)
"Grab input from user.
If there is an active region, use its contents, otherwise read
text from the minibuffer. PROMPT is a prompt to show,
INITIAL-INPUT is the initial input. If INITIAL-INPUT and
ADD-SPACE are not NIL, add one space after the initial input."
(if mark-active
(buffer-substring (region-beginning)
(region-end))
(read-string prompt
(concat initial-input
(when (and initial-input add-space) " ")))))
(defun mk-get-existing-projects (dir)
"Return a list of existing projects under DIR.
All projects are combinations of two directory segments (org or
user, then project name, slash separated) immediately under the
specified directory."
(cl-sort
(mapcar (lambda (path)
(f-relative path "~/projects"))
(f-glob "*/*" dir))
#'string-lessp))
(defun mk-project-jump (project-name)
"Jump to PROJECT-NAME opening it in Dired."
(interactive
(list
(completing-read "Projects: "
(mk-get-existing-projects "~/projects"))))
(find-file
(f-expand project-name "~/projects")))
(defun mk-grep (regexp)
"Grep for REGEXP in current directory recursively."
(interactive
(list
(read-string "Grep: ")))
(ripgrep-regexp regexp default-directory))
(defun mk-find-file (file)
"Find a FILE under current ‘default-directory’."
(interactive
(list
(completing-read
"Files: "
(cl-sort
(with-temp-buffer
(call-process
"fd" nil (current-buffer) nil
"--type" "file" "--ignore-case" ".")
(split-string (buffer-string) "\n" t " "))
#'string-lessp))))
(find-file (f-expand file default-directory)))
(defun mk-show-date (&optional stamp)
"Show current date in the minibuffer.
If STAMP is not NIL, insert date at point."
(interactive)
(funcall (if stamp #'insert #'message)
(format-time-string "%A, %e %B, %Y")))
(defun mk-file-name-to-kill-ring (arg)
"Put name of file into kill ring.
If user's visiting a buffer that's associated with a file, use
name of the file. If major mode is ‘dired-mode’, use name of
file at point, but if point is not placed at any file, put name
of actual directory into kill ring. Argument ARG, if given,
makes result string be quoted as for yanking into shell."
(interactive "P")
(let ((φ (if (cl-find major-mode
'(dired-mode wdired-mode))
(or (dired-get-filename nil t)
default-directory)
(buffer-file-name))))
(when φ
(message "%s → kill ring"
(kill-new
(expand-file-name
(if arg
(shell-quote-argument φ)
φ)))))))
(defun mk-compile-init-files ()
"Byte compile init files (all *.el files under ‘mk-dir’ directory)."
(interactive)
(let (once)
(save-window-excursion
;; TODO this often includes flycheck temporary files. Avoid that.
(dolist (item (cons (f-full user-init-file)
(directory-files mk-dir t "\\`[^#].*\\.el\\'" t)))
(let ((compiled (byte-compile-dest-file item)))
(when (or (not (f-file? compiled))
(file-newer-than-file-p item compiled))
(byte-compile-file item)
(setq once t)))))
(unless once
(message "Byte compiled init files exist and are up to date"))))
(provide 'mk-utils)
;;; mk-utils.el ends here