------------------------------------------------------------------------------
--                                                                          --
--                            GNATPP COMPONENTS                             --
--                                                                          --
--               G N A T P P . P O S T _ O P E R A T I O N S                --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--                     Copyright (C) 2001-2009, 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 Ada.Characters.Handling;         use Ada.Characters.Handling;
with Asis.Elements;                   use Asis.Elements;
with Asis.Declarations;               use Asis.Declarations;
with Asis.Definitions;                use Asis.Definitions;
with Asis.Statements;                 use Asis.Statements;
with Asis.Expressions;                use Asis.Expressions;
with Asis.Clauses;                    use Asis.Clauses;
with Asis.Extensions;                 use Asis.Extensions;

with ASIS_UL.Options;

with GNATPP.PP_Output;                use GNATPP.PP_Output;
with GNATPP.State;                    use GNATPP.State;
with GNATPP.Source_Line_Buffer;       use GNATPP.Source_Line_Buffer;
with GNATPP.Comments;                 use GNATPP.Comments;
with GNATPP.Options;                  use GNATPP.Options;
with GNATPP.Utilities;                use GNATPP.Utilities;
with GNATPP.Asis_Utilities;           use GNATPP.Asis_Utilities;
with GNATPP.General_Traversal_Stacks; use GNATPP.General_Traversal_Stacks;
with GNATPP.Layout;                   use GNATPP.Layout;
with GNATPP.Paragraphs;               use GNATPP.Paragraphs;

with A4G.Skip_TB;

package body GNATPP.Post_Operations is

   ------------------------
   --  Local Subprograms --
   ------------------------

   procedure Finalize_Declaration (Element : Asis.Element);
   --  Encapsulates actions which are in common for various declarations:
   --  printing out ';', checking the next keyword (such as 'begin', 'private')
   --  and printing it out if needed

   ----------------------------------
   -- A_Compound_Statement_Post_Op --
   ----------------------------------

   procedure A_Compound_Statement_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (Element);
   begin

      if (Arg_Kind = A_Case_Statement and then
          Statement_Paths (Element)'Length < Case_Threshold)
        or else
         (Arg_Kind = A_Variant_Part and then
          Variants (Element)'Length < Case_Threshold)
        or else
          Arg_Kind = An_Extended_Return_Statement
      then
         Decrease_Indentation;
      end if;

      PP_New_Line;

      PP_Keyword (KW_End);
      Get_Next_Ada_Lexem;
      PP_Continue_Line;
      Detect_Keyword;
      PP_Keyword (Keyword);
      Get_Next_Ada_Lexem;

      A_General_Statement_Post_Op (Element, Control, State);

   end A_Compound_Statement_Post_Op;

   ---------------------------
   -- A_Declaration_Post_Op --
   ---------------------------

   procedure A_Declaration_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (Element);
   begin

      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      if (Arg_Kind = A_Single_Task_Declaration and then
          Is_Nil (Object_Declaration_View (Element)))
        or else
         (Arg_Kind = A_Task_Type_Declaration and then
          Is_Nil (Type_Declaration_View (Element)))
      then
         Decrease_Indentation;
      end if;

      Finalize_Declaration (Element);

   end A_Declaration_Post_Op;

   -----------------------------
   -- A_Defining_Name_Post_Op --
   -----------------------------

   procedure A_Defining_Name_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Enclosing_El_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;

      Colon_At : Natural := 0;
      Adjust   : Integer := 1;
   begin
      --  The problem with a defining name is that it can be followed by
      --  some keyword(s) or delimiter (left parenthesis etc)

      --  In case of a defining identifier in a defining expanded name we do
      --  nothing leaving all the processing for the whole expanded name

      if Enclosing_El_Kind = A_Defining_Expanded_Name or else
         Is_Label (Element)
      then
         return;
      end if;

      Detect_Delimiter;

      case Delimiter is

         when Colon_Dlm =>

            if Enclosing_El_Kind = A_Parameter_Specification then
               Colon_At := Colon_Start_Pos;
            elsif Enclosing_El_Kind = A_Discriminant_Specification and then
                  not Discriminants_On_Separate_Lines
            then
               --  Here we have the paragraph-specific colon position
               --  computed, but this is meaningless if we have to place
               --  the discriminants in one line)
               Colon_At := 0;
            else
               Colon_At := Colon_In_Paragraph;
            end if;

            if Colon_At > 0 then
               PP_Pad_Up_To (Colon_At);
            elsif not RM_Style_Spacing
               or else
                  Enclosing_El_Kind not in Flat_Statement_Kinds
            then
               PP_Continue_Line;
            end if;

            PP_Delimiter (Delimiter);

            Get_Next_Ada_Lexem;

            case Enclosing_El_Kind is

               when A_Discriminant_Specification   |
                    A_Parameter_Specification      |
                    A_Formal_Object_Declaration    |
                    An_Object_Renaming_Declaration =>

                  --  Here we may have a null exclusion (Ada 2005)

                  Detect_Keyword;

                  while Keyword /= Not_A_KW loop
                     PP_Continue_Line;
                     PP_Keyword (Keyword);
                     Get_Next_Ada_Lexem;
                     Detect_Keyword;
                  end loop;

                  PP_Continue_Line_Postponed;

               when A_Component_Declaration          |
                    A_Choice_Parameter_Specification =>

                  PP_Continue_Line;

               when others =>
                  null;
            end case;

         when Comma_Dlm =>

            if Is_New_Output_Line then
               PP_New_Continuation_Line;
            end if;

            PP_Delimiter (Delimiter);

            Get_Next_Ada_Lexem;

            case Enclosing_El_Kind is
               when A_Loop_Statement .. A_Block_Statement =>
                  null;

               when others =>

                  PP_Continue_Line;
            end case;

         when others =>
            null;
      end case;

      Detect_Keyword;

      --  ??? The rest should be rewritten on the base of standard keyword
      --  processing

      if Keyword = KW_Aliased then
         PP_Continue_Line;
         PP_Keyword (Keyword);
         Get_Next_Ada_Lexem;
         Detect_Keyword;
      end if;

      case Keyword is

         when KW_Is =>

            if not (Is_New_Output_Line) then
               PP_Space;
            else

               if Adds_Indentation (Enclosing_El_Kind) then
                  Adjust := -1;
               else
                  Adjust := 0;
               end if;

               PP_New_Line (Adjust_Depth => Adjust);
            end if;

            PP_Keyword (Keyword);

            Get_Next_Ada_Lexem
               (Keep_Empty_Lines => Adds_Indentation (Enclosing_El_Kind));

            Detect_Keyword;

            case Keyword is

               when KW_Begin =>

                  PP_New_Line (Adjust_Depth => -1);

                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

               when KW_Private =>

                  if Enclosing_El_Kind = A_Package_Declaration
                   or else
                     Enclosing_El_Kind = A_Generic_Package_Declaration
                  then
                     PP_New_Line (Adjust_Depth => -1);

                     PP_Keyword (Keyword);
                     Get_Next_Ada_Lexem;
                  end if;

               when KW_New =>

                  if Enclosing_El_Kind in A_Flat_Generic_Instantiation
                    or else
                     Enclosing_El_Kind in A_Formal_Package_Declaration ..
                                          A_Formal_Package_Declaration_With_Box
                    or else
                     Enclosing_El_Kind in A_Task_Type_Declaration ..
                                          A_Protected_Type_Declaration
                    or else
                     Enclosing_El_Kind in A_Single_Task_Declaration ..
                                          A_Single_Protected_Declaration
                  then

                     if Available_In_Output > 3 then
                        PP_Space;
                     else
                        PP_New_Continuation_Line;
                     end if;

                     PP_Keyword (KW_New);
                     Get_Next_Ada_Lexem;

                     PP_Continue_Line_Postponed;
                  end if;

               when KW_Tagged =>

                     if Available_In_Output > 7 then
                        PP_Space;
                     else
                        PP_New_Continuation_Line;
                     end if;

                     PP_Keyword (KW_Tagged);
                     Get_Next_Ada_Lexem;

               when others =>
                  null;
            end case;

         when KW_Return  |
              KW_Renames |
              KW_When    =>

            PP_Continue_Line;
            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem;

            if Keyword = KW_Return then
               --  Keyword = KW_Return here and in Ada 2005 we may have a null
               --  exclusion:

               Detect_Keyword;

               while Keyword /= Not_A_KW loop
                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  Detect_Keyword;
               end loop;

            end if;

            PP_Continue_Line_Postponed;

         when KW_Constant =>

            PP_Continue_Line;
            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem;

            Detect_Delimiter;

            if Delimiter = Assignment_Dlm then

               if Available_In_Output > 3 then
                  PP_Space;
               else
                  PP_New_Line (Adjust_Depth => 1);
               end if;

               PP_Delimiter (Delimiter);
               Get_Next_Ada_Lexem;
               PP_Continue_Line_Postponed;
            end if;

         when KW_Exception =>

            PP_Continue_Line;
            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem;

            Detect_Keyword;

            if Keyword = KW_Renames then
               PP_Continue_Line;
               PP_Keyword (Keyword);
               Get_Next_Ada_Lexem;
               PP_Continue_Line_Postponed;
            end if;

         when KW_In     |
              KW_Out    |
              KW_Access =>

            if not (ASIS_UL.Options.ASIS_2005_Mode
                  and then
                    Keyword = KW_Access)
            then

               if Enclosing_El_Kind = A_Loop_Parameter_Specification or else
                  Enclosing_El_Kind = An_Entry_Index_Specification
               then
                  PP_Continue_Line;
               end if;

               PP_Keyword (Keyword);
               Get_Next_Ada_Lexem;
               PP_Continue_Line;

               Detect_Keyword;

               if Keyword /= Not_A_KW then
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  PP_Continue_Line;
               end if;

            end if;

         when KW_Declare |
              KW_Begin   |
              KW_Loop    |
              KW_For     |
              KW_While   =>
            --  block statement, something similar should be for named loops
            --  ??? What about labels???

            if Separate_Line_For_Stmt_Name then
               PP_New_Line (Adjust_Depth => -1);
            elsif not Compact_Layout then
               Increase_Indentation;
               PP_New_Line (Adjust_Depth => -1);
            elsif not Is_New_Output_Line then
               PP_Space;
            else
               PP_New_Line (Adjust_Depth => -1);
            end if;

            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem
               (Keep_Empty_Lines => True);

            if Keyword = KW_Declare then
               Detect_Keyword;

               if Keyword = KW_Begin then
                  PP_New_Line (Adjust_Depth => -1);
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

               end if;

            end if;

         when others =>
            --  ??? non-implemented case
            null;
      end case;

   end A_Defining_Name_Post_Op;

   -------------------------------------------
   -- A_Discrete_Subtype_Definition_Post_Op --
   -------------------------------------------

   procedure A_Discrete_Subtype_Definition_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Encl_Kind : constant Flat_Element_Kinds := Get_Enclosing_Kind;
   begin
      Detect_Delimiter;

      case Delimiter is
         when Comma_Dlm =>

            PP_Delimiter (Comma_Dlm);

            Get_Next_Ada_Lexem;

            PP_Continue_Line_Postponed;

         when Right_Parenthesis_Dlm =>

            PP_Delimiter (Right_Parenthesis_Dlm);

            Get_Next_Ada_Lexem;

            if not (Encl_Kind = An_Entry_Declaration or else
                    Encl_Kind = An_Entry_Index_Specification)
            then

               if Start_Index_Def_From_New_Line then
                  PP_New_Line_For_Index_Def;
               else
                  PP_Space;
               end if;

               PP_Keyword (KW_Of);

               Get_Next_Ada_Lexem;

               Detect_Keyword;

               if Keyword = KW_Aliased then

                  if Available_In_Output > 7 then
                     PP_Space;
                  else
                     PP_New_Line_For_Index_Def;
                  end if;

                  PP_Continue_Line_Postponed;

                  PP_Keyword (KW_Aliased);

                  Get_Next_Ada_Lexem;

               end if;

               --  ???This will not work correctly for long lines
            end if;

         when others =>
            null;
      end case;

   end A_Discrete_Subtype_Definition_Post_Op;

   ------------------------------------------
   -- A_Discriminant_Specification_Post_Op --
   ------------------------------------------

   procedure A_Discriminant_Specification_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Detect_Delimiter;

      if Delimiter = Semicolon_Dlm then
         PP_Delimiter (Delimiter);
         Get_Next_Ada_Lexem;
      end if;

   end A_Discriminant_Specification_Post_Op;

   ----------------------------------
   -- A_Formal_Declaration_Post_Op --
   ----------------------------------

   procedure A_Formal_Declaration_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (Element);
   begin

      Is_First_Parameter_Spec := True;

      if Arg_Kind = A_Formal_Procedure_Declaration and then
         Default_Kind (Element) = A_Box_Default
      then
         PP_Continue_Line;
         PP_Delimiter (Box_Dlm);
         Get_Next_Ada_Lexem;
      elsif Arg_Kind = A_Formal_Package_Declaration_With_Box then
         PP_Delimiter (Box_Dlm);
         Get_Next_Ada_Lexem;
         PP_Delimiter (Right_Parenthesis_Dlm);
         Get_Next_Ada_Lexem;
      end if;

      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      --  Check if we have an abstract subprogram

      if Trait_Kind (Element) = An_Abstract_Trait then

         --  "is" is already printed out

         if Available_In_Output >= 10 then
            PP_Continue_Line;
         else
            PP_New_Continuation_Line;
         end if;

         PP_Keyword (KW_Abstract);
         Get_Next_Ada_Lexem;
      end if;

      PP_Delimiter (Semicolon_Dlm);
      Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

      Detect_Keyword;

      case Keyword is
         when KW_Procedure |
              KW_Function  |
              KW_Package   =>

            PP_New_Line (Adjust_Depth => -1);
            PP_Keyword (Keyword);

            if Keyword = KW_Procedure or else
               Keyword = KW_Function
            then
               Decrease_Indentation;
            end if;

            Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
         when others =>
            null;
      end case;

   end A_Formal_Declaration_Post_Op;

   ----------------------------------------------
   -- A_Formal_Derived_Type_Definition_Post_Op --
   ----------------------------------------------

   procedure A_Formal_Derived_Type_Definition_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Detect_Keyword;

      if Keyword = KW_Private then
         PP_Continue_Line;
         PP_Keyword (Keyword);
         Get_Next_Ada_Lexem;
      end if;

   end A_Formal_Derived_Type_Definition_Post_Op;

   -----------------------------
   -- A_Function_Call_Post_Op --
   -----------------------------

   procedure A_Function_Call_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      An_Expression_Post_Op (Element, Control, State);
   end A_Function_Call_Post_Op;

   ---------------------------------
   -- A_General_Statement_Post_Op --
   ---------------------------------

   procedure A_General_Statement_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      PP_Delimiter (Semicolon_Dlm);

      Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

      Detect_Keyword;

      if Keyword = KW_Exception then
         PP_New_Line (Adjust_Depth => -1);
         PP_Keyword (Keyword);
         Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
      end if;

   end A_General_Statement_Post_Op;

   ---------------------------------------
   -- A_Known_Discriminant_Part_Post_Op --
   ---------------------------------------

   procedure A_Known_Discriminant_Part_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      PP_Delimiter (Right_Parenthesis_Dlm);

      Get_Next_Ada_Lexem;

      Detect_Keyword;

      if Keyword = KW_Is then

         if Discriminants_On_Separate_Lines or else
            Available_In_Output < 3
         then
            PP_New_Line;
         else
            PP_Space;
         end if;

         PP_Keyword (KW_Is);
         Get_Next_Ada_Lexem;
      end if;

      if Get_Enclosing_Kind = A_Tagged_Incomplete_Type_Declaration then

         if Available_In_Output > 7 then
            PP_Space;
         else
            PP_New_Continuation_Line;
         end if;

         PP_Keyword (KW_Tagged);
         Get_Next_Ada_Lexem;
      end if;

   end A_Known_Discriminant_Part_Post_Op;

   --------------------------------------------
   -- A_Loop_Parameter_Specification_Post_Op --
   --------------------------------------------

   procedure A_Loop_Parameter_Specification_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      if not Separate_Line_For_THEN_and_LOOP
        and then
         Available_In_Output > 5
        and then
         (Last_Loop_Start = Current_Out_Line
         or else
          No_Separate_Line_For_THEN_and_LOOP)
      then
         PP_Continue_Line;
      else
         PP_New_Line (Adjust_Depth => -1);
      end if;

      PP_Keyword (KW_Loop);
      Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
   end A_Loop_Parameter_Specification_Post_Op;

   -------------------------------
   -- A_Named_Statement_Post_Op --
   -------------------------------

   procedure A_Named_Statement_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      --  ??? We should think about possible unification of post-ops for
      --  ??? constructs which end with 'end [name];'

      Decrease_Indentation;
      PP_New_Line;
      PP_Keyword (KW_End);

      Get_Next_Ada_Lexem;

      Detect_Keyword;

      if Keyword = KW_Loop then

         if Is_New_Output_Line then
            PP_New_Continuation_Line;
         else
            PP_Space;
         end if;

         PP_Keyword (KW_Loop);
         Get_Next_Ada_Lexem;
      end if;

      Detect_Delimiter;

      if Delimiter = Not_A_Dlm then
         --  In the legal Ada code there can be only the block or loop name

         if not Is_New_Output_Line then
            PP_Space;
         else
            PP_New_Continuation_Line;
         end if;

         PP_Word (Capitalize (Statement_Identifier (Element), PP_Name_Casing));

         Get_Next_Ada_Lexem;

      end if;

      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      PP_Delimiter (Semicolon_Dlm);

      Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

      if not Compact_Layout
        and then
         not Separate_Line_For_Stmt_Name
        and then
         Asis.Statements.Is_Name_Repeated (Element)
      then
         Decrease_Indentation;
      end if;

      Detect_Keyword;

      if Keyword = KW_Exception then
         PP_New_Line (Adjust_Depth => -1);
         PP_Keyword (Keyword);
         Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
      end if;

   end A_Named_Statement_Post_Op;

   ---------------------------------------
   -- A_Parameter_Specification_Post_Op --
   ---------------------------------------

   procedure A_Parameter_Specification_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Ident           : Integer := 0;
      New_Line_Adjust : Integer := 0;

      Tmp_El   : Asis.Element := Nil_Element;
      Tmp_Len  : Natural;
      Tmp_Bool : Boolean;
      pragma Warnings (Off, Tmp_Bool);
      --  Next_Space is used as placeholder parameter in the call to
      --  Detect_Possible_Layout
   begin

      case Get_Enclosing_Kind is

         when A_Procedure_Declaration                    |
              A_Null_Procedure_Declaration               |
              A_Function_Declaration                     |
              A_Formal_Procedure_Declaration             |
              A_Formal_Function_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_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  =>

            Ident := 1;

         when others =>
            New_Line_Adjust := -1;
            null;
      end case;

      Detect_Delimiter;

      case Delimiter is

         when Semicolon_Dlm =>

            if Is_New_Output_Line then
               PP_New_Continuation_Line;
            end if;

            PP_Delimiter (Semicolon_Dlm);

            Get_Next_Ada_Lexem;

            if not Par_Spec_On_Separate_Lines then
               PP_Continue_Line (Adjust_New_Line_Depth => New_Line_Adjust);
            end if;

         when Right_Parenthesis_Dlm =>

            if Is_New_Output_Line then
               PP_New_Continuation_Line;
            end if;

            PP_Delimiter (Right_Parenthesis_Dlm);
            Get_Next_Ada_Lexem;

         when others =>
            null;

      end case;

      --  In Ada 2005 we may have something like
      --
      --     Put_Message : access procedure (Msg : in String) :=
      --        Ada.Text_IO.Put_Line'Access;
      --
      --  so:

      Detect_Delimiter;

      if Delimiter = Assignment_Dlm then

         if Available_In_Output > 3 then
            PP_Space;
         else
            PP_New_Line (Adjust_Depth => 1);
         end if;

         PP_Delimiter (Assignment_Dlm);

         Get_Next_Ada_Lexem;

         PP_Continue_Line_Postponed;
      end if;

      Detect_Keyword;

      case Keyword is

         when KW_Return =>

            if Par_Spec_On_Separate_Lines then
               PP_New_Line (Adjust_Depth => Ident);
            else
               PP_Continue_Line;
            end if;

            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem;

            --  Here we may have a null exclusion (Ada 2005)

            if Is_Not_Null_Return (Get_Enclosing_Element) then
               Detect_Keyword;

               while Keyword /= Not_A_KW loop
                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  Detect_Keyword;
               end loop;

            end if;

            if not Is_New_Output_Line then

               if Flat_Element_Kind (Get_Enclosing_Element) =
                  An_Access_To_Function
                 or else
                  Flat_Element_Kind (Get_Enclosing_Element) in
                    An_Anonymous_Access_To_Function ..
                    An_Anonymous_Access_To_Protected_Function
                 or else
                  Flat_Element_Kind (Get_Enclosing_Element) in
                    A_Formal_Access_To_Function ..
                    A_Formal_Access_To_Protected_Function
               then
                  Tmp_El :=
                     Access_To_Function_Result_Profile (Get_Enclosing_Element);
               else
                  Tmp_El := Result_Profile (Get_Enclosing_Element);
               end if;

               Detect_Possible_Layout
                 (The_Element     => Tmp_El,
                  Space_In_Output => Tmp_Len,
                  Comment_Inside  => Tmp_Bool);

               if Colon_Start_Pos + 1 + Tmp_Len + 1 < Max_Line_Length then
                  PP_Pad_Up_To (Colon_Start_Pos + 1);
               end if;

               if Definition_Kind (Tmp_El) = An_Access_Definition then

                  Detect_Keyword;

                  while Keyword /= Not_A_KW loop
                     PP_Continue_Line;

                     if Colon_Start_Pos /= 0 then
                        PP_Space;
                     end if;

                     PP_Keyword (Keyword);
                     Get_Next_Ada_Lexem;
                     Detect_Keyword;
                  end loop;

               end if;

            end if;

         when KW_Renames |
              KW_When    =>

            PP_Continue_Line;
            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem;

            if Keyword = KW_Renames then  --  ??? Not a good solution!!!
               PP_Continue_Line_Postponed;
            end if;

         when KW_Is =>

            if Get_Enclosing_Kind = A_Formal_Procedure_Declaration
              or else
               Get_Enclosing_Kind = A_Null_Procedure_Declaration
            then
               Ident := 0;
            else
               Ident := -1;
            end if;

            if (Par_Spec_On_Separate_Lines
              and then
               Separate_Line_For_IS)
             or else
               Available_In_Output < 3
             or else
               Is_New_Output_Line
            then
               PP_New_Line (Adjust_Depth => Ident);
            else
               PP_Continue_Line;
            end if;

            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

            Detect_Keyword;

            case Keyword is
               when KW_Begin =>
                  PP_New_Line (Adjust_Depth => -1);
                  PP_Keyword (KW_Begin);
                  Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

               when KW_Null =>
                  --  Ada 2005 null procedure:
                  if Available_In_Output >= 5 then
                     PP_Continue_Line;
                  else
                     PP_New_Continuation_Line;
                  end if;

                  PP_Keyword (KW_Null);
                  Get_Next_Ada_Lexem;
               when others =>
                  null;
            end case;

         when KW_Do =>
            PP_Continue_Line;
            PP_Keyword (Keyword);
            Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

         when others =>
            null;
      end case;

   end A_Parameter_Specification_Post_Op;

   --------------------
   -- A_Path_Post_Op --
   --------------------

   procedure A_Path_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Decrease_Indentation;
   end A_Path_Post_Op;

   ----------------------
   -- A_Pragma_Post_Op --
   ----------------------

   procedure A_Pragma_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Finalize_Declaration (Element);

      Prev_Element_Kind := Flat_Element_Kind (Element);

   end A_Pragma_Post_Op;

   --------------------------------------------
   -- A_Private_Extension_Definition_Post_Op --
   --------------------------------------------

   procedure A_Private_Extension_Definition_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      --  Note that if we have a discriminant constraint, the WITH keyword
      --  is not printed out by An_Expression_Post_Op (D105-004)

      loop
         Detect_Keyword;

         if Available_In_Output > KW_Length (Keyword) then
            PP_Continue_Line;
         else
            PP_New_Line (Adjust_Depth => 1);
         end if;

         PP_Keyword (Keyword);
         Get_Next_Ada_Lexem;
         exit when Keyword = KW_Private;
         PP_Continue_Line;
      end loop;

   end A_Private_Extension_Definition_Post_Op;

   ----------------------------------------
   -- A_Program_Unit_Declaration_Post_Op --
   ----------------------------------------

   procedure A_Program_Unit_Declaration_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);
   begin
      Is_First_Parameter_Spec := True;

      Detect_Keyword;

      if Keyword /= Not_A_KW then
         --  Two possibilities:
         --  - 'is separate'
         --  - ''is abstract'

         PP_Continue_Line;
         PP_Keyword (Keyword);
         Get_Next_Ada_Lexem;

      end if;

      Finalize_Declaration (Element);

   end A_Program_Unit_Declaration_Post_Op;

   ----------------------------
   -- A_Program_Unit_Post_Op --
   ----------------------------

   procedure A_Program_Unit_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Arg_Kind  : constant Flat_Element_Kinds := Flat_Element_Kind (Element);
      Name_Elem : Asis.Element                := Element;
   begin
      Is_First_Parameter_Spec := True;

      Decrease_Indentation;

      --  We have to print out 'end [Name];

      Detect_Keyword;

      if Keyword = KW_End then
         --  This code can also be applied to an accept statement which may not
         --  have the trailing 'end' keyword!
         PP_New_Line;
         Skip_Blanks; --  ???

         PP_Keyword (KW_End);

         Get_Next_Ada_Lexem;

         if Is_New_Output_Line then
            PP_New_Continuation_Line;
         end if;

         if End_Labels or else Line_Buf (Line_Pos) /= ';' then
            --  We have to reproduce the name in the end of the construct

            if not Is_New_Output_Line then
               PP_Space;
            end if;

            if Arg_Kind = An_Accept_Statement then
               Name_Elem := Accept_Entry_Direct_Name (Element);
               Name_Elem := Corresponding_Name_Definition (Name_Elem);
            else

               if Arg_Kind = A_Task_Definition or else
                  Arg_Kind = A_Protected_Definition
               then
                  Name_Elem := Get_Enclosing_Element;
               end if;

               Name_Elem := Def_Name (Name_Elem);
            end if;

            if Line_Buf (Line_Pos) /= ';' then

               if Flat_Element_Kind (Name_Elem) in
                  Flat_Defining_Operator_Kinds
               then
                  PP_Word
                    (Capitalize_Image
                      (Defining_Name_Image (Name_Elem), PP_Keyword_Casing));
               else
                  PP_Word (Capitalize (Name_Elem, PP_Name_Casing));
               end if;

               Get_Next_Ada_Lexem;

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

            else
               if Flat_Element_Kind (Name_Elem) in
                  Flat_Defining_Operator_Kinds
               then
                  PP_Word_No_Move
                    (Capitalize_Image
                      (Defining_Name_Image (Name_Elem), PP_Keyword_Casing));
               else
                  PP_Word_No_Move (Capitalize (Name_Elem, PP_Name_Casing));
               end if;

            end if;

         end if;

      end if;

      Finalize_Declaration (Element);

   end A_Program_Unit_Post_Op;

   ---------------------------------
   -- A_Record_Definition_Post_Op --
   ---------------------------------

   procedure A_Record_Definition_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Decrease_Indentation;

      PP_New_Line;
      PP_Keyword (KW_End);
      Get_Next_Ada_Lexem;

      PP_Continue_Line;

      PP_Keyword (KW_Record);
      Get_Next_Ada_Lexem;

      if not Compact_Layout or else
         Impose_Extra_Indentation
      then
         Decrease_Indentation;
      end if;

   end A_Record_Definition_Post_Op;

   --------------------------------------------
   -- A_Record_Representation_Clause_Post_Op --
   --------------------------------------------

   procedure A_Record_Representation_Clause_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Decrease_Indentation;
      PP_New_Line;
      PP_Keyword (KW_End);
      Get_Next_Ada_Lexem;
      PP_Continue_Line;
      PP_Keyword (KW_Record);
      Get_Next_Ada_Lexem;

      if not Compact_Layout then
         Decrease_Indentation;
      end if;

      Finalize_Declaration (Element);

   end A_Record_Representation_Clause_Post_Op;

   --------------------------------------------
   -- A_Requeue_Statement_With_Abort_Post_Op --
   --------------------------------------------

   procedure A_Requeue_Statement_With_Abort_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State) is
   begin
      PP_Continue_Line;
      PP_Keyword (KW_Abort);
      Get_Next_Ada_Lexem;
      A_General_Statement_Post_Op (Element, Control, State);
   end A_Requeue_Statement_With_Abort_Post_Op;

   ----------------------------------
   -- A_Subtype_Indication_Post_Op --
   ----------------------------------

   procedure A_Subtype_Indication_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Detect_Delimiter;

      case Delimiter is

         when Assignment_Dlm =>

            if Available_In_Output > 3 then
               PP_Space;
            else
               PP_New_Line (Adjust_Depth => 1);
            end if;

            PP_Delimiter (Assignment_Dlm);

            Get_Next_Ada_Lexem;

            PP_Continue_Line_Postponed;

         when Comma_Dlm =>

            if Is_New_Output_Line then
               PP_New_Continuation_Line;
            end if;

            PP_Delimiter (Delimiter);
            Get_Next_Ada_Lexem;

         when others =>
            null;
      end case;

   end A_Subtype_Indication_Post_Op;

   --------------------------
   -- A_Use_Clause_Post_Op --
   --------------------------

   procedure A_Use_Clause_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin

      --  ??? Compare with A_Declaration_Post_Op

      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      List_Start_Stack.Pop;

      if Get_Enclosing_Kind in A_Flat_Generic_Declaration and then
         Is_Part_Of_Generic_Formal_Part (Element)
      then
         A_Formal_Declaration_Post_Op (Element, Control, State);
      else
         Finalize_Declaration (Element);
      end if;

   end A_Use_Clause_Post_Op;

   ----------------------------
   -- An_Association_Post_Op --
   ----------------------------

   procedure An_Association_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Arg_Kind  : constant Flat_Element_Kinds := Flat_Element_Kind (Element);

      Pref         : Asis.Element;
      Is_Pref_Call : Boolean := False;
   begin

      if Arg_Kind = A_Parameter_Association then

         Is_Pref_Call :=
            Get_Enclosing_Kind /= A_Function_Call or else
            Is_Prefix_Call (Get_Enclosing_Element);

         if Is_First_Par_Association then

            if not Is_Pref_Call then
               Pref :=  Prefix (Get_Enclosing_Element);

               if not Is_Unary (Flat_Element_Kind (Pref)) then

                  if Operator_Len (Pref) + 1 <= Available_In_Output then
                     PP_Space;
                  else
                     PP_New_Line_And_Pad (Start_Par_Pos);
                  end if;

                  PP_Operator (Flat_Element_Kind (Pref));
                  Get_Next_Ada_Lexem;

                  if not Pars_On_Sep_Lines then
                     PP_Continue_Line; --  ???
                  end if;

                  return;
               end if;

            end if;

         end if;
      end if;

      Detect_Delimiter;

      case Delimiter is

         when Comma_Dlm =>

            if not (Arg_Kind = A_Parameter_Association
                 and then not Is_Pref_Call)
            then

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

               PP_Delimiter (Comma_Dlm);

               Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

               if not (((Arg_Kind = A_Parameter_Association or else
                         Arg_Kind = A_Pragma_Argument_Association)
                       and then
                        Pars_On_Sep_Lines)
                     or else
                       ((Arg_Kind in A_Record_Component_Association ..
                                     An_Array_Component_Association
                         or else
                         Arg_Kind = A_Discriminant_Association)
                      and then
                        Comp_Asctns_On_Sep_Lines)
                     or else
                        (Arg_Kind = A_Generic_Association and then
                         Generic_Associations_On_Separate_Lines))
               then
                  PP_Continue_Line;
               end if;

            end if;

         when Right_Parenthesis_Dlm =>

            if not (Arg_Kind = A_Parameter_Association and then
                    not Is_Pref_Call)
            then

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

               if Arg_Kind /= A_Discriminant_Association then
                  PP_Delimiter (Right_Parenthesis_Dlm);
                  Get_Next_Ada_Lexem;
               end if;

            end if;

         when others =>
            null;

      end case;

   end An_Association_Post_Op;

   --------------------------
   -- An_Attribute_Post_Op --
   --------------------------

   procedure An_Attribute_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (Element);
   begin
      Detect_Delimiter;

      if Delimiter = Right_Parenthesis_Dlm
        and then
         (Arg_Kind = A_First_Attribute                   or else
          Arg_Kind = A_Last_Attribute                    or else
          Arg_Kind = A_Length_Attribute                  or else
          Arg_Kind = A_Range_Attribute                   or else
          Arg_Kind = An_Implementation_Defined_Attribute or else
          Arg_Kind = An_Unknown_Attribute)

        and then

         not Is_Nil (Attribute_Designator_Expressions (Element))
      then
         PP_Delimiter (Delimiter);
         Get_Next_Ada_Lexem;
      end if;

      An_Expression_Post_Op (Element, Control, State);

   end An_Attribute_Post_Op;

   ------------------------------------------
   -- An_Entry_Index_Specification_Post_Op --
   ------------------------------------------

   procedure An_Entry_Index_Specification_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Detect_Keyword;

      if Keyword = KW_When then
         --  entry with no parameter
         PP_Continue_Line;
         PP_Keyword (KW_When);
         Get_Next_Ada_Lexem;
         PP_Continue_Line;
      end if;

   end An_Entry_Index_Specification_Post_Op;

   --------------------------------------------
   -- An_Enumeration_Type_Definition_Post_Op --
   --------------------------------------------

   procedure An_Enumeration_Type_Definition_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      PP_Delimiter (Right_Parenthesis_Dlm);

      Get_Next_Ada_Lexem;

   end An_Enumeration_Type_Definition_Post_Op;

   -------------------------------------
   -- An_Explicit_Dereference_Post_Op --
   -------------------------------------

   procedure An_Explicit_Dereference_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      if Is_New_Output_Line then
         PP_New_Continuation_Line;
      end if;

      PP_Keyword (KW_All);

      Get_Next_Ada_Lexem;

      An_Expression_Post_Op (Element, Control, State);

   end An_Explicit_Dereference_Post_Op;

   ---------------------------
   -- An_Expression_Post_Op --
   ---------------------------

   procedure An_Expression_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      Encl_Elmt : constant Asis.Element := Get_Enclosing_Element;
      Encl_Kind : Flat_Element_Kinds    := Get_Enclosing_Kind;

      Delim_At : Natural := 0;

      function Not_Need_Left_Par_Printed return Boolean;
      --  Describes the condition when the left parenthesis should not be
      --  printed here. Just a patch, should go away at some point

      function Is_From_Anc_Part return Boolean;
      --  Checks if Element is an ancestor part of the extension aggregate or
      --  a component thereof. In case if this function returns True, as a
      --  side-effect it sets Extension_Aggr (below) to the corresponding
      --  extension aggregate. Otherwise Extension_Aggr is set to Nil_Element

      Extension_Aggr : Asis.Element;

      Tmp_El            : Asis.Element;
      Needed_Space      : Natural;
      Contains_Comments : Boolean;
      pragma Warnings (Off, Contains_Comments);
      --  Next_Space is used as placeholder parameter in the call to
      --  Detect_Possible_Layout

      ----------------------
      -- Is_From_Anc_Part --
      ----------------------

      function Is_From_Anc_Part return Boolean is
         Result   : Boolean := False;
         Steps_Up : Natural := 0;
         Tmp_Kind : Flat_Element_Kinds := Get_Enclosing_Kind;
         Tmp_El   : Asis.Element;
      begin

         while not (Tmp_Kind = An_Extension_Aggregate
                  or else
                    Tmp_Kind not in Flat_Expression_Kinds)
         loop
            Steps_Up := Steps_Up + 1;
            Tmp_Kind := Get_Enclosing_Kind (Steps_Up);
         end loop;

         if Tmp_Kind = An_Extension_Aggregate then

            if Steps_Up > 0 then
               Tmp_El := Get_Enclosing_Element (Steps_Up - 1);
            else
               Tmp_El := Element;
            end if;

            Extension_Aggr := Get_Enclosing_Element (Steps_Up);

            Result :=
              Is_Equal (Tmp_El,
                        Extension_Aggregate_Expression (Extension_Aggr));

         end if;

         return Result;
      end Is_From_Anc_Part;

      -------------------------------
      -- Not_Need_Left_Par_Printed --
      -------------------------------

      function Not_Need_Left_Par_Printed return Boolean is
         Result    : Boolean := False;
         Tmp, Tmp1 : Asis.Element;
      begin

         if Last_Dlm = Arrow_Dlm
           or else
            Last_KW = KW_At
         then
            Result := True;
         elsif Is_Call (Encl_Elmt) or else
            Encl_Kind in A_Flat_Generic_Instantiation or else
            Encl_Kind =  A_Formal_Package_Declaration or else
            Encl_Kind =  An_Exit_Statement
         then
            Result := True;

         elsif Encl_Kind = A_Subtype_Indication and then
               Flat_Element_Kind (Subtype_Constraint (Encl_Elmt)) =
               A_Discriminant_Constraint
         then
            Result := True;
         else
            Tmp1 := Encl_Elmt;
            Tmp := Enclosing_Element (Encl_Elmt);

            if not (Encl_Kind = An_Indexed_Component
               or else
                Encl_Kind = A_Type_Conversion)
            and then
               Is_Call (Tmp)
            then
               Result := True;
            else

               while Flat_Element_Kind (Tmp) in Flat_Expression_Kinds loop
                  Tmp1 := Tmp;
                  Tmp := Enclosing_Element (Tmp);
               end loop;

               if Flat_Element_Kind (Tmp) = A_Subtype_Indication
                  --  Something like
                  --  S : array (1 .. 10) of Integer range 1 .. G * 100
                  --    := ( 1 .. G => 13);
                  --  see D626-001
                 or else
                   (Flat_Element_Kind (Tmp) =  An_Array_Component_Association
                      and then
                    (Flat_Element_Kind (Tmp1) in Flat_Discrete_Range_Kinds
                       or else
                     Flat_Element_Kind (Tmp1) = An_Others_Choice))
                     --  Something like
                     --  C : A := (-6 .. -4 => (4 .. 5 => S(2)));
                     --  see D707-008
               then
                  Result := True;
               end if;

            end if;

         end if;

         return Result;
      end Not_Need_Left_Par_Printed;

   begin

      if Encl_Kind = A_Selected_Component and then
         Is_Equal (Element, Selector (Encl_Elmt))
      then
         --  everything should be done for the whole name
         return;
      end if;

      loop

         Detect_Delimiter;

         case Delimiter is

            when Not_A_Dlm =>

               if Last_KW = KW_Range   or else
                  Last_Dlm = Comma_Dlm or else
                  Last_Dlm = Dot_Dlm
               then
                  exit;
               end if;

            when Semicolon_Dlm =>
               --  This delimiter is processed in Pre- and Post-Operations for
               --  specific constructs, except for a mod clause expression

               if Encl_Kind = A_Record_Representation_Clause and then
                  Is_Equal (Element, Mod_Clause_Expression (Encl_Elmt))
               then
                  PP_Delimiter (Delimiter);
                  Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
               end if;

               exit;

            when Right_Parenthesis_Dlm  =>

               if Encl_Kind = An_Unconstrained_Array_Definition       or else
                  Encl_Kind = A_Formal_Unconstrained_Array_Definition or else
                  Encl_Kind = An_Accept_Statement
               then
                  PP_Delimiter (Delimiter);
                  Get_Next_Ada_Lexem;

                  if Start_Index_Def_From_New_Line then
                     PP_New_Line_For_Index_Def;
                  end if;

               else
                  exit;
               end if;

            when Assignment_Dlm =>

               if Encl_Kind not in Flat_Expression_Kinds then
                  Delim_At := Assign_Start_Pos;

                  if Delim_At = 0 then
                     Delim_At := Assign_In_Paragraph;
                  end if;

                  if Delim_At /= 0         and then
                     Delim_At > Output_Pos and then
                     Delim_At <= Max_Line_Length - 2
                  then
                     PP_Pad_Up_To (Delim_At);
                  else

                     if Available_In_Output > 3 then
                        PP_Space;
                     else
                        PP_New_Line (Adjust_Depth => 1);
                     end if;

                  end if;

                  PP_Delimiter (Delimiter);

                  Get_Next_Ada_Lexem;

                  PP_Continue_Line_Postponed;
               end if;

               exit;

            when Double_Dot_Dlm =>

               if Encl_Kind in Flat_Expression_Kinds or else
                  Encl_Kind = A_Parameter_Association
               then
                  exit;
               end if;

               PP_Continue_Line;

               PP_Delimiter (Delimiter);

               Get_Next_Ada_Lexem;

               if not Start_Upper_Range_Expression_From_New_Line then
                  PP_Continue_Line_Postponed;
               end if;

               exit;

            when Left_Parenthesis_Dlm =>

               if Not_Need_Left_Par_Printed then
                  exit;
               end if;

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               elsif not RM_Style_Spacing then
                  PP_Space;
               end if;

               PP_Delimiter (Delimiter);
               Get_Next_Ada_Lexem;

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

               exit;

            when Dot_Dlm =>

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

               PP_Delimiter (Delimiter);
               Get_Next_Ada_Lexem;

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

               exit;

            when Comma_Dlm =>

               case Encl_Kind is
                  when An_Abort_Statement                      |
                       A_Discrete_Range_Attribute_Reference    |
                       An_Indexed_Component                    |
                       Flat_Context_Clause_Kinds               |
                       An_Unconstrained_Array_Definition       |
                       A_Formal_Unconstrained_Array_Definition |
                       A_Discrete_Simple_Expression_Range      =>

                     null;
                  when others =>
                     exit;
               end case;

               if Is_New_Output_Line then
                  PP_New_Continuation_Line;
               end if;

               PP_Delimiter (Delimiter);
               Get_Next_Ada_Lexem;

               if Start_Index_Def_From_New_Line then
                  PP_New_Line_For_Index_Def;
               else
                  PP_Continue_Line_Postponed;
               end if;

               exit;

            when Tick_Dlm =>

               if (Encl_Kind in Flat_Attribute_Reference_Kinds and then
                   Is_Equal (Element, Prefix (Encl_Elmt)))
                  or else
                   (Encl_Kind = A_Qualified_Expression and then
                   Is_Equal (Element,
                             Converted_Or_Qualified_Subtype_Mark (Encl_Elmt)))
               then

                  if Is_New_Output_Line then
                     PP_New_Continuation_Line;
                  end if;

                  PP_Delimiter (Delimiter);
                  Get_Next_Ada_Lexem;

                  if Is_New_Output_Line then
                     PP_New_Continuation_Line;
                  end if;

               end if;

               exit;

            when Arrow_Dlm =>

               if Encl_Kind in Flat_Expression_Kinds then
                  exit;
               end if;

               if Align_Arrow_As_Vert_Line then
                  Delim_At := Vert_Line_Start_Pos;
               else
                  Delim_At := Arrow_Start_Pos;
               end if;

               if Delim_At > 0 then
                  PP_Pad_Up_To (Delim_At);
               else
                  PP_Continue_Line;
               end if;

               PP_Delimiter (Arrow_Dlm);

               Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

               if Encl_Kind in Flat_Association_Kinds
                 or else
                  ((Encl_Kind = A_Simple_Expression_Range          or else
                    Encl_Kind = A_Discrete_Simple_Expression_Range or else
                    Encl_Kind = A_Discrete_Subtype_Indication)
                   and then
                    Get_Enclosing_Kind (1) in Flat_Association_Kinds)
               then
                  PP_Continue_Line_Postponed;
               end if;

               Detect_Delimiter;

               if Delimiter /= Box_Dlm then
                  exit;
               end if;

            when Box_Dlm =>
               PP_Continue_Line;
               PP_Delimiter (Delimiter);
               Get_Next_Ada_Lexem;

            when Vertical_Line_Dlm    |
                 Exclamation_Mark_Dlm =>

               if Encl_Kind in Flat_Expression_Kinds then
                  exit;
               elsif Encl_Kind in A_Discrete_Subtype_Indication ..
                                  A_Discrete_Simple_Expression_Range
               then
                  Encl_Kind := Get_Enclosing_Kind (1);
               end if;

               Delim_At := 0;

               if Has_Choices (Encl_Kind) and then
                  Choice_On_Separate_Line
               then
                  Delim_At := Vert_Line_Start_Pos;
               end if;

               if Delim_At /= 0 then
                  PP_Pad_Up_To (Delim_At);
               else
                  PP_Continue_Line;
               end if;

               PP_Delimiter (Delimiter);

               Get_Next_Ada_Lexem;

               if not Is_New_Output_Line then

                  if Delim_At /= 0 then
                     PP_New_Line;
                  else
                     PP_Continue_Line_Postponed;
                  end if;

               end if;

               exit;

            when Plus_Dlm               |
                 Asterisk_Dlm           |
                 Minus_Dlm              |
                 Divide_Dlm             |
                 Double_Star_Dlm        |
                 Ampersand_Dlm          |
                 Equals_Dlm             |
                 Inequality_Dlm         |
                 Less_Than_Dlm          |
                 Greater_Than_Dlm       |
                 Greater_Or_Equal_Dlm   |
                 Left_Label_Bracket_Dlm |
                 Less_Or_Equal_Dlm      =>

               exit;

            when others =>

               PP_Continue_Line;

               PP_Delimiter (Delimiter);

               Get_Next_Ada_Lexem;

               if not Is_New_Output_Line then
                  PP_Space;
               end if;

               exit;

         end case;

         Detect_Keyword;

         case Keyword is

            when KW_Digits |
                 KW_Delta  |
                 KW_In     |
                 KW_Renames =>

               if Available_In_Output > KW_Length (Keyword) then
                  PP_Space;
               else
                  PP_New_Line (Adjust_Depth => 1);
               end if;

               PP_Keyword (Keyword);

               Get_Next_Ada_Lexem;

               PP_Continue_Line_Postponed;

               exit;

            when KW_Aliased |
                 KW_Do      |
                 KW_All     =>

               PP_Continue_Line;

               PP_Keyword (Keyword);

               Get_Next_Ada_Lexem;

               exit;

            when KW_With =>

               if Available_In_Output > 5 then
                  PP_Continue_Line;
               else
                  PP_New_Line;
               end if;

               PP_Keyword (Keyword);

               Get_Next_Ada_Lexem;

               PP_Continue_Line_Postponed;

               if Is_From_Anc_Part
                 and then
                  Is_Nil (Record_Component_Associations (Extension_Aggr))
               then
                  --  'with null record' case
                  Detect_Keyword;

                  if Keyword = KW_Null then

                     if Comp_Asctns_On_Sep_Lines then
                        PP_New_Line (Adjust_Depth => 1);
                        PP_Pad_Up_To (Comp_Asctns_Start_Pos);
                     else
                        PP_Space;
                     end if;

                     PP_Keyword (KW_Null);
                     Get_Next_Ada_Lexem;

                     if Available_In_Output < 9 then
                        PP_New_Continuation_Line;
                     else
                        PP_Space;
                     end if;

                     PP_Keyword (KW_Record);

                     Get_Next_Ada_Lexem;
                     PP_Delimiter (Right_Parenthesis_Dlm);
                  end if;

               end if;

               exit;

            when KW_At  =>

               if (Last_KW /= KW_Record
                 or else
                  Is_Nil (Mod_Clause_Expression (Get_Enclosing_Element (1))))
                and then
                  Allign_Ats
                and then
                  At_In_Paragraph > 0
                and then
                  At_In_Paragraph <= Max_Line_Length - 2
               then
                  PP_Pad_Up_To (At_In_Paragraph);
               else
                  PP_Continue_Line;
               end if;

               PP_Keyword (Keyword);

               Get_Next_Ada_Lexem;

               PP_Continue_Line;

            when KW_Not =>

               if Encl_Kind = A_Not_In_Type_Membership_Test or else
                  Encl_Kind = A_Not_In_Range_Membership_Test
               then
                  --  ??? Should be rewritten as an infix operation call
                  PP_Continue_Line;

                  PP_Keyword (Keyword);

                  Get_Next_Ada_Lexem;

                  PP_Continue_Line;
               else
                  exit;
               end if;

            when KW_Is =>

               if Encl_Kind in Flat_Expression_Kinds then
                  exit;
               end if;

               if Has_Parameters (Encl_Kind) and then
                  Par_Spec_On_Separate_Lines
               then

                  if Trait_Kind (Encl_Elmt) = An_Abstract_Trait or else
                     Encl_Kind = A_Formal_Function_Declaration  or else
                     Encl_Kind in A_Flat_Body_Stub
                  then
                     PP_New_Line;
                  else
                     PP_New_Line (Adjust_Depth => -1);
                  end if;

               else
                  PP_Continue_Line;
               end if;

               PP_Keyword (Keyword);

               case Encl_Kind is
                  when A_Case_Statement =>
                     --  This list is not complete!!!

                     Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
                  when others =>
                     Get_Next_Ada_Lexem;
               end case;

            when KW_Of =>

               PP_Continue_Line;

               PP_Keyword (Keyword);

               Get_Next_Ada_Lexem;

            when KW_Then =>

               if Encl_Kind in Flat_Expression_Kinds or else
                  Encl_Kind in Flat_Association_Kinds
               then
                  exit;
               end if;

               if not Separate_Line_For_THEN_and_LOOP
                 and then
                  Available_In_Output > 5
                 and then
                  (Last_If_Path_Start = Current_Out_Line
                  or else
                   No_Separate_Line_For_THEN_and_LOOP)
               then
                  PP_Continue_Line;
               else
                  PP_New_Line (Adjust_Depth => -1);
               end if;

               PP_Keyword (Keyword);

               Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

            when KW_Begin =>

               if Last_KW /= KW_Then then
                  PP_New_Line (Adjust_Depth => -1);

                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
               end if;

               exit;

            when KW_Range =>

               if In_Unconstrained_Array_Definition (Element)         or else
                  Encl_Kind = A_Formal_Unconstrained_Array_Definition or else
                  Encl_Kind = A_Discrete_Subtype_Indication           or else
                  Encl_Kind = A_Component_Clause                      or else
                  Encl_Kind = A_Digits_Constraint                     or else
                  Encl_Kind = A_Delta_Constraint
               then
                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;

                  if Encl_Kind = A_Component_Clause then
                     PP_Continue_Line;
                  end if;

               else
                  exit;
               end if;

            when KW_Loop =>

               if Encl_Kind = A_While_Loop_Statement then

                  if not Separate_Line_For_THEN_and_LOOP
                    and then
                     Available_In_Output > 5
                    and then
                     (Last_Loop_Start = Current_Out_Line
                     or else
                      No_Separate_Line_For_THEN_and_LOOP)
                  then
                     PP_Continue_Line;
                  else
                     PP_New_Line (Adjust_Depth => -1);
                  end if;

                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
               end if;

               exit;

            when KW_When =>

               if Encl_Kind = An_Exit_Statement then
                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  PP_Continue_Line;
               else
                  exit;
               end if;

            when KW_New       |
                 KW_Abs       |
                 KW_Access    |
                 KW_Null      |
                 KW_Generic   |
                 KW_Separate  |
                 KW_Abstract  |
                 KW_Goto      |
                 KW_Exit      |
                 KW_Case      |
                 KW_If        |
                 KW_For       |
                 KW_While     |
                 KW_Function  |
                 KW_Procedure |
                 KW_Declare   |
                 KW_Package   |
                 KW_Pragma    |
                 KW_Type      |
                 KW_Subtype   |
                 KW_Raise     |
                 KW_Return    |
                 KW_Select    |
                 KW_Abort     |
                 KW_Delay     |
                 KW_Accept    |
                 KW_Mod       |
                 KW_Rem       |
                 KW_Xor       |
                 KW_Requeue   =>

               exit;

            when KW_And =>

               if Is_And_Then_Arg then

                  if Pars_On_Sep_Lines then
                     PP_New_Line_And_Pad (Short_Circuit_Padding);
                  else
                     PP_Continue_Line;
                  end if;

                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  PP_Continue_Line;
                  PP_Keyword (KW_Then);
                  Get_Next_Ada_Lexem;

                  if not Pars_On_Sep_Lines then
                     PP_Continue_Line_Postponed;
                  end if;

               else
                  case Encl_Kind is
                     when A_Derived_Record_Extension_Definition |
                          Flat_Interface_Kinds                  |
                          A_Formal_Derived_Type_Definition      |
                          Flat_Formal_Interface_Kinds           |
                          A_Private_Extension_Definition        |
                          A_Private_Type_Declaration            |
                          A_Task_Type_Declaration               |
                          A_Protected_Type_Declaration          |
                          A_Single_Task_Declaration             |
                          A_Single_Protected_Declaration        |
                          A_Subtype_Indication                  =>

                        PP_Continue_Line;
                        PP_Keyword (Keyword);
                        Get_Next_Ada_Lexem;
                     when others =>
                        null;
                  end case;
               end if;

               exit;

            when KW_Or =>

               if Is_Or_Else_Arg then

                  if Pars_On_Sep_Lines then
                     PP_New_Line_And_Pad (Short_Circuit_Padding);
                  else
                     PP_Continue_Line;
                  end if;

                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  PP_Continue_Line;
                  PP_Keyword (KW_Else);
                  Get_Next_Ada_Lexem;

                  if not Pars_On_Sep_Lines then
                     PP_Continue_Line_Postponed;
                  end if;

               end if;

               exit;

            when KW_Use =>
               --  representation clause or use clause?
               if Encl_Kind in Flat_Representation_Clause_Kinds then

                  if Encl_Kind = An_Attribute_Definition_Clause or else
                     Encl_Kind = An_At_Clause
                  then
                     Tmp_El := Representation_Clause_Expression (Encl_Elmt);
                  end if;

                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;

                  if Encl_Kind = An_Enumeration_Representation_Clause then
                     PP_Continue_Line_Postponed;
                     exit;
                  elsif Encl_Kind = An_At_Clause then
                     PP_Continue_Line;
                     PP_Keyword (KW_At);
                     Get_Next_Ada_Lexem;
                  end if;

                  Detect_Possible_Layout
                    (The_Element     => Tmp_El,
                     Space_In_Output => Needed_Space,
                     Comment_Inside  => Contains_Comments);

                  if Needed_Space + 1 <= Available_In_Output then
                     PP_Continue_Line_Postponed;
                  else
                     PP_New_Continuation_Line;
                  end if;

                  if Encl_Kind = An_Attribute_Definition_Clause then
                     exit;
                  end if;

               else
                  exit;
               end if;

            when KW_Record =>
               --  record representation clause

               if not Compact_Layout then
                  Increase_Indentation;
                  PP_New_Line;
               else

                  if Available_In_Output > 6 then
                     PP_Space;
                  else
                     PP_New_Line;
                  end if;

               end if;

               PP_Keyword (Keyword);
               Get_Next_Ada_Lexem;

               Detect_Keyword;

               if Keyword = KW_At then
                  --  mod clause
                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  Detect_Keyword;
                  PP_Continue_Line;
                  PP_Keyword (Keyword);
                  Get_Next_Ada_Lexem;
                  PP_Continue_Line;
               end if;

               Increase_Indentation;
               exit;
            when others =>
               null;

         end case;

         if Delimiter = Not_A_Dlm
           and then
           (Keyword   = Not_A_KW    or else
            --  We have to allow to use Ada 2005 keywords as identifiers in
            --  Ada 95 programs, see F406-003
            Keyword = KW_Overriding or else
            Keyword = KW_Interface  or else
            Keyword = KW_Synchronized)
         then
            exit;
         end if;

      end loop;

   end An_Expression_Post_Op;

   ------------------------------------------
   -- An_Extended_Return_Statement_Post_Op --
   ------------------------------------------

   procedure An_Extended_Return_Statement_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
      pragma Unreferenced (Control);
      pragma Unreferenced (State);
   begin
      Decrease_Indentation;

      Detect_Keyword;

      if Keyword = KW_End then
         PP_New_Line;
         Skip_Blanks; --  ???

         PP_Keyword (KW_End);
         Get_Next_Ada_Lexem;

         PP_Continue_Line;

         PP_Keyword (KW_Return);
         Get_Next_Ada_Lexem;
      end if;

      Finalize_Declaration (Element);
   end An_Extended_Return_Statement_Post_Op;

   ---------------------------------
   -- An_Index_Constraint_Post_Op --
   ---------------------------------

   procedure An_Index_Constraint_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      if Is_New_Output_Line then
         PP_Pad (List_Start_Stack.Top);
      end if;

      PP_Delimiter (Right_Parenthesis_Dlm);

      List_Start_Stack.Pop;

      Get_Next_Ada_Lexem;

      --  The rest is for the constraint that is a part of
      --  A_Return_Object_Declaration

      Detect_Keyword;

      if Keyword = KW_Do then
         PP_Continue_Line;
         PP_Keyword (KW_Do);
         Get_Next_Ada_Lexem;
      end if;

   end An_Index_Constraint_Post_Op;

   ----------------------------------
   -- An_Indexed_Component_Post_Op --
   ----------------------------------

   procedure An_Indexed_Component_Post_Op
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      --  ??? Also used for A_Type_Conversion and A_Qualified_Expression

      if not (Flat_Element_Kind (Element) = A_Qualified_Expression and then
              Flat_Element_Kind (Converted_Or_Qualified_Expression (Element))
                 in A_Record_Aggregate .. A_Named_Array_Aggregate  and then
               not A4G.Skip_TB.Needs_Extra_Parentheses
                     (Converted_Or_Qualified_Expression (Element)))
      then

         if Is_New_Output_Line then
            PP_New_Continuation_Line;
         end if;

         PP_Delimiter (Right_Parenthesis_Dlm);
         Get_Next_Ada_Lexem;
      end if;

      An_Expression_Post_Op (Element, Control, State);

   end An_Indexed_Component_Post_Op;

   ----------------------
   -- Clean_List_Stack --
   ----------------------

   procedure Clean_List_Stack
     (Element :        Asis.Element;
      Control : in out Traverse_Control;
      State   : in out Source_Traversal_State)
   is
   begin
      pragma Unreferenced (Element);
      pragma Unreferenced (Control);
      pragma Unreferenced (State);

      List_Start_Stack.Pop;
   end Clean_List_Stack;

   --------------------------
   -- Finalize_Declaration --
   --------------------------

   procedure Finalize_Declaration (Element : Asis.Element) is
      Arg_Kind : constant Flat_Element_Kinds := Flat_Element_Kind (Element);
   begin

      if Arg_Kind = A_Task_Definition or else
         Arg_Kind = A_Protected_Definition
      then
         --  For A_Task_Definition the declaration post-op is used
         return;
      end if;

      PP_Delimiter (Semicolon_Dlm);

      if Add_FF and then Arg_Kind = A_Page_Pragma then
         PP_Word_No_Move (To_Wide_String (String'(1 => ASCII.FF)));
      end if;

      Get_Next_Ada_Lexem (Keep_Empty_Lines => True);

      Detect_Keyword;

      case Keyword is
         when KW_Begin     |
              KW_Private   |
              KW_Exception =>

            if Get_Enclosing_Kind /= Not_An_Element then
               PP_New_Line (Adjust_Depth => -1);
               PP_Keyword (Keyword);
               Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
            end if;

         when KW_Procedure |
              KW_Function  |
              KW_Package   =>

            if Flat_Element_Kind (Element) in A_Flat_Formal_Declaration
              or else
               Is_Last_Pragma_In_Formal_Part (Element)
            then
               PP_New_Line (Adjust_Depth => -1);
               PP_Keyword (Keyword);
               Get_Next_Ada_Lexem (Keep_Empty_Lines => True);
            end if;

         when others =>
            null;
      end case;

   end Finalize_Declaration;

end GNATPP.Post_Operations;
