Sublime Forum

Default.exec.ExecCommand: how to wait for it to finish?

#1

So I have several commands which inherit from Default.exec.ExecCommand. Let’s call them:

class FirstCommand(Default.exec.ExecCommand):
    def run(self):
        # do stuff ...
        super().run(shell_cmd='pwd', working_dir='/home')
    def on_finished(self, proc):
        super().on_finished(proc)
        # finishing touches ...

class SecondCommand(Default.exec.ExecCommand):
    def run(self):
        # do stuff
        super().run(shell_cmd='ls', working_dir='/home')
    def on_finished(self, proc):
        super().on_finished(proc)
        # finishing touches ...

Now I have another command that wants to run these two sequentially, say:

class CombinedCommand(sublime_plugin.WindowCommand):
    def run(self):
        self.window.run_command('first')
        self.window.run_command('second') # OOPS!

The problem here is that these two commands are running parallel now… So obviously we need callbacks, right? So we adjust the FirstCommand class:

class FirstCommand(Default.exec.ExecCommand):
    def run(self, on_done=None):
        self.on_done_callback = on_done
        # do stuff ...
        super().run(shell_cmd='pwd', working_dir='/home')
    def on_finished(self, proc):
        super().on_finished(proc)
        # finishing touches ...
        if self.on_done_callback:
            self.on_done_callback()

And then adjust the CombinedCommand as follows:

class CombinedCommand(sublime_plugin.WindowCommand):
    def run(self):
        def on_done():
            self.window.run_command('second')
        self.window.run_command('first', args={"on_done": on_done})

I was hoping this would work, after all Sublime should just pass this args dictionary straight to the run method, right? Well, unfortunately I get:

  File "/Applications/Sublime Text.app/Contents/MacOS/sublime.py", line 299, in run_command
    sublime_api.window_run_command(self.window_id, cmd, args)
TypeError: Value required

So does anyone have another idea to sequentially run these kinds of commands?

0 Likes

#2

Yep, it’s annoying: you can call command only with arguments that are writable in JSON: int, float, list, dict.

What you can do though is a class that both command can access, which would store the callbacks as key/callback. And then, you just pass the key to your command, which will have to go look in for the corresponding callback in the callbacks object.

You can also do this straight in one of a command class (with static attributes), and your target command would go look for the callback in the class of the caller, but it’s a bit messier.

0 Likes

#3

You could use pass the command name in the argument, i.e.:

class FirstCommand(Default.exec.ExecCommand):
    def run(self, on_done=None, on_done_args={}):
        self.on_done_command = on_done
        self.on_done_args = on_done_args
        # do stuff ...
        super().run(shell_cmd='pwd', working_dir='/home')
    def on_finished(self, proc):
        super().on_finished(proc)
        # finishing touches ...
        if self.on_done_command:
            self.wind.run_command(self.on_done_command, on_done_args)
class CombinedCommand(sublime_plugin.WindowCommand):
    def run(self):
        self.window.run_command('first', args={"on_done": "second", "on_done_args": {"on_done": "third"}})

This is not as nice as normal callbacks, but should work.

0 Likes

#4

Other options include:

  1. Running the “multiple commands” from a shell script, which you invoke with the exec command.
  2. Depending on how complex the commands are, just batch them together in a shell_cmd paramter to build.
  3. Invoke subprocess.call (or other functions of that module) and do things manually and directly in python code.
    If output should be displayed to the user in an output panel, directly perform necessary API calls and run commands. (using one of the first two would be easier in that case)
0 Likes

#5

OK well, I took the subprocess approach, because it’s not vital to see the output of the second command in my case. @r-stein’s idea is clever, I’ll definitely remember that.

0 Likes