Skip to content

Commit

Permalink
Feature/add line numbers to prompt (#14223)
Browse files Browse the repository at this point in the history
This PR adds line numbers to the default prompt (#13965).

You can now display absolute, relative of both line number in the emacs and vi prompt by using the `TerminalInteractiveShell.prompt_line_number_format` option the at takes string to format with both
`line` (1 base int) and `rel_line` (int)`. For example:

`c.TerminalInteractiveShell.prompt_line_number_format='{line: 4d}/{rel_line:+03d} | '`
  • Loading branch information
Carreau committed Dec 17, 2023
2 parents 6ec51b6 + 77b5ce6 commit b5bf8f1
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 6 deletions.
15 changes: 13 additions & 2 deletions IPython/terminal/interactiveshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,17 @@ def _merge_shortcuts(self, user_shortcuts):
help="Display the current vi mode (when using vi editing mode)."
).tag(config=True)

prompt_line_number_format = Unicode(
"",
help="The format for line numbering, will be passed `line` (int, 1 based)"
" the current line number and `rel_line` the relative line number."
" for example to display both you can use the following template string :"
" c.TerminalInteractiveShell.prompt_line_number_format='{line: 4d}/{rel_line:+03d} | '"
" This will display the current line number, with leading space and a width of at least 4"
" character, as well as the relative line number 0 padded and always with a + or - sign."
" Note that when using Emacs mode the prompt of the first line may not update.",
).tag(config=True)

@observe('term_title')
def init_term_title(self, change=None):
# Enable or disable the terminal title.
Expand Down Expand Up @@ -736,7 +747,7 @@ def _extra_prompt_options(self):
def get_message():
return PygmentsTokens(self.prompts.in_prompt_tokens())

if self.editing_mode == 'emacs':
if self.editing_mode == "emacs" and self.prompt_line_number_format == "":
# with emacs mode the prompt is (usually) static, so we call only
# the function once. With VI mode it can toggle between [ins] and
# [nor] so we can't precompute.
Expand All @@ -753,7 +764,7 @@ def get_message():
"message": get_message,
"prompt_continuation": (
lambda width, lineno, is_soft_wrap: PygmentsTokens(
self.prompts.continuation_prompt_tokens(width)
self.prompts.continuation_prompt_tokens(width, lineno=lineno)
)
),
"multiline": True,
Expand Down
27 changes: 23 additions & 4 deletions IPython/terminal/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,42 @@ def vi_mode(self):
return '['+mode+'] '
return ''

def current_line(self) -> int:
if self.shell.pt_app is not None:
return self.shell.pt_app.default_buffer.document.cursor_position_row or 0
return 0

def in_prompt_tokens(self):
return [
(Token.Prompt, self.vi_mode() ),
(Token.Prompt, 'In ['),
(Token.Prompt, self.vi_mode()),
(
Token.Prompt,
self.shell.prompt_line_number_format.format(
line=1, rel_line=-self.current_line()
),
),
(Token.Prompt, "In ["),
(Token.PromptNum, str(self.shell.execution_count)),
(Token.Prompt, ']: '),
]

def _width(self):
return fragment_list_width(self.in_prompt_tokens())

def continuation_prompt_tokens(self, width=None):
def continuation_prompt_tokens(self, width=None, *, lineno=None):
if width is None:
width = self._width()
line = lineno + 1 if lineno is not None else 0
prefix = " " * len(
self.vi_mode()
) + self.shell.prompt_line_number_format.format(
line=line, rel_line=line - self.current_line() - 1
)
return [
(Token.Prompt, (' ' * (width - 5)) + '...: '),
(
Token.Prompt,
prefix + (" " * (width - len(prefix) - 5)) + "...: ",
),
]

def rewrite_prompt_tokens(self):
Expand Down
4 changes: 4 additions & 0 deletions docs/source/whatsnew/pr/incompat-line-numbers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Line Numbers
============

This PR add line numbers to the default prompt.

0 comments on commit b5bf8f1

Please sign in to comment.