SublimeText / sublime_lib

Utility library for frequently used functionality in Sublime Text and convenience functions or classes
https://sublimetext.github.io/sublime_lib
MIT License
52 stars 4 forks source link

input_handler class decorator to automatically add and create simple input handlers #136

Open FichteFoll opened 4 years ago

FichteFoll commented 4 years ago

I was just thinking that most (text) input handlers are fairly simple and provide mostly static values for their (abstract) methods, which makes creation of a separate class rather decoupled from the command's not as transparent as it needs to. Furthermore, implementing the command's input seems like busy work most of the time and things become especially complicated once you start using multiple handlers and want them to chain using next_input.

I believe a decorator in ArgumentParser.add_argument style could make this API more streamlined while still allowing for the complex case.

API example:

@input_handler("param", kind="text", placeholder="Replace me", validate=lambda _: True)
@input_handler("param2", kind="list", items=list(range(10)))
@input_handler(Param3InputHandler)  # name *can* be determined from handler's `name()`
class TestCommand(sublime_plugin.WindowCommand):
    def run(self, explicit_param, param, param2, param3, opt_param=None):
        pass

Questions

  1. Technically, input handlers don't have access to the command instance, the window/view or any of the previous parameters. Especially for the complex case, I'm unsure how to provide this information since you'd use custom handlers mostly in those situations when that information is needed. How to handle this? Just pass window/view and a dict of all args to the handler's __init__?

  2. The same issue also surfaces when the list items you want to provide are dynamic.

  3. For a similar reason, I don't think confirm, cancel, or description make much sense to be supplied in the decorator short-hand form.

  4. Use the kind param to distinguish, decide based on an items parameter, or use two decorators use text_input_handler and list_input_handler?

FichteFoll commented 4 years ago

If you have some complex input handler examples, I believe trying to wrap those into such a generator as an example would help with designing the API for this.

Thom1729 commented 3 years ago

I've played around with this recently. The stumbling block for me is that I can't really think of any especially complex examples out in the wild. (Maybe this is a chicken-and-egg thing, and more people would write input handlers if they were easier to work with.)

So I wrote up a quick example implementation of something like PackageResourceViewer's Open Resource command:

class InputTestCommand(sublime_plugin.WindowCommand):
    def run(self, path):
        content = sublime.load_resource(path)
        new_view(
            self.window,
            name=path,
            content=content,
            syntax=sublime.find_syntax_for_file(path),
            read_only=True,
            scratch=True,
        )

    @fancy_input
    def input(self, args):
        path = ResourcePath('Packages')

        while not path.exists():
            path_str = yield GenericListInputHandler(
                name='path',
                items=[
                    sublime.ListInputItem(child.name, str(child))
                    for child in path.children()
                ]
            )
            path = ResourcePath(path_str)

It's all done with generators behind the scenes, of course. How does something like this look?

FichteFoll commented 3 years ago

A recursive input handler surely sounds like a complex use case to me and a generator sounds like a good candidate as well (I love generators). Specifying the name of the input handler's parameter as an argument also makes sense and matches my suggestion of using decorators to generate a stub implementation for input.

Would be interested to try this out myself.