Skip to content
Merged
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
105 changes: 89 additions & 16 deletions devel/1143.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,87 @@

## 2 任务相关的代码文件
- `TeXmacs/progs/init-research.scm` — OPEN 事件埋点(:698)及之后的初始化收尾
- `src/Texmacs/Server/tm_server.cpp` — `init_app()`(:52)同步 `exec_file` 加载 init-research.scm
- `src/Plugins/Qt/QTMStartupTabWidget.cpp` — STARTUP 事件埋点(:104,首次 showEvent)
- `src/Plugins/Qt/QTMHomePage.cpp` — 启动页首页,`loadRecentDocs`(:440)依赖 scheme 读最近文档
- `src/Plugins/Qt/qt_tm_widget.cpp` — 启动页 widget 创建(:1102 `sync_startup_tab_mode`)
- `src/Plugins/Qt/QTMOAuth.cpp` — `refreshToken`(:242,"Start refresh token...")
- `src/Plugins/MuPDF/mupdf_renderer.cpp` — MuPDF 渲染器初始化(:37)
- `src/Edit/Interface/edit_interface.cpp` — `bench_start ("resume")`(:115)
- `src/Edit/Interface/edit_interface.cpp` — `resume()` 与 `bench_start ("resume")`(:113-115)
- `src/Texmacs/Data/new_view.cpp` — `attach_view()`(:566-587),:585 触发 `resume()`
- `TeXmacs/progs/kernel/gui/kbd-define.scm` — `lazy-keyboard-force` 实现

## 3 代码路径(OPEN → STARTUP)

启动日志实测总耗时约 1.6s(14:41:40.690 → 14:41:42.298),各段如下:
### 3.1 总体调用链

1. **OPEN 埋点**(40.690)— `init-research.scm:692-708`:`init-telemetry` 后 `(track-event "OPEN" '())`
2. **texmacs-banner + "Initialization done"** — `init-research.scm:709-710`
3. **Benchmark 2(tm-fib 30)**(~111ms)— `init-research.scm:712-728`,每次启动都跑的遗留基准代码
4. **"Forcing delayed loads"**(~1.0s,最大头)— `init-research.scm:733-744`:`(lazy-keyboard-force #t)` 同步强制加载所有延迟的键盘/模块定义
5. **"Start refresh token..."** — `QTMOAuth.cpp:244`,QNetworkAccessManager 异步请求,不阻塞
6. **"Use MuPDF render" + 字体加载**(~180ms)— `mupdf_renderer.cpp:37` 首次渲染时初始化,加载 `ecrm10.tfm`/`ecss10.tfm`、`larm.enc`/`grmn.enc`
7. **bench "resume"**(173ms)— `edit_interface.cpp:115`
8. **STARTUP 埋点**(42.298)— `QTMStartupTabWidget::showEvent` 首次显示时 `telemetry_track ("STARTUP")`(`QTMStartupTabWidget.cpp:99-106`)
1. **scheme 引导**:`init_app()`(tm_server.cpp:52)`exec_file` 加载 `init-research.scm`
(`STEM_INIT_FILE`,tm_configure.hpp:44),全程同步、发生在任何窗口创建之前
2. **OPEN 埋点**:init-research.scm:692-708,`init-telemetry` 后 `(track-event "OPEN" '())`
3. **"Forcing delayed loads"**:init-research.scm:733-744,`(lazy-keyboard-force #t)`
同步强制加载所有延迟键盘/模块定义(当前已临时关闭)
4. **窗口与视图创建**:创建初始 buffer/view,经 `attach_view()`
(new_view.cpp:566-587)挂到窗口,:585 调 `vw->ed->resume()`
5. **resume(编辑器 chrome 重建)**:`edit_interface_rep::resume()`
(edit_interface.cpp:113+),详见 3.2
6. **启动页创建**:`qt_tm_widget_rep::sync_startup_tab_mode()`(qt_tm_widget.cpp:1071);
启动页文件名判定 `is_startup_tab_file`(:101),`startupTabMode` 置位于 :1080
与 :1759-1765,widget 创建于 :1102
7. **STARTUP 埋点**:`QTMStartupTabWidget::showEvent` 首次显示时
`telemetry_track ("STARTUP")`(QTMStartupTabWidget.cpp:99-106)

### 3.2 resume() 内部细节

> 术语:**chrome**(UI chrome,与 Chrome 浏览器无关)指窗口内容区以外的
> 界面框架部分——菜单栏、工具栏、状态栏、通知栏、侧栏等。本文中
> 「chrome 重建」即 `resume()` 对菜单/工具栏/通知栏/侧栏的重建。

`edit_interface_rep::resume()`(edit_interface.cpp:113+)逐条通过 `SERVER()`
调 scheme 重建编辑器 chrome:

- `menu_main ("(horizontal (link texmacs-menu))")` — 重建菜单栏
- `menu_icons (0..4, ...)` — 重建 main/mode/focus/extra 工具栏及 tab pages 栏
(共 5 次 menu 展开)
- `notification_bar (...)` — 通知栏
- `side_tools` / `bottom_tools` — 侧栏/底部工具(动态 scheme 表达式)
- 光标/排版通知(`notify_change (THE_FOCUS + THE_EXTENTS)` 等)

menu 展开(menu-expand)会执行大量 scheme 代码,是 resume 的主要开销。
`resume()` 的其他调用点:new_view.cpp:544(关非当前标签页后强制刷新)、
:739/741(attach/detach view)、:857、new_window.cpp:240。

**关键观察**:启动页模式下工具栏/菜单全部隐藏(qt_tm_widget.cpp:920-928、
:1413 起),但 `resume()` 仍无条件重建全部 chrome——进入首屏的这条路径上
这些是纯浪费,可推迟到用户首次打开文档时再做。

### 3.3 实测耗时对比

| 阶段 | 14:41(原始) | 14:58(删 Benchmark 后) | 15:02(再关闭 lazy-keyboard-force) | 15:43(kbd-force 恢复 + resume 守卫) |
|---|---|---|---|---|
| 内核引导 | ~520ms | ~517ms | ~473ms | ~500ms |
| Benchmark 1/2 | ~220ms | 已删 | 已删 | 已删 |
| lazy-keyboard-force | ~1.1s | ~1.23s | ~1ms(临时关闭) | ~1.27s |
| resume | 173ms | 222ms | 1064ms | **12ms** |
| OPEN → STARTUP | ~1.6s | ~1.65s | ~1.29s | **~1.46s** |

关闭 lazy-keyboard-force 后净省 ~0.4s,其余 ~0.8s 转移进 resume
(被关闭的惰性加载改在首次 resume/排版时逐个触发)。

resume 守卫(15:43 运行)使 resume 从 222ms 降到 12ms(菜单/工具栏重建推迟到
首个真实文档);此时 lazy-keyboard-force(~1.27s)成为 OPEN → STARTUP 阶段
唯一的大头。

## 4 优化方向

- ~~**移除 Benchmark 1/2**~~(已完成):`init-research.scm` 中两处 `fib 30` 遗留基准代码(原 :62-78、:712-728),各占 ~100ms,已删除
- **拆解 `lazy-keyboard-force`**:占 ~1s,可改为 idle 分批加载,或只强制加载首屏必需的键盘定义
- **MuPDF/字体预热**:首屏渲染触发的渲染器初始化和字体加载(~180ms)可移到 idle 预热
- ~~**移除 Benchmark 1/2**~~(已完成):`init-research.scm` 中两处 `fib 30`
遗留基准代码(原 :62-78、:712-728),各占 ~100ms,已删除
- ~~**关闭 `lazy-keyboard-force`**~~(已临时验证):净省 ~0.4s,但 ~0.8s 转移进
resume;需要确认惰性加载触发点不影响首次交互
- ~~**启动页模式下推迟 `resume()` 的 chrome 重建**~~(已完成):`resume()`
照搬 `update_menus()` 的 `is_startup_tab_buffer` 守卫(edit_interface.cpp:118-139),
启动页 buffer 跳过 menu_main/menu_icons 0-3/notification_bar/side_tools,
仅保留 tab 栏(menu_icons 4);chrome 由首个真实文档的 resume 同步补建
- **MuPDF/字体预热**:实测字体加载仅 ~10ms,已排除

## 5 如何测试

Expand All @@ -45,11 +101,28 @@ MOGAN_TEST_GUI=1 xmake r <启动相关测试>
已完成改动:
1. 删除 `init-research.scm` 中 Benchmark 1(`fib 30`,原 :62-78)
2. 删除 Benchmark 2(`tm-fib 30`,原 :712-728,连同收尾分隔线输出)
3. 补充代码路径细节:总体调用链、resume() 内部 chrome 重建明细、
三次实测耗时对比
4. `resume()` 加启动页守卫:启动页 buffer 跳过菜单/工具栏/通知栏/侧栏重建,
推迟到首个真实文档的 resume 补建(`edit_interface.cpp`)
5. 修复守卫引入的菜单栏丢失回归:启动页期间到达 Qt 侧的菜单写入
(SLOT_MAIN_MENU/SLOT_MAIN_ICONS/SLOT_NOTIFICATION_BAR)原来被直接丢弃,
而 tm_window 的 menu_current 缓存认为已安装、静态菜单不再重发,
导致新建标签页后菜单栏不出现(mode/focus 工具栏因 slot 无守卫而正常)。
改为启动页期间「先存后装」:写入时仅缓存 widget 并置
`startupChromePending_`,退出启动页时由 `flush_startup_deferred_chrome()`
补装(`qt_tm_widget.cpp`)

涉及文件:`TeXmacs/progs/init-research.scm`(-39 行)
涉及文件:`TeXmacs/progs/init-research.scm`(-39 行)、
`src/Edit/Interface/edit_interface.cpp`(resume 守卫)、
`src/Plugins/Qt/qt_tm_widget.{cpp,hpp}`(推迟 chrome 补装)

## 7 Why
OPEN → STARTUP 约 1.6s,是用户可感知的启动延迟,其中 `lazy-keyboard-force` 和遗留 Benchmark 代码有明显优化空间。
OPEN → STARTUP 约 1.3~1.6s,是用户可感知的启动延迟。首屏(启动页)不依赖
编辑器,但当前路径上仍同步执行了编辑器专属的 `resume()` chrome 重建与
`lazy-keyboard-force`,有明显优化空间。

## 8 How
通过 `debug-std`/`debug-events` 日志时间戳分段定位,结合源码找到每段对应的埋点与实现位置。
通过 `debug-std`/`debug-events` 日志时间戳分段定位,结合源码找到每段对应的
埋点与实现位置;对 `resume()` 沿调用链(attach_view → resume)向上追溯
确认触发时机。
36 changes: 21 additions & 15 deletions src/Edit/Interface/edit_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,28 @@ edit_interface_rep::resume () {
// cout << "Resume " << buf->buf->name << LF;
bench_start ("resume");
got_focus= true;
SERVER (menu_main ("(horizontal (link texmacs-menu))"));
SERVER (menu_icons (0, "(horizontal (link texmacs-main-icons))"));
SERVER (menu_icons (1, "(horizontal (link texmacs-mode-icons))"));
SERVER (menu_icons (2, "(horizontal (link texmacs-focus-icons))"));
SERVER (menu_icons (3, "(horizontal (link texmacs-extra-icons))"));
// 启动页 buffer 的菜单/工具栏不可见,chrome 重建推迟到首个真实文档的 resume
bool is_startup= is_startup_tab_buffer (buf->buf->name);
if (!is_startup) {
SERVER (menu_main ("(horizontal (link texmacs-menu))"));
SERVER (menu_icons (0, "(horizontal (link texmacs-main-icons))"));
SERVER (menu_icons (1, "(horizontal (link texmacs-mode-icons))"));
SERVER (menu_icons (2, "(horizontal (link texmacs-focus-icons))"));
SERVER (menu_icons (3, "(horizontal (link texmacs-extra-icons))"));
SERVER (notification_bar ("(horizontal (link texmacs-notification-bar))"));
}
SERVER (menu_icons (4, "(horizontal (link texmacs-tab-pages))"));
SERVER (notification_bar ("(horizontal (link texmacs-notification-bar))"));
array<url> a= buffer_to_windows (buf->buf->name);
if (N (a) > 0) {
string win = "(string->url \"" * as_string (a[0]) * "\")";
string ldyn= "(dynamic (texmacs-left-tools " * win * "))";
string rdyn= "(dynamic (texmacs-side-tools " * win * "))";
string bdyn= "(dynamic (texmacs-bottom-tools " * win * "))";
SERVER (side_tools (1, "(vertical " * ldyn * ")"));
SERVER (side_tools (0, "(vertical " * rdyn * ")"));
SERVER (bottom_tools (0, "(vertical " * bdyn * ")"));
if (!is_startup) {
array<url> a= buffer_to_windows (buf->buf->name);
if (N (a) > 0) {
string win = "(string->url \"" * as_string (a[0]) * "\")";
string ldyn= "(dynamic (texmacs-left-tools " * win * "))";
string rdyn= "(dynamic (texmacs-side-tools " * win * "))";
string bdyn= "(dynamic (texmacs-bottom-tools " * win * "))";
SERVER (side_tools (1, "(vertical " * ldyn * ")"));
SERVER (side_tools (0, "(vertical " * rdyn * ")"));
SERVER (bottom_tools (0, "(vertical " * bdyn * ")"));
}
}
cur_sb = 2;
env_change= env_change & (~THE_FREEZE);
Expand Down
90 changes: 63 additions & 27 deletions src/Plugins/Qt/qt_tm_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,12 @@ qt_tm_widget_rep::qt_tm_widget_rep (int mask, command _quit)
membershipPeriodLabel (nullptr), membershipTitleLabel (nullptr),
loginActionButton (nullptr), logoutButton (nullptr), m_userId (""),
m_currentScmNotificationItem (""), startupContentWidget (nullptr),
startupTabMode (false), pdfViewerWidget (nullptr), pdfTabMode (false),
currentPdfPath (""), lastLoadedPdfPath (""), chatContentWidget (nullptr),
chatTabMode (false), chatSideDock (nullptr),
chatSidebarToggleBtn (nullptr), chatSidebarMode (false),
chatSidebarModeMemory_ (false), centralWidgetUpdatesFrozen_ (false) {
startupTabMode (false), startupChromePending_ (false),
pdfViewerWidget (nullptr), pdfTabMode (false), currentPdfPath (""),
lastLoadedPdfPath (""), chatContentWidget (nullptr), chatTabMode (false),
chatSideDock (nullptr), chatSidebarToggleBtn (nullptr),
chatSidebarMode (false), chatSidebarModeMemory_ (false),
centralWidgetUpdatesFrozen_ (false) {
type= texmacs_widget;

main_widget= concrete (::glue_widget (true, true, 1, 1));
Expand Down Expand Up @@ -1139,6 +1140,7 @@ qt_tm_widget_rep::sync_startup_tab_mode () {
show_widget_in_layout (editorWidget, layout);

update_visibility ();
flush_startup_deferred_chrome ();

if (scrollarea ())
scrollarea ()->surface ()->setSizePolicy (QSizePolicy::Fixed,
Expand Down Expand Up @@ -1963,6 +1965,42 @@ qt_tm_widget_rep::install_main_menu () {
menuToolBar->addWidget (dest);
}

void
qt_tm_widget_rep::apply_notification_bar_content () {
if (is_nil (notification_bar_widget)) return;
QList<QAction*>* action_list= notification_bar_widget->get_qactionlist ();
if (!action_list || action_list->isEmpty ()) {
m_currentScmNotificationItem.clear ();
if (scmNotificationBar) scmNotificationBar->clearContent ();
}
else {
QWidget* new_qwidget= notification_bar_widget->as_qwidget ();
if (new_qwidget && scmNotificationBar) {
scmNotificationBar->setContentWidget (new_qwidget);
}
eval ("(use-modules (texmacs menus notificationbar))");
m_currentScmNotificationItem=
to_qstring (as_string (call ("notification-bar-rendered-item")));
if (scmNotificationBar) {
scmNotificationBar->setSnoozeText (to_qstring (
as_string (call ("notification-bar-snooze-action-label"))));
}
}
}

void
qt_tm_widget_rep::flush_startup_deferred_chrome () {
if (!startupChromePending_) return;
startupChromePending_= false;
install_main_menu ();
if (!is_nil (main_icons_widget)) {
QList<QAction*>* list= main_icons_widget->get_qactionlist ();
if (list) replaceButtons (mainToolBar, list);
}
apply_notification_bar_content ();
update_visibility ();
}

void
qt_tm_widget_rep::write (slot s, blackbox index, widget w) {
if (DEBUG_QT_WIDGETS)
Expand Down Expand Up @@ -2005,9 +2043,14 @@ qt_tm_widget_rep::write (slot s, blackbox index, widget w) {

case SLOT_MAIN_MENU:
check_type_void (index, s);
if (startupTabMode || chatTabMode) break;
if (chatTabMode) break;
{
waiting_main_menu_widget= concrete (w);
// 启动页期间先存后装,退出启动页时由 flush_startup_deferred_chrome 补装
if (startupTabMode) {
startupChromePending_= true;
break;
}
if (menu_count <= 0) install_main_menu ();
else if (!contains (waiting_widgets, this))
// menu interaction ongoing, postpone new menu installation until done
Expand All @@ -2017,9 +2060,14 @@ qt_tm_widget_rep::write (slot s, blackbox index, widget w) {

case SLOT_MAIN_ICONS:
check_type_void (index, s);
if (startupTabMode || chatTabMode) break;
if (chatTabMode) break;
{
main_icons_widget = concrete (w);
main_icons_widget= concrete (w);
// 启动页期间先存后装,退出启动页时由 flush_startup_deferred_chrome 补装
if (startupTabMode) {
startupChromePending_= true;
break;
}
QList<QAction*>* list= main_icons_widget->get_qactionlist ();
if (list) {
replaceButtons (mainToolBar, list);
Expand Down Expand Up @@ -2050,27 +2098,15 @@ qt_tm_widget_rep::write (slot s, blackbox index, widget w) {

case SLOT_NOTIFICATION_BAR:
check_type_void (index, s);
if (startupTabMode || chatTabMode) break;
if (chatTabMode) break;
{
notification_bar_widget = concrete (w);
QList<QAction*>* action_list= notification_bar_widget->get_qactionlist ();
if (!action_list || action_list->isEmpty ()) {
m_currentScmNotificationItem.clear ();
if (scmNotificationBar) scmNotificationBar->clearContent ();
}
else {
QWidget* new_qwidget= notification_bar_widget->as_qwidget ();
if (new_qwidget && scmNotificationBar) {
scmNotificationBar->setContentWidget (new_qwidget);
}
eval ("(use-modules (texmacs menus notificationbar))");
m_currentScmNotificationItem=
to_qstring (as_string (call ("notification-bar-rendered-item")));
if (scmNotificationBar) {
scmNotificationBar->setSnoozeText (to_qstring (
as_string (call ("notification-bar-snooze-action-label"))));
}
notification_bar_widget= concrete (w);
// 启动页期间先存后装,退出启动页时由 flush_startup_deferred_chrome 补装
if (startupTabMode) {
startupChromePending_= true;
break;
}
apply_notification_bar_content ();
}
break;

Expand Down
3 changes: 3 additions & 0 deletions src/Plugins/Qt/qt_tm_widget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class qt_tm_widget_rep : public qt_window_widget_rep {
QWidget*
chatContentWidget; ///\< 聊天标签页模式下显示的控件(QTChatTabWidget)。
bool startupTabMode; ///\< 启动标签页视图是否激活。
bool startupChromePending_; ///\< 启动页期间是否有被推迟的 chrome 待补装。
PDFReaderWidget* pdfViewerWidget; ///\< PDF 标签页模式下的阅读器控件。
bool pdfTabMode; ///\< PDF 阅读器标签页是否激活。
QString currentPdfPath; ///\< 当前显示的 PDF 路径。
Expand Down Expand Up @@ -247,6 +248,8 @@ class qt_tm_widget_rep : public qt_window_widget_rep {
void set_full_screen (bool flag);
void update_visibility ();
void install_main_menu ();
void apply_notification_bar_content ();
void flush_startup_deferred_chrome ();
static void tweak_iconbar_size (QSize& sz);
void openRenewalPage ();

Expand Down
Loading