PageRenderTime 339ms CodeModel.GetById 22ms RepoModel.GetById 18ms app.codeStats 1ms

/src/output.cpp

http://github.com/bengardner/uncrustify
C++ | 2093 lines | 1729 code | 169 blank | 195 comment | 412 complexity | e42deaf9cda5ae877ec020c4160e6290 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /**
  2. * @file output.cpp
  3. * Does all the output & comment formatting.
  4. *
  5. * @author Ben Gardner
  6. * @author Guy Maurel since version 0.62 for uncrustify4Qt
  7. * October 2015, 2016
  8. * @license GPL v2+
  9. */
  10. #include "output.h"
  11. #include "uncrustify_types.h"
  12. #include "prototypes.h"
  13. #include "chunk_list.h"
  14. #include "unc_ctype.h"
  15. #include "uncrustify.h"
  16. #include "indent.h"
  17. #include "braces.h"
  18. #include "unicode.h"
  19. #include <cstdlib>
  20. static void output_comment_multi(chunk_t *pc);
  21. static void output_comment_multi_simple(chunk_t *pc, bool kw_subst);
  22. static void do_kw_subst(chunk_t *pc);
  23. struct cmt_reflow
  24. {
  25. chunk_t *pc;
  26. size_t column; /* Column of the comment start */
  27. size_t brace_col; /* Brace column (for indenting with tabs) */
  28. size_t base_col; /* Base column (for indenting with tabs) */
  29. size_t word_count; /* number of words on this line */
  30. size_t xtra_indent; /* extra indent of non-first lines (0 or 1) */
  31. unc_text cont_text; /* fixed text to output at the start of a line (0 to 3 chars) */
  32. bool reflow; /* reflow the current line */
  33. };
  34. static chunk_t *output_comment_c(chunk_t *pc);
  35. static chunk_t *output_comment_cpp(chunk_t *pc);
  36. static void add_comment_text(const unc_text &text, cmt_reflow &cmt, bool esc_close);
  37. #define LOG_CONTTEXT() \
  38. LOG_FMT(LCONTTEXT, "%s:%d set cont_text to '%s'\n", __func__, __LINE__, cmt.cont_text.c_str())
  39. /**
  40. * All output text is sent here, one char at a time.
  41. */
  42. static void add_char(UINT32 ch)
  43. {
  44. /* If we did a '\r' and it isn't followed by a '\n', then output a newline */
  45. if ((cpd.last_char == '\r') && (ch != '\n'))
  46. {
  47. write_string(cpd.newline);
  48. cpd.column = 1;
  49. cpd.did_newline = 1;
  50. cpd.spaces = 0;
  51. }
  52. /* convert a newline into the LF/CRLF/CR sequence */
  53. if (ch == '\n')
  54. {
  55. write_string(cpd.newline);
  56. cpd.column = 1;
  57. cpd.did_newline = 1;
  58. cpd.spaces = 0;
  59. }
  60. else if (ch == '\r')
  61. {
  62. /* do not output '\r' */
  63. cpd.column = 1;
  64. cpd.did_newline = 1;
  65. cpd.spaces = 0;
  66. }
  67. else if ((ch == '\t') && cpd.output_tab_as_space)
  68. {
  69. size_t endcol = next_tab_column(cpd.column);
  70. while (cpd.column < endcol)
  71. {
  72. add_char(' ');
  73. }
  74. return;
  75. }
  76. else
  77. {
  78. /* Explicitly disallow a tab after a space */
  79. if ((ch == '\t') && (cpd.last_char == ' '))
  80. {
  81. size_t endcol = next_tab_column(cpd.column);
  82. while (cpd.column < endcol)
  83. {
  84. add_char(' ');
  85. }
  86. return;
  87. }
  88. else if ((ch == ' ') && !cpd.output_trailspace)
  89. {
  90. cpd.spaces++;
  91. cpd.column++;
  92. }
  93. else
  94. {
  95. while (cpd.spaces > 0)
  96. {
  97. write_char(' ');
  98. cpd.spaces--;
  99. }
  100. write_char(ch);
  101. if (ch == '\t')
  102. {
  103. cpd.column = next_tab_column(cpd.column);
  104. }
  105. else
  106. {
  107. cpd.column++;
  108. }
  109. }
  110. }
  111. cpd.last_char = ch;
  112. } // add_char
  113. static void add_text(const char *ascii_text)
  114. {
  115. char ch;
  116. while ((ch = *ascii_text) != 0)
  117. {
  118. ascii_text++;
  119. add_char(ch);
  120. }
  121. }
  122. static void add_text(const unc_text &text, bool is_ignored = false)
  123. {
  124. for (size_t idx = 0; idx < text.size(); idx++)
  125. {
  126. int ch = text[idx];
  127. if (is_ignored)
  128. {
  129. write_char(ch);
  130. }
  131. else
  132. {
  133. add_char(ch);
  134. }
  135. }
  136. }
  137. /**
  138. * Count the number of characters to the end of the next chunk of text.
  139. * If it exceeds the limit, return true.
  140. */
  141. static bool next_word_exceeds_limit(const unc_text &text, size_t idx)
  142. {
  143. size_t length = 0;
  144. /* Count any whitespace */
  145. while ((idx < text.size()) && unc_isspace(text[idx]))
  146. {
  147. idx++;
  148. length++;
  149. }
  150. /* Count non-whitespace */
  151. while ((idx < text.size()) && !unc_isspace(text[idx]))
  152. {
  153. idx++;
  154. length++;
  155. }
  156. return((cpd.column + length - 1) > cpd.settings[UO_cmt_width].u);
  157. }
  158. /**
  159. * Advance to a specific column
  160. * cpd.column is the current column
  161. *
  162. * @param column The column to advance to
  163. */
  164. static void output_to_column(size_t column, bool allow_tabs)
  165. {
  166. cpd.did_newline = 0;
  167. if (allow_tabs)
  168. {
  169. /* tab out as far as possible and then use spaces */
  170. size_t next_column = next_tab_column(cpd.column);
  171. while (next_column <= column)
  172. {
  173. add_text("\t");
  174. next_column = next_tab_column(cpd.column);
  175. }
  176. }
  177. /* space out the final bit */
  178. while (cpd.column < column)
  179. {
  180. add_text(" ");
  181. }
  182. }
  183. /**
  184. * Output a comment to the column using indent_with_tabs and
  185. * indent_cmt_with_tabs as the rules.
  186. * base_col is the indent of the first line of the comment.
  187. * On the first line, column == base_col.
  188. * On subsequent lines, column >= base_col.
  189. *
  190. * @param brace_col the brace-level indent of the comment
  191. * @param base_col the indent of the start of the comment (multiline)
  192. * @param column the column that we should end up in
  193. */
  194. static void cmt_output_indent(size_t brace_col, size_t base_col, size_t column)
  195. {
  196. size_t iwt = cpd.settings[UO_indent_cmt_with_tabs].b ? 2 :
  197. (cpd.settings[UO_indent_with_tabs].n ? 1 : 0);
  198. size_t tab_col = (iwt == 0) ? 0 : ((iwt == 1) ? brace_col : base_col);
  199. // LOG_FMT(LSYS, "%s(brace=%d base=%d col=%d iwt=%d) tab=%d cur=%d\n",
  200. // __func__, brace_col, base_col, column, iwt, tab_col, cpd.column);
  201. cpd.did_newline = 0;
  202. if ((iwt == 2) || ((cpd.column == 1) && (iwt == 1)))
  203. {
  204. /* tab out as far as possible and then use spaces */
  205. while (next_tab_column(cpd.column) <= tab_col)
  206. {
  207. add_text("\t");
  208. }
  209. }
  210. /* space out the rest */
  211. while (cpd.column < column)
  212. {
  213. add_text(" ");
  214. }
  215. } // cmt_output_indent
  216. void output_parsed(FILE *pfile)
  217. {
  218. // save_option_file(pfile, false);
  219. save_option_file_kernel(pfile, false, true);
  220. fprintf(pfile, "# -=====-\n");
  221. fprintf(pfile, "# Line Tag Parent Columns Br/Lvl/pp Flag Nl Text");
  222. for (chunk_t *pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
  223. {
  224. fprintf(pfile, "\n# %3zu> %16.16s[%16.16s][%3zu/%3zu/%3d/%3d][%zu/%zu/%zu][%10" PRIx64 "][%zu-%d]",
  225. pc->orig_line, get_token_name(pc->type),
  226. get_token_name(pc->parent_type),
  227. pc->column, pc->orig_col, pc->orig_col_end, pc->orig_prev_sp,
  228. pc->brace_level, pc->level, pc->pp_level,
  229. pc->flags, pc->nl_count, pc->after_tab);
  230. if ((pc->type != CT_NEWLINE) && (pc->len() != 0))
  231. {
  232. for (size_t cnt = 0; cnt < pc->column; cnt++)
  233. {
  234. fprintf(pfile, " ");
  235. }
  236. if (pc->type != CT_NL_CONT)
  237. {
  238. fprintf(pfile, "%s", pc->text());
  239. }
  240. else
  241. {
  242. fprintf(pfile, "\\");
  243. }
  244. }
  245. }
  246. fprintf(pfile, "\n# -=====-\n");
  247. fflush(pfile);
  248. } // output_parsed
  249. void output_text(FILE *pfile)
  250. {
  251. cpd.fout = pfile;
  252. cpd.did_newline = 1;
  253. cpd.column = 1;
  254. if (cpd.bom)
  255. {
  256. write_bom();
  257. }
  258. chunk_t *pc;
  259. if (cpd.frag_cols > 0)
  260. {
  261. size_t indent = cpd.frag_cols - 1;
  262. for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
  263. {
  264. pc->column += indent;
  265. pc->column_indent += indent;
  266. }
  267. cpd.frag_cols = 0;
  268. }
  269. for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
  270. {
  271. LOG_FMT(LOUTIND, "text() %s, type %s, col=%zu\n",
  272. pc->text(), get_token_name(pc->type), pc->orig_col);
  273. cpd.output_tab_as_space = (cpd.settings[UO_cmt_convert_tab_to_spaces].b &&
  274. chunk_is_comment(pc));
  275. if (pc->type == CT_NEWLINE)
  276. {
  277. for (size_t cnt = 0; cnt < pc->nl_count; cnt++)
  278. {
  279. add_char('\n');
  280. }
  281. cpd.did_newline = 1;
  282. cpd.column = 1;
  283. LOG_FMT(LOUTIND, " xx\n");
  284. }
  285. else if (pc->type == CT_NL_CONT)
  286. {
  287. /* FIXME: this really shouldn't be done here! */
  288. if ((pc->flags & PCF_WAS_ALIGNED) == 0)
  289. {
  290. if (cpd.settings[UO_sp_before_nl_cont].a & AV_REMOVE)
  291. {
  292. pc->column = cpd.column + (cpd.settings[UO_sp_before_nl_cont].a == AV_FORCE);
  293. }
  294. else
  295. {
  296. /* Try to keep the same relative spacing */
  297. chunk_t *prev = chunk_get_prev(pc);
  298. while ((prev != NULL) && (prev->orig_col == 0) && (prev->nl_count == 0))
  299. {
  300. prev = chunk_get_prev(prev);
  301. }
  302. if ((prev != NULL) && (prev->nl_count == 0))
  303. {
  304. int orig_sp = (pc->orig_col - prev->orig_col_end);
  305. pc->column = cpd.column + orig_sp;
  306. // the value might be negative --> use an int
  307. int columnDiff = cpd.column + orig_sp;
  308. if ((cpd.settings[UO_sp_before_nl_cont].a != AV_IGNORE) &&
  309. (columnDiff < (cpd.column + 1)))
  310. {
  311. pc->column = cpd.column + 1;
  312. }
  313. }
  314. }
  315. output_to_column(pc->column, false);
  316. }
  317. else
  318. {
  319. output_to_column(pc->column, (cpd.settings[UO_indent_with_tabs].n == 2));
  320. }
  321. add_char('\\');
  322. add_char('\n');
  323. cpd.did_newline = 1;
  324. cpd.column = 1;
  325. LOG_FMT(LOUTIND, " \\xx\n");
  326. }
  327. else if (pc->type == CT_COMMENT_MULTI)
  328. {
  329. if (cpd.settings[UO_cmt_indent_multi].b)
  330. {
  331. output_comment_multi(pc);
  332. }
  333. else
  334. {
  335. output_comment_multi_simple(pc, (pc->flags & PCF_INSERTED) != 0); // forcing value to bool
  336. }
  337. }
  338. else if (pc->type == CT_COMMENT_CPP)
  339. {
  340. bool tmp = cpd.output_trailspace;
  341. // keep trailing spaces if they are still present in a chunk;
  342. // note that tokenize() already strips spaces in comments, so if they made it up to here, they are to stay
  343. cpd.output_trailspace = true;
  344. pc = output_comment_cpp(pc);
  345. cpd.output_trailspace = tmp;
  346. }
  347. else if (pc->type == CT_COMMENT)
  348. {
  349. pc = output_comment_c(pc);
  350. }
  351. else if ((pc->type == CT_JUNK) || (pc->type == CT_IGNORED))
  352. {
  353. /* do not adjust the column for junk */
  354. add_text(pc->str, true);
  355. }
  356. else if (pc->len() == 0)
  357. {
  358. /* don't do anything for non-visible stuff */
  359. LOG_FMT(LOUTIND, " <%zu> -", pc->column);
  360. }
  361. else
  362. {
  363. bool allow_tabs;
  364. cpd.output_trailspace = (pc->type == CT_STRING_MULTI);
  365. /* indent to the 'level' first */
  366. if (cpd.did_newline)
  367. {
  368. if (cpd.settings[UO_indent_with_tabs].n == 1)
  369. {
  370. size_t lvlcol;
  371. /* FIXME: it would be better to properly set column_indent in
  372. * indent_text(), but this hack for '}' and ':' seems to work. */
  373. if ((pc->type == CT_BRACE_CLOSE) ||
  374. chunk_is_str(pc, ":", 1) ||
  375. (pc->type == CT_PREPROC))
  376. {
  377. lvlcol = pc->column;
  378. }
  379. else
  380. {
  381. lvlcol = pc->column_indent;
  382. if (lvlcol > pc->column)
  383. {
  384. lvlcol = pc->column;
  385. }
  386. }
  387. if (lvlcol > 1)
  388. {
  389. output_to_column(lvlcol, true);
  390. }
  391. }
  392. allow_tabs = (cpd.settings[UO_indent_with_tabs].n == 2) ||
  393. (chunk_is_comment(pc) &&
  394. (cpd.settings[UO_indent_with_tabs].n != 0));
  395. LOG_FMT(LOUTIND, " %zu> col %zu/%zu/%d - ", pc->orig_line, pc->column, pc->column_indent, cpd.column);
  396. }
  397. else
  398. {
  399. /**
  400. * Reformatting multi-line comments can screw up the column.
  401. * Make sure we don't mess up the spacing on this line.
  402. * This has to be done here because comments are not formatted
  403. * until the output phase.
  404. */
  405. if (pc->column < cpd.column)
  406. {
  407. reindent_line(pc, cpd.column);
  408. }
  409. /* not the first item on a line */
  410. chunk_t *prev = chunk_get_prev(pc);
  411. allow_tabs = (cpd.settings[UO_align_with_tabs].b &&
  412. (pc->flags & PCF_WAS_ALIGNED) &&
  413. ((prev->column + prev->len() + 1) != pc->column));
  414. if (cpd.settings[UO_align_keep_tabs].b)
  415. {
  416. allow_tabs |= pc->after_tab;
  417. }
  418. LOG_FMT(LOUTIND, " %zu(%d) -", pc->column, allow_tabs);
  419. }
  420. output_to_column(pc->column, allow_tabs);
  421. add_text(pc->str);
  422. if (pc->type == CT_PP_DEFINE) // Issue #876
  423. {
  424. if (cpd.settings[UO_force_tab_after_define].b)
  425. {
  426. add_char('\t');
  427. }
  428. }
  429. cpd.did_newline = chunk_is_newline(pc);
  430. cpd.output_trailspace = false;
  431. }
  432. }
  433. } // output_text
  434. /**
  435. * Checks for and updates the lead chars.
  436. *
  437. * @param line the comment line
  438. * @return 0=not present, >0=number of chars that are part of the lead
  439. */
  440. static size_t cmt_parse_lead(const unc_text &line, bool is_last)
  441. {
  442. size_t len = 0;
  443. while ((len < 32) && (len < line.size()))
  444. {
  445. if ((len > 0) && (line[len] == '/'))
  446. {
  447. /* ignore combined comments */
  448. size_t tmp = len + 1;
  449. while ((tmp < line.size()) && unc_isspace(line[tmp]))
  450. {
  451. tmp++;
  452. }
  453. if ((tmp < line.size()) && (line[tmp] == '/'))
  454. {
  455. return(1);
  456. }
  457. break;
  458. }
  459. else if (strchr("*|\\#+", line[len]) == NULL)
  460. {
  461. break;
  462. }
  463. len++;
  464. }
  465. if (len > 30)
  466. {
  467. return(1);
  468. }
  469. if ((len > 0) && ((len >= line.size()) || unc_isspace(line[len])))
  470. {
  471. return(len);
  472. }
  473. if ((len == 1) && (line[0] == '*'))
  474. {
  475. return(len);
  476. }
  477. if (is_last && (len > 0))
  478. {
  479. return(len);
  480. }
  481. return(0);
  482. } // cmt_parse_lead
  483. /**
  484. * Scans a multiline comment to determine the following:
  485. * - the extra indent of the non-first line (0 or 1)
  486. * - the continuation text ('' or '* ')
  487. *
  488. * The decision is based on:
  489. * - cmt_indent_multi
  490. * - cmt_star_cont
  491. * - cmt_multi_first_len_minimum
  492. * - the first line length
  493. * - the second line leader length
  494. * - the last line length (without leading space/tab)
  495. *
  496. * If the first and last line are the same length and don't contain any alnum
  497. * chars and (the first line len > 2 or the second leader is the same as the
  498. * first line length), then the indent is 0.
  499. *
  500. * If the leader on the second line is 1 wide or missing, then the indent is 1.
  501. *
  502. * Otherwise, the indent is 0.
  503. *
  504. * @param str The comment string
  505. * @param len Length of the comment
  506. * @param start_col Starting column
  507. * @return cmt.xtra_indent is set to 0 or 1
  508. */
  509. static void calculate_comment_body_indent(cmt_reflow &cmt, const unc_text &str)
  510. {
  511. cmt.xtra_indent = 0;
  512. if (!cpd.settings[UO_cmt_indent_multi].b)
  513. {
  514. return;
  515. }
  516. size_t idx = 0;
  517. size_t len = str.size();
  518. size_t last_len = 0;
  519. if (cpd.settings[UO_cmt_multi_check_last].b)
  520. {
  521. /* find the last line length */
  522. for (idx = len - 1; idx > 0; idx--)
  523. {
  524. if ((str[idx] == '\n') || (str[idx] == '\r'))
  525. {
  526. idx++;
  527. while ((idx < len) && ((str[idx] == ' ') || (str[idx] == '\t')))
  528. {
  529. idx++;
  530. }
  531. last_len = len - idx;
  532. break;
  533. }
  534. }
  535. }
  536. /* find the first line length */
  537. size_t first_len = 0;
  538. for (idx = 0; idx < len; idx++)
  539. {
  540. if ((str[idx] == '\n') || (str[idx] == '\r'))
  541. {
  542. first_len = idx;
  543. while ((str[first_len - 1] == ' ') || (str[first_len - 1] == '\t'))
  544. {
  545. first_len--;
  546. }
  547. /* handle DOS endings */
  548. if ((str[idx] == '\r') && (str[idx + 1] == '\n'))
  549. {
  550. idx++;
  551. }
  552. idx++;
  553. break;
  554. }
  555. }
  556. /* Scan the second line */
  557. size_t width = 0;
  558. for (/* nada */; idx < len - 1; idx++)
  559. {
  560. if ((str[idx] == ' ') || (str[idx] == '\t'))
  561. {
  562. if (width > 0)
  563. {
  564. break;
  565. }
  566. continue;
  567. }
  568. if ((str[idx] == '\n') || (str[idx] == '\r'))
  569. {
  570. /* Done with second line */
  571. break;
  572. }
  573. /* Count the leading chars */
  574. if ((str[idx] == '*') ||
  575. (str[idx] == '|') ||
  576. (str[idx] == '\\') ||
  577. (str[idx] == '#') ||
  578. (str[idx] == '+'))
  579. {
  580. width++;
  581. }
  582. else
  583. {
  584. if ((width != 1) || (str[idx - 1] != '*'))
  585. {
  586. width = 0;
  587. }
  588. break;
  589. }
  590. }
  591. // LOG_FMT(LSYS, "%s: first=%d last=%d width=%d\n", __func__, first_len, last_len, width);
  592. // If the first and last line are the same length and don't contain any alnum
  593. // chars and (the first line len > cmt_multi_first_len_minimum or
  594. // the second leader is the same as the first line length), then the indent is 0.
  595. if ((first_len == last_len) &&
  596. ((first_len > cpd.settings[UO_cmt_multi_first_len_minimum].u) ||
  597. (first_len == width)))
  598. {
  599. return;
  600. }
  601. cmt.xtra_indent = ((width == 2) ? 0 : 1);
  602. } // calculate_comment_body_indent
  603. /* \todo can we use search_next_chunk here? */
  604. static chunk_t *get_next_function(chunk_t *pc)
  605. {
  606. while ((pc = chunk_get_next(pc)) != NULL)
  607. {
  608. if ((pc->type == CT_FUNC_DEF) ||
  609. (pc->type == CT_FUNC_PROTO) ||
  610. (pc->type == CT_FUNC_CLASS_DEF) ||
  611. (pc->type == CT_FUNC_CLASS_PROTO) ||
  612. (pc->type == CT_OC_MSG_DECL))
  613. {
  614. return(pc);
  615. }
  616. }
  617. return(NULL);
  618. }
  619. static chunk_t *get_next_class(chunk_t *pc)
  620. {
  621. return(chunk_get_next(search_next_chunk(pc, CT_CLASS)));
  622. }
  623. static chunk_t *get_prev_category(chunk_t *pc)
  624. {
  625. return(search_prev_chunk(pc, CT_OC_CATEGORY));
  626. }
  627. static chunk_t *get_next_scope(chunk_t *pc)
  628. {
  629. return(search_next_chunk(pc, CT_OC_SCOPE));
  630. }
  631. static chunk_t *get_prev_oc_class(chunk_t *pc)
  632. {
  633. return(search_prev_chunk(pc, CT_OC_CLASS));
  634. }
  635. static int next_up(const unc_text &text, size_t idx, unc_text &tag)
  636. {
  637. size_t offs = 0;
  638. while ((idx < text.size()) && unc_isspace(text[idx]))
  639. {
  640. idx++;
  641. offs++;
  642. }
  643. if (text.startswith(tag, idx))
  644. {
  645. return(offs);
  646. }
  647. return(-1);
  648. }
  649. /**
  650. * Outputs a comment. The initial opening '//' may be included in the text.
  651. * Subsequent openings (if combining comments), should not be included.
  652. * The closing (for C/D comments) should not be included.
  653. *
  654. * TODO:
  655. * If reflowing text, the comment should be added one word (or line) at a time.
  656. * A newline should only be sent if a blank line is encountered or if the next
  657. * line is indented beyond the current line (optional?).
  658. * If the last char on a line is a ':' or '.', then the next line won't be
  659. * combined.
  660. */
  661. static void add_comment_text(const unc_text &text,
  662. cmt_reflow &cmt, bool esc_close)
  663. {
  664. bool was_star = false;
  665. bool was_slash = false;
  666. bool in_word = false;
  667. size_t len = text.size();
  668. size_t ch_cnt = 0; /* chars since newline */
  669. /* If the '//' is included write it first else we may wrap an empty line */
  670. size_t idx = 0;
  671. if (text.startswith("//"))
  672. {
  673. add_text("//");
  674. idx += 2;
  675. while (unc_isspace(text[idx]))
  676. {
  677. add_char(text[idx++]);
  678. }
  679. }
  680. for ( ; idx < len; idx++)
  681. {
  682. /* Split the comment */
  683. if (text[idx] == '\n')
  684. {
  685. in_word = false;
  686. add_char('\n');
  687. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  688. if (cmt.xtra_indent > 0)
  689. {
  690. add_char(' ');
  691. }
  692. /* hack to get escaped newlines to align and not dup the leading '//' */
  693. int tmp = next_up(text, idx + 1, cmt.cont_text);
  694. if (tmp < 0)
  695. {
  696. add_text(cmt.cont_text);
  697. }
  698. else
  699. {
  700. idx += tmp;
  701. }
  702. ch_cnt = 0;
  703. }
  704. else if (cmt.reflow &&
  705. (text[idx] == ' ') &&
  706. (cpd.settings[UO_cmt_width].n > 0) &&
  707. ((cpd.column > cpd.settings[UO_cmt_width].n) ||
  708. ((ch_cnt > 1) && next_word_exceeds_limit(text, idx))))
  709. {
  710. in_word = false;
  711. add_char('\n');
  712. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  713. if (cmt.xtra_indent > 0)
  714. {
  715. add_char(' ');
  716. }
  717. add_text(cmt.cont_text);
  718. output_to_column(cmt.column + cpd.settings[UO_cmt_sp_after_star_cont].n,
  719. false);
  720. ch_cnt = 0;
  721. }
  722. else
  723. {
  724. /* Escape a C closure in a CPP comment */
  725. if (esc_close &&
  726. ((was_star && (text[idx] == '/')) ||
  727. (was_slash && (text[idx] == '*'))))
  728. {
  729. add_char(' ');
  730. }
  731. if (!in_word && !unc_isspace(text[idx]))
  732. {
  733. cmt.word_count++;
  734. }
  735. in_word = !unc_isspace(text[idx]);
  736. add_char(text[idx]);
  737. was_star = (text[idx] == '*');
  738. was_slash = (text[idx] == '/');
  739. ch_cnt++;
  740. }
  741. }
  742. } // add_comment_text
  743. static void output_cmt_start(cmt_reflow &cmt, chunk_t *pc)
  744. {
  745. cmt.pc = pc;
  746. cmt.column = pc->column;
  747. cmt.brace_col = pc->column_indent;
  748. cmt.base_col = pc->column_indent;
  749. cmt.word_count = 0;
  750. cmt.xtra_indent = 0;
  751. cmt.cont_text.clear();
  752. cmt.reflow = false;
  753. if ((pc->flags & PCF_INSERTED))
  754. {
  755. do_kw_subst(pc);
  756. }
  757. if (cmt.brace_col == 0)
  758. {
  759. cmt.brace_col = 1 + (pc->brace_level * cpd.settings[UO_output_tab_size].u);
  760. }
  761. // LOG_FMT(LSYS, "%s: line %d, brace=%d base=%d col=%d orig=%d aligned=%x\n",
  762. // __func__, pc->orig_line, cmt.brace_col, cmt.base_col, cmt.column, pc->orig_col,
  763. // pc->flags & (PCF_WAS_ALIGNED | PCF_RIGHT_COMMENT));
  764. if ((pc->parent_type == CT_COMMENT_START) ||
  765. (pc->parent_type == CT_COMMENT_WHOLE))
  766. {
  767. if (!cpd.settings[UO_indent_col1_comment].b &&
  768. (pc->orig_col == 1) &&
  769. !(pc->flags & PCF_INSERTED))
  770. {
  771. cmt.column = 1;
  772. cmt.base_col = 1;
  773. cmt.brace_col = 1;
  774. }
  775. }
  776. /* tab aligning code */
  777. if (cpd.settings[UO_indent_cmt_with_tabs].b &&
  778. ((pc->parent_type == CT_COMMENT_END) ||
  779. (pc->parent_type == CT_COMMENT_WHOLE)))
  780. {
  781. cmt.column = align_tab_column(cmt.column - 1);
  782. // LOG_FMT(LSYS, "%s: line %d, orig:%d new:%d\n",
  783. // __func__, pc->orig_line, pc->column, cmt.column);
  784. pc->column = cmt.column;
  785. }
  786. cmt.base_col = cmt.column;
  787. // LOG_FMT(LSYS, "%s: -- brace=%d base=%d col=%d\n",
  788. // __func__, cmt.brace_col, cmt.base_col, cmt.column);
  789. /* Bump out to the column */
  790. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  791. } // output_cmt_start
  792. /**
  793. * Checks to see if the current comment can be combined with the next comment.
  794. * The two can be combined if:
  795. * 1. They are the same type
  796. * 2. There is exactly one newline between then
  797. * 3. They are indented to the same level
  798. */
  799. static bool can_combine_comment(chunk_t *pc, cmt_reflow &cmt)
  800. {
  801. /* We can't combine if there is something other than a newline next */
  802. if (pc->parent_type == CT_COMMENT_START)
  803. {
  804. return(false);
  805. }
  806. /* next is a newline for sure, make sure it is a single newline */
  807. chunk_t *next = chunk_get_next(pc);
  808. if ((next != NULL) && (next->nl_count == 1))
  809. {
  810. /* Make sure the comment is the same type at the same column */
  811. next = chunk_get_next(next);
  812. if ((next != NULL) &&
  813. (next->type == pc->type) &&
  814. (((next->column == 1) && (pc->column == 1)) ||
  815. ((next->column == cmt.base_col) && (pc->column == cmt.base_col)) ||
  816. ((next->column > cmt.base_col) && (pc->parent_type == CT_COMMENT_END))))
  817. {
  818. return(true);
  819. }
  820. }
  821. return(false);
  822. } // can_combine_comment
  823. /**
  824. * Outputs the C comment at pc.
  825. * C comment combining is done here
  826. *
  827. * @return the last chunk output'd
  828. */
  829. static chunk_t *output_comment_c(chunk_t *first)
  830. {
  831. cmt_reflow cmt;
  832. output_cmt_start(cmt, first);
  833. cmt.reflow = (cpd.settings[UO_cmt_reflow_mode].n != 1);
  834. /* See if we can combine this comment with the next comment */
  835. if (!cpd.settings[UO_cmt_c_group].b ||
  836. !can_combine_comment(first, cmt))
  837. {
  838. /* Just add the single comment */
  839. cmt.cont_text = cpd.settings[UO_cmt_star_cont].b ? " * " : " ";
  840. LOG_CONTTEXT();
  841. add_comment_text(first->str, cmt, false);
  842. return(first);
  843. }
  844. cmt.cont_text = cpd.settings[UO_cmt_star_cont].b ? " *" : " ";
  845. LOG_CONTTEXT();
  846. add_text("/*");
  847. if (cpd.settings[UO_cmt_c_nl_start].b)
  848. {
  849. add_comment_text("\n", cmt, false);
  850. }
  851. chunk_t *pc = first;
  852. unc_text tmp;
  853. while (can_combine_comment(pc, cmt))
  854. {
  855. tmp.set(pc->str, 2, pc->len() - 4);
  856. if ((cpd.last_char == '*') && (tmp[0] == '/'))
  857. {
  858. add_text(" ");
  859. }
  860. add_comment_text(tmp, cmt, false);
  861. add_comment_text("\n", cmt, false);
  862. pc = chunk_get_next(chunk_get_next(pc));
  863. }
  864. tmp.set(pc->str, 2, pc->len() - 4);
  865. if ((cpd.last_char == '*') && (tmp[0] == '/'))
  866. {
  867. add_text(" ");
  868. }
  869. add_comment_text(tmp, cmt, false);
  870. if (cpd.settings[UO_cmt_c_nl_end].b)
  871. {
  872. cmt.cont_text = " ";
  873. LOG_CONTTEXT();
  874. add_comment_text("\n", cmt, false);
  875. }
  876. add_comment_text("*/", cmt, false);
  877. return(pc);
  878. } // output_comment_c
  879. /**
  880. * Outputs the CPP comment at pc.
  881. * CPP comment combining is done here
  882. *
  883. * @return the last chunk output'd
  884. */
  885. static chunk_t *output_comment_cpp(chunk_t *first)
  886. {
  887. cmt_reflow cmt;
  888. output_cmt_start(cmt, first);
  889. cmt.reflow = (cpd.settings[UO_cmt_reflow_mode].n != 1);
  890. unc_text leadin = "//"; // default setting to keep previous behaviour
  891. if (cpd.settings[UO_sp_cmt_cpp_doxygen].b) // special treatment for doxygen style comments (treat as unity)
  892. {
  893. const char *sComment = first->text();
  894. bool grouping = (sComment[2] == '@');
  895. size_t brace = 3;
  896. if ((sComment[2] == '/') || (sComment[2] == '!')) // doxygen style found!
  897. {
  898. leadin += sComment[2]; // at least one additional char (either "///" or "//!")
  899. if (sComment[3] == '<') // and a further one (either "///<" or "//!<")
  900. {
  901. leadin += '<';
  902. }
  903. else
  904. {
  905. grouping = (sComment[3] == '@'); // or a further one (grouping)
  906. brace = 4;
  907. }
  908. }
  909. if (grouping && ((sComment[brace] == '{') || (sComment[brace] == '}')))
  910. {
  911. leadin += '@';
  912. leadin += sComment[brace];
  913. }
  914. }
  915. /* Special treatment for Qt translator or meta-data comments (treat as unity) */
  916. if (cpd.settings[UO_sp_cmt_cpp_qttr].b)
  917. {
  918. const int c = first->str[2];
  919. if ((c == ':') || (c == '=') || (c == '~'))
  920. {
  921. leadin += c;
  922. }
  923. }
  924. /* CPP comments can't be grouped unless they are converted to C comments */
  925. if (!cpd.settings[UO_cmt_cpp_to_c].b)
  926. {
  927. cmt.cont_text = leadin;
  928. if (cpd.settings[UO_sp_cmt_cpp_start].a != AV_REMOVE)
  929. {
  930. cmt.cont_text += ' ';
  931. }
  932. LOG_CONTTEXT();
  933. if (cpd.settings[UO_sp_cmt_cpp_start].a == AV_IGNORE)
  934. {
  935. add_comment_text(first->str, cmt, false);
  936. }
  937. else
  938. {
  939. size_t iLISz = leadin.size();
  940. unc_text tmp(first->str, 0, iLISz);
  941. add_comment_text(tmp, cmt, false);
  942. tmp.set(first->str, iLISz, first->len() - iLISz);
  943. if (cpd.settings[UO_sp_cmt_cpp_start].a & AV_REMOVE)
  944. {
  945. while ((tmp.size() > 0) && unc_isspace(tmp[0]))
  946. {
  947. tmp.pop_front();
  948. }
  949. }
  950. if (tmp.size() > 0)
  951. {
  952. if (cpd.settings[UO_sp_cmt_cpp_start].a & AV_ADD)
  953. {
  954. if (!unc_isspace(tmp[0]) && (tmp[0] != '/'))
  955. {
  956. add_comment_text(" ", cmt, false);
  957. }
  958. }
  959. add_comment_text(tmp, cmt, false);
  960. }
  961. }
  962. return(first);
  963. }
  964. /* We are going to convert the CPP comments to C comments */
  965. cmt.cont_text = cpd.settings[UO_cmt_star_cont].b ? " * " : " ";
  966. LOG_CONTTEXT();
  967. unc_text tmp;
  968. /* See if we can combine this comment with the next comment */
  969. if (!cpd.settings[UO_cmt_cpp_group].b ||
  970. !can_combine_comment(first, cmt))
  971. {
  972. /* nothing to group: just output a single line */
  973. add_text("/*");
  974. // patch # 32, 2012-03-23
  975. if (!unc_isspace(first->str[2]) && (cpd.settings[UO_sp_cmt_cpp_start].a & AV_ADD))
  976. {
  977. add_char(' ');
  978. }
  979. tmp.set(first->str, 2, first->len() - 2);
  980. add_comment_text(tmp, cmt, true);
  981. add_text(" */");
  982. return(first);
  983. }
  984. add_text("/*");
  985. if (cpd.settings[UO_cmt_cpp_nl_start].b)
  986. {
  987. add_comment_text("\n", cmt, false);
  988. }
  989. else
  990. {
  991. add_text(" ");
  992. }
  993. chunk_t *pc = first;
  994. int offs;
  995. while (can_combine_comment(pc, cmt))
  996. {
  997. offs = unc_isspace(pc->str[2]) ? 1 : 0;
  998. tmp.set(pc->str, 2 + offs, pc->len() - (2 + offs));
  999. if ((cpd.last_char == '*') && (tmp[0] == '/'))
  1000. {
  1001. add_text(" ");
  1002. }
  1003. add_comment_text(tmp, cmt, true);
  1004. add_comment_text("\n", cmt, false);
  1005. pc = chunk_get_next(chunk_get_next(pc));
  1006. }
  1007. offs = unc_isspace(pc->str[2]) ? 1 : 0;
  1008. tmp.set(pc->str, 2 + offs, pc->len() - (2 + offs));
  1009. add_comment_text(tmp, cmt, true);
  1010. if (cpd.settings[UO_cmt_cpp_nl_end].b)
  1011. {
  1012. cmt.cont_text = "";
  1013. LOG_CONTTEXT();
  1014. add_comment_text("\n", cmt, false);
  1015. }
  1016. add_comment_text(" */", cmt, false);
  1017. return(pc);
  1018. } // output_comment_cpp
  1019. static void cmt_trim_whitespace(unc_text &line, bool in_preproc)
  1020. {
  1021. /* Remove trailing whitespace on the line */
  1022. while ((line.size() > 0) &&
  1023. ((line.back() == ' ') ||
  1024. (line.back() == '\t')))
  1025. {
  1026. line.pop_back();
  1027. }
  1028. /* If in a preproc, shift any bs-nl back to the comment text */
  1029. if (in_preproc && (line.size() > 1) && (line.back() == '\\'))
  1030. {
  1031. bool do_space = false;
  1032. /* If there was any space before the backslash, change it to 1 space */
  1033. line.pop_back();
  1034. while ((line.size() > 0) &&
  1035. ((line.back() == ' ') ||
  1036. (line.back() == '\t')))
  1037. {
  1038. do_space = true;
  1039. line.pop_back();
  1040. }
  1041. if (do_space)
  1042. {
  1043. line.append(' ');
  1044. }
  1045. line.append('\\');
  1046. }
  1047. } // cmt_trim_whitespace
  1048. /**
  1049. * A multiline comment -- woopeee!
  1050. * The only trick here is that we have to trim out whitespace characters
  1051. * to get the comment to line up.
  1052. */
  1053. static void output_comment_multi(chunk_t *pc)
  1054. {
  1055. cmt_reflow cmt;
  1056. // LOG_FMT(LSYS, "%s: line %d\n", __func__, pc->orig_line);
  1057. output_cmt_start(cmt, pc);
  1058. cmt.reflow = (cpd.settings[UO_cmt_reflow_mode].n != 1);
  1059. size_t cmt_col = cmt.base_col;
  1060. int col_diff = pc->orig_col - cmt.base_col;
  1061. calculate_comment_body_indent(cmt, pc->str);
  1062. cmt.cont_text = !cpd.settings[UO_cmt_indent_multi].b ? "" :
  1063. (cpd.settings[UO_cmt_star_cont].b ? "* " : " ");
  1064. LOG_CONTTEXT();
  1065. // LOG_FMT(LSYS, "Indenting1 line %d to col %d (orig=%d) col_diff=%d xtra=%d cont='%s'\n",
  1066. // pc->orig_line, cmt_col, pc->orig_col, col_diff, cmt.xtra_indent, cmt.cont_text.c_str());
  1067. size_t line_count = 0;
  1068. size_t ccol = pc->column; /* the col of subsequent comment lines */
  1069. size_t cmt_idx = 0;
  1070. bool nl_end = false;
  1071. unc_text line;
  1072. line.clear();
  1073. while (cmt_idx < pc->len())
  1074. {
  1075. int ch = pc->str[cmt_idx++];
  1076. /* handle the CRLF and CR endings. convert both to LF */
  1077. if (ch == '\r')
  1078. {
  1079. ch = '\n';
  1080. if ((cmt_idx < pc->len()) && (pc->str[cmt_idx] == '\n'))
  1081. {
  1082. cmt_idx++;
  1083. }
  1084. }
  1085. /* Find the start column */
  1086. if (line.size() == 0)
  1087. {
  1088. nl_end = false;
  1089. if (ch == ' ')
  1090. {
  1091. ccol++;
  1092. continue;
  1093. }
  1094. else if (ch == '\t')
  1095. {
  1096. ccol = calc_next_tab_column(ccol, cpd.settings[UO_input_tab_size].u);
  1097. continue;
  1098. }
  1099. else
  1100. {
  1101. // LOG_FMT(LSYS, "%d] Text starts in col %d\n", line_count, ccol);
  1102. }
  1103. }
  1104. /*
  1105. * Now see if we need/must fold the next line with the current to enable
  1106. * full reflow
  1107. */
  1108. if ((cpd.settings[UO_cmt_reflow_mode].n == 2) &&
  1109. (ch == '\n') &&
  1110. (cmt_idx < pc->len()))
  1111. {
  1112. int next_nonempty_line = -1;
  1113. int prev_nonempty_line = -1;
  1114. size_t nwidx = line.size();
  1115. bool star_is_bullet = false;
  1116. /* strip trailing whitespace from the line collected so far */
  1117. while (nwidx > 0)
  1118. {
  1119. nwidx--;
  1120. if ((prev_nonempty_line < 0) &&
  1121. !unc_isspace(line[nwidx]) &&
  1122. (line[nwidx] != '*') && // block comment: skip '*' at end of line
  1123. ((pc->flags & PCF_IN_PREPROC)
  1124. ? (line[nwidx] != '\\') ||
  1125. ((line[nwidx + 1] != 'r') &&
  1126. (line[nwidx + 1] != '\n'))
  1127. : true))
  1128. {
  1129. prev_nonempty_line = nwidx; // last nonwhitespace char in the previous line
  1130. }
  1131. }
  1132. size_t remaining = pc->len() - cmt_idx;
  1133. for (size_t nxt_len = 0;
  1134. (nxt_len <= remaining) &&
  1135. (pc->str[nxt_len] != 'r') &&
  1136. (pc->str[nxt_len] != '\n');
  1137. nxt_len++)
  1138. {
  1139. if ((next_nonempty_line < 0) &&
  1140. !unc_isspace(pc->str[nxt_len]) &&
  1141. (pc->str[nxt_len] != '*') &&
  1142. ((nxt_len == remaining) ||
  1143. ((pc->flags & PCF_IN_PREPROC)
  1144. ? (pc->str[nxt_len] != '\\') ||
  1145. ((pc->str[nxt_len + 1] != 'r') &&
  1146. (pc->str[nxt_len + 1] != '\n'))
  1147. : true)))
  1148. {
  1149. next_nonempty_line = nxt_len; // first nonwhitespace char in the next line
  1150. }
  1151. }
  1152. /*
  1153. * see if we should fold up; usually that'd be a YES, but there are a few
  1154. * situations where folding/reflowing by merging lines is frowned upon:
  1155. *
  1156. * - ASCII art in the comments (most often, these are drawings done in +-\/|.,*)
  1157. *
  1158. * - Doxygen/JavaDoc/etc. parameters: these often start with \ or @, at least
  1159. * something clearly non-alphanumeric (you see where we're going with this?)
  1160. *
  1161. * - bullet lists that are closely spaced: bullets are always non-alphanumeric
  1162. * characters, such as '-' or '+' (or, oh horror, '*' - that's bloody ambiguous
  1163. * to parse :-( ... with or without '*' comment start prefix, that's the
  1164. * question, then.)
  1165. *
  1166. * - semi-HTML formatted code, e.g. <pre>...</pre> comment sections (NDoc, etc.)
  1167. *
  1168. * - New lines which form a new paragraph without there having been added an
  1169. * extra empty line between the last sentence and the new one.
  1170. * A bit like this, really; so it is opportune to check if the last line ended
  1171. * in a terminal (that would be the set '.:;!?') and the new line starts with
  1172. * a capital.
  1173. * Though new lines starting with comment delimiters, such as '(', should be
  1174. * pulled up.
  1175. *
  1176. * So it bores down to this: the only folding (& reflowing) that's going to happen
  1177. * is when the next line starts with an alphanumeric character AND the last
  1178. * line didn't end with an non-alphanumeric character, except: ',' AND the next
  1179. * line didn't start with a '*' all of a sudden while the previous one didn't
  1180. * (the ambiguous '*'-for-bullet case!)
  1181. */
  1182. if ((prev_nonempty_line >= 0) && (next_nonempty_line >= 0) &&
  1183. (((unc_isalnum(line[prev_nonempty_line]) ||
  1184. strchr(",)]", line[prev_nonempty_line])) &&
  1185. (unc_isalnum(pc->str[next_nonempty_line]) ||
  1186. strchr("([", pc->str[next_nonempty_line]))) ||
  1187. (('.' == line[prev_nonempty_line]) && // dot followed by non-capital is NOT a new sentence start
  1188. unc_isupper(pc->str[next_nonempty_line]))) &&
  1189. !star_is_bullet)
  1190. {
  1191. // rewind the line to the last non-alpha:
  1192. line.resize(prev_nonempty_line + 1);
  1193. // roll the current line forward to the first non-alpha:
  1194. cmt_idx += next_nonempty_line;
  1195. // override the NL and make it a single whitespace:
  1196. ch = ' ';
  1197. }
  1198. }
  1199. line.append(ch);
  1200. /* If we just hit an end of line OR we just hit end-of-comment... */
  1201. if ((ch == '\n') || (cmt_idx == pc->len()))
  1202. {
  1203. line_count++;
  1204. /* strip trailing tabs and spaces before the newline */
  1205. if (ch == '\n')
  1206. {
  1207. nl_end = true;
  1208. line.pop_back();
  1209. cmt_trim_whitespace(line, pc->flags & PCF_IN_PREPROC);
  1210. }
  1211. // LOG_FMT(LSYS, "[%3d]%s\n", ccol, line);
  1212. if (line_count == 1)
  1213. {
  1214. /* this is the first line - add unchanged */
  1215. add_comment_text(line, cmt, false);
  1216. if (nl_end)
  1217. {
  1218. add_char('\n');
  1219. }
  1220. }
  1221. else
  1222. {
  1223. /* This is not the first line, so we need to indent to the
  1224. * correct column. Each line is indented 0 or more spaces.
  1225. */
  1226. ccol -= col_diff;
  1227. if (ccol < (cmt_col + 3))
  1228. {
  1229. ccol = cmt_col + 3;
  1230. }
  1231. if (line.size() == 0)
  1232. {
  1233. /* Empty line - just a '\n' */
  1234. if (cpd.settings[UO_cmt_star_cont].b)
  1235. {
  1236. cmt.column = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].u;
  1237. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  1238. if (cmt.xtra_indent > 0)
  1239. {
  1240. add_char(' ');
  1241. }
  1242. add_text(cmt.cont_text);
  1243. }
  1244. add_char('\n');
  1245. }
  1246. else
  1247. {
  1248. /* If this doesn't start with a '*' or '|'.
  1249. * '\name' is a common parameter documentation thing.
  1250. */
  1251. if (cpd.settings[UO_cmt_indent_multi].b &&
  1252. (line[0] != '*') && (line[0] != '|') && (line[0] != '#') &&
  1253. ((line[0] != '\\') || unc_isalpha(line[1])) && (line[0] != '+'))
  1254. {
  1255. size_t start_col = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].u;
  1256. if (cpd.settings[UO_cmt_star_cont].b)
  1257. {
  1258. cmt.column = start_col;
  1259. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  1260. if (cmt.xtra_indent > 0)
  1261. {
  1262. add_char(' ');
  1263. }
  1264. add_text(cmt.cont_text);
  1265. output_to_column(ccol + cpd.settings[UO_cmt_sp_after_star_cont].n,
  1266. false);
  1267. }
  1268. else
  1269. {
  1270. cmt.column = ccol;
  1271. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  1272. }
  1273. }
  1274. else
  1275. {
  1276. cmt.column = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].u;
  1277. cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column);
  1278. if (cmt.xtra_indent > 0)
  1279. {
  1280. add_char(' ');
  1281. }
  1282. size_t idx;
  1283. // Checks for and updates the lead chars.
  1284. // @return 0=not present, >0=number of chars that are part of the lead
  1285. idx = cmt_parse_lead(line, (cmt_idx == pc->len()));
  1286. if (idx > 0)
  1287. {
  1288. // >0=number of chars that are part of the lead
  1289. cmt.cont_text.set(line, 0, idx);
  1290. LOG_CONTTEXT();
  1291. if ((line.size() >= 2) && (line[0] == '*') && unc_isalnum(line[1]))
  1292. {
  1293. line.insert(1, ' ');
  1294. }
  1295. }
  1296. else
  1297. {
  1298. // bug #653
  1299. if (cpd.lang_flags & LANG_D)
  1300. {
  1301. // 0=no lead char present
  1302. add_text(cmt.cont_text);
  1303. }
  1304. }
  1305. }
  1306. add_comment_text(line, cmt, false);
  1307. if (nl_end)
  1308. {
  1309. add_text("\n");
  1310. }
  1311. }
  1312. }
  1313. line.clear();
  1314. ccol = 1;
  1315. }
  1316. }
  1317. } // output_comment_multi
  1318. static bool kw_fcn_filename(chunk_t *cmt, unc_text &out_txt)
  1319. {
  1320. UNUSED(cmt);
  1321. out_txt.append(path_basename(cpd.filename));
  1322. return(true);
  1323. }
  1324. static bool kw_fcn_class(chunk_t *cmt, unc_text &out_txt)
  1325. {
  1326. chunk_t *tmp = NULL;
  1327. if ((cpd.lang_flags & LANG_CPP) && (cpd.lang_flags & LANG_OC))
  1328. {
  1329. chunk_t *fcn = get_next_function(cmt);
  1330. if (fcn->type == CT_OC_MSG_DECL)
  1331. {
  1332. tmp = get_prev_oc_class(cmt);
  1333. }
  1334. else
  1335. {
  1336. tmp = get_next_class(cmt);
  1337. }
  1338. }
  1339. else if (cpd.lang_flags & LANG_OC)
  1340. {
  1341. tmp = get_prev_oc_class(cmt);
  1342. }
  1343. if (tmp == NULL)
  1344. {
  1345. tmp = get_next_class(cmt);
  1346. }
  1347. if (tmp)
  1348. {
  1349. out_txt.append(tmp->str);
  1350. if (cpd.lang_flags & cpd.lang_flags) /* \todo strange condition ? */
  1351. {
  1352. while ((tmp = chunk_get_next(tmp)) != NULL)
  1353. {
  1354. if (tmp->type != CT_DC_MEMBER)
  1355. {
  1356. break;
  1357. }
  1358. tmp = chunk_get_next(tmp);
  1359. if (tmp)
  1360. {
  1361. out_txt.append("::");
  1362. out_txt.append(tmp->str);
  1363. }
  1364. }
  1365. }
  1366. return(true);
  1367. }
  1368. return(false);
  1369. } // kw_fcn_class
  1370. static bool kw_fcn_message(chunk_t *cmt, unc_text &out_txt)
  1371. {
  1372. chunk_t *fcn = get_next_function(cmt);
  1373. if (!fcn)
  1374. {
  1375. return(false);
  1376. }
  1377. out_txt.append(fcn->str);
  1378. chunk_t *tmp = chunk_get_next_ncnl(fcn);
  1379. chunk_t *word = NULL;
  1380. while (tmp)
  1381. {
  1382. if ((tmp->type == CT_BRACE_OPEN) || (tmp->type == CT_SEMICOLON))
  1383. {
  1384. break;
  1385. }
  1386. if (tmp->type == CT_OC_COLON)
  1387. {
  1388. if (word != NULL)
  1389. {
  1390. out_txt.append(word->str);
  1391. word = NULL;
  1392. }
  1393. out_txt.append(":");
  1394. }
  1395. if (tmp->type == CT_WORD)
  1396. {
  1397. word = tmp;
  1398. }
  1399. tmp = chunk_get_next_ncnl(tmp);
  1400. }
  1401. return(true);
  1402. } // kw_fcn_message
  1403. static bool kw_fcn_category(chunk_t *cmt, unc_text &out_txt)
  1404. {
  1405. chunk_t *category = get_prev_category(cmt);
  1406. if (category)
  1407. {
  1408. out_txt.append('(');
  1409. out_txt.append(category->str);
  1410. out_txt.append(')');
  1411. }
  1412. return(true);
  1413. } // kw_fcn_category
  1414. static bool kw_fcn_scope(chunk_t *cmt, unc_text &out_txt)
  1415. {
  1416. chunk_t *scope = get_next_scope(cmt);
  1417. if (scope)
  1418. {
  1419. out_txt.append(scope->str);
  1420. return(true);
  1421. }
  1422. return(false);
  1423. } // kw_fcn_scope
  1424. static bool kw_fcn_function(chunk_t *cmt, unc_text &out_txt)
  1425. {
  1426. chunk_t *fcn = get_next_function(cmt);
  1427. if (fcn)
  1428. {
  1429. if (fcn->parent_type == CT_OPERATOR)
  1430. {
  1431. out_txt.append("operator ");
  1432. }
  1433. if (fcn->prev && fcn->prev->type == CT_DESTRUCTOR)
  1434. {
  1435. out_txt.append('~');
  1436. }
  1437. out_txt.append(fcn->str);
  1438. return(true);
  1439. }
  1440. return(false);
  1441. }
  1442. /**
  1443. * Adds the javadoc-style @param and @return stuff, based on the params and
  1444. * return value for pc.
  1445. * If the arg list is '()' or '(void)', then no @params are added.
  1446. * Likewise, if the return value is 'void', then no @return is added.
  1447. */
  1448. static bool kw_fcn_javaparam(chunk_t *cmt, unc_text &out_txt)
  1449. {
  1450. chunk_t *fcn = get_next_function(cmt);
  1451. if (!fcn)
  1452. {
  1453. return(false);
  1454. }
  1455. chunk_t *fpo;
  1456. chunk_t *fpc;
  1457. bool has_param = true;
  1458. bool need_nl = false;
  1459. if (fcn->type == CT_OC_MSG_DECL)
  1460. {
  1461. chunk_t *tmp = chunk_get_next_ncnl(fcn);
  1462. has_param = false;
  1463. while (tmp)
  1464. {
  1465. if ((tmp->type == CT_BRACE_OPEN) || (tmp->type == CT_SEMICOLON))
  1466. {
  1467. break;
  1468. }
  1469. if (has_param)
  1470. {
  1471. if (need_nl)
  1472. {
  1473. out_txt.append("\n");
  1474. }
  1475. need_nl = true;
  1476. out_txt.append("@param");
  1477. out_txt.append(" ");
  1478. out_txt.append(tmp->str);
  1479. out_txt.append(" TODO");
  1480. }
  1481. has_param = false;
  1482. if (tmp->type == CT_PAREN_CLOSE)
  1483. {
  1484. has_param = true;
  1485. }
  1486. tmp = chunk_get_next_ncnl(tmp);
  1487. }
  1488. fpo = fpc = NULL;
  1489. }
  1490. else
  1491. {
  1492. fpo = chunk_get_next_type(fcn, CT_FPAREN_OPEN, fcn->level);
  1493. if (fpo == NULL)
  1494. {
  1495. return(true);
  1496. }
  1497. fpc = chunk_get_next_type(fpo, CT_FPAREN_CLOSE, fcn->level);
  1498. if (fpc == NULL)
  1499. {
  1500. return(true);
  1501. }
  1502. }
  1503. chunk_t *tmp;
  1504. /* Check for 'foo()' and 'foo(void)' */
  1505. if (chunk_get_next_ncnl(fpo) == fpc)
  1506. {
  1507. has_param = false;
  1508. }
  1509. else
  1510. {
  1511. tmp = chunk_get_next_ncnl(fpo);
  1512. if ((tmp == chunk_get_prev_ncnl(fpc)) &&
  1513. chunk_is_str(tmp, "void", 4))
  1514. {
  1515. has_param = false;
  1516. }
  1517. }
  1518. if (has_param)
  1519. {
  1520. chunk_t *prev = NULL;
  1521. tmp = fpo;
  1522. while ((tmp = chunk_get_next(tmp)) != NULL)
  1523. {
  1524. if ((tmp->type == CT_COMMA) || (tmp == fpc))
  1525. {
  1526. if (need_nl)
  1527. {
  1528. out_txt.append("\n");
  1529. }
  1530. need_nl = true;
  1531. out_txt.append("@param");
  1532. if (prev != NULL)
  1533. {
  1534. out_txt.append(" ");
  1535. out_txt.append(prev->str);
  1536. out_txt.append(" TODO");
  1537. }
  1538. prev = NULL;
  1539. if (tmp == fpc)
  1540. {
  1541. break;
  1542. }
  1543. }
  1544. if (tmp->type == CT_WORD)
  1545. {
  1546. prev = tmp;
  1547. }
  1548. }
  1549. }
  1550. /* Do the return stuff */
  1551. tmp = chunk_get_prev_ncnl(fcn);
  1552. /* For Objective-C we need to go to the previous chunk */
  1553. if ((tmp->parent_type == CT_OC_MSG_DECL) && (tmp->type == CT_PAREN_CLOSE))
  1554. {
  1555. tmp = chunk_get_prev_ncnl(tmp);
  1556. }
  1557. if ((tmp != NULL) && !chunk_is_str(tmp, "void", 4))
  1558. {
  1559. if (need_nl)
  1560. {
  1561. out_txt.append("\n");
  1562. }
  1563. out_txt.append("@return TODO");
  1564. }
  1565. return(true);
  1566. } // kw_fcn_javaparam
  1567. static bool kw_fcn_fclass(chunk_t *cmt, unc_text &out_txt)
  1568. {
  1569. chunk_t *fcn = get_next_function(cmt);
  1570. if (!fcn)
  1571. {
  1572. return(false);
  1573. }
  1574. if (fcn->flags & PCF_IN_CLASS)
  1575. {
  1576. /* if inside a class, we need to find to the class name */
  1577. chunk_t *tmp = chunk_get_prev_type(fcn, CT_BRACE_OPEN, fcn->level - 1);
  1578. tmp = chunk_get_prev_type(tmp, CT_CLASS, tmp->level);
  1579. tmp = chunk_get_next_ncnl(tmp);
  1580. while (chunk_is_token(chunk_get_next_ncnl(tmp), CT_DC_MEMBER))
  1581. {
  1582. tmp = chunk_get_next_ncnl(tmp);
  1583. tmp = chunk_get_next_ncnl(tmp);
  1584. }
  1585. if (tmp)
  1586. {
  1587. out_txt.append(tmp->str);
  1588. return(true);
  1589. }
  1590. }
  1591. else
  1592. {
  1593. /* if outside a class, we expect "CLASS::METHOD(...)" */
  1594. chunk_t *tmp = chunk_get_prev_ncnl(fcn);
  1595. if ((tmp != NULL) && (tmp->type == CT_OPERATOR))
  1596. {
  1597. tmp = chunk_get_prev_ncnl(tmp);
  1598. }
  1599. if ((tmp != NULL) && ((tmp->type == CT_DC_MEMBER) ||
  1600. (tmp->type == CT_MEMBER)))
  1601. {
  1602. tmp = chunk_get_prev_ncnl(tmp);
  1603. out_txt.append(tmp->str);
  1604. return(true);
  1605. }
  1606. }
  1607. return(false);
  1608. } // kw_fcn_fclass
  1609. struct kw_subst_t
  1610. {
  1611. const char *tag;
  1612. bool (*func)(chunk_t *cmt, unc_text &out_txt);
  1613. };
  1614. static const kw_subst_t kw_subst_table[] =
  1615. {
  1616. { "$(filename)", kw_fcn_filename },
  1617. { "$(class)", kw_fcn_class },
  1618. { "$(message)", kw_fcn_message },
  1619. { "$(category)", kw_fcn_category },
  1620. { "$(scope)", kw_fcn_scope },
  1621. { "$(function)", kw_fcn_function },
  1622. { "$(javaparam)", kw_fcn_javaparam },
  1623. { "$(fclass)", kw_fcn_fclass },
  1624. };
  1625. /**
  1626. * Do keyword substitution on a comment.
  1627. * NOTE: it is assumed that a comment will contain at most one of each type
  1628. * of keyword.
  1629. */
  1630. static void do_kw_subst(chunk_t *pc)
  1631. {
  1632. for (size_t kw_idx = 0; kw_idx < ARRAY_SIZE(kw_subst_table); kw_idx++)
  1633. {
  1634. const kw_subst_

Large files files are truncated, but you can click here to view the full file