From 884929aad3758ad2076ff9daddebc8e48e06e853 Mon Sep 17 00:00:00 2001 From: gatsby <16312717+simplegatsby@user.noreply.gitee.com> Date: Fri, 13 Feb 2026 22:14:23 +0800 Subject: [PATCH 1/3] =?UTF-8?q?[201=5F82]=20=E5=9C=A8enumerate=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E4=B8=AD=E5=AE=9E=E7=8E=B0tab=E7=BC=A9=E8=BF=9B?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TeXmacs/progs/generic/generic-edit.scm | 116 +++++++++++++++++++++++++ devel/201_82.md | 49 +++++++++++ 2 files changed, 165 insertions(+) create mode 100644 devel/201_82.md diff --git a/TeXmacs/progs/generic/generic-edit.scm b/TeXmacs/progs/generic/generic-edit.scm index b5eef67813..3aeb285574 100644 --- a/TeXmacs/progs/generic/generic-edit.scm +++ b/TeXmacs/progs/generic/generic-edit.scm @@ -166,6 +166,122 @@ (set-message `(concat "Use " ,sh " in order to insert a tab") "tab")))) +;; 辅助函数:定义 enumerate-tag-list +(define (enumerate-tag-list) + '(enumerate enumerate-1 enumerate-2 enumerate-3 enumerate-4)) + +;; 辅助函数:检查是否在有序列表环境中 +(define (in-enumerate-context?) + (not (not (tree-search-upwards (focus-tree) (lambda (node) (tree-in? node (enumerate-tag-list))))))) + +;; 辅助函数:查找包含 item 的 concat 包装和真正的 item list +(define (find-item-wrapper-and-list item) + (let ((wrapper #f) + (item-list #f)) + (let loop ((current (tree-outer item))) + (if (tree-is? current 'concat) + (begin + (set! wrapper current) + (loop (tree-outer current)) + ) + (set! item-list current) + ) + ) + (values wrapper item-list) + ) +) + +;; 辅助函数:提取 item 内容(处理 concat 包装) +(define (extract-item-content item wrapper) + (if (and item (> (tree-arity item) 0)) + (tree-copy (tree-ref item 0)) + (if (and wrapper (> (tree-arity wrapper) 1)) + (let ((content #f)) + (do ((i 1 (+ i 1))) + ((or content (>= i (tree-arity wrapper)))) + (let ((child (tree-ref wrapper i))) + (if (not (tree-is? child 'item)) + (set! content (tree-copy child)) + #f + ) + ) + ) + content + ) + #f + ) + ) +) + +;; 辅助函数:在列表中移除 item(处理 concat 包装) +(define (remove-item-from-list item wrapper item-list) + (if wrapper + ;; 如果有 wrapper,移除整个 wrapper + (let ((wrapper-index (tree-index wrapper))) + (tree-remove! item-list wrapper-index 1) + ) + ;; 否则移除单个 item + (let ((item-index (tree-index item))) + (tree-remove! item-list item-index 1) + ) + ) +) + +;; 在有序列表中实现缩进功能 +(tm-define (kbd-variant t forwards?) + (:require (and in-enumerate-context? (tree-is? (focus-tree) 'item))) + "在有序列表中按Tab键创建子列表" + + (let ((item (focus-tree))) + + ;; 步骤 1: 查找包装和列表 + (call-with-values (lambda () (find-item-wrapper-and-list item)) + (lambda (wrapper item-list) + + (if (and item item-list) + (let ((item-index (if wrapper (tree-index wrapper) (tree-index item)))) + + (if (> item-index 0) + (let ((prev-item (tree-ref item-list (- item-index 1)))) + + ;; 步骤 2: 提取内容 + (let ((item-content (extract-item-content item wrapper))) + + ;; 步骤 3: 创建子列表并移动内容 + (tree-go-to prev-item :end) + (insert-return) + (make-tmlist 'enumerate) + + (if item-content + (let ((new-item (focus-tree))) + (let ((content-stree (tree->stree item-content))) + (tree-set! new-item `(concat (item), content-stree)) + (tree-go-to new-item) + ) + ) + #f + ) + + ;; 步骤 4: 从原列表中移除 + (remove-item-from-list item wrapper item-list) + ) + ) + (begin + (noop) + ) + ) + ) + (begin + (and-with p (tree-outer t) + (kbd-variant p forwards?) + ) + ) + ) + ) + ) + ) +) + (tm-define (kbd-variant t forwards?) (:require (and (tree-in? t '(label reference pageref eqref smart-ref)) (cursor-inside? t))) diff --git a/devel/201_82.md b/devel/201_82.md new file mode 100644 index 0000000000..43aff8ddf6 --- /dev/null +++ b/devel/201_82.md @@ -0,0 +1,49 @@ +# 201_82 有序列表中实现Tab键缩进功能 + +## 如何测试 +- 创建一个有序列表(enumerate环境) +- 在列表中添加多个item +- 将光标放在第二个或后续item上 +- 按Tab键,观察当前item是否缩进成为前一个item的子列表项 +- 测试带concat包装的item是否也能正常缩进 +- 测试第一个item按Tab键是否无反应(因为没有前一个item可以缩进) + +## 2026/02/13 新增有序列表Tab键缩进功能 + +### What +在TeXmacs/progs/generic/generic-edit.scm中新增了有序列表的Tab键缩进功能。当用户在有序列表的item上按Tab键时,该item会缩进成为前一个item的子列表项。 + +### Why +在LaTeX等文档编辑器中,有序列表支持通过Tab键将列表项缩进为子列表,这是常见的编辑功能。TeXmacs此前缺少这一功能,用户体验不够友好。通过添加此功能,用户可以更方便地创建嵌套的有序列表结构。 + +### How +在generic-edit.scm中新增了以下内容: + +1. **辅助函数**: + - `enumerate-tag-list`: 定义有序列表的标签列表(enumerate, enumerate-1, enumerate-2, enumerate-3, enumerate-4) + - `in-enumerate-context?`: 检查当前是否在有序列表环境中 + - `find-item-wrapper-and-list`: 查找包含item的concat包装和真正的item list + - `extract-item-content`: 提取item内容,处理concat包装的情况 + - `remove-item-from-list`: 从列表中移除item,处理concat包装的情况 + +2. **kbd-variant函数重载**: + - 新增了kbd-variant函数的特定版本,专门处理在有序列表中按Tab键的场景 + - 该函数会检查当前是否在有序列表环境中,并且光标是否在item上 + - 当条件满足时,执行以下操作: + a. 查找item的包装和所属列表 + b. 检查是否有前一个item + c. 提取当前item的内容 + d. 在前一个item末尾创建子列表 + e. 将提取的内容移动到新创建的子列表item中 + f. 从原列表中移除当前item + +### 涉及的功能模块 +- 文档编辑功能(generic-edit.scm) +- 列表环境处理(enumerate环境) +- 键盘快捷键处理(kbd-variant) +- 树结构操作(tree操作相关函数) + +### 解决的问题 +- 解决了有序列表中无法通过Tab键创建子列表的问题 +- 提升了用户在编辑有序列表时的效率 +- 使TeXmacs的列表编辑功能更符合主流文档编辑器的使用习惯 From fb1998e25bb6fb7a138f3d3412446968f3f2f6de Mon Sep 17 00:00:00 2001 From: gatsby <16312717+simplegatsby@user.noreply.gitee.com> Date: Sat, 14 Feb 2026 21:08:43 +0800 Subject: [PATCH 2/3] [201_84]:fix number conflicts --- devel/{201_82.md => 201_84.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename devel/{201_82.md => 201_84.md} (97%) diff --git a/devel/201_82.md b/devel/201_84.md similarity index 97% rename from devel/201_82.md rename to devel/201_84.md index 43aff8ddf6..3e4ad36715 100644 --- a/devel/201_82.md +++ b/devel/201_84.md @@ -1,4 +1,4 @@ -# 201_82 有序列表中实现Tab键缩进功能 +# 201_84 有序列表中实现Tab键缩进功能 ## 如何测试 - 创建一个有序列表(enumerate环境) From b995340b7a5d55626e645efcbf725186d5f4b33d Mon Sep 17 00:00:00 2001 From: gatsby <16312717+simplegatsby@user.noreply.gitee.com> Date: Mon, 2 Mar 2026 14:48:50 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=E6=97=A0?= =?UTF-8?q?=E5=BA=8F=E5=88=97=E8=A1=A8=E5=92=8C=E6=8F=8F=E8=BF=B0=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?debug=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TeXmacs/progs/generic/generic-edit.scm | 122 ++++++++++++++++++------- 1 file changed, 90 insertions(+), 32 deletions(-) diff --git a/TeXmacs/progs/generic/generic-edit.scm b/TeXmacs/progs/generic/generic-edit.scm index 3aeb285574..8111199a25 100644 --- a/TeXmacs/progs/generic/generic-edit.scm +++ b/TeXmacs/progs/generic/generic-edit.scm @@ -170,10 +170,37 @@ (define (enumerate-tag-list) '(enumerate enumerate-1 enumerate-2 enumerate-3 enumerate-4)) +;; 辅助函数:定义 itemize-tag-list +(define (itemize-tag-list) + '(itemize itemize-dot itemize-minus itemize-arrow)) + +;; 辅助函数:定义 description-tag-list +(define (description-tag-list) + '(description description-compact description-aligned description-dash description-long description-paragraphs)) + ;; 辅助函数:检查是否在有序列表环境中 (define (in-enumerate-context?) (not (not (tree-search-upwards (focus-tree) (lambda (node) (tree-in? node (enumerate-tag-list))))))) +;; 辅助函数:检查是否在无序列表环境中 +(define (in-itemize-context?) + (not (not (tree-search-upwards (focus-tree) (lambda (node) (tree-in? node (itemize-tag-list))))))) + +;; 辅助函数:检查是否在描述列表环境中 +(define (in-description-context?) + (not (not (tree-search-upwards (focus-tree) (lambda (node) (tree-in? node (description-tag-list))))))) + +;; 辅助函数:获取当前列表的类型 +;; 辅助函数:获取当前列表的类型 +(define (get-list-type) + (cond + ((in-description-context?) 'description) + ((in-itemize-context?) 'itemize) + ((in-enumerate-context?) 'enumerate) + (else #f) + ) +) + ;; 辅助函数:查找包含 item 的 concat 包装和真正的 item list (define (find-item-wrapper-and-list item) (let ((wrapper #f) @@ -192,24 +219,10 @@ ) ;; 辅助函数:提取 item 内容(处理 concat 包装) -(define (extract-item-content item wrapper) - (if (and item (> (tree-arity item) 0)) - (tree-copy (tree-ref item 0)) - (if (and wrapper (> (tree-arity wrapper) 1)) - (let ((content #f)) - (do ((i 1 (+ i 1))) - ((or content (>= i (tree-arity wrapper)))) - (let ((child (tree-ref wrapper i))) - (if (not (tree-is? child 'item)) - (set! content (tree-copy child)) - #f - ) - ) - ) - content - ) - #f - ) +(define (extract-item-content wrapper) + (if (and wrapper (> (tree-arity wrapper) 1)) + (tree-copy (tree-ref wrapper 1)) + #f ) ) @@ -227,59 +240,104 @@ ) ) -;; 在有序列表中实现缩进功能 +;; 在有序和无序列表中实现缩进功能 (tm-define (kbd-variant t forwards?) - (:require (and in-enumerate-context? (tree-is? (focus-tree) 'item))) - "在有序列表中按Tab键创建子列表" + (:require + (or + ;; 有序列表或者无序列表 + (and (or in-enumerate-context? in-itemize-context?) (tree-is? (focus-tree) 'item)) + ;; 描述列表 + (and in-description-context? (tree-is? (focus-tree) 'item*)) + ) + ) + + (display "=== kbd-variant 函数调用开始 ===\n") + (display "t: ") (display (tree->stree t)) (display "\n") + (display "forwards?: ") (display forwards?) (display "\n") + (display "focus-tree: ") (display (tree->stree (focus-tree))) (display "\n") (let ((item (focus-tree))) + (display "item: ") (display (tree->stree item)) (display "\n") + ;; 步骤 1: 查找包装和列表 (call-with-values (lambda () (find-item-wrapper-and-list item)) (lambda (wrapper item-list) + (display "wrapper: ") (display (if wrapper (tree->stree wrapper) #f)) (display "\n") + (display "item-list: ") (display (if item-list (tree->stree item-list) #f)) (display "\n") + (if (and item item-list) (let ((item-index (if wrapper (tree-index wrapper) (tree-index item)))) + (display "item-index: ") (display item-index) (display "\n") + (if (> item-index 0) (let ((prev-item (tree-ref item-list (- item-index 1)))) - + + (display "prev-item: ") (display (tree->stree prev-item)) (display "\n") + ;; 步骤 2: 提取内容 - (let ((item-content (extract-item-content item wrapper))) + (let ((item-content (extract-item-content wrapper))) + (display "item-content: ") (display (if item-content (tree->stree item-content) #f)) (display "\n") + ;; 步骤 3: 创建子列表并移动内容 (tree-go-to prev-item :end) (insert-return) - (make-tmlist 'enumerate) + (let ((list-type (get-list-type))) + + (display "list-type: ") (display list-type) (display "\n") + + (if list-type + (make-tmlist list-type) + (make-tmlist 'enumerate) ; 默认使用有序列表 + ) + ) + ;; 步骤4:拷贝内容 (if item-content - (let ((new-item (focus-tree))) - (let ((content-stree (tree->stree item-content))) - (tree-set! new-item `(concat (item), content-stree)) - (tree-go-to new-item) + (let ((new-item (focus-tree)) + (list-type (get-list-type))) + + (display "new-item: ") (display (tree->stree new-item)) (display "\n") + + (let ((content-stree (tree->stree item-content))) + (if (eq? list-type 'description) + (tree-set! new-item `(concat (item*), content-stree)) + (tree-set! new-item `(concat (item), content-stree)) ) + + (display "new-item after set: ") (display (tree->stree new-item)) (display "\n") + + (tree-go-to new-item) ) + ) #f ) - ;; 步骤 4: 从原列表中移除 + ;; 步骤 5: 从原列表中移除 + + (display "Removing item from list...\n") (remove-item-from-list item wrapper item-list) + (display "Item removed successfully\n") ) ) (begin + (display "item-index is 0, no operation\n") (noop) ) ) ) (begin - (and-with p (tree-outer t) - (kbd-variant p forwards?) - ) + (display "item or item-list is null, no operation\n") + (noop) ) ) ) ) ) + (display "=== kbd-variant 函数调用结束 ===\n") ) (tm-define (kbd-variant t forwards?)