------------------------------------------------------------------------------
--                                                                          --
--                            GNATPP COMPONENTS                             --
--                                                                          --
--                        G N A T P P . L A Y O U T                         --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--                    Copyright (C) 2001-2007, AdaCore                      --
--                                                                          --
-- GNATPP is free software; you can redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  GNATPP is  distributed in the  hope that it will  be  useful, but --
-- WITHOUT ANY WARRANTY; without even the implied warranty of  MERCHANTABI- --
-- LITY or  FITNESS  FOR A  PARTICULAR  PURPOSE. See the GNU General Public --
-- License  for more details. You  should  have  received a copy of the GNU --
-- General Public License  distributed with GNAT; see file COPYING. If not, --
-- write to the Free Software Foundation,  51 Franklin Street, Fifth Floor, --
-- Boston,                                                                  --
--                                                                          --
-- GNATPP is maintained by AdaCore (http://www.adacore.com)                 --
--                                                                          --
------------------------------------------------------------------------------

with Table;

with Asis.Clauses;                    use Asis.Clauses;
with Asis.Extensions;                 use Asis.Extensions;
with Asis.Declarations;               use Asis.Declarations;
with Asis.Elements;                   use Asis.Elements;
with Asis.Statements;                 use Asis.Statements;
with Asis.Definitions;                use Asis.Definitions;
with Asis.Expressions;                use Asis.Expressions;

with ASIS_UL.Options;

with GNATPP.State;                    use GNATPP.State;
with GNATPP.Common;                   use GNATPP.Common;
with GNATPP.Options;                  use GNATPP.Options;
with GNATPP.PP_Output;                use GNATPP.PP_Output;
with GNATPP.Output;                   use GNATPP.Output;
with GNATPP.Paragraphs;               use GNATPP.Paragraphs;
with GNATPP.Asis_Utilities;           use GNATPP.Asis_Utilities;
with GNATPP.General_Traversal_Stacks; use GNATPP.General_Traversal_Stacks;

package body GNATPP.Layout is

   -----------------------------------------------------------
   -- Table for collecting and computing the alignment info --
   -----------------------------------------------------------

   package Align_Info is new Table.Table
     (Table_Component_Type => Layout_Info,
      Table_Index_Type     => Natural,
      Table_Low_Bound      => 1,
      Table_Initial        => 100,
      Table_Increment      => 100,
      Table_Name           => "");
   --  In this table the information about lengths of the components of the
   --  "homogeneous"  constructions is stored in order to compute the
   --  possible alignment.

   -----------------------
   -- Local_Subprograms --
   -----------------------

   procedure Check_List_Layout
     (Curr_Pos             : in out Natural;
      Elements             :        Asis.Element_List;
      Needs_Separate_Lines :    out Boolean);
   --  This procedure checks, if the given Elements can be placed in one line
   --  starting from Curr_Pos. It is supposed, that the Elements in the
   --  pretty-printed output are separated by ', ', and the last one is
   --  followed by ') ' or ');', so we are adding '2'to the space needed for
   --  each element.
   --
   --  If all the Elements can be put in one line, Curr_Pos is set as if they
   --  are sent to the output and, therefore, it can be further used in layout
   --  computation, Needs_Separate_Lines is set OFF. Otherwise Curr_Pos should
   --  be considered by the client as undefined, and Needs_Separate_Lines is
   --  set ON.
   --
   --  There are two reasons for making the decision that Elements can not
   --  be put in the same line:
   --  (1) When simulating outputting these Elements we exceed the maximum
   --      line limit;
   --  (2) One of the Elements contain a comment inside its text image;
   --
   --  We do NOT consider comments BETWEEN Elements as the reason to print
   --  them on separate lines

   procedure Check_List_Layout_And_Max_Elem
     (Curr_Pos             : in out Natural;
      Elements             : Asis.Element_List;
      Needs_Separate_Lines : out Boolean;
      Max_Element_Space    : out Positive);
   --  Similar to Check_List_Layout, but also detects the space needed for the
   --  longest element in the list

   procedure Compute_Associations_Alignment
     (Start_Pos :     Natural;
      Elements  :     Asis.Element_List;
      Arrow_Pos : out Natural;
      Max_Expr  : out Natural);
   --  This procedure analyzes the list of associations and computes the layout
   --  parameters for aligning the associations (which are supposed to be
   --  placed on separate lines). Arrow_Pos is set to the starting position of
   --  the arrow delimiter. Max_Expr defines the length of the component
   --  expression (or of the first component thereof) which still allows to
   --  align the component association (that is, if the expression or its
   --  first component is longer then Max_Expr, the enclosing association
   --  should not be aligned.
   --
   --  At the moment computing the Max_Expr value is not implemented

   function Take_Then_Into_Account return Boolean;
   --  Checks if we have to take into account the 'THEN' keyword to avoid
   --  formatting like this
   --
   --  if .............
   --  then

   -----------------------
   -- Check_List_Layout --
   -----------------------

   procedure Check_List_Layout
     (Curr_Pos             : in out Natural;
      Elements             : Asis.Element_List;
      Needs_Separate_Lines : out Boolean)
   is
      Next_Space     : Natural;
      Has_Comments   : Boolean            := False;
   begin

      Needs_Separate_Lines := False;

      for J in Elements'Range loop

         Detect_Possible_Layout
           (The_Element     => Elements (J),
            Space_In_Output => Next_Space,
            Comment_Inside  => Has_Comments);

         Curr_Pos := Curr_Pos + Next_Space + 2;
         --  '+ 2' stands for ', ' or ') '

         if Has_Comments or else
            Curr_Pos > Max_Line_Length
         then
            Needs_Separate_Lines := True;
            exit;
         end if;

      end loop;

   end Check_List_Layout;

   ------------------------------------
   -- Check_List_Layout_And_Max_Elem --
   ------------------------------------

   procedure Check_List_Layout_And_Max_Elem
     (Curr_Pos             : in out Natural;
      Elements             : Asis.Element_List;
      Needs_Separate_Lines : out Boolean;
      Max_Element_Space    : out Positive)
   is
      Next_Space     : Natural;
      Has_Comments   : Boolean            := False;
   begin

      Needs_Separate_Lines := False;
      Max_Element_Space    := 1;

      for J in Elements'Range loop

         Detect_Possible_Layout
           (The_Element     => Elements (J),
            Space_In_Output => Next_Space,
            Comment_Inside  => Has_Comments);

         Curr_Pos := Curr_Pos + Next_Space + 2;
         --  '+ 2' stands for ', ' or ') '

         if Has_Comments or else
            Curr_Pos > Max_Line_Length
         then
            Needs_Separate_Lines := True;
         end if;

         if Next_Space > Max_Element_Space then
            Max_Element_Space := Next_Space;
         end if;

      end loop;

   end Check_List_Layout_And_Max_Elem;

   -------------------------------------
   -- Compute_Array_Definition_Layout --
   -------------------------------------

   procedure Compute_Array_Definition_Layout (E : Asis.Element) is
      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (E);

      Next_Out_Pos  : Natural;
      Next_Space    : Natural;
      Has_Comments  : Boolean;

      Start_From_New_Line : Boolean := False;

      function Get_Index_Defs return Element_List;
      --  returns the list of the index definitions

      function Get_Index_Defs return Element_List is
      begin

         case Arg_Kind is
            when A_Constrained_Array_Definition        |
                 A_Formal_Constrained_Array_Definition =>
               return Discrete_Subtype_Definitions (E);
            when others =>
               return Index_Subtype_Definitions (E);
         end case;

      end Get_Index_Defs;
   begin

      if Is_New_Output_Line then
         Next_Out_Pos := (Logical_Depth + 1) * PP_Indentation;
      else
         Next_Out_Pos := Output_Pos + 1;
      end if;

      Detect_Possible_Layout
        (The_Element     => E,
         Space_In_Output => Next_Space,
         Comment_Inside  => Has_Comments);

      if Next_Out_Pos + Next_Space < Max_Line_Length then
         Current_State.Layout.Flag1 := False;

         if not Has_Comments then
            Current_State.Layout.Flag2 := False;

            --  Everything is on one line...
            return;

            --  Otherwise we have to check if all the indexes can be placed
            --  into one line
         end if;

      else
         Current_State.Layout.Flag1 := True;
      end if;

      --  If we are here, we may think, that the first index start from
      --  the position corresponding to the situation when "ARRAY' is
      --  printed in the new line

      Next_Out_Pos := (Logical_Depth + 1) * PP_Indentation + 6;

      Check_List_Layout
        (Curr_Pos             => Next_Out_Pos,
         Elements             => Get_Index_Defs,
         Needs_Separate_Lines => Start_From_New_Line);

      if not Start_From_New_Line then

         Detect_Possible_Layout
           (The_Element     => Array_Component_Definition (E),
            Space_In_Output => Next_Space,
            Comment_Inside  => Has_Comments);

         if Has_Comments then
            Start_From_New_Line := True;
         else
            Next_Out_Pos := Next_Out_Pos + 4 + Next_Space + 1;

            if Next_Out_Pos > Max_Line_Length then
               Start_From_New_Line := True;
            end if;

         end if;

         if not Start_From_New_Line
           and then
            (Arg_Kind = A_Formal_Unconstrained_Array_Definition or else
             Arg_Kind = An_Unconstrained_Array_Definition)
         then
            --  Taking into account 'range <>' in index definitions
            Next_Out_Pos :=
               Next_Out_Pos + Get_Index_Defs'Length * 9;

            if Next_Out_Pos > Max_Line_Length then
               Start_From_New_Line := True;
            end if;
         end if;

      end if;

      Current_State.Layout.Flag2 := Start_From_New_Line;
   end Compute_Array_Definition_Layout;

   ----------------------------------------
   -- Compute_Assign_Statement_Alignment --
   ----------------------------------------

   procedure Compute_Assign_Statement_Alignment
     (Start_Pos  :     Natural;
      Elements   :     Asis.Element_List;
      Assign_Pos : out Natural)
   is
      Left_Part : Asis.Element;

      Next_Space           : Natural;
      Tmp_Layout           : Layout_Info;
      Alignment_Impossible : Boolean;
   begin

      Align_Info.Init;

      Assign_Pos := 0;

      for J in Elements'Range loop

         Left_Part := Assignment_Variable_Name (Elements (J));

         --  ??? This is a very primitive approach! We did not check if the
         --  statement can fit the line and if it contains comments inside
         --  ??? (The same is the case for all the other alignments!!!)

         Detect_Possible_Layout
           (The_Element     => Left_Part,
            Space_In_Output => Next_Space,
            Comment_Inside  => Alignment_Impossible);

         if not Alignment_Impossible and then
            not Is_Nil (Label_Names (Elements (J)))
         then
            --  No alignment in case of labeled statement(s)
            Alignment_Impossible := True;
         end if;

         if Alignment_Impossible then
            --  we just give up
            exit;
         end if;

         Tmp_Layout.Pos2 := Start_Pos + Next_Space + 1;

         if Tmp_Layout.Pos2 > Max_Line_Length then
            --  we just give up
            exit;
         end if;

         Align_Info.Append (Tmp_Layout);

      end loop;

      if not Alignment_Impossible then

         for J in 1 .. Align_Info.Last loop

            if Align_Info.Table (J).Pos2 > Assign_Pos then
               Assign_Pos := Align_Info.Table (J).Pos2;
            end if;

         end loop;

      end if;

   end Compute_Assign_Statement_Alignment;

   ------------------------------------
   -- Compute_Associations_Alignment --
   ------------------------------------

   procedure Compute_Associations_Alignment
     (Start_Pos :     Natural;
      Elements  :     Asis.Element_List;
      Arrow_Pos : out Natural;
      Max_Expr  : out Natural)
   is
      Choices : Element_List_Access;

      Tmp_Layout             : Layout_Info;
      Next_Pos               : Natural;
      Alignment_Impossible   : Boolean := True;
      --  By default we are pessimistic...

      function Get_Choices (E : Asis.Element) return Element_List;
      --  Returns the list of choices from the association E

      function Get_Choices (E : Asis.Element) return Element_List is
         Tmp_El : Asis.Element;
      begin
         case Flat_Element_Kind (E) is
            when A_Record_Component_Association =>
               return Record_Component_Choices (E);
            when An_Array_Component_Association =>
               return Array_Component_Choices (E);
            when A_Discriminant_Association =>
               return Discriminant_Selector_Names (E);
            when A_Parameter_Association       |
                 A_Generic_Association         |
                 A_Pragma_Argument_Association =>
               Tmp_El := Formal_Parameter (E);

               if not Is_Nil (Tmp_El) then
                  return (1 => Tmp_El);
               end if;

            when others =>
               null;
         end case;

         return Nil_Element_List;
      end Get_Choices;

   begin
      Arrow_Pos := 0;
      Max_Expr  := 0;

      Align_Info.Init;

      for J in Elements'Range loop

         Choices := new Element_List'(Get_Choices (Elements (J)));

         if Choices'Length /= 0 then

            Next_Pos := Start_Pos;

            Check_List_Layout
              (Curr_Pos             => Next_Pos,
               Elements             => Choices.all,
               Needs_Separate_Lines => Alignment_Impossible);

            if Alignment_Impossible then
               --  we just give up
               exit;
            end if;

            Tmp_Layout.Pos1 := Next_Pos - 1;
            Align_Info.Append (Tmp_Layout);

         end if;

         Free (Choices);
      end loop;

      if not Alignment_Impossible then
         --  At the moment we just compute the maximum length of
         --  choice list. later we are going to get smarter and to
         --  detect associations for which alignment does not make sense

         for J in 1 .. Align_Info.Last loop

            if Align_Info.Table (J).Pos1 > Arrow_Pos then
               Arrow_Pos := Align_Info.Table (J).Pos1;
            end if;

         end loop;

      end if;

   end Compute_Associations_Alignment;

   ------------------------------
   -- Compute_Aggregate_Layout --
   ------------------------------

   procedure Compute_Aggregate_Layout (E : Asis.Element) is
      Encl_Kind        : constant Flat_Element_Kinds := Get_Enclosing_Kind;
      Arg_Kind         : constant Flat_Element_Kinds := Flat_Element_Kind (E);
      All_Associations : Element_List_Access;
      Tmp_Expr         : Asis.Element;

      Next_Curr_Pos    : Natural;
      Next_Space       : Natural;
      Has_Comments     : Boolean            := False;

      --  Layout parameters to set:
      Separate_Line    : Boolean := False;
      Start_Pos        : Natural := 0;
      Arrow_Position   : Natural := 0;
      Max_Expr_Len     : Natural := 0;

      function Get_All_Associations return Element_List;
      --  returns the list of all the component associations of the argument
      --  aggregate

      function Get_All_Associations return Element_List is
      begin

         case Arg_Kind is
            when A_Record_Aggregate     |
                 An_Extension_Aggregate =>

               return Record_Component_Associations (E);

            when A_Positional_Array_Aggregate |
                 A_Named_Array_Aggregate      =>

               return Array_Component_Associations (E);

            when A_Discriminant_Constraint =>

               return Discriminant_Associations (E);

            when others =>

               return Nil_Element_List;

         end case;

      end Get_All_Associations;

   begin
      Next_Curr_Pos := Get_Output_Pos + 1; --  '+ 1' is for ' ('

      --  Let's first check the shortest possible aggregate

      if (Arg_Kind = An_Extension_Aggregate and then
          Next_Curr_Pos + 10 > Max_Line_Length)
        or else
         Next_Curr_Pos + 6 > Max_Line_Length
      then
         Separate_Line := True;
         goto Done;
      end if;

      if Arg_Kind = An_Extension_Aggregate then
         --  First, we have to check if there is enough room for ancestor_part
         Tmp_Expr := Extension_Aggregate_Expression (E);

         Detect_Possible_Layout
           (The_Element     => Tmp_Expr,
            Space_In_Output => Next_Space,
            Comment_Inside  => Has_Comments);

         Next_Curr_Pos := Next_Curr_Pos + 6; --  '+ 6' for ' WITH '

         if Has_Comments or else
            Next_Curr_Pos + 3 > Max_Line_Length
         then
            Separate_Line := True;
            goto Done;
         end if;

      end if;

      All_Associations := new Asis.Element_List'(Get_All_Associations);

      if All_Associations'Length = 0
        and then
         (Arg_Kind = A_Record_Aggregate
            or else
          Arg_Kind = An_Extension_Aggregate)
      then
         --  (null record);
         Separate_Line := Next_Curr_Pos + 13 > Max_Line_Length;
      else
         Check_List_Layout
           (Curr_Pos             => Next_Curr_Pos,
            Elements             => All_Associations.all,
            Needs_Separate_Lines => Separate_Line);
      end if;

      <<Done>> Set_Comp_Asctns_On_Sep_Lines (Separate_Line);

      --  Setting the starting position of the aggregate (and its
      --  associations)
      --
      --  The goal is to have a smart formatting for multi-level aggregates
      --
      --  There is no limits for getting smarter and smarter... At the moment
      --  we are not very clever

      if Separate_Line and then
         Encl_Kind in A_Record_Component_Association ..
                      An_Array_Component_Association
      then
         Start_Pos := Get_Output_Pos + 1;
      end if;

      Set_Comp_Asctns_Start_Pos (Start_Pos);

      --  Computing the associations alignment

      if Separate_Line      and then
         Align_Arrows       and then
         Arg_Kind /= A_Positional_Array_Aggregate
      then

         if Start_Pos = 0 then
            Start_Pos := (Logical_Depth + 1) * PP_Indentation + 1;
         end if;

         if All_Associations = null then
            --  May be the case if we've jumped to the Done label
            All_Associations := new Asis.Element_List'(Get_All_Associations);
         end if;

         Compute_Associations_Alignment
           (Start_Pos => Start_Pos,
            Elements  => All_Associations.all,
            Arrow_Pos => Arrow_Position,
            Max_Expr  => Max_Expr_Len);

      end if;

      Set_Arrow_Start_Pos (Arrow_Position);

      Free (All_Associations);

   end Compute_Aggregate_Layout;

   -------------------------
   -- Compute_Call_Layout --
   -------------------------

   procedure Compute_Call_Layout (E : Asis.Element) is
      Arg_Kind  : constant Flat_Element_Kinds := Flat_Element_Kind (E);
      Encl_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;

      All_Associations   : Element_List_Access;
      Called_Pref        : Asis.Element;

      Next_Curr_Pos      : Natural;
      Old_Next_Curr_Pos  : Natural;
      Next_Space         : Natural;
      Next_Call_Space    : Natural := 0;
      Has_Comments       : Boolean := False;
      Longest_Par        : Positive;

      --  Parameters to set:

      Pars_Flag      : Boolean := False;
      Move_Call_Flag : Boolean := False;
      Start_Par      : Natural := 0;
      Start_Call     : Natural := 0;
      Start_Arrow    : Natural := 0;
      Expr_Size      : Natural := 0;

      function Get_All_Associations return Element_List;
      --  returns the list of all the parameter associations of the call

      function Get_All_Associations return Element_List is
      begin

         case Arg_Kind is
            when A_Function_Call =>

               return Function_Call_Parameters (E);

            when An_Entry_Call_Statement    |
                 A_Procedure_Call_Statement =>

               return Call_Statement_Parameters (E);

            when Flat_Pragma_Kinds =>

               return Pragma_Argument_Associations (E);

            when others =>

               return Nil_Element_List;

         end case;

      end Get_All_Associations;

   begin

      if Arg_Kind = A_Function_Call then
         Called_Pref := Prefix (E);
      elsif Arg_Kind not in Flat_Pragma_Kinds then
         Called_Pref := Called_Name (E);
      end if;

      Next_Curr_Pos := Get_Output_Pos;

      if Arg_Kind = A_Function_Call and then
         not Space_Just_Printed     and then
         not Postponed_Space
      then
         Next_Curr_Pos := Next_Curr_Pos + 1;
         --  '+ 1' is for ' ' before the call
      end if;

      if Arg_Kind not in Flat_Pragma_Kinds then
         Detect_Possible_Layout
           (The_Element     => Called_Pref,
            Space_In_Output => Next_Space,
            Comment_Inside  => Has_Comments);

         if Arg_Kind = A_Function_Call          and then
            ((Encl_Kind = A_Parameter_Association and then
             not (Is_Nil (Formal_Parameter (Get_Enclosing_Element))))
            or else
             Last_Dlm = Assignment_Dlm)
         then
            Detect_Possible_Layout
              (The_Element     => E,
               Space_In_Output => Next_Call_Space,
               Comment_Inside  => Has_Comments);

         end if;

      else
         --  In case of a pragma, we have already printed out the keyword
         --  'PRAGMA" and the pragma name.
         Next_Space := 0;
      end if;

      Old_Next_Curr_Pos := Next_Curr_Pos;
      Next_Curr_Pos     := Next_Curr_Pos + Next_Space + 2;
      --  '+ 2" is for ' (' or ';'

      if Next_Curr_Pos > Max_Line_Length
        or else
           (((Encl_Kind = A_Parameter_Association and then
              not (Is_Nil (Formal_Parameter (Get_Enclosing_Element))))
           or else
             Last_Dlm = Assignment_Dlm)
         and then
           (Old_Next_Curr_Pos + Next_Call_Space + 1 > Max_Line_Length or else
            Has_Comments))
      then

         --  If we are in multi-level call, we try to move the call left to
         --  provide some reasonable layout...

         Move_Call_Flag := True;

         if Arg_Kind = A_Function_Call and then
            (Encl_Kind = A_Parameter_Association or else
             Last_Dlm = Assignment_Dlm)
         then

            if Encl_Kind = A_Parameter_Association then
               Start_Call := Enclosing_Association_Start_Pos;
            else
               Start_Call := Logical_Depth * PP_Indentation + 1;
            end if;

            if Start_Call > 0 then
               Start_Call := Start_Call + PP_Indentation;
            else
               Start_Call := (Logical_Depth + 1) * PP_Indentation + 1;
            end if;

            if Start_Call + Next_Space > Max_Line_Length then

               Start_Call :=
                  (Start_Call / PP_Indentation) * PP_Indentation +
                  1 - PP_Indentation;

               while Start_Call + Next_Space > Max_Line_Length loop
                  Start_Call := Start_Call - PP_Indentation;

                  if Start_Call <= PP_Indentation then
                     Start_Call := 1;
                     exit;
                  end if;
               end loop;

            end if;

         else
            --  What else can we do with a procedure or entry call?...
            Start_Call := 1;
         end if;

         if Start_Call = 1 then
            --  The situation is definitely hard - we can not provide any
            --  reasonable formatting
            Error ("the line is too long");
         end if;

         Next_Curr_Pos  := Start_Call + Next_Space + 2;

      end if;

      if Arg_Kind = A_Function_Call and then
         Take_Then_Into_Account
      then
         Next_Curr_Pos := Next_Curr_Pos + 5;
         --  We need this to avoid the formatting like this:
         --
         --        if Some_Condition (Some_Par)
         --        then
         --           ...
      end if;

      --  Now, computing the parameter associations layout

      All_Associations := new Element_List'(Get_All_Associations);

      Check_List_Layout_And_Max_Elem
        (Curr_Pos             => Next_Curr_Pos,
         Elements             => All_Associations.all,
         Needs_Separate_Lines => Pars_Flag,
         Max_Element_Space    => Longest_Par);

      if Pars_Flag then
         --  We have to set the starting position for associations

         if Move_Call_Flag then
            Start_Par := Start_Call;
         elsif Arg_Kind = A_Function_Call then
            Start_Par := Output_Pos;
         else
            Start_Par := Logical_Depth * PP_Indentation;
         end if;

         Start_Par := Start_Par + PP_Indentation;

         if not Move_Call_Flag then
            Start_Par := Start_Par + 1;
         end if;

         if Arg_Kind = A_Function_Call                    and then
            Start_Par + Longest_Par + 1 > Max_Line_Length and then
            Last_Dlm = Arrow_Dlm                          and then
            not Move_Call_Flag
         then
            --  Here we have a problem: even we place each parameter
            --  association on a separate line, there is not enough place
            --  for the longest association. here we try to move all the call
            --  left.
            --
            --  The only situation when we try to get the reasonable layout
            --  is if the call is the function call being the argument of
            --  some parameter association (or the right part of component
            --   association... what else?...

            Move_Call_Flag := True;

            --  We are in the association after '=>'
            --  And if we are here, we may be sure that this association
            --  starts from the new line
            Start_Call := Enclosing_Association_Start_Pos + PP_Indentation;

            --  Now check that it really helps, and if not - just move
            --  all the call to the leftmost position:

--            if not (Start_Call + PP_Indentation + 1 +
--                    Longest_Par + 1 > Available_In_Output)
--            then
--               Start_Call := 1;
--            end if;

--            Start_Par := Start_Call + PP_Indentation + 1;
            Start_Par := Start_Call + PP_Indentation;
         end if;

      end if;

      --  Setting the layout info:
      Set_Pars_On_Sep_Lines  (Separate_Line => Pars_Flag);
      Set_Move_All_Call_Left (Move_It       => Move_Call_Flag);
      Set_Start_Par_Pos      (Position      => Start_Par);
      Set_Start_Call_Pos     (Position      => Start_Call);

      --  Compute and set alignment:
      if Pars_Flag and then Align_Arrows then

         Compute_Associations_Alignment
           (Start_Pos => Start_Par,
            Elements  => All_Associations.all,
            Arrow_Pos => Start_Arrow,
            Max_Expr  => Expr_Size);

      end if;

      Set_Arrow_Start_Pos (Start_Arrow);

      Free (All_Associations);

   end Compute_Call_Layout;

   ---------------------------------
   -- Compute_Case_Choices_Layout --
   ---------------------------------

   procedure Compute_Case_Choices_Layout (E : Asis.Element) is
      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (E);

      function Get_Choices return Element_List;
      --  returns the list of choices from E

      function Get_Choices return Element_List is
      begin

         case Arg_Kind is
            when A_Case_Path =>
               return Case_Statement_Alternative_Choices (E);
            when An_Exception_Handler =>
               return Exception_Choices (E);
            when A_Variant =>
               return Variant_Choices (E);
            when A_Record_Component_Association =>
               return Record_Component_Choices (E);
            when An_Array_Component_Association =>
               return Array_Component_Choices (E);
            when others =>
               return Nil_Element_List;
         end case;

      end Get_Choices;

      Choices : constant Element_List := Get_Choices;

      --  Layout parameters to compute and to set
      Choices_On_Separate_Lines : Boolean := False;
      Start_Choice              : Natural := 0;
      Start_Vertical_Line       : Natural := 0;

      Choice_Space         : Positive;
      Tmp_Pos              : Positive;
      Alignment_Impossible : Boolean;
      Tmp_Layout           : Layout_Info;

   begin
      Current_State.Layout.Pos1  := 0;
      Current_State.Layout.Pos2  := 0;
      Current_State.Layout.Pos3  := 0;
      Current_State.Layout.Flag1 := False;
      Current_State.Layout.Flag2 := False;
      Current_State.Layout.Flag5 := False;

      if Choices'Length > 1 then

         if Is_New_Output_Line and then
            Arg_Kind not in Flat_Association_Kinds  --   ???
         then
            PP_Space;
         end if;

         Tmp_Pos := Get_Output_Pos;

         Check_List_Layout
           (Curr_Pos             => Tmp_Pos,
            Elements             => Choices,
            Needs_Separate_Lines => Choices_On_Separate_Lines);

         if not Choices_On_Separate_Lines then
            --  We can not be sure that we can place all the choices into
            --  one line:
            --  - Check_List_Layout supposes that the list elements are
            --    separated by two characters (',' and ' '), but here we
            --    have three characters ' | '
            --  - we have also to place ' =>'

            if Tmp_Pos + (Choices'Length - 1) + 3 > Max_Line_Length then
               Choices_On_Separate_Lines := True;
            end if;

         end if;

         if Choices_On_Separate_Lines then
            Start_Choice := Get_Output_Pos;

            Align_Info.Init;

               for J in Choices'Range loop

                  Detect_Possible_Layout
                    (The_Element     => Choices (J),
                     Space_In_Output => Choice_Space,
                     Comment_Inside  => Alignment_Impossible);

                  if Alignment_Impossible or else
                     Choice_Space + 2 > Available_In_Output
                  then
                     Alignment_Impossible := True;
                     exit;
                  else
                     Tmp_Layout.Pos1 := Start_Choice + Choice_Space + 1;
                     Align_Info.Append (Tmp_Layout);
                  end if;

               end loop;

               if not Alignment_Impossible then

                  for J in 1 .. Align_Info.Last loop

                     if Align_Info.Table (J).Pos1 > Start_Vertical_Line then
                        Start_Vertical_Line := Align_Info.Table (J).Pos1;
                     end if;

                  end loop;

               end if;

               Current_State.Layout.Pos1  := Start_Choice;
               Current_State.Layout.Pos2  := Start_Vertical_Line;
               Current_State.Layout.Flag1 := Choices_On_Separate_Lines;

               if Start_Vertical_Line > 0 then
                  Current_State.Layout.Flag5 := True;
               end if;

         end if;

      end if;

   end Compute_Case_Choices_Layout;

   ----------------------------------------
   -- Compute_Component_Clause_Alignment --
   ----------------------------------------

   procedure Compute_Component_Clause_Alignment
     (Start_Pos  :     Natural;
      Elements   :     Asis.Element_List;
      At_Pos     : out Natural)
   is
      Comp_Name : Asis.Element;
      --  Component name in a component clause

      At_Pos_Tmp : Natural;
   begin

      At_Pos := 0;

      for J in Elements'Range loop

         Comp_Name := Representation_Clause_Name (Elements (J));

         --  ??? This is a very primitive approach! We did not check if the
         --  component clause can fit the line and if it contains comments
         --  inside
         --  ??? (The same is the case for all the other alignments!!!)

         At_Pos_Tmp := Start_Pos + Name_Image (Comp_Name)'Length + 1;

         At_Pos := Natural'Max (At_Pos, At_Pos_Tmp);

      end loop;

   end Compute_Component_Clause_Alignment;

   ---------------------------------------
   -- Compute_Context_Clauses_Alignment --
   ---------------------------------------

   procedure Compute_Context_Clauses_Alignment
     (Clause_Elements : Asis.Element_List;
      Use_Pos         : out Natural)
   is
      With_Space           : Natural := 0;
      Alignment_Impossible : Boolean;
      Tmp_Layout           : Layout_Info;
   begin
      Use_Pos := 0;

      if Clause_Elements'Length <= 3 then
         return;
      end if;

      Align_Info.Init;

      for J in Clause_Elements'Range loop

         if Flat_Element_Kind (Clause_Elements (J)) = A_With_Clause then

            Detect_Possible_Layout
              (The_Element     => Clause_Elements (J),
               Space_In_Output => With_Space,
               Comment_Inside  => Alignment_Impossible);

            if Alignment_Impossible then
               exit;
            else
               Tmp_Layout.Pos1 := With_Space + 1;
               Align_Info.Append (Tmp_Layout);
            end if;

         end if;

      end loop;

      if not Alignment_Impossible then
         --  At the moment we just compute the maximum length of with clause

         for J in 1 .. Align_Info.Last loop

            if Align_Info.Table (J).Pos1 > Use_Pos then
               Use_Pos := Align_Info.Table (J).Pos1;
            end if;

         end loop;

      end if;

   end Compute_Context_Clauses_Alignment;

   ------------------------------------
   -- Compute_Declarations_Alignment --
   ------------------------------------

   procedure Compute_Declarations_Alignment
     (Start_Pos  :     Natural;
      Elements   :     Asis.Element_List;
      Colon_Pos  : out Natural;
      Assign_Pos : out Natural)
   is
      Nms                    : Element_List_Access;

      Tmp_Layout             : Layout_Info;
      Next_Pos               : Natural;
      Alignment_Impossible   : Boolean;
      Def_El                 : Element;
      Def_Space              : Natural := 0;
      Comments_Present       : Boolean;

      function Definition (E : Element) return Element;
      --  Returns the definition part of the declaration. Different ASIS
      --  queries may be needed for different kinds of declarations, so this
      --  function takes care of this.

      function Assignment_Present (E : Element) return Boolean;
      --  Check if E has an assignment sign (and the initialization
      --  expression) as a part of its structure

      function Assignment_Present (E : Element) return Boolean is
         Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (E);
         Result   : Boolean                     := False;
      begin

         case Arg_Kind is
            when An_Integer_Number_Declaration |
                 A_Real_Number_Declaration     |
                 A_Constant_Declaration        =>
               Result := True;

            when A_Variable_Declaration       |
                 A_Discriminant_Specification |
                 A_Component_Declaration      |
                 A_Parameter_Specification    |
                 A_Formal_Object_Declaration  =>

               Result := not Is_Nil (Initialization_Expression (E));

            when others =>
               null;
         end case;

         return Result;
      end Assignment_Present;

      function Definition (E : Element) return Element is
      begin

         case Flat_Element_Kind (E) is
            when A_Discriminant_Specification   |
                 A_Parameter_Specification      |
                 A_Formal_Object_Declaration    |
                 An_Object_Renaming_Declaration =>

               if ASIS_UL.Options.ASIS_2005_Mode then
                  return Object_Declaration_View (E);
               else
                  return Declaration_Subtype_Mark (E);
               end if;

            when A_Variable_Declaration          |
                 A_Constant_Declaration          |
                 A_Deferred_Constant_Declaration |
                 A_Component_Declaration         =>

               return Object_Declaration_View (E);

            when An_Integer_Number_Declaration |
                 A_Real_Number_Declaration     =>

               return Nil_Element;

            when others =>
               pragma Assert (False);
               return Nil_Element;
         end case;

      end Definition;

   begin
      Colon_Pos  := 0;
      Assign_Pos := 0;

      if Elements'Length = 0 or else
         (Elements'Length = 1 and then not Is_Function (Get_Enclosing_Kind))
      then
         return;
      end if;

      Align_Info.Init;

      for J in Elements'Range loop

         Nms := new Element_List'(Names (Elements (J)));

         Next_Pos := Start_Pos;

         Check_List_Layout
           (Curr_Pos             => Next_Pos,
            Elements             => Nms.all,
            Needs_Separate_Lines => Alignment_Impossible);

         if Alignment_Impossible then
            --  we just give up
            exit;
         end if;

         Tmp_Layout.Pos1 := Next_Pos - 1;

         if Align_Asign_In_Decl then

            Def_El := Definition (Elements (J));

            if Is_Nil (Def_El) then
               Def_Space := 0;
            else
               Detect_Possible_Layout
                 (The_Element     => Def_El,
                  Space_In_Output => Def_Space,
                  Comment_Inside  => Comments_Present);
            end if;

            if Comments_Present or else
               not Assignment_Present (Elements (J))
            then
               Def_Space := 0;

            else
               --  Compute the position of the assignment sign, taking
               --  into account all the additional keywords

               Next_Pos := Next_Pos + 2;

               case Mode_Kind (Elements (J)) is
                  when Not_A_Mode =>
                     null;
                  when A_Default_In_Mode =>
                     null;
--                     if Add_Default_In then
--                        Def_Space := Def_Space + 3;
--                     end if;

                  when An_In_Mode =>
                     Def_Space := Def_Space + 3;
                  when An_Out_Mode =>
                     Def_Space := Def_Space + 4;
                  when An_In_Out_Mode =>
                     Def_Space := Def_Space + 7;
               end case;

               if Trait_Kind (Elements (J)) = An_Aliased_Trait then
                  Def_Space := Def_Space + 8;
               end if;

               case Flat_Element_Kind (Elements (J)) is
                  when A_Constant_Declaration          |
                       A_Deferred_Constant_Declaration |
                       An_Integer_Number_Declaration   |
                       A_Real_Number_Declaration       =>

                     Def_Space := Def_Space + 9;
                  when others =>
                     null;
               end case;

               if Next_Pos + Def_Space + 1 > Max_Line_Length then
                  Def_Space := 0;
               end if;

            end if;

         end if;

         Tmp_Layout.Pos2 := Def_Space;

         Align_Info.Append (Tmp_Layout);

         Free (Nms);
      end loop;

      if not Alignment_Impossible then
         --  At the moment we just compute the maximum length of
         --  name list.

         for J in 1 .. Align_Info.Last loop

            if Align_Info.Table (J).Pos1 > Colon_Pos then
               Colon_Pos := Align_Info.Table (J).Pos1;
            end if;

            if Align_Info.Table (J).Pos2 > Assign_Pos then
               Assign_Pos := Align_Info.Table (J).Pos2;
            end if;

         end loop;

         if Align_Asign_In_Decl                          and then
            Assign_Pos > 0                               and then
            Assign_Pos + Colon_Pos + 4 < Max_Line_Length

         then
            Assign_Pos := Assign_Pos + Colon_Pos + 3;
         else
            Assign_Pos := 0;
         end if;

      end if;

   end Compute_Declarations_Alignment;

   ----------------------------------
   -- Compute_Discriminants_Layout --
   ----------------------------------

   procedure Compute_Discriminants_Layout (E : Asis.Element) is
      Next_Curr_Pos  : Natural;
      Separate_Lines : Boolean := False;
   begin
      Next_Curr_Pos := Get_Output_Pos + 2; --  '+ 2' is for ' ('

      Check_List_Layout
        (Curr_Pos             => Next_Curr_Pos,
         Elements             => Discriminants (E),
         Needs_Separate_Lines => Separate_Lines);

      Current_State.Layout.Flag1 := Separate_Lines;

   end Compute_Discriminants_Layout;

   -----------------------------
   -- Compute_Enum_Def_Layout --
   -----------------------------

   procedure Compute_Enum_Def_Layout (E : Asis.Element) is
      Next_Space         : Natural;
      Has_Comments       : Boolean := False;

      Sep_Lines          : Boolean := False;
   begin
      Current_State.Layout.Pos1  := 0;
      Current_State.Layout.Pos2  := 0;
      Current_State.Layout.Pos3  := 0;
      Current_State.Layout.Flag1 := False;
      Current_State.Layout.Flag2 := False;

      Detect_Possible_Layout
        (The_Element     => E,
         Space_In_Output => Next_Space,
         Comment_Inside  => Has_Comments);

      if Has_Comments
        or else Output_Pos + Next_Space + 2 > Max_Line_Length
        or else Enumeration_Literal_Declarations (E)'Length > Max_Enums_On_Line
      then
         Sep_Lines := True;
      end if;

      Current_State.Layout.Flag1 := Sep_Lines;
   end Compute_Enum_Def_Layout;

   -------------------------------------
   --  Compute_Generic_Actuals_Layout --
   -------------------------------------

   procedure Compute_Generic_Actuals_Layout is
      All_Associations : constant Element_List :=
         Generic_Actual_Part (Get_Enclosing_Element);
      Start_Arrow      : Natural := 0;
      Expr_Size        : Natural := 0;
   begin

      Compute_Associations_Alignment
        (Start_Pos => Output_Pos,
         Elements  => All_Associations,
         Arrow_Pos => Start_Arrow,
         Max_Expr  => Expr_Size);

      if Start_Arrow > 0 then
         Set_Generic_Arrow_Start_Pos (Start_Arrow);
      end if;

   end Compute_Generic_Actuals_Layout;

   -------------------------------
   -- Compute_Infix_Call_Layout --
   -------------------------------

   procedure Compute_Infix_Call_Layout (E : Asis.Element) is

      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (E);
      Next_Pos : Natural                     := Output_Pos;

      Next_Space   : Natural;
      Has_Comments : Boolean;

      Op_Kind    : Flat_Element_Kinds := Arg_Kind;

      Same_Priority_Sequence : Boolean := False;
      Enclosing_Call : Asis.Element := Nil_Element;
      Encl_Call_Kind : Flat_Element_Kinds;

      Move_In_Stack : Natural := 0;

   begin
      Current_State.Layout.Pos1  := 0;   --  ???
      Current_State.Layout.Pos2  := 0;   --  ???
      Current_State.Layout.Pos3  := 0;   --  ???
      Current_State.Layout.Flag1 := False;   --  ???
      Current_State.Layout.Flag2 := False;   --  ???

      if Postponed_Space and then
         not Space_Just_Printed
      then
         Next_Pos := Next_Pos + 1;
      end if;

      --  First, check if we are in the sequence of same-priority operations

      case Get_Enclosing_Kind is

         when An_And_Then_Short_Circuit |
              An_Or_Else_Short_Circuit  =>
            Enclosing_Call := Get_Enclosing_Element;
         when A_Parameter_Association =>
            Move_In_Stack := 1;
            Enclosing_Call := Get_Enclosing_Element (Move_In_Stack);
         when others =>
            null;
      end case;

      if not Is_Nil (Enclosing_Call) then

         Encl_Call_Kind := Flat_Element_Kind (Enclosing_Call);

         case Encl_Call_Kind is

            when An_And_Then_Short_Circuit |
                 An_Or_Else_Short_Circuit  =>

               Same_Priority_Sequence := Encl_Call_Kind = Arg_Kind;

            when A_Function_Call =>

               if Arg_Kind = A_Function_Call then
                  Op_Kind := Flat_Element_Kind (Prefix (E));
               end if;

               if not Is_Prefix_Call (Enclosing_Call) then
                  Same_Priority_Sequence :=
                     Are_Of_Same_Priority
                       (Op_Kind,
                        Flat_Element_Kind (Prefix (Enclosing_Call)));
               end if;

            when others =>
               null;
         end case;

      end if;

      if Same_Priority_Sequence then
         Current_State.Layout := Traversal_Stack.Top (Move_In_Stack).Layout;
         return;
      end if;

      --  If we are here, we have to compute the layout

      --  First, check if we have a simple case: all the call can fit one
      --  line

      Detect_Possible_Layout
        (The_Element     => E,
         Space_In_Output => Next_Space,
         Comment_Inside  => Has_Comments);

      if Take_Then_Into_Account then
         Next_Space := Next_Space + 5;
         --  We need this to avoid the formatting like this:
         --
         --        if Something + Something_Else
         --        then
         --           ...
      end if;

      if Has_Comments or else
         not (Next_Pos + Next_Space + 1 < Max_Line_Length)
      then
         --  We add 1 in case if ';' is needed after the call
         Current_State.Layout.Pos1  := Next_Pos;
         Current_State.Layout.Flag1 := True;
      end if;

   end Compute_Infix_Call_Layout;

   -----------------------------
   -- Compute_Obj_Decl_Layout --
   -----------------------------

   procedure Compute_Obj_Decl_Layout (E : Asis.Element) is
      Arg_Kind   : constant Flat_Element_Kinds := Flat_Element_Kind (E);
      Next_Pos   : Natural;
      Line_Break : Boolean;

      Tmp_El     : Asis.Element;
      Tmp_Space  : Natural;

      function Get_Definition (E : Asis.Element) return Asis.Element;
      --  Returns the definition part of the declaration. This function
      --  is trivial at the moment, but some more variants may be added when
      --  we extend the number of cases where Compute_Obj_Decl_Layout is
      --  applied

      function Get_Definition (E : Asis.Element) return Asis.Element is
         Result : Asis.Element := Nil_Element;
      begin
         case Arg_Kind is
            when A_Variable_Declaration .. A_Deferred_Constant_Declaration =>
               Result := Object_Declaration_View (E);
            when others =>
               null;
         end case;

         return Result;
      end Get_Definition;

      function Get_Init_Expression (E : Asis.Element) return Asis.Element;
      --  Returns the initialization expression part of the declaration, if
      --  any.

      function Get_Init_Expression (E : Asis.Element) return Asis.Element is
         Result : Asis.Element := Nil_Element;
      begin
         case Arg_Kind is
            when A_Variable_Declaration        |
                 A_Constant_Declaration        |
                 An_Integer_Number_Declaration |
                 A_Real_Number_Declaration     |
                 A_Discriminant_Specification  |
                 A_Component_Declaration       |
                 A_Parameter_Specification     |
                 A_Formal_Object_Declaration   =>

               Result := Initialization_Expression (E);
            when others =>
               null;
         end case;

         return Result;
      end Get_Init_Expression;

   begin
      Current_State.Layout.Flag1 := False;   --  ???
      Current_State.Layout.Flag2 := False;   --  ???

      Next_Pos := Logical_Depth * PP_Indentation + 1;

      Check_List_Layout
        (Curr_Pos             => Next_Pos,
         Elements             => Names (E),
         Needs_Separate_Lines => Line_Break);

      if Line_Break then
         Current_State.Layout.Flag1 := True;   --  ???
         Next_Pos := (Logical_Depth + 1) * PP_Indentation + 1;
      end if;

      Next_Pos := Natural'Max (Next_Pos, Colon_In_Paragraph);

      Tmp_El := Get_Definition (E);

      Detect_Possible_Layout
        (The_Element     => Tmp_El,
         Space_In_Output => Tmp_Space,
         Comment_Inside  => Line_Break);

      if Arg_Kind = A_Constant_Declaration or else
         Arg_Kind = A_Deferred_Constant_Declaration
      then
         Tmp_Space := Tmp_Space + 9;
      end if;

      if Trait_Kind (E) = An_Aliased_Trait then
         Tmp_Space := Tmp_Space + 8;
      end if;

      if Next_Pos + Tmp_Space + 3 > Max_Line_Length then
         --  '+ 3' stands for ' :=', this also covers ';'
         Current_State.Layout.Flag1 := True;   --  ???
         Next_Pos := (Logical_Depth + 1) * PP_Indentation + 1;
      end if;

      Tmp_El := Get_Init_Expression (E);

      if Is_Nil (Tmp_El) or else
         Flat_Element_Kind (Tmp_El) in A_Record_Aggregate ..
                                       A_Named_Array_Aggregate
      then
         return;
      end if;

      Next_Pos :=
         Natural'Max (Next_Pos + Tmp_Space + 2, Assign_In_Paragraph) + 3;

      Detect_Possible_Layout
        (The_Element     => Tmp_El,
         Space_In_Output => Tmp_Space,
         Comment_Inside  => Line_Break);

      Next_Pos := Next_Pos + Tmp_Space + 1;

      if Next_Pos > Max_Line_Length then
         Current_State.Layout.Flag2 := True;   --  ???
      end if;

   end Compute_Obj_Decl_Layout;

   ------------------------------
   -- Compute_Parameter_Layout --
   ------------------------------

   procedure Compute_Parameter_Layout is
      Encl_El   : constant Asis.Element       := Get_Enclosing_Element;
      Encl_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (Encl_El);

      All_Specs      : Element_List_Access;
      N_All_Spec     : Natural;
      Tmp_Res        : Asis.Element;

      Next_Curr_Pos  : Natural;
      Next_Space     : Natural;
      Has_Comments   : Boolean            := False;

      Separate_Lines : Boolean            := False;

      Colon_At       : Natural := 0;
      Assign_At      : Natural := 0;

      function Get_All_Specs return Element_List;
      --  returns the list of all the parameter specifications the argument
      --  belongs to

      function Get_All_Specs return Element_List is
      begin

         case Encl_Kind is
            when A_Procedure_Declaration          |
                 A_Null_Procedure_Declaration     |
                 A_Function_Declaration           |
                 A_Procedure_Body_Declaration     |
                 A_Function_Body_Declaration      |
                 A_Procedure_Renaming_Declaration |
                 A_Function_Renaming_Declaration  |
                 An_Entry_Declaration             |
                 An_Entry_Body_Declaration        |
                 A_Procedure_Body_Stub            |
                 A_Function_Body_Stub             |
                 A_Generic_Function_Declaration   |
                 A_Generic_Procedure_Declaration  |
                 A_Formal_Function_Declaration    |
                 A_Formal_Procedure_Declaration   =>

               return Parameter_Profile (Encl_El);

            when An_Access_To_Procedure                     |
                 An_Access_To_Protected_Procedure           |
                 An_Access_To_Function                      |
                 An_Access_To_Protected_Function            |
                 An_Anonymous_Access_To_Procedure           |
                 An_Anonymous_Access_To_Protected_Procedure |
                 An_Anonymous_Access_To_Function            |
                 An_Anonymous_Access_To_Protected_Function  =>

               return Access_To_Subprogram_Parameter_Profile (Encl_El);

            when An_Accept_Statement =>

               return Accept_Parameters (Encl_El);

            when others =>

               return Nil_Element_List;

         end case;

      end Get_All_Specs;

   begin
      --  So, we have to decide if all the parameter specification (??? not
      --  only) elements may be placed in the same line. Note, that this the
      --  same line where the enclosing construct is started

      All_Specs := new Asis.Element_List'(Get_All_Specs);

      --  The trivial criterium: if we have more then three parameter specs or
      --  more then two in a function - we always use separate lines for
      --  parameter specs.

      N_All_Spec := All_Specs'Length;

      if N_All_Spec > 3 or else
         (N_All_Spec > 2 and then Is_Function (Encl_Kind))
      then
         Separate_Lines := True;
         goto Done;
      end if;

      --  Now, trying to simulate putting all the parameter specs in one line:

      --  First, check is we already are on a new line:

      Next_Curr_Pos := Output_Pos + 2; --  '+ 2' stands for ' ('

      if Is_New_Output_Line or else
         Next_Curr_Pos + 7 > Max_Line_Length
      then
         --  May be, this is not the best decision ???
         Separate_Lines := True;
         goto Done;
      end if;

      Check_List_Layout
        (Curr_Pos             => Next_Curr_Pos,
         Elements             => All_Specs.all,
         Needs_Separate_Lines => Separate_Lines);

      --  Check if there is a room for 'IS'
      if not Separate_Lines and then
         Next_Curr_Pos + 2 > Max_Line_Length --  '+ 2' stands for 'is'
      then
         Separate_Lines := True;
      end if;

      --  in case of a function, we have to check if we have enough space
      --  for return Result_Type

      if not Separate_Lines and then
         Is_Function (Encl_Kind)
      then

         Next_Curr_Pos := Next_Curr_Pos + 6;  --  6 for 'RETURN'

         if Next_Curr_Pos + 5 > Max_Line_Length then
            --  '+ 5' is the most optimistic estimation 'return A is'
            Separate_Lines := True;
         else
            --  We have to compute the length of the return type

            if Encl_Kind = An_Access_To_Function
              or else Encl_Kind = An_Access_To_Protected_Function
              or else Encl_Kind = An_Anonymous_Access_To_Function
              or else Encl_Kind = An_Anonymous_Access_To_Protected_Function
            then
               Tmp_Res := Access_To_Function_Result_Profile (Encl_El);
            else
               Tmp_Res := Result_Profile (Encl_El);
            end if;

            Detect_Possible_Layout
              (The_Element     => Tmp_Res,
               Space_In_Output => Next_Space,
               Comment_Inside  => Has_Comments);

            Next_Curr_Pos := Next_Curr_Pos + 1 + Next_Space;
            --  '+ 1' means the space between 'RETURN' and the type name,

            if Has_Comments or else
               Next_Curr_Pos + 3 > Max_Line_Length
               --  '+ 3' means ' is'
            then
               Separate_Lines := True;
            end if;

         end if;

      end if;

      --  Check that there is enough room for "is abstract;" in case of
      --  abstract subprogram or for "is separate;" in case of body stub

      if (Trait_Kind (Encl_El) = An_Abstract_Trait or else
          Encl_Kind in A_Flat_Body_Stub)
        and then
         Next_Curr_Pos + 13 > Max_Line_Length
      then
         Separate_Lines := True;
      end if;

      <<Done>> Set_Par_Spec_On_Sep_Lines (Separate_Lines);

      --  Computing the alignment.

      if Separate_Lines and then Align_Colons_In_Decl then

         case Encl_Kind is

            when A_Procedure_Declaration          |
                 A_Null_Procedure_Declaration     |
                 A_Function_Declaration           |
                 A_Formal_Function_Declaration    |
                 A_Formal_Procedure_Declaration   |
                 A_Procedure_Renaming_Declaration |
                 A_Function_Renaming_Declaration  |
                 An_Entry_Declaration             |
                 A_Procedure_Body_Stub            |
                 A_Function_Body_Stub             |
                 A_Generic_Procedure_Declaration  |
                 A_Generic_Function_Declaration   |
                 An_Access_To_Function            |
                 An_Access_To_Protected_Function  =>

               Next_Curr_Pos := (Logical_Depth + 1) * PP_Indentation + 1;

            when others =>
               Next_Curr_Pos := Logical_Depth * PP_Indentation + 1;
         end case;

         Compute_Declarations_Alignment
           (Start_Pos  => Next_Curr_Pos,
            Elements   => All_Specs.all,
            Colon_Pos  => Colon_At,
            Assign_Pos => Assign_At);

         --  And now, in case of a function, we have to take into account
         --  the return type

         if Is_Function (Encl_Kind) then

            if Colon_At < Next_Curr_Pos + 5 then
               Assign_At := Assign_At + (Next_Curr_Pos + 5 - Colon_At);
               Colon_At  := Next_Curr_Pos + 5;
               --  ??? may be, here we have to recompute Assign_At if it
               --  ??? is too large
            end if;

         end if;

         Set_Assign_Start_Pos (Assign_At);
         Set_Colon_Start_Pos  (Colon_At);

      end if;

      Free (All_Specs);

   end Compute_Parameter_Layout;

   --------------------------------------------
   -- Compute_Simple_Expression_Range_Layout --
   --------------------------------------------

   procedure Compute_Simple_Expression_Range_Layout (E : Asis.Element) is
      Next_Space   : Positive;
      Has_Comments : Boolean;

      Separate_Line : Boolean := False;

      Then_Len : Natural := 0;
   begin
      Current_State.Layout.Pos1  := 0;
      Current_State.Layout.Pos2  := 0;
      Current_State.Layout.Pos3  := 0;
      Current_State.Layout.Flag1 := False;
      Current_State.Layout.Flag2 := False;

      if Get_Enclosing_Kind (1) in An_If_Path .. An_Elsif_Path and then
         Last_If_Path_Start = Current_Out_Line
      then
         Then_Len := 5;
      end if;

      Detect_Possible_Layout
        (The_Element     => E,
         Space_In_Output => Next_Space,
         Comment_Inside  => Has_Comments);

      if Has_Comments or else
         Next_Space + 2 + Then_Len > Available_In_Output
      then
         Separate_Line := True;
      end if;

      Current_State.Layout.Flag1 := Separate_Line;

      if not Has_Comments then
         --  Check if we have to start the right expression from the new line:

         if Next_Space + (Logical_Depth + 1) * PP_Indentation + 1 <
            Max_Line_Length
         then
            Separate_Line := False;
         end if;
      end if;

      Current_State.Layout.Flag2 := Separate_Line;

   end Compute_Simple_Expression_Range_Layout;

   ---------------------------
   -- Short_Circuit_Padding --
   ---------------------------

   function Short_Circuit_Padding return Positive is
      Encl_Expr_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
      Encl_Constr    : Asis.Element                := Get_Enclosing_Element;

      Result : Positive := Start_Par_Pos - 1;
   begin

      while Flat_Element_Kind (Encl_Constr) = Encl_Expr_Kind loop
         Encl_Constr := Enclosing_Element (Encl_Constr);
      end loop;

      case Flat_Element_Kind (Encl_Constr) is
         when An_If_Path             |
              An_Elsif_Path          |
              A_While_Loop_Statement =>

            --  The indentation has been already increased by the enclosed
            --  construct containing the expression
            Result := Logical_Depth * PP_Indentation;
         when others =>
            null;
      end case;

      return Result;

   end Short_Circuit_Padding;

   ----------------------------
   -- Take_Then_Into_Account --
   ----------------------------

   function Take_Then_Into_Account return Boolean is
      Encl_Kind : Flat_Element_Kinds := Get_Enclosing_Kind;
      Step_Up   : Natural := 0;

      Result    : Boolean            := False;
   begin

      if Last_If_Path_Start = Current_Out_Line then

         while Encl_Kind = A_Parenthesized_Expression loop
            Step_Up := Step_Up + 1;
            Encl_Kind := Get_Enclosing_Kind (Step_Up);
         end loop;

         case Encl_Kind is

            when An_If_Path .. An_Else_Path =>
               Result := True;

            when A_Parameter_Association =>

               if Function_Call_Parameters
                    (Get_Enclosing_Element (Step_Up + 1))'Length = 1
               then
                  Result := True;
               end if;

            when others =>
               null;
         end case;
      end if;

      return Result;
   end Take_Then_Into_Account;

   ----------------------
   -- Access functions --
   ----------------------

   function Align_Arrow_As_Vert_Line return Boolean is
   begin
      --  Some guard condition ???
      return Traversal_Stack.Top.Layout.Flag5;
   end Align_Arrow_As_Vert_Line;

   function Assign_Start_Pos return Natural is
      Step_Up : Natural := 0;
      Result  : Natural := 0;
   begin

      while not (Flat_Element_Kind (Traversal_Stack.Top (Step_Up).The_Element)
                 in Flat_Statement_Kinds
               or else
                 Flat_Element_Kind (Traversal_Stack.Top (Step_Up).The_Element)
                 in Flat_Declaration_Kinds)
      loop
         Step_Up := Step_Up + 1;
      end loop;

      if Flat_Element_Kind (Traversal_Stack.Top (Step_Up).The_Element) =
         A_Parameter_Specification
      then
         Result := Traversal_Stack.Top (Step_Up + 1).Layout.Pos2;
      end if;

      return Result;
   end Assign_Start_Pos;

   function Choice_On_Separate_Line return Boolean is
      Step_Up   : Natural            := 0;
      Result    : Boolean            := False;
      Encl_Kind : Flat_Element_Kinds := Get_Enclosing_Kind;
   begin

      if not Has_Choices (Encl_Kind) then
         Encl_Kind := Get_Enclosing_Kind (1);
         Step_Up   := 1;
      end if;

      if Has_Choices (Encl_Kind) then
         Result := Traversal_Stack.Top (Step_Up).Layout.Flag1;
      end if;

      return Result;
   end Choice_On_Separate_Line;

   function Choice_Start_Pos return Natural is
      Step_Up   : Natural            := 0;
      Result    : Natural            := 0;
      Encl_Kind : Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      if not Has_Choices (Encl_Kind) then
         Encl_Kind := Get_Enclosing_Kind (1);
         Step_Up   := 1;
      end if;

      if Has_Choices (Encl_Kind) then
         Result := Traversal_Stack.Top (Step_Up).Layout.Pos1;
      end if;

      return Result;

   end Choice_Start_Pos;

   function Colon_Start_Pos return Natural is
      Upper_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
      Step_Up    : Natural                     := 0;
   begin

      if Upper_Kind = A_Parameter_Specification then
         Step_Up := 1;
      end if;

      return Traversal_Stack.Top (Step_Up).Layout.Pos1;
   end Colon_Start_Pos;

   function Discriminants_On_Separate_Lines return Boolean is
      Result : Boolean := False;
   begin
      if Flat_Element_Kind (Current_State.The_Element) =
         A_Known_Discriminant_Part
      then
         Result := Current_State.Layout.Flag1;
      elsif Get_Enclosing_Kind = A_Known_Discriminant_Part then
         Result := Traversal_Stack.Top.Layout.Flag1;
      elsif Get_Enclosing_Kind = A_Discriminant_Specification and then
            Get_Enclosing_Kind (1) = A_Known_Discriminant_Part
      then
         Result := Traversal_Stack.Top (1).Layout.Flag1;
      end if;

      return Result;
   end Discriminants_On_Separate_Lines;

   function Enclosing_Association_Start_Pos return Natural is
   begin
      return Traversal_Stack.Top (1).Layout.Pos1;
   end Enclosing_Association_Start_Pos;

   function Enum_Literal_Def_On_Separate_Lines return Boolean
   is
   begin
      return Traversal_Stack.Top (1).Layout.Flag1;
   end Enum_Literal_Def_On_Separate_Lines;

   function Generic_Associations_On_Separate_Lines return Boolean is
   begin
      return Traversal_Stack.Top.Layout.Flag1;
   end Generic_Associations_On_Separate_Lines;

   function Impose_Extra_Indentation return Boolean is
   begin
      return Current_State.Layout.Flag1;
   end Impose_Extra_Indentation;

   function Is_And_Then_Arg return Boolean is
   begin
      return Current_State.Layout.Flag4;
   end Is_And_Then_Arg;

   function Is_First_Par_Association return Boolean is
   begin
      return Current_State.Layout.Flag1;
   end Is_First_Par_Association;

   function Is_Or_Else_Arg return Boolean is
   begin
      return Current_State.Layout.Flag5;
   end Is_Or_Else_Arg;

   function Start_Aggregate_From_New_Line return Boolean is
      Current_Kind : constant Flat_Element_Kinds :=
         Flat_Element_Kind (Current_State.The_Element);
   begin
      pragma Assert (False
         or else Current_Kind in A_Record_Aggregate .. A_Named_Array_Aggregate
         or else Current_Kind = A_Discriminant_Constraint);

      return (Current_State.Layout.Flag1 and then
              Current_State.Layout.Pos1 = 0);

   end Start_Aggregate_From_New_Line;

   function Start_Array_Def_From_New_Line return Boolean is
   begin
      return Current_State.Layout.Flag1;
   end Start_Array_Def_From_New_Line;

   function Start_Index_Def_From_New_Line return Boolean is
      Step_Up   : Natural            := 0;
      Result    : Boolean            := False;
      Encl_Kind : Flat_Element_Kinds;
   begin

      if Is_Array_Definition
           (Flat_Element_Kind (Current_State.The_Element))
      then
         Result := Current_State.Layout.Flag2;
      else

         --  going up and looking where we are...

         loop
            Encl_Kind := Get_Enclosing_Kind (Step_Up);

            if Is_Array_Definition (Encl_Kind) then
               Result := Traversal_Stack.Top (Step_Up).Layout.Flag2;
               exit;
            end if;

            case Encl_Kind is
               when Flat_Pragma_Kinds      |
                    Flat_Declaration_Kinds |
                    Flat_Association_Kinds |
                    Flat_Statement_Kinds   |
                    Flat_Clause_Kinds      =>
                  exit;
               when others => null;
            end case;

            Step_Up := Step_Up + 1;
         end loop;

      end if;

      return Result;
   end Start_Index_Def_From_New_Line;

   function Start_Range_Expression_From_New_Line return Boolean is
      Result : Boolean := False;
   begin

      if Get_Enclosing_Kind = A_Simple_Expression_Range
        or else
         Get_Enclosing_Kind = A_Discrete_Simple_Expression_Range
        or else
         Get_Enclosing_Kind =
           A_Discrete_Simple_Expression_Range_As_Subtype_Definition
      then

         if Is_Equal (Current_State.The_Element,
                      Lower_Bound (Get_Enclosing_Element))
         then
            Result := Traversal_Stack.Top.Layout.Flag1;
         elsif Is_Equal (Current_State.The_Element,
                         Upper_Bound (Get_Enclosing_Element))
         then
            Result := Traversal_Stack.Top.Layout.Flag2;
         end if;

      end if;

      return Result;
   end Start_Range_Expression_From_New_Line;

   function Start_Upper_Range_Expression_From_New_Line return Boolean is
      Result : Boolean := False;
   begin

      if (Get_Enclosing_Kind = A_Simple_Expression_Range
        or else
         Get_Enclosing_Kind = A_Discrete_Simple_Expression_Range
        or else
         Get_Enclosing_Kind =
           A_Discrete_Simple_Expression_Range_As_Subtype_Definition)
        and then
         Is_Equal (Current_State.The_Element,
                   Upper_Bound (Get_Enclosing_Element))
      then
         Result := Traversal_Stack.Top.Layout.Flag2;
      end if;

      return Result;
   end Start_Upper_Range_Expression_From_New_Line;

   function Vert_Line_Start_Pos return Natural is
      Step_Up   : Natural            := 0;
      Result    : Natural            := 0;
      Encl_Kind : Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      if not Has_Choices (Encl_Kind) then
         Encl_Kind := Get_Enclosing_Kind (1);
         Step_Up   := 1;
      end if;

      if Has_Choices (Encl_Kind) then
         Result := Traversal_Stack.Top (Step_Up).Layout.Pos2;
      end if;

      return Result;

   end Vert_Line_Start_Pos;

   -------------------------------------
   -- Layout info set/update routines --
   -------------------------------------

   Upper_Current_State : Traversal_Step_Record;
   --  Used in update routines for putting the layout info into the enclosing
   --  element layout record

   procedure Set_Assign_Start_Pos (Position : Natural) is
   begin
      Upper_Current_State             := Traversal_Stack.Pop;
      Upper_Current_State.Layout.Pos2 := Position;

      Traversal_Stack.Push (Upper_Current_State);
   end Set_Assign_Start_Pos;

   procedure Set_Colon_Start_Pos  (Position : Natural) is
   begin
      Upper_Current_State             := Traversal_Stack.Pop;
      Upper_Current_State.Layout.Pos1 := Position;

      Traversal_Stack.Push (Upper_Current_State);
   end Set_Colon_Start_Pos;

   procedure Set_Generic_Arrow_Start_Pos (Position : Natural) is
   begin
      Upper_Current_State             := Traversal_Stack.Pop;
      Upper_Current_State.Layout.Pos2 := Position;

      Traversal_Stack.Push (Upper_Current_State);
   end Set_Generic_Arrow_Start_Pos;

   procedure Set_Generic_Associations_On_Separate_Lines is
   begin
      Current_State.Layout.Flag1 := True;
   end Set_Generic_Associations_On_Separate_Lines;

   procedure Set_Impose_Extra_Indentation is
   begin
      Current_State.Layout.Flag1 := True;
   end Set_Impose_Extra_Indentation;

   procedure Set_Is_And_Then_Arg is
   begin
      Current_State.Layout.Flag4 := True;
   end Set_Is_And_Then_Arg;

   procedure Set_Is_First_Par_Association is
   begin
      Current_State.Layout.Flag1 := True;
   end Set_Is_First_Par_Association;

   procedure Set_Is_Or_Else_Arg is
   begin
      Current_State.Layout.Flag5 := True;
   end Set_Is_Or_Else_Arg;

   -------------------------------
   -- Old stuff, needs revising --
   -------------------------------

   function Get_Enclosing_Kind
     (Step_Down : Natural := 0)
      return      Flat_Element_Kinds
   is
   begin
      return Flat_Element_Kind (Get_Enclosing_Element (Step_Down));
   end Get_Enclosing_Kind;

   function Get_Enclosing_Element
     (Step_Down : Natural := 0)
      return      Asis.Element
   is
   begin
      return Traversal_Stack.Top (Step_Down).The_Element;
   end Get_Enclosing_Element;

   --  Aggregates:
   function Comp_Asctns_On_Sep_Lines return Boolean is
      Upper_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      pragma Assert (False
         or else Upper_Kind in A_Record_Aggregate .. A_Named_Array_Aggregate
         or else Upper_Kind = A_Discriminant_Constraint);

      return Traversal_Stack.Top.Layout.Flag1;

   end Comp_Asctns_On_Sep_Lines;

   function Comp_Asctns_Start_Pos return Natural is
      Upper_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      pragma Assert (False
         or else Upper_Kind in A_Record_Aggregate .. A_Named_Array_Aggregate
         or else Upper_Kind = A_Discriminant_Constraint);

      return Traversal_Stack.Top.Layout.Pos1;

   end Comp_Asctns_Start_Pos;

   function Arrow_Start_Pos return Natural is
      Result : Natural               := 0;
      Encl_Kind : Flat_Element_Kinds := Get_Enclosing_Kind (1);
      Step_Up   : Natural            := 1;
   begin

      if not Align_Arrow_As_Vert_Line then

         if Encl_Kind in Flat_Association_Kinds then
            Step_Up   := Step_Up + 1;
            Encl_Kind := Get_Enclosing_Kind (Step_Up);
         end if;

         if Encl_Kind = An_Entry_Call_Statement    or else
            Encl_Kind = A_Procedure_Call_Statement or else
            Encl_Kind = A_Function_Call            or else
            Encl_Kind = A_Record_Aggregate         or else
            Encl_Kind = An_Extension_Aggregate     or else
            Encl_Kind = A_Named_Array_Aggregate
         then
            Result := Traversal_Stack.Top (Step_Up).Layout.Pos2;
         end if;

      end if;

      return Result;
   end Arrow_Start_Pos;

   --  Elements having parameters? Parameter specifications?
   function Par_Spec_On_Separate_Lines return Boolean is
      Upper_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      pragma Assert (Has_Parameters (Upper_Kind));

      return Traversal_Stack.Top.Layout.Flag1;

   end Par_Spec_On_Separate_Lines;

   --  Procedure, entry and (prefix) function calls:
   function Pars_On_Sep_Lines return Boolean is
   begin
      pragma Assert (Is_Call (Traversal_Stack.Top.The_Element));

      return Traversal_Stack.Top.Layout.Flag1;
   end Pars_On_Sep_Lines;

   function Move_All_Call_Left return Boolean is
   begin
--      pragma Assert (Is_Call (Traversal_Stack.Top.The_Element));

--      return Traversal_Stack.Top.Layout.Flag2;
      return Current_State.Layout.Flag2;
   end Move_All_Call_Left;

   function Start_Par_Pos (Step_Down : Natural := 0) return Natural is
   begin
      pragma Assert (Is_Call (Traversal_Stack.Top (Step_Down).The_Element));

      return Traversal_Stack.Top (Step_Down).Layout.Pos1;
   end Start_Par_Pos;

   function Start_Call_Pos return Natural is
   begin
--      pragma Assert (Is_Call (Traversal_Stack.Top.The_Element));

--      return Traversal_Stack.Top.Layout.Pos3;
      return Current_State.Layout.Pos3;
   end Start_Call_Pos;

   --  Component, object and number declarations

   --  Definitions of the form
   --   <list of names> : <definition> [:= init_Expression]

   function Init_Expr_On_Separate_Line return Boolean is
      Upper_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      pragma Assert (May_Have_Init_Expr (Upper_Kind));

      return Traversal_Stack.Top.Layout.Flag2;
   end Init_Expr_On_Separate_Line;

   ----------------------------
   -- Layout update routines --
   ----------------------------

   --  Aggregates and discriminant constraints:
   procedure Set_Comp_Asctns_On_Sep_Lines (Separate_Line : Boolean) is
      Current_Kind : constant Flat_Element_Kinds :=
         Flat_Element_Kind (Current_State.The_Element);
   begin
      pragma Assert (False
         or else Current_Kind in A_Record_Aggregate .. A_Named_Array_Aggregate
         or else Current_Kind = A_Discriminant_Constraint);

      Current_State.Layout.Flag1 := Separate_Line;
   end Set_Comp_Asctns_On_Sep_Lines;

   procedure Set_Comp_Asctns_Start_Pos (Position : Natural) is
      Current_Kind : constant Flat_Element_Kinds :=
         Flat_Element_Kind (Current_State.The_Element);
   begin
      pragma Assert (False
         or else Current_Kind in A_Record_Aggregate .. A_Named_Array_Aggregate
         or else Current_Kind = A_Discriminant_Constraint);

      Current_State.Layout.Pos1 := Position;
   end Set_Comp_Asctns_Start_Pos;

   procedure Set_Arrow_Start_Pos (Position : Natural) is
      Current_Kind : constant Flat_Element_Kinds :=
         Flat_Element_Kind (Current_State.The_Element);
   begin
      pragma Assert (False
         or else Current_Kind in A_Record_Aggregate .. A_Named_Array_Aggregate
         or else Current_Kind = A_Discriminant_Constraint
         or else (Is_Call (Current_State.The_Element)));

      Current_State.Layout.Pos2 := Position;
   end Set_Arrow_Start_Pos;

   --  Elements having parameters? Parameter specifications?
   procedure Set_Par_Spec_On_Sep_Lines (Separate_Line : Boolean) is
      Upper_Current_State : Traversal_Step_Record := Traversal_Stack.Pop;
      Upper_Kind          : constant Flat_Element_Kinds :=
         Flat_Element_Kind (Upper_Current_State.The_Element);
   begin
      pragma Assert (Has_Parameters (Upper_Kind));

      Upper_Current_State.Layout.Flag1 := Separate_Line;

      Traversal_Stack.Push (Upper_Current_State);

   end Set_Par_Spec_On_Sep_Lines;

   --  Procedure, entry and (prefix) function calls:
   procedure Set_Pars_On_Sep_Lines  (Separate_Line : Boolean) is
   begin
      pragma Assert (Is_Call (Current_State.The_Element));

      Current_State.Layout.Flag1 := Separate_Line;
   end Set_Pars_On_Sep_Lines;

   procedure Set_Move_All_Call_Left (Move_It : Boolean) is
   begin
      pragma Assert (Is_Call (Current_State.The_Element));

      Current_State.Layout.Flag2 := Move_It;
   end Set_Move_All_Call_Left;

   procedure Set_Start_Par_Pos (Position : Natural) is
   begin
      pragma Assert (Is_Call (Current_State.The_Element));

      Current_State.Layout.Pos1 := Position;
   end Set_Start_Par_Pos;

   procedure Set_Start_Call_Pos (Position : Natural) is
   begin
      pragma Assert (Is_Call (Current_State.The_Element));

      Current_State.Layout.Pos3 := Position;
   end Set_Start_Call_Pos;

end GNATPP.Layout;
