functions.el 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. ;;; Functions
  2. ;;; Notes on mark and region: to get a consistent behaviour regardless of
  3. ;;; Transient mode, check `(use-region-p)'. It will work as expected if
  4. ;;; transient. If not, it will always be true as soon as the mark has been set
  5. ;;; once; so you need to make sure the mark is set as you want beforehand (e.g.
  6. ;;; whole buffer, single line...). This is the behaviour of `sort-lines'.
  7. ;;;
  8. ;;; The clean way to get static region boundaries and fallback on buffer boundaries:
  9. ;;
  10. ;; (let (start end)
  11. ;; (if (use-region-p)
  12. ;; (setq start (region-beginning) end (region-end))
  13. ;; (setq start (point-min) end (point-max)))
  14. ;;
  15. ;;; If several commands act on region and the region size/pos is susceptible to change:
  16. ;;
  17. ;; (let ((start (set-marker (make-marker) (if (use-region-p) (region-beginning) (point-min))))
  18. ;; (end (set-marker (make-marker) (if (use-region-p) (region-end) (point-end)))))
  19. ;;
  20. ;;; For commands that only work on regions:
  21. ;;
  22. ;; (defun count-lines-region (start end)
  23. ;; "Print number of lines and characters in the region."
  24. ;; (interactive "r")
  25. ;; ...
  26. (defun ambrevar/call-process-to-string (program &rest args)
  27. "Call PROGRAM with ARGS and return output.
  28. See also `process-lines'."
  29. ;; Or equivalently:
  30. ;; (with-temp-buffer
  31. ;; (apply 'process-file program nil t nil args)
  32. ;; (buffer-string))
  33. (with-output-to-string
  34. (with-current-buffer standard-output
  35. (apply 'process-file program nil t nil args))))
  36. (defun ambrevar/define-keys (map key def &rest bindings)
  37. "Like `define-key' but allow for defining several bindings at once.
  38. `KEY' must be acceptable for `kbd'."
  39. (while key
  40. (define-key map (kbd key) def)
  41. (setq key (pop bindings)
  42. def (pop bindings))))
  43. ;; TODO: Bind this to ediff control panel.
  44. (defun ambrevar/ediff-copy-both-to-C ()
  45. (interactive)
  46. (ediff-copy-diff ediff-current-difference nil 'C nil
  47. (concat
  48. (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer)
  49. (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer))))
  50. (defun ambrevar/escape-region (&optional regex to-string)
  51. "Escape double-quotes and backslashes.
  52. This is useful for writing Elisp strings containing those
  53. characters. The optional parameters let you control the replacement of REGEX for
  54. TO-STRING."
  55. (interactive)
  56. (unless regex (setq regex "\\([\"\\\\]\\)"))
  57. (unless to-string (setq to-string "\\\\\\1"))
  58. (while (re-search-forward regex (if (use-region-p) (region-end) (point-max)) t)
  59. (replace-match to-string)))
  60. (defun ambrevar/prettify ()
  61. "(Un)tabify, indent and delete trailing whitespace.
  62. Tabify if `indent-tabs-mode' is true, otherwise use spaces.
  63. Work on buffer or region.
  64. Require `ambrevar/tabify-leading'."
  65. (interactive)
  66. (let ((start (set-marker (make-marker) (if (use-region-p) (region-beginning) (point-min))))
  67. (end (set-marker (make-marker) (if (use-region-p) (region-end) (point-max)))))
  68. (if indent-tabs-mode
  69. (ambrevar/tabify-leading)
  70. (untabify start end))
  71. (indent-region start end)
  72. (save-restriction
  73. (narrow-to-region start end)
  74. (delete-trailing-whitespace))))
  75. (defun ambrevar/flyspell-and-whitespace-mode ()
  76. "Toggle `flyspell-mode' and `whitespace-mode'."
  77. (interactive)
  78. (if (derived-mode-p 'prog-mode)
  79. (flyspell-prog-mode)
  80. (flyspell-mode)
  81. (when flyspell-mode
  82. (flyspell-buffer)))
  83. (whitespace-mode 'toggle))
  84. (global-set-key (kbd "<f9>") #'ambrevar/flyspell-and-whitespace-mode)
  85. ;;; From https://www.reddit.com/r/emacs/comments/70bn7v/what_do_you_have_emacs_show_when_it_starts_up/.
  86. ;;; Supply a random fortune cookie as the *scratch* message.
  87. (defun ambrevar/fortune-scratch-message ()
  88. (interactive)
  89. (let ((fortune
  90. (when (executable-find "fortune")
  91. (with-temp-buffer
  92. (shell-command "fortune" t)
  93. (while (not (eobp))
  94. (insert ";; ")
  95. (forward-line))
  96. (delete-trailing-whitespace (point-min) (point-max))
  97. (concat (buffer-string) "\n")))))
  98. (if (called-interactively-p 'any)
  99. (insert fortune)
  100. fortune)))
  101. (defun ambrevar/global-set-keys (key def &rest bindings)
  102. "Like `global-set-key' but allow for defining several bindings at once.
  103. `KEY' must be acceptable for `kbd'."
  104. (while key
  105. (global-set-key (kbd key) def)
  106. (setq key (pop bindings)
  107. def (pop bindings))))
  108. (defun ambrevar/kernel-format ()
  109. "Search all CONFIG_* string in the current buffer and replace
  110. everything in a way that's suitable for kconfig."
  111. (interactive)
  112. (let ((list nil))
  113. (while (re-search-forward "\\<CONFIG_[_[:alnum:]]+" nil :noerror)
  114. (push (match-string 0) list))
  115. (erase-buffer)
  116. (dolist (entry list)
  117. (insert entry "=m")
  118. (newline))))
  119. (defun ambrevar/image-display-external ()
  120. "Display original image at point using external viewer."
  121. (interactive)
  122. (let ((file (or (get-text-property (point) 'original-file-name)
  123. (let ((image (get-text-property (point) 'display)))
  124. (when image
  125. (plist-get (cdr image) :file))))))
  126. (if (not file)
  127. (message "No original file name found")
  128. (start-process "image-dired-thumb-external" nil
  129. image-dired-external-viewer (expand-file-name file)))))
  130. (define-key image-map (kbd "S-<return>") 'ambrevar/image-display-external)
  131. (defun ambrevar/current-minor-modes ()
  132. "Return the list of minor modes enabled in the current buffer."
  133. (interactive)
  134. (delq nil
  135. (mapcar (lambda (mode)
  136. (if (and (boundp mode) (symbol-value mode))
  137. mode))
  138. minor-mode-list)))
  139. (defun ambrevar/local-set-keys (key def &rest bindings)
  140. "Like `local-set-key' but allow for defining several bindings at once.
  141. `KEY' must be acceptable for `kbd'."
  142. (while key
  143. (local-set-key (kbd key) def)
  144. (setq key (pop bindings)
  145. def (pop bindings))))
  146. (defun ambrevar/reset-fill-column ()
  147. "Reset `fill-column' to its default value."
  148. (interactive)
  149. (setq fill-column (default-value 'fill-column)))
  150. (defun ambrevar/ring-delete-first-item-duplicates (ring)
  151. "Remove duplicates of last command in history.
  152. Return RING.
  153. This should be faster then `seq-uniq'. Unlike
  154. `eshell-hist-ignoredups' or `comint-input-ignoredups', it does
  155. not allow duplicates ever.
  156. Surrounding spaces are ignored when comparing."
  157. (let ((first (ring-ref ring 0))
  158. (index 1))
  159. (while (<= index (1- (ring-length ring)))
  160. (if (string= (string-trim first)
  161. (string-trim (ring-ref ring index)))
  162. ;; REVIEW: We could stop at the first match, it would be faster and it
  163. ;; would eliminate duplicates if we started from a fresh history.
  164. ;; From an existing history that would not clean up existing
  165. ;; duplicates beyond the first one.
  166. (ring-remove ring index)
  167. (setq index (1+ index))))
  168. ring))
  169. (defun ambrevar/sort-lines-unique (arg)
  170. "Remove trailing white space, then duplicate lines, then sort the result.
  171. Do not fold case with \\[universal-argument] or non-nil ARG."
  172. (interactive "P")
  173. (let ((start (set-marker (make-marker) (if (use-region-p) (region-beginning) (point-min))))
  174. (end (set-marker (make-marker) (if (use-region-p) (region-end) (point-end)))))
  175. (let ((sort-fold-case (if arg nil t)))
  176. (delete-trailing-whitespace start end)
  177. (delete-duplicate-lines start end)
  178. (sort-lines nil start end))))
  179. (defun ambrevar/tabify-leading ()
  180. "Call `tabify' on leading spaces only.
  181. Works on whole buffer if region is unactive."
  182. (interactive)
  183. (require 'tabify) ; Need this to initialize `tabify-regexp'.
  184. (let ((tabify-regexp-old tabify-regexp) start end)
  185. (if (use-region-p)
  186. (setq start (region-beginning) end (region-end))
  187. (setq start (point-min) end (point-max)))
  188. (unwind-protect
  189. (progn
  190. (setq tabify-regexp "^\t* [ \t]+")
  191. (tabify start end))
  192. (setq tabify-regexp tabify-regexp-old))))
  193. ;; Inspired by https://github.com/abo-abo/oremacs.git.
  194. (defun ambrevar/test-emacs ()
  195. "Return nil on error, t on success so that it can be added to
  196. `kill-emacs-query-functions'."
  197. (interactive)
  198. (if (not user-init-file)
  199. (progn
  200. (message "No init file")
  201. t)
  202. (let ((output
  203. (shell-command-to-string
  204. (format "emacs --batch --eval \"
  205. (condition-case e
  206. (progn
  207. (load \\\"%s\\\")
  208. (message \\\"-OK-\\\"))
  209. (error
  210. (message \\\"ERROR:\\\")
  211. (signal (car e) (cdr e))))\""
  212. user-init-file))))
  213. (if (string-match "-OK-" output)
  214. (progn
  215. (when (called-interactively-p 'any)
  216. (message "All is well"))
  217. t)
  218. (switch-to-buffer-other-window "*init file error*")
  219. (erase-buffer)
  220. (insert output)
  221. (search-backward "ERROR:")
  222. nil))))
  223. (add-hook 'kill-emacs-query-functions 'ambrevar/test-emacs)
  224. (defun ambrevar/toggle-org-reader ()
  225. "Toggle `variable-pitch-mode' and other niceties for Org reading."
  226. (interactive)
  227. (make-variable-buffer-local 'org-hide-emphasis-markers)
  228. ;; Because we revert the buffer, we need to set org-hide-emphasis-markers twice.
  229. (if org-hide-emphasis-markers
  230. (progn
  231. (follow-mode -1)
  232. (setq org-hide-emphasis-markers nil)
  233. (setq display-line-numbers-type 'visual)
  234. (revert-buffer)
  235. (setq org-hide-emphasis-markers nil)
  236. (variable-pitch-mode -1))
  237. (split-window-right)
  238. (follow-mode)
  239. (setq org-hide-emphasis-markers t)
  240. (setq display-line-numbers-type t)
  241. (revert-buffer)
  242. (setq org-hide-emphasis-markers t)
  243. (variable-pitch-mode 1)))
  244. (defun ambrevar/toggle-word-delim ()
  245. "Make underscore part of the word syntax or not.
  246. This does not interfere with `subword-mode'."
  247. (interactive)
  248. (if (equal (char-syntax ?_) "_")
  249. (progn
  250. (modify-syntax-entry ?_ "w")
  251. (message "_ is a not word delimiter"))
  252. (modify-syntax-entry ?_ "_")
  253. (message "_ is a word delimiter")))
  254. (defun ambrevar/youtube-dl-url (&optional url)
  255. "Run 'youtube-dl' over the URL.
  256. If URL is nil, use URL at point."
  257. (interactive)
  258. (setq url (or url (thing-at-point-url-at-point)))
  259. (let ((eshell-buffer-name "*youtube-dl*")
  260. (directory (seq-find (lambda (dir)
  261. (and (file-directory-p dir) (expand-file-name dir)))
  262. '("~/Videos" "~/Downloads")
  263. ".")))
  264. (eshell)
  265. (when (eshell-interactive-process)
  266. (eshell t))
  267. (eshell-interrupt-process)
  268. (insert (format " cd '%s' && youtube-dl " directory) url)
  269. (eshell-send-input)))
  270. (defvar ambrevar/privoxy-services '(("http" . "127.0.0.1:8118")
  271. ("https" . "127.0.0.1:8118"))
  272. "See `url-proxy-services' and `ambrevar-toggle-proxy'.")
  273. (defun ambrevar/toggle-proxy ()
  274. "Toggle `url-proxy-services' between `ambrevar/privoxy-services' and nil."
  275. (interactive)
  276. (require 'url)
  277. (setq url-proxy-services
  278. (if (or url-proxy-services
  279. (not (member "privoxy"
  280. (mapcar (lambda (p) (alist-get 'comm (process-attributes p)))
  281. (list-system-processes)))))
  282. nil
  283. ambrevar/privoxy-services)))
  284. (provide 'functions)