mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
LocationList: highlight text segments
This commit is contained in:
parent
edfdd68c36
commit
ef69ee435f
@ -1 +1 @@
|
||||
Subproject commit 76a963c0e2ac488001872ac6f324ba1c8b177bfd
|
||||
Subproject commit a15d859141506142a11cbf843a2d93124ea65bfa
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -236,6 +236,10 @@
|
||||
"locationlist" : {
|
||||
"node_icon" : {
|
||||
"fg" : "@base#icon#fg"
|
||||
},
|
||||
"text_highlight" : {
|
||||
"fg" : "@base#master#fg",
|
||||
"bg" : "@base#master#bg"
|
||||
}
|
||||
},
|
||||
"viewsplit" : {
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,6 +232,10 @@
|
||||
"locationlist" : {
|
||||
"node_icon" : {
|
||||
"fg" : "@base#icon#fg"
|
||||
},
|
||||
"text_highlight" : {
|
||||
"fg" : "@base#master#fg",
|
||||
"bg" : "@base#master#bg"
|
||||
}
|
||||
},
|
||||
"viewsplit" : {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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));
|
||||
}
|
||||
p_item->setText(Columns::TextColumn, p_line.m_text);
|
||||
|
||||
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)
|
||||
|
@ -68,6 +68,10 @@ namespace vnotex
|
||||
static QIcon s_folderIcon;
|
||||
|
||||
static QIcon s_notebookIcon;
|
||||
|
||||
static QString s_textHighlightForeground;
|
||||
|
||||
static QString s_textHighlightBackground;
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user