s.el 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. ;;; s.el --- The long lost Emacs string manipulation library. -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2012-2022 Magnar Sveen
  3. ;; Author: Magnar Sveen <magnars@gmail.com>
  4. ;; Maintainer: Jason Milkins <jasonm23@gmail.com>
  5. ;; Version: 1.13.1
  6. ;; Keywords: strings
  7. ;; This program is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; The long lost Emacs string manipulation library.
  19. ;;
  20. ;; See documentation on https://github.com/magnars/s.el#functions
  21. ;;; Code:
  22. ;; Silence byte-compiler
  23. (defvar ucs-normalize-combining-chars) ; Defined in `ucs-normalize'
  24. (autoload 'slot-value "eieio")
  25. (defun s-trim-left (s)
  26. "Remove whitespace at the beginning of S."
  27. (declare (pure t) (side-effect-free t))
  28. (save-match-data
  29. (if (string-match "\\`[ \t\n\r]+" s)
  30. (replace-match "" t t s)
  31. s)))
  32. (defun s-trim-right (s)
  33. "Remove whitespace at the end of S."
  34. (declare (pure t) (side-effect-free t))
  35. (save-match-data
  36. (if (string-match "[ \t\n\r]+\\'" s)
  37. (replace-match "" t t s)
  38. s)))
  39. (defun s-trim (s)
  40. "Remove whitespace at the beginning and end of S."
  41. (declare (pure t) (side-effect-free t))
  42. (s-trim-left (s-trim-right s)))
  43. (defun s-collapse-whitespace (s)
  44. "Convert all adjacent whitespace characters to a single space."
  45. (declare (pure t) (side-effect-free t))
  46. (replace-regexp-in-string "[ \t\n\r]+" " " s))
  47. (defun s-unindent (s &optional bol)
  48. "Unindent S which has BOL (beginning of line) indicators.
  49. BOL will default to pipe. You can optionally supply your own."
  50. (declare (pure t) (side-effect-free t))
  51. (let ((case-fold-search nil)
  52. (bol (or bol "|")))
  53. (s-replace-regexp (concat "^[[:space:]]*" (regexp-quote bol)) "" s)))
  54. (defun s-split (separator s &optional omit-nulls)
  55. "Split S into substrings bounded by matches for regexp SEPARATOR.
  56. If OMIT-NULLS is non-nil, zero-length substrings are omitted.
  57. This is a simple wrapper around the built-in `split-string'."
  58. (declare (side-effect-free t))
  59. (save-match-data
  60. (split-string s separator omit-nulls)))
  61. (defun s-split-up-to (separator s n &optional omit-nulls)
  62. "Split S up to N times into substrings bounded by matches for regexp SEPARATOR.
  63. If OMIT-NULLS is non-nil, zero-length substrings are omitted.
  64. See also `s-split'."
  65. (declare (side-effect-free t))
  66. (save-match-data
  67. (let ((op 0)
  68. (r nil))
  69. (with-temp-buffer
  70. (insert s)
  71. (setq op (goto-char (point-min)))
  72. (while (and (re-search-forward separator nil t)
  73. (< 0 n))
  74. (let ((sub (buffer-substring op (match-beginning 0))))
  75. (unless (and omit-nulls
  76. (equal sub ""))
  77. (push sub r)))
  78. (setq op (goto-char (match-end 0)))
  79. (setq n (1- n)))
  80. (let ((sub (buffer-substring op (point-max))))
  81. (unless (and omit-nulls
  82. (equal sub ""))
  83. (push sub r))))
  84. (nreverse r))))
  85. (defun s-lines (s)
  86. "Splits S into a list of strings on newline characters."
  87. (declare (pure t) (side-effect-free t))
  88. (s-split "\\(\r\n\\|[\n\r]\\)" s))
  89. (defun s-join (separator strings)
  90. "Join all the strings in STRINGS with SEPARATOR in between."
  91. (declare (pure t) (side-effect-free t))
  92. (mapconcat 'identity strings separator))
  93. (defun s-concat (&rest strings)
  94. "Join all the string arguments into one string."
  95. (declare (pure t) (side-effect-free t))
  96. (apply 'concat strings))
  97. (defun s-prepend (prefix s)
  98. "Concatenate PREFIX and S."
  99. (declare (pure t) (side-effect-free t))
  100. (concat prefix s))
  101. (defun s-append (suffix s)
  102. "Concatenate S and SUFFIX."
  103. (declare (pure t) (side-effect-free t))
  104. (concat s suffix))
  105. (defun s-splice (needle n s)
  106. "Splice NEEDLE into S at position N.
  107. 0 is the beginning of the string, -1 is the end."
  108. (if (< n 0)
  109. (let ((left (substring s 0 (+ 1 n (length s))))
  110. (right (s-right (- -1 n) s)))
  111. (concat left needle right))
  112. (let ((left (s-left n s))
  113. (right (substring s n (length s))))
  114. (concat left needle right))))
  115. (defun s-repeat (num s)
  116. "Make a string of S repeated NUM times."
  117. (declare (pure t) (side-effect-free t))
  118. (let (ss)
  119. (while (> num 0)
  120. (setq ss (cons s ss))
  121. (setq num (1- num)))
  122. (apply 'concat ss)))
  123. (defun s-chop-suffix (suffix s)
  124. "Remove SUFFIX if it is at end of S."
  125. (declare (pure t) (side-effect-free t))
  126. (let ((pos (- (length suffix))))
  127. (if (and (>= (length s) (length suffix))
  128. (string= suffix (substring s pos)))
  129. (substring s 0 pos)
  130. s)))
  131. (defun s-chop-suffixes (suffixes s)
  132. "Remove SUFFIXES one by one in order, if they are at the end of S."
  133. (declare (pure t) (side-effect-free t))
  134. (while suffixes
  135. (setq s (s-chop-suffix (car suffixes) s))
  136. (setq suffixes (cdr suffixes)))
  137. s)
  138. (defun s-chop-prefix (prefix s)
  139. "Remove PREFIX if it is at the start of S."
  140. (declare (pure t) (side-effect-free t))
  141. (let ((pos (length prefix)))
  142. (if (and (>= (length s) (length prefix))
  143. (string= prefix (substring s 0 pos)))
  144. (substring s pos)
  145. s)))
  146. (defun s-chop-prefixes (prefixes s)
  147. "Remove PREFIXES one by one in order, if they are at the start of S."
  148. (declare (pure t) (side-effect-free t))
  149. (while prefixes
  150. (setq s (s-chop-prefix (car prefixes) s))
  151. (setq prefixes (cdr prefixes)))
  152. s)
  153. (defun s-shared-start (s1 s2)
  154. "Returns the longest prefix S1 and S2 have in common."
  155. (declare (pure t) (side-effect-free t))
  156. (let ((cmp (compare-strings s1 0 (length s1) s2 0 (length s2))))
  157. (if (eq cmp t) s1 (substring s1 0 (1- (abs cmp))))))
  158. (defun s-shared-end (s1 s2)
  159. "Returns the longest suffix S1 and S2 have in common."
  160. (declare (pure t) (side-effect-free t))
  161. (let* ((l1 (length s1))
  162. (l2 (length s2))
  163. (search-length (min l1 l2))
  164. (i 0))
  165. (while (and (< i search-length)
  166. (= (aref s1 (- l1 i 1)) (aref s2 (- l2 i 1))))
  167. (setq i (1+ i)))
  168. ;; If I is 0, then it means that there's no common suffix between
  169. ;; S1 and S2.
  170. ;;
  171. ;; However, since (substring s (- 0)) will return the whole
  172. ;; string, `s-shared-end' should simply return the empty string
  173. ;; when I is 0.
  174. (if (zerop i)
  175. ""
  176. (substring s1 (- i)))))
  177. (defun s-chomp (s)
  178. "Remove one trailing `\\n`, `\\r` or `\\r\\n` from S."
  179. (declare (pure t) (side-effect-free t))
  180. (s-chop-suffixes '("\n" "\r") s))
  181. (defun s-truncate (len s &optional ellipsis)
  182. "If S is longer than LEN, cut it down and add ELLIPSIS to the end.
  183. The resulting string, including ellipsis, will be LEN characters
  184. long.
  185. When not specified, ELLIPSIS defaults to ‘...’."
  186. (declare (pure t) (side-effect-free t))
  187. (unless ellipsis
  188. (setq ellipsis "..."))
  189. (if (> (length s) len)
  190. (format "%s%s" (substring s 0 (- len (length ellipsis))) ellipsis)
  191. s))
  192. (defun s-word-wrap (len s)
  193. "If S is longer than LEN, wrap the words with newlines."
  194. (declare (side-effect-free t))
  195. (save-match-data
  196. (with-temp-buffer
  197. (insert s)
  198. (let ((fill-column len))
  199. (fill-region (point-min) (point-max)))
  200. (buffer-substring (point-min) (point-max)))))
  201. (defun s-center (len s)
  202. "If S is shorter than LEN, pad it with spaces so it is centered."
  203. (declare (pure t) (side-effect-free t))
  204. (let ((extra (max 0 (- len (length s)))))
  205. (concat
  206. (make-string (ceiling extra 2) ?\s)
  207. s
  208. (make-string (floor extra 2) ?\s))))
  209. (defun s-pad-left (len padding s)
  210. "If S is shorter than LEN, pad it with PADDING on the left."
  211. (declare (pure t) (side-effect-free t))
  212. (let ((extra (max 0 (- len (length s)))))
  213. (concat (make-string extra (string-to-char padding))
  214. s)))
  215. (defun s-pad-right (len padding s)
  216. "If S is shorter than LEN, pad it with PADDING on the right."
  217. (declare (pure t) (side-effect-free t))
  218. (let ((extra (max 0 (- len (length s)))))
  219. (concat s
  220. (make-string extra (string-to-char padding)))))
  221. (defun s-left (len s)
  222. "Returns up to the LEN first chars of S."
  223. (declare (pure t) (side-effect-free t))
  224. (if (> (length s) len)
  225. (substring s 0 len)
  226. s))
  227. (defun s-right (len s)
  228. "Returns up to the LEN last chars of S."
  229. (declare (pure t) (side-effect-free t))
  230. (let ((l (length s)))
  231. (if (> l len)
  232. (substring s (- l len) l)
  233. s)))
  234. (defun s-chop-left (len s)
  235. "Remove the first LEN chars from S."
  236. (let ((l (length s)))
  237. (if (> l len)
  238. (substring s len l)
  239. "")))
  240. (defun s-chop-right (len s)
  241. "Remove the last LEN chars from S."
  242. (let ((l (length s)))
  243. (if (> l len)
  244. (substring s 0 (- l len))
  245. "")))
  246. (defun s-ends-with? (suffix s &optional ignore-case)
  247. "Does S end with SUFFIX?
  248. If IGNORE-CASE is non-nil, the comparison is done without paying
  249. attention to case differences.
  250. Alias: `s-suffix?'"
  251. (declare (pure t) (side-effect-free t))
  252. (let ((start-pos (- (length s) (length suffix))))
  253. (and (>= start-pos 0)
  254. (eq t (compare-strings suffix nil nil
  255. s start-pos nil ignore-case)))))
  256. (defun s-starts-with? (prefix s &optional ignore-case)
  257. "Does S start with PREFIX?
  258. If IGNORE-CASE is non-nil, the comparison is done without paying
  259. attention to case differences.
  260. Alias: `s-prefix?'. This is a simple wrapper around the built-in
  261. `string-prefix-p'."
  262. (declare (pure t) (side-effect-free t))
  263. (string-prefix-p prefix s ignore-case))
  264. (defun s--truthy? (val)
  265. (declare (pure t) (side-effect-free t))
  266. (not (null val)))
  267. (defun s-contains? (needle s &optional ignore-case)
  268. "Does S contain NEEDLE?
  269. If IGNORE-CASE is non-nil, the comparison is done without paying
  270. attention to case differences."
  271. (declare (pure t) (side-effect-free t))
  272. (let ((case-fold-search ignore-case))
  273. (s--truthy? (string-match-p (regexp-quote needle) s))))
  274. (defun s-equals? (s1 s2)
  275. "Is S1 equal to S2?
  276. This is a simple wrapper around the built-in `string-equal'."
  277. (declare (pure t) (side-effect-free t))
  278. (string-equal s1 s2))
  279. (defun s-less? (s1 s2)
  280. "Is S1 less than S2?
  281. This is a simple wrapper around the built-in `string-lessp'."
  282. (declare (pure t) (side-effect-free t))
  283. (string-lessp s1 s2))
  284. (defun s-matches? (regexp s &optional start)
  285. "Does REGEXP match S?
  286. If START is non-nil the search starts at that index.
  287. This is a simple wrapper around the built-in `string-match-p'."
  288. (declare (side-effect-free t))
  289. (s--truthy? (string-match-p regexp s start)))
  290. (defun s-blank? (s)
  291. "Is S nil or the empty string?"
  292. (declare (pure t) (side-effect-free t))
  293. (or (null s) (string= "" s)))
  294. (defun s-blank-str? (s)
  295. "Is S nil or the empty string or string only contains whitespace?"
  296. (declare (pure t) (side-effect-free t))
  297. (or (s-blank? s) (s-blank? (s-trim s))))
  298. (defun s-present? (s)
  299. "Is S anything but nil or the empty string?"
  300. (declare (pure t) (side-effect-free t))
  301. (not (s-blank? s)))
  302. (defun s-presence (s)
  303. "Return S if it's `s-present?', otherwise return nil."
  304. (declare (pure t) (side-effect-free t))
  305. (and (s-present? s) s))
  306. (defun s-lowercase? (s)
  307. "Are all the letters in S in lower case?"
  308. (declare (side-effect-free t))
  309. (let ((case-fold-search nil))
  310. (not (string-match-p "[[:upper:]]" s))))
  311. (defun s-uppercase? (s)
  312. "Are all the letters in S in upper case?"
  313. (declare (side-effect-free t))
  314. (let ((case-fold-search nil))
  315. (not (string-match-p "[[:lower:]]" s))))
  316. (defun s-mixedcase? (s)
  317. "Are there both lower case and upper case letters in S?"
  318. (let ((case-fold-search nil))
  319. (s--truthy?
  320. (and (string-match-p "[[:lower:]]" s)
  321. (string-match-p "[[:upper:]]" s)))))
  322. (defun s-capitalized? (s)
  323. "In S, is the first letter upper case, and all other letters lower case?"
  324. (declare (side-effect-free t))
  325. (let ((case-fold-search nil))
  326. (s--truthy?
  327. (string-match-p "^[[:upper:]][^[:upper:]]*$" s))))
  328. (defun s-numeric? (s)
  329. "Is S a number?"
  330. (declare (pure t) (side-effect-free t))
  331. (s--truthy?
  332. (string-match-p "^[0-9]+$" s)))
  333. (defun s-replace (old new s)
  334. "Replaces OLD with NEW in S."
  335. (declare (pure t) (side-effect-free t))
  336. (replace-regexp-in-string (regexp-quote old) new s t t))
  337. (defalias 's-replace-regexp 'replace-regexp-in-string)
  338. (defun s--aget (alist key)
  339. "Get the value of KEY in ALIST."
  340. (declare (pure t) (side-effect-free t))
  341. (cdr (assoc-string key alist)))
  342. (defun s-replace-all (replacements s)
  343. "REPLACEMENTS is a list of cons-cells. Each `car` is replaced with `cdr` in S."
  344. (declare (pure t) (side-effect-free t))
  345. (let ((case-fold-search nil))
  346. (replace-regexp-in-string (regexp-opt (mapcar 'car replacements))
  347. (lambda (it) (s--aget replacements it))
  348. s t t)))
  349. (defun s-downcase (s)
  350. "Convert S to lower case.
  351. This is a simple wrapper around the built-in `downcase'."
  352. (declare (side-effect-free t))
  353. (downcase s))
  354. (defun s-upcase (s)
  355. "Convert S to upper case.
  356. This is a simple wrapper around the built-in `upcase'."
  357. (declare (side-effect-free t))
  358. (upcase s))
  359. (defun s-capitalize (s)
  360. "Convert S first word's first character to upper and the rest to lower case."
  361. (declare (side-effect-free t))
  362. (concat (upcase (substring s 0 1)) (downcase (substring s 1))))
  363. (defun s-titleize (s)
  364. "Convert in S each word's first character to upper and the rest to lower case.
  365. This is a simple wrapper around the built-in `capitalize'."
  366. (declare (side-effect-free t))
  367. (capitalize s))
  368. (defmacro s-with (s form &rest more)
  369. "Threads S through the forms. Inserts S as the last item
  370. in the first form, making a list of it if it is not a list
  371. already. If there are more forms, inserts the first form as the
  372. last item in second form, etc."
  373. (declare (debug (form &rest [&or (function &rest form) fboundp])))
  374. (if (null more)
  375. (if (listp form)
  376. `(,(car form) ,@(cdr form) ,s)
  377. (list form s))
  378. `(s-with (s-with ,s ,form) ,@more)))
  379. (put 's-with 'lisp-indent-function 1)
  380. (defun s-index-of (needle s &optional ignore-case)
  381. "Returns first index of NEEDLE in S, or nil.
  382. If IGNORE-CASE is non-nil, the comparison is done without paying
  383. attention to case differences."
  384. (declare (pure t) (side-effect-free t))
  385. (let ((case-fold-search ignore-case))
  386. (string-match-p (regexp-quote needle) s)))
  387. (defun s-reverse (s)
  388. "Return the reverse of S."
  389. (declare (pure t) (side-effect-free t))
  390. (save-match-data
  391. (if (multibyte-string-p s)
  392. (let ((input (string-to-list s))
  393. output)
  394. (require 'ucs-normalize)
  395. (while input
  396. ;; Handle entire grapheme cluster as a single unit
  397. (let ((grapheme (list (pop input))))
  398. (while (memql (car input) ucs-normalize-combining-chars)
  399. (push (pop input) grapheme))
  400. (setq output (nconc (nreverse grapheme) output))))
  401. (concat output))
  402. (concat (nreverse (string-to-list s))))))
  403. (defun s-match-strings-all (regex string)
  404. "Return a list of matches for REGEX in STRING.
  405. Each element itself is a list of matches, as per
  406. `match-string'. Multiple matches at the same position will be
  407. ignored after the first."
  408. (declare (side-effect-free t))
  409. (save-match-data
  410. (let ((all-strings ())
  411. (i 0))
  412. (while (and (< i (length string))
  413. (string-match regex string i))
  414. (setq i (1+ (match-beginning 0)))
  415. (let (strings
  416. (num-matches (/ (length (match-data)) 2))
  417. (match 0))
  418. (while (/= match num-matches)
  419. (push (match-string match string) strings)
  420. (setq match (1+ match)))
  421. (push (nreverse strings) all-strings)))
  422. (nreverse all-strings))))
  423. (defun s-matched-positions-all (regexp string &optional subexp-depth)
  424. "Return a list of matched positions for REGEXP in STRING.
  425. SUBEXP-DEPTH is 0 by default."
  426. (declare (side-effect-free t))
  427. (if (null subexp-depth)
  428. (setq subexp-depth 0))
  429. (save-match-data
  430. (let ((pos 0) result)
  431. (while (and (string-match regexp string pos)
  432. (< pos (length string)))
  433. (push (cons (match-beginning subexp-depth) (match-end subexp-depth)) result)
  434. (setq pos (match-end 0)))
  435. (nreverse result))))
  436. (defun s-match (regexp s &optional start)
  437. "When the given expression matches the string, this function returns a list
  438. of the whole matching string and a string for each matched subexpressions.
  439. Subexpressions that didn't match are represented by nil elements
  440. in the list, except that non-matching subexpressions at the end
  441. of REGEXP might not appear at all in the list. That is, the
  442. returned list can be shorter than the number of subexpressions in
  443. REGEXP plus one. If REGEXP did not match the returned value is
  444. an empty list (nil).
  445. When START is non-nil the search will start at that index."
  446. (declare (side-effect-free t))
  447. (save-match-data
  448. (if (string-match regexp s start)
  449. (let ((match-data-list (match-data))
  450. result)
  451. (while match-data-list
  452. (let* ((beg (car match-data-list))
  453. (end (cadr match-data-list))
  454. (subs (if (and beg end) (substring s beg end) nil)))
  455. (setq result (cons subs result))
  456. (setq match-data-list
  457. (cddr match-data-list))))
  458. (nreverse result)))))
  459. (defun s-slice-at (regexp s)
  460. "Slices S up at every index matching REGEXP."
  461. (declare (side-effect-free t))
  462. (if (s-blank? s)
  463. (list s)
  464. (let (ss)
  465. (while (not (s-blank? s))
  466. (save-match-data
  467. (let ((i (string-match regexp s 1)))
  468. (if i
  469. (setq ss (cons (substring s 0 i) ss)
  470. s (substring s i))
  471. (setq ss (cons s ss)
  472. s "")))))
  473. (nreverse ss))))
  474. (defun s-split-words (s)
  475. "Split S into list of words."
  476. (declare (side-effect-free t))
  477. (s-split
  478. "[^[:word:]0-9]+"
  479. (let ((case-fold-search nil))
  480. (replace-regexp-in-string
  481. "\\([[:lower:]]\\)\\([[:upper:]]\\)" "\\1 \\2"
  482. (replace-regexp-in-string "\\([[:upper:]]\\)\\([[:upper:]][0-9[:lower:]]\\)" "\\1 \\2" s)))
  483. t))
  484. (defun s--mapcar-head (fn-head fn-rest list)
  485. "Like MAPCAR, but applies a different function to the first element."
  486. (if list
  487. (cons (funcall fn-head (car list)) (mapcar fn-rest (cdr list)))))
  488. (defun s-lower-camel-case (s)
  489. "Convert S to lowerCamelCase."
  490. (declare (side-effect-free t))
  491. (s-join "" (s--mapcar-head 'downcase 'capitalize (s-split-words s))))
  492. (defun s-upper-camel-case (s)
  493. "Convert S to UpperCamelCase."
  494. (declare (side-effect-free t))
  495. (s-join "" (mapcar 'capitalize (s-split-words s))))
  496. (defun s-snake-case (s)
  497. "Convert S to snake_case."
  498. (declare (side-effect-free t))
  499. (s-join "_" (mapcar 'downcase (s-split-words s))))
  500. (defun s-dashed-words (s)
  501. "Convert S to dashed-words."
  502. (declare (side-effect-free t))
  503. (s-join "-" (mapcar 'downcase (s-split-words s))))
  504. (defun s-spaced-words (s)
  505. "Convert S to spaced words."
  506. (declare (side-effect-free t))
  507. (s-join " " (s-split-words s)))
  508. (defun s-capitalized-words (s)
  509. "Convert S to Capitalized words."
  510. (declare (side-effect-free t))
  511. (let ((words (s-split-words s)))
  512. (s-join " " (cons (capitalize (car words)) (mapcar 'downcase (cdr words))))))
  513. (defun s-titleized-words (s)
  514. "Convert S to Titleized Words."
  515. (declare (side-effect-free t))
  516. (s-join " " (mapcar 's-titleize (s-split-words s))))
  517. (defun s-word-initials (s)
  518. "Convert S to its initials."
  519. (declare (side-effect-free t))
  520. (s-join "" (mapcar (lambda (ss) (substring ss 0 1))
  521. (s-split-words s))))
  522. ;; Errors for s-format
  523. (progn
  524. (put 's-format-resolve
  525. 'error-conditions
  526. '(error s-format s-format-resolve))
  527. (put 's-format-resolve
  528. 'error-message
  529. "Cannot resolve a template to values"))
  530. (defun s-format (template replacer &optional extra)
  531. "Format TEMPLATE with the function REPLACER.
  532. REPLACER takes an argument of the format variable and optionally
  533. an extra argument which is the EXTRA value from the call to
  534. `s-format'.
  535. Several standard `s-format' helper functions are recognized and
  536. adapted for this:
  537. (s-format \"${name}\" \\='gethash hash-table)
  538. (s-format \"${name}\" \\='aget alist)
  539. (s-format \"$0\" \\='elt sequence)
  540. The REPLACER function may be used to do any other kind of
  541. transformation."
  542. (let ((saved-match-data (match-data)))
  543. (unwind-protect
  544. (replace-regexp-in-string
  545. "\\$\\({\\([^}]+\\)}\\|[0-9]+\\)"
  546. (lambda (md)
  547. (let ((var
  548. (let ((m (match-string 2 md)))
  549. (if m m
  550. (string-to-number (match-string 1 md)))))
  551. (replacer-match-data (match-data)))
  552. (unwind-protect
  553. (let ((v
  554. (cond
  555. ((eq replacer 'gethash)
  556. (funcall replacer var extra))
  557. ((eq replacer 'aget)
  558. (funcall 's--aget extra var))
  559. ((eq replacer 'elt)
  560. (funcall replacer extra var))
  561. ((eq replacer 'oref)
  562. (funcall #'slot-value extra (intern var)))
  563. (t
  564. (set-match-data saved-match-data)
  565. (if extra
  566. (funcall replacer var extra)
  567. (funcall replacer var))))))
  568. (if v (format "%s" v) (signal 's-format-resolve md)))
  569. (set-match-data replacer-match-data))))
  570. template
  571. ;; Need literal to make sure it works
  572. t t)
  573. (set-match-data saved-match-data))))
  574. (defvar s-lex-value-as-lisp nil
  575. "If `t' interpolate lisp values as lisp.
  576. `s-lex-format' inserts values with (format \"%S\").")
  577. (defun s-lex-fmt|expand (fmt)
  578. "Expand FMT into lisp."
  579. (declare (side-effect-free t))
  580. (list 's-format fmt (quote 'aget)
  581. (append '(list)
  582. (mapcar
  583. (lambda (matches)
  584. (list
  585. 'cons
  586. (cadr matches)
  587. `(format
  588. (if s-lex-value-as-lisp "%S" "%s")
  589. ,(intern (cadr matches)))))
  590. (s-match-strings-all "${\\([^}]+\\)}" fmt)))))
  591. (defmacro s-lex-format (format-str)
  592. "`s-format` with the current environment.
  593. FORMAT-STR may use the `s-format' variable reference to refer to
  594. any variable:
  595. (let ((x 1))
  596. (s-lex-format \"x is: ${x}\"))
  597. The values of the variables are interpolated with \"%s\" unless
  598. the variable `s-lex-value-as-lisp' is `t' and then they are
  599. interpolated with \"%S\"."
  600. (declare (debug (form)))
  601. (s-lex-fmt|expand format-str))
  602. (defun s-count-matches (regexp s &optional start end)
  603. "Count occurrences of `regexp' in `s'.
  604. `start', inclusive, and `end', exclusive, delimit the part of `s' to
  605. match. `start' and `end' are both indexed starting at 1; the initial
  606. character in `s' is index 1.
  607. This function starts looking for the next match from the end of the
  608. previous match. Hence, it ignores matches that overlap a previously
  609. found match. To count overlapping matches, use
  610. `s-count-matches-all'."
  611. (declare (side-effect-free t))
  612. (save-match-data
  613. (with-temp-buffer
  614. (insert s)
  615. (goto-char (point-min))
  616. (count-matches regexp (or start 1) (or end (point-max))))))
  617. (defun s-count-matches-all (regexp s &optional start end)
  618. "Count occurrences of `regexp' in `s'.
  619. `start', inclusive, and `end', exclusive, delimit the part of `s' to
  620. match. `start' and `end' are both indexed starting at 1; the initial
  621. character in `s' is index 1.
  622. This function starts looking for the next match from the second
  623. character of the previous match. Hence, it counts matches that
  624. overlap a previously found match. To ignore matches that overlap a
  625. previously found match, use `s-count-matches'."
  626. (declare (side-effect-free t))
  627. (let* ((anchored-regexp (format "^%s" regexp))
  628. (match-count 0)
  629. (i 0)
  630. (narrowed-s (substring s (if start (1- start) 0)
  631. (when end (1- end)))))
  632. (save-match-data
  633. (while (< i (length narrowed-s))
  634. (when (s-matches? anchored-regexp (substring narrowed-s i))
  635. (setq match-count (1+ match-count)))
  636. (setq i (1+ i))))
  637. match-count))
  638. (defun s-wrap (s prefix &optional suffix)
  639. "Wrap string S with PREFIX and optionally SUFFIX.
  640. Return string S with PREFIX prepended. If SUFFIX is present, it
  641. is appended, otherwise PREFIX is used as both prefix and
  642. suffix."
  643. (declare (pure t) (side-effect-free t))
  644. (concat prefix s (or suffix prefix)))
  645. ;;; Aliases
  646. (defalias 's-blank-p 's-blank?)
  647. (defalias 's-blank-str-p 's-blank-str?)
  648. (defalias 's-capitalized-p 's-capitalized?)
  649. (defalias 's-contains-p 's-contains?)
  650. (defalias 's-ends-with-p 's-ends-with?)
  651. (defalias 's-equals-p 's-equals?)
  652. (defalias 's-less-p 's-less?)
  653. (defalias 's-lowercase-p 's-lowercase?)
  654. (defalias 's-matches-p 's-matches?)
  655. (defalias 's-mixedcase-p 's-mixedcase?)
  656. (defalias 's-numeric-p 's-numeric?)
  657. (defalias 's-prefix-p 's-starts-with?)
  658. (defalias 's-prefix? 's-starts-with?)
  659. (defalias 's-present-p 's-present?)
  660. (defalias 's-starts-with-p 's-starts-with?)
  661. (defalias 's-suffix-p 's-ends-with?)
  662. (defalias 's-suffix? 's-ends-with?)
  663. (defalias 's-uppercase-p 's-uppercase?)
  664. (provide 's)
  665. ;;; s.el ends here