Micron Document
IME and click

I've been playing with click for argument parsing and user input and output.

Turns out, click can handle IME (Input Method Editor) input just fine. As well as copy and paste in the terminal via a mouse.

In both cases, a single call to T383838click.getchar() returns the entire (pasted or IME input) string.

So then to treat IME input and pasting as though it were regular user input becomes possible. Just iterate over the value from T383838getchar() and process it.

Using click is a step above just using T383838input() because the developer doesn't have to relinquish control over what's in the terminal. Instead, the code can cheerfully read and process user input while presenting whatever kind of TUI the developer wants to create. But the cost is losing the ability to have nice readline features -- though few developers and users seem to take advantage of readline's cooler features.

Anyway, keep in mind, I've only just started using click, so this might not be the way to do it, but it is a way.

Simplest (complete) Example

T282828
Tff7b72import T7ee787click


Tff7b72def Td2a8ffmainTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72while Tff7b72TrueTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3char Tff7b72= Te6edf3clickTff7b72.Td2a8ffgetcharTb4b4b4(Tb4b4b4) T8b949e# (1)
Te6edf3clickTff7b72.Td2a8ffechoTb4b4b4(Te6edf3charTb4b4b4) T8b949e# (2)
Tff7b72except Te6edf3clickTff7b72.Td2a8ffexceptionsTff7b72.Td2a8ffAbortTb4b4b4:
Tff7b72break


Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3mainTb4b4b4(Tb4b4b4)


Upon running this, each key that is entered will be pulled one at a time from (1) and then printed one at a time from (2).

But if the user pastes something into the terminal, then (1) will pull the entire string and then (2) will print that entire string.

Or, if the user has an IME configured, hitting the key combination for the IME will switch to however the IME handles input, and upon entering the value, (1) will get the entire string and then (2) will print that entire string.

How I'm Using It (an incomplete example)

I'm toying with making a TUI and so my input event loop is a little more complicated. For example:

T282828
Tff7b72def Td2a8ffget_input_stringTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Runs the main interaction loop until Enter is pressed."""
Tff7b72tryTb4b4b4:
Te6edf3clickTff7b72.Td2a8ffechoTb4b4b4(Te6edf3EscapesTff7b72.Td2a8ffCURSOR_HIDETb4b4b4) T8b949e# (1)
Tff7b72selfTff7b72.Td2a8ff_renderTb4b4b4(Tb4b4b4) T8b949e# (2)
Tff7b72while Tff7b72TrueTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3char Tff7b72= Te6edf3clickTff7b72.Td2a8ffgetcharTb4b4b4(Tb4b4b4) T8b949e# (3)
Tff7b72except Te6edf3clickTff7b72.Td2a8ffexceptionsTff7b72.Td2a8ffAbortTb4b4b4:
Tff7b72break
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_handle_inputTb4b4b4(Te6edf3charTb4b4b4)Tb4b4b4: T8b949e# (4)
Tff7b72break
Tff7b72selfTff7b72.Td2a8ff_renderTb4b4b4(Tb4b4b4) T8b949e# (5)
Tff7b72return Tff7b72selfTff7b72.Td2a8ffstateTff7b72.Td2a8ffbuffer
Tff7b72finallyTb4b4b4:
Te6edf3clickTff7b72.Td2a8ffechoTb4b4b4(Te6edf3EscapesTff7b72.Td2a8ffCURSOR_SHOWTb4b4b4) T8b949e# (6)
Te6edf3clickTff7b72.Td2a8ffechoTb4b4b4(Ta5d6ff"Tffea00\nTa5d6ff"Tb4b4b4)


This snippet is pretty similar to the simplest case. It's reading characters at a time via (3) but instead of displaying whatever is typed, (4) sends them off to handle user input. What happens depends on a broader state of the program. If the program is reading input from the user in a prompt, the characters go into a buffer and are displayed in that prompt. Provided that the characters are regular input. If they are special sequences, like delete or arrow keys, then instead of putting stuff in the buffer, stuff is removed or the pointer to where the input lands is moved.

The addition of (1) and (6) are so that the cursor is hidden when the program starts and unhidden when the program exits. I have a fake cursor that I turn on with escape sequences to illustrate where input is going. And I don't want the cursor jumping around the screen when output is being produced.

The calls to T383838_render (2 & 5) are used to update the screen. If characters were input or removed during T383838_handle_input, then a buffer is updated and the call to T383838_render will move the cursor via escape sequences and change what the user sees.

The purpose of T383838_handle_input is to prompt the user and get a response about something. The specific behavior varies. But when it has finished then it returns a string of input from the user. But that's enough context, back to the example!

In T383838_handle_input there is some code that handles individual character input (eg the call to (3) returned only one letter or one arrow or ... and there is another spot towards the end, once all of the special handling is interpreted (if there was any) that looks like this:

T282828
Tff7b72if Tffa657lenTb4b4b4(Te6edf3charTb4b4b4) Tff7b72!= T79c0ff1Tb4b4b4:
Tff7b72for Te6edf3c Tff7b72in Te6edf3charTb4b4b4:
Tff7b72if Te6edf3v Tff7b72:= Tff7b72selfTff7b72.Td2a8ff_handle_inputTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3v


And the purpose of this is to handle the case where either the user pasted content or used an IME to enter something. A paste or IME use is not the only reason for a multi-character string, so all the expected cases have to be handled above this snippet. That way it's the only thing left.

Edge Cases

What about single character pasting or single-character IME input? Well, it's not much of an edge case, it's the same as the user typing and it will be handled well before it reaches the snippet at the end of T383838_handle_input. It's just not easily guessed -- nor does the distinction matter -- that the input wasn't the result of regular typing. Having the abillity to discern when a multi-character paste or IME input has happened is a higher priority because it may be necessary to discard that content to avoid unexpected behavior.

Tags: python, index, cli

Tags
Navigation





created: 2026-02-25

(re)generated: 2026-07-17