You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
15 KiB
625 lines
15 KiB
4 years ago
|
/**
|
||
|
* @file output.cpp
|
||
|
* Does all the output & comment formatting.
|
||
|
*
|
||
|
* $Id: output.cpp 510 2006-09-20 01:14:56Z bengardner $
|
||
|
*/
|
||
|
|
||
|
#include "uncrustify_types.h"
|
||
|
#include "prototypes.h"
|
||
|
#include "chunk_list.h"
|
||
|
#include <cstring>
|
||
|
#include <cstdlib>
|
||
|
|
||
|
|
||
|
|
||
|
void add_char(char ch)
|
||
|
{
|
||
|
/* convert a newline into the LF/CRLF/CR sequence */
|
||
|
if (ch == '\n')
|
||
|
{
|
||
|
fputs(cpd.newline, cpd.fout);
|
||
|
cpd.column = 1;
|
||
|
cpd.did_newline = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fputc(ch, cpd.fout);
|
||
|
|
||
|
if (ch == '\t')
|
||
|
cpd.column = next_tab_column(cpd.column);
|
||
|
else
|
||
|
cpd.column++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void add_text(const char *text)
|
||
|
{
|
||
|
char ch;
|
||
|
|
||
|
while ((ch = *text) != 0)
|
||
|
{
|
||
|
text++;
|
||
|
add_char(ch);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void add_text_len(const char *text, int len)
|
||
|
{
|
||
|
while (len-- > 0)
|
||
|
{
|
||
|
add_char(*text);
|
||
|
text++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Advance to a specific column
|
||
|
* cpd.column is the current column
|
||
|
*
|
||
|
* @param column The column to advance to
|
||
|
*/
|
||
|
void output_to_column(int column, bool allow_tabs)
|
||
|
{
|
||
|
int nc;
|
||
|
|
||
|
cpd.did_newline = 0;
|
||
|
|
||
|
if (allow_tabs)
|
||
|
{
|
||
|
/* tab out as far as possible and then use spaces */
|
||
|
while ((nc = next_tab_column(cpd.column)) <= column)
|
||
|
add_text("\t");
|
||
|
}
|
||
|
|
||
|
/* space out the final bit */
|
||
|
while (cpd.column < column)
|
||
|
add_text(" ");
|
||
|
}
|
||
|
|
||
|
void output_indent(int column, int brace_col)
|
||
|
{
|
||
|
if ((cpd.column == 1) && (cpd.settings[UO_indent_with_tabs].n != 0))
|
||
|
{
|
||
|
if (cpd.settings[UO_indent_with_tabs].n == 2)
|
||
|
brace_col = column;
|
||
|
|
||
|
/* tab out as far as possible and then use spaces */
|
||
|
int nc;
|
||
|
|
||
|
while ((nc = next_tab_column(cpd.column)) <= brace_col)
|
||
|
add_text("\t");
|
||
|
}
|
||
|
|
||
|
/* space out the rest */
|
||
|
while (cpd.column < column)
|
||
|
add_text(" ");
|
||
|
}
|
||
|
|
||
|
void output_parsed(FILE *pfile)
|
||
|
{
|
||
|
chunk_t *pc;
|
||
|
int cnt;
|
||
|
|
||
|
output_options(pfile);
|
||
|
output_defines(pfile);
|
||
|
output_types(pfile);
|
||
|
|
||
|
fprintf(pfile, "-=====-\n");
|
||
|
fprintf(pfile, "Line Tag Parent Columns Br/Lvl/pp Flg Nl Text");
|
||
|
|
||
|
for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
|
||
|
{
|
||
|
fprintf(pfile, "\n%3d> %13.13s[%13.13s][%2d/%2d/%2d][%d/%d/%d][%6x][%d-%d]",
|
||
|
pc->orig_line, get_token_name(pc->type),
|
||
|
get_token_name(pc->parent_type),
|
||
|
pc->column, pc->orig_col, pc->orig_col_end,
|
||
|
pc->brace_level, pc->level, pc->pp_level,
|
||
|
pc->flags, pc->nl_count, pc->after_tab);
|
||
|
|
||
|
if ((pc->type != CT_NEWLINE) && (pc->len != 0))
|
||
|
{
|
||
|
for (cnt = 0; cnt < pc->column; cnt++)
|
||
|
fprintf(pfile, " ");
|
||
|
|
||
|
fprintf(pfile, "%.*s", pc->len, pc->str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fprintf(pfile, "\n-=====-\n");
|
||
|
fflush(pfile);
|
||
|
}
|
||
|
|
||
|
void output_options(FILE *pfile)
|
||
|
{
|
||
|
int idx;
|
||
|
const option_map_value *ptr;
|
||
|
|
||
|
fprintf(pfile, "-== Options ==-\n");
|
||
|
|
||
|
for (idx = 0; idx < UO_option_count; idx++)
|
||
|
{
|
||
|
ptr = get_option_name(idx);
|
||
|
|
||
|
if (ptr != NULL)
|
||
|
{
|
||
|
if (ptr->type == AT_BOOL)
|
||
|
{
|
||
|
fprintf(pfile, "%3d) %32s = %s\n",
|
||
|
ptr->id, ptr->name,
|
||
|
cpd.settings[ptr->id].b ? "True" : "False");
|
||
|
}
|
||
|
else if (ptr->type == AT_IARF)
|
||
|
{
|
||
|
fprintf(pfile, "%3d) %32s = %s\n",
|
||
|
ptr->id, ptr->name,
|
||
|
(cpd.settings[ptr->id].a == AV_ADD) ? "Add" :
|
||
|
(cpd.settings[ptr->id].a == AV_REMOVE) ? "Remove" :
|
||
|
(cpd.settings[ptr->id].a == AV_FORCE) ? "Force" : "Ignore");
|
||
|
}
|
||
|
else if (ptr->type == AT_LINE)
|
||
|
{
|
||
|
fprintf(pfile, "%3d) %32s = %s\n",
|
||
|
ptr->id, ptr->name,
|
||
|
(cpd.settings[ptr->id].le == LE_AUTO) ? "Auto" :
|
||
|
(cpd.settings[ptr->id].le == LE_LF) ? "LF" :
|
||
|
(cpd.settings[ptr->id].le == LE_CRLF) ? "CRLF" :
|
||
|
(cpd.settings[ptr->id].le == LE_CR) ? "CR" : "???");
|
||
|
}
|
||
|
else /* AT_NUM */
|
||
|
fprintf(pfile, "%3d) %32s = %d\n",
|
||
|
ptr->id, ptr->name, cpd.settings[ptr->id].n);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This renders the chunk list to a file.
|
||
|
*/
|
||
|
void output_text(FILE *pfile)
|
||
|
{
|
||
|
chunk_t *pc;
|
||
|
chunk_t *prev;
|
||
|
int cnt;
|
||
|
int lvlcol;
|
||
|
bool allow_tabs;
|
||
|
|
||
|
cpd.fout = pfile;
|
||
|
|
||
|
for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
|
||
|
{
|
||
|
if (pc->type == CT_NEWLINE)
|
||
|
{
|
||
|
for (cnt = 0; cnt < pc->nl_count; cnt++)
|
||
|
add_char('\n');
|
||
|
|
||
|
cpd.did_newline = 1;
|
||
|
cpd.column = 1;
|
||
|
LOG_FMT(LOUTIND, " xx\n");
|
||
|
}
|
||
|
else if (pc->type == CT_COMMENT_MULTI)
|
||
|
output_comment_multi(pc);
|
||
|
else if (pc->type == CT_COMMENT_CPP)
|
||
|
pc = output_comment_cpp(pc);
|
||
|
else if (pc->len == 0)
|
||
|
/* don't do anything for non-visible stuff */
|
||
|
LOG_FMT(LOUTIND, " <%d> -", pc->column);
|
||
|
else
|
||
|
{
|
||
|
/* indent to the 'level' first */
|
||
|
if (cpd.did_newline)
|
||
|
{
|
||
|
if (cpd.settings[UO_indent_with_tabs].n == 1)
|
||
|
{
|
||
|
lvlcol = 1 + (pc->brace_level * cpd.settings[UO_indent_columns].n);
|
||
|
|
||
|
if ((pc->column >= lvlcol) && (lvlcol > 1))
|
||
|
output_to_column(lvlcol, true);
|
||
|
}
|
||
|
|
||
|
allow_tabs = (cpd.settings[UO_indent_with_tabs].n == 2) ||
|
||
|
(chunk_is_comment(pc) &&
|
||
|
(cpd.settings[UO_indent_with_tabs].n != 0));
|
||
|
|
||
|
LOG_FMT(LOUTIND, " %d> col %d/%d - ", pc->orig_line, pc->column, cpd.column);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* not the first item on a line */
|
||
|
if (cpd.settings[UO_align_keep_tabs].b)
|
||
|
allow_tabs = pc->after_tab;
|
||
|
else
|
||
|
{
|
||
|
prev = chunk_get_prev(pc);
|
||
|
allow_tabs = (cpd.settings[UO_align_with_tabs].b &&
|
||
|
((pc->flags & PCF_WAS_ALIGNED) != 0) &&
|
||
|
(((pc->column - 1) % cpd.settings[UO_output_tab_size].n) == 0) &&
|
||
|
((prev->column + prev->len + 1) != pc->column));
|
||
|
}
|
||
|
|
||
|
LOG_FMT(LOUTIND, " %d -", pc->column);
|
||
|
}
|
||
|
|
||
|
output_to_column(pc->column, allow_tabs);
|
||
|
add_text_len(pc->str, pc->len);
|
||
|
cpd.did_newline = chunk_is_newline(pc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Given a multi-line comemnt block that starts in column X, figure out how
|
||
|
* much subsequent lines should be indented.
|
||
|
*
|
||
|
* The answer is either 0 or 1.
|
||
|
*
|
||
|
* The decision is based on:
|
||
|
* - the first line length
|
||
|
* - the second line leader length
|
||
|
* - the last line length
|
||
|
*
|
||
|
* If the first and last line are the same length and don't contain any alnum
|
||
|
* chars and (the first line len > 2 or the second leader is the same as the
|
||
|
* first line length), then the indent is 0.
|
||
|
*
|
||
|
* If the leader on the second line is 1 wide or missing, then the indent is 1.
|
||
|
*
|
||
|
* Otherwise, the indent is 0.
|
||
|
*
|
||
|
* @param str The comment string
|
||
|
* @param len Length of the comment
|
||
|
* @param start_col Starting column
|
||
|
* @return 0 or 1
|
||
|
*/
|
||
|
static int calculate_comment_body_indent(const char *str, int len, int start_col)
|
||
|
{
|
||
|
int idx = 0;
|
||
|
int first_len = 0;
|
||
|
int last_len = 0;
|
||
|
int width = 0;
|
||
|
|
||
|
/* find the last line length */
|
||
|
for (idx = len - 1; idx > 0; idx--)
|
||
|
{
|
||
|
if ((str[idx] == '\n') || (str[idx] == '\r'))
|
||
|
{
|
||
|
idx++;
|
||
|
|
||
|
while ((idx < len) && ((str[idx] == ' ') || (str[idx] == '\t')))
|
||
|
idx++;
|
||
|
|
||
|
last_len = len - idx;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* find the first line length */
|
||
|
for (idx = 0; idx < len; idx++)
|
||
|
{
|
||
|
if ((str[idx] == '\n') || (str[idx] == '\r'))
|
||
|
{
|
||
|
first_len = idx;
|
||
|
|
||
|
while ((str[first_len - 1] == ' ') || (str[first_len - 1] == '\t'))
|
||
|
first_len--;
|
||
|
|
||
|
/* handle DOS endings */
|
||
|
if ((str[idx] == '\r') && (str[idx + 1] == '\n'))
|
||
|
idx++;
|
||
|
|
||
|
idx++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Scan the second line */
|
||
|
width = 0;
|
||
|
|
||
|
for (/* nada */; idx < len; idx++)
|
||
|
{
|
||
|
if ((str[idx] == ' ') || (str[idx] == '\t'))
|
||
|
{
|
||
|
if (width > 0)
|
||
|
break;
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((str[idx] == '\n') || (str[idx] == '\r'))
|
||
|
/* Done with second line */
|
||
|
break;
|
||
|
|
||
|
/* Count the leading chars */
|
||
|
if ((str[idx] == '*') ||
|
||
|
(str[idx] == '|') ||
|
||
|
(str[idx] == '\\') ||
|
||
|
(str[idx] == '#') ||
|
||
|
(str[idx] == '+'))
|
||
|
width++;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//LOG_FMT(LSYS, "%s: first=%d last=%d width=%d\n", __func__, first_len, last_len, width);
|
||
|
|
||
|
/*TODO: make the first_len minimum (4) configurable? */
|
||
|
if ((first_len == last_len) && ((first_len > 4) || first_len == width))
|
||
|
return 0;
|
||
|
|
||
|
return (width == 2) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Outputs the CPP comment at pc.
|
||
|
* CPP comment combining is done here
|
||
|
*
|
||
|
* @return the last chunk output'd
|
||
|
*/
|
||
|
chunk_t *output_comment_cpp(chunk_t *first)
|
||
|
{
|
||
|
int col = first->column;
|
||
|
int col_br = 1 + (first->brace_level * cpd.settings[UO_indent_columns].n);
|
||
|
|
||
|
/* Make sure we have at least one space past the last token */
|
||
|
if (first->parent_type == CT_COMMENT_END)
|
||
|
{
|
||
|
chunk_t *prev = chunk_get_prev(first);
|
||
|
|
||
|
if (prev != NULL)
|
||
|
{
|
||
|
int col_min = prev->column + prev->len + 1;
|
||
|
|
||
|
if (col < col_min)
|
||
|
col = col_min;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Bump out to the column */
|
||
|
output_indent(col, col_br);
|
||
|
|
||
|
if (!cpd.settings[UO_cmt_cpp_to_c].b)
|
||
|
{
|
||
|
add_text_len(first->str, first->len);
|
||
|
return first;
|
||
|
}
|
||
|
|
||
|
/* If we are grouping, see if there is something to group */
|
||
|
bool combined = false;
|
||
|
|
||
|
if (cpd.settings[UO_cmt_cpp_group].b)
|
||
|
{
|
||
|
/* next is a newline by definition */
|
||
|
chunk_t *next = chunk_get_next(first);
|
||
|
|
||
|
if ((next != NULL) && (next->nl_count == 1))
|
||
|
{
|
||
|
next = chunk_get_next(next);
|
||
|
|
||
|
/**
|
||
|
* Only combine the next comment if they are both at indent level or
|
||
|
* the second one is NOT at indent or less
|
||
|
*
|
||
|
* A trailing comment cannot be combined with a comment at indent
|
||
|
* level or less
|
||
|
*/
|
||
|
if ((next != NULL) &&
|
||
|
(next->type == CT_COMMENT_CPP) &&
|
||
|
(((next->column == 1) && (first->column == 1)) ||
|
||
|
((next->column == col_br) && (first->column == col_br)) ||
|
||
|
((next->column > col_br) && (first->parent_type == CT_COMMENT_END))))
|
||
|
combined = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!combined)
|
||
|
{
|
||
|
/* nothing to group: just output a single line */
|
||
|
add_text_len("/*", 2);
|
||
|
|
||
|
if ((first->str[2] != ' ') && (first->str[2] != '\t'))
|
||
|
add_char(' ');
|
||
|
|
||
|
add_text_len(&first->str[2], first->len - 2);
|
||
|
add_text_len(" */", 3);
|
||
|
return first;
|
||
|
}
|
||
|
|
||
|
chunk_t *pc = first;
|
||
|
chunk_t *last = first;
|
||
|
|
||
|
/* Output the first line */
|
||
|
add_text_len("/*", 2);
|
||
|
|
||
|
if (combined && cpd.settings[UO_cmt_cpp_nl_start].b)
|
||
|
/* I suppose someone more clever could do this without a goto or
|
||
|
* repeating too much code...
|
||
|
*/
|
||
|
goto cpp_newline;
|
||
|
|
||
|
goto cpp_addline;
|
||
|
|
||
|
/* Output combined lines */
|
||
|
while ((pc = chunk_get_next(pc)) != NULL)
|
||
|
{
|
||
|
if ((pc->type == CT_NEWLINE) && (pc->nl_count == 1))
|
||
|
continue;
|
||
|
|
||
|
if (pc->type != CT_COMMENT_CPP)
|
||
|
break;
|
||
|
|
||
|
if (((pc->column == 1) && (first->column == 1)) ||
|
||
|
((pc->column == col_br) && (first->column == col_br)) ||
|
||
|
((pc->column > col_br) && (first->parent_type == CT_COMMENT_END)))
|
||
|
{
|
||
|
last = pc;
|
||
|
cpp_newline:
|
||
|
add_char('\n');
|
||
|
output_indent(col, col_br);
|
||
|
add_char(' ');
|
||
|
add_char(cpd.settings[UO_cmt_star_cont].b ? '*' : ' ');
|
||
|
cpp_addline:
|
||
|
|
||
|
if ((pc->str[2] != ' ') && (pc->str[2] != '\t'))
|
||
|
add_char(' ');
|
||
|
|
||
|
add_text_len(&pc->str[2], pc->len - 2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cpd.settings[UO_cmt_cpp_nl_end].b)
|
||
|
{
|
||
|
add_char('\n');
|
||
|
output_indent(col, col_br);
|
||
|
}
|
||
|
|
||
|
add_text_len(" */", 3);
|
||
|
return last;
|
||
|
}
|
||
|
|
||
|
void output_comment_multi(chunk_t *pc)
|
||
|
{
|
||
|
int cmt_col = pc->column;
|
||
|
const char *cmt_str;
|
||
|
int remaining;
|
||
|
char ch;
|
||
|
chunk_t *prev;
|
||
|
char line[1024];
|
||
|
int line_len;
|
||
|
int line_count = 0;
|
||
|
int ccol;
|
||
|
int col_diff = 0;
|
||
|
int xtra = 1;
|
||
|
|
||
|
prev = chunk_get_prev(pc);
|
||
|
|
||
|
if ((prev != NULL) && (prev->type != CT_NEWLINE))
|
||
|
cmt_col = pc->orig_col;
|
||
|
else
|
||
|
col_diff = pc->orig_col - pc->column;
|
||
|
|
||
|
// fprintf(stderr, "Indenting1 line %d to col %d (orig=%d) col_diff=%d\n",
|
||
|
// pc->orig_line, cmt_col, pc->orig_col, col_diff);
|
||
|
|
||
|
xtra = calculate_comment_body_indent(pc->str, pc->len, pc->column);
|
||
|
|
||
|
ccol = 1;
|
||
|
remaining = pc->len;
|
||
|
cmt_str = pc->str;
|
||
|
line_len = 0;
|
||
|
|
||
|
while (remaining > 0)
|
||
|
{
|
||
|
ch = *cmt_str;
|
||
|
cmt_str++;
|
||
|
remaining--;
|
||
|
|
||
|
/* handle the CRLF and CR endings. convert both to LF */
|
||
|
if (ch == '\r')
|
||
|
{
|
||
|
ch = '\n';
|
||
|
|
||
|
if (*cmt_str == '\n')
|
||
|
{
|
||
|
cmt_str++;
|
||
|
remaining--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Find the start column */
|
||
|
if (line_len == 0)
|
||
|
{
|
||
|
if (ch == ' ')
|
||
|
{
|
||
|
ccol++;
|
||
|
continue;
|
||
|
}
|
||
|
else if (ch == '\t')
|
||
|
{
|
||
|
ccol = calc_next_tab_column(ccol, cpd.settings[UO_input_tab_size].n);
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//fprintf(stderr, "%d] Text starts in col %d\n", line_count, ccol);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
line[line_len++] = ch;
|
||
|
|
||
|
/* If we just hit an end of line OR we just hit end-of-comment... */
|
||
|
if ((ch == '\n') || (remaining == 0))
|
||
|
{
|
||
|
line_count++;
|
||
|
|
||
|
/* strip trailing tabs and spaces before the newline */
|
||
|
if (ch == '\n')
|
||
|
{
|
||
|
line_len--;
|
||
|
|
||
|
while ((line_len > 0) &&
|
||
|
((line[line_len - 1] == ' ') ||
|
||
|
(line[line_len - 1] == '\t')))
|
||
|
line_len--;
|
||
|
|
||
|
line[line_len++] = ch;
|
||
|
}
|
||
|
|
||
|
line[line_len] = 0;
|
||
|
|
||
|
if (line_count == 1)
|
||
|
{
|
||
|
/* this is the first line - add unchanged */
|
||
|
|
||
|
/*TODO: need to support indent_with_tabs mode 1 */
|
||
|
output_to_column(cmt_col, cpd.settings[UO_indent_with_tabs].b);
|
||
|
add_text_len(line, line_len);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* This is not the first line, so we need to indent to the
|
||
|
* correct column.
|
||
|
*/
|
||
|
ccol -= col_diff;
|
||
|
|
||
|
if (ccol < cmt_col)
|
||
|
ccol = cmt_col;
|
||
|
|
||
|
if (line[0] == '\n')
|
||
|
{
|
||
|
/* Emtpy line - just a '\n' */
|
||
|
if (cpd.settings[UO_cmt_star_cont].b)
|
||
|
{
|
||
|
output_to_column(cmt_col, cpd.settings[UO_indent_with_tabs].b);
|
||
|
add_text((xtra == 1) ? " *" : "*");
|
||
|
}
|
||
|
|
||
|
add_char('\n');
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* If this doesn't start with a '*' or '|' */
|
||
|
if ((line[0] != '*') && (line[0] != '|') && (line[0] != '#') &&
|
||
|
(line[0] != '\\') && (line[0] != '+'))
|
||
|
{
|
||
|
output_to_column(cmt_col, cpd.settings[UO_indent_with_tabs].b);
|
||
|
|
||
|
if (cpd.settings[UO_cmt_star_cont].b)
|
||
|
add_text((xtra == 1) ? " * " : "* ");
|
||
|
else
|
||
|
add_text(" ");
|
||
|
|
||
|
output_to_column(ccol, cpd.settings[UO_indent_with_tabs].b);
|
||
|
}
|
||
|
else
|
||
|
output_to_column(cmt_col + xtra, cpd.settings[UO_indent_with_tabs].b);
|
||
|
|
||
|
add_text_len(line, line_len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
line_len = 0;
|
||
|
ccol = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|