File indexing completed on 2025-02-16 14:29:47
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 }