From 69d60a52faff6f6d683ff773b40f883716a68f4e Mon Sep 17 00:00:00 2001 From: asm582 Date: Sun, 5 Apr 2015 20:43:06 -0400 Subject: [PATCH] Removed Dos style endings --- modules/clearinghouse/__init__.py | 18 +- modules/factoids/__init__.py | 378 ++-- modules/factoids/factoid.txt | 12 +- modules/modules/__init__.py | 316 ++-- modules/modules/command_callbacks.py | 366 ++-- pyreadline/__init__.py | 22 +- pyreadline/clipboard/__init__.py | 144 +- pyreadline/clipboard/ironpython_clipboard.py | 54 +- pyreadline/clipboard/no_clipboard.py | 36 +- pyreadline/clipboard/win32_clipboard.py | 216 +-- pyreadline/console/__init__.py | 44 +- pyreadline/console/ansi.py | 380 ++-- pyreadline/console/console.py | 1680 +++++++++--------- pyreadline/console/console_attributes.py | 32 +- pyreadline/console/consolebase.py | 102 +- pyreadline/console/event.py | 56 +- pyreadline/console/ironpython_console.py | 848 ++++----- pyreadline/error.py | 28 +- pyreadline/get_doc.py | 34 +- pyreadline/keysyms/__init__.py | 38 +- pyreadline/lineeditor/history.py | 528 +++--- pyreadline/lineeditor/lineobj.py | 1598 ++++++++--------- pyreadline/lineeditor/wordmatcher.py | 204 +-- pyreadline/logger.py | 146 +- pyreadline/logserver.py | 112 +- pyreadline/modes/__init__.py | 8 +- pyreadline/modes/basemode.py | 1122 ++++++------ pyreadline/modes/emacs.py | 1460 +++++++-------- pyreadline/modes/notemacs.py | 1204 ++++++------- pyreadline/release.py | 176 +- pyreadline/unicode_helper.py | 72 +- readline_windows.py | 156 +- tests/example.1.1.repy | 10 +- tests/modules/conflict/__init__.py | 38 +- tests/ut_seash_input_showparse_hide.py | 64 +- tests/ut_seash_input_showparse_show.py | 40 +- tests/ut_seash_moduleconflicterror.py | 34 +- 37 files changed, 5888 insertions(+), 5888 deletions(-) diff --git a/modules/clearinghouse/__init__.py b/modules/clearinghouse/__init__.py index dd0c344..f612dc2 100644 --- a/modules/clearinghouse/__init__.py +++ b/modules/clearinghouse/__init__.py @@ -1,10 +1,10 @@ -""" - - clearinghouse/__init__.py - - - Needed so that python can import this as a package. - -""" - +""" + + clearinghouse/__init__.py + + + Needed so that python can import this as a package. + +""" + from clearinghouse import moduledata \ No newline at end of file diff --git a/modules/factoids/__init__.py b/modules/factoids/__init__.py index 4b4ee07..7b4bc61 100644 --- a/modules/factoids/__init__.py +++ b/modules/factoids/__init__.py @@ -1,190 +1,190 @@ -""" - - factoids/__init__.py - - - Used to print saesh factoids. - - It implements the following command: - show factoids [number of factoids]/all - -""" - -import seash_exceptions - -import random -import os - - -# List which will contain all factoids after fatching from a file. -factoids = [] - - -def initialize(): - """ - - Used to print random seash factoid when user runs seash. - - - None - - - Prints random factoid onto the screen. - - - UserError: Error during generating path to "factoid.txt" file or - Error while opening, reading or closing "factoid.txt" file. - - - None - """ - - # Global 'factoids' list will be used to store factoids, fetched from a file. - global factoids - - # Path to "factoid.txt" file is created. - try: - current_path = os.getcwd() - file_path = os.path.join(current_path, "modules", "factoids", "factoid.txt") - except OSError, error: - raise seash_exceptions.InitializeError("Error during initializing factoids module: '" + str(error) + "'.") - - # We have to fatch list of factoids from "factoid.txt" file. - try: - file_object = open(file_path, 'r') - factoids_temp = file_object.readlines() - file_object.close() - except IOError, error: - raise seash_exceptions.InitializeError("Error during initializing factoids module: '" + str(error) + "'.") - - # Newline characters in a list, read from a file are removed. - for factoid in factoids_temp: - factoids.append(factoid.strip('\n')) - - # A random factoid is printed every time user runs seash. - print random.choice(factoids)+"\n" - - -def cleanup(): - """ - - Used to clean 'factoids' list when this module is going to be disabled. - - - None - - - None - - - None - - - None - """ - - # Data from a global 'factoids' list will be removed. - global factoids - factoids = [] - - -def print_factoids(input_dict, environment_dict): - """ - - Used to print seash factoids when user uses 'show factoids' - command. - - - input_dict: Input dictionary generated by seash_dictionary.parse_command(). - environment_dict: Dictionary describing the current seash environment. - - For more information, see command_callbacks.py's module docstring. - - - Prints factoids onto the screen. - - - UserError: If user does not type appropriate command. - ValueError: If user does not provide valid input (integer). - - - None - """ - - # User will insert an argument regarding how many factoids should be printed. - # We have to find what is user argument. - # User can type any positive number or he can type 'all' to see all factoids. - dict_mark = input_dict - try: - command = dict_mark.keys()[0] - while dict_mark[command]['name'] != 'args': - dict_mark = dict_mark[command]['children'] - command = dict_mark.keys()[0] - args = command - except IndexError: - raise seash_exceptions.UserError("\nError, Syntax of the command is: show factoids [number of factoids]/all \n") - - # User decided to print all factoids - if args == 'all': - print - for factoid in factoids: - print factoid - print - return - - # User can not insert other than integer number. - try: - no_of_factoids = int(args) - except ValueError: - raise seash_exceptions.UserError("\nYou have to enter number only.\n") - - # If number of factoids decided by user is greater than total number of - # available factoids than whole factoids list is printed. - if (no_of_factoids > (len(factoids))): - print "\nWe have only %d factoids. Here is the list of factoids:" % (len(factoids)) - no_of_factoids = len(factoids) - elif (no_of_factoids <= 0): - raise seash_exceptions.UserError("\nYou have to enter positive number only.\n") - - # 'factoids' list will be shuffled every time for printing random factoids. - random.shuffle(factoids) - - # Factoids will be printed. - for factoid in factoids[:no_of_factoids]: - print factoid - print - - - - -command_dict = { - 'show': {'name':'show', 'callback': None, 'children':{ - 'factoids':{'name':'factoids', 'callback': print_factoids, - 'summary': "Displays available seash factoids.", - 'help_text': '','children':{ - '[ARGUMENT]':{'name':'args', 'callback': None, 'children':{}} - }},}} -} - - - -help_text = """ -Factoids Module - -This module includes command that prints seash factoids. -'show factoids [number of factoids]/all' is used to print -available seash factoids. -You can type 'show factoids [number of factoids]' to print -that much number of factoids. -You can type 'show factoids all' to see all available factoids. -""" - - -# This is where the module importer loads the module from. -moduledata = { - 'command_dict': command_dict, - 'help_text': help_text, - 'url': None, - 'initialize': initialize, - 'cleanup': cleanup +""" + + factoids/__init__.py + + + Used to print saesh factoids. + + It implements the following command: + show factoids [number of factoids]/all + +""" + +import seash_exceptions + +import random +import os + + +# List which will contain all factoids after fatching from a file. +factoids = [] + + +def initialize(): + """ + + Used to print random seash factoid when user runs seash. + + + None + + + Prints random factoid onto the screen. + + + UserError: Error during generating path to "factoid.txt" file or + Error while opening, reading or closing "factoid.txt" file. + + + None + """ + + # Global 'factoids' list will be used to store factoids, fetched from a file. + global factoids + + # Path to "factoid.txt" file is created. + try: + current_path = os.getcwd() + file_path = os.path.join(current_path, "modules", "factoids", "factoid.txt") + except OSError, error: + raise seash_exceptions.InitializeError("Error during initializing factoids module: '" + str(error) + "'.") + + # We have to fatch list of factoids from "factoid.txt" file. + try: + file_object = open(file_path, 'r') + factoids_temp = file_object.readlines() + file_object.close() + except IOError, error: + raise seash_exceptions.InitializeError("Error during initializing factoids module: '" + str(error) + "'.") + + # Newline characters in a list, read from a file are removed. + for factoid in factoids_temp: + factoids.append(factoid.strip('\n')) + + # A random factoid is printed every time user runs seash. + print random.choice(factoids)+"\n" + + +def cleanup(): + """ + + Used to clean 'factoids' list when this module is going to be disabled. + + + None + + + None + + + None + + + None + """ + + # Data from a global 'factoids' list will be removed. + global factoids + factoids = [] + + +def print_factoids(input_dict, environment_dict): + """ + + Used to print seash factoids when user uses 'show factoids' + command. + + + input_dict: Input dictionary generated by seash_dictionary.parse_command(). + environment_dict: Dictionary describing the current seash environment. + + For more information, see command_callbacks.py's module docstring. + + + Prints factoids onto the screen. + + + UserError: If user does not type appropriate command. + ValueError: If user does not provide valid input (integer). + + + None + """ + + # User will insert an argument regarding how many factoids should be printed. + # We have to find what is user argument. + # User can type any positive number or he can type 'all' to see all factoids. + dict_mark = input_dict + try: + command = dict_mark.keys()[0] + while dict_mark[command]['name'] != 'args': + dict_mark = dict_mark[command]['children'] + command = dict_mark.keys()[0] + args = command + except IndexError: + raise seash_exceptions.UserError("\nError, Syntax of the command is: show factoids [number of factoids]/all \n") + + # User decided to print all factoids + if args == 'all': + print + for factoid in factoids: + print factoid + print + return + + # User can not insert other than integer number. + try: + no_of_factoids = int(args) + except ValueError: + raise seash_exceptions.UserError("\nYou have to enter number only.\n") + + # If number of factoids decided by user is greater than total number of + # available factoids than whole factoids list is printed. + if (no_of_factoids > (len(factoids))): + print "\nWe have only %d factoids. Here is the list of factoids:" % (len(factoids)) + no_of_factoids = len(factoids) + elif (no_of_factoids <= 0): + raise seash_exceptions.UserError("\nYou have to enter positive number only.\n") + + # 'factoids' list will be shuffled every time for printing random factoids. + random.shuffle(factoids) + + # Factoids will be printed. + for factoid in factoids[:no_of_factoids]: + print factoid + print + + + + +command_dict = { + 'show': {'name':'show', 'callback': None, 'children':{ + 'factoids':{'name':'factoids', 'callback': print_factoids, + 'summary': "Displays available seash factoids.", + 'help_text': '','children':{ + '[ARGUMENT]':{'name':'args', 'callback': None, 'children':{}} + }},}} +} + + + +help_text = """ +Factoids Module + +This module includes command that prints seash factoids. +'show factoids [number of factoids]/all' is used to print +available seash factoids. +You can type 'show factoids [number of factoids]' to print +that much number of factoids. +You can type 'show factoids all' to see all available factoids. +""" + + +# This is where the module importer loads the module from. +moduledata = { + 'command_dict': command_dict, + 'help_text': help_text, + 'url': None, + 'initialize': initialize, + 'cleanup': cleanup } \ No newline at end of file diff --git a/modules/factoids/factoid.txt b/modules/factoids/factoid.txt index 5575a42..ffed7a1 100644 --- a/modules/factoids/factoid.txt +++ b/modules/factoids/factoid.txt @@ -1,6 +1,6 @@ -- You can save seash's current state using 'savestate' command. -- You can load seash's previous state using 'loadstate' command. -- You can set user variables using 'set' command. -- You can press the Tab key to enable tab complete when typing commands. -- You can see available seash factoids using 'show factoids [number of factoids]/all' command. -- You can list all available seash modules using 'show modules' command. +- You can save seash's current state using 'savestate' command. +- You can load seash's previous state using 'loadstate' command. +- You can set user variables using 'set' command. +- You can press the Tab key to enable tab complete when typing commands. +- You can see available seash factoids using 'show factoids [number of factoids]/all' command. +- You can list all available seash modules using 'show modules' command. diff --git a/modules/modules/__init__.py b/modules/modules/__init__.py index 3ac7c66..ecfe466 100644 --- a/modules/modules/__init__.py +++ b/modules/modules/__init__.py @@ -1,159 +1,159 @@ -""" - - modules/__init__.py - - - Provides module manipulation functionality for seash. - - module_level_help contains instructions on how to use this module from the - seash command line. - -""" - - -import seash_modules -import seash_dictionary -import seash_helper -import seash_exceptions - -# This is where the module callbacks are defined -import command_callbacks - - -def autocomplete(input_list): - """ - - Returns all valid input completions for the specified command line input. - - - input_list: A list of tokens. - - - None - - - None - - - A list of strings representing valid completions. - """ - if input_list[0] in ['modulehelp', 'enable', 'disable']: - commands = [] - for modulename in seash_modules.module_data.keys(): - commands.append(input_list[0] + ' ' + modulename) - return commands - return [] - - - -module_level_help = """ -Modules Module - -This module contains commands that pertain to the manipulation of modules. -You can enable/disable modules that are used within seash from here. - -To enable a module, use the 'enable' command: - user@ !> enable variables - -To disable a module, use the 'disable' command. - user@ !> disable variables - -You can also view module-level help: - user@ !> modulehelp variables - -And finally, view information on all modules you currently have installed: - user@ !> show modules - -""" - - -list_all_modules_helptext = """ -show modules - -Shows information on all installed modules. Modules will be separated into -two categories: enabled and disabled. Each entry will be in the following -format: - [module name] - [URL where the module was installed from, if available] - -The URL will be missing if the module was installed manually. - - -user@ !> show modules -Enabled Modules: -geoip - -Installed Modules: -geoip - Install URL not available -selexor - https://seattle.poly.edu/plugins/selexor/ -""" - -enable_helptext = """ -enable [modulename] - -Enables use of the specified module. You can only enable modules if they do -not contain commands that conflict with existing commands. - -user@ !> enable modulename -user@ !> enable modulename -Module 'modulename' is already enabled. -user@ !> enable conflictingmodule -Module 'conflictingmodule' cannot be enabled due to these conflicting commands: -show info (default) -get (selexor) -""" - -disable_helptext = """ -disable [modulename] - -Disables the specified module. You will no longer be able to access the -commands that were found in the disabled module until the module is re-enabled. - -Important note: You cannot disable this module through the disable command. - -user@ !> disable modulename -user@ !> disable modulename -Module 'modulename' is not enabled. - -""" - -modulehelp_helptext = """ -help module [modulename] - -Displays the module-level help for a particular module. The module must -already be installed. - -""" - -command_dict = { - # show modules - 'show': {'children': { - 'modules': { 'name': 'modules', 'callback': command_callbacks.list_all_modules, - 'summary': "Shows basic information about all installed modules.", - 'help_text': list_all_modules_helptext, 'children': {}}, }}, - - # enable modulename - 'enable':{ 'name':'enable', 'callback': command_callbacks.enable_module, - 'summary': "Enables an installed module", 'example': "[modulename]", - 'help_text': enable_helptext, 'children': { - '[ARGUMENT]': { 'name':'modulename', 'callback': None, 'children':{}},}}, - - # disable modulename - 'disable': { 'name':'disable', 'callback': command_callbacks.disable_module, - 'summary': "Disables an enabled module", 'example': "[modulename]", - 'help_text': disable_helptext, 'children': { - '[ARGUMENT]': { 'name':'modulename', 'callback': None, 'children':{}},}}, - - # modulehelp modulename - 'modulehelp': { 'name': 'modulehelp', 'callback': command_callbacks.print_module_help, - 'summary': "Shows the module-level helptext for a particular module", - 'example': '[modulename]', 'help_text': modulehelp_helptext, - 'children': { - '[ARGUMENT]':{ 'name':'modulename', 'callback': None, 'children':{}},}}, -} - -moduledata = { - 'command_dict': command_dict, - 'help_text': module_level_help, - 'url': None, - 'tab_completer': autocomplete +""" + + modules/__init__.py + + + Provides module manipulation functionality for seash. + + module_level_help contains instructions on how to use this module from the + seash command line. + +""" + + +import seash_modules +import seash_dictionary +import seash_helper +import seash_exceptions + +# This is where the module callbacks are defined +import command_callbacks + + +def autocomplete(input_list): + """ + + Returns all valid input completions for the specified command line input. + + + input_list: A list of tokens. + + + None + + + None + + + A list of strings representing valid completions. + """ + if input_list[0] in ['modulehelp', 'enable', 'disable']: + commands = [] + for modulename in seash_modules.module_data.keys(): + commands.append(input_list[0] + ' ' + modulename) + return commands + return [] + + + +module_level_help = """ +Modules Module + +This module contains commands that pertain to the manipulation of modules. +You can enable/disable modules that are used within seash from here. + +To enable a module, use the 'enable' command: + user@ !> enable variables + +To disable a module, use the 'disable' command. + user@ !> disable variables + +You can also view module-level help: + user@ !> modulehelp variables + +And finally, view information on all modules you currently have installed: + user@ !> show modules + +""" + + +list_all_modules_helptext = """ +show modules + +Shows information on all installed modules. Modules will be separated into +two categories: enabled and disabled. Each entry will be in the following +format: + [module name] - [URL where the module was installed from, if available] + +The URL will be missing if the module was installed manually. + + +user@ !> show modules +Enabled Modules: +geoip + +Installed Modules: +geoip - Install URL not available +selexor - https://seattle.poly.edu/plugins/selexor/ +""" + +enable_helptext = """ +enable [modulename] + +Enables use of the specified module. You can only enable modules if they do +not contain commands that conflict with existing commands. + +user@ !> enable modulename +user@ !> enable modulename +Module 'modulename' is already enabled. +user@ !> enable conflictingmodule +Module 'conflictingmodule' cannot be enabled due to these conflicting commands: +show info (default) +get (selexor) +""" + +disable_helptext = """ +disable [modulename] + +Disables the specified module. You will no longer be able to access the +commands that were found in the disabled module until the module is re-enabled. + +Important note: You cannot disable this module through the disable command. + +user@ !> disable modulename +user@ !> disable modulename +Module 'modulename' is not enabled. + +""" + +modulehelp_helptext = """ +help module [modulename] + +Displays the module-level help for a particular module. The module must +already be installed. + +""" + +command_dict = { + # show modules + 'show': {'children': { + 'modules': { 'name': 'modules', 'callback': command_callbacks.list_all_modules, + 'summary': "Shows basic information about all installed modules.", + 'help_text': list_all_modules_helptext, 'children': {}}, }}, + + # enable modulename + 'enable':{ 'name':'enable', 'callback': command_callbacks.enable_module, + 'summary': "Enables an installed module", 'example': "[modulename]", + 'help_text': enable_helptext, 'children': { + '[ARGUMENT]': { 'name':'modulename', 'callback': None, 'children':{}},}}, + + # disable modulename + 'disable': { 'name':'disable', 'callback': command_callbacks.disable_module, + 'summary': "Disables an enabled module", 'example': "[modulename]", + 'help_text': disable_helptext, 'children': { + '[ARGUMENT]': { 'name':'modulename', 'callback': None, 'children':{}},}}, + + # modulehelp modulename + 'modulehelp': { 'name': 'modulehelp', 'callback': command_callbacks.print_module_help, + 'summary': "Shows the module-level helptext for a particular module", + 'example': '[modulename]', 'help_text': modulehelp_helptext, + 'children': { + '[ARGUMENT]':{ 'name':'modulename', 'callback': None, 'children':{}},}}, +} + +moduledata = { + 'command_dict': command_dict, + 'help_text': module_level_help, + 'url': None, + 'tab_completer': autocomplete } \ No newline at end of file diff --git a/modules/modules/command_callbacks.py b/modules/modules/command_callbacks.py index 66b9d42..d653e48 100644 --- a/modules/modules/command_callbacks.py +++ b/modules/modules/command_callbacks.py @@ -1,183 +1,183 @@ -""" - - modules/command_callbacks.py - - - Defines all the command callbacks that are used for the modules module. - - For more information on how the functions within this module are defined, - refer to the main command_callbacks.py in the same directory as seash.py. - -""" -import seash_modules -import seash_dictionary -import seash_helper -import seash_exceptions - -# show modules -def list_all_modules(input_dict, environment_dict): - """ - - Lists all installed modules and their status. - - - input_dict: The commanddict representing the user's input. - environment_dict: The dictionary representing the current seash - environment. - - - A list of all enabled modules and all installed modules will be printed to - stdout. - - - None - - - None - """ - print "Enabled Modules:" - print ", ".join(seash_modules.get_enabled_modules()) - print - print "Installed Modules:" - - # Now print the URLs... - # Output format: - # modulename - URL not available # URL is set to None - # modulename - https://seattle.poly.edu/seashplugins/modulename/ # URL is set - for module in seash_modules.module_data: - print module, '-', - if seash_modules.module_data[module]['url'] is None: - print "Install URL not available" - else: - print seash_modules.module_data[module]['url'] - print - - -# enable modulename -def enable_module(input_dict, environment_dict): - """ - - Enables an installed module. - - - input_dict: The commanddict representing the user's input. - environment_dict: The dictionary representing the current seash - environment. - - - The module specified in the 'modulename' node will be enabled. - - - UserError: input_dict did not contain a 'modulename' node. - - - None - """ - - # Get the modulename - dict_mark = input_dict - try: - command = dict_mark.keys()[0] - while dict_mark[command]['name'] != 'modulename': - dict_mark = input_dict[command]['children'] - command = dict_mark.keys()[0] - modulename = command - except IndexError: - raise seash_exceptions.UserError("Error, command requires a modulename") - - try: - seash_modules.enable(seash_dictionary.seashcommanddict, modulename) - except seash_exceptions.ModuleConflictError, e: - print "Module cannot be imported due to the following conflicting command:" - print str(e) - except seash_exceptions.InitializeError, e: - print "Error while enabling the '"+modulename+"' module." - seash_modules.disable(seash_dictionary.seashcommanddict, modulename) - - - -def disable_module(input_dict, environment_dict): - """ - - Disables an enabled module. - - - input_dict: The commanddict representing the user's input. - environment_dict: The dictionary representing the current seash - environment. - - - The module specified in the 'modulename' node will be enabled. - - - UserError: - input_dict did not contain a 'modulename' node, or the user tried to - disable this module. - - - None - """ - - # Get the modulename - dict_mark = input_dict - try: - command = dict_mark.keys()[0] - while dict_mark[command]['name'] != 'modulename': - dict_mark = input_dict[command]['children'] - command = dict_mark.keys()[0] - modulename = command - except IndexError: - raise seash_exceptions.UserError("Error, command requires a modulename") - - if modulename == 'modules': - raise seash_exceptions.UserError("Error, cannot disable the 'modules' module") - - seash_modules.disable(seash_dictionary.seashcommanddict, modulename) - - - -def print_module_help(input_dict, environment_dict): - """ - - Displays the module level help for a module. - - - input_dict: The commanddict representing the user's input. - environment_dict: The dictionary representing the current seash - environment. - - - The helptext for the module specified in the 'modulename' node will be - printed to stdout. - - - UserError: - input_dict did not contain a 'modulename' node, or the user specified - a module that is not installed. - - - None - """ - - # Get the modulename - dict_mark = input_dict - try: - command = dict_mark.keys()[0] - while dict_mark[command]['name'] != 'modulename': - dict_mark = input_dict[command]['children'] - command = dict_mark.keys()[0] - modulename = command - except IndexError: - raise seash_exceptions.UserError("Error, command requires a modulename") - - # Is this module installed? - if not modulename in seash_modules.module_data: - raise seash_exceptions.UserError("Module is not installed.") - - print seash_modules.module_data[modulename]['help_text'] - - # Now, print out all the commands under this module - print "Commands in this module:" - print '\n'.join(seash_helper.get_commands_from_commanddict(seash_modules.module_data[modulename]['command_dict'])) - - +""" + + modules/command_callbacks.py + + + Defines all the command callbacks that are used for the modules module. + + For more information on how the functions within this module are defined, + refer to the main command_callbacks.py in the same directory as seash.py. + +""" +import seash_modules +import seash_dictionary +import seash_helper +import seash_exceptions + +# show modules +def list_all_modules(input_dict, environment_dict): + """ + + Lists all installed modules and their status. + + + input_dict: The commanddict representing the user's input. + environment_dict: The dictionary representing the current seash + environment. + + + A list of all enabled modules and all installed modules will be printed to + stdout. + + + None + + + None + """ + print "Enabled Modules:" + print ", ".join(seash_modules.get_enabled_modules()) + print + print "Installed Modules:" + + # Now print the URLs... + # Output format: + # modulename - URL not available # URL is set to None + # modulename - https://seattle.poly.edu/seashplugins/modulename/ # URL is set + for module in seash_modules.module_data: + print module, '-', + if seash_modules.module_data[module]['url'] is None: + print "Install URL not available" + else: + print seash_modules.module_data[module]['url'] + print + + +# enable modulename +def enable_module(input_dict, environment_dict): + """ + + Enables an installed module. + + + input_dict: The commanddict representing the user's input. + environment_dict: The dictionary representing the current seash + environment. + + + The module specified in the 'modulename' node will be enabled. + + + UserError: input_dict did not contain a 'modulename' node. + + + None + """ + + # Get the modulename + dict_mark = input_dict + try: + command = dict_mark.keys()[0] + while dict_mark[command]['name'] != 'modulename': + dict_mark = input_dict[command]['children'] + command = dict_mark.keys()[0] + modulename = command + except IndexError: + raise seash_exceptions.UserError("Error, command requires a modulename") + + try: + seash_modules.enable(seash_dictionary.seashcommanddict, modulename) + except seash_exceptions.ModuleConflictError, e: + print "Module cannot be imported due to the following conflicting command:" + print str(e) + except seash_exceptions.InitializeError, e: + print "Error while enabling the '"+modulename+"' module." + seash_modules.disable(seash_dictionary.seashcommanddict, modulename) + + + +def disable_module(input_dict, environment_dict): + """ + + Disables an enabled module. + + + input_dict: The commanddict representing the user's input. + environment_dict: The dictionary representing the current seash + environment. + + + The module specified in the 'modulename' node will be enabled. + + + UserError: + input_dict did not contain a 'modulename' node, or the user tried to + disable this module. + + + None + """ + + # Get the modulename + dict_mark = input_dict + try: + command = dict_mark.keys()[0] + while dict_mark[command]['name'] != 'modulename': + dict_mark = input_dict[command]['children'] + command = dict_mark.keys()[0] + modulename = command + except IndexError: + raise seash_exceptions.UserError("Error, command requires a modulename") + + if modulename == 'modules': + raise seash_exceptions.UserError("Error, cannot disable the 'modules' module") + + seash_modules.disable(seash_dictionary.seashcommanddict, modulename) + + + +def print_module_help(input_dict, environment_dict): + """ + + Displays the module level help for a module. + + + input_dict: The commanddict representing the user's input. + environment_dict: The dictionary representing the current seash + environment. + + + The helptext for the module specified in the 'modulename' node will be + printed to stdout. + + + UserError: + input_dict did not contain a 'modulename' node, or the user specified + a module that is not installed. + + + None + """ + + # Get the modulename + dict_mark = input_dict + try: + command = dict_mark.keys()[0] + while dict_mark[command]['name'] != 'modulename': + dict_mark = input_dict[command]['children'] + command = dict_mark.keys()[0] + modulename = command + except IndexError: + raise seash_exceptions.UserError("Error, command requires a modulename") + + # Is this module installed? + if not modulename in seash_modules.module_data: + raise seash_exceptions.UserError("Module is not installed.") + + print seash_modules.module_data[modulename]['help_text'] + + # Now, print out all the commands under this module + print "Commands in this module:" + print '\n'.join(seash_helper.get_commands_from_commanddict(seash_modules.module_data[modulename]['command_dict'])) + + diff --git a/pyreadline/__init__.py b/pyreadline/__init__.py index 7a50e1c..fea2a30 100644 --- a/pyreadline/__init__.py +++ b/pyreadline/__init__.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2003-2006 Gary Bishop. -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import unicode_helper, logger, clipboard, lineeditor, modes, console -from rlmain import * -import rlmain +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Gary Bishop. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import unicode_helper, logger, clipboard, lineeditor, modes, console +from rlmain import * +import rlmain diff --git a/pyreadline/clipboard/__init__.py b/pyreadline/clipboard/__init__.py index ec9fff2..eb24742 100644 --- a/pyreadline/clipboard/__init__.py +++ b/pyreadline/clipboard/__init__.py @@ -1,72 +1,72 @@ -import sys -success = True -in_ironpython = u"IronPython" in sys.version -if in_ironpython: - try: - from ironpython_clipboard import GetClipboardText, SetClipboardText - except ImportError: - from no_clipboard import GetClipboardText, SetClipboardText - -else: - try: - from win32_clipboard import GetClipboardText, SetClipboardText - except ImportError: - from no_clipboard import GetClipboardText, SetClipboardText - - -def send_data(lists): - SetClipboardText(make_tab(lists)) - - -def set_clipboard_text(toclipboard): - SetClipboardText(str(toclipboard)) - -def make_tab(lists): - if hasattr(lists, u"tolist"): - lists = lists.tolist() - ut = [] - for rad in lists: - if type(rad) in [list, tuple]: - ut.append(u"\t".join([u"%s"%x for x in rad])) - else: - ut.append(u"%s"%rad) - return u"\n".join(ut) - -def make_list_of_list(txt): - def make_num(x): - try: - return int(x) - except ValueError: - try: - return float(x) - except ValueError: - try: - return complex(x) - except ValueError: - return x - return x - ut = [] - flag = False - for rad in [x for x in txt.split(u"\r\n") if x != u""]: - raden=[make_num(x) for x in rad.split(u"\t")] - if str in map(type,raden): - flag = True - ut.append(raden) - return ut, flag - - -def get_clipboard_text_and_convert(paste_list=False): - u"""Get txt from clipboard. if paste_list==True the convert tab separated - data to list of lists. Enclose list of list in array() if all elements are - numeric""" - txt = GetClipboardText() - if txt: - if paste_list and u"\t" in txt: - array, flag = make_list_of_list(txt) - if flag: - txt = repr(array) - else: - txt = u"array(%s)"%repr(array) - txt = u"".join([c for c in txt if c not in u" \t\r\n"]) - return txt - +import sys +success = True +in_ironpython = u"IronPython" in sys.version +if in_ironpython: + try: + from ironpython_clipboard import GetClipboardText, SetClipboardText + except ImportError: + from no_clipboard import GetClipboardText, SetClipboardText + +else: + try: + from win32_clipboard import GetClipboardText, SetClipboardText + except ImportError: + from no_clipboard import GetClipboardText, SetClipboardText + + +def send_data(lists): + SetClipboardText(make_tab(lists)) + + +def set_clipboard_text(toclipboard): + SetClipboardText(str(toclipboard)) + +def make_tab(lists): + if hasattr(lists, u"tolist"): + lists = lists.tolist() + ut = [] + for rad in lists: + if type(rad) in [list, tuple]: + ut.append(u"\t".join([u"%s"%x for x in rad])) + else: + ut.append(u"%s"%rad) + return u"\n".join(ut) + +def make_list_of_list(txt): + def make_num(x): + try: + return int(x) + except ValueError: + try: + return float(x) + except ValueError: + try: + return complex(x) + except ValueError: + return x + return x + ut = [] + flag = False + for rad in [x for x in txt.split(u"\r\n") if x != u""]: + raden=[make_num(x) for x in rad.split(u"\t")] + if str in map(type,raden): + flag = True + ut.append(raden) + return ut, flag + + +def get_clipboard_text_and_convert(paste_list=False): + u"""Get txt from clipboard. if paste_list==True the convert tab separated + data to list of lists. Enclose list of list in array() if all elements are + numeric""" + txt = GetClipboardText() + if txt: + if paste_list and u"\t" in txt: + array, flag = make_list_of_list(txt) + if flag: + txt = repr(array) + else: + txt = u"array(%s)"%repr(array) + txt = u"".join([c for c in txt if c not in u" \t\r\n"]) + return txt + diff --git a/pyreadline/clipboard/ironpython_clipboard.py b/pyreadline/clipboard/ironpython_clipboard.py index 10187d4..c01e7aa 100644 --- a/pyreadline/clipboard/ironpython_clipboard.py +++ b/pyreadline/clipboard/ironpython_clipboard.py @@ -1,28 +1,28 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import clr -clr.AddReferenceByPartialName(u"System.Windows.Forms") -import System.Windows.Forms.Clipboard as cb - -def GetClipboardText(): - text = "" - if cb.ContainsText(): - text = cb.GetText() - - return text - -def SetClipboardText(text): - cb.SetText(text) - -if __name__ == u'__main__': - txt = GetClipboardText() # display last text clipped - print txt - - - +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import clr +clr.AddReferenceByPartialName(u"System.Windows.Forms") +import System.Windows.Forms.Clipboard as cb + +def GetClipboardText(): + text = "" + if cb.ContainsText(): + text = cb.GetText() + + return text + +def SetClipboardText(text): + cb.SetText(text) + +if __name__ == u'__main__': + txt = GetClipboardText() # display last text clipped + print txt + + + \ No newline at end of file diff --git a/pyreadline/clipboard/no_clipboard.py b/pyreadline/clipboard/no_clipboard.py index 195b0ad..71c540a 100644 --- a/pyreadline/clipboard/no_clipboard.py +++ b/pyreadline/clipboard/no_clipboard.py @@ -1,18 +1,18 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - - -mybuffer = u"" - -def GetClipboardText(): - return mybuffer - -def SetClipboardText(text): - global mybuffer - mybuffer = text - +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + + +mybuffer = u"" + +def GetClipboardText(): + return mybuffer + +def SetClipboardText(text): + global mybuffer + mybuffer = text + diff --git a/pyreadline/clipboard/win32_clipboard.py b/pyreadline/clipboard/win32_clipboard.py index c0f4ef4..e1a2127 100644 --- a/pyreadline/clipboard/win32_clipboard.py +++ b/pyreadline/clipboard/win32_clipboard.py @@ -1,108 +1,108 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2003-2006 Jack Trainor. -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -################################### -# -# Based on recipe posted to ctypes-users -# see archive -# http://aspn.activestate.com/ASPN/Mail/Message/ctypes-users/1771866 -# -# - -################################################################################### -# -# The Python win32clipboard lib functions work well enough ... except that they -# can only cut and paste items from within one application, not across -# applications or processes. -# -# I've written a number of Python text filters I like to run on the contents of -# the clipboard so I need to call the Windows clipboard API with global memory -# for my filters to work properly. -# -# Here's some sample code solving this problem using ctypes. -# -# This is my first work with ctypes. It's powerful stuff, but passing arguments -# in and out of functions is tricky. More sample code would have been helpful, -# hence this contribution. -# -################################################################################### - -from ctypes import * -from pyreadline.keysyms.winconstants import CF_TEXT, GHND -from pyreadline.unicode_helper import ensure_unicode,ensure_str - -OpenClipboard = windll.user32.OpenClipboard -OpenClipboard.argtypes = [c_int] - -EmptyClipboard = windll.user32.EmptyClipboard - -GetClipboardData = windll.user32.GetClipboardData -GetClipboardData.argtypes = [c_int] - -GetClipboardFormatName = windll.user32.GetClipboardFormatNameA -GetClipboardFormatName.argtypes = [c_uint,c_char_p,c_int] - -SetClipboardData = windll.user32.SetClipboardData -SetClipboardData.argtypes = [c_int,c_int] - -EnumClipboardFormats = windll.user32.EnumClipboardFormats -EnumClipboardFormats.argtypes = [c_int] - -CloseClipboard = windll.user32.CloseClipboard -CloseClipboard.argtypes = [] - - -GlobalAlloc = windll.kernel32.GlobalAlloc -GlobalLock = windll.kernel32.GlobalLock -GlobalLock.argtypes = [c_int] -GlobalUnlock = windll.kernel32.GlobalUnlock -GlobalUnlock.argtypes = [c_int] -memcpy = cdll.msvcrt.memcpy - -def enum(): - OpenClipboard(0) - q = EnumClipboardFormats(0) - while q: - q = EnumClipboardFormats(q) - CloseClipboard() - -def getformatname(format): - buffer = c_buffer(" "*100) - bufferSize = sizeof(buffer) - OpenClipboard(0) - GetClipboardFormatName(format, buffer, bufferSize) - CloseClipboard() - return buffer.value - -def GetClipboardText(): - text = u"" - if OpenClipboard(0): - hClipMem = GetClipboardData(CF_TEXT) - if hClipMem: - GlobalLock.restype = c_char_p - text = GlobalLock(hClipMem) - GlobalUnlock(hClipMem) - CloseClipboard() - return ensure_unicode(text) - -def SetClipboardText(text): - buffer = c_buffer(ensure_str(text)) - bufferSize = sizeof(buffer) - hGlobalMem = GlobalAlloc(c_int(GHND), c_int(bufferSize)) - GlobalLock.restype = c_void_p - lpGlobalMem = GlobalLock(c_int(hGlobalMem)) - memcpy(lpGlobalMem, addressof(buffer), c_int(bufferSize)) - GlobalUnlock(c_int(hGlobalMem)) - if OpenClipboard(0): - EmptyClipboard() - SetClipboardData(c_int(CF_TEXT), c_int(hGlobalMem)) - CloseClipboard() - -if __name__ == u'__main__': - txt = GetClipboardText() # display last text clipped - print txt +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Jack Trainor. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +################################### +# +# Based on recipe posted to ctypes-users +# see archive +# http://aspn.activestate.com/ASPN/Mail/Message/ctypes-users/1771866 +# +# + +################################################################################### +# +# The Python win32clipboard lib functions work well enough ... except that they +# can only cut and paste items from within one application, not across +# applications or processes. +# +# I've written a number of Python text filters I like to run on the contents of +# the clipboard so I need to call the Windows clipboard API with global memory +# for my filters to work properly. +# +# Here's some sample code solving this problem using ctypes. +# +# This is my first work with ctypes. It's powerful stuff, but passing arguments +# in and out of functions is tricky. More sample code would have been helpful, +# hence this contribution. +# +################################################################################### + +from ctypes import * +from pyreadline.keysyms.winconstants import CF_TEXT, GHND +from pyreadline.unicode_helper import ensure_unicode,ensure_str + +OpenClipboard = windll.user32.OpenClipboard +OpenClipboard.argtypes = [c_int] + +EmptyClipboard = windll.user32.EmptyClipboard + +GetClipboardData = windll.user32.GetClipboardData +GetClipboardData.argtypes = [c_int] + +GetClipboardFormatName = windll.user32.GetClipboardFormatNameA +GetClipboardFormatName.argtypes = [c_uint,c_char_p,c_int] + +SetClipboardData = windll.user32.SetClipboardData +SetClipboardData.argtypes = [c_int,c_int] + +EnumClipboardFormats = windll.user32.EnumClipboardFormats +EnumClipboardFormats.argtypes = [c_int] + +CloseClipboard = windll.user32.CloseClipboard +CloseClipboard.argtypes = [] + + +GlobalAlloc = windll.kernel32.GlobalAlloc +GlobalLock = windll.kernel32.GlobalLock +GlobalLock.argtypes = [c_int] +GlobalUnlock = windll.kernel32.GlobalUnlock +GlobalUnlock.argtypes = [c_int] +memcpy = cdll.msvcrt.memcpy + +def enum(): + OpenClipboard(0) + q = EnumClipboardFormats(0) + while q: + q = EnumClipboardFormats(q) + CloseClipboard() + +def getformatname(format): + buffer = c_buffer(" "*100) + bufferSize = sizeof(buffer) + OpenClipboard(0) + GetClipboardFormatName(format, buffer, bufferSize) + CloseClipboard() + return buffer.value + +def GetClipboardText(): + text = u"" + if OpenClipboard(0): + hClipMem = GetClipboardData(CF_TEXT) + if hClipMem: + GlobalLock.restype = c_char_p + text = GlobalLock(hClipMem) + GlobalUnlock(hClipMem) + CloseClipboard() + return ensure_unicode(text) + +def SetClipboardText(text): + buffer = c_buffer(ensure_str(text)) + bufferSize = sizeof(buffer) + hGlobalMem = GlobalAlloc(c_int(GHND), c_int(bufferSize)) + GlobalLock.restype = c_void_p + lpGlobalMem = GlobalLock(c_int(hGlobalMem)) + memcpy(lpGlobalMem, addressof(buffer), c_int(bufferSize)) + GlobalUnlock(c_int(hGlobalMem)) + if OpenClipboard(0): + EmptyClipboard() + SetClipboardData(c_int(CF_TEXT), c_int(hGlobalMem)) + CloseClipboard() + +if __name__ == u'__main__': + txt = GetClipboardText() # display last text clipped + print txt diff --git a/pyreadline/console/__init__.py b/pyreadline/console/__init__.py index 9d821ab..f900147 100644 --- a/pyreadline/console/__init__.py +++ b/pyreadline/console/__init__.py @@ -1,22 +1,22 @@ -import glob,sys - -success = False -in_ironpython = "IronPython" in sys.version - -if in_ironpython: - try: - from ironpython_console import * - success = True - except ImportError: - raise -else: - try: - from console import * - success = True - except ImportError: - pass - raise - -if not success: - raise ImportError( - "Could not find a console implementation for your platform") +import glob,sys + +success = False +in_ironpython = "IronPython" in sys.version + +if in_ironpython: + try: + from ironpython_console import * + success = True + except ImportError: + raise +else: + try: + from console import * + success = True + except ImportError: + pass + raise + +if not success: + raise ImportError( + "Could not find a console implementation for your platform") diff --git a/pyreadline/console/ansi.py b/pyreadline/console/ansi.py index 13fd8dc..fb6d87e 100644 --- a/pyreadline/console/ansi.py +++ b/pyreadline/console/ansi.py @@ -1,190 +1,190 @@ -# -*- coding: ISO-8859-1 -*- -import re,sys,os - -terminal_escape = re.compile(u'(\001?\033\\[[0-9;]*m\002?)') -escape_parts = re.compile(u'\001?\033\\[([0-9;]*)m\002?') - - -class AnsiState(object): - def __init__(self,bold=False,inverse=False,color=u"white",background=u"black",backgroundbold=False): - self.bold = bold - self.inverse = inverse - self.color = color - self.background = background - self.backgroundbold = backgroundbold - - trtable = {u"black":0, u"red":4, u"green":2, u"yellow":6, - u"blue":1, u"magenta":5, u"cyan":3, u"white":7} - revtable = dict(zip(trtable.values(),trtable.keys())) - def get_winattr(self): - attr = 0 - if self.bold: - attr |= 0x0008 - if self.backgroundbold: - attr |= 0x0080 - if self.inverse: - attr |= 0x4000 - attr |= self.trtable[self.color] - attr |= (self.trtable[self.background] << 4) - return attr - - def set_winattr(self, attr): - self.bold = bool(attr & 0x0008) - self.backgroundbold = bool(attr & 0x0080) - self.inverse = bool(attr & 0x4000) - self.color = self.revtable[attr & 0x0007] - self.background = self.revtable[(attr & 0x0070) >> 4] - - winattr=property(get_winattr,set_winattr) - def __repr__(self): - return u'AnsiState(bold=%s,inverse=%s,color=%9s,' \ - u'background=%9s,backgroundbold=%s)# 0x%x'% \ - (self.bold, self.inverse, '"%s"'%self.color, - '"%s"'%self.background, self.backgroundbold, - self.winattr) - - def copy(self): - x = AnsiState() - x.bold = self.bold - x.inverse = self.inverse - x.color = self.color - x.background = self.background - x.backgroundbold = self.backgroundbold - return x - -defaultstate = AnsiState(False,False,u"white") - -trtable = {0:u"black", 1:u"red", 2:u"green", 3:u"yellow", - 4:u"blue", 5:u"magenta", 6:u"cyan", 7:u"white"} - -class AnsiWriter(object): - def __init__(self, default=defaultstate): - if isinstance(defaultstate, AnsiState): - self.defaultstate = default - else: - self.defaultstate=AnsiState() - self.defaultstate.winattr = defaultstate - - - def write_color(self,text, attr=None): - u'''write text at current cursor position and interpret color escapes. - - return the number of characters written. - ''' - if isinstance(attr,AnsiState): - defaultstate = attr - elif attr is None: #use attribute form initial console - attr = self.defaultstate.copy() - else: - defaultstate = AnsiState() - defaultstate.winattr = attr - attr = defaultstate - chunks = terminal_escape.split(text) - n = 0 # count the characters we actually write, omitting the escapes - res=[] - for chunk in chunks: - m = escape_parts.match(chunk) - if m: - parts = m.group(1).split(u";") - if len(parts) == 1 and parts[0] == u"0": - attr = self.defaultstate.copy() - continue - for part in parts: - if part == u"0": # No text attribute - attr = self.defaultstate.copy() - attr.bold=False - elif part == u"7": # switch on reverse - attr.inverse=True - elif part == u"1": # switch on bold (i.e. intensify foreground color) - attr.bold=True - elif len(part) == 2 and u"30" <= part <= u"37": # set foreground color - attr.color = trtable[int(part) - 30] - elif len(part) == 2 and u"40" <= part <= u"47": # set background color - attr.backgroundcolor = trtable[int(part) - 40] - continue - n += len(chunk) - if True: - res.append((attr.copy(), chunk)) - return n,res - - def parse_color(self,text, attr=None): - n,res=self.write_color(text, attr) - return n, [attr.winattr for attr, text in res] - -def write_color(text, attr=None): - a = AnsiWriter(defaultstate) - return a.write_color(text, attr) - -def write_color_old( text, attr=None): - u'''write text at current cursor position and interpret color escapes. - - return the number of characters written. - ''' - res = [] - chunks = terminal_escape.split(text) - n = 0 # count the characters we actually write, omitting the escapes - if attr is None:#use attribute from initial console - attr = 15 - for chunk in chunks: - m = escape_parts.match(chunk) - if m: - for part in m.group(1).split(u";"): - if part == u"0": # No text attribute - attr = 0 - elif part == u"7": # switch on reverse - attr |= 0x4000 - if part == u"1": # switch on bold (i.e. intensify foreground color) - attr |= 0x08 - elif len(part) == 2 and u"30" <= part <= u"37": # set foreground color - part = int(part)-30 - # we have to mirror bits - attr = (attr & ~0x07) | ((part & 0x1) << 2) | (part & 0x2) | ((part & 0x4) >> 2) - elif len(part) == 2 and u"40" <= part <= u"47": # set background color - part = int(part) - 40 - # we have to mirror bits - attr = (attr & ~0x70) | ((part & 0x1) << 6) | ((part & 0x2) << 4) | ((part & 0x4) << 2) - # ignore blink, underline and anything we don't understand - continue - n += len(chunk) - if chunk: - res.append((u"0x%x"%attr, chunk)) - return res - - -#trtable={0:"black",1:"red",2:"green",3:"yellow",4:"blue",5:"magenta",6:"cyan",7:"white"} - -if __name__==u"__main__x": - import pprint - pprint=pprint.pprint - - s=u"\033[0;31mred\033[0;32mgreen\033[0;33myellow\033[0;34mblue\033[0;35mmagenta\033[0;36mcyan\033[0;37mwhite\033[0m" - pprint (write_color(s)) - pprint (write_color_old(s)) - s=u"\033[1;31mred\033[1;32mgreen\033[1;33myellow\033[1;34mblue\033[1;35mmagenta\033[1;36mcyan\033[1;37mwhite\033[0m" - pprint (write_color(s)) - pprint (write_color_old(s)) - - s=u"\033[0;7;31mred\033[0;7;32mgreen\033[0;7;33myellow\033[0;7;34mblue\033[0;7;35mmagenta\033[0;7;36mcyan\033[0;7;37mwhite\033[0m" - pprint (write_color(s)) - pprint (write_color_old(s)) - s=u"\033[1;7;31mred\033[1;7;32mgreen\033[1;7;33myellow\033[1;7;34mblue\033[1;7;35mmagenta\033[1;7;36mcyan\033[1;7;37mwhite\033[0m" - pprint (write_color(s)) - pprint (write_color_old(s)) - - -if __name__==u"__main__": - import console - import pprint - pprint=pprint.pprint - - c=console.Console() - c.write_color(u"dhsjdhs") - c.write_color(u"\033[0;32mIn [\033[1;32m1\033[0;32m]:") - print - pprint (write_color(u"\033[0;32mIn [\033[1;32m1\033[0;32m]:")) - -if __name__==u"__main__x": - import pprint - pprint=pprint.pprint - s=u"\033[0;31mred\033[0;32mgreen\033[0;33myellow\033[0;34mblue\033[0;35mmagenta\033[0;36mcyan\033[0;37mwhite\033[0m" - pprint (write_color(s)) +# -*- coding: ISO-8859-1 -*- +import re,sys,os + +terminal_escape = re.compile(u'(\001?\033\\[[0-9;]*m\002?)') +escape_parts = re.compile(u'\001?\033\\[([0-9;]*)m\002?') + + +class AnsiState(object): + def __init__(self,bold=False,inverse=False,color=u"white",background=u"black",backgroundbold=False): + self.bold = bold + self.inverse = inverse + self.color = color + self.background = background + self.backgroundbold = backgroundbold + + trtable = {u"black":0, u"red":4, u"green":2, u"yellow":6, + u"blue":1, u"magenta":5, u"cyan":3, u"white":7} + revtable = dict(zip(trtable.values(),trtable.keys())) + def get_winattr(self): + attr = 0 + if self.bold: + attr |= 0x0008 + if self.backgroundbold: + attr |= 0x0080 + if self.inverse: + attr |= 0x4000 + attr |= self.trtable[self.color] + attr |= (self.trtable[self.background] << 4) + return attr + + def set_winattr(self, attr): + self.bold = bool(attr & 0x0008) + self.backgroundbold = bool(attr & 0x0080) + self.inverse = bool(attr & 0x4000) + self.color = self.revtable[attr & 0x0007] + self.background = self.revtable[(attr & 0x0070) >> 4] + + winattr=property(get_winattr,set_winattr) + def __repr__(self): + return u'AnsiState(bold=%s,inverse=%s,color=%9s,' \ + u'background=%9s,backgroundbold=%s)# 0x%x'% \ + (self.bold, self.inverse, '"%s"'%self.color, + '"%s"'%self.background, self.backgroundbold, + self.winattr) + + def copy(self): + x = AnsiState() + x.bold = self.bold + x.inverse = self.inverse + x.color = self.color + x.background = self.background + x.backgroundbold = self.backgroundbold + return x + +defaultstate = AnsiState(False,False,u"white") + +trtable = {0:u"black", 1:u"red", 2:u"green", 3:u"yellow", + 4:u"blue", 5:u"magenta", 6:u"cyan", 7:u"white"} + +class AnsiWriter(object): + def __init__(self, default=defaultstate): + if isinstance(defaultstate, AnsiState): + self.defaultstate = default + else: + self.defaultstate=AnsiState() + self.defaultstate.winattr = defaultstate + + + def write_color(self,text, attr=None): + u'''write text at current cursor position and interpret color escapes. + + return the number of characters written. + ''' + if isinstance(attr,AnsiState): + defaultstate = attr + elif attr is None: #use attribute form initial console + attr = self.defaultstate.copy() + else: + defaultstate = AnsiState() + defaultstate.winattr = attr + attr = defaultstate + chunks = terminal_escape.split(text) + n = 0 # count the characters we actually write, omitting the escapes + res=[] + for chunk in chunks: + m = escape_parts.match(chunk) + if m: + parts = m.group(1).split(u";") + if len(parts) == 1 and parts[0] == u"0": + attr = self.defaultstate.copy() + continue + for part in parts: + if part == u"0": # No text attribute + attr = self.defaultstate.copy() + attr.bold=False + elif part == u"7": # switch on reverse + attr.inverse=True + elif part == u"1": # switch on bold (i.e. intensify foreground color) + attr.bold=True + elif len(part) == 2 and u"30" <= part <= u"37": # set foreground color + attr.color = trtable[int(part) - 30] + elif len(part) == 2 and u"40" <= part <= u"47": # set background color + attr.backgroundcolor = trtable[int(part) - 40] + continue + n += len(chunk) + if True: + res.append((attr.copy(), chunk)) + return n,res + + def parse_color(self,text, attr=None): + n,res=self.write_color(text, attr) + return n, [attr.winattr for attr, text in res] + +def write_color(text, attr=None): + a = AnsiWriter(defaultstate) + return a.write_color(text, attr) + +def write_color_old( text, attr=None): + u'''write text at current cursor position and interpret color escapes. + + return the number of characters written. + ''' + res = [] + chunks = terminal_escape.split(text) + n = 0 # count the characters we actually write, omitting the escapes + if attr is None:#use attribute from initial console + attr = 15 + for chunk in chunks: + m = escape_parts.match(chunk) + if m: + for part in m.group(1).split(u";"): + if part == u"0": # No text attribute + attr = 0 + elif part == u"7": # switch on reverse + attr |= 0x4000 + if part == u"1": # switch on bold (i.e. intensify foreground color) + attr |= 0x08 + elif len(part) == 2 and u"30" <= part <= u"37": # set foreground color + part = int(part)-30 + # we have to mirror bits + attr = (attr & ~0x07) | ((part & 0x1) << 2) | (part & 0x2) | ((part & 0x4) >> 2) + elif len(part) == 2 and u"40" <= part <= u"47": # set background color + part = int(part) - 40 + # we have to mirror bits + attr = (attr & ~0x70) | ((part & 0x1) << 6) | ((part & 0x2) << 4) | ((part & 0x4) << 2) + # ignore blink, underline and anything we don't understand + continue + n += len(chunk) + if chunk: + res.append((u"0x%x"%attr, chunk)) + return res + + +#trtable={0:"black",1:"red",2:"green",3:"yellow",4:"blue",5:"magenta",6:"cyan",7:"white"} + +if __name__==u"__main__x": + import pprint + pprint=pprint.pprint + + s=u"\033[0;31mred\033[0;32mgreen\033[0;33myellow\033[0;34mblue\033[0;35mmagenta\033[0;36mcyan\033[0;37mwhite\033[0m" + pprint (write_color(s)) + pprint (write_color_old(s)) + s=u"\033[1;31mred\033[1;32mgreen\033[1;33myellow\033[1;34mblue\033[1;35mmagenta\033[1;36mcyan\033[1;37mwhite\033[0m" + pprint (write_color(s)) + pprint (write_color_old(s)) + + s=u"\033[0;7;31mred\033[0;7;32mgreen\033[0;7;33myellow\033[0;7;34mblue\033[0;7;35mmagenta\033[0;7;36mcyan\033[0;7;37mwhite\033[0m" + pprint (write_color(s)) + pprint (write_color_old(s)) + s=u"\033[1;7;31mred\033[1;7;32mgreen\033[1;7;33myellow\033[1;7;34mblue\033[1;7;35mmagenta\033[1;7;36mcyan\033[1;7;37mwhite\033[0m" + pprint (write_color(s)) + pprint (write_color_old(s)) + + +if __name__==u"__main__": + import console + import pprint + pprint=pprint.pprint + + c=console.Console() + c.write_color(u"dhsjdhs") + c.write_color(u"\033[0;32mIn [\033[1;32m1\033[0;32m]:") + print + pprint (write_color(u"\033[0;32mIn [\033[1;32m1\033[0;32m]:")) + +if __name__==u"__main__x": + import pprint + pprint=pprint.pprint + s=u"\033[0;31mred\033[0;32mgreen\033[0;33myellow\033[0;34mblue\033[0;35mmagenta\033[0;36mcyan\033[0;37mwhite\033[0m" + pprint (write_color(s)) diff --git a/pyreadline/console/console.py b/pyreadline/console/console.py index 213486c..0101c36 100644 --- a/pyreadline/console/console.py +++ b/pyreadline/console/console.py @@ -1,840 +1,840 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2003-2006 Gary Bishop. -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -u'''Cursor control and color for the Windows console. - -This was modeled after the C extension of the same name by Fredrik Lundh. -''' - -# primitive debug printing that won't interfere with the screen - -import sys,os -import traceback -import re - -import pyreadline.unicode_helper as unicode_helper - -from pyreadline.logger import log -from pyreadline.unicode_helper import ensure_unicode, ensure_str -from pyreadline.keysyms import make_KeyPress, KeyPress -from pyreadline.console.ansi import AnsiState,AnsiWriter - -try: - import ctypes.util - from ctypes import * - from _ctypes import call_function - from ctypes.wintypes import * -except ImportError: - raise ImportError(u"You need ctypes to run this code") - -def nolog(string): - pass - -log = nolog - - -# some constants we need -STD_INPUT_HANDLE = -10 -STD_OUTPUT_HANDLE = -11 -ENABLE_WINDOW_INPUT = 0x0008 -ENABLE_MOUSE_INPUT = 0x0010 -ENABLE_PROCESSED_INPUT = 0x0001 -WHITE = 0x7 -BLACK = 0 -MENU_EVENT = 0x0008 -KEY_EVENT = 0x0001 -MOUSE_MOVED = 0x0001 -MOUSE_EVENT = 0x0002 -WINDOW_BUFFER_SIZE_EVENT = 0x0004 -FOCUS_EVENT = 0x0010 -MENU_EVENT = 0x0008 -VK_SHIFT = 0x10 -VK_CONTROL = 0x11 -VK_MENU = 0x12 -GENERIC_READ = int(0x80000000L) -GENERIC_WRITE = 0x40000000 - -# Windows structures we'll need later -class COORD(Structure): - _fields_ = [("X", c_short), - ("Y", c_short)] - -class SMALL_RECT(Structure): - _fields_ = [("Left", c_short), - ("Top", c_short), - ("Right", c_short), - ("Bottom", c_short)] - -class CONSOLE_SCREEN_BUFFER_INFO(Structure): - _fields_ = [("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", c_short), - ("srWindow", SMALL_RECT), - ("dwMaximumWindowSize", COORD)] - -class CHAR_UNION(Union): - _fields_ = [("UnicodeChar", c_wchar), - ("AsciiChar", c_char)] - -class CHAR_INFO(Structure): - _fields_ = [("Char", CHAR_UNION), - ("Attributes", c_short)] - -class KEY_EVENT_RECORD(Structure): - _fields_ = [("bKeyDown", c_byte), - ("pad2", c_byte), - ('pad1', c_short), - ("wRepeatCount", c_short), - ("wVirtualKeyCode", c_short), - ("wVirtualScanCode", c_short), - ("uChar", CHAR_UNION), - ("dwControlKeyState", c_int)] - -class MOUSE_EVENT_RECORD(Structure): - _fields_ = [("dwMousePosition", COORD), - ("dwButtonState", c_int), - ("dwControlKeyState", c_int), - ("dwEventFlags", c_int)] - -class WINDOW_BUFFER_SIZE_RECORD(Structure): - _fields_ = [("dwSize", COORD)] - -class MENU_EVENT_RECORD(Structure): - _fields_ = [("dwCommandId", c_uint)] - -class FOCUS_EVENT_RECORD(Structure): - _fields_ = [("bSetFocus", c_byte)] - -class INPUT_UNION(Union): - _fields_ = [("KeyEvent", KEY_EVENT_RECORD), - ("MouseEvent", MOUSE_EVENT_RECORD), - ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD), - ("MenuEvent", MENU_EVENT_RECORD), - ("FocusEvent", FOCUS_EVENT_RECORD)] - -class INPUT_RECORD(Structure): - _fields_ = [("EventType", c_short), - ("Event", INPUT_UNION)] - -class CONSOLE_CURSOR_INFO(Structure): - _fields_ = [("dwSize", c_int), - ("bVisible", c_byte)] - - -# I didn't want to have to individually import these so I made a list, they are -# added to the Console class later in this file. - -funcs = [ - 'AllocConsole', - 'CreateConsoleScreenBuffer', - 'FillConsoleOutputAttribute', - 'FillConsoleOutputCharacterW', - 'FreeConsole', - 'GetConsoleCursorInfo', - 'GetConsoleMode', - 'GetConsoleScreenBufferInfo', - 'GetConsoleTitleW', - 'GetProcAddress', - 'GetStdHandle', - 'PeekConsoleInputW', - 'ReadConsoleInputW', - 'ScrollConsoleScreenBufferW', - 'SetConsoleActiveScreenBuffer', - 'SetConsoleCursorInfo', - 'SetConsoleCursorPosition', - 'SetConsoleMode', - 'SetConsoleScreenBufferSize', - 'SetConsoleTextAttribute', - 'SetConsoleTitleW', - 'SetConsoleWindowInfo', - 'WriteConsoleW', - 'WriteConsoleOutputCharacterW', - 'WriteFile', - ] - -# I don't want events for these keys, they are just a bother for my application -key_modifiers = { VK_SHIFT : 1, - VK_CONTROL : 1, - VK_MENU : 1, # alt key - 0x5b : 1, # windows key - } - -def split_block(text, size=1000): - return [text[start:start + size] for start in range(0, len(text), size)] - - - -class Console(object): - u'''Console driver for Windows. - - ''' - - def __init__(self, newbuffer=0): - u'''Initialize the Console object. - - newbuffer=1 will allocate a new buffer so the old content will be restored - on exit. - ''' - #Do I need the following line? It causes a console to be created whenever - #readline is imported into a pythonw application which seems wrong. Things - #seem to work without it... - #self.AllocConsole() - - if newbuffer: - self.hout = self.CreateConsoleScreenBuffer( - GENERIC_READ | GENERIC_WRITE, - 0, None, 1, None) - self.SetConsoleActiveScreenBuffer(self.hout) - else: - self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE) - - self.hin = self.GetStdHandle(STD_INPUT_HANDLE) - self.inmode = DWORD(0) - self.GetConsoleMode(self.hin, byref(self.inmode)) - self.SetConsoleMode(self.hin, 0xf) - info = CONSOLE_SCREEN_BUFFER_INFO() - self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - self.attr = info.wAttributes - self.saveattr = info.wAttributes # remember the initial colors - self.defaultstate = AnsiState() - self.defaultstate.winattr = info.wAttributes - self.ansiwriter = AnsiWriter(self.defaultstate) - - background = self.attr & 0xf0 - for escape in self.escape_to_color: - if self.escape_to_color[escape] is not None: - self.escape_to_color[escape] |= background - log(u'initial attr=%x' % self.attr) - self.softspace = 0 # this is for using it as a file-like object - self.serial = 0 - - self.pythondll = \ - CDLL(u'python%s%s' % (sys.version[0], sys.version[2])) - self.pythondll.PyMem_Malloc.restype = c_size_t - self.pythondll.PyMem_Malloc.argtypes = [c_size_t] - self.inputHookPtr = \ - c_void_p.from_address(addressof(self.pythondll.PyOS_InputHook)).value - setattr(Console, u'PyMem_Malloc', self.pythondll.PyMem_Malloc) - - def __del__(self): - u'''Cleanup the console when finished.''' - # I don't think this ever gets called - self.SetConsoleTextAttribute(self.hout, self.saveattr) - self.SetConsoleMode(self.hin, self.inmode) - self.FreeConsole() - - def _get_top_bot(self): - info = CONSOLE_SCREEN_BUFFER_INFO() - self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - rect = info.srWindow - top = rect.Top - bot = rect.Bottom - return top,bot - - def fixcoord(self, x, y): - u'''Return a long with x and y packed inside, - also handle negative x and y.''' - if x < 0 or y < 0: - info = CONSOLE_SCREEN_BUFFER_INFO() - self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - if x < 0: - x = info.srWindow.Right - x - y = info.srWindow.Bottom + y - - # this is a hack! ctypes won't pass structures but COORD is - # just like a long, so this works. - return c_int(y << 16 | x) - - def pos(self, x=None, y=None): - u'''Move or query the window cursor.''' - if x is None: - info = CONSOLE_SCREEN_BUFFER_INFO() - self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - return (info.dwCursorPosition.X, info.dwCursorPosition.Y) - else: - return self.SetConsoleCursorPosition(self.hout, - self.fixcoord(x, y)) - - def home(self): - u'''Move to home.''' - self.pos(0, 0) - -# Map ANSI color escape sequences into Windows Console Attributes - - terminal_escape = re.compile(u'(\001?\033\\[[0-9;]+m\002?)') - escape_parts = re.compile(u'\001?\033\\[([0-9;]+)m\002?') - escape_to_color = { u'0;30': 0x0, #black - u'0;31': 0x4, #red - u'0;32': 0x2, #green - u'0;33': 0x4+0x2, #brown? - u'0;34': 0x1, #blue - u'0;35': 0x1+0x4, #purple - u'0;36': 0x2+0x4, #cyan - u'0;37': 0x1+0x2+0x4, #grey - u'1;30': 0x1+0x2+0x4, #dark gray - u'1;31': 0x4+0x8, #red - u'1;32': 0x2+0x8, #light green - u'1;33': 0x4+0x2+0x8, #yellow - u'1;34': 0x1+0x8, #light blue - u'1;35': 0x1+0x4+0x8, #light purple - u'1;36': 0x1+0x2+0x8, #light cyan - u'1;37': 0x1+0x2+0x4+0x8, #white - u'0': None, - } - - # This pattern should match all characters that change the cursor position differently - # than a normal character. - motion_char_re = re.compile(u'([\n\r\t\010\007])') - - def write_scrolling(self, text, attr=None): - u'''write text at current cursor position while watching for scrolling. - - If the window scrolls because you are at the bottom of the screen - buffer, all positions that you are storing will be shifted by the - scroll amount. For example, I remember the cursor position of the - prompt so that I can redraw the line but if the window scrolls, - the remembered position is off. - - This variant of write tries to keep track of the cursor position - so that it will know when the screen buffer is scrolled. It - returns the number of lines that the buffer scrolled. - - ''' - x, y = self.pos() - w, h = self.size() - scroll = 0 # the result - # split the string into ordinary characters and funny characters - chunks = self.motion_char_re.split(text) - for chunk in chunks: - n = self.write_color(chunk, attr) - if len(chunk) == 1: # the funny characters will be alone - if chunk[0] == u'\n': # newline - x = 0 - y += 1 - elif chunk[0] == u'\r': # carriage return - x = 0 - elif chunk[0] == u'\t': # tab - x = 8 * (int(x / 8) + 1) - if x > w: # newline - x -= w - y += 1 - elif chunk[0] == u'\007': # bell - pass - elif chunk[0] == u'\010': - x -= 1 - if x < 0: - y -= 1 # backed up 1 line - else: # ordinary character - x += 1 - if x == w: # wrap - x = 0 - y += 1 - if y == h: # scroll - scroll += 1 - y = h - 1 - else: # chunk of ordinary characters - x += n - l = int(x / w) # lines we advanced - x = x % w # new x value - y += l - if y >= h: # scroll - scroll += y - h + 1 - y = h - 1 - return scroll - - def write_color(self, text, attr=None): - text = ensure_unicode(text) - n, res= self.ansiwriter.write_color(text, attr) - junk = DWORD(0) - for attr,chunk in res: - log(u"console.attr:%s"%unicode(attr)) - log(u"console.chunk:%s"%unicode(chunk)) - self.SetConsoleTextAttribute(self.hout, attr.winattr) - for short_chunk in split_block(chunk): - self.WriteConsoleW(self.hout, short_chunk, - len(short_chunk), byref(junk), None) - return n - - def write_plain(self, text, attr=None): - u'''write text at current cursor position.''' - text = ensure_unicode(text) - log(u'write("%s", %s)' %(text, attr)) - if attr is None: - attr = self.attr - junk = DWORD(0) - self.SetConsoleTextAttribute(self.hout, attr) - for short_chunk in split_block(chunk): - self.WriteConsoleW(self.hout, ensure_unicode(short_chunk), - len(short_chunk), byref(junk), None) - return len(text) - - #This function must be used to ensure functioning with EMACS - #Emacs sets the EMACS environment variable - if os.environ.has_key(u"EMACS"): - def write_color(self, text, attr=None): - text = ensure_str(text) - junk = DWORD(0) - self.WriteFile(self.hout, text, len(text), byref(junk), None) - return len(text) - write_plain = write_color - - # make this class look like a file object - def write(self, text): - text = ensure_unicode(text) - log(u'write("%s")' % text) - return self.write_color(text) - - #write = write_scrolling - - def isatty(self): - return True - - def flush(self): - pass - - def page(self, attr=None, fill=u' '): - u'''Fill the entire screen.''' - if attr is None: - attr = self.attr - if len(fill) != 1: - raise ValueError - info = CONSOLE_SCREEN_BUFFER_INFO() - self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0: - self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0)) - - w = info.dwSize.X - n = DWORD(0) - for y in range(info.dwSize.Y): - self.FillConsoleOutputAttribute(self.hout, attr, - w, self.fixcoord(0, y), byref(n)) - self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]), - w, self.fixcoord(0, y), byref(n)) - - self.attr = attr - - def text(self, x, y, text, attr=None): - u'''Write text at the given position.''' - if attr is None: - attr = self.attr - - pos = self.fixcoord(x, y) - n = DWORD(0) - self.WriteConsoleOutputCharacterW(self.hout, text, - len(text), pos, byref(n)) - self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n)) - - def clear_to_end_of_window(self): - top, bot = self._get_top_bot() - pos = self.pos() - w, h = self.size() - self.rectangle( (pos[0], pos[1], w, pos[1] + 1)) - if pos[1] < bot: - self.rectangle((0, pos[1] + 1, w, bot + 1)) - - def rectangle(self, rect, attr=None, fill=u' '): - u'''Fill Rectangle.''' - x0, y0, x1, y1 = rect - n = DWORD(0) - if attr is None: - attr = self.attr - for y in range(y0, y1): - pos = self.fixcoord(x0, y) - self.FillConsoleOutputAttribute(self.hout, attr, x1 - x0, - pos, byref(n)) - self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]), x1 - x0, - pos, byref(n)) - - def scroll(self, rect, dx, dy, attr=None, fill=' '): - u'''Scroll a rectangle.''' - if attr is None: - attr = self.attr - x0, y0, x1, y1 = rect - source = SMALL_RECT(x0, y0, x1 - 1, y1 - 1) - dest = self.fixcoord(x0 + dx, y0 + dy) - style = CHAR_INFO() - style.Char.AsciiChar = ensure_str(fill[0]) - style.Attributes = attr - - return self.ScrollConsoleScreenBufferW(self.hout, byref(source), - byref(source), dest, byref(style)) - - def scroll_window(self, lines): - u'''Scroll the window by the indicated number of lines.''' - info = CONSOLE_SCREEN_BUFFER_INFO() - self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - rect = info.srWindow - log(u'sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom)) - top = rect.Top + lines - bot = rect.Bottom + lines - h = bot - top - maxbot = info.dwSize.Y-1 - if top < 0: - top = 0 - bot = h - if bot > maxbot: - bot = maxbot - top = bot - h - - nrect = SMALL_RECT() - nrect.Top = top - nrect.Bottom = bot - nrect.Left = rect.Left - nrect.Right = rect.Right - log(u'sn: top=%d bot=%d' % (top, bot)) - r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect)) - log(u'r=%d' % r) - - def get(self): - u'''Get next event from queue.''' - inputHookFunc = c_void_p.from_address(self.inputHookPtr).value - - Cevent = INPUT_RECORD() - count = DWORD(0) - while 1: - if inputHookFunc: - call_function(inputHookFunc, ()) - status = self.ReadConsoleInputW(self.hin, - byref(Cevent), 1, byref(count)) - if status and count.value == 1: - e = event(self, Cevent) - return e - - def getkeypress(self): - u'''Return next key press event from the queue, ignoring others.''' - while 1: - e = self.get() - if e.type == u'KeyPress' and e.keycode not in key_modifiers: - log(u"console.getkeypress %s"%e) - if e.keyinfo.keyname == u'next': - self.scroll_window(12) - elif e.keyinfo.keyname == u'prior': - self.scroll_window(-12) - else: - return e - elif ((e.type == u'KeyRelease') and - (e.keyinfo == KeyPress('S', False, True, False, 'S'))): - log(u"getKeypress:%s,%s,%s"%(e.keyinfo, e.keycode, e.type)) - return e - - - def getchar(self): - u'''Get next character from queue.''' - - Cevent = INPUT_RECORD() - count = DWORD(0) - while 1: - status = self.ReadConsoleInputW(self.hin, - byref(Cevent), 1, byref(count)) - if (status and - (count.value == 1) and - (Cevent.EventType == 1) and - Cevent.Event.KeyEvent.bKeyDown): - sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode) - if len(sym) == 0: - sym = Cevent.Event.KeyEvent.uChar.AsciiChar - return sym - - def peek(self): - u'''Check event queue.''' - Cevent = INPUT_RECORD() - count = DWORD(0) - status = self.PeekConsoleInputW(self.hin, - byref(Cevent), 1, byref(count)) - if status and count == 1: - return event(self, Cevent) - - def title(self, txt=None): - u'''Set/get title.''' - if txt: - self.SetConsoleTitleW(txt) - else: - buffer = create_unicode_buffer(200) - n = self.GetConsoleTitleW(buffer, 200) - if n > 0: - return buffer.value[:n] - - def size(self, width=None, height=None): - u'''Set/get window size.''' - info = CONSOLE_SCREEN_BUFFER_INFO() - status = self.GetConsoleScreenBufferInfo(self.hout, byref(info)) - if not status: - return None - if width is not None and height is not None: - wmin = info.srWindow.Right - info.srWindow.Left + 1 - hmin = info.srWindow.Bottom - info.srWindow.Top + 1 - #print wmin, hmin - width = max(width, wmin) - height = max(height, hmin) - #print width, height - self.SetConsoleScreenBufferSize(self.hout, - self.fixcoord(width, height)) - else: - return (info.dwSize.X, info.dwSize.Y) - - def cursor(self, visible=None, size=None): - u'''Set cursor on or off.''' - info = CONSOLE_CURSOR_INFO() - if self.GetConsoleCursorInfo(self.hout, byref(info)): - if visible is not None: - info.bVisible = visible - if size is not None: - info.dwSize = size - self.SetConsoleCursorInfo(self.hout, byref(info)) - - def bell(self): - self.write(u'\007') - - def next_serial(self): - u'''Get next event serial number.''' - self.serial += 1 - return self.serial - -# add the functions from the dll to the class -for func in funcs: - setattr(Console, func, getattr(windll.kernel32, func)) - -if sys.version_info[:2] < (2, 6): - msvcrt = cdll.msvcrt -else: - msvcrt = cdll.LoadLibrary(ctypes.util.find_msvcrt()) -_strncpy = msvcrt.strncpy -_strncpy.restype = c_char_p -_strncpy.argtypes = [c_char_p, c_char_p, c_size_t] -_strdup = msvcrt._strdup -_strdup.restype = c_char_p -_strdup.argtypes = [c_char_p] - -LPVOID = c_void_p -LPCVOID = c_void_p -FARPROC = c_void_p -LPDWORD = POINTER(DWORD) - -Console.AllocConsole.restype = BOOL -Console.AllocConsole.argtypes = [] #void -Console.CreateConsoleScreenBuffer.restype = HANDLE -Console.CreateConsoleScreenBuffer.argtypes = [DWORD, DWORD, c_void_p, DWORD, LPVOID] #DWORD, DWORD, SECURITY_ATTRIBUTES*, DWORD, LPVOID -Console.FillConsoleOutputAttribute.restype = BOOL -Console.FillConsoleOutputAttribute.argtypes = [HANDLE, WORD, DWORD, c_int, LPDWORD] #HANDLE, WORD, DWORD, COORD, LPDWORD -Console.FillConsoleOutputCharacterW.restype = BOOL -Console.FillConsoleOutputCharacterW.argtypes = [HANDLE, c_ushort, DWORD, c_int, LPDWORD] #HANDLE, TCHAR, DWORD, COORD, LPDWORD -Console.FreeConsole.restype = BOOL -Console.FreeConsole.argtypes = [] #void -Console.GetConsoleCursorInfo.restype = BOOL -Console.GetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_CURSOR_INFO -Console.GetConsoleMode.restype = BOOL -Console.GetConsoleMode.argtypes = [HANDLE, LPDWORD] #HANDLE, LPDWORD -Console.GetConsoleScreenBufferInfo.restype = BOOL -Console.GetConsoleScreenBufferInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_SCREEN_BUFFER_INFO -Console.GetConsoleTitleW.restype = DWORD -Console.GetConsoleTitleW.argtypes = [c_wchar_p, DWORD] #LPTSTR , DWORD -Console.GetProcAddress.restype = FARPROC -Console.GetProcAddress.argtypes = [HMODULE, c_char_p] #HMODULE , LPCSTR -Console.GetStdHandle.restype = HANDLE -Console.GetStdHandle.argtypes = [DWORD] -Console.PeekConsoleInputW.restype = BOOL -Console.PeekConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD -Console.ReadConsoleInputW.restype = BOOL -Console.ReadConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD -Console.ScrollConsoleScreenBufferW.restype = BOOL -Console.ScrollConsoleScreenBufferW.argtypes = [HANDLE, c_void_p, c_void_p, c_int, c_void_p] #HANDLE, SMALL_RECT*, SMALL_RECT*, COORD, LPDWORD -Console.SetConsoleActiveScreenBuffer.restype = BOOL -Console.SetConsoleActiveScreenBuffer.argtypes = [HANDLE] #HANDLE -Console.SetConsoleCursorInfo.restype = BOOL -Console.SetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, CONSOLE_CURSOR_INFO* -Console.SetConsoleCursorPosition.restype = BOOL -Console.SetConsoleCursorPosition.argtypes = [HANDLE, c_int] #HANDLE, COORD -Console.SetConsoleMode.restype = BOOL -Console.SetConsoleMode.argtypes = [HANDLE, DWORD] #HANDLE, DWORD -Console.SetConsoleScreenBufferSize.restype = BOOL -Console.SetConsoleScreenBufferSize.argtypes = [HANDLE, c_int] #HANDLE, COORD -Console.SetConsoleTextAttribute.restype = BOOL -Console.SetConsoleTextAttribute.argtypes = [HANDLE, WORD] #HANDLE, WORD -Console.SetConsoleTitleW.restype = BOOL -Console.SetConsoleTitleW.argtypes = [c_wchar_p] #LPCTSTR -Console.SetConsoleWindowInfo.restype = BOOL -Console.SetConsoleWindowInfo.argtypes = [HANDLE, BOOL, c_void_p] #HANDLE, BOOL, SMALL_RECT* -Console.WriteConsoleW.restype = BOOL -Console.WriteConsoleW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD, LPVOID] #HANDLE, VOID*, DWORD, LPDWORD, LPVOID -Console.WriteConsoleOutputCharacterW.restype = BOOL -Console.WriteConsoleOutputCharacterW.argtypes = [HANDLE, c_wchar_p, DWORD, c_int, LPDWORD] #HANDLE, LPCTSTR, DWORD, COORD, LPDWORD -Console.WriteFile.restype = BOOL -Console.WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, c_void_p] #HANDLE, LPCVOID , DWORD, LPDWORD , LPOVERLAPPED - - - -from event import Event - -VkKeyScan = windll.user32.VkKeyScanA - - -class event(Event): - u'''Represent events from the console.''' - def __init__(self, console, input): - '''Initialize an event from the Windows input structure.''' - self.type = u'??' - self.serial = console.next_serial() - self.width = 0 - self.height = 0 - self.x = 0 - self.y = 0 - self.char = u'' - self.keycode = 0 - self.keysym = u'??' - self.keyinfo = None # a tuple with (control, meta, shift, keycode) for dispatch - self.width = None - - if input.EventType == KEY_EVENT: - if input.Event.KeyEvent.bKeyDown: - self.type = u"KeyPress" - else: - self.type = u"KeyRelease" - self.char = input.Event.KeyEvent.uChar.UnicodeChar - self.keycode = input.Event.KeyEvent.wVirtualKeyCode - self.state = input.Event.KeyEvent.dwControlKeyState - self.keyinfo = make_KeyPress(self.char,self.state,self.keycode) - - elif input.EventType == MOUSE_EVENT: - if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED: - self.type = u"Motion" - else: - self.type = u"Button" - self.x = input.Event.MouseEvent.dwMousePosition.X - self.y = input.Event.MouseEvent.dwMousePosition.Y - self.state = input.Event.MouseEvent.dwButtonState - elif input.EventType == WINDOW_BUFFER_SIZE_EVENT: - self.type = u"Configure" - self.width = input.Event.WindowBufferSizeEvent.dwSize.X - self.height = input.Event.WindowBufferSizeEvent.dwSize.Y - elif input.EventType == FOCUS_EVENT: - if input.Event.FocusEvent.bSetFocus: - self.type = u"FocusIn" - else: - self.type = u"FocusOut" - elif input.EventType == MENU_EVENT: - self.type = u"Menu" - self.state = input.Event.MenuEvent.dwCommandId - - -def getconsole(buffer=1): - """Get a console handle. - - If buffer is non-zero, a new console buffer is allocated and - installed. Otherwise, this returns a handle to the current - console buffer""" - - c = Console(buffer) - - return c - -# The following code uses ctypes to allow a Python callable to -# substitute for GNU readline within the Python interpreter. Calling -# raw_input or other functions that do input, inside your callable -# might be a bad idea, then again, it might work. - -# The Python callable can raise EOFError or KeyboardInterrupt and -# these will be translated into the appropriate outputs from readline -# so that they will then be translated back! - -# If the Python callable raises any other exception, a traceback will -# be printed and readline will appear to return an empty line. - -# I use ctypes to create a C-callable from a Python wrapper that -# handles the exceptions and gets the result into the right form. - -# the type for our C-callable wrapper -HOOKFUNC22 = CFUNCTYPE(c_char_p, c_char_p) -HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p) - -readline_hook = None # the python hook goes here -readline_ref = None # reference to the c-callable to keep it alive - -def hook_wrapper_23(stdin, stdout, prompt): - u'''Wrap a Python readline so it behaves like GNU readline.''' - try: - # call the Python hook - res = ensure_str(readline_hook(prompt)) - # make sure it returned the right sort of thing - if res and not isinstance(res, str): - raise TypeError, u'readline must return a string.' - except KeyboardInterrupt: - # GNU readline returns 0 on keyboard interrupt - return 0 - except EOFError: - # It returns an empty string on EOF - res = u'' - except: - print >>sys.stderr, u'Readline internal error' - traceback.print_exc() - res = u'\n' - # we have to make a copy because the caller expects to free the result - n = len(res) - p = Console.PyMem_Malloc(n + 1) - _strncpy(cast(p, c_char_p), res, n + 1) - return p - -def hook_wrapper(prompt): - u'''Wrap a Python readline so it behaves like GNU readline.''' - try: - # call the Python hook - res = ensure_str(readline_hook(prompt)) - # make sure it returned the right sort of thing - if res and not isinstance(res, str): - raise TypeError, u'readline must return a string.' - except KeyboardInterrupt: - # GNU readline returns 0 on keyboard interrupt - return 0 - except EOFError: - # It returns an empty string on EOF - res = u'' - except: - print >>sys.stderr, u'Readline internal error' - traceback.print_exc() - res = u'\n' - # we have to make a copy because the caller expects to free the result - p = _strdup(res) - return p - -def install_readline(hook): - '''Set up things for the interpreter to call - our function like GNU readline.''' - global readline_hook, readline_ref - # save the hook so the wrapper can call it - readline_hook = hook - # get the address of PyOS_ReadlineFunctionPointer so we can update it - PyOS_RFP = c_void_p.from_address(Console.GetProcAddress(sys.dllhandle, - "PyOS_ReadlineFunctionPointer")) - # save a reference to the generated C-callable so it doesn't go away - if sys.version < '2.3': - readline_ref = HOOKFUNC22(hook_wrapper) - else: - readline_ref = HOOKFUNC23(hook_wrapper_23) - # get the address of the function - func_start = c_void_p.from_address(addressof(readline_ref)).value - # write the function address into PyOS_ReadlineFunctionPointer - PyOS_RFP.value = func_start - -if __name__ == '__main__': - import time, sys - - - def p(char): - return chr(VkKeyScan(ord(char)) & 0xff) - - c = Console(0) - sys.stdout = c - sys.stderr = c - c.page() - print p("d"), p("D") - c.pos(5, 10) - c.write('hi there') - print 'some printed output' - for i in range(10): - q = c.getkeypress() - print q - del c +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Gary Bishop. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +u'''Cursor control and color for the Windows console. + +This was modeled after the C extension of the same name by Fredrik Lundh. +''' + +# primitive debug printing that won't interfere with the screen + +import sys,os +import traceback +import re + +import pyreadline.unicode_helper as unicode_helper + +from pyreadline.logger import log +from pyreadline.unicode_helper import ensure_unicode, ensure_str +from pyreadline.keysyms import make_KeyPress, KeyPress +from pyreadline.console.ansi import AnsiState,AnsiWriter + +try: + import ctypes.util + from ctypes import * + from _ctypes import call_function + from ctypes.wintypes import * +except ImportError: + raise ImportError(u"You need ctypes to run this code") + +def nolog(string): + pass + +log = nolog + + +# some constants we need +STD_INPUT_HANDLE = -10 +STD_OUTPUT_HANDLE = -11 +ENABLE_WINDOW_INPUT = 0x0008 +ENABLE_MOUSE_INPUT = 0x0010 +ENABLE_PROCESSED_INPUT = 0x0001 +WHITE = 0x7 +BLACK = 0 +MENU_EVENT = 0x0008 +KEY_EVENT = 0x0001 +MOUSE_MOVED = 0x0001 +MOUSE_EVENT = 0x0002 +WINDOW_BUFFER_SIZE_EVENT = 0x0004 +FOCUS_EVENT = 0x0010 +MENU_EVENT = 0x0008 +VK_SHIFT = 0x10 +VK_CONTROL = 0x11 +VK_MENU = 0x12 +GENERIC_READ = int(0x80000000L) +GENERIC_WRITE = 0x40000000 + +# Windows structures we'll need later +class COORD(Structure): + _fields_ = [("X", c_short), + ("Y", c_short)] + +class SMALL_RECT(Structure): + _fields_ = [("Left", c_short), + ("Top", c_short), + ("Right", c_short), + ("Bottom", c_short)] + +class CONSOLE_SCREEN_BUFFER_INFO(Structure): + _fields_ = [("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", c_short), + ("srWindow", SMALL_RECT), + ("dwMaximumWindowSize", COORD)] + +class CHAR_UNION(Union): + _fields_ = [("UnicodeChar", c_wchar), + ("AsciiChar", c_char)] + +class CHAR_INFO(Structure): + _fields_ = [("Char", CHAR_UNION), + ("Attributes", c_short)] + +class KEY_EVENT_RECORD(Structure): + _fields_ = [("bKeyDown", c_byte), + ("pad2", c_byte), + ('pad1', c_short), + ("wRepeatCount", c_short), + ("wVirtualKeyCode", c_short), + ("wVirtualScanCode", c_short), + ("uChar", CHAR_UNION), + ("dwControlKeyState", c_int)] + +class MOUSE_EVENT_RECORD(Structure): + _fields_ = [("dwMousePosition", COORD), + ("dwButtonState", c_int), + ("dwControlKeyState", c_int), + ("dwEventFlags", c_int)] + +class WINDOW_BUFFER_SIZE_RECORD(Structure): + _fields_ = [("dwSize", COORD)] + +class MENU_EVENT_RECORD(Structure): + _fields_ = [("dwCommandId", c_uint)] + +class FOCUS_EVENT_RECORD(Structure): + _fields_ = [("bSetFocus", c_byte)] + +class INPUT_UNION(Union): + _fields_ = [("KeyEvent", KEY_EVENT_RECORD), + ("MouseEvent", MOUSE_EVENT_RECORD), + ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD), + ("MenuEvent", MENU_EVENT_RECORD), + ("FocusEvent", FOCUS_EVENT_RECORD)] + +class INPUT_RECORD(Structure): + _fields_ = [("EventType", c_short), + ("Event", INPUT_UNION)] + +class CONSOLE_CURSOR_INFO(Structure): + _fields_ = [("dwSize", c_int), + ("bVisible", c_byte)] + + +# I didn't want to have to individually import these so I made a list, they are +# added to the Console class later in this file. + +funcs = [ + 'AllocConsole', + 'CreateConsoleScreenBuffer', + 'FillConsoleOutputAttribute', + 'FillConsoleOutputCharacterW', + 'FreeConsole', + 'GetConsoleCursorInfo', + 'GetConsoleMode', + 'GetConsoleScreenBufferInfo', + 'GetConsoleTitleW', + 'GetProcAddress', + 'GetStdHandle', + 'PeekConsoleInputW', + 'ReadConsoleInputW', + 'ScrollConsoleScreenBufferW', + 'SetConsoleActiveScreenBuffer', + 'SetConsoleCursorInfo', + 'SetConsoleCursorPosition', + 'SetConsoleMode', + 'SetConsoleScreenBufferSize', + 'SetConsoleTextAttribute', + 'SetConsoleTitleW', + 'SetConsoleWindowInfo', + 'WriteConsoleW', + 'WriteConsoleOutputCharacterW', + 'WriteFile', + ] + +# I don't want events for these keys, they are just a bother for my application +key_modifiers = { VK_SHIFT : 1, + VK_CONTROL : 1, + VK_MENU : 1, # alt key + 0x5b : 1, # windows key + } + +def split_block(text, size=1000): + return [text[start:start + size] for start in range(0, len(text), size)] + + + +class Console(object): + u'''Console driver for Windows. + + ''' + + def __init__(self, newbuffer=0): + u'''Initialize the Console object. + + newbuffer=1 will allocate a new buffer so the old content will be restored + on exit. + ''' + #Do I need the following line? It causes a console to be created whenever + #readline is imported into a pythonw application which seems wrong. Things + #seem to work without it... + #self.AllocConsole() + + if newbuffer: + self.hout = self.CreateConsoleScreenBuffer( + GENERIC_READ | GENERIC_WRITE, + 0, None, 1, None) + self.SetConsoleActiveScreenBuffer(self.hout) + else: + self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE) + + self.hin = self.GetStdHandle(STD_INPUT_HANDLE) + self.inmode = DWORD(0) + self.GetConsoleMode(self.hin, byref(self.inmode)) + self.SetConsoleMode(self.hin, 0xf) + info = CONSOLE_SCREEN_BUFFER_INFO() + self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + self.attr = info.wAttributes + self.saveattr = info.wAttributes # remember the initial colors + self.defaultstate = AnsiState() + self.defaultstate.winattr = info.wAttributes + self.ansiwriter = AnsiWriter(self.defaultstate) + + background = self.attr & 0xf0 + for escape in self.escape_to_color: + if self.escape_to_color[escape] is not None: + self.escape_to_color[escape] |= background + log(u'initial attr=%x' % self.attr) + self.softspace = 0 # this is for using it as a file-like object + self.serial = 0 + + self.pythondll = \ + CDLL(u'python%s%s' % (sys.version[0], sys.version[2])) + self.pythondll.PyMem_Malloc.restype = c_size_t + self.pythondll.PyMem_Malloc.argtypes = [c_size_t] + self.inputHookPtr = \ + c_void_p.from_address(addressof(self.pythondll.PyOS_InputHook)).value + setattr(Console, u'PyMem_Malloc', self.pythondll.PyMem_Malloc) + + def __del__(self): + u'''Cleanup the console when finished.''' + # I don't think this ever gets called + self.SetConsoleTextAttribute(self.hout, self.saveattr) + self.SetConsoleMode(self.hin, self.inmode) + self.FreeConsole() + + def _get_top_bot(self): + info = CONSOLE_SCREEN_BUFFER_INFO() + self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + rect = info.srWindow + top = rect.Top + bot = rect.Bottom + return top,bot + + def fixcoord(self, x, y): + u'''Return a long with x and y packed inside, + also handle negative x and y.''' + if x < 0 or y < 0: + info = CONSOLE_SCREEN_BUFFER_INFO() + self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + if x < 0: + x = info.srWindow.Right - x + y = info.srWindow.Bottom + y + + # this is a hack! ctypes won't pass structures but COORD is + # just like a long, so this works. + return c_int(y << 16 | x) + + def pos(self, x=None, y=None): + u'''Move or query the window cursor.''' + if x is None: + info = CONSOLE_SCREEN_BUFFER_INFO() + self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + return (info.dwCursorPosition.X, info.dwCursorPosition.Y) + else: + return self.SetConsoleCursorPosition(self.hout, + self.fixcoord(x, y)) + + def home(self): + u'''Move to home.''' + self.pos(0, 0) + +# Map ANSI color escape sequences into Windows Console Attributes + + terminal_escape = re.compile(u'(\001?\033\\[[0-9;]+m\002?)') + escape_parts = re.compile(u'\001?\033\\[([0-9;]+)m\002?') + escape_to_color = { u'0;30': 0x0, #black + u'0;31': 0x4, #red + u'0;32': 0x2, #green + u'0;33': 0x4+0x2, #brown? + u'0;34': 0x1, #blue + u'0;35': 0x1+0x4, #purple + u'0;36': 0x2+0x4, #cyan + u'0;37': 0x1+0x2+0x4, #grey + u'1;30': 0x1+0x2+0x4, #dark gray + u'1;31': 0x4+0x8, #red + u'1;32': 0x2+0x8, #light green + u'1;33': 0x4+0x2+0x8, #yellow + u'1;34': 0x1+0x8, #light blue + u'1;35': 0x1+0x4+0x8, #light purple + u'1;36': 0x1+0x2+0x8, #light cyan + u'1;37': 0x1+0x2+0x4+0x8, #white + u'0': None, + } + + # This pattern should match all characters that change the cursor position differently + # than a normal character. + motion_char_re = re.compile(u'([\n\r\t\010\007])') + + def write_scrolling(self, text, attr=None): + u'''write text at current cursor position while watching for scrolling. + + If the window scrolls because you are at the bottom of the screen + buffer, all positions that you are storing will be shifted by the + scroll amount. For example, I remember the cursor position of the + prompt so that I can redraw the line but if the window scrolls, + the remembered position is off. + + This variant of write tries to keep track of the cursor position + so that it will know when the screen buffer is scrolled. It + returns the number of lines that the buffer scrolled. + + ''' + x, y = self.pos() + w, h = self.size() + scroll = 0 # the result + # split the string into ordinary characters and funny characters + chunks = self.motion_char_re.split(text) + for chunk in chunks: + n = self.write_color(chunk, attr) + if len(chunk) == 1: # the funny characters will be alone + if chunk[0] == u'\n': # newline + x = 0 + y += 1 + elif chunk[0] == u'\r': # carriage return + x = 0 + elif chunk[0] == u'\t': # tab + x = 8 * (int(x / 8) + 1) + if x > w: # newline + x -= w + y += 1 + elif chunk[0] == u'\007': # bell + pass + elif chunk[0] == u'\010': + x -= 1 + if x < 0: + y -= 1 # backed up 1 line + else: # ordinary character + x += 1 + if x == w: # wrap + x = 0 + y += 1 + if y == h: # scroll + scroll += 1 + y = h - 1 + else: # chunk of ordinary characters + x += n + l = int(x / w) # lines we advanced + x = x % w # new x value + y += l + if y >= h: # scroll + scroll += y - h + 1 + y = h - 1 + return scroll + + def write_color(self, text, attr=None): + text = ensure_unicode(text) + n, res= self.ansiwriter.write_color(text, attr) + junk = DWORD(0) + for attr,chunk in res: + log(u"console.attr:%s"%unicode(attr)) + log(u"console.chunk:%s"%unicode(chunk)) + self.SetConsoleTextAttribute(self.hout, attr.winattr) + for short_chunk in split_block(chunk): + self.WriteConsoleW(self.hout, short_chunk, + len(short_chunk), byref(junk), None) + return n + + def write_plain(self, text, attr=None): + u'''write text at current cursor position.''' + text = ensure_unicode(text) + log(u'write("%s", %s)' %(text, attr)) + if attr is None: + attr = self.attr + junk = DWORD(0) + self.SetConsoleTextAttribute(self.hout, attr) + for short_chunk in split_block(chunk): + self.WriteConsoleW(self.hout, ensure_unicode(short_chunk), + len(short_chunk), byref(junk), None) + return len(text) + + #This function must be used to ensure functioning with EMACS + #Emacs sets the EMACS environment variable + if os.environ.has_key(u"EMACS"): + def write_color(self, text, attr=None): + text = ensure_str(text) + junk = DWORD(0) + self.WriteFile(self.hout, text, len(text), byref(junk), None) + return len(text) + write_plain = write_color + + # make this class look like a file object + def write(self, text): + text = ensure_unicode(text) + log(u'write("%s")' % text) + return self.write_color(text) + + #write = write_scrolling + + def isatty(self): + return True + + def flush(self): + pass + + def page(self, attr=None, fill=u' '): + u'''Fill the entire screen.''' + if attr is None: + attr = self.attr + if len(fill) != 1: + raise ValueError + info = CONSOLE_SCREEN_BUFFER_INFO() + self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0: + self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0)) + + w = info.dwSize.X + n = DWORD(0) + for y in range(info.dwSize.Y): + self.FillConsoleOutputAttribute(self.hout, attr, + w, self.fixcoord(0, y), byref(n)) + self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]), + w, self.fixcoord(0, y), byref(n)) + + self.attr = attr + + def text(self, x, y, text, attr=None): + u'''Write text at the given position.''' + if attr is None: + attr = self.attr + + pos = self.fixcoord(x, y) + n = DWORD(0) + self.WriteConsoleOutputCharacterW(self.hout, text, + len(text), pos, byref(n)) + self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n)) + + def clear_to_end_of_window(self): + top, bot = self._get_top_bot() + pos = self.pos() + w, h = self.size() + self.rectangle( (pos[0], pos[1], w, pos[1] + 1)) + if pos[1] < bot: + self.rectangle((0, pos[1] + 1, w, bot + 1)) + + def rectangle(self, rect, attr=None, fill=u' '): + u'''Fill Rectangle.''' + x0, y0, x1, y1 = rect + n = DWORD(0) + if attr is None: + attr = self.attr + for y in range(y0, y1): + pos = self.fixcoord(x0, y) + self.FillConsoleOutputAttribute(self.hout, attr, x1 - x0, + pos, byref(n)) + self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]), x1 - x0, + pos, byref(n)) + + def scroll(self, rect, dx, dy, attr=None, fill=' '): + u'''Scroll a rectangle.''' + if attr is None: + attr = self.attr + x0, y0, x1, y1 = rect + source = SMALL_RECT(x0, y0, x1 - 1, y1 - 1) + dest = self.fixcoord(x0 + dx, y0 + dy) + style = CHAR_INFO() + style.Char.AsciiChar = ensure_str(fill[0]) + style.Attributes = attr + + return self.ScrollConsoleScreenBufferW(self.hout, byref(source), + byref(source), dest, byref(style)) + + def scroll_window(self, lines): + u'''Scroll the window by the indicated number of lines.''' + info = CONSOLE_SCREEN_BUFFER_INFO() + self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + rect = info.srWindow + log(u'sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom)) + top = rect.Top + lines + bot = rect.Bottom + lines + h = bot - top + maxbot = info.dwSize.Y-1 + if top < 0: + top = 0 + bot = h + if bot > maxbot: + bot = maxbot + top = bot - h + + nrect = SMALL_RECT() + nrect.Top = top + nrect.Bottom = bot + nrect.Left = rect.Left + nrect.Right = rect.Right + log(u'sn: top=%d bot=%d' % (top, bot)) + r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect)) + log(u'r=%d' % r) + + def get(self): + u'''Get next event from queue.''' + inputHookFunc = c_void_p.from_address(self.inputHookPtr).value + + Cevent = INPUT_RECORD() + count = DWORD(0) + while 1: + if inputHookFunc: + call_function(inputHookFunc, ()) + status = self.ReadConsoleInputW(self.hin, + byref(Cevent), 1, byref(count)) + if status and count.value == 1: + e = event(self, Cevent) + return e + + def getkeypress(self): + u'''Return next key press event from the queue, ignoring others.''' + while 1: + e = self.get() + if e.type == u'KeyPress' and e.keycode not in key_modifiers: + log(u"console.getkeypress %s"%e) + if e.keyinfo.keyname == u'next': + self.scroll_window(12) + elif e.keyinfo.keyname == u'prior': + self.scroll_window(-12) + else: + return e + elif ((e.type == u'KeyRelease') and + (e.keyinfo == KeyPress('S', False, True, False, 'S'))): + log(u"getKeypress:%s,%s,%s"%(e.keyinfo, e.keycode, e.type)) + return e + + + def getchar(self): + u'''Get next character from queue.''' + + Cevent = INPUT_RECORD() + count = DWORD(0) + while 1: + status = self.ReadConsoleInputW(self.hin, + byref(Cevent), 1, byref(count)) + if (status and + (count.value == 1) and + (Cevent.EventType == 1) and + Cevent.Event.KeyEvent.bKeyDown): + sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode) + if len(sym) == 0: + sym = Cevent.Event.KeyEvent.uChar.AsciiChar + return sym + + def peek(self): + u'''Check event queue.''' + Cevent = INPUT_RECORD() + count = DWORD(0) + status = self.PeekConsoleInputW(self.hin, + byref(Cevent), 1, byref(count)) + if status and count == 1: + return event(self, Cevent) + + def title(self, txt=None): + u'''Set/get title.''' + if txt: + self.SetConsoleTitleW(txt) + else: + buffer = create_unicode_buffer(200) + n = self.GetConsoleTitleW(buffer, 200) + if n > 0: + return buffer.value[:n] + + def size(self, width=None, height=None): + u'''Set/get window size.''' + info = CONSOLE_SCREEN_BUFFER_INFO() + status = self.GetConsoleScreenBufferInfo(self.hout, byref(info)) + if not status: + return None + if width is not None and height is not None: + wmin = info.srWindow.Right - info.srWindow.Left + 1 + hmin = info.srWindow.Bottom - info.srWindow.Top + 1 + #print wmin, hmin + width = max(width, wmin) + height = max(height, hmin) + #print width, height + self.SetConsoleScreenBufferSize(self.hout, + self.fixcoord(width, height)) + else: + return (info.dwSize.X, info.dwSize.Y) + + def cursor(self, visible=None, size=None): + u'''Set cursor on or off.''' + info = CONSOLE_CURSOR_INFO() + if self.GetConsoleCursorInfo(self.hout, byref(info)): + if visible is not None: + info.bVisible = visible + if size is not None: + info.dwSize = size + self.SetConsoleCursorInfo(self.hout, byref(info)) + + def bell(self): + self.write(u'\007') + + def next_serial(self): + u'''Get next event serial number.''' + self.serial += 1 + return self.serial + +# add the functions from the dll to the class +for func in funcs: + setattr(Console, func, getattr(windll.kernel32, func)) + +if sys.version_info[:2] < (2, 6): + msvcrt = cdll.msvcrt +else: + msvcrt = cdll.LoadLibrary(ctypes.util.find_msvcrt()) +_strncpy = msvcrt.strncpy +_strncpy.restype = c_char_p +_strncpy.argtypes = [c_char_p, c_char_p, c_size_t] +_strdup = msvcrt._strdup +_strdup.restype = c_char_p +_strdup.argtypes = [c_char_p] + +LPVOID = c_void_p +LPCVOID = c_void_p +FARPROC = c_void_p +LPDWORD = POINTER(DWORD) + +Console.AllocConsole.restype = BOOL +Console.AllocConsole.argtypes = [] #void +Console.CreateConsoleScreenBuffer.restype = HANDLE +Console.CreateConsoleScreenBuffer.argtypes = [DWORD, DWORD, c_void_p, DWORD, LPVOID] #DWORD, DWORD, SECURITY_ATTRIBUTES*, DWORD, LPVOID +Console.FillConsoleOutputAttribute.restype = BOOL +Console.FillConsoleOutputAttribute.argtypes = [HANDLE, WORD, DWORD, c_int, LPDWORD] #HANDLE, WORD, DWORD, COORD, LPDWORD +Console.FillConsoleOutputCharacterW.restype = BOOL +Console.FillConsoleOutputCharacterW.argtypes = [HANDLE, c_ushort, DWORD, c_int, LPDWORD] #HANDLE, TCHAR, DWORD, COORD, LPDWORD +Console.FreeConsole.restype = BOOL +Console.FreeConsole.argtypes = [] #void +Console.GetConsoleCursorInfo.restype = BOOL +Console.GetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_CURSOR_INFO +Console.GetConsoleMode.restype = BOOL +Console.GetConsoleMode.argtypes = [HANDLE, LPDWORD] #HANDLE, LPDWORD +Console.GetConsoleScreenBufferInfo.restype = BOOL +Console.GetConsoleScreenBufferInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_SCREEN_BUFFER_INFO +Console.GetConsoleTitleW.restype = DWORD +Console.GetConsoleTitleW.argtypes = [c_wchar_p, DWORD] #LPTSTR , DWORD +Console.GetProcAddress.restype = FARPROC +Console.GetProcAddress.argtypes = [HMODULE, c_char_p] #HMODULE , LPCSTR +Console.GetStdHandle.restype = HANDLE +Console.GetStdHandle.argtypes = [DWORD] +Console.PeekConsoleInputW.restype = BOOL +Console.PeekConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD +Console.ReadConsoleInputW.restype = BOOL +Console.ReadConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD +Console.ScrollConsoleScreenBufferW.restype = BOOL +Console.ScrollConsoleScreenBufferW.argtypes = [HANDLE, c_void_p, c_void_p, c_int, c_void_p] #HANDLE, SMALL_RECT*, SMALL_RECT*, COORD, LPDWORD +Console.SetConsoleActiveScreenBuffer.restype = BOOL +Console.SetConsoleActiveScreenBuffer.argtypes = [HANDLE] #HANDLE +Console.SetConsoleCursorInfo.restype = BOOL +Console.SetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, CONSOLE_CURSOR_INFO* +Console.SetConsoleCursorPosition.restype = BOOL +Console.SetConsoleCursorPosition.argtypes = [HANDLE, c_int] #HANDLE, COORD +Console.SetConsoleMode.restype = BOOL +Console.SetConsoleMode.argtypes = [HANDLE, DWORD] #HANDLE, DWORD +Console.SetConsoleScreenBufferSize.restype = BOOL +Console.SetConsoleScreenBufferSize.argtypes = [HANDLE, c_int] #HANDLE, COORD +Console.SetConsoleTextAttribute.restype = BOOL +Console.SetConsoleTextAttribute.argtypes = [HANDLE, WORD] #HANDLE, WORD +Console.SetConsoleTitleW.restype = BOOL +Console.SetConsoleTitleW.argtypes = [c_wchar_p] #LPCTSTR +Console.SetConsoleWindowInfo.restype = BOOL +Console.SetConsoleWindowInfo.argtypes = [HANDLE, BOOL, c_void_p] #HANDLE, BOOL, SMALL_RECT* +Console.WriteConsoleW.restype = BOOL +Console.WriteConsoleW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD, LPVOID] #HANDLE, VOID*, DWORD, LPDWORD, LPVOID +Console.WriteConsoleOutputCharacterW.restype = BOOL +Console.WriteConsoleOutputCharacterW.argtypes = [HANDLE, c_wchar_p, DWORD, c_int, LPDWORD] #HANDLE, LPCTSTR, DWORD, COORD, LPDWORD +Console.WriteFile.restype = BOOL +Console.WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, c_void_p] #HANDLE, LPCVOID , DWORD, LPDWORD , LPOVERLAPPED + + + +from event import Event + +VkKeyScan = windll.user32.VkKeyScanA + + +class event(Event): + u'''Represent events from the console.''' + def __init__(self, console, input): + '''Initialize an event from the Windows input structure.''' + self.type = u'??' + self.serial = console.next_serial() + self.width = 0 + self.height = 0 + self.x = 0 + self.y = 0 + self.char = u'' + self.keycode = 0 + self.keysym = u'??' + self.keyinfo = None # a tuple with (control, meta, shift, keycode) for dispatch + self.width = None + + if input.EventType == KEY_EVENT: + if input.Event.KeyEvent.bKeyDown: + self.type = u"KeyPress" + else: + self.type = u"KeyRelease" + self.char = input.Event.KeyEvent.uChar.UnicodeChar + self.keycode = input.Event.KeyEvent.wVirtualKeyCode + self.state = input.Event.KeyEvent.dwControlKeyState + self.keyinfo = make_KeyPress(self.char,self.state,self.keycode) + + elif input.EventType == MOUSE_EVENT: + if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED: + self.type = u"Motion" + else: + self.type = u"Button" + self.x = input.Event.MouseEvent.dwMousePosition.X + self.y = input.Event.MouseEvent.dwMousePosition.Y + self.state = input.Event.MouseEvent.dwButtonState + elif input.EventType == WINDOW_BUFFER_SIZE_EVENT: + self.type = u"Configure" + self.width = input.Event.WindowBufferSizeEvent.dwSize.X + self.height = input.Event.WindowBufferSizeEvent.dwSize.Y + elif input.EventType == FOCUS_EVENT: + if input.Event.FocusEvent.bSetFocus: + self.type = u"FocusIn" + else: + self.type = u"FocusOut" + elif input.EventType == MENU_EVENT: + self.type = u"Menu" + self.state = input.Event.MenuEvent.dwCommandId + + +def getconsole(buffer=1): + """Get a console handle. + + If buffer is non-zero, a new console buffer is allocated and + installed. Otherwise, this returns a handle to the current + console buffer""" + + c = Console(buffer) + + return c + +# The following code uses ctypes to allow a Python callable to +# substitute for GNU readline within the Python interpreter. Calling +# raw_input or other functions that do input, inside your callable +# might be a bad idea, then again, it might work. + +# The Python callable can raise EOFError or KeyboardInterrupt and +# these will be translated into the appropriate outputs from readline +# so that they will then be translated back! + +# If the Python callable raises any other exception, a traceback will +# be printed and readline will appear to return an empty line. + +# I use ctypes to create a C-callable from a Python wrapper that +# handles the exceptions and gets the result into the right form. + +# the type for our C-callable wrapper +HOOKFUNC22 = CFUNCTYPE(c_char_p, c_char_p) +HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p) + +readline_hook = None # the python hook goes here +readline_ref = None # reference to the c-callable to keep it alive + +def hook_wrapper_23(stdin, stdout, prompt): + u'''Wrap a Python readline so it behaves like GNU readline.''' + try: + # call the Python hook + res = ensure_str(readline_hook(prompt)) + # make sure it returned the right sort of thing + if res and not isinstance(res, str): + raise TypeError, u'readline must return a string.' + except KeyboardInterrupt: + # GNU readline returns 0 on keyboard interrupt + return 0 + except EOFError: + # It returns an empty string on EOF + res = u'' + except: + print >>sys.stderr, u'Readline internal error' + traceback.print_exc() + res = u'\n' + # we have to make a copy because the caller expects to free the result + n = len(res) + p = Console.PyMem_Malloc(n + 1) + _strncpy(cast(p, c_char_p), res, n + 1) + return p + +def hook_wrapper(prompt): + u'''Wrap a Python readline so it behaves like GNU readline.''' + try: + # call the Python hook + res = ensure_str(readline_hook(prompt)) + # make sure it returned the right sort of thing + if res and not isinstance(res, str): + raise TypeError, u'readline must return a string.' + except KeyboardInterrupt: + # GNU readline returns 0 on keyboard interrupt + return 0 + except EOFError: + # It returns an empty string on EOF + res = u'' + except: + print >>sys.stderr, u'Readline internal error' + traceback.print_exc() + res = u'\n' + # we have to make a copy because the caller expects to free the result + p = _strdup(res) + return p + +def install_readline(hook): + '''Set up things for the interpreter to call + our function like GNU readline.''' + global readline_hook, readline_ref + # save the hook so the wrapper can call it + readline_hook = hook + # get the address of PyOS_ReadlineFunctionPointer so we can update it + PyOS_RFP = c_void_p.from_address(Console.GetProcAddress(sys.dllhandle, + "PyOS_ReadlineFunctionPointer")) + # save a reference to the generated C-callable so it doesn't go away + if sys.version < '2.3': + readline_ref = HOOKFUNC22(hook_wrapper) + else: + readline_ref = HOOKFUNC23(hook_wrapper_23) + # get the address of the function + func_start = c_void_p.from_address(addressof(readline_ref)).value + # write the function address into PyOS_ReadlineFunctionPointer + PyOS_RFP.value = func_start + +if __name__ == '__main__': + import time, sys + + + def p(char): + return chr(VkKeyScan(ord(char)) & 0xff) + + c = Console(0) + sys.stdout = c + sys.stderr = c + c.page() + print p("d"), p("D") + c.pos(5, 10) + c.write('hi there') + print 'some printed output' + for i in range(10): + q = c.getkeypress() + print q + del c diff --git a/pyreadline/console/console_attributes.py b/pyreadline/console/console_attributes.py index 1089fc8..9262331 100644 --- a/pyreadline/console/console_attributes.py +++ b/pyreadline/console/console_attributes.py @@ -1,16 +1,16 @@ - -FOREGROUND_BLUE = 0x0001 -FOREGROUND_GREEN = 0x0002 -FOREGROUND_RED = 0x0004 -FOREGROUND_INTENSITY = 0x0008 -BACKGROUND_BLUE = 0x0010 -BACKGROUND_GREEN = 0x0020 -BACKGROUND_RED = 0x0040 -BACKGROUND_INTENSITY = 0x0080 -COMMON_LVB_LEADING_BYTE = 0x0100 -COMMON_LVB_TRAILING_BYTE = 0x0200 -COMMON_LVB_GRID_HORIZONTAL= 0x0400 -COMMON_LVB_GRID_LVERTICAL = 0x0800 -COMMON_LVB_GRID_RVERTICAL = 0x1000 -COMMON_LVB_REVERSE_VIDEO = 0x2000 -COMMON_LVB_UNDERSCORE = 0x4000 + +FOREGROUND_BLUE = 0x0001 +FOREGROUND_GREEN = 0x0002 +FOREGROUND_RED = 0x0004 +FOREGROUND_INTENSITY = 0x0008 +BACKGROUND_BLUE = 0x0010 +BACKGROUND_GREEN = 0x0020 +BACKGROUND_RED = 0x0040 +BACKGROUND_INTENSITY = 0x0080 +COMMON_LVB_LEADING_BYTE = 0x0100 +COMMON_LVB_TRAILING_BYTE = 0x0200 +COMMON_LVB_GRID_HORIZONTAL= 0x0400 +COMMON_LVB_GRID_LVERTICAL = 0x0800 +COMMON_LVB_GRID_RVERTICAL = 0x1000 +COMMON_LVB_REVERSE_VIDEO = 0x2000 +COMMON_LVB_UNDERSCORE = 0x4000 diff --git a/pyreadline/console/consolebase.py b/pyreadline/console/consolebase.py index d15350e..b165664 100644 --- a/pyreadline/console/consolebase.py +++ b/pyreadline/console/consolebase.py @@ -1,52 +1,52 @@ -class baseconsole: - def __init__(self): - pass - - def bell(self): - raise NotImplementedError - - def pos(self, x=None, y=None): - u'''Move or query the window cursor.''' - raise NotImplementedError - - def size(self): - raise NotImplementedError - - def rectangle(self, rect, attr=None, fill=u' '): - u'''Fill Rectangle.''' - raise NotImplementedError - - def write_scrolling(self, text, attr=None): - u'''write text at current cursor position while watching for scrolling. - - If the window scrolls because you are at the bottom of the screen - buffer, all positions that you are storing will be shifted by the - scroll amount. For example, I remember the cursor position of the - prompt so that I can redraw the line but if the window scrolls, - the remembered position is off. - - This variant of write tries to keep track of the cursor position - so that it will know when the screen buffer is scrolled. It - returns the number of lines that the buffer scrolled. - - ''' - raise NotImplementedError - - def getkeypress(self): - u'''Return next key press event from the queue, ignoring others.''' - raise NotImplementedError - - def write(self, text): - raise NotImplementedError - - def page(self, attr=None, fill=' '): - u'''Fill the entire screen.''' - raise NotImplementedError - - def isatty(self): - return True - - def flush(self): - pass - +class baseconsole: + def __init__(self): + pass + + def bell(self): + raise NotImplementedError + + def pos(self, x=None, y=None): + u'''Move or query the window cursor.''' + raise NotImplementedError + + def size(self): + raise NotImplementedError + + def rectangle(self, rect, attr=None, fill=u' '): + u'''Fill Rectangle.''' + raise NotImplementedError + + def write_scrolling(self, text, attr=None): + u'''write text at current cursor position while watching for scrolling. + + If the window scrolls because you are at the bottom of the screen + buffer, all positions that you are storing will be shifted by the + scroll amount. For example, I remember the cursor position of the + prompt so that I can redraw the line but if the window scrolls, + the remembered position is off. + + This variant of write tries to keep track of the cursor position + so that it will know when the screen buffer is scrolled. It + returns the number of lines that the buffer scrolled. + + ''' + raise NotImplementedError + + def getkeypress(self): + u'''Return next key press event from the queue, ignoring others.''' + raise NotImplementedError + + def write(self, text): + raise NotImplementedError + + def page(self, attr=None, fill=' '): + u'''Fill the entire screen.''' + raise NotImplementedError + + def isatty(self): + return True + + def flush(self): + pass + \ No newline at end of file diff --git a/pyreadline/console/event.py b/pyreadline/console/event.py index c03e503..6c5c1a4 100644 --- a/pyreadline/console/event.py +++ b/pyreadline/console/event.py @@ -1,29 +1,29 @@ -class Event(object): - u'''Represent events from the console.''' - def __init__(self, console, input): - pass - - def __repr__(self): - u'''Display an event for debugging.''' - if self.type in [u'KeyPress', u'KeyRelease']: - chr = self.char - if ord(chr) -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -u'''Cursor control and color for the .NET console. -''' - -# -# Ironpython requires a patch to work do: -# -# In file PythonCommandLine.cs patch line: -# class PythonCommandLine -# { - -# to: -# public class PythonCommandLine -# { -# -# -# -# primitive debug printing that won't interfere with the screen - -import clr,sys -clr.AddReferenceToFileAndPath(sys.executable) -import IronPythonConsole - -import sys -import re -import os - -import System - -from event import Event -from pyreadline.logger import log - -from pyreadline.keysyms import \ - make_keysym, make_keyinfo, make_KeyPress, make_KeyPress_from_keydescr -from pyreadline.console.ansi import AnsiState -color = System.ConsoleColor - -ansicolor={u"0;30": color.Black, - u"0;31": color.DarkRed, - u"0;32": color.DarkGreen, - u"0;33": color.DarkYellow, - u"0;34": color.DarkBlue, - u"0;35": color.DarkMagenta, - u"0;36": color.DarkCyan, - u"0;37": color.DarkGray, - u"1;30": color.Gray, - u"1;31": color.Red, - u"1;32": color.Green, - u"1;33": color.Yellow, - u"1;34": color.Blue, - u"1;35": color.Magenta, - u"1;36": color.Cyan, - u"1;37": color.White - } - -winattr = {u"black" : 0, u"darkgray" : 0+8, - u"darkred" : 4, u"red" : 4+8, - u"darkgreen" : 2, u"green" : 2+8, - u"darkyellow" : 6, u"yellow" : 6+8, - u"darkblue" : 1, u"blue" : 1+8, - u"darkmagenta" : 5, u"magenta" : 5+8, - u"darkcyan" : 3, u"cyan" : 3+8, - u"gray" : 7, u"white" : 7+8} - -class Console(object): - u'''Console driver for Windows. - - ''' - - def __init__(self, newbuffer=0): - u'''Initialize the Console object. - - newbuffer=1 will allocate a new buffer so the old content will be restored - on exit. - ''' - self.serial = 0 - self.attr = System.Console.ForegroundColor - self.saveattr = winattr[str(System.Console.ForegroundColor).lower()] - self.savebg = System.Console.BackgroundColor - log(u'initial attr=%s' % self.attr) - - def _get(self): - top = System.Console.WindowTop - log(u"WindowTop:%s"%top) - return top - - def _set(self, value): - top = System.Console.WindowTop - log(u"Set WindowTop:old:%s,new:%s"%(top, value)) - - WindowTop = property(_get, _set) - del _get, _set - - def __del__(self): - u'''Cleanup the console when finished.''' - # I don't think this ever gets called - pass - - def pos(self, x=None, y=None): - u'''Move or query the window cursor.''' - if x is not None: - System.Console.CursorLeft=x - else: - x = System.Console.CursorLeft - if y is not None: - System.Console.CursorTop=y - else: - y = System.Console.CursorTop - return x, y - - def home(self): - u'''Move to home.''' - self.pos(0, 0) - -# Map ANSI color escape sequences into Windows Console Attributes - - terminal_escape = re.compile(u'(\001?\033\\[[0-9;]*m\002?)') - escape_parts = re.compile(u'\001?\033\\[([0-9;]*)m\002?') - - # This pattern should match all characters that change the cursor position differently - # than a normal character. - motion_char_re = re.compile(u'([\n\r\t\010\007])') - - def write_scrolling(self, text, attr=None): - u'''write text at current cursor position while watching for scrolling. - - If the window scrolls because you are at the bottom of the screen - buffer, all positions that you are storing will be shifted by the - scroll amount. For example, I remember the cursor position of the - prompt so that I can redraw the line but if the window scrolls, - the remembered position is off. - - This variant of write tries to keep track of the cursor position - so that it will know when the screen buffer is scrolled. It - returns the number of lines that the buffer scrolled. - - ''' - x, y = self.pos() - w, h = self.size() - scroll = 0 # the result - - # split the string into ordinary characters and funny characters - chunks = self.motion_char_re.split(text) - for chunk in chunks: - n = self.write_color(chunk, attr) - if len(chunk) == 1: # the funny characters will be alone - if chunk[0] == u'\n': # newline - x = 0 - y += 1 - elif chunk[0] == u'\r': # carriage return - x = 0 - elif chunk[0] == u'\t': # tab - x = 8 * (int(x / 8) + 1) - if x > w: # newline - x -= w - y += 1 - elif chunk[0] == u'\007': # bell - pass - elif chunk[0] == u'\010': - x -= 1 - if x < 0: - y -= 1 # backed up 1 line - else: # ordinary character - x += 1 - if x == w: # wrap - x = 0 - y += 1 - if y == h: # scroll - scroll += 1 - y = h - 1 - else: # chunk of ordinary characters - x += n - l = int(x / w) # lines we advanced - x = x % w # new x value - y += l - if y >= h: # scroll - scroll += y - h + 1 - y = h - 1 - return scroll - - trtable = {0 : color.Black, 4 : color.DarkRed, 2 : color.DarkGreen, - 6 : color.DarkYellow, 1 : color.DarkBlue, 5 : color.DarkMagenta, - 3 : color.DarkCyan, 7 : color.Gray, 8 : color.DarkGray, - 4+8 : color.Red, 2+8 : color.Green, 6+8 : color.Yellow, - 1+8 : color.Blue, 5+8 : color.Magenta,3+8 : color.Cyan, - 7+8 : color.White} - - def write_color(self, text, attr=None): - '''write text at current cursor position and interpret color escapes. - - return the number of characters written. - ''' - log(u'write_color("%s", %s)' % (text, attr)) - chunks = self.terminal_escape.split(text) - log(u'chunks=%s' % repr(chunks)) - bg = self.savebg - n = 0 # count the characters we actually write, omitting the escapes - if attr is None:#use attribute from initial console - attr = self.attr - try: - fg = self.trtable[(0x000f&attr)] - bg = self.trtable[(0x00f0&attr)>>4] - except TypeError: - fg = attr - - for chunk in chunks: - m = self.escape_parts.match(chunk) - if m: - log(m.group(1)) - attr = ansicolor.get(m.group(1), self.attr) - n += len(chunk) - System.Console.ForegroundColor = fg - System.Console.BackgroundColor = bg - System.Console.Write(chunk) - return n - - def write_plain(self, text, attr=None): - u'''write text at current cursor position.''' - log(u'write("%s", %s)' %(text, attr)) - if attr is None: - attr = self.attr - n = c_int(0) - self.SetConsoleTextAttribute(self.hout, attr) - self.WriteConsoleA(self.hout, text, len(text), byref(n), None) - return len(text) - - if os.environ.has_key(u"EMACS"): - def write_color(self, text, attr=None): - junk = c_int(0) - self.WriteFile(self.hout, text, len(text), byref(junk), None) - return len(text) - write_plain = write_color - - # make this class look like a file object - def write(self, text): - log(u'write("%s")' % text) - return self.write_color(text) - - #write = write_scrolling - - def isatty(self): - return True - - def flush(self): - pass - - def page(self, attr=None, fill=u' '): - u'''Fill the entire screen.''' - System.Console.Clear() - - def text(self, x, y, text, attr=None): - u'''Write text at the given position.''' - self.pos(x, y) - self.write_color(text, attr) - - def clear_to_end_of_window(self): - oldtop = self.WindowTop - lastline = self.WindowTop+System.Console.WindowHeight - pos = self.pos() - w, h = self.size() - length = w - pos[0] + min((lastline - pos[1] - 1), 5) * w - 1 - self.write_color(length * u" ") - self.pos(*pos) - self.WindowTop = oldtop - - def rectangle(self, rect, attr=None, fill=u' '): - u'''Fill Rectangle.''' - oldtop = self.WindowTop - oldpos = self.pos() - #raise NotImplementedError - x0, y0, x1, y1 = rect - if attr is None: - attr = self.attr - if fill: - rowfill = fill[:1] * abs(x1 - x0) - else: - rowfill = u' ' * abs(x1 - x0) - for y in range(y0, y1): - System.Console.SetCursorPosition(x0, y) - self.write_color(rowfill, attr) - self.pos(*oldpos) - - def scroll(self, rect, dx, dy, attr=None, fill=' '): - u'''Scroll a rectangle.''' - raise NotImplementedError - - def scroll_window(self, lines): - u'''Scroll the window by the indicated number of lines.''' - top = self.WindowTop + lines - if top < 0: - top = 0 - if top + System.Console.WindowHeight > System.Console.BufferHeight: - top = System.Console.BufferHeight - self.WindowTop = top - - def getkeypress(self): - u'''Return next key press event from the queue, ignoring others.''' - ck = System.ConsoleKey - while 1: - e = System.Console.ReadKey(True) - if e.Key == System.ConsoleKey.PageDown: #PageDown - self.scroll_window(12) - elif e.Key == System.ConsoleKey.PageUp:#PageUp - self.scroll_window(-12) - elif str(e.KeyChar) == u"\000":#Drop deadkeys - log(u"Deadkey: %s"%e) - return event(self, e) - else: - return event(self, e) - - def title(self, txt=None): - u'''Set/get title.''' - if txt: - System.Console.Title = txt - else: - return System.Console.Title - - def size(self, width=None, height=None): - u'''Set/get window size.''' - sc = System.Console - if width is not None and height is not None: - sc.BufferWidth, sc.BufferHeight = width,height - else: - return sc.BufferWidth, sc.BufferHeight - - if width is not None and height is not None: - sc.WindowWidth, sc.WindowHeight = width,height - else: - return sc.WindowWidth - 1, sc.WindowHeight - 1 - - def cursor(self, visible=True, size=None): - u'''Set cursor on or off.''' - System.Console.CursorVisible = visible - - def bell(self): - System.Console.Beep() - - def next_serial(self): - u'''Get next event serial number.''' - self.serial += 1 - return self.serial - -class event(Event): - u'''Represent events from the console.''' - def __init__(self, console, input): - u'''Initialize an event from the Windows input structure.''' - self.type = u'??' - self.serial = console.next_serial() - self.width = 0 - self.height = 0 - self.x = 0 - self.y = 0 - self.char = str(input.KeyChar) - self.keycode = input.Key - self.state = input.Modifiers - log(u"%s,%s,%s"%(input.Modifiers, input.Key, input.KeyChar)) - self.type = "KeyRelease" - self.keysym = make_keysym(self.keycode) - self.keyinfo = make_KeyPress(self.char, self.state, self.keycode) - -def make_event_from_keydescr(keydescr): - def input(): - return 1 - input.KeyChar = u"a" - input.Key = System.ConsoleKey.A - input.Modifiers = System.ConsoleModifiers.Shift - input.next_serial = input - e = event(input,input) - del input.next_serial - keyinfo = make_KeyPress_from_keydescr(keydescr) - e.keyinfo = keyinfo - return e - -CTRL_C_EVENT=make_event_from_keydescr(u"Control-c") - -def install_readline(hook): - def hook_wrap(): - try: - res = hook() - except KeyboardInterrupt,x: #this exception does not seem to be caught - res = u"" - except EOFError: - return None - if res[-1:] == u"\n": - return res[:-1] - else: - return res - class IronPythonWrapper(IronPythonConsole.IConsole): - def ReadLine(self, autoIndentSize): - return hook_wrap() - def Write(self, text, style): - System.Console.Write(text) - def WriteLine(self, text, style): - System.Console.WriteLine(text) - IronPythonConsole.PythonCommandLine.MyConsole = IronPythonWrapper() - - - -if __name__ == u'__main__': - import time, sys - c = Console(0) - sys.stdout = c - sys.stderr = c - c.page() - c.pos(5, 10) - c.write(u'hi there') - c.title(u"Testing console") -# c.bell() - print - print u"size", c.size() - print u' some printed output' - for i in range(10): - e = c.getkeypress() - print e.Key, chr(e.KeyChar), ord(e.KeyChar), e.Modifiers - del c - - System.Console.Clear() +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Gary Bishop. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +u'''Cursor control and color for the .NET console. +''' + +# +# Ironpython requires a patch to work do: +# +# In file PythonCommandLine.cs patch line: +# class PythonCommandLine +# { + +# to: +# public class PythonCommandLine +# { +# +# +# +# primitive debug printing that won't interfere with the screen + +import clr,sys +clr.AddReferenceToFileAndPath(sys.executable) +import IronPythonConsole + +import sys +import re +import os + +import System + +from event import Event +from pyreadline.logger import log + +from pyreadline.keysyms import \ + make_keysym, make_keyinfo, make_KeyPress, make_KeyPress_from_keydescr +from pyreadline.console.ansi import AnsiState +color = System.ConsoleColor + +ansicolor={u"0;30": color.Black, + u"0;31": color.DarkRed, + u"0;32": color.DarkGreen, + u"0;33": color.DarkYellow, + u"0;34": color.DarkBlue, + u"0;35": color.DarkMagenta, + u"0;36": color.DarkCyan, + u"0;37": color.DarkGray, + u"1;30": color.Gray, + u"1;31": color.Red, + u"1;32": color.Green, + u"1;33": color.Yellow, + u"1;34": color.Blue, + u"1;35": color.Magenta, + u"1;36": color.Cyan, + u"1;37": color.White + } + +winattr = {u"black" : 0, u"darkgray" : 0+8, + u"darkred" : 4, u"red" : 4+8, + u"darkgreen" : 2, u"green" : 2+8, + u"darkyellow" : 6, u"yellow" : 6+8, + u"darkblue" : 1, u"blue" : 1+8, + u"darkmagenta" : 5, u"magenta" : 5+8, + u"darkcyan" : 3, u"cyan" : 3+8, + u"gray" : 7, u"white" : 7+8} + +class Console(object): + u'''Console driver for Windows. + + ''' + + def __init__(self, newbuffer=0): + u'''Initialize the Console object. + + newbuffer=1 will allocate a new buffer so the old content will be restored + on exit. + ''' + self.serial = 0 + self.attr = System.Console.ForegroundColor + self.saveattr = winattr[str(System.Console.ForegroundColor).lower()] + self.savebg = System.Console.BackgroundColor + log(u'initial attr=%s' % self.attr) + + def _get(self): + top = System.Console.WindowTop + log(u"WindowTop:%s"%top) + return top + + def _set(self, value): + top = System.Console.WindowTop + log(u"Set WindowTop:old:%s,new:%s"%(top, value)) + + WindowTop = property(_get, _set) + del _get, _set + + def __del__(self): + u'''Cleanup the console when finished.''' + # I don't think this ever gets called + pass + + def pos(self, x=None, y=None): + u'''Move or query the window cursor.''' + if x is not None: + System.Console.CursorLeft=x + else: + x = System.Console.CursorLeft + if y is not None: + System.Console.CursorTop=y + else: + y = System.Console.CursorTop + return x, y + + def home(self): + u'''Move to home.''' + self.pos(0, 0) + +# Map ANSI color escape sequences into Windows Console Attributes + + terminal_escape = re.compile(u'(\001?\033\\[[0-9;]*m\002?)') + escape_parts = re.compile(u'\001?\033\\[([0-9;]*)m\002?') + + # This pattern should match all characters that change the cursor position differently + # than a normal character. + motion_char_re = re.compile(u'([\n\r\t\010\007])') + + def write_scrolling(self, text, attr=None): + u'''write text at current cursor position while watching for scrolling. + + If the window scrolls because you are at the bottom of the screen + buffer, all positions that you are storing will be shifted by the + scroll amount. For example, I remember the cursor position of the + prompt so that I can redraw the line but if the window scrolls, + the remembered position is off. + + This variant of write tries to keep track of the cursor position + so that it will know when the screen buffer is scrolled. It + returns the number of lines that the buffer scrolled. + + ''' + x, y = self.pos() + w, h = self.size() + scroll = 0 # the result + + # split the string into ordinary characters and funny characters + chunks = self.motion_char_re.split(text) + for chunk in chunks: + n = self.write_color(chunk, attr) + if len(chunk) == 1: # the funny characters will be alone + if chunk[0] == u'\n': # newline + x = 0 + y += 1 + elif chunk[0] == u'\r': # carriage return + x = 0 + elif chunk[0] == u'\t': # tab + x = 8 * (int(x / 8) + 1) + if x > w: # newline + x -= w + y += 1 + elif chunk[0] == u'\007': # bell + pass + elif chunk[0] == u'\010': + x -= 1 + if x < 0: + y -= 1 # backed up 1 line + else: # ordinary character + x += 1 + if x == w: # wrap + x = 0 + y += 1 + if y == h: # scroll + scroll += 1 + y = h - 1 + else: # chunk of ordinary characters + x += n + l = int(x / w) # lines we advanced + x = x % w # new x value + y += l + if y >= h: # scroll + scroll += y - h + 1 + y = h - 1 + return scroll + + trtable = {0 : color.Black, 4 : color.DarkRed, 2 : color.DarkGreen, + 6 : color.DarkYellow, 1 : color.DarkBlue, 5 : color.DarkMagenta, + 3 : color.DarkCyan, 7 : color.Gray, 8 : color.DarkGray, + 4+8 : color.Red, 2+8 : color.Green, 6+8 : color.Yellow, + 1+8 : color.Blue, 5+8 : color.Magenta,3+8 : color.Cyan, + 7+8 : color.White} + + def write_color(self, text, attr=None): + '''write text at current cursor position and interpret color escapes. + + return the number of characters written. + ''' + log(u'write_color("%s", %s)' % (text, attr)) + chunks = self.terminal_escape.split(text) + log(u'chunks=%s' % repr(chunks)) + bg = self.savebg + n = 0 # count the characters we actually write, omitting the escapes + if attr is None:#use attribute from initial console + attr = self.attr + try: + fg = self.trtable[(0x000f&attr)] + bg = self.trtable[(0x00f0&attr)>>4] + except TypeError: + fg = attr + + for chunk in chunks: + m = self.escape_parts.match(chunk) + if m: + log(m.group(1)) + attr = ansicolor.get(m.group(1), self.attr) + n += len(chunk) + System.Console.ForegroundColor = fg + System.Console.BackgroundColor = bg + System.Console.Write(chunk) + return n + + def write_plain(self, text, attr=None): + u'''write text at current cursor position.''' + log(u'write("%s", %s)' %(text, attr)) + if attr is None: + attr = self.attr + n = c_int(0) + self.SetConsoleTextAttribute(self.hout, attr) + self.WriteConsoleA(self.hout, text, len(text), byref(n), None) + return len(text) + + if os.environ.has_key(u"EMACS"): + def write_color(self, text, attr=None): + junk = c_int(0) + self.WriteFile(self.hout, text, len(text), byref(junk), None) + return len(text) + write_plain = write_color + + # make this class look like a file object + def write(self, text): + log(u'write("%s")' % text) + return self.write_color(text) + + #write = write_scrolling + + def isatty(self): + return True + + def flush(self): + pass + + def page(self, attr=None, fill=u' '): + u'''Fill the entire screen.''' + System.Console.Clear() + + def text(self, x, y, text, attr=None): + u'''Write text at the given position.''' + self.pos(x, y) + self.write_color(text, attr) + + def clear_to_end_of_window(self): + oldtop = self.WindowTop + lastline = self.WindowTop+System.Console.WindowHeight + pos = self.pos() + w, h = self.size() + length = w - pos[0] + min((lastline - pos[1] - 1), 5) * w - 1 + self.write_color(length * u" ") + self.pos(*pos) + self.WindowTop = oldtop + + def rectangle(self, rect, attr=None, fill=u' '): + u'''Fill Rectangle.''' + oldtop = self.WindowTop + oldpos = self.pos() + #raise NotImplementedError + x0, y0, x1, y1 = rect + if attr is None: + attr = self.attr + if fill: + rowfill = fill[:1] * abs(x1 - x0) + else: + rowfill = u' ' * abs(x1 - x0) + for y in range(y0, y1): + System.Console.SetCursorPosition(x0, y) + self.write_color(rowfill, attr) + self.pos(*oldpos) + + def scroll(self, rect, dx, dy, attr=None, fill=' '): + u'''Scroll a rectangle.''' + raise NotImplementedError + + def scroll_window(self, lines): + u'''Scroll the window by the indicated number of lines.''' + top = self.WindowTop + lines + if top < 0: + top = 0 + if top + System.Console.WindowHeight > System.Console.BufferHeight: + top = System.Console.BufferHeight + self.WindowTop = top + + def getkeypress(self): + u'''Return next key press event from the queue, ignoring others.''' + ck = System.ConsoleKey + while 1: + e = System.Console.ReadKey(True) + if e.Key == System.ConsoleKey.PageDown: #PageDown + self.scroll_window(12) + elif e.Key == System.ConsoleKey.PageUp:#PageUp + self.scroll_window(-12) + elif str(e.KeyChar) == u"\000":#Drop deadkeys + log(u"Deadkey: %s"%e) + return event(self, e) + else: + return event(self, e) + + def title(self, txt=None): + u'''Set/get title.''' + if txt: + System.Console.Title = txt + else: + return System.Console.Title + + def size(self, width=None, height=None): + u'''Set/get window size.''' + sc = System.Console + if width is not None and height is not None: + sc.BufferWidth, sc.BufferHeight = width,height + else: + return sc.BufferWidth, sc.BufferHeight + + if width is not None and height is not None: + sc.WindowWidth, sc.WindowHeight = width,height + else: + return sc.WindowWidth - 1, sc.WindowHeight - 1 + + def cursor(self, visible=True, size=None): + u'''Set cursor on or off.''' + System.Console.CursorVisible = visible + + def bell(self): + System.Console.Beep() + + def next_serial(self): + u'''Get next event serial number.''' + self.serial += 1 + return self.serial + +class event(Event): + u'''Represent events from the console.''' + def __init__(self, console, input): + u'''Initialize an event from the Windows input structure.''' + self.type = u'??' + self.serial = console.next_serial() + self.width = 0 + self.height = 0 + self.x = 0 + self.y = 0 + self.char = str(input.KeyChar) + self.keycode = input.Key + self.state = input.Modifiers + log(u"%s,%s,%s"%(input.Modifiers, input.Key, input.KeyChar)) + self.type = "KeyRelease" + self.keysym = make_keysym(self.keycode) + self.keyinfo = make_KeyPress(self.char, self.state, self.keycode) + +def make_event_from_keydescr(keydescr): + def input(): + return 1 + input.KeyChar = u"a" + input.Key = System.ConsoleKey.A + input.Modifiers = System.ConsoleModifiers.Shift + input.next_serial = input + e = event(input,input) + del input.next_serial + keyinfo = make_KeyPress_from_keydescr(keydescr) + e.keyinfo = keyinfo + return e + +CTRL_C_EVENT=make_event_from_keydescr(u"Control-c") + +def install_readline(hook): + def hook_wrap(): + try: + res = hook() + except KeyboardInterrupt,x: #this exception does not seem to be caught + res = u"" + except EOFError: + return None + if res[-1:] == u"\n": + return res[:-1] + else: + return res + class IronPythonWrapper(IronPythonConsole.IConsole): + def ReadLine(self, autoIndentSize): + return hook_wrap() + def Write(self, text, style): + System.Console.Write(text) + def WriteLine(self, text, style): + System.Console.WriteLine(text) + IronPythonConsole.PythonCommandLine.MyConsole = IronPythonWrapper() + + + +if __name__ == u'__main__': + import time, sys + c = Console(0) + sys.stdout = c + sys.stderr = c + c.page() + c.pos(5, 10) + c.write(u'hi there') + c.title(u"Testing console") +# c.bell() + print + print u"size", c.size() + print u' some printed output' + for i in range(10): + e = c.getkeypress() + print e.Key, chr(e.KeyChar), ord(e.KeyChar), e.Modifiers + del c + + System.Console.Clear() diff --git a/pyreadline/error.py b/pyreadline/error.py index e7c3305..be2c24e 100644 --- a/pyreadline/error.py +++ b/pyreadline/error.py @@ -1,14 +1,14 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - - -class ReadlineError(Exception): - pass - -class GetSetError(ReadlineError): - pass +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + + +class ReadlineError(Exception): + pass + +class GetSetError(ReadlineError): + pass diff --git a/pyreadline/get_doc.py b/pyreadline/get_doc.py index b56eb95..0846087 100644 --- a/pyreadline/get_doc.py +++ b/pyreadline/get_doc.py @@ -1,18 +1,18 @@ -import sys,textwrap - -rlmain = sys.modules[u"pyreadline.rlmain"] -rl = rlmain.rl - -def get_doc(rl): - methods = [(x, getattr(rl, x)) for x in dir(rl) if callable(getattr(rl, x))] - return [ (x, m.__doc__ )for x, m in methods if m.__doc__] - - -def get_rest(rl): - q = get_doc(rl) - out = [] - for funcname, doc in q: - out.append(funcname) - out.append(u"\n".join(textwrap.wrap(doc, 80, initial_indent=u" "))) - out.append(u"") +import sys,textwrap + +rlmain = sys.modules[u"pyreadline.rlmain"] +rl = rlmain.rl + +def get_doc(rl): + methods = [(x, getattr(rl, x)) for x in dir(rl) if callable(getattr(rl, x))] + return [ (x, m.__doc__ )for x, m in methods if m.__doc__] + + +def get_rest(rl): + q = get_doc(rl) + out = [] + for funcname, doc in q: + out.append(funcname) + out.append(u"\n".join(textwrap.wrap(doc, 80, initial_indent=u" "))) + out.append(u"") return out \ No newline at end of file diff --git a/pyreadline/keysyms/__init__.py b/pyreadline/keysyms/__init__.py index ae3aec2..ca89649 100644 --- a/pyreadline/keysyms/__init__.py +++ b/pyreadline/keysyms/__init__.py @@ -1,20 +1,20 @@ -import sys - -success = False -in_ironpython = u"IronPython" in sys.version - -if in_ironpython: - try: - from ironpython_keysyms import * - success = True - except ImportError, x: - raise -else: - try: - from keysyms import * - success = True - except ImportError, x: - pass - -if not success: +import sys + +success = False +in_ironpython = u"IronPython" in sys.version + +if in_ironpython: + try: + from ironpython_keysyms import * + success = True + except ImportError, x: + raise +else: + try: + from keysyms import * + success = True + except ImportError, x: + pass + +if not success: raise ImportError(u"Could not import keysym for local pythonversion", x) \ No newline at end of file diff --git a/pyreadline/lineeditor/history.py b/pyreadline/lineeditor/history.py index 8498435..59fc57f 100644 --- a/pyreadline/lineeditor/history.py +++ b/pyreadline/lineeditor/history.py @@ -1,264 +1,264 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import re, operator,string, sys,os - -from pyreadline.unicode_helper import ensure_unicode, ensure_str -if u"pyreadline" in sys.modules: - pyreadline = sys.modules[u"pyreadline"] -else: - import pyreadline - -import lineobj - -import exceptions - -class EscapeHistory(exceptions.Exception): - pass - -from pyreadline.logger import log - - -class LineHistory(object): - def __init__(self): - self.history = [] - self._history_length = 100 - self._history_cursor = 0 - self.history_filename = os.path.expanduser('~/.history') #Cannot expand unicode strings correctly on python2.4 - self.lastcommand = None - self.query = u"" - self.last_search_for = u"" - - def get_current_history_length(self): - u'''Return the number of lines currently in the history. - (This is different from get_history_length(), which returns - the maximum number of lines that will be written to a history file.)''' - value = len(self.history) - log(u"get_current_history_length:%d"%value) - return value - - def get_history_length(self): - u'''Return the desired length of the history file. Negative values imply - unlimited history file size.''' - value = self._history_length - log(u"get_history_length:%d"%value) - return value - - def get_history_item(self, index): - u'''Return the current contents of history item at index (starts with index 1).''' - item = self.history[index - 1] - log(u"get_history_item: index:%d item:%r"%(index, item)) - return item.get_line_text() - - def set_history_length(self, value): - log(u"set_history_length: old:%d new:%d"%(self._history_length, value)) - self._history_length = value - - def get_history_cursor(self): - value = self._history_cursor - log(u"get_history_cursor:%d"%value) - return value - - def set_history_cursor(self, value): - log(u"set_history_cursor: old:%d new:%d"%(self._history_cursor, value)) - self._history_cursor = value - - history_length = property(get_history_length, set_history_length) - history_cursor = property(get_history_cursor, set_history_cursor) - - def clear_history(self): - u'''Clear readline history.''' - self.history[:] = [] - self.history_cursor = 0 - - def read_history_file(self, filename=None): - u'''Load a readline history file.''' - if filename is None: - filename = self.history_filename - try: - for line in open(filename, u'r'): - self.add_history(lineobj.ReadLineTextBuffer(ensure_unicode(line.rstrip()))) - except IOError: - self.history = [] - self.history_cursor = 0 - - def write_history_file(self, filename = None): - u'''Save a readline history file.''' - if filename is None: - filename = self.history_filename - fp = open(filename, u'wb') - for line in self.history[-self.history_length:]: - fp.write(ensure_str(line.get_line_text())) - fp.write(u'\n') - fp.close() - - - def add_history(self, line): - u'''Append a line to the history buffer, as if it was the last line typed.''' - if not hasattr(line, "get_line_text"): - line = lineobj.ReadLineTextBuffer(line) - if not line.get_line_text(): - pass - elif len(self.history) > 0 and self.history[-1].get_line_text() == line.get_line_text(): - pass - else: - self.history.append(line) - self.history_cursor = len(self.history) - - def previous_history(self, current): # (C-p) - u'''Move back through the history list, fetching the previous command. ''' - if self.history_cursor == len(self.history): - self.history.append(current.copy()) #do not use add_history since we do not want to increment cursor - - if self.history_cursor > 0: - self.history_cursor -= 1 - current.set_line(self.history[self.history_cursor].get_line_text()) - current.point = lineobj.EndOfLine - - def next_history(self, current): # (C-n) - u'''Move forward through the history list, fetching the next command. ''' - if self.history_cursor < len(self.history) - 1: - self.history_cursor += 1 - current.set_line(self.history[self.history_cursor].get_line_text()) - - def beginning_of_history(self): # (M-<) - u'''Move to the first line in the history.''' - self.history_cursor = 0 - if len(self.history) > 0: - self.l_buffer = self.history[0] - - def end_of_history(self, current): # (M->) - u'''Move to the end of the input history, i.e., the line currently - being entered.''' - self.history_cursor = len(self.history) - current.set_line(self.history[-1].get_line_text()) - - def reverse_search_history(self, searchfor, startpos=None): - if startpos is None: - startpos = self.history_cursor - origpos = startpos - - result = lineobj.ReadLineTextBuffer("") - - for idx, line in list(enumerate(self.history))[startpos:0:-1]: - if searchfor in line: - startpos = idx - break - - #If we get a new search without change in search term it means - #someone pushed ctrl-r and we should find the next match - if self.last_search_for == searchfor and startpos > 0: - startpos -= 1 - for idx, line in list(enumerate(self.history))[startpos:0:-1]: - if searchfor in line: - startpos = idx - break - - if self.history: - result = self.history[startpos].get_line_text() - else: - result = u"" - self.history_cursor = startpos - self.last_search_for = searchfor - log(u"reverse_search_history: old:%d new:%d result:%r"%(origpos, self.history_cursor, result)) - return result - - def forward_search_history(self, searchfor, startpos=None): - if startpos is None: - startpos = min(self.history_cursor, max(0, self.get_current_history_length()-1)) - origpos = startpos - - result = lineobj.ReadLineTextBuffer("") - - for idx, line in list(enumerate(self.history))[startpos:]: - if searchfor in line: - startpos = idx - break - - #If we get a new search without change in search term it means - #someone pushed ctrl-r and we should find the next match - if self.last_search_for == searchfor and startpos < self.get_current_history_length()-1: - startpos += 1 - for idx, line in list(enumerate(self.history))[startpos:]: - if searchfor in line: - startpos = idx - break - - if self.history: - result = self.history[startpos].get_line_text() - else: - result = u"" - self.history_cursor = startpos - self.last_search_for = searchfor - return result - - def _search(self, direction, partial): - try: - if (self.lastcommand != self.history_search_forward and - self.lastcommand != self.history_search_backward): - self.query = u''.join(partial[0:partial.point].get_line_text()) - hcstart = max(self.history_cursor,0) - hc = self.history_cursor + direction - while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)): - h = self.history[hc] - if not self.query: - self.history_cursor = hc - result = lineobj.ReadLineTextBuffer(h, point=len(h.get_line_text())) - return result - elif (h.get_line_text().startswith(self.query) and (h != partial.get_line_text())): - self.history_cursor = hc - result = lineobj.ReadLineTextBuffer(h, point=partial.point) - return result - hc += direction - else: - if len(self.history) == 0: - pass - elif hc >= len(self.history) and not self.query: - self.history_cursor = len(self.history) - return lineobj.ReadLineTextBuffer(u"", point=0) - elif self.history[max(min(hcstart, len(self.history) - 1), 0)]\ - .get_line_text().startswith(self.query) and self.query: - return lineobj.ReadLineTextBuffer(self.history\ - [max(min(hcstart, len(self.history) - 1),0)], - point = partial.point) - else: - return lineobj.ReadLineTextBuffer(partial, - point=partial.point) - return lineobj.ReadLineTextBuffer(self.query, - point=min(len(self.query), - partial.point)) - except IndexError: - raise - - def history_search_forward(self, partial): # () - u'''Search forward through the history for the string of characters - between the start of the current line and the point. This is a - non-incremental search. By default, this command is unbound.''' - q= self._search(1, partial) - return q - - def history_search_backward(self, partial): # () - u'''Search backward through the history for the string of characters - between the start of the current line and the point. This is a - non-incremental search. By default, this command is unbound.''' - - q= self._search(-1, partial) - return q - -if __name__==u"__main__": - import pdb - q = LineHistory() - r = LineHistory() - s = LineHistory() - RL = lineobj.ReadLineTextBuffer - q.add_history(RL(u"aaaa")) - q.add_history(RL(u"aaba")) - q.add_history(RL(u"aaca")) - q.add_history(RL(u"akca")) - q.add_history(RL(u"bbb")) - q.add_history(RL(u"ako")) - r.add_history(RL(u"ako")) +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import re, operator,string, sys,os + +from pyreadline.unicode_helper import ensure_unicode, ensure_str +if u"pyreadline" in sys.modules: + pyreadline = sys.modules[u"pyreadline"] +else: + import pyreadline + +import lineobj + +import exceptions + +class EscapeHistory(exceptions.Exception): + pass + +from pyreadline.logger import log + + +class LineHistory(object): + def __init__(self): + self.history = [] + self._history_length = 100 + self._history_cursor = 0 + self.history_filename = os.path.expanduser('~/.history') #Cannot expand unicode strings correctly on python2.4 + self.lastcommand = None + self.query = u"" + self.last_search_for = u"" + + def get_current_history_length(self): + u'''Return the number of lines currently in the history. + (This is different from get_history_length(), which returns + the maximum number of lines that will be written to a history file.)''' + value = len(self.history) + log(u"get_current_history_length:%d"%value) + return value + + def get_history_length(self): + u'''Return the desired length of the history file. Negative values imply + unlimited history file size.''' + value = self._history_length + log(u"get_history_length:%d"%value) + return value + + def get_history_item(self, index): + u'''Return the current contents of history item at index (starts with index 1).''' + item = self.history[index - 1] + log(u"get_history_item: index:%d item:%r"%(index, item)) + return item.get_line_text() + + def set_history_length(self, value): + log(u"set_history_length: old:%d new:%d"%(self._history_length, value)) + self._history_length = value + + def get_history_cursor(self): + value = self._history_cursor + log(u"get_history_cursor:%d"%value) + return value + + def set_history_cursor(self, value): + log(u"set_history_cursor: old:%d new:%d"%(self._history_cursor, value)) + self._history_cursor = value + + history_length = property(get_history_length, set_history_length) + history_cursor = property(get_history_cursor, set_history_cursor) + + def clear_history(self): + u'''Clear readline history.''' + self.history[:] = [] + self.history_cursor = 0 + + def read_history_file(self, filename=None): + u'''Load a readline history file.''' + if filename is None: + filename = self.history_filename + try: + for line in open(filename, u'r'): + self.add_history(lineobj.ReadLineTextBuffer(ensure_unicode(line.rstrip()))) + except IOError: + self.history = [] + self.history_cursor = 0 + + def write_history_file(self, filename = None): + u'''Save a readline history file.''' + if filename is None: + filename = self.history_filename + fp = open(filename, u'wb') + for line in self.history[-self.history_length:]: + fp.write(ensure_str(line.get_line_text())) + fp.write(u'\n') + fp.close() + + + def add_history(self, line): + u'''Append a line to the history buffer, as if it was the last line typed.''' + if not hasattr(line, "get_line_text"): + line = lineobj.ReadLineTextBuffer(line) + if not line.get_line_text(): + pass + elif len(self.history) > 0 and self.history[-1].get_line_text() == line.get_line_text(): + pass + else: + self.history.append(line) + self.history_cursor = len(self.history) + + def previous_history(self, current): # (C-p) + u'''Move back through the history list, fetching the previous command. ''' + if self.history_cursor == len(self.history): + self.history.append(current.copy()) #do not use add_history since we do not want to increment cursor + + if self.history_cursor > 0: + self.history_cursor -= 1 + current.set_line(self.history[self.history_cursor].get_line_text()) + current.point = lineobj.EndOfLine + + def next_history(self, current): # (C-n) + u'''Move forward through the history list, fetching the next command. ''' + if self.history_cursor < len(self.history) - 1: + self.history_cursor += 1 + current.set_line(self.history[self.history_cursor].get_line_text()) + + def beginning_of_history(self): # (M-<) + u'''Move to the first line in the history.''' + self.history_cursor = 0 + if len(self.history) > 0: + self.l_buffer = self.history[0] + + def end_of_history(self, current): # (M->) + u'''Move to the end of the input history, i.e., the line currently + being entered.''' + self.history_cursor = len(self.history) + current.set_line(self.history[-1].get_line_text()) + + def reverse_search_history(self, searchfor, startpos=None): + if startpos is None: + startpos = self.history_cursor + origpos = startpos + + result = lineobj.ReadLineTextBuffer("") + + for idx, line in list(enumerate(self.history))[startpos:0:-1]: + if searchfor in line: + startpos = idx + break + + #If we get a new search without change in search term it means + #someone pushed ctrl-r and we should find the next match + if self.last_search_for == searchfor and startpos > 0: + startpos -= 1 + for idx, line in list(enumerate(self.history))[startpos:0:-1]: + if searchfor in line: + startpos = idx + break + + if self.history: + result = self.history[startpos].get_line_text() + else: + result = u"" + self.history_cursor = startpos + self.last_search_for = searchfor + log(u"reverse_search_history: old:%d new:%d result:%r"%(origpos, self.history_cursor, result)) + return result + + def forward_search_history(self, searchfor, startpos=None): + if startpos is None: + startpos = min(self.history_cursor, max(0, self.get_current_history_length()-1)) + origpos = startpos + + result = lineobj.ReadLineTextBuffer("") + + for idx, line in list(enumerate(self.history))[startpos:]: + if searchfor in line: + startpos = idx + break + + #If we get a new search without change in search term it means + #someone pushed ctrl-r and we should find the next match + if self.last_search_for == searchfor and startpos < self.get_current_history_length()-1: + startpos += 1 + for idx, line in list(enumerate(self.history))[startpos:]: + if searchfor in line: + startpos = idx + break + + if self.history: + result = self.history[startpos].get_line_text() + else: + result = u"" + self.history_cursor = startpos + self.last_search_for = searchfor + return result + + def _search(self, direction, partial): + try: + if (self.lastcommand != self.history_search_forward and + self.lastcommand != self.history_search_backward): + self.query = u''.join(partial[0:partial.point].get_line_text()) + hcstart = max(self.history_cursor,0) + hc = self.history_cursor + direction + while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)): + h = self.history[hc] + if not self.query: + self.history_cursor = hc + result = lineobj.ReadLineTextBuffer(h, point=len(h.get_line_text())) + return result + elif (h.get_line_text().startswith(self.query) and (h != partial.get_line_text())): + self.history_cursor = hc + result = lineobj.ReadLineTextBuffer(h, point=partial.point) + return result + hc += direction + else: + if len(self.history) == 0: + pass + elif hc >= len(self.history) and not self.query: + self.history_cursor = len(self.history) + return lineobj.ReadLineTextBuffer(u"", point=0) + elif self.history[max(min(hcstart, len(self.history) - 1), 0)]\ + .get_line_text().startswith(self.query) and self.query: + return lineobj.ReadLineTextBuffer(self.history\ + [max(min(hcstart, len(self.history) - 1),0)], + point = partial.point) + else: + return lineobj.ReadLineTextBuffer(partial, + point=partial.point) + return lineobj.ReadLineTextBuffer(self.query, + point=min(len(self.query), + partial.point)) + except IndexError: + raise + + def history_search_forward(self, partial): # () + u'''Search forward through the history for the string of characters + between the start of the current line and the point. This is a + non-incremental search. By default, this command is unbound.''' + q= self._search(1, partial) + return q + + def history_search_backward(self, partial): # () + u'''Search backward through the history for the string of characters + between the start of the current line and the point. This is a + non-incremental search. By default, this command is unbound.''' + + q= self._search(-1, partial) + return q + +if __name__==u"__main__": + import pdb + q = LineHistory() + r = LineHistory() + s = LineHistory() + RL = lineobj.ReadLineTextBuffer + q.add_history(RL(u"aaaa")) + q.add_history(RL(u"aaba")) + q.add_history(RL(u"aaca")) + q.add_history(RL(u"akca")) + q.add_history(RL(u"bbb")) + q.add_history(RL(u"ako")) + r.add_history(RL(u"ako")) diff --git a/pyreadline/lineeditor/lineobj.py b/pyreadline/lineeditor/lineobj.py index 4347ffb..902a49b 100644 --- a/pyreadline/lineeditor/lineobj.py +++ b/pyreadline/lineeditor/lineobj.py @@ -1,799 +1,799 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import re, operator, sys - -import wordmatcher -import pyreadline.clipboard as clipboard -from pyreadline.logger import log -from pyreadline.unicode_helper import ensure_unicode - -kill_ring_to_clipboard = False #set to true to copy every addition to kill ring to clipboard - - -class NotAWordError(IndexError): - pass - - -def quote_char(c): - if ord(c) > 0: - return c - -############## Line positioner ######################## - -class LinePositioner(object): - def __call__(self, line): - NotImplementedError(u"Base class !!!") - -class NextChar(LinePositioner): - def __call__(self, line): - if line.point < len(line.line_buffer): - return line.point + 1 - else: - return line.point -NextChar = NextChar() - -class PrevChar(LinePositioner): - def __call__(self, line): - if line.point > 0: - return line.point - 1 - else: - return line.point -PrevChar = PrevChar() - -class NextWordStart(LinePositioner): - def __call__(self, line): - return line.next_start_segment(line.line_buffer, line.is_word_token)[line.point] -NextWordStart = NextWordStart() - -class NextWordEnd(LinePositioner): - def __call__(self, line): - return line.next_end_segment(line.line_buffer, line.is_word_token)[line.point] -NextWordEnd = NextWordEnd() - -class PrevWordStart(LinePositioner): - def __call__(self, line): - return line.prev_start_segment(line.line_buffer, line.is_word_token)[line.point] -PrevWordStart = PrevWordStart() - - -class WordStart(LinePositioner): - def __call__(self, line): - if line.is_word_token(line.get_line_text()[Point(line):Point(line) + 1]): - if Point(line) > 0 and line.is_word_token(line.get_line_text()[Point(line) - 1:Point(line)]): - return PrevWordStart(line) - else: - return line.point - else: - raise NotAWordError(u"Point is not in a word") -WordStart = WordStart() - -class WordEnd(LinePositioner): - def __call__(self, line): - if line.is_word_token(line.get_line_text()[Point(line):Point(line) + 1]): - if line.is_word_token(line.get_line_text()[Point(line) + 1:Point(line) + 2]): - return NextWordEnd(line) - else: - return line.point - else: - raise NotAWordError(u"Point is not in a word") -WordEnd = WordEnd() - -class PrevWordEnd(LinePositioner): - def __call__(self, line): - return line.prev_end_segment(line.line_buffer, line.is_word_token)[line.point] -PrevWordEnd = PrevWordEnd() - -class PrevSpace(LinePositioner): - def __call__(self, line): - point = line.point - if line[point - 1:point].get_line_text() == u" ": - while point > 0 and line[point - 1:point].get_line_text() == u" ": - point -= 1 - while point > 0 and line[point - 1:point].get_line_text() != u" ": - point -= 1 - return point -PrevSpace = PrevSpace() - - -class StartOfLine(LinePositioner): - def __call__(self, line): - return 0 -StartOfLine = StartOfLine() - -class EndOfLine(LinePositioner): - def __call__(self, line): - return len(line.line_buffer) -EndOfLine = EndOfLine() - -class Point(LinePositioner): - def __call__(self, line): - return line.point -Point = Point() - -class Mark(LinePositioner): - def __call__(self, line): - return line.mark -k = Mark() - -all_positioners = [(value.__class__.__name__, value) - for key, value in globals().items() - if isinstance(value, LinePositioner)] -all_positioners.sort() - -############### LineSlice ################# - -class LineSlice(object): - def __call__(self, line): - NotImplementedError(u"Base class !!!") - - -class CurrentWord(LineSlice): - def __call__(self, line): - return slice(WordStart(line), WordEnd(line), None) -CurrentWord = CurrentWord() - -class NextWord(LineSlice): - def __call__(self, line): - work = TextLine(line) - work.point = NextWordStart - start = work.point - stop = NextWordEnd(work) - return slice(start, stop) -NextWord = NextWord() - -class PrevWord(LineSlice): - def __call__(self, line): - work = TextLine(line) - work.point = PrevWordEnd - stop = work.point - start = PrevWordStart(work) - return slice(start, stop) -PrevWord = PrevWord() - -class PointSlice(LineSlice): - def __call__(self, line): - return slice(Point(line), Point(line) + 1, None) -PointSlice = PointSlice() - - -############### TextLine ###################### - -class TextLine(object): - def __init__(self, txtstr, point = None, mark = None): - self.line_buffer = [] - self._point = 0 - self.mark = -1 - self.undo_stack = [] - self.overwrite = False - if isinstance(txtstr, TextLine): #copy - self.line_buffer = txtstr.line_buffer[:] - if point is None: - self.point = txtstr.point - else: - self.point = point - if mark is None: - self.mark = txtstr.mark - else: - self.mark = mark - else: - self._insert_text(txtstr) - if point is None: - self.point = 0 - else: - self.point = point - if mark is None: - self.mark = -1 - else: - self.mark = mark - - self.is_word_token = wordmatcher.is_word_token - self.next_start_segment = wordmatcher.next_start_segment - self.next_end_segment = wordmatcher.next_end_segment - self.prev_start_segment = wordmatcher.prev_start_segment - self.prev_end_segment = wordmatcher.prev_end_segment - - def push_undo(self): - ltext = self.get_line_text() - if self.undo_stack and ltext == self.undo_stack[-1].get_line_text(): - self.undo_stack[-1].point = self.point - else: - self.undo_stack.append(self.copy()) - - def pop_undo(self): - if len(self.undo_stack) >= 2: - self.undo_stack.pop() - self.set_top_undo() - self.undo_stack.pop() - else: - self.reset_line() - self.undo_stack = [] - - def set_top_undo(self): - if self.undo_stack: - undo = self.undo_stack[-1] - self.line_buffer = undo.line_buffer - self.point = undo.point - self.mark = undo.mark - else: - pass - - def __repr__(self): - return u'TextLine("%s",point=%s,mark=%s)'%(self.line_buffer, self.point, self.mark) - - def copy(self): - return self.__class__(self) - - def set_point(self,value): - if isinstance(value, LinePositioner): - value = value(self) - assert (value <= len(self.line_buffer)) - if value > len(self.line_buffer): - value = len(self.line_buffer) - self._point = value - def get_point(self): - return self._point - point = property(get_point, set_point) - - - def visible_line_width(self, position = Point): - """Return the visible width of the text in line buffer up to position.""" - extra_char_width = len([ None for c in self[:position].line_buffer if 0x2013 <= ord(c) <= 0xFFFD]) - return len(self[:position].quoted_text()) + self[:position].line_buffer.count(u"\t")*7 + extra_char_width - - def quoted_text(self): - quoted = [ quote_char(c) for c in self.line_buffer ] - self.line_char_width = [ len(c) for c in quoted ] - return u''.join(map(ensure_unicode, quoted)) - - def get_line_text(self): - buf = self.line_buffer - buf = map(ensure_unicode, buf) - return u''.join(buf) - - def set_line(self, text, cursor = None): - self.line_buffer = [ c for c in str(text) ] - if cursor is None: - self.point = len(self.line_buffer) - else: - self.point = cursor - - def reset_line(self): - self.line_buffer = [] - self.point = 0 - - def end_of_line(self): - self.point = len(self.line_buffer) - - def _insert_text(self, text, argument=1): - text = text * argument - if self.overwrite: - for c in text: - #if self.point: - self.line_buffer[self.point] = c - self.point += 1 - else: - for c in text: - self.line_buffer.insert(self.point, c) - self.point += 1 - - def __getitem__(self, key): - #Check if key is LineSlice, convert to regular slice - #and continue processing - if isinstance(key, LineSlice): - key = key(self) - if isinstance(key, slice): - if key.step is None: - pass - else: - raise Error - if key.start is None: - start = StartOfLine(self) - elif isinstance(key.start,LinePositioner): - start = key.start(self) - else: - start = key.start - if key.stop is None: - stop = EndOfLine(self) - elif isinstance(key.stop, LinePositioner): - stop = key.stop(self) - else: - stop = key.stop - return self.__class__(self.line_buffer[start:stop], point=0) - elif isinstance(key, LinePositioner): - return self.line_buffer[key(self)] - elif isinstance(key, tuple): - raise IndexError(u"Cannot use step in line buffer indexing") #Multiple slice not allowed - else: - # return TextLine(self.line_buffer[key]) - return self.line_buffer[key] - - def __delitem__(self, key): - point = self.point - if isinstance(key, LineSlice): - key = key(self) - if isinstance(key, slice): - start = key.start - stop = key.stop - if isinstance(start, LinePositioner): - start = start(self) - elif start is None: - start=0 - if isinstance(stop, LinePositioner): - stop = stop(self) - elif stop is None: - stop = EndOfLine(self) - elif isinstance(key, LinePositioner): - start = key(self) - stop = start + 1 - else: - start = key - stop = key + 1 - prev = self.line_buffer[:start] - rest = self.line_buffer[stop:] - self.line_buffer = prev + rest - if point > stop: - self.point = point - (stop - start) - elif point >= start and point <= stop: - self.point = start - - - def __setitem__(self, key, value): - if isinstance(key, LineSlice): - key = key(self) - if isinstance(key, slice): - start = key.start - stop = key.stop - elif isinstance(key, LinePositioner): - start = key(self) - stop = start + 1 - else: - start = key - stop = key + 1 - prev = self.line_buffer[:start] - value = self.__class__(value).line_buffer - rest = self.line_buffer[stop:] - out = prev + value + rest - if len(out) >= len(self): - self.point = len(self) - self.line_buffer = out - - def __len__(self): - return len(self.line_buffer) - - def upper(self): - self.line_buffer = [x.upper() for x in self.line_buffer] - return self - - def lower(self): - self.line_buffer = [x.lower() for x in self.line_buffer] - return self - - def capitalize(self): - self.set_line(self.get_line_text().capitalize(), self.point) - return self - - def startswith(self, txt): - return self.get_line_text().startswith(txt) - - def endswith(self, txt): - return self.get_line_text().endswith(txt) - - def __contains__(self, txt): - return txt in self.get_line_text() - - -lines = [TextLine(u"abc"), - TextLine(u"abc def"), - TextLine(u"abc def ghi"), - TextLine(u" abc def "), - ] -l = lines[2] -l.point = 5 - - - -class ReadLineTextBuffer(TextLine): - def __init__(self,txtstr, point = None, mark = None): - super(ReadLineTextBuffer, self).__init__(txtstr, point, mark) - self.enable_win32_clipboard = True - self.selection_mark = -1 - self.enable_selection = True - self.kill_ring = [] - - def __repr__(self): - return u'ReadLineTextBuffer'\ - u'("%s",point=%s,mark=%s,selection_mark=%s)'%\ - (self.line_buffer, self.point, self.mark,self.selection_mark) - - - def insert_text(self, char, argument=1): - self.delete_selection() - self.selection_mark = -1 - self._insert_text(char, argument) - - def to_clipboard(self): - if self.enable_win32_clipboard: - clipboard.set_clipboard_text(self.get_line_text()) - -######### Movement - - def beginning_of_line(self): - self.selection_mark = -1 - self.point = StartOfLine - - def end_of_line(self): - self.selection_mark = -1 - self.point = EndOfLine - - def forward_char(self,argument = 1): - if argument < 0: - self.backward_char(-argument) - self.selection_mark = -1 - for x in range(argument): - self.point = NextChar - - def backward_char(self, argument=1): - if argument < 0: - self.forward_char(-argument) - self.selection_mark = -1 - for x in range(argument): - self.point = PrevChar - - def forward_word(self,argument=1): - if argument<0: - self.backward_word(-argument) - self.selection_mark=-1 - for x in range(argument): - self.point = NextWordStart - - def backward_word(self, argument=1): - if argument < 0: - self.forward_word(-argument) - self.selection_mark = -1 - for x in range(argument): - self.point = PrevWordStart - - def forward_word_end(self, argument=1): - if argument < 0: - self.backward_word_end(-argument) - self.selection_mark = -1 - for x in range(argument): - self.point = NextWordEnd - - def backward_word_end(self, argument=1): - if argument < 0: - self.forward_word_end(-argument) - self.selection_mark = -1 - for x in range(argument): - self.point = NextWordEnd - -######### Movement select - def beginning_of_line_extend_selection(self): - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - self.point = StartOfLine - - def end_of_line_extend_selection(self): - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - self.point = EndOfLine - - def forward_char_extend_selection(self,argument=1): - if argument < 0: - self.backward_char_extend_selection(-argument) - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - for x in range(argument): - self.point = NextChar - - def backward_char_extend_selection(self, argument=1): - if argument < 0: - self.forward_char_extend_selection(-argument) - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - for x in range(argument): - self.point = PrevChar - - def forward_word_extend_selection(self, argument=1): - if argument < 0: - self.backward_word_extend_selection(-argument) - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - for x in range(argument): - self.point = NextWordStart - - def backward_word_extend_selection(self, argument=1): - if argument < 0: - self.forward_word_extend_selection(-argument) - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - for x in range(argument): - self.point = PrevWordStart - - - def forward_word_end_extend_selection(self, argument=1): - if argument < 0: - self.backward_word_end_extend_selection(-argument) - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - for x in range(argument): - self.point = NextWordEnd - - def backward_word_end_extend_selection(self, argument=1): - if argument < 0: - self.forward_word_end_extend_selection(-argument) - if self.enable_selection and self.selection_mark < 0: - self.selection_mark = self.point - for x in range(argument): - self.point = PrevWordEnd - - -######### delete - - def delete_selection(self): - if self.enable_selection and self.selection_mark >= 0: - if self.selection_mark < self.point: - del self[self.selection_mark:self.point] - self.selection_mark = -1 - else: - del self[self.point:self.selection_mark] - self.selection_mark = -1 - return True - else: - self.selection_mark = -1 - return False - - def delete_char(self, argument=1): - if argument < 0: - self.backward_delete_char(-argument) - if self.delete_selection(): - argument -= 1 - for x in range(argument): - del self[Point] - - def backward_delete_char(self, argument=1): - if argument < 0: - self.delete_char(-argument) - if self.delete_selection(): - argument -= 1 - for x in range(argument): - if self.point > 0: - self.backward_char() - self.delete_char() - - def forward_delete_word(self, argument=1): - if argument < 0: - self.backward_delete_word(-argument) - if self.delete_selection(): - argument -= 1 - for x in range(argument): - del self[Point:NextWordStart] - - def backward_delete_word(self, argument=1): - if argument < 0: - self.forward_delete_word(-argument) - if self.delete_selection(): - argument -= 1 - for x in range(argument): - del self[PrevWordStart:Point] - - def delete_current_word(self): - if not self.delete_selection(): - del self[CurrentWord] - self.selection_mark =- 1 - - def delete_horizontal_space(self): - if self[Point] in " \t": - del self[PrevWordEnd:NextWordStart] - self.selection_mark = -1 -######### Case - - def upcase_word(self): - p = self.point - try: - self[CurrentWord] = self[CurrentWord].upper() - self.point = p - except NotAWordError: - pass - - def downcase_word(self): - p = self.point - try: - self[CurrentWord] = self[CurrentWord].lower() - self.point = p - except NotAWordError: - pass - - def capitalize_word(self): - p = self.point - try: - self[CurrentWord] = self[CurrentWord].capitalize() - self.point = p - except NotAWordError: - pass -########### Transpose - def transpose_chars(self): - p2 = Point(self) - if p2 == 0: - return - elif p2 == len(self): - p2 = p2 - 1 - p1 = p2 - 1 - self[p2], self[p1] = self[p1], self[p2] - self.point = p2 + 1 - - def transpose_words(self): - word1 = TextLine(self) - word2 = TextLine(self) - if self.point == len(self): - word2.point = PrevWordStart - word1.point = PrevWordStart(word2) - else: - word1.point = PrevWordStart - word2.point = NextWordStart - stop1 = NextWordEnd(word1) - stop2 = NextWordEnd(word2) - start1 = word1.point - start2 = word2.point - self[start2:stop2] = word1[Point:NextWordEnd] - self[start1:stop1] = word2[Point:NextWordEnd] - self.point = stop2 - - -############ Kill - - def kill_line(self): - self.add_to_kill_ring(self[self.point:]) - del self.line_buffer[self.point:] - - def kill_whole_line(self): - self.add_to_kill_ring(self[:]) - del self[:] - - def backward_kill_line(self): - del self[StartOfLine:Point] - - def unix_line_discard(self): - del self[StartOfLine:Point] - pass - - def kill_word(self): - """Kills to next word ending""" - del self[Point:NextWordEnd] - - def backward_kill_word(self): - """Kills to next word ending""" - if not self.delete_selection(): - del self[PrevWordStart:Point] - self.selection_mark = -1 - - def forward_kill_word(self): - """Kills to next word ending""" - if not self.delete_selection(): - del self[Point:NextWordEnd] - self.selection_mark = -1 - - def unix_word_rubout(self): - if not self.delete_selection(): - del self[PrevSpace:Point] - self.selection_mark = -1 - - def kill_region(self): - pass - - def copy_region_as_kill(self): - pass - - def copy_backward_word(self): - pass - - def copy_forward_word(self): - pass - - - def yank(self): - self.paste_from_kill_ring() - - def yank_pop(self): - pass - -############## Mark - - def set_mark(self): - self.mark = self.point - - def exchange_point_and_mark(self): - pass - - - def copy_region_to_clipboard(self): # () - u'''Copy the text in the region to the windows clipboard.''' - if self.enable_win32_clipboard: - mark = min(self.mark, len(self.line_buffer)) - cursor = min(self.point, len(self.line_buffer)) - if self.mark == -1: - return - begin = min(cursor, mark) - end = max(cursor, mark) - toclipboard = u"".join(self.line_buffer[begin:end]) - clipboard.SetClipboardText(toclipboard) - - def copy_selection_to_clipboard(self): # () - u'''Copy the text in the region to the windows clipboard.''' - if self.enable_win32_clipboard and self.enable_selection and self.selection_mark >= 0: - selection_mark = min(self.selection_mark,len(self.line_buffer)) - cursor = min(self.point,len(self.line_buffer)) - if self.selection_mark == -1: - return - begin = min(cursor, selection_mark) - end = max(cursor, selection_mark) - toclipboard = u"".join(self.line_buffer[begin:end]) - clipboard.SetClipboardText(toclipboard) - - - def cut_selection_to_clipboard(self): # () - self.copy_selection_to_clipboard() - self.delete_selection() -############## Paste - - -############## Kill ring - def add_to_kill_ring(self,txt): - self.kill_ring = [txt] - if kill_ring_to_clipboard: - clipboard.SetClipboardText(txt.get_line_text()) - - - def paste_from_kill_ring(self): - if self.kill_ring: - self.insert_text(self.kill_ring[0]) - - -################################################################## -q = ReadLineTextBuffer(u"asff asFArw ewrWErhg", point=8) -q = TextLine(u"asff asFArw ewrWErhg", point=8) - -def show_pos(buff, pos, chr = u"."): - l = len(buff.line_buffer) - def choice(bool): - if bool: - return chr - else: - return u" " - return u"".join([choice(pos==idx) for idx in range(l + 1)]) - - -def test_positioner(buff, points, positioner): - print (u" %s "%positioner.__class__.__name__).center(40, u"-") - buffstr = buff.line_buffer - - print u'"%s"'%(buffstr) - for point in points: - b = TextLine(buff, point = point) - out=[u" "] * (len(buffstr) + 1) - pos = positioner(b) - if pos == point: - out[pos] = u"&" - else: - out[point] = u"." - out[pos] = u"^" - print u'"%s"'%(u"".join(out)) - -if __name__ == "__main__": - - print u'%-15s "%s"'%(u"Position", q.get_line_text()) - print u'%-15s "%s"'%(u"Point", show_pos(q, q.point)) - - - for name, positioner in all_positioners: - pos = positioner(q) - [] - print u'%-15s "%s"'%(name, show_pos(q, pos, u"^")) - - l = ReadLineTextBuffer(u"kjjk asads asad") - l.point = EndOfLine +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import re, operator, sys + +import wordmatcher +import pyreadline.clipboard as clipboard +from pyreadline.logger import log +from pyreadline.unicode_helper import ensure_unicode + +kill_ring_to_clipboard = False #set to true to copy every addition to kill ring to clipboard + + +class NotAWordError(IndexError): + pass + + +def quote_char(c): + if ord(c) > 0: + return c + +############## Line positioner ######################## + +class LinePositioner(object): + def __call__(self, line): + NotImplementedError(u"Base class !!!") + +class NextChar(LinePositioner): + def __call__(self, line): + if line.point < len(line.line_buffer): + return line.point + 1 + else: + return line.point +NextChar = NextChar() + +class PrevChar(LinePositioner): + def __call__(self, line): + if line.point > 0: + return line.point - 1 + else: + return line.point +PrevChar = PrevChar() + +class NextWordStart(LinePositioner): + def __call__(self, line): + return line.next_start_segment(line.line_buffer, line.is_word_token)[line.point] +NextWordStart = NextWordStart() + +class NextWordEnd(LinePositioner): + def __call__(self, line): + return line.next_end_segment(line.line_buffer, line.is_word_token)[line.point] +NextWordEnd = NextWordEnd() + +class PrevWordStart(LinePositioner): + def __call__(self, line): + return line.prev_start_segment(line.line_buffer, line.is_word_token)[line.point] +PrevWordStart = PrevWordStart() + + +class WordStart(LinePositioner): + def __call__(self, line): + if line.is_word_token(line.get_line_text()[Point(line):Point(line) + 1]): + if Point(line) > 0 and line.is_word_token(line.get_line_text()[Point(line) - 1:Point(line)]): + return PrevWordStart(line) + else: + return line.point + else: + raise NotAWordError(u"Point is not in a word") +WordStart = WordStart() + +class WordEnd(LinePositioner): + def __call__(self, line): + if line.is_word_token(line.get_line_text()[Point(line):Point(line) + 1]): + if line.is_word_token(line.get_line_text()[Point(line) + 1:Point(line) + 2]): + return NextWordEnd(line) + else: + return line.point + else: + raise NotAWordError(u"Point is not in a word") +WordEnd = WordEnd() + +class PrevWordEnd(LinePositioner): + def __call__(self, line): + return line.prev_end_segment(line.line_buffer, line.is_word_token)[line.point] +PrevWordEnd = PrevWordEnd() + +class PrevSpace(LinePositioner): + def __call__(self, line): + point = line.point + if line[point - 1:point].get_line_text() == u" ": + while point > 0 and line[point - 1:point].get_line_text() == u" ": + point -= 1 + while point > 0 and line[point - 1:point].get_line_text() != u" ": + point -= 1 + return point +PrevSpace = PrevSpace() + + +class StartOfLine(LinePositioner): + def __call__(self, line): + return 0 +StartOfLine = StartOfLine() + +class EndOfLine(LinePositioner): + def __call__(self, line): + return len(line.line_buffer) +EndOfLine = EndOfLine() + +class Point(LinePositioner): + def __call__(self, line): + return line.point +Point = Point() + +class Mark(LinePositioner): + def __call__(self, line): + return line.mark +k = Mark() + +all_positioners = [(value.__class__.__name__, value) + for key, value in globals().items() + if isinstance(value, LinePositioner)] +all_positioners.sort() + +############### LineSlice ################# + +class LineSlice(object): + def __call__(self, line): + NotImplementedError(u"Base class !!!") + + +class CurrentWord(LineSlice): + def __call__(self, line): + return slice(WordStart(line), WordEnd(line), None) +CurrentWord = CurrentWord() + +class NextWord(LineSlice): + def __call__(self, line): + work = TextLine(line) + work.point = NextWordStart + start = work.point + stop = NextWordEnd(work) + return slice(start, stop) +NextWord = NextWord() + +class PrevWord(LineSlice): + def __call__(self, line): + work = TextLine(line) + work.point = PrevWordEnd + stop = work.point + start = PrevWordStart(work) + return slice(start, stop) +PrevWord = PrevWord() + +class PointSlice(LineSlice): + def __call__(self, line): + return slice(Point(line), Point(line) + 1, None) +PointSlice = PointSlice() + + +############### TextLine ###################### + +class TextLine(object): + def __init__(self, txtstr, point = None, mark = None): + self.line_buffer = [] + self._point = 0 + self.mark = -1 + self.undo_stack = [] + self.overwrite = False + if isinstance(txtstr, TextLine): #copy + self.line_buffer = txtstr.line_buffer[:] + if point is None: + self.point = txtstr.point + else: + self.point = point + if mark is None: + self.mark = txtstr.mark + else: + self.mark = mark + else: + self._insert_text(txtstr) + if point is None: + self.point = 0 + else: + self.point = point + if mark is None: + self.mark = -1 + else: + self.mark = mark + + self.is_word_token = wordmatcher.is_word_token + self.next_start_segment = wordmatcher.next_start_segment + self.next_end_segment = wordmatcher.next_end_segment + self.prev_start_segment = wordmatcher.prev_start_segment + self.prev_end_segment = wordmatcher.prev_end_segment + + def push_undo(self): + ltext = self.get_line_text() + if self.undo_stack and ltext == self.undo_stack[-1].get_line_text(): + self.undo_stack[-1].point = self.point + else: + self.undo_stack.append(self.copy()) + + def pop_undo(self): + if len(self.undo_stack) >= 2: + self.undo_stack.pop() + self.set_top_undo() + self.undo_stack.pop() + else: + self.reset_line() + self.undo_stack = [] + + def set_top_undo(self): + if self.undo_stack: + undo = self.undo_stack[-1] + self.line_buffer = undo.line_buffer + self.point = undo.point + self.mark = undo.mark + else: + pass + + def __repr__(self): + return u'TextLine("%s",point=%s,mark=%s)'%(self.line_buffer, self.point, self.mark) + + def copy(self): + return self.__class__(self) + + def set_point(self,value): + if isinstance(value, LinePositioner): + value = value(self) + assert (value <= len(self.line_buffer)) + if value > len(self.line_buffer): + value = len(self.line_buffer) + self._point = value + def get_point(self): + return self._point + point = property(get_point, set_point) + + + def visible_line_width(self, position = Point): + """Return the visible width of the text in line buffer up to position.""" + extra_char_width = len([ None for c in self[:position].line_buffer if 0x2013 <= ord(c) <= 0xFFFD]) + return len(self[:position].quoted_text()) + self[:position].line_buffer.count(u"\t")*7 + extra_char_width + + def quoted_text(self): + quoted = [ quote_char(c) for c in self.line_buffer ] + self.line_char_width = [ len(c) for c in quoted ] + return u''.join(map(ensure_unicode, quoted)) + + def get_line_text(self): + buf = self.line_buffer + buf = map(ensure_unicode, buf) + return u''.join(buf) + + def set_line(self, text, cursor = None): + self.line_buffer = [ c for c in str(text) ] + if cursor is None: + self.point = len(self.line_buffer) + else: + self.point = cursor + + def reset_line(self): + self.line_buffer = [] + self.point = 0 + + def end_of_line(self): + self.point = len(self.line_buffer) + + def _insert_text(self, text, argument=1): + text = text * argument + if self.overwrite: + for c in text: + #if self.point: + self.line_buffer[self.point] = c + self.point += 1 + else: + for c in text: + self.line_buffer.insert(self.point, c) + self.point += 1 + + def __getitem__(self, key): + #Check if key is LineSlice, convert to regular slice + #and continue processing + if isinstance(key, LineSlice): + key = key(self) + if isinstance(key, slice): + if key.step is None: + pass + else: + raise Error + if key.start is None: + start = StartOfLine(self) + elif isinstance(key.start,LinePositioner): + start = key.start(self) + else: + start = key.start + if key.stop is None: + stop = EndOfLine(self) + elif isinstance(key.stop, LinePositioner): + stop = key.stop(self) + else: + stop = key.stop + return self.__class__(self.line_buffer[start:stop], point=0) + elif isinstance(key, LinePositioner): + return self.line_buffer[key(self)] + elif isinstance(key, tuple): + raise IndexError(u"Cannot use step in line buffer indexing") #Multiple slice not allowed + else: + # return TextLine(self.line_buffer[key]) + return self.line_buffer[key] + + def __delitem__(self, key): + point = self.point + if isinstance(key, LineSlice): + key = key(self) + if isinstance(key, slice): + start = key.start + stop = key.stop + if isinstance(start, LinePositioner): + start = start(self) + elif start is None: + start=0 + if isinstance(stop, LinePositioner): + stop = stop(self) + elif stop is None: + stop = EndOfLine(self) + elif isinstance(key, LinePositioner): + start = key(self) + stop = start + 1 + else: + start = key + stop = key + 1 + prev = self.line_buffer[:start] + rest = self.line_buffer[stop:] + self.line_buffer = prev + rest + if point > stop: + self.point = point - (stop - start) + elif point >= start and point <= stop: + self.point = start + + + def __setitem__(self, key, value): + if isinstance(key, LineSlice): + key = key(self) + if isinstance(key, slice): + start = key.start + stop = key.stop + elif isinstance(key, LinePositioner): + start = key(self) + stop = start + 1 + else: + start = key + stop = key + 1 + prev = self.line_buffer[:start] + value = self.__class__(value).line_buffer + rest = self.line_buffer[stop:] + out = prev + value + rest + if len(out) >= len(self): + self.point = len(self) + self.line_buffer = out + + def __len__(self): + return len(self.line_buffer) + + def upper(self): + self.line_buffer = [x.upper() for x in self.line_buffer] + return self + + def lower(self): + self.line_buffer = [x.lower() for x in self.line_buffer] + return self + + def capitalize(self): + self.set_line(self.get_line_text().capitalize(), self.point) + return self + + def startswith(self, txt): + return self.get_line_text().startswith(txt) + + def endswith(self, txt): + return self.get_line_text().endswith(txt) + + def __contains__(self, txt): + return txt in self.get_line_text() + + +lines = [TextLine(u"abc"), + TextLine(u"abc def"), + TextLine(u"abc def ghi"), + TextLine(u" abc def "), + ] +l = lines[2] +l.point = 5 + + + +class ReadLineTextBuffer(TextLine): + def __init__(self,txtstr, point = None, mark = None): + super(ReadLineTextBuffer, self).__init__(txtstr, point, mark) + self.enable_win32_clipboard = True + self.selection_mark = -1 + self.enable_selection = True + self.kill_ring = [] + + def __repr__(self): + return u'ReadLineTextBuffer'\ + u'("%s",point=%s,mark=%s,selection_mark=%s)'%\ + (self.line_buffer, self.point, self.mark,self.selection_mark) + + + def insert_text(self, char, argument=1): + self.delete_selection() + self.selection_mark = -1 + self._insert_text(char, argument) + + def to_clipboard(self): + if self.enable_win32_clipboard: + clipboard.set_clipboard_text(self.get_line_text()) + +######### Movement + + def beginning_of_line(self): + self.selection_mark = -1 + self.point = StartOfLine + + def end_of_line(self): + self.selection_mark = -1 + self.point = EndOfLine + + def forward_char(self,argument = 1): + if argument < 0: + self.backward_char(-argument) + self.selection_mark = -1 + for x in range(argument): + self.point = NextChar + + def backward_char(self, argument=1): + if argument < 0: + self.forward_char(-argument) + self.selection_mark = -1 + for x in range(argument): + self.point = PrevChar + + def forward_word(self,argument=1): + if argument<0: + self.backward_word(-argument) + self.selection_mark=-1 + for x in range(argument): + self.point = NextWordStart + + def backward_word(self, argument=1): + if argument < 0: + self.forward_word(-argument) + self.selection_mark = -1 + for x in range(argument): + self.point = PrevWordStart + + def forward_word_end(self, argument=1): + if argument < 0: + self.backward_word_end(-argument) + self.selection_mark = -1 + for x in range(argument): + self.point = NextWordEnd + + def backward_word_end(self, argument=1): + if argument < 0: + self.forward_word_end(-argument) + self.selection_mark = -1 + for x in range(argument): + self.point = NextWordEnd + +######### Movement select + def beginning_of_line_extend_selection(self): + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + self.point = StartOfLine + + def end_of_line_extend_selection(self): + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + self.point = EndOfLine + + def forward_char_extend_selection(self,argument=1): + if argument < 0: + self.backward_char_extend_selection(-argument) + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + for x in range(argument): + self.point = NextChar + + def backward_char_extend_selection(self, argument=1): + if argument < 0: + self.forward_char_extend_selection(-argument) + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + for x in range(argument): + self.point = PrevChar + + def forward_word_extend_selection(self, argument=1): + if argument < 0: + self.backward_word_extend_selection(-argument) + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + for x in range(argument): + self.point = NextWordStart + + def backward_word_extend_selection(self, argument=1): + if argument < 0: + self.forward_word_extend_selection(-argument) + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + for x in range(argument): + self.point = PrevWordStart + + + def forward_word_end_extend_selection(self, argument=1): + if argument < 0: + self.backward_word_end_extend_selection(-argument) + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + for x in range(argument): + self.point = NextWordEnd + + def backward_word_end_extend_selection(self, argument=1): + if argument < 0: + self.forward_word_end_extend_selection(-argument) + if self.enable_selection and self.selection_mark < 0: + self.selection_mark = self.point + for x in range(argument): + self.point = PrevWordEnd + + +######### delete + + def delete_selection(self): + if self.enable_selection and self.selection_mark >= 0: + if self.selection_mark < self.point: + del self[self.selection_mark:self.point] + self.selection_mark = -1 + else: + del self[self.point:self.selection_mark] + self.selection_mark = -1 + return True + else: + self.selection_mark = -1 + return False + + def delete_char(self, argument=1): + if argument < 0: + self.backward_delete_char(-argument) + if self.delete_selection(): + argument -= 1 + for x in range(argument): + del self[Point] + + def backward_delete_char(self, argument=1): + if argument < 0: + self.delete_char(-argument) + if self.delete_selection(): + argument -= 1 + for x in range(argument): + if self.point > 0: + self.backward_char() + self.delete_char() + + def forward_delete_word(self, argument=1): + if argument < 0: + self.backward_delete_word(-argument) + if self.delete_selection(): + argument -= 1 + for x in range(argument): + del self[Point:NextWordStart] + + def backward_delete_word(self, argument=1): + if argument < 0: + self.forward_delete_word(-argument) + if self.delete_selection(): + argument -= 1 + for x in range(argument): + del self[PrevWordStart:Point] + + def delete_current_word(self): + if not self.delete_selection(): + del self[CurrentWord] + self.selection_mark =- 1 + + def delete_horizontal_space(self): + if self[Point] in " \t": + del self[PrevWordEnd:NextWordStart] + self.selection_mark = -1 +######### Case + + def upcase_word(self): + p = self.point + try: + self[CurrentWord] = self[CurrentWord].upper() + self.point = p + except NotAWordError: + pass + + def downcase_word(self): + p = self.point + try: + self[CurrentWord] = self[CurrentWord].lower() + self.point = p + except NotAWordError: + pass + + def capitalize_word(self): + p = self.point + try: + self[CurrentWord] = self[CurrentWord].capitalize() + self.point = p + except NotAWordError: + pass +########### Transpose + def transpose_chars(self): + p2 = Point(self) + if p2 == 0: + return + elif p2 == len(self): + p2 = p2 - 1 + p1 = p2 - 1 + self[p2], self[p1] = self[p1], self[p2] + self.point = p2 + 1 + + def transpose_words(self): + word1 = TextLine(self) + word2 = TextLine(self) + if self.point == len(self): + word2.point = PrevWordStart + word1.point = PrevWordStart(word2) + else: + word1.point = PrevWordStart + word2.point = NextWordStart + stop1 = NextWordEnd(word1) + stop2 = NextWordEnd(word2) + start1 = word1.point + start2 = word2.point + self[start2:stop2] = word1[Point:NextWordEnd] + self[start1:stop1] = word2[Point:NextWordEnd] + self.point = stop2 + + +############ Kill + + def kill_line(self): + self.add_to_kill_ring(self[self.point:]) + del self.line_buffer[self.point:] + + def kill_whole_line(self): + self.add_to_kill_ring(self[:]) + del self[:] + + def backward_kill_line(self): + del self[StartOfLine:Point] + + def unix_line_discard(self): + del self[StartOfLine:Point] + pass + + def kill_word(self): + """Kills to next word ending""" + del self[Point:NextWordEnd] + + def backward_kill_word(self): + """Kills to next word ending""" + if not self.delete_selection(): + del self[PrevWordStart:Point] + self.selection_mark = -1 + + def forward_kill_word(self): + """Kills to next word ending""" + if not self.delete_selection(): + del self[Point:NextWordEnd] + self.selection_mark = -1 + + def unix_word_rubout(self): + if not self.delete_selection(): + del self[PrevSpace:Point] + self.selection_mark = -1 + + def kill_region(self): + pass + + def copy_region_as_kill(self): + pass + + def copy_backward_word(self): + pass + + def copy_forward_word(self): + pass + + + def yank(self): + self.paste_from_kill_ring() + + def yank_pop(self): + pass + +############## Mark + + def set_mark(self): + self.mark = self.point + + def exchange_point_and_mark(self): + pass + + + def copy_region_to_clipboard(self): # () + u'''Copy the text in the region to the windows clipboard.''' + if self.enable_win32_clipboard: + mark = min(self.mark, len(self.line_buffer)) + cursor = min(self.point, len(self.line_buffer)) + if self.mark == -1: + return + begin = min(cursor, mark) + end = max(cursor, mark) + toclipboard = u"".join(self.line_buffer[begin:end]) + clipboard.SetClipboardText(toclipboard) + + def copy_selection_to_clipboard(self): # () + u'''Copy the text in the region to the windows clipboard.''' + if self.enable_win32_clipboard and self.enable_selection and self.selection_mark >= 0: + selection_mark = min(self.selection_mark,len(self.line_buffer)) + cursor = min(self.point,len(self.line_buffer)) + if self.selection_mark == -1: + return + begin = min(cursor, selection_mark) + end = max(cursor, selection_mark) + toclipboard = u"".join(self.line_buffer[begin:end]) + clipboard.SetClipboardText(toclipboard) + + + def cut_selection_to_clipboard(self): # () + self.copy_selection_to_clipboard() + self.delete_selection() +############## Paste + + +############## Kill ring + def add_to_kill_ring(self,txt): + self.kill_ring = [txt] + if kill_ring_to_clipboard: + clipboard.SetClipboardText(txt.get_line_text()) + + + def paste_from_kill_ring(self): + if self.kill_ring: + self.insert_text(self.kill_ring[0]) + + +################################################################## +q = ReadLineTextBuffer(u"asff asFArw ewrWErhg", point=8) +q = TextLine(u"asff asFArw ewrWErhg", point=8) + +def show_pos(buff, pos, chr = u"."): + l = len(buff.line_buffer) + def choice(bool): + if bool: + return chr + else: + return u" " + return u"".join([choice(pos==idx) for idx in range(l + 1)]) + + +def test_positioner(buff, points, positioner): + print (u" %s "%positioner.__class__.__name__).center(40, u"-") + buffstr = buff.line_buffer + + print u'"%s"'%(buffstr) + for point in points: + b = TextLine(buff, point = point) + out=[u" "] * (len(buffstr) + 1) + pos = positioner(b) + if pos == point: + out[pos] = u"&" + else: + out[point] = u"." + out[pos] = u"^" + print u'"%s"'%(u"".join(out)) + +if __name__ == "__main__": + + print u'%-15s "%s"'%(u"Position", q.get_line_text()) + print u'%-15s "%s"'%(u"Point", show_pos(q, q.point)) + + + for name, positioner in all_positioners: + pos = positioner(q) + [] + print u'%-15s "%s"'%(name, show_pos(q, pos, u"^")) + + l = ReadLineTextBuffer(u"kjjk asads asad") + l.point = EndOfLine diff --git a/pyreadline/lineeditor/wordmatcher.py b/pyreadline/lineeditor/wordmatcher.py index 7454516..018b018 100644 --- a/pyreadline/lineeditor/wordmatcher.py +++ b/pyreadline/lineeditor/wordmatcher.py @@ -1,102 +1,102 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -import re, operator - - -def str_find_all(str, ch): - result = [] - index = 0 - while index >= 0: - index = str.find(ch, index) - if index >= 0: - result.append(index) - index += 1 - return result - - -word_pattern = re.compile(u"(x*)") - -def markwords(str, iswordfun): - markers = {True : u"x", False : u"o"} - return "".join([markers[iswordfun(ch)] for ch in str]) - -def split_words(str, iswordfun): - return [x for x in word_pattern.split(markwords(str,iswordfun)) if x != u""] - -def mark_start_segment(str, is_segment): - def mark_start(s): - if s[0:1] == u"x": - return u"s" + s[1:] - else: - return s - return u"".join(map(mark_start, split_words(str, is_segment))) - -def mark_end_segment(str, is_segment): - def mark_start(s): - if s[0:1] == u"x": - return s[:-1] + u"s" - else: - return s - return u"".join(map(mark_start, split_words(str, is_segment))) - -def mark_start_segment_index(str, is_segment): - return str_find_all(mark_start_segment(str, is_segment), u"s") - -def mark_end_segment_index(str, is_segment): - return [x + 1 for x in str_find_all(mark_end_segment(str, is_segment), u"s")] - - -################ Following are used in lineobj ########################### - -def is_word_token(str): - return not is_non_word_token(str) - -def is_non_word_token(str): - if len(str) != 1 or str in u" \t\n": - return True - else: - return False - -def next_start_segment(str, is_segment): - str = u"".join(str) - result = [] - for start in mark_start_segment_index(str, is_segment): - result[len(result):start] = [start for x in range(start - len(result))] - result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)] - return result - -def next_end_segment(str, is_segment): - str = u"".join(str) - result = [] - for start in mark_end_segment_index(str, is_segment): - result[len(result):start] = [start for x in range(start - len(result))] - result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)] - return result - - -def prev_start_segment(str, is_segment): - str = u"".join(str) - result = [] - prev = 0 - for start in mark_start_segment_index(str, is_segment): - result[len(result):start+1] = [prev for x in range(start - len(result) + 1)] - prev=start - result[len(result):len(str)] = [prev for x in range(len(str) - len(result) + 1)] - return result - -def prev_end_segment(str, is_segment): - str = u"".join(str) - result = [] - prev = 0 - for start in mark_end_segment_index(str, is_segment): - result[len(result):start + 1] = [prev for x in range(start - len(result) + 1)] - prev=start - result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)] - return result - +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +import re, operator + + +def str_find_all(str, ch): + result = [] + index = 0 + while index >= 0: + index = str.find(ch, index) + if index >= 0: + result.append(index) + index += 1 + return result + + +word_pattern = re.compile(u"(x*)") + +def markwords(str, iswordfun): + markers = {True : u"x", False : u"o"} + return "".join([markers[iswordfun(ch)] for ch in str]) + +def split_words(str, iswordfun): + return [x for x in word_pattern.split(markwords(str,iswordfun)) if x != u""] + +def mark_start_segment(str, is_segment): + def mark_start(s): + if s[0:1] == u"x": + return u"s" + s[1:] + else: + return s + return u"".join(map(mark_start, split_words(str, is_segment))) + +def mark_end_segment(str, is_segment): + def mark_start(s): + if s[0:1] == u"x": + return s[:-1] + u"s" + else: + return s + return u"".join(map(mark_start, split_words(str, is_segment))) + +def mark_start_segment_index(str, is_segment): + return str_find_all(mark_start_segment(str, is_segment), u"s") + +def mark_end_segment_index(str, is_segment): + return [x + 1 for x in str_find_all(mark_end_segment(str, is_segment), u"s")] + + +################ Following are used in lineobj ########################### + +def is_word_token(str): + return not is_non_word_token(str) + +def is_non_word_token(str): + if len(str) != 1 or str in u" \t\n": + return True + else: + return False + +def next_start_segment(str, is_segment): + str = u"".join(str) + result = [] + for start in mark_start_segment_index(str, is_segment): + result[len(result):start] = [start for x in range(start - len(result))] + result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)] + return result + +def next_end_segment(str, is_segment): + str = u"".join(str) + result = [] + for start in mark_end_segment_index(str, is_segment): + result[len(result):start] = [start for x in range(start - len(result))] + result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)] + return result + + +def prev_start_segment(str, is_segment): + str = u"".join(str) + result = [] + prev = 0 + for start in mark_start_segment_index(str, is_segment): + result[len(result):start+1] = [prev for x in range(start - len(result) + 1)] + prev=start + result[len(result):len(str)] = [prev for x in range(len(str) - len(result) + 1)] + return result + +def prev_end_segment(str, is_segment): + str = u"".join(str) + result = [] + prev = 0 + for start in mark_end_segment_index(str, is_segment): + result[len(result):start + 1] = [prev for x in range(start - len(result) + 1)] + prev=start + result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)] + return result + diff --git a/pyreadline/logger.py b/pyreadline/logger.py index 81e3dc4..10f5d82 100644 --- a/pyreadline/logger.py +++ b/pyreadline/logger.py @@ -1,73 +1,73 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -import socket, logging, logging.handlers -from pyreadline.unicode_helper import ensure_str - -host = u"localhost" -port = logging.handlers.DEFAULT_TCP_LOGGING_PORT - - -pyreadline_logger = logging.getLogger(u'PYREADLINE') -pyreadline_logger.setLevel(logging.DEBUG) -pyreadline_logger.propagate = False -formatter = logging.Formatter('%(message)s') -file_handler = None - -class NULLHandler(logging.Handler): - def emit(self, s): - pass - -class SocketStream(object): - def __init__(self, host, port): - self.logsocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - - def write(self, s): - self.logsocket.sendto(ensure_str(s), (host, port)) - - def flush(self): - pass - - def close(self): - pass - -socket_handler = None -pyreadline_logger.addHandler(NULLHandler()) - -def start_socket_log(): - global socket_handler - socket_handler = logging.StreamHandler(SocketStream(host, port)) - socket_handler.setFormatter(formatter) - pyreadline_logger.addHandler(socket_handler) - -def stop_socket_log(): - global socket_handler - if socket_handler: - pyreadline_logger.removeHandler(socket_handler) - socket_handler = None - -def start_file_log(filename): - global file_handler - file_handler = logging.FileHandler(filename, "w") - pyreadline_logger.addHandler(file_handler) - -def stop_file_log(): - global file_handler - if file_handler: - pyreadline_logger.removeHandler(file_handler) - file_handler.close() - file_handler = None - -def stop_logging(): - log(u"STOPING LOG") - stop_file_log() - stop_socket_log() - -def log(s): - s = ensure_str(s) - pyreadline_logger.debug(s) +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +import socket, logging, logging.handlers +from pyreadline.unicode_helper import ensure_str + +host = u"localhost" +port = logging.handlers.DEFAULT_TCP_LOGGING_PORT + + +pyreadline_logger = logging.getLogger(u'PYREADLINE') +pyreadline_logger.setLevel(logging.DEBUG) +pyreadline_logger.propagate = False +formatter = logging.Formatter('%(message)s') +file_handler = None + +class NULLHandler(logging.Handler): + def emit(self, s): + pass + +class SocketStream(object): + def __init__(self, host, port): + self.logsocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + + def write(self, s): + self.logsocket.sendto(ensure_str(s), (host, port)) + + def flush(self): + pass + + def close(self): + pass + +socket_handler = None +pyreadline_logger.addHandler(NULLHandler()) + +def start_socket_log(): + global socket_handler + socket_handler = logging.StreamHandler(SocketStream(host, port)) + socket_handler.setFormatter(formatter) + pyreadline_logger.addHandler(socket_handler) + +def stop_socket_log(): + global socket_handler + if socket_handler: + pyreadline_logger.removeHandler(socket_handler) + socket_handler = None + +def start_file_log(filename): + global file_handler + file_handler = logging.FileHandler(filename, "w") + pyreadline_logger.addHandler(file_handler) + +def stop_file_log(): + global file_handler + if file_handler: + pyreadline_logger.removeHandler(file_handler) + file_handler.close() + file_handler = None + +def stop_logging(): + log(u"STOPING LOG") + stop_file_log() + stop_socket_log() + +def log(s): + s = ensure_str(s) + pyreadline_logger.debug(s) diff --git a/pyreadline/logserver.py b/pyreadline/logserver.py index df2f365..40c1051 100644 --- a/pyreadline/logserver.py +++ b/pyreadline/logserver.py @@ -1,57 +1,57 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import cPickle -import logging -import logging.handlers -import SocketServer -import struct,socket - -try: - import msvcrt -except ImportError: - msvcrt = None - print u"problem" - - -port = logging.handlers.DEFAULT_TCP_LOGGING_PORT -host = u'localhost' - -def check_key(): - if msvcrt is None: - return False - else: - if msvcrt.kbhit() != 0: - q = msvcrt.getch() - return q - return u"" - - -singleline=False - -def main(): - print u"Starting TCP logserver on port:", port - print u"Press q to quit logserver", port - print u"Press c to clear screen", port - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - s.bind((u"", port)) - s.settimeout(1) - while 1: - try: - data, addr = s.recvfrom(100000) - print data, - except socket.timeout: - key = check_key().lower() - if u"q" == key: - print u"Quitting logserver" - break - elif u"c" == key: - print u"\n" * 100 - -if __name__ == u"__main__": +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import cPickle +import logging +import logging.handlers +import SocketServer +import struct,socket + +try: + import msvcrt +except ImportError: + msvcrt = None + print u"problem" + + +port = logging.handlers.DEFAULT_TCP_LOGGING_PORT +host = u'localhost' + +def check_key(): + if msvcrt is None: + return False + else: + if msvcrt.kbhit() != 0: + q = msvcrt.getch() + return q + return u"" + + +singleline=False + +def main(): + print u"Starting TCP logserver on port:", port + print u"Press q to quit logserver", port + print u"Press c to clear screen", port + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + s.bind((u"", port)) + s.settimeout(1) + while 1: + try: + data, addr = s.recvfrom(100000) + print data, + except socket.timeout: + key = check_key().lower() + if u"q" == key: + print u"Quitting logserver" + break + elif u"c" == key: + print u"\n" * 100 + +if __name__ == u"__main__": main() \ No newline at end of file diff --git a/pyreadline/modes/__init__.py b/pyreadline/modes/__init__.py index b4d6544..8b1ec1e 100644 --- a/pyreadline/modes/__init__.py +++ b/pyreadline/modes/__init__.py @@ -1,5 +1,5 @@ -__all__=["emacs","notemacs","vi"] -import emacs,notemacs,vi -editingmodes=[emacs.EmacsMode,notemacs.NotEmacsMode,vi.ViMode] - +__all__=["emacs","notemacs","vi"] +import emacs,notemacs,vi +editingmodes=[emacs.EmacsMode,notemacs.NotEmacsMode,vi.ViMode] + #add check to ensure all modes have unique mode names \ No newline at end of file diff --git a/pyreadline/modes/basemode.py b/pyreadline/modes/basemode.py index bf458d3..f793a51 100644 --- a/pyreadline/modes/basemode.py +++ b/pyreadline/modes/basemode.py @@ -1,561 +1,561 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2003-2006 Gary Bishop. -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import os,re,math,glob,sys,time -import pyreadline.logger as logger -from pyreadline.logger import log -from pyreadline.keysyms.common import make_KeyPress_from_keydescr -import pyreadline.lineeditor.lineobj as lineobj -import pyreadline.lineeditor.history as history -import pyreadline.clipboard as clipboard -from pyreadline.error import ReadlineError,GetSetError -from pyreadline.unicode_helper import ensure_str, ensure_unicode -in_ironpython=u"IronPython" in sys.version - -class BaseMode(object): - mode=u"base" - def __init__(self,rlobj): - self.argument=0 - self.rlobj=rlobj - self.exit_dispatch = {} - self.key_dispatch = {} - self.argument=1 - self.prevargument=None - self.l_buffer=lineobj.ReadLineTextBuffer("") - self._history=history.LineHistory() - self.completer_delims = u" \t\n\"\\'`@$><=;|&{(" - self.show_all_if_ambiguous = u'off' - self.mark_directories = u'on' - self.complete_filesystem = u'off' - self.completer = None - self.begidx = 0 - self.endidx = 0 - self.tabstop = 4 - self.startup_hook = None - self.pre_input_hook = None - self.first_prompt = True - self.cursor_size=25 - - self.prompt = u">>> " - - #Paste settings - #assumes data on clipboard is path if shorter than 300 characters and doesn't contain \t or \n - #and replace \ with / for easier use in ipython - self.enable_ipython_paste_for_paths=True - - #automatically convert tabseparated data to list of lists or array constructors - self.enable_ipython_paste_list_of_lists=True - self.enable_win32_clipboard=True - - self.paste_line_buffer=[] - - self._sub_modes=[] - - - def __repr__(self): - return u"" - - def _gs(x): - def g(self): - return getattr(self.rlobj,x) - def s(self,q): - setattr(self.rlobj,x,q) - return g,s - - def _g(x): - def g(self): - return getattr(self.rlobj,x) - return g - - def _argreset(self): - val=self.argument - self.argument=0 - if val==0: - val=1 - return val - argument_reset=property(_argreset) - -#used in readline - ctrl_c_tap_time_interval=property(*_gs(u"ctrl_c_tap_time_interval")) - allow_ctrl_c=property(*_gs(u"allow_ctrl_c")) - _print_prompt=property(_g(u"_print_prompt")) - _update_line=property(_g(u"_update_line")) - console=property(_g(u"console")) - prompt_begin_pos=property(_g(u"prompt_begin_pos")) - prompt_end_pos=property(_g(u"prompt_end_pos")) - -#used in completer _completions -# completer_delims=property(*_gs("completer_delims")) - _bell=property(_g(u"_bell")) - bell_style=property(_g(u"bell_style")) - -#used in emacs - _clear_after=property(_g(u"_clear_after")) - _update_prompt_pos=property(_g(u"_update_prompt_pos")) - -#not used in basemode or emacs - - def process_keyevent(self, keyinfo): - raise NotImplementedError - - def readline_setup(self, prompt=u''): - self.l_buffer.selection_mark=-1 - if self.first_prompt: - self.first_prompt = False - if self.startup_hook: - try: - self.startup_hook() - except: - print u'startup hook failed' - traceback.print_exc() - - self.l_buffer.reset_line() - self.prompt = prompt - - if self.pre_input_hook: - try: - self.pre_input_hook() - except: - print u'pre_input_hook failed' - traceback.print_exc() - self.pre_input_hook = None - - -#################################### - - - def finalize(self): - u"""Every bindable command should call this function for cleanup. - Except those that want to set argument to a non-zero value. - """ - self.argument = 0 - - - def add_history(self, text): - self._history.add_history(lineobj.ReadLineTextBuffer(text)) - - - #Create key bindings: - def rl_settings_to_string(self): - out=[u"%-20s: %s"%(u"show all if ambigous",self.show_all_if_ambiguous)] - out.append(u"%-20s: %s"%(u"mark_directories",self.mark_directories)) - out.append(u"%-20s: %s"%(u"bell_style",self.bell_style)) - out.append(u"------------- key bindings ------------") - tablepat=u"%-7s %-7s %-7s %-15s %-15s " - out.append(tablepat%(u"Control",u"Meta",u"Shift",u"Keycode/char",u"Function")) - bindings=[(k[0],k[1],k[2],k[3],v.__name__) for k,v in self.key_dispatch.iteritems()] - bindings.sort() - for key in bindings: - out.append(tablepat%(key)) - return out - - - def _bind_key(self, key, func): - u"""setup the mapping from key to call the function.""" - if not callable(func): - print u"Trying to bind non method to keystroke:%s,%s"%(key,func) - raise ReadlineError(u"Trying to bind non method to keystroke:%s,%s,%s,%s"%(key,func,type(func),type(self._bind_key))) - keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple() - log(u">>>%s -> %s<<<"%(keyinfo,func.__name__)) - self.key_dispatch[keyinfo] = func - - def _bind_exit_key(self, key): - u"""setup the mapping from key to call the function.""" - keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple() - self.exit_dispatch[keyinfo] = None - - def init_editing_mode(self, e): # (C-e) - """When in vi command mode, this causes a switch to emacs editing - mode.""" - - raise NotImplementedError -#completion commands - - def _get_completions(self): - """Return a list of possible completions for the string ending at the point. - Also set begidx and endidx in the process.""" - completions = [] - self.begidx = self.l_buffer.point - self.endidx = self.l_buffer.point - buf=self.l_buffer.line_buffer - if self.completer: - # get the string to complete - while self.begidx > 0: - self.begidx -= 1 - if buf[self.begidx] in self.completer_delims: - self.begidx += 1 - break - text = ensure_str(u''.join(buf[self.begidx:self.endidx])) - log(u'complete text="%s"' % ensure_unicode(text)) - i = 0 - while 1: - try: - r = ensure_unicode(self.completer(text, i)) - except IndexError: - break - i += 1 - if r is None: - break - elif r and r not in completions: - completions.append(r) - else: - pass - log(u'text completions=<%s>' % map(ensure_unicode, completions)) - if (self.complete_filesystem == "on") and not completions: - # get the filename to complete - while self.begidx > 0: - self.begidx -= 1 - if buf[self.begidx] in u' \t\n': - self.begidx += 1 - break - text = ensure_str(u''.join(buf[self.begidx:self.endidx])) - log(u'file complete text="%s"' % ensure_unicode(text)) - completions = map(ensure_unicode, glob.glob(os.path.expanduser(text) + '*')) - if self.mark_directories == u'on': - mc = [] - for f in completions: - if os.path.isdir(f): - mc.append(f + os.sep) - else: - mc.append(f) - completions = mc - log(u'fnames=<%s>' % map(ensure_unicode, completions)) - return completions - - - def _display_completions(self, completions): - if not completions: - return - self.console.write(u'\n') - wmax = max(map(len, completions)) - w, h = self.console.size() - cols = max(1, int((w-1) / (wmax+1))) - rows = int(math.ceil(float(len(completions)) / cols)) - for row in range(rows): - s = u'' - for col in range(cols): - i = col*rows + row - if i < len(completions): - self.console.write(completions[i].ljust(wmax+1)) - self.console.write(u'\n') - if in_ironpython: - self.prompt=sys.ps1 - self._print_prompt() - - - def complete(self, e): # (TAB) - u"""Attempt to perform completion on the text before point. The - actual completion performed is application-specific. The default is - filename completion.""" - completions = self._get_completions() - if completions: - cprefix = commonprefix(completions) - if len(cprefix) > 0: - rep = [ c for c in cprefix ] - point=self.l_buffer.point - self.l_buffer[self.begidx:self.endidx] = rep - self.l_buffer.point = point + len(rep) - (self.endidx - self.begidx) - if len(completions) > 1: - if self.show_all_if_ambiguous == u'on': - self._display_completions(completions) - else: - self._bell() - else: - self._bell() - self.finalize() - - def possible_completions(self, e): # (M-?) - u"""List the possible completions of the text before point. """ - completions = self._get_completions() - self._display_completions(completions) - self.finalize() - - def insert_completions(self, e): # (M-*) - u"""Insert all completions of the text before point that would have - been generated by possible-completions.""" - completions = self._get_completions() - b = self.begidx - e = self.endidx - for comp in completions: - rep = [ c for c in comp ] - rep.append(' ') - self.l_buffer[b:e] = rep - b += len(rep) - e = b - self.line_cursor = b - self.finalize() - - def menu_complete(self, e): # () - u"""Similar to complete, but replaces the word to be completed with a - single match from the list of possible completions. Repeated - execution of menu-complete steps through the list of possible - completions, inserting each match in turn. At the end of the list of - completions, the bell is rung (subject to the setting of bell-style) - and the original text is restored. An argument of n moves n - positions forward in the list of matches; a negative argument may be - used to move backward through the list. This command is intended to - be bound to TAB, but is unbound by default.""" - self.finalize() - - ### Methods below here are bindable emacs functions - - - def insert_text(self, string): - u"""Insert text into the command line.""" - self.l_buffer.insert_text(string, self.argument_reset) - self.finalize() - - def beginning_of_line(self, e): # (C-a) - u"""Move to the start of the current line. """ - self.l_buffer.beginning_of_line() - self.finalize() - - def end_of_line(self, e): # (C-e) - u"""Move to the end of the line. """ - self.l_buffer.end_of_line() - self.finalize() - - def forward_char(self, e): # (C-f) - u"""Move forward a character. """ - self.l_buffer.forward_char(self.argument_reset) - self.finalize() - - def backward_char(self, e): # (C-b) - u"""Move back a character. """ - self.l_buffer.backward_char(self.argument_reset) - self.finalize() - - def forward_word(self, e): # (M-f) - u"""Move forward to the end of the next word. Words are composed of - letters and digits.""" - self.l_buffer.forward_word(self.argument_reset) - self.finalize() - - def backward_word(self, e): # (M-b) - u"""Move back to the start of the current or previous word. Words are - composed of letters and digits.""" - self.l_buffer.backward_word(self.argument_reset) - self.finalize() - - def forward_word_end(self, e): # () - u"""Move forward to the end of the next word. Words are composed of - letters and digits.""" - self.l_buffer.forward_word_end(self.argument_reset) - self.finalize() - - def backward_word_end(self, e): # () - u"""Move forward to the end of the next word. Words are composed of - letters and digits.""" - self.l_buffer.backward_word_end(self.argument_reset) - self.finalize() - -### Movement with extend selection - def beginning_of_line_extend_selection(self, e): # - u"""Move to the start of the current line. """ - self.l_buffer.beginning_of_line_extend_selection() - self.finalize() - - def end_of_line_extend_selection(self, e): # - u"""Move to the end of the line. """ - self.l_buffer.end_of_line_extend_selection() - self.finalize() - - def forward_char_extend_selection(self, e): # - u"""Move forward a character. """ - self.l_buffer.forward_char_extend_selection(self.argument_reset) - self.finalize() - - def backward_char_extend_selection(self, e): # - u"""Move back a character. """ - self.l_buffer.backward_char_extend_selection(self.argument_reset) - self.finalize() - - def forward_word_extend_selection(self, e): # - u"""Move forward to the end of the next word. Words are composed of - letters and digits.""" - self.l_buffer.forward_word_extend_selection(self.argument_reset) - self.finalize() - - def backward_word_extend_selection(self, e): # - u"""Move back to the start of the current or previous word. Words are - composed of letters and digits.""" - self.l_buffer.backward_word_extend_selection(self.argument_reset) - self.finalize() - - def forward_word_end_extend_selection(self, e): # - u"""Move forward to the end of the next word. Words are composed of - letters and digits.""" - self.l_buffer.forward_word_end_extend_selection(self.argument_reset) - self.finalize() - - def backward_word_end_extend_selection(self, e): # - u"""Move forward to the end of the next word. Words are composed of - letters and digits.""" - self.l_buffer.forward_word_end_extend_selection(self.argument_reset) - self.finalize() - - -######## Change case - - def upcase_word(self, e): # (M-u) - u"""Uppercase the current (or following) word. With a negative - argument, uppercase the previous word, but do not move the cursor.""" - self.l_buffer.upcase_word() - self.finalize() - - def downcase_word(self, e): # (M-l) - u"""Lowercase the current (or following) word. With a negative - argument, lowercase the previous word, but do not move the cursor.""" - self.l_buffer.downcase_word() - self.finalize() - - def capitalize_word(self, e): # (M-c) - u"""Capitalize the current (or following) word. With a negative - argument, capitalize the previous word, but do not move the cursor.""" - self.l_buffer.capitalize_word() - self.finalize() - - -######## - def clear_screen(self, e): # (C-l) - u"""Clear the screen and redraw the current line, leaving the current - line at the top of the screen.""" - self.console.page() - self.finalize() - - def redraw_current_line(self, e): # () - u"""Refresh the current line. By default, this is unbound.""" - self.finalize() - - def accept_line(self, e): # (Newline or Return) - u"""Accept the line regardless of where the cursor is. If this line - is non-empty, it may be added to the history list for future recall - with add_history(). If this line is a modified history line, the - history line is restored to its original state.""" - self.finalize() - return True - - def delete_char(self, e): # (C-d) - u"""Delete the character at point. If point is at the beginning of - the line, there are no characters in the line, and the last - character typed was not bound to delete-char, then return EOF.""" - self.l_buffer.delete_char(self.argument_reset) - self.finalize() - - def backward_delete_char(self, e): # (Rubout) - u"""Delete the character behind the cursor. A numeric argument means - to kill the characters instead of deleting them.""" - self.l_buffer.backward_delete_char(self.argument_reset) - self.finalize() - - def backward_delete_word(self, e): # (Control-Rubout) - u"""Delete the character behind the cursor. A numeric argument means - to kill the characters instead of deleting them.""" - self.l_buffer.backward_delete_word(self.argument_reset) - self.finalize() - - def forward_delete_word(self, e): # (Control-Delete) - u"""Delete the character behind the cursor. A numeric argument means - to kill the characters instead of deleting them.""" - self.l_buffer.forward_delete_word(self.argument_reset) - self.finalize() - - def delete_horizontal_space(self, e): # () - u"""Delete all spaces and tabs around point. By default, this is unbound. """ - self.l_buffer.delete_horizontal_space() - self.finalize() - - def self_insert(self, e): # (a, b, A, 1, !, ...) - u"""Insert yourself. """ - if e.char and ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys. - self.insert_text(e.char) - self.finalize() - - -# Paste from clipboard - - def paste(self,e): - u"""Paste windows clipboard. - Assume single line strip other lines and end of line markers and trailing spaces""" #(Control-v) - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert(False) - txt=txt.split("\n")[0].strip("\r").strip("\n") - log("paste: >%s<"%map(ord,txt)) - self.insert_text(txt) - self.finalize() - - def paste_mulitline_code(self,e): - u"""Paste windows clipboard as multiline code. - Removes any empty lines in the code""" - reg=re.compile("\r?\n") - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert(False) - t=reg.split(txt) - t=[row for row in t if row.strip()!=""] #remove empty lines - if t!=[""]: - self.insert_text(t[0]) - self.add_history(self.l_buffer.copy()) - self.paste_line_buffer=t[1:] - log("multi: >%s<"%self.paste_line_buffer) - return True - else: - return False - self.finalize() - - def ipython_paste(self,e): - u"""Paste windows clipboard. If enable_ipython_paste_list_of_lists is - True then try to convert tabseparated data to repr of list of lists or - repr of array. - If enable_ipython_paste_for_paths==True then change \\ to / and spaces to \space""" - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert( - self.enable_ipython_paste_list_of_lists) - if self.enable_ipython_paste_for_paths: - if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt): - txt=txt.replace("\\","/").replace(" ",r"\ ") - self.insert_text(txt) - self.finalize() - - - def copy_region_to_clipboard(self, e): # () - u"""Copy the text in the region to the windows clipboard.""" - self.l_buffer.copy_region_to_clipboard() - self.finalize() - - def copy_selection_to_clipboard(self, e): # () - u"""Copy the text in the region to the windows clipboard.""" - self.l_buffer.copy_selection_to_clipboard() - self.finalize() - - def cut_selection_to_clipboard(self, e): # () - u"""Copy the text in the region to the windows clipboard.""" - self.l_buffer.cut_selection_to_clipboard() - self.finalize() - - def dump_functions(self, e): # () - u"""Print all of the functions and their key bindings to the Readline - output stream. If a numeric argument is supplied, the output is - formatted in such a way that it can be made part of an inputrc - file. This command is unbound by default.""" - print - txt="\n".join(self.rl_settings_to_string()) - print txt - self._print_prompt() - self.finalize() - -def commonprefix(m): - u"Given a list of pathnames, returns the longest common leading component" - if not m: return u'' - prefix = m[0] - for item in m: - for i in range(len(prefix)): - if prefix[:i+1].lower() != item[:i+1].lower(): - prefix = prefix[:i] - if i == 0: return u'' - break - return prefix +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Gary Bishop. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import os,re,math,glob,sys,time +import pyreadline.logger as logger +from pyreadline.logger import log +from pyreadline.keysyms.common import make_KeyPress_from_keydescr +import pyreadline.lineeditor.lineobj as lineobj +import pyreadline.lineeditor.history as history +import pyreadline.clipboard as clipboard +from pyreadline.error import ReadlineError,GetSetError +from pyreadline.unicode_helper import ensure_str, ensure_unicode +in_ironpython=u"IronPython" in sys.version + +class BaseMode(object): + mode=u"base" + def __init__(self,rlobj): + self.argument=0 + self.rlobj=rlobj + self.exit_dispatch = {} + self.key_dispatch = {} + self.argument=1 + self.prevargument=None + self.l_buffer=lineobj.ReadLineTextBuffer("") + self._history=history.LineHistory() + self.completer_delims = u" \t\n\"\\'`@$><=;|&{(" + self.show_all_if_ambiguous = u'off' + self.mark_directories = u'on' + self.complete_filesystem = u'off' + self.completer = None + self.begidx = 0 + self.endidx = 0 + self.tabstop = 4 + self.startup_hook = None + self.pre_input_hook = None + self.first_prompt = True + self.cursor_size=25 + + self.prompt = u">>> " + + #Paste settings + #assumes data on clipboard is path if shorter than 300 characters and doesn't contain \t or \n + #and replace \ with / for easier use in ipython + self.enable_ipython_paste_for_paths=True + + #automatically convert tabseparated data to list of lists or array constructors + self.enable_ipython_paste_list_of_lists=True + self.enable_win32_clipboard=True + + self.paste_line_buffer=[] + + self._sub_modes=[] + + + def __repr__(self): + return u"" + + def _gs(x): + def g(self): + return getattr(self.rlobj,x) + def s(self,q): + setattr(self.rlobj,x,q) + return g,s + + def _g(x): + def g(self): + return getattr(self.rlobj,x) + return g + + def _argreset(self): + val=self.argument + self.argument=0 + if val==0: + val=1 + return val + argument_reset=property(_argreset) + +#used in readline + ctrl_c_tap_time_interval=property(*_gs(u"ctrl_c_tap_time_interval")) + allow_ctrl_c=property(*_gs(u"allow_ctrl_c")) + _print_prompt=property(_g(u"_print_prompt")) + _update_line=property(_g(u"_update_line")) + console=property(_g(u"console")) + prompt_begin_pos=property(_g(u"prompt_begin_pos")) + prompt_end_pos=property(_g(u"prompt_end_pos")) + +#used in completer _completions +# completer_delims=property(*_gs("completer_delims")) + _bell=property(_g(u"_bell")) + bell_style=property(_g(u"bell_style")) + +#used in emacs + _clear_after=property(_g(u"_clear_after")) + _update_prompt_pos=property(_g(u"_update_prompt_pos")) + +#not used in basemode or emacs + + def process_keyevent(self, keyinfo): + raise NotImplementedError + + def readline_setup(self, prompt=u''): + self.l_buffer.selection_mark=-1 + if self.first_prompt: + self.first_prompt = False + if self.startup_hook: + try: + self.startup_hook() + except: + print u'startup hook failed' + traceback.print_exc() + + self.l_buffer.reset_line() + self.prompt = prompt + + if self.pre_input_hook: + try: + self.pre_input_hook() + except: + print u'pre_input_hook failed' + traceback.print_exc() + self.pre_input_hook = None + + +#################################### + + + def finalize(self): + u"""Every bindable command should call this function for cleanup. + Except those that want to set argument to a non-zero value. + """ + self.argument = 0 + + + def add_history(self, text): + self._history.add_history(lineobj.ReadLineTextBuffer(text)) + + + #Create key bindings: + def rl_settings_to_string(self): + out=[u"%-20s: %s"%(u"show all if ambigous",self.show_all_if_ambiguous)] + out.append(u"%-20s: %s"%(u"mark_directories",self.mark_directories)) + out.append(u"%-20s: %s"%(u"bell_style",self.bell_style)) + out.append(u"------------- key bindings ------------") + tablepat=u"%-7s %-7s %-7s %-15s %-15s " + out.append(tablepat%(u"Control",u"Meta",u"Shift",u"Keycode/char",u"Function")) + bindings=[(k[0],k[1],k[2],k[3],v.__name__) for k,v in self.key_dispatch.iteritems()] + bindings.sort() + for key in bindings: + out.append(tablepat%(key)) + return out + + + def _bind_key(self, key, func): + u"""setup the mapping from key to call the function.""" + if not callable(func): + print u"Trying to bind non method to keystroke:%s,%s"%(key,func) + raise ReadlineError(u"Trying to bind non method to keystroke:%s,%s,%s,%s"%(key,func,type(func),type(self._bind_key))) + keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple() + log(u">>>%s -> %s<<<"%(keyinfo,func.__name__)) + self.key_dispatch[keyinfo] = func + + def _bind_exit_key(self, key): + u"""setup the mapping from key to call the function.""" + keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple() + self.exit_dispatch[keyinfo] = None + + def init_editing_mode(self, e): # (C-e) + """When in vi command mode, this causes a switch to emacs editing + mode.""" + + raise NotImplementedError +#completion commands + + def _get_completions(self): + """Return a list of possible completions for the string ending at the point. + Also set begidx and endidx in the process.""" + completions = [] + self.begidx = self.l_buffer.point + self.endidx = self.l_buffer.point + buf=self.l_buffer.line_buffer + if self.completer: + # get the string to complete + while self.begidx > 0: + self.begidx -= 1 + if buf[self.begidx] in self.completer_delims: + self.begidx += 1 + break + text = ensure_str(u''.join(buf[self.begidx:self.endidx])) + log(u'complete text="%s"' % ensure_unicode(text)) + i = 0 + while 1: + try: + r = ensure_unicode(self.completer(text, i)) + except IndexError: + break + i += 1 + if r is None: + break + elif r and r not in completions: + completions.append(r) + else: + pass + log(u'text completions=<%s>' % map(ensure_unicode, completions)) + if (self.complete_filesystem == "on") and not completions: + # get the filename to complete + while self.begidx > 0: + self.begidx -= 1 + if buf[self.begidx] in u' \t\n': + self.begidx += 1 + break + text = ensure_str(u''.join(buf[self.begidx:self.endidx])) + log(u'file complete text="%s"' % ensure_unicode(text)) + completions = map(ensure_unicode, glob.glob(os.path.expanduser(text) + '*')) + if self.mark_directories == u'on': + mc = [] + for f in completions: + if os.path.isdir(f): + mc.append(f + os.sep) + else: + mc.append(f) + completions = mc + log(u'fnames=<%s>' % map(ensure_unicode, completions)) + return completions + + + def _display_completions(self, completions): + if not completions: + return + self.console.write(u'\n') + wmax = max(map(len, completions)) + w, h = self.console.size() + cols = max(1, int((w-1) / (wmax+1))) + rows = int(math.ceil(float(len(completions)) / cols)) + for row in range(rows): + s = u'' + for col in range(cols): + i = col*rows + row + if i < len(completions): + self.console.write(completions[i].ljust(wmax+1)) + self.console.write(u'\n') + if in_ironpython: + self.prompt=sys.ps1 + self._print_prompt() + + + def complete(self, e): # (TAB) + u"""Attempt to perform completion on the text before point. The + actual completion performed is application-specific. The default is + filename completion.""" + completions = self._get_completions() + if completions: + cprefix = commonprefix(completions) + if len(cprefix) > 0: + rep = [ c for c in cprefix ] + point=self.l_buffer.point + self.l_buffer[self.begidx:self.endidx] = rep + self.l_buffer.point = point + len(rep) - (self.endidx - self.begidx) + if len(completions) > 1: + if self.show_all_if_ambiguous == u'on': + self._display_completions(completions) + else: + self._bell() + else: + self._bell() + self.finalize() + + def possible_completions(self, e): # (M-?) + u"""List the possible completions of the text before point. """ + completions = self._get_completions() + self._display_completions(completions) + self.finalize() + + def insert_completions(self, e): # (M-*) + u"""Insert all completions of the text before point that would have + been generated by possible-completions.""" + completions = self._get_completions() + b = self.begidx + e = self.endidx + for comp in completions: + rep = [ c for c in comp ] + rep.append(' ') + self.l_buffer[b:e] = rep + b += len(rep) + e = b + self.line_cursor = b + self.finalize() + + def menu_complete(self, e): # () + u"""Similar to complete, but replaces the word to be completed with a + single match from the list of possible completions. Repeated + execution of menu-complete steps through the list of possible + completions, inserting each match in turn. At the end of the list of + completions, the bell is rung (subject to the setting of bell-style) + and the original text is restored. An argument of n moves n + positions forward in the list of matches; a negative argument may be + used to move backward through the list. This command is intended to + be bound to TAB, but is unbound by default.""" + self.finalize() + + ### Methods below here are bindable emacs functions + + + def insert_text(self, string): + u"""Insert text into the command line.""" + self.l_buffer.insert_text(string, self.argument_reset) + self.finalize() + + def beginning_of_line(self, e): # (C-a) + u"""Move to the start of the current line. """ + self.l_buffer.beginning_of_line() + self.finalize() + + def end_of_line(self, e): # (C-e) + u"""Move to the end of the line. """ + self.l_buffer.end_of_line() + self.finalize() + + def forward_char(self, e): # (C-f) + u"""Move forward a character. """ + self.l_buffer.forward_char(self.argument_reset) + self.finalize() + + def backward_char(self, e): # (C-b) + u"""Move back a character. """ + self.l_buffer.backward_char(self.argument_reset) + self.finalize() + + def forward_word(self, e): # (M-f) + u"""Move forward to the end of the next word. Words are composed of + letters and digits.""" + self.l_buffer.forward_word(self.argument_reset) + self.finalize() + + def backward_word(self, e): # (M-b) + u"""Move back to the start of the current or previous word. Words are + composed of letters and digits.""" + self.l_buffer.backward_word(self.argument_reset) + self.finalize() + + def forward_word_end(self, e): # () + u"""Move forward to the end of the next word. Words are composed of + letters and digits.""" + self.l_buffer.forward_word_end(self.argument_reset) + self.finalize() + + def backward_word_end(self, e): # () + u"""Move forward to the end of the next word. Words are composed of + letters and digits.""" + self.l_buffer.backward_word_end(self.argument_reset) + self.finalize() + +### Movement with extend selection + def beginning_of_line_extend_selection(self, e): # + u"""Move to the start of the current line. """ + self.l_buffer.beginning_of_line_extend_selection() + self.finalize() + + def end_of_line_extend_selection(self, e): # + u"""Move to the end of the line. """ + self.l_buffer.end_of_line_extend_selection() + self.finalize() + + def forward_char_extend_selection(self, e): # + u"""Move forward a character. """ + self.l_buffer.forward_char_extend_selection(self.argument_reset) + self.finalize() + + def backward_char_extend_selection(self, e): # + u"""Move back a character. """ + self.l_buffer.backward_char_extend_selection(self.argument_reset) + self.finalize() + + def forward_word_extend_selection(self, e): # + u"""Move forward to the end of the next word. Words are composed of + letters and digits.""" + self.l_buffer.forward_word_extend_selection(self.argument_reset) + self.finalize() + + def backward_word_extend_selection(self, e): # + u"""Move back to the start of the current or previous word. Words are + composed of letters and digits.""" + self.l_buffer.backward_word_extend_selection(self.argument_reset) + self.finalize() + + def forward_word_end_extend_selection(self, e): # + u"""Move forward to the end of the next word. Words are composed of + letters and digits.""" + self.l_buffer.forward_word_end_extend_selection(self.argument_reset) + self.finalize() + + def backward_word_end_extend_selection(self, e): # + u"""Move forward to the end of the next word. Words are composed of + letters and digits.""" + self.l_buffer.forward_word_end_extend_selection(self.argument_reset) + self.finalize() + + +######## Change case + + def upcase_word(self, e): # (M-u) + u"""Uppercase the current (or following) word. With a negative + argument, uppercase the previous word, but do not move the cursor.""" + self.l_buffer.upcase_word() + self.finalize() + + def downcase_word(self, e): # (M-l) + u"""Lowercase the current (or following) word. With a negative + argument, lowercase the previous word, but do not move the cursor.""" + self.l_buffer.downcase_word() + self.finalize() + + def capitalize_word(self, e): # (M-c) + u"""Capitalize the current (or following) word. With a negative + argument, capitalize the previous word, but do not move the cursor.""" + self.l_buffer.capitalize_word() + self.finalize() + + +######## + def clear_screen(self, e): # (C-l) + u"""Clear the screen and redraw the current line, leaving the current + line at the top of the screen.""" + self.console.page() + self.finalize() + + def redraw_current_line(self, e): # () + u"""Refresh the current line. By default, this is unbound.""" + self.finalize() + + def accept_line(self, e): # (Newline or Return) + u"""Accept the line regardless of where the cursor is. If this line + is non-empty, it may be added to the history list for future recall + with add_history(). If this line is a modified history line, the + history line is restored to its original state.""" + self.finalize() + return True + + def delete_char(self, e): # (C-d) + u"""Delete the character at point. If point is at the beginning of + the line, there are no characters in the line, and the last + character typed was not bound to delete-char, then return EOF.""" + self.l_buffer.delete_char(self.argument_reset) + self.finalize() + + def backward_delete_char(self, e): # (Rubout) + u"""Delete the character behind the cursor. A numeric argument means + to kill the characters instead of deleting them.""" + self.l_buffer.backward_delete_char(self.argument_reset) + self.finalize() + + def backward_delete_word(self, e): # (Control-Rubout) + u"""Delete the character behind the cursor. A numeric argument means + to kill the characters instead of deleting them.""" + self.l_buffer.backward_delete_word(self.argument_reset) + self.finalize() + + def forward_delete_word(self, e): # (Control-Delete) + u"""Delete the character behind the cursor. A numeric argument means + to kill the characters instead of deleting them.""" + self.l_buffer.forward_delete_word(self.argument_reset) + self.finalize() + + def delete_horizontal_space(self, e): # () + u"""Delete all spaces and tabs around point. By default, this is unbound. """ + self.l_buffer.delete_horizontal_space() + self.finalize() + + def self_insert(self, e): # (a, b, A, 1, !, ...) + u"""Insert yourself. """ + if e.char and ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys. + self.insert_text(e.char) + self.finalize() + + +# Paste from clipboard + + def paste(self,e): + u"""Paste windows clipboard. + Assume single line strip other lines and end of line markers and trailing spaces""" #(Control-v) + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + txt=txt.split("\n")[0].strip("\r").strip("\n") + log("paste: >%s<"%map(ord,txt)) + self.insert_text(txt) + self.finalize() + + def paste_mulitline_code(self,e): + u"""Paste windows clipboard as multiline code. + Removes any empty lines in the code""" + reg=re.compile("\r?\n") + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + t=reg.split(txt) + t=[row for row in t if row.strip()!=""] #remove empty lines + if t!=[""]: + self.insert_text(t[0]) + self.add_history(self.l_buffer.copy()) + self.paste_line_buffer=t[1:] + log("multi: >%s<"%self.paste_line_buffer) + return True + else: + return False + self.finalize() + + def ipython_paste(self,e): + u"""Paste windows clipboard. If enable_ipython_paste_list_of_lists is + True then try to convert tabseparated data to repr of list of lists or + repr of array. + If enable_ipython_paste_for_paths==True then change \\ to / and spaces to \space""" + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert( + self.enable_ipython_paste_list_of_lists) + if self.enable_ipython_paste_for_paths: + if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt): + txt=txt.replace("\\","/").replace(" ",r"\ ") + self.insert_text(txt) + self.finalize() + + + def copy_region_to_clipboard(self, e): # () + u"""Copy the text in the region to the windows clipboard.""" + self.l_buffer.copy_region_to_clipboard() + self.finalize() + + def copy_selection_to_clipboard(self, e): # () + u"""Copy the text in the region to the windows clipboard.""" + self.l_buffer.copy_selection_to_clipboard() + self.finalize() + + def cut_selection_to_clipboard(self, e): # () + u"""Copy the text in the region to the windows clipboard.""" + self.l_buffer.cut_selection_to_clipboard() + self.finalize() + + def dump_functions(self, e): # () + u"""Print all of the functions and their key bindings to the Readline + output stream. If a numeric argument is supplied, the output is + formatted in such a way that it can be made part of an inputrc + file. This command is unbound by default.""" + print + txt="\n".join(self.rl_settings_to_string()) + print txt + self._print_prompt() + self.finalize() + +def commonprefix(m): + u"Given a list of pathnames, returns the longest common leading component" + if not m: return u'' + prefix = m[0] + for item in m: + for i in range(len(prefix)): + if prefix[:i+1].lower() != item[:i+1].lower(): + prefix = prefix[:i] + if i == 0: return u'' + break + return prefix diff --git a/pyreadline/modes/emacs.py b/pyreadline/modes/emacs.py index 013cabe..d583485 100644 --- a/pyreadline/modes/emacs.py +++ b/pyreadline/modes/emacs.py @@ -1,730 +1,730 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2003-2006 Gary Bishop. -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import os, sys, time -import pyreadline.logger as logger -from pyreadline.logger import log -from pyreadline.lineeditor.lineobj import Point -import pyreadline.lineeditor.lineobj as lineobj -import pyreadline.lineeditor.history as history -import basemode -from pyreadline.unicode_helper import ensure_unicode - - -def format(keyinfo): - if len(keyinfo[-1]) != 1: - k = keyinfo + (-1,) - else: - k = keyinfo + (ord(keyinfo[-1]),) - return u"(%s,%s,%s,%s,%x)"%k - -in_ironpython = u"IronPython" in sys.version - - -class IncrementalSearchPromptMode(object): - def __init__(self, rlobj): - pass - - def _process_incremental_search_keyevent(self, keyinfo): - log("_process_incremental_search_keyevent") - keytuple = keyinfo.tuple() - #dispatch_func = self.key_dispatch.get(keytuple, default) - revtuples = [] - fwdtuples = [] - for ktuple, func in self.key_dispatch.iteritems(): - if func == self.reverse_search_history: - revtuples.append(ktuple) - elif func == self.forward_search_history: - fwdtuples.append(ktuple) - - - log(u"IncrementalSearchPromptMode %s %s"%(keyinfo, keytuple)) - if keyinfo.keyname == u'backspace': - self.subsearch_query = self.subsearch_query[:-1] - if len(self.subsearch_query) > 0: - self.line = self.subsearch_fun(self.subsearch_query) - else: - self._bell() - self.line = "" # empty query means no search result - elif keyinfo.keyname in [u'return', u'escape']: - self._bell() - self.prompt = self.subsearch_oldprompt - self.process_keyevent_queue = self.process_keyevent_queue[:-1] - self._history.history_cursor = len(self._history.history) - if keyinfo.keyname == u'escape': - self.l_buffer.set_line(self.subsearch_old_line) - return True - elif keyinfo.keyname: - pass - elif keytuple in revtuples: - self.subsearch_fun = self._history.reverse_search_history - self.subsearch_prompt = u"reverse-i-search%d`%s': " - self.line = self.subsearch_fun(self.subsearch_query) - elif keytuple in fwdtuples: - self.subsearch_fun = self._history.forward_search_history - self.subsearch_prompt = u"forward-i-search%d`%s': " - self.line = self.subsearch_fun(self.subsearch_query) - elif keyinfo.control == False and keyinfo.meta == False: - self.subsearch_query += keyinfo.char - self.line = self.subsearch_fun(self.subsearch_query) - else: - pass - self.prompt = self.subsearch_prompt%(self._history.history_cursor, self.subsearch_query) - self.l_buffer.set_line(self.line) - - def _init_incremental_search(self, searchfun, init_event): - u"""Initialize search prompt - """ - log("init_incremental_search") - self.subsearch_query = u'' - self.subsearch_fun = searchfun - self.subsearch_old_line = self.l_buffer.get_line_text() - - queue = self.process_keyevent_queue - queue.append(self._process_incremental_search_keyevent) - - self.subsearch_oldprompt = self.prompt - - if (self.previous_func != self.reverse_search_history and - self.previous_func != self.forward_search_history): - self.subsearch_query = self.l_buffer[0:Point].get_line_text() - - if self.subsearch_fun == self.reverse_search_history: - self.subsearch_prompt = u"reverse-i-search%d`%s': " - else: - self.subsearch_prompt = u"forward-i-search%d`%s': " - - self.prompt = self.subsearch_prompt%(self._history.history_cursor, "") - - if self.subsearch_query: - self.line = self._process_incremental_search_keyevent(init_event) - else: - self.line = u"" - - -class SearchPromptMode(object): - def __init__(self, rlobj): - pass - - def _process_non_incremental_search_keyevent(self, keyinfo): - keytuple = keyinfo.tuple() - log(u"SearchPromptMode %s %s"%(keyinfo, keytuple)) - history = self._history - - if keyinfo.keyname == u'backspace': - self.non_inc_query = self.non_inc_query[:-1] - elif keyinfo.keyname in [u'return', u'escape']: - if self.non_inc_query: - if self.non_inc_direction == -1: - res = history.reverse_search_history(self.non_inc_query) - else: - res = history.forward_search_history(self.non_inc_query) - - self._bell() - self.prompt = self.non_inc_oldprompt - self.process_keyevent_queue = self.process_keyevent_queue[:-1] - self._history.history_cursor = len(self._history.history) - if keyinfo.keyname == u'escape': - self.l_buffer = self.non_inc_oldline - else: - self.l_buffer.set_line(res) - return False - elif keyinfo.keyname: - pass - elif keyinfo.control == False and keyinfo.meta == False: - self.non_inc_query += keyinfo.char - else: - pass - self.prompt = self.non_inc_oldprompt + u":" + self.non_inc_query - - def _init_non_i_search(self, direction): - self.non_inc_direction = direction - self.non_inc_query = u"" - self.non_inc_oldprompt = self.prompt - self.non_inc_oldline = self.l_buffer.copy() - self.l_buffer.reset_line() - self.prompt = self.non_inc_oldprompt + u":" - queue = self.process_keyevent_queue - queue.append(self._process_non_incremental_search_keyevent) - - def non_incremental_reverse_search_history(self, e): # (M-p) - u'''Search backward starting at the current line and moving up - through the history as necessary using a non-incremental search for - a string supplied by the user.''' - return self._init_non_i_search(-1) - - def non_incremental_forward_search_history(self, e): # (M-n) - u'''Search forward starting at the current line and moving down - through the the history as necessary using a non-incremental search - for a string supplied by the user.''' - return self._init_non_i_search(1) - - -class LeaveModeTryNext(Exception): - pass - - -class DigitArgumentMode(object): - def __init__(self, rlobj): - pass - - def _process_digit_argument_keyevent(self, keyinfo): - log(u"DigitArgumentMode.keyinfo %s"%keyinfo) - keytuple = keyinfo.tuple() - log(u"DigitArgumentMode.keytuple %s %s"%(keyinfo, keytuple)) - if keyinfo.keyname in ['return']: - self.prompt = self._digit_argument_oldprompt - self.process_keyevent_queue = self.process_keyevent_queue[:-1] - return True - elif keyinfo.keyname: - pass - elif (keyinfo.char in u"0123456789" and - keyinfo.control == False and - keyinfo.meta == False): - log(u"arg %s %s"%(self.argument, keyinfo.char)) - self.argument = self.argument * 10 + int(keyinfo.char) - else: - self.prompt = self._digit_argument_oldprompt - raise LeaveModeTryNext - self.prompt = u"(arg: %s) "%self.argument - - def _init_digit_argument(self, keyinfo): - """Initialize search prompt - """ - c = self.console - line = self.l_buffer.get_line_text() - self._digit_argument_oldprompt = self.prompt - queue = self.process_keyevent_queue - queue = self.process_keyevent_queue - queue.append(self._process_digit_argument_keyevent) - - if keyinfo.char == "-": - self.argument = -1 - elif keyinfo.char in u"0123456789": - self.argument = int(keyinfo.char) - log(u"<%s> %s"%(self.argument, type(self.argument))) - self.prompt = u"(arg: %s) "%self.argument - log(u"arg-init %s %s"%(self.argument, keyinfo.char)) - - -class EmacsMode(DigitArgumentMode, IncrementalSearchPromptMode, - SearchPromptMode, basemode.BaseMode): - mode = "emacs" - - def __init__(self, rlobj): - basemode.BaseMode.__init__(self, rlobj) - IncrementalSearchPromptMode.__init__(self, rlobj) - SearchPromptMode.__init__(self, rlobj) - DigitArgumentMode.__init__(self, rlobj) - self._keylog = (lambda x, y: None) - self.previous_func = None - self.prompt = u">>> " - self._insert_verbatim = False - self.next_meta = False # True to force meta on next character - - self.process_keyevent_queue = [self._process_keyevent] - - def __repr__(self): - return u"" - - def add_key_logger(self, logfun): - u"""logfun should be function that takes disp_fun and line_"""\ - u"""buffer object """ - self._keylog = logfun - - def process_keyevent(self, keyinfo): - try: - r = self.process_keyevent_queue[-1](keyinfo) - except LeaveModeTryNext: - self.process_keyevent_queue = self.process_keyevent_queue[:-1] - r = self.process_keyevent(keyinfo) - if r: - self.add_history(self.l_buffer.copy()) - return True - return False - - def _process_keyevent(self, keyinfo): - u"""return True when line is final - """ - #Process exit keys. Only exit on empty line - log(u"_process_keyevent <%s>"%keyinfo) - - def nop(e): - pass - if self.next_meta: - self.next_meta = False - keyinfo.meta = True - keytuple = keyinfo.tuple() - - if self._insert_verbatim: - self.insert_text(keyinfo) - self._insert_verbatim = False - self.argument = 0 - return False - - if keytuple in self.exit_dispatch: - pars = (self.l_buffer, lineobj.EndOfLine(self.l_buffer)) - log(u"exit_dispatch:<%s, %s>"%pars) - if lineobj.EndOfLine(self.l_buffer) == 0: - raise EOFError - if keyinfo.keyname or keyinfo.control or keyinfo.meta: - default = nop - else: - default = self.self_insert - dispatch_func = self.key_dispatch.get(keytuple, default) - - log(u"readline from keyboard:<%s,%s>"%(keytuple, dispatch_func)) - - r = None - if dispatch_func: - r = dispatch_func(keyinfo) - self._keylog(dispatch_func, self.l_buffer) - self.l_buffer.push_undo() - - self.previous_func = dispatch_func - return r - -######### History commands - def previous_history(self, e): # (C-p) - u'''Move back through the history list, fetching the previous - command. ''' - self._history.previous_history(self.l_buffer) - self.l_buffer.point = lineobj.EndOfLine - self.finalize() - - def next_history(self, e): # (C-n) - u'''Move forward through the history list, fetching the next - command. ''' - self._history.next_history(self.l_buffer) - self.finalize() - - def beginning_of_history(self, e): # (M-<) - u'''Move to the first line in the history.''' - self._history.beginning_of_history() - self.finalize() - - def end_of_history(self, e): # (M->) - u'''Move to the end of the input history, i.e., the line currently - being entered.''' - self._history.end_of_history(self.l_buffer) - self.finalize() - - def reverse_search_history(self, e): # (C-r) - u'''Search backward starting at the current line and moving up - through the history as necessary. This is an incremental search.''' - log("rev_search_history") - self._init_incremental_search(self._history.reverse_search_history, e) - self.finalize() - - def forward_search_history(self, e): # (C-s) - u'''Search forward starting at the current line and moving down - through the the history as necessary. This is an incremental - search.''' - log("fwd_search_history") - self._init_incremental_search(self._history.forward_search_history, e) - self.finalize() - - def history_search_forward(self, e): # () - u'''Search forward through the history for the string of characters - between the start of the current line and the point. This is a - non-incremental search. By default, this command is unbound.''' - if (self.previous_func and - hasattr(self._history, self.previous_func.__name__)): - self._history.lastcommand = getattr(self._history, - self.previous_func.__name__) - else: - self._history.lastcommand = None - q = self._history.history_search_forward(self.l_buffer) - self.l_buffer = q - self.l_buffer.point = q.point - self.finalize() - - def history_search_backward(self, e): # () - u'''Search backward through the history for the string of characters - between the start of the current line and the point. This is a - non-incremental search. By default, this command is unbound.''' - if (self.previous_func and - hasattr(self._history, self.previous_func.__name__)): - self._history.lastcommand = getattr(self._history, - self.previous_func.__name__) - else: - self._history.lastcommand = None - q = self._history.history_search_backward(self.l_buffer) - self.l_buffer = q - self.l_buffer.point = q.point - self.finalize() - - def yank_nth_arg(self, e): # (M-C-y) - u'''Insert the first argument to the previous command (usually the - second word on the previous line) at point. With an argument n, - insert the nth word from the previous command (the words in the - previous command begin with word 0). A negative argument inserts the - nth word from the end of the previous command.''' - self.finalize() - - def yank_last_arg(self, e): # (M-. or M-_) - u'''Insert last argument to the previous command (the last word of - the previous history entry). With an argument, behave exactly like - yank-nth-arg. Successive calls to yank-last-arg move back through - the history list, inserting the last argument of each line in turn.''' - self.finalize() - - def forward_backward_delete_char(self, e): # () - u'''Delete the character under the cursor, unless the cursor is at - the end of the line, in which case the character behind the cursor - is deleted. By default, this is not bound to a key.''' - self.finalize() - - def quoted_insert(self, e): # (C-q or C-v) - u'''Add the next character typed to the line verbatim. This is how to - insert key sequences like C-q, for example.''' - self._insert_verbatim = True - self.finalize() - - def tab_insert(self, e): # (M-TAB) - u'''Insert a tab character. ''' - cursor = min(self.l_buffer.point, len(self.l_buffer.line_buffer)) - ws = ' ' * (self.tabstop - (cursor % self.tabstop)) - self.insert_text(ws) - self.finalize() - - def transpose_chars(self, e): # (C-t) - u'''Drag the character before the cursor forward over the character - at the cursor, moving the cursor forward as well. If the insertion - point is at the end of the line, then this transposes the last two - characters of the line. Negative arguments have no effect.''' - self.l_buffer.transpose_chars() - self.finalize() - - def transpose_words(self, e): # (M-t) - u'''Drag the word before point past the word after point, moving - point past that word as well. If the insertion point is at the end - of the line, this transposes the last two words on the line.''' - self.l_buffer.transpose_words() - self.finalize() - - def overwrite_mode(self, e): # () - u'''Toggle overwrite mode. With an explicit positive numeric - argument, switches to overwrite mode. With an explicit non-positive - numeric argument, switches to insert mode. This command affects only - emacs mode; vi mode does overwrite differently. Each call to - readline() starts in insert mode. In overwrite mode, characters - bound to self-insert replace the text at point rather than pushing - the text to the right. Characters bound to backward-delete-char - replace the character before point with a space.''' - self.finalize() - - def kill_line(self, e): # (C-k) - u'''Kill the text from point to the end of the line. ''' - self.l_buffer.kill_line() - self.finalize() - - def backward_kill_line(self, e): # (C-x Rubout) - u'''Kill backward to the beginning of the line. ''' - self.l_buffer.backward_kill_line() - self.finalize() - - def unix_line_discard(self, e): # (C-u) - u'''Kill backward from the cursor to the beginning of the current - line. ''' - # how is this different from backward_kill_line? - self.l_buffer.unix_line_discard() - self.finalize() - - def kill_whole_line(self, e): # () - u'''Kill all characters on the current line, no matter where point - is. By default, this is unbound.''' - self.l_buffer.kill_whole_line() - self.finalize() - - def kill_word(self, e): # (M-d) - u'''Kill from point to the end of the current word, or if between - words, to the end of the next word. Word boundaries are the same as - forward-word.''' - self.l_buffer.kill_word() - self.finalize() - - forward_kill_word = kill_word - - def backward_kill_word(self, e): # (M-DEL) - u'''Kill the word behind point. Word boundaries are the same as - backward-word. ''' - self.l_buffer.backward_kill_word() - self.finalize() - - def unix_word_rubout(self, e): # (C-w) - u'''Kill the word behind point, using white space as a word - boundary. The killed text is saved on the kill-ring.''' - self.l_buffer.unix_word_rubout() - self.finalize() - - def kill_region(self, e): # () - u'''Kill the text in the current region. By default, this command is - unbound. ''' - self.finalize() - - def copy_region_as_kill(self, e): # () - u'''Copy the text in the region to the kill buffer, so it can be - yanked right away. By default, this command is unbound.''' - self.finalize() - - def copy_backward_word(self, e): # () - u'''Copy the word before point to the kill buffer. The word - boundaries are the same as backward-word. By default, this command - is unbound.''' - self.finalize() - - def copy_forward_word(self, e): # () - u'''Copy the word following point to the kill buffer. The word - boundaries are the same as forward-word. By default, this command is - unbound.''' - self.finalize() - - def yank(self, e): # (C-y) - u'''Yank the top of the kill ring into the buffer at point. ''' - self.l_buffer.yank() - self.finalize() - - def yank_pop(self, e): # (M-y) - u'''Rotate the kill-ring, and yank the new top. You can only do this - if the prior command is yank or yank-pop.''' - self.l_buffer.yank_pop() - self.finalize() - - def delete_char_or_list(self, e): # () - u'''Deletes the character under the cursor if not at the beginning or - end of the line (like delete-char). If at the end of the line, - behaves identically to possible-completions. This command is unbound - by default.''' - self.finalize() - - def start_kbd_macro(self, e): # (C-x () - u'''Begin saving the characters typed into the current keyboard - macro. ''' - self.finalize() - - def end_kbd_macro(self, e): # (C-x )) - u'''Stop saving the characters typed into the current keyboard macro - and save the definition.''' - self.finalize() - - def call_last_kbd_macro(self, e): # (C-x e) - u'''Re-execute the last keyboard macro defined, by making the - characters in the macro appear as if typed at the keyboard.''' - self.finalize() - - def re_read_init_file(self, e): # (C-x C-r) - u'''Read in the contents of the inputrc file, and incorporate any - bindings or variable assignments found there.''' - self.finalize() - - def abort(self, e): # (C-g) - u'''Abort the current editing command and ring the terminals bell - (subject to the setting of bell-style).''' - self._bell() - self.finalize() - - def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...) - u'''If the metafied character x is lowercase, run the command that is - bound to the corresponding uppercase character.''' - self.finalize() - - def prefix_meta(self, e): # (ESC) - u'''Metafy the next character typed. This is for keyboards without a - meta key. Typing ESC f is equivalent to typing M-f. ''' - self.next_meta = True - self.finalize() - - def undo(self, e): # (C-_ or C-x C-u) - u'''Incremental undo, separately remembered for each line.''' - self.l_buffer.pop_undo() - self.finalize() - - def revert_line(self, e): # (M-r) - u'''Undo all changes made to this line. This is like executing the - undo command enough times to get back to the beginning.''' - self.finalize() - - def tilde_expand(self, e): # (M-~) - u'''Perform tilde expansion on the current word.''' - self.finalize() - - def set_mark(self, e): # (C-@) - u'''Set the mark to the point. If a numeric argument is supplied, the - mark is set to that position.''' - self.l_buffer.set_mark() - self.finalize() - - def exchange_point_and_mark(self, e): # (C-x C-x) - u'''Swap the point with the mark. The current cursor position is set - to the saved position, and the old cursor position is saved as the - mark.''' - self.finalize() - - def character_search(self, e): # (C-]) - u'''A character is read and point is moved to the next occurrence of - that character. A negative count searches for previous occurrences.''' - self.finalize() - - def character_search_backward(self, e): # (M-C-]) - u'''A character is read and point is moved to the previous occurrence - of that character. A negative count searches for subsequent - occurrences.''' - self.finalize() - - def insert_comment(self, e): # (M-#) - u'''Without a numeric argument, the value of the comment-begin - variable is inserted at the beginning of the current line. If a - numeric argument is supplied, this command acts as a toggle: if the - characters at the beginning of the line do not match the value of - comment-begin, the value is inserted, otherwise the characters in - comment-begin are deleted from the beginning of the line. In either - case, the line is accepted as if a newline had been typed.''' - self.finalize() - - def dump_variables(self, e): # () - u'''Print all of the settable variables and their values to the - Readline output stream. If a numeric argument is supplied, the - output is formatted in such a way that it can be made part of an - inputrc file. This command is unbound by default.''' - self.finalize() - - def dump_macros(self, e): # () - u'''Print all of the Readline key sequences bound to macros and the - strings they output. If a numeric argument is supplied, the output - is formatted in such a way that it can be made part of an inputrc - file. This command is unbound by default.''' - self.finalize() - - def digit_argument(self, e): # (M-0, M-1, ... M--) - u'''Add this digit to the argument already accumulating, or start a - new argument. M-- starts a negative argument.''' - self._init_digit_argument(e) - #Should not finalize - - def universal_argument(self, e): # () - u'''This is another way to specify an argument. If this command is - followed by one or more digits, optionally with a leading minus - sign, those digits define the argument. If the command is followed - by digits, executing universal-argument again ends the numeric - argument, but is otherwise ignored. As a special case, if this - command is immediately followed by a character that is neither a - digit or minus sign, the argument count for the next command is - multiplied by four. The argument count is initially one, so - executing this function the first time makes the argument count - four, a second time makes the argument count sixteen, and so on. By - default, this is not bound to a key.''' - #Should not finalize - - #Create key bindings: - def init_editing_mode(self, e): # (C-e) - u'''When in vi command mode, this causes a switch to emacs editing - mode.''' - self._bind_exit_key(u'Control-d') - self._bind_exit_key(u'Control-z') - - # I often accidentally hold the shift or control while typing space - self._bind_key(u'space', self.self_insert) - self._bind_key(u'Shift-space', self.self_insert) - self._bind_key(u'Control-space', self.self_insert) - self._bind_key(u'Return', self.accept_line) - self._bind_key(u'Left', self.backward_char) - self._bind_key(u'Control-b', self.backward_char) - self._bind_key(u'Right', self.forward_char) - self._bind_key(u'Control-f', self.forward_char) - self._bind_key(u'Control-h', self.backward_delete_char) - self._bind_key(u'BackSpace', self.backward_delete_char) - self._bind_key(u'Control-BackSpace', self.backward_delete_word) - - self._bind_key(u'Home', self.beginning_of_line) - self._bind_key(u'End', self.end_of_line) - self._bind_key(u'Delete', self.delete_char) - self._bind_key(u'Control-d', self.delete_char) - self._bind_key(u'Clear', self.clear_screen) - self._bind_key(u'Alt-f', self.forward_word) - self._bind_key(u'Alt-b', self.backward_word) - self._bind_key(u'Control-l', self.clear_screen) - self._bind_key(u'Control-p', self.previous_history) - self._bind_key(u'Up', self.history_search_backward) - self._bind_key(u'Control-n', self.next_history) - self._bind_key(u'Down', self.history_search_forward) - self._bind_key(u'Control-a', self.beginning_of_line) - self._bind_key(u'Control-e', self.end_of_line) - self._bind_key(u'Alt-<', self.beginning_of_history) - self._bind_key(u'Alt->', self.end_of_history) - self._bind_key(u'Control-r', self.reverse_search_history) - self._bind_key(u'Control-s', self.forward_search_history) - self._bind_key(u'Control-Shift-r', self.forward_search_history) - self._bind_key(u'Alt-p', - self.non_incremental_reverse_search_history) - self._bind_key(u'Alt-n', - self.non_incremental_forward_search_history) - self._bind_key(u'Control-z', self.undo) - self._bind_key(u'Control-_', self.undo) - self._bind_key(u'Escape', self.kill_whole_line) - self._bind_key(u'Meta-d', self.kill_word) - self._bind_key(u'Control-Delete', self.forward_delete_word) - self._bind_key(u'Control-w', self.unix_word_rubout) - #self._bind_key('Control-Shift-v', self.quoted_insert) - self._bind_key(u'Control-v', self.paste) - self._bind_key(u'Alt-v', self.ipython_paste) - self._bind_key(u'Control-y', self.yank) - self._bind_key(u'Control-k', self.kill_line) - self._bind_key(u'Control-m', self.set_mark) - self._bind_key(u'Control-q', self.copy_region_to_clipboard) -# self._bind_key('Control-shift-k', self.kill_whole_line) - self._bind_key(u'Control-Shift-v', self.paste_mulitline_code) - self._bind_key(u"Control-Right", self.forward_word_end) - self._bind_key(u"Control-Left", self.backward_word) - self._bind_key(u"Shift-Right", - self.forward_char_extend_selection) - self._bind_key(u"Shift-Left", - self.backward_char_extend_selection) - self._bind_key(u"Shift-Control-Right", - self.forward_word_end_extend_selection) - self._bind_key(u"Shift-Control-Left", - self.backward_word_extend_selection) - self._bind_key(u"Shift-Home", - self.beginning_of_line_extend_selection) - self._bind_key(u"Shift-End", - self.end_of_line_extend_selection) - self._bind_key(u"numpad0", self.self_insert) - self._bind_key(u"numpad1", self.self_insert) - self._bind_key(u"numpad2", self.self_insert) - self._bind_key(u"numpad3", self.self_insert) - self._bind_key(u"numpad4", self.self_insert) - self._bind_key(u"numpad5", self.self_insert) - self._bind_key(u"numpad6", self.self_insert) - self._bind_key(u"numpad7", self.self_insert) - self._bind_key(u"numpad8", self.self_insert) - self._bind_key(u"numpad9", self.self_insert) - self._bind_key(u"add", self.self_insert) - self._bind_key(u"subtract", self.self_insert) - self._bind_key(u"multiply", self.self_insert) - self._bind_key(u"divide", self.self_insert) - self._bind_key(u"vk_decimal", self.self_insert) - log(u"RUNNING INIT EMACS") - for i in range(0, 10): - self._bind_key(u"alt-%d"%i, self.digit_argument) - self._bind_key(u"alt--", self.digit_argument) - - -# make it case insensitive -def commonprefix(m): - u"Given a list of pathnames, returns the longest common leading component" - if not m: - return '' - prefix = m[0] - for item in m: - for i in range(len(prefix)): - if prefix[:i + 1].lower() != item[:i + 1].lower(): - prefix = prefix[:i] - if i == 0: - return u'' - break - return prefix +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Gary Bishop. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import os, sys, time +import pyreadline.logger as logger +from pyreadline.logger import log +from pyreadline.lineeditor.lineobj import Point +import pyreadline.lineeditor.lineobj as lineobj +import pyreadline.lineeditor.history as history +import basemode +from pyreadline.unicode_helper import ensure_unicode + + +def format(keyinfo): + if len(keyinfo[-1]) != 1: + k = keyinfo + (-1,) + else: + k = keyinfo + (ord(keyinfo[-1]),) + return u"(%s,%s,%s,%s,%x)"%k + +in_ironpython = u"IronPython" in sys.version + + +class IncrementalSearchPromptMode(object): + def __init__(self, rlobj): + pass + + def _process_incremental_search_keyevent(self, keyinfo): + log("_process_incremental_search_keyevent") + keytuple = keyinfo.tuple() + #dispatch_func = self.key_dispatch.get(keytuple, default) + revtuples = [] + fwdtuples = [] + for ktuple, func in self.key_dispatch.iteritems(): + if func == self.reverse_search_history: + revtuples.append(ktuple) + elif func == self.forward_search_history: + fwdtuples.append(ktuple) + + + log(u"IncrementalSearchPromptMode %s %s"%(keyinfo, keytuple)) + if keyinfo.keyname == u'backspace': + self.subsearch_query = self.subsearch_query[:-1] + if len(self.subsearch_query) > 0: + self.line = self.subsearch_fun(self.subsearch_query) + else: + self._bell() + self.line = "" # empty query means no search result + elif keyinfo.keyname in [u'return', u'escape']: + self._bell() + self.prompt = self.subsearch_oldprompt + self.process_keyevent_queue = self.process_keyevent_queue[:-1] + self._history.history_cursor = len(self._history.history) + if keyinfo.keyname == u'escape': + self.l_buffer.set_line(self.subsearch_old_line) + return True + elif keyinfo.keyname: + pass + elif keytuple in revtuples: + self.subsearch_fun = self._history.reverse_search_history + self.subsearch_prompt = u"reverse-i-search%d`%s': " + self.line = self.subsearch_fun(self.subsearch_query) + elif keytuple in fwdtuples: + self.subsearch_fun = self._history.forward_search_history + self.subsearch_prompt = u"forward-i-search%d`%s': " + self.line = self.subsearch_fun(self.subsearch_query) + elif keyinfo.control == False and keyinfo.meta == False: + self.subsearch_query += keyinfo.char + self.line = self.subsearch_fun(self.subsearch_query) + else: + pass + self.prompt = self.subsearch_prompt%(self._history.history_cursor, self.subsearch_query) + self.l_buffer.set_line(self.line) + + def _init_incremental_search(self, searchfun, init_event): + u"""Initialize search prompt + """ + log("init_incremental_search") + self.subsearch_query = u'' + self.subsearch_fun = searchfun + self.subsearch_old_line = self.l_buffer.get_line_text() + + queue = self.process_keyevent_queue + queue.append(self._process_incremental_search_keyevent) + + self.subsearch_oldprompt = self.prompt + + if (self.previous_func != self.reverse_search_history and + self.previous_func != self.forward_search_history): + self.subsearch_query = self.l_buffer[0:Point].get_line_text() + + if self.subsearch_fun == self.reverse_search_history: + self.subsearch_prompt = u"reverse-i-search%d`%s': " + else: + self.subsearch_prompt = u"forward-i-search%d`%s': " + + self.prompt = self.subsearch_prompt%(self._history.history_cursor, "") + + if self.subsearch_query: + self.line = self._process_incremental_search_keyevent(init_event) + else: + self.line = u"" + + +class SearchPromptMode(object): + def __init__(self, rlobj): + pass + + def _process_non_incremental_search_keyevent(self, keyinfo): + keytuple = keyinfo.tuple() + log(u"SearchPromptMode %s %s"%(keyinfo, keytuple)) + history = self._history + + if keyinfo.keyname == u'backspace': + self.non_inc_query = self.non_inc_query[:-1] + elif keyinfo.keyname in [u'return', u'escape']: + if self.non_inc_query: + if self.non_inc_direction == -1: + res = history.reverse_search_history(self.non_inc_query) + else: + res = history.forward_search_history(self.non_inc_query) + + self._bell() + self.prompt = self.non_inc_oldprompt + self.process_keyevent_queue = self.process_keyevent_queue[:-1] + self._history.history_cursor = len(self._history.history) + if keyinfo.keyname == u'escape': + self.l_buffer = self.non_inc_oldline + else: + self.l_buffer.set_line(res) + return False + elif keyinfo.keyname: + pass + elif keyinfo.control == False and keyinfo.meta == False: + self.non_inc_query += keyinfo.char + else: + pass + self.prompt = self.non_inc_oldprompt + u":" + self.non_inc_query + + def _init_non_i_search(self, direction): + self.non_inc_direction = direction + self.non_inc_query = u"" + self.non_inc_oldprompt = self.prompt + self.non_inc_oldline = self.l_buffer.copy() + self.l_buffer.reset_line() + self.prompt = self.non_inc_oldprompt + u":" + queue = self.process_keyevent_queue + queue.append(self._process_non_incremental_search_keyevent) + + def non_incremental_reverse_search_history(self, e): # (M-p) + u'''Search backward starting at the current line and moving up + through the history as necessary using a non-incremental search for + a string supplied by the user.''' + return self._init_non_i_search(-1) + + def non_incremental_forward_search_history(self, e): # (M-n) + u'''Search forward starting at the current line and moving down + through the the history as necessary using a non-incremental search + for a string supplied by the user.''' + return self._init_non_i_search(1) + + +class LeaveModeTryNext(Exception): + pass + + +class DigitArgumentMode(object): + def __init__(self, rlobj): + pass + + def _process_digit_argument_keyevent(self, keyinfo): + log(u"DigitArgumentMode.keyinfo %s"%keyinfo) + keytuple = keyinfo.tuple() + log(u"DigitArgumentMode.keytuple %s %s"%(keyinfo, keytuple)) + if keyinfo.keyname in ['return']: + self.prompt = self._digit_argument_oldprompt + self.process_keyevent_queue = self.process_keyevent_queue[:-1] + return True + elif keyinfo.keyname: + pass + elif (keyinfo.char in u"0123456789" and + keyinfo.control == False and + keyinfo.meta == False): + log(u"arg %s %s"%(self.argument, keyinfo.char)) + self.argument = self.argument * 10 + int(keyinfo.char) + else: + self.prompt = self._digit_argument_oldprompt + raise LeaveModeTryNext + self.prompt = u"(arg: %s) "%self.argument + + def _init_digit_argument(self, keyinfo): + """Initialize search prompt + """ + c = self.console + line = self.l_buffer.get_line_text() + self._digit_argument_oldprompt = self.prompt + queue = self.process_keyevent_queue + queue = self.process_keyevent_queue + queue.append(self._process_digit_argument_keyevent) + + if keyinfo.char == "-": + self.argument = -1 + elif keyinfo.char in u"0123456789": + self.argument = int(keyinfo.char) + log(u"<%s> %s"%(self.argument, type(self.argument))) + self.prompt = u"(arg: %s) "%self.argument + log(u"arg-init %s %s"%(self.argument, keyinfo.char)) + + +class EmacsMode(DigitArgumentMode, IncrementalSearchPromptMode, + SearchPromptMode, basemode.BaseMode): + mode = "emacs" + + def __init__(self, rlobj): + basemode.BaseMode.__init__(self, rlobj) + IncrementalSearchPromptMode.__init__(self, rlobj) + SearchPromptMode.__init__(self, rlobj) + DigitArgumentMode.__init__(self, rlobj) + self._keylog = (lambda x, y: None) + self.previous_func = None + self.prompt = u">>> " + self._insert_verbatim = False + self.next_meta = False # True to force meta on next character + + self.process_keyevent_queue = [self._process_keyevent] + + def __repr__(self): + return u"" + + def add_key_logger(self, logfun): + u"""logfun should be function that takes disp_fun and line_"""\ + u"""buffer object """ + self._keylog = logfun + + def process_keyevent(self, keyinfo): + try: + r = self.process_keyevent_queue[-1](keyinfo) + except LeaveModeTryNext: + self.process_keyevent_queue = self.process_keyevent_queue[:-1] + r = self.process_keyevent(keyinfo) + if r: + self.add_history(self.l_buffer.copy()) + return True + return False + + def _process_keyevent(self, keyinfo): + u"""return True when line is final + """ + #Process exit keys. Only exit on empty line + log(u"_process_keyevent <%s>"%keyinfo) + + def nop(e): + pass + if self.next_meta: + self.next_meta = False + keyinfo.meta = True + keytuple = keyinfo.tuple() + + if self._insert_verbatim: + self.insert_text(keyinfo) + self._insert_verbatim = False + self.argument = 0 + return False + + if keytuple in self.exit_dispatch: + pars = (self.l_buffer, lineobj.EndOfLine(self.l_buffer)) + log(u"exit_dispatch:<%s, %s>"%pars) + if lineobj.EndOfLine(self.l_buffer) == 0: + raise EOFError + if keyinfo.keyname or keyinfo.control or keyinfo.meta: + default = nop + else: + default = self.self_insert + dispatch_func = self.key_dispatch.get(keytuple, default) + + log(u"readline from keyboard:<%s,%s>"%(keytuple, dispatch_func)) + + r = None + if dispatch_func: + r = dispatch_func(keyinfo) + self._keylog(dispatch_func, self.l_buffer) + self.l_buffer.push_undo() + + self.previous_func = dispatch_func + return r + +######### History commands + def previous_history(self, e): # (C-p) + u'''Move back through the history list, fetching the previous + command. ''' + self._history.previous_history(self.l_buffer) + self.l_buffer.point = lineobj.EndOfLine + self.finalize() + + def next_history(self, e): # (C-n) + u'''Move forward through the history list, fetching the next + command. ''' + self._history.next_history(self.l_buffer) + self.finalize() + + def beginning_of_history(self, e): # (M-<) + u'''Move to the first line in the history.''' + self._history.beginning_of_history() + self.finalize() + + def end_of_history(self, e): # (M->) + u'''Move to the end of the input history, i.e., the line currently + being entered.''' + self._history.end_of_history(self.l_buffer) + self.finalize() + + def reverse_search_history(self, e): # (C-r) + u'''Search backward starting at the current line and moving up + through the history as necessary. This is an incremental search.''' + log("rev_search_history") + self._init_incremental_search(self._history.reverse_search_history, e) + self.finalize() + + def forward_search_history(self, e): # (C-s) + u'''Search forward starting at the current line and moving down + through the the history as necessary. This is an incremental + search.''' + log("fwd_search_history") + self._init_incremental_search(self._history.forward_search_history, e) + self.finalize() + + def history_search_forward(self, e): # () + u'''Search forward through the history for the string of characters + between the start of the current line and the point. This is a + non-incremental search. By default, this command is unbound.''' + if (self.previous_func and + hasattr(self._history, self.previous_func.__name__)): + self._history.lastcommand = getattr(self._history, + self.previous_func.__name__) + else: + self._history.lastcommand = None + q = self._history.history_search_forward(self.l_buffer) + self.l_buffer = q + self.l_buffer.point = q.point + self.finalize() + + def history_search_backward(self, e): # () + u'''Search backward through the history for the string of characters + between the start of the current line and the point. This is a + non-incremental search. By default, this command is unbound.''' + if (self.previous_func and + hasattr(self._history, self.previous_func.__name__)): + self._history.lastcommand = getattr(self._history, + self.previous_func.__name__) + else: + self._history.lastcommand = None + q = self._history.history_search_backward(self.l_buffer) + self.l_buffer = q + self.l_buffer.point = q.point + self.finalize() + + def yank_nth_arg(self, e): # (M-C-y) + u'''Insert the first argument to the previous command (usually the + second word on the previous line) at point. With an argument n, + insert the nth word from the previous command (the words in the + previous command begin with word 0). A negative argument inserts the + nth word from the end of the previous command.''' + self.finalize() + + def yank_last_arg(self, e): # (M-. or M-_) + u'''Insert last argument to the previous command (the last word of + the previous history entry). With an argument, behave exactly like + yank-nth-arg. Successive calls to yank-last-arg move back through + the history list, inserting the last argument of each line in turn.''' + self.finalize() + + def forward_backward_delete_char(self, e): # () + u'''Delete the character under the cursor, unless the cursor is at + the end of the line, in which case the character behind the cursor + is deleted. By default, this is not bound to a key.''' + self.finalize() + + def quoted_insert(self, e): # (C-q or C-v) + u'''Add the next character typed to the line verbatim. This is how to + insert key sequences like C-q, for example.''' + self._insert_verbatim = True + self.finalize() + + def tab_insert(self, e): # (M-TAB) + u'''Insert a tab character. ''' + cursor = min(self.l_buffer.point, len(self.l_buffer.line_buffer)) + ws = ' ' * (self.tabstop - (cursor % self.tabstop)) + self.insert_text(ws) + self.finalize() + + def transpose_chars(self, e): # (C-t) + u'''Drag the character before the cursor forward over the character + at the cursor, moving the cursor forward as well. If the insertion + point is at the end of the line, then this transposes the last two + characters of the line. Negative arguments have no effect.''' + self.l_buffer.transpose_chars() + self.finalize() + + def transpose_words(self, e): # (M-t) + u'''Drag the word before point past the word after point, moving + point past that word as well. If the insertion point is at the end + of the line, this transposes the last two words on the line.''' + self.l_buffer.transpose_words() + self.finalize() + + def overwrite_mode(self, e): # () + u'''Toggle overwrite mode. With an explicit positive numeric + argument, switches to overwrite mode. With an explicit non-positive + numeric argument, switches to insert mode. This command affects only + emacs mode; vi mode does overwrite differently. Each call to + readline() starts in insert mode. In overwrite mode, characters + bound to self-insert replace the text at point rather than pushing + the text to the right. Characters bound to backward-delete-char + replace the character before point with a space.''' + self.finalize() + + def kill_line(self, e): # (C-k) + u'''Kill the text from point to the end of the line. ''' + self.l_buffer.kill_line() + self.finalize() + + def backward_kill_line(self, e): # (C-x Rubout) + u'''Kill backward to the beginning of the line. ''' + self.l_buffer.backward_kill_line() + self.finalize() + + def unix_line_discard(self, e): # (C-u) + u'''Kill backward from the cursor to the beginning of the current + line. ''' + # how is this different from backward_kill_line? + self.l_buffer.unix_line_discard() + self.finalize() + + def kill_whole_line(self, e): # () + u'''Kill all characters on the current line, no matter where point + is. By default, this is unbound.''' + self.l_buffer.kill_whole_line() + self.finalize() + + def kill_word(self, e): # (M-d) + u'''Kill from point to the end of the current word, or if between + words, to the end of the next word. Word boundaries are the same as + forward-word.''' + self.l_buffer.kill_word() + self.finalize() + + forward_kill_word = kill_word + + def backward_kill_word(self, e): # (M-DEL) + u'''Kill the word behind point. Word boundaries are the same as + backward-word. ''' + self.l_buffer.backward_kill_word() + self.finalize() + + def unix_word_rubout(self, e): # (C-w) + u'''Kill the word behind point, using white space as a word + boundary. The killed text is saved on the kill-ring.''' + self.l_buffer.unix_word_rubout() + self.finalize() + + def kill_region(self, e): # () + u'''Kill the text in the current region. By default, this command is + unbound. ''' + self.finalize() + + def copy_region_as_kill(self, e): # () + u'''Copy the text in the region to the kill buffer, so it can be + yanked right away. By default, this command is unbound.''' + self.finalize() + + def copy_backward_word(self, e): # () + u'''Copy the word before point to the kill buffer. The word + boundaries are the same as backward-word. By default, this command + is unbound.''' + self.finalize() + + def copy_forward_word(self, e): # () + u'''Copy the word following point to the kill buffer. The word + boundaries are the same as forward-word. By default, this command is + unbound.''' + self.finalize() + + def yank(self, e): # (C-y) + u'''Yank the top of the kill ring into the buffer at point. ''' + self.l_buffer.yank() + self.finalize() + + def yank_pop(self, e): # (M-y) + u'''Rotate the kill-ring, and yank the new top. You can only do this + if the prior command is yank or yank-pop.''' + self.l_buffer.yank_pop() + self.finalize() + + def delete_char_or_list(self, e): # () + u'''Deletes the character under the cursor if not at the beginning or + end of the line (like delete-char). If at the end of the line, + behaves identically to possible-completions. This command is unbound + by default.''' + self.finalize() + + def start_kbd_macro(self, e): # (C-x () + u'''Begin saving the characters typed into the current keyboard + macro. ''' + self.finalize() + + def end_kbd_macro(self, e): # (C-x )) + u'''Stop saving the characters typed into the current keyboard macro + and save the definition.''' + self.finalize() + + def call_last_kbd_macro(self, e): # (C-x e) + u'''Re-execute the last keyboard macro defined, by making the + characters in the macro appear as if typed at the keyboard.''' + self.finalize() + + def re_read_init_file(self, e): # (C-x C-r) + u'''Read in the contents of the inputrc file, and incorporate any + bindings or variable assignments found there.''' + self.finalize() + + def abort(self, e): # (C-g) + u'''Abort the current editing command and ring the terminals bell + (subject to the setting of bell-style).''' + self._bell() + self.finalize() + + def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...) + u'''If the metafied character x is lowercase, run the command that is + bound to the corresponding uppercase character.''' + self.finalize() + + def prefix_meta(self, e): # (ESC) + u'''Metafy the next character typed. This is for keyboards without a + meta key. Typing ESC f is equivalent to typing M-f. ''' + self.next_meta = True + self.finalize() + + def undo(self, e): # (C-_ or C-x C-u) + u'''Incremental undo, separately remembered for each line.''' + self.l_buffer.pop_undo() + self.finalize() + + def revert_line(self, e): # (M-r) + u'''Undo all changes made to this line. This is like executing the + undo command enough times to get back to the beginning.''' + self.finalize() + + def tilde_expand(self, e): # (M-~) + u'''Perform tilde expansion on the current word.''' + self.finalize() + + def set_mark(self, e): # (C-@) + u'''Set the mark to the point. If a numeric argument is supplied, the + mark is set to that position.''' + self.l_buffer.set_mark() + self.finalize() + + def exchange_point_and_mark(self, e): # (C-x C-x) + u'''Swap the point with the mark. The current cursor position is set + to the saved position, and the old cursor position is saved as the + mark.''' + self.finalize() + + def character_search(self, e): # (C-]) + u'''A character is read and point is moved to the next occurrence of + that character. A negative count searches for previous occurrences.''' + self.finalize() + + def character_search_backward(self, e): # (M-C-]) + u'''A character is read and point is moved to the previous occurrence + of that character. A negative count searches for subsequent + occurrences.''' + self.finalize() + + def insert_comment(self, e): # (M-#) + u'''Without a numeric argument, the value of the comment-begin + variable is inserted at the beginning of the current line. If a + numeric argument is supplied, this command acts as a toggle: if the + characters at the beginning of the line do not match the value of + comment-begin, the value is inserted, otherwise the characters in + comment-begin are deleted from the beginning of the line. In either + case, the line is accepted as if a newline had been typed.''' + self.finalize() + + def dump_variables(self, e): # () + u'''Print all of the settable variables and their values to the + Readline output stream. If a numeric argument is supplied, the + output is formatted in such a way that it can be made part of an + inputrc file. This command is unbound by default.''' + self.finalize() + + def dump_macros(self, e): # () + u'''Print all of the Readline key sequences bound to macros and the + strings they output. If a numeric argument is supplied, the output + is formatted in such a way that it can be made part of an inputrc + file. This command is unbound by default.''' + self.finalize() + + def digit_argument(self, e): # (M-0, M-1, ... M--) + u'''Add this digit to the argument already accumulating, or start a + new argument. M-- starts a negative argument.''' + self._init_digit_argument(e) + #Should not finalize + + def universal_argument(self, e): # () + u'''This is another way to specify an argument. If this command is + followed by one or more digits, optionally with a leading minus + sign, those digits define the argument. If the command is followed + by digits, executing universal-argument again ends the numeric + argument, but is otherwise ignored. As a special case, if this + command is immediately followed by a character that is neither a + digit or minus sign, the argument count for the next command is + multiplied by four. The argument count is initially one, so + executing this function the first time makes the argument count + four, a second time makes the argument count sixteen, and so on. By + default, this is not bound to a key.''' + #Should not finalize + + #Create key bindings: + def init_editing_mode(self, e): # (C-e) + u'''When in vi command mode, this causes a switch to emacs editing + mode.''' + self._bind_exit_key(u'Control-d') + self._bind_exit_key(u'Control-z') + + # I often accidentally hold the shift or control while typing space + self._bind_key(u'space', self.self_insert) + self._bind_key(u'Shift-space', self.self_insert) + self._bind_key(u'Control-space', self.self_insert) + self._bind_key(u'Return', self.accept_line) + self._bind_key(u'Left', self.backward_char) + self._bind_key(u'Control-b', self.backward_char) + self._bind_key(u'Right', self.forward_char) + self._bind_key(u'Control-f', self.forward_char) + self._bind_key(u'Control-h', self.backward_delete_char) + self._bind_key(u'BackSpace', self.backward_delete_char) + self._bind_key(u'Control-BackSpace', self.backward_delete_word) + + self._bind_key(u'Home', self.beginning_of_line) + self._bind_key(u'End', self.end_of_line) + self._bind_key(u'Delete', self.delete_char) + self._bind_key(u'Control-d', self.delete_char) + self._bind_key(u'Clear', self.clear_screen) + self._bind_key(u'Alt-f', self.forward_word) + self._bind_key(u'Alt-b', self.backward_word) + self._bind_key(u'Control-l', self.clear_screen) + self._bind_key(u'Control-p', self.previous_history) + self._bind_key(u'Up', self.history_search_backward) + self._bind_key(u'Control-n', self.next_history) + self._bind_key(u'Down', self.history_search_forward) + self._bind_key(u'Control-a', self.beginning_of_line) + self._bind_key(u'Control-e', self.end_of_line) + self._bind_key(u'Alt-<', self.beginning_of_history) + self._bind_key(u'Alt->', self.end_of_history) + self._bind_key(u'Control-r', self.reverse_search_history) + self._bind_key(u'Control-s', self.forward_search_history) + self._bind_key(u'Control-Shift-r', self.forward_search_history) + self._bind_key(u'Alt-p', + self.non_incremental_reverse_search_history) + self._bind_key(u'Alt-n', + self.non_incremental_forward_search_history) + self._bind_key(u'Control-z', self.undo) + self._bind_key(u'Control-_', self.undo) + self._bind_key(u'Escape', self.kill_whole_line) + self._bind_key(u'Meta-d', self.kill_word) + self._bind_key(u'Control-Delete', self.forward_delete_word) + self._bind_key(u'Control-w', self.unix_word_rubout) + #self._bind_key('Control-Shift-v', self.quoted_insert) + self._bind_key(u'Control-v', self.paste) + self._bind_key(u'Alt-v', self.ipython_paste) + self._bind_key(u'Control-y', self.yank) + self._bind_key(u'Control-k', self.kill_line) + self._bind_key(u'Control-m', self.set_mark) + self._bind_key(u'Control-q', self.copy_region_to_clipboard) +# self._bind_key('Control-shift-k', self.kill_whole_line) + self._bind_key(u'Control-Shift-v', self.paste_mulitline_code) + self._bind_key(u"Control-Right", self.forward_word_end) + self._bind_key(u"Control-Left", self.backward_word) + self._bind_key(u"Shift-Right", + self.forward_char_extend_selection) + self._bind_key(u"Shift-Left", + self.backward_char_extend_selection) + self._bind_key(u"Shift-Control-Right", + self.forward_word_end_extend_selection) + self._bind_key(u"Shift-Control-Left", + self.backward_word_extend_selection) + self._bind_key(u"Shift-Home", + self.beginning_of_line_extend_selection) + self._bind_key(u"Shift-End", + self.end_of_line_extend_selection) + self._bind_key(u"numpad0", self.self_insert) + self._bind_key(u"numpad1", self.self_insert) + self._bind_key(u"numpad2", self.self_insert) + self._bind_key(u"numpad3", self.self_insert) + self._bind_key(u"numpad4", self.self_insert) + self._bind_key(u"numpad5", self.self_insert) + self._bind_key(u"numpad6", self.self_insert) + self._bind_key(u"numpad7", self.self_insert) + self._bind_key(u"numpad8", self.self_insert) + self._bind_key(u"numpad9", self.self_insert) + self._bind_key(u"add", self.self_insert) + self._bind_key(u"subtract", self.self_insert) + self._bind_key(u"multiply", self.self_insert) + self._bind_key(u"divide", self.self_insert) + self._bind_key(u"vk_decimal", self.self_insert) + log(u"RUNNING INIT EMACS") + for i in range(0, 10): + self._bind_key(u"alt-%d"%i, self.digit_argument) + self._bind_key(u"alt--", self.digit_argument) + + +# make it case insensitive +def commonprefix(m): + u"Given a list of pathnames, returns the longest common leading component" + if not m: + return '' + prefix = m[0] + for item in m: + for i in range(len(prefix)): + if prefix[:i + 1].lower() != item[:i + 1].lower(): + prefix = prefix[:i] + if i == 0: + return u'' + break + return prefix diff --git a/pyreadline/modes/notemacs.py b/pyreadline/modes/notemacs.py index 0e08e2a..0c65c0d 100644 --- a/pyreadline/modes/notemacs.py +++ b/pyreadline/modes/notemacs.py @@ -1,602 +1,602 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2003-2006 Gary Bishop. -# Copyright (C) 2006 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import os -import pyreadline.logger as logger -from pyreadline.logger import log -import pyreadline.lineeditor.lineobj as lineobj -import pyreadline.lineeditor.history as history -import basemode - -class NotEmacsMode(basemode.BaseMode): - mode=u"notemacs" - def __init__(self,rlobj): - super(NotEmacsMode,self).__init__(rlobj) - - def __repr__(self): - return u"" - - def _readline_from_keyboard(self): - c=self.console - while 1: - self._update_line() - event = c.getkeypress() - if self.next_meta: - self.next_meta = False - control, meta, shift, code = event.keyinfo - event.keyinfo = (control, True, shift, code) - - #Process exit keys. Only exit on empty line - if event.keyinfo in self.exit_dispatch: - if lineobj.EndOfLine(self.l_buffer) == 0: - raise EOFError - - dispatch_func = self.key_dispatch.get(event.keyinfo,self.self_insert) - log(u"readline from keyboard:%s"%(event.keyinfo,)) - r = None - if dispatch_func: - r = dispatch_func(event) - self.l_buffer.push_undo() - - self.previous_func = dispatch_func - if r: - self._update_line() - break - - def readline(self, prompt=''): - u'''Try to act like GNU readline.''' - # handle startup_hook - if self.first_prompt: - self.first_prompt = False - if self.startup_hook: - try: - self.startup_hook() - except: - print u'startup hook failed' - traceback.print_exc() - - c = self.console - self.l_buffer.reset_line() - self.prompt = prompt - self._print_prompt() - - if self.pre_input_hook: - try: - self.pre_input_hook() - except: - print u'pre_input_hook failed' - traceback.print_exc() - self.pre_input_hook = None - - log(u"in readline: %s"%self.paste_line_buffer) - if len(self.paste_line_buffer)>0: - self.l_buffer=lineobj.ReadlineTextBuffer(self.paste_line_buffer[0]) - self._update_line() - self.paste_line_buffer=self.paste_line_buffer[1:] - c.write(u'\r\n') - else: - self._readline_from_keyboard() - c.write(u'\r\n') - - self.add_history(self.l_buffer.copy()) - - log(u'returning(%s)' % self.l_buffer.get_line_text()) - return self.l_buffer.get_line_text() + '\n' - - ### Methods below here are bindable emacs functions - - def beginning_of_line(self, e): # (C-a) - u'''Move to the start of the current line. ''' - self.l_buffer.beginning_of_line() - - def end_of_line(self, e): # (C-e) - u'''Move to the end of the line. ''' - self.l_buffer.end_of_line() - - def forward_char(self, e): # (C-f) - u'''Move forward a character. ''' - self.l_buffer.forward_char() - - def backward_char(self, e): # (C-b) - u'''Move back a character. ''' - self.l_buffer.backward_char() - - def forward_word(self, e): # (M-f) - u'''Move forward to the end of the next word. Words are composed of - letters and digits.''' - self.l_buffer.forward_word() - - def backward_word(self, e): # (M-b) - u'''Move back to the start of the current or previous word. Words are - composed of letters and digits.''' - self.l_buffer.backward_word() - - def clear_screen(self, e): # (C-l) - u'''Clear the screen and redraw the current line, leaving the current - line at the top of the screen.''' - self.console.page() - - def redraw_current_line(self, e): # () - u'''Refresh the current line. By default, this is unbound.''' - pass - - def accept_line(self, e): # (Newline or Return) - u'''Accept the line regardless of where the cursor is. If this line - is non-empty, it may be added to the history list for future recall - with add_history(). If this line is a modified history line, the - history line is restored to its original state.''' - return True - -######### History commands - def previous_history(self, e): # (C-p) - u'''Move back through the history list, fetching the previous command. ''' - self._history.previous_history(self.l_buffer) - - def next_history(self, e): # (C-n) - u'''Move forward through the history list, fetching the next command. ''' - self._history.next_history(self.l_buffer) - - def beginning_of_history(self, e): # (M-<) - u'''Move to the first line in the history.''' - self._history.beginning_of_history() - - def end_of_history(self, e): # (M->) - u'''Move to the end of the input history, i.e., the line currently - being entered.''' - self._history.end_of_history(self.l_buffer) - - def _i_search(self, searchfun, direction, init_event): - c = self.console - line = self.get_line_buffer() - query = u'' - hc_start = self._history.history_cursor #+ direction - while 1: - x, y = self.prompt_end_pos - c.pos(0, y) - if direction < 0: - prompt = u'reverse-i-search' - else: - prompt = u'forward-i-search' - - scroll = c.write_scrolling(u"%s`%s': %s" % (prompt, query, line)) - self._update_prompt_pos(scroll) - self._clear_after() - - event = c.getkeypress() - if event.keysym == u'BackSpace': - if len(query) > 0: - query = query[:-1] - self._history.history_cursor = hc_start - else: - self._bell() - elif event.char in string.letters + string.digits + string.punctuation + u' ': - self._history.history_cursor = hc_start - query += event.char - elif event.keyinfo == init_event.keyinfo: - self._history.history_cursor += direction - line=searchfun(query) - pass - else: - if event.keysym != u'Return': - self._bell() - break - line=searchfun(query) - - px, py = self.prompt_begin_pos - c.pos(0, py) - self.l_buffer.set_line(line) - self._print_prompt() - self._history.history_cursor=len(self._history.history) - - def reverse_search_history(self, e): # (C-r) - u'''Search backward starting at the current line and moving up - through the history as necessary. This is an incremental search.''' -# print "HEJ" -# self.console.bell() - self._i_search(self._history.reverse_search_history, -1, e) - - def forward_search_history(self, e): # (C-s) - u'''Search forward starting at the current line and moving down - through the the history as necessary. This is an incremental search.''' -# print "HEJ" -# self.console.bell() - self._i_search(self._history.forward_search_history, 1, e) - - - def non_incremental_reverse_search_history(self, e): # (M-p) - u'''Search backward starting at the current line and moving up - through the history as necessary using a non-incremental search for - a string supplied by the user.''' - self._history.non_incremental_reverse_search_history(self.l_buffer) - - def non_incremental_forward_search_history(self, e): # (M-n) - u'''Search forward starting at the current line and moving down - through the the history as necessary using a non-incremental search - for a string supplied by the user.''' - self._history.non_incremental_reverse_search_history(self.l_buffer) - - def history_search_forward(self, e): # () - u'''Search forward through the history for the string of characters - between the start of the current line and the point. This is a - non-incremental search. By default, this command is unbound.''' - self.l_buffer=self._history.history_search_forward(self.l_buffer) - - def history_search_backward(self, e): # () - u'''Search backward through the history for the string of characters - between the start of the current line and the point. This is a - non-incremental search. By default, this command is unbound.''' - self.l_buffer=self._history.history_search_backward(self.l_buffer) - - def yank_nth_arg(self, e): # (M-C-y) - u'''Insert the first argument to the previous command (usually the - second word on the previous line) at point. With an argument n, - insert the nth word from the previous command (the words in the - previous command begin with word 0). A negative argument inserts the - nth word from the end of the previous command.''' - pass - - def yank_last_arg(self, e): # (M-. or M-_) - u'''Insert last argument to the previous command (the last word of - the previous history entry). With an argument, behave exactly like - yank-nth-arg. Successive calls to yank-last-arg move back through - the history list, inserting the last argument of each line in turn.''' - pass - - def delete_char(self, e): # (C-d) - u'''Delete the character at point. If point is at the beginning of - the line, there are no characters in the line, and the last - character typed was not bound to delete-char, then return EOF.''' - self.l_buffer.delete_char() - - def backward_delete_char(self, e): # (Rubout) - u'''Delete the character behind the cursor. A numeric argument means - to kill the characters instead of deleting them.''' - self.l_buffer.backward_delete_char() - - def forward_backward_delete_char(self, e): # () - u'''Delete the character under the cursor, unless the cursor is at - the end of the line, in which case the character behind the cursor - is deleted. By default, this is not bound to a key.''' - pass - - def quoted_insert(self, e): # (C-q or C-v) - u'''Add the next character typed to the line verbatim. This is how to - insert key sequences like C-q, for example.''' - e = self.console.getkeypress() - self.insert_text(e.char) - - def tab_insert(self, e): # (M-TAB) - u'''Insert a tab character. ''' - cursor = min(self.l_buffer.point, len(self.l_buffer.line_buffer)) - ws = ' ' * (self.tabstop - (cursor % self.tabstop)) - self.insert_text(ws) - - def self_insert(self, e): # (a, b, A, 1, !, ...) - u'''Insert yourself. ''' - if ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys. - self.insert_text(e.char) - - def transpose_chars(self, e): # (C-t) - u'''Drag the character before the cursor forward over the character - at the cursor, moving the cursor forward as well. If the insertion - point is at the end of the line, then this transposes the last two - characters of the line. Negative arguments have no effect.''' - self.l_buffer.transpose_chars() - - def transpose_words(self, e): # (M-t) - u'''Drag the word before point past the word after point, moving - point past that word as well. If the insertion point is at the end - of the line, this transposes the last two words on the line.''' - self.l_buffer.transpose_words() - - def upcase_word(self, e): # (M-u) - u'''Uppercase the current (or following) word. With a negative - argument, uppercase the previous word, but do not move the cursor.''' - self.l_buffer.upcase_word() - - def downcase_word(self, e): # (M-l) - u'''Lowercase the current (or following) word. With a negative - argument, lowercase the previous word, but do not move the cursor.''' - self.l_buffer.downcase_word() - - def capitalize_word(self, e): # (M-c) - u'''Capitalize the current (or following) word. With a negative - argument, capitalize the previous word, but do not move the cursor.''' - self.l_buffer.capitalize_word() - - def overwrite_mode(self, e): # () - u'''Toggle overwrite mode. With an explicit positive numeric - argument, switches to overwrite mode. With an explicit non-positive - numeric argument, switches to insert mode. This command affects only - emacs mode; vi mode does overwrite differently. Each call to - readline() starts in insert mode. In overwrite mode, characters - bound to self-insert replace the text at point rather than pushing - the text to the right. Characters bound to backward-delete-char - replace the character before point with a space.''' - pass - - def kill_line(self, e): # (C-k) - u'''Kill the text from point to the end of the line. ''' - self.l_buffer.kill_line() - - def backward_kill_line(self, e): # (C-x Rubout) - u'''Kill backward to the beginning of the line. ''' - self.l_buffer.backward_kill_line() - - def unix_line_discard(self, e): # (C-u) - u'''Kill backward from the cursor to the beginning of the current line. ''' - # how is this different from backward_kill_line? - self.l_buffer.unix_line_discard() - - def kill_whole_line(self, e): # () - u'''Kill all characters on the current line, no matter where point - is. By default, this is unbound.''' - self.l_buffer.kill_whole_line() - - def kill_word(self, e): # (M-d) - u'''Kill from point to the end of the current word, or if between - words, to the end of the next word. Word boundaries are the same as - forward-word.''' - self.l_buffer.kill_word() - - def backward_kill_word(self, e): # (M-DEL) - u'''Kill the word behind point. Word boundaries are the same as - backward-word. ''' - self.l_buffer.backward_kill_word() - - def unix_word_rubout(self, e): # (C-w) - u'''Kill the word behind point, using white space as a word - boundary. The killed text is saved on the kill-ring.''' - self.l_buffer.unix_word_rubout() - - def delete_horizontal_space(self, e): # () - u'''Delete all spaces and tabs around point. By default, this is unbound. ''' - pass - - def kill_region(self, e): # () - u'''Kill the text in the current region. By default, this command is unbound. ''' - pass - - def copy_region_as_kill(self, e): # () - u'''Copy the text in the region to the kill buffer, so it can be - yanked right away. By default, this command is unbound.''' - pass - - def copy_region_to_clipboard(self, e): # () - u'''Copy the text in the region to the windows clipboard.''' - if self.enable_win32_clipboard: - mark=min(self.l_buffer.mark,len(self.l_buffer.line_buffer)) - cursor=min(self.l_buffer.point,len(self.l_buffer.line_buffer)) - if self.l_buffer.mark==-1: - return - begin=min(cursor,mark) - end=max(cursor,mark) - toclipboard=u"".join(self.l_buffer.line_buffer[begin:end]) - clipboard.SetClipboardText(str(toclipboard)) - - def copy_backward_word(self, e): # () - u'''Copy the word before point to the kill buffer. The word - boundaries are the same as backward-word. By default, this command - is unbound.''' - pass - - def copy_forward_word(self, e): # () - u'''Copy the word following point to the kill buffer. The word - boundaries are the same as forward-word. By default, this command is - unbound.''' - pass - - def paste(self,e): - u'''Paste windows clipboard''' - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert(False) - self.insert_text(txt) - - def paste_mulitline_code(self,e): - u'''Paste windows clipboard''' - reg=re.compile(u"\r?\n") - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert(False) - t=reg.split(txt) - t=[row for row in t if row.strip()!=u""] #remove empty lines - if t!=[u""]: - self.insert_text(t[0]) - self.add_history(self.l_buffer.copy()) - self.paste_line_buffer=t[1:] - log(u"multi: %s"%self.paste_line_buffer) - return True - else: - return False - - def ipython_paste(self,e): - u'''Paste windows clipboard. If enable_ipython_paste_list_of_lists is - True then try to convert tabseparated data to repr of list of lists or - repr of array''' - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert( - self.enable_ipython_paste_list_of_lists) - if self.enable_ipython_paste_for_paths: - if len(txt)<300 and (u"\t" not in txt) and (u"\n" not in txt): - txt=txt.replace(u"\\", u"/").replace(u" ", ur"\ ") - self.insert_text(txt) - - def yank(self, e): # (C-y) - u'''Yank the top of the kill ring into the buffer at point. ''' - pass - - def yank_pop(self, e): # (M-y) - u'''Rotate the kill-ring, and yank the new top. You can only do this - if the prior command is yank or yank-pop.''' - pass - - - def digit_argument(self, e): # (M-0, M-1, ... M--) - u'''Add this digit to the argument already accumulating, or start a - new argument. M-- starts a negative argument.''' - pass - - def universal_argument(self, e): # () - u'''This is another way to specify an argument. If this command is - followed by one or more digits, optionally with a leading minus - sign, those digits define the argument. If the command is followed - by digits, executing universal-argument again ends the numeric - argument, but is otherwise ignored. As a special case, if this - command is immediately followed by a character that is neither a - digit or minus sign, the argument count for the next command is - multiplied by four. The argument count is initially one, so - executing this function the first time makes the argument count - four, a second time makes the argument count sixteen, and so on. By - default, this is not bound to a key.''' - pass - - def delete_char_or_list(self, e): # () - u'''Deletes the character under the cursor if not at the beginning or - end of the line (like delete-char). If at the end of the line, - behaves identically to possible-completions. This command is unbound - by default.''' - pass - - def start_kbd_macro(self, e): # (C-x () - u'''Begin saving the characters typed into the current keyboard macro. ''' - pass - - def end_kbd_macro(self, e): # (C-x )) - u'''Stop saving the characters typed into the current keyboard macro - and save the definition.''' - pass - - def call_last_kbd_macro(self, e): # (C-x e) - u'''Re-execute the last keyboard macro defined, by making the - characters in the macro appear as if typed at the keyboard.''' - pass - - def re_read_init_file(self, e): # (C-x C-r) - u'''Read in the contents of the inputrc file, and incorporate any - bindings or variable assignments found there.''' - pass - - def abort(self, e): # (C-g) - u'''Abort the current editing command and ring the terminals bell - (subject to the setting of bell-style).''' - self._bell() - - def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...) - u'''If the metafied character x is lowercase, run the command that is - bound to the corresponding uppercase character.''' - pass - - def prefix_meta(self, e): # (ESC) - u'''Metafy the next character typed. This is for keyboards without a - meta key. Typing ESC f is equivalent to typing M-f. ''' - self.next_meta = True - - def undo(self, e): # (C-_ or C-x C-u) - u'''Incremental undo, separately remembered for each line.''' - self.l_buffer.pop_undo() - - def revert_line(self, e): # (M-r) - u'''Undo all changes made to this line. This is like executing the - undo command enough times to get back to the beginning.''' - pass - - def tilde_expand(self, e): # (M-~) - u'''Perform tilde expansion on the current word.''' - pass - - def set_mark(self, e): # (C-@) - u'''Set the mark to the point. If a numeric argument is supplied, the - mark is set to that position.''' - self.l_buffer.set_mark() - - def exchange_point_and_mark(self, e): # (C-x C-x) - u'''Swap the point with the mark. The current cursor position is set - to the saved position, and the old cursor position is saved as the - mark.''' - pass - - def character_search(self, e): # (C-]) - u'''A character is read and point is moved to the next occurrence of - that character. A negative count searches for previous occurrences.''' - pass - - def character_search_backward(self, e): # (M-C-]) - u'''A character is read and point is moved to the previous occurrence - of that character. A negative count searches for subsequent - occurrences.''' - pass - - def insert_comment(self, e): # (M-#) - u'''Without a numeric argument, the value of the comment-begin - variable is inserted at the beginning of the current line. If a - numeric argument is supplied, this command acts as a toggle: if the - characters at the beginning of the line do not match the value of - comment-begin, the value is inserted, otherwise the characters in - comment-begin are deleted from the beginning of the line. In either - case, the line is accepted as if a newline had been typed.''' - pass - - def dump_functions(self, e): # () - u'''Print all of the functions and their key bindings to the Readline - output stream. If a numeric argument is supplied, the output is - formatted in such a way that it can be made part of an inputrc - file. This command is unbound by default.''' - pass - - def dump_variables(self, e): # () - u'''Print all of the settable variables and their values to the - Readline output stream. If a numeric argument is supplied, the - output is formatted in such a way that it can be made part of an - inputrc file. This command is unbound by default.''' - pass - - def dump_macros(self, e): # () - u'''Print all of the Readline key sequences bound to macros and the - strings they output. If a numeric argument is supplied, the output - is formatted in such a way that it can be made part of an inputrc - file. This command is unbound by default.''' - pass - - - #Create key bindings: - - def init_editing_mode(self, e): # (C-e) - u'''When in vi command mode, this causes a switch to emacs editing - mode.''' - - self._bind_exit_key(u'Control-d') - self._bind_exit_key(u'Control-z') - - # I often accidentally hold the shift or control while typing space - self._bind_key(u'Shift-space', self.self_insert) - self._bind_key(u'Control-space', self.self_insert) - self._bind_key(u'Return', self.accept_line) - self._bind_key(u'Left', self.backward_char) - self._bind_key(u'Control-b', self.backward_char) - self._bind_key(u'Right', self.forward_char) - self._bind_key(u'Control-f', self.forward_char) - self._bind_key(u'BackSpace', self.backward_delete_char) - self._bind_key(u'Home', self.beginning_of_line) - self._bind_key(u'End', self.end_of_line) - self._bind_key(u'Delete', self.delete_char) - self._bind_key(u'Control-d', self.delete_char) - self._bind_key(u'Clear', self.clear_screen) - - -# make it case insensitive -def commonprefix(m): - u"Given a list of pathnames, returns the longest common leading component" - if not m: return u'' - prefix = m[0] - for item in m: - for i in range(len(prefix)): - if prefix[:i+1].lower() != item[:i+1].lower(): - prefix = prefix[:i] - if i == 0: return u'' - break - return prefix - +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2003-2006 Gary Bishop. +# Copyright (C) 2006 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import os +import pyreadline.logger as logger +from pyreadline.logger import log +import pyreadline.lineeditor.lineobj as lineobj +import pyreadline.lineeditor.history as history +import basemode + +class NotEmacsMode(basemode.BaseMode): + mode=u"notemacs" + def __init__(self,rlobj): + super(NotEmacsMode,self).__init__(rlobj) + + def __repr__(self): + return u"" + + def _readline_from_keyboard(self): + c=self.console + while 1: + self._update_line() + event = c.getkeypress() + if self.next_meta: + self.next_meta = False + control, meta, shift, code = event.keyinfo + event.keyinfo = (control, True, shift, code) + + #Process exit keys. Only exit on empty line + if event.keyinfo in self.exit_dispatch: + if lineobj.EndOfLine(self.l_buffer) == 0: + raise EOFError + + dispatch_func = self.key_dispatch.get(event.keyinfo,self.self_insert) + log(u"readline from keyboard:%s"%(event.keyinfo,)) + r = None + if dispatch_func: + r = dispatch_func(event) + self.l_buffer.push_undo() + + self.previous_func = dispatch_func + if r: + self._update_line() + break + + def readline(self, prompt=''): + u'''Try to act like GNU readline.''' + # handle startup_hook + if self.first_prompt: + self.first_prompt = False + if self.startup_hook: + try: + self.startup_hook() + except: + print u'startup hook failed' + traceback.print_exc() + + c = self.console + self.l_buffer.reset_line() + self.prompt = prompt + self._print_prompt() + + if self.pre_input_hook: + try: + self.pre_input_hook() + except: + print u'pre_input_hook failed' + traceback.print_exc() + self.pre_input_hook = None + + log(u"in readline: %s"%self.paste_line_buffer) + if len(self.paste_line_buffer)>0: + self.l_buffer=lineobj.ReadlineTextBuffer(self.paste_line_buffer[0]) + self._update_line() + self.paste_line_buffer=self.paste_line_buffer[1:] + c.write(u'\r\n') + else: + self._readline_from_keyboard() + c.write(u'\r\n') + + self.add_history(self.l_buffer.copy()) + + log(u'returning(%s)' % self.l_buffer.get_line_text()) + return self.l_buffer.get_line_text() + '\n' + + ### Methods below here are bindable emacs functions + + def beginning_of_line(self, e): # (C-a) + u'''Move to the start of the current line. ''' + self.l_buffer.beginning_of_line() + + def end_of_line(self, e): # (C-e) + u'''Move to the end of the line. ''' + self.l_buffer.end_of_line() + + def forward_char(self, e): # (C-f) + u'''Move forward a character. ''' + self.l_buffer.forward_char() + + def backward_char(self, e): # (C-b) + u'''Move back a character. ''' + self.l_buffer.backward_char() + + def forward_word(self, e): # (M-f) + u'''Move forward to the end of the next word. Words are composed of + letters and digits.''' + self.l_buffer.forward_word() + + def backward_word(self, e): # (M-b) + u'''Move back to the start of the current or previous word. Words are + composed of letters and digits.''' + self.l_buffer.backward_word() + + def clear_screen(self, e): # (C-l) + u'''Clear the screen and redraw the current line, leaving the current + line at the top of the screen.''' + self.console.page() + + def redraw_current_line(self, e): # () + u'''Refresh the current line. By default, this is unbound.''' + pass + + def accept_line(self, e): # (Newline or Return) + u'''Accept the line regardless of where the cursor is. If this line + is non-empty, it may be added to the history list for future recall + with add_history(). If this line is a modified history line, the + history line is restored to its original state.''' + return True + +######### History commands + def previous_history(self, e): # (C-p) + u'''Move back through the history list, fetching the previous command. ''' + self._history.previous_history(self.l_buffer) + + def next_history(self, e): # (C-n) + u'''Move forward through the history list, fetching the next command. ''' + self._history.next_history(self.l_buffer) + + def beginning_of_history(self, e): # (M-<) + u'''Move to the first line in the history.''' + self._history.beginning_of_history() + + def end_of_history(self, e): # (M->) + u'''Move to the end of the input history, i.e., the line currently + being entered.''' + self._history.end_of_history(self.l_buffer) + + def _i_search(self, searchfun, direction, init_event): + c = self.console + line = self.get_line_buffer() + query = u'' + hc_start = self._history.history_cursor #+ direction + while 1: + x, y = self.prompt_end_pos + c.pos(0, y) + if direction < 0: + prompt = u'reverse-i-search' + else: + prompt = u'forward-i-search' + + scroll = c.write_scrolling(u"%s`%s': %s" % (prompt, query, line)) + self._update_prompt_pos(scroll) + self._clear_after() + + event = c.getkeypress() + if event.keysym == u'BackSpace': + if len(query) > 0: + query = query[:-1] + self._history.history_cursor = hc_start + else: + self._bell() + elif event.char in string.letters + string.digits + string.punctuation + u' ': + self._history.history_cursor = hc_start + query += event.char + elif event.keyinfo == init_event.keyinfo: + self._history.history_cursor += direction + line=searchfun(query) + pass + else: + if event.keysym != u'Return': + self._bell() + break + line=searchfun(query) + + px, py = self.prompt_begin_pos + c.pos(0, py) + self.l_buffer.set_line(line) + self._print_prompt() + self._history.history_cursor=len(self._history.history) + + def reverse_search_history(self, e): # (C-r) + u'''Search backward starting at the current line and moving up + through the history as necessary. This is an incremental search.''' +# print "HEJ" +# self.console.bell() + self._i_search(self._history.reverse_search_history, -1, e) + + def forward_search_history(self, e): # (C-s) + u'''Search forward starting at the current line and moving down + through the the history as necessary. This is an incremental search.''' +# print "HEJ" +# self.console.bell() + self._i_search(self._history.forward_search_history, 1, e) + + + def non_incremental_reverse_search_history(self, e): # (M-p) + u'''Search backward starting at the current line and moving up + through the history as necessary using a non-incremental search for + a string supplied by the user.''' + self._history.non_incremental_reverse_search_history(self.l_buffer) + + def non_incremental_forward_search_history(self, e): # (M-n) + u'''Search forward starting at the current line and moving down + through the the history as necessary using a non-incremental search + for a string supplied by the user.''' + self._history.non_incremental_reverse_search_history(self.l_buffer) + + def history_search_forward(self, e): # () + u'''Search forward through the history for the string of characters + between the start of the current line and the point. This is a + non-incremental search. By default, this command is unbound.''' + self.l_buffer=self._history.history_search_forward(self.l_buffer) + + def history_search_backward(self, e): # () + u'''Search backward through the history for the string of characters + between the start of the current line and the point. This is a + non-incremental search. By default, this command is unbound.''' + self.l_buffer=self._history.history_search_backward(self.l_buffer) + + def yank_nth_arg(self, e): # (M-C-y) + u'''Insert the first argument to the previous command (usually the + second word on the previous line) at point. With an argument n, + insert the nth word from the previous command (the words in the + previous command begin with word 0). A negative argument inserts the + nth word from the end of the previous command.''' + pass + + def yank_last_arg(self, e): # (M-. or M-_) + u'''Insert last argument to the previous command (the last word of + the previous history entry). With an argument, behave exactly like + yank-nth-arg. Successive calls to yank-last-arg move back through + the history list, inserting the last argument of each line in turn.''' + pass + + def delete_char(self, e): # (C-d) + u'''Delete the character at point. If point is at the beginning of + the line, there are no characters in the line, and the last + character typed was not bound to delete-char, then return EOF.''' + self.l_buffer.delete_char() + + def backward_delete_char(self, e): # (Rubout) + u'''Delete the character behind the cursor. A numeric argument means + to kill the characters instead of deleting them.''' + self.l_buffer.backward_delete_char() + + def forward_backward_delete_char(self, e): # () + u'''Delete the character under the cursor, unless the cursor is at + the end of the line, in which case the character behind the cursor + is deleted. By default, this is not bound to a key.''' + pass + + def quoted_insert(self, e): # (C-q or C-v) + u'''Add the next character typed to the line verbatim. This is how to + insert key sequences like C-q, for example.''' + e = self.console.getkeypress() + self.insert_text(e.char) + + def tab_insert(self, e): # (M-TAB) + u'''Insert a tab character. ''' + cursor = min(self.l_buffer.point, len(self.l_buffer.line_buffer)) + ws = ' ' * (self.tabstop - (cursor % self.tabstop)) + self.insert_text(ws) + + def self_insert(self, e): # (a, b, A, 1, !, ...) + u'''Insert yourself. ''' + if ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys. + self.insert_text(e.char) + + def transpose_chars(self, e): # (C-t) + u'''Drag the character before the cursor forward over the character + at the cursor, moving the cursor forward as well. If the insertion + point is at the end of the line, then this transposes the last two + characters of the line. Negative arguments have no effect.''' + self.l_buffer.transpose_chars() + + def transpose_words(self, e): # (M-t) + u'''Drag the word before point past the word after point, moving + point past that word as well. If the insertion point is at the end + of the line, this transposes the last two words on the line.''' + self.l_buffer.transpose_words() + + def upcase_word(self, e): # (M-u) + u'''Uppercase the current (or following) word. With a negative + argument, uppercase the previous word, but do not move the cursor.''' + self.l_buffer.upcase_word() + + def downcase_word(self, e): # (M-l) + u'''Lowercase the current (or following) word. With a negative + argument, lowercase the previous word, but do not move the cursor.''' + self.l_buffer.downcase_word() + + def capitalize_word(self, e): # (M-c) + u'''Capitalize the current (or following) word. With a negative + argument, capitalize the previous word, but do not move the cursor.''' + self.l_buffer.capitalize_word() + + def overwrite_mode(self, e): # () + u'''Toggle overwrite mode. With an explicit positive numeric + argument, switches to overwrite mode. With an explicit non-positive + numeric argument, switches to insert mode. This command affects only + emacs mode; vi mode does overwrite differently. Each call to + readline() starts in insert mode. In overwrite mode, characters + bound to self-insert replace the text at point rather than pushing + the text to the right. Characters bound to backward-delete-char + replace the character before point with a space.''' + pass + + def kill_line(self, e): # (C-k) + u'''Kill the text from point to the end of the line. ''' + self.l_buffer.kill_line() + + def backward_kill_line(self, e): # (C-x Rubout) + u'''Kill backward to the beginning of the line. ''' + self.l_buffer.backward_kill_line() + + def unix_line_discard(self, e): # (C-u) + u'''Kill backward from the cursor to the beginning of the current line. ''' + # how is this different from backward_kill_line? + self.l_buffer.unix_line_discard() + + def kill_whole_line(self, e): # () + u'''Kill all characters on the current line, no matter where point + is. By default, this is unbound.''' + self.l_buffer.kill_whole_line() + + def kill_word(self, e): # (M-d) + u'''Kill from point to the end of the current word, or if between + words, to the end of the next word. Word boundaries are the same as + forward-word.''' + self.l_buffer.kill_word() + + def backward_kill_word(self, e): # (M-DEL) + u'''Kill the word behind point. Word boundaries are the same as + backward-word. ''' + self.l_buffer.backward_kill_word() + + def unix_word_rubout(self, e): # (C-w) + u'''Kill the word behind point, using white space as a word + boundary. The killed text is saved on the kill-ring.''' + self.l_buffer.unix_word_rubout() + + def delete_horizontal_space(self, e): # () + u'''Delete all spaces and tabs around point. By default, this is unbound. ''' + pass + + def kill_region(self, e): # () + u'''Kill the text in the current region. By default, this command is unbound. ''' + pass + + def copy_region_as_kill(self, e): # () + u'''Copy the text in the region to the kill buffer, so it can be + yanked right away. By default, this command is unbound.''' + pass + + def copy_region_to_clipboard(self, e): # () + u'''Copy the text in the region to the windows clipboard.''' + if self.enable_win32_clipboard: + mark=min(self.l_buffer.mark,len(self.l_buffer.line_buffer)) + cursor=min(self.l_buffer.point,len(self.l_buffer.line_buffer)) + if self.l_buffer.mark==-1: + return + begin=min(cursor,mark) + end=max(cursor,mark) + toclipboard=u"".join(self.l_buffer.line_buffer[begin:end]) + clipboard.SetClipboardText(str(toclipboard)) + + def copy_backward_word(self, e): # () + u'''Copy the word before point to the kill buffer. The word + boundaries are the same as backward-word. By default, this command + is unbound.''' + pass + + def copy_forward_word(self, e): # () + u'''Copy the word following point to the kill buffer. The word + boundaries are the same as forward-word. By default, this command is + unbound.''' + pass + + def paste(self,e): + u'''Paste windows clipboard''' + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + self.insert_text(txt) + + def paste_mulitline_code(self,e): + u'''Paste windows clipboard''' + reg=re.compile(u"\r?\n") + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + t=reg.split(txt) + t=[row for row in t if row.strip()!=u""] #remove empty lines + if t!=[u""]: + self.insert_text(t[0]) + self.add_history(self.l_buffer.copy()) + self.paste_line_buffer=t[1:] + log(u"multi: %s"%self.paste_line_buffer) + return True + else: + return False + + def ipython_paste(self,e): + u'''Paste windows clipboard. If enable_ipython_paste_list_of_lists is + True then try to convert tabseparated data to repr of list of lists or + repr of array''' + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert( + self.enable_ipython_paste_list_of_lists) + if self.enable_ipython_paste_for_paths: + if len(txt)<300 and (u"\t" not in txt) and (u"\n" not in txt): + txt=txt.replace(u"\\", u"/").replace(u" ", ur"\ ") + self.insert_text(txt) + + def yank(self, e): # (C-y) + u'''Yank the top of the kill ring into the buffer at point. ''' + pass + + def yank_pop(self, e): # (M-y) + u'''Rotate the kill-ring, and yank the new top. You can only do this + if the prior command is yank or yank-pop.''' + pass + + + def digit_argument(self, e): # (M-0, M-1, ... M--) + u'''Add this digit to the argument already accumulating, or start a + new argument. M-- starts a negative argument.''' + pass + + def universal_argument(self, e): # () + u'''This is another way to specify an argument. If this command is + followed by one or more digits, optionally with a leading minus + sign, those digits define the argument. If the command is followed + by digits, executing universal-argument again ends the numeric + argument, but is otherwise ignored. As a special case, if this + command is immediately followed by a character that is neither a + digit or minus sign, the argument count for the next command is + multiplied by four. The argument count is initially one, so + executing this function the first time makes the argument count + four, a second time makes the argument count sixteen, and so on. By + default, this is not bound to a key.''' + pass + + def delete_char_or_list(self, e): # () + u'''Deletes the character under the cursor if not at the beginning or + end of the line (like delete-char). If at the end of the line, + behaves identically to possible-completions. This command is unbound + by default.''' + pass + + def start_kbd_macro(self, e): # (C-x () + u'''Begin saving the characters typed into the current keyboard macro. ''' + pass + + def end_kbd_macro(self, e): # (C-x )) + u'''Stop saving the characters typed into the current keyboard macro + and save the definition.''' + pass + + def call_last_kbd_macro(self, e): # (C-x e) + u'''Re-execute the last keyboard macro defined, by making the + characters in the macro appear as if typed at the keyboard.''' + pass + + def re_read_init_file(self, e): # (C-x C-r) + u'''Read in the contents of the inputrc file, and incorporate any + bindings or variable assignments found there.''' + pass + + def abort(self, e): # (C-g) + u'''Abort the current editing command and ring the terminals bell + (subject to the setting of bell-style).''' + self._bell() + + def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...) + u'''If the metafied character x is lowercase, run the command that is + bound to the corresponding uppercase character.''' + pass + + def prefix_meta(self, e): # (ESC) + u'''Metafy the next character typed. This is for keyboards without a + meta key. Typing ESC f is equivalent to typing M-f. ''' + self.next_meta = True + + def undo(self, e): # (C-_ or C-x C-u) + u'''Incremental undo, separately remembered for each line.''' + self.l_buffer.pop_undo() + + def revert_line(self, e): # (M-r) + u'''Undo all changes made to this line. This is like executing the + undo command enough times to get back to the beginning.''' + pass + + def tilde_expand(self, e): # (M-~) + u'''Perform tilde expansion on the current word.''' + pass + + def set_mark(self, e): # (C-@) + u'''Set the mark to the point. If a numeric argument is supplied, the + mark is set to that position.''' + self.l_buffer.set_mark() + + def exchange_point_and_mark(self, e): # (C-x C-x) + u'''Swap the point with the mark. The current cursor position is set + to the saved position, and the old cursor position is saved as the + mark.''' + pass + + def character_search(self, e): # (C-]) + u'''A character is read and point is moved to the next occurrence of + that character. A negative count searches for previous occurrences.''' + pass + + def character_search_backward(self, e): # (M-C-]) + u'''A character is read and point is moved to the previous occurrence + of that character. A negative count searches for subsequent + occurrences.''' + pass + + def insert_comment(self, e): # (M-#) + u'''Without a numeric argument, the value of the comment-begin + variable is inserted at the beginning of the current line. If a + numeric argument is supplied, this command acts as a toggle: if the + characters at the beginning of the line do not match the value of + comment-begin, the value is inserted, otherwise the characters in + comment-begin are deleted from the beginning of the line. In either + case, the line is accepted as if a newline had been typed.''' + pass + + def dump_functions(self, e): # () + u'''Print all of the functions and their key bindings to the Readline + output stream. If a numeric argument is supplied, the output is + formatted in such a way that it can be made part of an inputrc + file. This command is unbound by default.''' + pass + + def dump_variables(self, e): # () + u'''Print all of the settable variables and their values to the + Readline output stream. If a numeric argument is supplied, the + output is formatted in such a way that it can be made part of an + inputrc file. This command is unbound by default.''' + pass + + def dump_macros(self, e): # () + u'''Print all of the Readline key sequences bound to macros and the + strings they output. If a numeric argument is supplied, the output + is formatted in such a way that it can be made part of an inputrc + file. This command is unbound by default.''' + pass + + + #Create key bindings: + + def init_editing_mode(self, e): # (C-e) + u'''When in vi command mode, this causes a switch to emacs editing + mode.''' + + self._bind_exit_key(u'Control-d') + self._bind_exit_key(u'Control-z') + + # I often accidentally hold the shift or control while typing space + self._bind_key(u'Shift-space', self.self_insert) + self._bind_key(u'Control-space', self.self_insert) + self._bind_key(u'Return', self.accept_line) + self._bind_key(u'Left', self.backward_char) + self._bind_key(u'Control-b', self.backward_char) + self._bind_key(u'Right', self.forward_char) + self._bind_key(u'Control-f', self.forward_char) + self._bind_key(u'BackSpace', self.backward_delete_char) + self._bind_key(u'Home', self.beginning_of_line) + self._bind_key(u'End', self.end_of_line) + self._bind_key(u'Delete', self.delete_char) + self._bind_key(u'Control-d', self.delete_char) + self._bind_key(u'Clear', self.clear_screen) + + +# make it case insensitive +def commonprefix(m): + u"Given a list of pathnames, returns the longest common leading component" + if not m: return u'' + prefix = m[0] + for item in m: + for i in range(len(prefix)): + if prefix[:i+1].lower() != item[:i+1].lower(): + prefix = prefix[:i] + if i == 0: return u'' + break + return prefix + diff --git a/pyreadline/release.py b/pyreadline/release.py index 4998c23..6743d76 100644 --- a/pyreadline/release.py +++ b/pyreadline/release.py @@ -1,88 +1,88 @@ -# -*- coding: utf-8 -*- -u"""Release data for the pyreadline project. - -$Id$""" - -#***************************************************************************** -# Copyright (C) 2006 Jorgen Stenarson. -# -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -# Name of the package for release purposes. This is the name which labels -# the tarballs and RPMs made by distutils, so it's best to lowercase it. -name = u'pyreadline' - -# For versions with substrings (like 0.6.16.svn), use an extra . to separate -# the new substring. We have to avoid using either dashes or underscores, -# because bdist_rpm does not accept dashes (an RPM) convention, and -# bdist_deb does not accept underscores (a Debian convention). - -branch = u'' - -version = u'1.7.1' - -revision = u'$Revision$' - -description = u"A python implmementation of GNU readline." - -long_description = \ -u""" -The pyreadline package is a python implementation of GNU readline functionality -it is based on the ctypes based UNC readline package by Gary Bishop. -It is not complete. It has been tested for use with windows 2000 and windows xp. - -Version 1.7.1 includes fixes for 64-bit windows, thanks to Christoph Gohlke for -helping out. - -Version 1.7 will be the last release with compatibility with 2.4 and 2.5. The next -major release will target 2.6, 2.7 and 3.x. The 1.7 series will only receive bugfixes -from now on. - -Features: - * keyboard text selection and copy/paste - * Shift-arrowkeys for text selection - * Control-c can be used for copy activate with allow_ctrl_c(True) in config file - * Double tapping ctrl-c will raise a KeyboardInterrupt, use ctrl_c_tap_time_interval(x) - where x is your preferred tap time window, default 0.3 s. - * paste pastes first line of content on clipboard. - * ipython_paste, pastes tab-separated data as list of lists or numpy array if all data is numeric - * paste_mulitline_code pastes multi line code, removing any empty lines. - - - The latest development version is always available at the IPython subversion - repository_. - -.. _repository: - """ - -license = u'BSD' - -authors = {u'Jorgen' : (u'Jorgen Stenarson',u'jorgen.stenarson@bostream.nu'), - u'Gary': (u'Gary Bishop', ''), - u'Jack': (u'Jack Trainor', ''), - } - -url = u'http://ipython.scipy.org/moin/PyReadline/Intro' - -download_url = u'https://launchpad.net/pyreadline/+download' - -platforms = [u'Windows XP/2000/NT', - u'Windows 95/98/ME'] - -keywords = [u'readline', - u'pyreadline'] - -classifiers = [u'Development Status :: 5 - Production/Stable', - u'Environment :: Console', - u'Operating System :: Microsoft :: Windows', - u'License :: OSI Approved :: BSD License', - u'Programming Language :: Python :: 2.4', - u'Programming Language :: Python :: 2.5', - u'Programming Language :: Python :: 2.6', - u'Programming Language :: Python :: 2.7', - ] - - +# -*- coding: utf-8 -*- +u"""Release data for the pyreadline project. + +$Id$""" + +#***************************************************************************** +# Copyright (C) 2006 Jorgen Stenarson. +# +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +# Name of the package for release purposes. This is the name which labels +# the tarballs and RPMs made by distutils, so it's best to lowercase it. +name = u'pyreadline' + +# For versions with substrings (like 0.6.16.svn), use an extra . to separate +# the new substring. We have to avoid using either dashes or underscores, +# because bdist_rpm does not accept dashes (an RPM) convention, and +# bdist_deb does not accept underscores (a Debian convention). + +branch = u'' + +version = u'1.7.1' + +revision = u'$Revision$' + +description = u"A python implmementation of GNU readline." + +long_description = \ +u""" +The pyreadline package is a python implementation of GNU readline functionality +it is based on the ctypes based UNC readline package by Gary Bishop. +It is not complete. It has been tested for use with windows 2000 and windows xp. + +Version 1.7.1 includes fixes for 64-bit windows, thanks to Christoph Gohlke for +helping out. + +Version 1.7 will be the last release with compatibility with 2.4 and 2.5. The next +major release will target 2.6, 2.7 and 3.x. The 1.7 series will only receive bugfixes +from now on. + +Features: + * keyboard text selection and copy/paste + * Shift-arrowkeys for text selection + * Control-c can be used for copy activate with allow_ctrl_c(True) in config file + * Double tapping ctrl-c will raise a KeyboardInterrupt, use ctrl_c_tap_time_interval(x) + where x is your preferred tap time window, default 0.3 s. + * paste pastes first line of content on clipboard. + * ipython_paste, pastes tab-separated data as list of lists or numpy array if all data is numeric + * paste_mulitline_code pastes multi line code, removing any empty lines. + + + The latest development version is always available at the IPython subversion + repository_. + +.. _repository: + """ + +license = u'BSD' + +authors = {u'Jorgen' : (u'Jorgen Stenarson',u'jorgen.stenarson@bostream.nu'), + u'Gary': (u'Gary Bishop', ''), + u'Jack': (u'Jack Trainor', ''), + } + +url = u'http://ipython.scipy.org/moin/PyReadline/Intro' + +download_url = u'https://launchpad.net/pyreadline/+download' + +platforms = [u'Windows XP/2000/NT', + u'Windows 95/98/ME'] + +keywords = [u'readline', + u'pyreadline'] + +classifiers = [u'Development Status :: 5 - Production/Stable', + u'Environment :: Console', + u'Operating System :: Microsoft :: Windows', + u'License :: OSI Approved :: BSD License', + u'Programming Language :: Python :: 2.4', + u'Programming Language :: Python :: 2.5', + u'Programming Language :: Python :: 2.6', + u'Programming Language :: Python :: 2.7', + ] + + diff --git a/pyreadline/unicode_helper.py b/pyreadline/unicode_helper.py index bf171ab..3ce0e35 100644 --- a/pyreadline/unicode_helper.py +++ b/pyreadline/unicode_helper.py @@ -1,36 +1,36 @@ -# -*- coding: utf-8 -*- -#***************************************************************************** -# Copyright (C) 2007 Jorgen Stenarson. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** -import sys - -try: - pyreadline_codepage = sys.stdout.encoding -except AttributeError: - # This error occurs when pdb imports readline and doctest has replaced - # stdout with stdout collector. We will assume ascii codepage - pyreadline_codepage = u"ascii" - -if pyreadline_codepage is None: - pyreadline_codepage = u"ascii" - -def ensure_unicode(text): - u"""helper to ensure that text passed to WriteConsoleW is unicode""" - if isinstance(text, str): - try: - return text.decode(pyreadline_codepage, u"replace") - except (LookupError, TypeError): - return text.decode(u"ascii", u"replace") - return text - -def ensure_str(text): - u"""Convert unicode to str using pyreadline_codepage""" - if isinstance(text, unicode): - try: - return text.encode(pyreadline_codepage, u"replace") - except (LookupError, TypeError): - return text.encode(u"ascii", u"replace") - return text +# -*- coding: utf-8 -*- +#***************************************************************************** +# Copyright (C) 2007 Jorgen Stenarson. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** +import sys + +try: + pyreadline_codepage = sys.stdout.encoding +except AttributeError: + # This error occurs when pdb imports readline and doctest has replaced + # stdout with stdout collector. We will assume ascii codepage + pyreadline_codepage = u"ascii" + +if pyreadline_codepage is None: + pyreadline_codepage = u"ascii" + +def ensure_unicode(text): + u"""helper to ensure that text passed to WriteConsoleW is unicode""" + if isinstance(text, str): + try: + return text.decode(pyreadline_codepage, u"replace") + except (LookupError, TypeError): + return text.decode(u"ascii", u"replace") + return text + +def ensure_str(text): + u"""Convert unicode to str using pyreadline_codepage""" + if isinstance(text, unicode): + try: + return text.encode(pyreadline_codepage, u"replace") + except (LookupError, TypeError): + return text.encode(u"ascii", u"replace") + return text diff --git a/readline_windows.py b/readline_windows.py index 99db186..9c45d72 100644 --- a/readline_windows.py +++ b/readline_windows.py @@ -1,79 +1,79 @@ -# -*- coding: UTF-8 -*- -#this file is needed in site-packages to emulate readline -#necessary for rlcompleter since it relies on the existance -#of a readline module -from pyreadline.rlmain import Readline - -__all__ = [ 'parse_and_bind', - 'get_line_buffer', - 'insert_text', - 'clear_history', - 'read_init_file', - 'read_history_file', - 'write_history_file', - 'get_current_history_length', - 'get_history_length', - 'get_history_item', - 'set_history_length', - 'set_startup_hook', - 'set_pre_input_hook', - 'set_completer', - 'get_completer', - 'get_begidx', - 'get_endidx', - 'set_completer_delims', - 'get_completer_delims', - 'add_history', - 'callback_handler_install', - 'callback_handler_remove', - 'callback_read_char',] #Some other objects are added below - - -# create a Readline object to contain the state -rl = Readline() - -if rl.disable_readline: - def dummy(completer=""): - pass - for funk in __all__: - globals()[funk] = dummy -else: - def GetOutputFile(): - u'''Return the console object used by readline so that it can be used for printing in color.''' - return rl.console - __all__.append("GetOutputFile") - - import pyreadline.console as console - - # make these available so this looks like the python readline module - read_init_file = rl.read_init_file - parse_and_bind = rl.parse_and_bind - clear_history = rl.clear_history - add_history = rl.add_history - insert_text = rl.insert_text - - write_history_file = rl.write_history_file - read_history_file = rl.read_history_file - - get_completer_delims = rl.get_completer_delims - get_current_history_length = rl.get_current_history_length - get_history_length = rl.get_history_length - get_history_item = rl.get_history_item - get_line_buffer = rl.get_line_buffer - set_completer = rl.set_completer - get_completer = rl.get_completer - get_begidx = rl.get_begidx - get_endidx = rl.get_endidx - - set_completer_delims = rl.set_completer_delims - set_history_length = rl.set_history_length - set_pre_input_hook = rl.set_pre_input_hook - set_startup_hook = rl.set_startup_hook - - callback_handler_install=rl.callback_handler_install - callback_handler_remove=rl.callback_handler_remove - callback_read_char=rl.callback_read_char - - console.install_readline(rl.readline) - +# -*- coding: UTF-8 -*- +#this file is needed in site-packages to emulate readline +#necessary for rlcompleter since it relies on the existance +#of a readline module +from pyreadline.rlmain import Readline + +__all__ = [ 'parse_and_bind', + 'get_line_buffer', + 'insert_text', + 'clear_history', + 'read_init_file', + 'read_history_file', + 'write_history_file', + 'get_current_history_length', + 'get_history_length', + 'get_history_item', + 'set_history_length', + 'set_startup_hook', + 'set_pre_input_hook', + 'set_completer', + 'get_completer', + 'get_begidx', + 'get_endidx', + 'set_completer_delims', + 'get_completer_delims', + 'add_history', + 'callback_handler_install', + 'callback_handler_remove', + 'callback_read_char',] #Some other objects are added below + + +# create a Readline object to contain the state +rl = Readline() + +if rl.disable_readline: + def dummy(completer=""): + pass + for funk in __all__: + globals()[funk] = dummy +else: + def GetOutputFile(): + u'''Return the console object used by readline so that it can be used for printing in color.''' + return rl.console + __all__.append("GetOutputFile") + + import pyreadline.console as console + + # make these available so this looks like the python readline module + read_init_file = rl.read_init_file + parse_and_bind = rl.parse_and_bind + clear_history = rl.clear_history + add_history = rl.add_history + insert_text = rl.insert_text + + write_history_file = rl.write_history_file + read_history_file = rl.read_history_file + + get_completer_delims = rl.get_completer_delims + get_current_history_length = rl.get_current_history_length + get_history_length = rl.get_history_length + get_history_item = rl.get_history_item + get_line_buffer = rl.get_line_buffer + set_completer = rl.set_completer + get_completer = rl.get_completer + get_begidx = rl.get_begidx + get_endidx = rl.get_endidx + + set_completer_delims = rl.set_completer_delims + set_history_length = rl.set_history_length + set_pre_input_hook = rl.set_pre_input_hook + set_startup_hook = rl.set_startup_hook + + callback_handler_install=rl.callback_handler_install + callback_handler_remove=rl.callback_handler_remove + callback_read_char=rl.callback_read_char + + console.install_readline(rl.readline) + __all__.append("rl") \ No newline at end of file diff --git a/tests/example.1.1.repy b/tests/example.1.1.repy index c62c7f1..20832e2 100644 --- a/tests/example.1.1.repy +++ b/tests/example.1.1.repy @@ -1,5 +1,5 @@ -if callfunc == 'initialize': - if not callargs[0] == "test_argument": - raise Exception("incorrect argument") - else: - print "Hello World" +if callfunc == 'initialize': + if not callargs[0] == "test_argument": + raise Exception("incorrect argument") + else: + print "Hello World" diff --git a/tests/modules/conflict/__init__.py b/tests/modules/conflict/__init__.py index 23816bb..72eb302 100644 --- a/tests/modules/conflict/__init__.py +++ b/tests/modules/conflict/__init__.py @@ -1,20 +1,20 @@ -""" - - conflict/__init__.py - - - Module that is used with ut_seash_moduleconflicterror.py. It should not be - enabled successfully, as it will conflict with the default 'show' command. - - Other fields are simply defined so that the module importer can import the - module without problems. -""" - -moduledata = { - 'help_text': '', - 'url': None, - 'command_dict': { - 'show': { 'name':'nonexistant', 'help_text': 'bad help text', - 'callback': None, 'children': {}}, - } +""" + + conflict/__init__.py + + + Module that is used with ut_seash_moduleconflicterror.py. It should not be + enabled successfully, as it will conflict with the default 'show' command. + + Other fields are simply defined so that the module importer can import the + module without problems. +""" + +moduledata = { + 'help_text': '', + 'url': None, + 'command_dict': { + 'show': { 'name':'nonexistant', 'help_text': 'bad help text', + 'callback': None, 'children': {}}, + } } \ No newline at end of file diff --git a/tests/ut_seash_input_showparse_hide.py b/tests/ut_seash_input_showparse_hide.py index 8088e3b..4e479b5 100644 --- a/tests/ut_seash_input_showparse_hide.py +++ b/tests/ut_seash_input_showparse_hide.py @@ -1,33 +1,33 @@ -""" -Make sure that preprocessed command line input is printed to the screen -""" - -import sys - -import seash_dictionary - -#pragma out Enabled modules: -import seash - -import seash_modules -seash_modules.enable_modules_from_last_session(seash_dictionary.seashcommanddict) - -# There shouldn't be any output from here on... -# If the file has anything inside it after we're done, then it is an error. -sys.stdout = open('test_results.txt', 'w') - -seash.command_loop([ - 'enable variables', - 'set showparse off', - 'set username guest0', - 'loadkeys $username', - 'as $username$', - 'disable variables']) - -sys.stdout.close() -sys.stdout = sys.__stdout__ - -test_output = open('test_results.txt', 'r').read() -if test_output: - print test_output +""" +Make sure that preprocessed command line input is printed to the screen +""" + +import sys + +import seash_dictionary + +#pragma out Enabled modules: +import seash + +import seash_modules +seash_modules.enable_modules_from_last_session(seash_dictionary.seashcommanddict) + +# There shouldn't be any output from here on... +# If the file has anything inside it after we're done, then it is an error. +sys.stdout = open('test_results.txt', 'w') + +seash.command_loop([ + 'enable variables', + 'set showparse off', + 'set username guest0', + 'loadkeys $username', + 'as $username$', + 'disable variables']) + +sys.stdout.close() +sys.stdout = sys.__stdout__ + +test_output = open('test_results.txt', 'r').read() +if test_output: + print test_output \ No newline at end of file diff --git a/tests/ut_seash_input_showparse_show.py b/tests/ut_seash_input_showparse_show.py index 9862067..0704155 100644 --- a/tests/ut_seash_input_showparse_show.py +++ b/tests/ut_seash_input_showparse_show.py @@ -1,21 +1,21 @@ -""" -Make sure that preprocessed command line input is printed to the screen -""" - -import seash -import seash_dictionary -import seash_modules -seash_modules.enable_modules_from_last_session(seash_dictionary.seashcommanddict) - - -#pragma out Enabled modules: - -#pragma out loadkeys guest0 -#pragma out as guest0 - -seash.command_loop([ - 'enable variables', - 'set username guest0', - 'loadkeys $username', - 'as $username$', +""" +Make sure that preprocessed command line input is printed to the screen +""" + +import seash +import seash_dictionary +import seash_modules +seash_modules.enable_modules_from_last_session(seash_dictionary.seashcommanddict) + + +#pragma out Enabled modules: + +#pragma out loadkeys guest0 +#pragma out as guest0 + +seash.command_loop([ + 'enable variables', + 'set username guest0', + 'loadkeys $username', + 'as $username$', 'disable variables']) \ No newline at end of file diff --git a/tests/ut_seash_moduleconflicterror.py b/tests/ut_seash_moduleconflicterror.py index ceac69a..7fa2bf5 100644 --- a/tests/ut_seash_moduleconflicterror.py +++ b/tests/ut_seash_moduleconflicterror.py @@ -1,18 +1,18 @@ -""" -Ensure that modules that have conflicting commands do not get imported. -It should also display a readable error message indicating where the conflicting -command was found. -""" - -import seash_dictionary -import seash_modules -#pragma out Enabled modules: modules -seash_modules.enable_modules_from_last_session(seash_dictionary.seashcommanddict) - -#pragma out Module cannot be imported due to the following conflicting command: -#pragma out show (default) - -import seash -seash.command_loop([ - 'enable conflict' +""" +Ensure that modules that have conflicting commands do not get imported. +It should also display a readable error message indicating where the conflicting +command was found. +""" + +import seash_dictionary +import seash_modules +#pragma out Enabled modules: modules +seash_modules.enable_modules_from_last_session(seash_dictionary.seashcommanddict) + +#pragma out Module cannot be imported due to the following conflicting command: +#pragma out show (default) + +import seash +seash.command_loop([ + 'enable conflict' ]) \ No newline at end of file