"Fossies" - the Fresh Open Source Software Archive

Member "highlight-3.57-x64/src/core/astyle/ASEnhancer.cpp" (12 May 2020, 18983 Bytes) of package /windows/www/highlight-3.57-x64.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // ASEnhancer.cpp
    2 // Copyright (c) 2018 by Jim Pattee <jimp03@email.com>.
    3 // This code is licensed under the MIT License.
    4 // License.md describes the conditions under which this software may be distributed.
    5 
    6 //-----------------------------------------------------------------------------
    7 // headers
    8 //-----------------------------------------------------------------------------
    9 
   10 #include "astyle/astyle.h"
   11 
   12 //-----------------------------------------------------------------------------
   13 // astyle namespace
   14 //-----------------------------------------------------------------------------
   15 
   16 namespace astyle {
   17 //
   18 //-----------------------------------------------------------------------------
   19 // ASEnhancer class
   20 //-----------------------------------------------------------------------------
   21 
   22 /**
   23  * initialize the ASEnhancer.
   24  *
   25  * init() is called each time an ASFormatter object is initialized.
   26  */
   27 void ASEnhancer::init(int  _fileType,
   28                       int  _indentLength,
   29                       int  _tabLength,
   30                       bool _useTabs,
   31                       bool _forceTab,
   32                       bool _namespaceIndent,
   33                       bool _caseIndent,
   34                       bool _preprocBlockIndent,
   35                       bool _preprocDefineIndent,
   36                       bool _emptyLineFill,
   37                       vector<const pair<const string, const string>* >* _indentableMacros)
   38 {
   39     // formatting variables from ASFormatter and ASBeautifier
   40     ASBase::init(_fileType);
   41     indentLength = _indentLength;
   42     tabLength = _tabLength;
   43     useTabs = _useTabs;
   44     forceTab = _forceTab;
   45     namespaceIndent = _namespaceIndent;
   46     caseIndent = _caseIndent;
   47     preprocBlockIndent = _preprocBlockIndent;
   48     preprocDefineIndent = _preprocDefineIndent;
   49     emptyLineFill = _emptyLineFill;
   50     indentableMacros = _indentableMacros;
   51     quoteChar = '\'';
   52 
   53     // unindent variables
   54     lineNumber = 0;
   55     braceCount = 0;
   56     isInComment = false;
   57     isInQuote = false;
   58     switchDepth = 0;
   59     eventPreprocDepth = 0;
   60     lookingForCaseBrace = false;
   61     unindentNextLine = false;
   62     shouldUnindentLine = false;
   63     shouldUnindentComment = false;
   64 
   65     // switch struct and vector
   66     sw.switchBraceCount = 0;
   67     sw.unindentDepth = 0;
   68     sw.unindentCase = false;
   69     switchStack.clear();
   70 
   71     // other variables
   72     nextLineIsEventIndent = false;
   73     isInEventTable = false;
   74     nextLineIsDeclareIndent = false;
   75     isInDeclareSection = false;
   76 }
   77 
   78 /**
   79  * additional formatting for line of source code.
   80  * every line of source code in a source code file should be sent
   81  *     one after the other to this function.
   82  * indents event tables
   83  * unindents the case blocks
   84  *
   85  * @param line       the original formatted line will be updated if necessary.
   86  */
   87 void ASEnhancer::enhance(string& line, bool isInNamespace, bool isInPreprocessor, bool isInSQL)
   88 {
   89     shouldUnindentLine = true;
   90     shouldUnindentComment = false;
   91     lineNumber++;
   92 
   93     // check for beginning of event table
   94     if (nextLineIsEventIndent)
   95     {
   96         isInEventTable = true;
   97         nextLineIsEventIndent = false;
   98     }
   99 
  100     // check for beginning of SQL declare section
  101     if (nextLineIsDeclareIndent)
  102     {
  103         isInDeclareSection = true;
  104         nextLineIsDeclareIndent = false;
  105     }
  106 
  107     if (line.length() == 0
  108             && !isInEventTable
  109             && !isInDeclareSection
  110             && !emptyLineFill)
  111         return;
  112 
  113     // test for unindent on attached braces
  114     if (unindentNextLine)
  115     {
  116         sw.unindentDepth++;
  117         sw.unindentCase = true;
  118         unindentNextLine = false;
  119     }
  120 
  121     // parse characters in the current line
  122     parseCurrentLine(line, isInPreprocessor, isInSQL);
  123 
  124     // check for SQL indentable lines
  125     if (isInDeclareSection)
  126     {
  127         size_t firstText = line.find_first_not_of(" \t");
  128         if (firstText == string::npos || line[firstText] != '#')
  129             indentLine(line, 1);
  130     }
  131 
  132     // check for event table indentable lines
  133     if (isInEventTable
  134             && (eventPreprocDepth == 0
  135                 || (namespaceIndent && isInNamespace)))
  136     {
  137         size_t firstText = line.find_first_not_of(" \t");
  138         if (firstText == string::npos || line[firstText] != '#')
  139             indentLine(line, 1);
  140     }
  141 
  142     if (shouldUnindentComment && sw.unindentDepth > 0)
  143         unindentLine(line, sw.unindentDepth - 1);
  144     else if (shouldUnindentLine && sw.unindentDepth > 0)
  145         unindentLine(line, sw.unindentDepth);
  146 }
  147 
  148 /**
  149  * convert a force-tab indent to spaces
  150  *
  151  * @param line          a reference to the line that will be converted.
  152  */
  153 void ASEnhancer::convertForceTabIndentToSpaces(string& line) const
  154 {
  155     // replace tab indents with spaces
  156     for (size_t i = 0; i < line.length(); i++)
  157     {
  158         if (!isWhiteSpace(line[i]))
  159             break;
  160         if (line[i] == '\t')
  161         {
  162             line.erase(i, 1);
  163             line.insert(i, tabLength, ' ');
  164             i += tabLength - 1;
  165         }
  166     }
  167 }
  168 
  169 /**
  170  * convert a space indent to force-tab
  171  *
  172  * @param line          a reference to the line that will be converted.
  173  */
  174 void ASEnhancer::convertSpaceIndentToForceTab(string& line) const
  175 {
  176     assert(tabLength > 0);
  177 
  178     // replace leading spaces with tab indents
  179     size_t newSpaceIndentLength = line.find_first_not_of(" \t");
  180     size_t tabCount = newSpaceIndentLength / tabLength;     // truncate extra spaces
  181     line.replace(0U, tabCount * tabLength, tabCount, '\t');
  182 }
  183 
  184 /**
  185  * find the colon following a 'case' statement
  186  *
  187  * @param line          a reference to the line.
  188  * @param caseIndex     the line index of the case statement.
  189  * @return              the line index of the colon.
  190  */
  191 size_t ASEnhancer::findCaseColon(const string& line, size_t caseIndex) const
  192 {
  193     size_t i = caseIndex;
  194     bool isInQuote_ = false;
  195     char quoteChar_ = ' ';
  196     for (; i < line.length(); i++)
  197     {
  198         if (isInQuote_)
  199         {
  200             if (line[i] == '\\')
  201             {
  202                 i++;
  203                 continue;
  204             }
  205             else if (line[i] == quoteChar_)          // check ending quote
  206             {
  207                 isInQuote_ = false;
  208                 quoteChar_ = ' ';
  209                 continue;
  210             }
  211             else
  212             {
  213                 continue;                           // must close quote before continuing
  214             }
  215         }
  216         if (line[i] == '"'      // check opening quote
  217                 || (line[i] == '\'' && !isDigitSeparator(line, i)))
  218         {
  219             isInQuote_ = true;
  220             quoteChar_ = line[i];
  221             continue;
  222         }
  223         if (line[i] == ':')
  224         {
  225             if ((i + 1 < line.length()) && (line[i + 1] == ':'))
  226                 i++;                                // bypass scope resolution operator
  227             else
  228                 break;                              // found it
  229         }
  230     }
  231     return i;
  232 }
  233 
  234 /**
  235 * indent a line by a given number of tabsets
  236  *    by inserting leading whitespace to the line argument.
  237  *
  238  * @param line          a reference to the line to indent.
  239  * @param indent        the number of tabsets to insert.
  240  * @return              the number of characters inserted.
  241  */
  242 int ASEnhancer::indentLine(string& line, int indent) const
  243 {
  244     if (line.length() == 0
  245             && !emptyLineFill)
  246         return 0;
  247 
  248     size_t charsToInsert = 0;
  249 
  250     if (forceTab && indentLength != tabLength)
  251     {
  252         // replace tab indents with spaces
  253         convertForceTabIndentToSpaces(line);
  254         // insert the space indents
  255         charsToInsert = indent * indentLength;
  256         line.insert(line.begin(), charsToInsert, ' ');
  257         // replace leading spaces with tab indents
  258         convertSpaceIndentToForceTab(line);
  259     }
  260     else if (useTabs)
  261     {
  262         charsToInsert = indent;
  263         line.insert(line.begin(), charsToInsert, '\t');
  264     }
  265     else // spaces
  266     {
  267         charsToInsert = indent * indentLength;
  268         line.insert(line.begin(), charsToInsert, ' ');
  269     }
  270 
  271     return charsToInsert;
  272 }
  273 
  274 /**
  275  * check for SQL "BEGIN DECLARE SECTION".
  276  * must compare case insensitive and allow any spacing between words.
  277  *
  278  * @param line          a reference to the line to indent.
  279  * @param index         the current line index.
  280  * @return              true if a hit.
  281  */
  282 bool ASEnhancer::isBeginDeclareSectionSQL(const string& line, size_t index) const
  283 {
  284     string word;
  285     size_t hits = 0;
  286     size_t i;
  287     for (i = index; i < line.length(); i++)
  288     {
  289         i = line.find_first_not_of(" \t", i);
  290         if (i == string::npos)
  291             return false;
  292         if (line[i] == ';')
  293             break;
  294         if (!isCharPotentialHeader(line, i))
  295             continue;
  296         word = getCurrentWord(line, i);
  297         for (size_t j = 0; j < word.length(); j++)
  298             word[j] = (char) toupper(word[j]);
  299         if (word == "EXEC" || word == "SQL")
  300         {
  301             i += word.length() - 1;
  302             continue;
  303         }
  304         if (word == "DECLARE" || word == "SECTION")
  305         {
  306             hits++;
  307             i += word.length() - 1;
  308             continue;
  309         }
  310         if (word == "BEGIN")
  311         {
  312             hits++;
  313             i += word.length() - 1;
  314             continue;
  315         }
  316         return false;
  317     }
  318     if (hits == 3)
  319         return true;
  320     return false;
  321 }
  322 
  323 /**
  324  * check for SQL "END DECLARE SECTION".
  325  * must compare case insensitive and allow any spacing between words.
  326  *
  327  * @param line          a reference to the line to indent.
  328  * @param index         the current line index.
  329  * @return              true if a hit.
  330  */
  331 bool ASEnhancer::isEndDeclareSectionSQL(const string& line, size_t index) const
  332 {
  333     string word;
  334     size_t hits = 0;
  335     size_t i;
  336     for (i = index; i < line.length(); i++)
  337     {
  338         i = line.find_first_not_of(" \t", i);
  339         if (i == string::npos)
  340             return false;
  341         if (line[i] == ';')
  342             break;
  343         if (!isCharPotentialHeader(line, i))
  344             continue;
  345         word = getCurrentWord(line, i);
  346         for (size_t j = 0; j < word.length(); j++)
  347             word[j] = (char) toupper(word[j]);
  348         if (word == "EXEC" || word == "SQL")
  349         {
  350             i += word.length() - 1;
  351             continue;
  352         }
  353         if (word == "DECLARE" || word == "SECTION")
  354         {
  355             hits++;
  356             i += word.length() - 1;
  357             continue;
  358         }
  359         if (word == "END")
  360         {
  361             hits++;
  362             i += word.length() - 1;
  363             continue;
  364         }
  365         return false;
  366     }
  367     if (hits == 3)
  368         return true;
  369     return false;
  370 }
  371 
  372 /**
  373  * check if a one-line brace has been reached,
  374  * i.e. if the currently reached '{' character is closed
  375  * with a complimentary '}' elsewhere on the current line,
  376  *.
  377  * @return     false = one-line brace has not been reached.
  378  *             true  = one-line brace has been reached.
  379  */
  380 bool ASEnhancer::isOneLineBlockReached(const string& line, int startChar) const
  381 {
  382     assert(line[startChar] == '{');
  383 
  384     bool isInComment_ = false;
  385     bool isInQuote_ = false;
  386     int _braceCount = 1;
  387     int lineLength = line.length();
  388     char quoteChar_ = ' ';
  389     char ch = ' ';
  390 
  391     for (int i = startChar + 1; i < lineLength; ++i)
  392     {
  393         ch = line[i];
  394 
  395         if (isInComment_)
  396         {
  397             if (line.compare(i, 2, "*/") == 0)
  398             {
  399                 isInComment_ = false;
  400                 ++i;
  401             }
  402             continue;
  403         }
  404 
  405         if (ch == '\\')
  406         {
  407             ++i;
  408             continue;
  409         }
  410 
  411         if (isInQuote_)
  412         {
  413             if (ch == quoteChar_)
  414                 isInQuote_ = false;
  415             continue;
  416         }
  417 
  418         if (ch == '"'
  419                 || (ch == '\'' && !isDigitSeparator(line, i)))
  420         {
  421             isInQuote_ = true;
  422             quoteChar_ = ch;
  423             continue;
  424         }
  425 
  426         if (line.compare(i, 2, "//") == 0)
  427             break;
  428 
  429         if (line.compare(i, 2, "/*") == 0)
  430         {
  431             isInComment_ = true;
  432             ++i;
  433             continue;
  434         }
  435 
  436         if (ch == '{')
  437             ++_braceCount;
  438         else if (ch == '}')
  439             --_braceCount;
  440 
  441         if (_braceCount == 0)
  442             return true;
  443     }
  444 
  445     return false;
  446 }
  447 
  448 /**
  449  * parse characters in the current line to determine if an indent
  450  * or unindent is needed.
  451  */
  452 void ASEnhancer::parseCurrentLine(string& line, bool isInPreprocessor, bool isInSQL)
  453 {
  454     bool isSpecialChar = false;         // is a backslash escape character
  455 
  456     for (size_t i = 0; i < line.length(); i++)
  457     {
  458         char ch = line[i];
  459 
  460         // bypass whitespace
  461         if (isWhiteSpace(ch))
  462             continue;
  463 
  464         // handle special characters (i.e. backslash+character such as \n, \t, ...)
  465         if (isSpecialChar)
  466         {
  467             isSpecialChar = false;
  468             continue;
  469         }
  470         if (!(isInComment) && line.compare(i, 2, "\\\\") == 0)
  471         {
  472             i++;
  473             continue;
  474         }
  475         if (!(isInComment) && ch == '\\')
  476         {
  477             isSpecialChar = true;
  478             continue;
  479         }
  480 
  481         // handle quotes (such as 'x' and "Hello Dolly")
  482         if (!isInComment
  483                 && (ch == '"'
  484                     || (ch == '\'' && !isDigitSeparator(line, i))))
  485         {
  486             if (!isInQuote)
  487             {
  488                 quoteChar = ch;
  489                 isInQuote = true;
  490             }
  491             else if (quoteChar == ch)
  492             {
  493                 isInQuote = false;
  494                 continue;
  495             }
  496         }
  497 
  498         if (isInQuote)
  499             continue;
  500 
  501         // handle comments
  502 
  503         if (!(isInComment) && line.compare(i, 2, "//") == 0)
  504         {
  505             // check for windows line markers
  506             if (line.compare(i + 2, 1, "\xf0") > 0)
  507                 lineNumber--;
  508             // unindent if not in case braces
  509             if (line.find_first_not_of(" \t") == i
  510                     && sw.switchBraceCount == 1
  511                     && sw.unindentCase)
  512                 shouldUnindentComment = true;
  513             break;                 // finished with the line
  514         }
  515         else if (!(isInComment) && line.compare(i, 2, "/*") == 0)
  516         {
  517             // unindent if not in case braces
  518             if (sw.switchBraceCount == 1 && sw.unindentCase)
  519                 shouldUnindentComment = true;
  520             isInComment = true;
  521             size_t commentEnd = line.find("*/", i);
  522             if (commentEnd == string::npos)
  523                 i = line.length() - 1;
  524             else
  525                 i = commentEnd - 1;
  526             continue;
  527         }
  528         else if ((isInComment) && line.compare(i, 2, "*/") == 0)
  529         {
  530             // unindent if not in case braces
  531             if (sw.switchBraceCount == 1 && sw.unindentCase)
  532                 shouldUnindentComment = true;
  533             isInComment = false;
  534             i++;
  535             continue;
  536         }
  537 
  538         if (isInComment)
  539         {
  540             // unindent if not in case braces
  541             if (sw.switchBraceCount == 1 && sw.unindentCase)
  542                 shouldUnindentComment = true;
  543             size_t commentEnd = line.find("*/", i);
  544             if (commentEnd == string::npos)
  545                 i = line.length() - 1;
  546             else
  547                 i = commentEnd - 1;
  548             continue;
  549         }
  550 
  551         // if we have reached this far then we are NOT in a comment or string of special characters
  552 
  553         if (line[i] == '{')
  554             braceCount++;
  555 
  556         if (line[i] == '}')
  557             braceCount--;
  558 
  559         // check for preprocessor within an event table
  560         if (isInEventTable && line[i] == '#' && preprocBlockIndent)
  561         {
  562             string preproc;
  563             preproc = line.substr(i + 1);
  564             if (preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef)
  565                 eventPreprocDepth += 1;
  566             if (preproc.substr(0, 5) == "endif" && eventPreprocDepth > 0)
  567                 eventPreprocDepth -= 1;
  568         }
  569 
  570         bool isPotentialKeyword = isCharPotentialHeader(line, i);
  571 
  572         // ----------------  wxWidgets and MFC macros  ----------------------------------
  573 
  574         if (isPotentialKeyword)
  575         {
  576             for (size_t j = 0; j < indentableMacros->size(); j++)
  577             {
  578                 // 'first' is the beginning macro
  579                 if (findKeyword(line, i, indentableMacros->at(j)->first))
  580                 {
  581                     nextLineIsEventIndent = true;
  582                     break;
  583                 }
  584             }
  585             for (size_t j = 0; j < indentableMacros->size(); j++)
  586             {
  587                 // 'second' is the ending macro
  588                 if (findKeyword(line, i, indentableMacros->at(j)->second))
  589                 {
  590                     isInEventTable = false;
  591                     eventPreprocDepth = 0;
  592                     break;
  593                 }
  594             }
  595         }
  596 
  597         // ----------------  process SQL  -----------------------------------------------
  598 
  599         if (isInSQL)
  600         {
  601             if (isBeginDeclareSectionSQL(line, i))
  602                 nextLineIsDeclareIndent = true;
  603             if (isEndDeclareSectionSQL(line, i))
  604                 isInDeclareSection = false;
  605             break;
  606         }
  607 
  608         // ----------------  process switch statements  ---------------------------------
  609 
  610         if (isPotentialKeyword && findKeyword(line, i, ASResource::AS_SWITCH))
  611         {
  612             switchDepth++;
  613             switchStack.emplace_back(sw);                      // save current variables
  614             sw.switchBraceCount = 0;
  615             sw.unindentCase = false;                        // don't clear case until end of switch
  616             i += 5;                                         // bypass switch statement
  617             continue;
  618         }
  619 
  620         // just want unindented case statements from this point
  621 
  622         if (caseIndent
  623                 || switchDepth == 0
  624                 || (isInPreprocessor && !preprocDefineIndent))
  625         {
  626             // bypass the entire word
  627             if (isPotentialKeyword)
  628             {
  629                 string name = getCurrentWord(line, i);
  630                 i += name.length() - 1;
  631             }
  632             continue;
  633         }
  634 
  635         i = processSwitchBlock(line, i);
  636 
  637     }   // end of for loop * end of for loop * end of for loop * end of for loop
  638 }
  639 
  640 /**
  641  * process the character at the current index in a switch block.
  642  *
  643  * @param line          a reference to the line to indent.
  644  * @param index         the current line index.
  645  * @return              the new line index.
  646  */
  647 size_t ASEnhancer::processSwitchBlock(string& line, size_t index)
  648 {
  649     size_t i = index;
  650     bool isPotentialKeyword = isCharPotentialHeader(line, i);
  651 
  652     if (line[i] == '{')
  653     {
  654         sw.switchBraceCount++;
  655         if (lookingForCaseBrace)                      // if 1st after case statement
  656         {
  657             sw.unindentCase = true;                     // unindenting this case
  658             sw.unindentDepth++;
  659             lookingForCaseBrace = false;              // not looking now
  660         }
  661         return i;
  662     }
  663     lookingForCaseBrace = false;                      // no opening brace, don't indent
  664 
  665     if (line[i] == '}')
  666     {
  667         sw.switchBraceCount--;
  668         if (sw.switchBraceCount == 0)                 // if end of switch statement
  669         {
  670             int lineUnindent = sw.unindentDepth;
  671             if (line.find_first_not_of(" \t") == i
  672                     && !switchStack.empty())
  673                 lineUnindent = switchStack[switchStack.size() - 1].unindentDepth;
  674             if (shouldUnindentLine)
  675             {
  676                 if (lineUnindent > 0)
  677                     i -= unindentLine(line, lineUnindent);
  678                 shouldUnindentLine = false;
  679             }
  680             switchDepth--;
  681             sw = switchStack.back();
  682             switchStack.pop_back();
  683         }
  684         return i;
  685     }
  686 
  687     if (isPotentialKeyword
  688             && (findKeyword(line, i, ASResource::AS_CASE)
  689                 || findKeyword(line, i, ASResource::AS_DEFAULT)))
  690     {
  691         if (sw.unindentCase)                    // if unindented last case
  692         {
  693             sw.unindentCase = false;            // stop unindenting previous case
  694             sw.unindentDepth--;
  695         }
  696 
  697         i = findCaseColon(line, i);
  698 
  699         i++;
  700         for (; i < line.length(); i++)          // bypass whitespace
  701         {
  702             if (!isWhiteSpace(line[i]))
  703                 break;
  704         }
  705         if (i < line.length())
  706         {
  707             if (line[i] == '{')
  708             {
  709                 braceCount++;
  710                 sw.switchBraceCount++;
  711                 if (!isOneLineBlockReached(line, i))
  712                     unindentNextLine = true;
  713                 return i;
  714             }
  715         }
  716         lookingForCaseBrace = true;
  717         i--;                                    // need to process this char
  718         return i;
  719     }
  720     if (isPotentialKeyword)
  721     {
  722         string name = getCurrentWord(line, i);          // bypass the entire name
  723         i += name.length() - 1;
  724     }
  725     return i;
  726 }
  727 
  728 /**
  729  * unindent a line by a given number of tabsets
  730  *    by erasing the leading whitespace from the line argument.
  731  *
  732  * @param line          a reference to the line to unindent.
  733  * @param unindent      the number of tabsets to erase.
  734  * @return              the number of characters erased.
  735  */
  736 int ASEnhancer::unindentLine(string& line, int unindent) const
  737 {
  738     size_t whitespace = line.find_first_not_of(" \t");
  739 
  740     if (whitespace == string::npos)         // if line is blank
  741         whitespace = line.length();         // must remove padding, if any
  742 
  743     if (whitespace == 0)
  744         return 0;
  745 
  746     size_t charsToErase = 0;
  747 
  748     if (forceTab && indentLength != tabLength)
  749     {
  750         // replace tab indents with spaces
  751         convertForceTabIndentToSpaces(line);
  752         // remove the space indents
  753         size_t spaceIndentLength = line.find_first_not_of(" \t");
  754         charsToErase = unindent * indentLength;
  755         if (charsToErase <= spaceIndentLength)
  756             line.erase(0, charsToErase);
  757         else
  758             charsToErase = 0;
  759         // replace leading spaces with tab indents
  760         convertSpaceIndentToForceTab(line);
  761     }
  762     else if (useTabs)
  763     {
  764         charsToErase = unindent;
  765         if (charsToErase <= whitespace)
  766             line.erase(0, charsToErase);
  767         else
  768             charsToErase = 0;
  769     }
  770     else // spaces
  771     {
  772         charsToErase = unindent * indentLength;
  773         if (charsToErase <= whitespace)
  774             line.erase(0, charsToErase);
  775         else
  776             charsToErase = 0;
  777     }
  778 
  779     return charsToErase;
  780 }
  781 
  782 }   // end namespace astyle