Sublime Forum

Run_command("save") leaves dirty

#1

I’m trying to create a plugin that saves and closes views to non-saved but existing files. This used to work in ST3:

for v in sublime.active_window().views():
    if v.file_name() and os.path.isfile(v.file_name()):
        if v.is_dirty():
            v.run_command("save")
            v.print(v.is_dirty())
            v.close()

In ST4 it no longer works. v still reports as dirty after v.run_command("save") (though the file looks saved) and this causes the v.close() to triggers a Save Changes dialog. It seems that the actual saving of the file is postponed until the function that contains the call for saving returns. I could bypass the save dialog by issuing a v.set_scratch(True) before the v.close() but I feel that is a workaround rather than a solution. It could also conceal any real problems that might occur during the save and cause data loss.

Any hints on what could go wrong here?

0 Likes

#2

are you doing this in a TextCommand?

0 Likes

#3

No, it’s an ApplicationCommand.


class SaveAndCloseNonSavedFilesCommand(sublime_plugin.ApplicationCommand):
    def run(self):
        for v in sublime.active_window().views():
            if v.file_name() and os.path.isfile(v.file_name()):
                if v.is_dirty():
                    v.run_command("save")
                    v.close()

Am I doing something wrong?

0 Likes

#4

not that I know of, but if you want a different workaround, you could store a list of all the view ids which you have saved and want to close and invoke another command to close them - perhaps that would execute after save unmarks the buffer as dirty

0 Likes

#5

This might be related to the issue someone had recently in another thread related to a plugin that updates last saved time (linked below). That should also have worked (and in fact did previously) but now no longer does. I tried a few quick variations on it and the set_timeout() item is a workaround, though a bad one.

It may bear more investigation if this is a regression or not, and if so when it was introduced.

1 Like

#6

Yes, this works, and it’s only marginally slower than it was without the extra call overhead, at least for a couple of dozen files. I’ll see how it performs on a couple of hundreds. Thanks for the hint, @OdatNurd!

0 Likes

#7

Yes, that was my first idea to solve it but couldn’t figure out how to pass along the list to another command that is (or isn’t) executed later. Also, I’d rather do this in one step. @OdatNurd had an idea that seems to be working, I’m going with that one. Anyhow, thanks for the hint, @kingkeith!

0 Likes

#8

A shot in the dark but does using v.run_command("save", { "async": True }) help in anyway ?

0 Likes

#9

or 'async': False perhaps more likely to work :slight_smile:

0 Likes

#10

Hi @UltraInstinct05. I’ve tried both True and False, alas, to no avail.

0 Likes

#11

Hmm, it did something after all. It broke something. I restored the earlier workaround @OdatNurd suggested, and it no longer works :confused:

0 Likes

#12

Due to the way dialogs on macOS are required to be asynchronous the save command has been made always asynchronous. I’ve got a small fix in the works that allow it to be synchronous when no dialog pops up, but in general you can’t rely on the save command completing in any amount of time.

2 Likes

#13

If the save command is asynchronous by default, what does the async argument do ? And while we are at it, the save command also appears to take another argument quiet (bool). What does that do ?

1 Like

#14

"async" isn’t currently used for anything, that’s being fixed. "quiet" makes it only save if no dialog needs to be shown.

1 Like

#15

I made some changes to a document & ran

>>> view.run_command("save", { "quiet": True })
>>> view.run_command("save", { "quiet": False })

But I don’t notice any difference. Could you give an example, where I can see the difference.

0 Likes

#16

When saving the file fails or administrator permissions are required.

0 Likes

#17

I tried editing and saving the hosts file within C:\\Windows which requires elevated privileges.

>>> view.run_command("save", { "quiet": True })  # This one doesn't do anything, but the file is also not saved.
>>> view.run_command("save", { "quiet": False }) # This one just throws the access denied dialog

So, I am assuming the only use of this argument is to not show the dialog when the save fails, but I don’t see how that’s useful. At least, "quiet": False throws the dialog which indicates what the possible issue is.

0 Likes

#18

It’s used by "save_on_focus_lost". You don’t want to pop up a dialog when the user is leaving the application.

0 Likes

#19

That makes sense. Thanks !

0 Likes