-
Notifications
You must be signed in to change notification settings - Fork 3
Implement terminal_pager for log subcommand #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
||
|
||
bool m_grabbed; | ||
std::ostringstream m_oss; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that m_oss
is used only to get its internal buffer. Why not directly store a std::stringbuf
instead?
#include "terminal_pager.hpp" | ||
|
||
terminal_pager::terminal_pager() | ||
: m_grabbed(false), m_rows(0), m_columns(0), m_start_row_index(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::cout.rdbuf(std::cout.rdbuf())
is a noop, so I think we could always capture std::cout
buffer, and always restore it in release_cout
, even if it was not redirected. This way we could drop m_grabbed
and the condition in the implementaion of release_cout
.
// Unfortunately need to access _internal namespace of termcolor to check if a tty. | ||
if (!m_grabbed && termcolor::_internal::is_atty(std::cout)) | ||
{ | ||
// Should we do anything with cerr? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably capture std::cerr
too as by default (at least on some platforms) it outputs to the same place as std::cout
void maybe_grab_cout(); | ||
|
||
// Return true if should stop pager. | ||
bool process_input(const std::string& input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to take the argument by value; calling it with an rvalue will trigger the string's move constructor.
auto end_row_index = m_start_row_index + m_rows - 1; | ||
|
||
std::cout << "\e[2J"; // Erase screen. | ||
std::cout << "\e[H"; // Cursor to top. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we capture these "magic strings" into constants with expressive names?
return false; | ||
case '\e': // ANSI escape sequence. | ||
// Cannot switch on a std::string. | ||
if (input == "\e[A" || input == "\e[1A]") // Up arrow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question here.
auto new_termios = old_termios; | ||
// Disable canonical mode (buffered I/O) and echo from stdin to stdout. | ||
new_termios.c_lflag &= (~ICANON & ~ECHO); | ||
tcsetattr(fileno(stdin), TCSANOW, &new_termios); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this and the call to tcsetattr
to restore the initial state line 211 should probably be in a scope object to guarantee the initial state is restored even if an excpetion is thrown.
The start of a terminal pager for various
git2cpp
commands, initially enabled forlog
subcommand. Fixes #45.pager.mp4
Supports the following keys to navigate:
If cout is not to a tty or the output is short enough to fit within a single page the pager is not used.
Still to do, probably in separate PRs:
To add to other subcommands, see how it is used in
log_subcommand.cpp
. Theterminal_pager
constructor does the required initialisation, and it displays on theshow
call. I suppose I could have done theshow
automatically in the destructor but that didn't seem a good idea.Awaiting a new release of JupyterLite before we can easily try it in the terminal.