暗无天日

=============>DarkSun的个人博客

TIL: 数字小键盘的小数点陷阱与行内算术求值

Marcin Borkowski 的一篇文章1揭示了一个容易忽略的 Emacs 键盘行为:数字小键盘右下角那个用来分隔整数和小数部分的键——在 Emacs 里被硬编码为逗号。

注意:这个问题只影响欧洲键盘布局(波兰、德国、法国等)。这些布局下,该键发送的是 kp-separator 按键符号;而美式和中文键盘布局下,该键直接发送 . 字符,不受此问题影响。

问题:数字小键盘打不出小数点

受影响的用户在数字小键盘上按那个键想输入小数点,结果得到的是逗号。用 C-h c 查看这个键的 Emacs 名称:

, 'COMMA' (translated from <kp-separator>) runs the command self-insert-command

关键在 <kp-separator> 这个名字。Emacs 在 simple.el 里把 kp-separator 硬编码映射为逗号:

(mapc
 (lambda (keypad-normal)
   (let ((keypad (nth 0 keypad-normal))
         (normal (nth 1 keypad-normal)))
     (put keypad 'ascii-character normal)
     (define-key function-key-map (vector keypad) (vector normal))))
 '((kp-space ?\s)
   (kp-tab ?\t)
   (kp-enter ?\r)
   (kp-separator ?,)   ;; ← 这里
   (kp-equal ?=)
   ...))

这个映射和你的系统语言设置无关——不管你用的是中文、英文还是波兰文环境, kp-separator 一律变成逗号。修复方法是在配置中覆盖这个映射:

(keymap-set function-key-map "<kp-separator>" ".")

function-key-map 的机制是:按键先经过这个映射表转换,再交给后续的 keymap 处理。所以这一行配置的效果是,在所有模式中都把数字小键盘的分隔键变成小数点,而不是逗号。

延伸:行内算术求值

原文还提到了一个实用技巧:在任意 buffer 中将光标左侧的算术表达式直接替换为计算结果。核心逻辑很简洁:

(defun fast-calc (currency)
  "将光标左侧的算术表达式替换为计算结果。
带前缀参数时,保留两位小数并追加货币后缀。"
  (interactive "P")
  (when (looking-back "[-+/*().0-9]\\{2,\\} *"
                      (line-beginning-position)
                      t)
    (replace-match
     (save-match-data
       (calc-eval (if currency
                      (list
                       (concat (match-string-no-properties 0) "+0.0")
                       'calc-float-format
                       '(fix 2))
                    (match-string-no-properties 0))))
     t t)
    (if currency (insert " PLN"))))

使用方式:在 12.5*3 后面按 M-x fast-calc ,表达式被替换为 37.5 。带前缀参数( C-u M-x fast-calc )则保留两位小数并追加货币后缀,比如 12.5*337.50 PLN

原理是 calc-eval 接受一个字符串(算术表达式)并返回结果字符串。传入列表形式时可以控制格式—— (fix 2) 表示固定两位小数。 looking-back 用正则匹配光标左侧的数字和运算符序列, replace-match 把匹配到的内容替换为计算结果。

TIL Emacs 键盘配置