From 8383da5e74eda1e64115fcfd3cbb5e1b964b5374 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 1 Sep 2023 19:44:18 -0400 Subject: [PATCH] create individual completer classes for different styles, use macros --- tools/lammps-gui/codeeditor.cpp | 161 ++++++++++++++++++++++---------- tools/lammps-gui/codeeditor.h | 26 +++++- tools/lammps-gui/lammpsgui.cpp | 43 +++++++-- 3 files changed, 174 insertions(+), 56 deletions(-) diff --git a/tools/lammps-gui/codeeditor.cpp b/tools/lammps-gui/codeeditor.cpp index ac79562e33..3c5b76afa4 100644 --- a/tools/lammps-gui/codeeditor.cpp +++ b/tools/lammps-gui/codeeditor.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -119,11 +121,44 @@ static std::vector split_line(const std::string &text) } CodeEditor::CodeEditor(QWidget *parent) : - QPlainTextEdit(parent), command_completer(nullptr), highlight(NO_HIGHLIGHT) + QPlainTextEdit(parent), command_comp(new QCompleter(this)), fix_comp(new QCompleter(this)), + compute_comp(new QCompleter(this)), dump_comp(new QCompleter(this)), + atom_comp(new QCompleter(this)), pair_comp(new QCompleter(this)), + bond_comp(new QCompleter(this)), angle_comp(new QCompleter(this)), + dihedral_comp(new QCompleter(this)), improper_comp(new QCompleter(this)), + kspace_comp(new QCompleter(this)), region_comp(new QCompleter(this)), + integrate_comp(new QCompleter(this)), minimize_comp(new QCompleter(this)), + highlight(NO_HIGHLIGHT) { help_action = new QShortcut(QKeySequence::fromString("Ctrl+?"), parent); connect(help_action, &QShortcut::activated, this, &CodeEditor::get_help); + // set up completer class (without a model currently) +#define COMPLETER_SETUP(completer) \ + completer->setCompletionMode(QCompleter::PopupCompletion); \ + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); \ + completer->setWidget(this); \ + completer->setWrapAround(false); \ + QObject::connect(completer, QOverload::of(&QCompleter::activated), this, \ + &CodeEditor::insertCompletedCommand) + + COMPLETER_SETUP(command_comp); + COMPLETER_SETUP(fix_comp); + COMPLETER_SETUP(compute_comp); + COMPLETER_SETUP(dump_comp); + COMPLETER_SETUP(atom_comp); + COMPLETER_SETUP(pair_comp); + COMPLETER_SETUP(bond_comp); + COMPLETER_SETUP(angle_comp); + COMPLETER_SETUP(dihedral_comp); + COMPLETER_SETUP(improper_comp); + COMPLETER_SETUP(kspace_comp); + COMPLETER_SETUP(region_comp); + COMPLETER_SETUP(integrate_comp); + COMPLETER_SETUP(minimize_comp); + +#undef COMPLETER_SETUP + // initialize help system QFile help_index(":/help_index.table"); if (help_index.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -165,6 +200,26 @@ CodeEditor::CodeEditor(QWidget *parent) : setCursorWidth(2); } +CodeEditor::~CodeEditor() +{ + delete help_action; + delete lineNumberArea; + + delete command_comp; + delete fix_comp; + delete compute_comp; + delete atom_comp; + delete pair_comp; + delete bond_comp; + delete angle_comp; + delete dihedral_comp; + delete improper_comp; + delete kspace_comp; + delete region_comp; + delete integrate_comp; + delete minimize_comp; +} + int CodeEditor::lineNumberAreaWidth() { int digits = 1; @@ -175,7 +230,6 @@ int CodeEditor::lineNumberAreaWidth() } int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * (digits + 2); - return space; } @@ -273,25 +327,33 @@ QString CodeEditor::reformatLine(const QString &line) return newtext; } -void CodeEditor::setCommandList(const QStringList &words) -{ - delete command_completer; - command_completer = new QCompleter(this); - command_completer->setModel(new QStringListModel(words, command_completer)); - command_completer->setCompletionMode(QCompleter::PopupCompletion); - command_completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); - command_completer->setWidget(this); - command_completer->setWrapAround(false); +#define COMPLETER_INIT_FUNC(keyword, Type) \ + void CodeEditor::set##Type##List(const QStringList &words) \ + { \ + keyword##_comp->setModel(new QStringListModel(words, keyword##_comp)); \ + } - QObject::connect(command_completer, QOverload::of(&QCompleter::activated), - this, &CodeEditor::insertCompletedCommand); - reformatCurrentLine(); -} +COMPLETER_INIT_FUNC(command, Command) +COMPLETER_INIT_FUNC(fix, Fix) +COMPLETER_INIT_FUNC(compute, Compute) +COMPLETER_INIT_FUNC(dump, Dump) +COMPLETER_INIT_FUNC(atom, Atom) +COMPLETER_INIT_FUNC(pair, Pair) +COMPLETER_INIT_FUNC(bond, Bond) +COMPLETER_INIT_FUNC(angle, Angle) +COMPLETER_INIT_FUNC(dihedral, Dihedral) +COMPLETER_INIT_FUNC(improper, Improper) +COMPLETER_INIT_FUNC(kspace, Kspace) +COMPLETER_INIT_FUNC(region, Region) +COMPLETER_INIT_FUNC(integrate, Integrate) +COMPLETER_INIT_FUNC(minimize, Minimize) + +#undef COMPLETER_INIT_FUNC void CodeEditor::keyPressEvent(QKeyEvent *event) { const auto key = event->key(); - if (command_completer && command_completer->popup()->isVisible()) { + if (command_comp->popup()->isVisible()) { // The following keys are forwarded by the completer to the widget switch (key) { case Qt::Key_Enter: @@ -333,16 +395,23 @@ void CodeEditor::keyPressEvent(QKeyEvent *event) if (!line.isEmpty()) { auto words = split_line(line.toStdString()); cursor.select(QTextCursor::WordUnderCursor); - auto word = cursor.selectedText().trimmed(); - if (command_completer) { - if (words[0] == word.toStdString()) { + auto word = cursor.selectedText().trimmed(); + auto popup = command_comp->popup(); + // we're on the first word in a line -> complete on commands + if (words[0] == word.toStdString()) { + if (word.length() > 2) + runCompletion(); + else if (popup->isVisible()) + popup->hide(); + } else if (words[0] == "fix") { + if (words.size() > 2) { // fix style is 3rd word if (word.length() > 2) runCompletion(); - else if (command_completer->popup()->isVisible()) - command_completer->popup()->hide(); - } else { - if (command_completer->popup()->isVisible()) command_completer->popup()->hide(); + else if (popup->isVisible()) + popup->hide(); } + } else { + if (popup->isVisible()) popup->hide(); } } } @@ -493,40 +562,38 @@ void CodeEditor::reformatCurrentLine() void CodeEditor::runCompletion() { - if (command_completer) { - auto cursor = textCursor(); - auto line = cursor.block().text().trimmed(); - if (line.isEmpty()) return; + auto cursor = textCursor(); + auto line = cursor.block().text().trimmed(); + if (line.isEmpty()) return; - auto words = split_line(line.toStdString()); - cursor.select(QTextCursor::WordUnderCursor); + auto words = split_line(line.toStdString()); + cursor.select(QTextCursor::WordUnderCursor); - // if on first word, try to complete command - if ((words.size() > 0) && (words[0] == cursor.selectedText().toStdString())) { - // no completion on comment lines - if (words[0][0] == '#') return; + // if on first word, try to complete command + if ((words.size() > 0) && (words[0] == cursor.selectedText().toStdString())) { + // no completion on comment lines + if (words[0][0] == '#') return; - command_completer->setCompletionPrefix(words[0].c_str()); - auto popup = command_completer->popup(); - // if the command is already a complete command, remove existing popup - if (words[0] == command_completer->currentCompletion().toStdString()) { - if (popup->isVisible()) popup->hide(); - return; - } - QRect cr = cursorRect(); - cr.setWidth(popup->sizeHintForColumn(0) + - popup->verticalScrollBar()->sizeHint().width()); - popup->setAlternatingRowColors(true); - command_completer->complete(cr); + command_comp->setCompletionPrefix(words[0].c_str()); + auto popup = command_comp->popup(); + // if the command is already a complete command, remove existing popup + if (words[0] == command_comp->currentCompletion().toStdString()) { + if (popup->isVisible()) popup->hide(); + return; } + QRect cr = cursorRect(); + cr.setWidth(popup->sizeHintForColumn(0) + popup->verticalScrollBar()->sizeHint().width()); + popup->setAlternatingRowColors(true); + command_comp->complete(cr); } } void CodeEditor::insertCompletedCommand(const QString &completion) { - if (command_completer->widget() != this) return; + auto *completer = qobject_cast(sender()); + if (completer->widget() != this) return; auto cursor = textCursor(); - int extra = completion.length() - command_completer->completionPrefix().length(); + int extra = completion.length() - completer->completionPrefix().length(); cursor.movePosition(QTextCursor::Left); cursor.movePosition(QTextCursor::EndOfWord); cursor.insertText(completion.right(extra)); diff --git a/tools/lammps-gui/codeeditor.h b/tools/lammps-gui/codeeditor.h index bd2522de8e..d74110b949 100644 --- a/tools/lammps-gui/codeeditor.h +++ b/tools/lammps-gui/codeeditor.h @@ -14,19 +14,22 @@ #ifndef CODEEDITOR_H #define CODEEDITOR_H -#include #include #include #include -#include #include #include +class QCompleter; +class QStringListModel; +class QShortcut; + class CodeEditor : public QPlainTextEdit { Q_OBJECT public: CodeEditor(QWidget *parent = nullptr); + ~CodeEditor() override; void lineNumberAreaPaintEvent(QPaintEvent *event); int lineNumberAreaWidth(); @@ -36,7 +39,21 @@ public: void setReformatOnReturn(bool flag) { reformat_on_return = flag; } void setAutoComplete(bool flag) { automatic_completion = flag; } QString reformatLine(const QString &line); + void setCommandList(const QStringList &words); + void setFixList(const QStringList &words); + void setComputeList(const QStringList &words); + void setDumpList(const QStringList &words); + void setAtomList(const QStringList &words); + void setPairList(const QStringList &words); + void setBondList(const QStringList &words); + void setAngleList(const QStringList &words); + void setDihedralList(const QStringList &words); + void setImproperList(const QStringList &words); + void setKspaceList(const QStringList &words); + void setRegionList(const QStringList &words); + void setIntegrateList(const QStringList &words); + void setMinimizeList(const QStringList &words); static constexpr int NO_HIGHLIGHT = 1 << 30; @@ -61,7 +78,10 @@ private slots: private: QWidget *lineNumberArea; QShortcut *help_action; - QCompleter *command_completer; + QCompleter *command_comp, *fix_comp, *compute_comp, *dump_comp, *atom_comp, *pair_comp, + *bond_comp, *angle_comp, *dihedral_comp, *improper_comp, *kspace_comp, *region_comp, + *integrate_comp, *minimize_comp; + int highlight; bool reformat_on_return; bool automatic_completion; diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index da2898c834..1566971017 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -302,21 +302,52 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : // start LAMMPS and initialize command completion start_lammps(); - QStringList command_list; + QStringList style_list; + char buf[BUFLEN]; QFile internal_commands(":/lammps_internal_commands.txt"); if (internal_commands.open(QIODevice::ReadOnly | QIODevice::Text)) { while (!internal_commands.atEnd()) { - command_list << QString(internal_commands.readLine()).trimmed(); + style_list << QString(internal_commands.readLine()).trimmed(); } } internal_commands.close(); int ncmds = lammps.style_count("command"); - char buf[BUFLEN]; for (int i = 0; i < ncmds; ++i) { - if (lammps.style_name("command", i, buf, BUFLEN)) command_list << buf; + if (lammps.style_name("command", i, buf, BUFLEN)) { + if (strstr(buf, "/kk/host") || strstr(buf, "/kk/device")) continue; + style_list << buf; + } } - command_list.sort(); - ui->textEdit->setCommandList(command_list); + style_list.sort(); + ui->textEdit->setCommandList(style_list); + +#define ADD_STYLES(keyword, Type) \ + style_list.clear(); \ + ncmds = lammps.style_count(#keyword); \ + for (int i = 0; i < ncmds; ++i) { \ + if (lammps.style_name(#keyword, i, buf, BUFLEN)) { \ + if (strstr(buf, "/kk/host") || strstr(buf, "/kk/device")) continue; \ + style_list << buf; \ + } \ + } \ + style_list.sort(); \ + ui->textEdit->set##Type##List(style_list) + + ADD_STYLES(fix, Fix); + ADD_STYLES(compute, Compute); + ADD_STYLES(dump, Dump); + ADD_STYLES(atom, Atom); + ADD_STYLES(pair, Pair); + ADD_STYLES(bond, Bond); + ADD_STYLES(angle, Angle); + ADD_STYLES(dihedral, Dihedral); + ADD_STYLES(improper, Improper); + ADD_STYLES(kspace, Kspace); + ADD_STYLES(region, Region); + ADD_STYLES(integrate, Integrate); + ADD_STYLES(minimize, Minimize); +#undef ADD_STYLES + settings.beginGroup("reformat"); ui->textEdit->setReformatOnReturn(settings.value("return", true).toBool()); ui->textEdit->setAutoComplete(settings.value("automatic", true).toBool());