about summary refs log tree commit diff stats
path: root/lisp
diff options
context:
space:
mode:
authorCase Duckworth2021-09-01 18:14:55 -0500
committerCase Duckworth2021-09-01 18:14:55 -0500
commit963d94a0ec90311429807fdf1700271e54441f2c (patch)
treed15583eb5a13c23d6e3e21c63c5fc69ddd0b6430 /lisp
parentAutomatically call persistent-scratch-mode on *scratch* (diff)
parentAdd acdw-org/work-month-headings (diff)
downloademacs-963d94a0ec90311429807fdf1700271e54441f2c.tar.gz
emacs-963d94a0ec90311429807fdf1700271e54441f2c.zip
Merge branch 'main' of https://tildegit.org/acdw/emacs
Diffstat (limited to 'lisp')
-rw-r--r--lisp/acdw-compat.el8
-rw-r--r--lisp/acdw-org.el46
-rw-r--r--lisp/acdw.el13
-rw-r--r--lisp/titlecase.el157
4 files changed, 211 insertions, 13 deletions
diff --git a/lisp/acdw-compat.el b/lisp/acdw-compat.el index 04e42ae..0a1a037 100644 --- a/lisp/acdw-compat.el +++ b/lisp/acdw-compat.el
@@ -22,7 +22,7 @@
22;;; Code: 22;;; Code:
23 23
24;; Convenience macro 24;; Convenience macro
25(defmacro safe-define (&rest defines) 25(defmacro safely (&rest defines)
26 "Wrap DEFINES in tests to make sure they're not already defined. 26 "Wrap DEFINES in tests to make sure they're not already defined.
27Is it necessary? Who knows!!" 27Is it necessary? Who knows!!"
28 (let (output) 28 (let (output)
@@ -53,7 +53,7 @@ Is it necessary? Who knows!!"
53 53
54;;; Functions for changing capitalization that Do What I Mean 54;;; Functions for changing capitalization that Do What I Mean
55;; Defined in EMACS/lisp/simple.el 55;; Defined in EMACS/lisp/simple.el
56(safe-define 56(safely
57 (defun upcase-dwim (arg) 57 (defun upcase-dwim (arg)
58 "Upcase words in the region, if active; if not, upcase word at point. 58 "Upcase words in the region, if active; if not, upcase word at point.
59If the region is active, this function calls `upcase-region'. 59If the region is active, this function calls `upcase-region'.
@@ -88,7 +88,7 @@ to capitalize ARG words."
88;;; Repeat.el 88;;; Repeat.el
89;; Defined in EMACS/lisp/repeat.el 89;; Defined in EMACS/lisp/repeat.el
90 90
91(safe-define 91(safely
92 (defcustom repeat-too-dangerous '(kill-this-buffer) 92 (defcustom repeat-too-dangerous '(kill-this-buffer)
93 "Commands too dangerous to repeat with \\[repeat]." 93 "Commands too dangerous to repeat with \\[repeat]."
94 :group 'convenience 94 :group 'convenience
@@ -538,7 +538,7 @@ Used in `repeat-mode'.")
538 538
539 539
540;;; goto-address-mode 540;;; goto-address-mode
541(safe-define 541(safely
542 (defvar global-address-mode nil) 542 (defvar global-address-mode nil)
543 543
544 (define-globalized-minor-mode global-goto-address-mode 544 (define-globalized-minor-mode global-goto-address-mode
diff --git a/lisp/acdw-org.el b/lisp/acdw-org.el index 50a0488..89269ab 100644 --- a/lisp/acdw-org.el +++ b/lisp/acdw-org.el
@@ -370,19 +370,49 @@ instead of the true count."
370;;; Next and previous heading, with widening 370;;; Next and previous heading, with widening
371(defun acdw/org-next-heading-widen (arg) 371(defun acdw/org-next-heading-widen (arg)
372 (interactive "p") 372 (interactive "p")
373 (let ((point-target (if (> arg 0) 373 (let ((current-point (point))
374 (point-max) 374 (point-target (if (> arg 0) (point-max) (point-min))))
375 (point-min)))) 375 (org-next-visible-heading arg)
376 (unless (or (org-next-visible-heading arg) ; XXX: this doesn't work!!! ARGH 376 (when (and (buffer-narrowed-p)
377 (/= (point) point-target)) 377 (= (point) point-target)
378 (when (buffer-narrowed-p) 378 (or (and (> arg 0))
379 (widen) 379 (and (< arg 0)
380 (org-next-visible-heading arg))))) 380 (= (point) current-point))))
381 (widen)
382 (org-next-visible-heading arg))))
381 383
382(defun acdw/org-previous-heading-widen (arg) 384(defun acdw/org-previous-heading-widen (arg)
383 (interactive "p") 385 (interactive "p")
384 (acdw/org-next-heading-widen (- arg))) 386 (acdw/org-next-heading-widen (- arg)))
385 387
386 388
389;;; Add headings for every day of the work month
390;; Gets rid of weekends.
391
392(defun acdw-org/work-month-headings (&optional month year)
393 (interactive (list
394 (read-number "Month: " (car (calendar-current-date)))
395 (read-number "Year: " (nth 2 (calendar-current-date)))))
396 (let ((offset 0)
397 (month (or month
398 (car (calendar-current-date))))
399 (year (or year
400 (car (last (calendar-current-date))))))
401 (dotimes (day (calendar-last-day-of-month month year))
402 (let* ((day (1+ day))
403 (day-of-week (calendar-day-of-week (list month day year))))
404 (unless (memq day-of-week '(0 6)) ; weekend
405 (end-of-line)
406 (org-insert-heading nil t t)
407 (insert (concat "[" (mapconcat (lambda (n)
408 (format "%02d" n))
409 (list year month day)
410 "-")
411 " "
412 (nth day-of-week '("Sun" "Mon" "Tue" "Wed" "Thu"
413 "Fri" "Sat"))
414 "]")))))))
415
416
387(provide 'acdw-org) 417(provide 'acdw-org)
388;; acdw-org.el ends here 418;; acdw-org.el ends here
diff --git a/lisp/acdw.el b/lisp/acdw.el index f23ca0e..796c2f1 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el
@@ -579,7 +579,18 @@ It's called 'require-private' for historical reasons."
579 (switch-to-buffer nil) 579 (switch-to-buffer nil)
580 (other-window (or arg 1)))) 580 (other-window (or arg 1))))
581 581
582 582
583;;; Auth-sources
584;; https://github.com/emacs-circe/circe/wiki/Configuration
585(defun acdw/fetch-password (&rest params)
586 (require 'auth-source)
587 (let ((match (car (apply #'auth-source-search params))))
588 (if match
589 (let ((secret (plist-get match :secret)))
590 (if (functionp secret)
591 (funcall secret)
592 secret))
593 (warn "Password not found for %S" params))))
583 594
584(provide 'acdw) 595(provide 'acdw)
585;;; acdw.el ends here 596;;; acdw.el ends here
diff --git a/lisp/titlecase.el b/lisp/titlecase.el new file mode 100644 index 0000000..64da5b4 --- /dev/null +++ b/lisp/titlecase.el
@@ -0,0 +1,157 @@
1;;; titlecase.el -*- lexical-binding: t; -*-
2
3;; https://hungyi.net/posts/programmers-way-to-title-case/
4
5(require 'cl-lib)
6(require 'subr-x)
7
8;;;###autoload
9(defun titlecase-string (str)
10 "Convert string STR to title case and return the resulting string."
11 (let* ((case-fold-search nil)
12 (str-length (length str))
13 ;; A list of markers that indicate start of a new phrase within the
14 ;; title, e.g. "The Lonely Reindeer: A Christmas Story"
15 ;; must be followed by one of word-boundary-chars
16 (new-phrase-chars '(?: ?. ?? ?\; ?\n ?\r))
17 ;; immediately triggers new phrase behavior without waiting for word
18 ;; boundary
19 (immediate-new-phrase-chars '(?\n ?\r))
20 ;; A list of characters that indicate "word boundaries"; used to split
21 ;; the title into processable segments
22 (word-boundary-chars (append '(? ?– ?— ?- ?‑ ?/)
23 immediate-new-phrase-chars))
24 ;; A list of small words that should not be capitalized (in the right
25 ;; conditions)
26 (small-words '("a" "an" "and" "as" "at" "but" "by" "en" "for" "if"
27 "in" "of" "on" "or" "the" "to" "v" "v." "vs" "vs."
28 "via"))
29 ;; Fix if str is ALL CAPS
30 (str (if (string-match-p "[a-z]" str) str (downcase str)))
31 ;; Reduce over a state machine to do title casing
32 (final-state
33 (cl-reduce
34 (lambda (state char)
35 (let* ((result (aref state 0))
36 (last-segment (aref state 1))
37 (first-word-p (aref state 2))
38 (was-in-path-p (aref state 3))
39 (last-char (car last-segment))
40 (in-path-p (or (and (eq char ?/)
41 (or (not last-segment)
42 (member last-char '(?. ?~))))
43 (and was-in-path-p
44 (not
45 (or (eq char ? )
46 (member
47 char
48 immediate-new-phrase-chars))))))
49 (end-p
50 ;; are we at the end of the input string?
51 (eq (+ (length result) (length last-segment) 1)
52 str-length))
53 (pop-p
54 ;; do we need to pop a segment onto the output result?
55 (or end-p (and (not in-path-p)
56 (member char word-boundary-chars))))
57 (segment
58 ;; add the current char to the current segment
59 (cons char last-segment))
60 (segment-string
61 ;; the readable version of the segment
62 (apply #'string (reverse segment)))
63 (small-word-p
64 ;; was the last segment a small word?
65 (member (downcase (substring segment-string 0 -1))
66 small-words))
67 (capitalize-p
68 ;; do we need to capitalized this segment or lowercase it?
69 (or end-p first-word-p (not small-word-p)))
70 (ignore-segment-p
71 ;; ignore explicitly capitalized segments
72 (or (string-match-p "[a-zA-Z].*[A-Z]" segment-string)
73 ;; ignore URLs
74 (string-match-p "^https?:" segment-string)
75 ;; ignore hostnames and namespaces.like.this
76 (string-match-p "\\w\\.\\w" segment-string)
77 ;; ignore windows filesystem paths
78 (string-match-p "^[A-Za-z]:\\\\" segment-string)
79 ;; ignore unix filesystem paths
80 was-in-path-p
81 ;; ignore email addresses and user handles with @ symbol
82 (member ?@ segment)))
83 (next-result
84 (if pop-p
85 (concat result
86 (if ignore-segment-p
87 ;; pop segment onto the result without
88 ;; processing
89 segment-string
90 ;; titlecase the segment before popping onto
91 ;; result
92 (titlecase--segment
93 segment-string capitalize-p)))
94 result))
95 (next-segment
96 (unless pop-p segment))
97 (will-be-first-word-p
98 (if pop-p
99 (or (not last-segment)
100 (member last-char new-phrase-chars)
101 (member char immediate-new-phrase-chars))
102 first-word-p)))
103 (vector
104 next-result next-segment will-be-first-word-p in-path-p)))
105 str
106 :initial-value
107 (vector nil ; result stack
108 nil ; current working segment
109 t ; is it the first word of a phrase?
110 nil)))) ; are we inside of a filesystem path?
111 (aref final-state 0)))
112
113(defun titlecase--segment (segment capitalize-p)
114 "Convert a title's inner SEGMENT to capitalized or lower case
115depending on CAPITALIZE-P, then return the result."
116 (let* ((case-fold-search nil)
117 (ignore-chars '(?' ?\" ?\( ?\[ ?‘ ?“ ?’ ?” ?_))
118 (final-state
119 (cl-reduce
120 (lambda (state char)
121 (let ((result (aref state 0))
122 (downcase-p (aref state 1)))
123 (cond
124 (downcase-p
125 ;; already upcased start of segment, so lowercase the rest
126 (vector (cons (downcase char) result) t))
127 ((member char ignore-chars)
128 ;; check if start char of segment needs to be ignored
129 (vector (cons char result) downcase-p))
130 (t
131 ;; haven't upcased yet, and we can, so do it
132 (vector (cons (upcase char) result) t)))))
133 segment
134 :initial-value (vector nil (not capitalize-p)))))
135 (thread-last (aref final-state 0)
136 (reverse)
137 (apply #'string))))
138
139;;;###autoload
140(defun titlecase-region (begin end)
141 "Convert text in region from BEGIN to END to title case."
142 (interactive "*r")
143 (let ((pt (point)))
144 (insert (titlecase-string (delete-and-extract-region begin end)))
145 (goto-char pt)))
146
147;;;###autoload
148(defun titlecase-dwim ()
149 "Convert the region or current line to title case.
150If Transient Mark Mode is on and there is an active region, convert
151the region to title case. Otherwise, work on the current line."
152 (interactive)
153 (if (and transient-mark-mode mark-active)
154 (titlecase-region (region-beginning) (region-end))
155 (titlecase-region (point-at-bol) (point-at-eol))))
156
157(provide 'titlecase)