=head1 NAME

Error::Show - Locate and Diagnose Errors/Exceptions in Perl programs

=head1 SYNOPSIS

=head2 String EVAL

Consider the string/program to be evaluated that has a illegal divide by zero ( or could be a syntax error );

  use Error::Show;

  my $prog='

  sub call_me {
    $a+1/0;
  }

  say "hello there";
  call_me;

  ';


With normal string eval and exception checking, we get:

  my $result =eval $prog; 
  if($@){
    say $@;
  }
  #======== OUTPUT

  hello there
  Illegal division by zero at (eval 1) line 4.


Using normal string eval with C<context> we get:

  my $result =eval $prog; 
  if($@){
    say "ERROR is $@".Error::Show::context $@;
  }

  #====== OUTPUT

  (eval 2)
  Illegal division by zero at (eval 2) 0
      examples/eval.pl
      30
      31   {
      32     say "================== Normal string with context =====================";
      33     my $result =eval $prog;
      34     if($@){
      35=>     say Error::Show::context $@;
      36     }
      37
      38   }
      39
      40


Using C<streval> with C<context> we get:

  my $result =eval {streval $prog};
  if($@){
    say Error::Show::context $@;
  }

  #======= OUTPUT

  hello there
  (eval 4)
  1
  2
  3     sub call_me {
  4=>     $a+1/0;
  5     }
  6
  7     say "hello there";
  8     call_me;
  9
  Illegal division by zero at (eval 4) 4
      examples/eval.pl
      41
      42   say "";
      43
      44   {
      45     say "================== eval streval =====================";
      46=>   my $result =eval {streval $prog};
      47     if($@){
      48       say Error::Show::context $@;
      49     }
      50
      51


=head2 Command Line Syntax Checking

Consider the following program (at examples/synopsis.pl in this distribution).
It has a syntax error on line 13, and uses an experimental feature on line 7.


  use strict;
  use warnings;
  use Time::HiRes;

  use feature "refaliasing";

  \my $a=\"hello";
  my $time=time;
  for(1..1000){
    print "$_\n";
  }

  my $crazy-var=2;

  use Socket;

  print "this will never work";


Attempting to run this program with Perl normally gives this error output:

  ->perl examples/synopsis.pl       
  Aliasing via reference is experimental at examples/synopsis.pl line 7.
  Can't modify subtraction (-) in scalar assignment at examples/synopsis.pl line 13, near "2;"
  BEGIN not safe after errors--compilation aborted at examples/synopsis.pl line 15.


With C<Error::Show> enabled with the C<-M> switch, this instead looks like this:

  ->perl -MError::Show=warn  examples/synopsis.pl
  examples/synopsis.pl
   3   use Time::HiRes;
   4
   5   use feature "refaliasing";
   6
   7=> \my $a=\"hello";
   8   my $time=time;
   9   for(1..1000){
  10   	print "$_\n";
  11   }
  12
  Aliasing via reference is experimental at examples/synopsis.pl line 7.
  examples/synopsis.pl
   9   for(1..1000){
  10   	print "$_\n";
  11   }
  12
  13=> my $crazy-var=2;
  14
  15   use Socket;
  16
  17   print "this will never work";
  Can't modify subtraction (-) in scalar assignment at examples/synopsis.pl line 13, near "2;"
  examples/synopsis.pl
  11   }
  12
  13   my $crazy-var=2;
  14
  15=> use Socket;
  16
  17   print "this will never work";
  BEGIN not safe after errors--compilation aborted at examples/synopsis.pl line 15.

=head2 Command Line Exception Call Stack Dump (from v0.4.0)

Consider the following short program (examples/synopsis2.pl

  use v5.36;

  sub test {

    my $a=1/0;
  }


  test;

Running this program with Perl normally gives this error output:

  perl examples/synopsis2.pl  
  Illegal division by zero at examples/synopsis2.pl line 5.


With C<Error::Show> enabled with the C<-M> switch, this instead looks like this:

  perl -MError::Show examples/synopsis2.pl
  examples/synopsis2.pl
  1   use v5.36;
  2
  3   sub test {
  4
  5=>   my $a=1/0;
  6   }
  7
  8
  9   test;
  Illegal division by zero at examples/synopsis2.pl 5

      examples/synopsis2.pl
      4
      5     my $a=1/0;
      6   }
      7
      8
      9=> test;
  Illegal division by zero at examples/synopsis2.pl 5

=head2 In Program

Use at runtime to supplement exception handling without signal handler modification:

  use Error::Show;

  #an die caught in a try/eval triggers an exception

  # No argument uses $@ as error
  #
  eval { exceptional_code };
  say STDERR context if $@;


  # or a single exception argument of your choosing
  #
  use v5.36;
  try { 
    exceptional_code
  }
  catch($e) {
    say STDERR context $e;
  }

  # Show context down a stack

  try {

    Some_execption_class->throw("Bad things");

  }
  catch($e){
    say STDERR context $e;
  }
  



=head1 DESCRIPTION

This module provides three tools/modes to help locate and diagnose errors in
your Perl programs.

=head2 Command Line

From the command line this module transparently executes your syntactically
correct program. No code changes are required. However in the case of syntax
errors (or warnings if desired), it extracts context (lines of code)
surrounding them. The lines are prefixed with numbers  and the nicely formatted
context is dumped on STDERR for you to see the error or your ways.

The resulting output is optionally filtered seamlessly through the B<splain>
program (see L<diagnostics>), giving more information on why the reported
syntax errors and warnings might have occurred. 

=head2 In Program Exception Context

From within a program, this module can be used to give formatted code context
around the source of an exception and the context of each of the stack frames
captured when the exception was raised.

It supports Perl string exceptions and warnings directly and also provides the
ability to integrate third party CPAN exception objects and traces with minimal
effort. Please see examples in this document or in the examples directory of
the distribution showing use with L<Mojo::Exception>, L<Exception::Base>,
L<Exception::Class::Base> and L<Class::Throwable>.

From C<v0.5.0>  a C<throw> routine is exported which, generates a very basic
exception object, with stack frame capture, in case using an larger exception
object/class is unneeded


=head2 String evals with Execption/Syntax Error Context

From C<v0.5.0> the C<streval> subroutine has been added to allow context
information to be generated for errors originating from code dynamically
created from string evaluations.

=head1 Changes and Options

A handful of options are available for basic configuration of how many lines of
code to print before and after the error line, indenting of stack trace
context, etc.

B<v0.5.0> Has alot of changes which might break compatibility. THe up side is
the codes is simpler, has less errors, and easier to use. This is especial the
case for third party exceptions objects. It also gives the intended purpose of
handling errors and exceptions from  string evals like any other.

That being said, an earlier version might need to be used if you want the use
the C<$@> variable implicitly in a call to C<context>. This subroutine now
requires an explicated error argument

Sub routines C<context>, C<throw> and C<streval> are exported by default.


B<From v0.3.0:> C<context> subroutine is now exported by default. To prevent
this, import with an empty list, ie C<use Error::Show ()>.

C<From v0.2.0:>, Added 'advanced string eval' support has been added for better
context reporting of dynamically generated code.


=head1 USAGE

=head2 Command Line Usage (Syntax check and Exception Catching)

	perl -MError::Show  [options] file.pl 

When included in a command line switch to Perl, C<-MError::Show> syntax checks
the input program. If the syntax is OK, normal execution continues in a
transparent fashion.  Otherwise, detailed code context surrounding the source
of the error is generated and printed on STDERR.

B<From v0.4.0> a global __DIE__ handler is also installed, which will catch any
stray exceptions during execution and present a line numbered summary stack
trace. Programs a free to overload this handler. However the features of this
module will be lost.

B<NOTE:> It is important that it's the first C<-M> switch for this module to
operate correctly and to prevent any incompatibilities withe global signal
handlers.

If the B<-c> flag is specified, only a syntax check will be performed,
mimicking normal Perl behaviour.


Additional C<@INC> directories using the B<-I> switch are supported as are
additional modules via the B<-M> switch.

=head3 CLI Usage Options

The following options can be used in isolation or together:

=head4 clean

If you prefer just the code context without the Perl error, add the clean
option:

  perl -MError::Show=clean file.pl

=head4 warn

This options enables processing of warnings as well as errors.

  perl -MError::Show=warn file.pl

=head4 splain

Runs the output through the splain program (see L<diagnostics>), giving
probable reasons behind the error or warning

  perl -MError::Show=splain file.pl

=head4 no_handler (from v0.4.0)

  perl -MError::Show=no_handler file.pl

Prevents the global DIE handler from being installed.

=head3 Return code

When in check only mode (-c), the main process is exited, just has Perl
normally would have done. The return code is a replica of what Perl would have
reported for success/failure of a syntax check.

=head2 In Program (Exception) Usage

Simply bring L<Error::Show> into your program with a use statement:

  use Error::Show;

It provides a single subroutine for processing errors and exceptions.

=head3 Error::Show::context

  my $context=Error::Show::context $error , options_pairs, ...;


Takes an error string, or exception object C<$error> and extracts the code
surrounding the source of the error. The code lines are prefixed with line
numbers and the error line marked with a fat arrow.

The expected types of error are 

=over 

=item Error string, as per C<die> and C<warn>

This is expected to contain file and line number. These are extracted from the
string error to locate context.

=item Supported Execption Object

Several common exceptions classes on CPAN are supported:

=over 

=item Error::Show::Exception

=item Exception::Class::Base

=item Exception::Base

=item Class::Throwable

=item Mojo::Exception

=back


Other classes will likely work as long as the stringify to resemble a perl
error string  (with the filename and line number formatted correctly). The
stack frame capture would work however)

=back

The return value is the formatted context, followed by the original Perl error
strings, or stringified exception objects/messages:


  filename.pl 
  10  #code before 
  11  #code before 
  12=>#this line caused the error
  13  #code after
  14  #code after

  ... error... at filename.pl line 12 ...






The C<context> subroutine (5) is exported by default, so does not need a fully
qualified name

C<context> Can also be called with package arrow notation (6) ie
C<Error::Show-E<gt>context(...)> if prefered, with the same argument handling
as the other forms.




B<Options include:>

=head4 limit 

  limit=>$int

B<From v0.2.0:> Limits the number of errors to extract and generate context
for. Default is 100. If <=0,  no limiting is applied all all errors are
processed.

=head4 reverse
  
  reverse=>$bool

B<From v0.2.0:> Reverses the order of error processing. 

Perl type string errors are sorted and processing in ascending line number
order. When this option is used, the lines are processed by descending line
number first. Does not change order of files processed.

If frames are used instead, they are processed in reverse order to how they
where supplied when this option is in effect.

=head4 pre_lines

  pre_lines=>value

Specific the maximum lines of code to display before the error line. Default is
5.

=head4 post_lines

  post_lines=>value

Specific the maximum lines of code to display after the error line. Default is
5.

=head4 clean

  clean=>bool

When true, the normal Perl error string is not included in the context
information, for a cleaner look.


=head4 indent
  
    indent=>string

The string to use for each level of indent when printing multiple stack frames.
Defaults to 4 spaces.

=head4 splain

    splain=>1

The resulting output will be filtered through the L<splain> program.  

=head4 program

    program=>$prog

The B<program> option is used to specify the program text to process when
there is no actual file. This is needed when to show syntax errors in a string
C<eval>:

    my $prog='my $a="This will Fail"+b';
    eval $prog;
    if($@){
      say Error::Show::context error=>$@, program=>prog;
    }

For advanced string eval processing options please see the B<ADVANCED STRING
EVAL> section in this document.

=head3 streval

  local $@;

  my $result=eval { streval "program";

  if($@){
    print STDERR context $@;
  }


  ##### or ####


  try {
    my $result=strval "program";
  }
  catch($e){
    print STDERR context $e;
  }


C<streval> makes it possible to debug code generated from string eval.
Internally it caches program code, to allow a context to be generated in the
case of an exception.

B<NOTE:> It also throws an exception on syntax error, which also then can have
a context generated for it.

=head3 throw

  throw "My error message";

Raises an exception with a basic exception object with the provided message.
Captures the call stack frames at the point of call.

Use this if you don't want to use other exception classes/modules

=head1 ADVANCED STRING EVAL 

B<From v0.2.0> features to support advanced string evaluation are available.
This was added to support the error reporting needs of the L<Template::Plex>
module.

Consider the following example. The 'meat' is the sub  in the middle of the
string. Any errors/context reported should be relative to this, not the start
of the overall eval string. With some help from comment markers "##_PREAMBLE"
and "##_POSTAMBLE",  when can search for the middle and rebase the error line
numbering.

  eval {
    "my $some_prep_work=1;
    call_somthing();
    #comments...
    
    ##_PREAMBLE

    sub {
       #code generated from user input...
       say "My really cool dynamically created code"
    }

    ##_POSTAMBLE

    #More code 
    #Cleanup stuff.
    "
  }



Additional configuration options can be provided  to search for the relevant
code lines and offset the error line numbers.

B<NOTE> if these options are used, the B<message> field is modified with
updated line numbers if its in the form of a normal Perl errors ie 'error line
123 at file'.

=head3 start_mark

  start_mark=>$regexp

If specified, is a used as a regexp to match against source code lines. The line
after a successful match is now the first line.

This allows inserting a special marker to indicate the start of 'code of
interest' with out knowing the exact line number in the resulting code.

This is undefined and unused by default.

=head3 end_mark

  end_mark=>$regexp

If specified, is used as a regexp to match against source lines, in reverse
order. The line after a successful match is now the last line

This allows inserting a special marker to indicate the end of 'code of
interest'.

This is undefined unused by default.


=head3 start_offset

  start_offset=>$int

A static offset to add to the start line (which may have been modified by the
B<start_mark> option). The result will be classed as the minimum line number of
the file/string.

This is useful to prevent any preamble after the start_mark line in your string
eval showing up in the user program context.

=head3 end_offset

A static offset to subtract to the end line (which may have been modified by
the B<end_mark> option). The result will be classed as the maximum line number
of the file.

This is useful to prevent any postamble before the end_mark in your string eval
showing up in the user program context.



=head1 FUTURE WORK/TODO

=over 

=item Possible just use the DIE and WARN signal handler instead of forking processes?

=item Make usable from a Language Server?

=item Colour terminal output?

=item JSON output?

=back

=head1 KNOWN ISSUES/GOTCHAS

Checking/running  programs via STDIN, -e and -E switches is not supported and
will die with an error message.

More data then needed is pushed through the splain program when splain option
is used, which isn't ideal.


=head1 SEE ALSO

L<Perl::Syntax> provides syntax checking from the command line. However it
doesn't show any errors by design (only interested in process return code)

L<Syntax::Check> provides programmatic syntax checking of files.

L<Perl::Critic> gives actual Perl linting, but not great for syntax errors.

L<diagnostics>  and the C<splain> program give some very useful explanations
about the otherwise terse error strings normally output. It is part of the Perl
distribution

=head1 AUTHOR

Ruben Westerberg, E<lt>drclaw@mac.comE<gt>

=head1 REPOSITORTY and BUGS

Please report any bugs via git hub:
L<https://github.com/drclaw1394/perl-error-show>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2025 by Ruben Westerberg

This library is free software; you can redistribute it and/or modify it under
the same terms as Perl or the MIT license.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE.

=cut
