Community
    • Login

    Auto sort tabs (real-time)

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    49 Posts 10 Posters 2.2k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Alan KilbornA
      Alan Kilborn
      last edited by Alan Kilborn

      So not that I find this idea useful, but I needed some mental exercise while waiting for something to happen today, so I gave it a go with scripting, specifically Pythonscripting.

      The result is below; it is a bit of a visual treat when a new file is opened into a view with already a lot of tabs. It is probably too annoying to really be practical, but…

      I call the script SortFileTabsByFilename.py and here we go:

      # -*- coding: utf-8 -*-
      
      from Npp import editor, notepad, NOTIFICATION
      import os
      
      class SFTBF(object):
      
          def __init__(self):
              self.installed = False
      
          def install(self):
              if not self.installed:
                  notepad.callback(self.file_opened_callback, [NOTIFICATION.FILEOPENED])
                  self.installed = True
      
          def uninstall(self):
              if self.installed:
                  notepad.clearCallbacks(self.file_opened_callback)
                  self.installed = False
      
          def is_installed(self):
              return self.installed
      
          def file_opened_callback(self, args):
              current_view = notepad.getCurrentView()
              other_view = 1 if current_view == 0 else 0
              if notepad.getCurrentDocIndex(other_view) == 4294967295L: other_view = None
              curr_view_paths_list = []; curr_view_sorted_paths_list = []
              other_view_paths_list = []; other_view_sorted_paths_list = []
              for (filename, _, index_in_view, view) in notepad.getFiles():
                  if view == current_view:
                      curr_view_paths_list.append(filename)
                      curr_view_sorted_paths_list.append(filename)
                  else:
                      other_view_paths_list.append(filename)
                      other_view_sorted_paths_list.append(filename)
              curr_view_sorted_paths_list.sort(key=lambda x: x.rsplit(os.sep, 1)[-1].upper())
              curr_view_already_sorted = True if curr_view_paths_list == curr_view_sorted_paths_list else False
              other_view_sorted_paths_list.sort(key=lambda x: x.rsplit(os.sep, 1)[-1].upper())
              other_view_already_sorted = True if other_view_paths_list == other_view_sorted_paths_list else False
              if curr_view_already_sorted and other_view_already_sorted: return  # nothing to do
              processed_other_view = False
              if other_view != None and not other_view_already_sorted:
                  self.rearrange_tabs_in_view(other_view, other_view_sorted_paths_list)
                  processed_other_view = True
              processed_current_view = False
              if not curr_view_already_sorted:
                  self.rearrange_tabs_in_view(current_view, curr_view_sorted_paths_list)
                  processed_current_view = True
              if processed_other_view and not processed_current_view:
                  # leave the view we started in as the active one:
                  notepad.activateIndex(current_view, notepad.getCurrentDocIndex(current_view))
      
          def rearrange_tabs_in_view(self, view, sorted_name_list):
              notepad.activateIndex(view, notepad.getCurrentDocIndex(view))  # get switched into the correct view
              remembered_active_filename = notepad.getCurrentFilename()
              destination_index = 0
              num_of_tabs = len(sorted_name_list)
              while destination_index < num_of_tabs:
                  current_order_list = []
                  for (filename, _, index_in_view, v) in notepad.getFiles():
                      if v == view: current_order_list.append(filename)
                  curr_location_index = current_order_list.index(sorted_name_list[destination_index])
                  move_left_count = curr_location_index - destination_index
                  if move_left_count > 0:
                      notepad.activateFile(current_order_list[curr_location_index])
                      for _ in range(move_left_count): notepad.menuCommand(MENUCOMMAND.VIEW_TAB_MOVEBACKWARD)
                  destination_index += 1
              notepad.activateFile(remembered_active_filename)
      
      if __name__ == '__main__':
      
          if 'sftbf' not in globals(): sftbf = SFTBF()
      
          # each running of the script toggles install/uninstall:
          sftbf.uninstall() if sftbf.is_installed() else sftbf.install()
          notepad.messageBox('SortFileTabsByFilename {}INSTALLED!'.format('' if sftbf.is_installed() else 'UN'), '')
      
      PeterJonesP 1 Reply Last reply Reply Quote 5
      • PeterJonesP
        PeterJones @Alan Kilborn
        last edited by

        @Alan-Kilborn ,

        Nice.

        However, when I previously said,

        which I believe triggers for both opening an existing file and for creating a new file tab

        … I was apparently wrong. Because File > New doesn’t trigger the script’s sorting function for me, whereas Open does. I don’t see a message that does seem to be triggered for New, either. Hmm…

        Alan KilbornA PeterJonesP 2 Replies Last reply Reply Quote 2
        • EkopalypseE
          Ekopalypse
          last edited by

          I assume because, technically, new, does not open a file.

          Alan KilbornA 1 Reply Last reply Reply Quote 3
          • Alan KilbornA
            Alan Kilborn @PeterJones
            last edited by Alan Kilborn

            @PeterJones

            Yes, there are limitations.
            I should have mentioned.

            So, another one is if you “clone” or “move” a file from one view to another.
            It won’t be triggered then either.

            The ideal way would be to tie it into the “buffer activated” callback.
            But, hmm, there’s a problem there: As it switches tabs to move things into proper order (during a firing of “buffer activated”), it will cause more "buffer activated"s to happen.
            I didn’t do a lot of testing that way, but when I did give it a go, I got some nice N++'s hangs.
            I tried turning off the callback at the start of itself, and restoring it right before it returned; that was not liked either (hangs as well).

            But, like I said, this was just a diversion for today, not something for me to seriously use. If anyone really wants to use it, perhaps they also work on it and figure out how to avoid its deficiencies.

            Alan KilbornA 1 Reply Last reply Reply Quote 4
            • PeterJonesP
              PeterJones @PeterJones
              last edited by

              @PeterJones said in Auto sort tabs (real-time):

              Hmm…

              As a workaround, it could be done at FILESAVED, because that would be triggered when the new file is first given a name.

              That would require hanging line #13 to

                          notepad.callback(self.file_opened_callback, [NOTIFICATION.FILEOPENED,NOTIFICATION.FILESAVED])
              
              1 Reply Last reply Reply Quote 5
              • Alan KilbornA
                Alan Kilborn @Ekopalypse
                last edited by

                @Ekopalypse

                Perhaps you have some expertise regarding this…

                I noticed that “uninstalling” my script doesn’t actually work.
                Meaning that if, after uninstall, you open a file that is out of sort order, it will still be moved into its correct name-order.

                I did some debugging, and I see that, with uninstall, this line IS being executed:

                notepad.clearCallbacks(self.file_opened_callback)

                but, apparently, isn’t being noted by PS??

                Didn’t you, at one time, face a similar issue?

                Alan KilbornA 1 Reply Last reply Reply Quote 0
                • Alan KilbornA
                  Alan Kilborn @Alan Kilborn
                  last edited by Alan Kilborn

                  @Alan-Kilborn said in Auto sort tabs (real-time):

                  Didn’t you, at one time, face a similar issue?

                  I think you did --> https://github.com/bruderstein/PythonScript/issues/153

                  But maybe doesn’t apply in my case, as issue 153 seems to indicate that “Notepad” object callbacks don’t suffer this fate, just “Editor” object callbacks…

                  1 Reply Last reply Reply Quote 0
                  • EkopalypseE
                    Ekopalypse
                    last edited by

                    Your script is installing/uninstalling for me. Maybe this isn’t
                    fixed in PS2?? Let me give it a try with 1.5.X

                    1 Reply Last reply Reply Quote 2
                    • EkopalypseE
                      Ekopalypse
                      last edited by

                      Yep, the same script that works with PS3 does not uninstall the callback when you using PS2.

                      Alan KilbornA 1 Reply Last reply Reply Quote 2
                      • Alan KilbornA
                        Alan Kilborn @Ekopalypse
                        last edited by

                        @Ekopalypse said in Auto sort tabs (real-time):

                        Yep, the same script that works with PS3 does not uninstall the callback when you using PS2.

                        Thanks for the confirmation.
                        I keep forgetting about PS3.
                        But, in truth, I have a few project where I need non-UTF8 encodings, so I’m a bit afraid to make the jump anyway.
                        I will make a note in my copy of the script that it can’t be uninstalled under PS2.
                        But, if it is something that someone will use, I don’t think they’ll uninstall it, and will set up the install part in startup.py.

                        1 Reply Last reply Reply Quote 2
                        • Alan KilbornA
                          Alan Kilborn @Alan Kilborn
                          last edited by Alan Kilborn

                          @Alan-Kilborn said in Auto sort tabs (real-time):

                          The ideal way would be to tie it into the “buffer activated” callback.

                          So I ended up doing this to the script.
                          To avoid the script “retriggering” itself, I added a new member variable in the init:

                          self.is_installed = False

                          and then changed the callback function (its name, too!) a bit:

                              def buffer_activated_callback(self, args):
                                  if self.in_callback: return  # the script moving tabs around will cause more callbacks to trigger; prevent this
                                  self.in_callback = True
                                  ...
                                  ...what it was doing before...
                                  ...
                                  self.in_callback = False
                          

                          It seems to work okay.
                          If there’s interest, I can republish the entire script with the mods.

                          PeterJonesP 1 Reply Last reply Reply Quote 3
                          • PeterJonesP
                            PeterJones @Alan Kilborn
                            last edited by

                            @Alan-Kilborn said in Auto sort tabs (real-time):

                            I added a new member variable in the init:\

                            self.is_installed = False
                            ...
                                if self.in_callback:
                            

                            Was that a typo in the first line, or am I confused and those are two separate things?

                            Assuming that first line should have been self.in_callback = False, I was able to see that work for me.

                            It’s fun to try to “fight” it: try to rearrange the tabs manually while the script is in effect. ;-) But I guess the kind of person who would want this running all the time would not be the kind who would try to fight it, so wouldn’t accidentally trigger the fight.

                            Good work on that.

                            Given that it was multiple changes (adding a variable, changing method name, changing the notification it’s hooked to), it might be good to re-publish, so that future readers won’t make mistakes while piecing the script together, and then complain at you that it doesn’t work.

                            Thanks for the fun diversion.

                            Alan KilbornA 1 Reply Last reply Reply Quote 2
                            • Alan KilbornA
                              Alan Kilborn @PeterJones
                              last edited by Alan Kilborn

                              @PeterJones said in Auto sort tabs (real-time):

                              Was that a typo in the first line

                              Yep, dammit. Sorry.

                              it might be good to re-publish

                              OK, will do.

                              Alan KilbornA 1 Reply Last reply Reply Quote 1
                              • Alan KilbornA
                                Alan Kilborn @Alan Kilborn
                                last edited by

                                @PeterJones

                                It’s fun to try to “fight” it: try to rearrange the tabs manually while the script is in effect. ;-)

                                I certainly did NOT do any type of testing like that!
                                If it fails during something of that nature, well, sorry, but the user is to blame in that case. :-)

                                and then complain at you that it doesn’t work.

                                Hey, how did you make that little complain-at-you popup text?


                                Okay, so here’s an “a” version of the script, with all the changes previously discussed:

                                # -*- coding: utf-8 -*-
                                
                                from Npp import editor, notepad, NOTIFICATION
                                import os
                                
                                class SFTBFa(object):
                                
                                    def __init__(self):
                                        self.installed = False
                                        self.in_callback = False
                                
                                    def install(self):
                                        if not self.installed:
                                            notepad.callback(self.buffer_activated_callback, [NOTIFICATION.BUFFERACTIVATED])
                                            self.installed = True
                                
                                    def uninstall(self):
                                        if self.installed:
                                            notepad.clearCallbacks(self.buffer_activated_callback)  # <---- may not work until PS 3.0.4+; see https://github.com/bruderstein/PythonScript/issues/153
                                            self.installed = False
                                
                                    def is_installed(self):
                                        return self.installed
                                
                                    def buffer_activated_callback(self, args):
                                        if self.in_callback: return  # the script moving tabs around will cause more callbacks to trigger; prevent this
                                        self.in_callback = True
                                        current_view = notepad.getCurrentView()
                                        other_view = 1 if current_view == 0 else 0
                                        if notepad.getCurrentDocIndex(other_view) == 4294967295L: other_view = None
                                        curr_view_paths_list = []; curr_view_sorted_paths_list = []
                                        other_view_paths_list = []; other_view_sorted_paths_list = []
                                        for (filename, _, index_in_view, view) in notepad.getFiles():
                                            if view == current_view:
                                                curr_view_paths_list.append(filename)
                                                curr_view_sorted_paths_list.append(filename)
                                            else:
                                                other_view_paths_list.append(filename)
                                                other_view_sorted_paths_list.append(filename)
                                        curr_view_sorted_paths_list.sort(key=lambda x: x.rsplit(os.sep, 1)[-1].upper())
                                        curr_view_already_sorted = True if curr_view_paths_list == curr_view_sorted_paths_list else False
                                        other_view_sorted_paths_list.sort(key=lambda x: x.rsplit(os.sep, 1)[-1].upper())
                                        other_view_already_sorted = True if other_view_paths_list == other_view_sorted_paths_list else False
                                        if not (curr_view_already_sorted and other_view_already_sorted):
                                            processed_other_view = False
                                            if other_view != None and not other_view_already_sorted:
                                                self.rearrange_tabs_in_view(other_view, other_view_sorted_paths_list)
                                                processed_other_view = True
                                            processed_current_view = False
                                            if not curr_view_already_sorted:
                                                self.rearrange_tabs_in_view(current_view, curr_view_sorted_paths_list)
                                                processed_current_view = True
                                            if processed_other_view and not processed_current_view:
                                                # leave the view we started in as the active one:
                                                notepad.activateIndex(current_view, notepad.getCurrentDocIndex(current_view))
                                        self.in_callback = False
                                
                                    def rearrange_tabs_in_view(self, view, sorted_name_list):
                                        notepad.activateIndex(view, notepad.getCurrentDocIndex(view))  # get switched into the correct view
                                        remembered_active_filename = notepad.getCurrentFilename()
                                        destination_index = 0
                                        num_of_tabs = len(sorted_name_list)
                                        while destination_index < num_of_tabs:
                                            current_order_list = []
                                            for (filename, _, index_in_view, v) in notepad.getFiles():
                                                if v == view: current_order_list.append(filename)
                                            curr_location_index = current_order_list.index(sorted_name_list[destination_index])
                                            move_left_count = curr_location_index - destination_index
                                            if move_left_count > 0:
                                                notepad.activateFile(current_order_list[curr_location_index])
                                                for _ in range(move_left_count): notepad.menuCommand(MENUCOMMAND.VIEW_TAB_MOVEBACKWARD)
                                            destination_index += 1
                                        notepad.activateFile(remembered_active_filename)
                                
                                if __name__ == '__main__':
                                
                                    if 'sftbf_a' not in globals(): sftbf_a = SFTBFa()
                                
                                    # each running of the script toggles install/uninstall:
                                    sftbf_a.uninstall() if sftbf_a.is_installed() else sftbf_a.install()
                                    notepad.messageBox('SortFileTabsByFilename {}INSTALLED!'.format('' if sftbf_a.is_installed() else 'UN'), '')
                                
                                PeterJonesP Daniel TomberlinD 2 Replies Last reply Reply Quote 3
                                • Alan KilbornA
                                  Alan Kilborn
                                  last edited by Alan Kilborn

                                  @Alan-Kilborn said in Auto sort tabs (real-time):

                                  how did you make that little complain-at-you popup text?

                                  I think that I may have…

                                  Well, not quite, because when I click on mine it takes me somewhere (bad) whereas clicking on @PeterJones 's it doesn’t go anywhere.

                                  1 Reply Last reply Reply Quote 1
                                  • PeterJonesP
                                    PeterJones @Alan Kilborn
                                    last edited by

                                    @Alan-Kilborn said in Auto sort tabs (real-time):

                                    Hey, how did you make that little complain-at-you popup text?

                                    See How to Markdown Code on this forum. 😉

                                    No, really, it’s there:

                                    This is a simple [here](https://www.google.com “go Google!”) link with custom hover pop-up text

                                    The popup hover text is the “title” text of the link. It’s also described at the no-longer-officially-linked daringfireball markdown spec

                                    And then, while I’m typing, you said

                                    I think that I may have [figured it out myself]…

                                    Congrats. Of course, if I weren’t wanting the bragging rights, I wouldn’t have been able to post this reply, since you went and figured it out.

                                    Well, not quite,

                                    Oh, sure, you realize you had a problem while I am still writing about your problem. Let me type this reply! 😠

                                    [text](# "hover") will do what you want. The # is the HTML shortcut for “go to the unnamed anchor on the current page”, which has been HTML shorthand for “do nothing with this link” forever (or, at least, from early forms of HTML in the 90s)

                                    1 Reply Last reply Reply Quote 1
                                    • Maxitrol - MatM
                                      Maxitrol - Mat
                                      last edited by

                                      looks like auto-sort is really a rocket science…

                                      Alan KilbornA 1 Reply Last reply Reply Quote 0
                                      • Alan KilbornA
                                        Alan Kilborn @Maxitrol - Mat
                                        last edited by

                                        @Maxitrol-Mat said in Auto sort tabs (real-time):

                                        looks like auto-sort is really a rocket science…

                                        Why do you say that?

                                        1 Reply Last reply Reply Quote 0
                                        • Daniel TomberlinD
                                          Daniel Tomberlin @Alan Kilborn
                                          last edited by Daniel Tomberlin

                                          Regardless of other’s sense of entitlement, I certainly appreciate the free software of notepad++ :D

                                          @alan-kilborn, I have a couple of questions:

                                          • How could I modify this to work so I can get my tabs to sort by ‘creation date’ since I accidentally sorted by ‘Name’ when I hit that ‘Sort Tabs’ button in the existing feature? … Maybe there is a simpler method since I’m not in need of automation?

                                          • Also, how does one run a script for notepad++?

                                          Btw, there is another post for my particular request by @Ashil-Krishnan here, but no solution. However, @PeterJones referenced this post, which brought me here to inquire.

                                          Thanks!

                                          Alan KilbornA 3 Replies Last reply Reply Quote 0
                                          • Daniel TomberlinD Daniel Tomberlin referenced this topic on
                                          • Alan KilbornA
                                            Alan Kilborn @Daniel Tomberlin
                                            last edited by

                                            @daniel-tomberlin said in Auto sort tabs (real-time):

                                            sort by ‘creation date’

                                            This may be problematic as tabs unsaved into the filesystem, with names such as new 1, new 3, etc. make determining a “creation date/time” difficult.

                                            Daniel TomberlinD 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            The Community of users of the Notepad++ text editor.
                                            Powered by NodeBB | Contributors