@@ -303,6 +303,205 @@ com_toggle_breakpoint(exec_context& ec,
303303 return com_breakpoint (ec, cmdline, args);
304304}
305305
306+ static std::string
307+ schema_id_for_point (const std::string& point,
308+ logfile_sub_source* lss,
309+ textview_curses* tc)
310+ {
311+ static const auto POINT_RE
312+ = lnav::pcre2pp::code::from_const (R"( ^(?:([^:\s]+):)?([^:]+):(\d+)$)" );
313+ thread_local auto md = lnav::pcre2pp::match_data::unitialized ();
314+
315+ if (POINT_RE .capture_from (point).into (md).found_p ()) {
316+ std::string format_name;
317+
318+ if (md[1 ]) {
319+ format_name = md[1 ]->to_string ();
320+ } else if (lss != nullptr ) {
321+ auto line_pair = lss->find_line_with_file (tc->get_selection ());
322+ if (line_pair) {
323+ format_name = line_pair->first ->get_format_name ().to_string ();
324+ }
325+ }
326+
327+ if (!format_name.empty ()) {
328+ auto h = hasher ();
329+ h.update (format_name);
330+ h.update (md[2 ].value ());
331+ h.update (md[3 ].value ());
332+ return h.to_string ();
333+ }
334+ }
335+
336+ return {};
337+ }
338+
339+ static Result<std::string, lnav::console::user_message>
340+ com_disable_breakpoint (exec_context& ec,
341+ std::string cmdline,
342+ std::vector<std::string>& args)
343+ {
344+ static const intern_string_t SRC = intern_string::lookup (" point" );
345+ std::string retval;
346+
347+ auto pat = trim (remaining_args (cmdline, args));
348+ shlex lexer (pat);
349+ auto split_args_res = lexer.split (ec.create_resolver ());
350+ if (split_args_res.isErr ()) {
351+ auto split_err = split_args_res.unwrapErr ();
352+ auto um
353+ = lnav::console::user_message::error (
354+ " unable to parse breakpoint" )
355+ .with_reason (split_err.se_error .te_msg )
356+ .with_snippet (lnav::console::snippet::from (
357+ SRC , lexer.to_attr_line (split_err.se_error )))
358+ .move ();
359+
360+ return Err (um);
361+ }
362+
363+ auto * tc = *lnav_data.ld_view_stack .top ();
364+ auto * lss = dynamic_cast <logfile_sub_source*>(tc->get_sub_source ());
365+ auto & bps = lnav_data.ld_log_source .get_breakpoints ();
366+ auto split_args = split_args_res.unwrap ()
367+ | lnav::itertools::map ([](const auto & elem) { return elem.se_value ; });
368+
369+ if (ec.ec_dry_run ) {
370+ return Ok (retval);
371+ }
372+
373+ std::vector<std::string> disabled;
374+
375+ if (split_args.empty ()) {
376+ if (lss == nullptr ) {
377+ return ec.make_error (
378+ " A breakpoint definition must be given if the "
379+ " top view is not the LOG view" );
380+ }
381+
382+ auto schema_id
383+ = get_current_line_schema_id (*lss, tc->get_selection ().value ());
384+ if (schema_id.empty ()) {
385+ return ec.make_error (
386+ " Could not determine schema for the current line" );
387+ }
388+
389+ auto it = bps.find (schema_id);
390+ if (it == bps.end ()) {
391+ return ec.make_error (" No breakpoint set for the current line" );
392+ }
393+
394+ it->second .bp_enabled = false ;
395+ disabled.emplace_back (it->second .bp_description );
396+ }
397+
398+ for (const auto & point : split_args) {
399+ auto schema_id = schema_id_for_point (point, lss, tc);
400+ if (schema_id.empty ()) {
401+ return ec.make_error (" Invalid breakpoint: {}" , point);
402+ }
403+
404+ auto it = bps.find (schema_id);
405+ if (it == bps.end ()) {
406+ return ec.make_error (" No breakpoint matching: {}" , point);
407+ }
408+
409+ it->second .bp_enabled = false ;
410+ disabled.emplace_back (it->second .bp_description );
411+ }
412+
413+ if (!disabled.empty ()) {
414+ retval = fmt::format (FMT_STRING (" info: disabled breakpoints -- {}" ),
415+ fmt::join (disabled, " , " ));
416+ }
417+
418+ return Ok (retval);
419+ }
420+
421+ static Result<std::string, lnav::console::user_message>
422+ com_enable_breakpoint (exec_context& ec,
423+ std::string cmdline,
424+ std::vector<std::string>& args)
425+ {
426+ static const intern_string_t SRC = intern_string::lookup (" point" );
427+ std::string retval;
428+
429+ auto pat = trim (remaining_args (cmdline, args));
430+ shlex lexer (pat);
431+ auto split_args_res = lexer.split (ec.create_resolver ());
432+ if (split_args_res.isErr ()) {
433+ auto split_err = split_args_res.unwrapErr ();
434+ auto um
435+ = lnav::console::user_message::error (
436+ " unable to parse breakpoint" )
437+ .with_reason (split_err.se_error .te_msg )
438+ .with_snippet (lnav::console::snippet::from (
439+ SRC , lexer.to_attr_line (split_err.se_error )))
440+ .move ();
441+
442+ return Err (um);
443+ }
444+
445+ auto * tc = *lnav_data.ld_view_stack .top ();
446+ auto * lss = dynamic_cast <logfile_sub_source*>(tc->get_sub_source ());
447+ auto & bps = lnav_data.ld_log_source .get_breakpoints ();
448+ auto split_args = split_args_res.unwrap ()
449+ | lnav::itertools::map ([](const auto & elem) { return elem.se_value ; });
450+
451+ std::vector<std::string> enabled;
452+
453+ if (split_args.empty ()) {
454+ if (lss == nullptr ) {
455+ return ec.make_error (
456+ " A breakpoint definition must be given if the "
457+ " top view is not the LOG view" );
458+ }
459+
460+ auto schema_id
461+ = get_current_line_schema_id (*lss, tc->get_selection ().value ());
462+ if (schema_id.empty ()) {
463+ return ec.make_error (
464+ " Could not determine schema for the current line" );
465+ }
466+
467+ auto it = bps.find (schema_id);
468+ if (it != bps.end ()) {
469+ if (ec.ec_dry_run ) {
470+ return Ok (retval);
471+ }
472+ it->second .bp_enabled = true ;
473+ enabled.emplace_back (it->second .bp_description );
474+ } else {
475+ return com_breakpoint (ec, cmdline, args);
476+ }
477+ }
478+
479+ for (const auto & point : split_args) {
480+ auto schema_id = schema_id_for_point (point, lss, tc);
481+ if (schema_id.empty ()) {
482+ return ec.make_error (" Invalid breakpoint: {}" , point);
483+ }
484+
485+ auto it = bps.find (schema_id);
486+ if (it != bps.end ()) {
487+ if (ec.ec_dry_run ) {
488+ continue ;
489+ }
490+ it->second .bp_enabled = true ;
491+ enabled.emplace_back (it->second .bp_description );
492+ } else {
493+ return com_breakpoint (ec, cmdline, args);
494+ }
495+ }
496+
497+ if (!enabled.empty ()) {
498+ retval = fmt::format (FMT_STRING (" info: enabled breakpoints -- {}" ),
499+ fmt::join (enabled, " , " ));
500+ }
501+
502+ return Ok (retval);
503+ }
504+
306505static readline_context::command_t BREAKPOINT_COMMANDS [] = {
307506 {
308507 " breakpoint" ,
@@ -346,6 +545,36 @@ static readline_context::command_t BREAKPOINT_COMMANDS[] = {
346545 .one_or_more ())
347546 .with_example ({" To clear all breakpoints" , " *" }),
348547 },
548+ {
549+ " disable-breakpoint" ,
550+ com_disable_breakpoint,
551+ help_text (" :disable-breakpoint" )
552+ .with_summary (
553+ " Disable a breakpoint for the given "
554+ " [<format>:]<file>:<line> tuples "
555+ " or the current line" )
556+ .with_parameter (
557+ help_text (" point" )
558+ .with_summary (
559+ " The file and line number of the breakpoint" )
560+ .with_format (help_parameter_format_t ::HPF_BREAKPOINT )
561+ .zero_or_more ()),
562+ },
563+ {
564+ " enable-breakpoint" ,
565+ com_enable_breakpoint,
566+ help_text (" :enable-breakpoint" )
567+ .with_summary (
568+ " Enable or create a breakpoint for the given "
569+ " [<format>:]<file>:<line> tuples "
570+ " or the current line" )
571+ .with_parameter (
572+ help_text (" point" )
573+ .with_summary (
574+ " The file and line number of the breakpoint" )
575+ .with_format (help_parameter_format_t ::HPF_BREAKPOINT )
576+ .zero_or_more ()),
577+ },
349578};
350579
351580void
0 commit comments