PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/AStyle/src/ASEnhancer.cpp

#
C++ | 737 lines | 529 code | 72 blank | 136 comment | 191 complexity | 1fa4c98109436f582d28b1a842158aef MD5 | raw file
  1. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2. * ASEnhancer.cpp
  3. *
  4. * Copyright (C) 2006-2011 by Jim Pattee <jimp03@email.com>
  5. * Copyright (C) 1998-2002 by Tal Davidson
  6. * <http://www.gnu.org/licenses/lgpl-3.0.html>
  7. *
  8. * This file is a part of Artistic Style - an indentation and
  9. * reformatting tool for C, C++, C# and Java source files.
  10. * <http://astyle.sourceforge.net>
  11. *
  12. * Artistic Style is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Lesser General Public License as published
  14. * by the Free Software Foundation, either version 3 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * Artistic Style is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Lesser General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public License
  23. * along with Artistic Style. If not, see <http://www.gnu.org/licenses/>.
  24. *
  25. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  26. */
  27. #include "astyle.h"
  28. #include <iostream> // for cout
  29. namespace astyle
  30. {
  31. /**
  32. * ASEnhancer constructor
  33. */
  34. ASEnhancer::ASEnhancer()
  35. {
  36. }
  37. /**
  38. * Destructor of ASEnhancer
  39. */
  40. ASEnhancer::~ASEnhancer()
  41. {
  42. }
  43. /**
  44. * initialize the ASEnhancer.
  45. *
  46. * init() is called each time an ASFormatter object is initialized.
  47. */
  48. void ASEnhancer::init(int _fileType,
  49. int _indentLength,
  50. int _tabLength,
  51. bool _useTabs,
  52. bool _forceTab,
  53. bool _caseIndent,
  54. bool _preprocessorIndent,
  55. bool _emptyLineFill)
  56. {
  57. // formatting variables from ASFormatter and ASBeautifier
  58. ASBase::init(_fileType);
  59. indentLength = _indentLength;
  60. tabLength = _tabLength;
  61. useTabs = _useTabs;
  62. forceTab = _forceTab;
  63. caseIndent = _caseIndent;
  64. preprocessorIndent = _preprocessorIndent;
  65. emptyLineFill = _emptyLineFill;
  66. quoteChar = '\'';
  67. // unindent variables
  68. lineNumber = 0;
  69. bracketCount = 0;
  70. isInComment = false;
  71. isInQuote = false;
  72. switchDepth = 0;
  73. lookingForCaseBracket = false;
  74. unindentNextLine = false;
  75. shouldIndentLine = false;
  76. // switch struct and vector
  77. sw.switchBracketCount = 0;
  78. sw.unindentDepth = 0;
  79. sw.unindentCase = false;
  80. switchStack.clear();
  81. // other variables
  82. nextLineIsEventIndent = false;
  83. isInEventTable = false;
  84. nextLineIsDeclareIndent = false;
  85. isInDeclareSection = false;
  86. }
  87. /**
  88. * additional formatting for line of source code.
  89. * every line of source code in a source code file should be sent
  90. * one after the other to this function.
  91. * indents event tables
  92. * unindents the case blocks
  93. *
  94. * @param line the original formatted line will be updated if necessary.
  95. */
  96. void ASEnhancer::enhance(string &line, bool isInPreprocessor, bool isInSQL)
  97. {
  98. bool isSpecialChar = false; // is a backslash escape character
  99. shouldIndentLine = true;
  100. lineNumber++;
  101. // check for beginning of event table
  102. if (nextLineIsEventIndent)
  103. {
  104. isInEventTable = true;
  105. nextLineIsEventIndent = false;
  106. }
  107. // check for beginning of SQL declare section
  108. if (nextLineIsDeclareIndent)
  109. {
  110. isInDeclareSection = true;
  111. nextLineIsDeclareIndent = false;
  112. }
  113. if (line.length() == 0
  114. && ! isInEventTable
  115. && ! isInDeclareSection
  116. && ! emptyLineFill)
  117. return;
  118. // test for unindent on attached brackets
  119. if (unindentNextLine)
  120. {
  121. sw.unindentDepth++;
  122. sw.unindentCase = true;
  123. unindentNextLine = false;
  124. }
  125. // parse characters in the current line.
  126. for (size_t i = 0; i < line.length(); i++)
  127. {
  128. char ch = line[i];
  129. // bypass whitespace
  130. if (isWhiteSpace(ch))
  131. continue;
  132. // handle special characters (i.e. backslash+character such as \n, \t, ...)
  133. if (isSpecialChar)
  134. {
  135. isSpecialChar = false;
  136. continue;
  137. }
  138. if (!(isInComment) && line.compare(i, 2, "\\\\") == 0)
  139. {
  140. i++;
  141. continue;
  142. }
  143. if (!(isInComment) && ch == '\\')
  144. {
  145. isSpecialChar = true;
  146. continue;
  147. }
  148. // handle quotes (such as 'x' and "Hello Dolly")
  149. if (!isInComment && (ch == '"' || ch == '\''))
  150. {
  151. if (!isInQuote)
  152. {
  153. quoteChar = ch;
  154. isInQuote = true;
  155. }
  156. else if (quoteChar == ch)
  157. {
  158. isInQuote = false;
  159. continue;
  160. }
  161. }
  162. if (isInQuote)
  163. continue;
  164. // handle comments
  165. if (!(isInComment) && line.compare(i, 2, "//") == 0)
  166. {
  167. // check for windows line markers
  168. if (line.compare(i + 2, 1, "\xf0") > 0)
  169. lineNumber--;
  170. break; // finished with the line
  171. }
  172. else if (!(isInComment) && line.compare(i, 2, "/*") == 0)
  173. {
  174. isInComment = true;
  175. i++;
  176. continue;
  177. }
  178. else if ((isInComment) && line.compare(i, 2, "*/") == 0)
  179. {
  180. isInComment = false;
  181. i++;
  182. continue;
  183. }
  184. if (isInComment)
  185. continue;
  186. // if we have reached this far then we are NOT in a comment or string of special characters
  187. if (line[i] == '{')
  188. bracketCount++;
  189. if (line[i] == '}')
  190. bracketCount--;
  191. bool isPotentialKeyword = isCharPotentialHeader(line, i);
  192. // ---------------- wxWidgets and MFC macros ----------------------------------
  193. if (isPotentialKeyword)
  194. {
  195. if (findKeyword(line, i, "BEGIN_EVENT_TABLE")
  196. || findKeyword(line, i, "BEGIN_DISPATCH_MAP")
  197. || findKeyword(line, i, "BEGIN_EVENT_MAP")
  198. || findKeyword(line, i, "BEGIN_MESSAGE_MAP")
  199. || findKeyword(line, i, "BEGIN_PROPPAGEIDS"))
  200. {
  201. nextLineIsEventIndent = true;
  202. break;
  203. }
  204. if (findKeyword(line, i, "END_EVENT_TABLE")
  205. || findKeyword(line, i, "END_DISPATCH_MAP")
  206. || findKeyword(line, i, "END_EVENT_MAP")
  207. || findKeyword(line, i, "END_MESSAGE_MAP")
  208. || findKeyword(line, i, "END_PROPPAGEIDS"))
  209. {
  210. isInEventTable = false;
  211. break;
  212. }
  213. }
  214. // ---------------- process SQL -----------------------------------------------
  215. if (isInSQL)
  216. {
  217. if (isBeginDeclareSectionSQL(line, i))
  218. nextLineIsDeclareIndent = true;
  219. if (isEndDeclareSectionSQL(line, i))
  220. isInDeclareSection = false;
  221. break;
  222. }
  223. // ---------------- process switch statements ---------------------------------
  224. if (isPotentialKeyword && findKeyword(line, i, "switch"))
  225. {
  226. switchDepth++;
  227. switchStack.push_back(sw); // save current variables
  228. sw.switchBracketCount = 0;
  229. sw.unindentCase = false; // don't clear case until end of switch
  230. i += 5; // bypass switch statement
  231. continue;
  232. }
  233. // just want unindented case statements from this point
  234. if (caseIndent
  235. || switchDepth == 0
  236. || (isInPreprocessor && !preprocessorIndent))
  237. {
  238. // bypass the entire word
  239. if (isPotentialKeyword)
  240. {
  241. string name = getCurrentWord(line, i);
  242. i += name.length() - 1;
  243. }
  244. continue;
  245. }
  246. i = processSwitchBlock(line, i);
  247. } // end of for loop * end of for loop * end of for loop * end of for loop
  248. if (isInEventTable || isInDeclareSection)
  249. {
  250. if (line.length() == 0 || line[0] != '#')
  251. indentLine(line, 1);
  252. }
  253. if (shouldIndentLine && sw.unindentDepth > 0)
  254. unindentLine(line, sw.unindentDepth);
  255. }
  256. /**
  257. * find the colon following a 'case' statement
  258. *
  259. * @param line a reference to the line.
  260. * @param i the line index of the case statement.
  261. * @return the line index of the colon.
  262. */
  263. size_t ASEnhancer::findCaseColon(string &line, size_t caseIndex) const
  264. {
  265. size_t i = caseIndex;
  266. bool isInQuote_ = false;
  267. char quoteChar_ = ' ';
  268. for (; i < line.length(); i++)
  269. {
  270. if (isInQuote_)
  271. {
  272. if (line[i] == '\\')
  273. {
  274. i++;
  275. continue;
  276. }
  277. else if (line[i] == quoteChar_) // check ending quote
  278. {
  279. isInQuote_ = false;
  280. quoteChar_ = ' ';
  281. continue;
  282. }
  283. else
  284. {
  285. continue; // must close quote before continuing
  286. }
  287. }
  288. if (line[i] == '\'' || line[i] == '\"') // check opening quote
  289. {
  290. isInQuote_ = true;
  291. quoteChar_ = line[i];
  292. continue;
  293. }
  294. if (line[i] == ':')
  295. {
  296. if ((i + 1 < line.length()) && (line[i + 1] == ':'))
  297. i++; // bypass scope resolution operator
  298. else
  299. break; // found it
  300. }
  301. }
  302. return i;
  303. }
  304. /**
  305. * convert a force-tab indent to spaces
  306. *
  307. * @param line a reference to the line that will be converted.
  308. */
  309. void ASEnhancer::convertForceTabIndentToSpaces(string &line) const
  310. {
  311. // replace tab indents with spaces
  312. for (size_t i = 0; i < line.length(); i++)
  313. {
  314. if (!isWhiteSpace(line[i]))
  315. break;
  316. if (line[i] == '\t')
  317. {
  318. line.erase(i, 1);
  319. line.insert(i, tabLength, ' ');
  320. i += tabLength - 1;
  321. }
  322. }
  323. }
  324. /**
  325. * convert a space indent to force-tab
  326. *
  327. * @param line a reference to the line that will be converted.
  328. */
  329. void ASEnhancer::convertSpaceIndentToForceTab(string &line) const
  330. {
  331. assert(tabLength > 0);
  332. // replace leading spaces with tab indents
  333. size_t newSpaceIndentLength = line.find_first_not_of(" \t");
  334. size_t tabCount = newSpaceIndentLength / tabLength; // truncate extra spaces
  335. line.erase(0U, tabCount * tabLength);
  336. line.insert(0U, tabCount, '\t');
  337. }
  338. /**
  339. * indent a line by a given number of tabsets
  340. * by inserting leading whitespace to the line argument.
  341. *
  342. * @param line a reference to the line to indent.
  343. * @param indent the number of tabsets to insert.
  344. * @return the number of characters inserted.
  345. */
  346. int ASEnhancer::indentLine(string &line, int indent) const
  347. {
  348. if (line.length() == 0
  349. && ! emptyLineFill)
  350. return 0;
  351. size_t charsToInsert;
  352. if (forceTab && indentLength != tabLength)
  353. {
  354. // replace tab indents with spaces
  355. convertForceTabIndentToSpaces(line);
  356. // insert the space indents
  357. charsToInsert = indent * indentLength;
  358. line.insert(0U, charsToInsert, ' ');
  359. // replace leading spaces with tab indents
  360. convertSpaceIndentToForceTab(line);
  361. }
  362. else if (useTabs)
  363. {
  364. charsToInsert = indent;
  365. line.insert(0U, charsToInsert, '\t');
  366. }
  367. else // spaces
  368. {
  369. charsToInsert = indent * indentLength;
  370. line.insert(0U, charsToInsert, ' ');
  371. }
  372. return charsToInsert;
  373. }
  374. /**
  375. * check for SQL "BEGIN DECLARE SECTION".
  376. * must compare case insensitive and allow any spacing between words.
  377. *
  378. * @param line a reference to the line to indent.
  379. * @param index the current line index.
  380. * @return true if a hit.
  381. */
  382. bool ASEnhancer::isBeginDeclareSectionSQL(string &line, size_t index) const
  383. {
  384. string word;
  385. size_t hits = 0;
  386. size_t i;
  387. for (i = index; i < line.length(); i++)
  388. {
  389. i = line.find_first_not_of(" \t", i);
  390. if (i == string::npos)
  391. return false;
  392. if (line[i] == ';')
  393. break;
  394. if (!isCharPotentialHeader(line, i))
  395. continue;
  396. word = getCurrentWord(line, i);
  397. for (size_t j = 0; j < word.length(); j++)
  398. word[j] = (char) toupper(word[j]);
  399. if (word == "EXEC" || word == "SQL")
  400. {
  401. i += word.length() - 1;
  402. continue;
  403. }
  404. if (word == "DECLARE" || word == "SECTION")
  405. {
  406. hits++;
  407. i += word.length() - 1;
  408. continue;
  409. }
  410. if (word == "BEGIN")
  411. {
  412. hits++;
  413. i += word.length() - 1;
  414. continue;
  415. }
  416. return false;
  417. }
  418. if (hits == 3)
  419. return true;
  420. return false;
  421. }
  422. /**
  423. * check for SQL "END DECLARE SECTION".
  424. * must compare case insensitive and allow any spacing between words.
  425. *
  426. * @param line a reference to the line to indent.
  427. * @param index the current line index.
  428. * @return true if a hit.
  429. */
  430. bool ASEnhancer::isEndDeclareSectionSQL(string &line, size_t index) const
  431. {
  432. string word;
  433. size_t hits = 0;
  434. size_t i;
  435. for (i = index; i < line.length(); i++)
  436. {
  437. i = line.find_first_not_of(" \t", i);
  438. if (i == string::npos)
  439. return false;
  440. if (line[i] == ';')
  441. break;
  442. if (!isCharPotentialHeader(line, i))
  443. continue;
  444. word = getCurrentWord(line, i);
  445. for (size_t j = 0; j < word.length(); j++)
  446. word[j] = (char) toupper(word[j]);
  447. if (word == "EXEC" || word == "SQL")
  448. {
  449. i += word.length() - 1;
  450. continue;
  451. }
  452. if (word == "DECLARE" || word == "SECTION")
  453. {
  454. hits++;
  455. i += word.length() - 1;
  456. continue;
  457. }
  458. if (word == "END")
  459. {
  460. hits++;
  461. i += word.length() - 1;
  462. continue;
  463. }
  464. return false;
  465. }
  466. if (hits == 3)
  467. return true;
  468. return false;
  469. }
  470. /**
  471. * check if a one-line bracket has been reached,
  472. * i.e. if the currently reached '{' character is closed
  473. * with a complimentry '}' elsewhere on the current line,
  474. *.
  475. * @return false = one-line bracket has not been reached.
  476. * true = one-line bracket has been reached.
  477. */
  478. bool ASEnhancer::isOneLineBlockReached(string &line, int startChar) const
  479. {
  480. assert(line[startChar] == '{');
  481. bool isInComment_ = false;
  482. bool isInQuote_ = false;
  483. int _bracketCount = 1;
  484. int lineLength = line.length();
  485. char quoteChar_ = ' ';
  486. char ch = ' ';
  487. for (int i = startChar + 1; i < lineLength; ++i)
  488. {
  489. ch = line[i];
  490. if (isInComment_)
  491. {
  492. if (line.compare(i, 2, "*/") == 0)
  493. {
  494. isInComment_ = false;
  495. ++i;
  496. }
  497. continue;
  498. }
  499. if (ch == '\\')
  500. {
  501. ++i;
  502. continue;
  503. }
  504. if (isInQuote_)
  505. {
  506. if (ch == quoteChar_)
  507. isInQuote_ = false;
  508. continue;
  509. }
  510. if (ch == '"' || ch == '\'')
  511. {
  512. isInQuote_ = true;
  513. quoteChar_ = ch;
  514. continue;
  515. }
  516. if (line.compare(i, 2, "//") == 0)
  517. break;
  518. if (line.compare(i, 2, "/*") == 0)
  519. {
  520. isInComment_ = true;
  521. ++i;
  522. continue;
  523. }
  524. if (ch == '{')
  525. ++_bracketCount;
  526. else if (ch == '}')
  527. --_bracketCount;
  528. if (_bracketCount == 0)
  529. return true;
  530. }
  531. return false;
  532. }
  533. /**
  534. * process the character at the current index in a switch block.
  535. *
  536. * @param line a reference to the line to indent.
  537. * @param index the current line index.
  538. * @return the new line index.
  539. */
  540. size_t ASEnhancer::processSwitchBlock(string &line, size_t index)
  541. {
  542. size_t i = index;
  543. bool isPotentialKeyword = isCharPotentialHeader(line, i);
  544. if (line[i] == '{')
  545. {
  546. sw.switchBracketCount++;
  547. if (lookingForCaseBracket) // if 1st after case statement
  548. {
  549. sw.unindentCase = true; // unindenting this case
  550. sw.unindentDepth++;
  551. lookingForCaseBracket = false; // not looking now
  552. }
  553. return i;
  554. }
  555. lookingForCaseBracket = false; // no opening bracket, don't indent
  556. if (line[i] == '}')
  557. {
  558. sw.switchBracketCount--;
  559. assert(sw.switchBracketCount <= bracketCount);
  560. if (sw.switchBracketCount == 0) // if end of switch statement
  561. {
  562. int lineUnindent = sw.unindentDepth;
  563. if (line.find_first_not_of(" \t") == i
  564. && !switchStack.empty())
  565. lineUnindent = switchStack[switchStack.size()-1].unindentDepth;
  566. if (shouldIndentLine)
  567. {
  568. if (lineUnindent > 0)
  569. i -= unindentLine(line, lineUnindent);
  570. shouldIndentLine = false;
  571. }
  572. switchDepth--;
  573. sw = switchStack.back();
  574. switchStack.pop_back();
  575. }
  576. return i;
  577. }
  578. if (isPotentialKeyword
  579. && (findKeyword(line, i, "case") || findKeyword(line, i, "default")))
  580. {
  581. if (sw.unindentCase) // if unindented last case
  582. {
  583. sw.unindentCase = false; // stop unindenting previous case
  584. sw.unindentDepth--;
  585. }
  586. i = findCaseColon(line, i);
  587. i++;
  588. for (; i < line.length(); i++) // bypass whitespace
  589. {
  590. if (!isWhiteSpace(line[i]))
  591. break;
  592. }
  593. if (i < line.length())
  594. {
  595. if (line[i] == '{')
  596. {
  597. bracketCount++;
  598. sw.switchBracketCount++;
  599. if (!isOneLineBlockReached(line, i))
  600. unindentNextLine = true;
  601. return i;
  602. }
  603. }
  604. lookingForCaseBracket = true;
  605. i--; // need to process this char
  606. return i;
  607. }
  608. if (isPotentialKeyword)
  609. {
  610. string name = getCurrentWord(line, i); // bypass the entire name
  611. i += name.length() - 1;
  612. }
  613. return i;
  614. }
  615. /**
  616. * unindent a line by a given number of tabsets
  617. * by erasing the leading whitespace from the line argument.
  618. *
  619. * @param line a reference to the line to unindent.
  620. * @param unindent the number of tabsets to erase.
  621. * @return the number of characters erased.
  622. */
  623. int ASEnhancer::unindentLine(string &line, int unindent) const
  624. {
  625. size_t whitespace = line.find_first_not_of(" \t");
  626. if (whitespace == string::npos) // if line is blank
  627. whitespace = line.length(); // must remove padding, if any
  628. if (whitespace == 0)
  629. return 0;
  630. size_t charsToErase = 0;
  631. if (forceTab && indentLength != tabLength)
  632. {
  633. // replace tab indents with spaces
  634. convertForceTabIndentToSpaces(line);
  635. // remove the space indents
  636. size_t spaceIndentLength = line.find_first_not_of(" \t");
  637. charsToErase = unindent * indentLength;
  638. if (charsToErase <= spaceIndentLength)
  639. line.erase(0, charsToErase);
  640. else
  641. charsToErase = 0;
  642. // replace leading spaces with tab indents
  643. convertSpaceIndentToForceTab(line);
  644. }
  645. else if (useTabs)
  646. {
  647. charsToErase = unindent;
  648. if (charsToErase <= whitespace)
  649. line.erase(0, charsToErase);
  650. else
  651. charsToErase = 0;
  652. }
  653. else // spaces
  654. {
  655. charsToErase = unindent * indentLength;
  656. if (charsToErase <= whitespace)
  657. line.erase(0, charsToErase);
  658. else
  659. charsToErase = 0;
  660. }
  661. return charsToErase;
  662. }
  663. } // end namespace astyle