[cmds] allow commands to set the prompt

Fixes #574 and #573
This commit is contained in:
Timothy Stack 2018-11-21 17:00:28 -08:00
parent 49dce3cec2
commit 1a08eb0d12
10 changed files with 126 additions and 25 deletions

5
NEWS
View File

@ -9,6 +9,8 @@ lnav v0.8.5:
programmatically manipulate filters.
* The ":write-*" commands will now accept "/dev/clipboard" as a file name
that writes to the system clipboard.
* The ":write-to" and ":write-raw-to" commands will now print out comments
and tags attached to the lines.
* Added a ":redirect-to <path>" command to redirect command output to the
given file. This command is mostly useful in scripts where one might
want to redirect all output from commands like ":echo" and ":write-to -"
@ -24,6 +26,9 @@ 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.
* 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.
Fixes:
* The ":write-json-to" command will now pass through JSON cells as their

View File

@ -541,6 +541,35 @@ static void json_write_row(yajl_gen handle, int row)
}
}
static void write_line_to(FILE *outfile, attr_line_t &al)
{
auto al_attrs = al.get_attrs();
struct line_range lr = find_string_attr_range(
al_attrs, &textview_curses::SA_ORIGINAL_LINE);
auto line_meta = find_string_attr(
al_attrs, &logline::L_META);
fwrite(lr.substr(al.get_string()),
1,
lr.sublen(al.get_string()),
outfile);
fwrite("\n", 1, 1, outfile);
if (line_meta != al_attrs.end()) {
auto bm = static_cast<const bookmark_metadata *>(line_meta->sa_value.sav_ptr);
if (!bm->bm_comment.empty()) {
fprintf(outfile, " // %s\n", bm->bm_comment.c_str());
}
if (!bm->bm_tags.empty()) {
fprintf(outfile, " -- %s\n",
join(bm->bm_tags.begin(),
bm->bm_tags.end(),
" ").c_str());
}
}
}
static string com_save_to(exec_context &ec, string cmdline, vector<string> &args)
{
FILE *outfile = nullptr, *toclose = nullptr;
@ -773,13 +802,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
tc->listview_value_for_rows(*tc, top, rows);
for (auto &al : rows) {
struct line_range lr = find_string_attr_range(
al.get_attrs(), &textview_curses::SA_ORIGINAL_LINE);
fwrite(lr.substr(al.get_string()),
1,
lr.sublen(al.get_string()),
outfile);
fwrite("\n", 1, 1, outfile);
write_line_to(outfile, al);
line_count += 1;
}
@ -789,6 +812,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
}
}
else {
vector<attr_line_t> rows(1);
bookmark_vector<vis_line_t>::iterator iter;
size_t count = 0;
string line;
@ -797,8 +821,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
if (ec.ec_dry_run && count > 10) {
break;
}
tc->grep_value_for_line(*iter, line);
fprintf(outfile, "%s\n", line.c_str());
tc->listview_value_for_rows(*tc, *iter, rows);
write_line_to(outfile, rows[0]);
line_count += 1;
}
@ -1938,6 +1962,25 @@ static string com_comment(exec_context &ec, string cmdline, vector<string> &args
return retval;
}
static string com_comment_prompt(exec_context &ec, const string &cmdline)
{
textview_curses *tc = *lnav_data.ld_view_stack.top();
if (tc != &lnav_data.ld_views[LNV_LOG]) {
return "";
}
logfile_sub_source &lss = lnav_data.ld_log_source;
std::map<content_line_t, bookmark_metadata> &bm = lss.get_user_bookmark_metadata();
auto line_meta = bm.find(lss.at(tc->get_top()));
if (line_meta != bm.end() && !line_meta->second.bm_comment.empty()) {
return trim(cmdline) + " " + trim(line_meta->second.bm_comment);
}
return "";
}
static string com_clear_comment(exec_context &ec, string cmdline, vector<string> &args)
{
string retval;
@ -4030,7 +4073,9 @@ readline_context::command_t STD_COMMANDS[] = {
.with_summary("Attach a comment to the top log line")
.with_parameter(help_text("text", "The comment text"))
.with_example({"This is where it all went wrong"})
.with_tags({"metadata"})
.with_tags({"metadata"}),
com_comment_prompt,
},
{
"clear-comment",
@ -4050,7 +4095,7 @@ readline_context::command_t STD_COMMANDS[] = {
.with_parameter(help_text("tag", "The tags to attach")
.one_or_more())
.with_example({"#BUG123 #needs-review"})
.with_tags({"metadata"})
.with_tags({"metadata"}),
},
{
"untag",

View File

@ -66,6 +66,7 @@ string_attr_type logline::L_FILE("file");
string_attr_type logline::L_PARTITION("partition");
string_attr_type logline::L_MODULE("module");
string_attr_type logline::L_OPID("opid");
string_attr_type logline::L_META("meta");
external_log_format::mod_map_t external_log_format::MODULE_FORMATS;
std::vector<external_log_format *> external_log_format::GRAPH_ORDERED_FORMATS;

View File

@ -76,6 +76,7 @@ public:
static string_attr_type L_PARTITION;
static string_attr_type L_MODULE;
static string_attr_type L_OPID;
static string_attr_type L_META;
/**
* Construct a logline object with the given values.

View File

@ -505,6 +505,14 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
value_out.emplace_back(lr, &logline::L_PARTITION, &bm_iter->second);
}
}
auto bm_iter = this->lss_user_mark_metadata.find(this->at(vis_line_t(row)));
if (bm_iter != this->lss_user_mark_metadata.end()) {
lr.lr_start = 0;
lr.lr_end = -1;
value_out.emplace_back(lr, &logline::L_META, &bm_iter->second);
}
}
if (this->lss_token_file->is_time_adjusted()) {

View File

@ -58,12 +58,24 @@ void rl_change(void *dummy, readline_curses *rc)
switch (lnav_data.ld_mode) {
case LNM_COMMAND: {
static string last_command;
static int generation = 0;
string line = rc->get_line_buffer();
vector<string> args;
auto iter = lnav_commands.end();
split_ws(line, args);
if (args.empty()) {
generation = 0;
} else if (args[0] != last_command) {
last_command = args[0];
generation = 0;
} else {
generation += 1;
}
if (!args.empty()) {
iter = lnav_commands.find(args[0]);
}
@ -120,6 +132,16 @@ void rl_change(void *dummy, readline_curses *rc)
lnav_data.ld_example_source.replace_with(al);
}
if (cmd.c_prompt != nullptr && generation == 0 &&
trim(line) == args[0]) {
string new_prompt = cmd.c_prompt(
lnav_data.ld_exec_context, line);
if (!new_prompt.empty()) {
rc->rewrite_line(line.length(), new_prompt);
}
}
lnav_data.ld_bottom_source.grep_error("");
lnav_data.ld_status[LNS_BOTTOM].window_change();
}

View File

@ -176,7 +176,7 @@ static int recvall(int sock, char *buf, size_t len)
off_t offset = 0;
while (len > 0) {
int rc = recv(sock, &buf[offset], len, 0);
ssize_t rc = recv(sock, &buf[offset], len, 0);
if (rc == -1) {
switch (errno) {
@ -600,12 +600,11 @@ void readline_curses::start()
char type[32];
msg[rc] = '\0';
if (msg[0] == 'i') {
const char *initial = &msg[2];
if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start) == 1) {
const char *initial = &msg[prompt_start];
rl_extend_line_buffer(strlen(initial) + 1);
strcpy(rl_line_buffer, initial);
rl_point = 0;
rl_end = strlen(initial);
rl_redisplay();
}
@ -923,7 +922,7 @@ void readline_curses::focus(int context, const char *prompt, const char *initial
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
wclrtoeol(this->vc_window);
if (initial != nullptr) {
snprintf(buffer, sizeof(buffer), "i:%s", initial);
snprintf(buffer, sizeof(buffer), "i:0:%s", initial);
if (sendstring(this->rc_command_pipe[RCF_MASTER],
buffer,
strlen(buffer) + 1) == -1) {
@ -932,6 +931,18 @@ void readline_curses::focus(int context, const char *prompt, const char *initial
}
}
void readline_curses::rewrite_line(int pos, std::string value)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), "i:%d:%s", pos, value.c_str());
if (sendstring(this->rc_command_pipe[RCF_MASTER],
buffer,
strlen(buffer) + 1) == -1) {
perror("focus: write failed");
}
}
void readline_curses::abort()
{
char buffer[1024];

View File

@ -71,11 +71,14 @@ class readline_context {
public:
typedef std::string (*command_func_t)(exec_context &ec,
std::string cmdline, std::vector<std::string> &args);
typedef std::string (*prompt_func_t)(exec_context &ec,
const std::string &cmdline);
typedef struct _command_t {
const char *c_name;
command_func_t c_func;
struct help_text c_help;
prompt_func_t c_prompt{nullptr};
void operator=(command_func_t func) {
this->c_name = "anon";
@ -312,6 +315,8 @@ public:
void focus(int context, const char *prompt, const char *initial = nullptr);
void rewrite_line(int pos, std::string value);
readline_context *get_active_context() const {
require(this->rc_active_context != -1);

View File

@ -117,6 +117,8 @@ static bool bind_line(sqlite3 *db,
char timestamp[64];
sqlite3_clear_bindings(stmt);
sql_strftime(timestamp, sizeof(timestamp),
lf->original_line_time(line_iter), 'T');
if (sqlite3_bind_text(stmt, 1,
@ -499,9 +501,7 @@ static void load_time_bookmarks(void)
continue;
}
line_iter = lower_bound(lf->begin(),
lf->end(),
log_tv);
line_iter = lower_bound(lf->begin(), lf->end(), log_tv);
while (line_iter != lf->end()) {
struct timeval line_tv = line_iter->get_timeval();
@ -523,12 +523,12 @@ static void load_time_bookmarks(void)
base_content_line + std::distance(lf->begin(), line_iter));
bool meta = false;
if (part_name != NULL && part_name[0] != '\0') {
if (part_name != nullptr && part_name[0] != '\0') {
lss.set_user_mark(&textview_curses::BM_META, line_cl);
bm_meta[line_cl].bm_name = part_name;
meta = true;
}
if (comment != NULL && comment[0] != '\0') {
if (comment != nullptr && comment[0] != '\0') {
lss.set_user_mark(&textview_curses::BM_META,
line_cl);
bm_meta[line_cl].bm_comment = comment;

View File

@ -10,24 +10,27 @@ run_test ${lnav_test} -n \
-c ":comment Hello, World!" \
-c ":tag foo" \
-c ":save-session" \
-c ":write-raw-to -" \
${test_dir}/logfile_access_log.0
check_output ":tag did not work?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
+ Hello, World!
+ #foo
// Hello, World!
-- #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ":load-session" \
-c ";UPDATE access_log SET log_mark = 1" \
-c ":write-to -" \
${test_dir}/logfile_access_log.0
check_output "tag was not saved in session?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
+ Hello, World!
+ #foo
// Hello, World!
-- #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF