aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Ingebrigtsen2020-12-21 18:53:32 +0100
committerLars Ingebrigtsen2020-12-21 18:53:40 +0100
commit87e422f1044068a4d27e5e4bfdbc664d9e4bbc43 (patch)
tree0a383378932cbe4b51a33299df2f3cbf9abaac20
parente352abeac16725c226c1246e3c83f71b8d3fa689 (diff)
downloademacs-87e422f1044068a4d27e5e4bfdbc664d9e4bbc43.tar.gz
emacs-87e422f1044068a4d27e5e4bfdbc664d9e4bbc43.zip
Beef up the Emacs string utility set a bit
* doc/lispref/strings.texi (Modifying Strings): Document them. * lisp/emacs-lisp/shortdoc.el (string): Add examples. * lisp/emacs-lisp/subr-x.el (string-clean-whitespace) (string-fill, string-limit, string-lines, slice-string): New functions.
-rw-r--r--doc/lispref/strings.texi38
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/emacs-lisp/shortdoc.el15
-rw-r--r--lisp/emacs-lisp/subr-x.el53
-rw-r--r--test/lisp/emacs-lisp/subr-x-tests.el26
5 files changed, 137 insertions, 0 deletions
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index 0f157c39d63..e4ca2617512 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -381,6 +381,44 @@ The default value of @var{separators} for @code{split-string}. Its
381usual value is @w{@code{"[ \f\t\n\r\v]+"}}. 381usual value is @w{@code{"[ \f\t\n\r\v]+"}}.
382@end defvar 382@end defvar
383 383
384@defun slice-string string regexp
385Split @var{string} into a list of strings on @var{regexp} boundaries.
386As opposed to @code{split-string}, the boundaries are included in the
387result set:
388
389@example
390(slice-string " two words " " +")
391 @result{} (" two" " words" " ")
392@end example
393@end defun
394
395@defun string-clean-whitespace string
396Clean up the whitespace in @var{string} by collapsing stretches of
397whitespace to a single space character, as well as removing all
398whitespace from the start and the end of @var{string}.
399@end defun
400
401@defun string-fill string length
402Attempt to Word-wrap @var{string} so that no lines are longer than
403@var{length}. Filling is done on whitespace boundaries only. If
404there are individual words that are longer than @var{length}, these
405will not be shortened.
406@end defun
407
408@defun string-limit string length
409Return a string that's shorter than @var{length}. If @var{string} is
410shorter than @var{length}, @var{string} is returned as is. If
411@var{length} is positive, return a substring of @var{string}
412consisting of the first @var{length} characters. If @var{length} is
413negative, return a string of the @var{-length} last characters
414instead.
415@end defun
416
417@defun string-lines string &optional omit-nulls
418Split @var{string} into a list of strings on newline boundaries. If
419@var{omit-nulls}, remove empty lines from the results.
420@end defun
421
384@node Modifying Strings 422@node Modifying Strings
385@section Modifying Strings 423@section Modifying Strings
386@cindex modifying strings 424@cindex modifying strings
diff --git a/etc/NEWS b/etc/NEWS
index 7411295e1b5..17c6ce61f94 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1441,6 +1441,11 @@ that makes it a valid button.
1441** Miscellaneous 1441** Miscellaneous
1442 1442
1443+++ 1443+++
1444*** A number of new string manipulation functions have been added.
1445'string-clean-whitespace', 'string-fill', 'string-limit',
1446'string-limit' and 'slice-string'.
1447
1448+++
1444*** New variable 'current-minibuffer-command'. 1449*** New variable 'current-minibuffer-command'.
1445This is like 'this-command', but it is bound recursively when entering 1450This is like 'this-command', but it is bound recursively when entering
1446the minibuffer. 1451the minibuffer.
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 37d6170fee5..8b11b57ff7f 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -139,10 +139,20 @@ There can be any number of :example/:result elements."
139 (substring 139 (substring
140 :eval (substring "foobar" 0 3) 140 :eval (substring "foobar" 0 3)
141 :eval (substring "foobar" 3)) 141 :eval (substring "foobar" 3))
142 (string-limit
143 :eval (string-limit "foobar" 3)
144 :eval (string-limit "foobar" -3)
145 :eval (string-limit "foobar" 10))
142 (split-string 146 (split-string
143 :eval (split-string "foo bar") 147 :eval (split-string "foo bar")
144 :eval (split-string "|foo|bar|" "|") 148 :eval (split-string "|foo|bar|" "|")
145 :eval (split-string "|foo|bar|" "|" t)) 149 :eval (split-string "|foo|bar|" "|" t))
150 (slice-string
151 :eval (slice-string "foo-bar" "-")
152 :eval (slice-string "foo-bar--zot-" "-+"))
153 (string-lines
154 :eval (string-lines "foo\n\nbar")
155 :eval (string-lines "foo\n\nbar" t))
146 (string-replace 156 (string-replace
147 :eval (string-replace "foo" "bar" "foozot")) 157 :eval (string-replace "foo" "bar" "foozot"))
148 (replace-regexp-in-string 158 (replace-regexp-in-string
@@ -167,6 +177,11 @@ There can be any number of :example/:result elements."
167 (string-remove-prefix 177 (string-remove-prefix
168 :no-manual t 178 :no-manual t
169 :eval (string-remove-prefix "foo" "foobar")) 179 :eval (string-remove-prefix "foo" "foobar"))
180 (string-clean-whitespace
181 :eval (string-clean-whitespace " foo bar "))
182 (string-fill
183 :eval (string-fill "Three short words" 12)
184 :eval (string-fill "Long-word" 3))
170 (reverse 185 (reverse
171 :eval (reverse "foo")) 186 :eval (reverse "foo"))
172 (substring-no-properties 187 (substring-no-properties
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index e6abb39ddc6..41a20795378 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -264,6 +264,59 @@ carriage return."
264 (substring string 0 (- (length string) (length suffix))) 264 (substring string 0 (- (length string) (length suffix)))
265 string)) 265 string))
266 266
267(defun string-clean-whitespace (string)
268 "Clean up whitespace in STRING.
269All sequences of whitespaces in STRING are collapsed into a
270single space character, and leading/trailing whitespace is
271removed."
272 (string-trim (replace-regexp-in-string "[ \t\n\r]+" " " string)))
273
274(defun string-fill (string length)
275 "Try to word-wrap STRING so that no lines are longer than LENGTH.
276Wrapping is done where there is whitespace. If there are
277individual words in STRING that are longer than LENGTH, the
278result will have lines that are longer than LENGTH."
279 (with-temp-buffer
280 (insert string)
281 (goto-char (point-min))
282 (let ((fill-column length)
283 (adaptive-fill-mode nil))
284 (fill-region (point-min) (point-max)))
285 (buffer-string)))
286
287(defun string-limit (string length)
288 "Return (up to) a LENGTH substring of STRING.
289If STRING is shorter or equal to LENGTH, the entire string is
290returned unchanged. If STRING is longer than LENGTH, and LENGTH
291is a positive number, return a a substring consisting of the
292first LENGTH characters of STRING. If LENGTH is negative, return
293a substring consisitng of thelast LENGTH characters of STRING."
294 (cond
295 ((<= (length string) length) string)
296 ((>= length 0) (substring string 0 length))
297 (t (substring string (+ (length string) length)))))
298
299(defun string-lines (string &optional omit-nulls)
300 "Split STRING into a list of lines.
301If OMIT-NULLS, empty lines will be removed from the results."
302 (split-string string "\n" omit-nulls))
303
304(defun slice-string (string regexp)
305 "Split STRING at REGEXP boundaries and return a list of slices.
306The boundaries that match REGEXP are not omitted from the results."
307 (let ((start-substring 0)
308 (start-search 0)
309 (result nil))
310 (save-match-data
311 (while (string-match regexp string start-search)
312 (if (zerop (match-beginning 0))
313 (setq start-search (match-end 0))
314 (push (substring string start-substring (match-beginning 0)) result)
315 (setq start-substring (match-beginning 0)
316 start-search (match-end 0))))
317 (push (substring string start-substring) result)
318 (nreverse result))))
319
267(defun replace-region-contents (beg end replace-fn 320(defun replace-region-contents (beg end replace-fn
268 &optional max-secs max-costs) 321 &optional max-secs max-costs)
269 "Replace the region between BEG and END using REPLACE-FN. 322 "Replace the region between BEG and END using REPLACE-FN.
diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el
index 9d14a5ab7ec..949bbb163eb 100644
--- a/test/lisp/emacs-lisp/subr-x-tests.el
+++ b/test/lisp/emacs-lisp/subr-x-tests.el
@@ -582,5 +582,31 @@
582 (should (equal (string-remove-suffix "a" "aa") "a")) 582 (should (equal (string-remove-suffix "a" "aa") "a"))
583 (should (equal (string-remove-suffix "a" "ba") "b"))) 583 (should (equal (string-remove-suffix "a" "ba") "b")))
584 584
585(ert-deftest subr-clean-whitespace ()
586 (should (equal (string-clean-whitespace " foo ") "foo"))
587 (should (equal (string-clean-whitespace " foo \n\t Bar") "foo Bar")))
588
589(ert-deftest subr-string-fill ()
590 (should (equal (string-fill "foo" 10) "foo"))
591 (should (equal (string-fill "foobar" 5) "foobar"))
592 (should (equal (string-fill "foo bar zot" 5) "foo\nbar\nzot"))
593 (should (equal (string-fill "foo bar zot" 7) "foo bar\nzot")))
594
595(ert-deftest subr-string-limit ()
596 (should (equal (string-limit "foo" 10) "foo"))
597 (should (equal (string-limit "foo" 2) "fo"))
598 (should (equal (string-limit "foo" -2) "oo"))
599 (should (equal (string-limit "foo" 0) "")))
600
601(ert-deftest subr-string-lines ()
602 (should (equal (string-lines "foo") '("foo")))
603 (should (equal (string-lines "foo \nbar") '("foo " "bar"))))
604
605(ert-deftest subr-slice-string ()
606 (should (equal (slice-string "foo-bar" "-") '("foo" "-bar")))
607 (should (equal (slice-string "foo-bar-" "-") '("foo" "-bar" "-")))
608 (should (equal (slice-string "-foo-bar-" "-") '("-foo" "-bar" "-")))
609 (should (equal (slice-string "ooo" "lala") '("ooo"))))
610
585(provide 'subr-x-tests) 611(provide 'subr-x-tests)
586;;; subr-x-tests.el ends here 612;;; subr-x-tests.el ends here