| /** |
| * @file align.cpp |
| * Does all the aligning stuff. |
| * |
| * @author Ben Gardner |
| * @license GPL v2+ |
| */ |
| #include "uncrustify_types.h" |
| #include "chunk_list.h" |
| #include "ChunkStack.h" |
| #include "align_stack.h" |
| #include "prototypes.h" |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <cerrno> |
| #include "unc_ctype.h" |
| |
| |
| static chunk_t *align_var_def_brace(chunk_t *pc, int span, int *nl_count); |
| static chunk_t *align_trailing_comments(chunk_t *start); |
| static void align_init_brace(chunk_t *start); |
| static void align_func_params(); |
| static void align_same_func_call_params(); |
| static void align_func_proto(int span); |
| static void align_oc_msg_spec(int span); |
| static void align_typedefs(int span); |
| static void align_left_shift(void); |
| static void align_oc_msg_colons(); |
| static void align_oc_msg_colon(chunk_t *so); |
| static void align_oc_decl_colon(void); |
| |
| |
| /* |
| * Here are the items aligned: |
| * |
| * - enum value assignments |
| * enum { |
| * cat = 1, |
| * fred = 2, |
| * }; |
| * |
| * - struct/union variable & bit definitions |
| * struct foo { |
| * char cat; |
| * int id : 5; |
| * int name_len : 6; |
| * int heigth : 12; |
| * }; |
| * |
| * - variable definitions & assignments in normal code |
| * const char *cat = "feline"; |
| * int id = 4; |
| * a = 5; |
| * bat = 14; |
| * |
| * - simple array initializers |
| * int a[] = { |
| * 1, 2, 3, 4, 5, |
| * 6, 7, 8, 9, 10 |
| * }; |
| * |
| * - c99 array initializers |
| * const char *name[] = { |
| * [FRED] = "fred", |
| * [JOE] = "joe", |
| * [PETER] = "peter", |
| * }; |
| * struct foo b[] = { |
| * { .id = 1, .name = "text 1" }, |
| * { .id = 567, .name = "text 2" }, |
| * }; |
| * struct foo_t bars[] = |
| * { |
| * [0] = { .name = "bar", |
| * .age = 21 }, |
| * [1] = { .name = "barley", |
| * .age = 55 }, |
| * }; |
| * |
| * - compact array initializers |
| * struct foo b[] = { |
| * { 3, "dog" }, { 6, "spider" }, |
| * { 8, "elephant" }, { 3, "cat" }, |
| * }; |
| * |
| * - multiline array initializers (2nd line indented, not aligned) |
| * struct foo b[] = { |
| * { AD_NOT_ALLOWED, "Sorry, you failed to guess the password.", |
| * "Try again?", "Yes", "No" }, |
| * { AD_SW_ERROR, "A software error has occured.", "Bye!", NULL, NULL }, |
| * }; |
| * |
| * - Trailing comments |
| * |
| * - Back-slash newline groups |
| * |
| * - Function prototypes |
| * int foo(); |
| * void bar(); |
| * |
| * - Preprocessors |
| * #define FOO_VAL 15 |
| * #define MAX_TIMEOUT 60 |
| * #define FOO(x) ((x) * 65) |
| * |
| * - typedefs |
| * typedef uint8_t BYTE; |
| * typedef int32_t INT32; |
| * typedef uint32_t UINT32; |
| */ |
| |
| /** |
| * Aligns everything in the chunk stack to a particular column. |
| * The stack is empty after this function. |
| * |
| * @param col the column |
| * @param align_single align even if there is only one item on the stack |
| */ |
| static void align_stack(ChunkStack& cs, int col, bool align_single, log_sev_t sev) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| |
| if (cpd.settings[UO_align_on_tabstop].b) |
| { |
| col = align_tab_column(col); |
| } |
| |
| if ((cs.Len() > 1) || (align_single && (cs.Len() == 1))) |
| { |
| LOG_FMT(sev, "%s: max_col=%d\n", __func__, col); |
| while ((pc = cs.Pop_Back()) != NULL) |
| { |
| align_to_column(pc, col); |
| pc->flags |= PCF_WAS_ALIGNED; |
| |
| LOG_FMT(sev, "%s: indented [%s] on line %d to %d\n", |
| __func__, pc->str.c_str(), pc->orig_line, pc->column); |
| } |
| } |
| cs.Reset(); |
| } |
| |
| |
| /** |
| * Adds an item to the align stack and adjust the nl_count and max_col. |
| * Adjust max_col as needed |
| * |
| * @param pc the item to add |
| * @param max_col pointer to the column variable |
| * @param extra_pad extra padding |
| */ |
| static void align_add(ChunkStack& cs, chunk_t *pc, int& max_col, int min_pad, bool squeeze) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *prev; |
| int min_col; |
| |
| prev = chunk_get_prev(pc); |
| if ((prev == NULL) || chunk_is_newline(prev)) |
| { |
| min_col = squeeze ? 1 : pc->column; |
| LOG_FMT(LALADD, "%s: pc->col=%d max_col=%d min_pad=%d min_col=%d\n", |
| __func__, pc->column, max_col, min_pad, min_col); |
| } |
| else |
| { |
| if (prev->type == CT_COMMENT_MULTI) |
| { |
| min_col = prev->orig_col_end + min_pad; |
| } |
| else |
| { |
| min_col = prev->column + prev->len() + min_pad; |
| } |
| if (!squeeze) |
| { |
| if (min_col < pc->column) |
| { |
| min_col = pc->column; |
| } |
| } |
| LOG_FMT(LALADD, "%s: pc->col=%d max_col=%d min_pad=%d min_col=%d multi:%s prev->col=%d prev->len=%d %s\n", |
| __func__, pc->column, max_col, min_pad, min_col, (prev->type == CT_COMMENT_MULTI) ? "Y" : "N", |
| (prev->type == CT_COMMENT_MULTI) ? prev->orig_col_end : prev->column, prev->len(), get_token_name(prev->type)); |
| } |
| |
| if (cs.Empty()) |
| { |
| max_col = 0; |
| } |
| |
| cs.Push_Back(pc); |
| if (min_col > max_col) |
| { |
| max_col = min_col; |
| } |
| } |
| |
| |
| void quick_align_again(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *tmp; |
| AlignStack as; |
| |
| LOG_FMT(LALAGAIN, "%s:\n", __func__); |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if ((pc->align.next != NULL) && (pc->flags & PCF_ALIGN_START)) |
| { |
| as.Start(100, 0); |
| as.m_right_align = pc->align.right_align; |
| as.m_star_style = (AlignStack::StarStyle)pc->align.star_style; |
| as.m_amp_style = (AlignStack::StarStyle)pc->align.amp_style; |
| as.m_gap = pc->align.gap; |
| |
| LOG_FMT(LALAGAIN, " [%s:%d]", pc->str.c_str(), pc->orig_line); |
| as.Add(pc->align.start); |
| pc->flags |= PCF_WAS_ALIGNED; |
| for (tmp = pc->align.next; tmp != NULL; tmp = tmp->align.next) |
| { |
| tmp->flags |= PCF_WAS_ALIGNED; |
| as.Add(tmp->align.start); |
| LOG_FMT(LALAGAIN, " => [%s:%d]", tmp->str.c_str(), tmp->orig_line); |
| } |
| LOG_FMT(LALAGAIN, "\n"); |
| as.End(); |
| } |
| } |
| } |
| |
| |
| void quick_indent_again(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *tmp; |
| |
| for (pc = chunk_get_head(); pc; pc = chunk_get_next(pc)) |
| { |
| if (pc->indent.ref) |
| { |
| tmp = chunk_get_prev(pc); |
| if (chunk_is_newline(tmp)) |
| { |
| int col = pc->indent.ref->column + pc->indent.delta; |
| |
| indent_to_column(pc, col); |
| LOG_FMT(LINDENTAG, "%s: [%d] indent [%s] to %d based on [%s] @ %d:%d\n", |
| __func__, pc->orig_line, pc->text(), col, |
| pc->indent.ref->text(), |
| pc->indent.ref->orig_line, pc->indent.ref->column); |
| } |
| } |
| } |
| } |
| |
| |
| void align_all(void) |
| { |
| LOG_FUNC_ENTRY(); |
| if (cpd.settings[UO_align_typedef_span].n > 0) |
| { |
| align_typedefs(cpd.settings[UO_align_typedef_span].n); |
| } |
| |
| if (cpd.settings[UO_align_left_shift].b) |
| { |
| align_left_shift(); |
| } |
| |
| if (cpd.settings[UO_align_oc_msg_colon_span].n > 0) |
| { |
| align_oc_msg_colons(); |
| } |
| |
| /* Align variable definitions */ |
| if ((cpd.settings[UO_align_var_def_span].n > 0) || |
| (cpd.settings[UO_align_var_struct_span].n > 0)) |
| { |
| align_var_def_brace(chunk_get_head(), cpd.settings[UO_align_var_def_span].n, NULL); |
| } |
| |
| /* Align assignments */ |
| align_assign(chunk_get_head(), |
| cpd.settings[UO_align_assign_span].n, |
| cpd.settings[UO_align_assign_thresh].n); |
| |
| /* Align structure initializers */ |
| if (cpd.settings[UO_align_struct_init_span].n > 0) |
| { |
| align_struct_initializers(); |
| } |
| |
| /* Align function prototypes */ |
| if ((cpd.settings[UO_align_func_proto_span].n > 0) && |
| !cpd.settings[UO_align_mix_var_proto].b) |
| { |
| align_func_proto(cpd.settings[UO_align_func_proto_span].n); |
| } |
| |
| /* Align function prototypes */ |
| if (cpd.settings[UO_align_oc_msg_spec_span].n > 0) |
| { |
| align_oc_msg_spec(cpd.settings[UO_align_oc_msg_spec_span].n); |
| } |
| |
| /* Align OC colons */ |
| if (cpd.settings[UO_align_oc_decl_colon].b) |
| { |
| align_oc_decl_colon(); |
| } |
| |
| /* Align variable defs in function prototypes */ |
| if (cpd.settings[UO_align_func_params].b) |
| { |
| align_func_params(); |
| } |
| |
| if (cpd.settings[UO_align_same_func_call_params].b) |
| { |
| align_same_func_call_params(); |
| } |
| /* Just in case something was aligned out of order... do it again */ |
| quick_align_again(); |
| } |
| |
| |
| /** |
| * Aligns all function prototypes in the file. |
| */ |
| static void align_oc_msg_spec(int span) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| AlignStack as; |
| |
| LOG_FMT(LALIGN, "%s\n", __func__); |
| as.Start(span, 0); |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if (chunk_is_newline(pc)) |
| { |
| as.NewLines(pc->nl_count); |
| } |
| else if (pc->type == CT_OC_MSG_SPEC) |
| { |
| as.Add(pc); |
| } |
| } |
| as.End(); |
| } |
| |
| |
| /** |
| * Aligns all backslash-newline combos in the file. |
| * This should be done LAST. |
| */ |
| void align_backslash_newline(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| if (pc->type != CT_NL_CONT) |
| { |
| pc = chunk_get_next_type(pc, CT_NL_CONT, -1); |
| continue; |
| } |
| pc = align_nl_cont(pc); |
| } |
| } |
| |
| |
| void align_right_comments(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *prev; |
| bool skip; |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if ((pc->type == CT_COMMENT) || |
| (pc->type == CT_COMMENT_CPP) || |
| (pc->type == CT_COMMENT_MULTI)) |
| { |
| skip = false; |
| if (pc->parent_type == CT_COMMENT_END) |
| { |
| prev = chunk_get_prev(pc); |
| if (pc->orig_col < (prev->orig_col_end + cpd.settings[UO_align_right_cmt_gap].n)) |
| { |
| LOG_FMT(LALTC, "NOT changing END comment on line %d (%d <= %d + %d)\n", |
| pc->orig_line, |
| pc->orig_col, prev->orig_col_end, cpd.settings[UO_align_right_cmt_gap].n); |
| skip = true; |
| } |
| if (!skip) |
| { |
| LOG_FMT(LALTC, "Changing END comment on line %d into a RIGHT-comment\n", |
| pc->orig_line); |
| pc->flags |= PCF_RIGHT_COMMENT; |
| } |
| } |
| |
| /* Change certain WHOLE comments into RIGHT-alignable comments */ |
| if (pc->parent_type == CT_COMMENT_WHOLE) |
| { |
| int max_col = pc->column_indent + cpd.settings[UO_input_tab_size].n; |
| |
| /* If the comment is further right than the brace level... */ |
| if (pc->column >= max_col) |
| { |
| LOG_FMT(LALTC, "Changing WHOLE comment on line %d into a RIGHT-comment (col=%d col_ind=%d max_col=%d)\n", |
| pc->orig_line, pc->column, pc->column_indent, max_col); |
| |
| pc->flags |= PCF_RIGHT_COMMENT; |
| } |
| } |
| } |
| } |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| if ((pc->flags & PCF_RIGHT_COMMENT) != 0) |
| { |
| pc = align_trailing_comments(pc); |
| } |
| else |
| { |
| pc = chunk_get_next(pc); |
| } |
| } |
| } |
| |
| |
| /** |
| * Aligns stuff inside a multi-line "= { ... }" sequence. |
| */ |
| void align_struct_initializers(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *prev; |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| prev = chunk_get_prev_ncnl(pc); |
| if ((prev != NULL) && (prev->type == CT_ASSIGN) && |
| ((pc->type == CT_BRACE_OPEN) || |
| ((cpd.lang_flags & LANG_D) && (pc->type == CT_SQUARE_OPEN)))) |
| { |
| align_init_brace(pc); |
| } |
| pc = chunk_get_next_type(pc, CT_BRACE_OPEN, -1); |
| } |
| } |
| |
| |
| /** |
| * Scans the whole file for #defines. Aligns all within X lines of each other |
| */ |
| void align_preprocessor(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| AlignStack as; // value macros |
| AlignStack asf; // function macros |
| AlignStack *cur_as = &as; |
| |
| as.Start(cpd.settings[UO_align_pp_define_span].n); |
| as.m_gap = cpd.settings[UO_align_pp_define_gap].n; |
| |
| asf.Start(cpd.settings[UO_align_pp_define_span].n); |
| asf.m_gap = cpd.settings[UO_align_pp_define_gap].n; |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| /* Note: not counting back-slash newline combos */ |
| if (pc->type == CT_NEWLINE) |
| { |
| as.NewLines(pc->nl_count); |
| asf.NewLines(pc->nl_count); |
| } |
| |
| /* If we aren't on a 'define', then skip to the next non-comment */ |
| if (pc->type != CT_PP_DEFINE) |
| { |
| pc = chunk_get_next_nc(pc); |
| continue; |
| } |
| |
| /* step past the 'define' */ |
| pc = chunk_get_next_nc(pc); |
| if (pc == NULL) |
| { |
| break; |
| } |
| |
| LOG_FMT(LALPP, "%s: define (%s) on line %d col %d\n", |
| __func__, pc->str.c_str(), pc->orig_line, pc->orig_col); |
| |
| cur_as = &as; |
| if (pc->type == CT_MACRO_FUNC) |
| { |
| if (!cpd.settings[UO_align_pp_define_together].b) |
| { |
| cur_as = &asf; |
| } |
| |
| /* Skip to the close paren */ |
| pc = chunk_get_next_nc(pc); // point to open ( |
| pc = chunk_get_next_type(pc, CT_FPAREN_CLOSE, pc->level); |
| |
| LOG_FMT(LALPP, "%s: jumped to (%s) on line %d col %d\n", |
| __func__, pc->str.c_str(), pc->orig_line, pc->orig_col); |
| } |
| |
| /* step to the value past the close paren or the macro name */ |
| pc = chunk_get_next(pc); |
| if (pc == NULL) |
| { |
| break; |
| } |
| |
| /* don't align anything if the first line ends with a newline before |
| * a value is given */ |
| if (!chunk_is_newline(pc)) |
| { |
| LOG_FMT(LALPP, "%s: align on '%s', line %d col %d\n", |
| __func__, pc->str.c_str(), pc->orig_line, pc->orig_col); |
| |
| cur_as->Add(pc); |
| } |
| } |
| |
| as.End(); |
| asf.End(); |
| } |
| |
| |
| /** |
| * Aligns all assignment operators on the same level as first, starting with |
| * first. |
| * |
| * For variable definitions, only consider the '=' for the first variable. |
| * Otherwise, only look at the first '=' on the line. |
| */ |
| chunk_t *align_assign(chunk_t *first, int span, int thresh) |
| { |
| LOG_FUNC_ENTRY(); |
| int my_level; |
| chunk_t *pc; |
| int tmp; |
| int var_def_cnt = 0; |
| int equ_count = 0; |
| |
| if (first == NULL) |
| { |
| return(NULL); |
| } |
| my_level = first->level; |
| |
| if (span <= 0) |
| { |
| return(chunk_get_next(first)); |
| } |
| |
| LOG_FMT(LALASS, "%s[%d]: checking %s on line %d - span=%d thresh=%d\n", |
| __func__, my_level, first->str.c_str(), first->orig_line, |
| span, thresh); |
| |
| AlignStack as; // regular assigns |
| AlignStack vdas; // variable def assigns |
| |
| /* If we are aligning on a tabstop, we shouldn't right-align */ |
| as.Start(span, thresh); |
| as.m_right_align = !cpd.settings[UO_align_on_tabstop].b; |
| vdas.Start(span, thresh); |
| vdas.m_right_align = as.m_right_align; |
| |
| pc = first; |
| while ((pc != NULL) && ((pc->level >= my_level) || (pc->level == 0))) |
| { |
| /* Don't check inside PAREN or SQUARE groups */ |
| if ((pc->type == CT_SPAREN_OPEN) || |
| (pc->type == CT_FPAREN_OPEN) || |
| (pc->type == CT_SQUARE_OPEN) || |
| (pc->type == CT_PAREN_OPEN)) |
| { |
| tmp = pc->orig_line; |
| pc = chunk_skip_to_match(pc); |
| if (pc != NULL) |
| { |
| as.NewLines(pc->orig_line - tmp); |
| vdas.NewLines(pc->orig_line - tmp); |
| } |
| continue; |
| } |
| |
| /* Recurse if a brace set is found */ |
| if ((pc->type == CT_BRACE_OPEN) || |
| (pc->type == CT_VBRACE_OPEN)) |
| { |
| int myspan; |
| int mythresh; |
| |
| tmp = pc->orig_line; |
| |
| if (pc->parent_type == CT_ENUM) |
| { |
| myspan = cpd.settings[UO_align_enum_equ_span].n; |
| mythresh = cpd.settings[UO_align_enum_equ_thresh].n; |
| } |
| else |
| { |
| myspan = cpd.settings[UO_align_assign_span].n; |
| mythresh = cpd.settings[UO_align_assign_thresh].n; |
| } |
| |
| pc = align_assign(chunk_get_next_ncnl(pc), myspan, mythresh); |
| if (pc != NULL) |
| { |
| /* do a rough count of the number of lines just spanned */ |
| as.NewLines(pc->orig_line - tmp); |
| vdas.NewLines(pc->orig_line - tmp); |
| } |
| continue; |
| } |
| |
| if (chunk_is_newline(pc)) |
| { |
| as.NewLines(pc->nl_count); |
| vdas.NewLines(pc->nl_count); |
| |
| var_def_cnt = 0; |
| equ_count = 0; |
| } |
| else if ((pc->flags & PCF_VAR_DEF) != 0) |
| { |
| var_def_cnt++; |
| } |
| else if (var_def_cnt > 1) |
| { |
| /* we hit the second variable def - don't look for assigns */ |
| } |
| else if ((equ_count == 0) && (pc->type == CT_ASSIGN)) |
| { |
| //fprintf(stderr, "%s: ** %s level=%d line=%d col=%d prev=%d count=%d\n", |
| // __func__, pc->str, pc->level, pc->orig_line, pc->orig_col, prev_equ_type, |
| // equ_count); |
| |
| equ_count++; |
| if (var_def_cnt != 0) |
| { |
| vdas.Add(pc); |
| } |
| else |
| { |
| as.Add(pc); |
| } |
| } |
| |
| pc = chunk_get_next(pc); |
| } |
| |
| as.End(); |
| vdas.End(); |
| |
| if (pc != NULL) |
| { |
| LOG_FMT(LALASS, "%s: done on %s on line %d\n", |
| __func__, pc->str.c_str(), pc->orig_line); |
| } |
| else |
| { |
| LOG_FMT(LALASS, "%s: done on NULL\n", __func__); |
| } |
| |
| return(pc); |
| } |
| |
| |
| /** |
| * Counts how many '*' pointers are in a row, going backwards |
| * |
| * @param pc Pointer to the last '*' in the series |
| * @return The count, including the current one |
| */ |
| int count_prev_ptr_type(chunk_t *pc) |
| { |
| int count = 0; |
| |
| while ((pc != NULL) && (pc->type == CT_PTR_TYPE)) |
| { |
| count++; |
| pc = chunk_get_prev_nc(pc); |
| } |
| return(count); |
| } |
| |
| |
| static chunk_t *align_func_param(chunk_t *start) |
| { |
| LOG_FUNC_ENTRY(); |
| AlignStack as; |
| chunk_t *pc = start; |
| |
| as.Start(2, 0); |
| |
| as.m_star_style = (AlignStack::StarStyle)cpd.settings[UO_align_var_def_star_style].n; |
| as.m_amp_style = (AlignStack::StarStyle)cpd.settings[UO_align_var_def_amp_style].n; |
| |
| bool did_this_line = false; |
| int comma_count = 0; |
| int chunk_count = 0; |
| |
| while ((pc = chunk_get_next(pc)) != NULL) |
| { |
| chunk_count++; |
| if (chunk_is_newline(pc)) |
| { |
| did_this_line = false; |
| comma_count = 0; |
| chunk_count = 0; |
| } |
| else if (pc->level <= start->level) |
| { |
| break; |
| } |
| else if (!did_this_line && (pc->flags & PCF_VAR_DEF)) |
| { |
| if (chunk_count > 1) |
| { |
| as.Add(pc); |
| } |
| did_this_line = true; |
| } |
| else if (comma_count > 0) |
| { |
| if (!chunk_is_comment(pc)) |
| { |
| comma_count = 2; |
| break; |
| } |
| } |
| else if (pc->type == CT_COMMA) |
| { |
| comma_count++; |
| } |
| } |
| |
| if (comma_count <= 1) |
| { |
| as.End(); |
| } |
| |
| return(pc); |
| } |
| |
| |
| static void align_func_params() |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| |
| pc = chunk_get_head(); |
| while ((pc = chunk_get_next(pc)) != NULL) |
| { |
| if ((pc->type != CT_FPAREN_OPEN) || |
| ((pc->parent_type != CT_FUNC_PROTO) && |
| (pc->parent_type != CT_FUNC_DEF) && |
| (pc->parent_type != CT_FUNC_CLASS_PROTO) && |
| (pc->parent_type != CT_FUNC_CLASS_DEF) && |
| (pc->parent_type != CT_TYPEDEF))) |
| { |
| continue; |
| } |
| |
| /* We're on a open paren of a prototype */ |
| pc = align_func_param(pc); |
| } |
| } |
| |
| |
| static void align_params(chunk_t *start, deque<chunk_t *>& chunks) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc = start; |
| bool hit_comma = true; |
| |
| chunks.clear(); |
| |
| pc = chunk_get_next_type(start, CT_FPAREN_OPEN, start->level); |
| |
| while ((pc = chunk_get_next(pc)) != NULL) |
| { |
| if (chunk_is_newline(pc) || |
| (pc->type == CT_SEMICOLON) || |
| ((pc->type == CT_FPAREN_CLOSE) && (pc->level == start->level))) |
| { |
| break; |
| } |
| |
| if (pc->level == (start->level + 1)) |
| { |
| if (hit_comma) |
| { |
| chunks.push_back(pc); |
| hit_comma = false; |
| } |
| else if (pc->type == CT_COMMA) |
| { |
| hit_comma = true; |
| } |
| } |
| } |
| } |
| |
| |
| static void align_same_func_call_params() |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *align_root = NULL; |
| chunk_t *align_cur = NULL; |
| int align_len = 0; |
| chunk_t *align_fcn; |
| unc_text align_fcn_name; |
| unc_text align_root_name; |
| deque<chunk_t *> chunks; |
| deque<AlignStack> as; |
| AlignStack fcn_as; |
| int idx; |
| const char *add_str; |
| |
| fcn_as.Start(3); |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if (pc->type != CT_FUNC_CALL) |
| { |
| if (chunk_is_newline(pc)) |
| { |
| for (idx = 0; idx < (int)as.size(); idx++) |
| { |
| as[idx].NewLines(pc->nl_count); |
| } |
| fcn_as.NewLines(pc->nl_count); |
| } |
| else |
| { |
| /* if we drop below the brace level that started it, we are done */ |
| if (align_root && (align_root->brace_level > pc->brace_level)) |
| { |
| LOG_FMT(LASFCP, " ++ (drop) Ended with %d fcns\n", align_len); |
| |
| /* Flush it all! */ |
| fcn_as.Flush(); |
| for (idx = 0; idx < (int)as.size(); idx++) |
| { |
| as[idx].Flush(); |
| } |
| align_root = NULL; |
| } |
| } |
| continue; |
| } |
| |
| /* Only align function calls that are right after a newline */ |
| chunk_t *prev = chunk_get_prev(pc); |
| while (chunk_is_token(prev, CT_MEMBER) || chunk_is_token(prev, CT_DC_MEMBER)) |
| { |
| chunk_t *tprev = chunk_get_prev(prev); |
| if (!chunk_is_token(tprev, CT_TYPE)) |
| { |
| prev = tprev; |
| break; |
| } |
| prev = chunk_get_prev(tprev); |
| } |
| if (!chunk_is_newline(prev)) |
| { |
| continue; |
| } |
| prev = chunk_get_next(prev); |
| align_fcn = prev; |
| align_fcn_name.clear(); |
| while (prev != pc) |
| { |
| align_fcn_name += prev->str; |
| prev = chunk_get_next(prev); |
| } |
| align_fcn_name += pc->str; |
| LOG_FMT(LASFCP, "Func Call @ %d:%d [%s]\n", |
| align_fcn->orig_line, |
| align_fcn->orig_col, |
| align_fcn_name.c_str()); |
| |
| add_str = NULL; |
| if (align_root != NULL) |
| { |
| /* can only align functions on the same brace level */ |
| if ((align_root->brace_level == pc->brace_level) && |
| align_fcn_name.equals(align_root_name)) |
| { |
| fcn_as.Add(pc); |
| align_cur->align.next = pc; |
| align_cur = pc; |
| align_len++; |
| add_str = " Add"; |
| } |
| else |
| { |
| LOG_FMT(LASFCP, " ++ Ended with %d fcns\n", align_len); |
| |
| /* Flush it all! */ |
| fcn_as.Flush(); |
| for (idx = 0; idx < (int)as.size(); idx++) |
| { |
| as[idx].Flush(); |
| } |
| align_root = NULL; |
| } |
| } |
| |
| if (align_root == NULL) |
| { |
| fcn_as.Add(pc); |
| align_root = align_fcn; |
| align_root_name = align_fcn_name; |
| align_cur = pc; |
| align_len = 1; |
| add_str = "Start"; |
| } |
| |
| if (add_str != NULL) |
| { |
| LOG_FMT(LASFCP, "%s '%s' on line %d -", |
| add_str, align_fcn_name.c_str(), pc->orig_line); |
| align_params(pc, chunks); |
| LOG_FMT(LASFCP, " %d items:", (int)chunks.size()); |
| |
| for (idx = 0; idx < (int)chunks.size(); idx++) |
| { |
| LOG_FMT(LASFCP, " [%s]", chunks[idx]->str.c_str()); |
| if (idx >= (int)as.size()) |
| { |
| as.resize(idx + 1); |
| as[idx].Start(3); |
| if (!cpd.settings[UO_align_number_left].b) |
| { |
| if ((chunks[idx]->type == CT_NUMBER_FP) || |
| (chunks[idx]->type == CT_NUMBER) || |
| (chunks[idx]->type == CT_POS) || |
| (chunks[idx]->type == CT_NEG)) |
| { |
| as[idx].m_right_align = !cpd.settings[UO_align_on_tabstop].b; |
| } |
| } |
| } |
| as[idx].Add(chunks[idx]); |
| } |
| LOG_FMT(LASFCP, "\n"); |
| } |
| } |
| |
| if (align_len > 1) |
| { |
| LOG_FMT(LASFCP, " ++ Ended with %d fcns\n", align_len); |
| fcn_as.End(); |
| for (idx = 0; idx < (int)as.size(); idx++) |
| { |
| as[idx].End(); |
| } |
| } |
| } |
| |
| |
| chunk_t *step_back_over_member(chunk_t *pc) |
| { |
| chunk_t *tmp; |
| |
| /* Skip over any class stuff: bool CFoo::bar() */ |
| while (((tmp = chunk_get_prev_ncnl(pc)) != NULL) && |
| (tmp->type == CT_DC_MEMBER)) |
| { |
| /* TODO: verify that we are pointing at something sane? */ |
| pc = chunk_get_prev_ncnl(tmp); |
| } |
| return(pc); |
| } |
| |
| |
| /** |
| * Aligns all function prototypes in the file. |
| */ |
| static void align_func_proto(int span) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *toadd; |
| bool look_bro = false; |
| AlignStack as; |
| AlignStack as_br; |
| |
| LOG_FMT(LALIGN, "%s\n", __func__); |
| as.Start(span, 0); |
| as.m_gap = cpd.settings[UO_align_func_proto_gap].n; |
| as.m_star_style = (AlignStack::StarStyle)cpd.settings[UO_align_var_def_star_style].n; |
| as.m_amp_style = (AlignStack::StarStyle)cpd.settings[UO_align_var_def_amp_style].n; |
| |
| as_br.Start(span, 0); |
| as_br.m_gap = cpd.settings[UO_align_single_line_brace_gap].n; |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if (chunk_is_newline(pc)) |
| { |
| look_bro = false; |
| as.NewLines(pc->nl_count); |
| as_br.NewLines(pc->nl_count); |
| } |
| else if ((pc->type == CT_FUNC_PROTO) || |
| ((pc->type == CT_FUNC_DEF) && |
| cpd.settings[UO_align_single_line_func].b)) |
| { |
| if ((pc->parent_type == CT_OPERATOR) && |
| cpd.settings[UO_align_on_operator].b) |
| { |
| toadd = chunk_get_prev_ncnl(pc); |
| } |
| else |
| { |
| toadd = pc; |
| } |
| as.Add(step_back_over_member(toadd)); |
| look_bro = (pc->type == CT_FUNC_DEF) && |
| cpd.settings[UO_align_single_line_brace].b; |
| } |
| else if (look_bro && |
| (pc->type == CT_BRACE_OPEN) && |
| (pc->flags & PCF_ONE_LINER)) |
| { |
| as_br.Add(pc); |
| look_bro = false; |
| } |
| } |
| as.End(); |
| as_br.End(); |
| } |
| |
| |
| /** |
| * Scan everything at the current level until the close brace and find the |
| * variable def align column. Also aligns bit-colons, but that assumes that |
| * bit-types are the same! But that should always be the case... |
| */ |
| static chunk_t *align_var_def_brace(chunk_t *start, int span, int *p_nl_count) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *next; |
| chunk_t *prev; |
| UINT64 align_mask = PCF_IN_FCN_DEF | PCF_VAR_1ST; |
| int myspan = span; |
| int mythresh = 0; |
| int mygap = 0; |
| AlignStack as; /* var/proto/def */ |
| AlignStack as_bc; /* bit-colon */ |
| AlignStack as_at; /* attribute */ |
| AlignStack as_br; /* one-liner brace open */ |
| bool fp_active = cpd.settings[UO_align_mix_var_proto].b; |
| bool fp_look_bro = false; |
| |
| |
| if (start == NULL) |
| { |
| return(NULL); |
| } |
| |
| /* Override the span, if this is a struct/union */ |
| if ((start->parent_type == CT_STRUCT) || |
| (start->parent_type == CT_UNION)) |
| { |
| myspan = cpd.settings[UO_align_var_struct_span].n; |
| mythresh = cpd.settings[UO_align_var_struct_thresh].n; |
| mygap = cpd.settings[UO_align_var_struct_gap].n; |
| } |
| else |
| { |
| mythresh = cpd.settings[UO_align_var_def_thresh].n; |
| mygap = cpd.settings[UO_align_var_def_gap].n; |
| } |
| |
| /* can't be any variable definitions in a "= {" block */ |
| prev = chunk_get_prev_ncnl(start); |
| if ((prev != NULL) && (prev->type == CT_ASSIGN)) |
| { |
| LOG_FMT(LAVDB, "%s: start=%s [%s] on line %d (abort due to assign)\n", __func__, |
| start->str.c_str(), get_token_name(start->type), start->orig_line); |
| |
| pc = chunk_get_next_type(start, CT_BRACE_CLOSE, start->level); |
| return(chunk_get_next_ncnl(pc)); |
| } |
| |
| LOG_FMT(LAVDB, "%s: start=%s [%s] on line %d\n", __func__, |
| start->str.c_str(), get_token_name(start->type), start->orig_line); |
| |
| if (!cpd.settings[UO_align_var_def_inline].b) |
| { |
| align_mask |= PCF_VAR_INLINE; |
| } |
| |
| /* Set up the var/proto/def aligner */ |
| as.Start(myspan, mythresh); |
| as.m_gap = mygap; |
| as.m_star_style = (AlignStack::StarStyle)cpd.settings[UO_align_var_def_star_style].n; |
| as.m_amp_style = (AlignStack::StarStyle)cpd.settings[UO_align_var_def_amp_style].n; |
| |
| /* Set up the bit colon aligner */ |
| as_bc.Start(myspan, 0); |
| as_bc.m_gap = cpd.settings[UO_align_var_def_colon_gap].n; |
| |
| as_at.Start(myspan, 0); |
| |
| /* Set up the brace open aligner */ |
| as_br.Start(myspan, mythresh); |
| as_br.m_gap = cpd.settings[UO_align_single_line_brace_gap].n; |
| |
| bool did_this_line = false; |
| pc = chunk_get_next(start); |
| while ((pc != NULL) && ((pc->level >= start->level) || (pc->level == 0))) |
| { |
| if (chunk_is_comment(pc)) |
| { |
| if (pc->nl_count > 0) |
| { |
| as.NewLines(pc->nl_count); |
| as_bc.NewLines(pc->nl_count); |
| as_at.NewLines(pc->nl_count); |
| as_br.NewLines(pc->nl_count); |
| } |
| pc = chunk_get_next(pc); |
| continue; |
| } |
| |
| if (fp_active && !(pc->flags & PCF_IN_CLASS_BASE)) |
| { |
| if ((pc->type == CT_FUNC_PROTO) || |
| ((pc->type == CT_FUNC_DEF) && |
| cpd.settings[UO_align_single_line_func].b)) |
| { |
| LOG_FMT(LAVDB, " add=[%s] line=%d col=%d level=%d\n", |
| pc->str.c_str(), pc->orig_line, pc->orig_col, pc->level); |
| |
| as.Add(pc); |
| fp_look_bro = (pc->type == CT_FUNC_DEF) && |
| cpd.settings[UO_align_single_line_brace].b; |
| } |
| else if (fp_look_bro && |
| (pc->type == CT_BRACE_OPEN) && |
| (pc->flags & PCF_ONE_LINER)) |
| { |
| as_br.Add(pc); |
| fp_look_bro = false; |
| } |
| } |
| |
| /* process nested braces */ |
| if (pc->type == CT_BRACE_OPEN) |
| { |
| int sub_nl_count = 0; |
| |
| pc = align_var_def_brace(pc, span, &sub_nl_count); |
| if (sub_nl_count > 0) |
| { |
| fp_look_bro = false; |
| did_this_line = false; |
| as.NewLines(sub_nl_count); |
| as_bc.NewLines(sub_nl_count); |
| as_at.NewLines(sub_nl_count); |
| as_br.NewLines(sub_nl_count); |
| if (p_nl_count != NULL) |
| { |
| *p_nl_count += sub_nl_count; |
| } |
| } |
| continue; |
| } |
| |
| /* Done with this brace set? */ |
| if (pc->type == CT_BRACE_CLOSE) |
| { |
| pc = chunk_get_next(pc); |
| break; |
| } |
| |
| if (chunk_is_newline(pc)) |
| { |
| fp_look_bro = false; |
| did_this_line = false; |
| as.NewLines(pc->nl_count); |
| as_bc.NewLines(pc->nl_count); |
| as_at.NewLines(pc->nl_count); |
| as_br.NewLines(pc->nl_count); |
| if (p_nl_count != NULL) |
| { |
| *p_nl_count += pc->nl_count; |
| } |
| } |
| |
| /* don't align stuff inside parens/squares/angles */ |
| if (pc->level > pc->brace_level) |
| { |
| pc = chunk_get_next(pc); |
| continue; |
| } |
| |
| /* If this is a variable def, update the max_col */ |
| if (!(pc->flags & PCF_IN_CLASS_BASE) && |
| (pc->type != CT_FUNC_CLASS_DEF) && |
| (pc->type != CT_FUNC_CLASS_PROTO) && |
| ((pc->flags & align_mask) == PCF_VAR_1ST) && |
| ((pc->level == (start->level + 1)) || |
| (pc->level == 0)) && |
| pc->prev && (pc->prev->type != CT_MEMBER)) |
| { |
| if (!did_this_line) |
| { |
| LOG_FMT(LAVDB, " add=[%s] line=%d col=%d level=%d\n", |
| pc->str.c_str(), pc->orig_line, pc->orig_col, pc->level); |
| |
| as.Add(step_back_over_member(pc)); |
| |
| if (cpd.settings[UO_align_var_def_colon].b) |
| { |
| next = chunk_get_next_nc(pc); |
| if (next->type == CT_BIT_COLON) |
| { |
| as_bc.Add(next); |
| } |
| } |
| if (cpd.settings[UO_align_var_def_attribute].b) |
| { |
| next = pc; |
| while ((next = chunk_get_next_nc(next)) != NULL) |
| { |
| if (next->type == CT_ATTRIBUTE) |
| { |
| as_at.Add(next); |
| break; |
| } |
| if ((next->type == CT_SEMICOLON) || chunk_is_newline(next)) |
| { |
| break; |
| } |
| } |
| } |
| } |
| did_this_line = true; |
| } |
| else if (pc->type == CT_BIT_COLON) |
| { |
| if (!did_this_line) |
| { |
| as_bc.Add(pc); |
| did_this_line = true; |
| } |
| } |
| pc = chunk_get_next(pc); |
| } |
| |
| as.End(); |
| as_bc.End(); |
| as_at.End(); |
| as_br.End(); |
| |
| return(pc); |
| } |
| |
| |
| /** |
| * For a series of lines ending in backslash-newline, align them. |
| * The series ends when a newline or multi-line C comment is encountered. |
| * |
| * @param start Start point |
| * @return pointer the last item looked at (NULL/newline/comment) |
| */ |
| chunk_t *align_nl_cont(chunk_t *start) |
| { |
| LOG_FUNC_ENTRY(); |
| int max_col = 0; |
| chunk_t *pc = start; |
| chunk_t *tmp; |
| ChunkStack cs; |
| |
| LOG_FMT(LALNLC, "%s: start on [%s] on line %d\n", __func__, |
| get_token_name(start->type), start->orig_line); |
| |
| /* Find the max column */ |
| while ((pc != NULL) && |
| (pc->type != CT_NEWLINE) && |
| (pc->type != CT_COMMENT_MULTI)) |
| { |
| if (pc->type == CT_NL_CONT) |
| { |
| align_add(cs, pc, max_col, 1, true); |
| } |
| pc = chunk_get_next(pc); |
| } |
| |
| /* NL_CONT is always the last thing on a line */ |
| while ((tmp = cs.Pop_Back()) != NULL) |
| { |
| tmp->flags |= PCF_WAS_ALIGNED; |
| tmp->column = max_col; |
| } |
| |
| return(pc); |
| } |
| |
| |
| enum CmtAlignType |
| { |
| CAT_REGULAR, |
| CAT_BRACE, |
| CAT_ENDIF, |
| }; |
| |
| static CmtAlignType get_comment_align_type(chunk_t *cmt) |
| { |
| chunk_t *prev; |
| CmtAlignType cmt_type = CAT_REGULAR; |
| |
| if (!cpd.settings[UO_align_right_cmt_mix].b && |
| ((prev = chunk_get_prev(cmt)) != NULL)) |
| { |
| if ((prev->type == CT_PP_ENDIF) || |
| (prev->type == CT_PP_ELSE) || |
| (prev->type == CT_ELSE) || |
| (prev->type == CT_BRACE_CLOSE)) |
| { |
| /* REVISIT: someone may want this configurable */ |
| if ((cmt->column - (prev->column + prev->len())) < 3) |
| { |
| cmt_type = (prev->type == CT_PP_ENDIF) ? CAT_ENDIF : CAT_BRACE; |
| } |
| } |
| } |
| return(cmt_type); |
| } |
| |
| |
| /** |
| * For a series of lines ending in a comment, align them. |
| * The series ends when more than align_right_cmt_span newlines are found. |
| * |
| * Interesting info: |
| * - least physically allowed column |
| * - intended column |
| * - least original cmt column |
| * |
| * min_col is the minimum allowed column (based on prev token col/size) |
| * cmt_col less than |
| * |
| * @param start Start point |
| * @return pointer the last item looked at |
| */ |
| chunk_t *align_trailing_comments(chunk_t *start) |
| { |
| LOG_FUNC_ENTRY(); |
| int min_col = 0; |
| int min_orig = -1; |
| chunk_t *pc = start; |
| int nl_count = 0; |
| ChunkStack cs; |
| CmtAlignType cmt_type_start, cmt_type_cur; |
| int col; |
| int intended_col = cpd.settings[UO_align_right_cmt_at_col].n; |
| |
| cmt_type_start = get_comment_align_type(pc); |
| |
| LOG_FMT(LALADD, "%s: start on line=%d\n", __func__, pc->orig_line); |
| |
| /* Find the max column */ |
| while ((pc != NULL) && (nl_count < cpd.settings[UO_align_right_cmt_span].n)) |
| { |
| if (((pc->flags & PCF_RIGHT_COMMENT) != 0) && (pc->column > 1)) |
| { |
| cmt_type_cur = get_comment_align_type(pc); |
| |
| if (cmt_type_cur == cmt_type_start) |
| { |
| col = 1 + (pc->brace_level * cpd.settings[UO_indent_columns].n); |
| LOG_FMT(LALADD, "%s: line=%d col=%d max_col=%d pc->col=%d pc->len=%d %s\n", |
| __func__, pc->orig_line, col, min_col, pc->column, pc->len(), |
| get_token_name(pc->type)); |
| if ((min_orig < 0) || (min_orig > pc->column)) |
| { |
| min_orig = pc->column; |
| } |
| if (pc->column < col) |
| { |
| pc->column = col; |
| } |
| align_add(cs, pc, min_col, 1, true);//(intended_col < col)); |
| nl_count = 0; |
| } |
| } |
| if (chunk_is_newline(pc)) |
| { |
| nl_count += pc->nl_count; |
| } |
| pc = chunk_get_next(pc); |
| } |
| |
| /* Start with the minimum original column */ |
| col = min_orig; |
| /* fall back to the intended column */ |
| if ((intended_col > 0) && (col > intended_col)) |
| { |
| col = intended_col; |
| } |
| /* if less than allowed, bump it out */ |
| if (col < min_col) |
| { |
| col = min_col; |
| } |
| /* bump out to the intended column */ |
| if (col < intended_col) |
| { |
| col = intended_col; |
| } |
| LOG_FMT(LALADD, "%s: -- min_orig=%d intend=%d min_allowed=%d ==> col=%d\n", __func__, |
| min_orig, intended_col, min_col, col); |
| align_stack(cs, col, (intended_col != 0), LALTC); |
| |
| return(chunk_get_next(pc)); |
| } |
| |
| |
| /** |
| * Shifts out all columns by a certain amount. |
| * |
| * @param idx The index to start shifting |
| * @param num The number of columns to shift |
| */ |
| void ib_shift_out(int idx, int num) |
| { |
| while (idx < cpd.al_cnt) |
| { |
| cpd.al[idx].col += num; |
| idx++; |
| } |
| } |
| |
| |
| /** |
| * If sq_open is CT_SQUARE_OPEN and the matching close is followed by '=', |
| * then return the chunk after the '='. Otherwise, return NULL. |
| */ |
| static chunk_t *skip_c99_array(chunk_t *sq_open) |
| { |
| if (chunk_is_token(sq_open, CT_SQUARE_OPEN)) |
| { |
| chunk_t *tmp = chunk_get_next_nc(chunk_skip_to_match(sq_open)); |
| |
| if (chunk_is_token(tmp, CT_ASSIGN)) |
| { |
| return chunk_get_next_nc(tmp); |
| } |
| } |
| return NULL; |
| } |
| |
| |
| /** |
| * Scans a line for stuff to align on. |
| * |
| * We trigger on BRACE_OPEN, FPAREN_OPEN, ASSIGN, and COMMA. |
| * We want to align the NEXT item. |
| */ |
| static chunk_t *scan_ib_line(chunk_t *start, bool first_pass) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *next; |
| chunk_t *prev_match = NULL; |
| chunk_t *tmp; |
| int token_width; |
| int idx = 0; |
| |
| /* Skip past C99 "[xx] =" stuff */ |
| tmp = skip_c99_array(start); |
| if (tmp) |
| { |
| set_chunk_parent(start, CT_TSQUARE); |
| start = tmp; |
| cpd.al_c99_array = true; |
| } |
| pc = start; |
| |
| if (pc != NULL) |
| { |
| LOG_FMT(LSIB, "%s: start=%s col %d/%d line %d\n", __func__, |
| get_token_name(pc->type), pc->column, pc->orig_col, pc->orig_line); |
| } |
| |
| while ((pc != NULL) && !chunk_is_newline(pc) && |
| (pc->level >= start->level)) |
| { |
| //LOG_FMT(LSIB, "%s: '%s' col %d/%d line %d\n", __func__, |
| // pc->str.c_str(), pc->column, pc->orig_col, pc->orig_line); |
| |
| next = chunk_get_next(pc); |
| if ((next == NULL) || chunk_is_comment(next)) |
| { |
| /* do nothing */ |
| } |
| else if ((pc->type == CT_ASSIGN) || |
| (pc->type == CT_BRACE_OPEN) || |
| (pc->type == CT_BRACE_CLOSE) || |
| (pc->type == CT_COMMA)) |
| { |
| token_width = space_col_align(pc, next); |
| |
| /*TODO: need to handle missing structure defs? ie NULL vs { ... } ?? */ |
| |
| /* Is this a new entry? */ |
| if (idx >= cpd.al_cnt) |
| { |
| LOG_FMT(LSIB, " - New [%d] %.2d/%d - %10.10s\n", idx, |
| pc->column, token_width, get_token_name(pc->type)); |
| |
| cpd.al[cpd.al_cnt].type = pc->type; |
| cpd.al[cpd.al_cnt].col = pc->column; |
| cpd.al[cpd.al_cnt].len = token_width; |
| cpd.al_cnt++; |
| idx++; |
| } |
| else |
| { |
| /* expect to match stuff */ |
| if (cpd.al[idx].type == pc->type) |
| { |
| LOG_FMT(LSIB, " - Match [%d] %.2d/%d - %10.10s", idx, |
| pc->column, token_width, get_token_name(pc->type)); |
| |
| /* Shift out based on column */ |
| if (prev_match == NULL) |
| { |
| if (pc->column > cpd.al[idx].col) |
| { |
| LOG_FMT(LSIB, " [ pc->col(%d) > col(%d) ] ", |
| pc->column, cpd.al[idx].col); |
| |
| ib_shift_out(idx, pc->column - cpd.al[idx].col); |
| cpd.al[idx].col = pc->column; |
| } |
| } |
| else if (idx > 0) |
| { |
| int min_col_diff = pc->column - prev_match->column; |
| int cur_col_diff = cpd.al[idx].col - cpd.al[idx - 1].col; |
| if (cur_col_diff < min_col_diff) |
| { |
| LOG_FMT(LSIB, " [ min_col_diff(%d) > cur_col_diff(%d) ] ", |
| min_col_diff, cur_col_diff); |
| ib_shift_out(idx, min_col_diff - cur_col_diff); |
| } |
| } |
| LOG_FMT(LSIB, " - now col %d, len %d\n", cpd.al[idx].col, cpd.al[idx].len); |
| idx++; |
| } |
| } |
| prev_match = pc; |
| } |
| pc = chunk_get_next_nc(pc); |
| } |
| return(pc); |
| } |
| |
| |
| static void align_log_al(log_sev_t sev, int line) |
| { |
| int idx; |
| |
| if (log_sev_on(sev)) |
| { |
| log_fmt(sev, "%s: line %d, %d)", __func__, line, cpd.al_cnt); |
| for (idx = 0; idx < cpd.al_cnt; idx++) |
| { |
| log_fmt(sev, " %d/%d=%s", cpd.al[idx].col, cpd.al[idx].len, |
| get_token_name(cpd.al[idx].type)); |
| } |
| log_fmt(sev, "\n"); |
| } |
| } |
| |
| |
| /** |
| * Generically aligns on '=', '{', '(' and item after ',' |
| * It scans the first line and picks up the location of those tags. |
| * It then scans subsequent lines and adjusts the column. |
| * Finally it does a second pass to align everything. |
| * |
| * Aligns all the '=' signs in stucture assignments. |
| * a = { |
| * .a = 1; |
| * .type = fast; |
| * }; |
| * |
| * And aligns on '{', numbers, strings, words. |
| * colors[] = { |
| * {"red", {255, 0, 0}}, {"blue", { 0, 255, 0}}, |
| * {"green", { 0, 0, 255}}, {"purple", {255, 255, 0}}, |
| * }; |
| * |
| * For the C99 indexed array assignment, the leading []= is skipped (no aligning) |
| * struct foo_t bars[] = |
| * { |
| * [0] = { .name = "bar", |
| * .age = 21 }, |
| * [1] = { .name = "barley", |
| * .age = 55 }, |
| * }; |
| * |
| * NOTE: this assumes that spacing is at the minimum correct spacing (ie force) |
| * if it isn't, some extra spaces will be inserted. |
| * |
| * @param start Points to the open brace chunk |
| */ |
| static void align_init_brace(chunk_t *start) |
| { |
| LOG_FUNC_ENTRY(); |
| int idx; |
| chunk_t *pc; |
| chunk_t *next; |
| chunk_t *prev; |
| chunk_t *tmp; |
| chunk_t *num_token = NULL; |
| |
| cpd.al_cnt = 0; |
| cpd.al_c99_array = false; |
| |
| LOG_FMT(LALBR, "%s: start @ %d:%d\n", __func__, start->orig_line, start->orig_col); |
| |
| pc = chunk_get_next_ncnl(start); |
| pc = scan_ib_line(pc, true); |
| if ((pc == NULL) || ((pc->type == CT_BRACE_CLOSE) && |
| (pc->parent_type == CT_ASSIGN))) |
| { |
| /* single line - nothing to do */ |
| return; |
| } |
| |
| do |
| { |
| pc = scan_ib_line(pc, false); |
| |
| /* debug dump the current frame */ |
| align_log_al(LALBR, pc->orig_line); |
| |
| while (chunk_is_newline(pc)) |
| { |
| pc = chunk_get_next(pc); |
| } |
| } while ((pc != NULL) && (pc->level > start->level)); |
| |
| /* debug dump the current frame */ |
| align_log_al(LALBR, start->orig_line); |
| |
| if (cpd.settings[UO_align_on_tabstop].b && (cpd.al_cnt >= 1) && |
| (cpd.al[0].type == CT_ASSIGN)) |
| { |
| cpd.al[0].col = align_tab_column(cpd.al[0].col); |
| } |
| |
| pc = chunk_get_next(start); |
| idx = 0; |
| do |
| { |
| if ((idx == 0) && ((tmp = skip_c99_array(pc)) != NULL)) |
| { |
| pc = tmp; |
| if (pc) |
| { |
| LOG_FMT(LALBR, " -%d- skipped '[] =' to %s\n", |
| pc->orig_line, get_token_name(pc->type)); |
| } |
| continue; |
| } |
| |
| next = pc; |
| if (idx < cpd.al_cnt) |
| { |
| LOG_FMT(LALBR, " (%d) check %s vs %s -- ", |
| idx, get_token_name(pc->type), get_token_name(cpd.al[idx].type)); |
| if (pc->type == cpd.al[idx].type) |
| { |
| if ((idx == 0) && cpd.al_c99_array) |
| { |
| prev = chunk_get_prev(pc); |
| if (chunk_is_newline(prev)) |
| { |
| pc->flags |= PCF_DONT_INDENT; |
| } |
| } |
| LOG_FMT(LALBR, " [%s] to col %d\n", pc->str.c_str(), cpd.al[idx].col); |
| |
| if (num_token != NULL) |
| { |
| int col_diff = pc->column - num_token->column; |
| |
| reindent_line(num_token, cpd.al[idx].col - col_diff); |
| //LOG_FMT(LSYS, "-= %d =- NUM indent [%s] col=%d diff=%d\n", |
| // num_token->orig_line, |
| // num_token->str.c_str(), cpd.al[idx - 1].col, col_diff); |
| |
| num_token->flags |= PCF_WAS_ALIGNED; |
| num_token = NULL; |
| } |
| |
| /* Comma's need to 'fall back' to the previous token */ |
| if (pc->type == CT_COMMA) |
| { |
| next = chunk_get_next(pc); |
| if ((next != NULL) && !chunk_is_newline(next)) |
| { |
| //LOG_FMT(LSYS, "-= %d =- indent [%s] col=%d len=%d\n", |
| // next->orig_line, |
| // next->str.c_str(), cpd.al[idx].col, cpd.al[idx].len); |
| |
| if ((idx < (cpd.al_cnt - 1)) && |
| cpd.settings[UO_align_number_left].b && |
| ((next->type == CT_NUMBER_FP) || |
| (next->type == CT_NUMBER) || |
| (next->type == CT_POS) || |
| (next->type == CT_NEG))) |
| { |
| /* Need to wait until the next match to indent numbers */ |
| num_token = next; |
| } |
| else if (idx < (cpd.al_cnt - 1)) |
| { |
| reindent_line(next, cpd.al[idx].col + cpd.al[idx].len); |
| next->flags |= PCF_WAS_ALIGNED; |
| } |
| } |
| } |
| else |
| { |
| /* first item on the line */ |
| reindent_line(pc, cpd.al[idx].col); |
| pc->flags |= PCF_WAS_ALIGNED; |
| |
| /* see if we need to right-align a number */ |
| if ((idx < (cpd.al_cnt - 1)) && |
| cpd.settings[UO_align_number_left].b) |
| { |
| next = chunk_get_next(pc); |
| if ((next != NULL) && !chunk_is_newline(next) && |
| ((next->type == CT_NUMBER_FP) || |
| (next->type == CT_NUMBER) || |
| (next->type == CT_POS) || |
| (next->type == CT_NEG))) |
| { |
| /* Need to wait until the next match to indent numbers */ |
| num_token = next; |
| } |
| } |
| } |
| idx++; |
| } |
| else |
| { |
| LOG_FMT(LALBR, " no match\n"); |
| } |
| } |
| if (chunk_is_newline(pc) || chunk_is_newline(next)) |
| { |
| idx = 0; |
| } |
| pc = chunk_get_next(pc); |
| } while ((pc != NULL) && (pc->level > start->level)); |
| } |
| |
| |
| /** |
| * Aligns simple typedefs that are contained on a single line each. |
| * This should be called after the typedef target is marked as a type. |
| * |
| * typedef int foo_t; |
| * typedef char bar_t; |
| * typedef const char cc_t; |
| */ |
| static void align_typedefs(int span) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *c_typedef = NULL; |
| AlignStack as; |
| |
| as.Start(span); |
| as.m_gap = cpd.settings[UO_align_typedef_gap].n; |
| as.m_star_style = (AlignStack::StarStyle)cpd.settings[UO_align_typedef_star_style].n; |
| as.m_amp_style = (AlignStack::StarStyle)cpd.settings[UO_align_typedef_amp_style].n; |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| if (chunk_is_newline(pc)) |
| { |
| as.NewLines(pc->nl_count); |
| c_typedef = NULL; |
| } |
| else if (c_typedef != NULL) |
| { |
| if (pc->flags & PCF_ANCHOR) |
| { |
| as.Add(pc); |
| LOG_FMT(LALTD, "%s: typedef @ %d:%d, tag '%s' @ %d:%d\n", |
| __func__, c_typedef->orig_line, c_typedef->orig_col, |
| pc->text(), pc->orig_line, pc->orig_col); |
| c_typedef = NULL; |
| } |
| } |
| else |
| { |
| if (pc->type == CT_TYPEDEF) |
| { |
| c_typedef = pc; |
| } |
| } |
| |
| pc = chunk_get_next(pc); |
| } |
| |
| as.End(); |
| } |
| |
| |
| /** |
| * Align '<<' (CT_ARITH?) |
| */ |
| static void align_left_shift(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| chunk_t *start = NULL; |
| AlignStack as; |
| |
| as.Start(255); |
| |
| pc = chunk_get_head(); |
| while (pc != NULL) |
| { |
| if ((start != NULL) && |
| (pc->flags & PCF_IN_PREPROC) != (start->flags & PCF_IN_PREPROC)) |
| { |
| /* a change in preproc status restarts the aligning */ |
| as.Flush(); |
| start = NULL; |
| } |
| else if (chunk_is_newline(pc)) |
| { |
| as.NewLines(pc->nl_count); |
| } |
| else if ((start != NULL) && (pc->level < start->level)) |
| { |
| /* A drop in level restarts the aligning */ |
| as.Flush(); |
| start = NULL; |
| } |
| else if ((start != NULL) && (pc->level > start->level)) |
| { |
| /* Ignore any deeper levels when aligning */ |
| } |
| else if (pc->type == CT_SEMICOLON) |
| { |
| /* A semicolon at the same level flushes */ |
| as.Flush(); |
| start = NULL; |
| } |
| else if (!(pc->flags & PCF_IN_ENUM) && chunk_is_str(pc, "<<", 2)) |
| { |
| if (pc->parent_type == CT_OPERATOR) |
| { |
| /* Ignore operator<< */ |
| } |
| else if (as.m_aligned.Empty()) |
| { |
| /* check if the first one is actually on a blank line and then |
| * indent it. Eg: |
| * |
| * cout |
| * << "something"; |
| */ |
| chunk_t *prev = chunk_get_prev(pc); |
| if (prev && chunk_is_newline(prev)) |
| { |
| indent_to_column(pc, pc->column_indent + cpd.settings[UO_indent_columns].n); |
| pc->column_indent = pc->column; |
| pc->flags |= PCF_DONT_INDENT; |
| } |
| |
| /* first one can be anywhere */ |
| as.Add(pc); |
| start = pc; |
| } |
| else if (chunk_is_newline(chunk_get_prev(pc))) |
| { |
| /* subsequent ones must be after a newline */ |
| as.Add(pc); |
| } |
| } |
| else if (!as.m_aligned.Empty()) |
| { |
| /* check if the given statement is on a line of its own, immediately following << |
| * and then it. Eg: |
| * |
| * cout << |
| * "something"; |
| */ |
| chunk_t *prev = chunk_get_prev(pc); |
| if (prev && chunk_is_newline(prev)) |
| { |
| indent_to_column(pc, pc->column_indent + cpd.settings[UO_indent_columns].n); |
| pc->column_indent = pc->column; |
| pc->flags |= PCF_DONT_INDENT; |
| } |
| } |
| |
| pc = chunk_get_next(pc); |
| } |
| as.End(); |
| } |
| |
| |
| /** |
| * Aligns an OC message |
| * |
| * @param so the square open of the message |
| */ |
| static void align_oc_msg_colon(chunk_t *so) |
| { |
| LOG_FUNC_ENTRY(); |
| int span = cpd.settings[UO_align_oc_msg_colon_span].n; |
| chunk_t *pc; |
| chunk_t *tmp; |
| AlignStack cas; /* for the colons */ |
| AlignStack nas; /* for the parameter tag */ |
| int level; |
| bool did_line; |
| int lcnt; /* line count with no colon for span */ |
| bool has_colon; |
| |
| |
| nas.Reset(); |
| nas.m_right_align = !cpd.settings[UO_align_on_tabstop].b; |
| |
| cas.Start(span); |
| |
| level = so->level; |
| pc = chunk_get_next_ncnl(so, CNAV_PREPROC); |
| |
| did_line = false; |
| has_colon = false; |
| lcnt = 0; |
| |
| while ((pc != NULL) && (pc->level > level)) |
| { |
| if (pc->level > (level + 1)) |
| { |
| /* do nothing */ |
| } |
| else if (chunk_is_newline(pc)) |
| { |
| if (!has_colon) |
| { |
| ++lcnt; |
| } |
| did_line = false; |
| has_colon = !has_colon; |
| } |
| else if (!did_line && (lcnt - 1 < span) && (pc->type == CT_OC_COLON)) |
| { |
| has_colon = true; |
| cas.Add(pc); |
| tmp = chunk_get_prev(pc); |
| if ((tmp != NULL) && |
| ((tmp->type == CT_OC_MSG_FUNC) || |
| (tmp->type == CT_OC_MSG_NAME))) |
| { |
| nas.Add(tmp); |
| tmp->flags |= PCF_DONT_INDENT; |
| } |
| did_line = true; |
| } |
| pc = chunk_get_next(pc, CNAV_PREPROC); |
| } |
| |
| nas.m_skip_first = !cpd.settings[UO_align_oc_msg_colon_first].b; |
| cas.m_skip_first = !cpd.settings[UO_align_oc_msg_colon_first].b; |
| |
| /* find the longest args that isn't the first one */ |
| int idx, len; |
| int first_len = 0, len_diff; |
| int tlen, mlen = 0; |
| int indent_size = cpd.settings[UO_indent_columns].n; |
| chunk_t *longest = NULL; |
| |
| for (idx = 0, len = nas.m_aligned.Len(); idx < len; idx++) |
| { |
| tmp = nas.m_aligned.GetChunk(idx); |
| |
| tlen = tmp->str.size(); |
| if (tlen > mlen) |
| { |
| mlen = tlen; |
| if (idx != 0) |
| { |
| longest = tmp; |
| } |
| } |
| if (idx == 0) |
| { |
| first_len = tlen + 1; |
| } |
| } |
| |
| /* add spaces before the longest arg */ |
| len = cpd.settings[UO_indent_oc_msg_colon].n; |
| len_diff = mlen - first_len; |
| /* Align with first colon if possible by removing spaces */ |
| if (longest && |
| cpd.settings[UO_indent_oc_msg_prioritize_first_colon].b && |
| (len_diff > 0) && |
| ((longest->column - len_diff) > (longest->brace_level * indent_size))) |
| { |
| longest->column -= len_diff; |
| } |
| else if (longest && (len > 0)) |
| { |
| chunk_t chunk; |
| |
| chunk.type = CT_SPACE; |
| chunk.orig_line = longest->orig_line; |
| chunk.parent_type = CT_NONE; |
| chunk.level = longest->level; |
| chunk.brace_level = longest->brace_level; |
| chunk.flags = longest->flags & PCF_COPY_FLAGS; |
| |
| /* start at one since we already indent for the '[' */ |
| for (idx = 1; idx < len; idx++) |
| { |
| chunk.str.append(' '); |
| } |
| |
| chunk_add_before(&chunk, longest); |
| } |
| nas.End(); |
| cas.End(); |
| } |
| |
| |
| /** |
| * Aligns OC messages |
| */ |
| static void align_oc_msg_colons() |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc; |
| |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) |
| { |
| if ((pc->type == CT_SQUARE_OPEN) && (pc->parent_type == CT_OC_MSG)) |
| { |
| align_oc_msg_colon(pc); |
| } |
| } |
| } |
| |
| |
| /** |
| * Aligns OC declarations on the colon |
| * -(void) doSomething: (NSString*) param1 |
| * with: (NSString*) param2 |
| */ |
| static void align_oc_decl_colon(void) |
| { |
| LOG_FUNC_ENTRY(); |
| chunk_t *pc = chunk_get_head(); |
| chunk_t *tmp; |
| chunk_t *tmp2; |
| AlignStack cas; /* for the colons */ |
| AlignStack nas; /* for the parameter label */ |
| int level; |
| bool did_line; |
| |
| cas.Start(4); |
| nas.Start(4); |
| nas.m_right_align = !cpd.settings[UO_align_on_tabstop].b; |
| |
| while (pc != NULL) |
| { |
| if (pc->type != CT_OC_SCOPE) |
| { |
| pc = chunk_get_next(pc); |
| continue; |
| } |
| |
| nas.Reset(); |
| cas.Reset(); |
| |
| level = pc->level; |
| pc = chunk_get_next_ncnl(pc, CNAV_PREPROC); |
| |
| did_line = false; |
| |
| while ((pc != NULL) && (pc->level >= level)) |
| { |
| /* The decl ends with an open brace or semicolon */ |
| if ((pc->type == CT_BRACE_OPEN) || chunk_is_semicolon(pc)) |
| { |
| break; |
| } |
| |
| if (chunk_is_newline(pc)) |
| { |
| nas.NewLines(pc->nl_count); |
| cas.NewLines(pc->nl_count); |
| did_line = false; |
| } |
| else if (!did_line && (pc->type == CT_OC_COLON)) |
| { |
| cas.Add(pc); |
| |
| tmp = chunk_get_prev(pc, CNAV_PREPROC); |
| tmp2 = chunk_get_prev_ncnl(tmp, CNAV_PREPROC); |
| |
| /* Check for an un-labeled parameter */ |
| if ((tmp != NULL) && |
| (tmp2 != NULL) |
| && |
| ((tmp->type == CT_WORD) || |
| (tmp->type == CT_TYPE) || |
| (tmp->type == CT_OC_MSG_DECL) || |
| (tmp->type == CT_OC_MSG_SPEC)) |
| && |
| ((tmp2->type == CT_WORD) || |
| (tmp2->type == CT_TYPE) || |
| (tmp2->type == CT_PAREN_CLOSE))) |
| { |
| nas.Add(tmp); |
| } |
| did_line = true; |
| } |
| pc = chunk_get_next(pc, CNAV_PREPROC); |
| } |
| nas.End(); |
| cas.End(); |
| } |
| } |