* doc/lispref/strings.texi (Creating Strings): Document it.
* lisp/emacs-lisp/shortdoc.el (string): Mention it.
* lisp/emacs-lisp/subr-x.el (string-glyph-split): New function.
properties removed.
@end defun
+@defun string-glyph-split string
+Special care has to be taken when handling strings that are meant to
+be displayed. @code{substring} and friends work on individual
+characters (i.e., code points), but things like emojis are often
+represented by @dfn{grapheme clusters}, which are basically a bunch of
+code points ``glued together'' in various ways. This function splits
+up strings like that into a list of strings, where each of these
+resulting strings represents a glyph that should be displayed as a
+unit.
+
+For instance, if you want to display a string without the first glyph,
+you can say:
+
+@example
+(apply #'insert (cdr (string-glyph-split string))))
+@end example
+@end defun
+
@defun concat &rest sequences
@cindex copying strings
@cindex concatenating strings
dealing with variable pitch fonts and glyphs that have widths that
aren't integer multiples of the default font.
++++
+** New function 'string-glyph-split'.
+This function splits a string into a list of strings representing
+separate glyphs. This takes into account combining characters and
+grapheme clusters.
+
---
** 'lookup-key' is more allowing when searching for extended menu items.
In Emacs 28.1, the behavior of 'lookup-key' was changed: when looking
:eval (split-string-and-unquote "foo \"bar zot\""))
(split-string-shell-command
:eval (split-string-shell-command "ls /tmp/'foo bar'"))
+ (string-glyph-split
+ :eval (string-glyph-split "Hello, πΌπ»π§πΌβπ€βπ§π»"))
(string-lines
:eval (string-lines "foo\n\nbar")
:eval (string-lines "foo\n\nbar" t))
(car (window-text-pixel-size
(current-buffer) (point-min) (point)))))
+;;;###autoload
+(defun string-glyph-split (string)
+ "Split STRING into a list of strings representing separate glyphs.
+This takes into account combining characters and grapheme clusters."
+ (let ((result nil)
+ (start 0)
+ comp)
+ (while (< start (length string))
+ (if (setq comp (find-composition-internal start nil string nil))
+ (progn
+ (push (substring string (car comp) (cadr comp)) result)
+ (setq start (cadr comp)))
+ (push (substring string start (1+ start)) result)
+ (setq start (1+ start))))
+ (nreverse result)))
+
(provide 'subr-x)
;;; subr-x.el ends here