mirror of
https://github.com/tstack/lnav
synced 2024-11-03 23:15:38 +00:00
[keymap] support for /dev/clipboard in :write commands
The copy hotkey is now implemented in the keymap and the ':write-*' commands were enhanced to recognize /dev/clipboard as a special file name for writing to the system clipboard.
This commit is contained in:
parent
0b822739ea
commit
db6c619e4e
4
NEWS
4
NEWS
@ -1,5 +1,9 @@
|
||||
|
||||
lnav v0.8.5:
|
||||
Features:
|
||||
* The ":write-*" commands will now accept "/dev/clipboard" as a file name
|
||||
that writes to the system clipboard.
|
||||
|
||||
Interface Changes:
|
||||
* The auto-complete behavior in the prompt has been modified to fall back
|
||||
to a fuzzy search if the prefix search finds no matches. For example,
|
||||
|
@ -131,11 +131,14 @@ Output
|
||||
* append-to <file> - Append any bookmarked lines in the current view to the
|
||||
given file.
|
||||
* write-to <file> - Overwrite the given file with any bookmarked lines in
|
||||
the current view. Use '-' to write the lines to the terminal.
|
||||
the current view. Use '-' to write the lines to the terminal and '/dev/clipboard'
|
||||
to write to the system clipboard.
|
||||
* write-csv-to <file> - Write SQL query results to the given file in CSV format.
|
||||
Use '-' to write the lines to the terminal.
|
||||
Use '-' to write the lines to the terminal and '/dev/clipboard' to write to
|
||||
the system clipboard.
|
||||
* write-json-to <file> - Write SQL query results to the given file in JSON
|
||||
format. Use '-' to write the lines to the terminal.
|
||||
format. Use '-' to write the lines to the terminal and '/dev/clipboard'
|
||||
to write to the system clipboard..
|
||||
* pipe-to <shell-cmd> - Pipe the bookmarked lines in the current view to a
|
||||
shell command and open the output in lnav.
|
||||
* pipe-line-to <shell-cmd> - Pipe the top line in the current view to a shell
|
||||
|
@ -101,45 +101,6 @@ public:
|
||||
vector<logline_value> lh_line_values;
|
||||
};
|
||||
|
||||
/* XXX For one, this code is kinda crappy. For two, we should probably link
|
||||
* directly with X so we don't need to have xclip installed and it'll work if
|
||||
* we're ssh'd into a box.
|
||||
*/
|
||||
static void copy_to_xclip(void)
|
||||
{
|
||||
textview_curses *tc = lnav_data.ld_view_stack.back();
|
||||
|
||||
bookmark_vector<vis_line_t> &bv =
|
||||
tc->get_bookmarks()[&textview_curses::BM_USER];
|
||||
bookmark_vector<vis_line_t>::iterator iter;
|
||||
auto_mem<FILE> pfile(pclose);
|
||||
int line_count = 0;
|
||||
string line;
|
||||
|
||||
pfile = open_clipboard(CT_GENERAL);
|
||||
|
||||
if (!pfile.in()) {
|
||||
alerter::singleton().chime();
|
||||
lnav_data.ld_rl_view->set_value(
|
||||
"error: Unable to copy to clipboard. "
|
||||
"Make sure xclip or pbcopy is installed.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (iter = bv.begin(); iter != bv.end(); iter++) {
|
||||
tc->grep_value_for_line(*iter, line);
|
||||
fprintf(pfile, "%s\n", line.c_str());
|
||||
line_count += 1;
|
||||
}
|
||||
|
||||
char buffer[128];
|
||||
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Copied " ANSI_BOLD("%d") " lines to the clipboard",
|
||||
line_count);
|
||||
lnav_data.ld_rl_view->set_value(buffer);
|
||||
}
|
||||
|
||||
void handle_paging_key(int ch)
|
||||
{
|
||||
if (lnav_data.ld_view_stack.empty()) {
|
||||
@ -210,12 +171,6 @@ void handle_paging_key(int ch)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
copy_to_xclip();
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
|
||||
C, "to clear marked messages"));
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
if (lss) {
|
||||
lss->text_clear_marks(&textview_curses::BM_USER);
|
||||
@ -673,24 +628,20 @@ void handle_paging_key(int ch)
|
||||
if (tc == &lnav_data.ld_views[LNV_DB]) {
|
||||
db_label_source &dls = lnav_data.ld_db_row_source;
|
||||
|
||||
for (vector<db_label_source::header_meta>::iterator iter = dls.dls_headers.begin();
|
||||
iter != dls.dls_headers.end();
|
||||
++iter) {
|
||||
if (!iter->hm_graphable) {
|
||||
for (auto &dls_header : dls.dls_headers) {
|
||||
if (!dls_header.hm_graphable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
|
||||
"numeric-colname",
|
||||
iter->hm_name);
|
||||
dls_header.hm_name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (vector<logline_value>::iterator iter = ldh.ldh_line_values.begin();
|
||||
iter != ldh.ldh_line_values.end();
|
||||
++iter) {
|
||||
const logline_value_stats *stats = iter->lv_format->stats_for_value(
|
||||
iter->lv_name);
|
||||
for (auto &ldh_line_value : ldh.ldh_line_values) {
|
||||
const logline_value_stats *stats = ldh_line_value.lv_format->stats_for_value(
|
||||
ldh_line_value.lv_name);
|
||||
|
||||
if (stats == NULL) {
|
||||
continue;
|
||||
@ -698,14 +649,13 @@ void handle_paging_key(int ch)
|
||||
|
||||
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
|
||||
"numeric-colname",
|
||||
iter->lv_name.to_string());
|
||||
ldh_line_value.lv_name.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
for (vector<string>::iterator iter = ldh.ldh_namer->cn_names.begin();
|
||||
iter != ldh.ldh_namer->cn_names.end();
|
||||
++iter) {
|
||||
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "colname", *iter);
|
||||
for (auto &cn_name : ldh.ldh_namer->cn_names) {
|
||||
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "colname",
|
||||
cn_name);
|
||||
}
|
||||
for (auto iter : ldh.ldh_namer->cn_builtin_names) {
|
||||
if (iter == "col") {
|
||||
@ -834,7 +784,7 @@ void handle_paging_key(int ch)
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if (lnav_data.ld_text_source.current_file() == NULL) {
|
||||
if (lnav_data.ld_text_source.current_file() == nullptr) {
|
||||
alerter::singleton().chime();
|
||||
lnav_data.ld_rl_view->set_value("No text files loaded");
|
||||
}
|
||||
|
@ -8,7 +8,8 @@
|
||||
"keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view",
|
||||
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view",
|
||||
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
|
||||
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}z${ansi_norm} to zoom in/out"
|
||||
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}z${ansi_norm} to zoom in/out",
|
||||
"keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages"
|
||||
},
|
||||
"keymap_def": {
|
||||
"default": {
|
||||
@ -49,6 +50,11 @@
|
||||
":next-mark query"
|
||||
],
|
||||
|
||||
"x63": [
|
||||
":eval :alt-msg ${keymap_def_clear}",
|
||||
":write-to /dev/clipboard"
|
||||
],
|
||||
|
||||
"x67": [":goto 0"],
|
||||
"x6d": [
|
||||
":mark",
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "relative_time.hh"
|
||||
#include "log_search_table.hh"
|
||||
#include "shlex.hh"
|
||||
#include "sysclip.hh"
|
||||
#include "yajl/api/yajl_parse.h"
|
||||
#include "db_sub_source.hh"
|
||||
|
||||
@ -500,10 +501,11 @@ static void json_write_row(yajl_gen handle, int row)
|
||||
|
||||
static string com_save_to(exec_context &ec, string cmdline, vector<string> &args)
|
||||
{
|
||||
FILE *outfile = NULL, *toclose = NULL;
|
||||
FILE *outfile = nullptr, *toclose = nullptr;
|
||||
const char *mode = "";
|
||||
string fn, retval;
|
||||
bool to_term = false;
|
||||
int (*closer)(FILE *) = fclose;
|
||||
|
||||
if (args.empty()) {
|
||||
args.emplace_back("filename");
|
||||
@ -564,7 +566,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
outfile = tmpfile();
|
||||
toclose = outfile;
|
||||
}
|
||||
else if (split_args[0] == "-") {
|
||||
else if (split_args[0] == "-" || split_args[0] == "/dev/stdout") {
|
||||
if (lnav_data.ld_output_stack.empty()) {
|
||||
outfile = stdout;
|
||||
nodelay(lnav_data.ld_window, 0);
|
||||
@ -586,13 +588,24 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
lnav_data.ld_stdout_used = true;
|
||||
}
|
||||
}
|
||||
else if ((outfile = fopen(split_args[0].c_str(), mode)) == NULL) {
|
||||
else if (split_args[0] == "/dev/clipboard") {
|
||||
toclose = outfile = open_clipboard(CT_GENERAL);
|
||||
closer = pclose;
|
||||
if (!outfile) {
|
||||
alerter::singleton().chime();
|
||||
return "error: Unable to copy to clipboard. "
|
||||
"Make sure xclip or pbcopy is installed.";
|
||||
}
|
||||
}
|
||||
else if ((outfile = fopen(split_args[0].c_str(), mode)) == nullptr) {
|
||||
return "error: unable to open file -- " + split_args[0];
|
||||
}
|
||||
else {
|
||||
toclose = outfile;
|
||||
}
|
||||
|
||||
int line_count = 0;
|
||||
|
||||
if (args[0] == "write-csv-to") {
|
||||
std::vector<std::vector<const char *> >::iterator row_iter;
|
||||
std::vector<const char *>::iterator iter;
|
||||
@ -629,6 +642,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
first = false;
|
||||
}
|
||||
fprintf(outfile, "\n");
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
}
|
||||
else if (args[0] == "write-cols-to") {
|
||||
@ -648,6 +663,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
text_sub_source::RF_RAW);
|
||||
fputs(line.c_str(), outfile);
|
||||
fputc('\n', outfile);
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
}
|
||||
else if (args[0] == "write-json-to") {
|
||||
@ -696,6 +713,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
fputs(*iter, outfile);
|
||||
}
|
||||
fprintf(outfile, "\n");
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
} else {
|
||||
bool wrapped = tc->get_word_wrap();
|
||||
@ -714,6 +733,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
log_perror(write(STDOUT_FILENO, lr.substr(al.get_string()),
|
||||
lr.sublen(al.get_string())));
|
||||
log_perror(write(STDOUT_FILENO, "\n", 1));
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
|
||||
tc->set_word_wrap(wrapped);
|
||||
@ -731,6 +752,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
}
|
||||
tc->grep_value_for_line(*iter, line);
|
||||
fprintf(outfile, "%s\n", line.c_str());
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,13 +779,15 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
.truncate_to(10);
|
||||
lnav_data.ld_preview_status_source.get_description()
|
||||
.set_value("First lines of file: %s", fn.c_str());
|
||||
} else {
|
||||
retval = "Wrote " + to_string(line_count) + " line to " + split_args[0];
|
||||
}
|
||||
if (toclose != NULL) {
|
||||
fclose(toclose);
|
||||
if (toclose != nullptr) {
|
||||
closer(toclose);
|
||||
}
|
||||
outfile = NULL;
|
||||
outfile = nullptr;
|
||||
|
||||
return "";
|
||||
return retval;
|
||||
}
|
||||
|
||||
static string com_pipe_to(exec_context &ec, string cmdline, vector<string> &args)
|
||||
@ -834,13 +859,11 @@ static string com_pipe_to(exec_context &ec, string cmdline, vector<string> &args
|
||||
sql_strftime(tmp_str, sizeof(tmp_str), ldh.ldh_line->get_timeval());
|
||||
setenv("log_time", tmp_str, 1);
|
||||
setenv("log_path", ldh.ldh_file->get_filename().c_str(), 1);
|
||||
for (vector<logline_value>::iterator iter = ldh.ldh_line_values.begin();
|
||||
iter != ldh.ldh_line_values.end();
|
||||
++iter) {
|
||||
setenv(iter->lv_name.get(), iter->to_string().c_str(), 1);
|
||||
for (auto &ldh_line_value : ldh.ldh_line_values) {
|
||||
setenv(ldh_line_value.lv_name.get(),
|
||||
ldh_line_value.to_string().c_str(), 1);
|
||||
}
|
||||
data_parser::element_list_t::iterator iter =
|
||||
ldh.ldh_parser->dp_pairs.begin();
|
||||
auto iter = ldh.ldh_parser->dp_pairs.begin();
|
||||
for (size_t lpc = 0; lpc < ldh.ldh_parser->dp_pairs.size(); lpc++, ++iter) {
|
||||
std::string colname = ldh.ldh_parser->get_element_string(
|
||||
iter->e_sub_elements->front());
|
||||
|
@ -51,7 +51,7 @@ static clip_command *get_commands()
|
||||
static clip_command X_CMDS[] = {
|
||||
{ { "xclip -i > /dev/null 2>&1",
|
||||
"xclip -o < /dev/null 2>/dev/null" } },
|
||||
{ { NULL, NULL } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
if (system("which pbcopy > /dev/null 2>&1") == 0) {
|
||||
return OSX_CMDS;
|
||||
@ -59,7 +59,7 @@ static clip_command *get_commands()
|
||||
if (system("which xclip > /dev/null 2>&1") == 0) {
|
||||
return X_CMDS;
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* XXX For one, this code is kinda crappy. For two, we should probably link
|
||||
@ -70,9 +70,9 @@ FILE *open_clipboard(clip_type_t type, clip_op_t op)
|
||||
{
|
||||
const char *mode = op == CO_WRITE ? "w" : "r";
|
||||
clip_command *cc = get_commands();
|
||||
FILE *pfile = NULL;
|
||||
FILE *pfile = nullptr;
|
||||
|
||||
if (cc != NULL && cc[type].cc_cmd[op] != NULL) {
|
||||
if (cc != nullptr && cc[type].cc_cmd[op] != nullptr) {
|
||||
pfile = popen(cc[type].cc_cmd[op], mode);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user