C14.9 r1: tables keep extra cells and quote marks; preview refuses edits; lists only in list items; replace moves past its text (library edc328a)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d4a7d3ebd7
commit
2aa4a080bb
9 files changed
+120
-23
No files matched your search
Generated
+1
-1
@@ -1922,7 +1922,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ely-gpui-component"
|
||||
version = "0.1.1"
|
||||
source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=ce4a1e9cb5fc712e9d03f5bb703d13685f1cb67a#ce4a1e9cb5fc712e9d03f5bb703d13685f1cb67a"
|
||||
source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=edc328aadfaefb78f4b6b3e98b4f4780746a1d52#edc328aadfaefb78f4b6b3e98b4f4780746a1d52"
|
||||
dependencies = [
|
||||
"alacritty_terminal",
|
||||
"anyhow",
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ jiff = "0.2"
|
||||
anyhow = "1"
|
||||
directories = "6"
|
||||
dunce = "1"
|
||||
ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "ce4a1e9cb5fc712e9d03f5bb703d13685f1cb67a" }
|
||||
ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "edc328aadfaefb78f4b6b3e98b4f4780746a1d52" }
|
||||
env_logger = "0.11"
|
||||
gpui = { package = "gpui-pre", version = "=0.3.8", default-features = false, features = ["stacker"] }
|
||||
gpui_platform = { package = "gpui-pre-platform", version = "=0.3.8", features = ["font-kit", "wayland", "x11"] }
|
||||
|
||||
@@ -40,7 +40,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木
|
||||
|
||||
## 组件映射
|
||||
|
||||
依赖 `ely-gpui-component`(pin ce4a1e9,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。
|
||||
依赖 `ely-gpui-component`(pin edc328a,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。
|
||||
|
||||
| 需要 | 用 |
|
||||
| --- | --- |
|
||||
@@ -283,4 +283,4 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
|
||||
- 2026-10-11:Linux 包的实测(L1.6)。本机(Omarchy,Hyprland 0.56)装上六个 Arch 包与六个 AppImage,`linux-installed.sh` 两种都过;deb、rpm、Arch 包在 Ubuntu 26.04、Fedora 44、Arch 容器里装上、全部 ELF 找得到库;deb 与 rpm 的六个应用在各自容器的 Xvfb 下画出窗口(`linux-distros.sh`)。截图按窗口(`grim -T` 加 Hyprland 的 `stableId`):按区域截时用户切换工作区或别的窗口盖住即截错。ElyMark 的文件监视略去读取后不会自激:重读前先比指纹(长度与修改时间),不打开文件。
|
||||
- 2026-10-11(用户定:替换 v0.1.3 的 Linux 包):v0.1.3 发布里原有的六个 AppImage(无头,打不开窗口)已删去,换成本机容器里建出的六个应用各四种包(共 24 个),出自 L1.7 的提交(晚于该标签);上传后逐个核对大小,ElyTree 的 AppImage 下载回来散列相同。`package.sh` 把每个 RUNPATH 按所在 ELF 解析(`$ORIGIN` 或 `${ORIGIN}` 开头,规范化后须在 AppDir 内),否则拒绝(如未修正的 cairo):CI 用的是别处建好的引擎,旧构建会在这里报错而不是带进包里。替换的 Linux 包对应的源码随之上传到同一发布(`source.sh`,文件名带构建提交号),发布说明写明 Linux 包出自哪个提交。
|
||||
- 2026-10-11:`ci.sh` 用 `ELY_SHARE` 时,CI 克隆里的 `engines`、`reference` 须是指向共享克隆的链接(Windows 为目录联接,不查);是本克隆自建的目录或指向别处即报错,不混用。`.gitignore` 的 `/engines`、`/reference` 不带斜杠,链接也被忽略(`source.sh` 要求工作区干净)。
|
||||
- 2026-10-11(用户定:ElyMark 不用 CEF,不用 Electron):ElyMark 改为原生。界面用组件库的 Markdown 组件(`documents::MarkdownEditor` 一族,缺的在组件库补并推到 Gitea),解析用 markdown-rs(`markdown` 1.0.0),公式用 RaTeX,Mermaid 用 mermaid-rs-renderer,PDF 用 MuPDF Story;CEF 随之删去,ElyMail 的正文也不用 CEF 与 Electron。进度在 `tasks/TASKS.md` 的 C14。组件库 fcad0ff 与 508cc55:`MarkdownRenderer`、`MarkdownEditor`、Markdown 着色、块范围与链接查找改读 markdown-rs 的语法树(GitHub 风格加公式),引用式链接与图片按定义解析,图片把所在段落分开,单行 `$$…$$` 为块公式,软换行(LF、CRLF、CR)读作空格,嵌套在引用与列表里的定义同样解析,强调与链接里的图片仍是图片(链接里的图片可按);`BlockEditor` 的导入导出不属 ElyMark,仍用 pulldown-cmark。markdown-rs 读嵌套图片时只留外层的说明。组件库 d5ecc5a:块公式经 `typography::Latex` 由 RaTeX 排出(字形为轮廓,按窗口缩放画),```` ```mermaid ```` 块经 `documents::Diagram` 由 mermaid-rs-renderer 画出(Mermaid 自己的深浅主题,底色与字色取 Ely 色板),都作为 gpui 资源在后台排版,RaTeX 或 Mermaid 对已解析的输入 panic 时显示为错误;`MarkdownRenderer::base` 给出文档所在文件夹,相对与 file 地址的图片从中读取并按自身大小、不超过栏宽显示,`data:` 地址解码,网络图片不载入(写明)。组件库 ab9a14c:每个公式、图与图片元素经 `primitives::held` 持有其资源,按资源计数持有者(8b9820b;画出的图存在共享的记录里,9036c92 起由最后离开的持有者释放,哪怕它自己还没收到图),最后一个持有者换了内容或离开窗口才从 gpui 资源缓存移除、在本帧之后从图集释放,共用同一内容的元素(含 gpui 缓存的视图)不受影响;公式按字形实际范围取画框,字形不被裁;公式失败显示原因、RaTeX 给的位置与原文,日志不含公式;`file:` 地址去掉查询与片段,`localhost` 为本机,Windows 上别的主机为共享路径,他处拒绝;Mermaid 的节点与饼图文字取 Ely 的前景色。组件库 2b6f551:`documents::MarkdownDocument` 是文件级的 Markdown 文档:全文与撤销在一个 `CodeEditor` 里(`CodeEditor::highlighter` 以 `markdown_highlights` 着色,无行号);可视以 gpui `list` 逐块画出文件的顶层块(链接定义、HTML、front matter 照原文显示,每个字节都在块里或块间),按下的块开成源码(`TextInput`),其键入即刻算作文档的文字,写回为一步撤销,上下键越过块边进入相邻块,Esc 写回;Cmd(他处 Ctrl)按下交给链接;预览只读;源码为编辑器本身;128 KiB 以内当场解析,更长的在后台解析,解析中按下块不打开(日志);新解析保留两端相同的块,滚动不跳。`CodeEditor` 的撤销历史最多留 64 MiB 文字,超出先丢最旧的步;`CodeEditor::reset` 换文字且撤销重新开始;`MarkdownMode` 加 `Preview`。组件库 fc181b4:一个插入点通用于各模式(编辑器的选区,写回的块移动它);`set_caret` 与 `set_top_line` 分别设插入点与视图,换模式保持顶行;按下的块在前一块写回后仍是所按的块(按偏移换算,结构变了即不开并记日志);块是 Tab 停靠点,Enter 或空格打开;预览不收按下、撤销与重做;撤销与重做每次记录、输入法组字、撤销、重做后都限在 64 MiB 内(先丢最旧的撤销,再丢最远的重做)。组件库 433c38f:只有对当下文字的解析才能打开块(解析的文字与编辑器相同),否则拒绝;要求的顶行在其文字解析后才落定:先到所在块,下一帧按块高的比例到块内位置,再有新的要求即接着放下一个;源码与列表之间切换保持顶行,可视与预览共用一个列表,切换不动滚动。组件库 abac8b4:顶行的要求带编号,迟到的帧回调只完成自己的那个;文字一换即按新文字重新放,放时按当下文字的行数夹住;`reveal` 是同样的要求(插入点设在该行,该行放到顶上),新文字解析后才落定。组件库 f59c9f7:文字超过读取上限(2 MiB)即丢掉旧的读取,大纲与块为空。ElyMark(C14.4):视图即一个 `MarkdownDocument`,文本即文件原文(BOM 与 CRLF 照 `text.rs` 存回),新建、打开、保存、另存、重命名经外壳照旧;视图在建成后的下一轮更新向外壳报一次载入(文档在 `new` 里当场解析,外壳此时还未订阅);文字超过 2 MiB 即只用源码并转去源码(随当前文字,不只看打开时);网页实现的查找替换、表格、附件、书写视图的应用、导出与打印Line truncated
|
||||
- 2026-10-11(用户定:ElyMark 不用 CEF,不用 Electron):ElyMark 改为原生。界面用组件库的 Markdown 组件(`documents::MarkdownEditor` 一族,缺的在组件库补并推到 Gitea),解析用 markdown-rs(`markdown` 1.0.0),公式用 RaTeX,Mermaid 用 mermaid-rs-renderer,PDF 用 MuPDF Story;CEF 随之删去,ElyMail 的正文也不用 CEF 与 Electron。进度在 `tasks/TASKS.md` 的 C14。组件库 fcad0ff 与 508cc55:`MarkdownRenderer`、`MarkdownEditor`、Markdown 着色、块范围与链接查找改读 markdown-rs 的语法树(GitHub 风格加公式),引用式链接与图片按定义解析,图片把所在段落分开,单行 `$$…$$` 为块公式,软换行(LF、CRLF、CR)读作空格,嵌套在引用与列表里的定义同样解析,强调与链接里的图片仍是图片(链接里的图片可按);`BlockEditor` 的导入导出不属 ElyMark,仍用 pulldown-cmark。markdown-rs 读嵌套图片时只留外层的说明。组件库 d5ecc5a:块公式经 `typography::Latex` 由 RaTeX 排出(字形为轮廓,按窗口缩放画),```` ```mermaid ```` 块经 `documents::Diagram` 由 mermaid-rs-renderer 画出(Mermaid 自己的深浅主题,底色与字色取 Ely 色板),都作为 gpui 资源在后台排版,RaTeX 或 Mermaid 对已解析的输入 panic 时显示为错误;`MarkdownRenderer::base` 给出文档所在文件夹,相对与 file 地址的图片从中读取并按自身大小、不超过栏宽显示,`data:` 地址解码,网络图片不载入(写明)。组件库 ab9a14c:每个公式、图与图片元素经 `primitives::held` 持有其资源,按资源计数持有者(8b9820b;画出的图存在共享的记录里,9036c92 起由最后离开的持有者释放,哪怕它自己还没收到图),最后一个持有者换了内容或离开窗口才从 gpui 资源缓存移除、在本帧之后从图集释放,共用同一内容的元素(含 gpui 缓存的视图)不受影响;公式按字形实际范围取画框,字形不被裁;公式失败显示原因、RaTeX 给的位置与原文,日志不含公式;`file:` 地址去掉查询与片段,`localhost` 为本机,Windows 上别的主机为共享路径,他处拒绝;Mermaid 的节点与饼图文字取 Ely 的前景色。组件库 2b6f551:`documents::MarkdownDocument` 是文件级的 Markdown 文档:全文与撤销在一个 `CodeEditor` 里(`CodeEditor::highlighter` 以 `markdown_highlights` 着色,无行号);可视以 gpui `list` 逐块画出文件的顶层块(链接定义、HTML、front matter 照原文显示,每个字节都在块里或块间),按下的块开成源码(`TextInput`),其键入即刻算作文档的文字,写回为一步撤销,上下键越过块边进入相邻块,Esc 写回;Cmd(他处 Ctrl)按下交给链接;预览只读;源码为编辑器本身;128 KiB 以内当场解析,更长的在后台解析,解析中按下块不打开(日志);新解析保留两端相同的块,滚动不跳。`CodeEditor` 的撤销历史最多留 64 MiB 文字,超出先丢最旧的步;`CodeEditor::reset` 换文字且撤销重新开始;`MarkdownMode` 加 `Preview`。组件库 fc181b4:一个插入点通用于各模式(编辑器的选区,写回的块移动它);`set_caret` 与 `set_top_line` 分别设插入点与视图,换模式保持顶行;按下的块在前一块写回后仍是所按的块(按偏移换算,结构变了即不开并记日志);块是 Tab 停靠点,Enter 或空格打开;预览不收按下、撤销与重做;撤销与重做每次记录、输入法组字、撤销、重做后都限在 64 MiB 内(先丢最旧的撤销,再丢最远的重做)。组件库 433c38f:只有对当下文字的解析才能打开块(解析的文字与编辑器相同),否则拒绝;要求的顶行在其文字解析后才落定:先到所在块,下一帧按块高的比例到块内位置,再有新的要求即接着放下一个;源码与列表之间切换保持顶行,可视与预览共用一个列表,切换不动滚动。组件库 abac8b4:顶行的要求带编号,迟到的帧回调只完成自己的那个;文字一换即按新文字重新放,放时按当下文字的行数夹住;`reveal` 是同样的要求(插入点设在该行,该行放到顶上),新文字解析后才落定。组件库 f59c9f7:文字超过读取上限(2 MiB)即丢掉旧的读取,大纲与块为空。ElyMark(C14.4):视图即一个 `MarkdownDocument`,文本即文件原文(BOM 与 CRLF 照 `text.rs` 存回),新建、打开、保存、另存、重命名经外壳照旧;视图在建成后的下一轮更新向外壳报一次载入(文档在 `new` 里当场解析,外壳此时还未订阅);文字超过 2 MiB 即只用源码并转去源码(随当前文字,不只看打开时);网页实现的查找替换、表格、附件、书写视图的应用、导出与打印Line truncated
|
||||
@@ -140,17 +140,28 @@ impl Desk {
|
||||
if edits.is_empty() {
|
||||
return;
|
||||
}
|
||||
// The next match starts past the replaced text.
|
||||
let after = edits[0].0.start + edits[0].1.len();
|
||||
let document = view.read(cx).document().clone();
|
||||
document.update(cx, |document, cx| document.replace(edits, cx));
|
||||
self.count(cx);
|
||||
// The next match takes the replaced one's place.
|
||||
let next = self
|
||||
.finding
|
||||
.as_ref()
|
||||
.and_then(|find| find.current.and_then(|at| find.hits.get(at)).cloned());
|
||||
if let Some(range) = next.filter(|_| !every) {
|
||||
document.update(cx, |document, cx| document.show_match(range, window, cx));
|
||||
if !document.update(cx, |document, cx| document.replace(edits, cx)) {
|
||||
let said = text(cx, "mark.preview.read_only", &[]);
|
||||
return self.toast(said, Severity::Info, cx);
|
||||
}
|
||||
self.count(cx);
|
||||
if every {
|
||||
return;
|
||||
}
|
||||
let Some(find) = self.finding.as_mut() else {
|
||||
return;
|
||||
};
|
||||
let next = find.hits.iter().position(|hit| hit.start >= after);
|
||||
find.current = next.or((!find.hits.is_empty()).then_some(0));
|
||||
let Some(range) = find.current.map(|at| find.hits[at].clone()) else {
|
||||
return;
|
||||
};
|
||||
let hits = find.hits.clone();
|
||||
mark(&document, &hits, find.current, cx);
|
||||
document.update(cx, |document, cx| document.show_match(range, window, cx));
|
||||
}
|
||||
|
||||
fn close_find(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
|
||||
@@ -118,3 +118,60 @@ fn the_table_menu_inserts_and_edits_a_table(cx: &mut TestAppContext) {
|
||||
current_text(&desk, cx)
|
||||
);
|
||||
}
|
||||
|
||||
fn replace_one(desk: &Entity<Desk>, with: &str, cx: &mut VisualTestContext) {
|
||||
desk.update_in(cx, |desk, window, cx| {
|
||||
let field = cx.new(|cx| TextInput::new(window, cx));
|
||||
field.update(cx, |field, cx| field.set_text(with, cx));
|
||||
desk.finding.as_mut().expect("open").replace = Some(field);
|
||||
desk.replace(false, window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
}
|
||||
|
||||
fn current_hit(desk: &Entity<Desk>, cx: &mut VisualTestContext) -> Option<std::ops::Range<usize>> {
|
||||
desk.read_with(cx, |desk, _| {
|
||||
let find = desk.finding.as_ref()?;
|
||||
find.current.map(|at| find.hits[at].clone())
|
||||
})
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_replacement_that_still_matches_moves_on(cx: &mut TestAppContext) {
|
||||
let (desk, cx, _files) = opened("a a\n", cx);
|
||||
query(&desk, "a", cx);
|
||||
desk.update_in(cx, |desk, window, cx| desk.step(true, window, cx));
|
||||
replace_one(&desk, "aa", cx);
|
||||
assert_eq!(current_text(&desk, cx), "aa a\n");
|
||||
assert_eq!(
|
||||
current_hit(&desk, cx),
|
||||
Some(3..4),
|
||||
"the next original match"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn replacing_the_last_match_wraps_to_the_first(cx: &mut TestAppContext) {
|
||||
let (desk, cx, _files) = opened("a b a\n", cx);
|
||||
query(&desk, "a", cx);
|
||||
desk.update_in(cx, |desk, window, cx| desk.step(false, window, cx));
|
||||
replace_one(&desk, "x", cx);
|
||||
assert_eq!(current_text(&desk, cx), "a b x\n");
|
||||
assert_eq!(current_hit(&desk, cx), Some(0..1));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn preview_keeps_its_text_from_tables_and_replacement(cx: &mut TestAppContext) {
|
||||
let (desk, cx, _files) = opened("a\n", cx);
|
||||
let view = desk.read_with(cx, |desk, _| desk.active_view().expect("a tab"));
|
||||
view.update_in(cx, |view, window, cx| {
|
||||
view.switch(crate::view::Mode::Preview, window, cx)
|
||||
});
|
||||
let focus = desk.read_with(cx, |desk, _| desk.core.focus.clone());
|
||||
cx.update(|window, cx| window.focus(&focus, cx));
|
||||
cx.dispatch_action(crate::tools::InsertTable);
|
||||
query(&desk, "a", cx);
|
||||
desk.update_in(cx, |desk, window, cx| desk.step(true, window, cx));
|
||||
replace_one(&desk, "x", cx);
|
||||
assert_eq!(current_text(&desk, cx), "a\n");
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
//! The table menu's work on the shown file (MARK-06).
|
||||
|
||||
use ely_gpui_component::{documents::TableEdit, primitives::Severity};
|
||||
use ely_gpui_component::{
|
||||
documents::{TableEdit, TableRefusal},
|
||||
primitives::Severity,
|
||||
};
|
||||
use ely_office::prelude::*;
|
||||
use ely_ui::i18n::text;
|
||||
use gpui::{Context, InteractiveElement as _, actions};
|
||||
@@ -29,15 +32,20 @@ impl Desk {
|
||||
let document = view.read(cx).document().clone();
|
||||
let done = document.update(cx, |document, cx| match edit {
|
||||
Some(edit) => document.edit_table(edit, cx),
|
||||
None => {
|
||||
document.insert_table(cx);
|
||||
true
|
||||
}
|
||||
None => document.insert_table(cx),
|
||||
});
|
||||
if !done {
|
||||
let said = text(cx, "mark.table.none", &[]);
|
||||
self.toast(said, Severity::Info, cx);
|
||||
}
|
||||
let Err(refusal) = done else {
|
||||
return;
|
||||
};
|
||||
let key = match refusal {
|
||||
TableRefusal::Outside => "mark.table.none",
|
||||
TableRefusal::Kept => "mark.table.kept",
|
||||
TableRefusal::Beyond => "mark.table.beyond",
|
||||
TableRefusal::Mixed => "mark.table.mixed",
|
||||
TableRefusal::Preview => "mark.preview.read_only",
|
||||
};
|
||||
let said = text(cx, key, &[]);
|
||||
self.toast(said, Severity::Info, cx);
|
||||
}
|
||||
|
||||
pub fn table_actions(element: gpui::Div, cx: &mut Context<Self>) -> gpui::Div {
|
||||
|
||||
@@ -21,6 +21,10 @@ pub(super) const EN: &[(&str, &str)] = &[
|
||||
("mark.table.delete_row", "Delete Row"),
|
||||
("mark.table.delete_column", "Delete Column"),
|
||||
("mark.table.none", "Put the caret in a table first"),
|
||||
("mark.table.kept", "A table keeps its head row and one column"),
|
||||
("mark.table.beyond", "This cell is past the head's columns"),
|
||||
("mark.table.mixed", "This table's lines differ in quote or list marks; edit it as text"),
|
||||
("mark.preview.read_only", "Preview is read-only; switch to visual or source to edit"),
|
||||
("mark.attach.untitled", "Save the document first: images go beside it"),
|
||||
("mark.attach.preview", "Switch to an editing mode to add images"),
|
||||
("mark.attach.images", "Only images can be added"),
|
||||
@@ -109,6 +113,10 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[
|
||||
("mark.table.delete_row", "删除行"),
|
||||
("mark.table.delete_column", "删除列"),
|
||||
("mark.table.none", "请先把插入点放进表格"),
|
||||
("mark.table.kept", "表格保留表头行和至少一列"),
|
||||
("mark.table.beyond", "此单元格在表头的列之外"),
|
||||
("mark.table.mixed", "此表格各行的引用或列表标记不同,请直接编辑文字"),
|
||||
("mark.preview.read_only", "预览为只读,请切到可视或源码再编辑"),
|
||||
("mark.attach.untitled", "请先保存文档:图片放在它旁边"),
|
||||
("mark.attach.preview", "请切到编辑模式再添加图片"),
|
||||
("mark.attach.images", "只能添加图片"),
|
||||
|
||||
@@ -36,7 +36,7 @@ fetch() { # name url rev [sparse paths...]
|
||||
echo "fetch $name $rev"
|
||||
}
|
||||
|
||||
fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git ce4a1e9cb5fc712e9d03f5bb703d13685f1cb67a
|
||||
fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git edc328aadfaefb78f4b6b3e98b4f4780746a1d52
|
||||
fetch zed https://github.com/zed-industries/zed.git 1a28cff4b409169bac058bca40dfbfeb7621d19b
|
||||
fetch libreoffice-core https://github.com/LibreOffice/core.git 8221e31b3ac356a1623c672912a3d2b492f7e3d1 \
|
||||
include/LibreOfficeKit libreofficekit desktop/source/lib desktop/inc \
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
- `reference/Ely-GPUI-Components/src/documents/document/tables.rs:83`(ce4a1e9):列位置取自原始行的全部单元格,但随后网格被裁成表头宽度,合法 GFM 表格的多余单元格会让列操作越界 panic。复现:源码为 `| A | B |\n| - | - |\n| 1 | 2 | 3 |\n`,插入点放在 `3` 上,执行删除列,报 `removal index (is 2) should be < len (is 2)`。应在修改前核对插入点对应的有效列,对不可操作的位置明确拒绝,并覆盖超出表头宽度的行。失败探针:`probe_extra_table_cell_does_not_crash`,见 `.work/C14.9-r1/probes-confirm.log`。
|
||||
|
||||
- `reference/Ely-GPUI-Components/src/documents/document/tables.rs:90`(ce4a1e9):嵌套表格统一用无容器前缀的文本重写,破坏外层引用结构。复现:对 `> | A | B |\n> | - | - |\n> | 1 | 2 |\n` 的数据行执行下方插入行,结果只有首行保留 `> `,分隔行与数据行全部脱离引用,原表格不再按原结构解析,违反 §14.3 的内容保留要求。应保留引用或列表容器的逐行前缀;无法保留的结构须明确拒绝并提示用源码编辑。失败探针:`probe_nested_table_stays_quoted`,见 `.work/C14.9-r1/probes-confirm.log`。
|
||||
|
||||
- `apps/ely-mark/src/tools.rs:30`:表格动作不检查模式,直接修改只读预览中的文档。复现:打开 `Intro\n`,切到预览,执行“表格›插入表格”,仍停在预览模式,正文却已加入整个表格,违反 MARK-02 的只读预览要求。应在实际修改入口落实模式限制,覆盖表格及替换等修改动作;不能在保持只读预览状态时写入正文。失败探针:`probe_preview_table_is_read_only`,见 `.work/C14.9-r1/probes-confirm.log`。
|
||||
|
||||
- `apps/ely-mark/src/finding.rs:147`:逐次替换后沿用旧匹配序号,不能可靠地选中下一处。复现:源码 `a a\n`,查找 `a`,选中第一处并替换为 `aa`;结果为 `aa a\n`,选区仍为 `0..1`,而不是下一原匹配的 `3..4`,反复替换会不断扩张第一处。另外,替换 `a b a\n` 的最后一处为 `x` 后,当前项变成 `None`,没有按导航的循环规则转到第一处。应依据替换后的字节位置重新寻找下一处并处理回绕,补实际选区与连续替换回归。失败探针:`probe_replace_matching_text_advances`、`probe_replace_last_wraps`,见 `.work/C14.9-r1/probes-confirm.log`。
|
||||
|
||||
- `reference/Ely-GPUI-Components/src/documents/document/finding.rs:28`(ce4a1e9):可视模式在解析尚未跟上文本时直接丢弃显示匹配的请求。应用在替换后立即调用它,此时编辑器的 Changed 回调尚未完成解析,正常的短文档也会命中此分支。复现:可视模式查找 `a b a\n` 的第一处 `a`,替换为 `x`;计数更新了,但下一处没有打开或选中,键入 `z` 后正文仍为 `x b a\n`,而不是 `x b z\n`。应将匹配显示请求保留到对应文本解析完成再落实。失败探针 `probe_visual_replacement_selects_next`;对照探针在解析后再次调用 `show_match` 即通过,见 `.work/C14.9-r1/probes-confirm.log`。
|
||||
|
||||
- `reference/Ely-GPUI-Components/src/documents/document/lists.rs:125`(ce4a1e9;块内同见 159):列表续写只检查当前行的字符,不检查 Markdown 语法上下文,代码块中的普通代码也被当作列表。复现:源码为一个 text 围栏,内容为 `- a`;在该行末回车,实际插入 `\n- `,而正常回车应只换行。代码内容因此被额外改写,违反 MARK-06 与 §14.3;可视模式打开代码块后也走同一判定。应仅对实际列表项续写,排除围栏代码等原文区域,并覆盖源码与打开块两条输入路径。失败探针:`probe_code_is_not_a_list`,见 `.work/C14.9-r1/probes-confirm.log`。
|
||||
Reference in new issue
Block a user