number-headings-obsidian
number-headings-obsidian copied to clipboard
Update the reference to the heading
I have a lot of wiki links to headings. After I updated the heading numbers with this plugin, the links break.
Is it possible to update all the references when updating the heading numbers. Just like the official obsidian context menu item "rename this heading".
This would be really an awesome feature.
+1 for this feature.
+1 sincerely wish this improvement to be introduced
I tried to implement this improvement but found it harder than i thought.
This auto-numbering plugin uses editor.transaction([]{text, from, to})
to directly change the text of the heading, not considering the hierarchy of headings.
I looked up official api doc of obsidian, but did not find any function like "changeHeading" which expected to change the text of heading and trigger the update references of this heading.
Since obsidian is not an open-souce software, i cannot discover how internal "rename this heading" in right-click menu works.
I looked through the code of another plugin obsidian-filename-heading-sync which also modifies headings.
It does nothing to do with the editor, just uses obsidian api to open the file, modify lines, and use obsidian api to write it back(this.app.vault.modify(file, text)
).
However, it does trigger the update of references of the headings. I don't think this solution is graceful when the document is too large, but it works for the most of the time.
This has been an issue from the beginning. It does need addressing because one ends up with a lot of broken links in the course of time.
I am not sure, but I get the impression Kevin has abandoned this plug-in because he has not been active with any of the issues for quite a while. This particular one was raised in December last year, but Kevin has not reacted to any of the comments.
Does anyone have a workaround?
I'm still here, just slow to respond. I'll look into it!
Hi @onlyafly. I think @Maxlinn's suggestion is feasible. Updating the reference to the heading in the vault could be a manual command or option which helps a lot. Since this command is manual waiting is acceptable.
Notes are borned to be linked. Without updating reference to the heading Number headings
plugin could not be that useful. I hope this feature could be added. Thanks. 😃
I'm actively researching this issue now and hope to have a solution soon!
In case you are interested, I'm looking for help on the Obsidian developer forums at https://forum.obsidian.md/t/api-to-access-the-rename-heading-functionality/52803
@onlyafly Sincerely thanks to you. ❤️
Hi, @onlyafly. There's another method to solve reference problem. I've got the idea from plugin called Visually numbered heading.
So Number headings
plugin could introduce front matter attribute or even setting option to add heading prefix visually or trully. With various settings of number prefix Number headings
would be super convenient.
I hope this could help. :smiley:
@onlyafly I was using your plugin, but broken links got me really sad recently 😭 So I looked to what I can to about it. Then I found out, that in my workspace, Rename this heading...
option in context menu actually renames all links to it!
BUT... then I then tested it in new workspace and it was not working here... So I found out, that plugin Find orphaned files and broken links was doing it!
It does not update text of the link, but it does update the link!
Hopefully this will help find a way to implement this 🙏
Thanks! I'll look into it
Can confirm - that would be a great enhancement!
This would be a great feature.
Idea: rather than changing all the references, which is ugly as well as tricky to do, couldn't create an anchor that would work like the original heading?
So (optionally) instead of transforming
## Foo Header
into
## X.Y Foo Header
transform it into
## X.Y Foo Header
^foo-header
Then you can use [[#^foo-header]] (this is the kebab-cased version of the header) to refer to the anchor, and it won't change when the numbers change.
@onlyafly I was using your plugin, but broken links got me really sad recently 😭 So I looked to what I can to about it. Then I found out, that in my workspace,
Rename this heading...
option in context menu actually renames all links to it!BUT... then I then tested it in new workspace and it was not working here... So I found out, that plugin Find orphaned files and broken links was doing it!
Heyha ! This actually works without any plugin ! I just tested it in the obsidian sandbox, and it works ! It updates all links and linked sections ! So it's probably a setting in obsidian or a new update I'm not aware of? Anyway thanks for the pointer, with a shortcut in place it will greatly reduce linked section rot !
This actually works without any plugin
I can confirm, maybe it worked and I didn't check it properly, or maybe they actually implemented this idea. I think all that needs to be done now is to get this plugin to use this Rename this header...
feature @onlyafly 🙏🏻
Hey @onlyafly, here is the source code of how Obsidian updates all references of headings. I found them in debugger. They are easy to find if you discover the place of fsPromises.writeFile
.
I think fileManager.iterateAllRefs
is the function you need.
part1
t.prototype.submit = function(e) {
return v(this, void 0, void 0, (function() {
var t, n, i, r, o, a, s, l, c, u, h, p, d, f, m, g, v, b, w, k, C;
return y(this, (function(y) {
switch (y.label) {
case 0:
return (t = this.getError(e)) ? (qS(this.inputEl, t, {
placement: "right"
}),
[2, !0]) : (i = (n = this).file,
r = n.app,
o = r.fileManager,
a = r.vault,
s = this.getChanges(e),
0 !== (l = s.count()) ? [3, 1] : (this.replaceInEditor(e),
[3, 6]));
case 1:
return (c = s.get(i.path)) && c.length > 0 ? (s.removeKey(i.path),
u = this.replaceInFile(e),
h = {
link: "",
original: "",
position: {
start: {
line: 0,
col: 0,
offset: u.start
},
end: {
line: 0,
col: 0,
offset: u.end
}
}
},
[4, a.process(i, (function(e) {
var t = c;
return t.push({
sourcePath: i.path,
change: u.text,
reference: h
}),
DR(e, t)
}
))]) : [3, 3];
case 2:
return y.sent(),
[3, 4];
case 3:
this.replaceInEditor(e),
y.label = 4;
case 4:
return [4, o.updateInternalLinks(s)];
case 5:
y.sent(),
p = zf.nouns.linkWithCount({
count: l
}),
d = zf.nouns.fileWithCount({
count: Object.keys(s.data).length
}),
f = zf.dialogue.msgUpdatedLinks({
links: p,
files: d
}),
new hF(f),
y.label = 6;
case 6:
for (m = o.linkUpdaters,
g = 0,
v = Object.values(m); g < v.length; g++)
b = v[g],
w = this.getCustomReplacements(e),
k = w.oldSubpath,
C = w.newSubpath,
b.renameSubpath(this.file, k, C);
return this.close(),
[2]
}
}
))
}
))
}
part2
t.prototype.getError = function(e) {
return "" === e ? "Cannot be empty" : null
}
,
t.prototype.getChanges = function(e) {
var t = this.file
, n = this.app
, i = n.metadataCache
, r = new it
, o = cS(this.oldHeading).toLowerCase()
, a = uS(e);
return n.fileManager.iterateAllRefs((function(e, n) {
var s = nS(n.link)
, l = s.path
, c = s.subpath;
c && (cS(c.substring(1)).toLowerCase() === o && i.getFirstLinkpathDest(l, e) === t && r.add(e, {
sourcePath: e,
reference: n,
change: AR(n, l + "#" + a)
}))
}
)),
r
}
,
t.prototype.replaceInEditor = function(e) {
var t = this.cursor
, n = this.editor
, i = t.line
, r = n.getLine(i)
, o = this.replaceHeadingText(r, e);
n.setLine(i, o)
}
,
t.prototype.replaceInFile = function(e) {
var t = this.cursor
, n = this.editor
, i = t.line
, r = n.posToOffset(om(i))
, o = n.getLine(i)
, a = this.replaceHeadingText(o, e);
return {
start: r,
end: r + o.length,
text: a
}
}
,
t.prototype.replaceHeadingText = function(e, t) {
return e.replace(/^(#{1,6} ).*/m, (function(e, n) {
return n + t
}
))
}
,
t.prototype.getCustomReplacements = function(e) {
return {
oldSubpath: cS(this.oldHeading).toLowerCase(),
newSubpath: uS(e)
}
}
@onlyafly Somebody has implemented renaming headings in quickadd:https://forum-zh.obsidian.md/t/topic/30546/1
app.workspace.activeEditor
app.fileManager.iterateAllRefs
app.metadataCache.getFirstLinkpathDest
app.vault.process
app.metadataCache.getFileCache(file).headings
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);
Hi, @onlyafly . I have the same issue and feel sad to get almost all my links broken 😭. Is this about to be solved? Thanks a lot.
I think with all the ideas above, this should not be too difficult for me to implement. I just need to find some time to devote a few solid hours to implement and debug it!
Thank you! I'm very much looking forward to the new version of the plugin.😊
Looking forward to this new feature.