functions.el 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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. (with-output-to-string
  29. (with-current-buffer standard-output
  30. (apply 'call-process program nil t nil args))))
  31. (defun ambrevar/define-keys (map key def &rest bindings)
  32. "Like `define-key' but allow for defining several bindings at once.
  33. `KEY' must be acceptable for `kbd'."
  34. (while key
  35. (define-key map (kbd key) def)
  36. (setq key (pop bindings)
  37. def (pop bindings))))
  38. (defun ambrevar/escape-region (&optional regex to-string)
  39. "Escape double-quotes and backslashes.
  40. This is useful for writing Elisp strings containing those
  41. characters. The optional parameters let you control the replacement of REGEX for
  42. TO-STRING."
  43. (interactive)
  44. (unless regex (setq regex "\\([\"\\\\]\\)"))
  45. (unless to-string (setq to-string "\\\\\\1"))
  46. (while (re-search-forward regex (if (use-region-p) (region-end) (point-max)) t)
  47. (replace-match to-string)))
  48. (defun ambrevar/prettify ()
  49. "(Un)tabify, indent and delete trailing whitespace.
  50. Tabify if `indent-tabs-mode' is true, otherwise use spaces.
  51. Work on buffer or region.
  52. If `ambrevar/prettify-inhibit-p' is non-nil, it does nothing.
  53. Require `ambrevar/tabify-leading'."
  54. (interactive)
  55. (unless ambrevar/prettify-inhibit-p
  56. (let ((start (set-marker (make-marker) (if (use-region-p) (region-beginning) (point-min))))
  57. (end (set-marker (make-marker) (if (use-region-p) (region-end) (point-max)))))
  58. (if indent-tabs-mode
  59. (ambrevar/tabify-leading)
  60. (untabify start end))
  61. (indent-region start end)
  62. (save-restriction
  63. (narrow-to-region start end)
  64. (delete-trailing-whitespace)))))
  65. (defcustom ambrevar/prettify-inhibit-p t
  66. "Do not run `ambrevar/prettify' if non-nil.
  67. As this is not friendly to foreign projects, `ambrevar/prettify' should be run
  68. selectively."
  69. :safe 'booleanp)
  70. (defun ambrevar/flyspell-and-whitespace-mode ()
  71. "Toggle `flyspell-mode' and `whitespace-mode'."
  72. (interactive)
  73. (if (derived-mode-p 'text-mode)
  74. (flyspell-mode)
  75. (if flyspell-mode (flyspell-mode 0) (flyspell-prog-mode)))
  76. (whitespace-mode 'toggle))
  77. (global-set-key (kbd "<f9>") 'flyspell-and-whitespace-mode)
  78. ;;; From https://www.reddit.com/r/emacs/comments/70bn7v/what_do_you_have_emacs_show_when_it_starts_up/.
  79. ;;; Supply a random fortune cookie as the *scratch* message.
  80. (defun ambrevar/fortune-scratch-message ()
  81. (interactive)
  82. (let ((fortune
  83. (when (executable-find "fortune")
  84. (with-temp-buffer
  85. (shell-command "fortune" t)
  86. (while (not (eobp))
  87. (insert ";; ")
  88. (forward-line))
  89. (delete-trailing-whitespace (point-min) (point-max))
  90. (concat (buffer-string) "\n")))))
  91. (if (called-interactively-p 'any)
  92. (insert fortune)
  93. fortune)))
  94. (defun ambrevar/global-set-keys (key def &rest bindings)
  95. "Like `global-set-key' but allow for defining several bindings at once.
  96. `KEY' must be acceptable for `kbd'."
  97. (while key
  98. (global-set-key (kbd key) def)
  99. (setq key (pop bindings)
  100. def (pop bindings))))
  101. (defun ambrevar/insert-and-indent (text)
  102. "Insert indented TEXT at point."
  103. (interactive "s Text: ")
  104. (let ((oldpoint (point)))
  105. (insert text)
  106. (indent-region oldpoint (point))
  107. (newline-and-indent)))
  108. (defun ambrevar/local-set-keys (key def &rest bindings)
  109. "Like `local-set-key' but allow for defining several bindings at once.
  110. `KEY' must be acceptable for `kbd'."
  111. (while key
  112. (local-set-key (kbd key) def)
  113. (setq key (pop bindings)
  114. def (pop bindings))))
  115. (defun ambrevar/move-border-left (arg)
  116. "Move window border in a natural manner.
  117. If this is a window with its right edge being the edge of the
  118. screen, enlarge the window horizontally. If this is a window with
  119. its left edge being the edge of the screen, shrink the window
  120. horizontally. Otherwise, default to enlarging horizontally.\n
  121. Enlarge/Shrink by ARG columns, or 5 if ARG is nil."
  122. (interactive "P")
  123. (if (= (count-windows) 2)
  124. (ambrevar/move-border-left-or-right arg t)))
  125. (global-set-key (kbd "M-(") 'ambrevar/move-border-left)
  126. (defun ambrevar/move-border-left-or-right (arg dir-left)
  127. "Wrapper around ‘move-border-left’ and ‘move-border-right’.
  128. ARG is the number of columns to move.
  129. If DIR-LEFT is t, then move left, otherwise move right."
  130. (interactive)
  131. (unless arg (setq arg 5))
  132. (let ((left-edge (= (car (window-edges)) 0)))
  133. (if (or
  134. (and left-edge dir-left)
  135. (and (not left-edge) (not dir-left)))
  136. (shrink-window arg t)
  137. (enlarge-window arg t))))
  138. (defun ambrevar/move-border-right (arg)
  139. "See `move-border-left'."
  140. (interactive "P")
  141. (if (= (count-windows) 2)
  142. (ambrevar/move-border-left-or-right arg nil)))
  143. (global-set-key (kbd "M-)") 'ambrevar/move-border-right)
  144. (defun ambrevar/reset-fill-column ()
  145. "Reset `fill-column' to its default value."
  146. (setq fill-column (default-value 'fill-column)))
  147. (defun ambrevar/shell-last-command ()
  148. "Run last shell command."
  149. (interactive)
  150. (let ((last (car shell-command-history)))
  151. (if last
  152. (shell-command last)
  153. (error "Shell command history is empty"))))
  154. (global-set-key (kbd "C-M-!") 'shell-last-command)
  155. (defun ambrevar/skeleton-make-markers ()
  156. "Save last skeleton markers in a list.
  157. Hook function for skeletons."
  158. (while skeleton-markers
  159. (set-marker (pop skeleton-markers) nil))
  160. (setq skeleton-markers
  161. (mapcar 'copy-marker (reverse skeleton-positions))))
  162. (defvar skeleton-markers nil
  163. "Markers for locations saved in `skeleton-positions'.")
  164. (defun ambrevar/skeleton-previous-position ()
  165. "Move to previous skeleton placeholder.
  166. See `skeleton-next-position'."
  167. (skeleton-next-position t))
  168. (defun ambrevar/skeleton-next-position (&optional reverse)
  169. "Move to next skeleton placeholder.
  170. If REVERSE it t, move to previous placeholder."
  171. (interactive "P")
  172. (let ((positions (mapcar 'marker-position skeleton-markers))
  173. (comp (if reverse '< '<=))
  174. pos
  175. prev)
  176. (when positions
  177. (setq pos (pop positions))
  178. (while (and pos (funcall comp pos (point)))
  179. (setq prev pos)
  180. (setq pos (pop positions)))
  181. (cond
  182. ((and reverse prev) (goto-char prev))
  183. (reverse (goto-char (car (last skeleton-markers))))
  184. (pos (goto-char pos))
  185. (t (goto-char (car skeleton-markers)))))))
  186. (defun ambrevar/sort-lines-unique (arg)
  187. "Remove trailing white space, then duplicate lines, then sort the result.
  188. Do not fold case with \\[universal-argument] or non-nil ARG."
  189. (interactive "P")
  190. (let ((start (set-marker (make-marker) (if (use-region-p) (region-beginning) (point-min))))
  191. (end (set-marker (make-marker) (if (use-region-p) (region-end) (point-end)))))
  192. (let ((sort-fold-case (if arg nil t)))
  193. (delete-trailing-whitespace start end)
  194. (delete-duplicate-lines start end)
  195. (sort-lines nil start end))))
  196. (defun ambrevar/swap-windows (&optional w1 w2)
  197. "If 2 windows are up, swap them.
  198. Else if W1 is a window, swap it with current window.
  199. If W2 is a window too, swap both."
  200. (interactive)
  201. (unless (or (= 2 (count-windows))
  202. (windowp w1)
  203. (windowp w2))
  204. (error "Ambiguous window selection"))
  205. (let* ((w1 (or w1 (car (window-list))))
  206. (w2 (or w2
  207. (if (eq w1 (car (window-list)))
  208. (nth 1 (window-list))
  209. (car (window-list)))))
  210. (b1 (window-buffer w1))
  211. (b2 (window-buffer w2))
  212. (s1 (window-start w1))
  213. (s2 (window-start w2)))
  214. (with-temp-buffer
  215. ;; Some buffers like EXWM buffers can only be in one live buffer at once.
  216. ;; Switch to a dummy buffer in w2 so that we don't display any buffer twice.
  217. (set-window-buffer w2 (current-buffer))
  218. (set-window-buffer w1 b2)
  219. (set-window-buffer w2 b1))
  220. (set-window-start w1 s2)
  221. (set-window-start w2 s1))
  222. (select-window w1))
  223. (global-set-key (kbd "C-x \\") 'swap-windows)
  224. (defun ambrevar/swap-windows-left ()
  225. "Swap current window with the window to the left."
  226. (interactive)
  227. (ambrevar/swap-windows (window-in-direction 'left)))
  228. (defun ambrevar/swap-windows-below ()
  229. "Swap current window with the window below."
  230. (interactive)
  231. (ambrevar/swap-windows (window-in-direction 'below)))
  232. (defun ambrevar/swap-windows-above ()
  233. "Swap current window with the window above."
  234. (interactive)
  235. (ambrevar/swap-windows (window-in-direction 'above)))
  236. (defun ambrevar/swap-windows-right ()
  237. "Swap current window with the window to the right."
  238. (interactive)
  239. (ambrevar/swap-windows (window-in-direction 'right)))
  240. (defun ambrevar/switch-to-last-buffer ()
  241. "Switch to last open buffer in current window."
  242. (interactive)
  243. (switch-to-buffer (other-buffer (current-buffer) 1)))
  244. (defun ambrevar/tabify-leading ()
  245. "Call `tabify' on leading spaces only.
  246. Works on whole buffer if region is unactive."
  247. (interactive)
  248. (require 'tabify) ; Need this to initialize `tabify-regexp'.
  249. (let ((tabify-regexp-old tabify-regexp) start end)
  250. (if (use-region-p)
  251. (setq start (region-beginning) end (region-end))
  252. (setq start (point-min) end (point-max)))
  253. (unwind-protect
  254. (progn
  255. (setq tabify-regexp "^\t* [ \t]+")
  256. (tabify start end))
  257. (setq tabify-regexp tabify-regexp-old))))
  258. ;;; TODO: Store window configurations in a buffer-name-indexed alist? Not
  259. ;;; sure that would ever be useful.
  260. (defvar single-window--last-configuration nil "Last window configuration before calling `delete-other-windows'.")
  261. (defun ambrevar/toggle-single-window ()
  262. "Un-maximize current window.
  263. If multiple windows are active, save window configuration and
  264. delete other windows. If only one window is active and a window
  265. configuration was previously save, restore that configuration."
  266. (interactive)
  267. (if (= (count-windows) 1)
  268. (when single-window--last-configuration
  269. (set-window-configuration single-window--last-configuration))
  270. (setq single-window--last-configuration (current-window-configuration))
  271. (delete-other-windows)))
  272. (defun ambrevar/toggle-window-dedicated ()
  273. "Toggle whether the current active window is dedicated or not.
  274. Run it in each window you want to 'freeze', i.e. prevent Emacs
  275. from acting on it."
  276. (interactive)
  277. (message
  278. (if (let ((window (get-buffer-window (current-buffer))))
  279. (set-window-dedicated-p window
  280. (not (window-dedicated-p window))))
  281. "Window '%s' is dedicated"
  282. "Window '%s' is normal")
  283. (current-buffer)))
  284. (global-set-key (kbd "<pause>") 'ambrevar/toggle-window-dedicated)
  285. (defun ambrevar/toggle-window-split ()
  286. "Switch between vertical and horizontal split.
  287. It only works for frames with exactly two windows."
  288. (interactive)
  289. (if (= (count-windows) 2)
  290. (let* ((this-win-buffer (window-buffer))
  291. (next-win-buffer (window-buffer (next-window)))
  292. (this-win-edges (window-edges (selected-window)))
  293. (next-win-edges (window-edges (next-window)))
  294. (this-win-2nd (not (and (<= (car this-win-edges)
  295. (car next-win-edges))
  296. (<= (cadr this-win-edges)
  297. (cadr next-win-edges)))))
  298. (splitter
  299. (if (= (car this-win-edges)
  300. (car (window-edges (next-window))))
  301. 'split-window-horizontally
  302. 'split-window-vertically)))
  303. (delete-other-windows)
  304. (let ((first-win (selected-window)))
  305. (funcall splitter)
  306. (if this-win-2nd (other-window 1))
  307. (set-window-buffer (selected-window) this-win-buffer)
  308. (set-window-buffer (next-window) next-win-buffer)
  309. (select-window first-win)
  310. (if this-win-2nd (other-window 1))))))
  311. (global-set-key (kbd "C-x C-\\") 'toggle-window-split)
  312. (defun ambrevar/toggle-word-delim ()
  313. "Make underscore part of the word syntax or not.
  314. This does not interfere with `subword-mode'."
  315. (interactive)
  316. (if (equal (char-syntax ?_) "_")
  317. (progn
  318. (modify-syntax-entry ?_ "w")
  319. (message "_ is a not word delimiter"))
  320. (modify-syntax-entry ?_ "_")
  321. (message "_ is a word delimiter")))
  322. ;;; TODO: Move turn-on-* functions to 'hook-functions.el'?
  323. ;;; And replace useless individual comments with a single global comment.
  324. (defun ambrevar/turn-on-column-number-mode ()
  325. "Unconditionally turn on `column-number-mode' for the current buffer."
  326. (set (make-variable-buffer-local 'column-number-mode) t))
  327. (defun ambrevar/turn-on-complete-filename ()
  328. "Unconditionally turn on `comint-dynamic-complete-filename' for the current buffer."
  329. (add-to-list 'completion-at-point-functions 'comint-dynamic-complete-filename t))
  330. (defun ambrevar/turn-on-delete-trailing-whitespace ()
  331. "Add the `delete-trailing-whitespace' function to `before-save-hook'.
  332. This does not affect .csv files."
  333. (unless (string= (file-name-extension buffer-file-name) "csv")
  334. (add-hook 'before-save-hook 'delete-trailing-whitespace nil t)))
  335. (defun ambrevar/turn-off-delete-trailing-whitespace ()
  336. "Unconditionally remove the `delete-trailing-whitespace' function to `before-save-hook'."
  337. (remove-hook 'before-save-hook 'delete-trailing-whitespace t))
  338. (defun ambrevar/turn-on-prettify-before-save ()
  339. "Unconditionally add the `ambrevar/prettify' function to `before-save-hook'."
  340. (add-hook 'before-save-hook 'ambrevar/prettify nil t))
  341. (defun ambrevar/turn-off-prettify-before-save ()
  342. "Unconditionally remove the `ambrevar/prettify' function to `before-save-hook'."
  343. (remove-hook 'before-save-hook 'ambrevar/prettify t))
  344. (defun ambrevar/turn-off-indent-tabs ()
  345. "Unconditionally turn off tab indentation."
  346. (setq indent-tabs-mode nil))
  347. (defun ambrevar/turn-on-indent-tabs ()
  348. "Unconditionally turn on tab indentation."
  349. (setq indent-tabs-mode t))
  350. (defun ambrevar/turn-off-line-number-mode ()
  351. "Unconditionally turn off `line-number-mode' fur the current buffer.."
  352. (set (make-variable-buffer-local 'line-number-mode) nil))
  353. (defun ambrevar/turn-off-linum ()
  354. "Unconditionally turn off Linum mode."
  355. (linum-mode 0))
  356. (defun ambrevar/turn-on-newline-paragraph ()
  357. "Unconditionally make of newlines the start of a paragraph."
  358. (set (make-local-variable 'paragraph-start) "
  359. "))
  360. (defun ambrevar/turn-off-nobreak-char-display ()
  361. (set (make-local-variable 'nobreak-char-display) nil))
  362. (defun ambrevar/turn-on-skeleton-markers ()
  363. "Allow skeletons to make markers to ease field navigation."
  364. (add-hook 'skeleton-end-hook 'skeleton-make-markers))
  365. (defun ambrevar/turn-on-tab-width-to-4 ()
  366. "Unconditionally set tab width to 4."
  367. (setq tab-width 4))
  368. (defun ambrevar/turn-on-tab-width-to-8 ()
  369. "Unconditionally set tab width to 8."
  370. (setq tab-width 8))
  371. (defun ambrevar/unfill-paragraph ()
  372. "Paragraph at point is unwrapped on one single line."
  373. (interactive)
  374. (let ((fill-column (point-max)))
  375. (fill-paragraph nil)))
  376. (defun ambrevar/unfill-region ()
  377. "Unfill all paragraphs found in current region.
  378. Each paragraph stand on its line."
  379. (interactive)
  380. (let ((fill-column (point-max)))
  381. (fill-region (region-beginning) (region-end) nil)))
  382. (provide 'functions)