Function: describe-char

describe-char is an autoloaded, interactive and byte-compiled function defined in descr-text.el.gz.

Signature

(describe-char POS &optional BUFFER)

Documentation

Describe position POS (interactively, point) and the char after POS.

POS is taken to be in BUFFER, or the current buffer if BUFFER is nil. The information is displayed in buffer *Help*.

The position information includes POS; the total size of BUFFER; the region limits, if narrowed; the column number; and the horizontal scroll amount, if the buffer is horizontally scrolled.

The character information includes:
 its codepoint;
 its charset (see char-charset), overridden by the charset text
   property at POS, if any;
 the codepoint of the character in the above charset;
 the character's script (as defined by char-script-table)
 the character's syntax, as produced by syntax-after
   and internal-describe-syntax-value;
 its category (see char-category-set and describe-char-categories);
 how to input the character using the keyboard and input methods;
 how the character is encoded in BUFFER and in BUFFER's file;
 the font and font glyphs used to display the character;
 the composition information for displaying the character (if relevant);
 the character's canonical name and other properties defined by the
   Unicode Data Base;
 and widgets, buttons, overlays, and text properties relevant to POS.

This function has :around advice: evil--in-emacs-state.

View in manual

Probably introduced at or before Emacs version 22.1.

Key Bindings

Source Code

;; Defined in /usr/src/emacs/lisp/descr-text.el.gz
;;;###autoload
(defun describe-char (pos &optional buffer)
  "Describe position POS (interactively, point) and the char after POS.
POS is taken to be in BUFFER, or the current buffer if BUFFER is nil.
The information is displayed in buffer `*Help*'.

The position information includes POS; the total size of BUFFER; the
region limits, if narrowed; the column number; and the horizontal
scroll amount, if the buffer is horizontally scrolled.

The character information includes:
 its codepoint;
 its charset (see `char-charset'), overridden by the `charset' text
   property at POS, if any;
 the codepoint of the character in the above charset;
 the character's script (as defined by `char-script-table')
 the character's syntax, as produced by `syntax-after'
   and `internal-describe-syntax-value';
 its category (see `char-category-set' and `describe-char-categories');
 how to input the character using the keyboard and input methods;
 how the character is encoded in BUFFER and in BUFFER's file;
 the font and font glyphs used to display the character;
 the composition information for displaying the character (if relevant);
 the character's canonical name and other properties defined by the
   Unicode Data Base;
 and widgets, buttons, overlays, and text properties relevant to POS."
  (interactive "d")
  (unless (buffer-live-p buffer) (setq buffer (current-buffer)))
  (let ((src-buf (current-buffer)))
    (set-buffer buffer)
    (if (>= pos (point-max))
        (error "No character follows specified position"))
    (let* ((char (char-after pos))
           (eight-bit-p (and (not enable-multibyte-characters) (>= char 128)))
           (charset (if eight-bit-p 'eight-bit
                      (or (get-text-property pos 'charset)
                          (char-charset char))))
           (composition (find-composition pos nil nil t))
           (component-chars nil)
           (display-table (or (window-display-table)
                              buffer-display-table
                              standard-display-table))
           (composition-string nil)
           (disp-vector (and display-table (aref display-table char)))
           (multibyte-p enable-multibyte-characters)
           (overlays (mapcar #'overlay-properties (overlays-at pos)))
           (char-description (if (< char 128)
                                 (single-key-description char)
                               (string (if (not multibyte-p)
                                           (decode-char 'eight-bit char)
                                         char))))
           (text-props-desc
            (let ((tmp-buf (generate-new-buffer " *text-props*")))
              (unwind-protect
                  (progn
                    (describe-text-properties pos tmp-buf)
                    (with-current-buffer tmp-buf (buffer-string)))
                (kill-buffer tmp-buf))))
           item-list max-width code glyph-code trivial-p)

      (if multibyte-p
          (or (setq code (encode-char char charset))
              (setq charset (char-charset char)
                    code (encode-char char charset)))
        (setq code char))
      (cond
       ;; Append a PDF character to left-to-right directional
       ;; embeddings and overrides, to prevent potential messup of the
       ;; following text.
       ((memq char '(?\x202a ?\x202d))
	(setq char-description
	      (concat char-description
		      (propertize (string ?\x202c) 'invisible t))))
       ;; Append a PDF character followed by LRM to right-to-left
       ;; directional embeddings and overrides, to prevent potential
       ;; messup of the following numerical text.
       ((memq char '(?\x202b ?\x202e))
	(setq char-description
	      (concat char-description
		      (propertize (string ?\x202c ?\x200e) 'invisible t))))
       ;; Append a PDI character to directional isolate initiators, to
       ;; prevent potential messup of the following numerical text
       ((memq char '(?\x2066 ?\x2067 ?\x2068))
	(setq char-description
	      (concat char-description
		      (propertize (string ?\x2069) 'invisible t))))
       ;; Append a LRM character to any strong character to avoid
       ;; messing up the numerical codepoint.
       ((memq (get-char-code-property char 'bidi-class) '(R AL))
	(setq char-description
	      (concat char-description
		      (propertize (string ?\x200e) 'invisible t)))))
      (when composition
        ;; When the composition is trivial (i.e. composed only with the
        ;; current character itself without any alternate characters),
        ;; we don't show the composition information.  Otherwise, store
        ;; two descriptive strings in the first two elements of
        ;; COMPOSITION.
        (or (catch 'tag
              (let ((from (car composition))
                    (to (nth 1 composition))
                    (components (nth 2 composition))
                    ch)
                (if (and (vectorp components) (vectorp (aref components 0)))
                    (let ((idx (- pos from))
                          (nglyphs (lgstring-glyph-len components))
                          (i 0) j glyph glyph-from)
                      ;; COMPONENTS is a gstring.  Find a grapheme
                      ;; cluster containing the current character.
                      (while (and (< i nglyphs)
                                  (setq glyph (lgstring-glyph components i))
                                  (< (lglyph-to glyph) idx))
                        (setq i (1+ i)))
                      (if (or (not glyph) (= i nglyphs))
                          ;; The composition is broken.
                          (throw 'tag nil))
                      (setq glyph-from (lglyph-from glyph)
                            to (+ from (lglyph-to glyph) 1)
                            from (+ from glyph-from)
                            j i)
                      (while (and (< j nglyphs)
                                  (setq glyph (lgstring-glyph components j))
                                  (= (lglyph-from glyph) glyph-from))
                        (setq j (1+ j)))
                      (if (and (= to (1+ from))
                               (= i (1- j))
                               (setq glyph (lgstring-glyph components i))
                               (= char (lglyph-char glyph))
                               (setq trivial-p t))
                          ;; The composition is trivial.
                          (throw 'tag nil))
                      (nconc composition (list i (1- j))))
                  (dotimes (i (length components))
                    (if (integerp (setq ch (aref components i)))
                        (push (cons ch (describe-char-display pos ch))
                              component-chars)))
                  (setq component-chars (nreverse component-chars)))
                (if (< from pos)
                    (if (< (1+ pos) to)
                        (setcar composition
                                (concat
                                 " with the surrounding characters \""
                                 (mapconcat #'describe-char-padded-string
                                            (buffer-substring from pos))
                                 "\" and \""
                                 (mapconcat #'describe-char-padded-string
                                            (buffer-substring (1+ pos) to))
                                 "\""))
                      (setcar composition
                              (concat
                               " with the preceding character(s) \""
                               (mapconcat #'describe-char-padded-string
                                          (buffer-substring from pos))
                               "\"")))
                  (if (< (1+ pos) to)
                      (setcar composition
                              (concat
                               " with the following character(s) \""
                               (mapconcat #'describe-char-padded-string
                                          (buffer-substring (1+ pos) to))
                               "\""))
                    (setcar composition nil)))
                (setcar (cdr composition)
                        (format "composed to form \"%s\" (see below)"
                                (setq composition-string
                                      (buffer-substring from to))))))
            ;; For "trivial" compositions, such as ligatures of ASCII
            ;; characters, at least show the correct font glyph number.
            (setq glyph-code (if (and composition
                                      trivial-p
                                      (display-graphic-p (selected-frame)))
                                 (composition-find-pos-glyph composition pos))
                  composition nil)))

      (setq item-list
            `(("position"
               ,(let* ((beg      (point-min))
                       (end      (point-max))
                       (total    (buffer-size))
                       (percent  (round (* 100.0 (1- pos)) (max total 1)))
                       (hscroll  (if (= (window-hscroll) 0)
                                     ""
                                   (format ", Hscroll: %d" (window-hscroll))))
                       (col      (current-column)))
                  (if (or (/= beg 1)  (/= end (1+ total)))
                      (format "%d of %d (%d%%), restriction: <%d-%d>, column: %d%s"
                              pos total percent beg end col hscroll)
                    (if (= pos end)
                        (format "%d of %d (EOB), column: %d%s" pos total col hscroll)
                      (format "%d of %d (%d%%), column: %d%s"
                              pos total percent col hscroll)))))
              ("character"
               ,(format "%s (displayed as %s) (codepoint %d, #o%o, #x%x)"
			char-description
                        (apply #'propertize char-description
                               (text-properties-at pos))
                        char char char))
              ("charset"
               ,`(insert-text-button
                  ,(symbol-name charset)
                  'type 'help-character-set 'help-args '(,charset))
               ,(format "(%s)" (charset-description charset)))
              ("code point in charset"
               ,(let ((str (if (integerp code)
                               (format (if (< code 256) "0x%02X" "0x%04X")
                                       code)
                             (format "0x%04X%04X" (car code) (cdr code)))))
                  (if (<= (charset-dimension charset) 2)
                      `(insert-text-button
                        ,str
                        'action (lambda (&rest ignore)
                                  (list-charset-chars ',charset)
                                  (with-selected-window
                                      (get-buffer-window "*Character List*" 0)
                                    (goto-char (point-min))
                                    (forward-line 2) ;Skip the header.
                                    (let ((case-fold-search nil))
                                      (if (search-forward
                                           ,(char-to-string char) nil t)
                                          (goto-char (match-beginning 0))))))
                        'follow-link t
                        'help-echo
                        "mouse-2, RET: show this character in its character set")
                    str)))
              ,@(let ((script (aref char-script-table char)))
                  (if script
                      (list (list "script" (symbol-name script)))))
              ("syntax"
               ,(let ((syntax (syntax-after pos)))
                  (with-temp-buffer
                    (internal-describe-syntax-value syntax)
                    (buffer-string))))
              ("category"
               ,@(if (not eight-bit-p)
                     (let ((category-set (char-category-set char)))
                       (if category-set
                           (describe-char-categories category-set)
                         '("-- none --")))))
              ("to input"
               ,@(if (not eight-bit-p)
                     (let ((key-list (and (eq input-method-function
                                              'quail-input-method)
                                          (quail-find-key char))))
                       (if (consp key-list)
                           (list "type"
                                 (concat "\""
                                         (mapconcat #'identity
                                                    key-list "\" or \"")
                                         "\"")
                                 "with"
                                 `(insert-text-button
                                   ,current-input-method
                                   'type 'help-input-method
                                   'help-args '(,current-input-method))
				 "input method")
			 (list
                          (let* ((names (ucs-names))
                                 (name
                                  (or (when (= char ?\a)
				       ;; Special case for "BELL" which is
				       ;; apparently the only char which
				       ;; doesn't have a new name and whose
				       ;; old-name is shadowed by a newer char
				       ;; with that name (bug#25641).
				       "BELL (BEL)")
                                      (get-char-code-property char 'name)
                                      (get-char-code-property char 'old-name))))
                            (if (and name (gethash name names))
                                (format
                                 "type \"C-x 8 RET %x\" or \"C-x 8 RET %s\""
                                 char name)
                              (format "type \"C-x 8 RET %x\"" char))))))))
              ("buffer code"
               ,(if multibyte-p
                    (encoded-string-description
                     (encode-coding-string (char-to-string char)
                                           'emacs-internal)
                     nil)
                  (format "#x%02X" char)))
              ("file code"
               ,@(if multibyte-p
                     (let* ((coding buffer-file-coding-system)
                            (encoded
                             (and coding
                                  (encode-coding-char char coding charset))))
                       (if encoded
                           (list (encoded-string-description encoded coding)
                                 (format "(encoded by coding system %S)"
                                         coding))
                         (list "not encodable by coding system"
                               (symbol-name coding))))
                   (list (format "#x%02X" char))))
              ("display"
               ,(cond
                 (disp-vector
                  (setq disp-vector (copy-sequence disp-vector))
                  (dotimes (i (length disp-vector))
                    (aset disp-vector i
                          (cons (aref disp-vector i)
                                (describe-char-display
                                 pos (glyph-char (aref disp-vector i))))))
                  (format "by display table entry [%s] (see below)"
                          (mapconcat
                           (lambda (x)
                             (format "?%c" (glyph-char (car x))))
                           disp-vector " ")))
                 (composition
                  (cadr composition))
                 (t
                  (let ((display (describe-char-display pos char glyph-code)))
                    (if (display-graphic-p (selected-frame))
                        (if display
                            (concat "by this font (glyph code):\n    " display)
                          "no font available")
                      (if display
                          (format "terminal code %s" display)
                        "not encodable for terminal"))))))
              ,@(when-let* ((composition-name
                             (and composition-string
                                  (eq (aref char-script-table char) 'emoji)
                                  (emoji-describe composition-string))))
                  (list (list "composition name" composition-name)))
              ,@(let ((face
                       (if (not (or disp-vector composition))
                           (cond
                            ((and show-trailing-whitespace
                                  (save-excursion (goto-char pos)
                                                  (looking-at-p "[ \t]+$")))
                             'trailing-whitespace)
                            ((and nobreak-char-display char
                                  (> char 127)
                                  (eq (get-char-code-property char 'general-category) 'Zs))
                             'nobreak-space)
                            ((and nobreak-char-display char
				  (memq char '(#xad #x2010 #x2011)))
                             'escape-glyph)
                            ((and (< char 32) (not (memq char '(9 10))))
                             'escape-glyph)))))
                  (if face (list (list "hardcoded face"
                                       `(insert-text-button ;FIXME: Wrap in lambda!
                                         ,(symbol-name face)
                                         'type 'help-face
                                         'help-args '(,face))))))
              ,@(if (not eight-bit-p)
                    (let ((unicodedata (describe-char-unicode-data char)))
                      (if unicodedata
                          (cons (list "Unicode data" "") unicodedata))))))
      (setq max-width (apply #'max (mapcar (lambda (x)
                                            (if (cadr x) (length (car x)) 0))
                                          item-list)))
      (set-buffer src-buf)
      (help-setup-xref (list 'describe-char pos buffer)
                       (called-interactively-p 'interactive))
      (with-help-window (help-buffer)
        (with-current-buffer standard-output
          (let ((formatter (format "%%%ds:" max-width)))
            (dolist (elt item-list)
              (when (cadr elt)
                (insert (format formatter (car elt)))
                (dolist (clm (cdr elt))
		  (cond ((eq (car-safe clm) 'insert-text-button)
			 (insert " ")
			 (eval clm t))
			((not (zerop (length clm)))
			 (insert " " clm))))
                (insert "\n"))))

          (when overlays
            (save-excursion
              (goto-char (point-min))
              (re-search-forward "(displayed as ")
              (let ((end (+ (point) (length char-description))))
                (mapc (lambda (props)
                        (let ((o (make-overlay (point) end)))
                          (while props
                            (overlay-put o (car props) (nth 1 props))
                            (setq props (cddr props)))))
                      overlays))))

          (when disp-vector
            (insert
             "\nThe display table entry is displayed by ")
            (insert "these fonts (glyph codes):\n")
            (dotimes (i (length disp-vector))
              (insert (glyph-char (car (aref disp-vector i))) ?:
                      (propertize " " 'display '(space :align-to 5))
                      (or (cdr (aref disp-vector i)) "-- no font --")
                      "\n")
              (let ((face (glyph-face (car (aref disp-vector i)))))
                (when face
                  (insert (propertize " " 'display '(space :align-to 5))
                          "face: ")
                  (insert (format-message "`%s'\n" face))))))

          (when composition
            (insert "\nComposed")
            (if (car composition)
                (insert (car composition)))
            (if (and (vectorp (nth 2 composition))
                     (vectorp (aref (nth 2 composition) 0)))
                (let* ((gstring (nth 2 composition))
                       (font (lgstring-font gstring))
                       (from (nth 3 composition))
                       (to (nth 4 composition))
                       glyph)
                  (if (fontp font)
                      ;; GUI frame: show composition in terms of
                      ;; font glyphs and characters.
                      (progn
                        (insert " using this font:\n  "
                                (symbol-name (font-get font :type))
                                ?:
                                (aref (query-font font) 0)
                                "\nby these glyphs:\n")
                        (while (and (<= from to)
                                    (setq glyph (lgstring-glyph gstring from)))
                          (insert (format "  %S\n" glyph))
                          (setq from (1+ from)))
                        (when (and (stringp (car composition))
                                   (string-match "\"\\([^\"]+\\)\"" (car composition)))
                          (insert "with these character(s):\n")
                          (let ((chars (match-string 1 (car composition))))
                            (dotimes (i (length chars))
                              (let ((char (aref chars i)))
                                (insert (format "  %s (#x%x) %s\n"
                                                (describe-char-padded-string char) char
                                                (get-char-code-property
                                                 char 'name))))))))
                    ;; TTY frame: show composition in terms of characters.
                    (insert " by these characters:\n")
                    (while (and (<= from to)
                                (setq glyph (lgstring-glyph gstring from)))
                      (insert (format " %c (#x%x) %s\n"
                                      (lglyph-char glyph) (lglyph-char glyph)
                                      (get-char-code-property
                                       (lglyph-char glyph) 'name)))
                      (setq from (1+ from)))))
              (insert " by the rule:\n\t(")
              (let ((first t))
                (mapc (lambda (x)
                        (if first (setq first nil)
                          (insert " "))
                        (if (consp x) (insert (format "%S" x))
                          (if (= x ?\t) (insert (single-key-description x))
                            (insert ??)
                            (insert (describe-char-padded-string x)))))
                      (nth 2 composition)))
              (insert  ")\nThe component character(s) are displayed by ")
              (if (display-graphic-p (selected-frame))
                  (progn
                    (insert "these fonts (glyph codes):")
                    (dolist (elt component-chars)
                      (if (/= (car elt) ?\t)
                          (insert "\n "
                                  (describe-char-padded-string (car elt))
                                  ?:
                                  (propertize " "
                                              'display '(space :align-to 5))
                                  (or (cdr elt) "-- no font --")))))
                (insert "these terminal codes:")
                (dolist (elt component-chars)
                  (insert "\n  " (car elt) ":"
                          (propertize " " 'display '(space :align-to 4))
                          (or (cdr elt) "-- not encodable --"))))
              (insert (substitute-command-keys
		       "\nSee the variable `reference-point-alist' for ")
                      "the meaning of the rule.\n")))

          (unless eight-bit-p
            (insert (if (not describe-char-unidata-list)
                        "\nCharacter code properties are not shown: "
                      "\nCharacter code properties: "))
            (insert-text-button
             "customize what to show"
             'action (lambda (&rest _ignore)
                       (customize-variable
                        'describe-char-unidata-list))
             'follow-link t)
            (insert "\n")
            (dolist (elt
                     (cond ((eq describe-char-unidata-list t)
                            (nreverse (mapcar #'car char-code-property-alist)))
                           ((< char 32)
                            ;; Temporary fix (2016-05-22): The
                            ;; decomposition item for \n corrupts the
                            ;; display on a Linux virtual terminal.
                            ;; (Bug #23594).
                            (remq 'decomposition describe-char-unidata-list))
                           (t describe-char-unidata-list)))
              (let ((val (get-char-code-property char elt))
                    description)
                (when val
                  (setq description (char-code-property-description elt val))
                  (insert (if description
                              (format "  %s: %s (%s)\n" elt val description)
                            (format "  %s: %s\n" elt val)))))))

          (if text-props-desc (insert text-props-desc))
          (setq buffer-read-only t))))))