1 ;;; rainbow-fart.el --- Encourage when you programming -*- lexical-binding: t; -*-
3 ;;; Time-stamp: <2020-06-26 08:49:32 stardiviner>
5 ;; Authors: stardiviner <numbchild@gmail.com>
6 ;; Package-Requires: ((emacs "25.1") (flycheck "32-cvs"))
7 ;; Package-Version: 0.1
9 ;; homepage: https://github.com/stardiviner/emacs-rainbow-fart
11 ;; rainbow-fart is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
16 ;; rainbow-fart is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
19 ;; License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
28 ;; (add-hook 'prog-mode-hook #'rainbow-fart-mode)
35 (defgroup rainbow-fart nil
36 "rainbow-fart-mode customize group."
37 :prefix
"rainbow-fart-"
40 (defcustom rainbow-fart-voice-alist
41 '(("defun" .
("function.mp3" "function_01.mp3" "function_02.mp3" "function_03.mp3"))
42 ("defn" .
("function.mp3" "function_01.mp3" "function_02.mp3" "function_03.mp3"))
43 ("def" .
("function.mp3" "function_01.mp3" "function_02.mp3" "function_03.mp3"))
44 ("fn" .
("function.mp3" "function_01.mp3" "function_02.mp3" "function_03.mp3"))
45 ("lambda" .
("function.mp3" "function_01.mp3" "function_02.mp3" "function_03.mp3"))
46 ("function" .
("function.mp3" "function_01.mp3" "function_02.mp3" "function_03.mp3"))
47 ("->" .
("arrow_function_01.mp3"))
48 ("->>" .
("arrow_function_01.mp3"))
49 ("=>" .
("arrow_function_01.mp3"))
50 ("if" .
("if_01.mp3" "if_02.mp3" "if_03.mp3"))
51 ("while" .
("if_01.mp3" "if_02.mp3" "if_03.mp3"))
52 ("when" .
("if_01.mp3" "if_02.mp3" "if_03.mp3"))
53 ("until" .
("if_01.mp3" "if_02.mp3" "if_03.mp3"))
54 ("for" .
("for_01.mp3" "for_02.mp3" "for_03.mp3"))
55 ("loop" .
("for_01.mp3" "for_02.mp3" "for_03.mp3"))
56 ("await" .
("await_01.mp3" "await_02.mp3" "await_03.mp3"))
57 ("promise" .
("await_01.mp3" "await_02.mp3" "await_03.mp3"))
58 ("catch" .
("catch_01.mp3" "catch_02.mp3" "catch_03.mp3"))
59 ("import" .
("import_01.mp3" "import_02.mp3"))
60 (":import" .
("import_01.mp3" "import_02.mp3"))
61 (":require" .
("import_01.mp3" "import_02.mp3"))
62 ("require" .
("import_01.mp3" "import_02.mp3"))
63 ("load" .
("import_01.mp3" "import_02.mp3"))
64 ("load-file" .
("import_01.mp3" "import_02.mp3"))
65 ("v-html" .
("v_html_01.mp3"))
66 ("fuck" .
("fuck_pm_01.mp3" "fuck_pm_02.mp3"))
67 ("shit" .
("fuck_pm_01.mp3" "fuck_pm_02.mp3"))
68 ("damn" .
("fuck_pm_01.mp3" "fuck_pm_02.mp3"))
70 ("hour" .
("time_each_hour_01.mp3" "time_each_hour_02.mp3"
71 "time_each_hour_03.mp3" "time_each_hour_04.mp3" "time_each_hour_05.mp3"))
72 ("morning" .
("time_morning_01.mp3"))
73 ("before_noon" .
("time_before_noon_01.mp3" "time_before_noon_02.mp3"
74 "time_before_noon_03.mp3" "time_before_noon_04.mp3"))
75 ("noon" .
("time_noon_01.mp3"))
76 ("evening" .
("time_evening_01.mp3"))
77 ("midnight" .
("time_midnight_01.mp3"))
78 ;; TODO `flycheck' support
82 "An alist of pairs of programming language keywords and voice filenames."
87 (defcustom rainbow-fart-voice-model
"JustKowalski"
88 "The voice model to be used."
93 (defcustom rainbow-fart-voices-directory
94 (concat (file-name-directory (or load-file-name buffer-file-name
)) "voices/")
95 "The directory of voices."
100 (defcustom rainbow-fart-keyword-interval
(* 60 5)
101 "The time interval in seconds of rainbow-fart play voice for keywords.
102 If it is nil, will play sound for every keywords."
105 :group
'rainbow-fart
)
107 (defcustom rainbow-fart-time-interval
(* 60 15)
108 "The time interval in seconds of rainbow-fart play voice for hours.
109 If it's nil, the hours remind will not started."
112 :group
'rainbow-fart
)
114 (defvar rainbow-fart--playing nil
115 "The status of rainbow-fart playing.")
117 (defvar rainbow-fart--play-last-time nil
118 "The last time of rainbow-fart play.")
120 (defun rainbow-fart--get-media-uri (keyword)
121 "Get media uri based on KEYWORD."
122 (when-let ((uris (cdr (assoc keyword rainbow-fart-voice-alist
))))
123 (let ((uri (nth (random (length uris
)) uris
))
124 (voice-model-directory
125 (expand-file-name rainbow-fart-voice-model rainbow-fart-voices-directory
)))
126 (if (url-type (url-generic-parse-url uri
))
128 (let ((uri (expand-file-name uri voice-model-directory
)))
129 (when (file-exists-p uri
)
133 (defun rainbow-fart--play (keyword)
134 "A private function to play voice for matched KEYWORD."
135 (unless (or rainbow-fart--playing
136 (when rainbow-fart-keyword-interval
137 (not (if rainbow-fart--play-last-time
138 (> (- (float-time) rainbow-fart--play-last-time
) rainbow-fart-keyword-interval
)
139 (setq rainbow-fart--play-last-time
(float-time))))))
140 (when-let ((uri (rainbow-fart--get-media-uri keyword
))
142 (executable-find "mpg123")
143 (executable-find "mplayer")
144 (executable-find "mpv"))))
145 (setq rainbow-fart--playing t
)
146 (make-process :name
"rainbow-fart"
147 :command
`(,command
,uri
)
148 :buffer
"*rainbow-fart*"
149 :sentinel
(lambda (_ __
)
150 (setq rainbow-fart--playing nil
)
151 (setq rainbow-fart--play-last-time
(float-time)))))))
154 (defun rainbow-fart--post-self-insert ()
155 "A hook function on `post-self-insert-hook' to play audio."
156 (let* ((prefix (thing-at-point 'symbol
))
157 (face (get-text-property (1- (point)) 'face
)))
158 (when (or (memq face
'(font-lock-keyword-face))
160 (rainbow-fart--play prefix
))))
162 ;;; linter like `flycheck'
164 (defun rainbow-fart--linter-display-error (err)
165 "Play voice for `flycheck-error' ERR."
166 (let ((level (flycheck-error-level err
)))
167 (rainbow-fart--play level
)))
169 (defun rainbow-fart--linter-display-errors (errors)
170 "A function to report ERRORS used as replacement of linter like `flycheck' and `flymake'."
172 (mapc #'rainbow-fart--linter-display-error
174 (seq-mapcat #'flycheck-related-errors errors
)))))
178 (defun rainbow-fart--timing ()
179 "Play voice for current time quantum."
180 (let* ((time (format-time-string "%H:%M"))
181 (pair (split-string time
":"))
182 (hour (string-to-number (car pair
))))
184 ((and (> hour
05) (< hour
08)) ; 05:00 -- 08:00
186 ((and (> hour
08) (< hour
10)) ; 08:00 -- 10:00
188 ((and (> hour
10) (< hour
11)) ; 10:00 -- 11:00
190 ((and (> hour
11) (< hour
13)) ; 11:00 -- 13:00
192 ((and (> hour
13) (< hour
15)) ; 13:00 -- 15:00
194 ((and (> hour
15) (< hour
17)) ; 15:00 -- 17:00
196 ((and (> hour
18) (< hour
22)) ; 18:00 -- 21:00
198 ((or (> hour
23) (< hour
01)) ; 23:00 -- 01:00
201 (defun rainbow-fart--timing-remind ()
202 "Remind you in specific time quantum."
203 (when (and rainbow-fart--play-last-time
204 (> (- (float-time) rainbow-fart--play-last-time
) rainbow-fart-time-interval
))
205 (rainbow-fart--play (rainbow-fart--timing))
206 (setq rainbow-fart--play-last-time
(float-time))))
208 (defvar rainbow-fart--timer nil
)
211 (define-minor-mode rainbow-fart-mode
212 "A minor mode add an encourager when you programming."
214 :lighter
" rainbow-fart "
216 (if rainbow-fart-mode
218 (add-hook 'post-self-insert-hook
#'rainbow-fart--post-self-insert t t
)
219 (advice-add (buffer-local-value 'flycheck-display-errors-function
(current-buffer))
220 :before
'rainbow-fart--linter-display-errors
)
221 (when rainbow-fart-time-interval
222 (setq rainbow-fart--timer
223 (run-with-timer 10 rainbow-fart-time-interval
'rainbow-fart--timing-remind
))))
224 (remove-hook 'post-self-insert-hook
#'rainbow-fart--post-self-insert t
)
225 (advice-remove (buffer-local-value 'flycheck-display-errors-function
(current-buffer))
226 'rainbow-fart--linter-display-errors
)
227 (when rainbow-fart--timer
228 (cancel-timer rainbow-fart--timer
))))
232 (provide 'rainbow-fart
)
234 ;;; rainbow-fart.el ends here