@@ -467,8 +467,10 @@ def __init__(
467467 # An optional hint which prints above tab completion suggestions
468468 self .completion_hint = ''
469469
470- # Header which prints above CompletionItem tables
471- self .completion_header = ''
470+ # Already formatted completion results. If this is populated, then cmd2 will print it instead
471+ # of using readline's columnized results. ANSI style sequences and newlines in tab completion
472+ # results are supported by this member. ArgparseCompleter uses this to print tab completion tables.
473+ self .formatted_completions = ''
472474
473475 # Used by complete() for readline tab completion
474476 self .completion_matches = []
@@ -1118,7 +1120,7 @@ def _reset_completion_defaults(self) -> None:
11181120 self .allow_appended_space = True
11191121 self .allow_closing_quote = True
11201122 self .completion_hint = ''
1121- self .completion_header = ''
1123+ self .formatted_completions = ''
11221124 self .completion_matches = []
11231125 self .display_matches = []
11241126 self .matches_delimited = False
@@ -1645,22 +1647,6 @@ def _pad_matches_to_display(matches_to_display: List[str]) -> Tuple[List[str], i
16451647
16461648 return [cur_match + padding for cur_match in matches_to_display ], len (padding )
16471649
1648- def _build_completion_metadata_string (self ) -> str : # pragma: no cover
1649- """Build completion metadata string which can contain a hint and CompletionItem table header"""
1650- metadata = ''
1651-
1652- # Add hint if one exists and we are supposed to display it
1653- if self .always_show_hint and self .completion_hint :
1654- metadata += '\n ' + self .completion_hint
1655-
1656- # Add table header if one exists
1657- if self .completion_header :
1658- if not metadata :
1659- metadata += '\n '
1660- metadata += '\n ' + self .completion_header
1661-
1662- return metadata
1663-
16641650 def _display_matches_gnu_readline (
16651651 self , substitution : str , matches : List [str ], longest_match_length : int
16661652 ) -> None : # pragma: no cover
@@ -1673,45 +1659,55 @@ def _display_matches_gnu_readline(
16731659 """
16741660 if rl_type == RlType .GNU :
16751661
1676- # Check if we should show display_matches
1677- if self .display_matches :
1678- matches_to_display = self .display_matches
1662+ # Print hint if one exists and we are supposed to display it
1663+ hint_printed = False
1664+ if self .always_show_hint and self .completion_hint :
1665+ hint_printed = True
1666+ sys .stdout .write ('\n ' + self .completion_hint )
16791667
1680- # Recalculate longest_match_length for display_matches
1681- longest_match_length = 0
1668+ # Check if we already have formatted results to print
1669+ if self .formatted_completions :
1670+ if not hint_printed :
1671+ sys .stdout .write ('\n ' )
1672+ sys .stdout .write ('\n ' + self .formatted_completions + '\n \n ' )
16821673
1683- for cur_match in matches_to_display :
1684- cur_length = ansi .style_aware_wcswidth (cur_match )
1685- if cur_length > longest_match_length :
1686- longest_match_length = cur_length
1674+ # Otherwise use readline's formatter
16871675 else :
1688- matches_to_display = matches
1676+ # Check if we should show display_matches
1677+ if self .display_matches :
1678+ matches_to_display = self .display_matches
16891679
1690- # Add padding for visual appeal
1691- matches_to_display , padding_length = self ._pad_matches_to_display (matches_to_display )
1692- longest_match_length += padding_length
1680+ # Recalculate longest_match_length for display_matches
1681+ longest_match_length = 0
16931682
1694- # We will use readline's display function (rl_display_match_list()), so we
1695- # need to encode our string as bytes to place in a C array.
1696- encoded_substitution = bytes (substitution , encoding = 'utf-8' )
1697- encoded_matches = [bytes (cur_match , encoding = 'utf-8' ) for cur_match in matches_to_display ]
1683+ for cur_match in matches_to_display :
1684+ cur_length = ansi .style_aware_wcswidth (cur_match )
1685+ if cur_length > longest_match_length :
1686+ longest_match_length = cur_length
1687+ else :
1688+ matches_to_display = matches
1689+
1690+ # Add padding for visual appeal
1691+ matches_to_display , padding_length = self ._pad_matches_to_display (matches_to_display )
1692+ longest_match_length += padding_length
16981693
1699- # rl_display_match_list() expects matches to be in argv format where
1700- # substitution is the first element, followed by the matches, and then a NULL .
1701- # noinspection PyCallingNonCallable,PyTypeChecker
1702- strings_array = ( ctypes . c_char_p * ( 1 + len ( encoded_matches ) + 1 ))()
1694+ # We will use readline's display function (rl_display_match_list()), so we
1695+ # need to encode our string as bytes to place in a C array .
1696+ encoded_substitution = bytes ( substitution , encoding = 'utf-8' )
1697+ encoded_matches = [ bytes ( cur_match , encoding = 'utf-8' ) for cur_match in matches_to_display ]
17031698
1704- # Copy in the encoded strings and add a NULL to the end
1705- strings_array [ 0 ] = encoded_substitution
1706- strings_array [ 1 : - 1 ] = encoded_matches
1707- strings_array [ - 1 ] = None
1699+ # rl_display_match_list() expects matches to be in argv format where
1700+ # substitution is the first element, followed by the matches, and then a NULL.
1701+ # noinspection PyCallingNonCallable,PyTypeChecker
1702+ strings_array = ( ctypes . c_char_p * ( 1 + len ( encoded_matches ) + 1 ))()
17081703
1709- # Print any metadata like a hint or table header
1710- sys .stdout .write (self ._build_completion_metadata_string ())
1704+ # Copy in the encoded strings and add a NULL to the end
1705+ strings_array [0 ] = encoded_substitution
1706+ strings_array [1 :- 1 ] = encoded_matches
1707+ strings_array [- 1 ] = None
17111708
1712- # Call readline's display function
1713- # rl_display_match_list(strings_array, number of completion matches, longest match length)
1714- readline_lib .rl_display_match_list (strings_array , len (encoded_matches ), longest_match_length )
1709+ # rl_display_match_list(strings_array, number of completion matches, longest match length)
1710+ readline_lib .rl_display_match_list (strings_array , len (encoded_matches ), longest_match_length )
17151711
17161712 # Redraw prompt and input line
17171713 rl_force_redisplay ()
@@ -1724,20 +1720,34 @@ def _display_matches_pyreadline(self, matches: List[str]) -> None: # pragma: no
17241720 """
17251721 if rl_type == RlType .PYREADLINE :
17261722
1727- # Check if we should show display_matches
1728- if self . display_matches :
1729- matches_to_display = self .display_matches
1730- else :
1731- matches_to_display = matches
1723+ # Print hint if one exists and we are supposed to display it
1724+ hint_printed = False
1725+ if self . always_show_hint and self .completion_hint :
1726+ hint_printed = True
1727+ readline . rl . mode . console . write ( ' \n ' + self . completion_hint )
17321728
1733- # Add padding for visual appeal
1734- matches_to_display , _ = self ._pad_matches_to_display (matches_to_display )
1729+ # Check if we already have formatted results to print
1730+ if self .formatted_completions :
1731+ if not hint_printed :
1732+ readline .rl .mode .console .write ('\n ' )
1733+ readline .rl .mode .console .write ('\n ' + self .formatted_completions + '\n \n ' )
1734+
1735+ # Redraw the prompt and input lines
1736+ rl_force_redisplay ()
1737+
1738+ # Otherwise use pyreadline's formatter
1739+ else :
1740+ # Check if we should show display_matches
1741+ if self .display_matches :
1742+ matches_to_display = self .display_matches
1743+ else :
1744+ matches_to_display = matches
17351745
1736- # Print any metadata like a hint or table header
1737- readline . rl . mode . console . write ( self ._build_completion_metadata_string () )
1746+ # Add padding for visual appeal
1747+ matches_to_display , _ = self ._pad_matches_to_display ( matches_to_display )
17381748
1739- # Display matches using actual display function. This also redraws the prompt and line .
1740- orig_pyreadline_display (matches_to_display )
1749+ # Display matches using actual display function. This also redraws the prompt and input lines .
1750+ orig_pyreadline_display (matches_to_display )
17411751
17421752 def _perform_completion (
17431753 self , text : str , line : str , begidx : int , endidx : int , custom_settings : Optional [utils .CustomCompletionSettings ] = None
0 commit comments