Regular Expression example: Brush up and add a way to preview replacements

- Set a fixed font on text edits so that parentheses stand out
- Rearrange the layout to have the text at the top be prevent
  long texts from being wrapped
- Add a replacement field where one can exercise replacement
  with the \1, \2... placeholders.

Change-Id: I140a62e1fb2cd1c6bfe02a2f01b7f06a6f3b5eb2
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
(cherry picked from commit 6ac77e81aec0b48a76b385092d9050941fa0e257)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Friedemann Kleint 2021-04-28 13:26:35 +02:00 committed by Qt Cherry-pick Bot
parent 07ff2fcf41
commit a3b1f4e463
3 changed files with 100 additions and 28 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -66,12 +66,13 @@
#include <QAction> #include <QAction>
#include <QClipboard> #include <QClipboard>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QFont>
#include <QFontDatabase>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QGridLayout> #include <QGridLayout>
#include <QFormLayout> #include <QFormLayout>
#include <QRegularExpression>
#include <QRegularExpressionMatch> #include <QRegularExpressionMatch>
#include <QRegularExpressionMatchIterator> #include <QRegularExpressionMatchIterator>
@ -106,6 +107,20 @@ static QString codeToPattern(QString code)
return code; return code;
} }
static QFrame *createHorizontalSeparator()
{
auto *result = new QFrame;
result->setFrameStyle(QFrame::HLine | QFrame::Sunken);
return result;
}
static QFrame *createVerticalSeparator()
{
auto *result = new QFrame;
result->setFrameStyle(QFrame::VLine | QFrame::Sunken);
return result;
}
class PatternLineEdit : public QLineEdit class PatternLineEdit : public QLineEdit
{ {
Q_OBJECT Q_OBJECT
@ -239,6 +254,7 @@ void RegularExpressionDialog::setResultUiEnabled(bool enabled)
{ {
matchDetailsTreeWidget->setEnabled(enabled); matchDetailsTreeWidget->setEnabled(enabled);
namedGroupsTreeWidget->setEnabled(enabled); namedGroupsTreeWidget->setEnabled(enabled);
replacementTextEdit->setEnabled(enabled);
} }
static void setTextColor(QWidget *widget, const QColor &color) static void setTextColor(QWidget *widget, const QColor &color)
@ -264,6 +280,7 @@ void RegularExpressionDialog::refresh()
matchDetailsTreeWidget->clear(); matchDetailsTreeWidget->clear();
namedGroupsTreeWidget->clear(); namedGroupsTreeWidget->clear();
regexpStatusLabel->setText(QString()); regexpStatusLabel->setText(QString());
replacementTextEdit->clear();
if (pattern.isEmpty()) { if (pattern.isEmpty()) {
setResultUiEnabled(false); setResultUiEnabled(false);
@ -271,12 +288,12 @@ void RegularExpressionDialog::refresh()
return; return;
} }
QRegularExpression rx(pattern); regularExpression.setPattern(pattern);
if (!rx.isValid()) { if (!regularExpression.isValid()) {
setTextColor(patternLineEdit, Qt::red); setTextColor(patternLineEdit, Qt::red);
regexpStatusLabel->setText(tr("Invalid: syntax error at position %1 (%2)") regexpStatusLabel->setText(tr("Invalid: syntax error at position %1 (%2)")
.arg(rx.patternErrorOffset()) .arg(regularExpression.patternErrorOffset())
.arg(rx.errorString())); .arg(regularExpression.errorString()));
setResultUiEnabled(false); setResultUiEnabled(false);
setUpdatesEnabled(true); setUpdatesEnabled(true);
return; return;
@ -308,11 +325,13 @@ void RegularExpressionDialog::refresh()
if (useUnicodePropertiesOptionCheckBox->isChecked()) if (useUnicodePropertiesOptionCheckBox->isChecked())
patternOptions |= QRegularExpression::UseUnicodePropertiesOption; patternOptions |= QRegularExpression::UseUnicodePropertiesOption;
rx.setPatternOptions(patternOptions); regularExpression.setPatternOptions(patternOptions);
const int capturingGroupsCount = rx.captureCount() + 1; const int capturingGroupsCount = regularExpression.captureCount() + 1;
QRegularExpressionMatchIterator iterator = rx.globalMatch(text, offsetSpinBox->value(), matchType, matchOptions); const int offset = offsetSpinBox->value();
QRegularExpressionMatchIterator iterator =
regularExpression.globalMatch(text, offset, matchType, matchOptions);
int i = 0; int i = 0;
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -334,7 +353,7 @@ void RegularExpressionDialog::refresh()
regexpStatusLabel->setText(tr("Valid")); regexpStatusLabel->setText(tr("Valid"));
const QStringList namedCaptureGroups = rx.namedCaptureGroups(); const QStringList namedCaptureGroups = regularExpression.namedCaptureGroups();
for (int i = 0; i < namedCaptureGroups.size(); ++i) { for (int i = 0; i < namedCaptureGroups.size(); ++i) {
const QString currentNamedCaptureGroup = namedCaptureGroups.at(i); const QString currentNamedCaptureGroup = namedCaptureGroups.at(i);
@ -343,28 +362,44 @@ void RegularExpressionDialog::refresh()
namedGroupItem->setText(1, currentNamedCaptureGroup.isNull() ? tr("<no name>") : currentNamedCaptureGroup); namedGroupItem->setText(1, currentNamedCaptureGroup.isNull() ? tr("<no name>") : currentNamedCaptureGroup);
} }
updateReplacement();
setUpdatesEnabled(true); setUpdatesEnabled(true);
} }
void RegularExpressionDialog::setupUi() void RegularExpressionDialog::updateReplacement()
{ {
QWidget *leftHalfContainer = setupLeftUi(); replacementTextEdit->clear();
const QString &replacement = replacementLineEdit->text();
if (!regularExpression.isValid() || replacement.isEmpty())
return;
QFrame *verticalSeparator = new QFrame; QString replaced = subjectTextEdit->toPlainText();
verticalSeparator->setFrameStyle(QFrame::VLine | QFrame::Sunken); replaced.replace(regularExpression, replacement);
replacementTextEdit->setPlainText(replaced);
QWidget *rightHalfContainer = setupRightUi();
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(leftHalfContainer);
mainLayout->addWidget(verticalSeparator);
mainLayout->addWidget(rightHalfContainer);
setLayout(mainLayout);
} }
QWidget *RegularExpressionDialog::setupLeftUi() void RegularExpressionDialog::setupUi()
{
auto *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(setupTextUi());
mainLayout->addWidget(createHorizontalSeparator());
auto *horizontalLayout = new QHBoxLayout();
mainLayout->addLayout(horizontalLayout);
horizontalLayout->addWidget(setupOptionsUi());
horizontalLayout->addWidget(createVerticalSeparator());
horizontalLayout->addWidget(setupInfoUi());
auto font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
patternLineEdit->setFont(font);
rawStringLiteralLineEdit->setFont(font);
escapedPatternLineEdit->setFont(font);
replacementLineEdit->setFont(font);
subjectTextEdit->setFont(font);
replacementTextEdit->setFont(font);
}
QWidget *RegularExpressionDialog::setupTextUi()
{ {
QWidget *container = new QWidget; QWidget *container = new QWidget;
@ -387,6 +422,35 @@ QWidget *RegularExpressionDialog::setupLeftUi()
subjectTextEdit = new QPlainTextEdit; subjectTextEdit = new QPlainTextEdit;
layout->addRow(tr("&Subject text:"), subjectTextEdit); layout->addRow(tr("&Subject text:"), subjectTextEdit);
layout->addRow(createHorizontalSeparator());
QLabel *replaceLabel = new QLabel(tr("<h3>Replacement"));
layout->addRow(replaceLabel);
replacementLineEdit = new QLineEdit;
replacementLineEdit->setClearButtonEnabled(true);
connect(replacementLineEdit, &QLineEdit::textChanged, this,
&RegularExpressionDialog::updateReplacement);
layout->addRow(tr("&Replace by:"), replacementLineEdit);
replacementLineEdit->setToolTip(tr("Use \\1, \\2... as placeholders for the captured groups."));
replacementTextEdit = new QPlainTextEdit;
replacementTextEdit->setReadOnly(true);
layout->addRow(tr("Result:"), replacementTextEdit);
return container;
}
QWidget *RegularExpressionDialog::setupOptionsUi()
{
QWidget *container = new QWidget;
QFormLayout *layout = new QFormLayout(container);
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
layout->setContentsMargins(QMargins());
layout->addRow(new QLabel(tr("<h3>Options</h3>")));
caseInsensitiveOptionCheckBox = new QCheckBox(tr("Case insensitive (/i)")); caseInsensitiveOptionCheckBox = new QCheckBox(tr("Case insensitive (/i)"));
dotMatchesEverythingOptionCheckBox = new QCheckBox(tr("Dot matches everything (/s)")); dotMatchesEverythingOptionCheckBox = new QCheckBox(tr("Dot matches everything (/s)"));
multilineOptionCheckBox = new QCheckBox(tr("Multiline (/m)")); multilineOptionCheckBox = new QCheckBox(tr("Multiline (/m)"));
@ -431,7 +495,7 @@ QWidget *RegularExpressionDialog::setupLeftUi()
return container; return container;
} }
QWidget *RegularExpressionDialog::setupRightUi() QWidget *RegularExpressionDialog::setupInfoUi()
{ {
QWidget *container = new QWidget; QWidget *container = new QWidget;
@ -447,9 +511,7 @@ QWidget *RegularExpressionDialog::setupRightUi()
matchDetailsTreeWidget->setSizeAdjustPolicy(QTreeWidget::AdjustToContents); matchDetailsTreeWidget->setSizeAdjustPolicy(QTreeWidget::AdjustToContents);
layout->addRow(tr("Match details:"), matchDetailsTreeWidget); layout->addRow(tr("Match details:"), matchDetailsTreeWidget);
QFrame *horizontalSeparator = new QFrame; layout->addRow(createHorizontalSeparator());
horizontalSeparator->setFrameStyle(QFrame::HLine | QFrame::Sunken);
layout->addRow(horizontalSeparator);
QLabel *regexpInfoLabel = new QLabel(tr("<h3>Regular expression information</h3>")); QLabel *regexpInfoLabel = new QLabel(tr("<h3>Regular expression information</h3>"));
layout->addRow(regexpInfoLabel); layout->addRow(regexpInfoLabel);

View File

@ -54,6 +54,7 @@
#define REGULAREXPRESSIONDIALOG_H #define REGULAREXPRESSIONDIALOG_H
#include <QDialog> #include <QDialog>
#include <QRegularExpression>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QCheckBox; class QCheckBox;
@ -72,18 +73,25 @@ class RegularExpressionDialog : public QDialog
public: public:
RegularExpressionDialog(QWidget *parent = nullptr); RegularExpressionDialog(QWidget *parent = nullptr);
private slots:
void updateReplacement();
private: private:
void refresh(); void refresh();
void setupUi(); void setupUi();
QWidget *setupLeftUi(); QWidget *setupLeftUi();
QWidget *setupRightUi(); QWidget *setupTextUi();
QWidget *setupOptionsUi();
QWidget *setupInfoUi();
void setResultUiEnabled(bool enabled); void setResultUiEnabled(bool enabled);
QLineEdit *patternLineEdit; QLineEdit *patternLineEdit;
QLineEdit *rawStringLiteralLineEdit; QLineEdit *rawStringLiteralLineEdit;
QLineEdit *escapedPatternLineEdit; QLineEdit *escapedPatternLineEdit;
QLineEdit *replacementLineEdit;
QPlainTextEdit *subjectTextEdit; QPlainTextEdit *subjectTextEdit;
QPlainTextEdit *replacementTextEdit;
QCheckBox *caseInsensitiveOptionCheckBox; QCheckBox *caseInsensitiveOptionCheckBox;
QCheckBox *dotMatchesEverythingOptionCheckBox; QCheckBox *dotMatchesEverythingOptionCheckBox;
@ -104,6 +112,8 @@ private:
QLabel *regexpStatusLabel; QLabel *regexpStatusLabel;
QTreeWidget *namedGroupsTreeWidget; QTreeWidget *namedGroupsTreeWidget;
QRegularExpression regularExpression;
}; };
#endif #endif