completion.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. """
  2. Key binding handlers for displaying completions.
  3. """
  4. from __future__ import unicode_literals
  5. from prompt_toolkit.completion import CompleteEvent, get_common_complete_suffix
  6. from prompt_toolkit.utils import get_cwidth
  7. from prompt_toolkit.keys import Keys
  8. from prompt_toolkit.key_binding.registry import Registry
  9. import math
  10. __all__ = (
  11. 'generate_completions',
  12. 'display_completions_like_readline',
  13. )
  14. def generate_completions(event):
  15. r"""
  16. Tab-completion: where the first tab completes the common suffix and the
  17. second tab lists all the completions.
  18. """
  19. b = event.current_buffer
  20. # When already navigating through completions, select the next one.
  21. if b.complete_state:
  22. b.complete_next()
  23. else:
  24. event.cli.start_completion(insert_common_part=True, select_first=False)
  25. def display_completions_like_readline(event):
  26. """
  27. Key binding handler for readline-style tab completion.
  28. This is meant to be as similar as possible to the way how readline displays
  29. completions.
  30. Generate the completions immediately (blocking) and display them above the
  31. prompt in columns.
  32. Usage::
  33. # Call this handler when 'Tab' has been pressed.
  34. registry.add_binding(Keys.ControlI)(display_completions_like_readline)
  35. """
  36. # Request completions.
  37. b = event.current_buffer
  38. if b.completer is None:
  39. return
  40. complete_event = CompleteEvent(completion_requested=True)
  41. completions = list(b.completer.get_completions(b.document, complete_event))
  42. # Calculate the common suffix.
  43. common_suffix = get_common_complete_suffix(b.document, completions)
  44. # One completion: insert it.
  45. if len(completions) == 1:
  46. b.delete_before_cursor(-completions[0].start_position)
  47. b.insert_text(completions[0].text)
  48. # Multiple completions with common part.
  49. elif common_suffix:
  50. b.insert_text(common_suffix)
  51. # Otherwise: display all completions.
  52. elif completions:
  53. _display_completions_like_readline(event.cli, completions)
  54. def _display_completions_like_readline(cli, completions):
  55. """
  56. Display the list of completions in columns above the prompt.
  57. This will ask for a confirmation if there are too many completions to fit
  58. on a single page and provide a paginator to walk through them.
  59. """
  60. from prompt_toolkit.shortcuts import create_confirm_application
  61. assert isinstance(completions, list)
  62. # Get terminal dimensions.
  63. term_size = cli.output.get_size()
  64. term_width = term_size.columns
  65. term_height = term_size.rows
  66. # Calculate amount of required columns/rows for displaying the
  67. # completions. (Keep in mind that completions are displayed
  68. # alphabetically column-wise.)
  69. max_compl_width = min(term_width,
  70. max(get_cwidth(c.text) for c in completions) + 1)
  71. column_count = max(1, term_width // max_compl_width)
  72. completions_per_page = column_count * (term_height - 1)
  73. page_count = int(math.ceil(len(completions) / float(completions_per_page)))
  74. # Note: math.ceil can return float on Python2.
  75. def display(page):
  76. # Display completions.
  77. page_completions = completions[page * completions_per_page:
  78. (page+1) * completions_per_page]
  79. page_row_count = int(math.ceil(len(page_completions) / float(column_count)))
  80. page_columns = [page_completions[i * page_row_count:(i+1) * page_row_count]
  81. for i in range(column_count)]
  82. result = []
  83. for r in range(page_row_count):
  84. for c in range(column_count):
  85. try:
  86. result.append(page_columns[c][r].text.ljust(max_compl_width))
  87. except IndexError:
  88. pass
  89. result.append('\n')
  90. cli.output.write(''.join(result))
  91. cli.output.flush()
  92. # User interaction through an application generator function.
  93. def run():
  94. if len(completions) > completions_per_page:
  95. # Ask confirmation if it doesn't fit on the screen.
  96. message = 'Display all {} possibilities? (y on n) '.format(len(completions))
  97. confirm = yield create_confirm_application(message)
  98. if confirm:
  99. # Display pages.
  100. for page in range(page_count):
  101. display(page)
  102. if page != page_count - 1:
  103. # Display --MORE-- and go to the next page.
  104. show_more = yield _create_more_application()
  105. if not show_more:
  106. return
  107. else:
  108. cli.output.write('\n'); cli.output.flush()
  109. else:
  110. # Display all completions.
  111. display(0)
  112. cli.run_application_generator(run, render_cli_done=True)
  113. def _create_more_application():
  114. """
  115. Create an `Application` instance that displays the "--MORE--".
  116. """
  117. from prompt_toolkit.shortcuts import create_prompt_application
  118. registry = Registry()
  119. @registry.add_binding(' ')
  120. @registry.add_binding('y')
  121. @registry.add_binding('Y')
  122. @registry.add_binding(Keys.ControlJ)
  123. @registry.add_binding(Keys.ControlI) # Tab.
  124. def _(event):
  125. event.cli.set_return_value(True)
  126. @registry.add_binding('n')
  127. @registry.add_binding('N')
  128. @registry.add_binding('q')
  129. @registry.add_binding('Q')
  130. @registry.add_binding(Keys.ControlC)
  131. def _(event):
  132. event.cli.set_return_value(False)
  133. return create_prompt_application(
  134. '--MORE--', key_bindings_registry=registry, erase_when_done=True)