You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
13 KiB
484 lines
13 KiB
15 years ago
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||
|
*
|
||
|
* ASEnhancer.cpp
|
||
|
*
|
||
|
* This file is a part of "Artistic Style" - an indentation and
|
||
|
* reformatting tool for C, C++, C# and Java source files.
|
||
|
* http://astyle.sourceforge.net
|
||
|
*
|
||
|
* The "Artistic Style" project, including all files needed to
|
||
|
* compile it, is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2.1 of the License, or (at your option) any later
|
||
|
* version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this project; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||
|
*/
|
||
|
|
||
|
// can trace only if NDEBUG is not defined
|
||
|
#ifndef NDEBUG
|
||
|
// #define TRACEswitch
|
||
|
// #define TRACEcase
|
||
|
// #define TRACEmisc
|
||
|
#endif
|
||
|
|
||
|
#include "astyle.h"
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <fstream>
|
||
|
#include <sstream>
|
||
|
|
||
|
#ifdef TRACEswitch
|
||
|
#define TRswitch(a,b) *traceOut << lineNumber << a << b << endl;
|
||
|
#else
|
||
|
#define TRswitch(a,b) ((void)0)
|
||
|
#endif // TRACEswitch
|
||
|
#ifdef TRACEcase
|
||
|
#define TRcase(a,b) *traceOut << lineNumber << a << b << endl;
|
||
|
#else
|
||
|
#define TRcase(a,b) ((void)0)
|
||
|
#endif // TRACEcase
|
||
|
#ifdef TRACEmisc
|
||
|
#define TRmisc(a) *traceOut << lineNumber << a << endl;
|
||
|
#else
|
||
|
#define TRmisc(a) ((void)0)
|
||
|
#endif // TRACEmisc
|
||
|
|
||
|
|
||
|
namespace astyle
|
||
|
{
|
||
|
|
||
|
// ---------------------------- functions for ASEnhancer Class -------------------------------------
|
||
|
|
||
|
/**
|
||
|
* ASEnhancer constructor
|
||
|
*/
|
||
|
ASEnhancer::ASEnhancer()
|
||
|
{
|
||
|
// variables are initialized by init()
|
||
|
traceOut = new stringstream;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Destructor of ASEnhancer
|
||
|
* Display the TRACE entries.
|
||
|
*/
|
||
|
ASEnhancer::~ASEnhancer()
|
||
|
{
|
||
|
#if defined(TRACEswitch) || defined(TRACEcase) || defined(TRACEmisc)
|
||
|
string line;
|
||
|
string msg = "TRACE Entries\n\n";
|
||
|
char countLine[50];
|
||
|
int count = 0;
|
||
|
|
||
|
while (getline(*traceOut, line))
|
||
|
{
|
||
|
msg += line + '\n';
|
||
|
count++;
|
||
|
}
|
||
|
sprintf(countLine, "\n%d Entries", count);
|
||
|
msg += countLine;
|
||
|
// write a text file to "My Documents" (Windows)
|
||
|
char filename [_MAX_PATH + _MAX_FNAME + _MAX_EXT + 1]; // full path and filename
|
||
|
strcpy(filename, getenv("USERPROFILE"));
|
||
|
strcat(filename, "\\My Documents\\tracee.txt");
|
||
|
ofstream outfile(filename);
|
||
|
outfile << msg;
|
||
|
outfile.close();
|
||
|
#endif
|
||
|
delete traceOut;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* initialize the ASEnhancer.
|
||
|
*
|
||
|
* init() is called each time an ASFormatter object is initialized.
|
||
|
*/
|
||
|
void ASEnhancer::init(int _indentLength,
|
||
|
string _indentString,
|
||
|
bool _isCStyle,
|
||
|
bool _isJavaStyle,
|
||
|
bool _isSharpStyle,
|
||
|
bool _caseIndent,
|
||
|
bool _emptyLineFill)
|
||
|
{
|
||
|
// formatting variables from ASFormatter and ASBeautifier
|
||
|
indentLength = _indentLength;
|
||
|
if (_indentString.compare(0, 1, "\t") == 0)
|
||
|
useTabs = true;
|
||
|
else
|
||
|
useTabs = false;
|
||
|
isCStyle = _isCStyle;
|
||
|
isJavaStyle = _isJavaStyle;
|
||
|
isSharpStyle = _isSharpStyle;
|
||
|
caseIndent = _caseIndent;
|
||
|
emptyLineFill = _emptyLineFill;
|
||
|
|
||
|
// unindent variables
|
||
|
lineNumber = 0;
|
||
|
bracketCount = 0;
|
||
|
isInComment = false;
|
||
|
isInQuote = false;
|
||
|
switchDepth = 0;
|
||
|
lookingForCaseBracket = false;
|
||
|
unindentNextLine = false;
|
||
|
|
||
|
#if defined(TRACEswitch) || defined(TRACEcase) || defined(TRACEmisc)
|
||
|
*traceOut << "New file -------------" << endl;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* additional formatting for line of source code.
|
||
|
* every line of source code in a source code file should be sent
|
||
|
* one after the other to this function.
|
||
|
* indents event tables
|
||
|
* unindents the case blocks
|
||
|
*
|
||
|
* @param line the original formatted line will be updated if necessary.
|
||
|
*/
|
||
|
void ASEnhancer::enhance(string &line)
|
||
|
{
|
||
|
static vector<switchVariables> swVector; // stack vector of switch variables
|
||
|
static switchVariables sw; // switch variables struct
|
||
|
|
||
|
static bool nextLineIsEventTable; // begin event table is reached
|
||
|
static bool isInEventTable; // need to indent an event table
|
||
|
|
||
|
bool isSpecialChar = false;
|
||
|
size_t lineLength; // length of the line being parsed
|
||
|
|
||
|
lineNumber++;
|
||
|
lineLength = line.length();
|
||
|
|
||
|
// check for beginning of event table
|
||
|
if (nextLineIsEventTable)
|
||
|
{
|
||
|
isInEventTable = true;
|
||
|
nextLineIsEventTable = false;
|
||
|
}
|
||
|
|
||
|
if (lineLength == 0
|
||
|
&& ! isInEventTable
|
||
|
&& ! emptyLineFill)
|
||
|
return;
|
||
|
|
||
|
// test for unindent on attached brackets
|
||
|
if (unindentNextLine)
|
||
|
{
|
||
|
sw.unindentDepth++;
|
||
|
sw.unindentCase = true;
|
||
|
unindentNextLine = false;
|
||
|
TRcase(" unindent case ", sw.unindentDepth);
|
||
|
}
|
||
|
|
||
|
// parse characters in the current line.
|
||
|
|
||
|
for (size_t i = 0; i < lineLength; i++)
|
||
|
{
|
||
|
char ch = line[i];
|
||
|
|
||
|
// bypass whitespace
|
||
|
if (isWhiteSpaceX(ch))
|
||
|
continue;
|
||
|
|
||
|
// handle special characters (i.e. backslash+character such as \n, \t, ...)
|
||
|
if (isSpecialChar)
|
||
|
{
|
||
|
isSpecialChar = false;
|
||
|
continue;
|
||
|
}
|
||
|
if (!(isInComment) && line.compare(i, 2, "\\\\") == 0)
|
||
|
{
|
||
|
i++;
|
||
|
continue;
|
||
|
}
|
||
|
if (!(isInComment) && ch == '\\')
|
||
|
{
|
||
|
isSpecialChar = true;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// handle quotes (such as 'x' and "Hello Dolly")
|
||
|
if (!(isInComment) && (ch == '"' || ch == '\''))
|
||
|
if (!isInQuote)
|
||
|
{
|
||
|
quoteChar = ch;
|
||
|
isInQuote = true;
|
||
|
}
|
||
|
else if (quoteChar == ch)
|
||
|
{
|
||
|
isInQuote = false;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (isInQuote)
|
||
|
continue;
|
||
|
|
||
|
// handle comments
|
||
|
|
||
|
if (!(isInComment) && line.compare(i, 2, "//") == 0)
|
||
|
{
|
||
|
// check for windows line markers
|
||
|
if (line.compare(i + 2, 1, "\xf0") > 0)
|
||
|
lineNumber--;
|
||
|
break; // finished with the line
|
||
|
}
|
||
|
else if (!(isInComment) && line.compare(i, 2, "/*") == 0)
|
||
|
{
|
||
|
isInComment = true;
|
||
|
i++;
|
||
|
continue;
|
||
|
}
|
||
|
else if ((isInComment) && line.compare(i, 2, "*/") == 0)
|
||
|
{
|
||
|
isInComment = false;
|
||
|
i++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (isInComment)
|
||
|
continue;
|
||
|
|
||
|
// if we have reached this far then we are NOT in a comment or string of special characters
|
||
|
|
||
|
if (line[i] == '{') // if open bracket
|
||
|
bracketCount++;
|
||
|
|
||
|
if (line[i] == '}') // if close bracket
|
||
|
bracketCount--;
|
||
|
|
||
|
// ---------------- process event tables --------------------------------------
|
||
|
|
||
|
// check for event table begin
|
||
|
if (findKeyword(line, i, "BEGIN_EVENT_TABLE")
|
||
|
|| findKeyword(line, i, "BEGIN_MESSAGE_MAP"))
|
||
|
nextLineIsEventTable = true;
|
||
|
|
||
|
// check for event table end
|
||
|
if (findKeyword(line, i, "END_EVENT_TABLE")
|
||
|
|| findKeyword(line, i, "END_MESSAGE_MAP"))
|
||
|
isInEventTable = false;
|
||
|
|
||
|
// ---------------- process switch statements ---------------------------------
|
||
|
|
||
|
if (findKeyword(line, i, "switch")) // if switch statement
|
||
|
{
|
||
|
switchDepth++; // bump switch depth
|
||
|
TRswitch(" switch ", switchDepth);
|
||
|
swVector.push_back(sw); // save current variables
|
||
|
sw.switchBracketCount = 0;
|
||
|
sw.unindentCase = false; // don't clear case until end of switch
|
||
|
i += 5; // bypass switch statement
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// just want switch statements from this point
|
||
|
|
||
|
if (caseIndent || switchDepth == 0) // from here just want switch statements
|
||
|
continue; // get next char
|
||
|
|
||
|
if (line[i] == '{') // if open bracket
|
||
|
{
|
||
|
sw.switchBracketCount++;
|
||
|
if (lookingForCaseBracket) // if 1st after case statement
|
||
|
{
|
||
|
sw.unindentCase = true; // unindenting this case
|
||
|
sw.unindentDepth++; // bump depth
|
||
|
lookingForCaseBracket = false; // not looking now
|
||
|
TRcase(" unindent case ", sw.unindentDepth);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
lookingForCaseBracket = false; // no opening bracket, don't indent
|
||
|
|
||
|
if (line[i] == '}') // if close bracket
|
||
|
{
|
||
|
sw.switchBracketCount--;
|
||
|
if (sw.switchBracketCount == 0) // if end of switch statement
|
||
|
{
|
||
|
TRswitch(" endsw ", switchDepth);
|
||
|
switchDepth--; // one less switch
|
||
|
sw = swVector.back(); // restore sw struct
|
||
|
swVector.pop_back(); // remove last entry from stack
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// look for case or default header
|
||
|
|
||
|
if (findKeyword(line, i, "case") || findKeyword(line, i, "default"))
|
||
|
{
|
||
|
if (sw.unindentCase) // if unindented last case
|
||
|
{
|
||
|
sw.unindentCase = false; // stop unindenting previous case
|
||
|
sw.unindentDepth--; // reduce depth
|
||
|
}
|
||
|
for (; i < lineLength; i++) // bypass colon
|
||
|
{
|
||
|
if (line[i] == ':')
|
||
|
if ((i + 1 < lineLength) && (line[i + 1] == ':'))
|
||
|
i++; // bypass scope resolution operator
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
i++;
|
||
|
for (; i < lineLength; i++) // bypass whitespace
|
||
|
{
|
||
|
if (!(isWhiteSpaceX(line[i])))
|
||
|
break;
|
||
|
}
|
||
|
if (i < lineLength) // check for bracket
|
||
|
{
|
||
|
if (line[i] == '{') // if bracket found
|
||
|
{
|
||
|
sw.switchBracketCount++;
|
||
|
unindentNextLine = true; // start unindenting on next line
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
lookingForCaseBracket = true; // bracket must be on next line
|
||
|
i--; // need to check for comments
|
||
|
continue;
|
||
|
}
|
||
|
} // end of for loop
|
||
|
|
||
|
if (isInEventTable) // if need to indent
|
||
|
indentLine(line, 1); // do it
|
||
|
|
||
|
if (sw.unindentDepth > 0) // if need to unindent
|
||
|
unindentLine(line, sw.unindentDepth); // do it
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* indent a line by a given number of tabsets
|
||
|
* by inserting leading whitespace to the line argument.
|
||
|
*
|
||
|
* @param line a pointer to the line to indent.
|
||
|
* @param unindent the number of tabsets to insert.
|
||
|
* @return the number of characters inserted.
|
||
|
*/
|
||
|
int ASEnhancer::indentLine(string &line, const int indent) const
|
||
|
{
|
||
|
if (line.length() == 0
|
||
|
&& ! emptyLineFill)
|
||
|
return 0;
|
||
|
|
||
|
size_t charsToInsert; // number of chars to insert
|
||
|
|
||
|
if (useTabs) // if formatted with tabs
|
||
|
{
|
||
|
charsToInsert = indent; // tabs to insert
|
||
|
line.insert((size_t) 0, charsToInsert, '\t'); // insert the tabs
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
charsToInsert = indent * indentLength; // compute chars to insert
|
||
|
line.insert((size_t)0, charsToInsert, ' '); // insert the spaces
|
||
|
}
|
||
|
|
||
|
return charsToInsert;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* unindent a line by a given number of tabsets
|
||
|
* by erasing the leading whitespace from the line argument.
|
||
|
*
|
||
|
* @param line a pointer to the line to unindent.
|
||
|
* @param unindent the number of tabsets to erase.
|
||
|
* @return the number of characters erased.
|
||
|
*/
|
||
|
int ASEnhancer::unindentLine(string &line, const int unindent) const
|
||
|
{
|
||
|
size_t whitespace = line.find_first_not_of(" \t");
|
||
|
|
||
|
if (whitespace == string::npos) // if line is blank
|
||
|
whitespace = line.length(); // must remove padding, if any
|
||
|
|
||
|
if (whitespace == 0)
|
||
|
return 0;
|
||
|
|
||
|
size_t charsToErase; // number of chars to erase
|
||
|
|
||
|
if (useTabs) // if formatted with tabs
|
||
|
{
|
||
|
charsToErase = unindent; // tabs to erase
|
||
|
if (charsToErase <= whitespace) // if there is enough whitespace
|
||
|
line.erase(0, charsToErase); // erase the tabs
|
||
|
else
|
||
|
charsToErase = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
charsToErase = unindent * indentLength; // compute chars to erase
|
||
|
if (charsToErase <= whitespace) // if there is enough whitespace
|
||
|
line.erase(0, charsToErase); // erase the spaces
|
||
|
else
|
||
|
charsToErase = 0;
|
||
|
}
|
||
|
|
||
|
return charsToErase;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* check if a specific line position contains a keyword.
|
||
|
*
|
||
|
* @return true if the word was found. false if the word was not found.
|
||
|
*/
|
||
|
bool ASEnhancer::findKeyword(const string &line, int i, const char *keyword) const
|
||
|
{
|
||
|
if (line.compare(i, strlen(keyword), keyword) == 0)
|
||
|
{
|
||
|
// check that this is a header and not a part of a longer word
|
||
|
// (e.g. not at its begining, not at its middle...)
|
||
|
|
||
|
int lineLength = line.length();
|
||
|
int wordEnd = i + strlen(keyword);
|
||
|
char startCh = keyword[0]; // first char of header
|
||
|
char endCh = 0; // char just after header
|
||
|
char prevCh = 0; // char just before header
|
||
|
|
||
|
if (wordEnd < lineLength)
|
||
|
{
|
||
|
endCh = line[wordEnd];
|
||
|
}
|
||
|
if (i > 0)
|
||
|
{
|
||
|
prevCh = line[i-1];
|
||
|
}
|
||
|
|
||
|
if (prevCh != 0
|
||
|
&& isLegalNameCharX(startCh)
|
||
|
&& isLegalNameCharX(prevCh))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
else if (wordEnd >= lineLength
|
||
|
|| !isLegalNameCharX(startCh)
|
||
|
|| !isLegalNameCharX(endCh))
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
} // end namespace astyle
|