Sublime Forum

Simple examples for TextInputHandler

#1

Can anyone provide a minimal example of how to implement a TextInputHandler ? For example how can I enter a certain text in the command palette & print it in the console ? Thanks.

0 Likes

#2

The Default/arithmetic.py might be a good point to start with.

The Default/view_resource.py provides a simple example for ListInputHandlers.

I use them in https://github.com/deathaxe/MoveView or https://github.com/deathaxe/sublime-commands, too. Both are used in https://github.com/deathaxe/folder-aliaser. The latter one needs python 3.8!

1 Like

#3

Is that python file specific to 4xxx builds ? I can’t locate that resource on build 3211.

0 Likes

#4

It is zipped into the Default.sublime-package, which ships with ST.

You can call “View Package File” from the command palette and enter the Default/view_resource.py path as argument. You’ll see the file in action, then :wink:

0 Likes

#5

Yes. That’s precisely what I did. But there is no resource with that name.

0 Likes

#6

Sorry, the command is part of the Default/ui.py.

I was mislead as I moved it to a dedicated file in my sublime-commans package and added some filters to exclude resources from the Cache folder.

1 Like

#7

Gotcha. Thanks for the inputs (literally :stuck_out_tongue: )

0 Likes

#8

I have gone through the Default/arithmetic.py but I can’t understand how the input() method of the command is triggered ?

0 Likes

#9

When a command with arguments is called without them, but it defines an input() method, then instead of throwing an exception about missing arguments Sublime will call the input() method to see if there is an input handler that can be used to gather the arguments instead.

Every input handler represents an argument to the command, and once the entire chain of them is finished, Sublime re-invokes the command with the arguments that it gathered.

1 Like

#10

I tried to strip down the arithmetic.py to get a simple example. But I am getting some error. Here is the example.

import sublime_plugin


class ExprInputHandler(sublime_plugin.TextInputHandler):
    def __init__(self, view):
        self.view = view

    def placeholder(self):
        return "Expression"

    def initial_text(self):
    	return "Expression"


class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        pass

    def input(self, args):
        return ExprInputHandler(self.view)

I am getting the error as TypeError: __init__() missing 1 required positional argument: 'view'

0 Likes

#11

Are you sure this is the whole of the sample code for this? It compiles and runs (and unexpectedly, at least to me, prompts you with an input handler even though it supports no arguments).

Where it fails is when you enter something into the handler view and press Enter to run the command, because the command doesn’t take any arguments but the input handler gives it one:

Traceback (most recent call last):
  File "C:\Program Files\Sublime Text 3\sublime_plugin.py", line 1082, in run_
    return self.run(edit, **args)
TypeError: run() got an unexpected keyword argument 'expr'

0 Likes

#12

Now that I re started Sublime. It does not show that error anymore but It doesn’t show the input handler at all. I am jus running view.run_command('example') from the console but nothing happens with the crude example that I provided.

0 Likes

#13

Those sorts of transient errors can be caused by plugin reloading in some cases, particularly when you do something like change the name of an existing class to be something else (though there may be other situations as well; that’s just the one I tend to run into the most).

Cycling back to your original question, here’s a minimal version of the example you originally asked for (it could be even more minimal though):

import sublime
import sublime_plugin


class ArgumentInputHandler(sublime_plugin.TextInputHandler):
    def placeholder(self):
        return "Text to print"

class InputPrintCommand(sublime_plugin.TextCommand):
    def run(self, edit, argument):
        print(argument)

    def input(self, args):
        if "text" not in args:
            return ArgumentInputHandler()

To use this you also need to add the command to the command palette by adding an entry to a sublime-commands file;

    { "caption": "Print Input to Console", "command": "input_print" },

At this point, using view.run_command("input_print") from the console or selecting the command from the command palette will prompt you to enter the text:

Something you may have missed is that only commands that appear in the command palette support using input handlers because the handlers display input in the command palette itself as a part of it’s operation.

When the command is executed and not given a value for the argument argument, normally Sublime would throw an exception in the console, but because it has an input() method and is registered to be in the command palette, input() gets called to see if there is an input handler that can be used to provide the arguments.

The call gets the arguments that the command received, so you can determine how best to prompt (for example if the command took two arguments, then you could prompt for just the one that’s missing instead of making the user enter both).

The name of the InputHandler class is converted to an argument name in the same way that a Command class is converted to a command name (except that _input_handler is thrown off the end instead of _command). So because the argument is literally named argument, that is the name of the class as well.

You can implement the name() method in your handler if you want to change the name of the argument that it applies to. Similarly you can use the other methods to do things like set the initial text, show the placeholder text that hints to the user what the argument is for (which is what this example does), validate that the input is correct to stop the user from submitting the value, etc.

Once the user enters the text and hits enter, sublime calls next_input() on the current input handler to see which handler should be used for the next argument. Here that method isn’t specified, so there is no next input handler (the base class returns None for this method for you), so the input is deemed to be complete.

Now Sublime adds the value of argument to the list of arguments that it already had and executes the command again; since all of the arguments are specified, it prints the text to the console.

2 Likes

#14

Thanks a ton :slightly_smiling_face:

0 Likes

#15

Is it possible for the Command Palette to show one caption and for the TextInputHandler to show another caption ? Because when a user invokes the command from the Command Palette, I want them to know what the command does but when the TextInputHandler is invoked, I don’t want them to see that long caption again.

0 Likes

#16

You can achieve it by defining the following method in your TextCommand

class InputPrintCommand(sublime_plugin.TextCommand):
    def input_description(self):
        return "Title"
2 Likes