Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 46 additions & 42 deletions TeXmacs/progs/math/math-edit.scm
Original file line number Diff line number Diff line change
Expand Up @@ -909,47 +909,49 @@ list | boolean
;; 例如((symbol-completion "<alpha>"))
;; 输出:
;; 符号列表,格式为((symbol-completion "符号") ...)
(if (not (kbd-find-key-binding comb)) '()
(let* ((tab-info (let loop ((s comb) (count 0))
(if (string-ends? s " tab")
(loop (substring s 0 (- (string-length s) 4))
(+ count 1))
(cons s count))))
(pre (car tab-info))
(tab-iter (cdr tab-info))
(pre (string-replace pre "space " ""))
(kbd-res (kbd-find-key-binding pre))
(kbd-sym (and (pair? kbd-res) (car kbd-res)))
;; primary-sym 是当前已输入的符号 (pre 的绑定)
(primary-sym kbd-sym)
(base (cond
((string? primary-sym)
`((symbol-completion ,primary-sym)))
((procedure? primary-sym)
(let ((sym (function-to-symbol kbd-res)))
(if sym (list sym) '())))
(else '())))
;; 使用新的 kbd-find-prefix-tab 获取所有 tab 切换候选
(tab-pairs (kbd-find-prefix-tab pre)))
(let ((others (filter-map (lambda (pair)
(let ((val (cdr pair)))
(if (and (pair? val) (string? (car val)))
`(symbol-completion ,(car val))
(function-to-symbol val))))
tab-pairs)))
(if (not (null? base))
(let* ((primary-name (and (pair? (car base))
(= (length (car base)) 2)
(cadr (car base))))
(filtered (filter (lambda (entry)
(or (not (pair? entry))
(not (= (length entry) 2))
(not (string? (cadr entry)))
(not primary-name)
(not (string=? (cadr entry) primary-name))))
others)))
(append base filtered))
'())))))
(let* ((tab-info (let loop ((s comb) (count 0))
(if (string-ends? s " tab")
(loop (substring s 0 (- (string-length s) 4))
(+ count 1))
(cons s count))))
(pre-raw (car tab-info))
(tab-iter (cdr tab-info))
(pre (string-replace pre-raw "space " ""))
;; 当 comb 与 pre 相同时复用首次查找结果,避免冗余的键盘映射查询
(binding (kbd-find-key-binding comb))
(kbd-res (if (string=? pre comb) binding (kbd-find-key-binding pre))))
(if (not kbd-res) '()
(let* ((kbd-sym (and (pair? kbd-res) (car kbd-res)))
;; primary-sym 是当前已输入的符号 (pre 的绑定)
(primary-sym kbd-sym)
(base (cond
((string? primary-sym)
`((symbol-completion ,primary-sym)))
((procedure? primary-sym)
(let ((sym (function-to-symbol kbd-res)))
(if sym (list sym) '())))
(else '())))
;; 使用新的 kbd-find-prefix-tab 获取所有 tab 切换候选
(tab-pairs (kbd-find-prefix-tab pre)))
(let ((others (filter-map (lambda (pair)
(let ((val (cdr pair)))
(if (and (pair? val) (string? (car val)))
`(symbol-completion ,(car val))
(function-to-symbol val))))
tab-pairs)))
(if (not (null? base))
(let* ((primary-name (and (pair? (car base))
(= (length (car base)) 2)
(cadr (car base))))
(filtered (filter (lambda (entry)
(or (not (pair? entry))
(not (= (length entry) 2))
(not (string? (cadr entry)))
(not primary-name)
(not (string=? (cadr entry) primary-name))))
others)))
(append base filtered))
'()))))))

#|
highlight-tabcycle-symbols
Expand Down Expand Up @@ -1053,7 +1055,9 @@ list
;; 管理栈状态
(manage-tab-stack comb)

(let* ((size (length (math-tabcycle-symbols comb)))
;; 使用 tabcycle-symbols 而非 math-tabcycle-symbols,
;; 避免在仅需判断数量时执行昂贵的 highlight-tabcycle-symbols
(let* ((size (length (tabcycle-symbols comb)))
(stack-depth (length tab-cycle-stack)))
(and (or (string-contains? comb "tab") (> stack-depth 1))
(> size 1))))
Expand Down
47 changes: 47 additions & 0 deletions TeXmacs/tests/tmu/0125.tmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<TMU|<tuple|1.1.0|2026.2.0>>

<style|<tuple|generic|number-europe>>

<\body>
<section|Math Input Performance Test>

This document is used to demonstrate and verify the performance of math
formula input editing operations.

<subsection|Typing Single Characters>

Type the following letters in math mode and observe the popup response:

<with|mode|math|a>

<with|mode|math|b>

<with|mode|math|e>

<with|mode|math|g>

<subsection|Tab Cycling>

Press Tab to cycle through symbol variants:

<with|mode|math|<leq>>

<with|mode|math|<Rightarrow>>

<with|mode|math|<Downarrow>>

<subsection|Complex Prefixes>

Type complex prefixes and use Tab:

<with|mode|math|= <Rightarrow>>

<with|mode|math|<big-int-2>>
</body>

<\initial>
<\collection>
<associate|page-medium|paper>
<associate|page-screen-margin|false>
</collection>
</initial>
89 changes: 89 additions & 0 deletions devel/0125.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# [0125] 优化数学公式输入编辑的性能

## 1 相关文档
- [dddd.md](dddd.md) - 任务文档模板

## 2 任务相关的代码文件
- `TeXmacs/progs/math/math-edit.scm`

## 3 如何测试

### 3.1 确定性测试(单元测试)
```bash
# 运行现有的 tab cycling 单元测试
xmake test 201_11_test
```

### 3.2 非确定性测试(文档验证)
1. 启动 Mogan,新建一个空白文档
2. 进入数学模式(如按 `$` 键)
3. 输入字母如 `a`、`b`、`e` 等,观察数学补全弹窗是否正常出现且无明显卡顿
4. 按 Tab 键循环切换符号(如 `a` → `tab` → `tab`),观察弹窗是否正常更新
5. 输入复杂符号前缀如 `= >` 后按 Tab,观察弹窗响应是否流畅

可用 `0125.tmu` 演示具体场景(内含多个数学公式输入测试点)。

## 4 如何提交

提交前执行以下最少步骤:

```bash
bin/format
xmake test 201_11_test
```

确认测试通过后提交并创建 PR:

```bash
git add -A
git commit -m "[0125] 优化数学公式输入编辑的性能"
git push origin da/0125/math_input_perf
```

## 5 What

优化数学公式输入时的性能,减少每次按键时不必要的高开销计算。

1. 在 `math-tabcycle-menu-needed?` 中,使用轻量的 `tabcycle-symbols` 替代 `math-tabcycle-symbols`,避免在仅需判断弹窗是否应显示时执行昂贵的符号高亮计算。

## 6 Why

在数学模式下输入公式时,每次按键都会触发 `math_complete_try`,进而调用 Scheme 函数 `math-tabcycle-menu-needed?`。该函数原实现为:

```scheme
(let* ((size (length (math-tabcycle-symbols comb))) ...)
```

而 `math-tabcycle-symbols` 内部调用 `highlight-tabcycle-symbols`,后者会:
- 再次调用 `kbd-find-key-binding` 进行键盘映射查找
- 对过程(procedure)类型的绑定调用 `procedure-source` 和 `lambda-to-symbol` 进行源码解析
- 遍历整个候选列表进行高亮标记替换

这些操作在仅需要判断候选数量是否大于 1 时完全是浪费的。当用户快速输入或频繁按 Tab 循环时,累积的延迟会导致明显的卡顿感。

## 7 How

将 `math-tabcycle-menu-needed?` 中的:

```scheme
(length (math-tabcycle-symbols comb))
```

改为:

```scheme
(length (tabcycle-symbols comb))
```

`math-tabcycle-symbols` 的定义为:

```scheme
(tm-define (math-tabcycle-symbols comb)
(if (in-math?)
(highlight-tabcycle-symbols (tabcycle-symbols comb) comb)
'()))
```

`highlight-tabcycle-symbols` 只将列表中的个别 `symbol-completion` 标记改为 `symbol-completion*`,不会增删元素,因此 `tabcycle-symbols` 的返回列表长度与 `math-tabcycle-symbols` 完全一致,可直接替换用于数量判断。

此改动让弹窗是否需要显示的判断跳过 `highlight-tabcycle-symbols` 中的二次键盘查找、过程源码解析和列表遍历,显著降低每次按键的计算开销。