diff --git a/tools/lammps-gui/codeeditor.cpp b/tools/lammps-gui/codeeditor.cpp index 0d20b5bdf0..256f496dff 100644 --- a/tools/lammps-gui/codeeditor.cpp +++ b/tools/lammps-gui/codeeditor.cpp @@ -121,13 +121,14 @@ static std::vector split_line(const std::string &text) } CodeEditor::CodeEditor(QWidget *parent) : - 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)), + QPlainTextEdit(parent), current_comp(nullptr), 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)), variable_comp(new QCompleter(this)), highlight(NO_HIGHLIGHT) { help_action = new QShortcut(QKeySequence::fromString("Ctrl+?"), parent); @@ -138,6 +139,7 @@ CodeEditor::CodeEditor(QWidget *parent) : completer->setCompletionMode(QCompleter::PopupCompletion); \ completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); \ completer->setWidget(this); \ + completer->setMaxVisibleItems(16); \ completer->setWrapAround(false); \ QObject::connect(completer, QOverload::of(&QCompleter::activated), this, \ &CodeEditor::insertCompletedCommand) @@ -156,7 +158,7 @@ CodeEditor::CodeEditor(QWidget *parent) : COMPLETER_SETUP(region_comp); COMPLETER_SETUP(integrate_comp); COMPLETER_SETUP(minimize_comp); - + COMPLETER_SETUP(variable_comp); #undef COMPLETER_SETUP // initialize help system @@ -218,6 +220,7 @@ CodeEditor::~CodeEditor() delete region_comp; delete integrate_comp; delete minimize_comp; + delete variable_comp; } int CodeEditor::lineNumberAreaWidth() @@ -347,13 +350,14 @@ COMPLETER_INIT_FUNC(kspace, Kspace) COMPLETER_INIT_FUNC(region, Region) COMPLETER_INIT_FUNC(integrate, Integrate) COMPLETER_INIT_FUNC(minimize, Minimize) +COMPLETER_INIT_FUNC(variable, Variable) #undef COMPLETER_INIT_FUNC void CodeEditor::keyPressEvent(QKeyEvent *event) { const auto key = event->key(); - if (command_comp->popup()->isVisible()) { + if (current_comp && current_comp->popup()->isVisible()) { // The following keys are forwarded by the completer to the widget switch (key) { case Qt::Key_Enter: @@ -385,15 +389,22 @@ void CodeEditor::keyPressEvent(QKeyEvent *event) reformatCurrentLine(); } - // pass key event on to parent class + // pass key event to parent class QPlainTextEdit::keyPressEvent(event); - // pop up completion automatically after 3 characters + // if enabled, do pop up completion automatically after 3 characters if (automatic_completion) { auto cursor = textCursor(); - cursor.select(QTextCursor::WordUnderCursor); - auto word = cursor.selectedText().trimmed(); - if (word.length() > 2) runCompletion(); + auto line = cursor.block().text(); + + // QTextCursor::WordUnderCursor recognizes '/' as word boundary. + // Work around it by search to beginning of word since we only need the length + int begin = cursor.positionInBlock(); + while (begin >= 0) { + if (line[begin].isSpace()) break; + --begin; + } + if ((cursor.positionInBlock() - begin) > 2) runCompletion(); } } @@ -547,6 +558,7 @@ void CodeEditor::runCompletion() if (line.isEmpty()) return; auto words = split_line(line.toStdString()); + // FIXME. need a workaround for broken WordUnderCursor here cursor.select(QTextCursor::WordUnderCursor); // if on first word, try to complete command @@ -554,17 +566,113 @@ void CodeEditor::runCompletion() // no completion on comment lines if (words[0][0] == '#') return; - command_comp->setCompletionPrefix(words[0].c_str()); - auto popup = command_comp->popup(); + current_comp = command_comp; + current_comp->setCompletionPrefix(words[0].c_str()); + auto popup = current_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(); + if (words[0] == current_comp->currentCompletion().toStdString()) { + if (popup->isVisible()) { + popup->hide(); + current_comp = nullptr; + } return; } QRect cr = cursorRect(); cr.setWidth(popup->sizeHintForColumn(0) + popup->verticalScrollBar()->sizeHint().width()); popup->setAlternatingRowColors(true); - command_comp->complete(cr); + current_comp->complete(cr); + + // completions for second word + } else if ((words.size() > 1) && (words[1] == cursor.selectedText().toStdString())) { + // no completion on comment lines + if (words[0][0] == '#') return; + + current_comp = nullptr; + if (words[0] == "pair_style") + current_comp = pair_comp; + else if (words[0] == "bond_style") + current_comp = bond_comp; + else if (words[0] == "angle_style") + current_comp = angle_comp; + else if (words[0] == "dihedral_style") + current_comp = dihedral_comp; + else if (words[0] == "improper_style") + current_comp = improper_comp; + else if (words[0] == "kspace_style") + current_comp = kspace_comp; + else if (words[0] == "atom_style") + current_comp = atom_comp; + else if (words[0] == "run_style") + current_comp = integrate_comp; + else if (words[0] == "minimize_style") + current_comp = minimize_comp; + + if (current_comp) { + current_comp->setCompletionPrefix(words[1].c_str()); + auto popup = current_comp->popup(); + // if the command is already a complete command, remove existing popup + if (words[1] == current_comp->currentCompletion().toStdString()) { + if (popup->isVisible()) popup->hide(); + return; + } + QRect cr = cursorRect(); + cr.setWidth(popup->sizeHintForColumn(0) + + popup->verticalScrollBar()->sizeHint().width()); + popup->setAlternatingRowColors(true); + current_comp->complete(cr); + } + // completions for third word + } else if ((words.size() > 2) && (words[2] == cursor.selectedText().toStdString())) { + // no completion on comment lines + if (words[0][0] == '#') return; + + current_comp = nullptr; + if (words[0] == "region") + current_comp = region_comp; + else if (words[0] == "variable") + current_comp = variable_comp; + + if (current_comp) { + current_comp->setCompletionPrefix(words[2].c_str()); + auto popup = current_comp->popup(); + // if the command is already a complete command, remove existing popup + if (words[2] == current_comp->currentCompletion().toStdString()) { + if (popup->isVisible()) popup->hide(); + return; + } + QRect cr = cursorRect(); + cr.setWidth(popup->sizeHintForColumn(0) + + popup->verticalScrollBar()->sizeHint().width()); + popup->setAlternatingRowColors(true); + current_comp->complete(cr); + } + // completions for fourth word + } else if ((words.size() > 3) && (words[3] == cursor.selectedText().toStdString())) { + // no completion on comment lines + if (words[0][0] == '#') return; + + current_comp = nullptr; + if (words[0] == "fix") + current_comp = fix_comp; + else if (words[0] == "compute") + current_comp = compute_comp; + else if (words[0] == "dump") + current_comp = dump_comp; + + if (current_comp) { + current_comp->setCompletionPrefix(words[3].c_str()); + auto popup = current_comp->popup(); + // if the command is already a complete command, remove existing popup + if (words[3] == current_comp->currentCompletion().toStdString()) { + if (popup->isVisible()) popup->hide(); + return; + } + QRect cr = cursorRect(); + cr.setWidth(popup->sizeHintForColumn(0) + + popup->verticalScrollBar()->sizeHint().width()); + popup->setAlternatingRowColors(true); + current_comp->complete(cr); + } } } diff --git a/tools/lammps-gui/codeeditor.h b/tools/lammps-gui/codeeditor.h index d74110b949..1065b4785c 100644 --- a/tools/lammps-gui/codeeditor.h +++ b/tools/lammps-gui/codeeditor.h @@ -54,6 +54,7 @@ public: void setRegionList(const QStringList &words); void setIntegrateList(const QStringList &words); void setMinimizeList(const QStringList &words); + void setVariableList(const QStringList &words); static constexpr int NO_HIGHLIGHT = 1 << 30; @@ -78,9 +79,9 @@ private slots: private: QWidget *lineNumberArea; QShortcut *help_action; - 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; + QCompleter *current_comp, *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, *variable_comp; int highlight; bool reformat_on_return; diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index 1566971017..23674067d6 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -314,23 +314,39 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : int ncmds = lammps.style_count("command"); for (int i = 0; i < ncmds; ++i) { if (lammps.style_name("command", i, buf, BUFLEN)) { - if (strstr(buf, "/kk/host") || strstr(buf, "/kk/device")) continue; - style_list << buf; + // skip suffixed names + const QString style(buf); + if (style.endsWith("/kk/host") || style.endsWith("/kk/device") || style.endsWith("/kk")) + continue; + style_list << style; } } 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(); \ + style_list.clear(); + const char *varstyles[] = {"delete", "atomfile", "file", "format", "getenv", "index", + "internal", "loop", "python", "string", "timer", "uloop", + "universe", "world", "equal", "vector", "atom"}; + for (const auto var : varstyles) + style_list << var; + style_list.sort(); + ui->textEdit->setVariableList(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)) { \ + const QString style(buf); \ + if (style.endsWith("/gpu") || style.endsWith("/intel") || style.endsWith("/kk") || \ + style.endsWith("/kk/device") || style.endsWith("/kk/host") || \ + style.endsWith("/omp") || style.endsWith("/opt")) \ + continue; \ + style_list << style; \ + } \ + } \ + style_list.sort(); \ ui->textEdit->set##Type##List(style_list) ADD_STYLES(fix, Fix);