prompts.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. """Terminal input and output prompts."""
  2. from pygments.token import _TokenType, Token
  3. import sys
  4. from IPython.core.displayhook import DisplayHook
  5. from prompt_toolkit.formatted_text import fragment_list_width, PygmentsTokens
  6. from prompt_toolkit.shortcuts import print_formatted_text
  7. from prompt_toolkit.enums import EditingMode
  8. from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
  9. if TYPE_CHECKING:
  10. from IPython.terminal.interactiveshell import TerminalInteractiveShell
  11. class Prompts:
  12. def __init__(self, shell: "TerminalInteractiveShell"):
  13. self.shell = shell
  14. def vi_mode(self):
  15. if (getattr(self.shell.pt_app, 'editing_mode', None) == EditingMode.VI
  16. and self.shell.prompt_includes_vi_mode):
  17. mode = str(self.shell.pt_app.app.vi_state.input_mode)
  18. if mode.startswith('InputMode.'):
  19. mode = mode[10:13].lower()
  20. elif mode.startswith('vi-'):
  21. mode = mode[3:6]
  22. return '['+mode+'] '
  23. return ''
  24. def current_line(self) -> int:
  25. if self.shell.pt_app is not None:
  26. return self.shell.pt_app.default_buffer.document.cursor_position_row or 0
  27. return 0
  28. def in_prompt_tokens(self):
  29. return [
  30. (Token.Prompt.Mode, self.vi_mode()),
  31. (
  32. Token.Prompt.LineNumber,
  33. self.shell.prompt_line_number_format.format(
  34. line=1, rel_line=-self.current_line()
  35. ),
  36. ),
  37. (Token.Prompt, "In ["),
  38. (Token.PromptNum, str(self.shell.execution_count)),
  39. (Token.Prompt, ']: '),
  40. ]
  41. def _width(self):
  42. return fragment_list_width(self.in_prompt_tokens())
  43. def continuation_prompt_tokens(
  44. self,
  45. width: int | None = None,
  46. *,
  47. lineno: int | None = None,
  48. wrap_count: int | None = None,
  49. ):
  50. if width is None:
  51. width = self._width()
  52. line = lineno + 1 if lineno is not None else 0
  53. if wrap_count:
  54. return [
  55. (
  56. Token.Prompt.Wrap,
  57. # (" " * (width - 2)) + "\N{HORIZONTAL ELLIPSIS} ",
  58. (" " * (width - 2)) + "\N{VERTICAL ELLIPSIS} ",
  59. ),
  60. ]
  61. prefix = " " * len(
  62. self.vi_mode()
  63. ) + self.shell.prompt_line_number_format.format(
  64. line=line, rel_line=line - self.current_line() - 1
  65. )
  66. return [
  67. (
  68. getattr(Token.Prompt.Continuation, f"L{lineno}"),
  69. prefix + (" " * (width - len(prefix) - 5)) + "...:",
  70. ),
  71. (Token.Prompt.Padding, " "),
  72. ]
  73. def rewrite_prompt_tokens(self):
  74. width = self._width()
  75. return [
  76. (Token.Prompt, ('-' * (width - 2)) + '> '),
  77. ]
  78. def out_prompt_tokens(self) -> List[Tuple[_TokenType, str]]:
  79. return [
  80. (Token.OutPrompt, 'Out['),
  81. (Token.OutPromptNum, str(self.shell.execution_count - 1)),
  82. (Token.OutPrompt, ']: '),
  83. ]
  84. class ClassicPrompts(Prompts):
  85. def in_prompt_tokens(self):
  86. return [
  87. (Token.Prompt, '>>> '),
  88. ]
  89. def continuation_prompt_tokens(self, width=None):
  90. return [(Token.Prompt.Continuation, "... ")]
  91. def rewrite_prompt_tokens(self):
  92. return []
  93. def out_prompt_tokens(self):
  94. return []
  95. class RichPromptDisplayHook(DisplayHook):
  96. """Subclass of base display hook using coloured prompt"""
  97. def write_output_prompt(self):
  98. sys.stdout.write(self.shell.separate_out)
  99. # If we're not displaying a prompt, it effectively ends with a newline,
  100. # because the output will be left-aligned.
  101. self.prompt_end_newline = True
  102. if self.do_full_cache:
  103. tokens = self.shell.prompts.out_prompt_tokens()
  104. prompt_txt = "".join(s for _, s in tokens)
  105. if prompt_txt and not prompt_txt.endswith("\n"):
  106. # Ask for a newline before multiline output
  107. self.prompt_end_newline = False
  108. if self.shell.pt_app:
  109. print_formatted_text(PygmentsTokens(tokens),
  110. style=self.shell.pt_app.app.style, end='',
  111. )
  112. else:
  113. sys.stdout.write(prompt_txt)
  114. def write_format_data(self, format_dict: Dict[str, str], md_dict: Optional[Dict[Any, Any]]=None) -> None:
  115. assert self.shell is not None
  116. if self.shell.mime_renderers:
  117. for mime, handler in self.shell.mime_renderers.items():
  118. if mime in format_dict:
  119. handler(format_dict[mime], None)
  120. return
  121. super().write_format_data(format_dict, md_dict)