4646 Interp ,
4747 code_finished_will_parse ,
4848)
49- from .manual_readline import edit_keys
49+ from .manual_readline import edit_keys , cursor_on_closing_char_pair
5050from .parse import parse as bpythonparse , func_for_letter , color_for_letter
5151from .preprocess import preprocess
5252from .. import __version__
5858 SourceNotFound ,
5959)
6060from ..translations import _
61+ from ..line import CHARACTER_PAIR_MAP
6162
6263logger = logging .getLogger (__name__ )
6364
@@ -800,9 +801,74 @@ def process_key_event(self, e: str) -> None:
800801 self .incr_search_mode = None
801802 elif e in ("<SPACE>" ,):
802803 self .add_normal_character (" " )
804+ elif e in CHARACTER_PAIR_MAP .keys ():
805+ if e in ["'" , '"' ]:
806+ if self .is_closing_quote (e ):
807+ self .insert_char_pair_end (e )
808+ else :
809+ self .insert_char_pair_start (e )
810+ else :
811+ self .insert_char_pair_start (e )
812+ elif e in CHARACTER_PAIR_MAP .values ():
813+ self .insert_char_pair_end (e )
803814 else :
804815 self .add_normal_character (e )
805816
817+ def is_closing_quote (self , e ):
818+ char_count = self ._current_line .count (e )
819+ if (
820+ char_count % 2 == 0
821+ and cursor_on_closing_char_pair (
822+ self ._cursor_offset , self ._current_line , e
823+ )[0 ]
824+ ):
825+ return True
826+ return False
827+
828+ def insert_char_pair_start (self , e ):
829+ """Accepts character which is a part of CHARACTER_PAIR_MAP
830+ like brackets and quotes, and appends it to the line with
831+ an appropriate character pair ending. Closing character can only be inserted
832+ when the next character is either a closing character or a space
833+
834+ e.x. if you type "(" (lparen) , this will insert "()"
835+ into the line
836+ """
837+ self .add_normal_character (e )
838+ if self .config .brackets_completion :
839+ allowed_chars = ["}" , ")" , "]" , " " ]
840+ start_of_line = len (self ._current_line ) == 1
841+ end_of_line = len (self ._current_line ) == self ._cursor_offset
842+ can_lookup_next = len (self ._current_line ) > self ._cursor_offset
843+ next_char = (
844+ None
845+ if not can_lookup_next
846+ else self ._current_line [self ._cursor_offset ]
847+ )
848+ next_char_allowed = next_char in allowed_chars
849+ if start_of_line or end_of_line or next_char_allowed :
850+ closing_char = CHARACTER_PAIR_MAP [e ]
851+ self .add_normal_character (closing_char , narrow_search = False )
852+ self ._cursor_offset -= 1
853+
854+ def insert_char_pair_end (self , e ):
855+ """Accepts character which is a part of CHARACTER_PAIR_MAP
856+ like brackets and quotes, and checks whether it should be
857+ inserted to the line or overwritten
858+
859+ e.x. if you type ")" (rparen) , and your cursor is directly
860+ above another ")" (rparen) in the cmd, this will just skip
861+ it and move the cursor.
862+ If there is no same character underneath the cursor, the
863+ character will be printed/appended to the line
864+ """
865+ if self .config .brackets_completion :
866+ if self .cursor_offset < len (self ._current_line ):
867+ if self ._current_line [self .cursor_offset ] == e :
868+ self .cursor_offset += 1
869+ return
870+ self .add_normal_character (e )
871+
806872 def get_last_word (self ):
807873
808874 previous_word = _last_word (self .rl_history .entry )
@@ -903,7 +969,15 @@ def only_whitespace_left_of_cursor():
903969 for unused in range (to_add ):
904970 self .add_normal_character (" " )
905971 return
906-
972+ # if cursor on closing character from pair,
973+ # moves cursor behind it on tab
974+ # ? should we leave it here as default?
975+ if self .config .brackets_completion :
976+ on_closing_char , _ = cursor_on_closing_char_pair (
977+ self ._cursor_offset , self ._current_line
978+ )
979+ if on_closing_char :
980+ self ._cursor_offset += 1
907981 # run complete() if we don't already have matches
908982 if len (self .matches_iter .matches ) == 0 :
909983 self .list_win_visible = self .complete (tab = True )
@@ -915,7 +989,6 @@ def only_whitespace_left_of_cursor():
915989 # using _current_line so we don't trigger a completion reset
916990 if not self .matches_iter .matches :
917991 self .list_win_visible = self .complete ()
918-
919992 elif self .matches_iter .matches :
920993 self .current_match = (
921994 back and self .matches_iter .previous () or next (self .matches_iter )
@@ -924,6 +997,24 @@ def only_whitespace_left_of_cursor():
924997 self ._cursor_offset , self ._current_line = cursor_and_line
925998 # using _current_line so we don't trigger a completion reset
926999 self .list_win_visible = True
1000+ if self .config .brackets_completion :
1001+ # appends closing char pair if completion is a callable
1002+ if self .is_completion_callable (self ._current_line ):
1003+ self ._current_line = self .append_closing_character (
1004+ self ._current_line
1005+ )
1006+
1007+ def is_completion_callable (self , completion ):
1008+ """Checks whether given completion is callable (e.x. function)"""
1009+ completion_end = completion [- 1 ]
1010+ return completion_end in CHARACTER_PAIR_MAP
1011+
1012+ def append_closing_character (self , completion ):
1013+ """Appends closing character/bracket to the completion"""
1014+ completion_end = completion [- 1 ]
1015+ if completion_end in CHARACTER_PAIR_MAP :
1016+ completion = f"{ completion } { CHARACTER_PAIR_MAP [completion_end ]} "
1017+ return completion
9271018
9281019 def on_control_d (self ):
9291020 if self .current_line == "" :
@@ -1071,7 +1162,7 @@ def toggle_file_watch(self):
10711162 )
10721163
10731164 # Handler Helpers
1074- def add_normal_character (self , char ):
1165+ def add_normal_character (self , char , narrow_search = True ):
10751166 if len (char ) > 1 or is_nop (char ):
10761167 return
10771168 if self .incr_search_mode :
@@ -1087,12 +1178,18 @@ def add_normal_character(self, char):
10871178 reset_rl_history = False ,
10881179 clear_special_mode = False ,
10891180 )
1090- self .cursor_offset += 1
1181+ if narrow_search :
1182+ self .cursor_offset += 1
1183+ else :
1184+ self ._cursor_offset += 1
10911185 if self .config .cli_trim_prompts and self .current_line .startswith (
10921186 self .ps1
10931187 ):
10941188 self .current_line = self .current_line [4 :]
1095- self .cursor_offset = max (0 , self .cursor_offset - 4 )
1189+ if narrow_search :
1190+ self .cursor_offset = max (0 , self .cursor_offset - 4 )
1191+ else :
1192+ self ._cursor_offset += max (0 , self .cursor_offset - 4 )
10961193
10971194 def add_to_incremental_search (self , char = None , backspace = False ):
10981195 """Modify the current search term while in incremental search.
0 commit comments