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