prompt-toolkit / python-prompt-toolkit

Library for building powerful interactive command line applications in Python
https://python-prompt-toolkit.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
9.25k stars 714 forks source link

FR: ability to pause KeyProcessor #1910

Open eight04 opened 2 weeks ago

eight04 commented 2 weeks ago

I want to implement typeahead in a full screen app.

For example: if the user types a key while a list is loading, the key shouldn't be processed until the list is loaded and the item is focused.

# suppose the user types `nfoo\nbar\n` on a todo list

@kb.add("n") # create a new todo
async def _(event):
  app.key_processor.pause()

  await some_tasks()

  p = await prepare_and_focus_prompt()
  app.key_processor.resume()
  name = await p.readline(on_accept=app.key_processor.pause)
  # we have to pause key_processor immediately after `\n` so `bar\n` can be kept in `input_queue`

  await some_tasks()

  p2 = await prepare_and_focus_prompt()
  app.key_processor.resume() # now `bar` would be processed by the second prompt.
  desc = await p2.readline(on_accept=app.key_processor.pause)

  # back to the list and resume key_processor
  await create_item(name, desc)
  await load_and_focus_list()
  app.key_processor.resume() # any keys after `\n` should go to list window

Currently this is what I do:

  1. Configure asyncio to run tasks eagerly: asyncio.get_running_loop().set_task_factory(asyncio.eager_task_factory)
  2. Modify app._default_bindings to add an <any> handler, which will catch all unhandled keys.
  3. Before loading the list, focus a dummy window so key presses can be cought by (2)
  4. After the list is ready, focus the list item and send those keys to app.key_processor.feed_multiple.
  5. Call key_processor.process_keys. However, you have to call it via aynscio.get_running_loop().call_later(0.1, app.key_processor.process_keys) or the app will freeze. I have no idea why.

I also noticed that there is an input.detach function. However, it doesn't work in a key binding handler since keys typed are already sent to key_processor.input_queue.

eight04 commented 2 weeks ago

5. aynscio.get_running_loop().call_later(0.1, app.key_processor.process_keys)

It seems that it has to execute after app.after_render event?