[help] add some initial help text for search/SQL prompts

Also:
  * You can now "scroll out" of the visual editor to
    return to the main view.  This should alleviate the
    frustration of pressing up/down arrows thinking the
    main view should move but, really, you're stuck in
    the editor.
  * The three-dot-ellipsis for hidden fields has been
    replaced with a unicode vertical ellipsis so that
    less space is taken up.
This commit is contained in:
Timothy Stack 2019-01-24 22:05:15 -08:00
parent 6853034cce
commit 5fff4d8784
16 changed files with 174 additions and 32 deletions

4
NEWS
View File

@ -30,9 +30,13 @@ lnav v0.8.5:
strong fuzzy match. If there are multiple matches, as would happen
with ":dfil", readline's menu-complete behavior will be engaged and
you can press TAB cycle through the options.
* Some initial help text is now shown for the search and SQL prompts to
refresh the memory.
* When entering the ":comment" command for a line with a comment, the
command prompt will be filled in with the existing comment to make
editing easier.
* Hidden fields now show up as a unicode vertical ellipsis (⋮) instead of
three-dot ellipsis to save space.
Fixes:
* The ":write-json-to" command will now pass through JSON cells as their

View File

@ -43,8 +43,11 @@
#define ANSI_NORM ANSI_CSI "0m"
#define ANSI_BOLD(msg) ANSI_BOLD_START msg ANSI_NORM
#define ANSI_UNDERLINE(msg) ANSI_UNDERLINE_START msg ANSI_NORM
#define ANSI_ROLE(msg) ANSI_CSI "%dO" msg ANSI_NORM
#define XANSI_COLOR(col) ANSI_CSI "3" #col "m"
#define ANSI_COLOR(col) XANSI_COLOR(col)
/**
* Check a string for ANSI escape sequences, process them, remove them, and add

View File

@ -40,25 +40,38 @@ public:
typedef enum {
TSF_TITLE,
TSF_STITCH_TITLE,
TSF_DESCRIPTION,
TSF__MAX
} field_t;
doc_status_source() {
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_left_pad(1);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_VIEW_STATUS);
this->tss_fields[TSF_TITLE].set_value(" Command Help ");
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::ansi_color_pair_index(COLOR_BLUE, COLOR_WHITE));
this->tss_fields[TSF_DESCRIPTION].set_share(1);
this->tss_fields[TSF_DESCRIPTION].set_role(view_colors::VCR_STATUS);
};
size_t statusview_fields(void) { return TSF__MAX; };
size_t statusview_fields() override {
return TSF__MAX;
};
status_field &statusview_value_for_field(int field) {
status_field &statusview_value_for_field(int field) override {
return this->tss_fields[field];
};
void set_title(const std::string &title) {
this->tss_fields[TSF_TITLE].set_value(title);
}
void set_description(const std::string &description) {
this->tss_fields[TSF_DESCRIPTION].set_value(description);
}
private:
status_field tss_fields[TSF__MAX];
};

View File

@ -464,3 +464,9 @@ void filter_sub_source::rl_display_next(readline_curses *rc)
tc.shift_top(tc.get_height());
}
}
void filter_sub_source::list_input_handle_scroll_out(listview_curses &lv)
{
lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_filter_view.reload_data();
}

View File

@ -43,6 +43,8 @@ public:
bool list_input_handle_key(listview_curses &lv, int ch) override;
void list_input_handle_scroll_out(listview_curses &lv) override;
size_t text_line_count() override;;
size_t text_line_width(textview_curses &curses) override;

View File

@ -40,15 +40,15 @@
#include "session_data.hh"
#include "command_executor.hh"
#include "termios_guard.hh"
#include "readline_callbacks.hh"
#include "readline_possibilities.hh"
#include "readline_highlighters.hh"
#include "field_overlay_source.hh"
#include "hotkeys.hh"
#include "log_format_loader.hh"
using namespace std;
static bookmark_type_t BM_EXAMPLE("");
class logline_helper {
public:
@ -59,7 +59,7 @@ public:
logline &move_to_msg_start() {
content_line_t cl = this->lh_sub_source.at(this->lh_current_line);
std::shared_ptr<logfile> lf = this->lh_sub_source.find(cl);
logfile::iterator ll = lf->begin() + cl;
auto ll = lf->begin() + cl;
while (ll->is_continued()) {
--ll;
--this->lh_current_line;
@ -683,7 +683,7 @@ void handle_paging_key(int ch)
textview_curses & log_view = lnav_data.ld_views[LNV_LOG];
content_line_t cl = lss.at(log_view.get_top());
std::shared_ptr<logfile> lf = lss.find(cl);
logfile::iterator ll = lf->begin() + cl;
auto ll = lf->begin() + cl;
log_data_helper ldh(lss);
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
@ -764,6 +764,7 @@ void handle_paging_key(int ch)
}
}
lnav_data.ld_doc_status_source.set_title("Command Help");
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_COMMAND, "filter", tc);
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "filter", tc->get_last_search());
add_filter_possibilities(tc);
@ -781,6 +782,8 @@ void handle_paging_key(int ch)
lnav_data.ld_search_start_line = tc->get_top();
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_SEARCH, "*", tc);
lnav_data.ld_rl_view->focus(LNM_SEARCH, "/");
lnav_data.ld_doc_status_source.set_title("Syntax Help");
rl_set_help();
lnav_data.ld_bottom_source.set_prompt(
"Enter a regular expression to search for: "
"(Press " ANSI_BOLD("CTRL+]") " to abort)");
@ -791,25 +794,28 @@ void handle_paging_key(int ch)
tc == &lnav_data.ld_views[LNV_DB] ||
tc == &lnav_data.ld_views[LNV_SCHEMA]) {
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
lnav_data.ld_mode = LNM_SQL;
setup_logline_table(lnav_data.ld_exec_context);
lnav_data.ld_rl_view->focus(LNM_SQL, ";");
lnav_data.ld_doc_status_source.set_title("Query Help");
rl_set_help();
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_status[LNS_BOTTOM].do_update();
field_overlay_source *fos;
fos = (field_overlay_source *)log_view.get_overlay_source();
fos = (field_overlay_source *) log_view.get_overlay_source();
fos->fos_active_prev = fos->fos_active;
if (!fos->fos_active) {
fos->fos_active = true;
tc->reload_data();
}
lnav_data.ld_bottom_source.set_prompt("Enter an SQL query: (Press "
ANSI_BOLD("CTRL+]") " to abort)");
lnav_data.ld_bottom_source.set_prompt(
"Enter an SQL query: (Press " ANSI_BOLD("CTRL+]") " to abort)");
}
break;

View File

@ -114,6 +114,9 @@ public:
virtual ~list_input_delegate() { };
virtual bool list_input_handle_key(listview_curses &lv, int ch) = 0;
virtual void list_input_handle_scroll_out(listview_curses &lv) {
};
};
/**
@ -230,8 +233,8 @@ public:
new_selection < this->get_inner_height()) {
this->set_selection(new_selection);
this->scroll_selection_into_view();
} else {
alerter::singleton().chime();
} else if (!alerter::singleton().chime()) {
this->delegate_scroll_out();
}
}
@ -559,6 +562,12 @@ public:
}
protected:
void delegate_scroll_out() {
for (auto &lv_input_delegate : this->lv_input_delegates) {
lv_input_delegate->list_input_handle_scroll_out(*this);
}
}
enum lv_mode_t {
LV_MODE_NONE,
LV_MODE_DOWN,

View File

@ -36,6 +36,7 @@
#include "plain_text_source.hh"
#include "command_executor.hh"
#include "readline_curses.hh"
#include "readline_highlighters.hh"
#include "log_search_table.hh"
#include "log_format_loader.hh"
#include "help_text_formatter.hh"
@ -45,8 +46,98 @@ using namespace std;
#define ABORT_MSG "(Press " ANSI_BOLD("CTRL+]") " to abort)"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define ANSI_RE(msg) ANSI_CSI "1;3" STR(COLOR_CYAN) "m" msg ANSI_NORM
#define ANSI_CLS(msg) ANSI_CSI "1;3" STR(COLOR_MAGENTA) "m" msg ANSI_NORM
#define ANSI_KW(msg) ANSI_CSI "3" STR(COLOR_BLUE) "m" msg ANSI_NORM
#define ANSI_REV(msg) ANSI_CSI "7m" msg ANSI_NORM
#define ANSI_STR(msg) ANSI_CSI "32m" msg ANSI_NORM
const char *RE_HELP =
" " ANSI_RE(".") " Any character "
" " "a" ANSI_RE("|") "b a or b "
" " ANSI_RE("^") " Start of string\n"
" " ANSI_CLS("\\w") " Word character "
" " "a" ANSI_RE("?") " 0 or 1 a's "
" " ANSI_RE("$") " End of string\n"
" " ANSI_CLS("\\d") " Digit "
" " "a" ANSI_RE("*") " 0 or more a's "
" " ANSI_RE("(") "..." ANSI_RE(")") " Capture\n"
" " ANSI_RE("\\") " Escape character "
" " "a" ANSI_RE("+") " 1 or more a's "
" " ANSI_RE("[") "ab" ANSI_RE("-") "d" ANSI_RE("]") " Any of a, b, c, or d"
;
const char *RE_EXAMPLE =
ANSI_UNDERLINE("Examples") "\n"
" abc" ANSI_RE("*") " matches "
ANSI_STR("'ab'") ", " ANSI_STR("'abc'") ", " ANSI_STR("'abccc'") "\n"
" key=" ANSI_RE("(\\w+)")
" matches key=" ANSI_REV("123") ", key=" ANSI_REV("abc") " and captures 123 and abc\n"
" " ANSI_RE("\\") "[abc" ANSI_RE("\\") "] matches " ANSI_STR("'[abc]'")
;
const char *SQL_HELP =
" " ANSI_KW("SELECT") " Select rows from a table "
" " ANSI_KW("DELETE") " Delete rows from a table\n"
" " ANSI_KW("INSERT") " Insert rows into a table "
" " ANSI_KW("UPDATE") " Update rows in a table\n"
" " ANSI_KW("CREATE") " Create a table/index "
" " ANSI_KW("DROP") " Drop a table/index\n"
" " ANSI_KW("ATTACH") " Attach a SQLite database file "
" " ANSI_KW("DETACH") " Detach a SQLite database"
;
const char *SQL_EXAMPLE =
ANSI_UNDERLINE("Examples") "\n"
" SELECT * FROM %s WHERE log_level >= 'warning' LIMIT 10\n"
" UPDATE %s SET log_mark = 1 WHERE log_line = log_top_line()\n"
" SELECT * FROM logline LIMIT 10"
;
static const char *LNAV_CMD_PROMPT = "Enter an lnav command: " ABORT_MSG;
void rl_set_help()
{
switch (lnav_data.ld_mode) {
case LNM_SEARCH: {
lnav_data.ld_doc_source.replace_with(RE_HELP);
lnav_data.ld_example_source.replace_with(RE_EXAMPLE);
break;
}
case LNM_SQL: {
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
auto lss = (logfile_sub_source *) log_view.get_sub_source();
char example_txt[1024];
attr_line_t example_al;
if (log_view.get_inner_height() > 0) {
auto cl = lss->at(log_view.get_top());
auto lf = lss->find(cl);
auto format_name = lf->get_format()->get_name().get();
snprintf(example_txt, sizeof(example_txt),
SQL_EXAMPLE,
format_name,
format_name);
example_al.with_ansi_string(example_txt);
readline_sqlite_highlighter(example_al, 0);
}
lnav_data.ld_doc_source.replace_with(SQL_HELP);
lnav_data.ld_example_source.replace_with(example_al);
break;
}
}
}
void rl_change(void *dummy, readline_curses *rc)
{
textview_curses *tc = *lnav_data.ld_view_stack.top();
@ -368,8 +459,7 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
}
if (!has_doc) {
lnav_data.ld_doc_source.clear();
lnav_data.ld_example_source.clear();
rl_set_help();
lnav_data.ld_preview_source.clear();
}
return;
@ -454,7 +544,6 @@ void rl_callback(void *dummy, readline_curses *rc)
rl_search_internal(dummy, rc, true);
if (rc->get_value().size() > 0) {
auto_mem<FILE> pfile(pclose);
textview_curses *tc = *lnav_data.ld_view_stack.top();
vis_bookmarks &bm = tc->get_bookmarks();
const auto &bv = bm[&textview_curses::BM_SEARCH];
vis_line_t vl = bv.next(tc->get_top());

View File

@ -30,6 +30,7 @@
#ifndef LNAV_READLINE_CALLBACKS_HH
#define LNAV_READLINE_CALLBACKS_HH
void rl_set_help();
void rl_change(void *dummy, readline_curses *rc);
void rl_search(void *dummy, readline_curses *rc);
void rl_abort(void *dummy, readline_curses *rc);
@ -37,4 +38,9 @@ void rl_callback(void *dummy, readline_curses *rc);
void rl_display_matches(void *dummy, readline_curses *rc);
void rl_display_next(void *dummy, readline_curses *rc);
extern const char *RE_HELP;
extern const char *RE_EXAMPLE;
extern const char *SQL_HELP;
extern const char *SQL_EXAMPLE;
#endif //LNAV_READLINE_CALLBACKS_HH

View File

@ -1413,6 +1413,7 @@ void reset_session()
tc.reload_data();
}
lnav_data.ld_filter_view.reload_data();
for (auto format : log_format::get_root_formats()) {
auto *elf = dynamic_cast<external_log_format *>(format);

View File

@ -78,7 +78,9 @@ void statusview_curses::do_update()
}
}
}
left += sf.get_left_pad();
if (sf.get_left_pad() > 0) {
val.insert(0, sf.get_left_pad(), ' ');
}
if (sf.is_right_justified()) {
val.right_justify(sf.get_width());

View File

@ -299,12 +299,11 @@ void textview_curses::textview_value_for_row(vis_line_t row,
sattr.sa_range.length() > 3) {
struct line_range &lr = sattr.sa_range;
str.replace(lr.lr_start, lr.length(), "...");
str.replace(lr.lr_start, lr.length(), "\xE2\x8B\xAE");
shift_string_attrs(sa, lr.lr_start + 1, -(lr.length() - 3));
sattr.sa_type = &VC_GRAPHIC;
sattr.sa_type = &VC_STYLE;
sattr.sa_value.sav_int =
vc.attrs_for_role(view_colors::VCR_HIDDEN) |
ACS_BULLET | A_UNDERLINE;
vc.attrs_for_role(view_colors::VCR_HIDDEN);
lr.lr_end = lr.lr_start + 3;
for_each(sa.begin(), sa.end(), [&] (string_attr &attr) {

View File

@ -764,7 +764,7 @@ void view_colors::init_roles(int color_pair_base)
this->vc_role_colors[VCR_STRING] = attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_COMMENT] = attr_for_colors(color_pair_base, COLOR_GREEN, COLOR_BLACK);
this->vc_role_colors[VCR_VARIABLE] = attr_for_colors(color_pair_base, COLOR_CYAN, COLOR_BLACK);
this->vc_role_colors[VCR_SYMBOL] = attr_for_colors(color_pair_base, COLOR_MAGENTA, COLOR_BLACK);
this->vc_role_colors[VCR_SYMBOL] = attr_for_colors(color_pair_base, COLOR_MAGENTA, COLOR_BLACK) | A_BOLD;
this->vc_role_colors[VCR_RE_SPECIAL] = attr_for_colors(color_pair_base, COLOR_CYAN, COLOR_BLACK);
this->vc_role_colors[VCR_RE_REPEAT] = attr_for_colors(color_pair_base, COLOR_YELLOW, COLOR_BLACK);
this->vc_role_colors[VCR_FILE] = attr_for_colors(color_pair_base, COLOR_BLUE, COLOR_BLACK);

View File

@ -137,15 +137,17 @@ public:
void enabled(bool enable) { this->a_enabled = enable; };
void chime(void) {
bool chime() {
if (!this->a_enabled) {
return;
return true;
}
bool retval = this->a_do_flash;
if (this->a_do_flash) {
::flash();
}
this->a_do_flash = false;
return retval;
};
void new_input(int ch) {

View File

@ -56,9 +56,9 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "hide-fields with uri" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET ... HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET ... HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET ... HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
@ -66,9 +66,9 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "hide-fields with IP and uri" <<EOF
... - - [20/Jul/2009:22:59:26 +0000] "GET ... HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
... - - [20/Jul/2009:22:59:29 +0000] "GET ... HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
... - - [20/Jul/2009:22:59:29 +0000] "GET ... HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
⋮ - - [20/Jul/2009:22:59:26 +0000] "GET ⋮ HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
⋮ - - [20/Jul/2009:22:59:29 +0000] "GET ⋮ HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
⋮ - - [20/Jul/2009:22:59:29 +0000] "GET ⋮ HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -f- -n < ${test_dir}/formats/scripts/multiline-echo.lnav

View File

@ -83,9 +83,9 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "restoring hidden fields failed" <<EOF
... - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
... - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
... - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
- - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
- - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
- - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
rm -rf ./sessions