mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
search: support && and || logical operator
- Space-separated keywords are combined as AND; - && and || for AND and OR; - Do not support using both && and ||; - When multiple keywords are specified, will not show all the matched lines.
This commit is contained in:
parent
a2ee5413a1
commit
7c9da2a37c
@ -1329,3 +1329,50 @@ bool VUtils::fixTextWithCaptainShortcut(QAction *p_act, const QString &p_shortcu
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList VUtils::parseCombinedArgString(const QString &p_program)
|
||||||
|
{
|
||||||
|
QStringList args;
|
||||||
|
QString tmp;
|
||||||
|
int quoteCount = 0;
|
||||||
|
bool inQuote = false;
|
||||||
|
|
||||||
|
// handle quoting. tokens can be surrounded by double quotes
|
||||||
|
// "hello world". three consecutive double quotes represent
|
||||||
|
// the quote character itself.
|
||||||
|
for (int i = 0; i < p_program.size(); ++i) {
|
||||||
|
if (p_program.at(i) == QLatin1Char('"')) {
|
||||||
|
++quoteCount;
|
||||||
|
if (quoteCount == 3) {
|
||||||
|
// third consecutive quote
|
||||||
|
quoteCount = 0;
|
||||||
|
tmp += p_program.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quoteCount) {
|
||||||
|
if (quoteCount == 1) {
|
||||||
|
inQuote = !inQuote;
|
||||||
|
}
|
||||||
|
|
||||||
|
quoteCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inQuote && p_program.at(i).isSpace()) {
|
||||||
|
if (!tmp.isEmpty()) {
|
||||||
|
args += tmp;
|
||||||
|
tmp.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tmp += p_program.at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tmp.isEmpty()) {
|
||||||
|
args += tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
@ -309,6 +309,9 @@ public:
|
|||||||
|
|
||||||
static bool fixTextWithCaptainShortcut(QAction *p_act, const QString &p_shortcut);
|
static bool fixTextWithCaptainShortcut(QAction *p_act, const QString &p_shortcut);
|
||||||
|
|
||||||
|
// From QProcess code.
|
||||||
|
static QStringList parseCombinedArgString(const QString &p_program);
|
||||||
|
|
||||||
// Regular expression for image link.
|
// Regular expression for image link.
|
||||||
// 
|
// 
|
||||||
// Captured texts (need to be trimmed):
|
// Captured texts (need to be trimmed):
|
||||||
|
@ -62,47 +62,6 @@ void VExporter::prepareExport(const ExportOption &p_opt)
|
|||||||
prepareWKArguments(p_opt.m_pdfOpt);
|
prepareWKArguments(p_opt.m_pdfOpt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From QProcess code.
|
|
||||||
static QStringList parseCombinedArgString(const QString &program)
|
|
||||||
{
|
|
||||||
QStringList args;
|
|
||||||
QString tmp;
|
|
||||||
int quoteCount = 0;
|
|
||||||
bool inQuote = false;
|
|
||||||
|
|
||||||
// handle quoting. tokens can be surrounded by double quotes
|
|
||||||
// "hello world". three consecutive double quotes represent
|
|
||||||
// the quote character itself.
|
|
||||||
for (int i = 0; i < program.size(); ++i) {
|
|
||||||
if (program.at(i) == QLatin1Char('"')) {
|
|
||||||
++quoteCount;
|
|
||||||
if (quoteCount == 3) {
|
|
||||||
// third consecutive quote
|
|
||||||
quoteCount = 0;
|
|
||||||
tmp += program.at(i);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (quoteCount) {
|
|
||||||
if (quoteCount == 1)
|
|
||||||
inQuote = !inQuote;
|
|
||||||
quoteCount = 0;
|
|
||||||
}
|
|
||||||
if (!inQuote && program.at(i).isSpace()) {
|
|
||||||
if (!tmp.isEmpty()) {
|
|
||||||
args += tmp;
|
|
||||||
tmp.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tmp += program.at(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!tmp.isEmpty())
|
|
||||||
args += tmp;
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VExporter::prepareWKArguments(const ExportPDFOption &p_opt)
|
void VExporter::prepareWKArguments(const ExportPDFOption &p_opt)
|
||||||
{
|
{
|
||||||
m_wkArgs.clear();
|
m_wkArgs.clear();
|
||||||
@ -154,7 +113,7 @@ void VExporter::prepareWKArguments(const ExportPDFOption &p_opt)
|
|||||||
|
|
||||||
// Append additional global option.
|
// Append additional global option.
|
||||||
if (!p_opt.m_wkExtraArgs.isEmpty()) {
|
if (!p_opt.m_wkExtraArgs.isEmpty()) {
|
||||||
m_wkArgs.append(parseCombinedArgString(p_opt.m_wkExtraArgs));
|
m_wkArgs.append(VUtils::parseCombinedArgString(p_opt.m_wkExtraArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOC option.
|
// TOC option.
|
||||||
@ -929,7 +888,7 @@ int VExporter::startProcess(const QString &p_program, const QStringList &p_args)
|
|||||||
|
|
||||||
int VExporter::startProcess(const QString &p_cmd)
|
int VExporter::startProcess(const QString &p_cmd)
|
||||||
{
|
{
|
||||||
QStringList args = parseCombinedArgString(p_cmd);
|
QStringList args = VUtils::parseCombinedArgString(p_cmd);
|
||||||
if (args.isEmpty()) {
|
if (args.isEmpty()) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
@ -136,15 +136,13 @@ void VSearch::searchFirstPhase(VFile *p_file,
|
|||||||
Q_ASSERT(testTarget(VSearchConfig::Note));
|
Q_ASSERT(testTarget(VSearchConfig::Note));
|
||||||
|
|
||||||
QString name = p_file->getName();
|
QString name = p_file->getName();
|
||||||
if (!m_patternReg.isEmpty()) {
|
if (!matchPattern(name)) {
|
||||||
if (!matchOneLine(name, m_patternReg)) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString filePath = p_file->fetchPath();
|
QString filePath = p_file->fetchPath();
|
||||||
if (testObject(VSearchConfig::Name)) {
|
if (testObject(VSearchConfig::Name)) {
|
||||||
if (matchOneLine(name, m_searchReg)) {
|
if (matchNonContent(name)) {
|
||||||
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Note,
|
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Note,
|
||||||
VSearchResultItem::LineNumber,
|
VSearchResultItem::LineNumber,
|
||||||
name,
|
name,
|
||||||
@ -195,7 +193,7 @@ void VSearch::searchFirstPhase(VDirectory *p_directory,
|
|||||||
if (testTarget(VSearchConfig::Folder)
|
if (testTarget(VSearchConfig::Folder)
|
||||||
&& testObject(VSearchConfig::Name)) {
|
&& testObject(VSearchConfig::Name)) {
|
||||||
QString text = p_directory->getName();
|
QString text = p_directory->getName();
|
||||||
if (matchOneLine(text, m_searchReg)) {
|
if (matchNonContent(text)) {
|
||||||
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Folder,
|
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Folder,
|
||||||
VSearchResultItem::LineNumber,
|
VSearchResultItem::LineNumber,
|
||||||
text,
|
text,
|
||||||
@ -248,7 +246,7 @@ void VSearch::searchFirstPhase(VNotebook *p_notebook,
|
|||||||
if (testTarget(VSearchConfig::Notebook)
|
if (testTarget(VSearchConfig::Notebook)
|
||||||
&& testObject(VSearchConfig::Name)) {
|
&& testObject(VSearchConfig::Name)) {
|
||||||
QString text = p_notebook->getName();
|
QString text = p_notebook->getName();
|
||||||
if (matchOneLine(text, m_searchReg)) {
|
if (matchNonContent(text)) {
|
||||||
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Notebook,
|
VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Notebook,
|
||||||
VSearchResultItem::LineNumber,
|
VSearchResultItem::LineNumber,
|
||||||
text,
|
text,
|
||||||
@ -295,7 +293,7 @@ VSearchResultItem *VSearch::searchForOutline(const VFile *p_file) const
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matchOneLine(it.m_name, m_searchReg)) {
|
if (!matchNonContent(it.m_name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,8 +324,14 @@ VSearchResultItem *VSearch::searchForContent(const VFile *p_file) const
|
|||||||
int pos = 0;
|
int pos = 0;
|
||||||
int size = content.size();
|
int size = content.size();
|
||||||
QRegExp newLineReg = QRegExp("\\n|\\r\\n|\\r");
|
QRegExp newLineReg = QRegExp("\\n|\\r\\n|\\r");
|
||||||
Qt::CaseSensitivity cs = testOption(VSearchConfig::CaseSensitive)
|
VSearchToken &contentToken = m_config->m_contentToken;
|
||||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
bool singleToken = contentToken.tokenSize() == 1;
|
||||||
|
if (!singleToken) {
|
||||||
|
contentToken.startBatchMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allMatched = false;
|
||||||
|
|
||||||
while (pos < size) {
|
while (pos < size) {
|
||||||
int idx = content.indexOf(newLineReg, pos);
|
int idx = content.indexOf(newLineReg, pos);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
@ -337,10 +341,10 @@ VSearchResultItem *VSearch::searchForContent(const VFile *p_file) const
|
|||||||
if (idx > pos) {
|
if (idx > pos) {
|
||||||
QString lineText = content.mid(pos, idx - pos);
|
QString lineText = content.mid(pos, idx - pos);
|
||||||
bool matched = false;
|
bool matched = false;
|
||||||
if (m_contentSearchReg.isEmpty()) {
|
if (singleToken) {
|
||||||
matched = lineText.contains(m_config->m_keyword, cs);
|
matched = contentToken.matched(lineText);
|
||||||
} else {
|
} else {
|
||||||
matched = (m_contentSearchReg.indexIn(lineText) != -1);
|
matched = contentToken.matchBatchMode(lineText);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
@ -360,10 +364,25 @@ VSearchResultItem *VSearch::searchForContent(const VFile *p_file) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!singleToken && contentToken.readyToEndBatchMode(allMatched)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
pos = idx + newLineReg.matchedLength();
|
pos = idx + newLineReg.matchedLength();
|
||||||
++lineNum;
|
++lineNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!singleToken) {
|
||||||
|
contentToken.readyToEndBatchMode(allMatched);
|
||||||
|
contentToken.endBatchMode();
|
||||||
|
|
||||||
|
if (!allMatched && item) {
|
||||||
|
// This file does not meet all the tokens.
|
||||||
|
delete item;
|
||||||
|
item = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,9 @@ private:
|
|||||||
|
|
||||||
bool testOption(VSearchConfig::Option p_option) const;
|
bool testOption(VSearchConfig::Option p_option) const;
|
||||||
|
|
||||||
bool matchOneLine(const QString &p_text, const QRegExp &p_reg) const;
|
bool matchNonContent(const QString &p_text) const;
|
||||||
|
|
||||||
|
bool matchPattern(const QString &p_name) const;
|
||||||
|
|
||||||
VSearchResultItem *searchForOutline(const VFile *p_file) const;
|
VSearchResultItem *searchForOutline(const VFile *p_file) const;
|
||||||
|
|
||||||
@ -78,13 +80,6 @@ private:
|
|||||||
|
|
||||||
ISearchEngine *m_engine;
|
ISearchEngine *m_engine;
|
||||||
|
|
||||||
// Search reg used for name, outline, tag.
|
|
||||||
QRegExp m_searchReg;
|
|
||||||
|
|
||||||
// Search reg used for content.
|
|
||||||
// We use raw string to speed up if it is empty.
|
|
||||||
QRegExp m_contentSearchReg;
|
|
||||||
|
|
||||||
// Wildcard reg to for file name pattern.
|
// Wildcard reg to for file name pattern.
|
||||||
QRegExp m_patternReg;
|
QRegExp m_patternReg;
|
||||||
};
|
};
|
||||||
@ -99,40 +94,6 @@ inline void VSearch::setConfig(QSharedPointer<VSearchConfig> p_config)
|
|||||||
{
|
{
|
||||||
m_config = p_config;
|
m_config = p_config;
|
||||||
|
|
||||||
// Compile reg.
|
|
||||||
const QString &keyword = m_config->m_keyword;
|
|
||||||
m_contentSearchReg = QRegExp();
|
|
||||||
if (keyword.isEmpty()) {
|
|
||||||
m_searchReg = QRegExp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::CaseSensitivity cs = testOption(VSearchConfig::CaseSensitive)
|
|
||||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
|
||||||
if (testOption(VSearchConfig::RegularExpression)) {
|
|
||||||
m_searchReg = QRegExp(keyword, cs);
|
|
||||||
m_contentSearchReg = QRegExp(keyword, cs);
|
|
||||||
} else {
|
|
||||||
if (testOption(VSearchConfig::Fuzzy)) {
|
|
||||||
QString wildcardText(keyword.size() * 2 + 1, '*');
|
|
||||||
for (int i = 0, j = 1; i < keyword.size(); ++i, j += 2) {
|
|
||||||
wildcardText[j] = keyword[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
m_searchReg = QRegExp(wildcardText, cs, QRegExp::Wildcard);
|
|
||||||
} else {
|
|
||||||
QString pattern = QRegExp::escape(keyword);
|
|
||||||
if (testOption(VSearchConfig::WholeWordOnly)) {
|
|
||||||
pattern = "\\b" + pattern + "\\b";
|
|
||||||
|
|
||||||
// We only use m_contentSearchReg when WholeWordOnly is checked.
|
|
||||||
m_contentSearchReg = QRegExp(pattern, cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_searchReg = QRegExp(pattern, cs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_config->m_pattern.isEmpty()) {
|
if (m_config->m_pattern.isEmpty()) {
|
||||||
m_patternReg = QRegExp();
|
m_patternReg = QRegExp();
|
||||||
} else {
|
} else {
|
||||||
@ -155,8 +116,17 @@ inline bool VSearch::testOption(VSearchConfig::Option p_option) const
|
|||||||
return p_option & m_config->m_option;
|
return p_option & m_config->m_option;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool VSearch::matchOneLine(const QString &p_text, const QRegExp &p_reg) const
|
inline bool VSearch::matchNonContent(const QString &p_text) const
|
||||||
{
|
{
|
||||||
return p_reg.indexIn(p_text) != -1;
|
return m_config->m_token.matched(p_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VSearch::matchPattern(const QString &p_name) const
|
||||||
|
{
|
||||||
|
if (m_patternReg.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p_name.contains(m_patternReg);
|
||||||
}
|
}
|
||||||
#endif // VSEARCH_H
|
#endif // VSEARCH_H
|
||||||
|
@ -4,6 +4,185 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QRegExp>
|
||||||
|
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct VSearchToken
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
RawString = 0,
|
||||||
|
RegularExpression
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Operator
|
||||||
|
{
|
||||||
|
And = 0,
|
||||||
|
Or
|
||||||
|
};
|
||||||
|
|
||||||
|
VSearchToken()
|
||||||
|
: m_type(Type::RawString),
|
||||||
|
m_op(Operator::And),
|
||||||
|
m_caseSensitivity(Qt::CaseSensitive)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_keywords.clear();
|
||||||
|
m_regs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const QString &p_rawStr)
|
||||||
|
{
|
||||||
|
m_keywords.append(p_rawStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const QRegExp &p_reg)
|
||||||
|
{
|
||||||
|
m_regs.append(p_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString toString() const
|
||||||
|
{
|
||||||
|
return QString("token %1 %2 %3 %4 %5").arg(m_type)
|
||||||
|
.arg(m_op)
|
||||||
|
.arg(m_caseSensitivity)
|
||||||
|
.arg(m_keywords.size())
|
||||||
|
.arg(m_regs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether @p_text match all the constraint.
|
||||||
|
bool matched(const QString &p_text) const
|
||||||
|
{
|
||||||
|
int size = m_keywords.size();
|
||||||
|
if (m_type == Type::RegularExpression) {
|
||||||
|
size = m_regs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = m_op == Operator::And ? true : false;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
bool tmp = false;
|
||||||
|
if (m_type == Type::RawString) {
|
||||||
|
tmp = p_text.contains(m_keywords[i], m_caseSensitivity);
|
||||||
|
} else {
|
||||||
|
tmp = p_text.contains(m_regs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp) {
|
||||||
|
if (m_op == Operator::Or) {
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_op == Operator::And) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startBatchMode()
|
||||||
|
{
|
||||||
|
int size = m_type == Type::RawString ? m_keywords.size() : m_regs.size();
|
||||||
|
m_matchesInBatch.resize(size);
|
||||||
|
m_matchesInBatch.fill(false);
|
||||||
|
m_numOfMatches = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match one string in batch mode.
|
||||||
|
// Returns true if @p_text matches one.
|
||||||
|
bool matchBatchMode(const QString &p_text)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
int size = m_matchesInBatch.size();
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
if (m_matchesInBatch[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tmp = false;
|
||||||
|
if (m_type == Type::RawString) {
|
||||||
|
tmp = p_text.contains(m_keywords[i], m_caseSensitivity);
|
||||||
|
} else {
|
||||||
|
tmp = p_text.contains(m_regs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp) {
|
||||||
|
m_matchesInBatch[i] = true;
|
||||||
|
++m_numOfMatches;
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether it is OK to finished batch mode.
|
||||||
|
// @p_matched: the overall match result.
|
||||||
|
bool readyToEndBatchMode(bool &p_matched) const
|
||||||
|
{
|
||||||
|
if (m_op == VSearchToken::And) {
|
||||||
|
// We need all the tokens matched.
|
||||||
|
if (m_numOfMatches == m_matchesInBatch.size()) {
|
||||||
|
p_matched = true;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
p_matched = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We only need one match.
|
||||||
|
if (m_numOfMatches > 0) {
|
||||||
|
p_matched = true;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
p_matched = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void endBatchMode()
|
||||||
|
{
|
||||||
|
m_matchesInBatch.clear();
|
||||||
|
m_numOfMatches = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tokenSize() const
|
||||||
|
{
|
||||||
|
return m_type == Type::RawString ? m_keywords.size() : m_regs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
VSearchToken::Type m_type;
|
||||||
|
|
||||||
|
VSearchToken::Operator m_op;
|
||||||
|
|
||||||
|
Qt::CaseSensitivity m_caseSensitivity;
|
||||||
|
|
||||||
|
// Valid at RawString.
|
||||||
|
QVector<QString> m_keywords;
|
||||||
|
|
||||||
|
// Valid at RegularExpression.
|
||||||
|
QVector<QRegExp> m_regs;
|
||||||
|
|
||||||
|
// Bitmap for batch mode.
|
||||||
|
// True if m_regs[i] or m_keywords[i] has been matched.
|
||||||
|
QVector<bool> m_matchesInBatch;
|
||||||
|
|
||||||
|
int m_numOfMatches;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct VSearchConfig
|
struct VSearchConfig
|
||||||
@ -49,6 +228,7 @@ struct VSearchConfig
|
|||||||
RegularExpression = 0x8UL
|
RegularExpression = 0x8UL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
VSearchConfig()
|
VSearchConfig()
|
||||||
: VSearchConfig(Scope::NoneScope,
|
: VSearchConfig(Scope::NoneScope,
|
||||||
Object::NoneObject,
|
Object::NoneObject,
|
||||||
@ -73,9 +253,88 @@ struct VSearchConfig
|
|||||||
m_target(p_target),
|
m_target(p_target),
|
||||||
m_engine(p_engine),
|
m_engine(p_engine),
|
||||||
m_option(p_option),
|
m_option(p_option),
|
||||||
m_keyword(p_keyword),
|
|
||||||
m_pattern(p_pattern)
|
m_pattern(p_pattern)
|
||||||
{
|
{
|
||||||
|
compileToken(p_keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compileToken(const QString &p_keyword)
|
||||||
|
{
|
||||||
|
m_token.clear();
|
||||||
|
m_contentToken.clear();
|
||||||
|
if (p_keyword.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::CaseSensitivity cs = m_option & VSearchConfig::CaseSensitive
|
||||||
|
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||||
|
bool useReg = m_option & VSearchConfig::RegularExpression;
|
||||||
|
bool wwo = m_option & VSearchConfig::WholeWordOnly;
|
||||||
|
bool fuzzy = m_option & VSearchConfig::Fuzzy;
|
||||||
|
|
||||||
|
m_token.m_caseSensitivity = cs;
|
||||||
|
m_contentToken.m_caseSensitivity = cs;
|
||||||
|
|
||||||
|
if (useReg) {
|
||||||
|
m_token.m_type = VSearchToken::RegularExpression;
|
||||||
|
m_contentToken.m_type = VSearchToken::RegularExpression;
|
||||||
|
} else {
|
||||||
|
if (fuzzy) {
|
||||||
|
m_token.m_type = VSearchToken::RegularExpression;
|
||||||
|
m_contentToken.m_type = VSearchToken::RawString;
|
||||||
|
} else if (wwo) {
|
||||||
|
m_token.m_type = VSearchToken::RegularExpression;
|
||||||
|
m_contentToken.m_type = VSearchToken::RegularExpression;
|
||||||
|
} else {
|
||||||
|
m_token.m_type = VSearchToken::RawString;
|
||||||
|
m_contentToken.m_type = VSearchToken::RawString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VSearchToken::Operator op = VSearchToken::And;
|
||||||
|
|
||||||
|
// """ to input a ";
|
||||||
|
// && for AND, || for OR;
|
||||||
|
QStringList args = VUtils::parseCombinedArgString(p_keyword);
|
||||||
|
for (auto const & arg : args) {
|
||||||
|
if (arg == QStringLiteral("&&")) {
|
||||||
|
op = VSearchToken::And;
|
||||||
|
continue;
|
||||||
|
} else if (arg == QStringLiteral("||")) {
|
||||||
|
op = VSearchToken::Or;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useReg) {
|
||||||
|
QRegExp reg(arg, cs);
|
||||||
|
m_token.append(reg);
|
||||||
|
m_contentToken.append(reg);
|
||||||
|
} else {
|
||||||
|
if (fuzzy) {
|
||||||
|
QString wildcardText(arg.size() * 2 + 1, '*');
|
||||||
|
for (int i = 0, j = 1; i < arg.size(); ++i, j += 2) {
|
||||||
|
wildcardText[j] = arg[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegExp reg(wildcardText, cs, QRegExp::Wildcard);
|
||||||
|
m_token.append(reg);
|
||||||
|
m_contentToken.append(arg);
|
||||||
|
} else if (wwo) {
|
||||||
|
QString pattern = QRegExp::escape(arg);
|
||||||
|
pattern = "\\b" + pattern + "\\b";
|
||||||
|
|
||||||
|
QRegExp reg(pattern, cs);
|
||||||
|
m_token.append(reg);
|
||||||
|
m_contentToken.append(reg);
|
||||||
|
} else {
|
||||||
|
m_token.append(arg);
|
||||||
|
m_contentToken.append(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_token.m_op = op;
|
||||||
|
m_contentToken.m_op = op;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList toConfig() const
|
QStringList toConfig() const
|
||||||
@ -114,10 +373,14 @@ struct VSearchConfig
|
|||||||
int m_engine;
|
int m_engine;
|
||||||
int m_option;
|
int m_option;
|
||||||
|
|
||||||
QString m_keyword;
|
|
||||||
|
|
||||||
// Wildcard pattern to filter file.
|
// Wildcard pattern to filter file.
|
||||||
QString m_pattern;
|
QString m_pattern;
|
||||||
|
|
||||||
|
// Token for name, outline, and tag.
|
||||||
|
VSearchToken m_token;
|
||||||
|
|
||||||
|
// Token for content.
|
||||||
|
VSearchToken m_contentToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,14 +14,10 @@ VSearchEngineWorker::VSearchEngineWorker(QObject *p_parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VSearchEngineWorker::setData(const QStringList &p_files,
|
void VSearchEngineWorker::setData(const QStringList &p_files,
|
||||||
const QRegExp &p_reg,
|
const VSearchToken &p_token)
|
||||||
const QString &p_keyword,
|
|
||||||
Qt::CaseSensitivity p_cs)
|
|
||||||
{
|
{
|
||||||
m_files = p_files;
|
m_files = p_files;
|
||||||
m_reg = p_reg;
|
m_token = p_token;
|
||||||
m_keyword = p_keyword;
|
|
||||||
m_caseSensitivity = p_cs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSearchEngineWorker::stop()
|
void VSearchEngineWorker::stop()
|
||||||
@ -71,6 +67,14 @@ VSearchResultItem *VSearchEngineWorker::searchFile(const QString &p_fileName)
|
|||||||
VSearchResultItem *item = NULL;
|
VSearchResultItem *item = NULL;
|
||||||
QString line;
|
QString line;
|
||||||
QTextStream in(&file);
|
QTextStream in(&file);
|
||||||
|
|
||||||
|
bool singleToken = m_token.tokenSize() == 1;
|
||||||
|
if (!singleToken) {
|
||||||
|
m_token.startBatchMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allMatched = false;
|
||||||
|
|
||||||
while (!in.atEnd()) {
|
while (!in.atEnd()) {
|
||||||
if (m_stop.load() == 1) {
|
if (m_stop.load() == 1) {
|
||||||
m_state = VSearchState::Cancelled;
|
m_state = VSearchState::Cancelled;
|
||||||
@ -78,14 +82,12 @@ VSearchResultItem *VSearchEngineWorker::searchFile(const QString &p_fileName)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool matched = false;
|
|
||||||
line = in.readLine();
|
line = in.readLine();
|
||||||
if (m_reg.isEmpty()) {
|
bool matched = false;
|
||||||
if (line.contains(m_keyword, m_caseSensitivity)) {
|
if (singleToken) {
|
||||||
matched = true;
|
matched = m_token.matched(line);
|
||||||
}
|
} else {
|
||||||
} else if (m_reg.indexIn(line) != -1) {
|
matched = m_token.matchBatchMode(line);
|
||||||
matched = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
@ -100,9 +102,23 @@ VSearchResultItem *VSearchEngineWorker::searchFile(const QString &p_fileName)
|
|||||||
item->m_matches.append(sitem);
|
item->m_matches.append(sitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!singleToken && m_token.readyToEndBatchMode(allMatched)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
++lineNum;
|
++lineNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!singleToken) {
|
||||||
|
m_token.readyToEndBatchMode(allMatched);
|
||||||
|
m_token.endBatchMode();
|
||||||
|
|
||||||
|
if (!allMatched && item) {
|
||||||
|
delete item;
|
||||||
|
item = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,23 +145,14 @@ void VSearchEngine::search(const QSharedPointer<VSearchConfig> &p_config,
|
|||||||
|
|
||||||
m_result = p_result;
|
m_result = p_result;
|
||||||
|
|
||||||
QRegExp reg = compileRegExpFromConfig(p_config);
|
|
||||||
Qt::CaseSensitivity cs = (p_config->m_option & VSearchConfig::CaseSensitive)
|
|
||||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
|
||||||
|
|
||||||
clearAllWorkers();
|
clearAllWorkers();
|
||||||
m_workers.reserve(numThread);
|
m_workers.reserve(numThread);
|
||||||
m_finishedWorkers = 0;
|
m_finishedWorkers = 0;
|
||||||
int totalSize = m_result->m_secondPhaseItems.size();
|
int totalSize = m_result->m_secondPhaseItems.size();
|
||||||
int step = totalSize / numThread;
|
int step = totalSize / numThread;
|
||||||
int remain = totalSize % numThread;
|
int remain = totalSize % numThread;
|
||||||
|
int start = 0;
|
||||||
for (int i = 0; i < numThread; ++i) {
|
for (int i = 0; i < numThread && start < totalSize; ++i) {
|
||||||
int start = i * step;
|
|
||||||
if (start >= totalSize) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = step;
|
int len = step;
|
||||||
if (remain) {
|
if (remain) {
|
||||||
++len;
|
++len;
|
||||||
@ -158,9 +165,7 @@ void VSearchEngine::search(const QSharedPointer<VSearchConfig> &p_config,
|
|||||||
|
|
||||||
VSearchEngineWorker *th = new VSearchEngineWorker(this);
|
VSearchEngineWorker *th = new VSearchEngineWorker(this);
|
||||||
th->setData(m_result->m_secondPhaseItems.mid(start, len),
|
th->setData(m_result->m_secondPhaseItems.mid(start, len),
|
||||||
reg,
|
p_config->m_contentToken);
|
||||||
p_config->m_keyword,
|
|
||||||
cs);
|
|
||||||
connect(th, &VSearchEngineWorker::finished,
|
connect(th, &VSearchEngineWorker::finished,
|
||||||
this, &VSearchEngine::handleWorkerFinished);
|
this, &VSearchEngine::handleWorkerFinished);
|
||||||
connect(th, &VSearchEngineWorker::resultItemReady,
|
connect(th, &VSearchEngineWorker::resultItemReady,
|
||||||
@ -170,27 +175,13 @@ void VSearchEngine::search(const QSharedPointer<VSearchConfig> &p_config,
|
|||||||
|
|
||||||
m_workers.append(th);
|
m_workers.append(th);
|
||||||
th->start();
|
th->start();
|
||||||
|
|
||||||
|
start += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "schedule tasks to threads" << m_workers.size() << totalSize << step;
|
qDebug() << "schedule tasks to threads" << m_workers.size() << totalSize << step;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegExp VSearchEngine::compileRegExpFromConfig(const QSharedPointer<VSearchConfig> &p_config) const
|
|
||||||
{
|
|
||||||
const QString &keyword = p_config->m_keyword;
|
|
||||||
Qt::CaseSensitivity cs = (p_config->m_option & VSearchConfig::CaseSensitive)
|
|
||||||
? Qt::CaseSensitive : Qt::CaseInsensitive;
|
|
||||||
if (p_config->m_option & VSearchConfig::RegularExpression) {
|
|
||||||
return QRegExp(keyword, cs);
|
|
||||||
} else if (p_config->m_option & VSearchConfig::WholeWordOnly) {
|
|
||||||
QString pattern = QRegExp::escape(keyword);
|
|
||||||
pattern = "\\b" + pattern + "\\b";
|
|
||||||
return QRegExp(pattern, cs);
|
|
||||||
} else {
|
|
||||||
return QRegExp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSearchEngine::stop()
|
void VSearchEngine::stop()
|
||||||
{
|
{
|
||||||
qDebug() << "VSearchEngine asked to stop";
|
qDebug() << "VSearchEngine asked to stop";
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#ifndef VSEARCHENGINE_H
|
#ifndef VSEARCHENGINE_H
|
||||||
#define VSEARCHENGINE_H
|
#define VSEARCHENGINE_H
|
||||||
|
|
||||||
|
#include "isearchengine.h"
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInt>
|
||||||
#include "isearchengine.h"
|
|
||||||
|
#include "vsearchconfig.h"
|
||||||
|
|
||||||
class VSearchEngineWorker : public QThread
|
class VSearchEngineWorker : public QThread
|
||||||
{
|
{
|
||||||
@ -16,9 +19,7 @@ public:
|
|||||||
explicit VSearchEngineWorker(QObject *p_parent = nullptr);
|
explicit VSearchEngineWorker(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
void setData(const QStringList &p_files,
|
void setData(const QStringList &p_files,
|
||||||
const QRegExp &p_reg,
|
const VSearchToken &p_token);
|
||||||
const QString &p_keyword,
|
|
||||||
Qt::CaseSensitivity p_cs);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void stop();
|
void stop();
|
||||||
@ -38,11 +39,7 @@ private:
|
|||||||
|
|
||||||
QStringList m_files;
|
QStringList m_files;
|
||||||
|
|
||||||
QRegExp m_reg;
|
VSearchToken m_token;
|
||||||
|
|
||||||
QString m_keyword;
|
|
||||||
|
|
||||||
Qt::CaseSensitivity m_caseSensitivity;
|
|
||||||
|
|
||||||
VSearchState m_state;
|
VSearchState m_state;
|
||||||
|
|
||||||
@ -76,9 +73,6 @@ private slots:
|
|||||||
void handleWorkerFinished();
|
void handleWorkerFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Returns an empty object if raw string is preferred.
|
|
||||||
QRegExp compileRegExpFromConfig(const QSharedPointer<VSearchConfig> &p_config) const;
|
|
||||||
|
|
||||||
void clearAllWorkers();
|
void clearAllWorkers();
|
||||||
|
|
||||||
int m_finishedWorkers;
|
int m_finishedWorkers;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user