File indexing completed on 2024-05-12 09:56:56
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> 0003 SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "FileFilter.h" 0009 0010 #include <QDir> 0011 0012 #include "profile/Profile.h" 0013 #include "session/Session.h" 0014 #include "session/SessionManager.h" 0015 0016 #include "FileFilterHotspot.h" 0017 0018 using namespace Konsole; 0019 0020 // static 0021 QRegularExpression FileFilter::_regex; 0022 0023 FileFilter::FileFilter(Session *session, const QString &wordCharacters) 0024 : _session(session) 0025 , _dirPath(QString()) 0026 , _currentDirContents() 0027 { 0028 _regex = QRegularExpression(concatRegexPattern(wordCharacters), QRegularExpression::DontCaptureOption); 0029 setRegExp(_regex); 0030 } 0031 0032 QString FileFilter::concatRegexPattern(QString wordCharacters) const 0033 { 0034 /* The wordCharacters can be a potentially broken regexp, 0035 * so let's fix it manually if it has some troublesome characters. 0036 */ 0037 // Add a folder delimiter at the beginning. 0038 if (wordCharacters.contains(QLatin1Char('/'))) { 0039 wordCharacters.remove(QLatin1Char('/')); 0040 wordCharacters.prepend(QStringLiteral("\\/")); 0041 } 0042 0043 // Add minus at the end. 0044 if (wordCharacters.contains(QLatin1Char('-'))) { 0045 wordCharacters.remove(QLatin1Char('-')); 0046 wordCharacters.append(QLatin1Char('-')); 0047 } 0048 0049 const QString pattern = 0050 /* First part of the regexp means 'strings with spaces and starting with single quotes' 0051 * Second part means "Strings with double quotes" 0052 * Last part means "Everything else plus some special chars 0053 * This is much smaller, and faster, than the previous regexp 0054 * on the HotSpot creation we verify if this is indeed a file, so there's 0055 * no problem on testing on random words on the screen. 0056 */ 0057 QStringLiteral(R"RX('[^'\n]+')RX") // Matches everything between single quotes. 0058 + QStringLiteral(R"RX(|"[^\n"]+")RX") // Matches everything inside double quotes 0059 // Matches a contiguous line of alphanumeric characters plus some special ones 0060 // defined in the profile. With a special case for strings starting with '/' which 0061 // denotes a path on Linux. 0062 // Takes into account line numbers: 0063 // - grep output with line numbers: "/path/to/file:123" 0064 // - compiler error output: ":/path/to/file:123:123" 0065 // 0066 // ([^\n/\[]/) to not match "https://", and urls starting with "[" are matched by the 0067 // next | branch (ctest stuff) 0068 + QStringLiteral(R"RX(|([^\n\s/\[]/)?[\p{L}\w%1]+(:\d+)?(:\d+:)?)RX").arg(wordCharacters) 0069 // - ctest error output: "[/path/to/file(123)]" 0070 + QStringLiteral(R"RX(|\[[/\w%1]+\(\d+\)\])RX").arg(wordCharacters); 0071 0072 return pattern; 0073 } 0074 0075 /** 0076 * File Filter - Construct a filter that works on local file paths using the 0077 * posix portable filename character set combined with KDE's mimetype filename 0078 * extension blob patterns. 0079 * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 0080 */ 0081 0082 QSharedPointer<HotSpot> FileFilter::newHotSpot(int startLine, int startColumn, int endLine, int endColumn, const QStringList &capturedTexts) 0083 { 0084 if (_session.isNull()) { 0085 return nullptr; 0086 } 0087 0088 const QString &filenameRef = capturedTexts.first(); 0089 QStringView filename = filenameRef; 0090 if (filename.startsWith(QLatin1Char('\'')) && filename.endsWith(QLatin1Char('\''))) { 0091 filename = filename.mid(1, filename.size() - 2); 0092 } 0093 0094 // '.' and '..' could be valid hotspots, but '..................' most likely isn't 0095 static const QRegularExpression allDotRe{QRegularExpression::anchoredPattern(QStringLiteral("\\.{3}"))}; 0096 if (allDotRe.match(filename).hasMatch()) { 0097 return nullptr; 0098 } 0099 0100 if (filename.startsWith(QLatin1String("[/"))) { // ctest error output 0101 filename = filename.mid(1); 0102 } 0103 0104 const bool absolute = filename.startsWith(QLatin1Char('/')); 0105 if (!absolute) { 0106 auto match = std::find_if(_currentDirContents.cbegin(), _currentDirContents.cend(), [filename](const QString &s) { 0107 // early out if first char doesn't match 0108 if (!s.isEmpty() && filename.at(0) != s.at(0)) { 0109 return false; 0110 } 0111 0112 const bool startsWith = filename.startsWith(s); 0113 if (startsWith) { 0114 // are we equal ? 0115 if (filename.size() == s.size()) { 0116 return true; 0117 } 0118 int onePast = s.size(); 0119 if (onePast < filename.size()) { 0120 if (filename.at(onePast) == QLatin1Char(':') || filename.at(onePast) == QLatin1Char('/')) { 0121 return true; 0122 } 0123 } 0124 } 0125 return false; 0126 }); 0127 0128 if (match == _currentDirContents.cend()) { 0129 return nullptr; 0130 } 0131 } 0132 0133 return QSharedPointer<HotSpot>(new FileFilterHotSpot(startLine, 0134 startColumn, 0135 endLine, 0136 endColumn, 0137 capturedTexts, 0138 !absolute ? _dirPath + filename.toString() : filename.toString(), 0139 _session)); 0140 } 0141 0142 void FileFilter::process() 0143 { 0144 const QDir dir(_session->currentWorkingDirectory()); 0145 // Do not re-process. 0146 if (_dirPath != dir.canonicalPath() + QLatin1Char('/')) { 0147 _dirPath = dir.canonicalPath() + QLatin1Char('/'); 0148 0149 _currentDirContents = dir.entryList(QDir::Dirs | QDir::Files); 0150 } 0151 0152 RegExpFilter::process(); 0153 } 0154 0155 void FileFilter::updateRegex(const QString &wordCharacters) 0156 { 0157 _regex.setPattern(concatRegexPattern(wordCharacters)); 0158 setRegExp(_regex); 0159 }