Logo Search packages:      
Sourcecode: uncrustify version File versions

width.cpp

Go to the documentation of this file.
/**
 * @file width.cpp
 * Limits line width.
 *
 * @author  Ben Gardner
 * @license GPL v2+
 *
 * $Id: align.cpp 365 2006-07-27 02:40:27Z bengardner $
 */

#include "uncrustify_types.h"
#include "chunk_list.h"
#include "prototypes.h"

static void split_line(chunk_t *pc);
static void split_fcn_params(chunk_t *start);
static void split_fcn_params_full(chunk_t *start);
static void split_for_stmt(chunk_t *start);

static_inline bool is_past_width(chunk_t *pc)
{
   return((pc->column + pc->len) > cpd.settings[UO_code_width].n);
}


/**
 * Split right after the chunk
 */
00029 static void split_before_chunk(chunk_t *pc)
{
   LOG_FMT(LSYS, "%s: %.*s\n", __func__, pc->len, pc->str);

   if (!chunk_is_newline(chunk_get_prev(pc)))
   {
      newline_add_before(pc);
      reindent_line(pc, pc->brace_level * cpd.settings[UO_indent_columns].n);
      cpd.changes++;
   }
}


/**
 * Step forward until a token goes beyond the limit and then call split_line()
 * to split the line at or before that point.
 */
00046 void do_code_width(void)
{
   chunk_t *pc;

   LOG_FMT(LSPLIT, "%s\n", __func__);

   for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
   {
      if (!chunk_is_newline(pc) && !chunk_is_comment(pc) && is_past_width(pc))
      {
         split_line(pc);
      }
   }
}

struct cw_entry
{
   chunk_t *pc;
   int     pri;
};

struct token_pri
{
   c_token_t tok;
   int       pri;
};

static const token_pri pri_table[] =
{
   { CT_SEMICOLON, 1 },
   { CT_COMMA,     2 },
   { CT_BOOL,      3 },
   { CT_COMPARE,   4 },
   { CT_ARITH,     5 },
   { CT_DC_MEMBER, 10 },
   { CT_MEMBER,    10 },
};


static int get_split_pri(c_token_t tok)
{
   int idx;

   for (idx = 0; idx < (int)ARRAY_SIZE(pri_table); idx++)
   {
      if (pri_table[idx].tok == tok)
      {
         return(pri_table[idx].pri);
      }
   }
   return(0);
}


/**
 * Checks to see if pc is a better spot to split.
 * This should only be called going BACKWARDS (ie prev)
 * A lower level wins
 *
 * Splitting Preference:
 *  - semicolon
 *  - comma
 *  - boolean op
 *  - comparison
 *  - arithmetic op
 */
00112 static void try_split_here(cw_entry& ent, chunk_t *pc)
{
   chunk_t *prev;
   int     pc_pri = get_split_pri(pc->type);

   if (pc_pri == 0)
   {
      return;
   }

   /* Can't split after a newline */
   prev = chunk_get_prev(pc);
   if ((prev == NULL) || chunk_is_newline(prev))
   {
      return;
   }

   /* Check levels first */
   bool change = false;
   if ((ent.pc == NULL) || (pc->level < ent.pc->level))
   {
      change = true;
   }
   else
   {
      if ((pc->level > ent.pc->level) &&
          (pc_pri <= ent.pri))
      {
         change = true;
      }
   }

   if (change)
   {
      ent.pc  = pc;
      ent.pri = pc_pri;
   }
}


/**
 * Scan backwards to find the most appropriate spot to split the line
 * and insert a newline.
 *
 * See if this needs special function handling.
 * Scan backwards and find the best token for the split.
 *
 * @param start The first chunk that exceeded the limit
 */
00161 static void split_line(chunk_t *start)
{
   LOG_FMT(LSPLIT, "%s: line %d, col %d token:%.*s (IN_FUNC=%d) ",
           __func__, start->orig_line, start->column, start->len, start->str,
           (start->flags & PCF_IN_FCN_DEF) != 0);

   /* Don't break before a close, comma, or colon */
   if ((start->type == CT_PAREN_CLOSE) ||
       (start->type == CT_PAREN_OPEN) ||
       (start->type == CT_FPAREN_CLOSE) ||
       (start->type == CT_FPAREN_OPEN) ||
       (start->type == CT_SPAREN_CLOSE) ||
       (start->type == CT_SPAREN_OPEN) ||
       (start->type == CT_ANGLE_CLOSE) ||
       (start->type == CT_BRACE_CLOSE) ||
       (start->type == CT_COMMA) ||
       (start->type == CT_SEMICOLON) ||
       (start->type == CT_VSEMICOLON) ||
       (start->len == 0))
   {
      LOG_FMT(LSPLIT, " ** NO GO **\n");

      /*TODO: Add in logic to handle 'hard' limits by backing up a token */
      return;
   }

   /* Check to see if we are in a for statment */
   if ((start->flags & PCF_IN_FOR) != 0)
   {
      LOG_FMT(LSPLIT, " ** FOR SPLIT **\n");
      split_for_stmt(start);
      if (!is_past_width(start))
      {
         return;
      }
      LOG_FMT(LSPLIT, "%s: for split didn't work\n", __func__);
   }

   /* If this is in a function call or prototype, split on commas or right
    * after the open paren
    */
   else if (((start->flags & PCF_IN_FCN_DEF) != 0) ||
            ((start->level == start->brace_level) &&
             ((start->flags & PCF_IN_FCN_CALL) != 0)))
   {
      LOG_FMT(LSPLIT, " ** FUNC SPLIT **\n");

      if (cpd.settings[UO_ls_func_split_full].b)
      {
         split_fcn_params_full(start);
         if (!is_past_width(start))
         {
            return;
         }
      }
      split_fcn_params(start);
      if (!is_past_width(start))
      {
         return;
      }
      LOG_FMT(LSPLIT, "%s: func split didn't work\n", __func__);
   }

   /**
    * Try to find the best spot to split the line
    */
   cw_entry ent;

   memset(&ent, 0, sizeof(ent));
   chunk_t *pc = start;
   chunk_t *prev;

   while (((pc = chunk_get_prev(pc)) != NULL) && !chunk_is_newline(pc))
   {
      try_split_here(ent, pc);
   }

   if (ent.pc == NULL)
   {
      LOG_FMT(LSYS, "%s: TRY_SPLIT yeilded NO SOLUTION for line %d at %.*s\n",
              __func__, start->orig_line, start->len, start->str);
   }
   else
   {
      LOG_FMT(LSYS, "%s: TRY_SPLIT yeilded %.*s\n", __func__, ent.pc->len, ent.pc->str);
   }

   pc = chunk_get_next(ent.pc);
   if (pc == NULL)
   {
      pc = start;
   }

   /* add a newline before pc */
   prev = chunk_get_prev(pc);
   if ((prev != NULL) && !chunk_is_newline(pc) && !chunk_is_newline(prev))
   {
      int plen = (pc->len < 5) ? pc->len : 5;
      int slen = (start->len < 5) ? start->len : 5;
      LOG_FMT(LSPLIT, " '%.*s' [%s], started on token '%.*s' [%s]\n",
              plen, pc->str, get_token_name(pc->type),
              slen, start->str, get_token_name(start->type));

      split_before_chunk(pc);
   }
}


/**
 * A for statment is too long.
 * Step backwards and forwards to find the semicolons
 */
00273 static void split_for_stmt(chunk_t *start)
{
   int     count   = 0;
   int     max_cnt = cpd.settings[UO_ls_for_split_full].b ? 2 : 1;
   chunk_t *st[2];
   chunk_t *pc = start;

   LOG_FMT(LSYS, "\n\n%s: starting on %.*s\n", __func__, pc->len, pc->str);

   /* see if we started on the semicolon */
   if ((pc->type == CT_SEMICOLON) && (pc->parent_type == CT_FOR))
   {
      st[count++] = pc;
   }

   /* first scan backwards for the semicolons */
   while ((count < max_cnt) && ((pc = chunk_get_prev(pc)) != NULL) &&
          (pc->flags & PCF_IN_SPAREN))
   {
      if ((pc->type == CT_SEMICOLON) && (pc->parent_type == CT_FOR))
      {
         st[count++] = pc;
      }
   }

   /* And now scan forward */
   pc = start;
   while ((count < max_cnt) && ((pc = chunk_get_next(pc)) != NULL) &&
          (pc->flags & PCF_IN_SPAREN))
   {
      if ((pc->type == CT_SEMICOLON) && (pc->parent_type == CT_FOR))
      {
         st[count++] = pc;
      }
   }

   while (--count >= 0)
   {
      LOG_FMT(LSYS, "%s: %.*s\n", __func__, st[count]->len, st[count]->str);
      split_before_chunk(chunk_get_next(st[count]));
   }
}


/**
 * Splits the parameters at every comma that is at the fparen level.
 *
 * @param start   the offending token
 */
00322 static void split_fcn_params_full(chunk_t *start)
{
   LOG_FMT(LSPLIT, "\n\n  %s: ", __func__);

   chunk_t *fpo;
   chunk_t *pc;

   /* Find the opening fparen */
   fpo = start;
   while (((fpo = chunk_get_prev(fpo)) != NULL) &&
          (fpo->type != CT_FPAREN_OPEN))
   {
      /* do nothing */
   }

   /* Now break after every comma */
   pc = fpo;
   while ((pc = chunk_get_next_ncnl(pc)) != NULL)
   {
      if (pc->level <= fpo->level)
      {
         break;
      }
      if ((pc->level == (fpo->level + 1)) && (pc->type == CT_COMMA))
      {
         split_before_chunk(chunk_get_next(pc));
      }
   }
}


/**
 * Figures out where to split a function def/proto/call
 *
 * For fcn protos and defs. Also fcn calls where level == brace_level:
 *   - find the open fparen
 *     + if it doesn't have a newline right after it
 *       * see if all parameters will fit individually after the paren
 *       * if not, throw a newline after the open paren & return
 *   - scan backwards to the open fparen or comma
 *     + if there isn't a newline after that item, add one & return
 *     + otherwise, add a newline before the start token
 *
 * @param start   the offending token
 * @return        the token that should have a newline
 *                inserted before it
 */
00369 static void split_fcn_params(chunk_t *start)
{
   LOG_FMT(LSPLIT, "  %s: ", __func__);

   chunk_t *prev;
   chunk_t *fpo;
   chunk_t *pc;

   /* Find the opening fparen */
   fpo = start;
   while (((fpo = chunk_get_prev(fpo)) != NULL) &&
          (fpo->type != CT_FPAREN_OPEN))
   {
      /* do nothing */
   }

   pc = chunk_get_next(fpo);
   if (!chunk_is_newline(pc))
   {
      int min_col   = pc->column;
      int max_width = 0;
      int cur_width = 0;
      int last_col  = -1;

      LOG_FMT(LSPLIT, " mincol=%d, max_width=%d ",
              min_col,
              cpd.settings[UO_code_width].n - min_col);

      while (pc != NULL)
      {
         if (chunk_is_newline(pc))
         {
            last_col = -1;
         }
         else
         {
            if (last_col < 0)
            {
               last_col = pc->column;
            }
            cur_width += (pc->column - last_col) + pc->len;
            last_col   = pc->column + pc->len;

            if ((pc->type == CT_COMMA) ||
                (pc->type == CT_FPAREN_CLOSE))
            {
               cur_width--;
               LOG_FMT(LSPLIT, " width=%d ", cur_width);
               if (cur_width > max_width)
               {
                  max_width = cur_width;
                  if ((max_width + min_col) > cpd.settings[UO_code_width].n)
                  {
                     break;
                  }
               }
               cur_width = 0;
               last_col  = -1;
               if (pc->type == CT_FPAREN_CLOSE)
               {
                  break;
               }
            }
         }
         pc = chunk_get_next(pc);
      }

      if ((max_width + min_col) > cpd.settings[UO_code_width].n)
      {
         LOG_FMT(LSPLIT, " - A param won't fit, nl after open paren.");
         split_before_chunk(chunk_get_next(fpo));
         return;
      }
   }

   /* back up until the prev is a comma */
   prev = pc;
   while ((prev = chunk_get_prev(prev)) != NULL)
   {
      if (chunk_is_newline(prev) ||
          (prev->type == CT_COMMA) ||
          (prev->type == CT_FPAREN_OPEN))
      {
         break;
      }
   }
   if (prev != NULL)
   {
      LOG_FMT(LSPLIT, " -- ended on [%s] -- ", get_token_name(prev->type));
   }
   if (prev != NULL)
   {
      split_before_chunk(chunk_get_next(prev));
   }
}

Generated by  Doxygen 1.6.0   Back to index