GitHub - 0WD0/majutsu: Majutsu! Magit for jujutsu
Components
TODO majutsu-show
我的想法是把 majutsu-show 当成允许输出 patch 的 majutsu-log
因为 log 其实可以完全模拟实现 show 的输出!
TODO majutsu-evolog
我需要一种新的 log buffer !
在这里查看一个 working copy 的变更过程应该是非常有用的功能!
TODO majutsu-op
我希望它和 majutsu-process 可以结合起来
</home/disk/Dev/jj/docs/templates.md>
related: operation 应该有一个对应的 operationdiff 类型!
我们能比较 diff 但是这个 diff 不是在 operation 类型中的,而是在 不同的 op 命令中默认输出的
比如说 op show 就能用 -p 来控制是否显示:
Changed commits:
Changed working copy default@:
Changed local bookmarks:
Changed tags:
Changed remote bookmarks:这些内容我们都希望能控制 template!
这不能在 -T 做到,有没有可能做到?
jj-vcs/jj#6980 FR: `jj op show` should take templates (`-T` argument, like `j…
但是控制的还是 operation 类型的样式,不是 diff 的样式
想要到源码里看看
</home/disk/Dev/jj/cli/src/commands/operation/diff.rs>
这里在计算 commit diff。
关于 op diff
</home/disk/Dev/jj/cli/src/commands/operation/diff.rs>
现在还没有 template 实现!
它的 diff 首先是 commit 层面的 diff
majutsu-op-log
重构后已经有了基本的实现
DONE majutsu-color-words
这是 jj 原生版本的 diff 解析组件,需要和 majutsu-diff 的默认 git 格式输出相契合,应该是一个比较大的功能。
同时也是非常有必要存在的功能,因为 jj 的冲突模型天然不契合 :git 格式的 diff 输出。
DONE majutsu-workspace
我们需要做 workspace 支持,类似于 magit-worktree
DONE majutsu-edit
这里放的不只是 jj edit 相关的功能,需要实现的是不 edit 到对应 revision 也能修改那个 revision 的文件的 edit
我刚发现我们可以使用 diffedit 来满足这个需求。
DONE majutsu-diff
rev 参数 和 样式参数 需要分离
git 是直接把 revision 当作参数的,然后 file 用 — 放在最后输入;
jj 不一样,它把 filesets 作为参数,revset 作为参数,所以这里有一点区别。
我需要支持 diff 的 template 吗?
wash
性能优化的主要手段
diff buffer
应该用几个 insert section 的 hook 里的函数实现
TODO majutsu-diff-dwim
需要先设计一下行为
- 没有 transient-prefix
- 接受 region sections ,构造使用 -f -t 参数
- 在 jj-commit section 上,使用 -r rev
- 不在 jj-commit section 上,使用 -r @ 参数
- 接受 region sections ,构造使用 -f -t 参数
- 有 transient-prefix
- 支持用 -o 通过 toggle selection 手动构造 revset
- 支持 -f -t ,这种方式方便中途移开去其他地方找提交
- 支持用 -r 手动输入 revsets
- 支持用 -o 通过 toggle selection 手动构造 revset
DONE majutsu-sparse
这个实现起来挺简单的,照着 magit-sparse-checkout 抄就行。
DONE majutsu-jjdescription
我需要给 jjdescription 手动做一下美化!
需要让他不被 better-jumper 记录,
修改 better-jumper-ignored-file-patterns
magit 里自带有的是 recentf 的集成。
DONE majutsu-template
我设计了一种 jujutsu 模板语言的 DSL
历时两天实现了 MVP
能否利用 —config flag?
—config <NAME=VALUE>
Additional configuration options (can be repeated)The name should be specified as TOML dotted keys. The value should be specified as a TOML expression. If string value isn’t enclosed
by any TOML constructs (such as array notation), quotes can be omitted.
Insight
TODO 更完善的 jump-to-magit 支持
我们现在支持了 blob 中的 jump to magit,实际上还有其他地方也可以加这个功能
比如说 log 里,调到 magit-log 的对应条目(但是参数对应不过去啊)
那还是跳转到 magit-revision 比较好?
TODO marginalia 集成!
我们有几种需要考虑的数据类型
file
revision
commit (divergent revision)
tag
bookmark
workspace
git remote
operation
TODO 我觉得我们需要一个 ediff-directories 的替代品
或许可以参考 diffview.nvim 和 hunk.nvim
TODO 我突然发现 smerge-mode 提供了 smerge-diff 这个东西
majutsu 也应该需要一种修改 refine 行为的功能!
还有,这种功能需要强制启动 base 模式?那有些奇怪了
DONE 我们需要重新设计 majutsu-log 的模板和渲染模块
我觉得我们应该重新设计一下这个模板和渲染模块
我们有下面的需求:
- 保持 graph 合理:
输出要在 —no-graph 和 with graph 两种模式下可解析 - 具有可扩展性:
比如我们现在有四个模块的划分 heading, body, right margin 和 元数据,
我们需要能够扩展到更多模块,
我们没准还会再搞一个 left margin 出来。 - 多行内容支持:
我们需要在 heading 和 body 都支持多行内容输出,但是需求有所不同。
heading 需要和 graph 配合,这里的换行是真的换行;
但是 body 的换行是必须是转换出来的换行,我们打算用\x1f来替换为\n。 - 高亮保持,我们希望能可选地复用 jj 原输出的高亮。
为了实现这些需求,我们有一些设计
使用 \x1d 来表示一组数据的首尾
使用 \x1e 来划分 field
使用 \x1f 来替代不想输出的换行 \n
我们知道对于一个 commit 的模板的对应输出,它是共用相同的 indent 的(这是因为左侧有 graph 输出)
TODO 我突然想到可以复用 selection 的这种 overlay 来支持 log buffer 中的 revset 高亮!
一个比较好用的场景是我想要找满足 diff_contains(“xxx”) 条件的 revision 这种
TODO 我觉得还有比较重要的功能是临时展开一段被折叠的 log graph
这里就大概是类似 build revset 的功能了
TODO diff 依然存在性能问题!!!
我们将在 emacs 源码进行测试
TODO bookmark section 设计
一个 bookmark 可以有多个 remote,
我们需要更加方便的 track untrack forget delete 操作,
这可以在 jj-bookmark section 实现。
STRT majutsu-ediff 设计
新的 majutsu-ediff.el 文件
现状分析
当前 majutsu ediff 功能 (majutsu-diff.el 916-1039行):
majutsu-diffedit-emacs(绑定E) → 入口点majutsu-diffedit-with-ediff→ 只支持 @- vs @ 比较majutsu-diffedit-with-smerge→ smerge 冲突解决- 问题:功能单一,无 DWIM,无任意 revision 比较
Magit-ediff 架构参考
| 组件 | 作用 |
|---|---|
magit-ediff-buffers macro | Buffer 生命周期管理 (复用/创建/清理) |
magit-ediff-dwim | 根据 section 类型智能选择操作 |
magit-ediff-compare | 任意 revision 比较 |
magit-ediff-stage | 3-way staging (HEAD/Index/Working) |
magit-ediff-show-* | 查看 staged/unstaged/commit 变更 |
magit-ediff-resolve-* | 冲突解决 (all/rest) |
| 窗口配置保存/恢复 | 退出时恢复原布局 |
核心 macro magit-ediff-buffers 职责:
- 复用已有 revision buffer
- 创建临时 buffer
- 退出时只清理临时 buffer (保留用户 buffer)
- 管理窗口配置恢复
- 自定义 ediff quit hooks
jj 相关命令
# 获取任意 revision 的文件内容
jj file show -r REV FILE
# 交互式编辑 diff
jj diffedit -r REV # 编辑单个 revision 的变更
jj diffedit --from A --to B # 比较任意两个 revision
# 比较任意两个 revision
jj diff --from A --to B功能设计
┌───────────────────────────────────────────────────────────┐
│ majutsu-ediff.el (新文件) │
├───────────────────────────────────────────────────────────┤
│ 核心 Macro: │
│ majutsu-ediff-buffers → Buffer 管理 (复用/创建/清理) │
│ │
│ 入口命令: │
│ majutsu-ediff-dwim → 智能 DWIM (e) │
│ majutsu-ediff → Transient 菜单 (E) │
│ │
│ 具体操作: │
│ majutsu-ediff-compare → 任意 revision 比较 │
│ majutsu-ediff-show-revision → 查看 revision 变更 │
│ majutsu-ediff-resolve → 冲突解决 │
└───────────────────────────────────────────────────────────┘DWIM 逻辑
在 diff buffer 中,majutsu-ediff-dwim 的 from/to 直接读取 majutsu-buffer-diff-range~, 其内容格式为 ~("--revisions=…") 或 (“—from=…” “—to=…“),保持与 majutsu diff 展示一致。
(defun majutsu-ediff-dwim ()
(magit-section-case
;; diff buffer 中的 hunk/file → ediff 该文件 (使用 buffer 的 from/to)
(jj-hunk (majutsu-ediff--file-from-diff))
(jj-file (majutsu-ediff--file-from-diff))
;; log buffer 中的 commit → ediff parent vs commit
(jj-commit (majutsu-ediff-show-revision (oref it value)))
;; 其他情况 → 默认 @- vs @
(t (majutsu-ediff-show-revision "@"))))Transient 菜单设计
Ediff (E)
├── Ediff
│ ├── e Dwim
│ ├── c Compare (--from/--to)
│ └── r Show revision
│
└── Resolve
├── m Resolve (ediff, 仅 2-side)
└── M Resolve (majutsu-conflict)冲突解决策略
majutsu-ediff-compare 不强制归一化 revset;若 revset 解析为非单一 revision,
交由 jj 的命令错误提示处理。
ediff 仅支持 3-way merge(base + 2 sides),无法处理超过 2 个 side 的冲突。
因此冲突解决采用以下策略:
| side 数量 | 解决方式 |
|---|---|
| = 2 | 使用 ediff-merge-buffers-with-ancestor 进行 3-way merge |
| > 2 | 自动 fallback 到 majutsu-conflict 手动解决 |
用户也可以通过 Transient 菜单显式选择 “Resolve (majutsu-conflict)” 跳过 ediff。
关键改进点
- Buffer 管理: 复用已有 buffer,退出时只清理临时 buffer
- 窗口配置: 保存/恢复 ediff 前的窗口布局
- 任意比较: 支持
--from/--to任意 revision 比较 - DWIM: 根据上下文自动选择最合适的操作
实现优先级
| 优先级 | 功能 | 复杂度 |
|---|---|---|
| 1 | majutsu-ediff-compare (任意 rev) | 中 |
| 2 | majutsu-ediff-dwim (智能入口) | 低 |
| 3 | Buffer/窗口管理优化 | 中 |
| 4 | Transient 菜单 | 低 |
| 5 | 冲突解决增强 | 高 |
新增命令签名
;; 主入口 - DWIM
(defun majutsu-ediff-dwim ()
"Context-aware ediff based on current section.")
;; 任意 revision 比较
(defun majutsu-ediff-compare (from to &optional file)
"Compare FILE between FROM and TO revisions.")
;; 查看 revision 变更
(defun majutsu-ediff-show-revision (rev &optional file)
"Show changes in REV using ediff (parent vs rev).")
;; 冲突解决 (增强现有)
(defun majutsu-ediff-resolve (&optional file)
"Resolve conflicts in FILE using ediff.")STRT majutsu-conflict-mode 实现
Minor mode 用于 JJ 冲突文件的高亮、导航和解决。支持三种冲突风格:
jj-diff: JJ 默认风格,显示 diff + snapshotjj-snapshot: 所有 side 都是 snapshotgit: Git diff3 风格(委托给 =smerge-mode=)
冲突结构
JJ diff 风格中:
%%%%%%%显示的是 从 merge base 到 commit A 的 diff+++++++显示的是 commit B 的完整 snapshot(base)
<<<<<<< conflict 1 of 1
%%%%%%% diff from: merge_base
\\\\\\\ to: commit_A
apple <- context
-grape <- base 有,A 删除了
+grapefruit <- A 添加的
orange
+++++++ commit_B (snapshot / base)
APPLE
GRAPE
ORANGE
>>>>>>> conflict 1 of 1 ends数据结构
(cl-defstruct (majutsu-conflict (:constructor majutsu-conflict--create))
begin-pos ; <<<<<<< marker 位置
end-pos ; >>>>>>> marker 之后位置
marker-len ; marker 长度(用于重新生成)
style ; 'jj-diff, 'jj-snapshot, 或 'git
removes ; ((label . content) ...) - "from" sides
adds ; ((label . content) ...) - "to" sides
base) ; (label . content) - jj-diff 中的 snapshot base高亮策略
使用 font-lock 进行行级高亮,overlay 仅用于 word-level refinement。
| 区域类型 | Face | 颜色 (dark) | 说明 |
|---|---|---|---|
| Marker 行 | majutsu-conflict-marker-face | grey30 | 所有 marker 行 (<<<, >>>, 等) |
| Base 内容 | majutsu-conflict-base-face | inherit smerge | jj-diff 的 ~~~~++~~~~ / snapshot 首块 |
| Diff 添加行 | majutsu-conflict-added-face | #335533 | diff 中 + 开头 |
| Diff 删除行 | majutsu-conflict-removed-face | #553333 | diff 中 - 开头 |
| Diff 上下文 | majutsu-conflict-context-face | grey25 | diff 中空格开头 |
| Refined 添加 | majutsu-conflict-refined-added | #22aa22 | word-level 添加高亮 |
| Refined 删除 | majutsu-conflict-refined-removed | #aa2222 | word-level 删除高亮 |
风格处理策略
| 风格 | 高亮方案 | 原因 |
|---|---|---|
| git | smerge-mode | 成熟稳定,已有完整支持 |
| jj-diff | font-lock | 需要解析 diff 内容中的 +/- 行 |
| jj-snapshot | font-lock | 需要区分 base 和其他 side |
键绑定
所有键绑定使用 C-c ^ 前缀:
| 键 | 命令 | 说明 |
|---|---|---|
C-c ^ n | majutsu-conflict-next | 下一个冲突 |
C-c ^ p | majutsu-conflict-prev | 上一个冲突 |
C-c ^ b | majutsu-conflict-keep-base | 保留 base (snapshot) |
C-c ^ R | majutsu-conflict-refine | 添加 word-level 高亮 |
C-c ^ 1-9 | majutsu-conflict-keep-side N | 保留 side N (diff 后) |
C-c ^ M-1-9 | majutsu-conflict-keep-side N | 保留 side N (diff 前) |
自动启用
仅对 JJ 风格冲突启用,Git 风格委托给 =smerge-mode=:
(defun majutsu-conflict-check-enable ()
"Enable `majutsu-conflict-mode' if buffer has JJ-style conflicts.
Git-style conflicts are left to `smerge-mode'."
(when (save-excursion
(goto-char (point-min))
(catch 'found
(while (re-search-forward majutsu-conflict-begin-re nil t)
(let ((style (majutsu-conflict--detect-style (match-beginning 0))))
(when (memq style '(jj-diff jj-snapshot))
(throw 'found t))))
nil))
(majutsu-conflict-mode 1)))Word-level Refinement
majutsu-conflict-refine 使用 smerge-refine-regions 对 diff 区域添加细粒度高亮:
- 对 jj-diff 风格:解析
%%%%%%%...区域中的 +/- 行 - 对 jj-snapshot 风格:比较相邻的
-------和+++++++区域
导航时若 diff-refine 非 nil,自动触发 refinement。
DONE majutu-interactive
借鉴 magit-apply 的实现,然后 jj 还有非常重要的功能,它支持修改已提交的 commit
目标是实现 squash split commit restore 的 -i 功能,也就是 magit-apply 对应的局部 patch 操作功能。
我打算忽略 commit 的 -i 功能,因为局限性很大,而且会改变当前 working copy,不适合基于 region 的操作。
我知道怎么实现 split 的功能了!!!
magit manual link
我们可以在 diff section 上设计两个命令
一个是选中部分 split 出来作为上/下一个 rev(也就是 split)
一个是命令能让小 patch 在前/后 rev 直接传递(也就是 squash)
目前我觉得只能通过 --tool 和 --config 来实现
—config <NAME=VALUE>
Additional configuration options (can be repeated)The name should be specified as TOML dotted keys. The value should be specified as a TOML expression. If
string value isn’t enclosed by any TOML constructs (such as array notation), quotes can be omitted.
我觉得我可以在临时生成的 —config 参数里里写死 patch
然后可以用这个临时配置
放在 /tmp/jj-applypatch.toml
[merge-tools.applypatch]
program = "patch"
edit-args = ["-p1", "-d", "$right", "-i", "/tmp/foo.patch", "-s"]这个配置写法是正确的,但是需要注意一点的是,我需要 apply 的 patch 是新的 commit 中的反向变更(对于 commit 命令来说)
然后直接 commit -i 的操作选中的部分是前一个 commit 中的正向变更
我应该怎么设计才是合理的?
TODO UX
首先 基于 region 和 基于 section 的选项是最基本的,同时我还希望支持能复用 majutsu-selection,能做到同时操作多个 region,
可是这里又有一个问题,我们的 selection 是基于 section 的,不是 基于 region 的,我应该可以把它扩展到 sub section 的层次?
进行一下 transient 层面的设计:
我们的 selection 是需要在 diff buffer (或者单纯的 section,之后考虑) 进行的,
但是如果我们想要把这个 patch 发到前后以外的 revision 我们必然需要在 log buffer 标记,单纯的 completing-read 是满足不了需求的!
这可能可以通过一个二级 transient 实现。
STRT 关于 magit-files 中对应功能的想法,引入 majutsu-file.el
当前文件的 diff
magit-diff-buffer-file
可能挺好用
magit-log-trace-definition
这个函数不知道干啥用的,没有正常工作过
TODO 实现 magit-find-file
需要结合使用 jj file list 和 jj file show
-
目标行为(Majutsu 版本)
- 在 Majutsu 的 log/diff buffer 里,光标位于
jj-filesection 时,能“按 revision 打开该文件”。 - 在普通 file buffer 里(=buffer-file-name= 非空),能基于当前仓库上下文选择一个 revset,然后打开“该 revset 下的同名文件”。
- 打开的 buffer 应该是 只读 的 “blob buffer”(对齐 Magit 的
blob=),并且可复用(buffer identity 由 =<repo, revset, path>决定)。 - 支持 “下一个/上一个修改该文件的 changeset” 导航(对齐 Magit 在 blob/revision buffer 里的历史跳转体验)。
- 在 Majutsu 的 log/diff buffer 里,光标位于
-
输入来源与交互
- revset 的默认值(优先级):
- log/diff 中的
jj-commitsection value(若存在) - 当前工作副本
@
- log/diff 中的
- path 的来源(优先级):
jj-filesection value(若存在)- 当前 file buffer 的
buffer-file-name相对 repo 根目录的路径 - 手动 completing-read(候选来自
jj file list -r <revset>)
- revset 的默认值(优先级):
-
建议拆分(majutsu-file.el 的模块边界)
- “列文件”层:把 completion 所需的候选从 jj 拿到
jj file list -r <revset>- 返回 相对路径 列表(每行一个 path)
- “取内容”层:把 blob 内容拿到(不落盘也行)
jj file show -r <revset> <path>- 返回字符串(注意:这里应该使用 *string API*,不要走 process buffer)
- “显示/复用”层:把内容放进 blob buffer,并设置 buffer-local 状态
- buffer local:
majutsu-buffer-blob-revset/majutsu-buffer-blob-path/majutsu--default-directory revert-buffer-function支持刷新(重新跑 =jj file show=)
- buffer local:
- “列文件”层:把 completion 所需的候选从 jj 拿到
-
性能与缓存(先做最小可用)
jj file list在大仓库里可能很慢:
- MVP:只在用户显式调用时运行一次,结果缓存到一个简单的 (revset . paths) alist(buffer-local 或全局都行)
- 后续:可以参考 Magit 的 refresh-cache 思路,但先不要引入复杂度
- MVP:只在用户显式调用时运行一次,结果缓存到一个简单的 (revset . paths) alist(buffer-local 或全局都行)
TODO 实现 magit-blob-mode
可以用 jj file show 来得到临时文件
-
blob buffer 的定位
- blob buffer 是 “按某个 revset 查看某个文件内容” 的视图,不等同于工作区文件 buffer。
- 应该默认只读,避免用户误以为保存会写回仓库。
- 但可以提供显式命令把 blob 内容写出到文件或用于 diff(可选)。
- 需要支持
majutsu-edit/majutsu-new的 DWIM 操作:
- 在 blob buffer 中,这两个命令应该把当前 blob 当作“有 commit 上下文”的视图,默认目标为该 blob 对应的 *单个 revision*(见下文的 revset 归一化)。
- =majutsu-edit=:跳转到该 revision(或该 change 的最新 revision)并进入可修改状态。
- =majutsu-new=:以该 revision(或该 change 的最新 revision)为起点创建新的 changeset(例如作为 parent/after/before 的默认值之一,具体行为后续再对齐 Magit 的直觉)。
- 在 blob buffer 中,这两个命令应该把当前 blob 当作“有 commit 上下文”的视图,默认目标为该 blob 对应的 *单个 revision*(见下文的 revset 归一化)。
- blob buffer 是 “按某个 revset 查看某个文件内容” 的视图,不等同于工作区文件 buffer。
-
buffer 复用策略(对齐 Magit)
- 复用 key:=(repo-root, revset, path)=
- buffer name 建议包含最小信息(可读 + 稳定):
- 例如:=*majutsu-blob: <path> @<revset>*=(或者把 revset 缩短)
- 例如:=*majutsu-blob: <path> @<revset>*=(或者把 revset 缩短)
- 这里的 revset 应该支持 bookmark/workspace/commit-id/change-id/tag,以及更一般的任意 revset 表达式。
- 但 blob buffer 的语义必须绑定到 *单个 revision*;因此需要对用户输入的 revset 做一次归一化:
- 使用 revset 函数
latest(x)把任意 revset 收敛到最新的 1 个 commit(默认 count=1;见 =jj/docs/revsets.md=)。 - 如需更严格的错误提示,可以再套一层
exactly(latest(x), 1)来保证结果是且仅是一个 revision(空集或多集时直接报错)。
- 使用 revset 函数
- 建议同时保存两份信息:
- “输入 revset”(用于标题/显示/复用 key)
- “解析后的单个 revision”(用于 DWIM 的 =edit/new=、以及后续 prev/next 跳转的计算起点)
- “输入 revset”(用于标题/显示/复用 key)
- 但 blob buffer 的语义必须绑定到 *单个 revision*;因此需要对用户输入的 revset 做一次归一化:
- refresh 时不需要重新计算 “target-commit”,只需要:
- 重新插入内容
- 让 point/section 位置恢复(沿用 Majutsu 已对齐的 refresh 行为)
- 重新插入内容
- 复用 key:=(repo-root, revset, path)=
-
与 diff 的联动(对应 magit-diff-buffer-file)
- 当用户在 blob buffer 里调用 “diff 当前文件”:
- filesets: 仅当前 path
- revsets:
("--from" <revset> "--to" @)或者 =(“-r” <revset>)=(视 jj diff 语义) - 这要求
majutsu-diff的 transient/记忆逻辑能正确保留filesets与 =revsets=(你已经在重构)
- filesets: 仅当前 path
用这个命令得到上一个有对某个文件修改 change id
jj log -r "::rev-&files('file')" -G --limit 1 -T 'change_id'用这个命令得到下一个
jj log -r "rev+::&files('file')" -G --reversed --limit 1 -T 'change_id' - 当用户在 blob buffer 里调用 “diff 当前文件”:
-
prev/next 的抽象(建议落在 majutsu-file.el)
- 把 “rev 的前后跳转” 抽象成两个纯函数,方便在 blob/log/diff 里复用:
majutsu-file-prev-change (revset path)-> new revset (change-id 或 commit-id)majutsu-file-next-change (revset path)-> new revset
- 你给出的两个 revset 很好用,但需要注意:
rev可能是 change-id 也可能是 commit-id;如果用户输入 revset 表达式,插入时需要明确边界- 我建议先统一用
change-id做跳转目标(因为更稳定),并在显示时仍然用用户原始输入的 revset 字符串
不太清楚如何处理 conflict 和 merge,暂时不考虑处理它们。
- 把 “rev 的前后跳转” 抽象成两个纯函数,方便在 blob/log/diff 里复用:
TODO 是否要实现 jj file search ?
magit 好像没有实现 git-grep ?
https://github.com/magit/magit/issues/2849
的确还没有。
-
取舍建议
- 先不做 =jj file search=(保持与 Magit 现状一致),避免把 Majutsu 的 scope 扩到“搜索工具”。
- 但可以预留一个 “search provider” 接口:
- 默认 nil
- 用户可选择 ripgrep / deadgrep / consult-ripgrep 等外部工具
- Majutsu 只负责:从当前 repo-root 推导 search root + 默认 query(例如 symbol-at-point)
- 默认 nil
- 先不做 =jj file search=(保持与 Magit 现状一致),避免把 Majutsu 的 scope 扩到“搜索工具”。
TODO 需要学会写测试!!!
我觉得理论上来说所有行为都可能用 emacs lisp 命令实现?
TODO 如果能原生支持 forge 集成就好了
我得先琢磨琢磨 forge
TODO 添加 refs buffer (包含 working-copy remotes bookmarks tags)
可以参考类似 magit-refs.el 的实现
magit-show-refs
突然发现 magit 的 magit-refs buffer 里的 range diff 很多地方不能正常工作!
codex resume 019b66f2-02e7-7570-995c-df55f3906a9a
下面提供 3 套把 “bookmark list” 做成独立 buffer 类型的设计方案(使用 magit-section + jj bookmark list -T 模板)。
方案参考了你现有的 majutsu-op-log-mode 结构;同时基于 jj 文档中 jj bookmark list 的 -T 上下文为 CommitRef=(可用 =name() / remote() / tracked() / synced() / conflict() / normal_target() / added_targets() / removed_targets() / tracking_*_count() 等方法)。
-
方案 A:Bookmark List Buffer(MVP,JSONL + 平铺列表)
-
Buffer / Mode
- Buffer 名称:=*majutsu-bookmarks: <repo>*=
- Mode:=majutsu-bookmarks-mode=
define-derived-mode自majutsu-moderevert-buffer-function指向majutsu-refresh-buffer- 实际 refresh 函数:=majutsu-bookmarks-refresh=
- Buffer 名称:=*majutsu-bookmarks: <repo>*=
-
数据获取(单次 jj 调用)
- 调用:
jj bookmark list+ 你的过滤/开关参数(如--all-remotes/--remote/--tracked/--conflicted/--sort/-r等)
- 输出:
- 使用
-T输出 JSONL(每行一个 JSON object) - 在 Emacs 侧用
json-parse-string逐行解析 - 字段后续可扩展
- 使用
- 模板思路(伪码;每行拼一个 JSON)
- 示例字段:
nameremotetrackedsyncedconflicttarget_committarget_changeaheadbehinddesc- =added_targets=(数组)
- =removed_targets=(数组)
- 字段来源建议:
target_commit=:=if(normal_target, normal_target.commit_id().short(), "")desc=:=if(normal_target, normal_target.description().first_line(), "")ahead/behind=:=tracking_ahead_count.lower() / tracking_behind_count.lower()added/removed=:用类似 =json(added_targets.map(|c| c.commit_id().short()))的方式塞数组
- 示例字段:
- 调用:
-
渲染(magit-section)
- 根 section:
(magit-insert-section (bookmarks) ...)
- 每条 bookmark 一个 section:
- section 类型:=majutsu-bookmark-section=
value存 plist/struct(如name/remote/target等)
- section 类型:=majutsu-bookmark-section=
- heading 展示(单行、对齐):
NAME [@REMOTE] TARGET (ahead/behind) desc
- 根 section:
-
交互
RET
- 打开/切到
majutsu-log - 并对 target 执行
majutsu-goto-commit - 或备选:=jj show <bookmark>=
- 打开/切到
- 按键(复用现有 bookmark 命令):
- =d/f/r/m/t/u=:delete / forget / rename / move / track / untrack
- 执行后调用
majutsu-bookmarks-refresh
- =d/f/r/m/t/u=:delete / forget / rename / move / track / untrack
-
优点 / 代价
- 优点:
- 实现最直接
- 解析稳定
- 单 buffer 专用、结构清晰
- 字段扩展容易
- 实现最直接
- 代价:
- 列对齐/分组需要你自己维护(可后续加)
- 列对齐/分组需要你自己维护(可后续加)
- 优点:
-
-
方案 B:分组视图(Remote/Local/Conflicted 分组 + 可折叠)
在方案 A 的基础上增强 section 层级,让体验更接近 Magit。
-
section 层级
- 顶层 groups(示例):
- Local
- Remote (tracked)
- Remote (untracked)
- Conflicted
- Local
- Remote 组内再按 remote 名称分二级 section:
- origin / upstream / …
- origin / upstream / …
- 顶层 groups(示例):
-
jj bookmark list 参数建议
- 默认行为建议:
- 不带 =—all-remotes=(保持 jj 默认:只列 local + tracked 且不同步的 remote)
- 提供一个 transient toggle 决定是否追加
--all-remotes
- 不带 =—all-remotes=(保持 jj 默认:只列 local + tracked 且不同步的 remote)
- 仅看冲突:
- 使用
--conflicted
- 使用
- 仅看 tracked:
- 使用
--tracked
- 使用
- 默认行为建议:
-
交互增强
TAB折叠组(=magit-section= 自带)s弹 transient(类似 =majutsu-log-transient=),用于设置 filters:
--remote/--tracked/--conflicted/--sort/-r
-
优点 / 代价
- 优点:
- 信息密度高但不乱
- 大 repo 更好用(可折叠分组)
- 信息密度高但不乱
- 代价:
- 需要维护 buffer-local state(类似你现有的 =majutsu-log—state-*=)
- 需要维护 buffer-local state(类似你现有的 =majutsu-log—state-*=)
- 优点:
-
-
方案 C:两段式 “Bookmark → Target Commit” 视图(复用 majutsu-log 的列/样式)
核心是:bookmark buffer 仍用
jj bookmark list -T获取 bookmark → target(commit-id / change-id),但渲染时复用 log 的列定义与格式化。-
流程(两次 jj 调用)
- 调用 1:获取 bookmark → target 映射
jj bookmark list -T <jsonl>- 得到每条的
target_commit/target_change
- 调用 2:批量取 target 的提交信息(一次 log)
- 对所有 targets 组一个 revset:例如
commit_id1 | commit_id2 | ... - 跑一次:
jj log --no-graph -r <revset> -T <majutsu-log template>
- 拿到 author/time/desc 等字段
- 对所有 targets 组一个 revset:例如
- 调用 1:获取 bookmark → target 映射
-
Emacs 渲染策略
- “bookmark 行”左侧显示 bookmark 信息
- 右侧展示为熟悉的 log 样式(甚至直接复用
majutsu-log--format-entry-line的列宽计算/格式化) - 需要处理:
- targets 去重与 join
- bookmark 冲突/无 target(如 conflict 或没有 =normal_target=)的显示逻辑
- targets 去重与 join
- “bookmark 行”左侧显示 bookmark 信息
-
优点 / 代价
- 优点:
- 与主 log 视觉一致
- 字段/列复用度高
- bookmark 行可显示更丰富的 commit 信息
- 与主 log 视觉一致
- 代价:
- 两次 jj 调用
- 实现复杂度更高(revset 拼接、映射、异常情况)
- 两次 jj 调用
- 优点:
-
IDEA 使用 jj-evolog 来对特定 filesets 查看演变记录的思考
我觉得这完全是很有可能的,但是 稀疏evolog 貌似不是 jj 的原生功能,
一个妥协之计是在 template 里把不相关的文件筛选掉。
TODO 研究一下 evolog 如何实现
和正常的 log 不同,evolog 中 change-id 不重要,只需要显示 commit-id 即可
然后其它都差不多
使用的是 CommitEvolutionEntry 类型
包含一个 commit 和一个 operation
这么说起来其实先做一下 op-log 也行?
我觉得我们应该先处理好 op log 相关的东西之后再考虑 evolog
因为 evolog
TODO majtusu-goto-prev / majutsu-edit-prev
majutsu-goto-prev / majutsu-goto-next 的功能上
注意这东西和 jj-prev 和 jj-next 的目标不一样
我们可以用 jj-log 来解析 revsets
TODO 如何在 merge 之前先自动解决 jj 能自动解决的冲突?
我现在发现 jj 自动进行的冲突解决比我想象的智能,
我在重构代码的时候(整行的移动),貌似不会出现冲突?
但是如果我用 resolve,用其他工具打开(比如说 meld),
我就需要手动合并冲突文件(left)中实际上不冲突的部分到 output,
我觉得这很没有意义,需要一个解决方案能让我直接得到已经自动解决了可以自动解决的冲突的 left
Issues
TRAMP 支持
突然发现 diffstat 的处理有问题
DONE majtusu-selection 重构,我希望和 transient 深度集成
目标
- 让 selection 的状态和 transient option 的 value 绑定,避免重复维护 category 状态。
- 让 selection 的 UI、状态、argv 生成统一在 transient 生命周期中运行。
- 保持现有 API 可用,但把内部实现迁移到 option 驱动。
核心思路
- 取消 majutsu-selection-category 作为状态容器的角色。
- 新增 majutsu-selection-option (继承 transient-option),用 option slots 描述 selection 元数据:
- selection-key / selection-label / selection-face / selection-type
- locate-fn / targets-fn
- selection-key / selection-label / selection-face / selection-type
- 选择状态由 option 的 value 承载;majutsu-selection 只负责读取 option、渲染 overlay、提供 toggle/clear。
新结构与职责
- majutsu-selection-session 仅保留:
- buffer
- overlays
- buffer
- majutsu-selection-session-begin:
- 忽略旧 categories 参数(兼容旧调用)
- 仅初始化 session 并返回
- 提供 alias: majutsu-selection-setup-scope
- 忽略旧 categories 参数(兼容旧调用)
- 新增 class:
- majutsu-selection-option: 带 selection 元信息的 transient option
- majutsu-selection-toggle-option: 用于“在 point 上 toggle”的 infix option
- majutsu-selection-option: 带 selection 元信息的 transient option
API 行为调整
- majutsu-selection-values:
- 从 transient—suffixes 中查找 selection-key 对应 option
- 返回 option value (单值统一转 list)
- 从 transient—suffixes 中查找 selection-key 对应 option
- majutsu-selection-toggle:
- 根据 selection-type 处理 single/multi 的切换
- 最终调用 transient-infix-set 写回 option value
- 根据 selection-type 处理 single/multi 的切换
- majutsu-selection-clear:
- 清空所有 majutsu-selection-option 的 value
- 清空所有 majutsu-selection-option 的 value
- majutsu-selection-render:
- 遍历 transient—suffixes 收集 selection 状态
- 以 id 为 key 聚合 labels
- 调用 locate-fn 渲染 overlays
- 遍历 transient—suffixes 收集 selection 状态
同步策略(避免重复 argv)
- 在 transient-infix-set 中同步“相同 argument + selection-key”的 option。
- 用 majutsu-selection—infix-syncing 防止递归同步。
- toggle 选项不输出 argv,仅改变 selection 状态。
渲染策略
- active-ids: hash id -> labels
- 一个 id 可累积多个 label (来自多个 option)
- overlay 的 before-string 为合并后的 labels
迁移策略(按模块)
- 把 selection 配置从 majutsu-selection-session-begin 的 categories 迁移到 transient infix:
- :class ‘majutsu-xxx-option
- :selection-key / :selection-label / :selection-face / :selection-type
- :locate-fn / :targets-fn
- :class ‘majutsu-xxx-option
- 为“toggle at point”新增 infix:
- :class ‘majutsu-selection-toggle-option
- :argument 与 argv infix 保持一致,用于同步
- multi 时设置 :multi-value t
- :class ‘majutsu-selection-toggle-option
- 执行命令从 transient-args 取值,不再直接读 selection category。
测试调整
- 新增测试类 majutsu-test-option (继承 majutsu-selection-option)
- 构造 transient—suffixes 并设置 value,验证:
- render 产出 overlay
- clear 清空 value
- toggle 遵守 single/multi 规则
- render 产出 overlay
DONE diff 的 range 需要统一处理 -r / -f -t
最后还是用解析实现的划分
DONE 现在我们简化了 with-editor 设置,但是窗口行为不合我意。。。
正常来说编辑完是退回到 log buffer 的,现在是直接把 log buffer 关了。
还有我还希望它遵守我指定的 display-function 的。
现在恢复了之前的一些配置,可能还需要精简,但是能正常工作了。
DONE 使用 waser 重构 majutsu-log
我现在是在 majutsu-log—refresh-buffer 里用 majutsu-run-jj-async 来得到的输出,然后基于这个输出渲染,
这明显是不对的。
我们应该重构成使用 washer 来得到 buffer 内容。
DONE 重构 majutsu-process
完全删掉回调函数!
目标(对齐 Magit 的 process 模型)
我要把 majutsu-process 重构成和 Magit 同构的两条路径:
- “为副作用而运行”(默认路径):=run -> start -> start-process=,默认挂统一的 filter + sentinel;
调用点不传 callback,拿到 process 即可(结束后由 sentinel 自动 refresh)。 - “为解析输出而运行”(特殊路径):提供
parse-*-async返回 process,允许调用者覆盖 filter/sentinel 做增量解析或把 stdout 收集到自己的 buffer;
依然不暴露 callback 参数,而是用 process properties 串上下文。
对应到 Magit:
- magit-run-git-async -> magit-start-git -> magit-start-process
- 默认挂 magit-process-filter + magit-process-sentinel
对应到 Majutsu(目标形态):
- majutsu-run-jj-async -> majutsu-start-jj -> majutsu-start-process
(或直接把现有 majutsu-start-jj 变成 “start-process” 层) - 默认挂 majutsu—process-filter + majutsu—process-sentinel
API 设计(签名与职责)
-
Side-effect async(无回调,默认日志+refresh)
- majutsu-run-jj-async (&rest args)
- 仅负责拼接/flatten args + echo message(可选)+ 调用 majutsu-start-jj
- 返回 process
- 仅负责拼接/flatten args + echo message(可选)+ 调用 majutsu-start-jj
- majutsu-start-jj (args &optional success-msg)
- 仅负责“为副作用启动 jj 并写入
*majutsu-process*section” - 内部调用 majutsu-start-process(或把现有实现改名为 majutsu-start-process)
- 默认设置 process-put 元数据:‘section ‘command-buf ‘default-dir ‘inhibit-refresh ‘success-msg
- 默认挂 majutsu—process-filter + majutsu—process-sentinel
- 返回 process
- 仅负责“为副作用启动 jj 并写入
- majutsu-run-jj-async (&rest args)
-
Side-effect sync(保持现状)
- majutsu-call-jj (&rest args)
- 同步运行、写入
*majutsu-process*section、返回 exit code、触发 refresh
- 同步运行、写入
- majutsu-call-jj (&rest args)
-
Buffer 构建 / 解析(同步 wash,对齐 Magit 的 log 组织方式)
注意:magit-parse-git-async 在 Magit 里基本只用于 magit-blame 这类“增量解析输出”的功能;它并不参与 Magit 的 log/stage/status 的常规组织。
Magit 的 log 刷新路径是同步的:
因此 Majutsu 的 log/diff/op-log 这类“刷新时插入 section”的代码,优先迁移到同步 wash(不需要 callback,也不需要为每个 section 单独启动一个异步子进程):
- 提供(或公开)一个 wash API:majutsu-jj-wash (可以基于现有 majutsu—wash 实现/改名)
- 同步运行 jj,把 stdout 插入到当前 buffer,然后调用 washer 解析/渲染;
- 错误处理策略对齐 Magit:失败时在当前位置插入错误提示,或根据 keep-error 策略保留输出。
- 同步运行 jj,把 stdout 插入到当前 buffer,然后调用 washer 解析/渲染;
这条路径的特点:
-
优点:结构完全对齐 Magit(“插入 section = 同步取数据 + wash”),也天然满足“删掉回调函数”;
-
风险:如果 jj 命令慢,会阻塞 refresh(需要评估可接受性,或只对少量/关键 section 用同步)。
-
输出同步(保持现状)
- majutsu-run-jj (&rest args)
- 同步运行并返回 stdout 字符串(主要用于轻量读取/解析)
- 同步运行并返回 stdout 字符串(主要用于轻量读取/解析)
- majutsu-run-jj (&rest args)
-
with-editor runner(走 side-effect async)
- majutsu-run-jj-with-editor (args)
- (majutsu-with-editor (apply #‘majutsu-run-jj-async args))
- 不再依赖“output async + callback”
- (majutsu-with-editor (apply #‘majutsu-run-jj-async args))
- majutsu-run-jj-with-editor (args)
需要调整的命名(避免语义冲突)
当前代码里 majutsu-run-jj-async 是 “输出+callback” 语义;重构后它要变成 “side-effect async(无回调)”。
因此需要:
- 现有输出异步函数改名:majutsu-run-jj-async -> majutsu-run-jj-output-async (或直接删除;常规 UI 刷新改为用 majutsu-jj-wash)
- 新增/改造的 majutsu-run-jj-async 按上述 side-effect 语义实现
并且新增一个更贴近 Magit 的同步插入工具:
- majutsu-jj-wash(可以基于现有 majutsu—wash 实现/改名)
调用点迁移策略(不自己决定,按模块逐个改)
-
majutsu-log / majutsu-op 这类“需要拿输出渲染 UI”的地方:优先迁移到同步 wash(对齐 Magit)
- 不再调用 majutsu-run-jj-async (callback…);
- 改成在 refresh/insert hook 里使用 majutsu-jj-wash (同步获取输出 + wash 解析);
- 若某些 section 过慢(例如大型 diff),再单独讨论是否保留异步加载(但也尽量不暴露 callback API,而是用模块自定义 sentinel/filter + process-put 串状态)。
- 不再调用 majutsu-run-jj-async (callback…);
-
纯副作用命令(commit/squash/undo/redo/fetch 等):
- 统一走 majutsu-run-jj-async 或 majutsu-run-jj-with-editor;
- 依赖默认 sentinel 的 refresh/错误展示。
- 统一走 majutsu-run-jj-async 或 majutsu-run-jj-with-editor;
进程元数据约定(process-put keys)
统一约定 process-put 的键名(尽量对齐 Magit):
- ‘section: 对应的 process section(用于把输出 propertize 到 section)
- ‘command-buf: 启动命令时的 buffer(用于 sentinel refresh 回到正确上下文)
- ‘default-dir: 运行时 default-directory(用于 refresh/错误定位)
- ‘inhibit-refresh: 禁止 sentinel 自动 refresh(批处理用)
- ‘success-msg: exit code 0 时显示
- parse 路径额外:‘stderr-buf, ‘parsed, 以及模块自定义 key(例如 ‘majutsu-log-target 等)
仅在必要时引入 parse async(对齐 Magit 的实际使用范围)
如果某个功能确实需要“边输出边解析/增量更新 UI”(或者需要把 stderr 单独收集),再引入类似 magit-parse-git-async 的工具:
- majutsu-parse-jj-async
返回 process,调用者 set-process-filter/sentinel,并用 process-put 串模块状态。
但 log/op/diff 的常规 refresh 优先用同步 wash。
TODO local variable 管理
参考 Buffer-local variables (magit-mode.el)
我需要哪些 majutsu-buffer-* 变量?
每种能够变化的 buffer 都得有对应的 local variable
下面的变量忽略 majutsu-buffer- 前缀
arguments
diff-args
diff-range (需要同时考虑到 -r 和 -f -t)
diff-filesets
log-args
log-revsets
log-filesets
evolog-args
evolog-revsets
op-args
file-name (之后可能会搞类似 blob visit 类似的东西)
还有配套的 revision 和 refname
refs ?
只有 bookmarks list 可以设置参数,workspace tag remote 都不行,暂时不考虑。
TODO majutsu-log-refresh
我们可以用这个东西来切换 log 的 template!
magit 就在 log refresh 里控制了 margin 的显示,而我们可以有更细粒度的控制。
TODO 复现 magit-log-buffer-file
可以用 jj log -r files(expression) 得到我们想要的信息。
magit 还支持 region 参数,可以使用 log -L 来做得到更细粒度的 diff,这是 jj 还做不到的!
还有,他会支持在 log buffer 里就地显示 diff,我不知道这在有 graph 的时候如何展示。
总之,无论如何,这都是在 log-refresh 实现后的任务了
DONE diff 在大 hunk 交界处来回跳的时候会出现延迟,应该是反复字体化了
这种运行时延迟应该如何调试?
一个例子里是我的 org 笔记的 noxzsuzq 这个 change
不只是打开 diff 有延迟 在 section 边界移动光标、用 C-j/k 移动 section 也会有一样的问题
这是哪里来的延迟?magit没有这个问题,我也不能在其他 revision 上复现,也不是 大diff 的问题
和我想的一样,的确是在这里搞出的问题
uvlzxtll optimize diff performance
是单个长 hunk 的问题,是高亮刷新的问题,magit 对同一个 commit 在第一次光标移到那里的时候也会卡,但是只会卡一下,majutsu 现在会一直卡,好像是有高亮刷新策略的问题。
还有 magit 对于非常长的 diff 完全不会尝试显示背景高亮,这里的逻辑是我没有实现的。
TODO 在垂直分割的窗口下 diff 的时候会改变上方窗口高度,当 majutsu buffer 在下方的时候不会
毫无思路,可能是 transient 的问题?
DONE majutsu-new-dwim
现在在未添加 rev 参数的时候会退到 @
实际上应该先考虑当前 jj-commit section
TODO 支持 magit-show-commit 并对 change-id 提供支持
因为 jj 使用的 change-id 的确是不会变的
TODO jj-interdiff 支持
.
DONE jj-file-annotate 支持(类似 git 的 blame)
AnnotationLine Type 记录了这一行的 original_line_number 元数据
</home/disk/Dev/jj/docs/templates.md>
这是我必须实现的功能!而且很大程度上可以直接套 magit-blame
TODO 用 pop-to-buffer-same-window 跳到diff buffer之后 quit 到 log buffer 后 margin 丢失
不太重要,暂时搁置
TODO jj-simplify-parents command support
这种小功能,不知道怎么处理,感觉不是很常用,我需要绑到单独的键上吗?
TODO revset builder
codex resume 019aa1d9-0262-7c31-aa61-be1ae1635008
不太清楚怎么兼容手动输入和可视化选择
TODO fileset builder
先实现一下可以让用户输入 fileset 吧
TODO 支持 jj-tag
.
DONE diffstat 中路径过长 ATTACH
通过直接找 file header 解决的问题
DONE majutsu-jump-to-diffstat-or-diff 对 rename 条目不能正常工作
DONE 直接用 file section,支持跳转到文件
DONE diffstat 解析出错 ATTACH
终端中理想的解析(有 binary 还有 rename)
目前 majutsu 的解析
magit 的解析
可见主要问题在于 (binary) 格式的不同,
然后这个 | 的中文对齐不太确定需不需要管。
related issue
jj-vcs/jj#7218 diff —stat output visual details for binary files
DONE jj git push --allow-new is deprecated
DONE 要是能在 redo / undo 的时候回显它到底干了什么就好了
通过整个 process 模块解决了这个需求,
虽然我还没怎么 review 过,但是现在能跑。
DONE 重构 revision-section 结构
我觉得我们可能不需要同时拿到 commit-id 和 change-id
只需要在 hidden 或者 divergent 的时候记录 commit-id ,然后其他时候记录 change-id 就行!
记录 change-id 的目的是防止外部修改导致当前缓存的 hash 失效,会导致出现 divergent 问题
DONE --ignore-immutable support
我觉得这可能也是比较重要的功能?
可以加一个统一的 flag 来控制这个
DONE diff 解析有非常严重的解析性能问题
magit 可以在3秒内解析25万行的diff!
DONE 多 workspace 下 goto current 会出现问题!
现在是直接跳到 @ ,显然是不对的。。。
DONE flatten log template
∅ U+2205 Empty Set:数学上表示“空”,含义最贴合“无描述”。等宽终端普遍支持,宽度=1。
⌀ U+2300 Diameter Sign:视觉像被划掉的圆,容易读作“空”,常见于工程图,等宽字体支持度也不错。
Ø U+00D8 Latin Capital O with Stroke:与“空 / 无”关联明显,但有时被当作字母 O,可能在排序或搜索时产生歧义。
□ U+25A1 White Square:表示“留空 / 待填”,字体兼容好,但语义不如“∅”直观指向“无”。
🚫 U+1F6AB Prohibited:语义清晰,但宽度在等宽终端可能占 2;一些轻量终端或日志工具对 emoji 支持一般。
这东西其实可以做的很美观
DONE 表示工作区的 default@ 也会被认为是 bookmark!
问题在于,我在显示 log 行元素的哪里没有把 bookmarks 和 其他refs 分开,全部放到 refs 里了
DONE 除了 log-display-function 和 message-display-function 我还想要给其他组件预设显示函数
codex resume 019af299-08ff-7870-97e2-dcdde47b5089
DONE majutsu-jump-to-diffstat-or-diff
修复只能单项跳转的问题:
codex resume 019ac604-37d9-7f50-842f-c7f50d47496c
DONE 各种 refresh 没有确保当前的 buffer 正确
changeid: zprvnsxy
codex resume 019aca6b-5cb2-76e0-9392-d7ec4c3ef42a
DONE diff section refine
解决了当前section的背景样式丢失的问题
changeid: usxpzopo
codex resume 019ac5cc-e08b-7b50-9cfc-b58ebf804753
DONE diffstat 解析
叫它换用 rx 解析,然后就一下就写对了
changeid: owwuopzt
codex resume 019ac4fe-148c-7e52-b57d-9b8c724349f9
DONE with-editor 集成
关于 with-editor 的理解还是放在 with-editor 比较好
with-editor-server-window-alist
文件名和想让他运行的函数的对应
我现在运行的函数是 majutsu—with-editor-display-buffer
windows 下 with-editor 中的 start-file-process 不起作用
的确就是那个括号的问题,我让它在 windows 下强制删括号了,就能正常工作了
可能有更优雅的解决方案,但是我不想管了
with-editor 本身存在问题
最后的解决方案是不用 switch-to-buffer 改用 pop-to-buffer
在 log buffer 打开 message/diff buffer 再关掉,可以回到 log buffer 但是这时候再 quit-window 就不会自动删除 window 了
直接由 pop-to-buffer 得到的 window
((quit-restore window window #<window 3 on majutsu.el> #<buffer *majutsu-log:majutsu*>))调用 diff 后的 window parameter
((quit-restore-prev other
(#<buffer *majutsu-log:majutsu*> 1 #<marker at 11 in
*majutsu-log:majutsu*> 84)
#<window 232 on *majutsu-diff*> #<buffer *majutsu-diff*>)
(quit-restore window window #<window 3 on majutsu.el> #<buffer *majutsu-log:majutsu*>))调用 message buffer 之后的 window parameter
(quit-restore other
(#<buffer *majutsu-log:majutsu*> 1 #<marker at 11 in *majutsu-log:majutsu*> 84)
#<window 264 on editor-67yjNs.jjdescription> #<buffer editor-67yjNs.jjdescription>)关掉 diff 之后的 log buffer
((quit-restore-prev)
(quit-restore other
(#<buffer majutsu.el> 30168 #<marker at 30965 in majutsu.el> 84)
#<window 240 on *majutsu-log:majutsu*> #<buffer *majutsu-log:majutsu*>))关掉 message buffer 之后的 log buffer
((quit-restore-prev) (quit-restore))这个可以用来记录日志
(defvar my/quit-restore-log nil)
(defun my/track-quit-restore (orig window parameter value &rest args)
(when (memq parameter '(quit-restore quit-restore-prev))
(let ((trace (with-temp-buffer
(backtrace)
(buffer-string))))
(push (list :time (current-time-string)
:buffer (window-buffer window)
:window window
:parameter parameter
:value value
:stack trace)
my/quit-restore-log)
(message "set-window-parameter %s -> %S\n%s"
parameter value trace)
;; (debug) ; 若希望直接进入调试器
))
(apply orig window parameter value args))
(advice-add 'set-window-parameter :around #'my/track-quit-restore)DONE windows 下的 commit 字符编码问题
由于 jj 在 windows 上的性能优于 git
所以可能是有不少人是因为这一点来使用 jj 的
应该做好 windows 兼容
出现问题的地方是:子进程调用使用的 codepage 是固定的!并且默认不是 utf-8
问题:如果当前目录(default-directory)含有系统代码页无法表示的非 ASCII 字符,Emacs 在 Windows 上启动外部命令会失败,出现 “Spawning child process: Exec format error”。
原因:Emacs 在 Windows 调用程序时必须把程序路径和参数用系统代码页编码。你的系统代码页是 1252(西欧),而像 “zażółć gęślą jaźń” 这类波兰字符不能用 1252 表示,所以失败。很多 Windows 程序只支持当前代码页,不真正支持 UTF‑8/Unicode。
误区:在 Emacs 里改编码(如设为 cp1250)只影响缓冲区、文件和键盘/终端 I/O,不会改变操作系统的代码页,因此对启动子进程没用。
解决:把 Windows 的“非 Unicode 程序的语言”(系统代码页)改成能覆盖你语言字符的代码页(如 cp1250),或避免在路径中使用不在当前代码页内的字符,或使用真正支持 Unicode 的程序/环境。文末作者已将系统代码页全局改了,之后就能正常运行。
如何查询 codepage?
可按需查询两种不同的 code page(很重要的区别):
- 系统 ANSI 代码页(ACP):很多非 Unicode 程序和 Emacs 在 Windows 下给子进程编码时受它限制
- 控制台代码页(chcp):仅影响当前终端的输入/输出
查询方法:
- PowerShell
- 系统 ANSI(ACP):[System.Text.Encoding]::Default.CodePage 和 [System.Text.Encoding]::Default.WebName
- 控制台:[Console]::OutputEncoding.CodePage
- 系统 ANSI(ACP):[System.Text.Encoding]::Default.CodePage 和 [System.Text.Encoding]::Default.WebName
- CMD
- 控制台:chcp
- 注册表(系统 ANSI 与控制台 OEM):reg query “HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage” /v ACP 和 /v OEMCP
- 控制台:chcp
- Emacs 中(简便调用)
- 控制台:M-: (shell-command-to-string “cmd /c chcp”) RET
- 系统 ANSI:M-: (string-trim (shell-command-to-string “powershell -NoProfile -Command [System.Text.Encoding]::Default.CodePage”)) RET
- 控制台:M-: (shell-command-to-string “cmd /c chcp”) RET
- 图形界面
- 控制面板 → 区域 → 管理 → “非 Unicode 程序的语言”(这里显示/更改系统 ANSI 代码页;勾选“使用 Unicode UTF-8(测试版)”则为 65001)
- 控制面板 → 区域 → 管理 → “非 Unicode 程序的语言”(这里显示/更改系统 ANSI 代码页;勾选“使用 Unicode UTF-8(测试版)”则为 65001)
更好的解决方案
借鉴 magit 的解决方案,用 with-editor
DONE 实现在 majutsu buffer 外的 majutsu-diff
DONE 复杂 graph 测试 ATTACH
无意间出现了问题!
这是我在测试 duplicate 功能时的截图
这个是相同template下的正确的 graph 输出
行首空格被吞掉了!
DONE bookmark 向前移动
需要使用 --allow-backwards -B flag
我觉得可以放到 M 上
DONE 不及时刷新可能会出现 change-id?? 的情况 ATTACH
我应该让他优先解析 change-id 的
我觉得我需要对这种情况设置一个比较的函数
Manaul
不是很了解应该怎么组织啊
一些规范
fill-column 设置为 70
70 列主要来自 GNU/Emacs 的传统文本排版习惯:在 80 列终端里留出前导引号(邮件引用的 “> ” 多层缩进)、行首缩
进和对齐空间,还能降低长行 diff 带来的噪音。
Emacs 自带的 many manuals / NEWS 也常用 70(或 72)列作为 fill-column;
Magit 文档沿用了这一约定,以保证在终端、邮件补丁、git diff 和 Texinfo 导出时都保持良好的可读性。
但是这对中文来说的确是短了一点,不太能对中文使用,因为中文一般来说不会真的占两个字符长度!
然后中文行的长度就会显得短很多
sentence-end-double-space
那是为了写/导出手册时的“句间双空格”排版习惯:
- Emacs 的文本约定里,句子末尾默认用两个空格分隔,便于 fill-paragraph、句尾检测和排版工具(如 Texinfo)准确断句。
- Magit 手册是从 Org 导出 Texinfo 的长篇文档,保持双空格可以在 refilling 时减少把缩写/小数误判为句末的概率,
也方便后续 diff(少因自动 reflow 产生大面积改动)。 - 所以 magit.org 里句点后保留两个空格是有意遵循传统的 Emacs/Texinfo 文档格式,而不是手误。
要不要使用 fill-paragraph
不一定需要使用
Texinfo (and Info) ignore line breaks inside a paragraph, so authors
often “semantically line break” instead of using fill-paragraph.
Reasons the Magit docs do this:
- Keep identifiers (like winner-undo) off the wrap boundary so they
don’t get split with hyphens in the source. - Make diffs readable in version control: one clause/sentence per line
means edits produce small, localized hunks. - Avoid reflow churn when only a word changes.
- Preserve deliberate grouping (e.g., command + keybinding) that
fill-paragraph might separate.
So the odd-looking breaks are a maintenance choice; the rendered output is unchanged. You can keep
using semantic line breaks if that workflow (clean diffs, no code-word splits) matters to you.