# -----------------------------------------------------------------------------

=encoding utf8

=head1 NAME

Quiq::Unindent - Entferne Einrückung von "Here Document" oder String-Literal

=head1 SYNOPSIS

Klasse laden:

  use Quiq::Unindent;

Eingerücktes "Here Document":

  {
      $text = Quiq::Unindent->hereDoc(<<'    EOT');
      Dies ist
      ein Text
      EOT
  
      print $text;
  }

Eingerücktes mehrzeiliges String-Literal:

  {
      $text = Quiq::Unindent->string('
          Dies ist
          ein Text
      ');
  
      print $text;
  }

Resultat in beiden Fällen:

  Dies ist
  ein Text

=head1 DESCRIPTION

Die Klasse stellt Methoden zur Verfügung, mit denen die in der
Regel unerwünschte Einrückung von eingerückten mehrzeiligen
String-Literalen und "Here Documents" entfernt werden kann.

=cut

# -----------------------------------------------------------------------------

package Quiq::Unindent;

use v5.10;
use strict;
use warnings;

our $VERSION = '1.231';

use Quiq::String;

# -----------------------------------------------------------------------------

=head1 METHODS

=head2 Klassenmethoden

=head3 hereDoc() - Entferne Einrückung von "Here Document"

=head4 Synopsis

  $str = $class->hereDoc(<<'EOT');
      <Text>
  EOT
  
  $str = $class->hereDoc($char,<<'EOT');
      <Text>
  EOT

=head4 Arguments

=over 4

=item $char (optional)

Fortsetzungszeichen. Siehe Quiq::String->joinLines().

=item $text

Eingerückter Text.

=back

=head4 Description

Entferne von allen Zeilen die tiefste Einrückung, die allen Zeilen
gemeinsam ist, und liefere die resultierende Zeichenkette zurück,
wobei

=over 2

=item *

alle Sub-Einrückungen erhalten bleiben

=item *

alle Leerzeilen erhalten bleiben, auch am Anfang und am Ende

=back

Ist der Ende-Marker eingerückt, muss dessen Einrückung bei der
Vereinbarung des Markers angegeben werden. Siehe C<<< <<' EOT' >>> in
den Beispielen.

=head4 Examples

=over 4

=item 1.

Gegenüberstellung der Syntax

  {
      $text = Quiq::Unindent->hereDoc(<<'    EOT');
        Dies ist
        ein Text
      EOT
  }

ist äquivalent zu

  {
      $text = <<'EOT';
  Dies ist
  ein Text
  EOT
  }

=item 2.

Sub-Einrückungen und Leerzeilen

  {
      $text = Quiq::Unindent->hereDoc(<<'    EOT');
  
        Dies ist der
      erste Absatz.
  
        Dies ist ein
      zweiter Absatz.
  
      EOT
  }

ergibt

  |
  |  Dies ist der
  |erste Absatz.
  |
  |  Dies ist ein
  |zweiter Absatz.
  |

d.h. Sub-Einrückungen und Leerzeilen bleiben erhalten.

=back

=cut

# -----------------------------------------------------------------------------

sub hereDoc {
    my $class = shift;
    my $char = @_ == 2? shift: undef;
    my $str = shift // return '';

    # Wir brauchen uns nur mit dem String befassen, wenn das erste
    # Zeichen ein Whitespacezeichen ist. Wenn dies nicht der Fall
    # ist, existiert keine Einrückung, die wir entfernen müssten.

    if ($str =~ /^\s/) {
        my $ind;
        while ($str =~ /^([ \t]*)(.?)/gm) {
            if (length $2 == 0) {
                # Leerzeilen und Whitespace-Zeilen übergehen wir
            }
            elsif (!defined $ind || length $1 < length $ind) {
                $ind = $1;
                if (!$ind) {
                    # Zeile ohne Einrückung gefunden
                    last;
                }
            }
        }
        if ($ind) {
            # gemeinsame Einrückung von allen Zeilen entfernen
            $str =~ s/^$ind//gm;
        }
    }
    if (defined $char) {
        $str = Quiq::String->joinLines($char,$str);
    }

    return $str;
}

# -----------------------------------------------------------------------------

=head3 string() - Entferne Einrückung von mehrzeiligem String-Literal

=head4 Synopsis

  $str = $class->string('
      <Text>
  ');
  
  $str = $class->string($char,'
      <Text>
  ');

=head4 Arguments

=over 4

=item $char (optional)

Fortsetzungszeichen. Siehe Quiq::String->joinLines().

=item $text

Eingerückter Text.

=back

=head4 Description

Wie Methode L<hereDoc|"hereDoc() - Entferne Einrückung von "Here Document"">(), wobei über die Einrückung hinaus

=over 2

=item *

der erste Zeilenumbruch am Anfang entfernt wird (sofern vorhanden)

=item *

die Leerzeichen am Ende entfernt werden (sofern vorhanden)

=back

Diese (zusätzlichen) Manipulationen sorgen dafür, dass der
Leerraum entfernt wird, der dadurch entsteht, wenn die
Anführungsstriche auf einer eigenen Zeile stehen.

=head4 Examples

=over 4

=item 1.

Gegenüberstellung der Syntax:

  {
      $text = Quiq::Unindent->string('
          Dies ist
          ein Text
      ');
  }

ist äquivalent zu

  {
      $text = 'Dies ist
  ein Text
  ';
  }

=item 2.

Varianten

  $text = Quiq::Unindent->string(q~
      Dies ist
      ein Text
  ~);
  
  $text = Quiq::Unindent->string("
      Dies ist
      ein Text mit $variable
  ");
  
  $text = Quiq::Unindent->string(qq~
      Dies ist
      ein Text mit $variable
  ~);

=back

=cut

# -----------------------------------------------------------------------------

sub string {
    my $class = shift;
    my $char = @_ == 2? shift: undef;
    my $str = shift // return '';

    $str =~ s/^\n//;
    $str =~ s/ +$//;

    return $class->hereDoc($char,$str);
}

# -----------------------------------------------------------------------------

=head3 trim() - Entferne Einrückung und Whitespace am Anfang und Ende

=head4 Synopsis

  $strOut = $class->trim($strIn);
  $strOut = $class->trim($char,$strIn);

=head4 Arguments

=over 4

=item $char (optional)

Fortsetzungszeichen. Siehe Quiq::String->joinLines().

=item $strIn

Eingerückter Text.

=back

=head4 Description

Wie die Methoden L<hereDoc|"hereDoc() - Entferne Einrückung von "Here Document"">() und L<string|"string() - Entferne Einrückung von mehrzeiligem String-Literal">(), wobei über die
Einrückung hinaus

=over 2

=item *

alle Leerzeilen am Anfang entfernt werden

=item *

jeglicher Leerraum am Ende entfernt wird

=back

Diese (zusätzlichen) Manipulationen sorgen dafür, dass der Text
als solches - d.h. ohne Einrückung und ohne Leerraum am Anfang und
am Ende - geliefert wird.

Die Methode ist speziell für die I<interne> Bearbeitung eines
mehrzeiligen, ggf. mit einer Einrückung versehenen Parameterns
geeignet.

=head4 Examples

=over 4

=item 1.

Leerraum am Anfang und am Ende wird entfernt

  {
      $text = Quiq::Unindent->trim("
  
          SELECT
              *
          FROM
              person
          WHERE
              nachname = 'Schulz'
  
      ");
  }

ergibt

  |SELECT
  |    *
  |FROM
  |    person
  |WHERE
  |    nachname = 'Schulz'
                          ^
                          kein Newline

=item 2.

Interne Anwendung

  sub select {
      my ($self,$stmt) = @_;
  
      $stmt = Quiq::Unindent->trim($stmt);
      if ($self->debug) {
          warn $stmt,"\n";
      }
      ...
  }

Aufruf mit eingerücktem String-Literal, das I<intern> behandelt wird:

  $db->select("
      SELECT
          *
      FROM
          person
      WHERE
          nachname = 'Schulz'
  ");

=back

=cut

# -----------------------------------------------------------------------------

sub trim {
    my $class = shift;
    my $char = @_ == 2? shift: undef;
    my $str = shift // return '';

    $str =~ s/^\s*\n//;
    $str =~ s/\s+$//;

    return $class->hereDoc($char,$str);
}

# -----------------------------------------------------------------------------

=head3 trimNl() - Trim plus Newline

=head4 Synopsis

  $strOut = $class->trimNl($strIn);
  $strOut = $class->trimNl($char,$strIn);

=head4 Arguments

=over 4

=item $char (optional)

Fortsetzungszeichen. Siehe Quiq::String->joinLines().

=item $strIn

Eingerückter Text.

=back

=head4 Description

Wie die Methode L<trim|"trim() - Entferne Einrückung und Whitespace am Anfang und Ende">(), jedoch wird am Ende genau ein Newline
angehängt, sofern der Sting nicht leer ist.

=cut

# -----------------------------------------------------------------------------

sub trimNl {
    my $class = shift;
    my $char = @_ == 2? shift: undef;
    my $str = shift // return '';

    $str = $class->trim($char,$str);
    if ($str ne '') {
        $str .= "\n";
    }

    return $str;
}

# -----------------------------------------------------------------------------

=head1 VERSION

1.231

=head1 AUTHOR

Frank Seitz, L<http://fseitz.de/>

=head1 COPYRIGHT

Copyright (C) 2025 Frank Seitz

=head1 LICENSE

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

=cut

# -----------------------------------------------------------------------------

1;

# eof
