|
|
|
@@ -187,9 +187,10 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
|
|
QObject::connect(ui->lineInput, &RDLineEdit::keyPress, this, &PythonShell::interactive_keypress);
|
|
|
|
|
QObject::connect(ui->helpSearch, &RDLineEdit::keyPress, this, &PythonShell::helpSearch_keypress);
|
|
|
|
|
QObject::connect(ui->helpSearch, &RDLineEdit::keyPress, this, &PythonShell::interactive_keypress);
|
|
|
|
|
|
|
|
|
|
QObject::connect(ui->lineInput, &RDLineEdit::leave, [this]() { hideFunccompleteTooltip(); });
|
|
|
|
|
QObject::connect(ui->helpSearch, &RDLineEdit::leave, [this]() { hideFunccompleteTooltip(); });
|
|
|
|
|
|
|
|
|
|
// we create this up front so its state stays persistent as much as possible.
|
|
|
|
|
m_FindReplace = new FindReplace(m_Scintillas, this);
|
|
|
|
@@ -217,6 +218,7 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
m_FindReplace->setFindAllResultsDisplay(m_FindResults);
|
|
|
|
|
|
|
|
|
|
ui->lineInput->setFont(Formatter::FixedFont());
|
|
|
|
|
ui->helpSearch->setFont(Formatter::FixedFont());
|
|
|
|
|
ui->interactiveOutput->setFont(Formatter::FixedFont());
|
|
|
|
|
ui->scriptOutput->setFont(Formatter::FixedFont());
|
|
|
|
|
ui->helpText->setFont(Formatter::FixedFont());
|
|
|
|
@@ -226,6 +228,7 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
ui->replGroup->setWindowTitle(tr("Interactive REPL"));
|
|
|
|
|
|
|
|
|
|
ui->lineInput->setAcceptTabCharacters(true);
|
|
|
|
|
ui->helpSearch->setAcceptTabCharacters(true);
|
|
|
|
|
|
|
|
|
|
// don't repeatedly re-parse for errors. Have a reasonable timeout
|
|
|
|
|
m_SyntaxCheckTimer = new QTimer(this);
|
|
|
|
@@ -282,13 +285,6 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
|
|
|
|
|
completionContext = new PythonContext();
|
|
|
|
|
|
|
|
|
|
// if we're help printing in the completion context, append it to the help text
|
|
|
|
|
QObject::connect(completionContext, &PythonContext::textOutput,
|
|
|
|
|
[this](const QString &, bool isStdError, const QString &output) {
|
|
|
|
|
if(m_HelpPrinting)
|
|
|
|
|
appendText(ui->helpText, output);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
QObject::connect(m_SyntaxCheckTimer, &QTimer::timeout, this, &PythonShell::doSyntaxCheck);
|
|
|
|
|
|
|
|
|
|
QObject::connect(ui->interactiveOutput, &RDTextEdit::keyPress, [this](QKeyEvent *e) {
|
|
|
|
@@ -313,7 +309,6 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
m_InteractiveCompleter->popup()->setFont(Formatter::FixedFont());
|
|
|
|
|
m_InteractiveCompleter->popup()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
|
|
|
|
m_InteractiveCompleter->popup()->setTextElideMode(Qt::ElideNone);
|
|
|
|
|
m_InteractiveCompleter->setWidget(ui->lineInput);
|
|
|
|
|
m_InteractiveCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
|
|
|
m_InteractiveCompleter->setWrapAround(false);
|
|
|
|
|
m_InteractiveCompletionModel = new QStringListModel(this);
|
|
|
|
@@ -338,15 +333,18 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
QObject::connect(m_InteractiveCompleter,
|
|
|
|
|
OverloadedSlot<const QModelIndex &>::of(&QCompleter::activated),
|
|
|
|
|
[this](const QModelIndex &idx) {
|
|
|
|
|
RDLineEdit *edit = qobject_cast<RDLineEdit *>(m_InteractiveCompleter->widget());
|
|
|
|
|
if(!edit)
|
|
|
|
|
return;
|
|
|
|
|
int i = idx.row();
|
|
|
|
|
if(i >= 0 && i < m_InteractiveCompletionModel->rowCount())
|
|
|
|
|
{
|
|
|
|
|
QString curText = ui->lineInput->text();
|
|
|
|
|
QString curText = edit->text();
|
|
|
|
|
curText.resize(curText.size() - m_InteractiveCompletionPrefix);
|
|
|
|
|
curText += m_InteractiveCompletionModel->stringList()[i];
|
|
|
|
|
ui->lineInput->setText(curText);
|
|
|
|
|
edit->setText(curText);
|
|
|
|
|
|
|
|
|
|
ui->lineInput->setCursorPosition(curText.size());
|
|
|
|
|
edit->setCursorPosition(curText.size());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@@ -533,6 +531,15 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
|
|
|
|
|
QObject::connect(PythonContext::GetExtensionContext(), &PythonContext::extensionLoaded, this,
|
|
|
|
|
&PythonShell::extensionLoaded);
|
|
|
|
|
|
|
|
|
|
helpContext = newContext();
|
|
|
|
|
|
|
|
|
|
helpContext->makeHelpContext();
|
|
|
|
|
|
|
|
|
|
QObject::connect(helpContext, &PythonContext::textOutput,
|
|
|
|
|
[this](const QString &, bool isStdError, const QString &output) {
|
|
|
|
|
appendText(ui->helpText, output);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
m_Ctx.GetMainWindow()->RegisterShortcut("CTRL+S", this,
|
|
|
|
|
[this](QWidget *) { this->on_saveScript_clicked(); });
|
|
|
|
|
|
|
|
|
@@ -566,6 +573,7 @@ PythonShell::~PythonShell()
|
|
|
|
|
|
|
|
|
|
completionContext->Finish();
|
|
|
|
|
interactiveContext->Finish();
|
|
|
|
|
helpContext->Finish();
|
|
|
|
|
|
|
|
|
|
delete ui;
|
|
|
|
|
}
|
|
|
|
@@ -1777,7 +1785,8 @@ bool PythonShell::saveEditor(EditorWrapper *editor, QString filename)
|
|
|
|
|
void PythonShell::removeEditor(EditorWrapper *editor)
|
|
|
|
|
{
|
|
|
|
|
hideFunccompleteTooltip();
|
|
|
|
|
m_Watcher->removePath(editor->filename());
|
|
|
|
|
if(!editor->filename().isEmpty())
|
|
|
|
|
m_Watcher->removePath(editor->filename());
|
|
|
|
|
m_Editors.removeOne(editor);
|
|
|
|
|
m_Scintillas.removeOne(editor->scintilla());
|
|
|
|
|
updateEditorCloseButton();
|
|
|
|
@@ -2194,9 +2203,7 @@ void PythonShell::refreshCurrentHelp()
|
|
|
|
|
|
|
|
|
|
ui->helpText->clear();
|
|
|
|
|
|
|
|
|
|
m_HelpPrinting = true;
|
|
|
|
|
|
|
|
|
|
completionContext->executeString(lit(R"(
|
|
|
|
|
helpContext->executeString(lit(R"(
|
|
|
|
|
try:
|
|
|
|
|
import keyword
|
|
|
|
|
if keyword.iskeyword("%1"):
|
|
|
|
@@ -2206,15 +2213,17 @@ try:
|
|
|
|
|
except ImportError:
|
|
|
|
|
help(%1)
|
|
|
|
|
)")
|
|
|
|
|
.arg(ui->helpSearch->text()));
|
|
|
|
|
.arg(ui->helpSearch->text()));
|
|
|
|
|
|
|
|
|
|
ui->helpText->verticalScrollBar()->setValue(0);
|
|
|
|
|
|
|
|
|
|
m_HelpPrinting = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
{
|
|
|
|
|
RDLineEdit *edit = qobject_cast<RDLineEdit *>(QObject::sender());
|
|
|
|
|
if(!edit)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool triggerCompletion = false;
|
|
|
|
|
|
|
|
|
|
if(m_InteractiveCompleter->popup()->isVisible())
|
|
|
|
@@ -2224,12 +2233,13 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
// manually trigger a completion with tab
|
|
|
|
|
case Qt::Key_Tab:
|
|
|
|
|
// allow prefixed tabs to be inserted
|
|
|
|
|
if(ui->lineInput->text().trimmed().isEmpty())
|
|
|
|
|
if(edit->text().trimmed().isEmpty())
|
|
|
|
|
{
|
|
|
|
|
ui->lineInput->insert(lit("\t"));
|
|
|
|
|
edit->insert(lit("\t"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_InteractiveCompleter->setWidget(edit);
|
|
|
|
|
m_InteractiveCompleter->activated(
|
|
|
|
|
m_InteractiveCompleter->popup()->selectionModel()->currentIndex());
|
|
|
|
|
m_InteractiveCompleter->popup()->hide();
|
|
|
|
@@ -2259,7 +2269,7 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
|
|
|
|
|
if(triggerCompletion)
|
|
|
|
|
{
|
|
|
|
|
QString base = ui->lineInput->text();
|
|
|
|
|
QString base = edit->text();
|
|
|
|
|
|
|
|
|
|
QStringList completions;
|
|
|
|
|
int oldCount = m_CompletionTipList.count();
|
|
|
|
@@ -2267,8 +2277,10 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
|
|
|
|
|
if(base.trimmed() != QString())
|
|
|
|
|
{
|
|
|
|
|
m_CompletionTipList =
|
|
|
|
|
interactiveContext->completionOptions(0, base, m_InteractiveCompletionPrefix);
|
|
|
|
|
PythonContext *ctx = interactiveContext;
|
|
|
|
|
if(edit == ui->helpSearch)
|
|
|
|
|
ctx = helpContext;
|
|
|
|
|
m_CompletionTipList = ctx->completionOptions(0, base, m_InteractiveCompletionPrefix);
|
|
|
|
|
|
|
|
|
|
for(const QPair<QString, QString> &item : m_CompletionTipList)
|
|
|
|
|
completions << item.first;
|
|
|
|
@@ -2280,7 +2292,7 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
if(completions.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
if(event->key() == Qt::Key_Tab)
|
|
|
|
|
ui->lineInput->insert(lit("\t"));
|
|
|
|
|
edit->insert(lit("\t"));
|
|
|
|
|
m_InteractiveCompleter->popup()->hide();
|
|
|
|
|
|
|
|
|
|
QString prompt = interactiveContext->tryFunctionCompletion(0, base);
|
|
|
|
@@ -2289,13 +2301,13 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
{
|
|
|
|
|
m_ToolTip->configureTip(this, prompt);
|
|
|
|
|
|
|
|
|
|
QPoint p = ui->lineInput->fontMetrics().boundingRect(base).bottomRight();
|
|
|
|
|
p.setY(ui->lineInput->geometry().height());
|
|
|
|
|
p = ui->lineInput->mapToGlobal(p);
|
|
|
|
|
QPoint p = edit->fontMetrics().boundingRect(base).bottomRight();
|
|
|
|
|
p.setY(edit->geometry().height());
|
|
|
|
|
p = edit->mapToGlobal(p);
|
|
|
|
|
if(!m_ToolTip->isVisible())
|
|
|
|
|
m_ToolTip->showTipAtPos(p);
|
|
|
|
|
m_FuncTip = true;
|
|
|
|
|
m_FuncTipWidget = ui->lineInput;
|
|
|
|
|
m_FuncTipWidget = edit;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@@ -2309,8 +2321,8 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
|
|
|
|
|
m_InteractiveCompletionModel->setStringList(completions);
|
|
|
|
|
|
|
|
|
|
QRect r = ui->lineInput->rect();
|
|
|
|
|
QFontMetrics fm = ui->lineInput->fontMetrics();
|
|
|
|
|
QRect r = edit->rect();
|
|
|
|
|
QFontMetrics fm = edit->fontMetrics();
|
|
|
|
|
|
|
|
|
|
#if(QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
|
|
|
|
|
#define horizontalAdvance width
|
|
|
|
@@ -2325,9 +2337,10 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
base.resize(base.size() - m_InteractiveCompletionPrefix);
|
|
|
|
|
|
|
|
|
|
r.setLeft(r.left() + fm.horizontalAdvance(base));
|
|
|
|
|
r.setWidth(longestWidth + ui->lineInput->style()->pixelMetric(QStyle::PM_ScrollBarExtent) +
|
|
|
|
|
ui->lineInput->style()->pixelMetric(QStyle::PM_ButtonMargin));
|
|
|
|
|
r.setWidth(longestWidth + edit->style()->pixelMetric(QStyle::PM_ScrollBarExtent) +
|
|
|
|
|
edit->style()->pixelMetric(QStyle::PM_ButtonMargin));
|
|
|
|
|
|
|
|
|
|
m_InteractiveCompleter->setWidget(edit);
|
|
|
|
|
m_InteractiveCompleter->complete(r);
|
|
|
|
|
m_InteractiveCompleter->popup()->selectionModel()->setCurrentIndex(
|
|
|
|
|
m_InteractiveCompletionModel->index(0), QItemSelectionModel::ClearAndSelect);
|
|
|
|
@@ -2337,45 +2350,46 @@ void PythonShell::interactive_keypress(QKeyEvent *event)
|
|
|
|
|
|
|
|
|
|
if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
|
|
|
|
|
{
|
|
|
|
|
on_execute_clicked();
|
|
|
|
|
if(edit == ui->lineInput)
|
|
|
|
|
on_execute_clicked();
|
|
|
|
|
else if(edit == ui->helpSearch)
|
|
|
|
|
refreshCurrentHelp();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool moved = false;
|
|
|
|
|
|
|
|
|
|
if(event->key() == Qt::Key_Down && historyidx > -1)
|
|
|
|
|
// only line input has history
|
|
|
|
|
if(edit == ui->lineInput)
|
|
|
|
|
{
|
|
|
|
|
historyidx--;
|
|
|
|
|
bool moved = false;
|
|
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
|
if(event->key() == Qt::Key_Down && historyidx > -1)
|
|
|
|
|
{
|
|
|
|
|
historyidx--;
|
|
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString workingtext;
|
|
|
|
|
|
|
|
|
|
if(event->key() == Qt::Key_Up && historyidx + 1 < history.count())
|
|
|
|
|
{
|
|
|
|
|
if(historyidx == -1)
|
|
|
|
|
workingtext = ui->lineInput->text();
|
|
|
|
|
|
|
|
|
|
historyidx++;
|
|
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(moved)
|
|
|
|
|
{
|
|
|
|
|
if(historyidx == -1)
|
|
|
|
|
ui->lineInput->setText(workingtext);
|
|
|
|
|
else
|
|
|
|
|
ui->lineInput->setText(history[historyidx]);
|
|
|
|
|
|
|
|
|
|
ui->lineInput->deselect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString workingtext;
|
|
|
|
|
|
|
|
|
|
if(event->key() == Qt::Key_Up && historyidx + 1 < history.count())
|
|
|
|
|
{
|
|
|
|
|
if(historyidx == -1)
|
|
|
|
|
workingtext = ui->lineInput->text();
|
|
|
|
|
|
|
|
|
|
historyidx++;
|
|
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(moved)
|
|
|
|
|
{
|
|
|
|
|
if(historyidx == -1)
|
|
|
|
|
ui->lineInput->setText(workingtext);
|
|
|
|
|
else
|
|
|
|
|
ui->lineInput->setText(history[historyidx]);
|
|
|
|
|
|
|
|
|
|
ui->lineInput->deselect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PythonShell::helpSearch_keypress(QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
|
|
|
|
|
refreshCurrentHelp();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString PythonShell::scriptHeader()
|
|
|
|
|