The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Term::HiliteDiff::_impl;
BEGIN {
  $Term::HiliteDiff::_impl::VERSION = '0.10';
} ## no critic (NamingConventions::Capitalization)
# ABSTRACT: Highlights differences in text with ANSI escape codes
use strict;

## no critic (RequireUseWarnings)
## no critic (ProhibitCascadingIfElse)
## no critic (ProhibitMagicNumbers)
## no critic (RequireDotMatchAnything)

use constant _PREVIOUS_INPUT => 0;

sub new {
    my @self;
    $self[_PREVIOUS_INPUT] = undef;

    return bless \ @self, $_[0];
}

sub hilite_diff {
    # Arguments:
    #  0: self
    #  1: input string or arrayref

    return $_[0]->_do_diff( $_[1], 0 );
}

sub watch {
    # Arguments:
    #  0: self
    #  1: input string or arrayref

    return $_[0]->_do_diff( $_[1], 1 );
}

# This library uses the following ANSI escape codes
# \e[s   Save a cursor position
# \e[u   Go back to the saved cursor position
# \e[K   Erase everything from the cursor to the end of the line
# \e[31m Red
# \e[32m Green
# \e[33m Yellow
# \e[0m  Turns off all formatting

sub _parse_input {
    # Arguments:
    #  0: self
    #  1: array mode
    #  2: input string
    #
    # Returns:
    #  input arrayref, separator

    if ( $_[1] ) {
        return $_[2], "\t";
    }
    elsif ( -1 != index( $_[2], "\t" ) ) {
        return [ split /\t/, $_[2], -1 ], "\t";
    }
    elsif ( -1 != index( $_[2], '|' ) ) {
        return [ split /\|/, $_[2], -1 ], '|';
    }
    elsif ( $_[2] =~ /\n(?!\z)/ ) {
        return [ split /\n/, $_[2], -1 ], "\n";
    }
    else {
        return [ split ' ', $_[2], -1 ], ' ';
    }
}

sub _color {
    # Arguments:
    #  0: input hunk

    # Color the region
    local $^W = 0;
    my $val = "\e[7m$_[0]\e[0m";

    # Turn off coloring over newlines
    $val =~ s/\n/\e[0m\n\e[7m/g;

    # Remove empty colored regions
    $val =~ s/\e\[7m\e\[0m//g;

    return $val;
}

# sdiff does the primary markup work for this module. Everything else
# just riffs on this.

sub _sdiff {
    local $^W = 0;

    # Arguments:
    #  0: previous input arrayref
    #  1: input arrayref
    #  2: redraw?: 

    my $prev_max_index = $#{$_[0]};
    my $new_max_index  = $#{$_[1]};
    my $max_index =
        $prev_max_index >= $new_max_index
        ? $prev_max_index
        : $new_max_index;

    my @sdiff = (undef) x (1 + $max_index);
    for my $idx ( 0 .. $max_index ) {

        if ( $_[0][$idx] eq $_[1][$idx] ) {
            $sdiff[$idx] =
                defined $_[1][$idx]
                ? $_[1][$idx]
                : '';
        }
        elsif ( length $_[1][$idx] ) {
            $sdiff[$idx] = _color( $_[1][$idx] );
        }
        else {
            # This was changed but it's empty so there's nothing to color.
            $sdiff[$idx] = '';
        }
    }

    return \ @sdiff;
}

sub _do_diff {
    # Arguments:
    #  0: self
    #  1: input string or arrayref
    #  2: redraw mode?

    # We accept either an array and then we don't parse it or we
    # accept a string and we try to parse it.
    my $array_mode = 'ARRAY' eq ref $_[1];

    # Interpret input
    my ( $input, $separator ) =
        $_[0]->_parse_input(
            $array_mode,
            $_[1]
        );

    # Fetch previous input
    my $prev_input = $_[0][_PREVIOUS_INPUT];
    $prev_input ||= $input;

    # Diff it
    my $sdiff = _sdiff(
        $prev_input,
        $input,
        $_[2],
    );

    # Manage redrawing over the same place on the screen
    if ( $_[2] ) {
        if ( ! $_[0][_PREVIOUS_INPUT] ) {

            # Save the cursor position
            $sdiff->[0] = "\e[s$sdiff->[0]";
        }
        else {

            # Restore the cursor position
            $sdiff->[0] = "\e[u$sdiff->[0]";
        }
    }

    # Save our "previous" state into the object
    $_[0][_PREVIOUS_INPUT] = $input;

    if ( $array_mode ) {

        if ( $_[2] ) {
            for ( @$sdiff ) {
                s/(?<!\e\[K)(?=\n)/\e[K/g;
                s/(?<=\e\[K)(?:\e\[K)+//g;
                s/(?<!\e\[K)\z/\e[K/;
            }
        }

        return $sdiff;
    }
    else {
        my $output =
            join $separator,
            @$sdiff;

        if ( $_[2] ) {
            for ( $output ) {
                s/(?<!\e\[K)(?=\n)/\e[K/g;
                s/(?<=\e\[K)(?:\e\[K)+//g;
                s/(?<!\e\[K)\z/\e[K/;
            }
        }

        return $output;
    }
}

# Blatantly copied this from errantstory.com
q[Wow, you're pretty uptight for a guy who worships a multi-armed, hermaphrodite embodiment of destruction who has a fetish for vaguely phallic shaped headgear.];



=pod

=head1 NAME

Term::HiliteDiff::_impl - Highlights differences in text with ANSI escape codes

=head1 VERSION

version 0.10

=head1 METHODS

=head2 new
=method hilite_diff
=method watch

=head1 AUTHOR

Josh Jore <jjore@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Josh Jore.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


__END__