-
Notifications
You must be signed in to change notification settings - Fork 4
[WIP] Fancy command input #162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
st3/sublime_lib/command_input.py
Outdated
| __all__ = ['fancy_input', 'GenericListInputHandler', 'GenericTextInputHandler'] | ||
|
|
||
|
|
||
| def fancy_input( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A more descriptive suggestion: "input_generator"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me; renamed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, imports don't work anymore if the exported function has the same name as a module. The module needs to be renamed. I picked input_handlers.py locally.
| from functools import wraps | ||
|
|
||
| FancyInputHandler = Union['GenericListInputHandler', 'GenericTextInputHandler'] | ||
| GeneratorType = Generator[FancyInputHandler, object, None] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to resolve the object type through a type variable of the input handler's items param.
| self, | ||
| *, | ||
| name: str, | ||
| items: list |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other params we should look into supporting (and decide whether they are too advanced or not):
- placeholder
- initial_text
- initial_selection
- validate
- preview
- description
-
confirm(equivalent to the sent value into the generator) - cancel
For cancel we could throw an exception to the generator.
|
For the more complex case, i.e. a recursive input handler or similar, I find this implementation to be very flexible. However, it doesn't satisfy the initial criteria of #136, whose goal is to reduce boiler-plate as much as possible, notably when you don't want to implement
I believe that trying to preserve the type signature of the input handlers (via type variables) would still be possible using protocols, so unless it would unnecessarily complicate our implementation, striving for naive input handler compatibility should be our goal. |
|
An issue I've run into is that if the user presses backspace, things get weird. I'm not sure if there's a good way to handle this (we can't just rewind the generator). I think that the backspace issue is a hard blocker. Hopefully there's a good way around it. Other than the above, we could definitely implement a more #136-like interface on top of this. If the backspace issue proves fatal, we should be able to implement that sort of interface in a different way; the backspace issue shouldn't prevent that. (I wish we could just access the input handler UI directly.) |
|
When the user presses backspace, the event listener's Will need to experient with this myself. |
|
I did some experiments with the example from OP, also with raising an exception for
Honestly, this seems like a dead end. The fact that input handlers can be unwinded doesn't translate to an imperative generator. I was able to come up with a pretty neat multi-level resource browser, but that turned out to be too complex to embed into a generic interface like we're looking for. I'll include it anyway. Example for a complex input handler chainimport sublime
import sublime_plugin
from sublime_lib import ResourcePath, new_view
class TestCommand(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,
)
def input(self, args):
path = ResourcePath(args.get('path', 'Packages'))
if not path.exists():
return ResourcePathInputHandler('path', path)
class ResourcePathInputHandler(sublime_plugin.ListInputHandler):
_selected = None # type: ResourcePath
def __init__(self, name: str, path: ResourcePath):
self._name = name
self._path = path
def name(self) -> str:
return self._name
def list_items(self):
return [
sublime.ListInputItem(
text=path.name + ('/' if not path.exists() else ''),
value=str(path),
)
for path in self._path.children()
]
def confirm(self, value: str) -> None:
self._selected = ResourcePath(value)
def description(self, value: str, text: str) -> str:
return text.rstrip('/')
def next_input(self, args):
if not self._selected.exists():
return ResourcePathInputHandler(self._name, self._selected) |
|
Yeah, I came to a similar conclusion. Since we can't rewind or duplicate a generator, I don't think there's any sensible way to handle backspaces within this paradigm. |
For #136. Includes #160.
The basic moving parts are:
GenericListInputHandlerandGenericTextInputHandlerclasses.These, are, in principle orthogonal, though this initial implementation isn't quite. With a slightly different implementation, you could
yieldany arbitraryCommandInputHandlerrather than just these two special generic handlers.Usage example (a minor adaptation of the example from the issue):
Notes:
fancy_inputname could use some work.placeholderaren't present yet.cancel().Looking for input.