交流向实验性 QuickAdd 批量重命名小标题防止双链断裂

非计算机专业,研究 Ob 学习,卡在不知道 this.file、this. 具体指什么,代码发出来更多供测试,期待交流建议。

Ob“重命名当前小标题”命令无法做到的这个也无法做到。

比如同文档同名标题,引用时可以采用类似路径的格式做到区分,
【已解决】为什么引用文档内的同名标题永远显示第一个 #3

但重命名希望同步更新反向链接时,使用 Ob 内置“重命名当前小标题”命令无法区分,见下:

imageimage

测试结果能看到 Ob 内置的命令修改的是同文档同名二级标题的引用。

基本配置参 QuickAdd JS & Templater JS 简介及相互修改“QuickAdd Capture 加载内部代码”。

效果 GIF,点击展开

原理同 QuickAdd 正则保值替换选单
对鼠标选区内小标题使用 JS 方法 .replace() 正则查找替换,
选区需覆盖标题前 # 号。全文替换 Ctrl A 全选即可。

20240222_110417

预填了一个格式化序号便于调试。实际使用多半用来批量改错别字。

第一个填空输入希望作用的标题级别或级别范围。第二个输入正则。第三个输入替换函数。

js quickadd 代码
let rgx = '/.*/', form = '`$&-${i+1}`'; const prompt = (str, holder, value)=> this.quickAddApi.inputPrompt(str, holder, value)
, { editor: Editor, file } = app.workspace.activeEditor, lv = await prompt('级别', '1-6', '1-6')
, confirm = async file=> { let r = []
  , repChan = (raw, isRef)=> { chans.map(chan=> {
    let rgx = new RegExp(isRef ? `\\[\\[.+?#${chan[0]}.*?]]` : `^#{${chan[2]}} ${chan[0]}$`, 'gm')
    raw = raw.replace(rgx, m=> m.replace(chan[0], chan[1])) }); return raw }, test = chans[0].heading
  do { rgx = await prompt(`正则`, rgx, rgx); if (!rgx) return; form = await prompt(`替换`, form, form) } while (!form)
  if (await this.quickAddApi.yesNoPrompt(test, [test].map((p, i)=> test.replace(eval(rgx), eval(form))))) {
    chans = chans.filter(p=> p.level >= lv.slice(0, 1) && p.level <= lv.slice(-1))
      .map((p, i)=> [p.heading, p.heading.replace(eval(rgx), eval(form)), p.level])
    app.fileManager.iterateAllRefs((rPath, rbj)=> {
      if (app.metadataCache.getFirstLinkpathDest(rbj.link.split('#')[0], rPath)?.path == file.path) r.includes(rPath) || r.push(rPath)
    }); r.map(async rPath=> await app.vault.process(app.vault.getAbstractFileByPath(rPath), raw=> repChan(raw, 1)))
    setTimeout(()=> Editor.replaceSelection(repChan(Editor.getSelection(), 0)), 50)
  } else confirm(file)
}, hds = app.metadataCache.getFileCache(file).headings; if (!hds) return; let chans = hds.filter(hd=>
  hd.position.end.line >= Editor.getCursor('from').line && hd.position.end.line <= Editor.getCursor('to').line
); if (!chans[0]) { new Notice('无选中', 1000); return }; confirm(file)

用了一个应该不在 Obsidian Docs 里的 API,对应解释是根据返回结果猜测的:

app.fileManager.iterateAllRefs((参数1, 参数2)=> )

这个 API 似乎找到的是全库全部出链 outlinks。

参数 1 代表出链文本所在文档的路径,如上文中文档“Wiki”的路径。

参数 2 代表一个对象,包含这个出链文本和所在编辑器位置等信息。

1 个赞

this表示的是当前对象 this.file就指在使用这段代码的文件 没有特别研究过ob 单纯从编程js编程语言角度来分析

谢谢,主要是不知道怎么上溯这些 this.。
有些能猜出来,比如截图中 n.fileManager.iterateAllRefs() 的 n 是 app。

截图来源开发控制台 - 源代码 Sources - obsidian.md - app.js。

目前感觉,Ob 官方的“重命名当前小标题”命令也是通过检索全库文档内容正则查找替换实现,并未存储额外的链接信息来控制,这可能是为什么对于同文档同名标题,即使引用时已链接到正确的标题,官方命令也无法重命名正确的引用链接。

若确实如此,#1 代码和官方的逻辑便是一致的。

但一是我不能肯定,二是官方的更有保障,谨慎点总没错。