├── .gitignore ├── cows ├── bunny.cow ├── hellokitty.cow ├── default.cow ├── koala.cow ├── satanic.cow ├── moose.cow ├── small.cow ├── mutilated.cow ├── vader-koala.cow ├── luke-koala.cow ├── tux.cow ├── sheep.cow ├── squirrel.cow ├── www.cow ├── cower.cow ├── head-in.cow ├── moofasa.cow ├── ren.cow ├── supermilker.cow ├── vader.cow ├── elephant-in-snake.cow ├── elephant.cow ├── kitty.cow ├── three-eyes.cow ├── bong.cow ├── bud-frogs.cow ├── sodomized.cow ├── telebears.cow ├── udder.cow ├── scheming-pony.cow ├── stimpy.cow ├── kosh.cow ├── skeleton.cow ├── milk.cow ├── flaming-sheep.cow ├── meow.cow ├── cheese.cow ├── eyes.cow ├── daemon.cow ├── beavis-zen.cow ├── surgery.cow ├── kiss.cow ├── mech-and-cow.cow ├── stegosaurus.cow ├── dragon.cow ├── ghostbusters.cow ├── turtle.cow ├── dragon-and-cow.cow └── turkey.cow ├── LICENSE ├── README.md └── cowsay.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /cows/bunny.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## A cute little wabbit 3 | ## 4 | $the_cow = <> 12 | EOC 13 | -------------------------------------------------------------------------------- /cows/telebears.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## A cow performing an unnatural act, artist unknown. 3 | ## 4 | $the_cow = <> 12 | EOC 13 | -------------------------------------------------------------------------------- /cows/udder.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## The cow from a file called cow-n-horn, artist unknown. 3 | ## 4 | $other_eye = chop($eyes); 5 | $eyes .= " $other_eye"; 6 | $the_cow = < 21 | EOC 22 | -------------------------------------------------------------------------------- /cows/flaming-sheep.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## The flaming sheep, contributed by Geordan Rosario (geordan@csua.berkeley.edu) 3 | ## 4 | $the_cow = <> 5.4 3 | ## 4 | $the_cow = < < > .---. 8 | $thoughts | \\ \\ - ~ ~ - / / | 9 | _____ ..-~ ~-..-~ 10 | | | \\~~~\\.' `./~~~/ 11 | --------- \\__/ \\__/ 12 | .' O \\ / / \\ " 13 | (_____, `._.' | } \\/~~~/ 14 | `----. / } | / \\__/ 15 | `-. | / | / `. ,~~| 16 | ~-.__| /_ - ~ ^| /- _ `..-' 17 | | / | / ~-. `-. _ _ _ 18 | |_____| |_____| ~ - . _ _ _ _ _> 19 | EOC 20 | -------------------------------------------------------------------------------- /cows/dragon.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## The Whitespace Dragon 3 | ## 4 | $the_cow = < \\ _ -~ `. ^-` ^-_ 19 | ///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~ 20 | /.-~ 21 | EOC 22 | -------------------------------------------------------------------------------- /cows/ghostbusters.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## Ghostbusters! 3 | ## 4 | $the_cow = < 16 | ---___ XXX__/ XXXXXX \\__ / 17 | \\- --__/ ___/\\ XXXXXX / ___--/= 18 | \\-\\ ___/ XXXXXX '--- XXXXXX 19 | \\-\\/XXX\\ XXXXXX /XXXXX 20 | \\XXXXXXXXX \\ /XXXXX/ 21 | \\XXXXXX > _/XXXXX/ 22 | \\XXXXX--__/ __-- XXXX/ 23 | -XXXXXXXX--------------- XXXXXX- 24 | \\XXXXXXXXXXXXXXXXXXXXXXXXXX/ 25 | ""VXXXXXXXXXXXXXXXXXXV"" 26 | EOC 27 | -------------------------------------------------------------------------------- /cows/turtle.cow: -------------------------------------------------------------------------------- 1 | ## 2 | ## A mysterious turtle... 3 | ## 4 | $the_cow = <____) >___ ^\\_\\_\\_\\_\\_\\_\\) 24 | ^^^//\\\\_^^//\\\\_^ ^(\\_\\_\\_\\) 25 | ^^^ ^^ ^^^ ^ 26 | EOC 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cowsay in Emacs 2 | This is a workalike of the popular [cowsay](https://en.wikipedia.org/wiki/Cowsay "Link to English Wikipedia") amusement program that 3 | runs directly in Emacs and does not require any external programs. 4 | 5 | This port is not written by the original author of `cowsay`, but 6 | can load cartoon characters from the same files as the original and you can add custom cows (see [link](https://en.wikipedia.org/wiki/Cowsay "Repo") to repo with a lot of `.cow` files)! 7 | 8 | ## Commands 9 | Here Commands, before run use `cowsay-load-cows` for load cows, this is required, if you hasn't cows, then clone anything repo with cows, and add source's path to `cowsay-directories`: ([example](https://github.com/paulkaefer/cowsay-files)), this is required: 10 | 11 | * `cowsay-string` read string from minibuffer and in new buffer see `cowsay` result with user's string 12 | * `cowsay-region` read region, and with this region view `cowsay` result in new buffer 13 | * `cowsay-replace-region` read region and replace its with `cowsay` result 14 | 15 | ## Customization 16 | Here all variables, which you can change: 17 | 18 | * `cowsay-directories` is list of directories in which put `.cow` files, defaults to "/usr/local/share/cows" and "/usr/share/cowsay/cows" 19 | * `cowsay-preferred-cows` is list of names of cows, yea cows has names 20 | * `cowsay-eyes` is 2-character string to use as the cow's eyes. 21 | Example of customization: 22 | ```elisp 23 | (setq cowsay-eyes "..") 24 | ``` 25 | * `cowsay-tongue`is 2-character string to use as the cow's tongue 26 | Example of customization: 27 | ```elisp 28 | (setq cowsay-eyes "|]") 29 | ``` 30 | -------------------------------------------------------------------------------- /cowsay.el: -------------------------------------------------------------------------------- 1 | ;;; cowsay.el --- Poorly drawn ASCII cartoons saying things -*- lexical-binding: t -*- 2 | 3 | ;; Copyright 2016 Matt Smith (go-cowsay) 4 | ;; Copyright 2021 Lassi Kortela 5 | ;; SPDX-License-Identifier: MIT 6 | 7 | ;; Author: Lassi Kortela 8 | ;; URL: https://github.com/lassik/emacs-cowsay 9 | ;; Version: 0.1.0 10 | ;; Package-Requires: ((emacs "24.5")) 11 | ;; Keywords: games 12 | 13 | ;; This file is not part of GNU Emacs. 14 | 15 | ;;; Commentary: 16 | 17 | ;; This is a workalike of the popular `cowsay' amusement program that 18 | ;; runs directly in Emacs and does not require any external programs. 19 | ;; This port is not written by the original author of `cowsay', but 20 | ;; can load cartoon characters from the same files as the original. 21 | 22 | ;;; Code: 23 | 24 | ;;;###autoload 25 | (defgroup cowsay nil 26 | "Poorly drawn ASCII cartoons saying things." 27 | :group 'games) 28 | 29 | (defcustom cowsay-directories 30 | '("/usr/local/share/cows" "/usr/share/cowsay/cows") 31 | "List of directories to search for .cow files. 32 | 33 | Any directories listed in the COWPATH environment variable are 34 | searched as well." 35 | :group 'cowsay 36 | :type '(list string)) 37 | 38 | (defvar cowsay-cows '() 39 | "List of cows that are available for use. 40 | 41 | Each entry is a list whose first element is the cow name (a 42 | string) and remaining elements are cow parts. A cow part is 43 | either a string (which is inserted verbatim) or one of the 44 | symbols 'eyes, 'thoughts, 'tongue.") 45 | 46 | (defcustom cowsay-preferred-cows '("default") 47 | "List of your favorite cows. 48 | 49 | When you don't ask for a particular cow, the first loaded cow 50 | from this list is chosen by default. If none of the preferred 51 | cows are loaded, the first cow available is chosen. 52 | 53 | The value is a list of cow names as strings. Emacs Lisp code 54 | calling cowsay can temporarily override this variable 55 | using (let ((cowsay-preferred-cows ...)) ...) to use particular 56 | cows preferred by the program." 57 | :group 'cowsay 58 | :type '(list string)) 59 | 60 | (defvar cowsay-cow-history '() 61 | "History of which cows have been used to say things.") 62 | 63 | (defcustom cowsay-bubble-style 'say 64 | "Either 'say or 'think." 65 | :group 'cowsay 66 | :type '(radio (say :tag "Speech bubble") 67 | (think :tag "Thought bubble"))) 68 | 69 | (defvar cowsay-eyes "" 70 | "A 2-character string to use as the cow's eyes.") 71 | 72 | (defvar cowsay-tongue "" 73 | "A 2-character string to use as the cow's tongue.") 74 | 75 | (defun cowsay--get-accessory (string default) 76 | "Internal helper to get eyes/tongue from STRING or DEFAULT." 77 | (if (not (stringp string)) default 78 | (let ((n (length string))) 79 | (cond ((= n 0) default) 80 | ((= n 1) (concat string " ")) 81 | ((= n 2) string) 82 | (t (substring string 0 2)))))) 83 | 84 | (defun cowsay--get-bubble () 85 | "Internal helper to get the speech or thought bubble." 86 | (let ((style cowsay-bubble-style)) 87 | (cond ((eq style 'say) "<>/\\\\/||\\") 88 | ((eq style 'think) "()()()()o") 89 | (t (error "Unknown cowsay-bubble-style: %S" style))))) 90 | 91 | (defun cowsay--get-system-cowpath () 92 | "Internal helper to parse COWPATH from the environment." 93 | (let ((whole (or (getenv "COWPATH") ""))) 94 | (split-string whole (regexp-quote path-separator) t))) 95 | 96 | (defun cowsay--parse-cow-file (file) 97 | "Internal helper to parse a cow from text FILE." 98 | (with-temp-buffer 99 | (insert-file-contents file) 100 | (goto-char (point-max)) 101 | (unless (bolp) (insert "\n")) 102 | (goto-char (point-min)) 103 | (while (re-search-forward "^#.*?\n" nil t) 104 | (replace-match "")) 105 | (goto-char (point-min)) 106 | (while (re-search-forward "^.*EOC.*\n" nil t) 107 | (replace-match "")) 108 | (goto-char (point-min)) 109 | (while (search-forward "\\@" nil t) (replace-match "@")) 110 | (goto-char (point-min)) 111 | ;; Replace two backslashes with one: 112 | (while (search-forward "\\\\" nil t) (replace-match "\\\\")) 113 | (goto-char (point-min)) 114 | (let ((re "\\${?\\([a-z]+\\)}?") ; Match a variable $eyes or ${eyes}. 115 | (start (point)) 116 | (parts '())) 117 | (while (re-search-forward re nil t) 118 | (let ((tag (match-string 1))) 119 | (when (member tag '("eyes" "thoughts" "tongue")) 120 | (let ((end (match-beginning 0))) 121 | (when (< start end) 122 | (push (buffer-substring start end) parts)) 123 | (push (intern tag) parts) 124 | (setq start (match-end 0)))))) 125 | (let ((end (point-max))) 126 | (when (< start end) 127 | (push (buffer-substring start end) parts))) 128 | (reverse parts)))) 129 | 130 | ;;;###autoload 131 | (defun cowsay-load-cow-file (file) 132 | "Load cow definition from text FILE." 133 | (interactive "fLoad .cow file: ") 134 | (let ((cow (file-name-sans-extension (file-name-nondirectory file)))) 135 | (let ((cow-pair (or (assoc cow cowsay-cows) 136 | (car (setq cowsay-cows (cons (cons cow "") 137 | cowsay-cows)))))) 138 | (setcdr cow-pair (cowsay--parse-cow-file file))) 139 | cow)) 140 | 141 | ;;;###autoload 142 | (defun cowsay-load-cows-directory (directory) 143 | "Load cow definitions from all matching files in DIRECTORY. 144 | 145 | Subdirectories are not visited." 146 | (interactive "DLoad .cow files from directory: ") 147 | (let ((cows (mapcar #'cowsay-load-cow-file 148 | (let ((case-fold-search t)) 149 | (directory-files directory t "^[a-z0-9-]+\\.cow$"))))) 150 | (when (called-interactively-p 'interactive) 151 | (message "Loaded %d cows" (length cows))) 152 | cows)) 153 | 154 | ;;;###autoload 155 | (defun cowsay-load-cows () 156 | "Load cow definitions from the usual files. 157 | 158 | Find all the cow files in `cowsay-directories' and add each cow 159 | to to `cowsay-cows'. Existing cows with the same name are 160 | overwritten from the files. Existing cows without matching files 161 | are left intact." 162 | (interactive) 163 | (let ((cowpath (append cowsay-directories (cowsay--get-system-cowpath)))) 164 | (dolist (dir (reverse cowpath)) 165 | (when (file-directory-p dir) 166 | (cowsay-load-cows-directory dir))))) 167 | 168 | (defun cowsay-list-cows () 169 | "Return a list of all loaded cow names as strings." 170 | (sort (mapcar #'car cowsay-cows) #'string<)) 171 | 172 | (defun cowsay--get-cow-parts (cow) 173 | "Internal helper to get the strings and symbols constituting COW. 174 | 175 | Returns nil if COW is not loaded." 176 | (cdr (assoc cow cowsay-cows))) 177 | 178 | (defun cowsay--get-default-cow () 179 | "Internal helper to get the name of the default cow." 180 | (let ((default nil)) 181 | (dolist (cow cowsay-preferred-cows) 182 | (setq default (or default (and (cowsay--get-cow-parts cow) cow)))) 183 | (let ((cow (and cowsay-cows (caar cowsay-cows)))) 184 | (setq default (or default (and (cowsay--get-cow-parts cow) cow)))) 185 | (or default (error "No cows have been loaded")))) 186 | 187 | (defun cowsay--insert-cow (cow thoughts) 188 | "Internal helper to insert COW with THOUGHTS at point." 189 | (let ((cow (or cow (cowsay--get-default-cow)))) 190 | (dolist (part (or (cowsay--get-cow-parts cow) 191 | (error "No such cow: %S" cow))) 192 | (insert (cond ((stringp part) part) 193 | ((eq part 'eyes) 194 | (cowsay--get-accessory cowsay-eyes "oo")) 195 | ((eq part 'tongue) 196 | (cowsay--get-accessory cowsay-tongue " ")) 197 | ((eq part 'thoughts) thoughts) 198 | (t (error "Cow %S is ill-defined" cow))))))) 199 | 200 | (defun cowsay--bubble-wrap-buffer (bubble) 201 | "Internal helper to insert BUBBLE around visible region of buffer." 202 | (unless (and (stringp bubble) (= 9 (length bubble))) 203 | (error "Bubble should be a 9-character string: %S" bubble)) 204 | (let ((l-only (substring bubble 0 1)) 205 | (r-only (substring bubble 1 2)) 206 | (l-top (substring bubble 2 3)) 207 | (r-top (substring bubble 3 4)) 208 | (l-bot (substring bubble 4 5)) 209 | (r-bot (substring bubble 5 6)) 210 | (l-side (substring bubble 6 7)) 211 | (r-side (substring bubble 7 8)) 212 | (longest 0)) 213 | (goto-char (point-max)) 214 | (unless (eql ?\n (char-before)) (insert "\n")) 215 | (let ((fill-column 70)) 216 | (fill-region (point-min) (point-max))) 217 | (goto-char (point-min)) 218 | (while (not (eobp)) 219 | (setq longest (max longest (- (point-at-eol) (point-at-bol)))) 220 | (goto-char (min (point-max) (1+ (point-at-eol))))) 221 | (goto-char (point-min)) 222 | (while (< (point-at-eol) (point-max)) 223 | (let* ((len (- (point-at-eol) (point-at-bol))) 224 | (top-p (= (point-at-bol) (point-min))) 225 | (bot-p (= (point-at-eol) (1- (point-max)))) 226 | (only-p (and top-p bot-p)) 227 | (padding (make-string (- longest len) ?\s))) 228 | (looking-at "^.*$") 229 | (let ((text (concat " " (match-string 0) padding " "))) 230 | (replace-match 231 | (cond (only-p (concat l-only text r-only)) 232 | (top-p (concat l-top text r-top)) 233 | (bot-p (concat l-bot text r-bot)) 234 | (t (concat l-side text r-side))) 235 | t t)) 236 | (goto-char (1+ (point-at-eol))))) 237 | (goto-char (point-min)) 238 | (insert " " (make-string (+ 1 longest 1) ?\_) " \n") 239 | (goto-char (point-max)) 240 | (insert " " (make-string (+ 1 longest 1) ?\-) " \n"))) 241 | 242 | (defun cowsay--string-to-string (string cow) 243 | "Internal helper to return a string with COW saying STRING." 244 | (with-temp-buffer 245 | (let* ((bubble (cowsay--get-bubble)) 246 | (thoughts (substring bubble 8 9))) 247 | (insert string) 248 | (cowsay--bubble-wrap-buffer bubble) 249 | (cowsay--insert-cow cow thoughts) 250 | (buffer-string)))) 251 | 252 | (defun cowsay--display-string (display-p string) 253 | "Internal helper to display STRING when DISPLAY-P is non-nil." 254 | (when display-p (display-message-or-buffer string "*Cow Say*")) 255 | string) 256 | 257 | (defun cowsay--prompt-for-cow (prompt-p) 258 | "Internal helper to select a cow in the minibuffer when PROMPT-P." 259 | (and prompt-p 260 | (let ((default (cowsay--get-default-cow))) 261 | (completing-read "Cow: " cowsay-cows nil t default 262 | 'cowsay-cow-history default)))) 263 | 264 | (defun cowsay--get-region-and-cow (prompt-p) 265 | "Internal helper to interactively get (START END COW) arguments. 266 | 267 | When PROMPT-P is non-nil, prompt for the cow." 268 | (if (not (use-region-p)) 269 | (error "The region is not active now") 270 | (list (region-beginning) 271 | (region-end) 272 | (cowsay--prompt-for-cow prompt-p)))) 273 | 274 | ;;;###autoload 275 | (defun cowsay-replace-region (start end &optional cow) 276 | "Replace the text between START and END with COW saying it. 277 | 278 | When called interactively and a prefix argument is given, ask 279 | which cow to use in the minibuffer." 280 | (interactive (cowsay--get-region-and-cow current-prefix-arg)) 281 | (let* ((string (buffer-substring start end)) 282 | (cartoon (cowsay--string-to-string string cow))) 283 | (delete-region start end) 284 | (insert cartoon))) 285 | 286 | ;;;###autoload 287 | (defun cowsay-region (start end &optional cow) 288 | "Show ASCII art with COW saying the text between START and END. 289 | 290 | When called interactively and a prefix argument is given, ask 291 | which cow to use in the minibuffer." 292 | (interactive (cowsay--get-region-and-cow current-prefix-arg)) 293 | (cowsay--display-string 294 | (called-interactively-p 'interactive) 295 | (cowsay--string-to-string (buffer-substring start end) cow))) 296 | 297 | ;;;###autoload 298 | (defun cowsay-string (string &optional cow) 299 | "Show ASCII art with poorly drawn COW saying STRING. 300 | 301 | When called interactively, ask for a string in the minibuffer. 302 | When a prefix argument is given, first ask which cow to use." 303 | (interactive 304 | (let ((cow (cowsay--prompt-for-cow current-prefix-arg))) 305 | (list (read-from-minibuffer "Cow say: ") cow))) 306 | (cowsay--display-string 307 | (called-interactively-p 'interactive) 308 | (cowsay--string-to-string string cow))) 309 | 310 | ;;;###autoload 311 | (defun cowsay-shell-command (command &optional cow) 312 | "Show ASCII art with COW saying the output of COMMAND. 313 | 314 | COMMAND is passed to the system shell as with `shell-command'. 315 | 316 | When called interactively, ask for the command in the minibuffer. 317 | When a prefix argument is given, first ask which cow to use." 318 | (interactive 319 | (let ((cow (cowsay--prompt-for-cow current-prefix-arg))) 320 | (list (read-from-minibuffer 321 | "Shell command: " nil nil nil 'shell-command-history) 322 | cow))) 323 | (cowsay--display-string 324 | (called-interactively-p 'interactive) 325 | (cowsay--string-to-string (shell-command-to-string command) cow))) 326 | 327 | (provide 'cowsay) 328 | 329 | ;;; cowsay.el ends here 330 | --------------------------------------------------------------------------------