summaryrefslogtreecommitdiff
path: root/modules/custom-whitespace.el
blob: 48be080e846d2583c553ef787211c1aca002a83a (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
;;; custom-whitespace.el ---  -*- coding: utf-8; lexical-binding: t; -*-

;;; Commentary:
;;

;;; Code:


;;; ---------------------- Whitespace Operations And Keymap ---------------------

(defun cj/remove-leading-trailing-whitespace ()
  "Remove leading and trailing whitespace in a region, line, or buffer.

When called interactively:
- If a region is active, operate on the region.
- If called with a \[universal-argument] prefix, operate on the entire buffer.
- Otherwise, operate on the current line."
  (interactive)
  (let ((start (cond (current-prefix-arg (point-min))
					 ((use-region-p) (region-beginning))
					 (t (line-beginning-position))))
		(end (cond (current-prefix-arg (point-max))
				   ((use-region-p) (region-end))
				   (t (line-end-position)))))
	(save-excursion
	  (save-restriction
		(narrow-to-region start end)
		(goto-char (point-min))
		(while (re-search-forward "^[ \t]+" nil t) (replace-match ""))
		(goto-char (point-min))
		(while (re-search-forward "[ \t]+$" nil t) (replace-match ""))))))

(defun cj/collapse-whitespace-line-or-region ()
  "Collapse whitespace to one space in the current line or active region.

Ensure there is exactly one space between words and remove leading and trailing whitespace."
  (interactive)
  (save-excursion
	(let* ((region-active (use-region-p))
		   (beg (if region-active (region-beginning) (line-beginning-position)))
		   (end (if region-active (region-end) (line-end-position))))
	  (save-restriction
		(narrow-to-region beg end)
		;; Replace all tabs with space
		(goto-char (point-min))
		(while (search-forward "\t" nil t)
		  (replace-match " " nil t))
		;; Remove leading and trailing spaces
		(goto-char (point-min))
		(while (re-search-forward "^\\s-+\\|\\s-+$" nil t)
		  (replace-match "" nil nil))
		;; Ensure only one space between words/symbols
		(goto-char (point-min))
		(while (re-search-forward "\\s-\\{2,\\}" nil t)
		  (replace-match " " nil nil))))))

(defun cj/delete-blank-lines-region-or-buffer (start end)
  "Delete blank lines between START and END.

Treat blank lines as lines that contain nothing or only whitespace.
Operate on the active region when one exists.
Prompt before operating on the whole buffer when no region is selected.
Signal a user error and do nothing when the user declines.
Restore point to its original position after deletion."
  (interactive
   (if (use-region-p)
	   ;; grab its boundaries if there's a region
	   (list (region-beginning) (region-end))
	 ;; or ask if user intended operating on whole buffer
	 (if (yes-or-no-p "Delete blank lines in entire buffer? ")
		 (list (point-min) (point-max))
	   (user-error "Aborted"))))
  (save-excursion
	(save-restriction
	  (widen)
	  ;; Regexp "^[[:space:]]*$" matches lines of zero or more spaces/tabs.
	  (flush-lines "^[[:space:]]*$" start end)))
  ;; Return nil (Emacs conventions). Point is already restored.
  nil)

(defun cj/hyphenate-whitespace-in-region (start end)
  "Replace runs of whitespace between START and END with hyphens.

Operate on the active region designated by START and END."
  (interactive "*r")
  (if (use-region-p)
	  (save-excursion
		(save-restriction
		  (narrow-to-region start end)
		  (goto-char (point-min))
		  (while (re-search-forward "[ \t\n\r]+" nil t)
			(replace-match "-"))))
	(message "No region; nothing to hyphenate.")))


;; Whitespace operations prefix and keymap
(define-prefix-command 'cj/whitespace-map nil
					   "Keymap for whitespace operations.")
(define-key cj/custom-keymap "w" 'cj/whitespace-map)
(define-key cj/whitespace-map "r" 'cj/remove-leading-trailing-whitespace)
(define-key cj/whitespace-map "c" 'cj/collapse-whitespace-line-or-region)
(define-key cj/whitespace-map "l" 'cj/delete-blank-lines-region-or-buffer)
(define-key cj/whitespace-map "-" 'cj/hyphenate-whitespace-in-region)

(provide 'custom-whitespace)
;;; custom-whitespace.el ends here.