File indexing completed on 2024-05-19 16:47:16

0001 // clang-format off
0002 /*
0003  * KDiff3 - Text Diff And Merge Tool
0004  *
0005  * SPDX-FileCopyrightText: 2019-2020 Michael Reeves reeves.87@gmail.com
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 // clang-format on
0009 
0010 #include "CommentParser.h"
0011 #include "TypeUtils.h"
0012 
0013 #include <algorithm>
0014 
0015 #include <QChar>
0016 #include <QRegularExpression>
0017 #include <QString>
0018 
0019 void DefaultCommentParser::processChar(const QString &line, const QChar &inChar)
0020 {
0021     if(!bIsEscaped)
0022     {
0023         switch(inChar.unicode())
0024         {
0025             case '\\':
0026                 if(bInString)
0027                     bIsEscaped = true;
0028                 break;
0029             case '\'':
0030             case '"':
0031                 if(!inComment())
0032                 {
0033                     if(!bInString)
0034                     {
0035                         bInString = true;
0036                         mStartChar = inChar;
0037                     }
0038                     else if(mStartChar == inChar)
0039                     {
0040                         bInString = false;
0041                     }
0042                 }
0043                 break;
0044             case '/':
0045                 if(bInString)
0046                     break;
0047 
0048                 if(!inComment() && mLastChar == '/')
0049                 {
0050                     mCommentType = singleLine;
0051                     mIsPureComment = mIsCommentOrWhite = line.startsWith(u8"//");
0052                     lastComment.startOffset = offset - 1;
0053                     if(lastComment.startOffset != 0) //whitespace at begining
0054                     {
0055                         mIsPureComment = false;
0056                     }
0057                 }
0058                 else if(mLastChar == '*' && mCommentType == multiLine)
0059                 {
0060                     //ending multi-line comment
0061                     mCommentType = none;
0062                     lastComment.endOffset = offset + 1; //include last char in offset
0063                     comments.push_back(lastComment);
0064                     if(!isFirstLine)
0065                         mIsPureComment = mIsCommentOrWhite = line.endsWith(u8"*/") ? true : mIsCommentOrWhite;
0066                 }
0067                 break;
0068             case '*':
0069                 if(bInString)
0070                     break;
0071 
0072                 if(mLastChar == '/' && !inComment())
0073                 {
0074                     mCommentType = multiLine;
0075                     mIsPureComment = mIsCommentOrWhite = line.startsWith(u8"/*") ? true : mIsCommentOrWhite;
0076                     isFirstLine = true;
0077                     lastComment.startOffset = offset - 1;
0078                     if(lastComment.startOffset != 0) //whitespace at begining
0079                     {
0080                         mIsPureComment = false;
0081                     }
0082                 }
0083                 break;
0084             case '\n':
0085                 if(mCommentType == singleLine)
0086                 {
0087                     mCommentType = none;
0088                     lastComment.endOffset = offset;
0089                     comments.push_back(lastComment);
0090                 }
0091 
0092                 if(mCommentType == multiLine && !isFirstLine)
0093                 {
0094                     mIsPureComment = mIsCommentOrWhite = true;
0095                 }
0096 
0097                 if(lastComment.startOffset > 0 && lastComment.endOffset == 0)
0098                 {
0099                     lastComment.endOffset = offset;
0100                     comments.push_back(lastComment);
0101                 }
0102 
0103                 isFirstLine = false;
0104 
0105                 break;
0106             default:
0107                 if(inComment())
0108                 {
0109                     break;
0110                 }
0111 
0112                 mIsPureComment = mIsCommentOrWhite = false;
0113                 break;
0114         }
0115 
0116         mLastChar = inChar;
0117     }
0118     else
0119     {
0120         bIsEscaped = false;
0121         mLastChar = QChar();
0122     }
0123 
0124     ++offset;
0125 };
0126 
0127 /*
0128     Find comments if any and set its pure comment flag if it has nothing but whitespace and comments.
0129 */
0130 void DefaultCommentParser::processLine(const QString &line)
0131 {
0132     static const QRegularExpression nonWhiteRegexp = QRegularExpression("[\\S]", QRegularExpression::UseUnicodePropertiesOption);
0133     static const QRegularExpression tailRegexp = QRegularExpression("\\s+$", QRegularExpression::UseUnicodePropertiesOption);
0134     offset = line.indexOf(nonWhiteRegexp);
0135     const QtSizeType trailIndex = line.lastIndexOf(tailRegexp);
0136 
0137     lastComment.startOffset = lastComment.endOffset = 0; //reset these for each line
0138     comments.clear();
0139 
0140     //remove trailing and ending spaces.
0141     const QString trimmedLine = line.trimmed();
0142 
0143     for(const QChar &c : trimmedLine)
0144     {
0145         processChar(trimmedLine, c);
0146     }
0147     /*
0148         Line has trailing space after multi-line comment ended.
0149     */
0150     if(trailIndex != -1 && !inComment())
0151     {
0152         mIsPureComment = false;
0153     }
0154 
0155     //mIsPureComment = mIsPureComment && offset == 0;
0156     processChar(trimmedLine, '\n');
0157 }
0158 
0159 /*
0160  Modifies the input data, and replaces comments with whitespace when the line contains other data too.
0161 */
0162 void DefaultCommentParser::removeComment(QString &line)
0163 {
0164     if(isSkipable() || lastComment.startOffset == lastComment.endOffset) return;
0165 
0166     for(const CommentRange &range : comments)
0167     {
0168         /*
0169             assert isn't useful during auto testing as it causes the QTest not to show the actual line that
0170             the test failed on.
0171         */
0172 #ifndef AUTOTEST
0173         assert(range.endOffset <= line.length() && range.startOffset <= line.length());
0174         assert(range.endOffset >= range.startOffset);
0175 #else
0176         if(range.endOffset > line.length() && range.startOffset > line.length()) return;
0177         if(range.endOffset < range.startOffset) return;
0178 #endif
0179         QtSizeType size = range.endOffset - range.startOffset;
0180         line.replace(range.startOffset, size, QString(" ").repeated(size));
0181     }
0182 }