diff --git a/rtv/rtv.cfg b/rtv/rtv.cfg index 1aa0a62..034d4a0 100644 --- a/rtv/rtv.cfg +++ b/rtv/rtv.cfg @@ -104,10 +104,11 @@ INBOX = i REFRESH = r, ; Submission page -SUBMISSION_TOGGLE_COMMENT = l, 0x20, +SUBMISSION_TOGGLE_COMMENT = 0x20 SUBMISSION_OPEN_IN_BROWSER = o, , SUBMISSION_POST = c SUBMISSION_EXIT = h, +SUBMISSION_OPEN_IN_PAGER = l, ; Subreddit page SUBREDDIT_SEARCH = f diff --git a/rtv/submission.py b/rtv/submission.py index 5e18abd..091ddf5 100644 --- a/rtv/submission.py +++ b/rtv/submission.py @@ -69,6 +69,19 @@ class SubmissionPage(Page): else: self.term.flash() + @SubmissionController.register(Command('SUBMISSION_OPEN_IN_PAGER')) + def open_pager(self): + "Open the selected item with the system's pager" + data = self.content.get(self.nav.absolute_index) + if data['type'] == 'Submission': + text = '\n\n'.join((data['permalink'], data['text'])) + self.term.open_pager(text) + elif data['type'] == 'Comment': + text = '\n\n'.join((data['permalink'], data['body'])) + self.term.open_pager(text) + else: + self.term.flash() + @SubmissionController.register(Command('SUBMISSION_POST')) @logged_in def add_comment(self): diff --git a/rtv/terminal.py b/rtv/terminal.py index 1b70d4a..bd62cff 100644 --- a/rtv/terminal.py +++ b/rtv/terminal.py @@ -35,6 +35,7 @@ class Terminal(object): # ASCII code ESCAPE = 27 RETURN = 10 + SPACE = 32 def __init__(self, stdscr, ascii=False): @@ -356,6 +357,26 @@ class Terminal(object): with self.suspend(): webbrowser.open_new_tab(url) + def open_pager(self, data): + """ + View a long block of text using the system's default pager. + + The data string will be piped directly to the pager. + """ + + pager = os.getenv('PAGER') or 'less' + try: + with self.suspend(): + p = subprocess.Popen([pager], stdin=subprocess.PIPE) + p.stdin.write(self.clean(data)) + p.stdin.close() + try: + p.wait() + except KeyboardInterrupt: + p.terminate() + except OSError: + self.show_notification('Could not open pager %s' % pager) + def open_editor(self, data=''): """ Open a temporary file using the system's default editor. @@ -366,16 +387,19 @@ class Terminal(object): """ with NamedTemporaryFile(prefix='rtv-', suffix='.txt', mode='wb') as fp: - fp.write(codecs.encode(data, 'utf-8')) + fp.write(self.clean(data)) fp.flush() editor = os.getenv('RTV_EDITOR') or os.getenv('EDITOR') or 'nano' try: with self.suspend(): - subprocess.Popen([editor, fp.name]).wait() + p = subprocess.Popen([editor, fp.name]) + try: + p.wait() + except KeyboardInterrupt: + p.terminate() except OSError: - raise exceptions.ProgramError( - 'Could not open file with %s' % editor) + self.show_notification('Could not open file with %s' % editor) # Open a second file object to read. This appears to be necessary # in order to read the changes made by some editors (gedit). w+ diff --git a/tests/test_submission.py b/tests/test_submission.py index afc8fed..bbf8d1f 100644 --- a/tests/test_submission.py +++ b/tests/test_submission.py @@ -94,6 +94,23 @@ def test_submission_open(submission_page, terminal): assert terminal.open_browser.called +def test_submission_pager(submission_page, terminal): + + # View a submission with the pager + with mock.patch.object(terminal, 'open_pager'): + submission_page.controller.trigger(terminal.RETURN) + assert terminal.open_browser.called + + # Move down to the first comment + with mock.patch.object(submission_page, 'clear_input_queue'): + submission_page.controller.trigger('j') + + # View a comment with the pager + with mock.patch.object(terminal, 'open_pager'): + submission_page.controller.trigger(terminal.RETURN) + assert terminal.open_browser.called + + def test_submission_vote(submission_page, refresh_token): # Log in diff --git a/tests/test_terminal.py b/tests/test_terminal.py index 8b5acb5..39d5a7b 100644 --- a/tests/test_terminal.py +++ b/tests/test_terminal.py @@ -308,4 +308,26 @@ def test_open_browser(terminal): terminal.open_browser(url) open_new_tab.assert_called_with(url) assert curses.endwin.called - assert curses.doupdate.called \ No newline at end of file + assert curses.doupdate.called + + +def test_open_pager(terminal, stdscr): + + data = "Hello World!" + + def side_effect(*_, stdin=None): + assert stdin is not None + raise OSError + + with mock.patch('subprocess.Popen', autospec=True) as Popen, \ + mock.patch.dict('os.environ', {'PAGER': 'fake'}): + + terminal.open_pager(data) + assert Popen.called + assert not stdscr.addstr.called + + # Raise an OS error + Popen.side_effect = side_effect + terminal.open_pager(data) + message = 'Could not open pager fake'.encode('ascii') + assert stdscr.addstr.called_with(0, 0, message) \ No newline at end of file