Sublime Forum

Sorting Tabs

#1

Hello. I thought I would re-post the following TextCommand here. Assign it a key-binding and it will sort all open tabs alphabetically (case-insensitive). It sorts within groups as well.

[code]import sublime, sublime_plugin, os
from operator import itemgetter

class SortTabsCommand(sublime_plugin.TextCommand):
def run(self, edit):
file_views = ]
win = self.view.window()
curr_view = win.active_view()
for vw in win.views():
, tail = os.path.split(vw.file_name())
group, _ = win.get_view_index(vw)
file_views.append((tail.lower(), vw, group))
file_views.sort(key = itemgetter(2, 0))
for index, (
, vw, group) in enumerate(file_views):
if not index or group > prev_group:
moving_index = 0
prev_group = group
else:
moving_index += 1
win.set_view_index(vw, group, moving_index)
win.focus_view(curr_view)[/code]
It’s available as a Gist as well gist.github.com/1995854

Someone may have already created this in the past - I haven’t checked :question: . Andy.

0 Likes

#2

Thanks for this. I am currently using it.

0 Likes

#3

I just tested this on Win XP, ST build 2183, and get the following error:

Traceback (most recent call last):
  File ".\sublime_plugin.py", line 350, in run_
  File ".\SortTabs.py", line 10, in run
    head, tail = os.path.split(vw.file_name())
  File ".\ntpath.py", line 170, in split
  File ".\ntpath.py", line 125, in splitdrive
TypeError: 'NoneType' object is unsubscriptable

Any ideas?

0 Likes

#4

An untitled View has None for its file_name()

0 Likes

#5

[quote=“quodlibet”]I just tested this on Win XP, ST build 2183, and get the following error:

Traceback (most recent call last):
  File ".\sublime_plugin.py", line 350, in run_
  File ".\SortTabs.py", line 10, in run
    head, tail = os.path.split(vw.file_name())
  File ".\ntpath.py", line 170, in split
  File ".\ntpath.py", line 125, in splitdrive
TypeError: 'NoneType' object is unsubscriptable

Any ideas?[/quote]

My first guess would be that you have an unsaved file, in which case their is no file_name() to split. If that’s the case I’ll need to re-visit the code to handle this scenario.

I also haven’t tested it with a Cloned file. I’ll incorporate these concerns when I revisit the code - perhaps tonight. Andy.

0 Likes

#6

Yup. After saving all my untitled files, the plugin works.

It would be nice if untitled files could be accounted for. I often find myself using ST as a scratchpad :smile:

(Maybe using the tab’s label instead of the filename?)

0 Likes

#7

[quote=“quodlibet”]

Yup. After saving all my untitled files, the plugin works.

It would be nice if untitled files could be accounted for. I often find myself using ST as a scratchpad :smile:

(Maybe using the tab’s label instead of the filename?)[/quote]

Hi. Yes, I’ll “fix” this later :smile:

0 Likes

#8

This should “do it” for unsaved files and/or cloned files.

[code]import sublime, sublime_plugin, os
from operator import itemgetter

class SortTabsCommand(sublime_plugin.TextCommand):
def run(self, edit):
file_views = ]
win = self.view.window()
curr_view = win.active_view()
for vw in win.views():
, tail = os.path.split(vw.file_name() or os.path.sep)
group, _ = win.get_view_index(vw)
file_views.append((tail.lower(), vw, group))
file_views.sort(key = itemgetter(2, 0))
for index, (
, vw, group) in enumerate(file_views):
if not index or group > prev_group:
moving_index = 0
prev_group = group
else:
moving_index += 1
win.set_view_index(vw, group, moving_index)
win.focus_view(curr_view)[/code]
but let me know… Andy. I’ll update my Gist in a wee while.

0 Likes

#9

As indicated in another thread, this should really be a WindowCommand rather than a TextCommand. It required a small change to the code, but you can just replace the code with the following - your key-binding will work the same.

[code]import sublime, sublime_plugin
from os import path
from operator import itemgetter

class SortTabsCommand(sublime_plugin.WindowCommand):
def run(self):
file_views = ]
win = self.window
curr_view = win.active_view()
for vw in win.views():
, tail = path.split(vw.file_name() or path.sep)
group, _ = win.get_view_index(vw)
file_views.append((tail.lower(), vw, group))
file_views.sort(key = itemgetter(2, 0))
for index, (
, vw, group) in enumerate(file_views):
if index == 0 or group > prev_group:
moving_index = 0
prev_group = group
else:
moving_index += 1
win.set_view_index(vw, group, moving_index)
win.focus_view(curr_view)[/code]
Basically, sorting tabs applies to the whole Window, rather than a particular view. https://gist.github.com/2007980

I’m also only importing os.path, rather than the whole library, as only ‘path’ is needed. Andy.

0 Likes

#10

You don’t need to import sublime since you aren’t using anything from that namespace.

import sublime, sublime_plugin

Also, prev_group is not intialized before you use it.

if index == 0 or group > prev_group:

Edit
I guess technically index will always equal 0 first so you won’t try and compare prev_group until after it gets set where index = 0…but it still kind of bugs me, oh well, it is technically sound unless index doesn’t equal 0 first.

0 Likes

#11

@facelessuser Ta.

I guess technically index will always equal 0 first so you won't try and compare prev_group until after it gets set where index = 0...but it still kind of bugs me, oh well, it is technically sound unless index doesn't equal 0 first.

Yes, I was being clever :sunglasses: - making use of the fact that index would always be zero initially.

In JavaScript I can do things like:

some_thing = some_thing || 0;

That is, “if it doesn’t exist…”.

Is there a way to do something similar in Python? I can’t test an attribute if it doesn’t yet exist (Catch 22)?? Otherwise, I’ll end up with lots of ‘new_thing = 0’ :astonished:

0 Likes

#12
if "prev_group" in locals():

You can also check if in globals

if "prev_group" in globals():

What you are doing works; it just caught me off guard, doing what you see above is more explicit.

0 Likes

#13

Hi @facelessuser and thank you.

I was just reading around this subject and I noted that there can be occasions when the var falls between locals() and globals(). A Try…Catch can also be used… I’m not happy with either of these, so I suppose ‘moving_index = 0; prev_group = 0/None’ will have to do :wink:

**Added **But index will always be zero, so I hope it doesn’t ruin your weekend if I leave as-is :smiley:

0 Likes

#14

I have only the vaguest idea of what the last four posts were about.

You had me at:

Thanks a lot Andy!

0 Likes

#15

@quodlibet I addressed the recent posts @facelessuser :wink:

[quote]agibsonsw wrote:
This should “do it” for unsaved files[/quote]

“The code will now sort all open tabs, even if some of them are unsaved files”

0 Likes

#16

[quote=“agibsonsw”]Hi @facelessuser and thank you.

I was just reading around this subject and I noted that there can be occasions when the var falls between locals() and globals(). A Try…Catch can also be used… I’m not happy with either of these, so I suppose ‘moving_index = 0; prev_group = 0/None’ will have to do :wink:

**Added **But index will always be zero, so I hope it doesn’t ruin your weekend if I leave as-is :smiley:[/quote]

No it is fine :smile: . Hope I didn’t come off as a code snob. Looked like an issue at first, but after second look, I realized it was all accounted for. Great work.

0 Likes

#17

@facelessuser No problem :smile: and thank you.

Coming from a JS perspective it bugs me a little to have to look out for unassigned variables. I’m used to writing:

blah = blah || "TaDa!";

It’s also odd behaviour (to me) that in Python:

class Mine: class_attr = "shared" def method(self): self.class_attr = ".. no it's not" # 'magic' attribute! Mine.class_attr = "..yes it is!"
But I like ‘list comprehensions’ (and a *curry *is not out of the question…)

But I realise that this is off topic :smile:

0 Likes

#18

This seemed a little odd to me at first until I thought about it. Mine is the class object not that particular instance. When you instantiate it, self is an instance of Mine, a child if you will, derived from the Mine object. Mine and self are not the same thing.

Coming from a C/C++ background, javascript is bonkers :smile:. I do like fooling with javascript though. I hated it at first, but I grew to like it; I realized what I really hated was the inconsistencies of web browsers. But I still have a habit of looking out for non-accounted for variables even in javascript. It’s a cool language once you get into it; probably why nodeJS is getting so popular.

0 Likes

#19

@facelessuser JS is weirdand wonderful.

BTW If I bung the ST API methods into ‘Python.tmLanguage’ as part of, for example, ‘support.function.builtin.python’ then at least they would appear in a different colour. But I wonder how tricky it might be to create a new category, and amend my theme to include this category? I can’t really create a new language syntax, as it’s still a ‘.py’ file. But might it be possible to create a language-syntax file that checks for ‘import sublime’ specifically?

But I should probably start a new topic :wink:

0 Likes