implement command completion popup
This commit is contained in:
@ -15,6 +15,7 @@
|
|||||||
#include "lammpsgui.h"
|
#include "lammpsgui.h"
|
||||||
#include "linenumberarea.h"
|
#include "linenumberarea.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemView>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QDragEnterEvent>
|
#include <QDragEnterEvent>
|
||||||
@ -25,7 +26,9 @@
|
|||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QScrollBar>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QStringListModel>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
@ -42,8 +45,8 @@ static std::vector<std::string> split_line(const std::string &text)
|
|||||||
std::size_t beg = 0;
|
std::size_t beg = 0;
|
||||||
std::size_t len = 0;
|
std::size_t len = 0;
|
||||||
std::size_t add = 0;
|
std::size_t add = 0;
|
||||||
char c = *buf;
|
|
||||||
|
|
||||||
|
char c = *buf;
|
||||||
while (c) { // leading whitespace
|
while (c) { // leading whitespace
|
||||||
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f') {
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f') {
|
||||||
c = *++buf;
|
c = *++buf;
|
||||||
@ -115,7 +118,8 @@ static std::vector<std::string> split_line(const std::string &text)
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent), highlight(NO_HIGHLIGHT)
|
CodeEditor::CodeEditor(QWidget *parent) :
|
||||||
|
QPlainTextEdit(parent), command_completer(nullptr), highlight(NO_HIGHLIGHT)
|
||||||
{
|
{
|
||||||
help_action = new QShortcut(QKeySequence::fromString("Ctrl+?"), parent);
|
help_action = new QShortcut(QKeySequence::fromString("Ctrl+?"), parent);
|
||||||
connect(help_action, &QShortcut::activated, this, &CodeEditor::get_help);
|
connect(help_action, &QShortcut::activated, this, &CodeEditor::get_help);
|
||||||
@ -269,14 +273,64 @@ QString CodeEditor::reformatLine(const QString &line)
|
|||||||
return newtext;
|
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);
|
||||||
|
|
||||||
|
QObject::connect(command_completer, QOverload<const QString &>::of(&QCompleter::activated),
|
||||||
|
this, &CodeEditor::insertCompletedCommand);
|
||||||
|
reformatCurrentLine();
|
||||||
|
}
|
||||||
|
|
||||||
void CodeEditor::keyPressEvent(QKeyEvent *event)
|
void CodeEditor::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
|
if (command_completer && command_completer->popup()->isVisible()) {
|
||||||
|
// The following keys are forwarded by the completer to the widget
|
||||||
|
switch (event->key()) {
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
case Qt::Key_Return:
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
case Qt::Key_Backtab:
|
||||||
|
event->ignore();
|
||||||
|
return; // let the completer do default behavior
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (event->key() == Qt::Key_Tab) {
|
if (event->key() == Qt::Key_Tab) {
|
||||||
reformatCurrentLine();
|
reformatCurrentLine();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->key() == Qt::Key_Backtab) {
|
if (event->key() == Qt::Key_Backtab) {
|
||||||
fprintf(stderr, "Shift + Tab key hit\n");
|
if (command_completer) {
|
||||||
|
auto cursor = textCursor();
|
||||||
|
auto line = cursor.block().text().trimmed();
|
||||||
|
if (line.isEmpty()) return;
|
||||||
|
|
||||||
|
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;
|
||||||
|
command_completer->setCompletionPrefix(words[0].c_str());
|
||||||
|
auto popup = command_completer->popup();
|
||||||
|
QRect cr = cursorRect();
|
||||||
|
cr.setWidth(popup->sizeHintForColumn(0) +
|
||||||
|
popup->verticalScrollBar()->sizeHint().width());
|
||||||
|
popup->setAlternatingRowColors(true);
|
||||||
|
command_completer->setCurrentRow(0);
|
||||||
|
command_completer->complete(cr);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QPlainTextEdit::keyPressEvent(event);
|
QPlainTextEdit::keyPressEvent(event);
|
||||||
@ -425,6 +479,17 @@ void CodeEditor::reformatCurrentLine()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CodeEditor::insertCompletedCommand(const QString &completion)
|
||||||
|
{
|
||||||
|
if (command_completer->widget() != this) return;
|
||||||
|
auto cursor = textCursor();
|
||||||
|
int extra = completion.length() - command_completer->completionPrefix().length();
|
||||||
|
cursor.movePosition(QTextCursor::Left);
|
||||||
|
cursor.movePosition(QTextCursor::EndOfWord);
|
||||||
|
cursor.insertText(completion.right(extra));
|
||||||
|
setTextCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
void CodeEditor::get_help()
|
void CodeEditor::get_help()
|
||||||
{
|
{
|
||||||
QString page, help;
|
QString page, help;
|
||||||
|
|||||||
@ -14,11 +14,13 @@
|
|||||||
#ifndef CODEEDITOR_H
|
#ifndef CODEEDITOR_H
|
||||||
#define CODEEDITOR_H
|
#define CODEEDITOR_H
|
||||||
|
|
||||||
|
#include <QCompleter>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
class CodeEditor : public QPlainTextEdit {
|
class CodeEditor : public QPlainTextEdit {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -32,6 +34,7 @@ public:
|
|||||||
void setCursor(int block);
|
void setCursor(int block);
|
||||||
void setHighlight(int block, bool error);
|
void setHighlight(int block, bool error);
|
||||||
QString reformatLine(const QString &line);
|
QString reformatLine(const QString &line);
|
||||||
|
void setCommandList(const QStringList &words);
|
||||||
|
|
||||||
static constexpr int NO_HIGHLIGHT = 1 << 30;
|
static constexpr int NO_HIGHLIGHT = 1 << 30;
|
||||||
|
|
||||||
@ -50,10 +53,12 @@ private slots:
|
|||||||
void find_help(QString &page, QString &help);
|
void find_help(QString &page, QString &help);
|
||||||
void open_help();
|
void open_help();
|
||||||
void reformatCurrentLine();
|
void reformatCurrentLine();
|
||||||
|
void insertCompletedCommand(const QString &completion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget *lineNumberArea;
|
QWidget *lineNumberArea;
|
||||||
QShortcut *help_action;
|
QShortcut *help_action;
|
||||||
|
QCompleter *command_completer;
|
||||||
int highlight;
|
int highlight;
|
||||||
|
|
||||||
QMap<QString, QString> cmd_map;
|
QMap<QString, QString> cmd_map;
|
||||||
|
|||||||
70
tools/lammps-gui/lammps_internal_commands.txt
Normal file
70
tools/lammps-gui/lammps_internal_commands.txt
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
clear
|
||||||
|
echo
|
||||||
|
if
|
||||||
|
include
|
||||||
|
jump
|
||||||
|
label
|
||||||
|
log
|
||||||
|
next
|
||||||
|
partition
|
||||||
|
print
|
||||||
|
python
|
||||||
|
quit
|
||||||
|
shell
|
||||||
|
variable
|
||||||
|
angle_coeff
|
||||||
|
angle_style
|
||||||
|
atom_modify
|
||||||
|
atom_style
|
||||||
|
bond_coeff
|
||||||
|
bond_style
|
||||||
|
bond_write
|
||||||
|
boundary
|
||||||
|
comm_modify
|
||||||
|
comm_style
|
||||||
|
compute
|
||||||
|
compute_modify
|
||||||
|
dielectric
|
||||||
|
dihedral_coeff
|
||||||
|
dihedral_style
|
||||||
|
dimension
|
||||||
|
dump
|
||||||
|
dump_modify
|
||||||
|
fix
|
||||||
|
fix_modify
|
||||||
|
group
|
||||||
|
improper_coeff
|
||||||
|
improper_style
|
||||||
|
kspace_modify
|
||||||
|
kspace_style
|
||||||
|
labelmap
|
||||||
|
lattice
|
||||||
|
mass
|
||||||
|
min_modify
|
||||||
|
min_style
|
||||||
|
molecule
|
||||||
|
neigh_modify
|
||||||
|
neighbor
|
||||||
|
newton
|
||||||
|
package
|
||||||
|
pair_coeff
|
||||||
|
pair_modify
|
||||||
|
pair_style
|
||||||
|
pair_write
|
||||||
|
processors
|
||||||
|
region
|
||||||
|
reset_timestep
|
||||||
|
restart
|
||||||
|
run_style
|
||||||
|
special_bonds
|
||||||
|
suffix
|
||||||
|
thermo
|
||||||
|
thermo_modify
|
||||||
|
thermo_style
|
||||||
|
timestep
|
||||||
|
timer
|
||||||
|
uncompute
|
||||||
|
undump
|
||||||
|
unfix
|
||||||
|
units
|
||||||
|
reset_atoms
|
||||||
@ -57,6 +57,7 @@
|
|||||||
|
|
||||||
static const QString blank(" ");
|
static const QString blank(" ");
|
||||||
static constexpr int MAXRECENT = 5;
|
static constexpr int MAXRECENT = 5;
|
||||||
|
static constexpr int BUFLEN = 128;
|
||||||
|
|
||||||
// duplicate string
|
// duplicate string
|
||||||
static char *mystrdup(const std::string &text)
|
static char *mystrdup(const std::string &text)
|
||||||
@ -296,6 +297,25 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) :
|
|||||||
setWindowTitle(QString("LAMMPS-GUI - *unknown*"));
|
setWindowTitle(QString("LAMMPS-GUI - *unknown*"));
|
||||||
}
|
}
|
||||||
resize(settings.value("mainx", "500").toInt(), settings.value("mainy", "320").toInt());
|
resize(settings.value("mainx", "500").toInt(), settings.value("mainy", "320").toInt());
|
||||||
|
|
||||||
|
// start LAMMPS and initialize command completion
|
||||||
|
start_lammps();
|
||||||
|
QStringList command_list;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
command_list.sort();
|
||||||
|
ui->textEdit->setCommandList(command_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
LammpsGui::~LammpsGui()
|
LammpsGui::~LammpsGui()
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>lammps-icon-128x128.png</file>
|
<file>lammps-icon-128x128.png</file>
|
||||||
|
<file>help_index.table</file>
|
||||||
|
<!-- This file is updated with: grep 'mycmd ==' ../../src/input.cpp | sed -e 's/^.*mycmd == "\(.*\)".*$/\1/' > lammps_internal_commands.txt -->
|
||||||
|
<file>lammps_internal_commands.txt</file>
|
||||||
<file>antialias.png</file>
|
<file>antialias.png</file>
|
||||||
<file>application-calc.png</file>
|
<file>application-calc.png</file>
|
||||||
<file>application-exit.png</file>
|
<file>application-exit.png</file>
|
||||||
@ -34,7 +37,6 @@
|
|||||||
<file>help-about.png</file>
|
<file>help-about.png</file>
|
||||||
<file>help-browser.png</file>
|
<file>help-browser.png</file>
|
||||||
<file>help-faq.png</file>
|
<file>help-faq.png</file>
|
||||||
<file>help_index.table</file>
|
|
||||||
<file>image-x-generic.png</file>
|
<file>image-x-generic.png</file>
|
||||||
<file>media-playback-start-2.png</file>
|
<file>media-playback-start-2.png</file>
|
||||||
<file>media-playlist-repeat.png</file>
|
<file>media-playlist-repeat.png</file>
|
||||||
|
|||||||
@ -97,6 +97,33 @@ int LammpsWrapper::id_name(const char *keyword, int idx, char *buf, int len)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LammpsWrapper::style_count(const char *keyword)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
if (lammps_handle) {
|
||||||
|
#if defined(LAMMPS_GUI_USE_PLUGIN)
|
||||||
|
val = ((liblammpsplugin_t *)plugin_handle)->style_count(lammps_handle, keyword);
|
||||||
|
#else
|
||||||
|
val = lammps_style_count(lammps_handle, keyword);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LammpsWrapper::style_name(const char *keyword, int idx, char *buf, int len)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
if (lammps_handle) {
|
||||||
|
#if defined(LAMMPS_GUI_USE_PLUGIN)
|
||||||
|
val =
|
||||||
|
((liblammpsplugin_t *)plugin_handle)->style_name(lammps_handle, keyword, idx, buf, len);
|
||||||
|
#else
|
||||||
|
val = lammps_style_name(lammps_handle, keyword, idx, buf, len);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
int LammpsWrapper::variable_info(int idx, char *buf, int len)
|
int LammpsWrapper::variable_info(int idx, char *buf, int len)
|
||||||
{
|
{
|
||||||
int val = 0;
|
int val = 0;
|
||||||
|
|||||||
@ -35,6 +35,8 @@ public:
|
|||||||
|
|
||||||
int id_count(const char *idtype);
|
int id_count(const char *idtype);
|
||||||
int id_name(const char *idtype, int idx, char *buf, int buflen);
|
int id_name(const char *idtype, int idx, char *buf, int buflen);
|
||||||
|
int style_count(const char *keyword);
|
||||||
|
int style_name(const char *keyword, int idx, char *buf, int buflen);
|
||||||
int variable_info(int idx, char *buf, int buflen);
|
int variable_info(int idx, char *buf, int buflen);
|
||||||
|
|
||||||
double get_thermo(const char *keyword);
|
double get_thermo(const char *keyword);
|
||||||
|
|||||||
Reference in New Issue
Block a user