Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions IPython/terminal/interactiveshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ def get_default_editor():

_use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)

def black_reformat_handler(text_before_cursor):
import black
formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
formatted_text = formatted_text[:-1]
return formatted_text


class TerminalInteractiveShell(InteractiveShell):
space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
'to reserve for the completion menu'
Expand Down Expand Up @@ -120,6 +128,11 @@ def debugger_cls(self):
help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
).tag(config=True)

autoformatter = Unicode(None,
help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
allow_none=True
).tag(config=True)

mouse_support = Bool(False,
help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
).tag(config=True)
Expand Down Expand Up @@ -150,6 +163,16 @@ def _editing_mode(self, change):
if self.pt_app:
self.pt_app.editing_mode = u_mode

@observe('autoformatter')
def _autoformatter_changed(self, change):
formatter = change.new
if formatter is None:
self.reformat_handler = lambda x:x
elif formatter == 'black':
self.reformat_handler = black_reformat_handler
else:
raise ValueError

@observe('highlighting_style')
@observe('colors')
def _highlighting_style_changed(self, change):
Expand Down
27 changes: 26 additions & 1 deletion IPython/terminal/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ def create_ipython_shortcuts(shell):
& insert_mode
))(return_handler)

def reformat_and_execute(event):
reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
event.current_buffer.validate_and_handle()

kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
& ~has_selection
& insert_mode
))(reformat_and_execute)

kb.add('c-\\')(force_exit)

kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
Expand Down Expand Up @@ -86,7 +95,17 @@ def create_ipython_shortcuts(shell):
return kb


def reformat_text_before_cursor(buffer, document, shell):
text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
try:
formatted_text = shell.reformat_handler(text)
buffer.insert_text(formatted_text)
except Exception as e:
buffer.insert_text(text)


def newline_or_execute_outer(shell):

def newline_or_execute(event):
"""When the user presses return, insert a newline or execute the code."""
b = event.current_buffer
Expand All @@ -107,7 +126,12 @@ def newline_or_execute(event):
else:
check_text = d.text[:d.cursor_position]
status, indent = shell.check_complete(check_text)


# if all we have after the cursor is whitespace: reformat current text
# before cursor
after_cursor = d.text[d.cursor_position:]
if not after_cursor.strip():
reformat_text_before_cursor(b, d, shell)
if not (d.on_last_line or
d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
):
Expand All @@ -118,6 +142,7 @@ def newline_or_execute(event):
return

if (status != 'incomplete') and b.accept_handler:
reformat_text_before_cursor(b, d, shell)
b.validate_and_handle()
else:
if shell.autoindent:
Expand Down
24 changes: 24 additions & 0 deletions docs/source/whatsnew/pr/autoformatting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Code autoformatting
===================

The IPython terminal can now auto format your code just before entering a new
line or executing a command. To do so use the
``--TerminalInteractiveShell.autoformatter`` option and set it to ``'black'``;
if black is installed IPython will use black to format your code when possible.

IPython cannot always properly format your code; in particular it will
auto formatting with *black* will only work if:

- Your code does not contains magics or special python syntax.

- There is no code after your cursor.

The Black API is also still in motion; so this may not work with all versions of
black.

It should be possible to register custom reformatter, though the API is till in
flux.