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; } } }
/** * 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; cpd.did_newline = 1; cpd.column = 1; if (cpd.bom) { write_bom(pfile, cpd.enc); } if (cpd.frag_cols > 0) { int indent = cpd.frag_cols - 1; for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc)) { pc->column += indent; pc->column_indent += indent; } cpd.frag_cols = 0; } 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_NL_CONT) { /* FIXME: this really shouldn't be done here! */ if ((pc->flags & PCF_WAS_ALIGNED) == 0) { if (cpd.settings[UO_sp_before_nl_cont].a & AV_REMOVE) { pc->column = cpd.column + (cpd.settings[UO_sp_before_nl_cont].a == AV_FORCE); } else { /* Try to keep the same relative spacing */ prev = chunk_get_prev(pc); while ((prev != NULL) && (prev->orig_col == 0) && (prev->nl_count == 0)) { prev = chunk_get_prev(prev); } if ((prev != NULL) && (prev->nl_count == 0)) { int orig_sp = (pc->orig_col - prev->orig_col_end); pc->column = cpd.column + orig_sp; if ((cpd.settings[UO_sp_before_nl_cont].a != AV_IGNORE) && (pc->column < (cpd.column + 1))) { pc->column = cpd.column + 1; } } } } output_to_column(pc->column, (cpd.settings[UO_indent_with_tabs].n == 2)); add_char('\\'); add_char('\n'); cpd.did_newline = 1; cpd.column = 1; LOG_FMT(LOUTIND, " \\xx\n"); } else if (pc->type == CT_COMMENT_MULTI) { if (cpd.settings[UO_cmt_indent_multi].b) { output_comment_multi(pc); } else { output_comment_multi_simple(pc); } } else if (pc->type == CT_COMMENT_CPP) { pc = output_comment_cpp(pc); } else if (pc->type == CT_COMMENT) { pc = output_comment_c(pc); } else if ((pc->type == CT_JUNK) || (pc->type == CT_IGNORED)) { /* do not adjust the column for junk */ add_text(pc->str); } 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) { /* FIXME: it would be better to properly set column_indent in * indent_text(), but this hack for '}' and ':' seems to work. */ if ((pc->type == CT_BRACE_CLOSE) || chunk_is_str(pc, ":", 1) || (pc->type == CT_PREPROC)) { lvlcol = pc->column; } else { lvlcol = pc->column_indent; if (lvlcol > pc->column) { lvlcol = pc->column; } } if (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 { /** * Reformatting multi-line comments can screw up the column. * Make sure we don't mess up the spacing on this line. * This has to be done here because comments are not formatted * until the output phase. */ if (pc->column < cpd.column) { reindent_line(pc, cpd.column); } /* not the first item on a line */ prev = chunk_get_prev(pc); allow_tabs = (cpd.settings[UO_align_with_tabs].b && ((pc->flags & PCF_WAS_ALIGNED) != 0) && ((prev->column + prev->len() + 1) != pc->column)); if (cpd.settings[UO_align_keep_tabs].b) { allow_tabs |= pc->after_tab; } LOG_FMT(LOUTIND, " %d(%d) -", pc->column, allow_tabs); } output_to_column(pc->column, allow_tabs); add_text(pc->str); cpd.did_newline = chunk_is_newline(pc); } } }
/** * 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); } } }
/** * A multiline comment -- woopeee! * The only trick here is that we have to trim out whitespace characters * to get the comment to line up. */ static void output_comment_multi(chunk_t *pc) { int cmt_col; int cmt_idx; int ch; unc_text line; int line_count = 0; int ccol; /* the col of subsequent comment lines */ int col_diff = 0; bool nl_end = false; cmt_reflow cmt; //LOG_FMT(LSYS, "%s: line %d\n", __func__, pc->orig_line); output_cmt_start(cmt, pc); cmt.reflow = (cpd.settings[UO_cmt_reflow_mode].n != 1); cmt_col = cmt.base_col; col_diff = pc->orig_col - cmt.base_col; calculate_comment_body_indent(cmt, pc->str); cmt.cont_text = !cpd.settings[UO_cmt_indent_multi].b ? "" : (cpd.settings[UO_cmt_star_cont].b ? "* " : " "); LOG_CONTTEXT(); //LOG_FMT(LSYS, "Indenting1 line %d to col %d (orig=%d) col_diff=%d xtra=%d cont='%s'\n", // pc->orig_line, cmt_col, pc->orig_col, col_diff, cmt.xtra_indent, cmt.cont_text.c_str()); ccol = pc->column; cmt_idx = 0; line.clear(); while (cmt_idx < pc->len()) { ch = pc->str[cmt_idx++]; /* handle the CRLF and CR endings. convert both to LF */ if (ch == '\r') { ch = '\n'; if ((cmt_idx < pc->len()) && (pc->str[cmt_idx] == '\n')) { cmt_idx++; } } /* Find the start column */ if (line.size() == 0) { nl_end = false; if (ch == ' ') { ccol++; continue; } else if (ch == '\t') { ccol = calc_next_tab_column(ccol, cpd.settings[UO_input_tab_size].n); continue; } else { //LOG_FMT(LSYS, "%d] Text starts in col %d\n", line_count, ccol); } } /* * Now see if we need/must fold the next line with the current to enable * full reflow */ if ((cpd.settings[UO_cmt_reflow_mode].n == 2) && (ch == '\n') && (cmt_idx < pc->len())) { int nxt_len = 0; int next_nonempty_line = -1; int prev_nonempty_line = -1; int nwidx = line.size(); bool star_is_bullet = false; /* strip trailing whitespace from the line collected so far */ while (nwidx > 0) { nwidx--; if ((prev_nonempty_line < 0) && !unc_isspace(line[nwidx]) && (line[nwidx] != '*') && // block comment: skip '*' at end of line ((pc->flags & PCF_IN_PREPROC) ? (line[nwidx] != '\\') || ((line[nwidx + 1] != 'r') && (line[nwidx + 1] != '\n')) : true)) { prev_nonempty_line = nwidx; // last nonwhitespace char in the previous line } } int remaining = pc->len() - cmt_idx; for (nxt_len = 0; (nxt_len <= remaining) && (pc->str[nxt_len] != 'r') && (pc->str[nxt_len] != '\n'); nxt_len++) { if ((next_nonempty_line < 0) && !unc_isspace(pc->str[nxt_len]) && (pc->str[nxt_len] != '*') && ((nxt_len == remaining) || ((pc->flags & PCF_IN_PREPROC) ? (pc->str[nxt_len] != '\\') || ((pc->str[nxt_len + 1] != 'r') && (pc->str[nxt_len + 1] != '\n')) : true))) { next_nonempty_line = nxt_len; // first nonwhitespace char in the next line } } /* * see if we should fold up; usually that'd be a YES, but there are a few * situations where folding/reflowing by merging lines is frowned upon: * * - ASCII art in the comments (most often, these are drawings done in +-\/|.,*) * * - Doxygen/JavaDoc/etc. parameters: these often start with \ or @, at least * something clearly non-alphanumeric (you see where we're going with this?) * * - bullet lists that are closely spaced: bullets are always non-alphanumeric * characters, such as '-' or '+' (or, oh horor, '*' - that's bloody ambiguous * to parse :-( ... with or without '*' comment start prefix, that's the * question, then.) * * - semi-HTML formatted code, e.g. <pre>...</pre> comment sections (NDoc, etc.) * * - New lines which form a new paragraph without there having been added an * extra empty line between the last sentence and the new one. * A bit like this, really; so it is opportune to check if the last line ended * in a terminal (that would be the set '.:;!?') and the new line starts with * a capital. * Though new lines starting with comment delimiters, such as '(', should be * pulled up. * * So it bores down to this: the only folding (& reflowing) that's going to happen * is when the next line starts with an alphanumeric character AND the last * line didn't end with an non-alphanumeric character, except: ',' AND the next * line didn't start with a '*' all of a sudden while the previous one didn't * (the ambiguous '*'-for-bullet case!) */ if ((prev_nonempty_line >= 0) && (next_nonempty_line >= 0) && (((unc_isalnum(line[prev_nonempty_line]) || strchr(",)]", line[prev_nonempty_line])) && (unc_isalnum(pc->str[next_nonempty_line]) || strchr("([", pc->str[next_nonempty_line]))) || (('.' == line[prev_nonempty_line]) && // dot followed by non-capital is NOT a new sentence start unc_isupper(pc->str[next_nonempty_line]))) && !star_is_bullet) { // rewind the line to the last non-alpha: line.resize(prev_nonempty_line + 1); // roll the current line forward to the first non-alpha: cmt_idx += next_nonempty_line; // override the NL and make it a single whitespace: ch = ' '; } } line.append(ch); /* If we just hit an end of line OR we just hit end-of-comment... */ if ((ch == '\n') || (cmt_idx == pc->len())) { line_count++; /* strip trailing tabs and spaces before the newline */ if (ch == '\n') { nl_end = true; line.pop_back(); cmt_trim_whitespace(line, pc->flags & PCF_IN_PREPROC); } //LOG_FMT(LSYS, "[%3d]%s\n", ccol, line); if (line_count == 1) { /* this is the first line - add unchanged */ add_comment_text(line, cmt, false); if (nl_end) { add_char('\n'); } } else { /* This is not the first line, so we need to indent to the * correct column. Each line is indented 0 or more spaces. */ ccol -= col_diff; if (ccol < (cmt_col + 3)) { ccol = cmt_col + 3; } if (line.size() == 0) { /* Empty line - just a '\n' */ if (cpd.settings[UO_cmt_star_cont].b) { cmt.column = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].n; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } add_text(cmt.cont_text); } add_char('\n'); } else { /* If this doesn't start with a '*' or '|'. * '\name' is a common parameter documentation thing. */ if (cpd.settings[UO_cmt_indent_multi].b && (line[0] != '*') && (line[0] != '|') && (line[0] != '#') && ((line[0] != '\\') || unc_isalpha(line[1])) && (line[0] != '+')) { int start_col = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].n; if (cpd.settings[UO_cmt_star_cont].b) { cmt.column = start_col; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } add_text(cmt.cont_text); output_to_column(ccol + cpd.settings[UO_cmt_sp_after_star_cont].n, false); } else { cmt.column = ccol; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); } } else { cmt.column = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].n; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } int idx; idx = cmt_parse_lead(line, (cmt_idx == pc->len())); if (idx > 0) { cmt.cont_text.set(line, 0, idx); LOG_CONTTEXT(); if ((line.size() >= 2) && (line[0] == '*') && unc_isalnum(line[1])) { line.insert(1, ' '); } } else { add_text(cmt.cont_text); } } add_comment_text(line, cmt, false); if (nl_end) { add_text("\n"); } } } line.clear(); ccol = 1; } } }