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.
159 lines
5.8 KiB
159 lines
5.8 KiB
/* |
|
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> |
|
SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@gmail.com> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "FileFilter.h" |
|
|
|
#include <QDir> |
|
|
|
#include "profile/Profile.h" |
|
#include "session/Session.h" |
|
#include "session/SessionManager.h" |
|
|
|
#include "FileFilterHotspot.h" |
|
|
|
using namespace Konsole; |
|
|
|
// static |
|
QRegularExpression FileFilter::_regex; |
|
|
|
FileFilter::FileFilter(Session *session, const QString &wordCharacters) |
|
: _session(session) |
|
, _dirPath(QString()) |
|
, _currentDirContents() |
|
{ |
|
_regex = QRegularExpression(concatRegexPattern(wordCharacters), QRegularExpression::DontCaptureOption); |
|
setRegExp(_regex); |
|
} |
|
|
|
QString FileFilter::concatRegexPattern(QString wordCharacters) const |
|
{ |
|
/* The wordCharacters can be a potentially broken regexp, |
|
* so let's fix it manually if it has some troublesome characters. |
|
*/ |
|
// Add a folder delimiter at the beginning. |
|
if (wordCharacters.contains(QLatin1Char('/'))) { |
|
wordCharacters.remove(QLatin1Char('/')); |
|
wordCharacters.prepend(QStringLiteral("\\/")); |
|
} |
|
|
|
// Add minus at the end. |
|
if (wordCharacters.contains(QLatin1Char('-'))) { |
|
wordCharacters.remove(QLatin1Char('-')); |
|
wordCharacters.append(QLatin1Char('-')); |
|
} |
|
|
|
const QString pattern = |
|
/* First part of the regexp means 'strings with spaces and starting with single quotes' |
|
* Second part means "Strings with double quotes" |
|
* Last part means "Everything else plus some special chars |
|
* This is much smaller, and faster, than the previous regexp |
|
* on the HotSpot creation we verify if this is indeed a file, so there's |
|
* no problem on testing on random words on the screen. |
|
*/ |
|
QStringLiteral(R"RX('[^'\n]+')RX") // Matches everything between single quotes. |
|
+ QStringLiteral(R"RX(|"[^\n"]+")RX") // Matches everything inside double quotes |
|
// Matches a contiguous line of alphanumeric characters plus some special ones |
|
// defined in the profile. With a special case for strings starting with '/' which |
|
// denotes a path on Linux. |
|
// Takes into account line numbers: |
|
// - grep output with line numbers: "/path/to/file:123" |
|
// - compiler error output: ":/path/to/file:123:123" |
|
// |
|
// ([^\n/\[]/) to not match "https://", and urls starting with "[" are matched by the |
|
// next | branch (ctest stuff) |
|
+ QStringLiteral(R"RX(|([^\n\s/\[]/)?[\p{L}\w%1]+(:\d+)?(:\d+:)?)RX").arg(wordCharacters) |
|
// - ctest error output: "[/path/to/file(123)]" |
|
+ QStringLiteral(R"RX(|\[[/\w%1]+\(\d+\)\])RX").arg(wordCharacters); |
|
|
|
return pattern; |
|
} |
|
|
|
/** |
|
* File Filter - Construct a filter that works on local file paths using the |
|
* posix portable filename character set combined with KDE's mimetype filename |
|
* extension blob patterns. |
|
* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 |
|
*/ |
|
|
|
QSharedPointer<HotSpot> FileFilter::newHotSpot(int startLine, int startColumn, int endLine, int endColumn, const QStringList &capturedTexts) |
|
{ |
|
if (_session.isNull()) { |
|
return nullptr; |
|
} |
|
|
|
const QString &filenameRef = capturedTexts.first(); |
|
QStringView filename = filenameRef; |
|
if (filename.startsWith(QLatin1Char('\'')) && filename.endsWith(QLatin1Char('\''))) { |
|
filename = filename.mid(1, filename.size() - 2); |
|
} |
|
|
|
// '.' and '..' could be valid hotspots, but '..................' most likely isn't |
|
static const QRegularExpression allDotRe{QRegularExpression::anchoredPattern(QStringLiteral("\\.{3}"))}; |
|
if (allDotRe.match(filename).hasMatch()) { |
|
return nullptr; |
|
} |
|
|
|
if (filename.startsWith(QLatin1String("[/"))) { // ctest error output |
|
filename = filename.mid(1); |
|
} |
|
|
|
const bool absolute = filename.startsWith(QLatin1Char('/')); |
|
if (!absolute) { |
|
auto match = std::find_if(_currentDirContents.cbegin(), _currentDirContents.cend(), [filename](const QString &s) { |
|
// early out if first char doesn't match |
|
if (!s.isEmpty() && filename.at(0) != s.at(0)) { |
|
return false; |
|
} |
|
|
|
const bool startsWith = filename.startsWith(s); |
|
if (startsWith) { |
|
// are we equal ? |
|
if (filename.size() == s.size()) { |
|
return true; |
|
} |
|
int onePast = s.size(); |
|
if (onePast < filename.size()) { |
|
if (filename.at(onePast) == QLatin1Char(':') || filename.at(onePast) == QLatin1Char('/')) { |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
}); |
|
|
|
if (match == _currentDirContents.cend()) { |
|
return nullptr; |
|
} |
|
} |
|
|
|
return QSharedPointer<HotSpot>(new FileFilterHotSpot(startLine, |
|
startColumn, |
|
endLine, |
|
endColumn, |
|
capturedTexts, |
|
!absolute ? _dirPath + filename.toString() : filename.toString(), |
|
_session)); |
|
} |
|
|
|
void FileFilter::process() |
|
{ |
|
const QDir dir(_session->currentWorkingDirectory()); |
|
// Do not re-process. |
|
if (_dirPath != dir.canonicalPath() + QLatin1Char('/')) { |
|
_dirPath = dir.canonicalPath() + QLatin1Char('/'); |
|
|
|
_currentDirContents = dir.entryList(QDir::Dirs | QDir::Files); |
|
} |
|
|
|
RegExpFilter::process(); |
|
} |
|
|
|
void FileFilter::updateRegex(const QString &wordCharacters) |
|
{ |
|
_regex.setPattern(concatRegexPattern(wordCharacters)); |
|
setRegExp(_regex); |
|
}
|
|
|