Skip to content

Commit 538109e

Browse files
lujun9972claude
andcommitted
blog: TIL — 数字小键盘的小数点陷阱与行内算术求值
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 23e81bf commit 538109e

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#+TITLE: TIL: 数字小键盘的小数点陷阱与行内算术求值
2+
#+AUTHOR: lujun9972, Claude Code
3+
#+TAGS: TIL Emacs 键盘配置
4+
#+DATE: [2026-04-27 Sun]
5+
#+LANGUAGE: zh-CN
6+
#+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil
7+
8+
Marcin Borkowski 的一篇文章[fn:1]揭示了一个容易忽略的 Emacs 键盘行为:数字小键盘右下角那个用来分隔整数和小数部分的键——在 Emacs 里被硬编码为逗号。
9+
10+
*注意:这个问题只影响欧洲键盘布局(波兰、德国、法国等)。这些布局下,该键发送的是 =kp-separator= 按键符号;而美式和中文键盘布局下,该键直接发送 =.= 字符,不受此问题影响。*
11+
12+
* 问题:数字小键盘打不出小数点
13+
14+
受影响的用户在数字小键盘上按那个键想输入小数点,结果得到的是逗号。用 =C-h c= 查看这个键的 Emacs 名称:
15+
16+
#+begin_example
17+
, 'COMMA' (translated from <kp-separator>) runs the command self-insert-command
18+
#+end_example
19+
20+
关键在 =<kp-separator>= 这个名字。Emacs 在 =simple.el= 里把 =kp-separator= 硬编码映射为逗号:
21+
22+
#+begin_src emacs-lisp
23+
(mapc
24+
(lambda (keypad-normal)
25+
(let ((keypad (nth 0 keypad-normal))
26+
(normal (nth 1 keypad-normal)))
27+
(put keypad 'ascii-character normal)
28+
(define-key function-key-map (vector keypad) (vector normal))))
29+
'((kp-space ?\s)
30+
(kp-tab ?\t)
31+
(kp-enter ?\r)
32+
(kp-separator ?,) ;; ← 这里
33+
(kp-equal ?=)
34+
...))
35+
#+end_src
36+
37+
这个映射和你的系统语言设置无关——不管你用的是中文、英文还是波兰文环境, =kp-separator= 一律变成逗号。修复方法是在配置中覆盖这个映射:
38+
39+
#+begin_src emacs-lisp
40+
(keymap-set function-key-map "<kp-separator>" ".")
41+
#+end_src
42+
43+
=function-key-map= 的机制是:按键先经过这个映射表转换,再交给后续的 keymap 处理。所以这一行配置的效果是,在所有模式中都把数字小键盘的分隔键变成小数点,而不是逗号。
44+
45+
* 延伸:行内算术求值
46+
47+
原文还提到了一个实用技巧:在任意 buffer 中将光标左侧的算术表达式直接替换为计算结果。核心逻辑很简洁:
48+
49+
#+begin_src emacs-lisp
50+
(defun fast-calc (currency)
51+
"将光标左侧的算术表达式替换为计算结果。
52+
带前缀参数时,保留两位小数并追加货币后缀。"
53+
(interactive "P")
54+
(when (looking-back "[-+/*().0-9]\\{2,\\} *"
55+
(line-beginning-position)
56+
t)
57+
(replace-match
58+
(save-match-data
59+
(calc-eval (if currency
60+
(list
61+
(concat (match-string-no-properties 0) "+0.0")
62+
'calc-float-format
63+
'(fix 2))
64+
(match-string-no-properties 0))))
65+
t t)
66+
(if currency (insert " PLN"))))
67+
#+end_src
68+
69+
使用方式:在 =12.5*3= 后面按 =M-x fast-calc= ,表达式被替换为 =37.5= 。带前缀参数( =C-u M-x fast-calc= )则保留两位小数并追加货币后缀,比如 =12.5*3= → =37.50 PLN= 。
70+
71+
原理是 =calc-eval= 接受一个字符串(算术表达式)并返回结果字符串。传入列表形式时可以控制格式—— =(fix 2)= 表示固定两位小数。 =looking-back= 用正则匹配光标左侧的数字和运算符序列, =replace-match= 把匹配到的内容替换为计算结果。
72+
73+
* Footnotes
74+
75+
[fn:1] [[https://mbork.pl/2026-04-25_How_I_use_my_numeric_keypad_with_Emacs_Ledger_mode][How I use my numeric keypad with Emacs Ledger mode]]

0 commit comments

Comments
 (0)