Hi forum,
Let’s say I want to mimic the functionality of the local symbol list (Ctrl+R by default), but target a specific scope selector.
(The reason I want to do this is, I would like to be able to have multiple different “symbol lists” for one file. One for “headings”, and one for “functions”. These are both useful types of symbols, which can (and in my case do) legitimately occur in the same file, but I don’t want to have them in the same list, as they don’t belong together logically. So if I can target a different scope on the fly, then I can maintain two separate lists for each.)
I can use the following code to add a test_show_overlay
command to Sublime Text (similar to the built-in show_overlay
command), which will bring up a list of functions defined in a python file. (We could of course pass it whatever scope selector we please instead.)
import sublime
import sublime_plugin
class TestShowOverlayCommand(sublime_plugin.WindowCommand):
def run(self, scope="entity.name.function.python"):
self.original_selection = [
(region.a, region.b) for region in self.window.active_view().sel()
]
self.matches = sorted(
self.window.active_view().find_by_selector(scope),
key=lambda region: region.begin(),
)
self.window.show_quick_panel(
[
self.window.active_view().substr(region)
for region in self.matches
],
self.on_done,
on_highlight=self.on_highlight,
)
def on_done(self, index):
regions = [sublime.Region(*tup) for tup in self.original_selection]
middle_region = regions[len(regions) // 2]
self.window.active_view().sel().clear()
self.window.active_view().sel().add_all(regions)
self.window.active_view().show_at_center(middle_region)
def on_highlight(self, index):
if 0 <= index < len(self.matches):
region = self.matches[index]
self.window.active_view().sel().clear()
self.window.active_view().sel().add(
sublime.Region(region.a, region.b)
)
self.window.active_view().show_at_center(region)
To test this command, we can (for example) add it to the command palette with a .sublime-commands
file.
{
"caption": "run test overlay",
"command": "test_show_overlay"
}
And finally, for a file to test this command on, consider the following Python file.
def foo(): pass
def bar(): pass
Compare what happens when we bring up the local symbol list, vs. what happens when we run our “run test overlay” command on this file. In both cases we get the list:
foo
bar
But when you switch between selecting different items in this list, the results are different. In the built-in version, the active selection in the file changes, to highlight whichever thing in the list you are currently selecting. Whilst in this test version, the selection stays the same, even though in the code we should be changing the selection.
(Note: modify our example file to
def foo(): pass
def bar(): pass
and insert as many blank lines as necessary between our definition of foo
and bar
, until one is at the top of your screen and the other is at the bottom. Then try test_show_overlay
again, and observe that now the active selection does change. Why does it work in this situation?)
So that’s my question: why doesn’t the selection (appear to?) change on highlighting different choices in the test_show_overlay
list? How can I get the same behaviour as the local symbol list, in that the active selection changes appropriately?