1#!/usr/local/bin/python3.8 2# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai 3 4 5__license__ = 'GPL v3' 6__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>' 7__docformat__ = 'restructuredtext en' 8 9import os, re, sys 10from calibre.constants import iswindows, cache_dir, get_version 11from polyglot.builtins import exec_path 12 13ipydir = os.path.join(cache_dir(), 'ipython') 14 15BANNER = ('Welcome to the interactive calibre shell!\n') 16 17 18def setup_pyreadline(): 19 config = ''' 20#Bind keys for exit (keys only work on empty lines 21#disable_readline(True) #Disable pyreadline completely. 22debug_output("off") #"on" saves log info to./pyreadline_debug_log.txt 23 #"on_nologfile" only enables print warning messages 24bind_exit_key("Control-d") 25bind_exit_key("Control-z") 26 27#Commands for moving 28bind_key("Home", "beginning_of_line") 29bind_key("End", "end_of_line") 30bind_key("Left", "backward_char") 31bind_key("Control-b", "backward_char") 32bind_key("Right", "forward_char") 33bind_key("Control-f", "forward_char") 34bind_key("Alt-f", "forward_word") 35bind_key("Alt-b", "backward_word") 36bind_key("Clear", "clear_screen") 37bind_key("Control-l", "clear_screen") 38bind_key("Control-a", "beginning_of_line") 39bind_key("Control-e", "end_of_line") 40#bind_key("Control-l", "redraw_current_line") 41 42#Commands for Manipulating the History 43bind_key("Return", "accept_line") 44bind_key("Control-p", "previous_history") 45bind_key("Control-n", "next_history") 46bind_key("Up", "history_search_backward") 47bind_key("Down", "history_search_forward") 48bind_key("Alt-<", "beginning_of_history") 49bind_key("Alt->", "end_of_history") 50bind_key("Control-r", "reverse_search_history") 51bind_key("Control-s", "forward_search_history") 52bind_key("Alt-p", "non_incremental_reverse_search_history") 53bind_key("Alt-n", "non_incremental_forward_search_history") 54 55bind_key("Control-z", "undo") 56bind_key("Control-_", "undo") 57 58#Commands for Changing Text 59bind_key("Delete", "delete_char") 60bind_key("Control-d", "delete_char") 61bind_key("BackSpace", "backward_delete_char") 62#bind_key("Control-Shift-v", "quoted_insert") 63bind_key("Control-space", "self_insert") 64bind_key("Control-BackSpace", "backward_delete_word") 65 66#Killing and Yanking 67bind_key("Control-k", "kill_line") 68bind_key("Control-shift-k", "kill_whole_line") 69bind_key("Escape", "kill_whole_line") 70bind_key("Meta-d", "kill_word") 71bind_key("Control-w", "unix_word_rubout") 72#bind_key("Control-Delete", "forward_kill_word") 73 74#Copy paste 75bind_key("Shift-Right", "forward_char_extend_selection") 76bind_key("Shift-Left", "backward_char_extend_selection") 77bind_key("Shift-Control-Right", "forward_word_extend_selection") 78bind_key("Shift-Control-Left", "backward_word_extend_selection") 79bind_key("Control-m", "set_mark") 80 81bind_key("Control-Shift-x", "copy_selection_to_clipboard") 82#bind_key("Control-c", "copy_selection_to_clipboard") #Needs allow_ctrl_c(True) below to be uncommented 83bind_key("Control-q", "copy_region_to_clipboard") 84bind_key('Control-Shift-v', "paste_mulitline_code") 85bind_key("Control-x", "cut_selection_to_clipboard") 86 87bind_key("Control-v", "paste") 88bind_key("Control-y", "yank") 89bind_key("Alt-v", "ipython_paste") 90 91#Unbinding keys: 92#un_bind_key("Home") 93 94#Other 95bell_style("none") #modes: none, audible, visible(not implemented) 96show_all_if_ambiguous("on") 97mark_directories("on") 98completer_delims(" \t\n\"\\'`@$><=;|&{(?") 99complete_filesystem("on") 100debug_output("off") 101#allow_ctrl_c(True) #(Allows use of ctrl-c as copy key, still propagate keyboardinterrupt when not waiting for input) 102 103history_filename(%r) 104history_length(2000) #value of -1 means no limit 105 106#set_mode("vi") #will cause following bind_keys to bind to vi mode as well as activate vi mode 107#ctrl_c_tap_time_interval(0.3) 108 ''' 109 try: 110 import pyreadline.rlmain 111 if not os.path.exists(ipydir): 112 os.makedirs(ipydir) 113 conf = os.path.join(ipydir, 'pyreadline.txt') 114 hist = os.path.join(ipydir, 'history.txt') 115 config = config % hist 116 with open(conf, 'wb') as f: 117 f.write(config.encode('utf-8')) 118 pyreadline.rlmain.config_path = conf 119 import readline, atexit 120 import pyreadline.unicode_helper # noqa 121 # Normally the codepage for pyreadline is set to be sys.stdout.encoding 122 # if you need to change this uncomment the following line 123 # pyreadline.unicode_helper.pyreadline_codepage="utf8" 124 except ImportError: 125 print("Module readline not available.") 126 else: 127 # import tab completion functionality 128 import rlcompleter 129 130 # Override completer from rlcompleter to disable automatic ( on callable 131 completer_obj = rlcompleter.Completer() 132 133 def nop(val, word): 134 return word 135 completer_obj._callable_postfix = nop 136 readline.set_completer(completer_obj.complete) 137 138 # activate tab completion 139 readline.parse_and_bind("tab: complete") 140 readline.read_history_file() 141 atexit.register(readline.write_history_file) 142 del readline, rlcompleter, atexit 143 144 145class Exit: 146 147 def __repr__(self): 148 raise SystemExit(0) 149 __str__ = __repr__ 150 151 def __call__(self): 152 raise SystemExit(0) 153 154 155class Helper: 156 157 def __repr__(self): 158 return "Type help() for interactive help, " \ 159 "or help(object) for help about object." 160 161 def __call__(self, *args, **kwds): 162 import pydoc 163 return pydoc.help(*args, **kwds) 164 165 166def simple_repl(user_ns={}): 167 if iswindows: 168 setup_pyreadline() 169 else: 170 try: 171 import rlcompleter # noqa 172 import readline # noqa 173 readline.parse_and_bind("tab: complete") 174 except ImportError: 175 pass 176 177 user_ns = user_ns or {} 178 import sys, re # noqa 179 for x in ('os', 'sys', 're'): 180 user_ns[x] = user_ns.get(x, globals().get(x, locals().get(x))) 181 user_ns['exit'] = Exit() 182 user_ns['help'] = Helper() 183 from code import InteractiveConsole 184 console = InteractiveConsole(user_ns) 185 console.interact(BANNER + 'Use exit to quit') 186 187 188def ipython(user_ns=None): 189 os.environ['IPYTHONDIR'] = ipydir 190 have_ipython = True 191 try: 192 from IPython.terminal.embed import InteractiveShellEmbed 193 from traitlets.config.loader import Config 194 from IPython.terminal.prompts import Prompts, Token 195 except ImportError: 196 have_ipython = False 197 if not have_ipython: 198 return simple_repl(user_ns=user_ns) 199 200 class CustomPrompt(Prompts): 201 202 def in_prompt_tokens(self, cli=None): 203 return [ 204 (Token.Prompt, 'calibre['), 205 (Token.PromptNum, get_version()), 206 (Token.Prompt, ']> '), 207 ] 208 209 def out_prompt_tokens(self): 210 return [] 211 212 defns = {'os':os, 're':re, 'sys':sys} 213 defns.update(user_ns or {}) 214 215 c = Config() 216 user_conf = os.path.expanduser('~/.ipython/profile_default/ipython_config.py') 217 if os.path.exists(user_conf): 218 exec_path(user_conf, {'get_config': lambda: c}) 219 c.TerminalInteractiveShell.prompts_class = CustomPrompt 220 c.InteractiveShellApp.exec_lines = [ 221 'from __future__ import division, absolute_import, unicode_literals, print_function', 222 ] 223 c.TerminalInteractiveShell.confirm_exit = False 224 c.TerminalInteractiveShell.banner1 = BANNER 225 c.BaseIPythonApplication.ipython_dir = ipydir 226 227 c.InteractiveShell.separate_in = '' 228 c.InteractiveShell.separate_out = '' 229 c.InteractiveShell.separate_out2 = '' 230 231 ipshell = InteractiveShellEmbed.instance(config=c, user_ns=user_ns) 232 ipshell() 233