##// END OF EJS Templates
Upgrade to prompt_toolkit 2.0
Jonathan Slenders -
Show More
@@ -1,124 +1,117 b''
1 1 import signal
2 2 import sys
3 3
4 4 from IPython.core.debugger import Pdb
5 5
6 6 from IPython.core.completer import IPCompleter
7 7 from .ptutils import IPythonPTCompleter
8 8 from .shortcuts import suspend_to_bg, cursor_in_leading_ws
9 9
10 10 from prompt_toolkit.enums import DEFAULT_BUFFER
11 from prompt_toolkit.filters import (Condition, HasFocus, HasSelection,
12 ViInsertMode, EmacsInsertMode)
13 from prompt_toolkit.keys import Keys
14 from prompt_toolkit.key_binding.manager import KeyBindingManager
11 from prompt_toolkit.filters import (Condition, has_focus, has_selection,
12 vi_insert_mode, emacs_insert_mode)
13 from prompt_toolkit.key_binding import KeyBindings
15 14 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
16 from prompt_toolkit.token import Token
17 from prompt_toolkit.shortcuts import create_prompt_application
18 from prompt_toolkit.interface import CommandLineInterface
15 from pygments.token import Token
16 from prompt_toolkit.shortcuts.prompt import PromptSession
19 17 from prompt_toolkit.enums import EditingMode
18 from prompt_toolkit.formatted_text import PygmentsTokens
20 19
21 20
22 21 class TerminalPdb(Pdb):
23 22 def __init__(self, *args, **kwargs):
24 23 Pdb.__init__(self, *args, **kwargs)
25 24 self._ptcomp = None
26 25 self.pt_init()
27 26
28 27 def pt_init(self):
29 def get_prompt_tokens(cli):
28 def get_prompt_tokens():
30 29 return [(Token.Prompt, self.prompt)]
31 30
32 def patch_stdout(**kwargs):
33 return self.pt_cli.patch_stdout_context(**kwargs)
34
35 31 if self._ptcomp is None:
36 32 compl = IPCompleter(shell=self.shell,
37 33 namespace={},
38 34 global_namespace={},
39 35 parent=self.shell,
40 36 )
41 self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout)
37 self._ptcomp = IPythonPTCompleter(compl)
42 38
43 kbmanager = KeyBindingManager.for_prompt()
44 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
45 kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend
46 )(suspend_to_bg)
39 kb = KeyBindings()
40 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
41 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
47 42
48 43 if self.shell.display_completions == 'readlinelike':
49 kbmanager.registry.add_binding(Keys.ControlI,
50 filter=(HasFocus(DEFAULT_BUFFER)
51 & ~HasSelection()
52 & ViInsertMode() | EmacsInsertMode()
44 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
45 & ~has_selection
46 & vi_insert_mode | emacs_insert_mode
53 47 & ~cursor_in_leading_ws
54 48 ))(display_completions_like_readline)
55 multicolumn = (self.shell.display_completions == 'multicolumn')
56 49
57 self._pt_app = create_prompt_application(
50 self.pt_app = PromptSession(
51 message=(lambda: PygmentsTokens(get_prompt_tokens())),
58 52 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
59 key_bindings_registry=kbmanager.registry,
53 key_bindings=kb,
60 54 history=self.shell.debugger_history,
61 55 completer= self._ptcomp,
62 56 enable_history_search=True,
63 57 mouse_support=self.shell.mouse_support,
64 get_prompt_tokens=get_prompt_tokens,
65 display_completions_in_columns=multicolumn,
66 style=self.shell.style
58 complete_style=self.shell.pt_complete_style,
59 style=self.shell.style,
60 inputhook=self.shell.inputhook,
67 61 )
68 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
69 62
70 63 def cmdloop(self, intro=None):
71 64 """Repeatedly issue a prompt, accept input, parse an initial prefix
72 65 off the received input, and dispatch to action methods, passing them
73 66 the remainder of the line as argument.
74 67
75 68 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
76 69 """
77 70 if not self.use_rawinput:
78 71 raise ValueError('Sorry ipdb does not support use_rawinput=False')
79 72
80 73 self.preloop()
81 74
82 75 try:
83 76 if intro is not None:
84 77 self.intro = intro
85 78 if self.intro:
86 79 self.stdout.write(str(self.intro)+"\n")
87 80 stop = None
88 81 while not stop:
89 82 if self.cmdqueue:
90 83 line = self.cmdqueue.pop(0)
91 84 else:
92 85 self._ptcomp.ipy_completer.namespace = self.curframe_locals
93 86 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
94 87 try:
95 line = self.pt_cli.run(reset_current_buffer=True).text
88 line = self.pt_app.prompt() # reset_current_buffer=True)
96 89 except EOFError:
97 90 line = 'EOF'
98 91 line = self.precmd(line)
99 92 stop = self.onecmd(line)
100 93 stop = self.postcmd(stop, line)
101 94 self.postloop()
102 95 except Exception:
103 96 raise
104 97
105 98
106 99 def set_trace(frame=None):
107 100 """
108 101 Start debugging from `frame`.
109 102
110 103 If frame is not specified, debugging starts from caller's frame.
111 104 """
112 105 TerminalPdb().set_trace(frame or sys._getframe().f_back)
113 106
114 107
115 108 if __name__ == '__main__':
116 109 import pdb
117 110 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
118 111 # bdb.BdbQuit. When started through __main__ and an exception
119 112 # happened after hitting "c", this is needed in order to
120 113 # be able to quit the debugging session (see #9950).
121 114 old_trace_dispatch = pdb.Pdb.trace_dispatch
122 115 pdb.Pdb = TerminalPdb
123 116 pdb.Pdb.trace_dispatch = old_trace_dispatch
124 117 pdb.main()
@@ -1,542 +1,535 b''
1 1 """IPython terminal interface using prompt_toolkit"""
2 2
3 3 import os
4 4 import sys
5 5 import warnings
6 6 from warnings import warn
7 7
8 8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 9 from IPython.utils import io
10 10 from IPython.utils.py3compat import input
11 11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 12 from IPython.utils.process import abbrev_cwd
13 13 from traitlets import (
14 14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 15 Any,
16 16 )
17 17
18 18 from prompt_toolkit.document import Document
19 19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
20 20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
21 from prompt_toolkit.formatted_text import PygmentsTokens
21 22 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
23 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
23 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle
24 from prompt_toolkit.output.defaults import create_output
25 from prompt_toolkit.key_binding import KeyBindings
25 26 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27 from prompt_toolkit.patch_stdout import patch_stdout
28 from prompt_toolkit.styles import DynamicStyle, merge_styles
29 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
27 30
28 31 from pygments.styles import get_style_by_name
29 32 from pygments.style import Style
30 33 from pygments.token import Token
31 34
32 35 from .debugger import TerminalPdb, Pdb
33 36 from .magics import TerminalMagics
34 37 from .pt_inputhooks import get_inputhook_name_and_func
35 38 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 39 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 from .shortcuts import register_ipython_shortcuts
40 from .shortcuts import create_ipython_shortcuts
38 41
39 42 DISPLAY_BANNER_DEPRECATED = object()
40 43
41 44
42 45 class _NoStyle(Style): pass
43 46
44 47
45 48
46 49 _style_overrides_light_bg = {
47 50 Token.Prompt: '#0000ff',
48 51 Token.PromptNum: '#0000ee bold',
49 52 Token.OutPrompt: '#cc0000',
50 53 Token.OutPromptNum: '#bb0000 bold',
51 54 }
52 55
53 56 _style_overrides_linux = {
54 57 Token.Prompt: '#00cc00',
55 58 Token.PromptNum: '#00bb00 bold',
56 59 Token.OutPrompt: '#cc0000',
57 60 Token.OutPromptNum: '#bb0000 bold',
58 61 }
59 62
60 63 def get_default_editor():
61 64 try:
62 65 return os.environ['EDITOR']
63 66 except KeyError:
64 67 pass
65 68 except UnicodeError:
66 69 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 70 "default editor.")
68 71
69 72 if os.name == 'posix':
70 73 return 'vi' # the only one guaranteed to be there!
71 74 else:
72 75 return 'notepad' # same in Windows!
73 76
74 77 # conservatively check for tty
75 78 # overridden streams can result in things like:
76 79 # - sys.stdin = None
77 80 # - no isatty method
78 81 for _name in ('stdin', 'stdout', 'stderr'):
79 82 _stream = getattr(sys, _name)
80 83 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
81 84 _is_tty = False
82 85 break
83 86 else:
84 87 _is_tty = True
85 88
86 89
87 90 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88 91
89 92 class TerminalInteractiveShell(InteractiveShell):
90 93 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
91 94 'to reserve for the completion menu'
92 95 ).tag(config=True)
93 96
94 def _space_for_menu_changed(self, old, new):
95 self._update_layout()
97 # def _space_for_menu_changed(self, old, new):
98 # self._update_layout()
96 99
97 pt_cli = None
100 pt_app = None
98 101 debugger_history = None
99 _pt_app = None
100 102
101 103 simple_prompt = Bool(_use_simple_prompt,
102 104 help="""Use `raw_input` for the REPL, without completion and prompt colors.
103 105
104 106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
105 107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
106 108
107 109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
108 110 environment variable is set, or the current terminal is not a tty."""
109 111 ).tag(config=True)
110 112
111 113 @property
112 114 def debugger_cls(self):
113 115 return Pdb if self.simple_prompt else TerminalPdb
114 116
115 117 confirm_exit = Bool(True,
116 118 help="""
117 119 Set to confirm when you try to exit IPython with an EOF (Control-D
118 120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
119 121 you can force a direct exit without any confirmation.""",
120 122 ).tag(config=True)
121 123
122 124 editing_mode = Unicode('emacs',
123 125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
124 126 ).tag(config=True)
125 127
126 128 mouse_support = Bool(False,
127 129 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
128 130 ).tag(config=True)
129 131
130 132 # We don't load the list of styles for the help string, because loading
131 133 # Pygments plugins takes time and can cause unexpected errors.
132 134 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
133 135 help="""The name or class of a Pygments style to use for syntax
134 136 highlighting. To see available styles, run `pygmentize -L styles`."""
135 137 ).tag(config=True)
136 138
137 139
138 140 @observe('highlighting_style')
139 141 @observe('colors')
140 142 def _highlighting_style_changed(self, change):
141 143 self.refresh_style()
142 144
143 145 def refresh_style(self):
144 146 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
145 147
146 148
147 149 highlighting_style_overrides = Dict(
148 150 help="Override highlighting format for specific tokens"
149 151 ).tag(config=True)
150 152
151 153 true_color = Bool(False,
152 154 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
153 155 "If your terminal supports true color, the following command "
154 156 "should print 'TRUECOLOR' in orange: "
155 157 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
156 158 ).tag(config=True)
157 159
158 160 editor = Unicode(get_default_editor(),
159 161 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
160 162 ).tag(config=True)
161 163
162 164 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
163 165
164 166 prompts = Instance(Prompts)
165 167
166 168 @default('prompts')
167 169 def _prompts_default(self):
168 170 return self.prompts_class(self)
169 171
170 @observe('prompts')
171 def _(self, change):
172 self._update_layout()
172 # @observe('prompts')
173 # def _(self, change):
174 # self._update_layout()
173 175
174 176 @default('displayhook_class')
175 177 def _displayhook_class_default(self):
176 178 return RichPromptDisplayHook
177 179
178 180 term_title = Bool(True,
179 181 help="Automatically set the terminal title"
180 182 ).tag(config=True)
181 183
182 184 term_title_format = Unicode("IPython: {cwd}",
183 185 help="Customize the terminal title format. This is a python format string. " +
184 186 "Available substitutions are: {cwd}."
185 187 ).tag(config=True)
186 188
187 189 display_completions = Enum(('column', 'multicolumn','readlinelike'),
188 190 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
189 191 "'readlinelike'. These options are for `prompt_toolkit`, see "
190 192 "`prompt_toolkit` documentation for more information."
191 193 ),
192 194 default_value='multicolumn').tag(config=True)
193 195
194 196 highlight_matching_brackets = Bool(True,
195 197 help="Highlight matching brackets.",
196 198 ).tag(config=True)
197 199
198 200 extra_open_editor_shortcuts = Bool(False,
199 201 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
200 202 "This is in addition to the F2 binding, which is always enabled."
201 203 ).tag(config=True)
202 204
203 205 handle_return = Any(None,
204 206 help="Provide an alternative handler to be called when the user presses "
205 207 "Return. This is an advanced option intended for debugging, which "
206 208 "may be changed or removed in later releases."
207 209 ).tag(config=True)
208 210
209 211 enable_history_search = Bool(True,
210 212 help="Allows to enable/disable the prompt toolkit history search"
211 213 ).tag(config=True)
212 214
213 215 @observe('term_title')
214 216 def init_term_title(self, change=None):
215 217 # Enable or disable the terminal title.
216 218 if self.term_title:
217 219 toggle_set_term_title(True)
218 220 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
219 221 else:
220 222 toggle_set_term_title(False)
221 223
222 224 def init_display_formatter(self):
223 225 super(TerminalInteractiveShell, self).init_display_formatter()
224 226 # terminal only supports plain text
225 227 self.display_formatter.active_types = ['text/plain']
226 228 # disable `_ipython_display_`
227 229 self.display_formatter.ipython_display_formatter.enabled = False
228 230
229 231 def init_prompt_toolkit_cli(self):
230 232 if self.simple_prompt:
231 233 # Fall back to plain non-interactive output for tests.
232 234 # This is very limited, and only accepts a single line.
233 235 def prompt():
234 236 isp = self.input_splitter
235 237 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
236 238 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
237 239 while isp.push_accepts_more():
238 240 line = input(prompt_text)
239 241 isp.push(line)
240 242 prompt_text = prompt_continuation
241 243 return isp.source_reset()
242 244 self.prompt_for_code = prompt
243 245 return
244 246
245 247 # Set up keyboard shortcuts
246 kbmanager = KeyBindingManager.for_prompt(
247 enable_open_in_editor=self.extra_open_editor_shortcuts,
248 )
249 register_ipython_shortcuts(kbmanager.registry, self)
248 key_bindings = create_ipython_shortcuts(self)
250 249
251 250 # Pre-populate history from IPython's history database
252 251 history = InMemoryHistory()
253 252 last_cell = u""
254 253 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
255 254 include_latest=True):
256 255 # Ignore blank lines and consecutive duplicates
257 256 cell = cell.rstrip()
258 257 if cell and (cell != last_cell):
259 history.append(cell)
258 history.append_string(cell)
260 259 last_cell = cell
261 260
262 261 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
263 262 self.style = DynamicStyle(lambda: self._style)
264 263
265 264 editing_mode = getattr(EditingMode, self.editing_mode.upper())
266 265
267 def patch_stdout(**kwargs):
268 return self.pt_cli.patch_stdout_context(**kwargs)
269
270 self._pt_app = create_prompt_application(
266 self.pt_app = PromptSession(
271 267 editing_mode=editing_mode,
272 key_bindings_registry=kbmanager.registry,
268 key_bindings=key_bindings,
273 269 history=history,
274 completer=IPythonPTCompleter(shell=self,
275 patch_stdout=patch_stdout),
270 completer=IPythonPTCompleter(shell=self),
276 271 enable_history_search=self.enable_history_search,
277 272 style=self.style,
273 include_default_pygments_style=False,
278 274 mouse_support=self.mouse_support,
279 **self._layout_options()
280 )
281 self._eventloop = create_eventloop(self.inputhook)
282 self.pt_cli = CommandLineInterface(
283 self._pt_app, eventloop=self._eventloop,
284 output=create_output(true_color=self.true_color))
275 enable_open_in_editor=self.extra_open_editor_shortcuts,
276 color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None),
277 **self._extra_prompt_options())
285 278
286 279 def _make_style_from_name_or_cls(self, name_or_cls):
287 280 """
288 281 Small wrapper that make an IPython compatible style from a style name
289 282
290 283 We need that to add style for prompt ... etc.
291 284 """
292 285 style_overrides = {}
293 286 if name_or_cls == 'legacy':
294 287 legacy = self.colors.lower()
295 288 if legacy == 'linux':
296 289 style_cls = get_style_by_name('monokai')
297 290 style_overrides = _style_overrides_linux
298 291 elif legacy == 'lightbg':
299 292 style_overrides = _style_overrides_light_bg
300 293 style_cls = get_style_by_name('pastie')
301 294 elif legacy == 'neutral':
302 295 # The default theme needs to be visible on both a dark background
303 296 # and a light background, because we can't tell what the terminal
304 297 # looks like. These tweaks to the default theme help with that.
305 298 style_cls = get_style_by_name('default')
306 299 style_overrides.update({
307 300 Token.Number: '#007700',
308 301 Token.Operator: 'noinherit',
309 302 Token.String: '#BB6622',
310 303 Token.Name.Function: '#2080D0',
311 304 Token.Name.Class: 'bold #2080D0',
312 305 Token.Name.Namespace: 'bold #2080D0',
313 306 Token.Prompt: '#009900',
314 307 Token.PromptNum: '#00ff00 bold',
315 308 Token.OutPrompt: '#990000',
316 309 Token.OutPromptNum: '#ff0000 bold',
317 310 })
318 311
319 312 # Hack: Due to limited color support on the Windows console
320 313 # the prompt colors will be wrong without this
321 314 if os.name == 'nt':
322 315 style_overrides.update({
323 316 Token.Prompt: '#ansidarkgreen',
324 317 Token.PromptNum: '#ansigreen bold',
325 318 Token.OutPrompt: '#ansidarkred',
326 319 Token.OutPromptNum: '#ansired bold',
327 320 })
328 321 elif legacy =='nocolor':
329 322 style_cls=_NoStyle
330 323 style_overrides = {}
331 324 else :
332 325 raise ValueError('Got unknown colors: ', legacy)
333 326 else :
334 327 if isinstance(name_or_cls, str):
335 328 style_cls = get_style_by_name(name_or_cls)
336 329 else:
337 330 style_cls = name_or_cls
338 331 style_overrides = {
339 332 Token.Prompt: '#009900',
340 333 Token.PromptNum: '#00ff00 bold',
341 334 Token.OutPrompt: '#990000',
342 335 Token.OutPromptNum: '#ff0000 bold',
343 336 }
344 337 style_overrides.update(self.highlighting_style_overrides)
345 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
346 style_dict=style_overrides)
338 style = merge_styles([
339 style_from_pygments_cls(style_cls),
340 style_from_pygments_dict(style_overrides),
341 ])
347 342
348 343 return style
349 344
350 def _layout_options(self):
345 @property
346 def pt_complete_style(self):
347 return {
348 'multicolumn': CompleteStyle.MULTI_COLUMN,
349 'column': CompleteStyle.COLUMN,
350 'readlinelike': CompleteStyle.READLINE_LIKE,
351 }[self.display_completions],
352
353 def _extra_prompt_options(self):
351 354 """
352 355 Return the current layout option for the current Terminal InteractiveShell
353 356 """
357 def get_message():
358 return PygmentsTokens(self.prompts.in_prompt_tokens())
359
354 360 return {
361 'complete_in_thread': False,
355 362 'lexer':IPythonPTLexer(),
356 363 'reserve_space_for_menu':self.space_for_menu,
357 'get_prompt_tokens':self.prompts.in_prompt_tokens,
358 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
364 'message': get_message,
365 'prompt_continuation': (
366 lambda width, lineno, is_soft_wrap:
367 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
359 368 'multiline':True,
360 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
369 'complete_style': self.pt_complete_style,
361 370
362 371 # Highlight matching brackets, but only when this setting is
363 372 # enabled, and only when the DEFAULT_BUFFER has the focus.
364 'extra_input_processors': [ConditionalProcessor(
373 'input_processors': [ConditionalProcessor(
365 374 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
366 375 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
367 Condition(lambda cli: self.highlight_matching_brackets))],
376 Condition(lambda: self.highlight_matching_brackets))],
368 377 }
369 378
370 def _update_layout(self):
371 """
372 Ask for a re computation of the application layout, if for example ,
373 some configuration options have changed.
374 """
375 if self._pt_app:
376 self._pt_app.layout = create_prompt_layout(**self._layout_options())
377
378 379 def prompt_for_code(self):
379 with self.pt_cli.patch_stdout_context(raw=True):
380 document = self.pt_cli.run(
381 pre_run=self.pre_prompt, reset_current_buffer=True)
382 return document.text
380 if self.rl_next_input:
381 default = self.rl_next_input
382 self.rl_next_input = None
383 else:
384 default = ''
385
386 with patch_stdout(raw=True):
387 text = self.pt_app.prompt(
388 default=default,
389 # pre_run=self.pre_prompt,# reset_current_buffer=True,
390 **self._extra_prompt_options())
391 return text
383 392
384 393 def enable_win_unicode_console(self):
385 394 if sys.version_info >= (3, 6):
386 395 # Since PEP 528, Python uses the unicode APIs for the Windows
387 396 # console by default, so WUC shouldn't be needed.
388 397 return
389 398
390 399 import win_unicode_console
391 400 win_unicode_console.enable()
392 401
393 402 def init_io(self):
394 403 if sys.platform not in {'win32', 'cli'}:
395 404 return
396 405
397 406 self.enable_win_unicode_console()
398 407
399 408 import colorama
400 409 colorama.init()
401 410
402 411 # For some reason we make these wrappers around stdout/stderr.
403 412 # For now, we need to reset them so all output gets coloured.
404 413 # https://github.com/ipython/ipython/issues/8669
405 414 # io.std* are deprecated, but don't show our own deprecation warnings
406 415 # during initialization of the deprecated API.
407 416 with warnings.catch_warnings():
408 417 warnings.simplefilter('ignore', DeprecationWarning)
409 418 io.stdout = io.IOStream(sys.stdout)
410 419 io.stderr = io.IOStream(sys.stderr)
411 420
412 421 def init_magics(self):
413 422 super(TerminalInteractiveShell, self).init_magics()
414 423 self.register_magics(TerminalMagics)
415 424
416 425 def init_alias(self):
417 426 # The parent class defines aliases that can be safely used with any
418 427 # frontend.
419 428 super(TerminalInteractiveShell, self).init_alias()
420 429
421 430 # Now define aliases that only make sense on the terminal, because they
422 431 # need direct access to the console in a way that we can't emulate in
423 432 # GUI or web frontend
424 433 if os.name == 'posix':
425 434 for cmd in ['clear', 'more', 'less', 'man']:
426 435 self.alias_manager.soft_define_alias(cmd, cmd)
427 436
428 437
429 438 def __init__(self, *args, **kwargs):
430 439 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
431 440 self.init_prompt_toolkit_cli()
432 441 self.init_term_title()
433 442 self.keep_running = True
434 443
435 444 self.debugger_history = InMemoryHistory()
436 445
437 446 def ask_exit(self):
438 447 self.keep_running = False
439 448
440 449 rl_next_input = None
441 450
442 def pre_prompt(self):
443 if self.rl_next_input:
444 # We can't set the buffer here, because it will be reset just after
445 # this. Adding a callable to pre_run_callables does what we need
446 # after the buffer is reset.
447 s = self.rl_next_input
448 def set_doc():
449 self.pt_cli.application.buffer.document = Document(s)
450 if hasattr(self.pt_cli, 'pre_run_callables'):
451 self.pt_cli.pre_run_callables.append(set_doc)
452 else:
453 # Older version of prompt_toolkit; it's OK to set the document
454 # directly here.
455 set_doc()
456 self.rl_next_input = None
457
458 451 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
459 452
460 453 if display_banner is not DISPLAY_BANNER_DEPRECATED:
461 454 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
462 455
463 456 self.keep_running = True
464 457 while self.keep_running:
465 458 print(self.separate_in, end='')
466 459
467 460 try:
468 461 code = self.prompt_for_code()
469 462 except EOFError:
470 463 if (not self.confirm_exit) \
471 464 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
472 465 self.ask_exit()
473 466
474 467 else:
475 468 if code:
476 469 self.run_cell(code, store_history=True)
477 470
478 471 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
479 472 # An extra layer of protection in case someone mashing Ctrl-C breaks
480 473 # out of our internal code.
481 474 if display_banner is not DISPLAY_BANNER_DEPRECATED:
482 475 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
483 476 while True:
484 477 try:
485 478 self.interact()
486 479 break
487 480 except KeyboardInterrupt as e:
488 481 print("\n%s escaped interact()\n" % type(e).__name__)
489 482 finally:
490 483 # An interrupt during the eventloop will mess up the
491 484 # internal state of the prompt_toolkit library.
492 485 # Stopping the eventloop fixes this, see
493 486 # https://github.com/ipython/ipython/pull/9867
494 487 if hasattr(self, '_eventloop'):
495 488 self._eventloop.stop()
496 489
497 490 _inputhook = None
498 491 def inputhook(self, context):
499 492 if self._inputhook is not None:
500 493 self._inputhook(context)
501 494
502 495 active_eventloop = None
503 496 def enable_gui(self, gui=None):
504 497 if gui:
505 498 self.active_eventloop, self._inputhook =\
506 499 get_inputhook_name_and_func(gui)
507 500 else:
508 501 self.active_eventloop = self._inputhook = None
509 502
510 503 # Run !system commands directly, not through pipes, so terminal programs
511 504 # work correctly.
512 505 system = InteractiveShell.system_raw
513 506
514 507 def auto_rewrite_input(self, cmd):
515 508 """Overridden from the parent class to use fancy rewriting prompt"""
516 509 if not self.show_rewritten_input:
517 510 return
518 511
519 512 tokens = self.prompts.rewrite_prompt_tokens()
520 if self.pt_cli:
521 self.pt_cli.print_tokens(tokens)
513 if self.pt_app:
514 self.pt_app.print_tokens(tokens) # XXX
522 515 print(cmd)
523 516 else:
524 517 prompt = ''.join(s for t, s in tokens)
525 518 print(prompt, cmd, sep='')
526 519
527 520 _prompts_before = None
528 521 def switch_doctest_mode(self, mode):
529 522 """Switch prompts to classic for %doctest_mode"""
530 523 if mode:
531 524 self._prompts_before = self.prompts
532 525 self.prompts = ClassicPrompts(self)
533 526 elif self._prompts_before:
534 527 self.prompts = self._prompts_before
535 528 self._prompts_before = None
536 self._update_layout()
529 # self._update_layout()
537 530
538 531
539 532 InteractiveShellABC.register(TerminalInteractiveShell)
540 533
541 534 if __name__ == '__main__':
542 535 TerminalInteractiveShell.instance().interact()
@@ -1,79 +1,82 b''
1 1 """Terminal input and output prompts."""
2 2
3 3 from pygments.token import Token
4 4 import sys
5 5
6 6 from IPython.core.displayhook import DisplayHook
7 7
8 from prompt_toolkit.layout.utils import token_list_width
8 from prompt_toolkit.formatted_text import fragment_list_width, PygmentsTokens
9 from prompt_toolkit.shortcuts import print_formatted_text
10
9 11
10 12 class Prompts(object):
11 13 def __init__(self, shell):
12 14 self.shell = shell
13 15
14 def in_prompt_tokens(self, cli=None):
16 def in_prompt_tokens(self):
15 17 return [
16 18 (Token.Prompt, 'In ['),
17 19 (Token.PromptNum, str(self.shell.execution_count)),
18 20 (Token.Prompt, ']: '),
19 21 ]
20 22
21 23 def _width(self):
22 return token_list_width(self.in_prompt_tokens())
24 return fragment_list_width(self.in_prompt_tokens())
23 25
24 def continuation_prompt_tokens(self, cli=None, width=None):
26 def continuation_prompt_tokens(self, width=None):
25 27 if width is None:
26 28 width = self._width()
27 29 return [
28 30 (Token.Prompt, (' ' * (width - 5)) + '...: '),
29 31 ]
30 32
31 33 def rewrite_prompt_tokens(self):
32 34 width = self._width()
33 35 return [
34 36 (Token.Prompt, ('-' * (width - 2)) + '> '),
35 37 ]
36 38
37 39 def out_prompt_tokens(self):
38 40 return [
39 41 (Token.OutPrompt, 'Out['),
40 42 (Token.OutPromptNum, str(self.shell.execution_count)),
41 43 (Token.OutPrompt, ']: '),
42 44 ]
43 45
44 46 class ClassicPrompts(Prompts):
45 def in_prompt_tokens(self, cli=None):
47 def in_prompt_tokens(self):
46 48 return [
47 49 (Token.Prompt, '>>> '),
48 50 ]
49 51
50 def continuation_prompt_tokens(self, cli=None, width=None):
52 def continuation_prompt_tokens(self, width=None):
51 53 return [
52 54 (Token.Prompt, '... ')
53 55 ]
54 56
55 57 def rewrite_prompt_tokens(self):
56 58 return []
57 59
58 60 def out_prompt_tokens(self):
59 61 return []
60 62
61 63 class RichPromptDisplayHook(DisplayHook):
62 64 """Subclass of base display hook using coloured prompt"""
63 65 def write_output_prompt(self):
64 66 sys.stdout.write(self.shell.separate_out)
65 67 # If we're not displaying a prompt, it effectively ends with a newline,
66 68 # because the output will be left-aligned.
67 69 self.prompt_end_newline = True
68 70
69 71 if self.do_full_cache:
70 72 tokens = self.shell.prompts.out_prompt_tokens()
71 73 prompt_txt = ''.join(s for t, s in tokens)
72 74 if prompt_txt and not prompt_txt.endswith('\n'):
73 75 # Ask for a newline before multiline output
74 76 self.prompt_end_newline = False
75 77
76 if self.shell.pt_cli:
77 self.shell.pt_cli.print_tokens(tokens)
78 if self.shell.pt_app:
79 print_formatted_text(
80 PygmentsTokens(tokens), style=self.shell.pt_app.app.style)
78 81 else:
79 82 sys.stdout.write(prompt_txt)
@@ -1,160 +1,158 b''
1 1 """prompt-toolkit utilities
2 2
3 3 Everything in this module is a private API,
4 4 not to be used outside IPython.
5 5 """
6 6
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10 10 import unicodedata
11 11 from wcwidth import wcwidth
12 12
13 13 from IPython.core.completer import (
14 14 provisionalcompleter, cursor_to_position,
15 15 _deduplicate_completions)
16 16 from prompt_toolkit.completion import Completer, Completion
17 from prompt_toolkit.layout.lexers import Lexer
18 from prompt_toolkit.layout.lexers import PygmentsLexer
17 from prompt_toolkit.lexers import Lexer
18 from prompt_toolkit.lexers import PygmentsLexer
19 from prompt_toolkit.patch_stdout import patch_stdout
19 20
20 21 import pygments.lexers as pygments_lexers
21 22
22 23 _completion_sentinel = object()
23 24
24 25 def _elide(string, *, min_elide=30):
25 26 """
26 27 If a string is long enough, and has at least 2 dots,
27 28 replace the middle part with ellipses.
28 29
29 30 If three consecutive dots, or two consecutive dots are encountered these are
30 31 replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
31 32 equivalents
32 33 """
33 34 string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
34 35 string = string.replace('..','\N{TWO DOT LEADER}')
35 36 if len(string) < min_elide:
36 37 return string
37 38
38 39 parts = string.split('.')
39 40
40 41 if len(parts) <= 3:
41 42 return string
42 43
43 44 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(parts[0], parts[1][0], parts[-2][-1], parts[-1])
44 45
45 46
46 47 def _adjust_completion_text_based_on_context(text, body, offset):
47 48 if text.endswith('=') and len(body) > offset and body[offset] is '=':
48 49 return text[:-1]
49 50 else:
50 51 return text
51 52
52 53
53 54 class IPythonPTCompleter(Completer):
54 55 """Adaptor to provide IPython completions to prompt_toolkit"""
55 def __init__(self, ipy_completer=None, shell=None, patch_stdout=None):
56 def __init__(self, ipy_completer=None, shell=None):
56 57 if shell is None and ipy_completer is None:
57 58 raise TypeError("Please pass shell=an InteractiveShell instance.")
58 59 self._ipy_completer = ipy_completer
59 60 self.shell = shell
60 if patch_stdout is None:
61 raise TypeError("Please pass patch_stdout")
62 self.patch_stdout = patch_stdout
63 61
64 62 @property
65 63 def ipy_completer(self):
66 64 if self._ipy_completer:
67 65 return self._ipy_completer
68 66 else:
69 67 return self.shell.Completer
70 68
71 69 def get_completions(self, document, complete_event):
72 70 if not document.current_line.strip():
73 71 return
74 72 # Some bits of our completion system may print stuff (e.g. if a module
75 73 # is imported). This context manager ensures that doesn't interfere with
76 74 # the prompt.
77 75
78 with self.patch_stdout(), provisionalcompleter():
76 with patch_stdout(), provisionalcompleter():
79 77 body = document.text
80 78 cursor_row = document.cursor_position_row
81 79 cursor_col = document.cursor_position_col
82 80 cursor_position = document.cursor_position
83 81 offset = cursor_to_position(body, cursor_row, cursor_col)
84 82 yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
85 83
86 84 @staticmethod
87 85 def _get_completions(body, offset, cursor_position, ipyc):
88 86 """
89 87 Private equivalent of get_completions() use only for unit_testing.
90 88 """
91 89 debug = getattr(ipyc, 'debug', False)
92 90 completions = _deduplicate_completions(
93 91 body, ipyc.completions(body, offset))
94 92 for c in completions:
95 93 if not c.text:
96 94 # Guard against completion machinery giving us an empty string.
97 95 continue
98 96 text = unicodedata.normalize('NFC', c.text)
99 97 # When the first character of the completion has a zero length,
100 98 # then it's probably a decomposed unicode character. E.g. caused by
101 99 # the "\dot" completion. Try to compose again with the previous
102 100 # character.
103 101 if wcwidth(text[0]) == 0:
104 102 if cursor_position + c.start > 0:
105 103 char_before = body[c.start - 1]
106 104 fixed_text = unicodedata.normalize(
107 105 'NFC', char_before + text)
108 106
109 107 # Yield the modified completion instead, if this worked.
110 108 if wcwidth(text[0:1]) == 1:
111 109 yield Completion(fixed_text, start_position=c.start - offset - 1)
112 110 continue
113 111
114 112 # TODO: Use Jedi to determine meta_text
115 113 # (Jedi currently has a bug that results in incorrect information.)
116 114 # meta_text = ''
117 115 # yield Completion(m, start_position=start_pos,
118 116 # display_meta=meta_text)
119 117 display_text = c.text
120 118
121 119 adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
122 120 if c.type == 'function':
123 121 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature)
124 122 else:
125 123 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
126 124
127 125 class IPythonPTLexer(Lexer):
128 126 """
129 127 Wrapper around PythonLexer and BashLexer.
130 128 """
131 129 def __init__(self):
132 130 l = pygments_lexers
133 131 self.python_lexer = PygmentsLexer(l.Python3Lexer)
134 132 self.shell_lexer = PygmentsLexer(l.BashLexer)
135 133
136 134 self.magic_lexers = {
137 135 'HTML': PygmentsLexer(l.HtmlLexer),
138 136 'html': PygmentsLexer(l.HtmlLexer),
139 137 'javascript': PygmentsLexer(l.JavascriptLexer),
140 138 'js': PygmentsLexer(l.JavascriptLexer),
141 139 'perl': PygmentsLexer(l.PerlLexer),
142 140 'ruby': PygmentsLexer(l.RubyLexer),
143 141 'latex': PygmentsLexer(l.TexLexer),
144 142 }
145 143
146 def lex_document(self, cli, document):
144 def lex_document(self, document):
147 145 text = document.text.lstrip()
148 146
149 147 lexer = self.python_lexer
150 148
151 149 if text.startswith('!') or text.startswith('%%bash'):
152 150 lexer = self.shell_lexer
153 151
154 152 elif text.startswith('%%'):
155 153 for magic, l in self.magic_lexers.items():
156 154 if text.startswith('%%' + magic):
157 155 lexer = l
158 156 break
159 157
160 return lexer.lex_document(cli, document)
158 return lexer.lex_document(document)
@@ -1,256 +1,244 b''
1 1 """
2 2 Module to define and register Terminal IPython shortcuts with
3 3 :mod:`prompt_toolkit`
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import warnings
10 10 import signal
11 11 import sys
12 12 from typing import Callable
13 13
14 14
15 from prompt_toolkit.application.current import get_app
15 16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
17 ViInsertMode, EmacsInsertMode, HasCompletions)
18 from prompt_toolkit.filters.cli import ViMode, ViNavigationMode
19 from prompt_toolkit.keys import Keys
17 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
18 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
20 19 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
20 from prompt_toolkit.key_binding import KeyBindings
21 21
22 22 from IPython.utils.decorators import undoc
23 23
24 24 @undoc
25 25 @Condition
26 def cursor_in_leading_ws(cli):
27 before = cli.application.buffer.document.current_line_before_cursor
26 def cursor_in_leading_ws():
27 before = get_app().current_buffer.document.current_line_before_cursor
28 28 return (not before) or before.isspace()
29 29
30 def register_ipython_shortcuts(registry, shell):
30
31 def create_ipython_shortcuts(shell):
31 32 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 insert_mode = ViInsertMode() | EmacsInsertMode()
33
34 kb = KeyBindings()
35 insert_mode = vi_insert_mode | emacs_insert_mode
33 36
34 37 if getattr(shell, 'handle_return', None):
35 38 return_handler = shell.handle_return(shell)
36 39 else:
37 40 return_handler = newline_or_execute_outer(shell)
38 41
39 # Ctrl+J == Enter, seemingly
40 registry.add_binding(Keys.ControlJ,
41 filter=(HasFocus(DEFAULT_BUFFER)
42 & ~HasSelection()
42 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
43 & ~has_selection
43 44 & insert_mode
44 45 ))(return_handler)
45 46
46 registry.add_binding(Keys.ControlBackslash)(force_exit)
47 kb.add('c-\\')(force_exit)
47 48
48 registry.add_binding(Keys.ControlP,
49 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
50 ))(previous_history_or_previous_completion)
49 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
50 )(previous_history_or_previous_completion)
51 51
52 registry.add_binding(Keys.ControlN,
53 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
54 ))(next_history_or_next_completion)
52 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
53 )(next_history_or_next_completion)
55 54
56 registry.add_binding(Keys.ControlG,
57 filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()
58 ))(dismiss_completion)
55 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
56 )(dismiss_completion)
59 57
60 registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)
61 )(reset_buffer)
58 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
62 59
63 registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)
64 )(reset_search_buffer)
60 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
65 61
66 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
67 registry.add_binding(Keys.ControlZ, filter=supports_suspend
68 )(suspend_to_bg)
62 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
63 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
69 64
70 65 # Ctrl+I == Tab
71 registry.add_binding(Keys.ControlI,
72 filter=(HasFocus(DEFAULT_BUFFER)
73 & ~HasSelection()
66 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
67 & ~has_selection
74 68 & insert_mode
75 69 & cursor_in_leading_ws
76 70 ))(indent_buffer)
77 71
78 registry.add_binding(Keys.ControlO,
79 filter=(HasFocus(DEFAULT_BUFFER)
80 & EmacsInsertMode()))(newline_autoindent_outer(shell.input_splitter))
72 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER)
73 & emacs_insert_mode))(newline_autoindent_outer(shell.input_splitter))
81 74
82 registry.add_binding(Keys.F2,
83 filter=HasFocus(DEFAULT_BUFFER)
84 )(open_input_in_editor)
75 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
85 76
86 77 if shell.display_completions == 'readlinelike':
87 registry.add_binding(Keys.ControlI,
88 filter=(HasFocus(DEFAULT_BUFFER)
89 & ~HasSelection()
78 kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
79 & ~has_selection
90 80 & insert_mode
91 81 & ~cursor_in_leading_ws
92 82 ))(display_completions_like_readline)
93 83
94 84 if sys.platform == 'win32':
95 registry.add_binding(Keys.ControlV,
96 filter=(
97 HasFocus(
98 DEFAULT_BUFFER) & ~ViMode()
99 ))(win_paste)
85 kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
86
87 return kb
100 88
101 89
102 90 def newline_or_execute_outer(shell):
103 91 def newline_or_execute(event):
104 92 """When the user presses return, insert a newline or execute the code."""
105 93 b = event.current_buffer
106 94 d = b.document
107 95
108 96 if b.complete_state:
109 97 cc = b.complete_state.current_completion
110 98 if cc:
111 99 b.apply_completion(cc)
112 100 else:
113 101 b.cancel_completion()
114 102 return
115 103
116 104 # If there's only one line, treat it as if the cursor is at the end.
117 105 # See https://github.com/ipython/ipython/issues/10425
118 106 if d.line_count == 1:
119 107 check_text = d.text
120 108 else:
121 109 check_text = d.text[:d.cursor_position]
122 110 status, indent = shell.input_splitter.check_complete(check_text + '\n')
123 111
124 112 if not (d.on_last_line or
125 113 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
126 114 ):
127 115 b.insert_text('\n' + (' ' * (indent or 0)))
128 116 return
129 117
130 if (status != 'incomplete') and b.accept_action.is_returnable:
131 b.accept_action.validate_and_handle(event.cli, b)
118 if (status != 'incomplete') and b.accept_handler:
119 b.validate_and_handle()
132 120 else:
133 121 b.insert_text('\n' + (' ' * (indent or 0)))
134 122 return newline_or_execute
135 123
136 124
137 125 def previous_history_or_previous_completion(event):
138 126 """
139 127 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
140 128
141 129 If completer is open this still select previous completion.
142 130 """
143 131 event.current_buffer.auto_up()
144 132
145 133
146 134 def next_history_or_next_completion(event):
147 135 """
148 136 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
149 137
150 138 If completer is open this still select next completion.
151 139 """
152 140 event.current_buffer.auto_down()
153 141
154 142
155 143 def dismiss_completion(event):
156 144 b = event.current_buffer
157 145 if b.complete_state:
158 146 b.cancel_completion()
159 147
160 148
161 149 def reset_buffer(event):
162 150 b = event.current_buffer
163 151 if b.complete_state:
164 152 b.cancel_completion()
165 153 else:
166 154 b.reset()
167 155
168 156
169 157 def reset_search_buffer(event):
170 158 if event.current_buffer.document.text:
171 159 event.current_buffer.reset()
172 160 else:
173 event.cli.push_focus(DEFAULT_BUFFER)
161 event.app.layout.focus(DEFAULT_BUFFER)
174 162
175 163 def suspend_to_bg(event):
176 event.cli.suspend_to_background()
164 event.app.suspend_to_background()
177 165
178 166 def force_exit(event):
179 167 """
180 168 Force exit (with a non-zero return value)
181 169 """
182 170 sys.exit("Quit")
183 171
184 172 def indent_buffer(event):
185 173 event.current_buffer.insert_text(' ' * 4)
186 174
187 175 @undoc
188 176 def newline_with_copy_margin(event):
189 177 """
190 178 DEPRECATED since IPython 6.0
191 179
192 180 See :any:`newline_autoindent_outer` for a replacement.
193 181
194 182 Preserve margin and cursor position when using
195 183 Control-O to insert a newline in EMACS mode
196 184 """
197 185 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
198 186 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
199 187 DeprecationWarning, stacklevel=2)
200 188
201 189 b = event.current_buffer
202 190 cursor_start_pos = b.document.cursor_position_col
203 191 b.newline(copy_margin=True)
204 192 b.cursor_up(count=1)
205 193 cursor_end_pos = b.document.cursor_position_col
206 194 if cursor_start_pos != cursor_end_pos:
207 195 pos_diff = cursor_start_pos - cursor_end_pos
208 196 b.cursor_right(count=pos_diff)
209 197
210 198 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
211 199 """
212 200 Return a function suitable for inserting a indented newline after the cursor.
213 201
214 202 Fancier version of deprecated ``newline_with_copy_margin`` which should
215 203 compute the correct indentation of the inserted line. That is to say, indent
216 204 by 4 extra space after a function definition, class definition, context
217 205 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
218 206 """
219 207
220 208 def newline_autoindent(event):
221 209 """insert a newline after the cursor indented appropriately."""
222 210 b = event.current_buffer
223 211 d = b.document
224 212
225 213 if b.complete_state:
226 214 b.cancel_completion()
227 215 text = d.text[:d.cursor_position] + '\n'
228 216 _, indent = inputsplitter.check_complete(text)
229 217 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
230 218
231 219 return newline_autoindent
232 220
233 221
234 222 def open_input_in_editor(event):
235 event.cli.current_buffer.tempfile_suffix = ".py"
236 event.cli.current_buffer.open_in_editor(event.cli)
223 event.app.current_buffer.tempfile_suffix = ".py"
224 event.app.current_buffer.open_in_editor()
237 225
238 226
239 227 if sys.platform == 'win32':
240 228 from IPython.core.error import TryNext
241 229 from IPython.lib.clipboard import (ClipboardEmpty,
242 230 win32_clipboard_get,
243 231 tkinter_clipboard_get)
244 232
245 233 @undoc
246 234 def win_paste(event):
247 235 try:
248 236 text = win32_clipboard_get()
249 237 except TryNext:
250 238 try:
251 239 text = tkinter_clipboard_get()
252 240 except (TryNext, ClipboardEmpty):
253 241 return
254 242 except ClipboardEmpty:
255 243 return
256 244 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
@@ -1,264 +1,264 b''
1 1 #!/usr/bin/env python3
2 2 # -*- coding: utf-8 -*-
3 3 """Setup script for IPython.
4 4
5 5 Under Posix environments it works like a typical setup.py script.
6 6 Under Windows, the command sdist is not supported, since IPython
7 7 requires utilities which are not available under Windows."""
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (c) 2008-2011, IPython Development Team.
11 11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 14 #
15 15 # Distributed under the terms of the Modified BSD License.
16 16 #
17 17 # The full license is in the file COPYING.rst, distributed with this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from __future__ import print_function
21 21
22 22 import os
23 23 import sys
24 24
25 25 # **Python version check**
26 26 #
27 27 # This check is also made in IPython/__init__, don't forget to update both when
28 28 # changing Python version requirements.
29 29 if sys.version_info < (3, 4):
30 30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
31 31 try:
32 32 import pip
33 33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
34 34 if pip_version < (9, 0, 1) :
35 35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
36 36 'pip {} detected.'.format(pip.__version__)
37 37 else:
38 38 # pip is new enough - it must be something else
39 39 pip_message = ''
40 40 except Exception:
41 41 pass
42 42
43 43
44 44 error = """
45 45 IPython 7.0+ supports Python 3.4 and above.
46 46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
47 47 Python 3.3 was supported up to IPython 6.x.
48 48
49 49 See IPython `README.rst` file for more information:
50 50
51 51 https://github.com/ipython/ipython/blob/master/README.rst
52 52
53 53 Python {py} detected.
54 54 {pip}
55 55 """.format(py=sys.version_info, pip=pip_message )
56 56
57 57 print(error, file=sys.stderr)
58 58 sys.exit(1)
59 59
60 60 # At least we're on the python version we need, move on.
61 61
62 62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
63 63 # update it when the contents of directories change.
64 64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
65 65
66 66 from distutils.core import setup
67 67
68 68 # Our own imports
69 69 from setupbase import target_update
70 70
71 71 from setupbase import (
72 72 setup_args,
73 73 find_packages,
74 74 find_package_data,
75 75 check_package_data_first,
76 76 find_entry_points,
77 77 build_scripts_entrypt,
78 78 find_data_files,
79 79 git_prebuild,
80 80 install_symlinked,
81 81 install_lib_symlink,
82 82 install_scripts_for_symlink,
83 83 unsymlink,
84 84 )
85 85
86 86 isfile = os.path.isfile
87 87 pjoin = os.path.join
88 88
89 89 #-------------------------------------------------------------------------------
90 90 # Handle OS specific things
91 91 #-------------------------------------------------------------------------------
92 92
93 93 if os.name in ('nt','dos'):
94 94 os_name = 'windows'
95 95 else:
96 96 os_name = os.name
97 97
98 98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
99 99 # Sphinx it might work, but let's not turn it on until someone confirms that it
100 100 # actually works.
101 101 if os_name == 'windows' and 'sdist' in sys.argv:
102 102 print('The sdist command is not available under Windows. Exiting.')
103 103 sys.exit(1)
104 104
105 105
106 106 #-------------------------------------------------------------------------------
107 107 # Things related to the IPython documentation
108 108 #-------------------------------------------------------------------------------
109 109
110 110 # update the manuals when building a source dist
111 111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
112 112
113 113 # List of things to be updated. Each entry is a triplet of args for
114 114 # target_update()
115 115 to_update = [
116 116 ('docs/man/ipython.1.gz',
117 117 ['docs/man/ipython.1'],
118 118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
119 119 ]
120 120
121 121
122 122 [ target_update(*t) for t in to_update ]
123 123
124 124 #---------------------------------------------------------------------------
125 125 # Find all the packages, package data, and data_files
126 126 #---------------------------------------------------------------------------
127 127
128 128 packages = find_packages()
129 129 package_data = find_package_data()
130 130
131 131 data_files = find_data_files()
132 132
133 133 setup_args['packages'] = packages
134 134 setup_args['package_data'] = package_data
135 135 setup_args['data_files'] = data_files
136 136
137 137 #---------------------------------------------------------------------------
138 138 # custom distutils commands
139 139 #---------------------------------------------------------------------------
140 140 # imports here, so they are after setuptools import if there was one
141 141 from distutils.command.sdist import sdist
142 142
143 143 setup_args['cmdclass'] = {
144 144 'build_py': \
145 145 check_package_data_first(git_prebuild('IPython')),
146 146 'sdist' : git_prebuild('IPython', sdist),
147 147 'symlink': install_symlinked,
148 148 'install_lib_symlink': install_lib_symlink,
149 149 'install_scripts_sym': install_scripts_for_symlink,
150 150 'unsymlink': unsymlink,
151 151 }
152 152
153 153
154 154 #---------------------------------------------------------------------------
155 155 # Handle scripts, dependencies, and setuptools specific things
156 156 #---------------------------------------------------------------------------
157 157
158 158 # For some commands, use setuptools. Note that we do NOT list install here!
159 159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
160 160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
161 161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
162 162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
163 163 }
164 164
165 165 if len(needs_setuptools.intersection(sys.argv)) > 0:
166 166 import setuptools
167 167
168 168 # This dict is used for passing extra arguments that are setuptools
169 169 # specific to setup
170 170 setuptools_extra_args = {}
171 171
172 172 # setuptools requirements
173 173
174 174 extras_require = dict(
175 175 parallel = ['ipyparallel'],
176 176 qtconsole = ['qtconsole'],
177 177 doc = ['Sphinx>=1.3'],
178 178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
179 179 terminal = [],
180 180 kernel = ['ipykernel'],
181 181 nbformat = ['nbformat'],
182 182 notebook = ['notebook', 'ipywidgets'],
183 183 nbconvert = ['nbconvert'],
184 184 )
185 185
186 186 install_requires = [
187 187 'setuptools>=18.5',
188 188 'jedi>=0.10',
189 189 'decorator',
190 190 'pickleshare',
191 191 'simplegeneric>0.8',
192 192 'traitlets>=4.2',
193 'prompt_toolkit>=1.0.15,<2.0.0',
193 'prompt_toolkit>=2.0.0,<2.1.0',
194 194 'pygments',
195 195 'backcall',
196 196 ]
197 197
198 198 # Platform-specific dependencies:
199 199 # This is the correct way to specify these,
200 200 # but requires pip >= 6. pip < 6 ignores these.
201 201
202 202 extras_require.update({
203 203 ':python_version == "3.4"': ['typing'],
204 204 ':sys_platform != "win32"': ['pexpect'],
205 205 ':sys_platform == "darwin"': ['appnope'],
206 206 ':sys_platform == "win32"': ['colorama'],
207 207 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
208 208 })
209 209 # FIXME: re-specify above platform dependencies for pip < 6
210 210 # These would result in non-portable bdists.
211 211 if not any(arg.startswith('bdist') for arg in sys.argv):
212 212 if sys.platform == 'darwin':
213 213 install_requires.extend(['appnope'])
214 214
215 215 if not sys.platform.startswith('win'):
216 216 install_requires.append('pexpect')
217 217
218 218 # workaround pypa/setuptools#147, where setuptools misspells
219 219 # platform_python_implementation as python_implementation
220 220 if 'setuptools' in sys.modules:
221 221 for key in list(extras_require):
222 222 if 'platform_python_implementation' in key:
223 223 new_key = key.replace('platform_python_implementation', 'python_implementation')
224 224 extras_require[new_key] = extras_require.pop(key)
225 225
226 226 everything = set()
227 227 for key, deps in extras_require.items():
228 228 if ':' not in key:
229 229 everything.update(deps)
230 230 extras_require['all'] = everything
231 231
232 232 if 'setuptools' in sys.modules:
233 233 setuptools_extra_args['python_requires'] = '>=3.4'
234 234 setuptools_extra_args['zip_safe'] = False
235 235 setuptools_extra_args['entry_points'] = {
236 236 'console_scripts': find_entry_points(),
237 237 'pygments.lexers': [
238 238 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
239 239 'ipython = IPython.lib.lexers:IPythonLexer',
240 240 'ipython3 = IPython.lib.lexers:IPython3Lexer',
241 241 ],
242 242 }
243 243 setup_args['extras_require'] = extras_require
244 244 setup_args['install_requires'] = install_requires
245 245
246 246 else:
247 247 # scripts has to be a non-empty list, or install_scripts isn't called
248 248 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
249 249
250 250 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
251 251
252 252 #---------------------------------------------------------------------------
253 253 # Do the actual setup now
254 254 #---------------------------------------------------------------------------
255 255
256 256 setup_args.update(setuptools_extra_args)
257 257
258 258
259 259
260 260 def main():
261 261 setup(**setup_args)
262 262
263 263 if __name__ == '__main__':
264 264 main()
General Comments 0
You need to be logged in to leave comments. Login now