|
6 | 6 | import os |
7 | 7 | import re |
8 | 8 | import signal |
| 9 | +import subprocess |
9 | 10 | import sys |
| 11 | +import tempfile |
10 | 12 | import threading |
11 | 13 | import unicodedata |
12 | 14 |
|
|
15 | 17 | from bpython.config import Struct, loadini, default_config_path |
16 | 18 | from bpython.formatter import BPythonFormatter |
17 | 19 | from pygments import format |
| 20 | +from pygments.lexers import PythonLexer |
| 21 | +from pygments.formatters import TerminalFormatter |
18 | 22 | from bpython import importcompletion |
19 | 23 | from bpython import translations |
20 | 24 | translations.init() |
21 | 25 | from bpython.translations import _ |
22 | 26 | from bpython._py3compat import py3 |
23 | 27 |
|
24 | | -from curtsies import FSArray, fmtstr, FmtStr |
| 28 | +from curtsies import FSArray, fmtstr, FmtStr, Termmode |
25 | 29 | from curtsies.bpythonparse import parse as bpythonparse |
26 | 30 | from curtsies.bpythonparse import func_for_letter, color_for_letter |
27 | 31 | from curtsies import fmtfuncs |
@@ -152,7 +156,8 @@ class Repl(BpythonRepl): |
152 | 156 | ## initialization, cleanup |
153 | 157 | def __init__(self, locals_=None, config=None, |
154 | 158 | request_refresh=lambda: None, get_term_hw=lambda:(50, 10), |
155 | | - get_cursor_vertical_diff=lambda: 0, banner=None, interp=None, interactive=True): |
| 159 | + get_cursor_vertical_diff=lambda: 0, banner=None, interp=None, interactive=True, |
| 160 | + orig_tcattrs=None): |
156 | 161 | """ |
157 | 162 | locals_ is a mapping of locals to pass into the interpreter |
158 | 163 | config is a bpython config.Struct with config attributes |
@@ -223,6 +228,7 @@ def smarter_request_refresh(): |
223 | 228 | self.scroll_offset = 0 # how many times display has been scrolled down |
224 | 229 | # because there wasn't room to display everything |
225 | 230 | self.cursor_offset = 0 # from the left, 0 means first char |
| 231 | + self.orig_tcattrs = orig_tcattrs # useful for shelling out with normal terminal |
226 | 232 |
|
227 | 233 | self.coderunner = CodeRunner(self.interp, self.request_refresh) |
228 | 234 | self.stdout = FakeOutput(self.coderunner, self.send_to_stdout) |
@@ -360,8 +366,17 @@ def process_event(self, e): |
360 | 366 | self.request_paint_to_clear_screen = True |
361 | 367 | elif e in key_dispatch[self.config.last_output_key]: #TODO Not Implemented |
362 | 368 | pass |
363 | | - elif e in key_dispatch[self.config.show_source_key]: #TODO Not Implemented |
364 | | - pass |
| 369 | + elif e in ('\x1f',) + key_dispatch[self.config.show_source_key]: #TODO Not Implemented |
| 370 | + source = self.get_source_of_current_name() |
| 371 | + if source is None: |
| 372 | + self.status_bar.message('whoops!') |
| 373 | + else: |
| 374 | + if self.config.highlight_show_source: |
| 375 | + source = format(PythonLexer().get_tokens(source), TerminalFormatter()) |
| 376 | + with tempfile.NamedTemporaryFile() as tmp: |
| 377 | + tmp.write(source) |
| 378 | + tmp.flush() |
| 379 | + self.focus_on_subprocess(['less', '-R', tmp.name]) |
365 | 380 | elif e in key_dispatch[self.config.suspend_key]: |
366 | 381 | raise SystemExit() |
367 | 382 | elif e in ("",) + key_dispatch[self.config.exit_key]: |
@@ -937,6 +952,27 @@ def getstdout(self): |
937 | 952 | ) if lines else '' |
938 | 953 | return s |
939 | 954 |
|
| 955 | + def focus_on_subprocess(self, args): |
| 956 | + import blessings |
| 957 | + prev_sigwinch_handler = signal.getsignal(signal.SIGWINCH) |
| 958 | + try: |
| 959 | + signal.signal(signal.SIGWINCH, self.orig_sigwinch_handler) |
| 960 | + with Termmode(self.orig_stdin, self.orig_tcattrs): |
| 961 | + terminal = blessings.Terminal(stream=sys.__stdout__) |
| 962 | + with terminal.fullscreen(): |
| 963 | + sys.__stdout__.write(terminal.save) |
| 964 | + sys.__stdout__.write(terminal.move(0, 0)) |
| 965 | + sys.__stdout__.flush() |
| 966 | + p = subprocess.Popen(args, |
| 967 | + stdin=self.orig_stdin, |
| 968 | + stderr=sys.__stderr__, |
| 969 | + stdout=sys.__stdout__) |
| 970 | + p.wait() |
| 971 | + sys.__stdout__.write(terminal.restore) |
| 972 | + sys.__stdout__.flush() |
| 973 | + finally: |
| 974 | + signal.signal(signal.SIGWINCH, prev_sigwinch_handler) |
| 975 | + |
940 | 976 | def is_nop(char): |
941 | 977 | return unicodedata.category(unicode(char)) == 'Cc' |
942 | 978 |
|
|
0 commit comments