[help] add builtin help to the main help text as a reference

Defect Number:
    Reviewed By:
   Testing Done:
This commit is contained in:
Timothy Stack 2017-04-14 22:49:36 -07:00
parent 01716ba2ab
commit 4cedde9a43
13 changed files with 1984 additions and 174 deletions

View File

@ -380,11 +380,23 @@ public:
attr_line_t &with_ansi_string(const char *str, ...);
attr_line_t &with_ansi_string(const std::string &str);
attr_line_t &with_attr(const string_attr &sa) {
this->al_attrs.push_back(sa);
return *this;
};
attr_line_t &ensure_space() {
if (!this->al_string.empty() &&
this->al_string.back() != ' ' &&
this->al_string.back() != '[') {
this->append(1, ' ');
}
return *this;
};
template<typename S, typename T = void *>
attr_line_t &append(S str,
string_attr_type_t type = nullptr,

View File

@ -106,9 +106,9 @@ down to display the new lines, much like 'tail -f'.
On color displays, the lines will be highlighted as follows:
* Errors will be colored in red;
* warnings will be yellow;
* boundaries between days will be underlined; and
* Errors will be colored in ${ansi_red}red${ansi_norm};
* warnings will be ${ansi_yellow}yellow${ansi_norm};
* boundaries between days will be ${ansi_underline}underlined${ansi_norm}; and
* various color highlights will be applied to: IP addresses, SQL keywords,
XML tags, file and line numbers in Java backtraces, and quoted strings.
@ -379,7 +379,7 @@ Query
if you wanted to search for ethernet device names,
regardless of their ID number, you can type:
eth\d+
eth\\d+
You can find more information about Perl regular
expressions at:
@ -403,11 +403,11 @@ Query
|<script> [arg1 .. argN]
Execute an lnav script contained in a format directory
(e.g. ~/.lnav/formats/default). The script can contain
(e.g. \~/.lnav/formats/default). The script can contain
lines starting with ':', ';', or '|' to execute commands,
SQL queries or execute other files in lnav. Any values
after the script name are treated as arguments can be
referenced in the script using '$1', '$2', and so on, like
referenced in the script using '\$1', '\$2', and so on, like
in a shell script.
CTRL+] Abort command-line entry started with '/', ':', ';', or '|'.
@ -598,7 +598,7 @@ COMMANDS
syslog message.
session <cmd> Add the given command to the session file
(~/.lnav/session). Any commands listed in the session file
(\~/.lnav/session). Any commands listed in the session file
are executed on startup. Only the highlight, word-wrap, and
filter-related commands can be added to the session file.
@ -670,7 +670,7 @@ COMMANDS
'*' to reset all options.
save-config Save the current configuration state to:
~/.lnav/config.json
\~/.lnav/config.json
SQL QUERIES (experimental)
@ -829,21 +829,21 @@ OTHER SQL FEATURES
==================
Environment variables can be used in SQL statements by prefixing the
variable name with a dollar-sign ($). For example, to read the value of
variable name with a dollar-sign (\$). For example, to read the value of
the HOME variable, you can do:
;SELECT $HOME;
;SELECT \$HOME;
To select the syslog messages that have a hostname field that is equal
to the HOSTNAME variable:
;SELECT * FROM syslog_log WHERE log_hostname = $HOSTNAME;
;SELECT * FROM syslog_log WHERE log_hostname = \$HOSTNAME;
NOTE: Variable substitution is done for fields in the query and is not
a plain text substitution. For example, the following statement
WILL NOT WORK:
;SELECT * FROM $TABLE_NAME; -- Syntax error
;SELECT * FROM \$TABLE_NAME; -- Syntax error
Access to lnav's environment variables is also available via the "environ"
@ -884,8 +884,8 @@ with "pt:" and passing the value as a file name. For example, to search
for log messages with the string 'Critical Error' when starting lnav you
can do the following:
$ setenv PAPERTRAIL_API_TOKEN xxxxxxxxx
$ lnav "pt:'Critical Error'"
\$ setenv PAPERTRAIL_API_TOKEN xxxxxxxxx
\$ lnav "pt:'Critical Error'"
If lnav is already started, you can use the ':open' command like so:
@ -908,3 +908,7 @@ For more information, visit the lnav website at:
For support questions, email:
lnav@googlegroups.com
REFERENCE
=========

View File

@ -43,6 +43,7 @@ void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
static size_t body_indent = 2;
text_wrap_settings tws;
size_t start_index = out.get_string().length();
tws.with_width(width);
@ -118,32 +119,34 @@ void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
for (auto &param : ht.ht_parameters) {
if (break_all ||
(out.get_string().length() - line_start + 10) >=
(out.get_string().length() - start_index - line_start + 10) >=
tws.tws_width) {
out.append("\n");
line_start = out.get_string().length();
out.append(body_indent + strlen(ht.ht_name) + 1, ' ');
break_all = true;
} else {
out.append(" ");
}
if (param.ht_nargs == HN_ZERO_OR_MORE ||
param.ht_nargs == HN_OPTIONAL) {
if (!break_all) {
out.append(" ");
}
out.append("[");
}
if (param.ht_flag_name) {
out.append(param.ht_flag_name, &view_curses::VC_STYLE,
A_BOLD);
out.ensure_space()
.append(param.ht_flag_name, &view_curses::VC_STYLE,
A_BOLD);
}
if (param.ht_name[0]) {
out.append(" ")
out.ensure_space()
.append(param.ht_name, &view_curses::VC_STYLE,
A_UNDERLINE);
if (param.ht_nargs == HN_ZERO_OR_MORE ||
param.ht_nargs == HN_ONE_OR_MORE) {
out.append("1", &view_curses::VC_STYLE, A_UNDERLINE);
}
if (!param.ht_parameters.empty()) {
if (param.ht_nargs == HN_ZERO_OR_MORE ||
param.ht_nargs == HN_ONE_OR_MORE) {
out.append("1", &view_curses::VC_STYLE, A_UNDERLINE);
}
if (param.ht_parameters[0].ht_flag_name) {
out.append(" ")
.append(param.ht_parameters[0].ht_flag_name,
@ -158,12 +161,15 @@ void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
}
if (param.ht_nargs == HN_ZERO_OR_MORE ||
param.ht_nargs == HN_ONE_OR_MORE) {
bool needs_comma = param.ht_parameters.empty() ||
!param.ht_flag_name;
out.append("1", &view_curses::VC_STYLE, A_UNDERLINE)
.append(" [")
.append(param.ht_flag_name ? "" : ", ")
.append(needs_comma ? ", " : "")
.append("...")
.append(param.ht_flag_name ? " " : "")
.append(param.ht_flag_name ? param.ht_flag_name : "",
.append(needs_comma ? "" : " ")
.append((needs_comma || !param.ht_flag_name) ? "" : param.ht_flag_name,
&view_curses::VC_STYLE,
A_BOLD)
.append(" ")

View File

@ -878,6 +878,57 @@ static int key_sql_callback(exec_context &ec, sqlite3_stmt *stmt)
return 0;
}
static void build_all_help_text()
{
if (!lnav_data.ld_help_source.empty()) {
return;
}
attr_line_t all_help_text;
shlex lexer(help_txt, strlen(help_txt));
string sub_help_text;
lexer.with_ignore_quotes(true)
.eval(sub_help_text, lnav_data.ld_exec_context.ec_global_vars);
all_help_text.with_ansi_string(sub_help_text);
map<string, help_text *> sql_funcs;
map<string, help_text *> sql_keywords;
for (auto iter : sqlite_function_help) {
switch (iter.second->ht_context) {
case HC_SQL_FUNCTION:
sql_funcs[iter.second->ht_name] = iter.second;
break;
case HC_SQL_KEYWORD:
sql_keywords[iter.second->ht_name] = iter.second;
break;
default:
break;
}
}
for (auto iter : sql_funcs) {
all_help_text.append(2, '\n');
format_help_text_for_term(*iter.second, 79, all_help_text);
if (!iter.second->ht_example.empty()) {
all_help_text.append(1, '\n');
format_example_text_for_term(*iter.second, 79, all_help_text);
}
}
for (auto iter : sql_keywords) {
all_help_text.append(2, '\n');
format_help_text_for_term(*iter.second, 79, all_help_text);
if (!iter.second->ht_example.empty()) {
all_help_text.append(1, '\n');
format_example_text_for_term(*iter.second, 79, all_help_text);
}
}
lnav_data.ld_help_source.replace_with(all_help_text);
}
bool toggle_view(textview_curses *toggle_tc)
{
textview_curses *tc = lnav_data.ld_view_stack.empty() ? NULL : lnav_data.ld_view_stack.back();
@ -902,6 +953,9 @@ bool toggle_view(textview_curses *toggle_tc)
// Rebuild to reflect changes in marks.
rebuild_hist();
}
else if (toggle_tc == &lnav_data.ld_views[LNV_HELP]) {
build_all_help_text();
}
lnav_data.ld_last_view = NULL;
lnav_data.ld_view_stack.push_back(toggle_tc);
retval = true;
@ -3177,7 +3231,7 @@ int main(int argc, char *argv[])
init_lnav_commands(lnav_commands);
lnav_data.ld_views[LNV_HELP]
.set_sub_source(new plain_text_source(help_txt))
.set_sub_source(&lnav_data.ld_help_source)
.set_word_wrap(true);
lnav_data.ld_views[LNV_LOG]
.set_sub_source(&lnav_data.ld_log_source)

View File

@ -251,6 +251,8 @@ struct _lnav_data {
time_t ld_bottom_time;
int ld_bottom_time_millis;
plain_text_source ld_help_source;
plain_text_source ld_doc_source;
textview_curses ld_doc_view;
plain_text_source ld_example_source;

View File

@ -95,11 +95,14 @@ public:
return *this;
};
size_t text_line_count()
{
size_t text_line_count() {
return this->tds_lines.size();
};
bool empty() const {
return this->tds_lines.empty();
};
size_t text_line_width(textview_curses &curses) {
return this->tds_longest_line;
};

View File

@ -81,16 +81,28 @@ public:
class shlex {
public:
shlex(const char *str, size_t len)
: s_str(str), s_len(len), s_index(0), s_state(STATE_NORMAL) {
: s_str(str),
s_len(len),
s_ignore_quotes(false),
s_index(0),
s_state(STATE_NORMAL) {
};
shlex(const std::string &str)
: s_str(str.c_str()), s_len(str.size()), s_index(0),
: s_str(str.c_str()),
s_len(str.size()),
s_ignore_quotes(false),
s_index(0),
s_state(STATE_NORMAL) {
};
shlex &with_ignore_quotes(bool val) {
this->s_ignore_quotes = val;
return *this;
}
bool tokenize(pcre_context::capture_t &cap_out, shlex_token_t &token_out) {
while (this->s_index < this->s_len) {
switch (this->s_str[this->s_index]) {
@ -108,43 +120,47 @@ public:
}
return true;
case '\"':
switch (this->s_state) {
case STATE_NORMAL:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_DOUBLE_QUOTE_START;
this->s_state = STATE_IN_DOUBLE_QUOTE;
return true;
case STATE_IN_DOUBLE_QUOTE:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_DOUBLE_QUOTE_END;
this->s_state = STATE_NORMAL;
return true;
default:
break;
if (!this->s_ignore_quotes) {
switch (this->s_state) {
case STATE_NORMAL:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_DOUBLE_QUOTE_START;
this->s_state = STATE_IN_DOUBLE_QUOTE;
return true;
case STATE_IN_DOUBLE_QUOTE:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_DOUBLE_QUOTE_END;
this->s_state = STATE_NORMAL;
return true;
default:
break;
}
}
break;
case '\'':
switch (this->s_state) {
case STATE_NORMAL:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_SINGLE_QUOTE_START;
this->s_state = STATE_IN_SINGLE_QUOTE;
return true;
case STATE_IN_SINGLE_QUOTE:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_SINGLE_QUOTE_END;
this->s_state = STATE_NORMAL;
return true;
default:
break;
if (!this->s_ignore_quotes) {
switch (this->s_state) {
case STATE_NORMAL:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_SINGLE_QUOTE_START;
this->s_state = STATE_IN_SINGLE_QUOTE;
return true;
case STATE_IN_SINGLE_QUOTE:
cap_out.c_begin = this->s_index;
this->s_index += 1;
cap_out.c_end = this->s_index;
token_out = ST_SINGLE_QUOTE_END;
this->s_state = STATE_NORMAL;
return true;
default:
break;
}
}
break;
case '$':
@ -377,6 +393,7 @@ public:
const char *s_str;
ssize_t s_len;
bool s_ignore_quotes;
ssize_t s_index;
state_t s_state;
};

View File

@ -100,6 +100,14 @@ attr_line_t &attr_line_t::with_ansi_string(const char *str, ...)
return *this;
}
attr_line_t &attr_line_t::with_ansi_string(const std::string &str)
{
this->al_string = str;
scrub_ansi_string(this->al_string, this->al_attrs);
return *this;
}
attr_line_t &attr_line_t::append(const attr_line_t &al, text_wrap_settings *tws)
{
size_t start_len = this->al_string.length();

View File

@ -21,12 +21,6 @@ add_executable(test_abbrev test_abbrev.cc
../src/pcrepp.cc
../src/lnav_log.cc
../src/spookyhash/SpookyV2.cpp)
add_executable(test_help_text_formatter test_help_text_formatter.cc
../src/help_text_formatter.cc
../src/view_curses.cc
../src/lnav_log.cc
../src/ansi_scrubber.cc
../src/pcrepp.cc)
add_executable(drive_sql_anno drive_sql_anno.cc ../src/lnav_log.cc ../src/pcrepp.cc)
link_directories(/opt/local/lib)
target_link_libraries(test_pcrepp /opt/local/lib/libpcre.a)
@ -34,6 +28,3 @@ target_link_libraries(test_reltime /opt/local/lib/libpcre.a)
target_link_libraries(test_date_time_scanner /opt/local/lib/libpcre.a)
target_link_libraries(test_abbrev /opt/local/lib/libpcre.a)
target_link_libraries(drive_sql_anno /opt/local/lib/libpcre.a)
target_link_libraries(test_help_text_formatter
/opt/local/lib/libpcre.a
/opt/local/lib/libncurses.a)

View File

@ -42,7 +42,6 @@ check_PROGRAMS = \
test_concise \
test_date_time_scanner \
test_grep_proc2 \
test_help_text_formatter \
test_hist_source \
test_json_ptr \
test_line_buffer2 \
@ -92,10 +91,6 @@ test_date_time_scanner_LDADD = ../src/libdiag.a $(SQLITE3_LIBS)
test_grep_proc2_SOURCES = test_grep_proc2.cc
test_grep_proc2_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz
test_help_text_formatter_SOURCES = test_help_text_formatter.cc
test_help_text_formatter_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz \
$(CONFIG_OBJS) $(SQLITE3_LIBS)
test_hist_source_SOURCES = test_hist_source.cc
test_hist_source_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz \
$(CONFIG_OBJS)
@ -282,6 +277,7 @@ dist_noinst_DATA = \
datafile_simple.19 \
datafile_simple.20 \
datafile_xml.0 \
expected_help.txt \
listview_output.0 \
listview_output.1 \
listview_output.2 \

1804
test/expected_help.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,12 @@
#! /bin/bash
run_test ${lnav_test} -n \
-c ":switch-to-view help" \
${test_dir}/logfile_access_log.0
check_output "switch-to-view help is not working" < ${top_srcdir}/test/expected_help.txt
run_test ${lnav_test} -n \
-c ":hide-fields foobar" \
${test_dir}/logfile_access_log.0
@ -361,13 +368,6 @@ error: filter limit reached, try combining filters with a pipe symbol (e.g. foo|
EOF
run_test ${lnav_test} -n \
-c ":switch-to-view help" \
${test_dir}/logfile_access_log.0
check_output "switch-to-view help is not working" < ${top_srcdir}/src/help.txt
run_test ${lnav_test} -n \
-c ":close" \
${test_dir}/logfile_access_log.0

View File

@ -1,87 +0,0 @@
/**
* Copyright (c) 2017, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <stdio.h>
#include <assert.h>
#include <view_curses.hh>
#include <attr_line.hh>
#include "lnav_config.hh"
#include "help_text_formatter.hh"
struct _lnav_config lnav_config;
lnav_config_listener *lnav_config_listener::LISTENER_LIST;
int main(int argc, char *argv[])
{
int retval = EXIT_SUCCESS;
static help_text ht = help_text(
"regexp_replace",
"Replace parts of a string that match a regular expression")
.with_parameters(
{
{"str", "The string to perform replacements on"},
{"re", "The regular expression to match"},
{"repl", "The replacement string"},
})
.with_example(
{
";SELECT regexp_replace('abbb bbbc', 'b+', '') AS res",
"a c",
});
{
setenv("TERM", "ansi", 1);
screen_curses sc;
view_colors::init();
attr_line_t al;
format_help_text_for_term(ht, 35, al);
std::vector<attr_line_t> lines;
al.split_lines(lines);
line_range lr{0, 80};
int y = 0;
for (auto &line : lines) {
view_curses::mvwattrline(sc.get_window(), y++, 0, line, lr);
}
getch();
}
return retval;
}