Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

643 lines
20 KiB

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