pane.destoryItem not working


#1

I found this code to get a custom merge panes command working. However, I am trying to modify it so that when the panes merge, duplicates are ignored.

I have the following code:

atom.commands.add 'atom-workspace', 'custom:merge-panes', ->
  panes = atom.workspace.getCenter().getPanes()
  firstPane = panes.shift()
  # loop through all panes except for the first pane
  for pane in panes
    for item in pane.getItems()
      # if item is already in first pane, delete it, otherwise move it to first pane
      for firstPaneItem in firstPane.getItems()
        if firstPaneItem.getPath() == item.getPath()
          pane.destroyItem item
        else
          pane.moveItemToPane item, firstPane

but it is not working. Any ideas what I am doing wrong?


#2

When I try that, I get the error, Uncaught TypeError: firstPaneItem.getPath is not a function. When I try again with console.log statements, this is what I see.

atom.commands.add 'atom-workspace', 'custom:merge-panes', ->
  panes = atom.workspace.getCenter().getPanes()
  firstPane = panes.shift()
  # loop through all panes except for the first pane
  for pane in panes
    for item in pane.getItems()
      # if item is already in first pane, delete it, otherwise move it to first pane
      for firstPaneItem in firstPane.getItems()
        console.log firstPaneItem
        if firstPaneItem.getPath() == item.getPath()
          console.log item
          pane.destroyItem item
        else
          pane.moveItemToPane item, firstPane

The SettingsView object contains no getPath() method, but it does contain a uri property. Since there’s no common way to identify these objects, it’s better to just check to see when we’re working with a TextEditor. For that, we’ll need to import the object definition.

{TextEditor} = require 'atom'

atom.commands.add 'atom-workspace', 'custom:merge-panes', ->
  panes = atom.workspace.getCenter().getPanes()
  firstPane = panes.shift()
  # loop through all panes except for the first pane
  for pane in panes
    for item in pane.getItems()
      # if item is already in first pane, delete it, otherwise move it to first pane
      for firstPaneItem in firstPane.getItems()
        if firstPaneItem instanceof TextEditor && item instanceof TextEditor
          if firstPaneItem.getPath() == item.getPath()
            console.log "We have a winner! TextEditor paths match."
            pane.destroyItem item
          else
            pane.moveItemToPane item, firstPane
        else
          if firstPaneItem.uri == item.uri
            console.log "We have a winner! Item URIs match."
            pane.destroyItem item
          else
            pane.moveItemToPane item, firstPane

If you try this, you’ll notice that the console reports a match, but your duplicate tabs aren’t deleted. Why’s that? Because pane.destroyItem() returns a Promise. The function is asynchronous, so it doesn’t hold up the execution of the rest of your code. Your loop is faster than the deletion of the pane item, so before it can be deleted, it gets picked up and moved.

The ultimate answer will probably have to do with using a generator to wait on the resolution of the Promise for every iteration before it continues, but I don’t have the mental bandwidth tonight to connect the rest of the dots.


#3

Hi @DamnedScholar, thanks for getting back to me.

From your suggestion I am trying to play around with generators to get this working, however I am new to both CoffeeScript and generators and thus am struggling a little.

Things I have tried:

  • first I tried using the await keyword but then found out that is only in CoffeeScript v2 and Atom is still on 1.x
  • Then I tried using generators like this
atom.commands.add 'atom-workspace', 'custom:merge-panes', ->
  console.log "in merge panes"
  panes = atom.workspace.getCenter().getPanes()
  firstPane = panes.shift()
  # loop through all panes except for the first pane
  for pane in panes
    for item in pane.getItems()
      # if item is already in first pane, delete it, otherwise move it to first pane
      for firstPaneItem in firstPane.getItems()
        if firstPaneItem instanceof TextEditor && item instanceof TextEditor
          if firstPaneItem.getPath() == item.getPath()
            for i from destroy(pane, item)
          else
            pane.moveItemToPane item, firstPane
          else
             if firstPaneItem.uri == item.uri
                for i from destroy(pane, item)
             else
               pane.moveItemToPane item, firstPane

destroy = (pane, item) ->
    yield pane.destroyItem item

but I get the error Failed to load init.coffee: Unexpected(. But I based it off the example here so as far as I can tell the syntax is correct?


#4

Sorry the example I posted above is incorrect, see this example instead

atom.commands.add 'atom-workspace', 'custom:merge-panes', ->
  console.log "in merge panes"
  panes = atom.workspace.getCenter().getPanes()
  firstPane = panes.shift()
  # loop through all panes except for the first pane
  for pane in panes
    for item in pane.getItems()
      # if item is already in first pane, delete it, otherwise move it to first pane
      for firstPaneItem in firstPane.getItems()
        if firstPaneItem.getPath() == item.getPath()
          for i from destroy(pane, item)
        else
          pane.moveItemToPane item, firstPane

destroy = (pane, item) ->
  yield pane.destroyItem item

#5

Piping up to say that I’m working on this and will let you know if I figure it out. This is an area of JS that I don’t fully understand and I probably will need to use at some point in the future.


#6

@DamnedScholar Thank you again for helping me out! Here is my current status on this.

  • I tried using await keyword to wait for the promise to resolve. But then I found out that await was introduced in CoffeeScript 2 and Atom still is running CoffeeScript 1
  • I then tried to get it working using generators in CoffeeScript 1 and failed horribly
  • I then tried porting my init script to JS so I can use async/await and ended up with this code
atom.commands.add("atom-workspace", "custom:merge-panes", async () => {
  const panes = atom.workspace.getCenter().getPanes()
  const firstPane = panes.shift()

  // loop through all panes except for the first pane
  for (pane of panes) {
    for (item of pane.getItems()) {
      // if item is already in first pane, delete it, otherwise move it to first pane
      for (firstPaneItem of firstPane.getItems()) {
        if (firstPaneItem.getPath() === item.getPath()) {
          await pane.destroyItem(item)
        } else {
          pane.moveItemToPane(item, firstPane)
        }
      }
    }
  }
})

but that code doesn’t work either :confused:


#7

The problem has been solved over here! Thank you for your help @DamnedScholar :slight_smile:


#8

Sweet. Nathan’s solution is so much more obvious than what I was fishing for. Now I feel bad for over-engineering it.