diff --git a/CONTROLS.rst b/CONTROLS.rst index a185be0..f5075f4 100644 --- a/CONTROLS.rst +++ b/CONTROLS.rst @@ -57,5 +57,6 @@ Submission Mode In submission mode you can view the self text for a submission and browse comments. :``h`` or ``◄``: Return to the subreddit +:``l`` or ``►``: Open the selected comment in a new window :``o`` or ``ENTER``: Open the comment permalink with your web browser :``SPACE``: Fold the selected comment, or load additional comments diff --git a/README.rst b/README.rst index 21e0387..a3b1b3a 100644 --- a/README.rst +++ b/README.rst @@ -32,15 +32,15 @@ Install using pip... .. code-block:: bash - $ pip install rtv + $ pip install rtv or clone the repository. .. code-block:: bash - $ git clone https://github.com/michael-lazar/rtv.git - $ cd rtv - $ python3 setup.py install + $ git clone https://github.com/michael-lazar/rtv.git + $ cd rtv + $ python3 setup.py install ===== Usage @@ -50,7 +50,7 @@ To run the program, type .. code-block:: bash - $ rtv --help + $ rtv --help -------- Controls @@ -96,7 +96,7 @@ Auto-generate the config file by running .. code-block:: bash - $ rtv --copy-config + $ rtv --copy-config See the `default config `_ for the full list of settings. @@ -109,7 +109,7 @@ Set the editor by changing ``$RTV_EDITOR`` in your environment. .. code-block:: bash - $ export RTV_EDITOR=gedit + $ export RTV_EDITOR=gedit If not specified, the default system ``$EDITOR`` (or *nano*) will be used. @@ -123,7 +123,7 @@ If you prefer the complete terminal experience, set ``$BROWSER`` to a console-ba .. code-block:: bash - $ export BROWSER=w3m + $ export BROWSER=w3m `w3m `_, `lynx `_, and `elinks `_ are all good choices. @@ -155,6 +155,28 @@ How do I run the repository code directly? $ cd ~/rtv_project $ python3 -m rtv +How do I run the tests? + This project uses `pytest `_ and `VCR.py `_. + + .. code-block:: bash + + $ pip3 install pytest + $ # The pip release for VCR.py is out-of-date + $ pip3 install git+https://github.com/kevin1024/vcrpy.git + $ cd ~/rtv_project + $ # Run the full suite + $ PYTHONPATH=. py.test + $ # or a single test + $ PYTHONPATH=. py.test tests/test_config.py::test_copy_default_config + + VCR.py will record HTTP requests made during the test run and store + them in *tests/cassettes/*. By default these cassettes are read-only, + if you would like to record new cassettes you must provide your own refresh token. + + .. code-block:: bash + + $ PYTHONPATH=. py.test --record-mode=once --refresh-token=~/.config/rtv/refresh-token + ========= Changelog ========= diff --git a/rtv/docs.py b/rtv/docs.py index f375c1d..bb9eab8 100644 --- a/rtv/docs.py +++ b/rtv/docs.py @@ -17,7 +17,7 @@ Press `?` to open the help screen. """ HELP = """ -Basic Commands +[Basic Commands] `j/k` or `UP/DOWN` : Move the cursor up/down `m/n` or `PgUp/PgDn`: Jump to the previous/next page `o` or `ENTER` : Open the selected item as a webpage @@ -27,7 +27,7 @@ Basic Commands `?` : Show the help screen `q/Q` : Quit/Force quit -Authenticated Commands +[Authenticated Commands] `a/z` : Upvote/downvote `c` : Compose a new post or comment `e` : Edit an existing post or comment @@ -35,13 +35,14 @@ Authenticated Commands `i` : Display new messages prompt `s` : Open/close subscribed subreddits list -Subreddit Mode +[Subreddit Mode] `l` or `RIGHT` : Enter the selected submission `/` : Open a prompt to switch subreddits `f` : Open a prompt to search the current subreddit -Submission Mode +[Submission Mode] `h` or `LEFT` : Return to subreddit mode + `l` or `RIGHT` : Open the selected comment in a new window `SPACE` : Fold the selected comment, or load additional comments """ @@ -75,4 +76,4 @@ SUBMISSION_EDIT_FILE = """{content} # and an empty message aborts the submission. # # Editing {name} -""" \ No newline at end of file +""" diff --git a/rtv/page.py b/rtv/page.py index 0e006b1..53ac2c8 100644 --- a/rtv/page.py +++ b/rtv/page.py @@ -79,7 +79,7 @@ class Page(object): @PageController.register(Command('HELP')) def show_help(self): - self.term.show_notification(docs.HELP.strip().splitlines()) + self.term.show_notification(docs.HELP.strip('\n').splitlines()) @PageController.register(Command('SORT_HOT')) def sort_content_hot(self): 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/scripts/rtv.1.template b/scripts/rtv.1.template index a1ccbd9..68da2d1 100644 --- a/scripts/rtv.1.template +++ b/scripts/rtv.1.template @@ -25,13 +25,13 @@ for future sessions. You can disable this behavior by setting the option .SH ENVIRONMENT .TP .BR RTV_EDITOR -Specifies which text editor RTV will attempt to use when editing comments and -posts. RTV will fallback to \fI$EDITOR\fR if the editor is unspecified. +Text editor to use when editing comments and submissions. Will fallback to +\fI$EDITOR\fR. .TP .BR BROWSER -Specifies which webbrowser RTV will attempt to use when opening links. -This can be set to a terminal browser (w3m, lynx, elinks, etc.) for a true -terminal experience. RTV will fallback to the system's default browser. +Web browser to use when opening links. +.BR PAGER +Pager to use when expanding individual comments and submissions. .SH AUTHOR Michael Lazar (2016). .SH BUGS diff --git a/tests/cassettes/test_submission_pager.yaml b/tests/cassettes/test_submission_pager.yaml new file mode 100644 index 0000000..a5538c3 --- /dev/null +++ b/tests/cassettes/test_submission_pager.yaml @@ -0,0 +1,179 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty'] + method: GET + uri: https://www.reddit.com/r/Python/comments/2xmo63.json + response: + body: + string: !!binary | + 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 + headers: + CF-RAY: [27e2870df58d0701-SJC] + Connection: [keep-alive] + Content-Encoding: [gzip] + Content-Length: ['7681'] + Content-Type: [application/json; charset=UTF-8] + Date: ['Fri, 04 Mar 2016 04:19:01 GMT'] + Server: [cloudflare-nginx] + Set-Cookie: ['__cfduid=d74b0638b5cdf1ae3e52258dd917f74761457065141; expires=Sat, + 04-Mar-17 04:19:01 GMT; path=/; domain=.reddit.com; HttpOnly', 'loid=FIHszkGB7O46d84R4x; + Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sun, 04-Mar-2018 04:19:01 + GMT; secure', 'loidcreated=2016-03-04T04%3A19%3A01.483Z; Domain=reddit.com; + Max-Age=63071999; Path=/; expires=Sun, 04-Mar-2018 04:19:01 GMT; secure'] + Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload] + Vary: [accept-encoding] + X-Moose: [majestic] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['X-Reddit-Tracking, X-Moose'] + cache-control: ['max-age=0, must-revalidate'] + set-cookie: ['__cfduid=d74b0638b5cdf1ae3e52258dd917f74761457065141; expires=Sat, + 04-Mar-17 04:19:01 GMT; path=/; domain=.reddit.com; HttpOnly', 'loid=FIHszkGB7O46d84R4x; + Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sun, 04-Mar-2018 04:19:01 + GMT; secure', 'loidcreated=2016-03-04T04%3A19%3A01.483Z; Domain=reddit.com; + Max-Age=63071999; Path=/; expires=Sun, 04-Mar-2018 04:19:01 GMT; secure'] + x-content-type-options: [nosniff] + x-frame-options: [SAMEORIGIN] + x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=WFLujq0%2BidDL%2F5%2FEeUPNp5nimP4wqGpVG5FP8Mx8Jg0%2Bh%2FCnbI6mePESqBT0oQ36bwCzJoaTgDU%3D'] + x-ua-compatible: [IE=edge] + x-xss-protection: [1; mode=block] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_page.py b/tests/test_page.py index 2891e16..0a721a3 100644 --- a/tests/test_page.py +++ b/tests/test_page.py @@ -72,7 +72,7 @@ def test_page_unauthenticated(reddit, terminal, config, oauth): # Show help page.controller.trigger('?') - message = 'Basic Commands'.encode('utf-8') + message = '[Basic Commands]'.encode('utf-8') terminal.stdscr.subwin.addstr.assert_any_call(1, 1, message) # Sort content diff --git a/tests/test_submission.py b/tests/test_submission.py index afc8fed..c9f8feb 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('l') + assert terminal.open_pager.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('l') + assert terminal.open_pager.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..de6a07f 100644 --- a/tests/test_terminal.py +++ b/tests/test_terminal.py @@ -308,4 +308,27 @@ 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(args, 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'}): + Popen.return_value.stdin = mock.Mock() + + 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