Templater icon indicating copy to clipboard operation
Templater copied to clipboard

Unexpected behavior when using hooks.on_all_templates_executed on a blank file

Open simonalveteg opened this issue 1 year ago • 21 comments

  • OS: Windows 11
  • Templater version: 2.1.2
  • Obsidian version: 1.5.3
  • Templater settings: default settings with templates folder set to /templates/

Describe the bug When using the hooks.on_all_templates_executed callback like in the documentation with some text that should get added to the note before the frontmatter is updated:

<%*
tp.hooks.on_all_templates_executed(async () => {
  const file = tp.file.find_tfile(tp.file.path(true));
  await app.fileManager.processFrontMatter(file, (frontmatter) => {
    frontmatter["key"] = "value";
  });
});
%>

TEXT THAT SHOULD STAY

if the note the template is added to is blank (no text, no frontmatter), the text gets added and then removed when the frontmatter is added. By just adding a single character to the note before executing the template, both the text and frontmatter gets added without problem. (There is a pop-up "error" that the file has been modified externally and that it's merging the changes, not sure if that's an issue but you asked about it on discord)

Expected behavior The text and frontmatter should get added to the note even if the note is completely empty.

Additional context I've tried running the template above on two windows 11 computers with the same result, and also tried it in a new empty vault with only templater installed. I'm not 100% sure, but I believe that it worked as expected before, since I have a few templates that rely on this method. I don't use them regularly though, and I updated them to use this method last time I needed to use them (when the hook had just gotten added), so it's possible that I'm misremembering or that the notes I tested them on weren't completely empty.

link to message on discord

simonalveteg avatar Jan 25 '24 21:01 simonalveteg

I believe I've fixed this in the latest release, can you verify on your end?

Zachatoo avatar Feb 02 '24 06:02 Zachatoo

Yes! It works now, thanks!

simonalveteg avatar Feb 02 '24 07:02 simonalveteg

I'm actually still experiencing this issue. I'm on the latest version of templater.

My templates are structured like this: image image

I've tried to isolate the situations that it occurs in and this is what I've found:

Scenario 1: If I "Create new note from template" and I rely upon the "on_all_templates_executed" hook, I get an error "no such file or directory". This is obviously due to me using templater to move the file.

Should the hook handle that scenario?

My workaround for this scenario is to wrap the entire command within a setTimeout, and even with a 0 as the timeout it all works as expected.

Scenario 2: If I "Insert template" into a blank document the template is overwritten with nothing but the properties. Even using a super simple text using the exact example from the docs produces the same result. If you add a single character to the note before inserting the template, everything works as expected. If you add a really large setTimeout it bypasses the issue too.

Thanks,

hjone72 avatar Feb 27 '24 07:02 hjone72

@hjone72

Scenario 1: How are you moving the file? Is it in one of the templates you're including? If you're moving in a tp.hooks.on_all_templates_executed function, there's no guarantee on the ordering of those functions. Regardless, I have added a 1ms delay to make it less likely for this to happen.

Scenario 2: Can you provide a minimum reproducible example that I can test with to make a fix for this?

Zachatoo avatar Mar 02 '24 03:03 Zachatoo

I am still experiencing this bug as well. Obsidian 1.5.8, Templater 2.2.3

This template overrides the text for me if inserted into an empty file

Line 1
Line 2
Line 3
Line 4
Line 5

<%*
tp.hooks.on_all_templates_executed(async () => {
  const file = tp.file.find_tfile(tp.file.path(true));
  await app.fileManager.processFrontMatter(file, (frontmatter) => {
    frontmatter["key"] = Math.random();
  });
});
%>

If a delay is added with await new Promise(resolve => setTimeout(resolve, 3000)) then the template works. I'd guess it's because Obsidian hasn't saved the changes to the file yet, and so considers it an empty file for the time being.

HyperEpsilon avatar Mar 12 '24 22:03 HyperEpsilon

The issue is back for me as well, when I try to run the same example template as I posted earlier the text once again disappears once the properties have been added.

obsidian version 1.5.12, templater version 2.2.3

simonalveteg avatar Apr 04 '24 06:04 simonalveteg

I can also reproduce this issue.

Obsidian: 1.5.12 Installer: 1.5.12 Templater: 2.2.3

Steps to reproduce:

  1. Apply a template to a blank note using the "Open insert template modal"
  2. The note must only have one line! If you add more lines, the issue disappears.
  3. The template must include content and tp.hooks.on_all_templates_executed with some application of app.fileManager.processFrontMatter.

Minimal example template:


## Attendees
- John
- Hendrik
- 
## Agenda
- CallGate  

<%*
tp.hooks.on_all_templates_executed(async () => {
	const file = tp.file.find_tfile(tp.file.path(true));
	await app.fileManager.processFrontMatter(file, (frontmatter) => {
		frontmatter["status"] = "In progress"; // create a new property or update an existing one by name
		frontmatter["Title"] = "New title";
	});
});
-%>

Demo: Obsidian_WdW5uyVPe0

The "Note.md was modified externally, merging changes automatically" notice does not appear when the issue occurs. When it does, the issue does not occur, as far as I can tell. If you add more than the one aforementioned line, this notice appears, and the note content is not overwritten!

FeralFlora avatar Apr 26 '24 11:04 FeralFlora

Thank you all, I can reproduce this issue and am investigating. I know exactly what line of code I can add to resolve this issue, however it would cause a regression for another issue #1253. I am investigating how I can resolve both of these issues.

Zachatoo avatar May 05 '24 19:05 Zachatoo

Should be fixed in 2.2.4!

Zachatoo avatar May 05 '24 19:05 Zachatoo

I'm seeing some very unpredictable behaviour with this change.

EDIT: I've isolated it down to a race condition while saving a file and creating a new one at the same time. (See image below)

image

The weird part is, once this error occurs Obsidian never recovers. You must reload the app and then everything will work again, until the race condition is triggered and the error occurs.

If I revert back to the previous version of Templater or remove the line await app.vault.append(active_editor.file, ""); then everything works again.

hjone72 avatar May 06 '24 00:05 hjone72

Should be fixed in 2.3.0, thanks @johnfromoptus !

Zachatoo avatar May 08 '24 04:05 Zachatoo

Obsidian OS plugin1
1.5.12 Win 10 x64 pro (french) 22H2 v2.3.1

Hi,

It still happens in 2.3.1 when using open insert template modal on an empty file.

Here is a template. The only workaround is to replace hook by setTimeout() with a delay of about 4000 on my system. If less than 1000, bug occurs for sure.

<%*
/* Be sure to switch to edit mode first (live preview)
   It is required so it can insert the template in existing file. */
tp.user.Switch_to_edit_mode();

// start of template construct
let title = tp.file.title;
// tags are type of list and are expressed as array
const defTag = ["Obsidian/Plugin"];

if (title.startsWith("Untitled")) { 
  title = await tp.system.prompt("What is the plugin name ?"); 
  await tp.file.rename(title);
}

let repo = await tp.system.prompt("What is the plugin repo URL ?");

setTimeout(() => {
  app.fileManager.processFrontMatter(tp.config.target_file, frontmatter => {
  frontmatter["tags"] = defTag;
  frontmatter["repo"] = repo;
  frontmatter["type"] = "plugin";
  })
}, 500) // increase this timeout until the bug goes away

await tp.file.move("Categories/Tutos/Obsidian/Plugins/" + title);
%>
# Description

<% tp.file.cursor(1) %><%* app.workspace.activeLeaf.view.editor?.focus(); %>

# My use

# Shortkeys

| Shortkeys | Actions |
| --------- | ------- |
|           |         |
|           |         |


If use this script version using hook with command create new note from template, I get the opposite: the template content is inserted but not the properties.

<%*
/* Be sure to switch to edit mode first (live preview)
   It is required so it can insert the template in existing file. */
tp.user.Switch_to_edit_mode();

// start of template construct
let title = tp.file.title;
// tags are type of list and are expressed as array
const defTag = ["Obsidian/Plugin"];

if (title.startsWith("Untitled")) { 
  title = await tp.system.prompt("What is the plugin name ?"); 
  await tp.file.rename(title);
} 

//don’t insert the title as a tag because it can be long and contains space
//defTag.push(title)

let repo = await tp.system.prompt("What is the plugin repo URL ?");

tp.hooks.on_all_templates_executed(async () => {
  const file = tp.file.find_tfile(tp.file.path(true));
  await app.fileManager.processFrontMatter(file, (frontmatter) => {
	  frontmatter["tags"] = defTag;
	  frontmatter["repo"] = repo;
	  frontmatter["type"] = "plugin";
  });
});

/* setTimeout(() => {
  app.fileManager.processFrontMatter(tp.config.target_file, frontmatter => {
  frontmatter["tags"] = defTag;
  frontmatter["repo"] = repo;
  frontmatter["type"] = "plugin";
  })
}, 500) */ // this timeout will prevent a bug when insert the template to an existing file that content only one empty line. When it happens, the note content is deleted. See: https://github.com/SilentVoid13/Templater/issues/1309#issuecomment-2079178033

await tp.file.move("Categories/Tutos/Obsidian/Plugins/" + title);
%>

Fred-Vatin avatar May 10 '24 22:05 Fred-Vatin

@Fred-Vatin I tested your template (with the userscript part removed), and got the same result when inserting into a blank file: The content was lost, and the frontmatter changes stayed. The file was moved as expected.


When I compared your problematic template to my working ones, the main difference was that you had the prompts and renaming outside tp.hooks. If I put all the prompts and such that you have inside tp.hooks.on_all_templates_executed, the content of the note is not lost anymore. I changed it like so:


<%*
tp.hooks.on_all_templates_executed(async () => {
	// start of template construct
	let title = tp.file.title;
	// tags are type of list and are expressed as array
	const defTag = ["Obsidian/Plugin"];
	
	if (title.startsWith("Untitled")) { 
	title = await tp.system.prompt("What is the plugin name ?"); 
	await tp.file.rename(title);
	} 
	//don’t insert the title as a tag because it can be long and contains space
	//defTag.push(title)
	
	let repo = await tp.system.prompt("What is the plugin repo URL ?");
	const file = tp.file.find_tfile(tp.file.path(true));
	await app.fileManager.processFrontMatter(file, (frontmatter) => {
	  frontmatter["tags"] = defTag;
	  frontmatter["repo"] = repo;
	  frontmatter["type"] = "plugin";
	});
});
/* setTimeout(() => {
  app.fileManager.processFrontMatter(tp.config.target_file, frontmatter => {
  frontmatter["tags"] = defTag;
  frontmatter["repo"] = repo;
  frontmatter["type"] = "plugin";
  })
}, 1000); */ // this timeout will prevent a bug when insert the template to an existing file that content only one empty line. When it happens, the note content is deleted. See: https://github.com/SilentVoid13/Templater/issues/1309#issuecomment-2079178033
%>

Actually, moving the renaming part to after tp.hooks also prevented the issue, and the content was not lost. Tp.hooks still executes after the renaming code (see the included logs).

<%*
tp.hooks.on_all_templates_executed(async () => {
	console.log("inside tp.hooks");
	//don’t insert the title as a tag because it can be long and contains space
	//defTag.push(title)
	
	let repo = await tp.system.prompt("What is the plugin repo URL ?");
	const file = tp.file.find_tfile(tp.file.path(true));
	await app.fileManager.processFrontMatter(file, (frontmatter) => {
	  frontmatter["tags"] = defTag;
	  frontmatter["repo"] = repo;
	  frontmatter["type"] = "plugin";
	});
});

// start of template construct
console.log("after tp.hooks");
let title = tp.file.title;
// tags are type of list and are expressed as array
const defTag = ["Obsidian/Plugin"];

if (title.startsWith("Untitled")) { 
  title = await tp.system.prompt("What is the plugin name ?"); 
  await tp.file.rename(title);
} 
%>

FeralFlora avatar May 13 '24 13:05 FeralFlora

Doesn't matter if you move the file, or whether the content is before the codeblock, or after.

FeralFlora avatar May 13 '24 13:05 FeralFlora

This is the last version that works in any case using tp.hooks and your feedback.

<%*
/* Be sure to switch to edit mode first (live preview)
   It is required so it can insert the template in existing file.
   It must be called first and outside the tp.hooks. */
tp.user.Switch_to_edit_mode();

tp.hooks.on_all_templates_executed(async () => {
// tp.hooks will prevent a bug when insert the template to an existing file that content only one empty line. When it happens, the note content is deleted. See: https://github.com/SilentVoid13/Templater/issues/1309#issuecomment-2079178033
	
	// start of template construct
	let title = tp.file.title;
	// tags are type of list and are expressed as array
	const defTag = ["Obsidian/Plugin"];
	
	if (title.startsWith("Untitled")) { 
	  title = await tp.system.prompt("What is the plugin name ?"); 
	  await tp.file.rename(title);
	} 
	
	//don’t insert the title as a tag because it can be long and contains space
	//defTag.push(title)
	
	let repo = await tp.system.prompt("What is the plugin repo URL ?");
	const file = tp.file.find_tfile(tp.file.path(true));
	
	await app.fileManager.processFrontMatter(file, (frontmatter) => {
	  frontmatter["tags"] = defTag;
	  frontmatter["repo"] = repo;
	  frontmatter["type"] = "plugin";
	});
	
	await tp.file.move("Categories/Tutos/Obsidian/Plugins/" + title);
});
%>
# Description

<% tp.file.cursor(1) %><%* app.workspace.activeLeaf.view.editor?.focus(); %>

# Comment je m’en sers

# Shortkeys

| Shortkeys | Actions |
| --------- | ------- |
|           |         |
|           |         |

Content of my script Switch_to_edit_mode.js:

function switch_to_editmode () {
	/* Be sure to switch to edit mode first (live preview)
		 It is required so it can insert the template in existing file. */
	const view = app.workspace.activeLeaf.getViewState()
	view.state.mode = 'source'
	view.state.source = false
	app.workspace.activeLeaf.setViewState(view)
}
module.exports = switch_to_editmode;

Thanks.

Fred-Vatin avatar May 14 '24 10:05 Fred-Vatin

I'm afraid the issue still exists for me as of 2.3.1. Again using

<%*
tp.hooks.on_all_templates_executed(async () => {
  const file = tp.file.find_tfile(tp.file.path(true));
  await app.fileManager.processFrontMatter(file, (frontmatter) => {
    frontmatter["key"] = "value";
  });
});
%>

TEXT THAT SHOULD STAY

on a new empty file the text that should stay doesn't stay. However, now the text doesn't pop up for half a second before it disappears which it used to do.

simonalveteg avatar May 15 '24 12:05 simonalveteg

Should be fixed in 2.3.2!

Zachatoo avatar May 21 '24 05:05 Zachatoo

Should be fixed in 2.3.2!

I re-tested @Fred-Vatin's previously problematic template here, and can confirm that the fix seems to have worked 🎉

FeralFlora avatar May 22 '24 09:05 FeralFlora

<%*
tp.hooks.on_all_templates_executed(async () => {
  const file = tp.file.find_tfile(tp.file.path(true));
  await app.fileManager.processFrontMatter(file, (frontmatter) => {
    frontmatter["key"] = "value";
  });
});
%>

TEXT THAT SHOULD STAY

on a new empty file the text that should stay doesn't stay. However, now the text doesn't pop up for half a second before it disappears which it used to do.

However, when testing this simple template, the text stays but the frontmatter disappears (which is strange, since @simonalveteg reported that the text would disappear):

Obsidian_b2uxKIFrU5

However, I can't reproduce it reliably. Sometimes, the frontmatter stays, sometimes it disappears.

Here a time where it worked: Obsidian_DfsLCSiZsD

FeralFlora avatar May 22 '24 09:05 FeralFlora

But I suppose the above doesn't apply directly to this issue, because the issue is about inserting templates into blank notes. In the above testing, I was creating a new note from a template.

I can't reproduce the issue when inserting the template, so I suppose the fix did work. So it's only creating new notes from templates that is bugging out, sometimes.

FeralFlora avatar May 22 '24 10:05 FeralFlora

Reverted the fix for this template as it was causing more harm than good, will look into this further when I get a chance.

Zachatoo avatar May 26 '24 21:05 Zachatoo

Still having this issue, any updates?

alancunha26 avatar Nov 09 '24 06:11 alancunha26

Still running into the issue as well.

mitchkm avatar Apr 21 '25 06:04 mitchkm

Any chances of looking into this issue?

btonasse avatar Apr 25 '25 12:04 btonasse

I've tried several of the examples multiple times using a variety of methods of execution (insert command, create command, folder templates) and haven't been able to reproduce.

I'm going to close this as the conversation is hard to follow what parts are working vs not working in the latest version of Templater.

Please open a new issue with a reproducible example, ideally with a video.

Zachatoo avatar Apr 26 '25 02:04 Zachatoo

Here is a new ticket with simple repro. It's no different than this ticket's original post's description honestly. https://github.com/SilentVoid13/Templater/issues/1569

mitchkm avatar Apr 26 '25 07:04 mitchkm