diff --git a/src/CommentParser.cpp b/src/CommentParser.cpp index 0eb43a8..abded22 100644 --- a/src/CommentParser.cpp +++ b/src/CommentParser.cpp @@ -1,126 +1,123 @@ /** * Copyright (C) 2019 Michael Reeves * * This file is part of KDiff3. * * KDiff3 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * KDiff3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KDiff3. If not, see . */ #include "CommentParser.h" #include #include #include #include void DefaultCommentParser::processChar(const QString &line, const QChar &inChar) { if(!bIsEscaped) { switch(inChar.unicode()) { case '\\': if(bInString) bIsEscaped = true; break; case '\'': case '"': if(!inComment()) { if(!bInString) { bInString = true; mStartChar = inChar; } else if(mStartChar == inChar) { bInString = false; } } break; case '/': if(bInString) break; if(!inComment() && mLastChar == '/') { mCommentType = singleLine; mIsPureComment = line.startsWith(QLatin1String("//")) ? yes : no; } else if(mLastChar == '*' && mCommentType == multiLine) { //ending multi-line comment mCommentType = none; if(!isFirstLine) mIsPureComment = line.endsWith(QLatin1String("*/")) ? yes : mIsPureComment; } break; case '*': if(bInString) break; if(mLastChar == '/' && !inComment()) { mCommentType = multiLine; mIsPureComment = line.startsWith(QLatin1String("/*")) ? yes : mIsPureComment; isFirstLine = true; } break; case '\n': if(mCommentType == singleLine) { mCommentType = none; } if(mCommentType == multiLine && !isFirstLine) { mIsPureComment = yes; } isFirstLine = false; break; default: if(inComment()) { break; } - if(inChar.isSpace() && isBlank()) - break; - - if(!inChar.isSpace()) - bIsBlank = false; - mIsPureComment = no; break; } mLastChar = inChar; } else { bIsEscaped = false; mLastChar = QChar(); } }; void DefaultCommentParser::processLine(const QString &line) { - for(const QChar &c : line) + //remove trailing and ending spaces. + const QString trimmedLine = line.trimmed(); + + for(const QChar &c : trimmedLine) { - processChar(line, c); + processChar(trimmedLine, c); } - processChar(line, '\n'); + processChar(trimmedLine, '\n'); } diff --git a/src/CommentParser.h b/src/CommentParser.h index 05d48f1..c93d483 100644 --- a/src/CommentParser.h +++ b/src/CommentParser.h @@ -1,67 +1,64 @@ /** * Copyright (C) 2019 Michael Reeves * * This file is part of KDiff3. * * KDiff3 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * KDiff3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KDiff3. If not, see . */ #ifndef COMMENTPARSER_H #define COMMENTPARSER_H #include #include class CommentParser { public: virtual void processChar(const QString &line, const QChar &inChar) = 0; virtual void processLine(const QString &line) = 0; virtual bool inComment() const = 0; virtual bool isPureComment() const = 0; virtual ~CommentParser(){}; }; class DefaultCommentParser : public CommentParser { private: typedef enum {none, singleLine, multiLine}CommentType; typedef enum {no, yes, unknown}TriState; public: void processLine(const QString &line) override; inline bool inComment() const override { return mCommentType != none; }; inline bool isPureComment() const override { return mIsPureComment == yes; }; - protected: friend class CommentParserTest; void processChar(const QString &line, const QChar &inChar) override; //For tests only. inline bool isEscaped(){ return bIsEscaped; } inline bool inString(){ return bInString; } - inline bool isBlank() const { return bIsBlank; }; private: QChar mLastChar, mStartChar; bool isFirstLine = false; TriState mIsPureComment = unknown; bool bInString = false; bool bIsEscaped = false; - bool bIsBlank = true; CommentType mCommentType = none; }; #endif // !COMMENTPASER_H diff --git a/src/autotests/commentparser.cpp b/src/autotests/commentparser.cpp index 5b9f1b3..692ba1d 100644 --- a/src/autotests/commentparser.cpp +++ b/src/autotests/commentparser.cpp @@ -1,256 +1,266 @@ /** * Copyright (C) 2019 Michael Reeves * * This file is part of KDiff3. * * KDiff3 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * KDiff3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KDiff3. If not, see . */ #include #include "../CommentParser.h" class CommentParserTest : public QObject { Q_OBJECT private slots: void init() { DefaultCommentParser parser; //Sanity check defaults. QVERIFY(!parser.isPureComment()); QVERIFY(!parser.isEscaped()); QVERIFY(!parser.inString()); QVERIFY(!parser.inComment()); - QVERIFY(parser.isBlank()); } - void singleLineComment1() + void singleLineComment() { - DefaultCommentParser test1; - - test1.processLine("//ddj ?*8"); - QVERIFY(!test1.inComment()); - QVERIFY(test1.isPureComment()); - } - - void singleLineComment2() - { - DefaultCommentParser test1; + DefaultCommentParser test; - test1.processLine("//comment with quotes embedded \"\""); - QVERIFY(!test1.inComment()); - QVERIFY(test1.isPureComment()); - } + test.processLine("//ddj ?*8"); + QVERIFY(!test.inComment()); + QVERIFY(test.isPureComment()); - void singleLineComment3() - { - DefaultCommentParser test1; + //ignore trailing+leading whitespace + test = DefaultCommentParser(); + test.processLine("\t \t // comment "); + QVERIFY(!test.inComment()); + QVERIFY(test.isPureComment()); - test1.processLine("anythis//endof line comment"); - QVERIFY(!test1.inComment()); - QVERIFY(!test1.isPureComment()); - } + test = DefaultCommentParser(); + test.processLine("//comment with quotes embedded \"\""); + QVERIFY(!test.inComment()); + QVERIFY(test.isPureComment()); - void singleLineComment4() - { - DefaultCommentParser test1; + test = DefaultCommentParser(); + test.processLine("anythis//endof line comment"); + QVERIFY(!test.inComment()); + QVERIFY(!test.isPureComment()); - test1.processLine("anythis//ignore embedded multiline sequence /*"); - QVERIFY(!test1.inComment()); - QVERIFY(!test1.isPureComment()); + test = DefaultCommentParser(); + test.processLine("anythis//ignore embedded multiline sequence /*"); + QVERIFY(!test.inComment()); + QVERIFY(!test.isPureComment()); } void inComment() { DefaultCommentParser test; test.mCommentType = DefaultCommentParser::multiLine; QVERIFY(test.inComment()); test.mCommentType = DefaultCommentParser::singleLine; QVERIFY(test.inComment()); test.mCommentType = DefaultCommentParser::none; QVERIFY(!test.inComment()); } void multiLineComment() { DefaultCommentParser test; //mutiline syntax on one line test.processLine("/* kjd*/"); QVERIFY(test.isPureComment()); QVERIFY(!test.inComment()); //mid line comment start. test = DefaultCommentParser(); test.processLine("fskk /* kjd */"); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); //mid line comment start. multiple lines test = DefaultCommentParser(); test.processLine("fskk /* kjd "); QVERIFY(test.inComment()); QVERIFY(!test.isPureComment()); test.processLine(" comment line "); QVERIFY(test.inComment()); QVERIFY(test.isPureComment()); test.processLine(" comment */ not comment "); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); //mid line comment start. multiple lines test = DefaultCommentParser(); test.processLine("fskk /* kjd "); QVERIFY(test.inComment()); QVERIFY(!test.isPureComment()); test.processLine(" comment line "); QVERIFY(test.inComment()); QVERIFY(test.isPureComment()); //embedded single line character sequence should not end comment test.processLine(" comment line //"); QVERIFY(test.inComment()); QVERIFY(test.isPureComment()); test.processLine(" comment */"); QVERIFY(!test.inComment()); QVERIFY(test.isPureComment()); //Escape squeances not relavate to comments test.processLine("/* comment \\*/"); QVERIFY(!test.inComment()); QVERIFY(test.isPureComment()); //invalid in C++ should not be flagged as pure comment test.processLine("/* comment */ */"); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); //leading whitespace test.processLine("\t \t /* comment */"); QVERIFY(!test.inComment()); QVERIFY(test.isPureComment()); + + //trailing whitespace + test.processLine("\t \t /* comment */ "); + QVERIFY(!test.inComment()); + QVERIFY(test.isPureComment()); } void stringTest() { DefaultCommentParser test; test.processLine("\"quoted string // \""); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test = DefaultCommentParser(); test.processLine("\"quoted string /* \""); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test = DefaultCommentParser(); test.processLine("\"\""); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test = DefaultCommentParser(); test.processChar("\"", '"'); QVERIFY(!test.isEscaped()); QVERIFY(test.inString()); test.processChar("\"'", '\''); QVERIFY(test.inString()); test.processChar("\"'\"", '"'); QVERIFY(!test.inString()); //test only escape sequence we care about test = DefaultCommentParser(); test.processChar("\"", '"'); test.processChar("\"", '\\'); QVERIFY(test.isEscaped()); QVERIFY(test.inString()); test.processChar("\"\\\"", '"'); QVERIFY(!test.isEscaped()); QVERIFY(test.inString()); test.processChar("\"\\\"\"", '"'); QVERIFY(!test.isEscaped()); QVERIFY(!test.inString()); } void quotedCharacter() { DefaultCommentParser test; test.processLine("'\"'"); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test.processLine("'a'"); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test.processLine("'\\\''"); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test.processLine("'*'"); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test.processLine("'/'"); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); } void nonComment() { DefaultCommentParser test; test.processLine(" int i = 8 / 8 * 3;"); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test = DefaultCommentParser(); test.processLine(" "); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); test = DefaultCommentParser(); test.processLine(""); QVERIFY(!test.inString()); QVERIFY(!test.inComment()); QVERIFY(!test.isPureComment()); + + test = DefaultCommentParser(); + test.processLine(" / "); + QVERIFY(!test.inString()); + QVERIFY(!test.inComment()); + QVERIFY(!test.isPureComment()); + + test = DefaultCommentParser(); + test.processLine(" * "); + QVERIFY(!test.inString()); + QVERIFY(!test.inComment()); + QVERIFY(!test.isPureComment()); } }; QTEST_MAIN(CommentParserTest); #include "commentparser.moc"