Confusion on init execution

Hello! So I’ve been working to make my workflow better with atom. As such I started using atom-shell-commands and wrote two commands that compile and run c++ code, respectively. Then I looked to making a command in my init.coffee that would save my current editor, execute my compile command, then execute my execute command.

atom.commands.add 'atom-text-editor', 'custom:compile-and-run', ->
  editor = atom.workspace.getActiveTextEditor()
  editor.save()
  atom.commands.dispatch(this, 'atom-shell-commands:Compile-build')
  atom.commands.dispatch(this, 'atom-shell-commands:Execute-build')

All fun, however when I execute this compile-and-run command, I get an executable that hasn’t been updated yet. So I was under the impression that either the editor does not save before the compile is executed, or the compile isn’t finished before the execute is called. So I decided to have the program sleep and print between each command. This led to a strange behavior.

As you can see from the footage, the file isn’t actually saved even after the log that happens after it. It gets saved only at the end of the command. Do commands not execute sequentially? I’m really confused as to this.

Commands don’t execute sequentially, no, because if they did they would block the entirety of Atom from doing anything. Atom doesn’t have a queue where commands sit until they can be processed, so everything is run as an asynchronous Node operation, which means that they get done when they get done, and the fastest thing that happens is atom-shell-commands sending instructions to your shell.

If you want to make a specific sequence of actions you need to use atom.commands.onDidDispatch(), like in this example. I give Atom instructions on what to do once the next command is dispatched, run the command, and then clean up the listener.

atom.commands.add 'atom-text-editor', 'custom:beautify', ->
    editor = atom.workspace.getActiveTextEditor()
    editorElement = atom.views.getView(editor)

    trigger = new CompositeDisposable
    trigger.add atom.commands.onDidDispatch( (e) ->
        regMatch = /"{{/
        editor.scan(regMatch, (match) -> match.replace('{{'))
        regMatch = /}}"/
        editor.scan(regMatch, (match) -> match.replace('}}'))
    )

    regMatch = /{{/
    editor.scan(regMatch, (match) -> match.replace('"{{'))
    regMatch = /}}/
    editor.scan(regMatch, (match) -> match.replace('}}"'))

    atom.commands.dispatch(editorElement, 'atom-beautify:beautify-editor')

    trigger.dispose()

To identify when the editor has been saved, you want to use editor.onDidSave().

So I’m a little confused on how exactly atom handles callbacks from listeners. My code is now

atom.commands.add 'atom-text-editor', 'custom:compile-and-run', ->
  editor = atom.workspace.getActiveTextEditor()
  trigger = new CompositeDisposable
  
  trigger.add editor.onDidSave((event) ->
    atom.commands.dispatch(this, 'atom-shell-commands:Compile-build')
    atom.commands.dispatch(this, 'atom-shell-commands:Execute-build')
    console.log("ding")
  )

  editor.save()
  trigger.dispose()

and the callback is never actually called. I figured this was because the dispose() is executing before the onDidSave() could fire I confirmed this suspicion by moving the dispose() to within the callback. But now only the console.log fires, the two dispatches don’t even happen. Do the dispatches in the callback get killed because (after the log) the disposal happens and that maybe terminates commands evoked by the listener? I’m confused by this behavior.

It might be this. I’ve never used the keyword like that, in an init.coffee function. Try editor.element, which will pass the HTMLElement that’s associated with the TextEditor. (You should test it out yourself in the developer tools with ctrl-shift-i, switching to the Console, and typing atom.workspace.getActiveTextEditor().element.)

I think that passing the HTML element fixed the issue (I can really only tell by logging). However now the two shell commands are still executing in the wrong order. I figured maybe having a onDidDispatch would successfully finish the sequence. Code is below

atom.commands.add 'atom-text-editor', 'custom:compile-and-run', ->
  editor = atom.workspace.getActiveTextEditor()
  trigger = new CompositeDisposable

  trigger.add atom.commands.onDidDispatch((event) ->
    if event.type is "atom-shell-commands:Compile-build"
      atom.commands.dispatch(editor.element, 'atom-shell-commands:Execute-build')
      trigger.dispose()
  )

  trigger.add editor.onDidSave((event) ->
    atom.commands.dispatch(editor.element, 'atom-shell-commands:Compile-build')
  )

  editor.save()

This still doesn’t work. The executable opens before the compile finishes successfully.

I don’t know anything about how it’s set up but I’m guessing the atom-shell-commands package marks it’s command as completed only after it has sent the command to the shell and not once it has checked for successful execution. The only way I can get it now is having a sleep for a little while in the onDidDispatch callback which isn’t terribly viable given compile time varies.

I think that you’re right about why onDidDispatch() didn’t work, but it’s not the package’s fault. Or Atom’s. The final part of the execution happens outside of the influence of either.

The ultimate solution will be to make the different actions follow each other because they’re defined in the same place. You might be able to do this with atom-shell-commands, but I personally know and use process-palette, and here’s how it would look:

The funny thing about atom-shell-commands is that it splits the instruction into commands and arguments keys when the native way is just to write everything out. As a result, I’m not sure how you add a follow-up command like I do in that screenshot with &&.

You can also tell both packages to save the file automatically when you run the command (I didn’t know that about atom-shell-commands when I made my first post).

Process-palette worked great for this! Thank you!

I have one other question though. I wanted to set up another workflow that is fairly similar. On a keypress Atom would save the project, compile a debug version of the program, then start up a debugger (dbg is the package I use for in-editor debugging). The method shown here works great but I was wondering if it’s possible to execute an atom command after a process is executed with process-palette. (also I can’t actually edit the global config for some reason but that’s beside the point)

Nevermind. Didn’t see that you could run javascript after a command has executed. Thank you for your help!

No problem. process-palette is how I do most things. It is so flexible and has so many features that even though it takes more setup time than other packages that do comparable things (all of the code-runners, for example), the control and options you have are well worth it.

1 Like