| /** |
| * @file indent.cpp |
| * Does all the indenting stuff. |
| * |
| * $Id: indent.cpp 548 2006-10-21 02:31:55Z bengardner $ |
| */ |
| #include "uncrustify_types.h" |
| #include "chunk_list.h" |
| #include "prototypes.h" |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <cerrno> |
| #include <cctype> |
| |
| |
| /** |
| * General indenting approach: |
| * Indenting levels are put into a stack. |
| * |
| * The stack entries contain: |
| * - opening type |
| * - brace column |
| * - continuation column |
| * |
| * Items that start a new stack item: |
| * - preprocessor (new parse frame) |
| * - Brace Open (Virtual brace also) |
| * - Paren, Square, Angle open |
| * - Assignments |
| * - C++ '<<' operator (ie, cout << "blah") |
| * - case |
| * - class colon |
| * - return |
| * - types |
| * - any other continued statement |
| * |
| * Note that the column of items marked 'PCF_WAS_ALIGNED' is not changed. |
| * |
| * For an open brace: |
| * - indent increases by indent_columns |
| * - if part of if/else/do/while/switch/etc, an extra indent may be applied |
| * - if in a paren, then cont-col is set to column + 1, ie "({ some code })" |
| * |
| * Open paren/square/angle: |
| * cont-col is set to the column of the item after the open paren, unless |
| * followed by a newline, then it is set to (brace-col + indent_columns). |
| * Examples: |
| * a_really_long_funcion_name( |
| * param1, param2); |
| * a_really_long_funcion_name(param1, |
| * param2); |
| * |
| * Assignments: |
| * Assignments are continued aligned with the first item after the assignment, |
| * unless the assign is followed by a newline. |
| * Examples: |
| * some.variable = asdf + asdf + |
| * asdf; |
| * some.variable = |
| * asdf + asdf + asdf; |
| * |
| * C++ << operator: |
| * Handled the same as assignment. |
| * Examples: |
| * cout << "this is test number: " |
| * << test_number; |
| * |
| * case: |
| * Started with case or default. |
| * Terminated with close brace at level or another case or default. |
| * Special indenting according to various rules. |
| * - indent of case label |
| * - indent of case body |
| * - how to handle optional braces |
| * Examples: |
| * { |
| * case x: { |
| * a++; |
| * break; |
| * } |
| * case y: |
| * b--; |
| * break; |
| * default: |
| * c++; |
| * break; |
| * } |
| * |
| * Class colon: |
| * Indent continuation by indent_columns: |
| * class my_class : |
| * baseclass1, |
| * baseclass2 |
| * { |
| * |
| * Return: same as assignemts |
| * If the return statement is not fully paren'd, then the indent continues at |
| * the column of the item after the return. If it is paren'd, then the paren |
| * rules apply. |
| * return somevalue + |
| * othervalue; |
| * |
| * Type: pretty much the same as assignments |
| * Examples: |
| * int foo, |
| * bar, |
| * baz; |
| * |
| * Any other continued item: |
| * There shouldn't be anything not covered by the above cases, but any other |
| * continued item is indented by indent_columns: |
| * Example: |
| * somereallycrazylongname.with[lotsoflongstuff]. |
| * thatreallyannoysme.whenIhavetomaintain[thecode] = 3; |
| */ |
| |
| static void indent_comment(chunk_t *pc, int col); |
| |
| |
| void indent_to_column(chunk_t *pc, int column) |
| { |
| if (column < pc->column) |
| { |
| column = pc->column; |
| } |
| reindent_line(pc, column); |
| } |
| |
| /** |
| * Changes the initial indent for a line to the given column |
| * |
| * @param pc The chunk at the start of the line |
| * @param column The desired column |
| */ |
| void reindent_line(chunk_t *pc, int column) |
| { |
| int col_delta; |
| int min_col; |
| |
| LOG_FMT(LINDLINE, "%s: %d] col %d on %.*s [%s] => %d\n", |
| __func__, pc->orig_line, pc->column, pc->len, pc->str, |
| get_token_name(pc->type), column); |
| |
| if (column == pc->column) |
| { |
| return; |
| } |
| col_delta = column - pc->column; |
| pc->column = column; |
| min_col = pc->column; |
| |
| do |
| { |
| min_col += pc->len; |
| pc = chunk_get_next(pc); |
| if (pc != NULL) |
| { |
| if (chunk_is_comment(pc)) |
| { |
| pc->column = pc->orig_col; |
| if (pc->column < min_col) |
| { |
| pc->column = min_col + 1; |
| } |
| LOG_FMT(LINDLINE, "%s: set comment on line %d to col %d (orig %d)\n", |
| __func__, pc->orig_line, pc->column, pc->orig_col); |
| } |
| else |
| { |
| pc->column += col_delta; |
| if (pc->column < min_col) |
| { |
| pc->column = min_col; |
| } |
| } |
| } |
| } while ((pc != NULL) && (pc->nl_count == 0)); |
| } |
| |
| |
| /** |
| * Starts a new entry |
| * |
| * @param frm The parse frame |
| * @param pc The chunk causing the push |
| */ |
| static void indent_pse_push(struct parse_frame& frm, chunk_t *pc) |
| { |
| static int ref = 0; |
| |
| /* check the stack depth */ |
| if (frm.pse_tos < (int)ARRAY_SIZE(frm.pse)) |
| { |
| /* Bump up the index and initialize it */ |
| frm.pse_tos++; |
| memset(&frm.pse[frm.pse_tos], 0, sizeof(frm.pse[frm.pse_tos])); |
| |
| LOG_FMT(LINDPSE, "%4d] OPEN [%d,%s] level=%d\n", |
| pc->orig_line, frm.pse_tos, get_token_name(pc->type), pc->level); |
| |
| frm.pse[frm.pse_tos].type = pc->type; |
| frm.pse[frm.pse_tos].level = pc->level; |
| frm.pse[frm.pse_tos].open_line = pc->orig_line; |
| frm.pse[frm.pse_tos].ref = ++ref; |
| frm.pse[frm.pse_tos].in_preproc = (pc->flags & PCF_IN_PREPROC) != 0; |
| } |
| } |
| |
| |
| /** |
| * Removes the top entry |
| * |
| * @param frm The parse frame |
| * @param pc The chunk causing the push |
| */ |
| static void indent_pse_pop(struct parse_frame& frm, chunk_t *pc) |
| { |
| /* Bump up the index and initialize it */ |
| if (frm.pse_tos > 0) |
| { |
| if (pc != NULL) |
| { |
| LOG_FMT(LINDPSE, "%4d] CLOSE [%d,%s] on %s, started on line %d, level=%d/%d\n", |
| pc->orig_line, frm.pse_tos, |
| get_token_name(frm.pse[frm.pse_tos].type), |
| get_token_name(pc->type), |
| frm.pse[frm.pse_tos].open_line, |
| frm.pse[frm.pse_tos].level, |
| pc->level); |
| } |
| else |
| { |
| LOG_FMT(LINDPSE, " EOF] CLOSE [%d,%s], started on line %d\n", |
| frm.pse_tos, get_token_name(frm.pse[frm.pse_tos].type), |
| frm.pse[frm.pse_tos].open_line); |
| } |
| frm.pse_tos--; |
| } |
| } |
| |
| |
| static int token_indent(c_token_t type) |
| { |
| switch (type) |
| { |
| case CT_IF: |
| case CT_DO: |
| return(3); |
| |
| case CT_FOR: |
| case CT_ELSE: // wacky, but that's what is wanted |
| return(4); |
| |
| case CT_WHILE: |
| return(6); |
| |
| case CT_SWITCH: |
| return(7); |
| |
| case CT_ELSEIF: |
| return(8); |
| |
| default: |
| return(0); //cpd.settings[UO_indent_braces].n; |
| } |
| } |
| |
| |
| /** |
| * Change the top-level indentation only by changing the column member in |
| * the chunk structures. |
| * The level indicator must already be set. |
| */ |
| void indent_text(void) |
| { |
| chunk_t *pc; |
| chunk_t *next; |
| chunk_t *prev = NULL; |
| bool did_newline = true; |
| int idx; |
| int vardefcol = 0; |
| int indent_size = cpd.settings[UO_indent_columns].n; |
| int tmp; |
| struct parse_frame frm; |
| bool in_preproc = false, was_preproc = false; |
| int indent_column; |
| int cout_col = 0; // for aligning << stuff |
| int cout_level = 0; // for aligning << stuff |
| int parent_token_indent = 0; |
| |
| memset(&frm, 0, sizeof(frm)); |
| |
| /* dummy top-level entry */ |
| frm.pse[0].indent = 1; |
| frm.pse[0].indent_tmp = 1; |
| frm.pse[0].type = CT_EOF; |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| /* Handle proprocessor transitions */ |
| was_preproc = in_preproc; |
| in_preproc = (pc->flags & PCF_IN_PREPROC) != 0; |
| |
| if (cpd.settings[UO_indent_brace_parent].b) |
| { |
| parent_token_indent = token_indent(pc->parent_type); |
| } |
| |
| /* Clean up after a #define */ |
| if (!in_preproc) |
| { |
| while ((frm.pse_tos > 0) && frm.pse[frm.pse_tos].in_preproc) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| } |
| else |
| { |
| pf_check(&frm, pc); |
| |
| if (!was_preproc) |
| { |
| /* Transition into a preproc by creating a dummy indent */ |
| frm.level++; |
| indent_pse_push(frm, pc); |
| |
| frm.pse[frm.pse_tos].indent = 1 + indent_size; |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| } |
| } |
| |
| if ((cout_col > 0) && |
| (chunk_is_semicolon(pc) || |
| (pc->level < cout_level))) |
| { |
| cout_col = 0; |
| cout_level = 0; |
| } |
| |
| /** |
| * Handle non-brace closures |
| */ |
| |
| int old_pse_tos; |
| do |
| { |
| old_pse_tos = frm.pse_tos; |
| |
| /* End anything that drops a level |
| * REVISIT: not sure about the preproc check |
| */ |
| if (!chunk_is_newline(pc) && |
| !chunk_is_comment(pc) && |
| ((pc->flags & PCF_IN_PREPROC) == 0) && |
| (frm.pse[frm.pse_tos].level > pc->level)) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| |
| if (frm.pse[frm.pse_tos].level == pc->level) |
| { |
| /* process virtual braces closes (no text output) */ |
| if ((pc->type == CT_VBRACE_CLOSE) && |
| (frm.pse[frm.pse_tos].type == CT_VBRACE_OPEN)) |
| { |
| indent_pse_pop(frm, pc); |
| frm.level--; |
| pc = chunk_get_next(pc); |
| } |
| |
| /* End any assign operations with a semicolon on the same level */ |
| if ((frm.pse[frm.pse_tos].type == CT_ASSIGN) && |
| (chunk_is_semicolon(pc) || |
| (pc->type == CT_COMMA) || |
| (pc->type == CT_BRACE_OPEN))) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| |
| /* End any CPP class colon crap */ |
| if ((frm.pse[frm.pse_tos].type == CT_CLASS_COLON) && |
| ((pc->type == CT_BRACE_OPEN) || |
| chunk_is_semicolon(pc))) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| |
| /* a case is ended with another case or a close brace */ |
| if ((frm.pse[frm.pse_tos].type == CT_CASE) && |
| ((pc->type == CT_BRACE_CLOSE) || |
| (pc->type == CT_CASE))) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| |
| /* a return is ended with a semicolon */ |
| if ((frm.pse[frm.pse_tos].type == CT_RETURN) && |
| chunk_is_semicolon(pc)) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| |
| /* Close out parens and squares */ |
| if ((frm.pse[frm.pse_tos].type == (pc->type - 1)) && |
| ((pc->type == CT_PAREN_CLOSE) || |
| (pc->type == CT_SPAREN_CLOSE) || |
| (pc->type == CT_FPAREN_CLOSE) || |
| (pc->type == CT_SQUARE_CLOSE) || |
| (pc->type == CT_ANGLE_CLOSE))) |
| { |
| indent_pse_pop(frm, pc); |
| frm.paren_count--; |
| } |
| } |
| } while (old_pse_tos > frm.pse_tos); |
| |
| /* Grab a copy of the current indent */ |
| indent_column = frm.pse[frm.pse_tos].indent_tmp; |
| |
| if (!chunk_is_newline(pc) && !chunk_is_comment(pc)) |
| { |
| LOG_FMT(LINDPC, " -=[ %.*s ]=- top=%d %s %d/%d\n", |
| pc->len, pc->str, |
| frm.pse_tos, |
| get_token_name(frm.pse[frm.pse_tos].type), |
| frm.pse[frm.pse_tos].indent_tmp, |
| frm.pse[frm.pse_tos].indent); |
| } |
| |
| /** |
| * Handle stuff that can affect the current indent: |
| * - brace close |
| * - vbrace open |
| * - brace open |
| * - case (immediate) |
| * - labels (immediate) |
| * - class colons (immediate) |
| * |
| * And some stuff that can't |
| * - open paren |
| * - open square |
| * - assignment |
| * - return |
| */ |
| |
| if (pc->type == CT_BRACE_CLOSE) |
| { |
| if (frm.pse[frm.pse_tos].type == CT_BRACE_OPEN) |
| { |
| indent_pse_pop(frm, pc); |
| frm.level--; |
| |
| /* Update the indent_column if needed */ |
| if (!cpd.settings[UO_indent_braces].b && |
| (parent_token_indent == 0)) |
| { |
| indent_column = frm.pse[frm.pse_tos].indent_tmp; |
| } |
| |
| if ((pc->parent_type == CT_IF) || |
| (pc->parent_type == CT_ELSE) || |
| (pc->parent_type == CT_ELSEIF) || |
| (pc->parent_type == CT_DO) || |
| (pc->parent_type == CT_WHILE) || |
| (pc->parent_type == CT_SWITCH) || |
| (pc->parent_type == CT_FOR)) |
| { |
| indent_column += cpd.settings[UO_indent_brace].n; |
| } |
| } |
| } |
| else if (pc->type == CT_VBRACE_OPEN) |
| { |
| frm.level++; |
| indent_pse_push(frm, pc); |
| |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent + indent_size; |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| |
| /* Always indent on virtual braces */ |
| indent_column = frm.pse[frm.pse_tos].indent_tmp; |
| } |
| else if (pc->type == CT_BRACE_OPEN) |
| { |
| frm.level++; |
| indent_pse_push(frm, pc); |
| |
| if (frm.paren_count != 0) |
| { |
| /* We are inside ({ ... }) -- indent one tab from the paren */ |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent_tmp + indent_size; |
| } |
| else |
| { |
| /* Use the prev indent level + indent_size. */ |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent + indent_size; |
| |
| /* If this brace is part of a statement, bump it out by indent_brace */ |
| if ((pc->parent_type == CT_IF) || |
| (pc->parent_type == CT_ELSE) || |
| (pc->parent_type == CT_ELSEIF) || |
| (pc->parent_type == CT_DO) || |
| (pc->parent_type == CT_WHILE) || |
| (pc->parent_type == CT_SWITCH) || |
| (pc->parent_type == CT_FOR)) |
| { |
| if (parent_token_indent != 0) |
| { |
| frm.pse[frm.pse_tos].indent += parent_token_indent - indent_size; |
| } |
| else |
| { |
| frm.pse[frm.pse_tos].indent += cpd.settings[UO_indent_brace].n; |
| indent_column += cpd.settings[UO_indent_brace].n; |
| } |
| } |
| else if (pc->parent_type == CT_CASE) |
| { |
| /* The indent_case_brace setting affects the parent CT_CASE */ |
| frm.pse[frm.pse_tos].indent_tmp += cpd.settings[UO_indent_case_brace].n; |
| frm.pse[frm.pse_tos].indent += cpd.settings[UO_indent_case_brace].n; |
| } |
| else if ((pc->parent_type == CT_CLASS) && !cpd.settings[UO_indent_class].b) |
| { |
| frm.pse[frm.pse_tos].indent -= indent_size; |
| } |
| else if ((pc->parent_type == CT_NAMESPACE) && !cpd.settings[UO_indent_namespace].b) |
| { |
| frm.pse[frm.pse_tos].indent -= indent_size; |
| } |
| } |
| |
| if ((pc->flags & PCF_DONT_INDENT) != 0) |
| { |
| frm.pse[frm.pse_tos].indent = pc->column; |
| indent_column = pc->column; |
| } |
| else |
| { |
| /** |
| * If there isn't a newline between the open brace and the next |
| * item, just indent to wherever the next token is. |
| * This covers this sort of stuff: |
| * { a++; |
| * b--; }; |
| */ |
| next = chunk_get_next_ncnl(pc); |
| if (!chunk_is_newline_between(pc, next)) |
| { |
| frm.pse[frm.pse_tos].indent = next->column; |
| } |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| frm.pse[frm.pse_tos].open_line = pc->orig_line; |
| |
| /* Update the indent_column if needed */ |
| if (cpd.settings[UO_indent_braces].n || |
| (parent_token_indent != 0)) |
| { |
| indent_column = frm.pse[frm.pse_tos].indent_tmp; |
| } |
| } |
| } |
| else if (pc->type == CT_CASE) |
| { |
| /* Start a case - indent UO_indent_switch_case from the switch level */ |
| tmp = frm.pse[frm.pse_tos].indent + cpd.settings[UO_indent_switch_case].n; |
| |
| indent_pse_push(frm, pc); |
| |
| frm.pse[frm.pse_tos].indent = tmp; |
| frm.pse[frm.pse_tos].indent_tmp = tmp - indent_size; |
| |
| /* Always set on case statements */ |
| indent_column = frm.pse[frm.pse_tos].indent_tmp; |
| } |
| else if (pc->type == CT_LABEL) |
| { |
| /* Labels get sent to the left or backed up */ |
| if (cpd.settings[UO_indent_label].n > 0) |
| { |
| indent_column = cpd.settings[UO_indent_label].n; |
| } |
| else |
| { |
| indent_column = frm.pse[frm.pse_tos].indent + |
| cpd.settings[UO_indent_label].n; |
| } |
| } |
| else if (pc->type == CT_CLASS_COLON) |
| { |
| /* just indent one level */ |
| indent_pse_push(frm, pc); |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent_tmp + indent_size; |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| |
| indent_column = frm.pse[frm.pse_tos].indent_tmp; |
| |
| if (cpd.settings[UO_indent_class_colon].b) |
| { |
| prev = chunk_get_prev(pc); |
| if (chunk_is_newline(prev)) |
| { |
| frm.pse[frm.pse_tos].indent += 2; |
| /* don't change indent of current line */ |
| } |
| } |
| } |
| else if ((pc->type == CT_PAREN_OPEN) || |
| (pc->type == CT_SPAREN_OPEN) || |
| (pc->type == CT_FPAREN_OPEN) || |
| (pc->type == CT_SQUARE_OPEN) || |
| (pc->type == CT_ANGLE_OPEN)) |
| { |
| /* Open parens and squares - never update indent_column */ |
| indent_pse_push(frm, pc); |
| frm.pse[frm.pse_tos].indent = pc->column + pc->len; |
| |
| if (cpd.settings[UO_indent_func_call_param].b && |
| (pc->type == CT_FPAREN_OPEN) && |
| (pc->parent_type == CT_FUNC_CALL)) |
| { |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent + indent_size; |
| } |
| |
| if ((chunk_is_str(pc, "(", 1) && !cpd.settings[UO_indent_paren_nl].b) || |
| (chunk_is_str(pc, "[", 1) && !cpd.settings[UO_indent_square_nl].b)) |
| { |
| next = chunk_get_next_nc(pc); |
| if (chunk_is_newline(next)) |
| { |
| int sub = 1; |
| if (frm.pse[frm.pse_tos - 1].type == CT_ASSIGN) |
| { |
| sub = 2; |
| } |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - sub].indent + indent_size; |
| } |
| } |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| frm.paren_count++; |
| } |
| else if (pc->type == CT_ASSIGN) |
| { |
| /** |
| * if there is a newline after the '=', just indent one level, |
| * otherwise align on the '='. |
| * Never update indent_column. |
| */ |
| next = chunk_get_next(pc); |
| if (next != NULL) |
| { |
| indent_pse_push(frm, pc); |
| if (chunk_is_newline(next)) |
| { |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent_tmp + indent_size; |
| } |
| else |
| { |
| frm.pse[frm.pse_tos].indent = pc->column + pc->len + 1; |
| } |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| } |
| } |
| else if (pc->type == CT_RETURN) |
| { |
| /* don't count returns inside a () or [] */ |
| if (pc->level == pc->brace_level) |
| { |
| indent_pse_push(frm, pc); |
| frm.pse[frm.pse_tos].indent = frm.pse[frm.pse_tos - 1].indent + pc->len + 1; |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos - 1].indent; |
| } |
| } |
| else if (chunk_is_str(pc, "<<", 2)) |
| { |
| if (cout_col == 0) |
| { |
| cout_col = pc->column; |
| cout_level = pc->level; |
| } |
| } |
| else |
| { |
| /* anything else? */ |
| } |
| |
| |
| /** |
| * Indent the line if needed |
| */ |
| if (did_newline && !chunk_is_newline(pc) && (pc->len != 0)) |
| { |
| /** |
| * Check for special continuations. |
| * Note that some of these could be done as a stack item like |
| * everything else |
| */ |
| |
| prev = chunk_get_prev_ncnl(pc); |
| if ((pc->type == CT_MEMBER) || |
| (pc->type == CT_DC_MEMBER) || |
| ((prev != NULL) && |
| ((prev->type == CT_MEMBER) || |
| (prev->type == CT_DC_MEMBER)))) |
| { |
| tmp = cpd.settings[UO_indent_member].n + indent_column; |
| LOG_FMT(LINDENT, "%s: %d] member => %d\n", |
| __func__, pc->orig_line, tmp); |
| reindent_line(pc, tmp); |
| } |
| else if (chunk_is_str(pc, "<<", 2) && (cout_col > 0)) |
| { |
| LOG_FMT(LINDENT, "%s: %d] cout_col => %d\n", |
| __func__, pc->orig_line, cout_col); |
| reindent_line(pc, cout_col); |
| } |
| else if ((vardefcol > 0) && |
| (pc->type == CT_WORD) && |
| ((pc->flags & PCF_VAR_DEF) != 0) && |
| (prev != NULL) && (prev->type == CT_COMMA)) |
| { |
| LOG_FMT(LINDENT, "%s: %d] Vardefcol => %d\n", |
| __func__, pc->orig_line, vardefcol); |
| reindent_line(pc, vardefcol); |
| } |
| else if ((pc->type == CT_STRING) && (prev->type == CT_STRING) && |
| cpd.settings[UO_indent_align_string].b) |
| { |
| LOG_FMT(LINDENT, "%s: %d] String => %d\n", |
| __func__, pc->orig_line, prev->column); |
| reindent_line(pc, prev->column); |
| } |
| else if (chunk_is_comment(pc)) |
| { |
| LOG_FMT(LINDENT, "%s: %d] comment => %d\n", |
| __func__, pc->orig_line, frm.pse[frm.pse_tos].indent_tmp); |
| indent_comment(pc, frm.pse[frm.pse_tos].indent_tmp); |
| } |
| else if (pc->type == CT_PREPROC) |
| { |
| /* Preprocs are always in column 1. See indent_preproc() */ |
| if (pc->column != 1) |
| { |
| reindent_line(pc, 1); |
| } |
| } |
| else |
| { |
| if (pc->column != indent_column) |
| { |
| LOG_FMT(LINDENT, "%s: %d] indent => %d [%.*s]\n", |
| __func__, pc->orig_line, indent_column, pc->len, pc->str); |
| reindent_line(pc, indent_column); |
| } |
| } |
| did_newline = false; |
| } |
| |
| /** |
| * Handle variable definition continuation indenting |
| */ |
| if ((pc->type == CT_WORD) && |
| ((pc->flags & PCF_IN_FCN_DEF) == 0) && |
| ((pc->flags & PCF_VAR_1ST_DEF) == PCF_VAR_1ST_DEF)) |
| { |
| vardefcol = pc->column; |
| } |
| if (chunk_is_semicolon(pc) || |
| ((pc->type == CT_BRACE_OPEN) && (pc->parent_type == CT_FUNCTION))) |
| { |
| vardefcol = 0; |
| } |
| |
| /* if we hit a newline, reset indent_tmp */ |
| if (chunk_is_newline(pc) || |
| (pc->type == CT_COMMENT_MULTI) || |
| (pc->type == CT_COMMENT_CPP)) |
| { |
| frm.pse[frm.pse_tos].indent_tmp = frm.pse[frm.pse_tos].indent; |
| |
| /** |
| * Handle the case of a multi-line #define w/o anything on the |
| * first line (indent_tmp will be 1 or 0) |
| */ |
| if ((pc->type == CT_NL_CONT) && |
| (frm.pse[frm.pse_tos].indent_tmp <= indent_size)) |
| { |
| frm.pse[frm.pse_tos].indent_tmp = indent_size + 1; |
| } |
| |
| /* Get ready to indent the next item */ |
| did_newline = true; |
| } |
| |
| if (!chunk_is_comment(pc) && !chunk_is_newline(pc)) |
| { |
| prev = pc; |
| } |
| pc = chunk_get_next(pc); |
| } |
| |
| /* Throw out any stuff inside a preprocessor - no need to warn */ |
| while ((frm.pse_tos > 0) && frm.pse[frm.pse_tos].in_preproc) |
| { |
| indent_pse_pop(frm, pc); |
| } |
| |
| for (idx = 1; idx <= frm.pse_tos; idx++) |
| { |
| LOG_FMT(LWARN, "%s:%d Unmatched %s\n", |
| cpd.filename, frm.pse[idx].open_line, |
| get_token_name(frm.pse[idx].type)); |
| cpd.error_count++; |
| } |
| } |
| |
| /** |
| * returns true if forward scan reveals only single newlines or comments |
| * stops when hits code |
| * false if next thing hit is a closing brace, also if 2 newlines in a row |
| */ |
| |
| |
| static bool single_line_comment_indent_rule_applies(chunk_t *start) |
| { |
| chunk_t *pc = start; |
| int nl_count = 0; |
| if (!chunk_is_single_line_comment(pc)) |
| { |
| return(false); |
| } |
| /* scan forward, if only single newlines and comments before next line of code, we want to apply */ |
| while ((pc = chunk_get_next(pc)) != NULL) |
| { |
| if (chunk_is_newline(pc)) |
| { |
| if (nl_count > 0 || pc->nl_count > 1) |
| { |
| return(false); |
| } |
| |
| nl_count++; |
| } |
| else |
| { |
| nl_count = 0; |
| if (!chunk_is_single_line_comment(pc)) |
| { |
| /* here we check for things to run into that we wouldn't want to indent the comment for */ |
| /* for example, non-single line comment, closing brace */ |
| if (chunk_is_comment(pc) || chunk_is_closing_brace(pc)) |
| { |
| return(false); |
| } |
| |
| return(true); |
| } |
| } |
| } |
| |
| return(false); |
| } |
| |
| /** |
| * REVISIT: This needs to be re-checked, maybe cleaned up |
| * |
| * Indents comments in a (hopefully) smart manner. |
| * |
| * There are two type of comments that get indented: |
| * - stand alone (ie, no tokens on the line before the comment) |
| * - trailing comments (last token on the line apart from a linefeed) |
| * + note that a stand-alone comment is a special case of a trailing |
| * |
| * The stand alone comments will get indented in one of three ways: |
| * - column 1: |
| * + There is an empty line before the comment AND the indent level is 0 |
| * + The comment was originally in column 1 |
| * |
| * - Same column as trailing comment on previous line (ie, aligned) |
| * + if originally within TBD (3) columns of the previous comment |
| * |
| * - syntax indent level |
| * + doesn't fit in the previous categories |
| * |
| * Options modify this behavior: |
| * - keep original column (don't move the comment, if possible) |
| * - keep relative column (move out the same amount as first item on line) |
| * - fix trailing comment in column TBD |
| * |
| * @param pc The comment, which is the first item on a line |
| * @param col The column if this is to be put at indent level |
| */ |
| static void indent_comment(chunk_t *pc, int col) |
| { |
| chunk_t *nl; |
| chunk_t *prev; |
| |
| LOG_FMT(LCMTIND, "%s: line %d, col %d, level %d: ", __func__, |
| pc->orig_line, pc->orig_col, pc->level); |
| |
| /* force column 1 comment to column 1 if not changing them */ |
| if ((pc->orig_col == 1) && !cpd.settings[UO_indent_col1_comment].b) |
| { |
| LOG_FMT(LCMTIND, "rule 1 - keep in col 1\n"); |
| pc->column = 1; |
| return; |
| } |
| |
| nl = chunk_get_prev(pc); |
| |
| /* outside of any expression or statement? */ |
| if (pc->level == 0) |
| { |
| if ((nl != NULL) && (nl->nl_count > 1)) |
| { |
| LOG_FMT(LCMTIND, "rule 2 - level 0, nl before\n"); |
| pc->column = 1; |
| return; |
| } |
| } |
| |
| prev = chunk_get_prev(nl); |
| if (chunk_is_comment(prev) && (nl->nl_count == 1)) |
| { |
| int coldiff = prev->orig_col - pc->orig_col; |
| |
| if ((coldiff <= 3) && (coldiff >= -3)) |
| { |
| pc->column = prev->column; |
| LOG_FMT(LCMTIND, "rule 3 - prev comment, coldiff = %d, now in %d\n", |
| coldiff, pc->column); |
| return; |
| } |
| } |
| /* check if special single line comment rule applies */ |
| if (cpd.settings[UO_indent_sing_line_comments].n > 0 && single_line_comment_indent_rule_applies(pc)) |
| { |
| pc->column = col + cpd.settings[UO_indent_sing_line_comments].n; |
| LOG_FMT(LCMTIND, "rule 4 - single line comment indent, now in %d\n", pc->column); |
| return; |
| } |
| LOG_FMT(LCMTIND, "rule 5 - fall-through, stay in %d\n", col); |
| |
| pc->column = col; |
| } |
| |
| |
| /** |
| * Put spaces on either side of the preproc (#) symbol. |
| * This is done by pointing pc->str into pp_str and adjusting the |
| * length. |
| */ |
| void indent_preproc(void) |
| { |
| chunk_t *pc; |
| chunk_t *next; |
| int pp_level; |
| int pp_level_sub = 0; |
| int tmp; |
| |
| /* Define a string of 16 spaces + # + 16 spaces */ |
| static const char *pp_str = " # "; |
| static const char *alt_str = " %: "; |
| |
| /* Scan to see if the whole file is covered by one #ifdef */ |
| int stage = 0; |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if (chunk_is_comment(pc) || chunk_is_newline(pc)) |
| { |
| continue; |
| } |
| |
| if (stage == 0) |
| { |
| /* Check the first PP, make sure it is an #if type */ |
| if (pc->type != CT_PREPROC) |
| { |
| break; |
| } |
| next = chunk_get_next(pc); |
| if ((next == NULL) || (next->type != CT_PP_IF)) |
| { |
| break; |
| } |
| stage = 1; |
| } |
| else if (stage == 1) |
| { |
| /* Scan until a PP at level 0 is found - the close to the #if */ |
| if ((pc->type == CT_PREPROC) && |
| (pc->pp_level == 0)) |
| { |
| stage = 2; |
| } |
| continue; |
| } |
| else if (stage == 2) |
| { |
| /* We should only see the rest of the preprocessor */ |
| if ((pc->type == CT_PREPROC) || |
| ((pc->flags & PCF_IN_PREPROC) == 0)) |
| { |
| stage = 0; |
| break; |
| } |
| } |
| } |
| |
| if (stage == 2) |
| { |
| LOG_FMT(LINFO, "The whole file is covered by a #IF\n"); |
| pp_level_sub = 1; |
| } |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if (pc->type != CT_PREPROC) |
| { |
| continue; |
| } |
| |
| if (pc->column != 1) |
| { |
| /* Don't handle preprocessors that aren't in column 1 */ |
| LOG_FMT(LINFO, "%s: Line %d doesn't start in column 1 (%d)\n", |
| __func__, pc->orig_line, pc->column); |
| continue; |
| } |
| |
| /* point into pp_str */ |
| if (pc->len == 2) |
| { |
| /* alternate token crap */ |
| pc->str = &alt_str[16]; |
| } |
| else |
| { |
| pc->str = &pp_str[16]; |
| } |
| |
| pp_level = pc->pp_level - pp_level_sub; |
| if (pp_level < 0) |
| { |
| pp_level = 0; |
| } |
| else if (pp_level > 16) |
| { |
| pp_level = 16; |
| } |
| |
| /* Note that the indent is removed by default */ |
| if ((cpd.settings[UO_pp_indent].a & AV_ADD) != 0) |
| { |
| /* Need to add some spaces */ |
| pc->str -= pp_level; |
| pc->len += pp_level; |
| } |
| else if (cpd.settings[UO_pp_indent].a == AV_IGNORE) |
| { |
| tmp = (pc->orig_col <= 16) ? pc->orig_col - 1 : 16; |
| pc->str -= tmp; |
| pc->len += tmp; |
| } |
| |
| /* Add spacing by adjusting the length */ |
| if ((cpd.settings[UO_pp_space].a & AV_ADD) != 0) |
| { |
| pc->len += pp_level; |
| } |
| |
| next = chunk_get_next(pc); |
| if (next != NULL) |
| { |
| reindent_line(next, pc->len + 1); |
| } |
| |
| LOG_FMT(LPPIS, "%s: Indent line %d to %d (len %d, next->col %d)\n", |
| __func__, pc->orig_line, pp_level, pc->len, next->column); |
| } |
| } |