diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-04-17 01:23:07 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-04-17 01:23:07 -0400 |
commit | 9e24c983806c3cb6ff0722f5f2314ff56de2933c (patch) | |
tree | 36f24729faefb958ee84c1738bfa1a1d3ace748e | |
parent | 07c283eb484c9f2513f23594b364ad331b2b0e81 (diff) | |
download | cmd2-git-9e24c983806c3cb6ff0722f5f2314ff56de2933c.tar.gz |
Added common file to provide readline utility functions
-rwxr-xr-x | AutoCompleter.py | 5 | ||||
-rwxr-xr-x | cmd2.py | 81 | ||||
-rw-r--r-- | rl_utils.py | 61 |
3 files changed, 90 insertions, 57 deletions
diff --git a/AutoCompleter.py b/AutoCompleter.py index 83228b3b..f188a46e 100755 --- a/AutoCompleter.py +++ b/AutoCompleter.py @@ -3,6 +3,7 @@ import argparse import re as _re import sys from argparse import OPTIONAL, ZERO_OR_MORE, ONE_OR_MORE, REMAINDER, PARSER, ArgumentError, _ +from rl_utils import rl_force_redisplay try: from typing import List, Dict, Tuple, Callable, Union except: @@ -488,8 +489,8 @@ class AutoCompleter(object): out_str += '\n{0: <{width}}'.format('', width=pref_len).join(help_lines) print('\nHint:' + out_str + '\n') - from cmd2 import readline_lib - readline_lib.rl_forced_update_display() + # Redraw prompt and input line + rl_force_redisplay() # noinspection PyUnusedLocal @staticmethod @@ -45,21 +45,38 @@ import traceback import unittest from code import InteractiveConsole -try: - from enum34 import Enum -except ImportError: - from enum import Enum - import pyparsing import pyperclip +# Set up readline +from rl_utils import rl_force_redisplay, readline, rl_type, RlType + +if rl_type == RlType.PYREADLINE: + + # Save the original pyreadline display completion function since we need to override it and restore it + # noinspection PyProtectedMember + orig_pyreadline_display = readline.rl.mode._display_completions + +elif rl_type == RlType.GNU: + + # We need wcswidth to calculate display width of tab completions + from wcwidth import wcswidth + + # Get the readline lib so we can make changes to it + import ctypes + from rl_utils import readline_lib + + # Save address that rl_basic_quote_characters is pointing to since we need to override and restore it + rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters") + orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value + # Newer versions of pyperclip are released as a single file, but older versions had a more complicated structure try: from pyperclip.exceptions import PyperclipException except ImportError: # noinspection PyUnresolvedReferences from pyperclip import PyperclipException - + # Collection is a container that is sizable and iterable # It was introduced in Python 3.6. We will try to import it, otherwise use our implementation try: @@ -96,47 +113,6 @@ try: except ImportError: ipython_available = False -# Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit) -try: - import gnureadline as readline -except ImportError: - # Try to import readline, but allow failure for convenience in Windows unit testing - # Note: If this actually fails, you should install readline on Linux or Mac or pyreadline on Windows - try: - # noinspection PyUnresolvedReferences - import readline - except ImportError: - pass - -# Check what implementation of readline we are using -class RlType(Enum): - GNU = 1 - PYREADLINE = 2 - NONE = 3 - -rl_type = RlType.NONE - -if 'pyreadline' in sys.modules: - rl_type = RlType.PYREADLINE - - # Save the original pyreadline display completion function since we need to override it and restore it - # noinspection PyProtectedMember - orig_pyreadline_display = readline.rl.mode._display_completions - -elif 'gnureadline' in sys.modules or 'readline' in sys.modules: - rl_type = RlType.GNU - - # We need wcswidth to calculate display width of tab completions - from wcwidth import wcswidth - - # Load the readline lib so we can make changes to it - import ctypes - readline_lib = ctypes.CDLL(readline.__file__) - - # Save address that rl_basic_quote_characters is pointing to since we need to override and restore it - rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters") - orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value - __version__ = '0.9.0' # Pyparsing enablePackrat() can greatly speed up parsing, but problems have been seen in Python 3 in the past @@ -1669,13 +1645,8 @@ class Cmd(cmd.Cmd): # rl_display_match_list(strings_array, number of completion matches, longest match length) readline_lib.rl_display_match_list(strings_array, len(encoded_matches), longest_match_length) - # rl_forced_update_display() is the proper way to redraw the prompt and line, but we - # have to use ctypes to do it since Python's readline API does not wrap the function - readline_lib.rl_forced_update_display() - - # Since we updated the display, readline asks that rl_display_fixed be set for efficiency - display_fixed = ctypes.c_int.in_dll(readline_lib, "rl_display_fixed") - display_fixed.value = 1 + # Redraw prompt and input line + rl_force_redisplay() def _display_matches_pyreadline(self, matches): """ @@ -1695,7 +1666,7 @@ class Cmd(cmd.Cmd): # Add padding for visual appeal matches_to_display, _ = self._pad_matches_to_display(matches_to_display) - # Display the matches + # Display matches using actual display function. This also redraws the prompt and line. orig_pyreadline_display(matches_to_display) # ----- Methods which override stuff in cmd ----- diff --git a/rl_utils.py b/rl_utils.py new file mode 100644 index 00000000..11c45ee4 --- /dev/null +++ b/rl_utils.py @@ -0,0 +1,61 @@ +# coding=utf-8 +""" +Imports the proper readline for the platform and provides utility functions for it +""" +import sys + +try: + from enum34 import Enum +except ImportError: + from enum import Enum + +# Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit) +try: + import gnureadline as readline +except ImportError: + # Try to import readline, but allow failure for convenience in Windows unit testing + # Note: If this actually fails, you should install readline on Linux or Mac or pyreadline on Windows + try: + # noinspection PyUnresolvedReferences + import readline + except ImportError: + pass + + +# Check what implementation of readline we are using +class RlType(Enum): + GNU = 1 + PYREADLINE = 2 + NONE = 3 + + +rl_type = RlType.NONE + +# The order of this check matters since importing pyreadline will also show readline in the modules list +if 'pyreadline' in sys.modules: + rl_type = RlType.PYREADLINE + +elif 'gnureadline' in sys.modules or 'readline' in sys.modules: + rl_type = RlType.GNU + + # Load the readline lib so we can access members of it + import ctypes + readline_lib = ctypes.CDLL(readline.__file__) + + +def rl_force_redisplay() -> None: + """ + Causes readline to redraw prompt and input line + """ + if rl_type == RlType.GNU: + # rl_forced_update_display() is the proper way to redraw the prompt and line, but we + # have to use ctypes to do it since Python's readline API does not wrap the function + readline_lib.rl_forced_update_display() + + # After manually updating the display, readline asks that rl_display_fixed be set to 1 for efficiency + display_fixed = ctypes.c_int.in_dll(readline_lib, "rl_display_fixed") + display_fixed.value = 1 + + elif rl_type == RlType.PYREADLINE: + # noinspection PyProtectedMember + readline.rl.mode._print_prompt() |