LocationList: highlight text segments

This commit is contained in:
Le Tan 2021-07-21 20:54:58 +08:00
parent edfdd68c36
commit ef69ee435f
15 changed files with 184 additions and 41 deletions

@ -1 +1 @@
Subproject commit 76a963c0e2ac488001872ac6f324ba1c8b177bfd
Subproject commit a15d859141506142a11cbf843a2d93124ea65bfa

View File

@ -109,6 +109,30 @@ namespace vnotex
Right
};
struct Segment
{
Segment() = default;
Segment(int p_offset, int p_length)
: m_offset(p_offset),
m_length(p_length)
{
}
bool operator<(const Segment &p_other) const
{
if (m_offset < p_other.m_offset) {
return true;;
} else {
return m_length < p_other.m_length;
}
}
int m_offset = 0;
int m_length = -1;
};
} // ns vnotex
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);

View File

@ -3,6 +3,8 @@
#include <QDebug>
#include "global.h"
namespace vnotex
{
struct Location
@ -37,9 +39,10 @@ namespace vnotex
{
Line() = default;
Line(int p_lineNumber, const QString &p_text)
Line(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
: m_lineNumber(p_lineNumber),
m_text(p_text)
m_text(p_text),
m_segments(p_segments)
{
}
@ -47,11 +50,13 @@ namespace vnotex
int m_lineNumber = -1;
QString m_text;
QVector<Segment> m_segments;
};
void addLine(int p_lineNumber, const QString &p_text)
void addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
{
m_lines.push_back(Line(p_lineNumber, p_text));
m_lines.push_back(Line(p_lineNumber, p_text, p_segments));
}
friend QDebug operator<<(QDebug p_dbg, const ComplexLocation &p_loc)

View File

@ -236,6 +236,10 @@
"locationlist" : {
"node_icon" : {
"fg" : "@base#icon#fg"
},
"text_highlight" : {
"fg" : "@base#master#fg",
"bg" : "@base#master#bg"
}
},
"viewsplit" : {

View File

@ -44,6 +44,10 @@
"danger" : {
"fg": "@base#danger#fg"
}
},
"master" : {
"fg" : "#fbffff",
"bg" : "#535c65"
}
},
"widgets" : {
@ -53,8 +57,8 @@
"button": {
"fg" : "@base#normal#fg",
"active" : {
"fg" : "#fbffff",
"bg" : "#535c65"
"fg" : "@base#master#fg",
"bg" : "@base#master#bg"
}
}
}
@ -95,6 +99,10 @@
"locationlist" : {
"node_icon" : {
"fg" : "@base#icon#fg"
},
"text_highlight" : {
"fg" : "@base#master#fg",
"bg" : "@base#master#bg"
}
},
"viewsplit" : {
@ -123,8 +131,8 @@
},
"quickselector" : {
"item_icon" : {
"fg" : "#535c65",
"border" : "#535c65"
"fg" : "@base#master#bg",
"border" : "@base#master#bg"
}
}
}

View File

@ -232,6 +232,10 @@
"locationlist" : {
"node_icon" : {
"fg" : "@base#icon#fg"
},
"text_highlight" : {
"fg" : "@base#master#fg",
"bg" : "@base#master#bg"
}
},
"viewsplit" : {

View File

@ -97,17 +97,18 @@ void FileSearchEngineWorker::searchFile(const QString &p_filePath, const QString
const auto lineText = ins.readLine();
bool matched = false;
QVector<Segment> segments;
if (!shouldStartBatchMode) {
matched = m_token.matched(lineText);
matched = m_token.matched(lineText, &segments);
} else {
matched = m_token.matchedInBatchMode(lineText);
matched = m_token.matchedInBatchMode(lineText, &segments);
}
if (matched) {
if (resultItem) {
resultItem->addLine(lineNum, lineText);
resultItem->addLine(lineNum, lineText, segments);
} else {
resultItem = SearchResultItem::createFileItem(p_filePath, p_displayPath, lineNum, lineText);
resultItem = SearchResultItem::createFileItem(p_filePath, p_displayPath, lineNum, lineText, segments);
}
}

View File

@ -207,13 +207,13 @@ bool Searcher::firstPhaseSearch(const File *p_file)
if (testObject(SearchObject::SearchName)) {
if (isTokenMatched(name)) {
emit resultItemAdded(SearchResultItem::createBufferItem(filePath, relativePath, -1, name));
emit resultItemAdded(SearchResultItem::createBufferItem(filePath, relativePath));
}
}
if (testObject(SearchObject::SearchPath)) {
if (isTokenMatched(relativePath)) {
emit resultItemAdded(SearchResultItem::createBufferItem(filePath, relativePath, -1, name));
emit resultItemAdded(SearchResultItem::createBufferItem(filePath, relativePath));
}
}
@ -294,17 +294,18 @@ bool Searcher::searchContent(const File *p_file)
if (idx > pos) {
QString lineText = content.mid(pos, idx - pos);
bool matched = false;
QVector<Segment> segments;
if (!shouldStartBatchMode) {
matched = m_token.matched(lineText);
matched = m_token.matched(lineText, &segments);
} else {
matched = m_token.matchedInBatchMode(lineText);
matched = m_token.matchedInBatchMode(lineText, &segments);
}
if (matched) {
if (resultItem) {
resultItem->addLine(lineNum, lineText);
resultItem->addLine(lineNum, lineText, segments);
} else {
resultItem = SearchResultItem::createBufferItem(filePath, relativePath, lineNum, lineText);
resultItem = SearchResultItem::createBufferItem(filePath, relativePath, lineNum, lineText, segments);
}
}
}
@ -407,13 +408,13 @@ bool Searcher::firstPhaseSearch(Node *p_node, QVector<SearchSecondPhaseItem> &p_
if (testObject(SearchObject::SearchName)) {
if (isTokenMatched(name)) {
emit resultItemAdded(SearchResultItem::createFileItem(filePath, relativePath, -1, name));
emit resultItemAdded(SearchResultItem::createFileItem(filePath, relativePath));
}
}
if (testObject(SearchObject::SearchPath)) {
if (isTokenMatched(relativePath)) {
emit resultItemAdded(SearchResultItem::createFileItem(filePath, relativePath, -1, name));
emit resultItemAdded(SearchResultItem::createFileItem(filePath, relativePath));
}
}

View File

@ -5,26 +5,42 @@ using namespace vnotex;
QSharedPointer<SearchResultItem> SearchResultItem::createBufferItem(const QString &p_targetPath,
const QString &p_displayPath,
int p_lineNumber,
const QString &p_text)
const QString &p_text,
const QVector<Segment> &p_segments)
{
auto item = createBufferItem(p_targetPath, p_displayPath);
item->m_location.addLine(p_lineNumber, p_text, p_segments);
return item;
}
QSharedPointer<SearchResultItem> SearchResultItem::createBufferItem(const QString &p_targetPath,
const QString &p_displayPath)
{
auto item = QSharedPointer<SearchResultItem>::create();
item->m_location.m_type = LocationType::Buffer;
item->m_location.m_path = p_targetPath;
item->m_location.m_displayPath = p_displayPath;
item->m_location.addLine(p_lineNumber, p_text);
return item;
}
QSharedPointer<SearchResultItem> SearchResultItem::createFileItem(const QString &p_targetPath,
const QString &p_displayPath,
int p_lineNumber,
const QString &p_text)
const QString &p_text,
const QVector<Segment> &p_segments)
{
auto item = createFileItem(p_targetPath, p_displayPath);
item->m_location.addLine(p_lineNumber, p_text, p_segments);
return item;
}
QSharedPointer<SearchResultItem> SearchResultItem::createFileItem(const QString &p_targetPath,
const QString &p_displayPath)
{
auto item = QSharedPointer<SearchResultItem>::create();
item->m_location.m_type = LocationType::File;
item->m_location.m_path = p_targetPath;
item->m_location.m_displayPath = p_displayPath;
item->m_location.addLine(p_lineNumber, p_text);
return item;
}
@ -48,7 +64,7 @@ QSharedPointer<SearchResultItem> SearchResultItem::createNotebookItem(const QStr
return item;
}
void SearchResultItem::addLine(int p_lineNumber, const QString &p_text)
void SearchResultItem::addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
{
m_location.addLine(p_lineNumber, p_text);
m_location.addLine(p_lineNumber, p_text, p_segments);
}

View File

@ -17,17 +17,25 @@ namespace vnotex
return p_dbg;
}
void addLine(int p_lineNumber, const QString &p_text);
void addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments);
static QSharedPointer<SearchResultItem> createBufferItem(const QString &p_targetPath,
const QString &p_displayPath,
int p_lineNumber,
const QString &p_text);
const QString &p_text,
const QVector<Segment> &p_segments);
static QSharedPointer<SearchResultItem> createBufferItem(const QString &p_targetPath,
const QString &p_displayPath);
static QSharedPointer<SearchResultItem> createFileItem(const QString &p_targetPath,
const QString &p_displayPath,
int p_lineNumber,
const QString &p_text);
const QString &p_text,
const QVector<Segment> &p_segments);
static QSharedPointer<SearchResultItem> createFileItem(const QString &p_targetPath,
const QString &p_displayPath);
static QSharedPointer<SearchResultItem> createFolderItem(const QString &p_targetPath,
const QString &p_displayPath);

View File

@ -31,7 +31,7 @@ void SearchToken::append(const QRegularExpression &p_regExp)
m_regularExpressions.append(p_regExp);
}
bool SearchToken::matched(const QString &p_text) const
bool SearchToken::matched(const QString &p_text, QVector<Segment> *p_segments) const
{
const int consSize = constraintSize();
if (consSize == 0) {
@ -42,9 +42,22 @@ bool SearchToken::matched(const QString &p_text) const
for (int i = 0; i < consSize; ++i) {
bool consMatched = false;
if (m_type == Type::PlainText) {
consMatched = p_text.contains(m_keywords[i], m_caseSensitivity);
int idx = p_text.indexOf(m_keywords[i], 0, m_caseSensitivity);
if (idx > -1) {
consMatched = true;
if (p_segments) {
p_segments->push_back(Segment(idx, m_keywords[i].size()));
}
}
} else {
consMatched = p_text.contains(m_regularExpressions[i]);
QRegularExpressionMatch match;
int idx = p_text.indexOf(m_regularExpressions[i], 0, &match);
if (idx > -1) {
consMatched = true;
if (p_segments) {
p_segments->push_back(Segment(idx, match.capturedLength()));
}
}
}
if (consMatched) {
@ -77,7 +90,7 @@ void SearchToken::startBatchMode()
m_matchedConstraintsCountInBatchMode = 0;
}
bool SearchToken::matchedInBatchMode(const QString &p_text)
bool SearchToken::matchedInBatchMode(const QString &p_text, QVector<Segment> *p_segments)
{
bool isMatched = false;
const int consSize = m_matchedConstraintsInBatchMode.size();
@ -88,9 +101,22 @@ bool SearchToken::matchedInBatchMode(const QString &p_text)
bool consMatched = false;
if (m_type == Type::PlainText) {
consMatched = p_text.contains(m_keywords[i], m_caseSensitivity);
int idx = p_text.indexOf(m_keywords[i], 0, m_caseSensitivity);
if (idx > -1) {
consMatched = true;
if (p_segments) {
p_segments->push_back(Segment(idx, m_keywords[i].size()));
}
}
} else {
consMatched = p_text.contains(m_regularExpressions[i]);
QRegularExpressionMatch match;
int idx = p_text.indexOf(m_regularExpressions[i], 0, &match);
if (idx > -1) {
consMatched = true;
if (p_segments) {
p_segments->push_back(Segment(idx, match.capturedLength()));
}
}
}
if (consMatched) {

View File

@ -5,7 +5,6 @@
#include <QVector>
#include <QRegularExpression>
#include <QBitArray>
#include <QPair>
#include <QScopedPointer>
#include <core/global.h>
@ -36,7 +35,7 @@ namespace vnotex
void append(const QRegularExpression &p_regExp);
// Whether @p_text is matched.
bool matched(const QString &p_text) const;
bool matched(const QString &p_text, QVector<Segment> *p_segments = nullptr) const;
int constraintSize() const;
@ -49,7 +48,7 @@ namespace vnotex
// Match one string in batch mode.
// Return true if @p_text is matched.
bool matchedInBatchMode(const QString &p_text);
bool matchedInBatchMode(const QString &p_text, QVector<Segment> *p_segments = nullptr);
bool readyToEndBatchMode() const;

View File

@ -32,7 +32,7 @@ SnippetInfoWidget::SnippetInfoWidget(const Snippet *p_snippet, QWidget *p_parent
void SnippetInfoWidget::setupUI()
{
auto mainLayout = new QFormLayout(this);
auto mainLayout = WidgetsFactory::createFormLayout(this);
m_nameLineEdit = WidgetsFactory::createLineEdit(this);
auto validator = new QRegularExpressionValidator(QRegularExpression(PathUtils::c_fileNameRegularExpression),

View File

@ -2,12 +2,14 @@
#include <QVBoxLayout>
#include <QToolButton>
#include <QLabel>
#include "treewidget.h"
#include "widgetsfactory.h"
#include "titlebar.h"
#include <core/vnotex.h>
#include <core/thememgr.h>
#include <utils/iconutils.h>
#include <utils/widgetutils.h>
@ -21,10 +23,20 @@ QIcon LocationList::s_folderIcon;
QIcon LocationList::s_notebookIcon;
QString LocationList::s_textHighlightForeground;
QString LocationList::s_textHighlightBackground;
LocationList::LocationList(QWidget *p_parent)
: QFrame(p_parent)
{
setupUI();
if (s_textHighlightForeground.isEmpty()) {
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
s_textHighlightForeground = themeMgr.paletteColor(QStringLiteral("widgets#locationlist#text_highlight#fg"));
s_textHighlightBackground = themeMgr.paletteColor(QStringLiteral("widgets#locationlist#text_highlight#bg"));
}
}
void LocationList::setupUI()
@ -120,7 +132,38 @@ void LocationList::setItemLocationLineAndText(QTreeWidgetItem *p_item, const Com
if (p_line.m_lineNumber != -1) {
p_item->setText(Columns::LineColumn, QString::number(p_line.m_lineNumber + 1));
}
if (p_line.m_segments.isEmpty()) {
p_item->setText(Columns::TextColumn, p_line.m_text);
} else {
auto segments = p_line.m_segments;
std::sort(segments.begin(), segments.end());
// Use \n as a marker for < and use \r for >.
QString text(p_line.m_text);
int lastOffset = text.size();
for (int i = segments.size() - 1; i >= 0; --i) {
Q_ASSERT(segments[i].m_length > 0);
if (segments[i].m_offset + segments[i].m_length > lastOffset) {
// Interset.
continue;
}
lastOffset = segments[i].m_offset;
text.insert(segments[i].m_offset + segments[i].m_length, QStringLiteral("\n/span\r"));
text.insert(segments[i].m_offset,
QString("\nspan style='color:%1;background-color:%2'\r").arg(s_textHighlightForeground, s_textHighlightBackground));
}
text = text.toHtmlEscaped();
text.replace(QLatin1Char('\n'), QLatin1Char('<'));
text.replace(QLatin1Char('\r'), QLatin1Char('>'));
auto label = new QLabel(m_tree);
label->setTextFormat(Qt::RichText);
label->setText(text);
m_tree->setItemWidget(p_item, Columns::TextColumn, label);
}
}
void LocationList::addLocation(const ComplexLocation &p_location)

View File

@ -68,6 +68,10 @@ namespace vnotex
static QIcon s_folderIcon;
static QIcon s_notebookIcon;
static QString s_textHighlightForeground;
static QString s_textHighlightBackground;
};
}