From 535ab0aa16c0950adc845f8bec91d9c220a33e61 Mon Sep 17 00:00:00 2001 From: dece Date: Sat, 17 Apr 2021 21:54:11 +0200 Subject: [PATCH] command_line: allow external editor for writing --- bebop/browser/browser.py | 14 +++----------- bebop/command_line.py | 35 ++++++++++++++++++++++++++++++++++- bebop/external.py | 17 +++++++++++++++++ 3 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 bebop/external.py diff --git a/bebop/browser/browser.py b/bebop/browser/browser.py index 5a522b3..3b3a073 100644 --- a/bebop/browser/browser.py +++ b/bebop/browser/browser.py @@ -4,7 +4,6 @@ import curses import curses.ascii import curses.textpad import os -import subprocess import tempfile from math import inf @@ -13,6 +12,7 @@ from bebop.bookmarks import ( ) from bebop.colors import ColorPair, init_colors from bebop.command_line import CommandLine +from bebop.external import open_external_program from bebop.history import History from bebop.links import Links from bebop.mouse import ButtonState @@ -428,15 +428,6 @@ class Browser: save_bookmark(self.current_url, title) self.reset_status() - def open_external_program(self, command): - """Pauses the curses modes to open an external program.""" - curses.nocbreak() - curses.echo() - subprocess.run(command) - curses.noecho() - curses.cbreak() - self.refresh_windows() - def edit_page(self): """Open a text editor to edit the page source. @@ -463,6 +454,7 @@ class Browser: delete_source_after = True command.append(source_filename) - self.open_external_program(command) + open_external_program(command) if delete_source_after: os.unlink(source_filename) + self.refresh_windows() diff --git a/bebop/command_line.py b/bebop/command_line.py index 3992cef..73dd1cc 100644 --- a/bebop/command_line.py +++ b/bebop/command_line.py @@ -3,12 +3,21 @@ import curses import curses.ascii import curses.textpad +import os +import tempfile +from bebop.external import open_external_program from bebop.links import Links class CommandLine: - """Basic and flaky command-line à la Vim, using curses module's Textbox.""" + """Basic and flaky command-line à la Vim, using curses module's Textbox. + + I don't understand how to get proper pad-like behaviour, e.g. to scroll past + the window's right border when writing more content than the width allows. + Therefore I just added the M-e keybind to call an external editor and use + its content as result. + """ def __init__(self, window): self.window = window @@ -78,6 +87,9 @@ class CommandLine: ch = self.window.getch() if ch == -1: raise EscapeCommandInterrupt() + else: # ALT keybinds. + if ch == ord("e"): + self.open_editor(self.gather()) self.window.nodelay(False) return ch @@ -151,6 +163,27 @@ class CommandLine: # Everything else could be a control character and should be processed. return ch + def open_editor(self, existing_content=None): + """Open an external editor and raise termination interrupt.""" + try: + with tempfile.NamedTemporaryFile("w+t", delete=False) as temp_file: + if existing_content: + temp_file.write(existing_content) + temp_filepath = temp_file.name + except OSError: + return + + command = ["vi", temp_filepath] + open_external_program(command) + + try: + with open(temp_filepath, "rt") as temp_file: + content = temp_file.read() + os.unlink(temp_filepath) + except OSError: + return + raise TerminateCommandInterrupt(content) + class EscapeCommandInterrupt(Exception): """Signal that ESC has been pressed during command line.""" diff --git a/bebop/external.py b/bebop/external.py new file mode 100644 index 0000000..5d819fe --- /dev/null +++ b/bebop/external.py @@ -0,0 +1,17 @@ +"""Call external commands.""" + +import curses +import subprocess + + +def open_external_program(command): + """Call command as a subprocess, suspending curses rendering. + + The caller has to refresh whatever windows it manages after calling this + method or garbage may be left on the screen. + """ + curses.nocbreak() + curses.echo() + subprocess.run(command) + curses.noecho() + curses.cbreak()