Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add tranformation for autobalance [{()}]
  • Loading branch information
jb-leger committed May 19, 2023
commit 86e13a07c7228c35d96d91923d0377d3d6100758
70 changes: 70 additions & 0 deletions IPython/core/inputtransformer2.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import tokenize
from typing import List, Tuple, Optional, Any
import warnings
import io

# only for type checking, do not import this, it lead to a circular
# import
Expand Down Expand Up @@ -189,6 +190,74 @@ def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
+ [parts[-1].rstrip()]) # Strip newline from last line

_AUTOBALANCE_MARKERS = (("(", ")"), ("{", "}"), ("[", "]"))
_AUTOBALANCE_OPEN = {x: i for i, (x, _) in enumerate(_AUTOBALANCE_MARKERS)}
_AUTOBALANCE_CLOSE = {x: i for i, (_, x) in enumerate(_AUTOBALANCE_MARKERS)}


def _autobalance_line(inpt_code):
"""Add necessary [{( in the begin of the expr and )}] at the end to balance."""

tokens = tokenize.generate_tokens(io.StringIO(inpt_code).readline)
closed_without_open = []
opened_without_close = []
begin_expr = 0
while True:
try:
token = next(tokens)
except (StopIteration, tokenize.TokenError):
break
if token.type == tokenize.OP:
if token.string in _AUTOBALANCE_OPEN:
opened_without_close.append(_AUTOBALANCE_OPEN[token.string])
elif token.string in _AUTOBALANCE_CLOSE:
if opened_without_close:
last_opened = opened_without_close.pop(-1)
if last_opened != _AUTOBALANCE_CLOSE[token.string]:
# can not be balanced only adding in the begin and end
# of expr
return (False, inpt_code)
else:
closed_without_open.append(_AUTOBALANCE_CLOSE[token.string])
elif token.string == "=":
if not opened_without_close:
# this is a assigment
begin_expr = token.end[1]

if not opened_without_close and not closed_without_open:
# no needed changes
return (False, inpt_code)

new_code = (
inpt_code[:begin_expr]
+ (" " if begin_expr else "")
+ "".join(_AUTOBALANCE_MARKERS[k][0] for k in closed_without_open[::-1])
+ inpt_code[begin_expr:].strip()
+ "".join(_AUTOBALANCE_MARKERS[k][1] for k in opened_without_close)
)
return (True, new_code)


def autobalance(lines, **kwargs):
"""For single line cell, add necessary [{( and )}] to balance."""

shell = kwargs.get("shell")
display = kwargs.get("display", False)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you are adding kwargs everywhere to get shell and display here, but that will break API of external transforms that don't take a kwargs. So we may wan to have a

class Autobalancer:

    def __init__(self, shell):
        ....

    def __call__(...):



Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good idea, thank you. This solution is a lot less intrusive. Done in 79a23bf.


if len(lines) != 1:
# apply only for single line cell
return lines

if shell is None or not shell.autobalance:
return lines

modified, new_line = _autobalance_line(lines[0])
if modified and display:
shell.auto_rewrite_input(new_line)
return [new_line]
return lines


class TokenTransformBase:
"""Base class for transformations which examine tokens.

Expand Down Expand Up @@ -589,6 +658,7 @@ def __init__(self, shell=None):
]
self.line_transforms = [
cell_magic,
autobalance,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here you should be able to have Autobalancer(shell)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuation of last thread. Done in 79a23bf.

]
self.token_transformers = [
MagicAssign,
Expand Down
15 changes: 12 additions & 3 deletions IPython/core/interactiveshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,16 @@ class InteractiveShell(SingletonConfigurable):
"""
).tag(config=True)

autobalance = Bool(
False,
help="""
Add automatically opening and closing parenthesis, braces and brackets
at the begin matching respectively closing and opening ones. Opening
symbols are added in the begin of the expression, and closings symbols
at the end of the line. E.g. `1+2)/2]*(2+3` becomes `[(1+2)/2]*(2+3)`.
""",
).tag(config=True)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


autoindent = Bool(True, help=
"""
Autoindent IPython code entered interactively.
Expand Down Expand Up @@ -485,9 +495,8 @@ def input_splitter(self):
will be displayed as regular output instead."""
).tag(config=True)


show_rewritten_input = Bool(True,
help="Show rewritten input, e.g. for autocall."
show_rewritten_input = Bool(
True, help="Show rewritten input, e.g. for autocall and autobalance."
).tag(config=True)

quiet = Bool(False).tag(config=True)
Expand Down