Package command runs twice


Hi, I’m just getting started with package development and I’m (re)writing a package which opens the relate controller/view/style for the current file.

I’m running into an issue where my command runs twice… which seems odd

In my activate method I have:

    atom.commands.add 'atom-text-editor', 'ti-alloy-related:openRelated', => @openRelated()

and openRelated does:

openFile = (uri) ->

module.exports =
    openRelated: ->
        return unless editor = atom.workspace.getActiveTextEditor()
        uri = "#{editor.getPath()}"

        if uri.match(/.*\.js/)
            openFile(uri.replace('/controllers/', '/views/').replace('.js', '.xml'))
            openFile(uri.replace('/controllers/', '/styles/').replace('.js', '.tss'))
        else if uri.match(/.*\.tss/)
            openFile(uri.replace('/styles/', '/controllers/').replace('.tss', '.js'))
            openFile(uri.replace('/styles/', '/views/').replace('.tss', '.xml'))
        else if uri.match(/.*\.xml/)
            openFile(uri.replace('/views/', '/controllers/').replace('.xml', '.js'))
            openFile(uri.replace('/views/', '/styles/').replace('.xml', '.tss'))

This is bound to a menu option and a shortcut key:

'menu': [{
    'label': 'Packages'
    'submenu': [
        'label': 'Titanium'
        'submenu': [{
            'label': 'Open related',
            'command': 'ti-alloy-related:openRelated'
  'ctrl-alt-r': 'ti-alloy-related:openRelated'

Can anyone shed some light on why each related file would open twice?


The code looks fine to me. Does the openRelated function run twice? I.e. if you log something in that function, does it log that twice? If so, you know that the command is run twice (maybe you forget to dispose of the command), otherwise, could you share the full code? Debugging snippets is kinda hard :wink:


Yea, sticking a console.log in there shows it running twice, but interestingly if I alert then it only runs once

The full code is:

openFile = (uri) ->

module.exports =
          type: 'string'
          default: ''

  activate: ->
    atom.commands.add 'atom-text-editor', 'ti-alloy-related:openRelated', => @openRelated()

  openRelated: ->
    return unless editor = atom.workspace.getActiveTextEditor()

    uri = "#{editor.getPath()}"
    testFilePath = atom.config.get 'ti-alloy-related.testFilePath'

    if uri.match(/.*Spec\.js/)
        openFile(uri.replace(testFilePath + 'controllers/', 'app/controllers/').replace('Spec.js', '.js'))
        openFile(uri.replace(testFilePath + 'controllers/', 'app/styles/').replace('Spec.js', '.tss'))
        openFile(uri.replace(testFilePath + 'controllers/', 'app/views/').replace('Spec.js', '.xml'))
    else if uri.match(/.*\.js/)
        openFile(uri.replace('app/controllers/', 'app/views/').replace('.js', '.xml'))
        openFile(uri.replace('app/controllers/', 'app/styles/').replace('.js', '.tss'))
        openFile(uri.replace('app/controllers/', testFilePath + 'controllers/').replace('.js', 'Spec.js'))
    else if uri.match(/.*\.tss/)
        openFile(uri.replace('app/styles/', 'app/controllers/').replace('.tss', '.js'))
        openFile(uri.replace('app/styles/', 'app/views/').replace('.tss', '.xml'))
        openFile(uri.replace('app/styles/', testFilePath + 'controllers/').replace('.tss', 'Spec.js'))
    else if uri.match(/.*\.xml/)
        openFile(uri.replace('app/views/', 'app/controllers/').replace('.xml', '.js'))
        openFile(uri.replace('app/views/', 'app/styles/').replace('.xml', '.tss'))
        openFile(uri.replace('app/views/', testFilePath + 'controllers/').replace('.xml', 'Spec.js'))


Hmmm yeah that’s weird. Still I’m not seeing anything wrong, except for one thing, but that should only be an issue when you deactivate the package and then activate it again (without reloading atom in the meantime): atom.commands.add returns a disposable that removes the command on disposal. If it isn’t disposed, and the command is added again to the command registry, it will trigger twice. So if you activate the package twice, the command is added to the command registry twice, and it will trigger twice if it’s called. Could this be your issue?

You should do something like this:

activate: ->
  @disposable = atom.commands.add(...);

deactivate: ->
  @disposable = null;


Hmm… adding that activate/deactivate still shows the same issue :confused:


Hmmm, too bad. Worth the shot though. By the way, does it matter how you call the command (keybinding, menu, command palette or even calling atom.commands.dispatch from the devtools console)? I assumed you tried them all, but you never know :wink:

Can you run atom.commands.selectorBasedListenersByCommandName['ti-alloy-related:openRelated'] in the console, and see if it has a length of 2?


Ok so I copied the code to a new package, and it works fine for me. Really looks like somehow the command gets added twice on your machine…


Sorry, was caught up doing ‘proper’ work :wink:

I started again, with a completely fresh package, and I think I’m about 90% of the way there now… …but I’ve just noticed a enable/disable error being thrown


I checked it out, and it works fine for me :smile:

The error is because you call @tiAlloyRelatedPlusView.destroy() in deactivate, but @tiAlloyRelatedPlusView isn’t defined.


Yup, I think all of the initial, major, functionality is now done, and I’ve patched the published package by removing the destroy() call