diff options
Diffstat (limited to '')
-rw-r--r-- | src/readline_callbacks.cc | 487 |
1 files changed, 399 insertions, 88 deletions
diff --git a/src/readline_callbacks.cc b/src/readline_callbacks.cc index 34a188f..6a751c7 100644 --- a/src/readline_callbacks.cc +++ b/src/readline_callbacks.cc @@ -27,37 +27,40 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "base/fs_util.hh" #include "base/humanize.network.hh" #include "base/injector.hh" +#include "base/paths.hh" +#include "bound_tags.hh" #include "command_executor.hh" #include "config.h" #include "field_overlay_source.hh" #include "help_text_formatter.hh" #include "lnav.hh" #include "lnav_config.hh" -#include "lnav_util.hh" #include "log_format_loader.hh" #include "plain_text_source.hh" #include "readline_curses.hh" #include "readline_highlighters.hh" #include "service_tags.hh" #include "sql_help.hh" -#include "sqlite-extension-func.hh" #include "tailer/tailer.looper.hh" #include "view_helpers.examples.hh" #include "vtab_module.hh" #include "yajlpp/yajlpp.hh" using namespace std::chrono_literals; +using namespace lnav::roles::literals; #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_RE(msg) \ + ANSI_CSI ANSI_BOLD_PARAM ";" ANSI_COLOR_PARAM(COLOR_CYAN) "m" msg ANSI_NORM +#define ANSI_CLS(msg) \ + ANSI_CSI ANSI_BOLD_PARAM \ + ";" ANSI_COLOR_PARAM(COLOR_MAGENTA) "m" msg ANSI_NORM +#define ANSI_KW(msg) \ + ANSI_CSI ANSI_BOLD_PARAM ";" ANSI_COLOR_PARAM(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 @@ -96,6 +99,18 @@ const char *RE_EXAMPLE = " " ANSI_RE("(?-i)") "ABC matches " ANSI_STR("'ABC'") " and " ANSI_UNDERLINE("not") " " ANSI_STR("'abc'") ; +const char* CMD_HELP = + " " ANSI_KW(":goto") " Go to a line #, timestamp, etc...\n" + " " ANSI_KW(":filter-out") " Filter out lines that match a pattern\n" + " " ANSI_KW(":hide-lines-before") " Hide lines before a timestamp\n" + " " ANSI_KW(":open") " Open another file/directory\n"; + +const char* CMD_EXAMPLE = + ANSI_UNDERLINE("Examples") "\n" + " " ANSI_KW(":goto") " 123\n" + " " ANSI_KW(":filter-out") " spam\n" + " " ANSI_KW(":hide-lines-before") " here\n"; + const char *SQL_HELP = " " ANSI_KW("SELECT") " Select rows from a table " " " ANSI_KW("DELETE") " Delete rows from a table\n" @@ -114,8 +129,41 @@ const char *SQL_EXAMPLE = " SELECT * FROM logline LIMIT 10" ; +const char *PRQL_HELP = + " " ANSI_KW("from") " Specify a data source " + " " ANSI_KW("derive") " Derive one or more columns\n" + " " ANSI_KW("select") " Select one or more columns " + " " ANSI_KW("aggregate") " Summary many rows into one\n" + " " ANSI_KW("group") " Partition rows into groups " + " " ANSI_KW("filter") " Pick rows based on their values\n" + ; + +const char *PRQL_EXAMPLE = + ANSI_UNDERLINE("Examples") "\n" + " from %s | stats.count_by { log_level }\n" + " from %s | filter log_line == lnav.view.top_line\n" + ; + static const char* LNAV_CMD_PROMPT = "Enter an lnav command: " ABORT_MSG; +static attr_line_t +format_sql_example(const char* sql_example_fmt) +{ + auto& log_view = lnav_data.ld_views[LNV_LOG]; + auto* lss = (logfile_sub_source*) log_view.get_sub_source(); + attr_line_t retval; + + if (log_view.get_inner_height() > 0) { + auto cl = lss->at(log_view.get_top()); + auto lf = lss->find(cl); + const auto* format_name = lf->get_format()->get_name().get(); + + retval.with_ansi_string(sql_example_fmt, format_name, format_name); + readline_sqlite_highlighter(retval, 0); + } + return retval; +} + void rl_set_help() { @@ -126,24 +174,16 @@ rl_set_help() break; } case ln_mode_t::SQL: { - textview_curses& log_view = lnav_data.ld_views[LNV_LOG]; - auto* lss = (logfile_sub_source*) log_view.get_sub_source(); - 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); - const auto* format_name = lf->get_format()->get_name().get(); - - example_al.with_ansi_string( - SQL_EXAMPLE, format_name, format_name); - readline_sqlite_highlighter(example_al, 0); - } - + auto example_al = format_sql_example(SQL_EXAMPLE); lnav_data.ld_doc_source.replace_with(SQL_HELP); lnav_data.ld_example_source.replace_with(example_al); break; } + case ln_mode_t::COMMAND: { + lnav_data.ld_doc_source.replace_with(CMD_HELP); + lnav_data.ld_example_source.replace_with(CMD_EXAMPLE); + break; + } default: break; } @@ -152,9 +192,9 @@ rl_set_help() static bool rl_sql_help(readline_curses* rc) { - attr_line_t al(rc->get_line_buffer()); - const string_attrs_t& sa = al.get_attrs(); - size_t x = rc->get_x(); + auto al = attr_line_t(rc->get_line_buffer()); + const auto& sa = al.get_attrs(); + size_t x = rc->get_cursor_x(); bool has_doc = false; if (x > 0) { @@ -164,11 +204,15 @@ rl_sql_help(readline_curses* rc) annotate_sql_statement(al); auto avail_help = find_sql_help_for_line(al, x); + auto lang = help_example::language::undefined; + if (lnav::sql::is_prql(al.get_string())) { + lang = help_example::language::prql; + } if (!avail_help.empty()) { size_t help_count = avail_help.size(); - textview_curses& dtc = lnav_data.ld_doc_view; - textview_curses& etc = lnav_data.ld_example_view; + auto& dtc = lnav_data.ld_doc_view; + auto& etc = lnav_data.ld_example_view; unsigned long doc_width, ex_width; vis_line_t doc_height, ex_height; attr_line_t doc_al, ex_al; @@ -185,7 +229,7 @@ rl_sql_help(readline_curses* rc) : help_text_content::full); if (help_count == 1) { format_example_text_for_term( - *ht, eval_example, std::min(70UL, ex_width), ex_al); + *ht, eval_example, std::min(70UL, ex_width), ex_al, lang); } else { doc_al.append("\n"); } @@ -204,6 +248,10 @@ rl_sql_help(readline_curses* rc) auto ident_iter = find_string_attr_containing( sa, &SQL_IDENTIFIER_ATTR, al.nearest_text(x)); + if (ident_iter == sa.end()) { + ident_iter = find_string_attr_containing( + sa, &lnav::sql::PRQL_IDENTIFIER_ATTR, al.nearest_text(x)); + } if (ident_iter != sa.end()) { auto ident = al.get_substring(ident_iter->sa_range); auto intern_ident = intern_string::lookup(ident); @@ -224,10 +272,14 @@ rl_sql_help(readline_curses* rc) } if (!ddl.empty()) { - lnav_data.ld_preview_source.replace_with(ddl) + lnav_data.ld_preview_view[0].set_sub_source( + &lnav_data.ld_preview_source[0]); + lnav_data.ld_preview_view[0].set_overlay_source(nullptr); + lnav_data.ld_preview_source[0] + .replace_with(ddl) .set_text_format(text_format_t::TF_SQL) .truncate_to(30); - lnav_data.ld_preview_status_source.get_description().set_value( + lnav_data.ld_preview_status_source[0].get_description().set_value( "Definition for table -- %s", ident.c_str()); } } @@ -248,19 +300,50 @@ rl_change(readline_curses* rc) "show-fields", }; - textview_curses* tc = get_textview_for_mode(lnav_data.ld_mode); + auto* tc = get_textview_for_mode(lnav_data.ld_mode); tc->get_highlights().erase({highlight_source_t::PREVIEW, "preview"}); tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"}); lnav_data.ld_log_source.set_preview_sql_filter(nullptr); lnav_data.ld_user_message_source.clear(); - lnav_data.ld_preview_source.clear(); - lnav_data.ld_preview_status_source.get_description() - .set_cylon(false) - .clear(); switch (lnav_data.ld_mode) { + case ln_mode_t::SEARCH: { + if (rc->get_line_buffer().empty() && tc->tc_selected_text) { + rc->set_suggestion(tc->tc_selected_text->sti_value); + } + break; + } + case ln_mode_t::SQL: { + static const auto* sql_cmd_map + = injector::get<readline_context::command_map_t*, + sql_cmd_map_tag>(); + + const auto line = rc->get_line_buffer(); + std::vector<std::string> args; + + if (!lnav::sql::is_prql(line)) { + clear_preview(); + } + + split_ws(line, args); + if (!args.empty()) { + auto cmd_iter = sql_cmd_map->find(args[0]); + if (cmd_iter != sql_cmd_map->end()) { + const auto* sql_cmd = cmd_iter->second; + if (sql_cmd->c_prompt != nullptr) { + const auto prompt_res = sql_cmd->c_prompt( + lnav_data.ld_exec_context, line); + + rc->set_suggestion(prompt_res.pr_suggestion); + } + } + } + break; + } case ln_mode_t::COMMAND: { + clear_preview(); + static std::string last_command; static int generation = 0; @@ -307,13 +390,11 @@ rl_change(readline_curses* rc) if (!args.empty()) { iter = lnav_commands.find(args[0]); } - if (iter == lnav_commands.end()) { - lnav_data.ld_doc_source.clear(); - lnav_data.ld_example_source.clear(); - lnav_data.ld_preview_source.clear(); - lnav_data.ld_preview_status_source.get_description() - .set_cylon(false) - .clear(); + if (iter == lnav_commands.end() + || (args.size() == 1 && !endswith(line, " "))) + { + lnav_data.ld_doc_source.replace_with(CMD_HELP); + lnav_data.ld_example_source.replace_with(CMD_EXAMPLE); lnav_data.ld_bottom_source.set_prompt(LNAV_CMD_PROMPT); lnav_data.ld_bottom_source.grep_error(""); } else if (args[0] == "config" && args.size() > 1) { @@ -342,12 +423,12 @@ rl_change(readline_curses* rc) } else if ((args[0] != "filter-expr" && args[0] != "mark-expr") || !rl_sql_help(rc)) { - readline_context::command_t& cmd = *iter->second; - const help_text& ht = cmd.c_help; + const auto& cmd = *iter->second; + const auto& ht = cmd.c_help; if (ht.ht_name) { - textview_curses& dtc = lnav_data.ld_doc_view; - textview_curses& etc = lnav_data.ld_example_view; + auto& dtc = lnav_data.ld_doc_view; + auto& etc = lnav_data.ld_example_view; unsigned long width; vis_line_t height; attr_line_t al; @@ -364,15 +445,17 @@ rl_change(readline_curses* rc) etc.set_needs_update(); } - if (cmd.c_prompt != nullptr && generation == 0 - && trim(line) == args[0]) - { - const auto new_prompt + if (cmd.c_prompt != nullptr) { + const auto prompt_res = cmd.c_prompt(lnav_data.ld_exec_context, line); - if (!new_prompt.empty()) { - rc->rewrite_line(line.length(), new_prompt); + if (generation == 0 && trim(line) == args[0] + && !prompt_res.pr_new_prompt.empty()) + { + rc->rewrite_line(line.length(), + prompt_res.pr_new_prompt); } + rc->set_suggestion(prompt_res.pr_suggestion); } lnav_data.ld_bottom_source.grep_error(""); @@ -381,6 +464,8 @@ rl_change(readline_curses* rc) break; } case ln_mode_t::EXEC: { + clear_preview(); + const auto line = rc->get_line_buffer(); size_t name_end = line.find(' '); const auto script_name = line.substr(0, name_end); @@ -413,7 +498,7 @@ rl_change(readline_curses* rc) static void rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false) { - textview_curses* tc = get_textview_for_mode(mode); + auto* tc = get_textview_for_mode(mode); std::string term_val; std::string name; @@ -440,10 +525,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false) lnav_data.ld_exec_context.ec_dry_run = true; lnav_data.ld_preview_generation += 1; - lnav_data.ld_preview_status_source.get_description() - .set_cylon(false) - .clear(); - lnav_data.ld_preview_source.clear(); + clear_preview(); auto result = execute_command(lnav_data.ld_exec_context, rc->get_value().get_string()); @@ -463,20 +545,215 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false) result.unwrapErr().um_message.get_string()); } - lnav_data.ld_preview_view.reload_data(); + lnav_data.ld_preview_view[0].reload_data(); lnav_data.ld_exec_context.ec_dry_run = false; return; } case ln_mode_t::SQL: { - term_val = trim(rc->get_value().get_string() + ";"); + term_val = trim(rc->get_value().get_string()); if (!term_val.empty() && term_val[0] == '.') { lnav_data.ld_bottom_source.grep_error(""); - } else if (!sqlite3_complete(term_val.c_str())) { + } else if (lnav::sql::is_prql(term_val)) { + std::string alt_msg; + + lnav_data.ld_doc_source.replace_with(PRQL_HELP); + lnav_data.ld_example_source.replace_with( + format_sql_example(PRQL_EXAMPLE)); + lnav_data.ld_db_preview_source[0].clear(); + lnav_data.ld_db_preview_source[1].clear(); + rc->clear_possibilities(ln_mode_t::SQL, "prql-expr"); + + auto orig_prql_stmt = attr_line_t(term_val); + orig_prql_stmt.rtrim("| \r\n\t"); + annotate_sql_statement(orig_prql_stmt); + auto cursor_x = rc->get_cursor_x(); + if (cursor_x > orig_prql_stmt.get_string().length()) { + cursor_x = orig_prql_stmt.length() - 1; + } + auto curr_stage_iter + = find_string_attr_containing(orig_prql_stmt.get_attrs(), + &lnav::sql::PRQL_STAGE_ATTR, + cursor_x); + auto curr_stage_prql = orig_prql_stmt.subline( + 0, curr_stage_iter->sa_range.lr_end); + for (auto riter = curr_stage_prql.get_attrs().rbegin(); + riter != curr_stage_prql.get_attrs().rend(); + ++riter) + { + if (riter->sa_type != &lnav::sql::PRQL_STAGE_ATTR + || riter->sa_range.lr_start == 0) + { + continue; + } + curr_stage_prql.insert(riter->sa_range.lr_start, + "| take 10000 "); + } + curr_stage_prql.rtrim(); + curr_stage_prql.append(" | take 5"); + log_debug("preview prql: %s", + curr_stage_prql.get_string().c_str()); + + size_t curr_stage_index = 0; + if (curr_stage_iter->sa_range.lr_start > 0) { + auto prev_stage_iter = find_string_attr_containing( + orig_prql_stmt.get_attrs(), + &lnav::sql::PRQL_STAGE_ATTR, + curr_stage_iter->sa_range.lr_start - 1); + auto prev_stage_prql = orig_prql_stmt.subline( + 0, prev_stage_iter->sa_range.lr_end); + for (auto riter = prev_stage_prql.get_attrs().rbegin(); + riter != prev_stage_prql.get_attrs().rend(); + ++riter) + { + if (riter->sa_type != &lnav::sql::PRQL_STAGE_ATTR + || riter->sa_range.lr_start == 0) + { + continue; + } + prev_stage_prql.insert(riter->sa_range.lr_start, + "| take 10000 "); + } + prev_stage_prql.append(" | take 5"); + + curr_stage_index = 1; + auto db_guard = lnav_data.ld_exec_context.enter_db_source( + &lnav_data.ld_db_preview_source[0]); + auto exec_res = execute_sql(lnav_data.ld_exec_context, + prev_stage_prql.get_string(), + alt_msg); + lnav_data.ld_preview_status_source[0] + .get_description() + .set_value("Result for query: %s", + prev_stage_prql.get_string().c_str()); + if (exec_res.isOk()) { + for (const auto& hdr : + lnav_data.ld_db_preview_source[0].dls_headers) + { + rc->add_possibility( + ln_mode_t::SQL, + "prql-expr", + lnav::prql::quote_ident(hdr.hm_name)); + } + + lnav_data.ld_preview_view[0].set_sub_source( + &lnav_data.ld_db_preview_source[0]); + lnav_data.ld_preview_view[0].set_overlay_source( + &lnav_data.ld_db_preview_overlay_source[0]); + } else { + lnav_data.ld_preview_source[0].replace_with( + exec_res.unwrapErr().to_attr_line()); + lnav_data.ld_preview_view[0].set_sub_source( + &lnav_data.ld_preview_source[0]); + lnav_data.ld_preview_view[0].set_overlay_source( + nullptr); + } + } + + auto db_guard = lnav_data.ld_exec_context.enter_db_source( + &lnav_data.ld_db_preview_source[curr_stage_index]); + auto exec_res = execute_sql(lnav_data.ld_exec_context, + curr_stage_prql.get_string(), + alt_msg); + auto err = exec_res.isErr() + ? exec_res.unwrapErr() + : lnav::console::user_message::ok({}); + if (exec_res.isErr()) { + lnav_data.ld_bottom_source.grep_error( + err.um_reason.get_string()); + + curr_stage_prql.erase(curr_stage_prql.get_string().length() + - 9); + auto near = curr_stage_prql.get_string().length() - 1; + while (near > 0) { + auto paren_iter = rfind_string_attr_if( + curr_stage_prql.get_attrs(), + near, + [](const string_attr& sa) { + return sa.sa_type + == &lnav::sql::PRQL_UNTERMINATED_PAREN_ATTR; + }); + + if (paren_iter == curr_stage_prql.get_attrs().end()) { + break; + } + switch ( + curr_stage_prql + .get_string()[paren_iter->sa_range.lr_start]) + { + case '(': + curr_stage_prql.append(")"); + break; + case '{': + curr_stage_prql.append("}"); + break; + } + near = paren_iter->sa_range.lr_start - 1; + } + + curr_stage_prql.append(" | take 5"); + auto exec_termed_res + = execute_sql(lnav_data.ld_exec_context, + curr_stage_prql.get_string(), + alt_msg); + if (exec_termed_res.isErr()) { + err = exec_termed_res.unwrapErr(); + } + } else { + lnav_data.ld_bottom_source.grep_error(""); + } + + rc->add_possibility( + ln_mode_t::SQL, "prql-expr", lnav::sql::prql_keywords); + for (const auto& pair : lnav::sql::prql_functions) { + rc->add_possibility( + ln_mode_t::SQL, "prql-expr", pair.first); + } + + rl_sql_help(rc); + + lnav_data.ld_preview_status_source[curr_stage_index] + .get_description() + .set_value("Result for query: %s", + curr_stage_prql.get_string().c_str()); + if (!lnav_data.ld_db_preview_source[curr_stage_index] + .dls_headers.empty()) + { + if (curr_stage_index == 0) { + for (const auto& hdr : + lnav_data.ld_db_preview_source[curr_stage_index] + .dls_headers) + { + rc->add_possibility( + ln_mode_t::SQL, + "prql-expr", + lnav::prql::quote_ident(hdr.hm_name)); + } + } + + lnav_data.ld_preview_view[curr_stage_index].set_sub_source( + &lnav_data.ld_db_preview_source[curr_stage_index]); + lnav_data.ld_preview_view[curr_stage_index] + .set_overlay_source( + &lnav_data.ld_db_preview_overlay_source + [curr_stage_index]); + } else if (exec_res.isErr()) { + lnav_data.ld_preview_source[curr_stage_index].replace_with( + err.to_attr_line()); + lnav_data.ld_preview_view[curr_stage_index].set_sub_source( + &lnav_data.ld_preview_source[curr_stage_index]); + lnav_data.ld_preview_view[curr_stage_index] + .set_overlay_source(nullptr); + } + return; + } + + term_val += ";"; + if (!sqlite3_complete(term_val.c_str())) { lnav_data.ld_bottom_source.grep_error( - "sql error: incomplete statement"); + "SQL error: incomplete statement"); } else { auto_mem<sqlite3_stmt> stmt(sqlite3_finalize); int retcode; @@ -491,7 +768,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false) const char* errmsg = sqlite3_errmsg(lnav_data.ld_db); lnav_data.ld_bottom_source.grep_error( - fmt::format(FMT_STRING("sql error: {}"), errmsg)); + fmt::format(FMT_STRING("SQL error: {}"), errmsg)); } else { lnav_data.ld_bottom_source.grep_error(""); } @@ -499,7 +776,6 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false) if (!rl_sql_help(rc)) { rl_set_help(); - lnav_data.ld_preview_source.clear(); } return; } @@ -538,10 +814,7 @@ lnav_rl_abort(readline_curses* rc) lnav_data.ld_bottom_source.set_prompt(""); lnav_data.ld_example_source.clear(); lnav_data.ld_doc_source.clear(); - lnav_data.ld_preview_status_source.get_description() - .set_cylon(false) - .clear(); - lnav_data.ld_preview_source.clear(); + clear_preview(); tc->get_highlights().erase({highlight_source_t::PREVIEW, "preview"}); tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"}); lnav_data.ld_log_source.set_preview_sql_filter(nullptr); @@ -576,10 +849,7 @@ rl_callback_int(readline_curses* rc, bool is_alt) lnav_data.ld_bottom_source.set_prompt(""); lnav_data.ld_doc_source.clear(); lnav_data.ld_example_source.clear(); - lnav_data.ld_preview_status_source.get_description() - .set_cylon(false) - .clear(); - lnav_data.ld_preview_source.clear(); + clear_preview(); tc->get_highlights().erase({highlight_source_t::PREVIEW, "preview"}); tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"}); lnav_data.ld_log_source.set_preview_sql_filter(nullptr); @@ -697,13 +967,21 @@ rl_callback_int(readline_curses* rc, bool is_alt) break; case ln_mode_t::SQL: { + auto sql_str = rc->get_value().get_string(); ec.ec_source.back().s_content - = fmt::format(FMT_STRING(";{}"), rc->get_value().get_string()); + = fmt::format(FMT_STRING(";{}"), sql_str); readline_lnav_highlighter(ec.ec_source.back().s_content, -1); ec.ec_source.back().s_content.with_attr_for_all( VC_ROLE.value(role_t::VCR_QUOTED_CODE)); - auto result - = execute_sql(ec, rc->get_value().get_string(), alt_msg); + + rc->set_attr_value( + lnav::console::user_message::info( + attr_line_t("executing SQL statement, press ") + .append("CTRL+]"_hotkey) + .append(" to cancel")) + .to_attr_line()); + rc->set_needs_update(); + auto result = execute_sql(ec, sql_str, alt_msg); auto& dls = lnav_data.ld_db_row_source; attr_line_t prompt; @@ -736,23 +1014,28 @@ rl_callback_int(readline_curses* rc, bool is_alt) } case ln_mode_t::EXEC: { - auto_mem<FILE> tmpout(fclose); - - tmpout = std::tmpfile(); + std::error_code errc; + ghc::filesystem::create_directories(lnav::paths::workdir(), errc); + auto open_temp_res = lnav::filesystem::open_temp_file( + lnav::paths::workdir() / "exec.XXXXXX"); - if (!tmpout) { + if (open_temp_res.isErr()) { rc->set_value(fmt::format( FMT_STRING("Unable to open temporary output file: {}"), - strerror(errno))); + open_temp_res.unwrapErr())); } else { - auto fd_copy = auto_fd::dup_of(fileno(tmpout)); char desc[256], timestamp[32]; time_t current_time = time(nullptr); const auto path_and_args = rc->get_value(); + auto tmp_pair = open_temp_res.unwrap(); + auto fd_copy = tmp_pair.second.dup(); { exec_context::output_guard og( - ec, "tmp", std::make_pair(tmpout.release(), fclose)); + ec, + "tmp", + std::make_pair(fdopen(tmp_pair.second.release(), "w"), + fclose)); auto exec_res = execute_file(ec, path_and_args.get_string()); @@ -770,22 +1053,24 @@ rl_callback_int(readline_curses* rc, bool is_alt) } } + tm current_tm; struct stat st; if (fstat(fd_copy, &st) != -1 && st.st_size > 0) { strftime(timestamp, sizeof(timestamp), "%a %b %d %H:%M:%S %Z", - localtime(¤t_time)); + localtime_r(¤t_time, ¤t_tm)); snprintf(desc, sizeof(desc), "Output of %s (%s)", path_and_args.get_string().c_str(), timestamp); - lnav_data.ld_active_files.fc_file_names[desc] - .with_fd(std::move(fd_copy)) + lnav_data.ld_active_files.fc_file_names[tmp_pair.first] + .with_filename(desc) .with_include_in_session(false) - .with_detect_format(false); + .with_detect_format(false) + .with_init_location(0_vl); lnav_data.ld_files_to_front.emplace_back(desc, 0_vl); if (lnav_data.ld_rl_view != nullptr) { @@ -890,7 +1175,7 @@ rl_focus(readline_curses* rc) auto fos = (field_overlay_source*) lnav_data.ld_views[LNV_LOG] .get_overlay_source(); - fos->fos_contexts.emplace("", false, true); + fos->fos_contexts.emplace("", false, true, true); get_textview_for_mode(lnav_data.ld_mode)->save_current_search(); } @@ -902,7 +1187,33 @@ rl_blur(readline_curses* rc) .get_overlay_source(); fos->fos_contexts.pop(); - lnav_data.ld_views[LNV_LOG].set_sync_selection_and_top( - fos->fos_contexts.top().c_show); + for (auto& tc : lnav_data.ld_views) { + tc.set_sync_selection_and_top(false); + } lnav_data.ld_preview_generation += 1; } + +readline_context::split_result_t +prql_splitter(readline_context& rc, const std::string& cmdline) +{ + auto stmt = attr_line_t(cmdline); + readline_context::split_result_t retval; + readline_context::stage st; + + lnav::sql::annotate_prql_statement(stmt); + for (const auto& attr : stmt.get_attrs()) { + if (attr.sa_type == &lnav::sql::PRQL_STAGE_ATTR) { + } else if (attr.sa_type == &lnav::sql::PRQL_PIPE_ATTR) { + retval.sr_stages.emplace_back(st); + st.s_args.clear(); + } else { + st.s_args.emplace_back(attr.sa_range); + } + } + if (!cmdline.empty() && isspace(cmdline.back())) { + st.s_args.emplace_back(cmdline.length(), cmdline.length()); + } + retval.sr_stages.emplace_back(st); + + return retval; +} |