Trigger-rebuild bookmarklet stopped working as intended

Earlier the bookmarklet from this project https://glitch.com/edit/#!/no-autorestart worked as intended, but now it’s stopped (Chrome v.68).

application.selectedFile(triggerFile) does not have time to switch to the .trigger-rebuild before the replacement application.editor().replaceRange() occurs, thus screwing up the current file.
A quick workaround is to wrap the replacement in setTimeout:

javascript:(function() {
  var currFile = application.selectedFile();
  var triggerFile = application.files().filter(function (file) {
    return file.path() === ".trigger-rebuild";
  })[0];
  if (!triggerFile) {
    alert("Please create a file named '.trigger-rebuild'.");
    return;
  }
  application.selectedFile(triggerFile);
  setTimeout(function() {
    application.editor().replaceRange(
      "rebuild", { line: 0, ch: 0 }, { line: 0, ch: 10 }
    );
    application.selectedFile(currFile);
  }, 5);
})()

But I would like to know why this is happening.

Edit:
I forgot to mention that your FAQ refers to this project, so it is worth noting that it should be corrected.

This happened because we changed application.selectedFile() to be asynchronous recently. I added a new member to application called selectedFilePromise that you can wait on to execute something after the file changes, and updated the bookmarklet code in no-autorestart. This is the key bit:

  application.selectedFile(triggerFile);
  application.selectedFilePromise.then(function () {
    application.editor().replaceRange(
      "rebuild", { line: 0, ch: 0 }, { line: 0, ch: 10 }
    );
    application.selectedFile(currFile);
  });
1 Like

That promise is new, BTW, so you may have to refresh your editor tab to pick up the code change.

Thank you!
In fact, quick workaround with setTimeout did not always work as intended and this was the reason to start studying promises :thinking:
I have already fixed my bookmarklet with this code but I don’t know whether it’s correct (I’m not a guru):

javascript:(function() {
  var promise = new Promise(resolve => resolve());
  var currFile = application.selectedFile();
  var triggerFile = application.files().filter(file => file.path() === ".trigger-rebuild")[0];
  if (!triggerFile) return alert("Please create a file named '.trigger-rebuild'.");
  promise
    .then(() => application.selectedFile(triggerFile))
    .then(() => application.editor().replaceRange("rebuild", {line: 0, ch: 0}, {line: 0, ch: 10}))
    .then(() => application.selectedFile(currFile));
})()

That won’t really work because none of those application functions return a promise, so the code in then() will run right away. You need to do it like I did above, or it won’t wait.

Ok, but in the end this code did not corrupt the current file, unlike the original version (does not work at all) and the version with setTimeout (works 50/50)

Maybe you missed it, but Tim updated the bookmarklet in https://glitch.com/edit/#!/no-autorestart?path=README.md with a working version.

I started using that bookmarklet right away.
I do not speak English and maybe I did not express it correctly. I was just wondering why the wrong code does not spoil the current file.

It has to do with the way promises work. Even though your newer code isn’t really waiting for the file to change, the fact that the rest of your code is inside then() means that it won’t be called immediately. In this case you’re lucky and the code that switches files happens to run before the code inside then() gets a chance. This would break again if the code we use to switch files gets a bit more complicated. Waiting for the selectedFilePromise to complete will always prevent your code from changing the wrong file.

1 Like

Thanks for clarifying