summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md6
-rw-r--r--cmd2/cmd2.py39
-rwxr-xr-xexamples/paged_output.py43
3 files changed, 66 insertions, 22 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 973ed6c6..078af8d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,12 @@
* Fixed issue where piping and redirecting did not work correctly with paths that had spaces
* Enhancements
* Added ability to print a header above tab-completion suggestions using `completion_header` member
+ * Added ``pager`` and ``pager_chop`` attributes to the ``cmd2.Cmd`` class
+ * ``pager`` defaults to **less -RXF** on POSIX and **more** on Windows
+ * ``pager_chop`` defaults to **less -SRXF** on POSIX and **more** on Windows
+ * Added ``chop`` argument to ``cmd2.Cmd.ppaged()`` method for displaying output using a pager
+ * If ``chop`` is ``False``, then ``self.pager`` is used as the pager
+ * Otherwise ``self.pager_chop`` is used as the pager
## 0.8.8 (TBD, 2018)
* Bug Fixes
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 75946764..f143d7b3 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -534,6 +534,18 @@ class Cmd(cmd.Cmd):
# quote matches that are completed in a delimited fashion
self.matches_delimited = False
+ # Set the pager(s) for use with the ppaged() method for displaying output using a pager
+ if sys.platform.startswith('win'):
+ self.pager = self.pager_chop = 'more'
+ else:
+ # Here is the meaning of the various flags we are using with the less command:
+ # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped
+ # -R causes ANSI "color" escape sequences to be output in raw form (i.e. colors are displayed)
+ # -X disables sending the termcap initialization and deinitialization strings to the terminal
+ # -F causes less to automatically exit if the entire file can be displayed on the first screen
+ self.pager = 'less -RXF'
+ self.pager_chop = 'less -SRXF'
+
# ----- Methods related to presenting output to the user -----
@property
@@ -608,14 +620,20 @@ class Cmd(cmd.Cmd):
else:
sys.stderr.write("{}\n".format(msg))
- def ppaged(self, msg: str, end: str='\n') -> None:
+ def ppaged(self, msg: str, end: str='\n', chop: bool=False) -> None:
"""Print output using a pager if it would go off screen and stdout isn't currently being redirected.
Never uses a pager inside of a script (Python or text) or when output is being redirected or piped or when
stdout or stdin are not a fully functional terminal.
- :param msg: str - message to print to current stdout - anything convertible to a str with '{}'.format() is OK
- :param end: str - string appended after the end of the message if not already present, default a newline
+ :param msg: message to print to current stdout - anything convertible to a str with '{}'.format() is OK
+ :param end: string appended after the end of the message if not already present, default a newline
+ :param chop: True -> causes lines longer than the screen width to be chopped (truncated) rather than wrapped
+ - truncated text is still accessible by scrolling with the right & left arrow keys
+ - chopping is ideal for displaying wide tabular data as is done in utilities like pgcli
+ False -> causes lines longer than the screen width to wrap to the next line
+ - wrapping is ideal when you want to avoid users having to use horizontal scrolling
+ WARNING: On Windows, the text always wraps regardless of what the chop argument is set to
"""
import subprocess
if msg is not None and msg != '':
@@ -635,17 +653,10 @@ class Cmd(cmd.Cmd):
# Don't attempt to use a pager that can block if redirecting or running a script (either text or Python)
# Also only attempt to use a pager if actually running in a real fully functional terminal
if functional_terminal and not self.redirecting and not self._in_py and not self._script_dir:
-
- if sys.platform.startswith('win'):
- pager_cmd = 'more'
- else:
- # Here is the meaning of the various flags we are using with the less command:
- # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped
- # -R causes ANSI "color" escape sequences to be output in raw form (i.e. colors are displayed)
- # -X disables sending the termcap initialization and deinitialization strings to the terminal
- # -F causes less to automatically exit if the entire file can be displayed on the first screen
- pager_cmd = 'less -SRXF'
- self.pipe_proc = subprocess.Popen(pager_cmd, shell=True, stdin=subprocess.PIPE)
+ pager = self.pager
+ if chop:
+ pager = self.pager_chop
+ self.pipe_proc = subprocess.Popen(pager, shell=True, stdin=subprocess.PIPE)
try:
self.pipe_proc.stdin.write(msg_str.encode('utf-8', 'replace'))
self.pipe_proc.stdin.close()
diff --git a/examples/paged_output.py b/examples/paged_output.py
index c56dcb89..d1b1b2c2 100755
--- a/examples/paged_output.py
+++ b/examples/paged_output.py
@@ -2,28 +2,55 @@
# coding=utf-8
"""A simple example demonstrating the using paged output via the ppaged() method.
"""
+import os
+from typing import List
import cmd2
class PagedOutput(cmd2.Cmd):
- """ Example cmd2 application where we create commands that just print the arguments they are called with."""
+ """ Example cmd2 application which shows how to display output using a pager."""
def __init__(self):
super().__init__()
+ def page_file(self, file_path: str, chop: bool=False):
+ """Helper method to prevent having too much duplicated code."""
+ filename = os.path.expanduser(file_path)
+ try:
+ with open(filename, 'r') as f:
+ text = f.read()
+ self.ppaged(text, chop=chop)
+ except FileNotFoundError as ex:
+ self.perror('ERROR: file {!r} not found'.format(filename), traceback_war=False)
+
@cmd2.with_argument_list
- def do_page_file(self, args):
- """Read in a text file and display its output in a pager."""
+ def do_page_wrap(self, args: List[str]):
+ """Read in a text file and display its output in a pager, wrapping long lines if they don't fit.
+
+ Usage: page_wrap <file_path>
+ """
if not args:
- self.perror('page_file requires a path to a file as an argument', traceback_war=False)
+ self.perror('page_wrap requires a path to a file as an argument', traceback_war=False)
return
+ self.page_file(args[0], chop=False)
+
+ complete_page_wrap = cmd2.Cmd.path_complete
+
+ @cmd2.with_argument_list
+ def do_page_truncate(self, args: List[str]):
+ """Read in a text file and display its output in a pager, truncating long lines if they don't fit.
+
+ Truncated lines can still be accessed by scrolling to the right using the arrow keys.
- with open(args[0], 'r') as f:
- text = f.read()
- self.ppaged(text)
+ Usage: page_chop <file_path>
+ """
+ if not args:
+ self.perror('page_truncate requires a path to a file as an argument', traceback_war=False)
+ return
+ self.page_file(args[0], chop=True)
- complete_page_file = cmd2.Cmd.path_complete
+ complete_page_truncate = cmd2.Cmd.path_complete
if __name__ == '__main__':