Commit 75a222b6 authored by David Redondo's avatar David Redondo 🏎
Browse files

Grepview - Introduce a new intermediate level corresponding to lines

Summary: Add items that correspond to whole lines which is useful if a line contains multiple matches.

Test Plan:
{F6868680}
Tests pass

Reviewers: #kdevelop, apol

Reviewed By: #kdevelop, apol

Subscribers: kdevelop-devel

Tags: #kdevelop

Differential Revision: https://phabricator.kde.org/D21580
parent 6be8e8ae
......@@ -78,28 +78,50 @@ void GrepOutputDelegate::paint( QPainter* painter, const QStyleOptionViewItem& o
if(item && item->isText())
{
// Use custom manual highlighting
const KTextEditor::Range rng = item->change()->m_range;
// the line number appears grayed
fmt.setForeground(options.palette.brush(QPalette::Disabled, cr));
cur.insertText(i18n("Line %1: ",item->lineNumber()), fmt);
// switch to normal color
fmt.setForeground(options.palette.brush(cg, cr));
cur.insertText(item->text().left(rng.start().column()).remove(leftspaces), fmt);
fmt.setFontWeight(QFont::Bold);
if ( !(options.state & QStyle::State_Selected) ) {
QColor bgHighlight = option.palette.color(QPalette::AlternateBase);
fmt.setBackground(bgHighlight);
if (item->hasChildren()) {
// the line number appears grayed
fmt.setForeground(options.palette.brush(QPalette::Disabled, cr));
cur.insertText(i18n("Line %1: ",item->lineNumber()), fmt);
fmt.setForeground(options.palette.brush(cg, cr));
int firstStart = static_cast<GrepOutputItem*>(item->child(0))->change()->m_range.start().column();
cur.insertText(item->text().left(firstStart).remove(leftspaces), fmt);
KTextEditor::Range previousRange(item->lineNumber(), 0, item->lineNumber(), firstStart);
for (int i = 0; i < item->rowCount(); ++i) {
const KTextEditor::Range range = static_cast<GrepOutputItem*>(item->child(i))->change()->m_range;
fmt.setForeground(options.palette.brush(cg, cr));
cur.insertText(item->text().mid(previousRange.end().column(), range.start().column() - previousRange.end().column()), fmt);
fmt.setFontWeight(QFont::Bold);
if ( !(options.state & QStyle::State_Selected) ) {
QColor bgHighlight = option.palette.color(QPalette::AlternateBase);
fmt.setBackground(bgHighlight);
}
cur.insertText(item->text().mid(range.start().column(), range.columnWidth()), fmt);
fmt.clearBackground();
previousRange = range;
}
fmt.setFontWeight(QFont::Normal);
cur.insertText(item->text().mid(previousRange.end().column()), fmt);
} else {
// Use custom manual highlighting
const KTextEditor::Range rng = item->change()->m_range;
// switch to normal color
fmt.setForeground(options.palette.brush(cg, cr));
cur.insertText(item->text().left(rng.start().column()).remove(leftspaces), fmt);
fmt.setFontWeight(QFont::Bold);
if ( !(options.state & QStyle::State_Selected) ) {
QColor bgHighlight = option.palette.color(QPalette::AlternateBase);
fmt.setBackground(bgHighlight);
}
cur.insertText(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column()), fmt);
fmt.clearBackground();
fmt.setFontWeight(QFont::Normal);
cur.insertText(item->text().mid(rng.end().column()), fmt);
}
cur.insertText(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column()), fmt);
fmt.clearBackground();
fmt.setFontWeight(QFont::Normal);
cur.insertText(item->text().mid(rng.end().column()), fmt);
}else{
QString text;
if(item)
......@@ -146,15 +168,25 @@ QSize GrepOutputDelegate::sizeHint(const QStyleOptionViewItem& option, const QMo
QFontMetrics metrics(font);
font.setBold(true);
QFontMetrics bMetrics(font);
const KTextEditor::Range rng = item->change()->m_range;
int width = metrics.width(item->text().left(rng.start().column())) +
metrics.width(item->text().mid(rng.end().column())) +
bMetrics.width(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column())) +
option.fontMetrics.width(i18n("Line %1: ",item->lineNumber())) +
std::max(option.decorationSize.width(), 0);
ret.setWidth(width);
if (item->hasChildren()) {
int bWidth = 0;
for (int i = 0; i < item->rowCount(); ++i) {
bWidth += static_cast<GrepOutputItem*>(item->child(i))->change()->m_range.columnWidth();
}
int width = option.fontMetrics.width(i18n("Line %1: ",item->lineNumber())) +
metrics.width(item->text().length() - bWidth) + bMetrics.width(bWidth) +
std::max(option.decorationSize.width(), 0);
ret.setWidth(width);
} else {
const KTextEditor::Range rng = item->change()->m_range;
int width = metrics.width(item->text().left(rng.start().column())) +
metrics.width(item->text().mid(rng.end().column())) +
bMetrics.width(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column())) +
std::max(option.decorationSize.width(), 0);
ret.setWidth(width);
}
}else{
// This is only used for titles, so not very performance critical
QString text;
......
......@@ -30,7 +30,7 @@
using namespace KDevelop;
GrepOutputItem::GrepOutputItem(const DocumentChangePointer& change, const QString &text, bool checkable)
GrepOutputItem::GrepOutputItem(const DocumentChangePointer &change, const QString &text, bool checkable)
: QStandardItem(), m_change(change)
{
setText(text);
......@@ -53,6 +53,18 @@ GrepOutputItem::GrepOutputItem(const QString& filename, const QString& text, boo
setCheckState(Qt::Checked);
}
}
GrepOutputItem::GrepOutputItem(const QString &filename, const QString &text, int line, bool checkable)
: QStandardItem(), m_change(new DocumentChange(IndexedString(filename), KTextEditor::Range(line, 0, line, text.length() - 1), QString(), QString()))
{
setText(text);
setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
setCheckable(checkable);
if(checkable)
{
setAutoTristate(true);
setCheckState(Qt::Checked);
}
}
int GrepOutputItem::lineNumber() const
{
......@@ -144,13 +156,28 @@ QVariant GrepOutputItem::data ( int role ) const {
auto *grepModel = static_cast<GrepOutputModel *>(model());
if(role == Qt::ToolTipRole && grepModel && isText())
{
QString start = text().left(m_change->m_range.start().column()).toHtmlEscaped();
// show replaced version in tooltip if we are in replace mode
const QString match = isCheckable() ? grepModel->replacementFor(m_change->m_oldText) : m_change->m_oldText;
const QString repl = QLatin1String("<b>") + match.toHtmlEscaped() + QLatin1String("</b>");
QString end = text().mid(m_change->m_range.end().column()).toHtmlEscaped();
const QString toolTip = QLatin1String("<span style=\"white-space:nowrap\">") + QString(start + repl + end).trimmed() + QLatin1String("</span>");
return toolTip;
if (hasChildren()) {
QString toolTip;
KTextEditor::Range previousRange(lineNumber(), 0, lineNumber(), 0);
for (int i = 0; i < rowCount(); ++i) {
const GrepOutputItem *item = static_cast<GrepOutputItem*>(child(i));
const KTextEditor::Range range = item->change()->m_range;
toolTip += text().mid(previousRange.end().column(), range.start().column() - previousRange.end().column()).toHtmlEscaped();
const QString match = isCheckable() ? grepModel->replacementFor(item->change()->m_oldText) : item->change()->m_oldText;
toolTip += QLatin1String("<b>") + match.toHtmlEscaped() + QLatin1String("</b>");
previousRange = range;
}
toolTip += text().mid(previousRange.end().column());
return QString(QLatin1String("<span style=\"white-space:nowrap\">") + toolTip.trimmed() + QLatin1String("</span>"));
} else {
QString start = text().left(m_change->m_range.start().column()).toHtmlEscaped();
// show replaced version in tooltip if we are in replace mode
const QString match = isCheckable() ? grepModel->replacementFor(m_change->m_oldText) : m_change->m_oldText;
const QString repl = QLatin1String("<b>") + match.toHtmlEscaped() + QLatin1String("</b>");
QString end = text().mid(m_change->m_range.end().column()).toHtmlEscaped();
const QString toolTip = QLatin1String("<span style=\"white-space:nowrap\">") + QString(start + repl + end).trimmed() + QLatin1String("</span>");
return toolTip;
}
} else if (role == Qt::FontRole) {
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
} else {
......@@ -260,29 +287,31 @@ QModelIndex GrepOutputModel::previousItemIndex(const QModelIndex &currentIdx) co
if (current_item->parent() != nullptr) {
int row = currentIdx.row();
if(!current_item->isText()) // the item is a file
{
int item_row = current_item->row();
if(item_row > 0)
if(item_row > 0) {
int last_line = current_item->parent()->child(item_row-1)->rowCount() - 1;
int last_item = current_item->parent()->child(item_row-1)->child(last_line)->rowCount() - 1;
return current_item->parent()->child(item_row-1)->child(last_line)->child(last_item)->index();
}
}
else if (current_item->hasChildren())//the item is a line
{
int idx_line = current_item->row();
if (idx_line > 0)
{
int idx_last_item = current_item->parent()->child(item_row - 1)->rowCount() - 1;
return current_item->parent()->child(item_row - 1)->child(idx_last_item)->index();
int last_item = current_item->parent()->child(idx_line - 1)->rowCount() - 1;
return current_item->parent()->child(idx_line - 1)->child(last_item)->index();
}
return previousItemIndex(current_item->parent()->index());
}
else // the item is a match
{
if(row > 0)
if(row > 0) {
return current_item->parent()->child(row - 1)->index();
else // we return the index of the last item of the previous file
{
int parrent_row = current_item->parent()->row();
if(parrent_row > 0)
{
int idx_last_item = current_item->parent()->parent()->child(parrent_row - 1)->rowCount() - 1;
return current_item->parent()->parent()->child(parrent_row - 1)->child(idx_last_item)->index();
}
}
return previousItemIndex(current_item->parent()->index());
}
}
return currentIdx;
......@@ -313,19 +342,27 @@ QModelIndex GrepOutputModel::nextItemIndex(const QModelIndex &currentIdx) const
int item_row = current_item->row();
if(item_row < current_item->parent()->rowCount())
{
return current_item->parent()->child(item_row)->child(0)->index();
return current_item->parent()->child(item_row)->child(0)->child(0)->index();
}
}
else if(current_item->hasChildren()) //the item is a line
{
return current_item->child(0)->index();
}
else // the item is a match
{
if(row < current_item->parent()->rowCount() - 1)
return current_item->parent()->child(row + 1)->index();
else if(current_item->parent()->row() < current_item->parent()->parent()->rowCount() - 1) //next line
{
return current_item->parent()->parent()->child(current_item->parent()->row() + 1)->child(0)->index();
}
else // we return the index of the first item of the next file
{
int parrent_row = current_item->parent()->row();
int parrent_row = current_item->parent()->parent()->row();
if(parrent_row < current_item->parent()->parent()->rowCount() - 1)
{
return current_item->parent()->parent()->child(parrent_row + 1)->child(0)->index();
return current_item->parent()->parent()->parent()->child(parrent_row + 1)->child(0)->child(0)->index();
}
}
}
......@@ -388,6 +425,7 @@ void GrepOutputModel::appendOutputs( const QString &filename, const GrepOutputIt
auto *fileItem = new GrepOutputItem(filename, fnString, m_itemsCheckable);
m_rootItem->appendRow(fileItem);
GrepOutputItem *lineItem = new GrepOutputItem(filename, items[0].text(), items[0].change()->m_range.start().line(), m_itemsCheckable);
for (const GrepOutputItem& item : items) {
auto* copy = new GrepOutputItem(item);
copy->setCheckable(m_itemsCheckable);
......@@ -397,9 +435,13 @@ void GrepOutputModel::appendOutputs( const QString &filename, const GrepOutputIt
if(copy->rowCount())
copy->setAutoTristate(true);
}
fileItem->appendRow(copy);
if (copy->change()->m_range.start().line() != lineItem->change()->m_range.start().line()) {
fileItem->appendRow(lineItem);
lineItem = new GrepOutputItem(filename, copy->text(), copy->change()->m_range.start().line(), m_itemsCheckable);
}
lineItem->appendRow(copy);
}
fileItem->appendRow(lineItem);
}
void GrepOutputModel::updateCheckState(QStandardItem* item)
......@@ -430,19 +472,22 @@ void GrepOutputModel::doReplacements()
for(int fileRow = 0; fileRow < m_rootItem->rowCount(); fileRow++)
{
auto *file = static_cast<GrepOutputItem *>(m_rootItem->child(fileRow));
for(int matchRow = 0; matchRow < file->rowCount(); matchRow++)
for(int lineRow = 0; lineRow < file->rowCount(); lineRow++)
{
auto *match = static_cast<GrepOutputItem *>(file->child(matchRow));
if(match->checkState() == Qt::Checked)
auto *line = static_cast<GrepOutputItem*>(file->child(lineRow));
for(int matchRow = 0; matchRow < line->rowCount(); matchRow++)
{
DocumentChangePointer change = match->change();
// setting replacement text based on current replace value
change->m_newText = replacementFor(change->m_oldText);
changeSet.addChange(change);
// this item cannot be checked anymore
match->setCheckState(Qt::Unchecked);
match->setEnabled(false);
auto *match = static_cast<GrepOutputItem *>(line->child(matchRow));
if(match->checkState() == Qt::Checked)
{
DocumentChangePointer change = match->change();
// setting replacement text based on current replace value
change->m_newText = replacementFor(change->m_oldText);
changeSet.addChange(change);
// this item cannot be checked anymore
match->setCheckState(Qt::Unchecked);
match->setEnabled(false);
}
}
}
}
......
......@@ -34,6 +34,7 @@ public:
GrepOutputItem(const KDevelop::DocumentChangePointer& change, const QString& text, bool checkable);
GrepOutputItem(const QString &filename, const QString &text, bool checkable);
GrepOutputItem(const QString &filename, const QString &text, int line, bool checkable);
~GrepOutputItem() override;
QString filename() const ;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment