emacs tui在遇到文件里有emoji的时候在主流terminal都有渲染错误

nvim helix打开相同文件都没有问题,emacs tui有问题. 在不同的terminal问题还不同

wezterm: 看着一个emoji变小了 只占用一个char宽度,对于零宽的unicode也有占用一个char宽,evil移动能看到,忍一忍还能用

ghosrtty kitty 这两个terminal里现象就导致有些行残留 或者消失,几乎不可用

将内容复制十几份到scrach buffer, 会出现更多奇怪的现象

录屏已经准备好, 还没搞清楚怎么上传


目前自用的缓解措施, 让emoji显示为问号

;; TUI: 不显示 emoji,避免 Kitty/WezTerm 的光标错位与重绘花屏。
;; 说明:只改显示,不改 buffer/file 内容。
(when (not (display-graphic-p))
  (setq-default bidi-display-reordering nil)
  (setq bidi-inhibit-bpa t)

  (defun my/make-tui-no-emoji-display-table ()
    "Return a display table that masks emoji-like chars in terminal Emacs."
    (let ((dt (make-display-table)))
      ;; Fall back to any existing standard display behavior.
      (set-char-table-parent dt standard-display-table)
      ;; Flags (regional indicators)
      (set-char-table-range dt (cons #x1F1E6 #x1F1FF) (vector ??))
      ;; Emoji blocks
      (set-char-table-range dt (cons #x1F300 #x1FAFF) (vector ??))
      ;; Misc symbols / dingbats where emoji presentation often appears (e.g. U+2695)
      (set-char-table-range dt (cons #x2600 #x27BF) (vector ??))
      ;; Zero-width joiners/selectors/modifiers: hide them completely.
      (set-char-table-range dt (cons #x200D #x200D) (vector))
      (set-char-table-range dt (cons #xFE0E #xFE0F) (vector))
      (set-char-table-range dt (cons #x1F3FB #x1F3FF) (vector))
      dt))

  (defvar my/tui-no-emoji-display-table nil)
  (setq my/tui-no-emoji-display-table (my/make-tui-no-emoji-display-table))

  ;; Global default + per-buffer reinforcement (some major modes set buffer-display-table).
  (setq standard-display-table my/tui-no-emoji-display-table)
  (setq-default buffer-display-table my/tui-no-emoji-display-table)
  (when (fboundp 'global-auto-composition-mode)
    (global-auto-composition-mode -1))
  (add-hook 'after-change-major-mode-hook
            (lambda ()
              (setq-local buffer-display-table my/tui-no-emoji-display-table))))


我的环境是macos 15.7.2 (24G325), emacs plus install by homebrew 截图文件是 https://raw.githubusercontent.com/badlogic/pi-mono/f129ac93c508c2cbe45e8342bbf59ce4ba04acdc/README.md

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**


将此内容放到scrach buffer后, 也不会


感谢 lgfang 提醒, 已经增加截图说明.

1 个赞

你是纯抱怨还是来找解决办法?如果是后者,你应该上传几张截图和测试样本,否则我们很难帮忙啊。

下面是我以前处理ZWJ的办法,仅供参考。

When running Emacs in a terminal, cursor movements may garble the display of the buffer’s content if it contains emojis, particularly those using Zero Width Joiners (ZWJ).

To prevent this issue, please disable auto-composition-mode as shown below. Please note that as a trade-off, ZWJ emojis will render as multiple distinct characters rather than a single combined image. See [[file:emacs-unicode examples.org][my unicode examples]].

NOTE: disabling it on the fly does not work very well, must restart Emacs.

  (use-package emacs
    :if (not (display-graphic-p))
    :config
    (setq-default auto-composition-mode nil)
    )

测试文件

*NOTE:* To avoid display errors that might block editing, please store this
snippet outside the main Emacs configuration file.

Move cursor around and press ~C-l~, see if the cursor and chars are still
displayed correctly.

- 平安喜乐 :: Chinese characters
- 😃 (\u01F603) :: Smiling face with open mouth,
- 🏖 (\u1F3D6) :: This symbol(🏖) renders as a single-width character unless
  followed by a trailing space, which allows it to expand to two characters
  wide as demonstrated by "🏖 ".
  + This symbol originates from an older set of pictograph. When it stands
    alone, the Unicode standard dictates that it should default to a /text
    presentation/ (usually rendered as a flat, black-and-white icon, similar to
    a standard font character). While modern terminal emulators are smart enough
    to draw it as a wide graphical emoji anyway, Emacs's core C code strictly
    follows the baseline Unicode definitions for character width and register
    this one as 1.
- 👩‍💻 (\u01F469 \u200D \u01F4BB) :: Woman + Zero Width Joiner (ZWJ) + Personal
  Computer
- 🏖️ (\u1F3D6 \uFE0F) :: Beach with Umbrella + Variation Selector 16

显示效果

关于emoji只有一个字符宽度的解释

以下来自于AI,感觉有道理

  • This symbol originates from an older set of pictograph. When it stands alone, the Unicode standard actually dictates that it should default to a /text presentation/ (usually rendered as a flat, black-and-white icon, similar to a standard font character). While modern terminal emulators are smart enough to draw it as a wide graphical emoji anyway, Emacs’s core C code strictly follows the baseline Unicode definitions for character width and register this one as 1.

AI也给出了解决方法,不过经我测试,好像不管用。

Applying a width of 2 to a massive Unicode block (#x1F300 to #x1F5FF) is a blunt instrument. That range contains hundreds of characters, and your terminal emulator likely renders some of them as strictly single-width (1 column). Instead of forcing an entire block, it is much safer to target only the specific characters that are causing issues in your specific terminal/font setup.

(dolist (char '(?🏖 ?⛱ ?⛄))
  (set-char-table-range char-width-table char 2))

测试环境

  • GNU Emacs 30.1 (build 1, aarch64-apple-darwin21.6.0, NS appkit-2113.65 Version 12.7.6 (Build 21H1320)) of 2025-02-25
  • wezterm 20240203-110809-5046fc22
  • macOS 26.3 (25D125)
1 个赞

【求助】在 wsl2 中启动 Emacs,组合的 Emoji 会导致 buffer 渲染错误

有一个类似的帖子。

或许 @lgfang 的方案可行。

修改后 , scrach buffer 里显示 下面内容还是有问题


# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**


# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**

# 🏖️ OSS Vacation

**Issue tracker and PRs reopen March 2, 2026.**


emoji 还是有问题

>>> s = "🏖️"
... for ch in s:
...     print(hex(ord(ch)), ch)
...
0x1f3d6 🏖
0xfe0f

我有观察到这个emoji实际上是 U+1F3D6U+FE0F, 它后面跟了 个 Variation Selector 16

就是说这种组合emoji显示为一个,但是实际占位依旧是二。光标在这个emoji上运动的时候看的很清楚,得走两步。

甚至有些终端没办法组合还会直接显示两个完全不同的符号。

不支持组合的终端:

支持组合的终端:

Screenshot From 2026-03-04 13-30-01

我在这个帖子中发现了类似的问题

单个 emoji 不会错位,如果当前屏幕内容有某一行包含多个 emoji,又用了 C-n, C-p 这类按键,就会导致显示异常,得手动 C-l 刷新纠正

这种是通过vim打开的情况。

这种是helix在kitty中打开的情况。

这些都是异常的情况,大致可以分为三类,第一类,最开头的那部分看起来正常,用helix打开会看到光标在emoji的时候只显示一半;第二类和第三类都是二合一,但是第三类也是光标占占一半。

修改后 , scrach buffer 里显示 下面内容还是有问题

你所说的问题是不是 1):beach_with_umbrella: 只占一个字符宽度;2)带ZWJ的emoji ,例如👩‍💻 ,显示成多个字符?这两个都是预期的结果。具体原因我更新在我第一个回复里了。

(setq-default auto-composition-mode nil) 目的是让Emacs 不要因为Emoji而乱显示buffer的内容,这样并不能完美解决Emoji显示本身。

下图左边关掉了auto-composition-mode , 右边没有。

  • 左边能够“正常”显示buffer,只是含ZWJ的emoji(例如buffer中第一个emoji)没有合成一个字符,而是显示成3个字符。但至少可以正常查看编辑
  • 右边在我按了几次 C-l 后显示完全不正常。甚至前面几行都消失了。

  • GNU Emacs 30.1 (build 1, aarch64-apple-darwin21.6.0, NS appkit-2113.65 Version 12.7.6 (Build 21H1320)) of 2025-02-25
  • wezterm 20240203-110809-5046fc22
  • macOS 26.3 (25D125)

关掉auto-composition-mode这个就是目前最优解了

(unless (display-graphic-p)
  (set-char-table-range char-width-table #xFE0F 1))

这样就可以了,测试kitty上没啥问题了,但是🙂‍↔🙂‍↕这样的表情在ghostty上不支持合并,所以它稍微有点问题,与neovim表现一致。

另外在普通终端中因为无法支持zwj,所以渲染问题还是存在。

确实,这个问题一直困扰我很久,我之前主要是在 git commit 里用emoji,导致查看 magit-status 时经常出现错行重叠,最后没办法改掉了在commit 里用emoji的习惯 :sob: