• Login
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.
  • M
    Michael Vincent @Maxitrol - Mat
    last edited by Dec 7, 2020, 2:12 PM

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

    p.s. Who put sort tabs as manual option in menu where you have to click 4 times before tabs get sorted - crazy! LOL

    How about just using the shortcut:

    Alt+W, W, T

    That is, press and hold the Alt key while then pressing in succession (while still holding the Alt key through all key presses) W, W, T

    Cheers.

    A A 2 Replies Last reply Dec 7, 2020, 3:25 PM Reply Quote 3
    • A
      Alan Kilborn @Michael Vincent
      last edited by Alan Kilborn Dec 7, 2020, 3:26 PM Dec 7, 2020, 3:25 PM

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

      …press and hold the Alt key while then pressing in succession (while still holding the Alt key through all key presses) W, W, T

      I might be tempted to buy-a-vowel and put an O (the letter “Oh”, not a zero) at the end of that sequence. It closes the extra window that has appeared (from the first W press).

      M 1 Reply Last reply Dec 7, 2020, 3:34 PM Reply Quote 0
      • M
        Michael Vincent @Alan Kilborn
        last edited by Dec 7, 2020, 3:34 PM

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

        It closes the extra window that has appeared

        Indeed it does! I wonder can that be setup as a macro so just need one keypress by shortcutting the Macro? That’s about as close as the OP can get to a plugin that does this automatically - one “keystroke”.

        Cheers.

        P 1 Reply Last reply Dec 7, 2020, 3:45 PM Reply Quote 0
        • P
          PeterJones @Michael Vincent
          last edited by Dec 7, 2020, 3:45 PM

          @Alan-Kilborn ,

          (from the first W press).

          Nitpick: the window opens on the second W. ;-)

          @Michael-Vincent

          I wonder can that be setup as a macro

          You cannot record it with the macros.
          Using type=2 wParam="11001", you can open the Windows dialog with a macro.
          Hmm, english.xml shows Sort tabs button as 7004… but running 7004 alone as the wParam doesn’t sort the tabs, and if you try a 7004 after the 11001, the 7004 (and anything subsequent) doesn’t try to run until after the Windows dialog is closed.

          However, knowing that the “Sort tabs” button ID for that dialog is 7004 (and OK is 2), @Ekopalypse could probably figure out the necessary PythonScript to launch the dialog, push “sort tabs”, and push “ok”.

          1 Reply Last reply Reply Quote 2
          • A
            astrosofista @Michael Vincent
            last edited by Dec 7, 2020, 4:10 PM

            @Michael-Vincent, All:

            Beware that those shortcuts are language-dependant, for example, Alt-W won’t work in my configuration, instead I need to press Alt-V to open that window.

            1 Reply Last reply Reply Quote 1
            • E
              Ekopalypse
              last edited by Dec 7, 2020, 4:42 PM

              @PeterJones

              that, of course, could be a solution but to be honest,
              it would drive me nuts to see this blinking window all the time.

              @Maxitrol-Mat
              are you interested in such a solution?

              P 1 Reply Last reply Dec 7, 2020, 4:49 PM Reply Quote 1
              • P
                PeterJones @Ekopalypse
                last edited by PeterJones Dec 7, 2020, 4:49 PM Dec 7, 2020, 4:49 PM

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

                it would drive me nuts to see this blinking window all the time.

                Me too. But would it really need to be “all the time”? The only time you would need to re-sort tabs would be when a new tab is added (if you close/remove a tab, the rest are still sorted properly), so wouldn’t you just hook onto the file-opened notification (which I believe triggers for both opening an existing file and for creating a new file tab)?

                But yes, no need to do further work if @Maxitrol-Mat isn’t willing to use PythonScript for the solution. ;-)

                1 Reply Last reply Reply Quote 2
                • D
                  dinkumoil @Maxitrol - Mat
                  last edited by dinkumoil Dec 12, 2020, 4:27 PM Dec 7, 2020, 6:32 PM

                  @Maxitrol-Mat and all

                  Instead of using the built-in dialog, tab sorting can be done very easily using the Move Tab Forward / Backward commands from the View -> Tab menu.

                  In the following I provide a solution in NppExec plugin script language. It uses a slightly optimized version of the Bubble Sort algorithm and sorts the tabs case-insensitive per view. The active tabs of the views are restored after sorting.

                  ::SortTabs
                  npp_console keep
                  
                  // Retrieve values of some constants
                  set local $(PrimaryViewId) ~ MAIN_VIEW
                  set local $(SecondViewId) ~ SUB_VIEW
                  set local $(PrimaryView) ~ PRIMARY_VIEW
                  set local $(SecondView) ~ SECOND_VIEW
                  
                  // Get id of active buffer's view
                  npp_sendmsg NPPM_GETCURRENTVIEW
                  set local $(CurViewId) = $(MSG_RESULT)
                  
                  // Init variables for view switching
                  if $(CurViewId) == $(PrimaryViewId) then
                    set local $(ActiveView) = $(PrimaryView)
                    set local $(ActiveViewId) = $(PrimaryViewId)
                    set local $(InactiveView) = $(SecondView)
                    set local $(InactiveViewId) = $(SecondViewId)
                  else if $(CurViewId) == $(SecondViewId) then
                    set local $(ActiveView) = $(SecondView)
                    set local $(ActiveViewId) = $(SecondViewId)
                    set local $(InactiveView) = $(PrimaryView)
                    set local $(InactiveViewId) = $(PrimaryViewId)
                  else
                    exit
                  endif
                  
                  // Remember buffer id of active tab in active view
                  npp_sendmsg NPPM_GETCURRENTDOCINDEX 0 $(ActiveViewId)
                  npp_sendmsg NPPM_GETBUFFERIDFROMPOS $(MSG_RESULT) $(ActiveViewId)
                  set local $(ActiveViewCurBufferId) = $(MSG_RESULT)
                  
                  // Remember buffer id of active tab in inactive view if this view is visible
                  npp_sendmsg NPPM_GETCURRENTDOCINDEX 0 $(InactiveViewId)
                  
                  if $(MSG_RESULT) == -1 then
                    set local $(ProcessInactiveView) = 0
                  else
                    npp_sendmsg NPPM_GETBUFFERIDFROMPOS $(MSG_RESULT) $(InactiveViewId)
                    set local $(InactiveViewCurBufferId) = $(MSG_RESULT)
                    set local $(ProcessInactiveView) = 1
                  endif
                  
                  // Start tab sorting in active view
                  set local $(CurView) = $(ActiveView)
                  
                  :IterateAllViews
                    // Retrieve number of open tabs in current view
                    npp_sendmsg NPPM_GETNBOPENFILES 0 $(CurView)
                    set local $(MaxTabIdx) ~ $(MSG_RESULT) - 1
                    set local $(MinTabIdx) = 0
                  
                    // Sort tabs using optimized version of Bubble Sort
                    // and "Move Tab xxx" commands from menu "View -> Tab"
                    :DoLoopStart
                      set local $(Swapped) = 0
                      set local $(CurTabIdx) = $(MaxTabIdx)
                  
                      npp_sendmsg NPPM_ACTIVATEDOC $(CurViewId) $(CurTabIdx)
                      set local $(LeftFileName) ~ strupper $(FILE_NAME)
                  
                      :ForLoopStart
                        if $(CurTabIdx) <= $(MinTabIdx) goto :ForLoopEnd
                  
                        set local $(RightFileName) = $(LeftFileName)
                        set local $(NextTabIdx) ~ $(CurTabIdx) - 1
                  
                        npp_sendmsg NPPM_ACTIVATEDOC $(CurViewId) $(NextTabIdx)
                        set local $(LeftFileName) ~ strupper $(FILE_NAME)
                  
                        if "$(LeftFileName)" > "$(RightFileName)" then
                          npp_sendmsg WM_COMMAND IDM_VIEW_TAB_MOVEFORWARD
                          set local $(LeftFileName) = $(RightFileName)
                          set local $(Swapped) = 1
                        endif
                  
                        set local $(CurTabIdx) ~ $(CurTabIdx) - 1
                      goto :ForLoopStart
                  
                      :ForLoopEnd
                      set local $(MinTabIdx) ~ $(MinTabIdx) + 1
                    if $(Swapped) == 1 goto :DoLoopStart
                  
                  // As long as the inactive view is visible ...
                  if $(ProcessInactiveView) == 1 then
                    // ... and we didn't process all views ...
                    if $(CurView) != $(InactiveView) then
                      // ... advance to next view and repeat tab sorting
                      set local $(CurView) = $(InactiveView)
                      set local $(CurViewId) = $(InactiveViewId)
                      goto :IterateAllViews
                    endif
                  endif
                  
                  // If inactive view is visible restore its active tab
                  if $(ProcessInactiveView) == 1 then
                    npp_sendmsg NPPM_GETPOSFROMBUFFERID $(InactiveViewCurBufferId) $(InactiveViewId)
                    set local $(InactiveViewActiveTabIdx) ~ $(MSG_RESULT) & 0x3FFFFFFF
                    npp_sendmsg NPPM_ACTIVATEDOC $(InactiveViewId) $(InactiveViewActiveTabIdx)
                  endif
                  
                  // Restore active tab of active view
                  npp_sendmsg NPPM_GETPOSFROMBUFFERID $(ActiveViewCurBufferId) $(ActiveViewId)
                  set local $(ActiveViewActiveTabIdx) ~ $(MSG_RESULT) & 0x3FFFFFFF
                  npp_sendmsg NPPM_ACTIVATEDOC $(ActiveViewId) $(ActiveViewActiveTabIdx)
                  

                  I have to admit that it produces lots of screen flickering (depending on the number of open tabs), but as tab sorting normally is not a frequently executed task it should be OK.

                  1 Reply Last reply Reply Quote 4
                  • Maxitrol - MatM
                    Maxitrol - Mat
                    last edited by Dec 7, 2020, 7:53 PM

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

                    f screen flickering (depending on the number of open tabs), but as tab sorting normally is not a frequently executed task it should be

                    I don’t mind flickering if it will auto-sort each time I add a new file.

                    If the author happened to see a comment like that (he won’t BTW), it would totally seal the coffin from such a feature ever happening.

                    That would make such an author an idiot. Have you done any business, ever?

                    D 1 Reply Last reply Dec 7, 2020, 7:59 PM Reply Quote -3
                    • D
                      dinkumoil @Maxitrol - Mat
                      last edited by dinkumoil Dec 7, 2020, 8:03 PM Dec 7, 2020, 7:59 PM

                      @Maxitrol-Mat

                      Have you done any business, ever?

                      Notepad++ is not a business, it’s a completely free product. Thus, business rules don’t fit for Npp project.

                      I don’t mind flickering if it will auto-sort each time I add a new file.

                      My script will not auto-sort. The script has to be assigned to a keyboard shortcut. Another way would be to install the NppEventExec plugin as well. This plugin is intended to work together with NppExec and can call NppExec scripts when an event is fired.

                      That would make such an author an idiot.

                      Because you were insulting, go figure out the solution which is perfect for you by yourself.

                      1 Reply Last reply Reply Quote 3
                      • A
                        Alan Kilborn
                        last edited by Dec 20, 2020, 12:01 PM

                        Same poster, same topic, DIFFERENT thread.

                        1 Reply Last reply Reply Quote 1
                        • A
                          Alan Kilborn
                          last edited by Alan Kilborn Dec 21, 2020, 9:27 PM Dec 21, 2020, 9:27 PM

                          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'), '')
                          
                          P 1 Reply Last reply Dec 21, 2020, 9:48 PM Reply Quote 5
                          • P
                            PeterJones @Alan Kilborn
                            last edited by Dec 21, 2020, 9:48 PM

                            @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…

                            A P 2 Replies Last reply Dec 21, 2020, 9:55 PM Reply Quote 2
                            • E
                              Ekopalypse
                              last edited by Dec 21, 2020, 9:54 PM

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

                              A 1 Reply Last reply Dec 22, 2020, 1:47 PM Reply Quote 3
                              • A
                                Alan Kilborn @PeterJones
                                last edited by Alan Kilborn Dec 21, 2020, 9:56 PM Dec 21, 2020, 9:55 PM

                                @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.

                                A 1 Reply Last reply Dec 22, 2020, 4:29 PM Reply Quote 4
                                • P
                                  PeterJones @PeterJones
                                  last edited by Dec 21, 2020, 9:56 PM

                                  @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
                                  • A
                                    Alan Kilborn @Ekopalypse
                                    last edited by Dec 22, 2020, 1:47 PM

                                    @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?

                                    A 1 Reply Last reply Dec 22, 2020, 2:45 PM Reply Quote 0
                                    • A
                                      Alan Kilborn @Alan Kilborn
                                      last edited by Alan Kilborn Dec 22, 2020, 2:47 PM Dec 22, 2020, 2:45 PM

                                      @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
                                      • E
                                        Ekopalypse
                                        last edited by Dec 22, 2020, 3:49 PM

                                        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
                                        • E
                                          Ekopalypse
                                          last edited by Dec 22, 2020, 4:16 PM

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

                                          A 1 Reply Last reply Dec 22, 2020, 4:25 PM Reply Quote 2
                                          • First post
                                            Last post
                                          The Community of users of the Notepad++ text editor.
                                          Powered by NodeBB | Contributors