##// END OF EJS Templates
some more work
Matthias Bussonnier -
Show More
@@ -1,556 +1,580 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import os
3 import os
4 import sys
4 import sys
5 import warnings
5 import warnings
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
9 from IPython.utils import io
10 from IPython.utils.py3compat import input
10 from IPython.utils.py3compat import input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any, validate
15 Any, validate
16 )
16 )
17
17
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.formatted_text import PygmentsTokens
20 from prompt_toolkit.formatted_text import PygmentsTokens
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.output import ColorDepth
23 from prompt_toolkit.output import ColorDepth
24 from prompt_toolkit.patch_stdout import patch_stdout
24 from prompt_toolkit.patch_stdout import patch_stdout
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28
28
29 from pygments.styles import get_style_by_name
29 from pygments.styles import get_style_by_name
30 from pygments.style import Style
30 from pygments.style import Style
31 from pygments.token import Token
31 from pygments.token import Token
32
32
33 from .debugger import TerminalPdb, Pdb
33 from .debugger import TerminalPdb, Pdb
34 from .magics import TerminalMagics
34 from .magics import TerminalMagics
35 from .pt_inputhooks import get_inputhook_name_and_func
35 from .pt_inputhooks import get_inputhook_name_and_func
36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
38 from .shortcuts import create_ipython_shortcuts
38 from .shortcuts import create_ipython_shortcuts
39
39
40 DISPLAY_BANNER_DEPRECATED = object()
40 DISPLAY_BANNER_DEPRECATED = object()
41
41
42
42
43 class _NoStyle(Style): pass
43 class _NoStyle(Style): pass
44
44
45
45
46
46
47 _style_overrides_light_bg = {
47 _style_overrides_light_bg = {
48 Token.Prompt: '#0000ff',
48 Token.Prompt: '#0000ff',
49 Token.PromptNum: '#0000ee bold',
49 Token.PromptNum: '#0000ee bold',
50 Token.OutPrompt: '#cc0000',
50 Token.OutPrompt: '#cc0000',
51 Token.OutPromptNum: '#bb0000 bold',
51 Token.OutPromptNum: '#bb0000 bold',
52 }
52 }
53
53
54 _style_overrides_linux = {
54 _style_overrides_linux = {
55 Token.Prompt: '#00cc00',
55 Token.Prompt: '#00cc00',
56 Token.PromptNum: '#00bb00 bold',
56 Token.PromptNum: '#00bb00 bold',
57 Token.OutPrompt: '#cc0000',
57 Token.OutPrompt: '#cc0000',
58 Token.OutPromptNum: '#bb0000 bold',
58 Token.OutPromptNum: '#bb0000 bold',
59 }
59 }
60
60
61 def get_default_editor():
61 def get_default_editor():
62 try:
62 try:
63 return os.environ['EDITOR']
63 return os.environ['EDITOR']
64 except KeyError:
64 except KeyError:
65 pass
65 pass
66 except UnicodeError:
66 except UnicodeError:
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 "default editor.")
68 "default editor.")
69
69
70 if os.name == 'posix':
70 if os.name == 'posix':
71 return 'vi' # the only one guaranteed to be there!
71 return 'vi' # the only one guaranteed to be there!
72 else:
72 else:
73 return 'notepad' # same in Windows!
73 return 'notepad' # same in Windows!
74
74
75 # conservatively check for tty
75 # conservatively check for tty
76 # overridden streams can result in things like:
76 # overridden streams can result in things like:
77 # - sys.stdin = None
77 # - sys.stdin = None
78 # - no isatty method
78 # - no isatty method
79 for _name in ('stdin', 'stdout', 'stderr'):
79 for _name in ('stdin', 'stdout', 'stderr'):
80 _stream = getattr(sys, _name)
80 _stream = getattr(sys, _name)
81 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
81 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
82 _is_tty = False
82 _is_tty = False
83 break
83 break
84 else:
84 else:
85 _is_tty = True
85 _is_tty = True
86
86
87
87
88 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89
89
90 def black_reformat_handler(text_before_cursor):
91 import black
92 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
93 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
94 formatted_text = formatted_text[:-1]
95 return formatted_text
96
97
90 class TerminalInteractiveShell(InteractiveShell):
98 class TerminalInteractiveShell(InteractiveShell):
91 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
99 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 'to reserve for the completion menu'
100 'to reserve for the completion menu'
93 ).tag(config=True)
101 ).tag(config=True)
94
102
95 pt_app = None
103 pt_app = None
96 debugger_history = None
104 debugger_history = None
97
105
98 simple_prompt = Bool(_use_simple_prompt,
106 simple_prompt = Bool(_use_simple_prompt,
99 help="""Use `raw_input` for the REPL, without completion and prompt colors.
107 help="""Use `raw_input` for the REPL, without completion and prompt colors.
100
108
101 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
109 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
102 IPython own testing machinery, and emacs inferior-shell integration through elpy.
110 IPython own testing machinery, and emacs inferior-shell integration through elpy.
103
111
104 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
112 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
105 environment variable is set, or the current terminal is not a tty."""
113 environment variable is set, or the current terminal is not a tty."""
106 ).tag(config=True)
114 ).tag(config=True)
107
115
108 @property
116 @property
109 def debugger_cls(self):
117 def debugger_cls(self):
110 return Pdb if self.simple_prompt else TerminalPdb
118 return Pdb if self.simple_prompt else TerminalPdb
111
119
112 confirm_exit = Bool(True,
120 confirm_exit = Bool(True,
113 help="""
121 help="""
114 Set to confirm when you try to exit IPython with an EOF (Control-D
122 Set to confirm when you try to exit IPython with an EOF (Control-D
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
123 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 you can force a direct exit without any confirmation.""",
124 you can force a direct exit without any confirmation.""",
117 ).tag(config=True)
125 ).tag(config=True)
118
126
119 editing_mode = Unicode('emacs',
127 editing_mode = Unicode('emacs',
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
128 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
121 ).tag(config=True)
129 ).tag(config=True)
122
130
131 autoformatter = Unicode(None,
132 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
133 allow_none=True
134 ).tag(config=True)
135
123 mouse_support = Bool(False,
136 mouse_support = Bool(False,
124 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
137 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
125 ).tag(config=True)
138 ).tag(config=True)
126
139
127 # We don't load the list of styles for the help string, because loading
140 # We don't load the list of styles for the help string, because loading
128 # Pygments plugins takes time and can cause unexpected errors.
141 # Pygments plugins takes time and can cause unexpected errors.
129 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
142 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
130 help="""The name or class of a Pygments style to use for syntax
143 help="""The name or class of a Pygments style to use for syntax
131 highlighting. To see available styles, run `pygmentize -L styles`."""
144 highlighting. To see available styles, run `pygmentize -L styles`."""
132 ).tag(config=True)
145 ).tag(config=True)
133
146
134 @validate('editing_mode')
147 @validate('editing_mode')
135 def _validate_editing_mode(self, proposal):
148 def _validate_editing_mode(self, proposal):
136 if proposal['value'].lower() == 'vim':
149 if proposal['value'].lower() == 'vim':
137 proposal['value']= 'vi'
150 proposal['value']= 'vi'
138 elif proposal['value'].lower() == 'default':
151 elif proposal['value'].lower() == 'default':
139 proposal['value']= 'emacs'
152 proposal['value']= 'emacs'
140
153
141 if hasattr(EditingMode, proposal['value'].upper()):
154 if hasattr(EditingMode, proposal['value'].upper()):
142 return proposal['value'].lower()
155 return proposal['value'].lower()
143
156
144 return self.editing_mode
157 return self.editing_mode
145
158
146
159
147 @observe('editing_mode')
160 @observe('editing_mode')
148 def _editing_mode(self, change):
161 def _editing_mode(self, change):
149 u_mode = change.new.upper()
162 u_mode = change.new.upper()
150 if self.pt_app:
163 if self.pt_app:
151 self.pt_app.editing_mode = u_mode
164 self.pt_app.editing_mode = u_mode
152
165
166 @observe('autoformatter')
167 def _autoformatter_changed(self, change):
168 formatter = change.new
169 if formatter is None:
170 self.reformat_handler = lambda x:x
171 elif formatter == 'black':
172 self.reformat_handler = black_reformat_handler
173 else:
174 raise ValueError
175
153 @observe('highlighting_style')
176 @observe('highlighting_style')
154 @observe('colors')
177 @observe('colors')
155 def _highlighting_style_changed(self, change):
178 def _highlighting_style_changed(self, change):
156 self.refresh_style()
179 self.refresh_style()
157
180
158 def refresh_style(self):
181 def refresh_style(self):
159 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
182 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
160
183
161
184
162 highlighting_style_overrides = Dict(
185 highlighting_style_overrides = Dict(
163 help="Override highlighting format for specific tokens"
186 help="Override highlighting format for specific tokens"
164 ).tag(config=True)
187 ).tag(config=True)
165
188
166 true_color = Bool(False,
189 true_color = Bool(False,
167 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
190 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
168 "If your terminal supports true color, the following command "
191 "If your terminal supports true color, the following command "
169 "should print 'TRUECOLOR' in orange: "
192 "should print 'TRUECOLOR' in orange: "
170 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
193 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
171 ).tag(config=True)
194 ).tag(config=True)
172
195
173 editor = Unicode(get_default_editor(),
196 editor = Unicode(get_default_editor(),
174 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
197 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
175 ).tag(config=True)
198 ).tag(config=True)
176
199
177 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
200 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
178
201
179 prompts = Instance(Prompts)
202 prompts = Instance(Prompts)
180
203
181 @default('prompts')
204 @default('prompts')
182 def _prompts_default(self):
205 def _prompts_default(self):
183 return self.prompts_class(self)
206 return self.prompts_class(self)
184
207
185 # @observe('prompts')
208 # @observe('prompts')
186 # def _(self, change):
209 # def _(self, change):
187 # self._update_layout()
210 # self._update_layout()
188
211
189 @default('displayhook_class')
212 @default('displayhook_class')
190 def _displayhook_class_default(self):
213 def _displayhook_class_default(self):
191 return RichPromptDisplayHook
214 return RichPromptDisplayHook
192
215
193 term_title = Bool(True,
216 term_title = Bool(True,
194 help="Automatically set the terminal title"
217 help="Automatically set the terminal title"
195 ).tag(config=True)
218 ).tag(config=True)
196
219
197 term_title_format = Unicode("IPython: {cwd}",
220 term_title_format = Unicode("IPython: {cwd}",
198 help="Customize the terminal title format. This is a python format string. " +
221 help="Customize the terminal title format. This is a python format string. " +
199 "Available substitutions are: {cwd}."
222 "Available substitutions are: {cwd}."
200 ).tag(config=True)
223 ).tag(config=True)
201
224
202 display_completions = Enum(('column', 'multicolumn','readlinelike'),
225 display_completions = Enum(('column', 'multicolumn','readlinelike'),
203 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
226 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
204 "'readlinelike'. These options are for `prompt_toolkit`, see "
227 "'readlinelike'. These options are for `prompt_toolkit`, see "
205 "`prompt_toolkit` documentation for more information."
228 "`prompt_toolkit` documentation for more information."
206 ),
229 ),
207 default_value='multicolumn').tag(config=True)
230 default_value='multicolumn').tag(config=True)
208
231
209 highlight_matching_brackets = Bool(True,
232 highlight_matching_brackets = Bool(True,
210 help="Highlight matching brackets.",
233 help="Highlight matching brackets.",
211 ).tag(config=True)
234 ).tag(config=True)
212
235
213 extra_open_editor_shortcuts = Bool(False,
236 extra_open_editor_shortcuts = Bool(False,
214 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
237 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
215 "This is in addition to the F2 binding, which is always enabled."
238 "This is in addition to the F2 binding, which is always enabled."
216 ).tag(config=True)
239 ).tag(config=True)
217
240
218 handle_return = Any(None,
241 handle_return = Any(None,
219 help="Provide an alternative handler to be called when the user presses "
242 help="Provide an alternative handler to be called when the user presses "
220 "Return. This is an advanced option intended for debugging, which "
243 "Return. This is an advanced option intended for debugging, which "
221 "may be changed or removed in later releases."
244 "may be changed or removed in later releases."
222 ).tag(config=True)
245 ).tag(config=True)
223
246
224 enable_history_search = Bool(True,
247 enable_history_search = Bool(True,
225 help="Allows to enable/disable the prompt toolkit history search"
248 help="Allows to enable/disable the prompt toolkit history search"
226 ).tag(config=True)
249 ).tag(config=True)
227
250
228 prompt_includes_vi_mode = Bool(True,
251 prompt_includes_vi_mode = Bool(True,
229 help="Display the current vi mode (when using vi editing mode)."
252 help="Display the current vi mode (when using vi editing mode)."
230 ).tag(config=True)
253 ).tag(config=True)
231
254
232 @observe('term_title')
255 @observe('term_title')
233 def init_term_title(self, change=None):
256 def init_term_title(self, change=None):
234 # Enable or disable the terminal title.
257 # Enable or disable the terminal title.
235 if self.term_title:
258 if self.term_title:
236 toggle_set_term_title(True)
259 toggle_set_term_title(True)
237 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
260 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
238 else:
261 else:
239 toggle_set_term_title(False)
262 toggle_set_term_title(False)
240
263
241 def init_display_formatter(self):
264 def init_display_formatter(self):
242 super(TerminalInteractiveShell, self).init_display_formatter()
265 super(TerminalInteractiveShell, self).init_display_formatter()
243 # terminal only supports plain text
266 # terminal only supports plain text
244 self.display_formatter.active_types = ['text/plain']
267 self.display_formatter.active_types = ['text/plain']
245 # disable `_ipython_display_`
268 # disable `_ipython_display_`
246 self.display_formatter.ipython_display_formatter.enabled = False
269 self.display_formatter.ipython_display_formatter.enabled = False
247
270
248 def init_prompt_toolkit_cli(self):
271 def init_prompt_toolkit_cli(self):
272 self.reformat_handler = lambda x:x
249 if self.simple_prompt:
273 if self.simple_prompt:
250 # Fall back to plain non-interactive output for tests.
274 # Fall back to plain non-interactive output for tests.
251 # This is very limited.
275 # This is very limited.
252 def prompt():
276 def prompt():
253 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
277 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
254 lines = [input(prompt_text)]
278 lines = [input(prompt_text)]
255 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
279 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
256 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
280 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
257 lines.append( input(prompt_continuation) )
281 lines.append( input(prompt_continuation) )
258 return '\n'.join(lines)
282 return '\n'.join(lines)
259 self.prompt_for_code = prompt
283 self.prompt_for_code = prompt
260 return
284 return
261
285
262 # Set up keyboard shortcuts
286 # Set up keyboard shortcuts
263 key_bindings = create_ipython_shortcuts(self)
287 key_bindings = create_ipython_shortcuts(self)
264
288
265 # Pre-populate history from IPython's history database
289 # Pre-populate history from IPython's history database
266 history = InMemoryHistory()
290 history = InMemoryHistory()
267 last_cell = u""
291 last_cell = u""
268 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
292 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
269 include_latest=True):
293 include_latest=True):
270 # Ignore blank lines and consecutive duplicates
294 # Ignore blank lines and consecutive duplicates
271 cell = cell.rstrip()
295 cell = cell.rstrip()
272 if cell and (cell != last_cell):
296 if cell and (cell != last_cell):
273 history.append_string(cell)
297 history.append_string(cell)
274 last_cell = cell
298 last_cell = cell
275
299
276 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
300 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
277 self.style = DynamicStyle(lambda: self._style)
301 self.style = DynamicStyle(lambda: self._style)
278
302
279 editing_mode = getattr(EditingMode, self.editing_mode.upper())
303 editing_mode = getattr(EditingMode, self.editing_mode.upper())
280
304
281 self.pt_app = PromptSession(
305 self.pt_app = PromptSession(
282 editing_mode=editing_mode,
306 editing_mode=editing_mode,
283 key_bindings=key_bindings,
307 key_bindings=key_bindings,
284 history=history,
308 history=history,
285 completer=IPythonPTCompleter(shell=self),
309 completer=IPythonPTCompleter(shell=self),
286 enable_history_search = self.enable_history_search,
310 enable_history_search = self.enable_history_search,
287 style=self.style,
311 style=self.style,
288 include_default_pygments_style=False,
312 include_default_pygments_style=False,
289 mouse_support=self.mouse_support,
313 mouse_support=self.mouse_support,
290 enable_open_in_editor=self.extra_open_editor_shortcuts,
314 enable_open_in_editor=self.extra_open_editor_shortcuts,
291 color_depth=self.color_depth,
315 color_depth=self.color_depth,
292 **self._extra_prompt_options())
316 **self._extra_prompt_options())
293
317
294 def _make_style_from_name_or_cls(self, name_or_cls):
318 def _make_style_from_name_or_cls(self, name_or_cls):
295 """
319 """
296 Small wrapper that make an IPython compatible style from a style name
320 Small wrapper that make an IPython compatible style from a style name
297
321
298 We need that to add style for prompt ... etc.
322 We need that to add style for prompt ... etc.
299 """
323 """
300 style_overrides = {}
324 style_overrides = {}
301 if name_or_cls == 'legacy':
325 if name_or_cls == 'legacy':
302 legacy = self.colors.lower()
326 legacy = self.colors.lower()
303 if legacy == 'linux':
327 if legacy == 'linux':
304 style_cls = get_style_by_name('monokai')
328 style_cls = get_style_by_name('monokai')
305 style_overrides = _style_overrides_linux
329 style_overrides = _style_overrides_linux
306 elif legacy == 'lightbg':
330 elif legacy == 'lightbg':
307 style_overrides = _style_overrides_light_bg
331 style_overrides = _style_overrides_light_bg
308 style_cls = get_style_by_name('pastie')
332 style_cls = get_style_by_name('pastie')
309 elif legacy == 'neutral':
333 elif legacy == 'neutral':
310 # The default theme needs to be visible on both a dark background
334 # The default theme needs to be visible on both a dark background
311 # and a light background, because we can't tell what the terminal
335 # and a light background, because we can't tell what the terminal
312 # looks like. These tweaks to the default theme help with that.
336 # looks like. These tweaks to the default theme help with that.
313 style_cls = get_style_by_name('default')
337 style_cls = get_style_by_name('default')
314 style_overrides.update({
338 style_overrides.update({
315 Token.Number: '#007700',
339 Token.Number: '#007700',
316 Token.Operator: 'noinherit',
340 Token.Operator: 'noinherit',
317 Token.String: '#BB6622',
341 Token.String: '#BB6622',
318 Token.Name.Function: '#2080D0',
342 Token.Name.Function: '#2080D0',
319 Token.Name.Class: 'bold #2080D0',
343 Token.Name.Class: 'bold #2080D0',
320 Token.Name.Namespace: 'bold #2080D0',
344 Token.Name.Namespace: 'bold #2080D0',
321 Token.Prompt: '#009900',
345 Token.Prompt: '#009900',
322 Token.PromptNum: '#ansibrightgreen bold',
346 Token.PromptNum: '#ansibrightgreen bold',
323 Token.OutPrompt: '#990000',
347 Token.OutPrompt: '#990000',
324 Token.OutPromptNum: '#ansibrightred bold',
348 Token.OutPromptNum: '#ansibrightred bold',
325 })
349 })
326
350
327 # Hack: Due to limited color support on the Windows console
351 # Hack: Due to limited color support on the Windows console
328 # the prompt colors will be wrong without this
352 # the prompt colors will be wrong without this
329 if os.name == 'nt':
353 if os.name == 'nt':
330 style_overrides.update({
354 style_overrides.update({
331 Token.Prompt: '#ansidarkgreen',
355 Token.Prompt: '#ansidarkgreen',
332 Token.PromptNum: '#ansigreen bold',
356 Token.PromptNum: '#ansigreen bold',
333 Token.OutPrompt: '#ansidarkred',
357 Token.OutPrompt: '#ansidarkred',
334 Token.OutPromptNum: '#ansired bold',
358 Token.OutPromptNum: '#ansired bold',
335 })
359 })
336 elif legacy =='nocolor':
360 elif legacy =='nocolor':
337 style_cls=_NoStyle
361 style_cls=_NoStyle
338 style_overrides = {}
362 style_overrides = {}
339 else :
363 else :
340 raise ValueError('Got unknown colors: ', legacy)
364 raise ValueError('Got unknown colors: ', legacy)
341 else :
365 else :
342 if isinstance(name_or_cls, str):
366 if isinstance(name_or_cls, str):
343 style_cls = get_style_by_name(name_or_cls)
367 style_cls = get_style_by_name(name_or_cls)
344 else:
368 else:
345 style_cls = name_or_cls
369 style_cls = name_or_cls
346 style_overrides = {
370 style_overrides = {
347 Token.Prompt: '#009900',
371 Token.Prompt: '#009900',
348 Token.PromptNum: '#ansibrightgreen bold',
372 Token.PromptNum: '#ansibrightgreen bold',
349 Token.OutPrompt: '#990000',
373 Token.OutPrompt: '#990000',
350 Token.OutPromptNum: '#ansibrightred bold',
374 Token.OutPromptNum: '#ansibrightred bold',
351 }
375 }
352 style_overrides.update(self.highlighting_style_overrides)
376 style_overrides.update(self.highlighting_style_overrides)
353 style = merge_styles([
377 style = merge_styles([
354 style_from_pygments_cls(style_cls),
378 style_from_pygments_cls(style_cls),
355 style_from_pygments_dict(style_overrides),
379 style_from_pygments_dict(style_overrides),
356 ])
380 ])
357
381
358 return style
382 return style
359
383
360 @property
384 @property
361 def pt_complete_style(self):
385 def pt_complete_style(self):
362 return {
386 return {
363 'multicolumn': CompleteStyle.MULTI_COLUMN,
387 'multicolumn': CompleteStyle.MULTI_COLUMN,
364 'column': CompleteStyle.COLUMN,
388 'column': CompleteStyle.COLUMN,
365 'readlinelike': CompleteStyle.READLINE_LIKE,
389 'readlinelike': CompleteStyle.READLINE_LIKE,
366 }[self.display_completions]
390 }[self.display_completions]
367
391
368 @property
392 @property
369 def color_depth(self):
393 def color_depth(self):
370 return (ColorDepth.TRUE_COLOR if self.true_color else None)
394 return (ColorDepth.TRUE_COLOR if self.true_color else None)
371
395
372 def _extra_prompt_options(self):
396 def _extra_prompt_options(self):
373 """
397 """
374 Return the current layout option for the current Terminal InteractiveShell
398 Return the current layout option for the current Terminal InteractiveShell
375 """
399 """
376 def get_message():
400 def get_message():
377 return PygmentsTokens(self.prompts.in_prompt_tokens())
401 return PygmentsTokens(self.prompts.in_prompt_tokens())
378
402
379 return {
403 return {
380 'complete_in_thread': False,
404 'complete_in_thread': False,
381 'lexer':IPythonPTLexer(),
405 'lexer':IPythonPTLexer(),
382 'reserve_space_for_menu':self.space_for_menu,
406 'reserve_space_for_menu':self.space_for_menu,
383 'message': get_message,
407 'message': get_message,
384 'prompt_continuation': (
408 'prompt_continuation': (
385 lambda width, lineno, is_soft_wrap:
409 lambda width, lineno, is_soft_wrap:
386 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
410 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
387 'multiline': True,
411 'multiline': True,
388 'complete_style': self.pt_complete_style,
412 'complete_style': self.pt_complete_style,
389
413
390 # Highlight matching brackets, but only when this setting is
414 # Highlight matching brackets, but only when this setting is
391 # enabled, and only when the DEFAULT_BUFFER has the focus.
415 # enabled, and only when the DEFAULT_BUFFER has the focus.
392 'input_processors': [ConditionalProcessor(
416 'input_processors': [ConditionalProcessor(
393 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
417 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
394 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
418 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
395 Condition(lambda: self.highlight_matching_brackets))],
419 Condition(lambda: self.highlight_matching_brackets))],
396 'inputhook': self.inputhook,
420 'inputhook': self.inputhook,
397 }
421 }
398
422
399 def prompt_for_code(self):
423 def prompt_for_code(self):
400 if self.rl_next_input:
424 if self.rl_next_input:
401 default = self.rl_next_input
425 default = self.rl_next_input
402 self.rl_next_input = None
426 self.rl_next_input = None
403 else:
427 else:
404 default = ''
428 default = ''
405
429
406 with patch_stdout(raw=True):
430 with patch_stdout(raw=True):
407 text = self.pt_app.prompt(
431 text = self.pt_app.prompt(
408 default=default,
432 default=default,
409 # pre_run=self.pre_prompt,# reset_current_buffer=True,
433 # pre_run=self.pre_prompt,# reset_current_buffer=True,
410 **self._extra_prompt_options())
434 **self._extra_prompt_options())
411 return text
435 return text
412
436
413 def enable_win_unicode_console(self):
437 def enable_win_unicode_console(self):
414 if sys.version_info >= (3, 6):
438 if sys.version_info >= (3, 6):
415 # Since PEP 528, Python uses the unicode APIs for the Windows
439 # Since PEP 528, Python uses the unicode APIs for the Windows
416 # console by default, so WUC shouldn't be needed.
440 # console by default, so WUC shouldn't be needed.
417 return
441 return
418
442
419 import win_unicode_console
443 import win_unicode_console
420 win_unicode_console.enable()
444 win_unicode_console.enable()
421
445
422 def init_io(self):
446 def init_io(self):
423 if sys.platform not in {'win32', 'cli'}:
447 if sys.platform not in {'win32', 'cli'}:
424 return
448 return
425
449
426 self.enable_win_unicode_console()
450 self.enable_win_unicode_console()
427
451
428 import colorama
452 import colorama
429 colorama.init()
453 colorama.init()
430
454
431 # For some reason we make these wrappers around stdout/stderr.
455 # For some reason we make these wrappers around stdout/stderr.
432 # For now, we need to reset them so all output gets coloured.
456 # For now, we need to reset them so all output gets coloured.
433 # https://github.com/ipython/ipython/issues/8669
457 # https://github.com/ipython/ipython/issues/8669
434 # io.std* are deprecated, but don't show our own deprecation warnings
458 # io.std* are deprecated, but don't show our own deprecation warnings
435 # during initialization of the deprecated API.
459 # during initialization of the deprecated API.
436 with warnings.catch_warnings():
460 with warnings.catch_warnings():
437 warnings.simplefilter('ignore', DeprecationWarning)
461 warnings.simplefilter('ignore', DeprecationWarning)
438 io.stdout = io.IOStream(sys.stdout)
462 io.stdout = io.IOStream(sys.stdout)
439 io.stderr = io.IOStream(sys.stderr)
463 io.stderr = io.IOStream(sys.stderr)
440
464
441 def init_magics(self):
465 def init_magics(self):
442 super(TerminalInteractiveShell, self).init_magics()
466 super(TerminalInteractiveShell, self).init_magics()
443 self.register_magics(TerminalMagics)
467 self.register_magics(TerminalMagics)
444
468
445 def init_alias(self):
469 def init_alias(self):
446 # The parent class defines aliases that can be safely used with any
470 # The parent class defines aliases that can be safely used with any
447 # frontend.
471 # frontend.
448 super(TerminalInteractiveShell, self).init_alias()
472 super(TerminalInteractiveShell, self).init_alias()
449
473
450 # Now define aliases that only make sense on the terminal, because they
474 # Now define aliases that only make sense on the terminal, because they
451 # need direct access to the console in a way that we can't emulate in
475 # need direct access to the console in a way that we can't emulate in
452 # GUI or web frontend
476 # GUI or web frontend
453 if os.name == 'posix':
477 if os.name == 'posix':
454 for cmd in ('clear', 'more', 'less', 'man'):
478 for cmd in ('clear', 'more', 'less', 'man'):
455 self.alias_manager.soft_define_alias(cmd, cmd)
479 self.alias_manager.soft_define_alias(cmd, cmd)
456
480
457
481
458 def __init__(self, *args, **kwargs):
482 def __init__(self, *args, **kwargs):
459 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
483 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
460 self.init_prompt_toolkit_cli()
484 self.init_prompt_toolkit_cli()
461 self.init_term_title()
485 self.init_term_title()
462 self.keep_running = True
486 self.keep_running = True
463
487
464 self.debugger_history = InMemoryHistory()
488 self.debugger_history = InMemoryHistory()
465
489
466 def ask_exit(self):
490 def ask_exit(self):
467 self.keep_running = False
491 self.keep_running = False
468
492
469 rl_next_input = None
493 rl_next_input = None
470
494
471 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
495 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
472
496
473 if display_banner is not DISPLAY_BANNER_DEPRECATED:
497 if display_banner is not DISPLAY_BANNER_DEPRECATED:
474 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
498 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
475
499
476 self.keep_running = True
500 self.keep_running = True
477 while self.keep_running:
501 while self.keep_running:
478 print(self.separate_in, end='')
502 print(self.separate_in, end='')
479
503
480 try:
504 try:
481 code = self.prompt_for_code()
505 code = self.prompt_for_code()
482 except EOFError:
506 except EOFError:
483 if (not self.confirm_exit) \
507 if (not self.confirm_exit) \
484 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
508 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
485 self.ask_exit()
509 self.ask_exit()
486
510
487 else:
511 else:
488 if code:
512 if code:
489 self.run_cell(code, store_history=True)
513 self.run_cell(code, store_history=True)
490
514
491 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
515 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
492 # An extra layer of protection in case someone mashing Ctrl-C breaks
516 # An extra layer of protection in case someone mashing Ctrl-C breaks
493 # out of our internal code.
517 # out of our internal code.
494 if display_banner is not DISPLAY_BANNER_DEPRECATED:
518 if display_banner is not DISPLAY_BANNER_DEPRECATED:
495 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
519 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
496 while True:
520 while True:
497 try:
521 try:
498 self.interact()
522 self.interact()
499 break
523 break
500 except KeyboardInterrupt as e:
524 except KeyboardInterrupt as e:
501 print("\n%s escaped interact()\n" % type(e).__name__)
525 print("\n%s escaped interact()\n" % type(e).__name__)
502 finally:
526 finally:
503 # An interrupt during the eventloop will mess up the
527 # An interrupt during the eventloop will mess up the
504 # internal state of the prompt_toolkit library.
528 # internal state of the prompt_toolkit library.
505 # Stopping the eventloop fixes this, see
529 # Stopping the eventloop fixes this, see
506 # https://github.com/ipython/ipython/pull/9867
530 # https://github.com/ipython/ipython/pull/9867
507 if hasattr(self, '_eventloop'):
531 if hasattr(self, '_eventloop'):
508 self._eventloop.stop()
532 self._eventloop.stop()
509
533
510 _inputhook = None
534 _inputhook = None
511 def inputhook(self, context):
535 def inputhook(self, context):
512 if self._inputhook is not None:
536 if self._inputhook is not None:
513 self._inputhook(context)
537 self._inputhook(context)
514
538
515 active_eventloop = None
539 active_eventloop = None
516 def enable_gui(self, gui=None):
540 def enable_gui(self, gui=None):
517 if gui:
541 if gui:
518 self.active_eventloop, self._inputhook =\
542 self.active_eventloop, self._inputhook =\
519 get_inputhook_name_and_func(gui)
543 get_inputhook_name_and_func(gui)
520 else:
544 else:
521 self.active_eventloop = self._inputhook = None
545 self.active_eventloop = self._inputhook = None
522
546
523 # Run !system commands directly, not through pipes, so terminal programs
547 # Run !system commands directly, not through pipes, so terminal programs
524 # work correctly.
548 # work correctly.
525 system = InteractiveShell.system_raw
549 system = InteractiveShell.system_raw
526
550
527 def auto_rewrite_input(self, cmd):
551 def auto_rewrite_input(self, cmd):
528 """Overridden from the parent class to use fancy rewriting prompt"""
552 """Overridden from the parent class to use fancy rewriting prompt"""
529 if not self.show_rewritten_input:
553 if not self.show_rewritten_input:
530 return
554 return
531
555
532 tokens = self.prompts.rewrite_prompt_tokens()
556 tokens = self.prompts.rewrite_prompt_tokens()
533 if self.pt_app:
557 if self.pt_app:
534 print_formatted_text(PygmentsTokens(tokens), end='',
558 print_formatted_text(PygmentsTokens(tokens), end='',
535 style=self.pt_app.app.style)
559 style=self.pt_app.app.style)
536 print(cmd)
560 print(cmd)
537 else:
561 else:
538 prompt = ''.join(s for t, s in tokens)
562 prompt = ''.join(s for t, s in tokens)
539 print(prompt, cmd, sep='')
563 print(prompt, cmd, sep='')
540
564
541 _prompts_before = None
565 _prompts_before = None
542 def switch_doctest_mode(self, mode):
566 def switch_doctest_mode(self, mode):
543 """Switch prompts to classic for %doctest_mode"""
567 """Switch prompts to classic for %doctest_mode"""
544 if mode:
568 if mode:
545 self._prompts_before = self.prompts
569 self._prompts_before = self.prompts
546 self.prompts = ClassicPrompts(self)
570 self.prompts = ClassicPrompts(self)
547 elif self._prompts_before:
571 elif self._prompts_before:
548 self.prompts = self._prompts_before
572 self.prompts = self._prompts_before
549 self._prompts_before = None
573 self._prompts_before = None
550 # self._update_layout()
574 # self._update_layout()
551
575
552
576
553 InteractiveShellABC.register(TerminalInteractiveShell)
577 InteractiveShellABC.register(TerminalInteractiveShell)
554
578
555 if __name__ == '__main__':
579 if __name__ == '__main__':
556 TerminalInteractiveShell.instance().interact()
580 TerminalInteractiveShell.instance().interact()
@@ -1,264 +1,273 b''
1 """
1 """
2 Module to define and register Terminal IPython shortcuts with
2 Module to define and register Terminal IPython shortcuts with
3 :mod:`prompt_toolkit`
3 :mod:`prompt_toolkit`
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import warnings
9 import warnings
10 import signal
10 import signal
11 import sys
11 import sys
12 from typing import Callable
12 from typing import Callable
13
13
14
14
15 from prompt_toolkit.application.current import get_app
15 from prompt_toolkit.application.current import get_app
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
17 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
17 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
18 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
18 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
19 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
19 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
20 from prompt_toolkit.key_binding import KeyBindings
20 from prompt_toolkit.key_binding import KeyBindings
21
21
22 from IPython.utils.decorators import undoc
22 from IPython.utils.decorators import undoc
23
23
24 @undoc
24 @undoc
25 @Condition
25 @Condition
26 def cursor_in_leading_ws():
26 def cursor_in_leading_ws():
27 before = get_app().current_buffer.document.current_line_before_cursor
27 before = get_app().current_buffer.document.current_line_before_cursor
28 return (not before) or before.isspace()
28 return (not before) or before.isspace()
29
29
30
30
31 def create_ipython_shortcuts(shell):
31 def create_ipython_shortcuts(shell):
32 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
33
33
34 kb = KeyBindings()
34 kb = KeyBindings()
35 insert_mode = vi_insert_mode | emacs_insert_mode
35 insert_mode = vi_insert_mode | emacs_insert_mode
36
36
37 if getattr(shell, 'handle_return', None):
37 if getattr(shell, 'handle_return', None):
38 return_handler = shell.handle_return(shell)
38 return_handler = shell.handle_return(shell)
39 else:
39 else:
40 return_handler = newline_or_execute_outer(shell)
40 return_handler = newline_or_execute_outer(shell)
41
41
42 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
42 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
43 & ~has_selection
43 & ~has_selection
44 & insert_mode
44 & insert_mode
45 ))(return_handler)
45 ))(return_handler)
46
46
47 def reformat_and_execute(event):
48 reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
49 event.current_buffer.validate_and_handle()
50
51 kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
52 & ~has_selection
53 & insert_mode
54 ))(reformat_and_execute)
55
47 kb.add('c-\\')(force_exit)
56 kb.add('c-\\')(force_exit)
48
57
49 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
58 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
50 )(previous_history_or_previous_completion)
59 )(previous_history_or_previous_completion)
51
60
52 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
61 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
53 )(next_history_or_next_completion)
62 )(next_history_or_next_completion)
54
63
55 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
64 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
56 )(dismiss_completion)
65 )(dismiss_completion)
57
66
58 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
67 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
59
68
60 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
69 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
61
70
62 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
71 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
63 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
72 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
64
73
65 # Ctrl+I == Tab
74 # Ctrl+I == Tab
66 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
75 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
67 & ~has_selection
76 & ~has_selection
68 & insert_mode
77 & insert_mode
69 & cursor_in_leading_ws
78 & cursor_in_leading_ws
70 ))(indent_buffer)
79 ))(indent_buffer)
71 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
80 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
72 )(newline_autoindent_outer(shell.input_transformer_manager))
81 )(newline_autoindent_outer(shell.input_transformer_manager))
73
82
74 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
83 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
75
84
76 if shell.display_completions == 'readlinelike':
85 if shell.display_completions == 'readlinelike':
77 kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
86 kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
78 & ~has_selection
87 & ~has_selection
79 & insert_mode
88 & insert_mode
80 & ~cursor_in_leading_ws
89 & ~cursor_in_leading_ws
81 ))(display_completions_like_readline)
90 ))(display_completions_like_readline)
82
91
83 if sys.platform == 'win32':
92 if sys.platform == 'win32':
84 kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
93 kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
85
94
86 return kb
95 return kb
87
96
88
97
98 def reformat_text_before_cursor(buffer, document, shell):
99 text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
100 try:
101 formatted_text = shell.reformat_handler(text)
102 buffer.insert_text(formatted_text)
103 except Exception as e:
104 buffer.insert_text(text)
105
106
89 def newline_or_execute_outer(shell):
107 def newline_or_execute_outer(shell):
90
108
91 import black
92 def newline_or_execute(event):
109 def newline_or_execute(event):
93 """When the user presses return, insert a newline or execute the code."""
110 """When the user presses return, insert a newline or execute the code."""
94 b = event.current_buffer
111 b = event.current_buffer
95 d = b.document
112 d = b.document
96 def try_reformat():
97 try:
98 tbc = b.delete_before_cursor(len(d.text[:d.cursor_position]))
99 fmt= black.format_str(tbc, mode=black.FileMode())
100 if not tbc.endswith('\n') and fmt.endswith('\n'):
101 fmt = fmt[:-1]
102 b.insert_text(fmt)
103 #print(f'no eexc |{tbc[-1]}|,|{d.text[-1]}|, |{fmt[-3:-1]}|')
104 except Exception as e:
105 b.insert_text(tbc)
106
107
113
108 if b.complete_state:
114 if b.complete_state:
109 cc = b.complete_state.current_completion
115 cc = b.complete_state.current_completion
110 if cc:
116 if cc:
111 b.apply_completion(cc)
117 b.apply_completion(cc)
112 else:
118 else:
113 b.cancel_completion()
119 b.cancel_completion()
114 return
120 return
115
121
116 # If there's only one line, treat it as if the cursor is at the end.
122 # If there's only one line, treat it as if the cursor is at the end.
117 # See https://github.com/ipython/ipython/issues/10425
123 # See https://github.com/ipython/ipython/issues/10425
118 if d.line_count == 1:
124 if d.line_count == 1:
119 check_text = d.text
125 check_text = d.text
120 else:
126 else:
121 check_text = d.text[:d.cursor_position]
127 check_text = d.text[:d.cursor_position]
122 status, indent = shell.check_complete(check_text)
128 status, indent = shell.check_complete(check_text)
123
129
130 # if all we have after the cursor is whitespace: reformat current text
131 # before cursor
132 if d.text[d.cursor_position:].isspace():
133 reformat_text_before_cursor(b, d, shell)
124 if not (d.on_last_line or
134 if not (d.on_last_line or
125 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
135 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
126 ):
136 ):
127 if shell.autoindent:
137 if shell.autoindent:
128 b.insert_text('\n' + indent)
138 b.insert_text('\n' + indent)
129 else:
139 else:
130 b.insert_text('\n')
140 b.insert_text('\n')
131 return
141 return
132
142
133 if (status != 'incomplete') and b.accept_handler:
143 if (status != 'incomplete') and b.accept_handler:
134 try_reformat()
144 reformat_text_before_cursor(b, d, shell)
135 b.validate_and_handle()
145 b.validate_and_handle()
136 else:
146 else:
137 if shell.autoindent:
147 if shell.autoindent:
138 try_reformat()
139 b.insert_text('\n' + indent)
148 b.insert_text('\n' + indent)
140 else:
149 else:
141 b.insert_text('\n')
150 b.insert_text('\n')
142 return newline_or_execute
151 return newline_or_execute
143
152
144
153
145 def previous_history_or_previous_completion(event):
154 def previous_history_or_previous_completion(event):
146 """
155 """
147 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
156 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
148
157
149 If completer is open this still select previous completion.
158 If completer is open this still select previous completion.
150 """
159 """
151 event.current_buffer.auto_up()
160 event.current_buffer.auto_up()
152
161
153
162
154 def next_history_or_next_completion(event):
163 def next_history_or_next_completion(event):
155 """
164 """
156 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
165 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
157
166
158 If completer is open this still select next completion.
167 If completer is open this still select next completion.
159 """
168 """
160 event.current_buffer.auto_down()
169 event.current_buffer.auto_down()
161
170
162
171
163 def dismiss_completion(event):
172 def dismiss_completion(event):
164 b = event.current_buffer
173 b = event.current_buffer
165 if b.complete_state:
174 if b.complete_state:
166 b.cancel_completion()
175 b.cancel_completion()
167
176
168
177
169 def reset_buffer(event):
178 def reset_buffer(event):
170 b = event.current_buffer
179 b = event.current_buffer
171 if b.complete_state:
180 if b.complete_state:
172 b.cancel_completion()
181 b.cancel_completion()
173 else:
182 else:
174 b.reset()
183 b.reset()
175
184
176
185
177 def reset_search_buffer(event):
186 def reset_search_buffer(event):
178 if event.current_buffer.document.text:
187 if event.current_buffer.document.text:
179 event.current_buffer.reset()
188 event.current_buffer.reset()
180 else:
189 else:
181 event.app.layout.focus(DEFAULT_BUFFER)
190 event.app.layout.focus(DEFAULT_BUFFER)
182
191
183 def suspend_to_bg(event):
192 def suspend_to_bg(event):
184 event.app.suspend_to_background()
193 event.app.suspend_to_background()
185
194
186 def force_exit(event):
195 def force_exit(event):
187 """
196 """
188 Force exit (with a non-zero return value)
197 Force exit (with a non-zero return value)
189 """
198 """
190 sys.exit("Quit")
199 sys.exit("Quit")
191
200
192 def indent_buffer(event):
201 def indent_buffer(event):
193 event.current_buffer.insert_text(' ' * 4)
202 event.current_buffer.insert_text(' ' * 4)
194
203
195 @undoc
204 @undoc
196 def newline_with_copy_margin(event):
205 def newline_with_copy_margin(event):
197 """
206 """
198 DEPRECATED since IPython 6.0
207 DEPRECATED since IPython 6.0
199
208
200 See :any:`newline_autoindent_outer` for a replacement.
209 See :any:`newline_autoindent_outer` for a replacement.
201
210
202 Preserve margin and cursor position when using
211 Preserve margin and cursor position when using
203 Control-O to insert a newline in EMACS mode
212 Control-O to insert a newline in EMACS mode
204 """
213 """
205 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
214 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
206 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
215 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
207 DeprecationWarning, stacklevel=2)
216 DeprecationWarning, stacklevel=2)
208
217
209 b = event.current_buffer
218 b = event.current_buffer
210 cursor_start_pos = b.document.cursor_position_col
219 cursor_start_pos = b.document.cursor_position_col
211 b.newline(copy_margin=True)
220 b.newline(copy_margin=True)
212 b.cursor_up(count=1)
221 b.cursor_up(count=1)
213 cursor_end_pos = b.document.cursor_position_col
222 cursor_end_pos = b.document.cursor_position_col
214 if cursor_start_pos != cursor_end_pos:
223 if cursor_start_pos != cursor_end_pos:
215 pos_diff = cursor_start_pos - cursor_end_pos
224 pos_diff = cursor_start_pos - cursor_end_pos
216 b.cursor_right(count=pos_diff)
225 b.cursor_right(count=pos_diff)
217
226
218 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
227 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
219 """
228 """
220 Return a function suitable for inserting a indented newline after the cursor.
229 Return a function suitable for inserting a indented newline after the cursor.
221
230
222 Fancier version of deprecated ``newline_with_copy_margin`` which should
231 Fancier version of deprecated ``newline_with_copy_margin`` which should
223 compute the correct indentation of the inserted line. That is to say, indent
232 compute the correct indentation of the inserted line. That is to say, indent
224 by 4 extra space after a function definition, class definition, context
233 by 4 extra space after a function definition, class definition, context
225 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
234 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
226 """
235 """
227
236
228 def newline_autoindent(event):
237 def newline_autoindent(event):
229 """insert a newline after the cursor indented appropriately."""
238 """insert a newline after the cursor indented appropriately."""
230 b = event.current_buffer
239 b = event.current_buffer
231 d = b.document
240 d = b.document
232
241
233 if b.complete_state:
242 if b.complete_state:
234 b.cancel_completion()
243 b.cancel_completion()
235 text = d.text[:d.cursor_position] + '\n'
244 text = d.text[:d.cursor_position] + '\n'
236 _, indent = inputsplitter.check_complete(text)
245 _, indent = inputsplitter.check_complete(text)
237 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
246 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
238
247
239 return newline_autoindent
248 return newline_autoindent
240
249
241
250
242 def open_input_in_editor(event):
251 def open_input_in_editor(event):
243 event.app.current_buffer.tempfile_suffix = ".py"
252 event.app.current_buffer.tempfile_suffix = ".py"
244 event.app.current_buffer.open_in_editor()
253 event.app.current_buffer.open_in_editor()
245
254
246
255
247 if sys.platform == 'win32':
256 if sys.platform == 'win32':
248 from IPython.core.error import TryNext
257 from IPython.core.error import TryNext
249 from IPython.lib.clipboard import (ClipboardEmpty,
258 from IPython.lib.clipboard import (ClipboardEmpty,
250 win32_clipboard_get,
259 win32_clipboard_get,
251 tkinter_clipboard_get)
260 tkinter_clipboard_get)
252
261
253 @undoc
262 @undoc
254 def win_paste(event):
263 def win_paste(event):
255 try:
264 try:
256 text = win32_clipboard_get()
265 text = win32_clipboard_get()
257 except TryNext:
266 except TryNext:
258 try:
267 try:
259 text = tkinter_clipboard_get()
268 text = tkinter_clipboard_get()
260 except (TryNext, ClipboardEmpty):
269 except (TryNext, ClipboardEmpty):
261 return
270 return
262 except ClipboardEmpty:
271 except ClipboardEmpty:
263 return
272 return
264 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
273 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
General Comments 0
You need to be logged in to leave comments. Login now