diff --git a/TeXmacs/progs/math/math-edit.scm b/TeXmacs/progs/math/math-edit.scm index 6c1fe3f0bd..1343bdcef1 100644 --- a/TeXmacs/progs/math/math-edit.scm +++ b/TeXmacs/progs/math/math-edit.scm @@ -909,47 +909,49 @@ list | boolean ;; 例如((symbol-completion "")) ;; 输出: ;; 符号列表,格式为((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 @@ -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)))) diff --git a/TeXmacs/tests/tmu/0125.tmu b/TeXmacs/tests/tmu/0125.tmu new file mode 100644 index 0000000000..576619a6bd --- /dev/null +++ b/TeXmacs/tests/tmu/0125.tmu @@ -0,0 +1,47 @@ +> + +> + +<\body> + + + This document is used to demonstrate and verify the performance of math + formula input editing operations. + + + + Type the following letters in math mode and observe the popup response: + + + + + + + + + + + + Press Tab to cycle through symbol variants: + + > + + > + + > + + + + Type complex prefixes and use Tab: + + > + + > + + +<\initial> + <\collection> + + + + diff --git a/devel/0125.md b/devel/0125.md new file mode 100644 index 0000000000..de0d659f7b --- /dev/null +++ b/devel/0125.md @@ -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` 中的二次键盘查找、过程源码解析和列表遍历,显著降低每次按键的计算开销。