/**
 * @file newlines.cpp
 * Adds or removes newlines.
 *
 * Information
 *   "Ignore" means do not change it.
 *   "Add" in the context of spaces means make sure there is at least 1.
 *   "Add" elsewhere means make sure one is present.
 *   "Remove" mean remove the space/brace/newline/etc.
 *   "Force" in the context of spaces means ensure that there is exactly 1.
 *   "Force" in other contexts means the same as "add".
 *
 *   Rmk: spaces = space + nl
 *
 * @author  Ben Gardner
 * @author  Guy Maurel
 * @license GPL v2+
 */

#include "newlines.h"

#include "align_stack.h"
#include "combine_skip.h"
#include "flag_parens.h"
#include "indent.h"
#include "keywords.h"
#include "prototypes.h"
#include "space.h"
#include "unc_tools.h"

#ifdef WIN32
#include <algorithm>                   // to get max
#endif // ifdef WIN32


constexpr static auto LCURRENT = LNEWLINE;

using namespace std;
using namespace uncrustify;


static void mark_change(const char *func, size_t line);


/**
 * Check to see if we are allowed to increase the newline count.
 * We can't increase the newline count:
 *  - if nl_squeeze_ifdef and a preproc is after the newline.
 *  - if eat_blanks_before_close_brace and the next is '}'
 *    - unless function contains an empty body and
 *      nl_inside_empty_func is non-zero
 *  - if eat_blanks_after_open_brace and the prev is '{'
 *    - unless the brace belongs to a namespace
 *      and nl_inside_namespace is non-zero
 */
static bool can_increase_nl(Chunk *nl);


/**
 * Basic approach:
 * 1. Find next open brace
 * 2. Find next close brace
 * 3. Determine why the braces are there
 * a. struct/union/enum "enum [name] {"
 * c. assignment "= {"
 * b. if/while/switch/for/etc ") {"
 * d. else "} else {"
 */
static void setup_newline_add(Chunk *prev, Chunk *nl, Chunk *next);


//! Make sure there is a blank line after a commented group of values
static void newlines_double_space_struct_enum_union(Chunk *open_brace);


//! If requested, make sure each entry in an enum is on its own line
static void newlines_enum_entries(Chunk *open_brace, iarf_e av);


/**
 * Checks to see if it is OK to add a newline around the chunk.
 * Don't want to break one-liners...
 * return value:
 *  true: a new line may be added
 * false: a new line may NOT be added
 */
static bool one_liner_nl_ok(Chunk *pc);


static void nl_create_one_liner(Chunk *vbrace_open);


static void nl_create_list_liner(Chunk *brace_open);


/**
 * Test if a chunk belongs to a one-liner method definition inside a class body
 */
static bool is_class_one_liner(Chunk *pc);


/**
 * Test if a chunk may be combined with a function prototype group.
 *
 * If nl_class_leave_one_liner_groups is enabled, a chunk may be combined with
 * a function prototype group if it is a one-liner inside a class body, and is
 * a definition of the same sort as surrounding prototypes. This checks against
 * either the function name, or the function closing brace.
 */
bool is_func_proto_group(Chunk *pc, E_Token one_liner_type);

/**
 * Test if an opening brace is part of a function call or definition.
 */
static bool is_func_call_or_def(Chunk *pc);


//! Find the next newline or nl_cont
static void nl_handle_define(Chunk *pc);


/**
 * Does the Ignore, Add, Remove, or Force thing between two chunks
 *
 * @param before  The first chunk
 * @param after   The second chunk
 * @param av      The IARF value
 */
static void newline_iarf_pair(Chunk *before, Chunk *after, iarf_e av, bool check_nl_assign_leave_one_liners = false);


/**
 * Adds newlines to multi-line function call/decl/def
 * Start points to the open paren
 */
static void newline_func_multi_line(Chunk *start);


static void newline_template(Chunk *start);


/**
 * Formats a function declaration
 * Start points to the open paren
 */
static void newline_func_def_or_call(Chunk *start);


/**
 * Formats a message, adding newlines before the item before the colons.
 *
 * Start points to the open '[' in:
 * [myObject doFooWith:arg1 name:arg2  // some lines with >1 arg
 *            error:arg3];
 */
static void newline_oc_msg(Chunk *start);


//! Ensure that the next non-comment token after close brace is a newline
static void newline_end_newline(Chunk *br_close);


/**
 * Add or remove a newline between the closing paren and opening brace.
 * Also uncuddles anything on the closing brace. (may get fixed later)
 *
 * "if (...) { \n" or "if (...) \n { \n"
 *
 * For virtual braces, we can only add a newline after the vbrace open.
 * If we do so, also add a newline after the vbrace close.
 */
static bool newlines_if_for_while_switch(Chunk *start, iarf_e nl_opt);


/**
 * Add or remove extra newline before the chunk.
 * Adds before comments
 * Doesn't do anything if open brace before it
 * "code\n\ncomment\nif (...)" or "code\ncomment\nif (...)"
 */
static void newlines_if_for_while_switch_pre_blank_lines(Chunk *start, iarf_e nl_opt);


static void blank_line_set(Chunk *pc, Option<unsigned> &opt);


/**
 * Add one/two newline(s) before the chunk.
 * Adds before comments
 * Adds before destructor
 * Doesn't do anything if open brace before it
 * "code\n\ncomment\nif (...)" or "code\ncomment\nif (...)"
 */
static void newlines_func_pre_blank_lines(Chunk *start, E_Token start_type);


static Chunk *get_closing_brace(Chunk *start);


/**
 * remove any consecutive newlines following this chunk
 * skip vbraces
 */
static void remove_next_newlines(Chunk *start);


/**
 * Add or remove extra newline after end of the block started in chunk.
 * Doesn't do anything if close brace after it
 * Interesting issue is that at this point, nls can be before or after vbraces
 * VBraces will stay VBraces, conversion to real ones should have already happened
 * "if (...)\ncode\ncode" or "if (...)\ncode\n\ncode"
 */
static void newlines_if_for_while_switch_post_blank_lines(Chunk *start, iarf_e nl_opt);


/**
 * Adds or removes a newline between the keyword and the open brace.
 * If there is something after the '{' on the same line, then
 * the newline is removed unconditionally.
 * If there is a '=' between the keyword and '{', do nothing.
 *
 * "struct [name] {" or "struct [name] \n {"
 */
static void newlines_struct_union(Chunk *start, iarf_e nl_opt, bool leave_trailing);
static void newlines_enum(Chunk *start);
static void newlines_namespace(Chunk *start); // Issue #2186


/**
 * Cuddles or un-cuddles a chunk with a previous close brace
 *
 * "} while" vs "} \n while"
 * "} else" vs "} \n else"
 *
 * @param start  The chunk - should be CT_ELSE or CT_WHILE_OF_DO
 */
static void newlines_cuddle_uncuddle(Chunk *start, iarf_e nl_opt);


/**
 * Adds/removes a newline between else and '{'.
 * "else {" or "else \n {"
 */
static void newlines_do_else(Chunk *start, iarf_e nl_opt);


//! Check if token starts a variable declaration
static bool is_var_def(Chunk *pc, Chunk *next);


//! Put newline(s) before and/or after a block of variable definitions
static Chunk *newline_var_def_blk(Chunk *start);


/**
 * Handles the brace_on_func_line setting and decides if the closing brace
 * of a pair should be right after a newline.
 * The only cases where the closing brace shouldn't be the first thing on a line
 * is where the opening brace has junk after it AND where a one-liner in a
 * class is supposed to be preserved.
 *
 * General rule for break before close brace:
 * If the brace is part of a function (call or definition) OR if the only
 * thing after the opening brace is comments, the there must be a newline
 * before the close brace.
 *
 * Example of no newline before close
 * struct mystring { int  len;
 *                   char str[]; };
 * while (*(++ptr) != 0) { }
 *
 * Examples of newline before close
 * void foo() {
 * }
 */
static void newlines_brace_pair(Chunk *br_open);


/**
 * Put a empty line between the 'case' statement and the previous case colon
 * or semicolon.
 * Does not work with PAWN (?)
 */
static void newline_case(Chunk *start);


static void newline_case_colon(Chunk *start);


//! Put a blank line before a return statement, unless it is after an open brace
static void newline_before_return(Chunk *start);


/**
 * Put a empty line after a return statement, unless it is followed by a
 * close brace.
 *
 * May not work with PAWN
 */
static void newline_after_return(Chunk *start);


static void blank_line_max(Chunk *pc, Option<unsigned> &opt);


static iarf_e newline_template_option(Chunk *pc, iarf_e special, iarf_e base, iarf_e fallback);


#define MARK_CHANGE()    mark_change(__func__, __LINE__)


static void mark_change(const char *func, size_t line)
{
   LOG_FUNC_ENTRY();

   cpd.changes++;

   if (cpd.pass_count == 0)
   {
      LOG_FMT(LCHANGE, "%s(%d): change %d on %s:%zu\n",
              __func__, __LINE__, cpd.changes, func, line);
   }
} // mark_change


static bool can_increase_nl(Chunk *nl)
{
   LOG_FUNC_ENTRY();

   Chunk *prev = nl->GetPrevNc();

   Chunk *pcmt = nl->GetPrev();
   Chunk *next = nl->GetNext();

   if (options::nl_squeeze_ifdef())
   {
      log_rule_B("nl_squeeze_ifdef");

      Chunk *pp_start = prev->GetPpStart();

      if (  pp_start->IsNotNullChunk()
         && (  pp_start->GetParentType() == CT_PP_IF
            || pp_start->GetParentType() == CT_PP_ELSE)
         && (  pp_start->GetLevel() > 0
            || options::nl_squeeze_ifdef_top_level()))
      {
         log_rule_B("nl_squeeze_ifdef_top_level");
         bool rv = ifdef_over_whole_file() && pp_start->TestFlags(PCF_WF_IF);
         LOG_FMT(LBLANKD, "%s(%d): nl_squeeze_ifdef %zu (prev) pp_lvl=%zu rv=%d\n",
                 __func__, __LINE__, nl->GetOrigLine(), nl->GetPpLevel(), rv);
         return(rv);
      }

      if (  next->Is(CT_PREPROC)
         && (  next->GetParentType() == CT_PP_ELSE
            || next->GetParentType() == CT_PP_ENDIF)
         && (  next->GetLevel() > 0
            || options::nl_squeeze_ifdef_top_level()))
      {
         log_rule_B("nl_squeeze_ifdef_top_level");
         bool rv = ifdef_over_whole_file() && next->TestFlags(PCF_WF_ENDIF);
         LOG_FMT(LBLANKD, "%s(%d): nl_squeeze_ifdef %zu (next) pp_lvl=%zu rv=%d\n",
                 __func__, __LINE__, nl->GetOrigLine(), nl->GetPpLevel(), rv);
         return(rv);
      }
   }

   if (next->Is(CT_BRACE_CLOSE))
   {
      if (  options::nl_inside_namespace() > 0
         && next->GetParentType() == CT_NAMESPACE)
      {
         log_rule_B("nl_inside_namespace");
         LOG_FMT(LBLANKD, "%s(%d): nl_inside_namespace %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(true);
      }

      if (  options::nl_inside_empty_func() > 0
         && prev->Is(CT_BRACE_OPEN)
         && (  next->GetParentType() == CT_FUNC_DEF
            || next->GetParentType() == CT_FUNC_CLASS_DEF))
      {
         log_rule_B("nl_inside_empty_func");
         LOG_FMT(LBLANKD, "%s(%d): nl_inside_empty_func %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(true);
      }

      if (options::eat_blanks_before_close_brace())
      {
         log_rule_B("eat_blanks_before_close_brace");
         LOG_FMT(LBLANKD, "%s(%d): eat_blanks_before_close_brace %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(false);
      }
   }

   if (prev->Is(CT_BRACE_CLOSE))
   {
      if (  options::nl_before_namespace()
         && prev->GetParentType() == CT_NAMESPACE)
      {
         log_rule_B("nl_before_namespace");
         LOG_FMT(LBLANKD, "%s(%d): nl_before_namespace %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(true);
      }
   }

   if (prev->Is(CT_BRACE_OPEN))
   {
      if (  options::nl_inside_namespace() > 0
         && prev->GetParentType() == CT_NAMESPACE)
      {
         log_rule_B("nl_inside_namespace");
         LOG_FMT(LBLANKD, "%s(%d): nl_inside_namespace %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(true);
      }

      if (  options::nl_inside_empty_func() > 0
         && next->Is(CT_BRACE_CLOSE)
         && (  prev->GetParentType() == CT_FUNC_DEF
            || prev->GetParentType() == CT_FUNC_CLASS_DEF))
      {
         log_rule_B("nl_inside_empty_func");
         LOG_FMT(LBLANKD, "%s(%d): nl_inside_empty_func %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(true);
      }

      if (options::eat_blanks_after_open_brace())
      {
         log_rule_B("eat_blanks_after_open_brace");
         LOG_FMT(LBLANKD, "%s(%d): eat_blanks_after_open_brace %zu\n",
                 __func__, __LINE__, nl->GetOrigLine());
         return(false);
      }
   }
   log_rule_B("nl_start_of_file");

   if (  !pcmt
      && (options::nl_start_of_file() != IARF_IGNORE))
   {
      LOG_FMT(LBLANKD, "%s(%d): SOF no prev %zu\n", __func__, __LINE__, nl->GetOrigLine());
      return(false);
   }
   log_rule_B("nl_end_of_file");

   if (  next->IsNullChunk()
      && (options::nl_end_of_file() != IARF_IGNORE))
   {
      LOG_FMT(LBLANKD, "%s(%d): EOF no next %zu\n", __func__, __LINE__, nl->GetOrigLine());
      return(false);
   }
   return(true);
} // can_increase_nl


static void setup_newline_add(Chunk *prev, Chunk *nl, Chunk *next)
{
   LOG_FUNC_ENTRY();

   if (  prev->IsNullChunk()
      || nl->IsNullChunk()
      || next->IsNullChunk())
   {
      return;
   }
   undo_one_liner(prev);

   nl->SetOrigLine(prev->GetOrigLine());
   nl->SetLevel(prev->GetLevel());
   nl->SetPpLevel(prev->GetPpLevel());
   nl->SetBraceLevel(prev->GetBraceLevel());
   nl->SetPpLevel(prev->GetPpLevel());
   nl->SetNlCount(1);
   nl->SetFlags((prev->GetFlags() & PCF_COPY_FLAGS) & ~PCF_IN_PREPROC);
   nl->SetOrigCol(prev->GetOrigColEnd());
   nl->SetColumn(prev->GetOrigCol());

   if (  prev->TestFlags(PCF_IN_PREPROC)
      && next->TestFlags(PCF_IN_PREPROC))
   {
      nl->SetFlagBits(PCF_IN_PREPROC);
   }

   if (nl->TestFlags(PCF_IN_PREPROC))
   {
      nl->SetType(CT_NL_CONT);
      nl->Str() = "\\\n";
   }
   else
   {
      nl->SetType(CT_NEWLINE);
      nl->Str() = "\n";
   }
} // setup_newline_add


void double_newline(Chunk *nl)
{
   LOG_FUNC_ENTRY();

   Chunk *prev = nl->GetPrev();

   if (prev->IsNullChunk())
   {
      return;
   }
   LOG_FMT(LNEWLINE, "%s(%d): add newline after ", __func__, __LINE__);

   if (prev->Is(CT_VBRACE_CLOSE))
   {
      LOG_FMT(LNEWLINE, "VBRACE_CLOSE ");
   }
   else
   {
      LOG_FMT(LNEWLINE, "'%s' ", prev->Text());
   }
   LOG_FMT(LNEWLINE, "on line %zu", prev->GetOrigLine());

   if (!can_increase_nl(nl))
   {
      LOG_FMT(LNEWLINE, " - denied\n");
      return;
   }
   LOG_FMT(LNEWLINE, " - done\n");

   if (nl->GetNlCount() != 2)
   {
      nl->SetNlCount(2);
      MARK_CHANGE();
   }
} // double_newline


Chunk *newline_add_before(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   Chunk nl;
   Chunk *prev = pc->GetPrevNvb();

   if (prev->IsNewline())
   {
      // Already has a newline before this chunk
      return(prev);
   }
   LOG_FMT(LNEWLINE, "%s(%d): Text() '%s', on orig line is %zu, orig col is %zu, pc column is %zu",
           __func__, __LINE__, pc->Text(), pc->GetOrigLine(), pc->GetOrigCol(), pc->GetColumn());
   log_func_stack_inline(LNEWLINE);

   setup_newline_add(prev, &nl, pc);
   nl.SetOrigCol(pc->GetOrigCol());
   nl.SetPpLevel(pc->GetPpLevel());
   LOG_FMT(LNEWLINE, "%s(%d): nl column is %zu\n",
           __func__, __LINE__, nl.GetColumn());

   MARK_CHANGE();
   return(nl.CopyAndAddBefore(pc));
} // newline_add_before


Chunk *newline_force_before(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   Chunk *nl = newline_add_before(pc);

   if (  nl->IsNotNullChunk()
      && nl->GetNlCount() > 1)
   {
      nl->SetNlCount(1);
      MARK_CHANGE();
   }
   return(nl);
} // newline_force_before


Chunk *newline_add_after(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   if (pc->IsNullChunk())
   {
      return(Chunk::NullChunkPtr);
   }
   Chunk *next = pc->GetNextNvb();

   if (next->IsNewline())
   {
      // Already has a newline after this chunk
      return(next);
   }
   LOG_FMT(LNEWLINE, "%s(%d): '%s' on line %zu",
           __func__, __LINE__, pc->Text(), pc->GetOrigLine());
   log_func_stack_inline(LNEWLINE);

   Chunk nl;

   nl.SetOrigLine(pc->GetOrigLine());
   nl.SetOrigCol(pc->GetOrigCol());
   setup_newline_add(pc, &nl, next);

   MARK_CHANGE();
   // TO DO: check why the next statement is necessary
   nl.SetOrigCol(pc->GetOrigCol());
   nl.SetPpLevel(pc->GetPpLevel());
   return(nl.CopyAndAddAfter(pc));
} // newline_add_after


Chunk *newline_force_after(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   Chunk *nl = newline_add_after(pc);   // add a newline

   if (  nl->IsNotNullChunk()
      && nl->GetNlCount() > 1) // check if there are more than 1 newline
   {
      nl->SetNlCount(1);                   // if so change the newline count back to 1
      MARK_CHANGE();
   }
   return(nl);
} // newline_force_after


static void newline_end_newline(Chunk *br_close)
{
   LOG_FUNC_ENTRY();

   Chunk *next = br_close->GetNext();
   Chunk nl;

   if (!next->IsCommentOrNewline())
   {
      nl.SetOrigLine(br_close->GetOrigLine());
      nl.SetOrigCol(br_close->GetOrigCol());
      nl.SetNlCount(1);
      nl.SetPpLevel(0);
      nl.SetFlags((br_close->GetFlags() & PCF_COPY_FLAGS) & ~PCF_IN_PREPROC);

      if (  br_close->TestFlags(PCF_IN_PREPROC)
         && next->IsNotNullChunk()
         && next->TestFlags(PCF_IN_PREPROC))
      {
         nl.SetFlagBits(PCF_IN_PREPROC);
      }

      if (nl.TestFlags(PCF_IN_PREPROC))
      {
         nl.SetType(CT_NL_CONT);
         nl.Str() = "\\\n";
      }
      else
      {
         nl.SetType(CT_NEWLINE);
         nl.Str() = "\n";
      }
      MARK_CHANGE();
      LOG_FMT(LNEWLINE, "%s(%d): %zu:%zu add newline after '%s'\n",
              __func__, __LINE__, br_close->GetOrigLine(), br_close->GetOrigCol(), br_close->Text());
      nl.CopyAndAddAfter(br_close);
   }
} // newline_end_newline


static void newline_min_after(Chunk *ref, size_t count, E_PcfFlag flag)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNEWLINE, "%s(%d): for '%s', at orig line %zu, count is %zu,\n   flag is %s:",
           __func__, __LINE__, ref->Text(), ref->GetOrigLine(), count,
           pcf_flags_str(flag).c_str());
   log_func_stack_inline(LNEWLINE);

   Chunk *pc = ref;

   do
   {
      pc = pc->GetNext();
   } while (  pc->IsNotNullChunk()
           && !pc->IsNewline());

   if (pc->IsNotNullChunk())                 // Coverity CID 76002
   {
      LOG_FMT(LNEWLINE, "%s(%d): type is %s, orig line %zu, orig col %zu\n",
              __func__, __LINE__, get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());
   }
   Chunk *next = pc->GetNext();

   if (next->IsNullChunk())
   {
      return;
   }

   if (  next->IsComment()
      && next->GetNlCount() == 1
      && pc->GetPrev()->IsComment())
   {
      newline_min_after(next, count, flag);
      return;
   }
   pc->SetFlagBits(flag);

   if (  pc->IsNewline()
      && can_increase_nl(pc))
   {
      if (pc->GetNlCount() < count)
      {
         pc->SetNlCount(count);
         MARK_CHANGE();
      }
   }
} // newline_min_after


Chunk *newline_add_between(Chunk *start, Chunk *end)
{
   LOG_FUNC_ENTRY();

   if (  start->IsNullChunk()
      || end->IsNullChunk()
      || end->Is(CT_IGNORED))
   {
      return(Chunk::NullChunkPtr);
   }
   LOG_FMT(LNEWLINE, "%s(%d): start->Text() is '%s', type is %s, orig line is %zu, orig col is %zu\n",
           __func__, __LINE__, start->Text(), get_token_name(start->GetType()),
           start->GetOrigLine(), start->GetOrigCol());
   LOG_FMT(LNEWLINE, "%s(%d): and end->Text() is '%s', orig line is %zu, orig col is %zu\n  ",
           __func__, __LINE__, end->Text(), end->GetOrigLine(), end->GetOrigCol());
   log_func_stack_inline(LNEWLINE);

   // Back-up check for one-liners (should never be true!)
   if (!one_liner_nl_ok(start))
   {
      return(Chunk::NullChunkPtr);
   }

   /*
    * Scan for a line break, if there is a line break between start and end
    * we won't add another one
    */
   for (Chunk *pc = start; pc != end; pc = pc->GetNext())
   {
      if (pc->IsNewline())
      {
         return(pc);
      }
   }

   /*
    * If the second one is a brace open, then check to see
    * if a comment + newline follows
    */
   if (end->Is(CT_BRACE_OPEN))
   {
      Chunk *pc = end->GetNext();

      if (pc->IsComment())
      {
         pc = pc->GetNext();

         if (pc->IsNewline())
         {
            // are there some more (comment + newline)s ?
            Chunk *pc1 = end->GetNextNcNnl();

            if (!pc1->IsNewline())
            {
               // yes, go back
               Chunk *pc2 = pc1->GetPrev();
               pc = pc2;
            }

            if (end == pc)
            {
               LOG_FMT(LNEWLINE, "%s(%d): pc1 and pc are identical\n",
                       __func__, __LINE__);
            }
            else
            {
               // Move the open brace to after the newline
               end->MoveAfter(pc);
            }
            LOG_FMT(LNEWLINE, "%s(%d):\n", __func__, __LINE__);
            newline_add_after(end);
            return(pc);
         }
         else                  // Issue #3873
         {
            LOG_FMT(LNEWLINE, "%s(%d):\n", __func__, __LINE__);
         }
      }
      else
      {
         LOG_FMT(LNEWLINE, "%s(%d):\n", __func__, __LINE__);
      }
   }
   else
   {
      LOG_FMT(LNEWLINE, "%s(%d):\n", __func__, __LINE__);
   }
   Chunk *tmp = newline_add_before(end);

   return(tmp);
} // newline_add_between


void newline_del_between(Chunk *start, Chunk *end)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNEWLINE, "%s(%d): start->Text() is '%s', orig line is %zu, orig col is %zu\n",
           __func__, __LINE__, start->Text(), start->GetOrigLine(), start->GetOrigCol());
   LOG_FMT(LNEWLINE, "%s(%d): and end->Text() is '%s', orig line is %zu, orig col is %zu: preproc=%c/%c\n",
           __func__, __LINE__, end->Text(), end->GetOrigLine(), end->GetOrigCol(),
           start->TestFlags(PCF_IN_PREPROC) ? 'y' : 'n',
           end->TestFlags(PCF_IN_PREPROC) ? 'y' : 'n');
   log_func_stack_inline(LNEWLINE);

   // Can't remove anything if the preproc status differs
   if (!start->IsSamePreproc(end))
   {
      return;
   }
   Chunk *pc           = start;
   bool  start_removed = false;

   do
   {
      Chunk *next = pc->GetNext();

      if (pc->IsNewline())
      {
         Chunk *prev = pc->GetPrev();

         if (  (  !prev->IsComment()
               && !next->IsComment())
            || prev->IsNewline()
            || next->IsNewline())
         {
            if (pc->SafeToDeleteNl())
            {
               if (pc == start)
               {
                  start_removed = true;
               }
               Chunk::Delete(pc);
               MARK_CHANGE();

               if (prev->IsNotNullChunk())
               {
                  size_t temp = space_col_align(prev, next);
                  align_to_column(next, prev->GetColumn() + temp);
               }
            }
         }
         else
         {
            if (pc->GetNlCount() > 1)
            {
               pc->SetNlCount(1);
               MARK_CHANGE();
            }
         }
      }
      pc = next;
   } while (pc != end);

   if (  !start_removed
      && end->IsString("{")
      && (  start->IsString(")")
         || start->Is(CT_DO)
         || start->Is(CT_ELSE)))
   {
      end->MoveAfter(start);
   }
} // newline_del_between


void newlines_sparens()
{
   LOG_FUNC_ENTRY();

   //Chunk *sparen_open;

   for (Chunk *sparen_open = Chunk::GetHead()->GetNextType(CT_SPAREN_OPEN, ANY_LEVEL);
        sparen_open->IsNotNullChunk();
        sparen_open = sparen_open->GetNextType(CT_SPAREN_OPEN, ANY_LEVEL))
   {
      Chunk *sparen_close = sparen_open->GetNextType(CT_SPAREN_CLOSE, sparen_open->GetLevel());

      if (sparen_close->IsNullChunk())
      {
         continue;
      }
      Chunk *sparen_content_start = sparen_open->GetNextNnl();
      Chunk *sparen_content_end   = sparen_close->GetPrevNnl();
      bool  is_multiline          = (
         sparen_content_start != sparen_content_end
                                    && !sparen_content_start->IsOnSameLine(sparen_content_end));

      // Add a newline after '(' if an if/for/while/switch condition spans multiple lines,
      // as e.g. required by the ROS 2 development style guidelines:
      // https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#open-versus-cuddled-braces
      if (is_multiline)
      {
         log_rule_B("nl_multi_line_sparen_open");
         newline_iarf(sparen_open, options::nl_multi_line_sparen_open());
      }

      // Add a newline before ')' if an if/for/while/switch condition spans multiple lines. Overrides nl_before_if_closing_paren if both are specified.
      if (  is_multiline
         && options::nl_multi_line_sparen_close() != IARF_IGNORE)
      {
         log_rule_B("nl_multi_line_sparen_close");
         newline_iarf(sparen_content_end, options::nl_multi_line_sparen_close());
      }
      else
      {
         // add/remove trailing newline in an if condition
         Chunk *ctrl_structure = sparen_open->GetPrevNcNnl();

         if (  ctrl_structure->Is(CT_IF)
            || ctrl_structure->Is(CT_ELSEIF))
         {
            log_rule_B("nl_before_if_closing_paren");
            newline_iarf_pair(sparen_content_end, sparen_close, options::nl_before_if_closing_paren());
         }
      }
   }
} // newlines_sparens


static bool newlines_if_for_while_switch(Chunk *start, iarf_e nl_opt)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_define_macro");

   if (  nl_opt == IARF_IGNORE
      || (  start->TestFlags(PCF_IN_PREPROC)
         && !options::nl_define_macro()))
   {
      return(false);
   }
   bool  retval = false;
   Chunk *pc    = start->GetNextNcNnl();

   if (pc->Is(CT_SPAREN_OPEN))
   {
      Chunk *close_paren = pc->GetNextType(CT_SPAREN_CLOSE, pc->GetLevel());
      Chunk *brace_open  = close_paren->GetNextNcNnl();

      if (  (  brace_open->Is(CT_BRACE_OPEN)
            || brace_open->Is(CT_VBRACE_OPEN))
         && one_liner_nl_ok(brace_open))
      {
         log_rule_B("nl_multi_line_cond");

         if (options::nl_multi_line_cond())
         {
            while ((pc = pc->GetNext()) != close_paren)
            {
               if (pc->IsNewline())
               {
                  nl_opt = IARF_ADD;
                  break;
               }
            }
         }

         if (brace_open->Is(CT_VBRACE_OPEN))
         {
            // Can only add - we don't want to create a one-line here
            if (nl_opt & IARF_ADD)
            {
               newline_iarf_pair(close_paren, brace_open->GetNextNcNnl(), nl_opt);
               pc = brace_open->GetNextType(CT_VBRACE_CLOSE, brace_open->GetLevel());

               if (  !pc->GetPrevNc()->IsNewline()
                  && !pc->GetNextNc()->IsNewline())
               {
                  newline_add_after(pc);
                  retval = true;
               }
            }
         }
         else
         {
            newline_iarf_pair(close_paren, brace_open, nl_opt);
            Chunk *next = brace_open->GetNextNcNnl();

            if (brace_open->GetType() != next->GetType())                       // Issue #2836
            {
               newline_add_between(brace_open, brace_open->GetNextNcNnl());
            }
            // Make sure nothing is cuddled with the closing brace
            pc = brace_open->GetNextType(CT_BRACE_CLOSE, brace_open->GetLevel());
            newline_add_between(pc, pc->GetNextNcNnlNet());
            retval = true;
         }
      }
   }
   return(retval);
} // newlines_if_for_while_switch


static void newlines_if_for_while_switch_pre_blank_lines(Chunk *start, iarf_e nl_opt)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNEWLINE, "%s(%d): start->Text() is '%s', type is %s, orig line is %zu, orig col is %zu\n",
           __func__, __LINE__, start->Text(), get_token_name(start->GetType()), start->GetOrigLine(), start->GetOrigCol());

   log_rule_B("nl_define_macro");

   if (  nl_opt == IARF_IGNORE
      || (  start->TestFlags(PCF_IN_PREPROC)
         && !options::nl_define_macro()))
   {
      return;
   }

   /*
    * look backwards until we find
    *   open brace (don't add or remove)
    *   2 newlines in a row (don't add)
    *   something else (don't remove)
    */
   for (Chunk *pc = start->GetPrev(); pc->IsNotNullChunk(); pc = pc->GetPrev())
   {
      size_t level    = start->GetLevel();
      bool   do_add   = (nl_opt & IARF_ADD) != IARF_IGNORE;  // forcing value to bool
      Chunk  *last_nl = Chunk::NullChunkPtr;

      if (pc->IsNewline())
      {
         last_nl = pc;

         // if we found 2 or more in a row
         if (  pc->GetNlCount() > 1
            || pc->GetPrevNvb()->IsNewline())
         {
            // need to remove
            if (  (nl_opt & IARF_REMOVE)
               && !pc->TestFlags(PCF_VAR_DEF))
            {
               // if we're also adding, take care of that here
               size_t nl_count = do_add ? 2 : 1;

               if (nl_count != pc->GetNlCount())
               {
                  pc->SetNlCount(nl_count);
                  MARK_CHANGE();
               }
               Chunk *prev;

               // can keep using pc because anything other than newline stops loop, and we delete if newline
               while ((prev = pc->GetPrevNvb())->IsNewline())
               {
                  // Make sure we don't combine a preproc and non-preproc
                  if (!prev->SafeToDeleteNl())
                  {
                     break;
                  }
                  Chunk::Delete(prev);
                  MARK_CHANGE();
               }
            }
            return;
         }
      }
      else if (  pc->IsBraceOpen()
              || pc->GetLevel() < level)
      {
         return;
      }
      else if (pc->IsComment())
      {
         // vbrace close is ok because it won't go into output, so we should skip it
         last_nl = Chunk::NullChunkPtr;
         continue;
      }
      else
      {
         if (  pc->Is(CT_CASE_COLON)
            && options::nl_before_ignore_after_case())
         {
            return;
         }

         if (do_add) // we found something previously besides a comment or a new line
         {
            // if we have run across a newline
            if (last_nl->IsNotNullChunk())
            {
               if (last_nl->GetNlCount() < 2)
               {
                  double_newline(last_nl);
               }
            }
            else
            {
               Chunk *next;

               // we didn't run into a newline, so we need to add one
               if (  ((next = pc->GetNext())->IsNotNullChunk())
                  && next->IsComment())
               {
                  pc = next;
               }

               if ((last_nl = newline_add_after(pc))->IsNotNullChunk())
               {
                  double_newline(last_nl);
               }
            }
         }
         return;
      }
   }
} // newlines_if_for_while_switch_pre_blank_lines


static void blank_line_set(Chunk *pc, Option<unsigned> &opt)
{
   LOG_FUNC_ENTRY();

   if (pc->IsNullChunk())
   {
      return;
   }
   const unsigned optval = opt();

   if (  (optval > 0)
      && (pc->GetNlCount() != optval))
   {
      LOG_FMT(LBLANKD, "%s(%d): do_blank_lines: %s set line %zu to %u\n",
              __func__, __LINE__, opt.name(), pc->GetOrigLine(), optval);
      pc->SetNlCount(optval);
      MARK_CHANGE();
   }
} // blank_line_set


bool do_it_newlines_func_pre_blank_lines(Chunk *last_nl, E_Token start_type)
{
   LOG_FUNC_ENTRY();

   if (last_nl->IsNullChunk())
   {
      return(false);
   }
   LOG_FMT(LNLFUNCT, "%s(%d): orig line is %zu, orig col is %zu, type is %s, Text() is '%s'\n",
           __func__, __LINE__,
           last_nl->GetOrigLine(), last_nl->GetOrigCol(), get_token_name(last_nl->GetType()), last_nl->Text());

   switch (start_type)
   {
   case CT_FUNC_CLASS_DEF:
   {
      log_rule_B("nl_before_func_class_def");
      bool diff = options::nl_before_func_class_def() <= last_nl->GetNlCount();
      LOG_FMT(LNLFUNCT, "%s(%d): is %s\n",
              __func__, __LINE__, diff ? "TRUE" : "FALSE");

      log_rule_B("nl_before_func_class_def");

      if (options::nl_before_func_class_def() != last_nl->GetNlCount())
      {
         LOG_FMT(LNLFUNCT, "%s(%d):   set blank line(s) to %u\n",
                 __func__, __LINE__, options::nl_before_func_class_def());
         blank_line_set(last_nl, options::nl_before_func_class_def);
      }
      return(diff);
   }

   case CT_FUNC_CLASS_PROTO:
   {
      log_rule_B("nl_before_func_class_proto");
      bool diff = options::nl_before_func_class_proto() <= last_nl->GetNlCount();
      LOG_FMT(LNLFUNCT, "%s(%d): is %s\n",
              __func__, __LINE__, diff ? "TRUE" : "FALSE");

      log_rule_B("nl_before_func_class_proto");

      if (options::nl_before_func_class_proto() != last_nl->GetNlCount())
      {
         LOG_FMT(LNLFUNCT, "%s(%d):   set blank line(s) to %u\n",
                 __func__, __LINE__, options::nl_before_func_class_proto());
         blank_line_set(last_nl, options::nl_before_func_class_proto);
      }
      return(diff);
   }

   case CT_FUNC_DEF:
   {
      LOG_FMT(LNLFUNCT, "%s(%d): nl_before_func_body_def() is %u, last_nl new line count is %zu\n",
              __func__, __LINE__, options::nl_before_func_body_def(), last_nl->GetNlCount());
      log_rule_B("nl_before_func_body_def");
      bool diff = options::nl_before_func_body_def() <= last_nl->GetNlCount();
      LOG_FMT(LNLFUNCT, "%s(%d): is %s\n",
              __func__, __LINE__, diff ? "TRUE" : "FALSE");

      log_rule_B("nl_before_func_body_def");

      if (options::nl_before_func_body_def() != last_nl->GetNlCount())
      {
         LOG_FMT(LNLFUNCT, "%s(%d):    set blank line(s) to %u\n",
                 __func__, __LINE__, options::nl_before_func_body_def());
         log_rule_B("nl_before_func_body_def");
         blank_line_set(last_nl, options::nl_before_func_body_def);
      }
      LOG_FMT(LNLFUNCT, "%s(%d): nl_before_func_body_def() is %u, last_nl new line count is %zu\n",
              __func__, __LINE__, options::nl_before_func_body_def(), last_nl->GetNlCount());
      return(diff);
   }

   case CT_FUNC_PROTO:
   {
      log_rule_B("nl_before_func_body_proto");
      bool diff = options::nl_before_func_body_proto() <= last_nl->GetNlCount();
      LOG_FMT(LNLFUNCT, "%s(%d): is %s\n",
              __func__, __LINE__, diff ? "TRUE" : "FALSE");

      log_rule_B("nl_before_func_body_proto");

      if (options::nl_before_func_body_proto() != last_nl->GetNlCount())
      {
         LOG_FMT(LNLFUNCT, "%s(%d):   set blank line(s) to %u\n",
                 __func__, __LINE__, options::nl_before_func_body_proto());
         log_rule_B("nl_before_func_body_proto");
         blank_line_set(last_nl, options::nl_before_func_body_proto);
      }
      return(diff);
   }

   default:
   {
      LOG_FMT(LERR, "%s(%d):   setting to blank line(s) at line %zu not possible\n",
              __func__, __LINE__, last_nl->GetOrigLine());
      return(false);
   }
   } // switch
} // do_it_newlines_func_pre_blank_lines


static void newlines_func_pre_blank_lines(Chunk *start, E_Token start_type)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_before_func_class_def");
   log_rule_B("nl_before_func_class_proto");
   log_rule_B("nl_before_func_body_def");
   log_rule_B("nl_before_func_body_proto");

   if (  start->IsNullChunk()
      || (  (  start_type != CT_FUNC_CLASS_DEF
            || options::nl_before_func_class_def() == 0)
         && (  start_type != CT_FUNC_CLASS_PROTO
            || options::nl_before_func_class_proto() == 0)
         && (  start_type != CT_FUNC_DEF
            || options::nl_before_func_body_def() == 0)
         && (  start_type != CT_FUNC_PROTO
            || options::nl_before_func_body_proto() == 0)))
   {
      return;
   }
   LOG_FMT(LNLFUNCT, "%s(%d):    set blank line(s): for <NL> at line %zu, column %zu, start_type is %s\n",
           __func__, __LINE__, start->GetOrigLine(), start->GetOrigCol(), get_token_name(start_type));
   LOG_FMT(LNLFUNCT, "%s(%d): BEGIN set blank line(s) for '%s' at line %zu\n",
           __func__, __LINE__, start->Text(), start->GetOrigLine());
   /*
    * look backwards until we find:
    *   - open brace (don't add or remove)
    *   - two newlines in a row (don't add)
    *   - a destructor
    *   - something else (don't remove)
    */
   Chunk  *pc           = Chunk::NullChunkPtr;
   Chunk  *last_nl      = Chunk::NullChunkPtr;
   Chunk  *last_comment = Chunk::NullChunkPtr;
   size_t first_line    = start->GetOrigLine();

   for (pc = start->GetPrev(); pc->IsNotNullChunk(); pc = pc->GetPrev())
   {
      LOG_FMT(LNLFUNCT, "%s(%d): orig line is %zu, orig col is %zu, type is %s, Text() is '%s', new line count is %zu\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()), pc->Text(), pc->GetNlCount());

      if (pc->IsNewline())
      {
         last_nl = pc;
         LOG_FMT(LNLFUNCT, "%s(%d):    <Chunk::IsNewline> found at line %zu, column %zu, new line count is %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->GetNlCount());
         LOG_FMT(LNLFUNCT, "%s(%d):    last_nl set to %zu\n",
                 __func__, __LINE__, last_nl->GetOrigLine());
         bool break_now = false;

         if (pc->GetNlCount() > 1)
         {
            break_now = do_it_newlines_func_pre_blank_lines(last_nl, start_type);
            LOG_FMT(LNLFUNCT, "%s(%d): break_now is %s\n",
                    __func__, __LINE__, break_now ? "TRUE" : "FALSE");
         }

         if (break_now)
         {
            break;
         }
         else
         {
            continue;
         }
      }
      else if (pc->IsComment())
      {
         LOG_FMT(LNLFUNCT, "%s(%d):    <Chunk::IsComment> found at line %zu, column %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());

         if (  (  pc->GetOrigLine() < first_line
               && ((first_line - pc->GetOrigLine()
                    - (pc->Is(CT_COMMENT_MULTI) ? pc->GetNlCount() : 0))) < 2)
            || (  last_comment->IsNotNullChunk()
               && pc->Is(CT_COMMENT_CPP)          // combine only cpp comments
               && last_comment->Is(pc->GetType()) // don't mix comment types
               && last_comment->GetOrigLine() > pc->GetOrigLine()
               && (last_comment->GetOrigLine() - pc->GetOrigLine()) < 2))
         {
            last_comment = pc;
            continue;
         }
         bool break_now = do_it_newlines_func_pre_blank_lines(last_nl, start_type);
         LOG_FMT(LNLFUNCT, "%s(%d): break_now is %s\n",
                 __func__, __LINE__, break_now ? "TRUE" : "FALSE");
         continue;
      }
      else if (  pc->Is(CT_DESTRUCTOR)
              || pc->Is(CT_TYPE)
              || pc->Is(CT_TEMPLATE)
              || pc->Is(CT_QUALIFIER)
              || pc->Is(CT_PTR_TYPE)
              || pc->Is(CT_BYREF)                  // Issue #2163
              || pc->Is(CT_DC_MEMBER)
              || pc->Is(CT_EXTERN)
              || (  pc->Is(CT_STRING)
                 && pc->GetParentType() == CT_EXTERN))
      {
         LOG_FMT(LNLFUNCT, "%s(%d): first_line set to %zu\n",
                 __func__, __LINE__, pc->GetOrigLine());
         first_line = pc->GetOrigLine();
         continue;
      }
      else if (  pc->Is(CT_ANGLE_CLOSE)
              && pc->GetParentType() == CT_TEMPLATE)
      {
         LOG_FMT(LNLFUNCT, "%s(%d):\n", __func__, __LINE__);
         // skip template stuff to add newlines before it
         pc = pc->GetOpeningParen();

         if (pc->IsNotNullChunk())
         {
            first_line = pc->GetOrigLine();
         }
         continue;
      }
      else
      {
         LOG_FMT(LNLFUNCT, "%s(%d): else ==================================\n",
                 __func__, __LINE__);
         bool break_now = do_it_newlines_func_pre_blank_lines(last_nl, start_type);
         LOG_FMT(LNLFUNCT, "%s(%d): break_now is %s\n",
                 __func__, __LINE__, break_now ? "TRUE" : "FALSE");
         break;
      }
   }
} // newlines_func_pre_blank_lines


static Chunk *get_closing_brace(Chunk *start)
{
   LOG_FUNC_ENTRY();

   Chunk  *pc;
   size_t level = start->GetLevel();

   for (pc = start; (pc = pc->GetNext())->IsNotNullChunk();)
   {
      if (  (pc->IsBraceClose())
         && pc->GetLevel() == level)
      {
         return(pc);
      }

      // for some reason, we can have newlines between if and opening brace that are lower level than either
      if (  !pc->IsNewline()
         && pc->GetLevel() < level)
      {
         return(Chunk::NullChunkPtr);
      }
   }

   return(Chunk::NullChunkPtr);
} // get_closing_brace


static void remove_next_newlines(Chunk *start)
{
   LOG_FUNC_ENTRY();

   Chunk *next;

   while ((next = start->GetNext())->IsNotNullChunk())
   {
      if (  next->IsNewline()
         && next->SafeToDeleteNl())
      {
         Chunk::Delete(next);
         MARK_CHANGE();
      }
      else if (next->IsVBrace())
      {
         start = next;
      }
      else
      {
         break;
      }
   }
} // remove_next_newlines


static void newlines_if_for_while_switch_post_blank_lines(Chunk *start, iarf_e nl_opt)
{
   LOG_FUNC_ENTRY();

   Chunk *prev;

   LOG_FMT(LNEWLINE, "%s(%d): start->Text() is '%s', type is %s, orig line is %zu, orig col is %zu\n",
           __func__, __LINE__, start->Text(), get_token_name(start->GetType()), start->GetOrigLine(), start->GetOrigCol());

   log_rule_B("nl_define_macro");

   if (  nl_opt == IARF_IGNORE
      || (  start->TestFlags(PCF_IN_PREPROC)
         && !options::nl_define_macro()))
   {
      return;
   }
   Chunk *pc = get_closing_brace(start);

   // first find ending brace
   if (pc->IsNullChunk())
   {
      return;
   }
   LOG_FMT(LNEWLINE, "%s(%d): pc->Text() is '%s', type is %s, orig line is %zu, orig col is %zu\n",
           __func__, __LINE__, pc->Text(), get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());

   /*
    * if we're dealing with an if, we actually want to add or remove
    * blank lines after any else
    */
   if (start->Is(CT_IF))
   {
      Chunk *next;

      while (true)
      {
         next = pc->GetNextNcNnl();

         if (  next->IsNotNullChunk()
            && (  next->Is(CT_ELSE)
               || next->Is(CT_ELSEIF)))
         {
            // point to the closing brace of the else
            if ((pc = get_closing_brace(next))->IsNullChunk())
            {
               return;
            }
            LOG_FMT(LNEWLINE, "%s(%d): pc->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                    __func__, __LINE__, pc->Text(), get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());
         }
         else
         {
            break;
         }
      }
   }

   /*
    * if we're dealing with a do/while, we actually want to add or
    * remove blank lines after while and its condition
    */
   if (start->Is(CT_DO))
   {
      // point to the next semicolon
      if ((pc = pc->GetNextType(CT_SEMICOLON, start->GetLevel()))->IsNullChunk())
      {
         return;
      }
      LOG_FMT(LNEWLINE, "%s(%d): pc->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
              __func__, __LINE__, pc->Text(), get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());
   }
   bool isVBrace = (pc->Is(CT_VBRACE_CLOSE));

   if (isVBrace)
   {
      LOG_FMT(LNEWLINE, "%s(%d): isVBrace is TRUE\n", __func__, __LINE__);
   }
   else
   {
      LOG_FMT(LNEWLINE, "%s(%d): isVBrace is FALSE\n", __func__, __LINE__);
   }

   if ((prev = pc->GetPrevNvb())->IsNullChunk())
   {
      return;
   }
   bool have_pre_vbrace_nl = isVBrace && prev->IsNewline();

   if (have_pre_vbrace_nl)
   {
      LOG_FMT(LNEWLINE, "%s(%d): have_pre_vbrace_nl is TRUE\n", __func__, __LINE__);
   }
   else
   {
      LOG_FMT(LNEWLINE, "%s(%d): have_pre_vbrace_nl is FALSE\n", __func__, __LINE__);
   }

   if (nl_opt & IARF_REMOVE)
   {
      Chunk *next;

      // if chunk before is a vbrace, remove any newlines after it
      if (have_pre_vbrace_nl)
      {
         if (prev->GetNlCount() != 1)
         {
            prev->SetNlCount(1);
            MARK_CHANGE();
         }
         remove_next_newlines(pc);
      }
      else if (  ((next = pc->GetNextNvb())->IsNewline())
              && !next->TestFlags(PCF_VAR_DEF))
      {
         // otherwise just deal with newlines after brace
         if (next->GetNlCount() != 1)
         {
            next->SetNlCount(1);
            MARK_CHANGE();
         }
         remove_next_newlines(next);
      }
   }

   // may have a newline before and after vbrace
   // don't do anything with it if the next non newline chunk is a closing brace
   if (nl_opt & IARF_ADD)
   {
      Chunk *next = pc->GetNextNnl();

      do
      {
         if (next->IsNullChunk())
         {
            return;
         }

         if (next->IsNot(CT_VBRACE_CLOSE))
         {
            break;
         }
         next = next->GetNextNnl();
      } while (true);

      LOG_FMT(LNEWLINE, "%s(%d): next->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
              __func__, __LINE__, next->Text(), get_token_name(next->GetType()), next->GetOrigLine(), next->GetOrigCol());

      if (next->IsNot(CT_BRACE_CLOSE))
      {
         // if vbrace, have to check before and after
         // if chunk before vbrace, check its count
         size_t nl_count = have_pre_vbrace_nl ? prev->GetNlCount() : 0;
         LOG_FMT(LNEWLINE, "%s(%d): new line count %zu\n", __func__, __LINE__, nl_count);

         if ((next = pc->GetNextNvb())->IsNewline())
         {
            LOG_FMT(LNEWLINE, "%s(%d): next->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                    __func__, __LINE__, next->Text(), get_token_name(next->GetType()), next->GetOrigLine(), next->GetOrigCol());
            nl_count += next->GetNlCount();
            LOG_FMT(LNEWLINE, "%s(%d): new line count is %zu\n", __func__, __LINE__, nl_count);
         }

         // if we have no newlines, add one and make it double
         if (nl_count == 0)
         {
            LOG_FMT(LNEWLINE, "%s(%d): new line count is 0\n", __func__, __LINE__);

            if (  ((next = pc->GetNext())->IsNotNullChunk())
               && next->IsComment())
            {
               LOG_FMT(LNEWLINE, "%s(%d): next->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                       __func__, __LINE__, next->Text(), get_token_name(next->GetType()), next->GetOrigLine(), next->GetOrigCol());
               pc = next;
               LOG_FMT(LNEWLINE, "%s(%d): pc->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                       __func__, __LINE__, pc->Text(), get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());
            }

            if ((next = newline_add_after(pc))->IsNullChunk())
            {
               return;
            }
            LOG_FMT(LNEWLINE, "%s(%d): next->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                    __func__, __LINE__, next->Text(), get_token_name(next->GetType()), next->GetOrigLine(), next->GetOrigCol());
            double_newline(next);
         }
         else if (nl_count == 1) // if we don't have enough newlines
         {
            LOG_FMT(LNEWLINE, "%s(%d): new line count is 1\n", __func__, __LINE__);

            // if we have a preceding vbrace, add one after it
            if (have_pre_vbrace_nl)
            {
               LOG_FMT(LNEWLINE, "%s(%d): have_pre_vbrace_nl is TRUE\n", __func__, __LINE__);
               next = newline_add_after(pc);
               LOG_FMT(LNEWLINE, "%s(%d): next->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                       __func__, __LINE__, next->Text(), get_token_name(next->GetType()), next->GetOrigLine(), next->GetOrigCol());
            }
            else
            {
               LOG_FMT(LNEWLINE, "%s(%d): have_pre_vbrace_nl is FALSE\n", __func__, __LINE__);
               prev = next->GetPrevNnl();
               LOG_FMT(LNEWLINE, "%s(%d): prev->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                       __func__, __LINE__, prev->Text(), get_token_name(prev->GetType()), prev->GetOrigLine(), prev->GetOrigCol());
               pc = next->GetNextNl();
               LOG_FMT(LNEWLINE, "%s(%d): pc->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                       __func__, __LINE__, pc->Text(), get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());
               Chunk *pc2 = pc->GetNext();

               if (pc2->IsNotNullChunk())
               {
                  pc = pc2;
                  LOG_FMT(LNEWLINE, "%s(%d): pc->Text() is '%s', type %s, orig line %zu, orig col %zu\n",
                          __func__, __LINE__, pc->Text(), get_token_name(pc->GetType()), pc->GetOrigLine(), pc->GetOrigCol());
               }
               else
               {
                  LOG_FMT(LNEWLINE, "%s(%d): no next found: <EOF>\n", __func__, __LINE__);
               }
               log_rule_B("nl_squeeze_ifdef");

               if (  pc->Is(CT_PREPROC)
                  && pc->GetParentType() == CT_PP_ENDIF
                  && options::nl_squeeze_ifdef())
               {
                  LOG_FMT(LNEWLINE, "%s(%d): cannot add newline after orig line %zu due to nl_squeeze_ifdef\n",
                          __func__, __LINE__, prev->GetOrigLine());
               }
               else
               {
                  // make newline after double
                  LOG_FMT(LNEWLINE, "%s(%d): call double_newline\n", __func__, __LINE__);
                  double_newline(next);
               }
            }
         }
      }
   }
} // newlines_if_for_while_switch_post_blank_lines


static void newlines_struct_union(Chunk *start, iarf_e nl_opt, bool leave_trailing)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_define_macro");

   if (  nl_opt == IARF_IGNORE
      || (  start->TestFlags(PCF_IN_PREPROC)
         && !options::nl_define_macro()))
   {
      return;
   }
   /*
    * step past any junk between the keyword and the open brace
    * Quit if we hit a semicolon or '=', which are not expected.
    */
   size_t level = start->GetLevel();
   Chunk  *pc   = start->GetNextNcNnl();

   while (  pc->IsNotNullChunk()
         && pc->GetLevel() >= level)
   {
      if (  pc->GetLevel() == level
         && (  pc->Is(CT_BRACE_OPEN)
            || pc->IsSemicolon()
            || pc->Is(CT_ASSIGN)))
      {
         break;
      }
      start = pc;
      pc    = pc->GetNextNcNnl();
   }

   // If we hit a brace open, then we need to toy with the newlines
   if (pc->Is(CT_BRACE_OPEN))
   {
      // Skip over embedded C comments
      Chunk *next = pc->GetNext();

      while (next->Is(CT_COMMENT))
      {
         next = next->GetNext();
      }

      if (  leave_trailing
         && !next->IsCommentOrNewline())
      {
         nl_opt = IARF_IGNORE;
      }
      newline_iarf_pair(start, pc, nl_opt);
   }
} // newlines_struct_union


// enum {
// enum class angle_state_e : unsigned int {
// enum-key attr(optional) identifier(optional) enum-base(optional) { enumerator-list(optional) }
// enum-key attr(optional) nested-name-specifier(optional) identifier enum-base(optional) ; TODO
// enum-key         - one of enum, enum class or enum struct  TODO
// identifier       - the name of the enumeration that's being declared
// enum-base(C++11) - colon (:), followed by a type-specifier-seq
// enumerator-list  - comma-separated list of enumerator definitions
static void newlines_enum(Chunk *start)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_define_macro");

   if (  start->TestFlags(PCF_IN_PREPROC)
      && !options::nl_define_macro())
   {
      return;
   }
   // look for 'enum class'
   Chunk *pcClass = start->GetNextNcNnl();

   if (pcClass->Is(CT_ENUM_CLASS))
   {
      log_rule_B("nl_enum_class");
      newline_iarf_pair(start, pcClass, options::nl_enum_class());
      // look for 'identifier'/ 'type'
      Chunk *pcType = pcClass->GetNextNcNnl();

      if (pcType->Is(CT_TYPE))
      {
         log_rule_B("nl_enum_class_identifier");
         newline_iarf_pair(pcClass, pcType, options::nl_enum_class_identifier());
         // look for ':'
         Chunk *pcColon = pcType->GetNextNcNnl();

         if (pcColon->Is(CT_ENUM_COLON))                       // Issue #4040
         {
            log_rule_B("nl_enum_identifier_colon");
            newline_iarf_pair(pcType, pcColon, options::nl_enum_identifier_colon());
            // look for 'type' i.e. unsigned
            Chunk *pcType1 = pcColon->GetNextNcNnl();

            if (pcType1->Is(CT_TYPE))
            {
               log_rule_B("nl_enum_colon_type");
               newline_iarf_pair(pcColon, pcType1, options::nl_enum_colon_type());
               // look for 'type' i.e. int
               Chunk *pcType2 = pcType1->GetNextNcNnl();

               if (pcType2->Is(CT_TYPE))
               {
                  log_rule_B("nl_enum_colon_type");
                  newline_iarf_pair(pcType1, pcType2, options::nl_enum_colon_type());
               }
            }
         }
      }
   }
   /*
    * step past any junk between the keyword and the open brace
    * Quit if we hit a semicolon or '=', which are not expected.
    */
   size_t level = start->GetLevel();
   Chunk  *pc   = start->GetNextNcNnl();

   while (  pc->IsNotNullChunk()
         && pc->GetLevel() >= level)
   {
      if (  pc->GetLevel() == level
         && (  pc->Is(CT_BRACE_OPEN)
            || pc->IsSemicolon()
            || pc->Is(CT_ASSIGN)))
      {
         break;
      }
      start = pc;
      pc    = pc->GetNextNcNnl();
   }

   // If we hit a brace open, then we need to toy with the newlines
   if (pc->Is(CT_BRACE_OPEN))
   {
      // Skip over embedded C comments
      Chunk *next = pc->GetNext();

      while (next->Is(CT_COMMENT))
      {
         next = next->GetNext();
      }
      iarf_e nl_opt;

      if (!next->IsCommentOrNewline())
      {
         nl_opt = IARF_IGNORE;
      }
      else
      {
         log_rule_B("nl_enum_brace");
         nl_opt = options::nl_enum_brace();
      }
      newline_iarf_pair(start, pc, nl_opt);
   }
} // newlines_enum


// namespace {
// namespace word {
// namespace type::word {
static void newlines_namespace(Chunk *start)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_namespace_brace");

   // Add or remove newline between 'namespace' and 'BRACE_OPEN'
   log_rule_B("nl_define_macro");
   iarf_e nl_opt = options::nl_namespace_brace();

   if (  nl_opt == IARF_IGNORE
      || (  start->TestFlags(PCF_IN_PREPROC)
         && !options::nl_define_macro()))
   {
      return;
   }
   Chunk *braceOpen = start->GetNextType(CT_BRACE_OPEN, start->GetLevel());

   LOG_FMT(LNEWLINE, "%s(%d): braceOpen orig line is %zu, orig col is %zu, Text() is '%s'\n",
           __func__, __LINE__, braceOpen->GetOrigLine(), braceOpen->GetOrigCol(), braceOpen->Text());
   // produces much more log output. Use it only debugging purpose
   //log_pcf_flags(LNEWLINE, braceOpen->GetFlags());

   if (braceOpen->TestFlags(PCF_ONE_LINER))
   {
      LOG_FMT(LNEWLINE, "%s(%d): is one_liner\n",
              __func__, __LINE__);
      return;
   }
   Chunk *beforeBrace = braceOpen->GetPrev();

   LOG_FMT(LNEWLINE, "%s(%d): beforeBrace orig line is %zu, orig col is %zu, Text() is '%s'\n",
           __func__, __LINE__, beforeBrace->GetOrigLine(), beforeBrace->GetOrigCol(), beforeBrace->Text());
   // 'namespace' 'BRACE_OPEN'
   newline_iarf_pair(beforeBrace, braceOpen, nl_opt);
} // newlines_namespace


static void newlines_cuddle_uncuddle(Chunk *start, iarf_e nl_opt)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_define_macro");

   if (  start->TestFlags(PCF_IN_PREPROC)
      && !options::nl_define_macro())
   {
      return;
   }
   Chunk *br_close = start->GetPrevNcNnlNi();   // Issue #2279

   if (br_close->Is(CT_BRACE_CLOSE))
   {
      newline_iarf_pair(br_close, start, nl_opt);
   }
} // newlines_cuddle_uncuddle


static void newlines_do_else(Chunk *start, iarf_e nl_opt)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_define_macro");
   log_ruleNL("nl_define_macro", start);

   if (  nl_opt == IARF_IGNORE
      || (  start->TestFlags(PCF_IN_PREPROC)
         && !options::nl_define_macro()))
   {
      return;
   }
   Chunk *next = start->GetNextNcNnl();

   if (  next->IsNotNullChunk()
      && (  next->Is(CT_BRACE_OPEN)
         || next->Is(CT_VBRACE_OPEN)))
   {
      if (!one_liner_nl_ok(next))
      {
         LOG_FMT(LNL1LINE, "%s(%d): a new line may NOT be added\n", __func__, __LINE__);
         return;
      }
      LOG_FMT(LNL1LINE, "%s(%d): a new line may be added\n", __func__, __LINE__);

      if (next->Is(CT_VBRACE_OPEN))
      {
         // Can only add - we don't want to create a one-line here
         if (nl_opt & IARF_ADD)
         {
            newline_iarf_pair(start, next->GetNextNcNnl(), nl_opt);
            Chunk *tmp = next->GetNextType(CT_VBRACE_CLOSE, next->GetLevel());

            if (  !tmp->GetNextNc()->IsNewline()
               && !tmp->GetPrevNc()->IsNewline())
            {
               newline_add_after(tmp);
            }
         }
      }
      else
      {
         newline_iarf_pair(start, next, nl_opt);
         newline_add_between(next, next->GetNextNcNnl());
      }
   }
} // newlines_do_else


static bool is_var_def(Chunk *pc, Chunk *next)
{
   if (  pc->Is(CT_DECLTYPE)
      && next->Is(CT_PAREN_OPEN))
   {
      // If current token starts a decltype expression, skip it
      next = next->GetClosingParen();
      next = next->GetNextNcNnl();
   }
   else if (!pc->IsTypeDefinition())
   {
      // Otherwise, if the current token is not a type --> not a declaration
      return(false);
   }
   else if (next->Is(CT_DC_MEMBER))
   {
      // If next token is CT_DC_MEMBER, skip it
      next = next->SkipDcMember();
   }
   else if (next->Is(CT_ANGLE_OPEN))
   {
      // If we have a template type, skip it
      next = next->GetClosingParen();
      next = next->GetNextNcNnl();
   }
   bool is = (  (  next->IsTypeDefinition()
                && next->GetParentType() != CT_FUNC_DEF)           // Issue #2639
             || next->Is(CT_WORD)
             || next->Is(CT_FUNC_CTOR_VAR));

   return(is);
} // is_var_def


static bool is_func_call_or_def(Chunk *pc)
{
   if (  pc->GetParentType() == CT_FUNC_DEF
      || pc->GetParentType() == CT_FUNC_CALL
      || pc->GetParentType() == CT_FUNC_CALL_USER
      || pc->GetParentType() == CT_FUNC_CLASS_DEF
      || pc->GetParentType() == CT_OC_CLASS
      || pc->GetParentType() == CT_OC_MSG_DECL
      || pc->GetParentType() == CT_CS_PROPERTY
      || pc->GetParentType() == CT_CPP_LAMBDA)
   {
      return(true);
   }
   return(false);
} // is_func_call_or_def


// Put newline(s) before and/or after a block of variable definitions
static Chunk *newline_var_def_blk(Chunk *start)
{
   LOG_FUNC_ENTRY();

   Chunk *pc           = start;
   Chunk *prev         = start->GetPrevNcNnlNi(); // Issue #2279
   bool  did_this_line = false;
   bool  fn_top        = false;
   bool  var_blk       = false;
   bool  first_var_blk = true;

   LOG_FMT(LVARDFBLK, "%s(%d): start orig line is %zu, orig col is %zu, Text() is '%s'\n",
           __func__, __LINE__, start->GetOrigLine(), start->GetOrigCol(), start->Text());

   if (start->Is(CT_BRACE_OPEN))
   {
      // can't be any variable definitions in a "= {" block
      if (  prev->IsNotNullChunk()
         && prev->Is(CT_ASSIGN))
      {
         Chunk *tmp = start->GetClosingParen();
         return(tmp->GetNextNcNnl());
      }
      // check if we're at the top of a function definition, or function call with a
      // possible variable block
      fn_top = is_func_call_or_def(start);
      // opening brace is processed, start with next chunk
      pc = pc->GetNext();
   }

   while (  pc->IsNotNullChunk()
         && (  pc->GetLevel() >= start->GetLevel()
            || pc->GetLevel() == 0))
   {
      LOG_CHUNK(LTOK, pc);

      Chunk *next_pc = pc->GetNext();
      LOG_FMT(LVARDFBLK, "%s(%d): next_pc orig line is %zu, orig col is %zu, type is %s, Text() is '%s'\n",
              __func__, __LINE__, next_pc->GetOrigLine(), next_pc->GetOrigCol(), get_token_name(next_pc->GetType()), next_pc->Text());

      // If next_pc token is CT_DC_MEMBER, skip it
      if (next_pc->Is(CT_DC_MEMBER))
      {
         pc = pc->SkipDcMember();
      }

      // skip qualifiers
      if (pc->Is(CT_QUALIFIER))
      {
         pc = pc->GetNext();
         continue;
      }

      if (pc->IsComment())
      {
         pc = pc->GetNext();
         continue;
      }

      // process nested braces
      if (pc->Is(CT_BRACE_OPEN))
      {
         pc = newline_var_def_blk(pc);
         continue;
      }

      // Done with this brace set?
      if (pc->Is(CT_BRACE_CLOSE))
      {
         pc = pc->GetNext();
         break;
      }

      // skip vbraces
      if (pc->Is(CT_VBRACE_OPEN))
      {
         pc = pc->GetNextType(CT_VBRACE_CLOSE, pc->GetLevel());
         pc = pc->GetNext();
         continue;
      }

      // Ignore stuff inside parenthesis/squares/angles
      if (pc->GetLevel() > pc->GetBraceLevel())
      {
         pc = pc->GetNext();
         continue;
      }

      if (pc->IsNewline())
      {
         did_this_line = false;
         pc            = pc->GetNext();
         continue;
      }

      // Determine if this is a variable definition or code
      if (  !did_this_line
         && pc->IsNot(CT_FUNC_CLASS_DEF)
         && pc->IsNot(CT_FUNC_CLASS_PROTO)
         && (  (pc->GetLevel() == (start->GetLevel() + 1))
            || pc->GetLevel() == 0))
      {
         Chunk *next = pc->GetNextNcNnl();
         LOG_FMT(LVARDFBLK, "%s(%d): next orig line is %zu, orig col is %zu, Text() is '%s'\n",
                 __func__, __LINE__, next->GetOrigLine(), next->GetOrigCol(), next->Text());

         // skip over all other type-like things
         while (  next->Is(CT_PTR_TYPE)  // Issue #2692
               || next->Is(CT_BYREF)     // Issue #3018
               || next->Is(CT_QUALIFIER)
               || next->Is(CT_TSQUARE))
         {
            next = next->GetNextNcNnl();
            LOG_FMT(LVARDFBLK, "%s(%d): next orig line is %zu, orig col is %zu, Text() is '%s'\n",
                    __func__, __LINE__, next->GetOrigLine(), next->GetOrigCol(), next->Text());
         }

         if (next->IsNullChunk())
         {
            break;
         }
         LOG_FMT(LVARDFBLK, "%s(%d): next orig line is %zu, orig col is %zu, Text() is '%s'\n",
                 __func__, __LINE__, next->GetOrigLine(), next->GetOrigCol(), next->Text());

         prev = pc->GetPrevNcNnl();

         while (  prev->Is(CT_DC_MEMBER)
               || prev->Is(CT_QUALIFIER)
               || prev->Is(CT_TYPE))
         {
            prev = prev->GetPrevNcNnl();
         }

         if (!(  prev->IsBraceOpen()
              || prev->IsBraceClose()))
         {
            prev = pc->GetPrevType(CT_SEMICOLON, pc->GetLevel());
         }

         if (prev->IsNullChunk())
         {
            prev = pc->GetPrevType(CT_BRACE_OPEN, pc->GetLevel() - 1);      // Issue #2692
         }

         if (  prev->Is(CT_STRING)
            && prev->GetParentType() == CT_EXTERN
            && prev->GetPrev()->Is(CT_EXTERN))
         {
            prev = prev->GetPrev()->GetPrevNcNnlNi();   // Issue #2279
         }
         LOG_FMT(LVARDFBLK, "%s(%d): pc orig line is %zu, orig col is %zu, type is %s, Text() is '%s'\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()), pc->Text());
         LOG_FMT(LVARDFBLK, "%s(%d): next orig line is %zu, orig col is %zu, type is %s, Text() is '%s'\n",
                 __func__, __LINE__, next->GetOrigLine(), next->GetOrigCol(), get_token_name(next->GetType()), next->Text());

         if (is_var_def(pc, next))
         {
            LOG_FMT(LVARDFBLK, "%s(%d): 'typ==var' found: '%s %s' at line %zu\n",
                    __func__, __LINE__, pc->Text(), next->Text(), pc->GetOrigLine());
            LOG_FMT(LBLANKD, "%s(%d): var_blk %s, first_var_blk %s, fn_top %s\n",
                    __func__, __LINE__, var_blk ? "TRUE" : "FALSE",
                    first_var_blk ? "TRUE" : "FALSE", fn_top ? "TRUE" : "FALSE");
            // Put newline(s) before a block of variable definitions
            log_rule_B("nl_var_def_blk_start");

            if (  !var_blk
               && !first_var_blk
               && options::nl_var_def_blk_start() > 0)
            {
               LOG_FMT(LVARDFBLK, "%s(%d): pc is '%s', orig line is %zu\n",
                       __func__, __LINE__, pc->Text(), pc->GetOrigLine());

               if (prev->IsNullChunk())
               {
                  LOG_FMT(LVARDFBLK, "%s(%d): prev is a null chunk\n", __func__, __LINE__);
               }
               else
               {
                  LOG_FMT(LVARDFBLK, "%s(%d): prev is '%s', orig line is %zu\n",
                          __func__, __LINE__, prev->Text(), prev->GetOrigLine());

                  if (!prev->IsBraceOpen())
                  {
                     newline_min_after(prev, options::nl_var_def_blk_start() + 1, PCF_VAR_DEF);
                  }
               }
            }
            // set newlines within var def block
            log_rule_B("nl_var_def_blk_in");

            if (  var_blk
               && (options::nl_var_def_blk_in() > 0))
            {
               prev = pc->GetPrev();
               LOG_FMT(LVARDFBLK, "%s(%d): prev orig line is %zu, orig col is %zu, Text() is '%s'\n",
                       __func__, __LINE__, prev->GetOrigLine(), prev->GetOrigCol(), prev->Text());

               if (prev->IsNewline())
               {
                  if (prev->GetNlCount() > options::nl_var_def_blk_in())
                  {
                     prev->SetNlCount(options::nl_var_def_blk_in());
                     MARK_CHANGE();
                  }
               }
            }
            pc      = pc->GetNextType(CT_SEMICOLON, pc->GetLevel());
            var_blk = true;
         }
         else if (var_blk)
         {
            LOG_FMT(LVARDFBLK, "%s(%d): var_blk %s, first_var_blk %s, fn_top %s\n",
                    __func__, __LINE__, var_blk ? "TRUE" : "FALSE",
                    first_var_blk ? "TRUE" : "FALSE", fn_top ? "TRUE" : "FALSE");
            log_rule_B("nl_var_def_blk_end_func_top");
            log_rule_B("nl_var_def_blk_end");

            if (  first_var_blk
               && fn_top)
            {
               // set blank lines after first var def block at the top of a function
               if (options::nl_var_def_blk_end_func_top() > 0)
               {
                  LOG_FMT(LVARDFBLK, "%s(%d): nl_var_def_blk_end_func_top at line %zu\n",
                          __func__, __LINE__, prev->GetOrigLine());
                  newline_min_after(prev, options::nl_var_def_blk_end_func_top() + 1, PCF_VAR_DEF);
               }
            }
            else if (  !pc->IsPreproc()
                    && options::nl_var_def_blk_end() > 0)
            {
               // set blank lines after other var def blocks
               LOG_FMT(LVARDFBLK, "%s(%d): nl_var_def_blk_end at line %zu\n",
                       __func__, __LINE__, prev->GetOrigLine());
               // Issue #3516
               newline_min_after(prev, options::nl_var_def_blk_end() + 1, PCF_VAR_DEF);
            }
            // reset the variables for the next block
            first_var_blk = false;
            var_blk       = false;
         }
         else
         {
            first_var_blk = false;
            var_blk       = false;
         }
      }
      else
      {
         if (pc->Is(CT_FUNC_CLASS_DEF))
         {
            log_rule_B("nl_var_def_blk_end");

            if (  var_blk
               && options::nl_var_def_blk_end() > 0)
            {
               prev = pc->GetPrev();
               prev = prev->GetPrev();
               newline_min_after(prev, options::nl_var_def_blk_end() + 1, PCF_VAR_DEF);
               pc            = pc->GetNext();
               first_var_blk = false;
               var_blk       = false;
            }
         }
      }
      did_this_line = true;

      if (pc == nullptr)
      {
         pc = Chunk::NullChunkPtr;
      }
      pc = pc->GetNext();
   }
   LOG_FMT(LVARDFBLK, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', level is %zu\n",
           __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), pc->GetLevel());
   LOG_FMT(LVARDFBLK, "%s(%d): start orig line is %zu, orig col is %zu, Text() is '%s', level is %zu\n",
           __func__, __LINE__, start->GetOrigLine(), start->GetOrigCol(), start->Text(), start->GetLevel());
   return(pc);
} // newline_var_def_blk


static void collapse_empty_body(Chunk *br_open)
{
   for (Chunk *pc = br_open->GetNext(); pc->IsNot(CT_BRACE_CLOSE); pc = pc->GetNext())
   {
      if (  pc->Is(CT_NEWLINE)
         && pc->SafeToDeleteNl())
      {
         pc = pc->GetPrev();
         Chunk *next = pc->GetNext();
         Chunk::Delete(next);
         MARK_CHANGE();
      }
   }
} // collapse_empty_body


static void newlines_brace_pair(Chunk *br_open)
{
   LOG_FUNC_ENTRY();

   log_rule_B("nl_define_macro");

   if (  br_open->TestFlags(PCF_IN_PREPROC)
      && !options::nl_define_macro())
   {
      return;
   }

   //fixes 1235 Add single line namespace support
   if (  br_open->Is(CT_BRACE_OPEN)
      && (br_open->GetParentType() == CT_NAMESPACE)
      && br_open->GetPrev()->IsNewline())
   {
      Chunk *chunk_brace_close = br_open->GetClosingParen();

      if (chunk_brace_close->IsNotNullChunk())
      {
         if (br_open->IsOnSameLine(chunk_brace_close))
         {
            log_rule_B("nl_namespace_two_to_one_liner - 1");

            if (options::nl_namespace_two_to_one_liner())
            {
               Chunk *prev = br_open->GetPrevNnl();
               newline_del_between(prev, br_open);
            }
            /* Below code is to support conversion of 2 liner to 4 liners
             * else
             * {
             *  Chunk *nxt = br_open->GetNext();
             *  newline_add_between(br_open, nxt);
             * }*/
         }
      }
   }
   // fix 1247 oneliner function support - converts 4,3,2  liners to oneliner
   log_rule_B("nl_create_func_def_one_liner");

   if (  br_open->GetParentType() == CT_FUNC_DEF
      && options::nl_create_func_def_one_liner()
      && !br_open->TestFlags(PCF_NOT_POSSIBLE))          // Issue #2795
   {
      Chunk *br_close = br_open->GetClosingParen();
      Chunk *tmp      = br_open->GetPrevNcNnlNi(); // Issue #2279

      if (  br_close->IsNotNullChunk()             // Issue #2594
         && ((br_close->GetOrigLine() - br_open->GetOrigLine()) <= 2)
         && tmp->IsParenClose())                   // need to check the conditions.
      {
         // Issue #1825
         bool is_it_possible = true;

         while (  tmp->IsNotNullChunk()
               && (tmp = tmp->GetNext())->IsNotNullChunk()
               && !tmp->IsBraceClose()
               && (tmp->GetNext()->IsNotNullChunk()))
         {
            LOG_FMT(LNL1LINE, "%s(%d): tmp orig line is %zu, orig col is %zu, Text() is '%s'\n",
                    __func__, __LINE__, tmp->GetOrigLine(), tmp->GetOrigCol(), tmp->Text());

            if (tmp->IsComment())
            {
               is_it_possible = false;
               break;
            }
         }

         if (is_it_possible)
         {
            // Issue 2795
            // we have to check if it could be too long for code_width
            // make a vector to save the chunk
            vector<Chunk> saved_chunk;
            log_rule_B("code_width");

            if (options::code_width() > 0)
            {
               saved_chunk.reserve(16);
               Chunk *current       = br_open->GetPrevNcNnlNi();
               Chunk *next_br_close = br_close->GetNext();
               current = current->GetNext();

               while (current->IsNotNullChunk())
               {
                  LOG_FMT(LNL1LINE, "%s(%d): zu  kopieren: current orig line is %zu, orig col is %zu, Text() is '%s'\n",
                          __func__, __LINE__, current->GetOrigLine(), current->GetOrigCol(), current->Text());
                  saved_chunk.push_back(*current);
                  Chunk *the_next = current->GetNext();

                  if (  the_next->IsNullChunk()
                     || the_next == next_br_close)
                  {
                     break;
                  }
                  current = the_next;
               }
            }
            Chunk *tmp_1 = br_open->GetPrevNcNnlNi();

            while (  tmp_1->IsNotNullChunk()
                  && (tmp_1 = tmp_1->GetNext())->IsNotNullChunk()
                  && !tmp_1->IsBraceClose()
                  && (tmp_1->GetNext()->IsNotNullChunk()))
            {
               LOG_FMT(LNL1LINE, "%s(%d): tmp_1 orig line is %zu, orig col is %zu, Text() is '%s'\n",
                       __func__, __LINE__, tmp_1->GetOrigLine(), tmp_1->GetOrigCol(), tmp_1->Text());

               if (tmp_1->IsNewline())
               {
                  tmp_1 = tmp_1->GetPrev();                 // Issue #1825
                  newline_iarf_pair(tmp_1, tmp_1->GetNextNcNnl(), IARF_REMOVE);
               }
            }
            br_open->SetFlagBits(PCF_ONE_LINER);         // set the one liner flag if needed
            br_close->SetFlagBits(PCF_ONE_LINER);
            log_rule_B("code_width");

            if (  options::code_width() > 0
               && br_close->GetColumn() > options::code_width())
            {
               // the created line is too long
               // it is not possible to make an one_liner
               // because the line would be too long
               br_open->SetFlagBits(PCF_NOT_POSSIBLE);
               // restore the code
               size_t count;
               Chunk  tmp_2;
               Chunk  *current = br_open;

               for (count = 0; count < saved_chunk.size(); count++)
               {
                  tmp_2 = saved_chunk.at(count);

                  if (tmp_2.GetOrigLine() != current->GetOrigLine())
                  {
                     // restore the newline
                     Chunk chunk;
                     chunk.SetType(CT_NEWLINE);
                     chunk.SetOrigLine(current->GetOrigLine());
                     chunk.SetOrigCol(current->GetOrigCol());
                     chunk.SetPpLevel(current->GetPpLevel());
                     chunk.SetNlCount(1);
                     chunk.CopyAndAddBefore(current);
                     LOG_FMT(LNEWLINE, "%s(%d): %zu:%zu add newline before '%s'\n",
                             __func__, __LINE__, current->GetOrigLine(), current->GetOrigCol(), current->Text());
                  }
                  else
                  {
                     current = current->GetNext();
                  }
               }
            }
         }
      }
   }

   // Make sure we don't break a one-liner
   if (!one_liner_nl_ok(br_open))
   {
      LOG_FMT(LNL1LINE, "%s(%d): br_open orig line is %zu, orig col is %zu, a new line may NOT be added\n",
              __func__, __LINE__, br_open->GetOrigLine(), br_open->GetOrigCol());
      return;
   }
   LOG_FMT(LNL1LINE, "%s(%d): a new line may be added\n", __func__, __LINE__);

   Chunk *next = br_open->GetNextNc();

   // Insert a newline between the '=' and open brace, if needed
   LOG_FMT(LNL1LINE, "%s(%d): br_open->Text() '%s', br_open->GetType() [%s], br_open->GetParentType() [%s]\n",
           __func__, __LINE__, br_open->Text(), get_token_name(br_open->GetType()),
           get_token_name(br_open->GetParentType()));

   if (br_open->GetParentType() == CT_ASSIGN)
   {
      // Only mess with it if the open brace is followed by a newline
      if (next->IsNewline())
      {
         Chunk *prev = br_open->GetPrevNcNnlNi();   // Issue #2279
         log_rule_B("nl_assign_brace");
         newline_iarf_pair(prev, br_open, options::nl_assign_brace());
      }
   }

   if (  br_open->GetParentType() == CT_OC_MSG_DECL
      || br_open->GetParentType() == CT_FUNC_DEF
      || br_open->GetParentType() == CT_FUNC_CLASS_DEF
      || br_open->GetParentType() == CT_OC_CLASS
      || br_open->GetParentType() == CT_CS_PROPERTY
      || br_open->GetParentType() == CT_CPP_LAMBDA
      || br_open->GetParentType() == CT_FUNC_CALL
      || br_open->GetParentType() == CT_FUNC_CALL_USER)
   {
      Chunk  *prev = Chunk::NullChunkPtr;
      iarf_e val;

      if (br_open->GetParentType() == CT_OC_MSG_DECL)
      {
         log_rule_B("nl_oc_mdef_brace");
         val = options::nl_oc_mdef_brace();
      }
      else
      {
         if (  br_open->GetParentType() == CT_FUNC_DEF
            || br_open->GetParentType() == CT_FUNC_CLASS_DEF
            || br_open->GetParentType() == CT_OC_CLASS)
         {
            val = IARF_NOT_DEFINED;
            log_rule_B("nl_fdef_brace_cond");
            const iarf_e nl_fdef_brace_cond_v = options::nl_fdef_brace_cond();

            if (nl_fdef_brace_cond_v != IARF_IGNORE)
            {
               prev = br_open->GetPrevNcNnlNi();   // Issue #2279

               if (prev->Is(CT_FPAREN_CLOSE))
               {
                  val = nl_fdef_brace_cond_v;
               }
            }

            if (val == IARF_NOT_DEFINED)
            {
               log_rule_B("nl_fdef_brace");
               val = options::nl_fdef_brace();
            }
         }
         else
         {
            log_rule_B("nl_property_brace");
            log_rule_B("nl_cpp_ldef_brace");
            log_rule_B("nl_fcall_brace");
            val = ((br_open->GetParentType() == CT_CS_PROPERTY) ?
                   options::nl_property_brace() :
                   ((br_open->GetParentType() == CT_CPP_LAMBDA) ?
                    options::nl_cpp_ldef_brace() :
                    options::nl_fcall_brace()));
         }
      }

      if (val != IARF_IGNORE)
      {
         if (prev->IsNullChunk())
         {
            // Grab the chunk before the open brace
            prev = br_open->GetPrevNcNnlNi();   // Issue #2279
         }
         newline_iarf_pair(prev, br_open, val);
      }
   }

   if (br_open->GetNextNnl()->Is(CT_BRACE_CLOSE))
   {
      // Chunk is "{" and "}" with only whitespace/newlines in between

      if (br_open->GetParentType() == CT_FUNC_DEF)
      {
         // Braces belong to a function definition
         log_rule_B("nl_collapse_empty_body_functions");

         if (options::nl_collapse_empty_body_functions())
         {
            collapse_empty_body(br_open);
            return;
         }
      }
      else
      {
         log_rule_B("nl_collapse_empty_body");

         if (options::nl_collapse_empty_body())
         {
            collapse_empty_body(br_open);
            return;
         }
      }
   }
   //fixes #1245 will add new line between tsquare and brace open based on nl_tsquare_brace

   if (br_open->Is(CT_BRACE_OPEN))
   {
      Chunk *chunk_closing_brace = br_open->GetClosingParen();

      if (chunk_closing_brace->IsNotNullChunk())
      {
         if (chunk_closing_brace->GetOrigLine() > br_open->GetOrigLine())
         {
            Chunk *prev = br_open->GetPrevNc();

            if (  prev->Is(CT_TSQUARE)
               && next->IsNewline())
            {
               log_rule_B("nl_tsquare_brace");
               newline_iarf_pair(prev, br_open, options::nl_tsquare_brace());
            }
         }
      }
   }
   // Eat any extra newlines after the brace open
   log_rule_B("eat_blanks_after_open_brace");

   if (options::eat_blanks_after_open_brace())
   {
      if (next->IsNewline())
      {
         log_rule_B("nl_inside_empty_func");
         log_rule_B("nl_inside_namespace");

         if (  options::nl_inside_empty_func() > 0
            && br_open->GetNextNnl()->Is(CT_BRACE_CLOSE)
            && (  br_open->GetParentType() == CT_FUNC_CLASS_DEF
               || br_open->GetParentType() == CT_FUNC_DEF))
         {
            blank_line_set(next, options::nl_inside_empty_func);
         }
         else if (  options::nl_inside_namespace() > 0
                 && br_open->GetParentType() == CT_NAMESPACE)
         {
            blank_line_set(next, options::nl_inside_namespace);
         }
         else if (next->GetNlCount() > 1)
         {
            next->SetNlCount(1);
            LOG_FMT(LBLANKD, "%s(%d): eat_blanks_after_open_brace %zu\n",
                    __func__, __LINE__, next->GetOrigLine());
            MARK_CHANGE();
         }
      }
   }
   bool nl_close_brace = false;

   // Handle the cases where the brace is part of a function call or definition
   if (is_func_call_or_def(br_open))
   {
      // Need to force a newline before the close brace, if not in a class body
      if (!br_open->TestFlags(PCF_IN_CLASS))
      {
         nl_close_brace = true;
      }
      // handle newlines after the open brace
      Chunk *pc = br_open->GetNextNcNnl();
      newline_add_between(br_open, pc);
   }
   // Grab the matching brace close
   Chunk *br_close = br_open->GetNextType(CT_BRACE_CLOSE, br_open->GetLevel());

   if (br_close->IsNullChunk())
   {
      return;
   }

   if (!nl_close_brace)
   {
      /*
       * If the open brace hits a CT_NEWLINE, CT_NL_CONT, CT_COMMENT_MULTI, or
       * CT_COMMENT_CPP without hitting anything other than CT_COMMENT, then
       * there should be a newline before the close brace.
       */
      Chunk *pc = br_open->GetNext();

      while (pc->Is(CT_COMMENT))
      {
         pc = pc->GetNext();
      }

      if (pc->IsCommentOrNewline())
      {
         nl_close_brace = true;
      }
   }
   Chunk *prev = br_close->GetPrevNcNnlNet();

   if (nl_close_brace)
   {
      newline_add_between(prev, br_close);
   }
   else
   {
      newline_del_between(prev, br_close);
   }
} // newlines_brace_pair


static void newline_case(Chunk *start)
{
   LOG_FUNC_ENTRY();

   //   printf("%s case (%s) on line %d col %d\n",
   //          __func__, c_chunk_names[start->GetType()],
   //          start->GetOrigLine(), start->GetOrigCol());

   // Scan backwards until a '{' or ';' or ':'. Abort if a multi-newline is found
   Chunk *prev = start;

   do
   {
      prev = prev->GetPrevNc();

      if (  prev->IsNotNullChunk()
         && prev->IsNewline()
         && prev->GetNlCount() > 1)
      {
         return;
      }
   } while (  prev->IsNot(CT_BRACE_OPEN)
           && prev->IsNot(CT_BRACE_CLOSE)
           && prev->IsNot(CT_SEMICOLON)
           && prev->IsNot(CT_CASE_COLON));

   if (prev->IsNullChunk())
   {
      return;
   }
   Chunk *pc = newline_add_between(prev, start);

   if (pc == nullptr)
   {
      return;
   }

   // Only add an extra line after a semicolon or brace close
   if (  prev->Is(CT_SEMICOLON)
      || prev->Is(CT_BRACE_CLOSE))
   {
      if (  pc->IsNewline()
         && pc->GetNlCount() < 2)
      {
         double_newline(pc);
      }
   }
} // newline_case


static void newline_case_colon(Chunk *start)
{
   LOG_FUNC_ENTRY();

   // Scan forwards until a non-comment is found
   Chunk *pc = start;

   do
   {
      pc = pc->GetNext();
   } while (pc->IsComment());

   if (  pc->IsNotNullChunk()
      && !pc->IsNewline())
   {
      newline_add_before(pc);
   }
} // newline_case_colon


static void newline_before_return(Chunk *start)
{
   LOG_FUNC_ENTRY();

   Chunk *pc = Chunk::NullChunkPtr;

   if (start != nullptr)
   {
      pc = start->GetPrev();
   }
   Chunk *nl = pc;

   // Skip over single preceding newline
   if (pc->IsNewline())
   {
      // Do we already have a blank line?
      if (nl->GetNlCount() > 1)
      {
         return;
      }
      pc = nl->GetPrev();
   }

   // Skip over preceding comments that are not a trailing comment, taking
   // into account that comment blocks may span multiple lines.
   // Trailing comments are considered part of the previous token, not the
   // return statement.  They are handled below.
   while (  pc->IsComment()
         && pc->GetParentType() != CT_COMMENT_END)
   {
      pc = pc->GetPrev();

      if (!pc->IsNewline())
      {
         return;
      }
      nl = pc;
      pc = pc->GetPrev();
   }
   pc = nl->GetPrev();

   // Peek over trailing comment of previous token
   if (  pc->IsComment()
      && pc->GetParentType() == CT_COMMENT_END)
   {
      pc = pc->GetPrev();
   }

   // Don't add extra blanks after an opening brace or a case statement
   if (  pc->IsNullChunk()
      || (  pc->Is(CT_BRACE_OPEN)
         || pc->Is(CT_VBRACE_OPEN)
         || pc->Is(CT_CASE_COLON)))
   {
      return;
   }

   if (  nl->IsNewline()
      && nl->GetNlCount() < 2)
   {
      nl->SetNlCount(nl->GetNlCount() + 1);
      MARK_CHANGE();
      LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, text is '%s', new line count is now %zu\n",
              __func__, __LINE__, nl->GetOrigLine(), nl->GetOrigCol(), nl->Text(), nl->GetNlCount());
   }
} // newline_before_return


static void newline_after_return(Chunk *start)
{
   LOG_FUNC_ENTRY();

   Chunk *semi  = start->GetNextType(CT_SEMICOLON, start->GetLevel());
   Chunk *after = semi->GetNextNcNnlNet();

   // If we hit a brace or an 'else', then a newline isn't needed
   if (  after->IsNullChunk()
      || after->IsBraceClose()
      || after->Is(CT_ELSE))
   {
      return;
   }
   Chunk *pc;

   for (pc = semi->GetNext(); pc != after; pc = pc->GetNext())
   {
      if (pc->Is(CT_NEWLINE))
      {
         if (pc->GetNlCount() < 2)
         {
            double_newline(pc);
         }
         return;
      }
   }
} // newline_after_return


static void newline_iarf_pair(Chunk *before, Chunk *after, iarf_e av, bool check_nl_assign_leave_one_liners)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNEWLINE, "%s(%d): ", __func__, __LINE__);
   log_func_stack(LNEWLINE, "CallStack:");

   if (  before == nullptr
      || before == Chunk::NullChunkPtr
      || after == nullptr
      || after == Chunk::NullChunkPtr
      || after->Is(CT_IGNORED))
   {
      return;
   }

   if (av & IARF_ADD)
   {
      if (  check_nl_assign_leave_one_liners
         && options::nl_assign_leave_one_liners()
         && after->TestFlags(PCF_ONE_LINER))
      {
         log_rule_B("nl_assign_leave_one_liners");
         return;
      }
      Chunk *nl = newline_add_between(before, after);
      LOG_FMT(LNEWLINE, "%s(%d): newline_add_between '%s' and '%s'\n",
              __func__, __LINE__, before->Text(), after->Text());

      if (  nl != nullptr
         && av == IARF_FORCE
         && nl->GetNlCount() > 1)
      {
         nl->SetNlCount(1);
      }
   }
   else if (av & IARF_REMOVE)
   {
      LOG_FMT(LNEWLINE, "%s(%d): newline_remove_between '%s' and '%s'\n",
              __func__, __LINE__, before->Text(), after->Text());
      newline_del_between(before, after);
   }
} // newline_iarf_pair


void newline_iarf(Chunk *pc, iarf_e av)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNFD, "%s(%d): ", __func__, __LINE__);
   log_func_stack(LNFD, "CallStack:");
   Chunk *after = Chunk::NullChunkPtr;

   if (pc != nullptr)
   {
      after = pc->GetNextNnl();
   }

   if (  (pc != nullptr && pc->Is(CT_FPAREN_OPEN))                         // Issue #2914
      && pc->GetParentType() == CT_FUNC_CALL
      && after->Is(CT_COMMENT_CPP)
      && options::donot_add_nl_before_cpp_comment())
   {
      return;
   }
   newline_iarf_pair(pc, after, av);
} // newline_iarf


static void newline_func_multi_line(Chunk *start)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNFD, "%s(%d): called on %zu:%zu '%s' [%s/%s]\n",
           __func__, __LINE__, start->GetOrigLine(), start->GetOrigCol(),
           start->Text(), get_token_name(start->GetType()), get_token_name(start->GetParentType()));

   bool add_start;
   bool add_args;
   bool add_end;

   if (  start->GetParentType() == CT_FUNC_DEF
      || start->GetParentType() == CT_FUNC_CLASS_DEF)
   {
      log_rule_B("nl_func_def_start_multi_line");
      add_start = options::nl_func_def_start_multi_line();
      log_rule_B("nl_func_def_args_multi_line");
      add_args = options::nl_func_def_args_multi_line();
      log_rule_B("nl_func_def_end_multi_line");
      add_end = options::nl_func_def_end_multi_line();
   }
   else if (  start->GetParentType() == CT_FUNC_CALL
           || start->GetParentType() == CT_FUNC_CALL_USER)
   {
      log_rule_B("nl_func_call_start_multi_line");
      add_start = options::nl_func_call_start_multi_line();
      log_rule_B("nl_func_call_args_multi_line");
      add_args = options::nl_func_call_args_multi_line();
      log_rule_B("nl_func_call_end_multi_line");
      add_end = options::nl_func_call_end_multi_line();
   }
   else
   {
      log_rule_B("nl_func_decl_start_multi_line");
      add_start = options::nl_func_decl_start_multi_line();
      log_rule_B("nl_func_decl_args_multi_line");
      add_args = options::nl_func_decl_args_multi_line();
      log_rule_B("nl_func_decl_end_multi_line");
      add_end = options::nl_func_decl_end_multi_line();
   }

   if (  !add_start
      && !add_args
      && !add_end)
   {
      return;
   }
   Chunk *pc = start->GetNextNcNnl();

   while (  pc->IsNotNullChunk()
         && pc->GetLevel() > start->GetLevel())
   {
      pc = pc->GetNextNcNnl();
   }

   if (  pc->Is(CT_FPAREN_CLOSE)
      && start->IsNewlineBetween(pc))
   {
      Chunk *start_next         = start->GetNextNcNnl();
      bool  has_leading_closure = (  start_next->GetParentType() == CT_OC_BLOCK_EXPR
                                  || start_next->GetParentType() == CT_CPP_LAMBDA
                                  || start_next->Is(CT_BRACE_OPEN));

      Chunk *prev_end            = pc->GetPrevNcNnl();
      bool  has_trailing_closure = (  prev_end->GetParentType() == CT_OC_BLOCK_EXPR
                                   || prev_end->GetParentType() == CT_CPP_LAMBDA
                                   || prev_end->Is(CT_BRACE_OPEN));

      if (  add_start
         && !start->GetNext()->IsNewline())
      {
         log_rule_B("nl_func_call_args_multi_line_ignore_closures");

         if (options::nl_func_call_args_multi_line_ignore_closures())
         {
            if (  !has_leading_closure
               && !has_trailing_closure)
            {
               newline_iarf(start, IARF_ADD);
            }
         }
         else
         {
            newline_iarf(start, IARF_ADD);
         }
      }

      if (  add_end
         && !pc->GetPrev()->IsNewline())
      {
         log_rule_B("nl_func_call_args_multi_line_ignore_closures");

         if (options::nl_func_call_args_multi_line_ignore_closures())
         {
            if (  !has_leading_closure
               && !has_trailing_closure)
            {
               newline_iarf(pc->GetPrev(), IARF_ADD);
            }
         }
         else
         {
            newline_iarf(pc->GetPrev(), IARF_ADD);
         }
      }

      if (add_args)
      {
         // process the function in reverse and leave the first comma if the option to leave trailing closure
         // is on. nl_func_call_args_multi_line_ignore_trailing_closure
         for (pc = start->GetNextNcNnl();
              pc->IsNotNullChunk() && pc->GetLevel() > start->GetLevel();
              pc = pc->GetNextNcNnl())
         {
            if (  pc->Is(CT_COMMA)
               && (pc->GetLevel() == (start->GetLevel() + 1)))
            {
               Chunk *tmp = pc->GetNext();

               if (tmp->IsComment())
               {
                  pc = tmp;
               }

               if (!pc->GetNext()->IsNewline())
               {
                  log_rule_B("nl_func_call_args_multi_line_ignore_closures");

                  if (options::nl_func_call_args_multi_line_ignore_closures())
                  {
                     Chunk *prev_comma  = pc->GetPrevNcNnl();
                     Chunk *after_comma = pc->GetNextNcNnl();

                     if (!(  (  prev_comma->GetParentType() == CT_OC_BLOCK_EXPR
                             || prev_comma->GetParentType() == CT_CPP_LAMBDA
                             || prev_comma->Is(CT_BRACE_OPEN))
                          || (  after_comma->GetParentType() == CT_OC_BLOCK_EXPR
                             || after_comma->GetParentType() == CT_CPP_LAMBDA
                             || after_comma->Is(CT_BRACE_OPEN))))
                     {
                        newline_iarf(pc, IARF_ADD);
                     }
                  }
                  else
                  {
                     newline_iarf(pc, IARF_ADD);
                  }
               }
            }
         }
      }
   }
} // newline_func_multi_line


static void newline_template(Chunk *start)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNFD, "%s(%d): called on %zu:%zu '%s' [%s/%s]\n",
           __func__, __LINE__, start->GetOrigLine(), start->GetOrigCol(),
           start->Text(), get_token_name(start->GetType()), get_token_name(start->GetParentType()));

   log_rule_B("nl_template_start");
   bool add_start = options::nl_template_start();

   log_rule_B("nl_template_args");
   bool add_args = options::nl_template_args();

   log_rule_B("nl_template_end");
   bool add_end = options::nl_template_end();

   if (  !add_start
      && !add_args
      && !add_end)
   {
      return;
   }
   Chunk *pc = start->GetNextNcNnl();

   while (  pc->IsNotNullChunk()
         && pc->GetLevel() > start->GetLevel())
   {
      pc = pc->GetNextNcNnl();
   }

   if (pc->Is(CT_ANGLE_CLOSE))
   {
      if (add_start)
      {
         newline_iarf(start, IARF_ADD);
      }

      if (add_end)
      {
         newline_iarf(pc->GetPrev(), IARF_ADD);
      }

      if (add_args)
      {
         Chunk *pc_1;

         for (pc_1 = start->GetNextNcNnl();
              pc_1->IsNotNullChunk() && pc_1->GetLevel() > start->GetLevel();
              pc_1 = pc_1->GetNextNcNnl())
         {
            if (  pc_1->Is(CT_COMMA)
               && (pc_1->GetLevel() == (start->GetLevel() + 1)))
            {
               Chunk *tmp = pc_1->GetNext();

               if (tmp->IsComment())
               {
                  pc_1 = tmp;
               }

               if (!pc_1->GetNext()->IsNewline())
               {
                  newline_iarf(pc_1, IARF_ADD);
               }
            }
         }
      }
   }
} // newline_template


static void newline_func_def_or_call(Chunk *start)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNFD, "%s(%d): called on start->Text() is '%s', orig line is %zu, orig col is %zu, [%s/%s]\n",
           __func__, __LINE__, start->Text(), start->GetOrigLine(), start->GetOrigCol(),
           get_token_name(start->GetType()), get_token_name(start->GetParentType()));

   bool is_def = (start->GetParentType() == CT_FUNC_DEF)
                 || start->GetParentType() == CT_FUNC_CLASS_DEF;
   bool is_call = (start->GetParentType() == CT_FUNC_CALL)
                  || start->GetParentType() == CT_FUNC_CALL_USER;

   LOG_FMT(LNFD, "%s(%d): is_def is %s, is_call is %s\n",
           __func__, __LINE__, is_def ? "TRUE" : "FALSE", is_call ? "TRUE" : "FALSE");

   if (is_call)
   {
      log_rule_B("nl_func_call_paren");
      iarf_e atmp = options::nl_func_call_paren();

      if (atmp != IARF_IGNORE)
      {
         Chunk *prev = start->GetPrevNcNnlNi();   // Issue #2279

         if (prev->IsNotNullChunk())
         {
            newline_iarf(prev, atmp);
         }
      }
      Chunk *pc = start->GetNextNcNnl();

      if (pc->IsString(")"))
      {
         log_rule_B("nl_func_call_paren_empty");
         atmp = options::nl_func_call_paren_empty();

         if (atmp != IARF_IGNORE)
         {
            Chunk *prev = start->GetPrevNcNnlNi();   // Issue #2279

            if (prev->IsNotNullChunk())
            {
               newline_iarf(prev, atmp);
            }
         }
         log_rule_B("nl_func_call_empty");
         atmp = options::nl_func_call_empty();

         if (atmp != IARF_IGNORE)
         {
            newline_iarf(start, atmp);
         }
         return;
      }
   }
   else
   {
      log_rule_B("nl_func_def_paren");
      log_rule_B("nl_func_paren");
      iarf_e atmp = is_def ? options::nl_func_def_paren()
                  : options::nl_func_paren();
      LOG_FMT(LSPACE, "%s(%d): atmp is %s\n",
              __func__, __LINE__,
              (atmp == IARF_IGNORE) ? "IGNORE" :
              (atmp == IARF_ADD) ? "ADD" :
              (atmp == IARF_REMOVE) ? "REMOVE" : "FORCE");

      if (atmp != IARF_IGNORE)
      {
         Chunk *prev = start->GetPrevNcNnlNi();      // Issue #2279

         if (prev->IsNotNullChunk())
         {
            newline_iarf(prev, atmp);
         }
      }
      // Handle break newlines type and function
      Chunk *prev = start->GetPrevNcNnlNi();   // Issue #2279
      prev = skip_template_prev(prev);
      // Don't split up a function variable
      prev = prev->IsParenClose() ? Chunk::NullChunkPtr : prev->GetPrevNcNnlNi();   // Issue #2279

      log_rule_B("nl_func_class_scope");

      if (  prev->Is(CT_DC_MEMBER)
         && (options::nl_func_class_scope() != IARF_IGNORE))
      {
         newline_iarf(prev->GetPrevNcNnlNi(), options::nl_func_class_scope());   // Issue #2279
      }

      if (prev->IsNot(CT_ACCESS_COLON))
      {
         Chunk *tmp;

         if (prev->Is(CT_OPERATOR))
         {
            tmp  = prev;
            prev = prev->GetPrevNcNnlNi();   // Issue #2279
         }
         else
         {
            tmp = start;
         }

         if (prev->Is(CT_DC_MEMBER))
         {
            log_rule_B("nl_func_scope_name");

            if (  options::nl_func_scope_name() != IARF_IGNORE
               && !start->TestFlags(PCF_IN_DECLTYPE))
            {
               newline_iarf(prev, options::nl_func_scope_name());
            }
         }
         const Chunk *tmp_next = prev->GetNextNcNnl();

         if (tmp_next->IsNot(CT_FUNC_CLASS_DEF))
         {
            Chunk  *closing = tmp->GetClosingParen();
            Chunk  *brace   = closing->GetNextNcNnl();
            iarf_e a;                                            // Issue #2561

            if (  tmp->GetParentType() == CT_FUNC_PROTO
               || tmp->GetParentType() == CT_FUNC_CLASS_PROTO)
            {
               // proto
               log_rule_B("nl_func_proto_type_name");
               a = options::nl_func_proto_type_name();
            }
            else
            {
               // def

               log_rule_B("nl_func_leave_one_liners");

               if (  options::nl_func_leave_one_liners()
                  && (  brace == nullptr
                     || brace->TestFlags(PCF_ONE_LINER)))   // Issue #1511 and #3274
               {
                  a = IARF_IGNORE;
               }
               else
               {
                  log_rule_B("nl_func_type_name");
                  a = options::nl_func_type_name();
               }
            }
            log_rule_B("nl_func_type_name_class");

            if (  tmp->TestFlags(PCF_IN_CLASS)
               && (options::nl_func_type_name_class() != IARF_IGNORE))
            {
               a = options::nl_func_type_name_class();
            }

            if (  (a != IARF_IGNORE)
               && prev->IsNotNullChunk())
            {
               LOG_FMT(LNFD, "%s(%d): prev->Text() '%s', orig line is %zu, orig col is %zu, [%s/%s]\n",
                       __func__, __LINE__, prev->Text(), prev->GetOrigLine(), prev->GetOrigCol(),
                       get_token_name(prev->GetType()),
                       get_token_name(prev->GetParentType()));

               if (prev->Is(CT_DESTRUCTOR))
               {
                  prev = prev->GetPrevNcNnlNi();   // Issue #2279
               }

               /*
                * If we are on a '::', step back two tokens
                * TODO: do we also need to check for '.' ?
                */
               while (prev->Is(CT_DC_MEMBER))
               {
                  prev = prev->GetPrevNcNnlNi();   // Issue #2279
                  prev = skip_template_prev(prev);
                  prev = prev->GetPrevNcNnlNi();   // Issue #2279
               }

               if (  !prev->IsBraceClose()
                  && prev->IsNot(CT_BRACE_OPEN)
                  && prev->IsNot(CT_SEMICOLON)
                  && prev->IsNot(CT_ACCESS_COLON)
                     // #1008: if we landed on an operator check that it is having
                     // a type before it, in order to not apply nl_func_type_name
                     // on conversion operators as they don't have a normal
                     // return type syntax
                  && (tmp_next->IsNot(CT_OPERATOR) ? true : prev->IsTypeDefinition()))
               {
                  newline_iarf(prev, a);
               }
            }
         }
      }
      Chunk *pc = start->GetNextNcNnl();

      if (pc->IsString(")"))
      {
         log_rule_B("nl_func_def_empty");
         log_rule_B("nl_func_decl_empty");
         atmp = is_def ? options::nl_func_def_empty()
                : options::nl_func_decl_empty();

         if (atmp != IARF_IGNORE)
         {
            newline_iarf(start, atmp);
         }
         log_rule_B("nl_func_def_paren_empty");
         log_rule_B("nl_func_paren_empty");
         atmp = is_def ? options::nl_func_def_paren_empty()
                : options::nl_func_paren_empty();

         if (atmp != IARF_IGNORE)
         {
            prev = start->GetPrevNcNnlNi();   // Issue #2279

            if (prev->IsNotNullChunk())
            {
               newline_iarf(prev, atmp);
            }
         }
         return;
      }
   }
   // Now scan for commas
   size_t comma_count = 0;
   Chunk  *tmp;
   Chunk  *pc;

   for (pc = start->GetNextNcNnl();
        pc->IsNotNullChunk() && pc->GetLevel() > start->GetLevel();
        pc = pc->GetNextNcNnl())
   {
      if (  pc->Is(CT_COMMA)
         && (pc->GetLevel() == (start->GetLevel() + 1)))
      {
         comma_count++;
         tmp = pc->GetNext();

         if (tmp->IsComment())
         {
            pc = tmp;
         }

         if (is_def)
         {
            log_rule_B("nl_func_def_args");
            newline_iarf(pc, options::nl_func_def_args());
         }
         else if (is_call)
         {
            // Issue #2604
            log_rule_B("nl_func_call_args");
            newline_iarf(pc, options::nl_func_call_args());
         }
         else // start->GetParentType() == CT_FUNC_DECL
         {
            log_rule_B("nl_func_decl_args");
            newline_iarf(pc, options::nl_func_decl_args());
         }
      }
   }

   log_rule_B("nl_func_def_start");
   log_rule_B("nl_func_decl_start");
   iarf_e as = is_def ? options::nl_func_def_start() : options::nl_func_decl_start();

   log_rule_B("nl_func_def_end");
   log_rule_B("nl_func_decl_end");
   iarf_e ae = is_def ? options::nl_func_def_end() : options::nl_func_decl_end();

   if (comma_count == 0)
   {
      iarf_e atmp;
      log_rule_B("nl_func_def_start_single");
      log_rule_B("nl_func_decl_start_single");
      atmp = is_def ? options::nl_func_def_start_single() :
             options::nl_func_decl_start_single();

      if (atmp != IARF_IGNORE)
      {
         as = atmp;
      }
      log_rule_B("nl_func_def_end_single");
      log_rule_B("nl_func_decl_end_single");
      atmp = is_def ? options::nl_func_def_end_single() :
             options::nl_func_decl_end_single();

      if (atmp != IARF_IGNORE)
      {
         ae = atmp;
      }
   }

   if (!is_call)
   {
      newline_iarf(start, as);
   }

   // and fix up the close parenthesis
   if (pc->Is(CT_FPAREN_CLOSE))
   {
      Chunk *prev = pc->GetPrevNnl();

      if (  prev->IsNot(CT_FPAREN_OPEN)
         && !is_call)
      {
         newline_iarf(prev, ae);
      }
      newline_func_multi_line(start);
   }
} // newline_func_def_or_call


static void newline_oc_msg(Chunk *start)
{
   LOG_FUNC_ENTRY();

   Chunk *sq_c = start->GetClosingParen();

   if (sq_c->IsNullChunk())
   {
      return;
   }
   log_rule_B("nl_oc_msg_leave_one_liner");

   if (options::nl_oc_msg_leave_one_liner())
   {
      return;
   }
   bool should_nl_msg = false;

   // Get count of parameters
   size_t parameter_count = 0;

   for (Chunk *pc = start->GetNextNcNnl(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      if (pc->GetLevel() <= start->GetLevel())
      {
         break;
      }

      if (pc->Is(CT_OC_COLON) && pc->GetLevel() - 1 == start->GetLevel())
      {
         parameter_count++;
      }
   }

   size_t min_params = options::nl_oc_msg_args_min_params();

   if (  parameter_count >= min_params
      && min_params != 0)
   {
      should_nl_msg = true;
   }
   // Get length of longest line
   size_t longest_line = 0;

   for (Chunk *pc = start->GetNextNcNnl(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      if (pc->GetLevel() <= start->GetLevel())
      {
         break;
      }

      if (pc->GetOrigColEnd() > longest_line)
      {
         longest_line = pc->GetOrigColEnd();
      }
   }

   size_t max_code_width = options::nl_oc_msg_args_max_code_width();

   if (  longest_line > max_code_width
      && max_code_width != 0)
   {
      should_nl_msg = true;
   }

   // If both nl_oc_msg_args_min_params and nl_oc_msg_args_max_code_width are disabled
   // we should newline all messages.
   if (  max_code_width == 0
      && min_params == 0)
   {
      should_nl_msg = true;
   }

   if (!should_nl_msg)
   {
      return;
   }

   for (Chunk *pc = start->GetNextNcNnl(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      if (pc->GetLevel() <= start->GetLevel())
      {
         break;
      }

      if (pc->Is(CT_OC_MSG_NAME) && pc->GetLevel() - 1 == start->GetLevel())
      {
         newline_add_before(pc);
      }
   }
} // newline_oc_msg


static bool one_liner_nl_ok(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNL1LINE, "%s(%d): check type is %s, parent is %s, flag is %s, orig line is %zu, orig col is %zu\n",
           __func__, __LINE__, get_token_name(pc->GetType()), get_token_name(pc->GetParentType()),
           pcf_flags_str(pc->GetFlags()).c_str(), pc->GetOrigLine(), pc->GetOrigCol());

   if (!pc->TestFlags(PCF_ONE_LINER))
   {
      LOG_FMT(LNL1LINE, "%s(%d): true (not 1-liner), a new line may be added\n", __func__, __LINE__);
      return(true);
   }
   // Step back to find the opening brace
   Chunk *br_open = pc;

   if (br_open->IsBraceClose())
   {
      br_open = br_open->GetPrevType(br_open->Is(CT_BRACE_CLOSE) ? CT_BRACE_OPEN : CT_VBRACE_OPEN,
                                     br_open->GetLevel(), E_Scope::ALL);
   }
   else
   {
      while (  br_open->IsNotNullChunk()
            && br_open->TestFlags(PCF_ONE_LINER)
            && !br_open->IsBraceOpen()
            && !br_open->IsBraceClose())
      {
         br_open = br_open->GetPrev();
      }
   }
   pc = br_open;

   if (  pc->IsNotNullChunk()
      && pc->TestFlags(PCF_ONE_LINER)
      && (  pc->IsBraceOpen()
         || pc->IsBraceClose()))
   {
      log_rule_B("nl_class_leave_one_liners");

      if (  options::nl_class_leave_one_liners()
         && pc->TestFlags(PCF_IN_CLASS))
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (class)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_assign_leave_one_liners");

      if (  options::nl_assign_leave_one_liners()
         && pc->GetParentType() == CT_ASSIGN)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (assign)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_enum_leave_one_liners");

      if (  options::nl_enum_leave_one_liners()
         && pc->GetParentType() == CT_ENUM)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (enum)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_getset_leave_one_liners");

      if (  options::nl_getset_leave_one_liners()
         && pc->GetParentType() == CT_GETSET)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (get/set), a new line may NOT be added\n", __func__, __LINE__);
         return(false);
      }
      // Issue #UT-98
      log_rule_B("nl_cs_property_leave_one_liners");

      if (  options::nl_cs_property_leave_one_liners()
         && pc->GetParentType() == CT_CS_PROPERTY)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (c# property), a new line may NOT be added\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_func_leave_one_liners");

      if (  options::nl_func_leave_one_liners()
         && (  pc->GetParentType() == CT_FUNC_DEF
            || pc->GetParentType() == CT_FUNC_CLASS_DEF))
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (func def)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_func_leave_one_liners");

      if (  options::nl_func_leave_one_liners()
         && pc->GetParentType() == CT_OC_MSG_DECL)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (method def)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_cpp_lambda_leave_one_liners");

      if (  options::nl_cpp_lambda_leave_one_liners()
         && ((pc->GetParentType() == CT_CPP_LAMBDA)))
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (lambda)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_oc_msg_leave_one_liner");

      if (  options::nl_oc_msg_leave_one_liner()
         && pc->TestFlags(PCF_IN_OC_MSG))
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (message)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_if_leave_one_liners");

      if (  options::nl_if_leave_one_liners()
         && (  pc->GetParentType() == CT_IF
            || pc->GetParentType() == CT_ELSEIF
            || pc->GetParentType() == CT_ELSE))
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (if/else)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_while_leave_one_liners");

      if (  options::nl_while_leave_one_liners()
         && pc->GetParentType() == CT_WHILE)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (while)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_do_leave_one_liners");

      if (  options::nl_do_leave_one_liners()
         && pc->GetParentType() == CT_DO)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (do)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_for_leave_one_liners");

      if (  options::nl_for_leave_one_liners()
         && pc->GetParentType() == CT_FOR)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (for)\n", __func__, __LINE__);
         return(false);
      }
      log_rule_B("nl_namespace_two_to_one_liner - 2");

      if (  options::nl_namespace_two_to_one_liner()
         && pc->GetParentType() == CT_NAMESPACE)
      {
         LOG_FMT(LNL1LINE, "%s(%d): false (namespace)\n", __func__, __LINE__);
         return(false);
      }
   }
   LOG_FMT(LNL1LINE, "%s(%d): true, a new line may be added\n", __func__, __LINE__);
   return(true);
} // one_liner_nl_ok


void undo_one_liner(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   if (  pc != nullptr
      && pc->TestFlags(PCF_ONE_LINER))
   {
      LOG_FMT(LNL1LINE, "%s(%d): pc->Text() '%s', orig line is %zu, orig col is %zu",
              __func__, __LINE__, pc->Text(), pc->GetOrigLine(), pc->GetOrigCol());
      pc->ResetFlagBits(PCF_ONE_LINER);

      // scan backward
      LOG_FMT(LNL1LINE, "%s(%d): scan backward\n", __func__, __LINE__);
      Chunk *tmp = pc;

      while ((tmp = tmp->GetPrev())->IsNotNullChunk())
      {
         if (!tmp->TestFlags(PCF_ONE_LINER))
         {
            LOG_FMT(LNL1LINE, "%s(%d): tmp->Text() '%s', orig line is %zu, orig col is %zu, --> break\n",
                    __func__, __LINE__, tmp->Text(), tmp->GetOrigLine(), tmp->GetOrigCol());
            break;
         }
         LOG_FMT(LNL1LINE, "%s(%d): clear for tmp->Text() '%s', orig line is %zu, orig col is %zu",
                 __func__, __LINE__, tmp->Text(), tmp->GetOrigLine(), tmp->GetOrigCol());
         tmp->ResetFlagBits(PCF_ONE_LINER);
      }
      // scan forward
      LOG_FMT(LNL1LINE, "%s(%d): scan forward\n", __func__, __LINE__);
      tmp = pc;
      LOG_FMT(LNL1LINE, "%s(%d): - \n", __func__, __LINE__);

      while ((tmp = tmp->GetNext())->IsNotNullChunk())
      {
         if (!tmp->TestFlags(PCF_ONE_LINER))
         {
            LOG_FMT(LNL1LINE, "%s(%d): tmp->Text() '%s', orig line is %zu, orig col is %zu, --> break\n",
                    __func__, __LINE__, tmp->Text(), tmp->GetOrigLine(), tmp->GetOrigCol());
            break;
         }
         LOG_FMT(LNL1LINE, "%s(%d): clear for tmp->Text() '%s', orig line is %zu, orig col is %zu",
                 __func__, __LINE__, tmp->Text(), tmp->GetOrigLine(), tmp->GetOrigCol());
         tmp->ResetFlagBits(PCF_ONE_LINER);
      }
      LOG_FMT(LNL1LINE, "\n");
   }
} // undo_one_liner


static void nl_create_one_liner(Chunk *vbrace_open)
{
   LOG_FUNC_ENTRY();

   // See if we get a newline between the next text and the vbrace_close
   Chunk *tmp   = vbrace_open->GetNextNcNnl();
   Chunk *first = tmp;

   if (  first->IsNullChunk()
      || get_token_pattern_class(first->GetType()) != pattern_class_e::NONE)
   {
      return;
   }
   size_t nl_total = 0;

   while (tmp->IsNot(CT_VBRACE_CLOSE))
   {
      if (tmp->IsNewline())
      {
         nl_total += tmp->GetNlCount();

         if (nl_total > 1)
         {
            return;
         }
      }
      tmp = tmp->GetNext();
   }

   if (  tmp->IsNotNullChunk()
      && first->IsNotNullChunk())
   {
      newline_del_between(vbrace_open, first);
   }
} // nl_create_one_liner


static void nl_create_list_liner(Chunk *brace_open)
{
   LOG_FUNC_ENTRY();

   // See if we get a newline between the next text and the vbrace_close
   if (brace_open == nullptr)
   {
      return;
   }
   Chunk *closing = brace_open->GetNextType(CT_BRACE_CLOSE, brace_open->GetLevel());
   Chunk *tmp     = brace_open;

   do
   {
      if (tmp->Is(CT_COMMA))
      {
         return;
      }
      tmp = tmp->GetNext();
   } while (tmp != closing);

   newline_del_between(brace_open, closing);
} // nl_create_list_liner


void newlines_remove_newlines()
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LBLANK, "%s(%d):\n", __func__, __LINE__);
   Chunk *pc = Chunk::GetHead();

   if (!pc->IsNewline())
   {
      pc = pc->GetNextNl();
   }
   Chunk *next;
   Chunk *prev;

   while (pc->IsNotNullChunk())
   {
      // Remove all newlines not in preproc
      if (!pc->TestFlags(PCF_IN_PREPROC))
      {
         next = pc->GetNext();
         prev = pc->GetPrev();
         newline_iarf(pc, IARF_REMOVE);

         if (next == Chunk::GetHead())
         {
            pc = next;
            continue;
         }
         else if (  prev->IsNotNullChunk()
                 && !prev->GetNext()->IsNewline())
         {
            pc = prev;
         }
      }
      pc = pc->GetNextNl();
   }
} // newlines_remove_newlines


void newlines_remove_disallowed()
{
   LOG_FUNC_ENTRY();

   Chunk *pc = Chunk::GetHead();
   Chunk *next;

   while ((pc = pc->GetNextNl())->IsNotNullChunk())
   {
      LOG_FMT(LBLANKD, "%s(%d): orig line is %zu, orig col is %zu, <Newline>, nl is %zu\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->GetNlCount());

      next = pc->GetNext();

      if (  next->IsNotNullChunk()
         && !next->Is(CT_NEWLINE)
         && !can_increase_nl(pc))
      {
         LOG_FMT(LBLANKD, "%s(%d): force to 1 orig line is %zu, orig col is %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());

         if (pc->GetNlCount() != 1)
         {
            pc->SetNlCount(1);
            MARK_CHANGE();
         }
      }
   }
} // newlines_remove_disallowed


void newlines_cleanup_angles()
{
   // Issue #1167
   LOG_FUNC_ENTRY();

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      char copy[1000];
      LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, Text() is '%s'\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->ElidedText(copy));

      if (pc->Is(CT_ANGLE_OPEN))
      {
         newline_template(pc);
      }
   }
} // newlines_cleanup_angles


void newlines_cleanup_braces(bool first)
{
   LOG_FUNC_ENTRY();

   // Get the first token that's not an empty line:
   Chunk *pc = Chunk::GetHead();

   if (pc->IsNewline())
   {
      pc = pc->GetNextNcNnl();
   }

   for ( ; pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      char copy[1000];
      LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, Text() is '%s'\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->ElidedText(copy));

      if (  pc->Is(CT_IF)
         || pc->Is(CT_CONSTEXPR))
      {
         log_rule_B("nl_if_brace");
         newlines_if_for_while_switch(pc, options::nl_if_brace());
      }
      else if (pc->Is(CT_ELSEIF))
      {
         log_rule_B("nl_elseif_brace");
         iarf_e arg = options::nl_elseif_brace();
         log_rule_B("nl_if_brace");
         newlines_if_for_while_switch(
            pc, (arg != IARF_IGNORE) ? arg : options::nl_if_brace());
      }
      else if (pc->Is(CT_FOR))
      {
         log_rule_B("nl_for_brace");
         newlines_if_for_while_switch(pc, options::nl_for_brace());
      }
      else if (pc->Is(CT_CATCH))
      {
         log_rule_B("nl_oc_brace_catch");

         if (  language_is_set(LANG_OC)
            && (pc->GetStr()[0] == '@')
            && (options::nl_oc_brace_catch() != IARF_IGNORE))
         {
            newlines_cuddle_uncuddle(pc, options::nl_oc_brace_catch());
         }
         else
         {
            log_rule_B("nl_brace_catch");
            newlines_cuddle_uncuddle(pc, options::nl_brace_catch());
         }
         Chunk *next = pc->GetNextNcNnl();

         if (next->Is(CT_BRACE_OPEN))
         {
            log_rule_B("nl_oc_catch_brace");

            if (  language_is_set(LANG_OC)
               && (options::nl_oc_catch_brace() != IARF_IGNORE))
            {
               log_rule_B("nl_oc_catch_brace");
               newlines_do_else(pc, options::nl_oc_catch_brace());
            }
            else
            {
               log_rule_B("nl_catch_brace");
               newlines_do_else(pc, options::nl_catch_brace());
            }
         }
         else
         {
            log_rule_B("nl_oc_catch_brace");

            if (  language_is_set(LANG_OC)
               && (options::nl_oc_catch_brace() != IARF_IGNORE))
            {
               newlines_if_for_while_switch(pc, options::nl_oc_catch_brace());
            }
            else
            {
               log_rule_B("nl_catch_brace");
               newlines_if_for_while_switch(pc, options::nl_catch_brace());
            }
         }
      }
      else if (pc->Is(CT_WHILE))
      {
         log_rule_B("nl_while_brace");
         newlines_if_for_while_switch(pc, options::nl_while_brace());
      }
      else if (pc->Is(CT_USING_STMT))
      {
         log_rule_B("nl_using_brace");
         newlines_if_for_while_switch(pc, options::nl_using_brace());
      }
      else if (pc->Is(CT_D_SCOPE_IF))
      {
         log_rule_B("nl_scope_brace");
         newlines_if_for_while_switch(pc, options::nl_scope_brace());
      }
      else if (pc->Is(CT_UNITTEST))
      {
         log_rule_B("nl_unittest_brace");
         newlines_do_else(pc, options::nl_unittest_brace());
      }
      else if (pc->Is(CT_D_VERSION_IF))
      {
         log_rule_B("nl_version_brace");
         newlines_if_for_while_switch(pc, options::nl_version_brace());
      }
      else if (pc->Is(CT_SWITCH))
      {
         log_rule_B("nl_switch_brace");
         newlines_if_for_while_switch(pc, options::nl_switch_brace());
      }
      else if (pc->Is(CT_SYNCHRONIZED))
      {
         log_rule_B("nl_synchronized_brace");
         newlines_if_for_while_switch(pc, options::nl_synchronized_brace());
      }
      else if (pc->Is(CT_DO))
      {
         log_rule_B("nl_do_brace");
         newlines_do_else(pc, options::nl_do_brace());
      }
      else if (pc->Is(CT_ELSE))
      {
         log_rule_B("nl_brace_else");
         newlines_cuddle_uncuddle(pc, options::nl_brace_else());
         Chunk *next = pc->GetNextNcNnl();

         if (next->Is(CT_ELSEIF))
         {
            log_rule_B("nl_else_if");
            newline_iarf_pair(pc, next, options::nl_else_if());
         }
         log_rule_B("nl_else_brace");
         newlines_do_else(pc, options::nl_else_brace());
      }
      else if (pc->Is(CT_TRY))
      {
         log_rule_B("nl_try_brace");
         newlines_do_else(pc, options::nl_try_brace());
         // Issue #1734
         Chunk *po = pc->GetNextNcNnl();
         flag_parens(po, PCF_IN_TRY_BLOCK, po->GetType(), CT_NONE, false);
      }
      else if (pc->Is(CT_GETSET))
      {
         log_rule_B("nl_getset_brace");
         newlines_do_else(pc, options::nl_getset_brace());
      }
      else if (pc->Is(CT_FINALLY))
      {
         log_rule_B("nl_brace_finally");
         newlines_cuddle_uncuddle(pc, options::nl_brace_finally());
         log_rule_B("nl_finally_brace");
         newlines_do_else(pc, options::nl_finally_brace());
      }
      else if (pc->Is(CT_WHILE_OF_DO))
      {
         log_rule_B("nl_brace_while");
         newlines_cuddle_uncuddle(pc, options::nl_brace_while());
      }
      else if (pc->Is(CT_BRACE_OPEN))
      {
         switch (pc->GetParentType())
         {
         case CT_DOUBLE_BRACE:
         {
            log_rule_B("nl_paren_dbrace_open");

            if (options::nl_paren_dbrace_open() != IARF_IGNORE)
            {
               Chunk *prev = pc->GetPrevNcNnlNi(E_Scope::PREPROC);    // Issue #2279

               if (prev->IsParenClose())
               {
                  log_rule_B("nl_paren_dbrace_open");
                  newline_iarf_pair(prev, pc, options::nl_paren_dbrace_open());
               }
            }
            break;
         }

         case CT_ENUM:
         {
            log_rule_B("nl_enum_own_lines");

            if (options::nl_enum_own_lines() != IARF_IGNORE)
            {
               newlines_enum_entries(pc, options::nl_enum_own_lines());
            }
            log_rule_B("nl_ds_struct_enum_cmt");

            if (options::nl_ds_struct_enum_cmt())
            {
               newlines_double_space_struct_enum_union(pc);
            }
            break;
         }

         case CT_STRUCT:
         case CT_UNION:
         {
            log_rule_B("nl_ds_struct_enum_cmt");

            if (options::nl_ds_struct_enum_cmt())
            {
               newlines_double_space_struct_enum_union(pc);
            }
            break;
         }

         case CT_CLASS:
         {
            if (pc->GetLevel() == pc->GetBraceLevel())
            {
               log_rule_B("nl_class_brace");
               log_ruleNL("nl_class_brace", pc->GetPrevNnl());
               newlines_do_else(pc->GetPrevNnl(), options::nl_class_brace());
            }
            break;
         }

         case CT_OC_CLASS:
         {
            if (pc->GetLevel() == pc->GetBraceLevel())
            {
               // Request #126
               // introduce two new options
               // look back if we have a @interface or a @implementation
               for (Chunk *tmp = pc->GetPrev(); tmp->IsNotNullChunk(); tmp = tmp->GetPrev())
               {
                  LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, Text() is '%s'\n",
                          __func__, __LINE__, tmp->GetOrigLine(), tmp->GetOrigCol(), tmp->Text());

                  if (  tmp->Is(CT_OC_INTF)
                     || tmp->Is(CT_OC_IMPL))
                  {
                     LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, may be remove/force newline before {\n",
                             __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());

                     if (tmp->Is(CT_OC_INTF))
                     {
                        log_rule_B("nl_oc_interface_brace");
                        newlines_do_else(pc->GetPrevNnl(), options::nl_oc_interface_brace());
                     }
                     else
                     {
                        log_rule_B("nl_oc_implementation_brace");
                        newlines_do_else(pc->GetPrevNnl(), options::nl_oc_implementation_brace());
                     }
                     break;
                  }
               }
            }
            break;
         }

         case CT_BRACED_INIT_LIST:
         {
            // Issue #1052
            log_rule_B("nl_create_list_one_liner");

            if (options::nl_create_list_one_liner())
            {
               nl_create_list_liner(pc);
               break;
            }
            Chunk *prev = pc->GetPrevNnl();

            if (  prev->IsNotNullChunk()
               && (  prev->GetType() == CT_TYPE
                  || prev->GetType() == CT_WORD
                  || prev->GetType() == CT_ASSIGN                      // Issue #2957
                  || prev->GetParentType() == CT_TEMPLATE
                  || prev->GetParentType() == CT_DECLTYPE))
            {
               log_rule_B("nl_type_brace_init_lst");
               newline_iarf_pair(prev, pc, options::nl_type_brace_init_lst(), true);
            }
            break;
         }

         case CT_OC_BLOCK_EXPR:
         {
            // issue # 477
            log_rule_B("nl_oc_block_brace");
            newline_iarf_pair(pc->GetPrev(), pc, options::nl_oc_block_brace());
            break;
         }

         case CT_FUNC_CLASS_DEF:                             // Issue #2343
         {
            if (!one_liner_nl_ok(pc))
            {
               LOG_FMT(LNL1LINE, "a new line may NOT be added\n");
               // no change - preserve one liner body
            }
            else
            {
               log_rule_B("nl_before_opening_brace_func_class_def");

               if (options::nl_before_opening_brace_func_class_def() != IARF_IGNORE)
               {
                  newline_iarf_pair(pc->GetPrev(), pc, options::nl_before_opening_brace_func_class_def());
               }
            }
         }

         default:
         {
            break;
         }
         } // switch

         log_rule_B("nl_brace_brace");

         if (options::nl_brace_brace() != IARF_IGNORE)
         {
            Chunk *next = pc->GetNextNc(E_Scope::PREPROC);

            if (next->Is(CT_BRACE_OPEN))
            {
               newline_iarf_pair(pc, next, options::nl_brace_brace());
            }
         }
         Chunk *next = pc->GetNextNnl();

         if (next->IsNullChunk())
         {
            // do nothing
         }
         else if (next->Is(CT_BRACE_CLOSE))
         {
            // TODO: add an option to split open empty statements? { };
         }
         else if (next->Is(CT_BRACE_OPEN))
         {
            // already handled
         }
         else
         {
            next = pc->GetNextNcNnl();

            // Handle unnamed temporary direct-list-initialization
            if (pc->GetParentType() == CT_BRACED_INIT_LIST)
            {
               log_rule_B("nl_type_brace_init_lst_open");
               newline_iarf_pair(pc, pc->GetNextNnl(),
                                 options::nl_type_brace_init_lst_open(), true);
            }
            // Handle nl_after_brace_open
            else if (  (  pc->GetParentType() == CT_CPP_LAMBDA
                       || pc->GetLevel() == pc->GetBraceLevel())
                    && options::nl_after_brace_open())
            {
               log_rule_B("nl_after_brace_open");

               if (!one_liner_nl_ok(pc))
               {
                  LOG_FMT(LNL1LINE, "a new line may NOT be added (nl_after_brace_open)\n");
                  // no change - preserve one liner body
               }
               else if (  pc->TestFlags(PCF_IN_PREPROC)
                       || (  pc->TestFlags(PCF_ONE_LINER)
                          && pc->TestFlags(PCF_IN_ARRAY_ASSIGN)
                          && options::nl_assign_leave_one_liners()))
               {
                  // no change - don't break up preprocessors
               }
               else
               {
                  // Step back from next to the first non-newline item
                  Chunk *tmp = next->GetPrev();

                  while (tmp != pc)
                  {
                     if (tmp->IsComment())
                     {
                        log_rule_B("nl_after_brace_open_cmt");

                        if (  !options::nl_after_brace_open_cmt()
                           && tmp->IsNot(CT_COMMENT_MULTI))
                        {
                           break;
                        }
                     }
                     tmp = tmp->GetPrev();
                  }
                  // Add the newline
                  newline_iarf(tmp, IARF_ADD);
               }
            }
         }
         // braced-init-list is more like a function call with arguments,
         // than curly braces that determine a structure of a source code,
         // so, don't add a newline before a closing brace. Issue #1405.
         log_rule_B("nl_type_brace_init_lst_open");
         log_rule_B("nl_type_brace_init_lst_close");

         if (!(  pc->GetParentType() == CT_BRACED_INIT_LIST
              && options::nl_type_brace_init_lst_open() == IARF_IGNORE
              && options::nl_type_brace_init_lst_close() == IARF_IGNORE))
         {
            newlines_brace_pair(pc);
         }

         // Handle nl_before_brace_open
         if (  pc->Is(CT_BRACE_OPEN)
            && pc->GetLevel() == pc->GetBraceLevel()
            && options::nl_before_brace_open())
         {
            log_rule_B("nl_before_brace_open");

            if (!one_liner_nl_ok(pc))
            {
               LOG_FMT(LNL1LINE, "a new line may NOT be added (nl_before_brace_open)\n");
               // no change - preserve one liner body
            }
            else if (  pc->TestFlags(PCF_IN_PREPROC)
                    || pc->TestFlags(PCF_IN_ARRAY_ASSIGN))
            {
               // no change - don't break up array assignments or preprocessors
            }
            else
            {
               // Step back to previous non-newline item
               Chunk *tmp = pc->GetPrev();

               if (!tmp->Is(CT_NEWLINE))
               {
                  newline_iarf(tmp, IARF_ADD);
               }
            }
         }
      }
      else if (pc->Is(CT_BRACE_CLOSE))
      {
         // newline between a close brace and x
         log_rule_B("nl_brace_brace");

         if (options::nl_brace_brace() != IARF_IGNORE)
         {
            Chunk *next = pc->GetNextNc(E_Scope::PREPROC);

            if (next->Is(CT_BRACE_CLOSE))
            {
               log_rule_B("nl_brace_brace");
               newline_iarf_pair(pc, next, options::nl_brace_brace());
            }
         }
         log_rule_B("nl_brace_square");

         if (options::nl_brace_square() != IARF_IGNORE)
         {
            Chunk *next = pc->GetNextNc(E_Scope::PREPROC);

            if (next->Is(CT_SQUARE_CLOSE))
            {
               log_rule_B("nl_brace_square");
               newline_iarf_pair(pc, next, options::nl_brace_square());
            }
         }
         log_rule_B("nl_brace_fparen");

         if (options::nl_brace_fparen() != IARF_IGNORE)
         {
            Chunk *next = pc->GetNextNc(E_Scope::PREPROC);

            log_rule_B("nl_brace_fparen");

            if (  next->Is(CT_NEWLINE)
               && (options::nl_brace_fparen() == IARF_REMOVE))
            {
               next = next->GetNextNc(E_Scope::PREPROC);  // Issue #1000
            }

            if (next->Is(CT_FPAREN_CLOSE))
            {
               log_rule_B("nl_brace_fparen");
               newline_iarf_pair(pc, next, options::nl_brace_fparen());
            }
         }
         // newline before a close brace
         log_rule_B("nl_type_brace_init_lst_close");

         if (  pc->GetParentType() == CT_BRACED_INIT_LIST
            && options::nl_type_brace_init_lst_close() != IARF_IGNORE)
         {
            // Handle unnamed temporary direct-list-initialization
            newline_iarf_pair(pc->GetPrevNnl(), pc,
                              options::nl_type_brace_init_lst_close(), true);
         }
         // blanks before a close brace
         log_rule_B("eat_blanks_before_close_brace");

         if (options::eat_blanks_before_close_brace())
         {
            // Limit the newlines before the close brace to 1
            Chunk *prev = pc->GetPrev();

            if (prev->IsNewline())
            {
               log_rule_B("nl_inside_namespace");
               log_rule_B("nl_inside_empty_func");

               if (  options::nl_inside_empty_func() > 0
                  && pc->GetPrevNnl()->Is(CT_BRACE_OPEN)
                  && (  pc->GetParentType() == CT_FUNC_CLASS_DEF
                     || pc->GetParentType() == CT_FUNC_DEF))
               {
                  blank_line_set(prev, options::nl_inside_empty_func);
               }
               else if (  options::nl_inside_namespace() > 0
                       && pc->GetParentType() == CT_NAMESPACE)
               {
                  blank_line_set(prev, options::nl_inside_namespace);
               }
               else if (prev->GetNlCount() != 1)
               {
                  prev->SetNlCount(1);
                  LOG_FMT(LBLANKD, "%s(%d): eat_blanks_before_close_brace %zu\n",
                          __func__, __LINE__, prev->GetOrigLine());
                  MARK_CHANGE();
               }
            }
         }
         else if (  options::nl_ds_struct_enum_close_brace()
                 && (  pc->GetParentType() == CT_ENUM
                    || pc->GetParentType() == CT_STRUCT
                    || pc->GetParentType() == CT_UNION))
         {
            log_rule_B("nl_ds_struct_enum_close_brace");

            if (!pc->TestFlags(PCF_ONE_LINER))
            {
               // Make sure the brace is preceded by two newlines
               Chunk *prev = pc->GetPrev();

               if (!prev->IsNewline())
               {
                  prev = newline_add_before(pc);
               }

               if (prev->GetNlCount() < 2)
               {
                  double_newline(prev);
               }
            }
         }
         // Force a newline after a close brace
         log_rule_B("nl_brace_struct_var");

         if (  (options::nl_brace_struct_var() != IARF_IGNORE)
            && (  pc->GetParentType() == CT_STRUCT
               || pc->GetParentType() == CT_ENUM
               || pc->GetParentType() == CT_UNION))
         {
            Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC);

            if (  next->IsNot(CT_SEMICOLON)
               && next->IsNot(CT_COMMA))
            {
               log_rule_B("nl_brace_struct_var");
               newline_iarf(pc, options::nl_brace_struct_var());
            }
         }
         else if (  pc->GetParentType() != CT_OC_AT
                 && pc->GetParentType() != CT_BRACED_INIT_LIST
                 && (  options::nl_after_brace_close()
                    || pc->GetParentType() == CT_FUNC_CLASS_DEF
                    || pc->GetParentType() == CT_FUNC_DEF
                    || pc->GetParentType() == CT_OC_MSG_DECL))
         {
            log_rule_B("nl_after_brace_close");
            Chunk *next = pc->GetNext();

            if (  next->IsNot(CT_SEMICOLON)
               && next->IsNot(CT_COMMA)
               && next->IsNot(CT_SPAREN_CLOSE)    // Issue #664
               && next->IsNot(CT_SQUARE_CLOSE)
               && next->IsNot(CT_FPAREN_CLOSE)
               && next->IsNot(CT_PAREN_CLOSE)
               && next->IsNot(CT_WHILE_OF_DO)
               && next->IsNot(CT_VBRACE_CLOSE)        // Issue #666
               && (  next->IsNot(CT_BRACE_CLOSE)
                  || !next->TestFlags(PCF_ONE_LINER)) // #1258
               && !pc->TestFlags(PCF_IN_ARRAY_ASSIGN)
               && !pc->TestFlags(PCF_IN_TYPEDEF)
               && !next->IsCommentOrNewline()
               && next->IsNotNullChunk())
            {
               // #1258
               // dont add newline between two consecutive braces closes, if the second is a part of one liner.
               newline_end_newline(pc);
            }
         }
         else if (pc->GetParentType() == CT_NAMESPACE)
         {
            log_rule_B("nl_after_namespace");

            if (options::nl_after_namespace() > 0)
            {
               Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC);

               if (next->IsNotNullChunk())
               {
                  newline_add_before(next);
                  // newline_iarf(next, IARF_ADD);
               }
            }
         }
      }
      else if (pc->Is(CT_VBRACE_OPEN))
      {
         log_rule_B("nl_after_vbrace_open");
         log_rule_B("nl_after_vbrace_open_empty");

         if (  options::nl_after_vbrace_open()
            || options::nl_after_vbrace_open_empty())
         {
            Chunk *next = pc->GetNext(E_Scope::PREPROC);
            bool  add_it;

            if (next->IsSemicolon())
            {
               log_rule_B("nl_after_vbrace_open_empty");
               add_it = options::nl_after_vbrace_open_empty();
            }
            else
            {
               log_rule_B("nl_after_vbrace_open");
               add_it = (  options::nl_after_vbrace_open()
                        && next->IsNot(CT_VBRACE_CLOSE)
                        && !next->IsCommentOrNewline());
            }

            if (add_it)
            {
               newline_iarf(pc, IARF_ADD);
            }
         }
         log_rule_B("nl_create_if_one_liner");
         log_rule_B("nl_create_for_one_liner");
         log_rule_B("nl_create_while_one_liner");

         if (  (  (  pc->GetParentType() == CT_IF
                  || pc->GetParentType() == CT_ELSEIF
                  || pc->GetParentType() == CT_ELSE)
               && options::nl_create_if_one_liner())
            || (  pc->GetParentType() == CT_FOR
               && options::nl_create_for_one_liner())
            || (  pc->GetParentType() == CT_WHILE
               && options::nl_create_while_one_liner()))
         {
            nl_create_one_liner(pc);
         }
         log_rule_B("nl_split_if_one_liner");
         log_rule_B("nl_split_for_one_liner");
         log_rule_B("nl_split_while_one_liner");

         if (  (  (  pc->GetParentType() == CT_IF
                  || pc->GetParentType() == CT_ELSEIF
                  || pc->GetParentType() == CT_ELSE)
               && options::nl_split_if_one_liner())
            || (  pc->GetParentType() == CT_FOR
               && options::nl_split_for_one_liner())
            || (  pc->GetParentType() == CT_WHILE
               && options::nl_split_while_one_liner()))
         {
            if (pc->TestFlags(PCF_ONE_LINER))
            {
               // split one-liner
               Chunk *end = pc->GetNext()->GetNextType(CT_SEMICOLON)->GetNext();
               // Scan for clear flag
               LOG_FMT(LNEWLINE, "(%d) ", __LINE__);
               LOG_FMT(LNEWLINE, "\n");

               for (Chunk *temp = pc; temp != end; temp = temp->GetNext())
               {
                  LOG_FMT(LNEWLINE, "%s(%d): Text() is '%s', type is %s, level is %zu\n",
                          __func__, __LINE__, temp->Text(), get_token_name(temp->GetType()), temp->GetLevel());
                  // produces much more log output. Use it only debugging purpose
                  //log_pcf_flags(LNEWLINE, temp->GetFlags());
                  temp->ResetFlagBits(PCF_ONE_LINER);
               }

               // split
               newline_add_between(pc, pc->GetNext());
            }
         }
      }
      else if (pc->Is(CT_VBRACE_CLOSE))
      {
         log_rule_B("nl_after_vbrace_close");

         if (options::nl_after_vbrace_close())
         {
            if (!pc->GetNextNc()->IsNewline())
            {
               newline_iarf(pc, IARF_ADD);
            }
         }
      }
      else if (  pc->Is(CT_SQUARE_OPEN)
              && pc->GetParentType() == CT_OC_MSG)
      {
         log_rule_B("nl_oc_msg_args");

         if (options::nl_oc_msg_args())
         {
            newline_oc_msg(pc);
         }
      }
      else if (pc->Is(CT_STRUCT))
      {
         log_rule_B("nl_struct_brace");
         newlines_struct_union(pc, options::nl_struct_brace(), true);
      }
      else if (pc->Is(CT_UNION))
      {
         log_rule_B("nl_union_brace");
         newlines_struct_union(pc, options::nl_union_brace(), true);
      }
      else if (pc->Is(CT_ENUM))
      {
         newlines_enum(pc);
      }
      else if (pc->Is(CT_CASE))
      {
         // Note: 'default' also maps to CT_CASE
         log_rule_B("nl_before_case");

         if (options::nl_before_case())
         {
            newline_case(pc);
         }
      }
      else if (pc->Is(CT_THROW))
      {
         Chunk *prev = pc->GetPrev();

         if (  prev->Is(CT_PAREN_CLOSE)
            || prev->Is(CT_FPAREN_CLOSE))         // Issue #1122
         {
            log_rule_B("nl_before_throw");
            newline_iarf(pc->GetPrevNcNnlNi(), options::nl_before_throw());   // Issue #2279
         }
      }
      else if (  pc->Is(CT_QUALIFIER)
              && !strcmp(pc->Text(), "throws"))
      {
         Chunk *prev = pc->GetPrev();

         if (  prev->Is(CT_PAREN_CLOSE)
            || prev->Is(CT_FPAREN_CLOSE))         // Issue #1122
         {
            log_rule_B("nl_before_throw");
            newline_iarf(pc->GetPrevNcNnlNi(), options::nl_before_throw());   // Issue #2279
         }
      }
      else if (pc->Is(CT_CASE_COLON))
      {
         Chunk *next = pc->GetNextNnl();

         log_rule_B("nl_case_colon_brace");

         if (  next->Is(CT_BRACE_OPEN)
            && options::nl_case_colon_brace() != IARF_IGNORE)
         {
            newline_iarf(pc, options::nl_case_colon_brace());
         }
         else if (options::nl_after_case())
         {
            log_rule_B("nl_after_case");
            newline_case_colon(pc);
         }
      }
      else if (pc->Is(CT_SPAREN_CLOSE))
      {
         Chunk *next = pc->GetNextNcNnl();

         if (next->Is(CT_BRACE_OPEN))
         {
            /*
             * TODO: this could be used to control newlines between the
             * the if/while/for/switch close parenthesis and the open brace, but
             * that is currently handled elsewhere.
             */
         }
      }
      else if (pc->Is(CT_RETURN))
      {
         log_rule_B("nl_before_return");

         if (options::nl_before_return())
         {
            newline_before_return(pc);
         }
         log_rule_B("nl_after_return");

         if (options::nl_after_return())
         {
            newline_after_return(pc);
         }
      }
      else if (pc->Is(CT_SEMICOLON))
      {
         log_rule_B("nl_after_semicolon");
         log_rule_NL("nl_after_semicolon");

         if (  !pc->TestFlags(PCF_IN_SPAREN)
            && !pc->TestFlags(PCF_IN_PREPROC)
            && options::nl_after_semicolon())
         {
            Chunk *next = pc->GetNext();

            while (next->Is(CT_VBRACE_CLOSE))
            {
               next = next->GetNext();
            }

            if (  next->IsNotNullChunk()
               && !next->IsCommentOrNewline())
            {
               if (one_liner_nl_ok(next))
               {
                  LOG_FMT(LNL1LINE, "%s(%d): a new line may be added\n", __func__, __LINE__);
                  newline_iarf(pc, IARF_ADD);
               }
               else
               {
                  LOG_FMT(LNL1LINE, "%s(%d): a new line may NOT be added\n", __func__, __LINE__);
               }
            }
         }
         else if (pc->GetParentType() == CT_CLASS)
         {
            log_rule_B("nl_after_class");

            if (options::nl_after_class() > 0)
            {
               /*
                * If there is already a "class" comment, then don't add a newline if
                * one exists after the comment. or else this will interfere with the
                * mod_add_long_class_closebrace_comment option.
                */
               iarf_e mode  = IARF_ADD;
               Chunk  *next = pc->GetNext();

               if (next->IsComment())
               {
                  pc   = next;
                  next = pc->GetNext();

                  if (next->IsNewline())
                  {
                     mode = IARF_IGNORE;
                  }
               }
               newline_iarf(pc, mode);
            }
         }
      }
      else if (pc->Is(CT_FPAREN_OPEN))
      {
         log_rule_B("nl_func_decl_start");
         log_rule_B("nl_func_def_start");
         log_rule_B("nl_func_decl_start_single");
         log_rule_B("nl_func_def_start_single");
         log_rule_B("nl_func_decl_start_multi_line");
         log_rule_B("nl_func_def_start_multi_line");
         log_rule_B("nl_func_decl_args");
         log_rule_B("nl_func_def_args");
         log_rule_B("nl_func_decl_args_multi_line");
         log_rule_B("nl_func_def_args_multi_line");
         log_rule_B("nl_func_decl_end");
         log_rule_B("nl_func_def_end");
         log_rule_B("nl_func_decl_end_single");
         log_rule_B("nl_func_def_end_single");
         log_rule_B("nl_func_decl_end_multi_line");
         log_rule_B("nl_func_def_end_multi_line");
         log_rule_B("nl_func_decl_empty");
         log_rule_B("nl_func_def_empty");
         log_rule_B("nl_func_type_name");
         log_rule_B("nl_func_type_name_class");
         log_rule_B("nl_func_class_scope");
         log_rule_B("nl_func_scope_name");
         log_rule_B("nl_func_proto_type_name");
         log_rule_B("nl_func_paren");
         log_rule_B("nl_func_def_paren");
         log_rule_B("nl_func_def_paren_empty");
         log_rule_B("nl_func_paren_empty");

         if (  (  pc->GetParentType() == CT_FUNC_DEF
               || pc->GetParentType() == CT_FUNC_PROTO
               || pc->GetParentType() == CT_FUNC_CLASS_DEF
               || pc->GetParentType() == CT_FUNC_CLASS_PROTO
               || pc->GetParentType() == CT_OPERATOR)
            && (  options::nl_func_decl_start() != IARF_IGNORE
               || options::nl_func_def_start() != IARF_IGNORE
               || options::nl_func_decl_start_single() != IARF_IGNORE
               || options::nl_func_def_start_single() != IARF_IGNORE
               || options::nl_func_decl_start_multi_line()
               || options::nl_func_def_start_multi_line()
               || options::nl_func_decl_args() != IARF_IGNORE
               || options::nl_func_def_args() != IARF_IGNORE
               || options::nl_func_decl_args_multi_line()
               || options::nl_func_def_args_multi_line()
               || options::nl_func_decl_end() != IARF_IGNORE
               || options::nl_func_def_end() != IARF_IGNORE
               || options::nl_func_decl_end_single() != IARF_IGNORE
               || options::nl_func_def_end_single() != IARF_IGNORE
               || options::nl_func_decl_end_multi_line()
               || options::nl_func_def_end_multi_line()
               || options::nl_func_decl_empty() != IARF_IGNORE
               || options::nl_func_def_empty() != IARF_IGNORE
               || options::nl_func_type_name() != IARF_IGNORE
               || options::nl_func_type_name_class() != IARF_IGNORE
               || options::nl_func_class_scope() != IARF_IGNORE
               || options::nl_func_scope_name() != IARF_IGNORE
               || options::nl_func_proto_type_name() != IARF_IGNORE
               || options::nl_func_paren() != IARF_IGNORE
               || options::nl_func_def_paren() != IARF_IGNORE
               || options::nl_func_def_paren_empty() != IARF_IGNORE
               || options::nl_func_paren_empty() != IARF_IGNORE))
         {
            newline_func_def_or_call(pc);
         }
         else if (  (  pc->GetParentType() == CT_FUNC_CALL
                    || pc->GetParentType() == CT_FUNC_CALL_USER)
                 && (  (options::nl_func_call_start_multi_line())
                    || (options::nl_func_call_args_multi_line())
                    || (options::nl_func_call_end_multi_line())
                    || (options::nl_func_call_start() != IARF_IGNORE)        // Issue #2020
                    || (options::nl_func_call_args() != IARF_IGNORE)         // Issue #2604
                    || (options::nl_func_call_paren() != IARF_IGNORE)
                    || (options::nl_func_call_paren_empty() != IARF_IGNORE)
                    || (options::nl_func_call_empty() != IARF_IGNORE)))
         {
            log_rule_B("nl_func_call_start_multi_line");
            log_rule_B("nl_func_call_args_multi_line");
            log_rule_B("nl_func_call_end_multi_line");
            log_rule_B("nl_func_call_start");
            log_rule_B("nl_func_call_args");
            log_rule_B("nl_func_call_paren");
            log_rule_B("nl_func_call_paren_empty");
            log_rule_B("nl_func_call_empty");

            if (options::nl_func_call_start() != IARF_IGNORE)
            {
               newline_iarf(pc, options::nl_func_call_start());
            }
            // note that newline_func_def_or_call() calls newline_func_multi_line()
            newline_func_def_or_call(pc);
         }
         else if (  first
                 && (options::nl_remove_extra_newlines() == 1))
         {
            log_rule_B("nl_remove_extra_newlines");
            newline_iarf(pc, IARF_REMOVE);
         }
      }
      else if (pc->Is(CT_FPAREN_CLOSE))                          // Issue #2758
      {
         if (  (  pc->GetParentType() == CT_FUNC_CALL
               || pc->GetParentType() == CT_FUNC_CALL_USER)
            && options::nl_func_call_end() != IARF_IGNORE)
         {
            log_rule_B("nl_func_call_end");
            newline_iarf(pc->GetPrev(), options::nl_func_call_end());
         }
      }
      else if (pc->Is(CT_ANGLE_CLOSE))
      {
         if (pc->GetParentType() == CT_TEMPLATE)
         {
            Chunk *next = pc->GetNextNcNnl();

            if (  next->IsNotNullChunk()
               && next->GetLevel() == next->GetBraceLevel())
            {
               Chunk *tmp = pc->GetPrevType(CT_ANGLE_OPEN, pc->GetLevel())->GetPrevNcNnlNi();   // Issue #2279

               if (tmp->Is(CT_TEMPLATE))
               {
                  if (next->Is(CT_USING))
                  {
                     newline_iarf(pc, options::nl_template_using());
                     log_rule_B("nl_template_using");
                  }
                  else if (next->GetParentType() == CT_FUNC_DEF) // function definition
                  {
                     iarf_e const action =
                        newline_template_option(
                           pc,
                           options::nl_template_func_def_special(),
                           options::nl_template_func_def(),
                           options::nl_template_func());
                     log_rule_B("nl_template_func_def_special");
                     log_rule_B("nl_template_func_def");
                     log_rule_B("nl_template_func");
                     newline_iarf(pc, action);
                  }
                  else if (next->GetParentType() == CT_FUNC_PROTO) // function declaration
                  {
                     iarf_e const action =
                        newline_template_option(
                           pc,
                           options::nl_template_func_decl_special(),
                           options::nl_template_func_decl(),
                           options::nl_template_func());
                     log_rule_B("nl_template_func_decl_special");
                     log_rule_B("nl_template_func_decl");
                     log_rule_B("nl_template_func");
                     newline_iarf(pc, action);
                  }
                  else if (  next->Is(CT_TYPE)
                          || next->Is(CT_QUALIFIER)) // variable
                  {
                     newline_iarf(pc, options::nl_template_var());
                     log_rule_B("nl_template_var");
                  }
                  else if (next->TestFlags(PCF_INCOMPLETE)) // class declaration
                  {
                     iarf_e const action =
                        newline_template_option(
                           pc,
                           options::nl_template_class_decl_special(),
                           options::nl_template_class_decl(),
                           options::nl_template_class());
                     log_rule_B("nl_template_class_decl_special");
                     log_rule_B("nl_template_class_decl");
                     log_rule_B("nl_template_class");
                     newline_iarf(pc, action);
                  }
                  else // class definition
                  {
                     iarf_e const action =
                        newline_template_option(
                           pc,
                           options::nl_template_class_def_special(),
                           options::nl_template_class_def(),
                           options::nl_template_class());
                     log_rule_B("nl_template_class_def_special");
                     log_rule_B("nl_template_class_def");
                     log_rule_B("nl_template_class");
                     newline_iarf(pc, action);
                  }
               }
            }
         }
      }
      else if (  pc->Is(CT_NAMESPACE)
              && pc->GetParentType() != CT_USING)
      {
         // Issue #2387
         Chunk *next = pc->GetNextNcNnl();

         if (next->IsNotNullChunk())
         {
            next = next->GetNextNcNnl();

            if (!next->Is(CT_ASSIGN))
            {
               // Issue #1235
               // Issue #2186
               Chunk *braceOpen = pc->GetNextType(CT_BRACE_OPEN, pc->GetLevel());

               if (braceOpen->IsNullChunk())
               {
                  // fatal error
                  LOG_FMT(LERR, "%s(%d): Missing BRACE_OPEN after namespace\n   orig line is %zu, orig col is %zu\n",
                          __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());
                  exit(EXIT_FAILURE);
               }
               LOG_FMT(LNEWLINE, "%s(%d): braceOpen orig line is %zu, orig col is %zu, Text() is '%s'\n",
                       __func__, __LINE__, braceOpen->GetOrigLine(), braceOpen->GetOrigCol(), braceOpen->Text());
               // produces much more log output. Use it only debugging purpose
               //log_pcf_flags(LNEWLINE, braceOpen->GetFlags());
               newlines_namespace(pc);
            }
         }
      }
      else if (pc->Is(CT_SQUARE_OPEN))
      {
         if (  pc->GetParentType() == CT_ASSIGN
            && !pc->TestFlags(PCF_ONE_LINER))
         {
            Chunk *tmp = pc->GetPrevNcNnlNi();   // Issue #2279
            newline_iarf(tmp, options::nl_assign_square());
            log_rule_B("nl_assign_square");

            iarf_e arg = options::nl_after_square_assign();
            log_rule_B("nl_after_square_assign");

            if (options::nl_assign_square() & IARF_ADD)
            {
               log_rule_B("nl_assign_square");
               arg = IARF_ADD;
            }
            newline_iarf(pc, arg);

            /*
             * if there is a newline after the open, then force a newline
             * before the close
             */
            tmp = pc->GetNextNc();

            if (tmp->IsNewline())
            {
               tmp = pc->GetNextType(CT_SQUARE_CLOSE, pc->GetLevel());

               if (tmp->IsNotNullChunk())
               {
                  newline_add_before(tmp);
               }
            }
         }
      }
      else if (pc->Is(CT_ACCESS))
      {
         // Make sure there is a newline before an access spec
         if (options::nl_before_access_spec() > 0)
         {
            log_rule_B("nl_before_access_spec");
            Chunk *prev = pc->GetPrev();

            if (!prev->IsNewline())
            {
               newline_add_before(pc);
            }
         }
      }
      else if (pc->Is(CT_ACCESS_COLON))
      {
         // Make sure there is a newline after an access spec
         if (options::nl_after_access_spec() > 0)
         {
            log_rule_B("nl_after_access_spec");
            Chunk *next = pc->GetNext();

            if (!next->IsNewline())
            {
               newline_add_before(next);
            }
         }
      }
      else if (pc->Is(CT_PP_DEFINE))
      {
         if (options::nl_multi_line_define())
         {
            log_rule_B("nl_multi_line_define");
            nl_handle_define(pc);
         }
      }
      else if (  first
              && (options::nl_remove_extra_newlines() == 1)
              && !pc->TestFlags(PCF_IN_PREPROC))
      {
         log_rule_B("nl_remove_extra_newlines");
         log_rule_NL("nl_remove_extra_newlines");
         newline_iarf(pc, IARF_REMOVE);
      }
      else if (  pc->Is(CT_MEMBER)
              && (  language_is_set(LANG_JAVA)
                 || language_is_set(LANG_CPP)))                 // Issue #2574
      {
         // Issue #1124
         if (pc->GetParentType() != CT_FUNC_DEF)
         {
            newline_iarf(pc->GetPrevNnl(), options::nl_before_member());
            log_rule_B("nl_before_member");
            newline_iarf(pc, options::nl_after_member());
            log_rule_B("nl_after_member");
         }
      }
      else
      {
         // ignore it
      }
   }

   newline_var_def_blk(Chunk::GetHead());
} // newlines_cleanup_braces


static void nl_handle_define(Chunk *pc)
{
   LOG_FUNC_ENTRY();

   Chunk *nl  = pc;
   Chunk *ref = Chunk::NullChunkPtr;

   while ((nl = nl->GetNext())->IsNotNullChunk())
   {
      if (nl->Is(CT_NEWLINE))
      {
         return;
      }

      if (  nl->Is(CT_MACRO)
         || (  nl->Is(CT_FPAREN_CLOSE)
            && nl->GetParentType() == CT_MACRO_FUNC))
      {
         ref = nl;
      }

      if (nl->Is(CT_NL_CONT))
      {
         if (ref->IsNotNullChunk())
         {
            newline_add_after(ref);
         }
         return;
      }
   }
} // nl_handle_define


void newline_after_multiline_comment()
{
   LOG_FUNC_ENTRY();

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNext())
   {
      if (pc->IsNot(CT_COMMENT_MULTI))
      {
         continue;
      }
      Chunk *tmp = pc;

      while (  ((tmp = tmp->GetNext())->IsNotNullChunk())
            && !tmp->IsNewline())
      {
         if (!tmp->IsComment())
         {
            newline_add_before(tmp);
            break;
         }
      }
   }
} // newline_after_multiline_comment


void newline_after_label_colon()
{
   LOG_FUNC_ENTRY();

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNext())
   {
      if (pc->IsNot(CT_LABEL_COLON))
      {
         continue;
      }
      newline_add_after(pc);
   }
} // newline_after_label_colon


static bool is_class_one_liner(Chunk *pc)
{
   if (  (  pc->Is(CT_FUNC_CLASS_DEF)
         || pc->Is(CT_FUNC_DEF))
      && pc->TestFlags(PCF_IN_CLASS))
   {
      // Find opening brace
      pc = pc->GetNextType(CT_BRACE_OPEN, pc->GetLevel());
      return(  pc->IsNotNullChunk()
            && pc->TestFlags(PCF_ONE_LINER));
   }
   return(false);
} // is_class_one_liner


void newlines_insert_blank_lines()
{
   LOG_FUNC_ENTRY();

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      //LOG_FMT(LNEWLINE, "%s(%d): orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
      //        __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
      if (pc->Is(CT_IF))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_before_if());
         log_rule_B("nl_before_if");
         newlines_if_for_while_switch_post_blank_lines(pc, options::nl_after_if());
         log_rule_B("nl_after_if");
      }
      else if (pc->Is(CT_FOR))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_before_for());
         log_rule_B("nl_before_for");
         newlines_if_for_while_switch_post_blank_lines(pc, options::nl_after_for());
         log_rule_B("nl_after_for");
      }
      else if (pc->Is(CT_WHILE))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_before_while());
         log_rule_B("nl_before_while");
         newlines_if_for_while_switch_post_blank_lines(pc, options::nl_after_while());
         log_rule_B("nl_after_while");
      }
      else if (pc->Is(CT_SWITCH))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_before_switch());
         log_rule_B("nl_before_switch");
         newlines_if_for_while_switch_post_blank_lines(pc, options::nl_after_switch());
         log_rule_B("nl_after_switch");
      }
      else if (pc->Is(CT_SYNCHRONIZED))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_before_synchronized());
         log_rule_B("nl_before_synchronized");
         newlines_if_for_while_switch_post_blank_lines(pc, options::nl_after_synchronized());
         log_rule_B("nl_after_synchronized");
      }
      else if (pc->Is(CT_DO))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_before_do());
         log_rule_B("nl_before_do");
         newlines_if_for_while_switch_post_blank_lines(pc, options::nl_after_do());
         log_rule_B("nl_after_do");
      }
      else if (pc->Is(CT_OC_INTF))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_oc_before_interface());
         log_rule_B("nl_oc_before_interface");
      }
      else if (pc->Is(CT_OC_END))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_oc_before_end());
         log_rule_B("nl_oc_before_end");
      }
      else if (pc->Is(CT_OC_IMPL))
      {
         newlines_if_for_while_switch_pre_blank_lines(pc, options::nl_oc_before_implementation());
         log_rule_B("nl_oc_before_implementation");
      }
      else if (  pc->Is(CT_FUNC_CLASS_DEF)
              || pc->Is(CT_FUNC_DEF)
              || pc->Is(CT_FUNC_CLASS_PROTO)
              || pc->Is(CT_FUNC_PROTO))
      {
         if (  options::nl_class_leave_one_liner_groups()
            && is_class_one_liner(pc))
         {
            log_rule_B("nl_class_leave_one_liner_groups");
            newlines_func_pre_blank_lines(pc, CT_FUNC_PROTO);
         }
         else
         {
            newlines_func_pre_blank_lines(pc, pc->GetType());
         }
      }
      else
      {
         // ignore it
         //LOG_FMT(LNEWLINE, "%s(%d): ignore it\n", __func__, __LINE__);
      }
   }
} // newlines_insert_blank_lines


void newlines_functions_remove_extra_blank_lines()
{
   LOG_FUNC_ENTRY();

   const size_t nl_max_blank_in_func = options::nl_max_blank_in_func();

   log_rule_B("nl_max_blank_in_func");

   if (nl_max_blank_in_func == 0)
   {
      LOG_FMT(LNEWLINE, "%s(%d): nl_max_blank_in_func is zero\n", __func__, __LINE__);
      return;
   }

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNext())
   {
      LOG_FMT(LNEWLINE, "%s(%d): orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));

      if (  pc->IsNot(CT_BRACE_OPEN)
         || (  pc->GetParentType() != CT_FUNC_DEF
            && pc->GetParentType() != CT_CPP_LAMBDA))
      {
         continue;
      }
      const size_t startMoveLevel = pc->GetLevel();

      while (pc->IsNotNullChunk())
      {
         if (  pc->Is(CT_BRACE_CLOSE)
            && pc->GetLevel() == startMoveLevel)
         {
            break;
         }

         // delete newlines
         if (  !pc->Is(CT_COMMENT_MULTI)   // Issue #2195
            && pc->GetNlCount() > nl_max_blank_in_func)
         {
            LOG_FMT(LNEWLINE, "%s(%d): orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                    __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
            pc->SetNlCount(nl_max_blank_in_func);
            MARK_CHANGE();
            remove_next_newlines(pc);
         }
         else
         {
            pc = pc->GetNext();
         }
      }
   }
} // newlines_functions_remove_extra_blank_lines


void newlines_squeeze_ifdef()
{
   LOG_FUNC_ENTRY();

   Chunk *pc;

   for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      if (  pc->Is(CT_PREPROC)
         && (  pc->GetLevel() > 0
            || options::nl_squeeze_ifdef_top_level()))
      {
         log_rule_B("nl_squeeze_ifdef_top_level");
         Chunk *ppr = pc->GetNext();

         if (  ppr->Is(CT_PP_IF)
            || ppr->Is(CT_PP_ELSE)
            || ppr->Is(CT_PP_ENDIF))
         {
            Chunk *pnl = Chunk::NullChunkPtr;
            Chunk *nnl = ppr->GetNextNl();

            if (  ppr->Is(CT_PP_ELSE)
               || ppr->Is(CT_PP_ENDIF))
            {
               pnl = pc->GetPrevNl();
            }
            Chunk *tmp1;
            Chunk *tmp2;

            if (nnl->IsNotNullChunk())
            {
               if (pnl->IsNotNullChunk())
               {
                  if (pnl->GetNlCount() > 1)
                  {
                     pnl->SetNlCount(1);
                     MARK_CHANGE();

                     tmp1 = pnl->GetPrevNnl();
                     tmp2 = nnl->GetPrevNnl();

                     LOG_FMT(LNEWLINE, "%s(%d): moved from after line %zu to after %zu\n",
                             __func__, __LINE__, tmp1->GetOrigLine(), tmp2->GetOrigLine());
                  }
               }

               if (  ppr->Is(CT_PP_IF)
                  || ppr->Is(CT_PP_ELSE))
               {
                  if (nnl->GetNlCount() > 1)
                  {
                     tmp1 = nnl->GetPrevNnl();
                     LOG_FMT(LNEWLINE, "%s(%d): trimmed newlines after line %zu from %zu\n",
                             __func__, __LINE__, tmp1->GetOrigLine(), nnl->GetNlCount());
                     nnl->SetNlCount(1);
                     MARK_CHANGE();
                  }
               }
            }
         }
      }
   }
} // newlines_squeeze_ifdef


void newlines_squeeze_paren_close()
{
   LOG_FUNC_ENTRY();

   Chunk *pc;

   for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNext())
   {
      Chunk *next;
      Chunk *prev;

      if (pc->Is(CT_NEWLINE))
      {
         prev = pc->GetPrev();
      }
      else
      {
         prev = pc;
      }
      next = pc->GetNext();

      if (  next->IsNotNullChunk()
         && prev->IsNotNullChunk()
         && next->IsParenClose()
         && prev->IsParenClose())
      {
         Chunk *prev_op = prev->GetOpeningParen();
         Chunk *next_op = next->GetOpeningParen();
         bool  flag     = true;

         Chunk *tmp = prev;

         while (tmp->IsParenClose())
         {
            tmp = tmp->GetPrev();
         }

         if (tmp->IsNot(CT_NEWLINE))
         {
            flag = false;
         }

         if (flag)
         {
            if (next_op->IsOnSameLine(prev_op))
            {
               if (pc->Is(CT_NEWLINE))
               {
                  pc = next;
               }
               newline_del_between(prev, next);
            }
            else
            {
               newline_add_between(prev, next);
            }
         }
      }
   }
} // newlines_squeeze_paren_close


void newlines_eat_start_end()
{
   LOG_FUNC_ENTRY();

   Chunk *pc;

   // Process newlines at the start of the file
   if (  cpd.frag_cols == 0
      && (  (options::nl_start_of_file() & IARF_REMOVE)
         || (  (options::nl_start_of_file() & IARF_ADD)
            && (options::nl_start_of_file_min() > 0))))
   {
      log_rule_B("nl_start_of_file");
      log_rule_B("nl_start_of_file_min");
      pc = Chunk::GetHead();

      if (pc->IsNotNullChunk())
      {
         if (pc->Is(CT_NEWLINE))
         {
            if (options::nl_start_of_file() == IARF_REMOVE)
            {
               log_rule_B("nl_start_of_file");
               LOG_FMT(LBLANKD, "%s(%d): eat_blanks_start_of_file %zu\n",
                       __func__, __LINE__, pc->GetOrigLine());
               Chunk::Delete(pc);
               MARK_CHANGE();
            }
            else if (  options::nl_start_of_file() == IARF_FORCE
                    || (pc->GetNlCount() < options::nl_start_of_file_min()))
            {
               log_rule_B("nl_start_of_file");
               LOG_FMT(LBLANKD, "%s(%d): set_blanks_start_of_file %zu\n",
                       __func__, __LINE__, pc->GetOrigLine());
               pc->SetNlCount(options::nl_start_of_file_min());
               log_rule_B("nl_start_of_file_min");
               MARK_CHANGE();
            }
         }
         else if (  (options::nl_start_of_file() & IARF_ADD)
                 && (options::nl_start_of_file_min() > 0))
         {
            log_rule_B("nl_start_of_file");
            log_rule_B("nl_start_of_file_min");
            Chunk chunk;
            chunk.SetType(CT_NEWLINE);
            chunk.SetOrigLine(pc->GetOrigLine());
            chunk.SetOrigCol(pc->GetOrigCol());
            chunk.SetPpLevel(pc->GetPpLevel());
            chunk.SetNlCount(options::nl_start_of_file_min());
            log_rule_B("nl_start_of_file_min");
            chunk.CopyAndAddBefore(pc);
            LOG_FMT(LNEWLINE, "%s(%d): %zu:%zu add newline before '%s'\n",
                    __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text());
            MARK_CHANGE();
         }
      }
   }

   // Process newlines at the end of the file
   if (  cpd.frag_cols == 0
      && (  (options::nl_end_of_file() & IARF_REMOVE)
         || (  (options::nl_end_of_file() & IARF_ADD)
            && (options::nl_end_of_file_min() > 0))))
   {
      log_rule_B("nl_end_of_file");
      log_rule_B("nl_end_of_file_min");
      pc = Chunk::GetTail();

      if (pc->IsNotNullChunk())
      {
         if (pc->Is(CT_NEWLINE))
         {
            if (options::nl_end_of_file() == IARF_REMOVE)
            {
               log_rule_B("nl_end_of_file");
               LOG_FMT(LBLANKD, "%s(%d): eat_blanks_end_of_file %zu\n",
                       __func__, __LINE__, pc->GetOrigLine());
               Chunk::Delete(pc);
               MARK_CHANGE();
            }
            else if (  options::nl_end_of_file() == IARF_FORCE
                    || (pc->GetNlCount() < options::nl_end_of_file_min()))
            {
               log_rule_B("nl_end_of_file");
               log_rule_B("nl_end_of_file_min");

               if (pc->GetNlCount() != options::nl_end_of_file_min())
               {
                  log_rule_B("nl_end_of_file_min");
                  LOG_FMT(LBLANKD, "%s(%d): set_blanks_end_of_file %zu\n",
                          __func__, __LINE__, pc->GetOrigLine());
                  pc->SetNlCount(options::nl_end_of_file_min());
                  log_rule_B("nl_end_of_file_min");
                  MARK_CHANGE();
               }
            }
         }
         else if (  (options::nl_end_of_file() & IARF_ADD)
                 && (options::nl_end_of_file_min() > 0))
         {
            log_rule_B("nl_end_of_file");
            log_rule_B("nl_end_of_file_min");
            Chunk chunk;
            chunk.SetType(CT_NEWLINE);
            chunk.SetOrigLine(pc->GetOrigLine());
            chunk.SetOrigCol(pc->GetOrigCol());
            chunk.SetPpLevel(pc->GetPpLevel());
            chunk.SetNlCount(options::nl_end_of_file_min());
            log_rule_B("nl_end_of_file_min");
            chunk.CopyAndAddBefore(Chunk::NullChunkPtr);
            LOG_FMT(LNEWLINE, "%s(%d): %zu:%zu add newline after '%s'\n",
                    __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text());
            MARK_CHANGE();
         }
      }
   }
} // newlines_eat_start_end


void newlines_chunk_pos(E_Token chunk_type, token_pos_e mode)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LNEWLINE, "%s(%d): mode is %s\n",
           __func__, __LINE__, to_string(mode));

   if (  !(mode & (TP_JOIN | TP_LEAD | TP_TRAIL))
      && chunk_type != CT_COMMA)
   {
      return;
   }

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      char copy[1000];
      LOG_FMT(LNEWLINE, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s'\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->ElidedText(copy));
      // produces much more log output. Use it only debugging purpose
      //log_pcf_flags(LNEWLINE, pc->GetFlags());

      if (pc->Is(chunk_type))
      {
         token_pos_e mode_local;

         if (chunk_type == CT_COMMA)
         {
            LOG_FMT(LNEWLINE, "%s(%d): orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
                    __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
            // produces much more log output. Use it only debugging purpose
            //log_pcf_flags(LNEWLINE, pc->GetFlags());

            if (pc->TestFlags(PCF_IN_CONST_ARGS)) // Issue #2250
            {
               continue;
            }

            /*
             * for chunk_type == CT_COMMA
             * we get 'mode' from options::pos_comma()
             * BUT we must take care of options::pos_class_comma()
             * TODO and options::pos_constr_comma()
             */
            if (pc->TestFlags(PCF_IN_CLASS_BASE))
            {
               // change mode
               log_rule_B("pos_class_comma");
               mode_local = options::pos_class_comma();
            }
            else if (pc->TestFlags(PCF_IN_ENUM))
            {
               log_rule_B("pos_enum_comma");
               mode_local = options::pos_enum_comma();
            }
            else
            {
               mode_local = mode;
            }
            LOG_FMT(LNEWLINE, "%s(%d): mode_local is %s\n",
                    __func__, __LINE__, to_string(mode_local));
         }
         else
         {
            mode_local = mode;
         }
         Chunk *prev = pc->GetPrevNc();
         Chunk *next = pc->GetNextNc();

         LOG_FMT(LNEWLINE, "%s(%d): mode_local is %s\n",
                 __func__, __LINE__, to_string(mode_local));

         LOG_FMT(LNEWLINE, "%s(%d): prev orig line is %zu, orig col is %zu, Text() is '%s'\n",
                 __func__, __LINE__, prev->GetOrigLine(), prev->GetOrigCol(), prev->Text());
         LOG_FMT(LNEWLINE, "%s(%d): next orig line is %zu, orig col is %zu, Text() is '%s'\n",
                 __func__, __LINE__, next->GetOrigLine(), next->GetOrigCol(), next->Text());
         size_t nl_flag = ((prev->IsNewline() ? 1 : 0) |
                           (next->IsNewline() ? 2 : 0));
         LOG_FMT(LNEWLINE, "%s(%d): nl_flag is %zu\n",
                 __func__, __LINE__, nl_flag);

         if (mode_local & TP_JOIN)
         {
            if (nl_flag & 1)
            {
               // remove newline if not preceded by a comment
               Chunk *prev2 = prev->GetPrev();

               if (  prev2->IsNotNullChunk()
                  && !(prev2->IsComment()))
               {
                  remove_next_newlines(prev2);
               }
            }

            if (nl_flag & 2)
            {
               // remove newline if not followed by a comment or by '{'
               Chunk *next2 = next->GetNext();

               if (  next2->IsNotNullChunk()
                  && !next2->IsComment()
                  && !next2->Is(CT_BRACE_OPEN))
               {
                  remove_next_newlines(pc);
               }
            }
            continue;
         }

         if (  (  nl_flag == 0
               && !(mode_local & (TP_FORCE | TP_BREAK)))
            || (  nl_flag == 3
               && !(mode_local & TP_FORCE)))
         {
            // No newlines and not adding any or both and not forcing
            continue;
         }

         if (  (  (mode_local & TP_LEAD)
               && nl_flag == 1)
            || (  (mode_local & TP_TRAIL)
               && nl_flag == 2))
         {
            // Already a newline before (lead) or after (trail)
            continue;
         }

         // If there were no newlines, we need to add one
         if (nl_flag == 0)
         {
            if (mode_local & TP_LEAD)
            {
               newline_add_before(pc);
            }
            else
            {
               newline_add_after(pc);
            }
            continue;
         }

         // If there were both newlines, we need to remove one
         if (nl_flag == 3)
         {
            if (mode_local & TP_LEAD)
            {
               remove_next_newlines(pc);
            }
            else
            {
               remove_next_newlines(pc->GetPrevNcNnlNi());   // Issue #2279
            }
            continue;
         }

         // we need to move the newline
         if (mode_local & TP_LEAD)
         {
            Chunk *next2 = next->GetNext();

            if (  next2->Is(CT_PREPROC)
               || (  chunk_type == CT_ASSIGN
                  && next2->Is(CT_BRACE_OPEN)))
            {
               continue;
            }

            if (next->GetNlCount() == 1)
            {
               if (  prev != nullptr
                  && !prev->TestFlags(PCF_IN_PREPROC))
               {
                  // move the CT_BOOL to after the newline
                  pc->MoveAfter(next);
               }
            }
         }
         else
         {
            LOG_FMT(LNEWLINE, "%s(%d): prev orig line is %zu, orig col is %zu, Text() is '%s', new line count is %zu\n",
                    __func__, __LINE__, prev->GetOrigLine(), prev->GetOrigCol(), prev->Text(), prev->GetNlCount());

            if (prev->GetNlCount() == 1)
            {
               // Back up to the next non-comment item
               prev = prev->GetPrevNc();
               LOG_FMT(LNEWLINE, "%s(%d): prev orig line is %zu, orig col is %zu, Text() is '%s'\n",
                       __func__, __LINE__, prev->GetOrigLine(), prev->GetOrigCol(), prev->Text());

               if (  prev->IsNotNullChunk()
                  && !prev->IsNewline()
                  && !prev->TestFlags(PCF_IN_PREPROC)
                  && !prev->TestFlags(PCF_IN_OC_MSG))
               {
                  pc->MoveAfter(prev);
               }
            }
         }
      }
   }
} // newlines_chunk_pos


void newlines_class_colon_pos(E_Token tok)
{
   LOG_FUNC_ENTRY();

   token_pos_e tpc;
   token_pos_e pcc;
   iarf_e      anc;
   iarf_e      ncia;

   if (tok == CT_CLASS_COLON)
   {
      tpc = options::pos_class_colon();
      log_rule_B("pos_class_colon");
      anc = options::nl_class_colon();
      log_rule_B("nl_class_colon");
      ncia = options::nl_class_init_args();
      log_rule_B("nl_class_init_args");
      pcc = options::pos_class_comma();
      log_rule_B("pos_class_comma");
   }
   else // tok == CT_CONSTR_COLON
   {
      tpc = options::pos_constr_colon();
      log_rule_B("pos_constr_colon");
      anc = options::nl_constr_colon();
      log_rule_B("nl_constr_colon");
      ncia = options::nl_constr_init_args();
      log_rule_B("nl_constr_init_args");
      pcc = options::pos_constr_comma();
      log_rule_B("pos_constr_comma");
   }
   Chunk  *ccolon  = nullptr;
   size_t acv_span = options::align_constr_value_span();

   log_rule_B("align_constr_value_span");
   bool       with_acv = (acv_span > 0) && language_is_set(LANG_CPP);
   AlignStack constructorValue;    // ABC_Member(abc_value)

   if (with_acv)
   {
      int    acv_thresh = options::align_constr_value_thresh();
      log_rule_B("align_constr_value_thresh");
      size_t acv_gap = options::align_constr_value_gap();
      log_rule_B("align_constr_value_gap");
      constructorValue.Start(acv_span, acv_thresh);
      constructorValue.m_gap         = acv_gap;
      constructorValue.m_right_align = !options::align_on_tabstop();
      log_rule_B("align_on_tabstop");
   }

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
   {
      if (  ccolon == nullptr
         && pc->IsNot(tok))
      {
         continue;
      }
      Chunk *prev;
      Chunk *next;

      if (pc->Is(tok))
      {
         LOG_FMT(LBLANKD, "%s(%d): orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
         ccolon = pc;
         prev   = pc->GetPrevNc();
         next   = pc->GetNextNc();

         if (pc->Is(CT_CONSTR_COLON))
         {
            LOG_FMT(LBLANKD, "%s(%d): pc orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                    __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
            Chunk *paren_vor_value = pc->GetNextType(CT_FPAREN_OPEN, pc->GetLevel());

            if (  with_acv
               && paren_vor_value->IsNotNullChunk())
            {
               LOG_FMT(LBLANKD, "%s(%d): paren_vor_value orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                       __func__, __LINE__, paren_vor_value->GetOrigLine(), paren_vor_value->GetOrigCol(),
                       paren_vor_value->Text(), get_token_name(paren_vor_value->GetType()));
               constructorValue.NewLines(paren_vor_value->GetNlCount());
               constructorValue.Add(paren_vor_value);
            }
         }

         if (  !prev->IsNewline()
            && !next->IsNewline()
            && (anc & IARF_ADD))                   // nl_class_colon, nl_constr_colon: 1

         {
            newline_add_after(pc);
            prev = pc->GetPrevNc();
            next = pc->GetNextNc();
         }

         if (anc == IARF_REMOVE)                   // nl_class_colon, nl_constr_colon: 2
         {
            if (  prev->IsNewline()
               && prev->SafeToDeleteNl())
            {
               Chunk::Delete(prev);
               MARK_CHANGE();
               prev = pc->GetPrevNc();
            }

            if (  next->IsNewline()
               && next->SafeToDeleteNl())
            {
               Chunk::Delete(next);
               MARK_CHANGE();
               next = pc->GetNextNc();
            }
         }

         if (tpc & TP_TRAIL)                       // pos_class_colon, pos_constr_colon: 4
         {
            if (  prev->IsNewline()
               && prev->GetNlCount() == 1
               && prev->SafeToDeleteNl())
            {
               pc->Swap(prev);
            }
         }
         else if (tpc & TP_LEAD)                   // pos_class_colon, pos_constr_colon: 3
         {
            if (  next->IsNewline()
               && next->GetNlCount() == 1
               && next->SafeToDeleteNl())
            {
               pc->Swap(next);
            }
         }
      }
      else
      {
         // (pc->GetType() != tok)
         if (  pc->Is(CT_BRACE_OPEN)
            || pc->Is(CT_SEMICOLON))
         {
            ccolon = nullptr;

            if (with_acv)
            {
               constructorValue.End();
            }
            continue;
         }

         if (  pc->Is(CT_COMMA)
            && pc->GetLevel() == ccolon->GetLevel())
         {
            LOG_FMT(LBLANKD, "%s(%d): orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                    __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
            Chunk *paren_vor_value = pc->GetNextType(CT_FPAREN_OPEN, pc->GetLevel());

            if (  with_acv
               && paren_vor_value->IsNotNullChunk())
            {
               LOG_FMT(LBLANKD, "%s(%d): paren_vor_value orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                       __func__, __LINE__, paren_vor_value->GetOrigLine(), paren_vor_value->GetOrigCol(),
                       paren_vor_value->Text(), get_token_name(paren_vor_value->GetType()));
               constructorValue.NewLines(paren_vor_value->GetNlCount());
               constructorValue.Add(paren_vor_value);
            }

            if (ncia & IARF_ADD)                   // nl_class_init_args, nl_constr_init_args:
            {
               if (pcc & TP_TRAIL)                 // pos_class_comma, pos_constr_comma
               {
                  if (ncia == IARF_FORCE)          // nl_class_init_args, nl_constr_init_args: 5
                  {
                     Chunk *after = pc->GetNext();   // Issue #2759

                     if (after->IsNot(CT_COMMENT_CPP))
                     {
                        newline_force_after(pc);
                     }
                  }
                  else
                  {
                     // (ncia == IARF_ADD)         // nl_class_init_args, nl_constr_init_args: 8
                     newline_add_after(pc);
                  }
                  prev = pc->GetPrevNc();

                  if (  prev->IsNewline()
                     && prev->SafeToDeleteNl())
                  {
                     Chunk::Delete(prev);
                     MARK_CHANGE();
                  }
               }
               else if (pcc & TP_LEAD)             // pos_class_comma, pos_constr_comma
               {
                  if (ncia == IARF_FORCE)          // nl_class_init_args, nl_constr_init_args: 7
                  {
                     newline_force_before(pc);
                  }
                  else
                  {
                     // (ncia == IARF_ADD)         // nl_class_init_args, nl_constr_init_args: 9
                     newline_add_before(pc);
                  }
                  next = pc->GetNextNc();

                  if (  next->IsNewline()
                     && next->SafeToDeleteNl())
                  {
                     Chunk::Delete(next);
                     MARK_CHANGE();
                  }
               }
            }
            else if (ncia == IARF_REMOVE)          // nl_class_init_args, nl_constr_init_args: 6
            {
               next = pc->GetNext();

               if (  next->IsNewline()
                  && next->SafeToDeleteNl())
               {
                  // comma is after
                  Chunk::Delete(next);
                  MARK_CHANGE();
               }
               else
               {
                  prev = pc->GetPrev();

                  if (  prev->IsNewline()
                     && prev->SafeToDeleteNl())
                  {
                     // comma is before
                     Chunk::Delete(prev);
                     MARK_CHANGE();
                  }
               }
            }
         }
      }
   }
} // newlines_class_colon_pos


static void blank_line_max(Chunk *pc, Option<unsigned> &opt)
{
   LOG_FUNC_ENTRY();

   if (pc == nullptr)
   {
      return;
   }
   const auto optval = opt();

   if (  (optval > 0)
      && (pc->GetNlCount() > optval))
   {
      LOG_FMT(LBLANKD, "%s(%d): do_blank_lines: %s max line %zu\n",
              __func__, __LINE__, opt.name(), pc->GetOrigLine());
      pc->SetNlCount(optval);
      MARK_CHANGE();
   }
} // blank_line_max


iarf_e newline_template_option(Chunk *pc, iarf_e special, iarf_e base, iarf_e fallback)
{
   Chunk *const prev = pc->GetPrevNcNnl();

   if (  prev->Is(CT_ANGLE_OPEN)
      && special != IARF_IGNORE)
   {
      return(special);
   }
   else if (base != IARF_IGNORE)
   {
      return(base);
   }
   else
   {
      return(fallback);
   }
} // newline_template_option


bool is_func_proto_group(Chunk *pc, E_Token one_liner_type)
{
   if (  pc != nullptr
      && options::nl_class_leave_one_liner_groups()
      && (  pc->Is(one_liner_type)
         || pc->GetParentType() == one_liner_type)
      && pc->TestFlags(PCF_IN_CLASS))
   {
      log_rule_B("nl_class_leave_one_liner_groups");

      if (pc->Is(CT_BRACE_CLOSE))
      {
         return(pc->TestFlags(PCF_ONE_LINER));
      }
      else
      {
         // Find opening brace
         pc = pc->GetNextType(CT_BRACE_OPEN, pc->GetLevel());
         return(  pc->IsNotNullChunk()
               && pc->TestFlags(PCF_ONE_LINER));
      }
   }
   return(false);
} // is_func_proto_group


void do_blank_lines()
{
   LOG_FUNC_ENTRY();

   for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNext())
   {
      if (pc->Is(CT_NEWLINE))
      {
         LOG_FMT(LBLANKD, "%s(%d): orig line is %zu, orig col is %zu, <Newline>, nl is %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->GetNlCount());
      }
      else
      {
         char copy[1000];
         LOG_FMT(LBLANKD, "%s(%d): orig line is %zu, orig col is %zu, Text() '%s', type is %s\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->ElidedText(copy), get_token_name(pc->GetType()));
      }
      LOG_FMT(LBLANK, "%s(%d): new line count is %zu\n",
              __func__, __LINE__, pc->GetNlCount());

      if (pc->IsNot(CT_NEWLINE))
      {
         continue;
      }
      Chunk *prev = pc->GetPrevNc();

      if (prev->IsNotNullChunk())
      {
         LOG_FMT(LBLANK, "%s(%d): prev orig line is %zu, prev->Text() '%s', prev->GetType() is %s\n",
                 __func__, __LINE__, pc->GetOrigLine(),
                 prev->Text(), get_token_name(prev->GetType()));

         if (prev->Is(CT_IGNORED))
         {
            continue;
         }
      }
      Chunk *next = pc->GetNext();
      Chunk *pcmt = pc->GetPrev();

      bool  line_added = false;

      /*
       * If this is the first or the last token, pretend that there is an extra
       * line. It will be removed at the end.
       */
      if (  pc == Chunk::GetHead()
         || next->IsNullChunk())
      {
         line_added = true;
         pc->SetNlCount(pc->GetNlCount() + 1);
         LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, text is '%s', new line count is now %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), pc->GetNlCount());
      }

      // Limit consecutive newlines
      if (  (options::nl_max() > 0)
         && (pc->GetNlCount() > options::nl_max()))
      {
         log_rule_B("nl_max");
         blank_line_max(pc, options::nl_max);
      }

      if (!can_increase_nl(pc))
      {
         LOG_FMT(LBLANKD, "%s(%d): force to 1 orig line is %zu, orig col is %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());

         if (pc->GetNlCount() != 1)
         {
            pc->SetNlCount(1);
            MARK_CHANGE();
         }
         continue;
      }

      // Control blanks before multi-line comments
      if (  (options::nl_before_block_comment() > pc->GetNlCount())
         && next->Is(CT_COMMENT_MULTI))
      {
         log_rule_B("nl_before_block_comment");

         // Don't add blanks after an open brace or a case statement
         if (  (  prev->IsNullChunk()
               || (  prev->IsNot(CT_BRACE_OPEN)
                  && prev->IsNot(CT_VBRACE_OPEN)
                  && prev->IsNot(CT_CASE_COLON)))
            && pcmt->IsNot(CT_COMMENT_MULTI))    // Issue #2383
         {
            blank_line_set(pc, options::nl_before_block_comment);
            log_rule_B("nl_before_block_comment");
         }
      }

      // Control blanks before single line C comments
      if (  (options::nl_before_c_comment() > pc->GetNlCount())
         && next->Is(CT_COMMENT))
      {
         log_rule_B("nl_before_c_comment");

         // Don't add blanks after an open brace, a case stamement, or a comment
         if (  (  prev->IsNullChunk()
               || (  prev->IsNot(CT_BRACE_OPEN)
                  && prev->IsNot(CT_VBRACE_OPEN)
                  && prev->IsNot(CT_CASE_COLON)))
            && pcmt->IsNot(CT_COMMENT))    // Issue #2383
         {
            blank_line_set(pc, options::nl_before_c_comment);
            log_rule_B("nl_before_c_comment");
         }
      }

      // Control blanks before CPP comments
      if (  (options::nl_before_cpp_comment() > pc->GetNlCount())
         && next->Is(CT_COMMENT_CPP))
      {
         log_rule_B("nl_before_cpp_comment");

         // Don't add blanks after an open brace or a case statement
         if (  (  prev->IsNullChunk()
               || (  prev->IsNot(CT_BRACE_OPEN)
                  && prev->IsNot(CT_VBRACE_OPEN)
                  && prev->IsNot(CT_CASE_COLON)))
            && pcmt->IsNot(CT_COMMENT_CPP))    // Issue #2383
         {
            blank_line_set(pc, options::nl_before_cpp_comment);
            log_rule_B("nl_before_cpp_comment");
         }
      }

      // Control blanks before a class/struct
      if (  (  prev->Is(CT_SEMICOLON)
            || prev->Is(CT_BRACE_CLOSE))
         && (  prev->GetParentType() == CT_CLASS
            || prev->GetParentType() == CT_STRUCT))
      {
         E_Token parent_type = prev->GetParentType();
         Chunk   *start      = prev->GetPrevType(parent_type, prev->GetLevel());
         Chunk   *tmp        = start;

         // Is this a class/struct template?
         if (tmp->GetParentType() == CT_TEMPLATE)
         {
            tmp = tmp->GetPrevType(CT_TEMPLATE, prev->GetLevel());
            tmp = tmp->GetPrevNc();
         }
         else
         {
            tmp = tmp->GetPrevNc();

            while (  tmp->Is(CT_NEWLINE)
                  && tmp->GetPrev()->IsComment())
            {
               tmp = tmp->GetPrev()->GetPrevNc();
            }

            if (tmp->Is(CT_FRIEND))
            {
               // Account for a friend declaration
               tmp = tmp->GetPrevNc();
            }
         }

         while (  tmp->Is(CT_NEWLINE)
               && tmp->GetPrev()->IsComment())
         {
            tmp = tmp->GetPrev()->GetPrevNc();
         }

         if (  tmp->IsNotNullChunk()
            && !start->TestFlags(PCF_INCOMPLETE))
         {
            if (parent_type == CT_CLASS && options::nl_before_class() > tmp->GetNlCount())
            {
               log_rule_B("nl_before_class");
               blank_line_set(tmp, options::nl_before_class);
            }
            else if (parent_type == CT_STRUCT && options::nl_before_struct() > tmp->GetNlCount())
            {
               log_rule_B("nl_before_struct");
               blank_line_set(tmp, options::nl_before_struct);
            }
         }
      }

      if (  prev->Is(CT_BRACE_CLOSE)
         && prev->GetParentType() == CT_NAMESPACE)
      {
         // Control blanks before a namespace
         Chunk *tmp = prev->GetPrevType(CT_NAMESPACE, prev->GetLevel());
         tmp = tmp->GetPrevNc();

         while (  tmp->Is(CT_NEWLINE)
               && tmp->GetPrev()->IsComment())
         {
            tmp = tmp->GetPrev()->GetPrevNc();
         }

         if (  tmp->IsNotNullChunk()
            && options::nl_before_namespace() > tmp->GetNlCount())
         {
            log_rule_B("nl_before_namespace");
            blank_line_set(tmp, options::nl_before_namespace);
         }

         // Add blanks after namespace
         if (options::nl_after_namespace() > pc->GetNlCount())
         {
            log_rule_B("nl_after_namespace");
            blank_line_set(pc, options::nl_after_namespace);
         }
      }

      // Control blanks inside empty function body
      if (  prev->Is(CT_BRACE_OPEN)
         && next->Is(CT_BRACE_CLOSE)
         && (  prev->GetParentType() == CT_FUNC_DEF
            || prev->GetParentType() == CT_FUNC_CLASS_DEF)
         && options::nl_inside_empty_func() > pc->GetNlCount()
         && prev->TestFlags(PCF_EMPTY_BODY))
      {
         blank_line_set(pc, options::nl_inside_empty_func);
         log_rule_B("nl_inside_empty_func");
      }

      // Control blanks after an access spec
      if (  (options::nl_after_access_spec() > 0)
         && (options::nl_after_access_spec() != pc->GetNlCount())
         && prev->Is(CT_ACCESS_COLON))
      {
         log_rule_B("nl_after_access_spec");

         // Don't add blanks before a closing brace
         if (  next->IsNullChunk()
            || !next->IsBraceClose())
         {
            log_rule_B("nl_after_access_spec");
            blank_line_set(pc, options::nl_after_access_spec);
         }
      }

      // Add blanks after function bodies
      if (  prev->Is(CT_BRACE_CLOSE)
         && (  prev->GetParentType() == CT_FUNC_DEF
            || prev->GetParentType() == CT_FUNC_CLASS_DEF
            || prev->GetParentType() == CT_OC_MSG_DECL
            || prev->GetParentType() == CT_ASSIGN))
      {
         if (prev->TestFlags(PCF_ONE_LINER))
         {
            if (options::nl_after_func_body_one_liner() > pc->GetNlCount())
            {
               log_rule_B("nl_after_func_body_one_liner");
               blank_line_set(pc, options::nl_after_func_body_one_liner);
            }
         }
         else
         {
            if (  prev->TestFlags(PCF_IN_CLASS)
               && (options::nl_after_func_body_class() > 0))
            {
               log_rule_B("nl_after_func_body_class");

               if (options::nl_after_func_body_class() != pc->GetNlCount())
               {
                  log_rule_B("nl_after_func_body_class");
                  blank_line_set(pc, options::nl_after_func_body_class);
               }
            }
            else
            {
               if (!(pc->GetPrev()->TestFlags(PCF_IN_TRY_BLOCK))) // Issue #1734
               {
                  if (options::nl_after_func_body() > 0)
                  {
                     log_rule_B("nl_after_func_body");

                     if (options::nl_after_func_body() != pc->GetNlCount())
                     {
                        log_rule_B("nl_after_func_body");
                        blank_line_set(pc, options::nl_after_func_body);
                     }
                  }
                  else
                  {
                     if (options::nl_min_after_func_body() > 0) // Issue #2787
                     {
                        log_rule_B("nl_min_after_func_body");

                        if (options::nl_min_after_func_body() > pc->GetNlCount())
                        {
                           log_rule_B("nl_min_after_func_body");
                           blank_line_set(pc, options::nl_min_after_func_body);
                        }
                     }

                     if (options::nl_max_after_func_body() > 0)
                     {
                        log_rule_B("nl_max_after_func_body");

                        if (options::nl_max_after_func_body() < pc->GetNlCount())
                        {
                           log_rule_B("nl_max_after_func_body");
                           blank_line_set(pc, options::nl_max_after_func_body);
                        }
                     }
                  }
               }
            }
         }
      }

      // Add blanks after function prototypes
      if (  (  prev->Is(CT_SEMICOLON)
            && prev->GetParentType() == CT_FUNC_PROTO)
         || is_func_proto_group(prev, CT_FUNC_DEF))
      {
         if (options::nl_after_func_proto() > pc->GetNlCount())
         {
            log_rule_B("nl_after_func_proto");
            pc->SetNlCount(options::nl_after_func_proto());
            MARK_CHANGE();
         }

         if (  (options::nl_after_func_proto_group() > pc->GetNlCount())
            && next->IsNotNullChunk()
            && next->GetParentType() != CT_FUNC_PROTO
            && !is_func_proto_group(next, CT_FUNC_DEF))
         {
            log_rule_B("nl_after_func_proto_group");
            blank_line_set(pc, options::nl_after_func_proto_group);
         }
      }

      // Issue #411: Add blanks after function class prototypes
      if (  (  prev->Is(CT_SEMICOLON)
            && prev->GetParentType() == CT_FUNC_CLASS_PROTO)
         || is_func_proto_group(prev, CT_FUNC_CLASS_DEF))
      {
         if (options::nl_after_func_class_proto() > pc->GetNlCount())
         {
            log_rule_B("nl_after_func_class_proto");
            pc->SetNlCount(options::nl_after_func_class_proto());
            MARK_CHANGE();
         }

         if (  (options::nl_after_func_class_proto_group() > pc->GetNlCount())
            && next->IsNot(CT_FUNC_CLASS_PROTO)
            && next->GetParentType() != CT_FUNC_CLASS_PROTO
            && !is_func_proto_group(next, CT_FUNC_CLASS_DEF))
         {
            log_rule_B("nl_after_func_class_proto_group");
            blank_line_set(pc, options::nl_after_func_class_proto_group);
         }
      }

      // Add blanks after struct/enum/union/class
      if (  (  prev->Is(CT_SEMICOLON)
            || prev->Is(CT_BRACE_CLOSE))
         && (  prev->GetParentType() == CT_STRUCT
            || prev->GetParentType() == CT_ENUM
            || prev->GetParentType() == CT_UNION
            || prev->GetParentType() == CT_CLASS))
      {
         auto &opt = (prev->GetParentType() == CT_CLASS
         ? options::nl_after_class
         : options::nl_after_struct);
         log_rule_B("nl_after_class");
         log_rule_B("nl_after_struct");

         if (opt() > pc->GetNlCount())
         {
            // Issue #1702
            // look back if we have a variable
            Chunk *tmp        = pc;
            bool  is_var_def  = false;
            bool  is_fwd_decl = false;

            while ((tmp = tmp->GetPrev())->IsNotNullChunk())
            {
               if (tmp->GetLevel() > pc->GetLevel())
               {
                  continue;
               }
               LOG_FMT(LBLANK, "%s(%d): %zu:%zu token is '%s'\n",
                       __func__, __LINE__, tmp->GetOrigLine(), tmp->GetOrigCol(), tmp->Text());

               if (tmp->TestFlags(PCF_VAR_DEF))
               {
                  is_var_def = true;
                  break;
               }

               if (tmp->Is(prev->GetParentType()))
               {
                  is_fwd_decl = tmp->TestFlags(PCF_INCOMPLETE);
                  break;
               }
            }
            LOG_FMT(LBLANK, "%s(%d): var_def = %s, fwd_decl = %s\n",
                    __func__, __LINE__,
                    is_var_def ? "yes" : "no",
                    is_fwd_decl ? "yes" : "no");

            if (  !is_var_def
               && !is_fwd_decl)
            {
               blank_line_set(pc, opt);
            }
         }
      }

      // Change blanks between a function comment and body
      if (  (options::nl_comment_func_def() != 0)
         && pcmt->Is(CT_COMMENT_MULTI)
         && pcmt->GetParentType() == CT_COMMENT_WHOLE
         && next->IsNotNullChunk()
         && (  next->GetParentType() == CT_FUNC_DEF
            || next->GetParentType() == CT_FUNC_CLASS_DEF))
      {
         log_rule_B("nl_comment_func_def");

         if (options::nl_comment_func_def() != pc->GetNlCount())
         {
            log_rule_B("nl_comment_func_def");
            blank_line_set(pc, options::nl_comment_func_def);
         }
      }

      // Change blanks after a try-catch-finally block
      if (  (options::nl_after_try_catch_finally() != 0)
         && (options::nl_after_try_catch_finally() != pc->GetNlCount())
         && prev->IsNotNullChunk()
         && next->IsNotNullChunk())
      {
         log_rule_B("nl_after_try_catch_finally");

         if (  prev->Is(CT_BRACE_CLOSE)
            && (  prev->GetParentType() == CT_CATCH
               || prev->GetParentType() == CT_FINALLY))
         {
            if (  next->IsNot(CT_BRACE_CLOSE)
               && next->IsNot(CT_CATCH)
               && next->IsNot(CT_FINALLY))
            {
               blank_line_set(pc, options::nl_after_try_catch_finally);
               log_rule_B("nl_after_try_catch_finally");
            }
         }
      }

      // Change blanks after a try-catch-finally block
      if (  (options::nl_between_get_set() != 0)
         && (options::nl_between_get_set() != pc->GetNlCount())
         && prev->IsNotNullChunk()
         && next->IsNotNullChunk())
      {
         log_rule_B("nl_between_get_set");

         if (  prev->GetParentType() == CT_GETSET
            && next->IsNot(CT_BRACE_CLOSE)
            && (  prev->Is(CT_BRACE_CLOSE)
               || prev->Is(CT_SEMICOLON)))
         {
            blank_line_set(pc, options::nl_between_get_set);
            log_rule_B("nl_between_get_set");
         }
      }

      // Change blanks after a try-catch-finally block
      if (  (options::nl_around_cs_property() != 0)
         && (options::nl_around_cs_property() != pc->GetNlCount())
         && prev->IsNotNullChunk()
         && next->IsNotNullChunk())
      {
         log_rule_B("nl_around_cs_property");

         if (  prev->Is(CT_BRACE_CLOSE)
            && prev->GetParentType() == CT_CS_PROPERTY
            && next->IsNot(CT_BRACE_CLOSE))
         {
            blank_line_set(pc, options::nl_around_cs_property);
            log_rule_B("nl_around_cs_property");
         }
         else if (  next->GetParentType() == CT_CS_PROPERTY
                 && next->TestFlags(PCF_STMT_START))
         {
            blank_line_set(pc, options::nl_around_cs_property);
            log_rule_B("nl_around_cs_property");
         }
      }

      // Control blanks before an access spec
      if (  (options::nl_before_access_spec() > 0)
         && (options::nl_before_access_spec() != pc->GetNlCount())
         && next->Is(CT_ACCESS))
      {
         log_rule_B("nl_before_access_spec");

         // Don't add blanks after an open brace
         if (  prev->IsNullChunk()
            || (  prev->IsNot(CT_BRACE_OPEN)
               && prev->IsNot(CT_VBRACE_OPEN)))
         {
            log_rule_B("nl_before_access_spec");
            blank_line_set(pc, options::nl_before_access_spec);
         }
      }

      // Change blanks inside namespace braces
      if (  (options::nl_inside_namespace() != 0)
         && (options::nl_inside_namespace() != pc->GetNlCount())
         && (  (  prev->Is(CT_BRACE_OPEN)
               && prev->GetParentType() == CT_NAMESPACE)
            || (  next->Is(CT_BRACE_CLOSE)
               && next->GetParentType() == CT_NAMESPACE)))
      {
         log_rule_B("nl_inside_namespace");
         blank_line_set(pc, options::nl_inside_namespace);
      }

      // Control blanks before a whole-file #ifdef
      if (  options::nl_before_whole_file_ifdef() != 0
         && options::nl_before_whole_file_ifdef() != pc->GetNlCount()
         && next->Is(CT_PREPROC)
         && next->GetParentType() == CT_PP_IF
         && ifdef_over_whole_file()
         && next->TestFlags(PCF_WF_IF))
      {
         log_rule_B("nl_before_whole_file_ifdef");
         blank_line_set(pc, options::nl_before_whole_file_ifdef);
      }

      // Control blanks after a whole-file #ifdef
      if (  options::nl_after_whole_file_ifdef() != 0
         && options::nl_after_whole_file_ifdef() != pc->GetNlCount())
      {
         Chunk *pp_start = prev->GetPpStart();

         if (  pp_start->IsNotNullChunk()
            && pp_start->GetParentType() == CT_PP_IF
            && ifdef_over_whole_file()
            && pp_start->TestFlags(PCF_WF_IF))
         {
            log_rule_B("nl_after_whole_file_ifdef");
            blank_line_set(pc, options::nl_after_whole_file_ifdef);
         }
      }

      // Control blanks before a whole-file #endif
      if (  options::nl_before_whole_file_endif() != 0
         && options::nl_before_whole_file_endif() != pc->GetNlCount()
         && next->Is(CT_PREPROC)
         && next->GetParentType() == CT_PP_ENDIF
         && ifdef_over_whole_file()
         && next->TestFlags(PCF_WF_ENDIF))
      {
         log_rule_B("nl_before_whole_file_endif");
         blank_line_set(pc, options::nl_before_whole_file_endif);
      }

      // Control blanks after a whole-file #endif
      if (  options::nl_after_whole_file_endif() != 0
         && options::nl_after_whole_file_endif() != pc->GetNlCount())
      {
         Chunk *pp_start = prev->GetPpStart();

         if (  pp_start->IsNotNullChunk()
            && pp_start->GetParentType() == CT_PP_ENDIF
            && ifdef_over_whole_file()
            && pp_start->TestFlags(PCF_WF_ENDIF))
         {
            log_rule_B("nl_after_whole_file_endif");
            blank_line_set(pc, options::nl_after_whole_file_endif);
         }
      }

      if (  line_added
         && pc->GetNlCount() > 1)
      {
         pc->SetNlCount(pc->GetNlCount() - 1);
         LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, text is '%s', new line count is now %zu\n",
                 __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), pc->GetNlCount());
      }
      LOG_FMT(LBLANK, "%s(%d): orig line is %zu, orig col is %zu, text is '%s', end new line count is now %zu\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), pc->GetNlCount());
   }
} // do_blank_lines


void newlines_cleanup_dup()
{
   LOG_FUNC_ENTRY();

   Chunk *pc   = Chunk::GetHead();
   Chunk *next = pc;

   while (pc->IsNotNullChunk())
   {
      next = next->GetNext();

      if (  pc->Is(CT_NEWLINE)
         && next->Is(CT_NEWLINE))
      {
         next->SetNlCount(max(pc->GetNlCount(), next->GetNlCount()));
         Chunk::Delete(pc);
         MARK_CHANGE();
      }
      pc = next;
   }
} // newlines_cleanup_dup


static void newlines_enum_entries(Chunk *open_brace, iarf_e av)
{
   LOG_FUNC_ENTRY();

   for (Chunk *pc = open_brace->GetNextNc();
        pc->IsNotNullChunk() && pc->GetLevel() > open_brace->GetLevel();
        pc = pc->GetNextNc())
   {
      if (  (pc->GetLevel() != (open_brace->GetLevel() + 1))
         || pc->IsNot(CT_COMMA)
         || (  pc->Is(CT_COMMA)
            && (  pc->GetNext()->GetType() == CT_COMMENT_CPP
               || pc->GetNext()->GetType() == CT_COMMENT
               || pc->GetNext()->GetType() == CT_COMMENT_MULTI)))
      {
         continue;
      }
      newline_iarf(pc, av);
   }

   newline_iarf(open_brace, av);
} // newlines_enum_entries


static void newlines_double_space_struct_enum_union(Chunk *open_brace)
{
   LOG_FUNC_ENTRY();

   Chunk *pc = Chunk::NullChunkPtr;

   if (open_brace != nullptr)
   {
      pc = open_brace;
   }

   while (  (pc = pc->GetNextNc())->IsNotNullChunk()
         && pc->GetLevel() > open_brace->GetLevel())
   {
      if (  pc->GetLevel() != (open_brace->GetLevel() + 1)
         || pc->IsNot(CT_NEWLINE))
      {
         continue;
      }
      /*
       * If the newline is NOT after a comment or a brace open and
       * it is before a comment, then make sure that the newline is
       * at least doubled
       */
      Chunk *prev = pc->GetPrev();

      if (  !prev->IsComment()
         && prev->IsNot(CT_BRACE_OPEN)
         && pc->GetNext()->IsComment())
      {
         if (pc->GetNlCount() < 2)
         {
            double_newline(pc);
         }
      }
   }
} // newlines_double_space_struct_enum_union


void annotations_newlines()
{
   LOG_FUNC_ENTRY();

   Chunk *next;
   Chunk *prev;
   Chunk *ae;   // last token of the annotation
   Chunk *pc = Chunk::GetHead();

   while (  (pc = pc->GetNextType(CT_ANNOTATION))->IsNotNullChunk()
         && (next = pc->GetNextNnl())->IsNotNullChunk())
   {
      // find the end of this annotation
      if (next->IsParenOpen())
      {
         // TODO: control newline between annotation and '(' ?
         ae = next->GetClosingParen();
      }
      else
      {
         ae = pc;
      }

      if (ae->IsNullChunk())
      {
         break;
      }
      LOG_FMT(LANNOT, "%s(%d): orig line is %zu, orig col is %zu, annotation is '%s',  end @ orig line %zu, orig col %zu, is '%s'\n",
              __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(),
              ae->GetOrigLine(), ae->GetOrigCol(), ae->Text());

      prev = ae->GetPrev();             // Issue #1845
      LOG_FMT(LANNOT, "%s(%d): prev orig line is %zu, orig col is %zu, Text() is '%s'\n",
              __func__, __LINE__, prev->GetOrigLine(), prev->GetOrigCol(), prev->Text());
      next = ae->GetNextNnl();

      if (next->Is(CT_ANNOTATION))
      {
         LOG_FMT(LANNOT, "%s(%d):  -- nl_between_annotation\n",
                 __func__, __LINE__);
         newline_iarf(ae, options::nl_between_annotation());
         log_rule_B("nl_between_annotation");
      }

      if (next->Is(CT_NEWLINE))
      {
         if (next->Is(CT_ANNOTATION))
         {
            LOG_FMT(LANNOT, "%s(%d):  -- nl_after_annotation\n",
                    __func__, __LINE__);
            newline_iarf(ae, options::nl_after_annotation());
            log_rule_B("nl_after_annotation");
         }
      }
   }
} // annotations_newlines


bool newlines_between(Chunk *pc_start, Chunk *pc_end, size_t &newlines, E_Scope scope)
{
   if (  pc_start->IsNullChunk()
      || pc_end->IsNullChunk())
   {
      return(false);
   }
   newlines = 0;

   Chunk *it = pc_start;

   for ( ; it->IsNotNullChunk() && it != pc_end; it = it->GetNext(scope))
   {
      newlines += it->GetNlCount();
   }

   // newline count is valid if search stopped on expected chunk
   return(it == pc_end);
} // newlines_between
