Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4603a22
basic parentheses and quote closing on key events
samuelgregorovic Oct 6, 2021
594a468
basic pair deletion for common pairs
samuelgregorovic Oct 6, 2021
691def4
jump behind closing paren/quote on TAB
samuelgregorovic Oct 6, 2021
ee98f16
jump on tab even with content in parens
samuelgregorovic Oct 30, 2021
a59daed
move cursor on closing paren ovrwrite
samuelgregorovic Oct 30, 2021
9206403
removed quotes completion + fixed bad cursor bug
samuelgregorovic Oct 30, 2021
82a32e6
nested tabbing/ovewriting brackets working
samuelgregorovic Oct 30, 2021
e00f87b
fixed backspace event when cursor on closing paren
samuelgregorovic Oct 30, 2021
a902deb
added quote completion + autocomplete compatibility
samuelgregorovic Oct 30, 2021
c11b5fc
added cmd argument + update manpage help
samuelgregorovic Oct 30, 2021
a55830c
added config option + updated sample config
samuelgregorovic Oct 30, 2021
49c98df
obey config option + docstrings
samuelgregorovic Oct 30, 2021
aa321b1
update and document on_tab method
samuelgregorovic Oct 30, 2021
05dc36d
added comment
samuelgregorovic Oct 30, 2021
dc9d176
removed cmd argument
samuelgregorovic Oct 30, 2021
a34140a
removed unnecessary __class__ call + fix bad function call
samuelgregorovic Nov 2, 2021
5d302a5
removed unnecessary manpage entry
samuelgregorovic Nov 2, 2021
192663c
moved CHARACTER_PAIR_MAP to __init__ + 0 index fix
samuelgregorovic Nov 2, 2021
66c317b
moved cursor_on_closing_char_pair to line.py
samuelgregorovic Nov 2, 2021
a23843b
moved CHARACTER_PAIR_MAP to line.py
samuelgregorovic Nov 2, 2021
41750bf
fix overwriting quotes
samuelgregorovic Nov 2, 2021
6138b7a
fix autoclose quotes bug
samuelgregorovic Nov 2, 2021
8be9bbb
autocomplete enabled only before closing params or space
samuelgregorovic Nov 2, 2021
73782de
fix matching history entries
samuelgregorovic Nov 2, 2021
f3f03e2
basic tests without history
samuelgregorovic Nov 3, 2021
81bc622
added quote test
samuelgregorovic Nov 3, 2021
f33cf55
renamed add_to_search to narrow_search
samuelgregorovic Nov 3, 2021
66d34cf
changed default config value to False
samuelgregorovic Nov 3, 2021
d14207d
fixed test DRY problem with helper method
samuelgregorovic Nov 3, 2021
100735d
fixed insert_char_pair_start docstring
samuelgregorovic Nov 3, 2021
2c683fc
fix bad behavior of reverse-incremental search
samuelgregorovic Nov 3, 2021
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
4 changes: 4 additions & 0 deletions bpython/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class Config:
"syntax": True,
"tab_length": 4,
"unicode_box": True,
"brackets_completion": False,
},
"keyboard": {
"backspace": "C-h",
Expand Down Expand Up @@ -358,6 +359,9 @@ def get_key_no_doublebind(command: str) -> str:
if self.unicode_box and supports_box_chars()
else ("|", "|", "-", "-", "+", "+", "+", "+")
)
self.brackets_completion = config.getboolean(
"general", "brackets_completion"
)


def load_theme(
Expand Down
15 changes: 14 additions & 1 deletion bpython/curtsiesfrontend/manual_readline.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
based on http://www.bigsmoke.us/readline/shortcuts"""

from ..lazyre import LazyReCompile

import inspect

from ..line import cursor_on_closing_char_pair

INDENT = 4

# TODO Allow user config of keybindings for these actions
Expand Down Expand Up @@ -244,6 +245,18 @@ def backspace(cursor_offset, line):
cursor_offset - to_delete,
line[: cursor_offset - to_delete] + line[cursor_offset:],
)
# removes opening bracket along with closing bracket
# if there is nothing between them
# TODO: could not get config value here, works even without -B option
on_closing_char, pair_close = cursor_on_closing_char_pair(
cursor_offset, line
)
if on_closing_char and pair_close:
return (
cursor_offset - 1,
line[: cursor_offset - 1] + line[cursor_offset + 1 :],
)

return (cursor_offset - 1, line[: cursor_offset - 1] + line[cursor_offset:])


Expand Down
109 changes: 103 additions & 6 deletions bpython/curtsiesfrontend/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
Interp,
code_finished_will_parse,
)
from .manual_readline import edit_keys
from .manual_readline import edit_keys, cursor_on_closing_char_pair
from .parse import parse as bpythonparse, func_for_letter, color_for_letter
from .preprocess import preprocess
from .. import __version__
Expand All @@ -54,6 +54,7 @@
SourceNotFound,
)
from ..translations import _
from ..line import CHARACTER_PAIR_MAP

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -789,9 +790,74 @@ def process_key_event(self, e):
self.incr_search_mode = None
elif e in ("<SPACE>",):
self.add_normal_character(" ")
elif e in CHARACTER_PAIR_MAP.keys():
if e in ["'", '"']:
if self.is_closing_quote(e):
self.insert_char_pair_end(e)
else:
self.insert_char_pair_start(e)
else:
self.insert_char_pair_start(e)
elif e in CHARACTER_PAIR_MAP.values():
self.insert_char_pair_end(e)
else:
self.add_normal_character(e)

def is_closing_quote(self, e):
char_count = self._current_line.count(e)
if (
char_count % 2 == 0
and cursor_on_closing_char_pair(
self._cursor_offset, self._current_line, e
)[0]
):
return True
return False

def insert_char_pair_start(self, e):
"""Accepts character which is a part of CHARACTER_PAIR_MAP
like brackets and quotes, and appends it to the line with
an appropriate character pair ending. Closing character can only be inserted
when the next character is either a closing character or a space

e.x. if you type "(" (lparen) , this will insert "()"
into the line
"""
self.add_normal_character(e)
if self.config.brackets_completion:
allowed_chars = ["}", ")", "]", " "]
start_of_line = len(self._current_line) == 1
end_of_line = len(self._current_line) == self._cursor_offset
can_lookup_next = len(self._current_line) > self._cursor_offset
next_char = (
None
if not can_lookup_next
else self._current_line[self._cursor_offset]
)
next_char_allowed = next_char in allowed_chars
if start_of_line or end_of_line or next_char_allowed:
closing_char = CHARACTER_PAIR_MAP[e]
self.add_normal_character(closing_char, narrow_search=False)
self._cursor_offset -= 1

def insert_char_pair_end(self, e):
"""Accepts character which is a part of CHARACTER_PAIR_MAP
like brackets and quotes, and checks whether it should be
inserted to the line or overwritten

e.x. if you type ")" (rparen) , and your cursor is directly
above another ")" (rparen) in the cmd, this will just skip
it and move the cursor.
If there is no same character underneath the cursor, the
character will be printed/appended to the line
"""
if self.config.brackets_completion:
if self.cursor_offset < len(self._current_line):
if self._current_line[self.cursor_offset] == e:
self.cursor_offset += 1
return
self.add_normal_character(e)

def get_last_word(self):

previous_word = _last_word(self.rl_history.entry)
Expand Down Expand Up @@ -892,7 +958,15 @@ def only_whitespace_left_of_cursor():
for unused in range(to_add):
self.add_normal_character(" ")
return

# if cursor on closing character from pair,
# moves cursor behind it on tab
# ? should we leave it here as default?
if self.config.brackets_completion:
on_closing_char, _ = cursor_on_closing_char_pair(
self._cursor_offset, self._current_line
)
if on_closing_char:
self._cursor_offset += 1
# run complete() if we don't already have matches
if len(self.matches_iter.matches) == 0:
self.list_win_visible = self.complete(tab=True)
Expand All @@ -904,7 +978,6 @@ def only_whitespace_left_of_cursor():
# using _current_line so we don't trigger a completion reset
if not self.matches_iter.matches:
self.list_win_visible = self.complete()

elif self.matches_iter.matches:
self.current_match = (
back and self.matches_iter.previous() or next(self.matches_iter)
Expand All @@ -913,6 +986,24 @@ def only_whitespace_left_of_cursor():
self._cursor_offset, self._current_line = cursor_and_line
# using _current_line so we don't trigger a completion reset
self.list_win_visible = True
if self.config.brackets_completion:
# appends closing char pair if completion is a callable
if self.is_completion_callable(self._current_line):
self._current_line = self.append_closing_character(
self._current_line
)

def is_completion_callable(self, completion):
"""Checks whether given completion is callable (e.x. function)"""
completion_end = completion[-1]
return completion_end in CHARACTER_PAIR_MAP

def append_closing_character(self, completion):
"""Appends closing character/bracket to the completion"""
completion_end = completion[-1]
if completion_end in CHARACTER_PAIR_MAP:
completion = f"{completion}{CHARACTER_PAIR_MAP[completion_end]}"
return completion

def on_control_d(self):
if self.current_line == "":
Expand Down Expand Up @@ -1060,7 +1151,7 @@ def toggle_file_watch(self):
)

# Handler Helpers
def add_normal_character(self, char):
def add_normal_character(self, char, narrow_search=True):
if len(char) > 1 or is_nop(char):
return
if self.incr_search_mode:
Expand All @@ -1076,12 +1167,18 @@ def add_normal_character(self, char):
reset_rl_history=False,
clear_special_mode=False,
)
self.cursor_offset += 1
if narrow_search:
self.cursor_offset += 1
else:
self._cursor_offset += 1
if self.config.cli_trim_prompts and self.current_line.startswith(
self.ps1
):
self.current_line = self.current_line[4:]
self.cursor_offset = max(0, self.cursor_offset - 4)
if narrow_search:
self.cursor_offset = max(0, self.cursor_offset - 4)
else:
self._cursor_offset += max(0, self.cursor_offset - 4)

def add_to_incremental_search(self, char=None, backspace=False):
"""Modify the current search term while in incremental search.
Expand Down
23 changes: 23 additions & 0 deletions bpython/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class LinePart(NamedTuple):


_current_word_re = LazyReCompile(r"(?<![)\]\w_.])" r"([\w_][\w0-9._]*[(]?)")
CHARACTER_PAIR_MAP = {"(": ")", "{": "}", "[": "]", "'": "'", '"': '"'}


def current_word(cursor_offset: int, line: str) -> Optional[LinePart]:
Expand Down Expand Up @@ -287,3 +288,25 @@ def current_expression_attribute(
if m.start(1) <= cursor_offset <= m.end(1):
return LinePart(m.start(1), m.end(1), m.group(1))
return None


def cursor_on_closing_char_pair(cursor_offset, line, ch=None):
"""Checks if cursor sits on closing character of a pair
and whether its pair character is directly behind it
"""
on_closing_char, pair_close = False, False
if line is None:
return on_closing_char, pair_close
if cursor_offset < len(line):
cur_char = line[cursor_offset]
if cur_char in CHARACTER_PAIR_MAP.values():
on_closing_char = True if not ch else cur_char == ch
if cursor_offset > 0:
prev_char = line[cursor_offset - 1]
if (
on_closing_char
and prev_char in CHARACTER_PAIR_MAP
and CHARACTER_PAIR_MAP[prev_char] == cur_char
):
pair_close = True if not ch else prev_char == ch
return on_closing_char, pair_close
2 changes: 2 additions & 0 deletions bpython/sample-config
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@

# Enable autoreload feature by default (default: False).
# default_autoreload = False
# Enable autocompletion of brackets and quotes (default: False)
# brackets_completion = False

[keyboard]

Expand Down
Loading