From 2cabea286df8d42cf02ee2e424c27ba22693ee7c Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 15:51:12 +0800 Subject: [PATCH 1/3] =?UTF-8?q?[1293]=20=E6=B7=BB=E5=8A=A0=E8=8D=89?= =?UTF-8?q?=E7=A8=BF=E6=96=87=E4=BB=B6=E6=94=BE=E5=BC=80=20.stem=20?= =?UTF-8?q?=E5=90=8E=E7=BC=80=E4=B8=8E=E5=AF=BC=E5=87=BA=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E5=8C=85=E4=BB=BB=E5=8A=A1=E8=A7=84=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devel/1293.md | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 devel/1293.md diff --git a/devel/1293.md b/devel/1293.md new file mode 100644 index 0000000000..0f0751cd55 --- /dev/null +++ b/devel/1293.md @@ -0,0 +1,115 @@ +# [1293] 草稿文件后缀放开为 .tmu 与 .stem 并导出样式包为 .stem 草稿 + +## 1 相关文档 +- [dddd.md](dddd.md) - 任务文档模板 +- [1131.md](1131.md) - 添加 stem 格式(等价于 ts,Scheme S-expression 存储) +- [1257.md](1257.md) - 草稿文件名用 `_` 分隔日期与时刻(草稿命名与标题规则) +- [1279.md](1279.md) - 另存为引导到继任格式:.ts 默认 .stem,.tm 默认 .tmu +- [1289.md](1289.md) - 导出样式文件/包宏名称使用 utf8->herk 修复中文乱码 + +## 2 任务相关的代码文件 +- `TeXmacs/progs/texmacs/texmacs/tm-files.scm` — 草稿文件名候选生成、唯一名分配、时间戳解析放开 `.stem` 支持;草稿保存检查支持 `.stem` 格式 +- `TeXmacs/progs/texmacs/texmacs/tm-files-test.scm` — 草稿 `.stem` 后缀生成与标题解析的单元测试 +- `TeXmacs/progs/kernel/boot/abbrevs.scm` — `choose-file` 针对样式包草稿解析原文档目录与同名 `.stem` 建议文件名 +- `src/Texmacs/Data/new_buffer.cpp` / `src/Texmacs/Data/new_buffer.hpp` — `propose_title` 识别 `.stem` 草稿,`make_new_buffer` 支持可选后缀参数 +- `src/Texmacs/Data/new_window.cpp` / `src/Texmacs/Data/new_window.hpp` — `create_buffer` 支持指定后缀参数 +- `src/Scheme/Glue/glue_basic.lua` — 导出指定后缀的新建 buffer glue 接口 +- `src/Plugins/Qt/qt_chooser_widget.cpp` — 另存为对话框对于 `.stem` 草稿默认选中 `STEM files (*.stem)` 过滤器 +- `TeXmacs/progs/source/source-menu.scm` — 菜单移除「导出样式文件」,仅保留「导出样式包」 +- `TeXmacs/progs/source/source-edit.scm` — `extract-style-package` 使用 `.stem` 后缀新建草稿、关联原文档并填充内容 +- `TeXmacs/progs/source/tests/extract-style-package-test.scm` — 导出样式包与菜单清理单元测试 + +## 3 如何测试 + +### 3.1 单元测试 +```bash +xmake b stem +xmake b tm-files-test && xmake r tm-files-test +xmake b extract-style-package-test && xmake r extract-style-package-test +``` +预期: +1. `(scratch-buffer-name)` 默认生成 `.tmu` 路径; +2. `(scratch-buffer-name ".stem")` 生成形如 `draft_YYYYMMDD_HHMMSS.stem` 的路径; +3. `(scratch-buffer-title)` 对 `.stem` 草稿与同时间戳的 `.tmu` 草稿能够解析出完全相同的友好标题; +4. `extract-style-package-test` 验证菜单项已移除「导出样式文件」,导出的样式包为 `.stem` 草稿,且记录的原文档信息能推导出同目录、仅后缀变为 `.stem` 的默认保存路径; +5. 单元测试全部通过。 + +### 3.2 手动测试 +1. 启动 Mogan:`xmake r stem`; +2. 查看菜单栏「工具 → 宏命令」: + - 确认原「导出样式文件」菜单项已被移除; + - 确认仅保留「导出样式包」菜单项; +3. 打开已保存的文档(如 `/path/to/xxx.tmu`),点击「工具 → 宏命令 → 导出样式包」: + - 观察新建的标签页,其标题显示为草稿时间(如「草稿(周X HH:MM:SS)」); + - 查看文档对应的真实草稿路径,文件后缀为 `.stem`; +4. 在导出的样式包标签页中执行「保存」(Ctrl+S)或「另存为」(Ctrl+Shift+S): + - 弹出的文件选择器格式首选项为 `STEM files (*.stem)`,默认后缀为 `.stem`; + - 默认目录为原文档所在目录(`/path/to`); + - 默认建议文件名为 `xxx.stem`(与原文档同名,仅后缀不同); + - 点击保存后,文件在原目录成功生成 `xxx.stem`。 + +## 4 如何提交 + +提交前执行以下最少步骤: + +```bash +xmake b stem +xmake b tm-files-test && xmake r tm-files-test +xmake b extract-style-package-test && xmake r extract-style-package-test +gf fmt --changed-since=main +``` + +PR 提交流程: +- 第一个 commit:更新任务文档 `devel/1293.md` +- 后续 commit:代码改动 + +## 5 What + +1. **放开草稿文件的后缀限制**: + - 草稿文件(位于 `Documents/LiiiSTEM/no_name/`)允许两种格式后缀:`.tmu`(默认文档草稿)与 `.stem`(样式包/源码草稿)。 + - `scratch-candidate`、`scratch-unique-name`、`scratch-buffer-name` 支持接收可选后缀参数,默认保持 `".tmu"`,支持传入 `".stem"`。 + - `draft-stamp` 放开对 `.stem` 扩展名的匹配,去除末尾 5 字符后提取时间戳,使 `.stem` 草稿享有与 `.tmu` 相同的时间标题推导逻辑(如「草稿(周几 HH:MM:SS)」)。 + - C++ 侧 `propose_title` 放开对 `draft_*.stem` 的判断,让 `.stem` 草稿同样调用 Scheme 标题计算。 +2. **菜单项清理与调整**: + - 在「工具 → 宏命令」菜单中移除不再使用的「导出样式文件」(`Extract style file`)。 + - 保留「导出样式包」(`Extract style package`),并将绑定的命令显式定义为 `(extract-style-package)`。 +3. **导出样式包使用 `.stem` 草稿并关联原文档**: + - `extract-style-package` 在新建 buffer 时指定 `.stem` 后缀,生成 `draft_YYYYMMDD_HHMMSS.stem` 草稿; + - 导出时记录原文档的路径关联; + - 在保存(Ctrl+S)和另存为(Ctrl+Shift+S)时,格式首选项强制为 `stem` 格式(`STEM files (*.stem)`); + - 默认保存目录为原文档所在目录,默认文件名与原文档同名(仅后缀由 `.tmu` 变为 `.stem`,例如 `xxx.tmu` 对应 `xxx.stem`)。 + +## 6 Why + +1. 随着任务 1131 将样式文件/样式包格式全面升级为 `.stem`(Scheme S-expression 存储,便于版本控制 diff/merge),样式包不再适合以 `.tmu` 存储。 +2. 导出样式包后,用户需要将其作为当前文档或项目的专属样式包保存。保存和另存为的首选项必须直接引导到 `stem` 格式,默认目录与原文档同目录,默认文件名与原文档同名仅换后缀为 `.stem`,极大简化用户操作流程。 +3. 现代 Mogan 中样式包统一采用 `.stem` 格式,不再区分独立的样式文件(旧 `.ts`)与样式包,移除「导出样式文件」可精简菜单概念,避免用户混淆。 + +## 7 How + +### 7.1 草稿文件命名与标题系统放开 `.stem` + +1. **Scheme `tm-files.scm`**: + - 增加辅助函数标准化后缀参数,默认返回 `".tmu"`,支持传入 `"stem"`、`".stem"` 等; + - `scratch-candidate` 与 `scratch-unique-name` 扩展支持传入后缀参数,将原先写死的 `".tmu"` 替换为参数传入的后缀; + - `scratch-buffer-name` 增加可选后缀参数,缺省时保持 `".tmu"` 默认值; + - `draft-stamp` 放开扩展名检查,根据 `.tmu`(4 字符)或 `.stem`(5 字符)动态截取文件名主体并提取纯数字时间戳; + - 在 `save-buffer-check-permissions` 中,当草稿 buffer 属于 `.stem` 或关联样式包时,将选择器类型定为 `action_save_as` / `stem`,不再写死 `"tmu"`。 +2. **C++ `new_buffer.cpp` 与 `new_window.cpp`**: + - `propose_title` 放开对以 `draft_` 开头且以 `.stem` 结尾的文件名的识别,同样交由 `scratch-buffer-title` 处理; + - 为 `make_new_buffer` 与 `create_buffer` 增加支持后缀参数的重载,调用 `scratch-buffer-name` 时将后缀传递给 Scheme 侧。 +3. **Glue `glue_basic.lua`**: + - 导出支持指定后缀的新建 buffer 接口至 Scheme 层; + - 在 Scheme 侧封装 `(new-buffer . opt-ext)`,保持向后兼容无参调用的同时支持指定扩展名。 + +### 7.2 导出样式包与保存/另存为联动 + +1. **原文档关联与建议路径推导**: + - 在 `source-edit.scm` 中定义样式包原文档关联表,导出时记录 `(style-package-set-origin! new-buf orig-buf)`; + - 提供 `(style-package-target-url buf)`:当原文档存在时,取得其所在目录和去除扩展名后的根文件名,拼接 `.stem` 作为目标建议路径(例如 `/path/to/xxx.tmu` → `/path/to/xxx.stem`)。 +2. **保存与另存为(`abbrevs.scm` 与 `qt_chooser_widget.cpp`)**: + - 在 `choose-file`(`abbrevs.scm`)中,若当前 buffer 是样式包草稿,将建议路径设为原文档目录下的同名 `.stem` 路径; + - 在 `qt_chooser_widget.cpp` 中,针对 `action_save_as`,当传入的文件名或当前 buffer 为 `.stem` 时,确保激活 `STEM files (*.stem)` 过滤器与 `stem` 默认后缀。 +3. **菜单项清理(`source-menu.scm`)**: + - 在 `source-macros-menu` 宏菜单中移除「导出样式文件」菜单项; + - 「导出样式包」调用 `(extract-style-package)`。 From 69e4162119144afdee9782c6afd748927d27a1cc Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 16:06:06 +0800 Subject: [PATCH 2/3] =?UTF-8?q?[1293]=20=E8=8D=89=E7=A8=BF=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=94=BE=E5=BC=80=20.stem=20=E5=90=8E=E7=BC=80?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B9=B6=E5=AF=BC=E5=87=BA=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E5=8C=85=E4=B8=BA=20.stem=20=E8=8D=89=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TeXmacs/progs/kernel/boot/abbrevs.scm | 8 +- TeXmacs/progs/source/source-edit.scm | 37 ++++++-- TeXmacs/progs/source/source-menu.scm | 3 +- .../tests/extract-style-package-test.scm | 84 +++++++++++++++++++ .../progs/texmacs/texmacs/tm-files-test.scm | 26 ++++++ TeXmacs/progs/texmacs/texmacs/tm-files.scm | 67 ++++++++++----- src/Plugins/Qt/qt_chooser_widget.cpp | 11 ++- src/Scheme/Glue/glue_basic.lua | 10 ++- src/Texmacs/Data/new_buffer.cpp | 12 ++- src/Texmacs/Data/new_buffer.hpp | 1 + src/Texmacs/Data/new_window.cpp | 9 +- src/Texmacs/Data/new_window.hpp | 1 + 12 files changed, 232 insertions(+), 37 deletions(-) create mode 100644 TeXmacs/progs/source/tests/extract-style-package-test.scm diff --git a/TeXmacs/progs/kernel/boot/abbrevs.scm b/TeXmacs/progs/kernel/boot/abbrevs.scm index 8d62aa9b27..094d4b3bb6 100644 --- a/TeXmacs/progs/kernel/boot/abbrevs.scm +++ b/TeXmacs/progs/kernel/boot/abbrevs.scm @@ -222,13 +222,19 @@ (when (null? (cdr opts)) ;; Issue #327: Use last file dialog directory if current buffer is scratch (let* ((master (buffer-get-master (current-buffer))) + (style-target (and (defined? 'style-package-target-url) + (style-package-target-url (current-buffer)) + ) ;and + ) ;style-target (last-dir (and (url-scratch? master) + (not style-target) (defined? 'get-last-file-dialog-directory) (get-last-file-dialog-directory) ) ;and ) ;last-dir ) ; - (cond ((and last-dir (string? last-dir) (not (string-null? last-dir))) + (cond (style-target (set! opts (list (car opts) style-target))) + ((and last-dir (string? last-dir) (not (string-null? last-dir))) (set! opts (list (car opts) (system->url last-dir))) ) ; ((url-scratch? master) diff --git a/TeXmacs/progs/source/source-edit.scm b/TeXmacs/progs/source/source-edit.scm index 5750c885af..c626ccebab 100644 --- a/TeXmacs/progs/source/source-edit.scm +++ b/TeXmacs/progs/source/source-edit.scm @@ -192,9 +192,31 @@ ) ;with ) ;define -(tm-define (extract-style-file style?) - (let* ((tit (extract-source-title style?)) - (packs (extract-use-package style?)) +(define style-package-origins (make-ahash-table)) + +(tm-define (style-package-set-origin! buf orig) + (ahash-set! style-package-origins buf orig) +) ;tm-define + +(tm-define (style-package-get-origin buf) (ahash-ref style-package-origins buf)) + +(tm-define (style-package-target-url buf) + (and-with orig + (style-package-get-origin buf) + (cond ((or (url-scratch? orig) (url-rooted-tmfs? orig)) + (url-append (get-documents-path) + (string-append "LiiiSTEM/" (url-basename orig) ".stem") + ) ;url-append + ) ; + (else (url-append (url-head orig) (string-append (url-basename orig) ".stem"))) + ) ;cond + ) ;and-with +) ;tm-define + +(tm-define (extract-style-package) + (let* ((orig (current-buffer)) + (tit (extract-source-title #f)) + (packs (extract-use-package #f)) (inits (extract-style-parameters)) (defs (extract-macro-definitions)) (body `(document ,tit ,@packs ,@inits ,@defs)) @@ -203,7 +225,12 @@ (body ,body)) ) ;doc ) ; - (new-buffer) - (delayed (:idle 1) (buffer-set (current-buffer) doc)) + (new-buffer ".stem") + (let ((new-buf (current-buffer))) + (style-package-set-origin! new-buf orig) + (delayed (:idle 1) (buffer-set (current-buffer) doc)) + ) ;let ) ;let* ) ;tm-define + +(tm-define (extract-style-file style?) (extract-style-package)) diff --git a/TeXmacs/progs/source/source-menu.scm b/TeXmacs/progs/source/source-menu.scm index cf6ac44b1e..659690dc6e 100644 --- a/TeXmacs/progs/source/source-menu.scm +++ b/TeXmacs/progs/source/source-menu.scm @@ -175,8 +175,7 @@ ("Edit macros" (open-macros-editor :global)) ("Edit preamble" (toggle-preamble-mode)) --- - ("Extract style file" (extract-style-file #t)) - ("Extract style package" (extract-style-file #f)) + ("Extract style package" (extract-style-package)) ) ;menu-bind (menu-bind source-menu diff --git a/TeXmacs/progs/source/tests/extract-style-package-test.scm b/TeXmacs/progs/source/tests/extract-style-package-test.scm new file mode 100644 index 0000000000..37fc0a763c --- /dev/null +++ b/TeXmacs/progs/source/tests/extract-style-package-test.scm @@ -0,0 +1,84 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; MODULE : extract-style-package-test.scm +;; DESCRIPTION : 单元测试:导出样式包生成 .stem 草稿文件及宏菜单结构验证 +;; COPYRIGHT : (C) 2026 Mogan STEM +;; +;; USAGE +;; xmake b stem +;; xmake r extract-style-package-test +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(import (liii check)) + +(check-set-mode! 'report-failed) + +(load "./TeXmacs/progs/source/source-menu.scm") +(load "./TeXmacs/progs/source/source-edit.scm") + +(define (menu-contains-label? m label) + (cond ((null? m) #f) + ((pair? m) + (or (menu-contains-label? (car m) label) + (menu-contains-label? (cdr m) label))) + ((string? m) (string=? m label)) + (else #f))) + +(define (test-source-macros-menu-items) + (let ((menu (source-macros-menu))) + ;; 验证已移除 "Extract style file",仅保留 "Extract style package" + (check (menu-contains-label? menu "Extract style file") => #f) + (check (menu-contains-label? menu "Extract style package") => #t))) + +(define (test-extract-style-package-stem-buffer) + (let ((orig-buf (current-buffer))) + (extract-style-package) + (let* ((new-buf (current-buffer)) + (name (url->string (url-tail new-buf)))) + ;; 验证新创建的 buffer 是 .stem 后缀的草稿文件 + (check (string-starts? name "draft_") => #t) + (check (string-ends? name ".stem") => #t) + (check (url-scratch? new-buf) => #t) + ;; 验证记录了原文档关联 + (check (style-package-get-origin new-buf) => orig-buf)) + ;; 切回原 buffer 并关闭测试草稿 buffer + (buffer-close (current-buffer)) + (switch-to-buffer orig-buf))) + +(define (test-style-package-target-url) + (let ((fake-buf (system->url "/home/da/docs/draft_demo.stem")) + (orig-tmu (system->url "/home/da/docs/paper.tmu")) + (orig-tm (system->url "/home/da/projects/report.tm")) + (orig-cjk (system->url "/home/da/文档/测试.tmu")) + ) ; + (style-package-set-origin! fake-buf orig-tmu) + (check (url->system (style-package-target-url fake-buf)) + => "/home/da/docs/paper.stem") + + (style-package-set-origin! fake-buf orig-tm) + (check (url->system (style-package-target-url fake-buf)) + => "/home/da/projects/report.stem") + + (style-package-set-origin! fake-buf orig-cjk) + (check (url->system (style-package-target-url fake-buf)) + => "/home/da/文档/测试.stem") + ) ;let +) ;define + +(define (test-extract-style-package-target-resolution) + (let* ((orig-buf (system->url "/tmp/my_paper.tmu")) + (new-buf (system->url "/home/da/Documents/LiiiSTEM/no_name/draft_20260909_120000.stem")) + ) ; + (style-package-set-origin! new-buf orig-buf) + (check (url->system (style-package-target-url new-buf)) + => "/tmp/my_paper.stem") + ) ;let* +) ;define + +(tm-define (regtest-extract-style-package) + (test-source-macros-menu-items) + (test-extract-style-package-stem-buffer) + (test-style-package-target-url) + (test-extract-style-package-target-resolution) + (check-report)) diff --git a/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm b/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm index 0335bd8604..4321310446 100644 --- a/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm +++ b/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm @@ -80,6 +80,30 @@ ) ;let* ) ;define +(define (test-scratch-buffer-name-stem) + (let* ((path (scratch-buffer-name ".stem")) + (name (url->string (url-tail (system->url path)))) + (stamp (substring name 6 (- (string-length name) 5))) + ) ; + (check (string-starts? name "draft_") => #t) + (check (string-ends? name ".stem") => #t) + (check (string-length stamp) => 15) + (check (substring stamp 8 9) => "_") + ) ;let* +) ;define + +(define (test-scratch-buffer-title-stem) + (let ((tmu-title (scratch-buffer-title (draft-test-url "draft_20250802_153000.tmu"))) + (stem-title (scratch-buffer-title (draft-test-url "draft_20250802_153000.stem")) + ) ;stem-title + (stem-n-title (scratch-buffer-title (draft-test-url "draft_20250802_153000-1.stem")) + ) ;stem-n-title + ) ; + (check stem-title => tmu-title) + (check stem-n-title => tmu-title) + ) ;let +) ;define + (define (test-scratch-buffer-title-old-and-new-stamp) ;; 往年草稿不显示时刻,新旧文件名必须得到同一标题 (let ((old (scratch-buffer-title (draft-test-url "draft_20250802153000.tmu"))) @@ -122,6 +146,8 @@ (test-auto-backup-official-url) (test-auto-backup-texmacs-path-buffer?) (test-scratch-buffer-name-has-date-time-underscore) + (test-scratch-buffer-name-stem) + (test-scratch-buffer-title-stem) (test-scratch-buffer-title-old-and-new-stamp) (test-scratch-buffer-title-legacy-one-underscore-this-week) (check-report) diff --git a/TeXmacs/progs/texmacs/texmacs/tm-files.scm b/TeXmacs/progs/texmacs/texmacs/tm-files.scm index 4bd476a046..3a3572da41 100644 --- a/TeXmacs/progs/texmacs/texmacs/tm-files.scm +++ b/TeXmacs/progs/texmacs/texmacs/tm-files.scm @@ -573,7 +573,7 @@ (save-buffer-check-faithful name opts) (choose-file (lambda (x) (apply save-buffer-as-main (cons x opts))) "Save TeXmacs file" - "tmu" + "action_save_as" ) ;choose-file ) ;if ) ; @@ -1201,57 +1201,80 @@ (and (not (buffer-exists? u)) (not (url-exists? u))) ) ;define -(define (scratch-candidate dir stamp) - (url-append dir (string-append "draft_" stamp ".tmu")) +(define (normalize-draft-ext ext) + (cond ((or (eq? ext 'stem) (equal? ext "stem") (equal? ext ".stem")) ".stem") + (else ".tmu") + ) ;cond +) ;define + +(define (scratch-candidate dir stamp ext) + (url-append dir (string-append "draft_" stamp ext)) ) ;define ;; 秒级名字仍冲突时,追加 -2、-3 …… 保证唯一 -(define (scratch-unique-name dir stamp i) +(define (scratch-unique-name dir stamp i ext) (let ((u (scratch-candidate dir (if (= i 0) stamp (string-append stamp "-" (number->string i))) + ext ) ;scratch-candidate ) ;u ) ; - (if (scratch-name-free? u) u (scratch-unique-name dir stamp (+ i 1))) + (if (scratch-name-free? u) u (scratch-unique-name dir stamp (+ i 1) ext)) ) ;let ) ;define ;; 新 scratch buffer 的名字:日期与时刻用 _ 分开,精确到秒,冲突加 -N -;; 例: draft_20260901_194700.tmu / draft_20260901_194700-1.tmu +;; 例: draft_20260901_194700.tmu / draft_20260901_194700-1.tmu / .stem ;; 返回系统路径字符串(供 C++ make_new_buffer 使用) -(tm-define (scratch-buffer-name) +(tm-define (scratch-buffer-name . opt-ext) (with dir (scratch-buffer-dir) (when (not (url-exists? dir)) (system-mkdir dir) ) ;when - (with full - (date->string (current-date) "~Y~m~d_~H~M~S") - (url->system (scratch-unique-name dir full 0)) - ) ;with + (let* ((ext (if (null? opt-ext) ".tmu" (normalize-draft-ext (car opt-ext)))) + (full (date->string (current-date) "~Y~m~d_~H~M~S")) + ) ; + (url->system (scratch-unique-name dir full 0 ext)) + ) ;let* ) ;with ) ;tm-define +(tm-define (new-buffer . opt-ext) + (if (null? opt-ext) + (cpp-new-buffer) + (cpp-new-buffer-with-ext (normalize-draft-ext (car opt-ext))) + ) ;if +) ;tm-define + ;; draft 文件名 → 纯数字时间戳,供标题按位切分(YYYYMMDDHH[MM][SS])。 ;; 新格式 "draft_20260901_194700.tmu" → "20260901194700"; +;; ".stem" 格式 "draft_20260901_194700.stem" → "20260901194700"; ;; 旧格式 "draft_202608242127-2.tmu" → "202608242127"; ;; 非 draft 名返回 #f (define (draft-stamp u) (with name (url->string (url-tail u)) - (and (string-starts? name "draft_") - (string-ends? name ".tmu") - (let* ((body (substring name 6 (- (string-length name) 4))) - (cut (or (string-index body #\-) (string-length body))) - (raw (substring body 0 cut)) - ;; 去掉日期与时刻之间的 _,标题逻辑仍按连续数字下标切 - (digits (string-join (string-split raw "_") "")) - ) ; - (and (>= (string-length digits) 12) digits) - ) ;let* - ) ;and + (let ((ext-len (cond ((string-ends? name ".tmu") 4) + ((string-ends? name ".stem") 5) + (else #f) + ) ;cond + ) ;ext-len + ) ; + (and ext-len + (string-starts? name "draft_") + (let* ((body (substring name 6 (- (string-length name) ext-len))) + (cut (or (string-index body #\-) (string-length body))) + (raw (substring body 0 cut)) + ;; 去掉日期与时刻之间的 _,标题逻辑仍按连续数字下标切 + (digits (string-join (string-split raw "_") "")) + ) ; + (and (>= (string-length digits) 12) digits) + ) ;let* + ) ;and + ) ;let ) ;with ) ;define diff --git a/src/Plugins/Qt/qt_chooser_widget.cpp b/src/Plugins/Qt/qt_chooser_widget.cpp index 1b846957f6..8fa1f4c672 100644 --- a/src/Plugins/Qt/qt_chooser_widget.cpp +++ b/src/Plugins/Qt/qt_chooser_widget.cpp @@ -16,6 +16,7 @@ #include "editor.hpp" #include "image_files.hpp" #include "message.hpp" +#include "new_buffer.hpp" #include "new_view.hpp" // get_current_editor() #include "qt_utilities.hpp" #include "scheme.hpp" @@ -325,7 +326,15 @@ qt_chooser_widget_rep::perform_dialog () { // .tm 默认 .tmu(过滤器保持 tmu/tm);建议文件名同步改为默认后缀,让默认 // 目标在弹窗时即可见;普通保存仍写回原文件 string save_as_target; // 非空时最终文件名规范为所选过滤器的后缀 - if (type == "action_save_as") save_as_target= chooser_save_as_target (file); + if (type == "action_save_as") { + save_as_target= chooser_save_as_target (file); + if (save_as_target == "") { + url cur_buf= get_current_buffer_safe (); + if (!is_none (cur_buf) && ends (as_string (tail (cur_buf)), ".stem")) { + save_as_target= "stem"; + } + } + } QString caption= to_qstring (win_title); c_string tmp (directory * "/" * file); diff --git a/src/Scheme/Glue/glue_basic.lua b/src/Scheme/Glue/glue_basic.lua index a218394b6d..12a141d2e0 100644 --- a/src/Scheme/Glue/glue_basic.lua +++ b/src/Scheme/Glue/glue_basic.lua @@ -1758,10 +1758,18 @@ function main() }, { - scm_name = "new-buffer", + scm_name = "cpp-new-buffer", cpp_name = "create_buffer", ret_type = "url" }, + { + scm_name = "cpp-new-buffer-with-ext", + cpp_name = "create_buffer", + ret_type = "url", + arg_list = { + "string" + } + }, { scm_name = "open-buffer-in-window", cpp_name = "new_buffer_in_new_window", diff --git a/src/Texmacs/Data/new_buffer.cpp b/src/Texmacs/Data/new_buffer.cpp index cf90d8ac50..90a8e15801 100644 --- a/src/Texmacs/Data/new_buffer.cpp +++ b/src/Texmacs/Data/new_buffer.cpp @@ -209,14 +209,19 @@ make_welcome_buffer () { } url -make_new_buffer () { +make_new_buffer (string ext) { // 新建标签页时 tm-files 必已加载,scratch-buffer-name 保证返回唯一名 - string s = as_string (call ("scratch-buffer-name")); + string s = as_string (call ("scratch-buffer-name", ext)); url name= url_system (s); set_buffer_tree (name, tree (DOCUMENT)); return name; } +url +make_new_buffer () { + return make_new_buffer (".tmu"); +} + bool buffer_has_name (url name) { return !is_scratch (name); @@ -229,7 +234,8 @@ buffer_has_name (url name) { string propose_title (string old_title, url u, tree doc) { string name= as_string (tail (u)); - if (starts (name, "draft_") && ends (name, ".tmu")) { + if (starts (name, "draft_") && + (ends (name, ".tmu") || ends (name, ".stem"))) { // 标题规则(Draft Monday / 草稿(周一21:27)等)由 scheme 侧实现 try { name= as_string (call ("scratch-buffer-title", object (u))); diff --git a/src/Texmacs/Data/new_buffer.hpp b/src/Texmacs/Data/new_buffer.hpp index f5e1392aeb..de92fc64a3 100644 --- a/src/Texmacs/Data/new_buffer.hpp +++ b/src/Texmacs/Data/new_buffer.hpp @@ -79,6 +79,7 @@ tm_buffer concrete_buffer_insist (url name); array get_all_buffers (); url make_welcome_buffer (); +url make_new_buffer (string ext); url make_new_buffer (); void remove_buffer (url name); int number_buffers (); diff --git a/src/Texmacs/Data/new_window.cpp b/src/Texmacs/Data/new_window.cpp index 8da1a2141f..d6ceb94083 100644 --- a/src/Texmacs/Data/new_window.cpp +++ b/src/Texmacs/Data/new_window.cpp @@ -293,12 +293,17 @@ new_buffer_in_new_window (url name, tree doc, tree geom) { ******************************************************************************/ url -create_buffer () { - url name= make_new_buffer (); +create_buffer (string ext) { + url name= make_new_buffer (ext); switch_to_buffer (name); return name; } +url +create_buffer () { + return create_buffer (".tmu"); +} + url open_window (tree geom) { url name= make_new_buffer (); diff --git a/src/Texmacs/Data/new_window.hpp b/src/Texmacs/Data/new_window.hpp index 1c07c705fb..f5a7010d2f 100644 --- a/src/Texmacs/Data/new_window.hpp +++ b/src/Texmacs/Data/new_window.hpp @@ -26,6 +26,7 @@ void window_focus (url win); void switch_to_window (url win); void switch_to_parent_window (); +url create_buffer (string ext); url create_buffer (); url new_window (bool map_flag= true, tree geom= "", bool force_tab_bar= false); url open_window (tree geom= ""); From 89f870229b58fb798ad21b864e7c46a45740646f Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 17:05:22 +0800 Subject: [PATCH 3/3] =?UTF-8?q?[1293]=20=E6=B8=85=E7=90=86=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E5=8C=85=E5=AF=BC=E5=87=BA=E6=94=B9=E5=8A=A8=EF=BC=9A?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=20suffix/url-suffix=E3=80=81=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=AD=BB=E4=BB=A3=E7=A0=81=E3=80=81=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=20new-buffer=20=E5=90=8E=E7=BC=80=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- TeXmacs/progs/source/source-edit.scm | 40 +++++------ .../tests/extract-style-package-test.scm | 67 ++++++++----------- .../progs/texmacs/texmacs/tm-files-test.scm | 7 +- TeXmacs/progs/texmacs/texmacs/tm-files.scm | 32 +++------ devel/1293.md | 18 +++++ src/Plugins/Qt/qt_chooser_widget.cpp | 2 +- src/Scheme/Glue/glue_basic.lua | 5 -- src/Texmacs/Data/new_window.cpp | 5 -- src/Texmacs/Data/new_window.hpp | 1 - 9 files changed, 79 insertions(+), 98 deletions(-) diff --git a/TeXmacs/progs/source/source-edit.scm b/TeXmacs/progs/source/source-edit.scm index c626ccebab..a5a3c68d0e 100644 --- a/TeXmacs/progs/source/source-edit.scm +++ b/TeXmacs/progs/source/source-edit.scm @@ -192,26 +192,19 @@ ) ;with ) ;define -(define style-package-origins (make-ahash-table)) +(define style-package-targets (make-ahash-table)) -(tm-define (style-package-set-origin! buf orig) - (ahash-set! style-package-origins buf orig) -) ;tm-define - -(tm-define (style-package-get-origin buf) (ahash-ref style-package-origins buf)) +(tm-define (style-package-target-url buf) (ahash-ref style-package-targets buf)) -(tm-define (style-package-target-url buf) - (and-with orig - (style-package-get-origin buf) - (cond ((or (url-scratch? orig) (url-rooted-tmfs? orig)) - (url-append (get-documents-path) - (string-append "LiiiSTEM/" (url-basename orig) ".stem") - ) ;url-append - ) ; - (else (url-append (url-head orig) (string-append (url-basename orig) ".stem"))) - ) ;cond - ) ;and-with -) ;tm-define +(define (style-package-compute-target orig) + (cond ((or (url-scratch? orig) (url-rooted-tmfs? orig)) + (url-append (get-documents-path) + (string-append "LiiiSTEM/" (url-basename orig) ".stem") + ) ;url-append + ) ; + (else (url-append (url-head orig) (string-append (url-basename orig) ".stem"))) + ) ;cond +) ;define (tm-define (extract-style-package) (let* ((orig (current-buffer)) @@ -226,11 +219,10 @@ ) ;doc ) ; (new-buffer ".stem") - (let ((new-buf (current-buffer))) - (style-package-set-origin! new-buf orig) - (delayed (:idle 1) (buffer-set (current-buffer) doc)) - ) ;let + (ahash-set! style-package-targets + (current-buffer) + (style-package-compute-target orig) + ) ;ahash-set! + (delayed (:idle 1) (buffer-set (current-buffer) doc)) ) ;let* ) ;tm-define - -(tm-define (extract-style-file style?) (extract-style-package)) diff --git a/TeXmacs/progs/source/tests/extract-style-package-test.scm b/TeXmacs/progs/source/tests/extract-style-package-test.scm index 37fc0a763c..0fd297c9a5 100644 --- a/TeXmacs/progs/source/tests/extract-style-package-test.scm +++ b/TeXmacs/progs/source/tests/extract-style-package-test.scm @@ -20,65 +20,56 @@ (define (menu-contains-label? m label) (cond ((null? m) #f) ((pair? m) - (or (menu-contains-label? (car m) label) - (menu-contains-label? (cdr m) label))) + (or (menu-contains-label? (car m) label) (menu-contains-label? (cdr m) label)) + ) ; ((string? m) (string=? m label)) - (else #f))) + (else #f) + ) ;cond +) ;define (define (test-source-macros-menu-items) (let ((menu (source-macros-menu))) ;; 验证已移除 "Extract style file",仅保留 "Extract style package" (check (menu-contains-label? menu "Extract style file") => #f) - (check (menu-contains-label? menu "Extract style package") => #t))) + (check (menu-contains-label? menu "Extract style package") => #t) + ) ;let +) ;define (define (test-extract-style-package-stem-buffer) (let ((orig-buf (current-buffer))) (extract-style-package) - (let* ((new-buf (current-buffer)) - (name (url->string (url-tail new-buf)))) + (let* ((new-buf (current-buffer)) (name (url->string (url-tail new-buf)))) ;; 验证新创建的 buffer 是 .stem 后缀的草稿文件 (check (string-starts? name "draft_") => #t) (check (string-ends? name ".stem") => #t) (check (url-scratch? new-buf) => #t) - ;; 验证记录了原文档关联 - (check (style-package-get-origin new-buf) => orig-buf)) + ;; 验证记录了目标保存路径 + (check (url? (style-package-target-url new-buf)) => #t) + ) ;let* ;; 切回原 buffer 并关闭测试草稿 buffer (buffer-close (current-buffer)) - (switch-to-buffer orig-buf))) - -(define (test-style-package-target-url) - (let ((fake-buf (system->url "/home/da/docs/draft_demo.stem")) - (orig-tmu (system->url "/home/da/docs/paper.tmu")) - (orig-tm (system->url "/home/da/projects/report.tm")) - (orig-cjk (system->url "/home/da/文档/测试.tmu")) - ) ; - (style-package-set-origin! fake-buf orig-tmu) - (check (url->system (style-package-target-url fake-buf)) - => "/home/da/docs/paper.stem") - - (style-package-set-origin! fake-buf orig-tm) - (check (url->system (style-package-target-url fake-buf)) - => "/home/da/projects/report.stem") - - (style-package-set-origin! fake-buf orig-cjk) - (check (url->system (style-package-target-url fake-buf)) - => "/home/da/文档/测试.stem") + (switch-to-buffer orig-buf) ) ;let ) ;define -(define (test-extract-style-package-target-resolution) - (let* ((orig-buf (system->url "/tmp/my_paper.tmu")) - (new-buf (system->url "/home/da/Documents/LiiiSTEM/no_name/draft_20260909_120000.stem")) - ) ; - (style-package-set-origin! new-buf orig-buf) - (check (url->system (style-package-target-url new-buf)) - => "/tmp/my_paper.stem") - ) ;let* +(define (test-style-package-compute-target) + (for-each (lambda (case + ) ;case + (check (url->system (style-package-compute-target (system->url (car case)))) + => + (cdr case) + ) ;check + ) ;lambda + '(("/home/da/docs/paper.tmu" . "/home/da/docs/paper.stem") + ("/home/da/projects/report.tm" . "/home/da/projects/report.stem") + ("/home/da/文档/测试.tmu" . "/home/da/文档/测试.stem") + ("/tmp/my_paper.tmu" . "/tmp/my_paper.stem")) + ) ;for-each ) ;define (tm-define (regtest-extract-style-package) (test-source-macros-menu-items) (test-extract-style-package-stem-buffer) - (test-style-package-target-url) - (test-extract-style-package-target-resolution) - (check-report)) + (test-style-package-compute-target) + (check-report) +) ;tm-define diff --git a/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm b/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm index 4321310446..1603e0a553 100644 --- a/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm +++ b/TeXmacs/progs/texmacs/texmacs/tm-files-test.scm @@ -60,7 +60,8 @@ ) ;define (define (draft-name-stamp-part name) - (let* ((body (substring name 6 (- (string-length name) 4))) + (let* ((dot (string-rindex name #\.)) + (body (substring name 6 dot)) (cut (or (string-index body #\-) (string-length body))) ) ; (substring body 0 cut) @@ -68,7 +69,7 @@ ) ;define (define (test-scratch-buffer-name-has-date-time-underscore) - (let* ((path (scratch-buffer-name)) + (let* ((path (scratch-buffer-name ".tmu")) (name (url->string (url-tail (system->url path)))) (stamp (draft-name-stamp-part name)) ) ; @@ -83,7 +84,7 @@ (define (test-scratch-buffer-name-stem) (let* ((path (scratch-buffer-name ".stem")) (name (url->string (url-tail (system->url path)))) - (stamp (substring name 6 (- (string-length name) 5))) + (stamp (draft-name-stamp-part name)) ) ; (check (string-starts? name "draft_") => #t) (check (string-ends? name ".stem") => #t) diff --git a/TeXmacs/progs/texmacs/texmacs/tm-files.scm b/TeXmacs/progs/texmacs/texmacs/tm-files.scm index 3a3572da41..03ee4aa5bc 100644 --- a/TeXmacs/progs/texmacs/texmacs/tm-files.scm +++ b/TeXmacs/progs/texmacs/texmacs/tm-files.scm @@ -1202,9 +1202,7 @@ ) ;define (define (normalize-draft-ext ext) - (cond ((or (eq? ext 'stem) (equal? ext "stem") (equal? ext ".stem")) ".stem") - (else ".tmu") - ) ;cond + (if (equal? ext ".stem") ".stem" ".tmu") ) ;define (define (scratch-candidate dir stamp ext) @@ -1227,25 +1225,22 @@ ;; 新 scratch buffer 的名字:日期与时刻用 _ 分开,精确到秒,冲突加 -N ;; 例: draft_20260901_194700.tmu / draft_20260901_194700-1.tmu / .stem ;; 返回系统路径字符串(供 C++ make_new_buffer 使用) -(tm-define (scratch-buffer-name . opt-ext) +(tm-define (scratch-buffer-name ext) (with dir (scratch-buffer-dir) (when (not (url-exists? dir)) (system-mkdir dir) ) ;when - (let* ((ext (if (null? opt-ext) ".tmu" (normalize-draft-ext (car opt-ext)))) - (full (date->string (current-date) "~Y~m~d_~H~M~S")) - ) ; - (url->system (scratch-unique-name dir full 0 ext)) - ) ;let* + (with full + (date->string (current-date) "~Y~m~d_~H~M~S") + (url->system (scratch-unique-name dir full 0 (normalize-draft-ext ext))) + ) ;with ) ;with ) ;tm-define (tm-define (new-buffer . opt-ext) - (if (null? opt-ext) - (cpp-new-buffer) - (cpp-new-buffer-with-ext (normalize-draft-ext (car opt-ext))) - ) ;if + (cpp-new-buffer-with-ext (if (null? opt-ext) ".tmu" (normalize-draft-ext (car opt-ext))) + ) ;cpp-new-buffer-with-ext ) ;tm-define ;; draft 文件名 → 纯数字时间戳,供标题按位切分(YYYYMMDDHH[MM][SS])。 @@ -1257,15 +1252,10 @@ (define (draft-stamp u) (with name (url->string (url-tail u)) - (let ((ext-len (cond ((string-ends? name ".tmu") 4) - ((string-ends? name ".stem") 5) - (else #f) - ) ;cond - ) ;ext-len - ) ; - (and ext-len + (let ((ext (url-suffix u))) + (and (in? ext '("tmu" "stem")) (string-starts? name "draft_") - (let* ((body (substring name 6 (- (string-length name) ext-len))) + (let* ((body (substring name 6 (- (string-length name) (string-length ext) 1))) (cut (or (string-index body #\-) (string-length body))) (raw (substring body 0 cut)) ;; 去掉日期与时刻之间的 _,标题逻辑仍按连续数字下标切 diff --git a/devel/1293.md b/devel/1293.md index 0f0751cd55..4ab9c8bc80 100644 --- a/devel/1293.md +++ b/devel/1293.md @@ -113,3 +113,21 @@ PR 提交流程: 3. **菜单项清理(`source-menu.scm`)**: - 在 `source-macros-menu` 宏菜单中移除「导出样式文件」菜单项; - 「导出样式包」调用 `(extract-style-package)`。 + +## 8 /simplify 清理(2026-09-09) + +What:对本次 diff 做复用/简化/效率/抽象层级四个维度的清理。 + +How 与涉及文件: +1. **复用**: + - `qt_chooser_widget.cpp` 改用 `suffix (cur_buf) == "stem"` 判断后缀(复用 lolly `suffix()`,自动小写归一),替代手写 `ends (as_string (tail ...))`; + - `draft-stamp`(`tm-files.scm`)改用 `url-suffix` 取扩展名,按扩展名长度截取主体,替代硬编码 4/5 字符长度表; + - `tm-files-test.scm` 的 `draft-name-stamp-part` 改为按 `.` 定位截取,`.tmu`/`.stem` 两个用例共用。 +2. **简化**: + - 删除无调用者的 `extract-style-file` 兼容包装(`source-edit.scm`); + - 样式包关联表改为直接存储目标路径:导出时调用 `style-package-compute-target` 计算一次并存入 `style-package-targets`,`style-package-target-url` 退化为单次 `ahash-ref`,删除 `set-origin!`/`get-origin` 两个接口; + - `normalize-draft-ext` 只接受规范形式 `".stem"`,其余一律 `".tmu"`; + - `new-buffer` 统一走 `cpp-new-buffer-with-ext`(缺省传 `".tmu"`),删除 `cpp-new-buffer` glue 项与 C++ 无参 `create_buffer()` 重载;`scratch-buffer-name` 改为必传后缀。 +3. **测试**:`extract-style-package-test.scm` 将目标路径用例合并为 `for-each` 数据驱动,删除重复用例,断言改为校验记录的目标路径。 + +未处理(跳过):样式包目标表在 buffer 关闭时不清条目(缓慢增长,量极小);`choose-file` 的通用「建议保存路径」钩子重构;C++ 侧 `propose_title` 草稿后缀判断下沉到 scheme——均需更大范围改动,留待后续。 diff --git a/src/Plugins/Qt/qt_chooser_widget.cpp b/src/Plugins/Qt/qt_chooser_widget.cpp index 8fa1f4c672..7e5e67c695 100644 --- a/src/Plugins/Qt/qt_chooser_widget.cpp +++ b/src/Plugins/Qt/qt_chooser_widget.cpp @@ -330,7 +330,7 @@ qt_chooser_widget_rep::perform_dialog () { save_as_target= chooser_save_as_target (file); if (save_as_target == "") { url cur_buf= get_current_buffer_safe (); - if (!is_none (cur_buf) && ends (as_string (tail (cur_buf)), ".stem")) { + if (!is_none (cur_buf) && suffix (cur_buf) == "stem") { save_as_target= "stem"; } } diff --git a/src/Scheme/Glue/glue_basic.lua b/src/Scheme/Glue/glue_basic.lua index 12a141d2e0..3ea4437f33 100644 --- a/src/Scheme/Glue/glue_basic.lua +++ b/src/Scheme/Glue/glue_basic.lua @@ -1757,11 +1757,6 @@ function main() ret_type = "void", }, - { - scm_name = "cpp-new-buffer", - cpp_name = "create_buffer", - ret_type = "url" - }, { scm_name = "cpp-new-buffer-with-ext", cpp_name = "create_buffer", diff --git a/src/Texmacs/Data/new_window.cpp b/src/Texmacs/Data/new_window.cpp index d6ceb94083..c56b59ce64 100644 --- a/src/Texmacs/Data/new_window.cpp +++ b/src/Texmacs/Data/new_window.cpp @@ -299,11 +299,6 @@ create_buffer (string ext) { return name; } -url -create_buffer () { - return create_buffer (".tmu"); -} - url open_window (tree geom) { url name= make_new_buffer (); diff --git a/src/Texmacs/Data/new_window.hpp b/src/Texmacs/Data/new_window.hpp index f5a7010d2f..8f224ec616 100644 --- a/src/Texmacs/Data/new_window.hpp +++ b/src/Texmacs/Data/new_window.hpp @@ -27,7 +27,6 @@ void switch_to_window (url win); void switch_to_parent_window (); url create_buffer (string ext); -url create_buffer (); url new_window (bool map_flag= true, tree geom= "", bool force_tab_bar= false); url open_window (tree geom= ""); url ensure_window (tree geom= "");