forked from qt-creator/qt-creator
Python editor: Add buttons & actions for opening REPL
Opens interactive Python, optionally with the current file imported, for testing and experimentation. Change-Id: Ieb120e3698bdba77a1445c40fe7fda533773a0cf Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -136,6 +136,11 @@ void ConsoleProcess::setCommand(const CommandLine &command)
|
||||
d->m_commandLine = command;
|
||||
}
|
||||
|
||||
CommandLine ConsoleProcess::command() const
|
||||
{
|
||||
return d->m_commandLine;
|
||||
}
|
||||
|
||||
void ConsoleProcess::setSettings(QSettings *settings)
|
||||
{
|
||||
d->m_settings = settings;
|
||||
|
@@ -65,6 +65,7 @@ public:
|
||||
~ConsoleProcess() override;
|
||||
|
||||
void setCommand(const Utils::CommandLine &command);
|
||||
Utils::CommandLine command() const;
|
||||
void setAbortOnMetaChars(bool abort);
|
||||
|
||||
void setWorkingDirectory(const QString &dir);
|
||||
|
@@ -37,6 +37,10 @@ const char C_EDITOR_DISPLAY_NAME[] =
|
||||
const char C_PYTHONOPTIONS_PAGE_ID[] = "PythonEditor.OptionsPage";
|
||||
const char C_PYTHON_SETTINGS_CATEGORY[] = "P.Python";
|
||||
|
||||
const char PYTHON_OPEN_REPL[] = "Python.OpenRepl";
|
||||
const char PYTHON_OPEN_REPL_IMPORT[] = "Python.OpenReplImport";
|
||||
const char PYTHON_OPEN_REPL_IMPORT_TOPLEVEL[] = "Python.OpenReplImportToplevel";
|
||||
|
||||
/*******************************************************************************
|
||||
* MIME type
|
||||
******************************************************************************/
|
||||
|
@@ -27,16 +27,81 @@
|
||||
#include "pythonconstants.h"
|
||||
#include "pythonhighlighter.h"
|
||||
#include "pythonindenter.h"
|
||||
#include "pythonsettings.h"
|
||||
#include "pythonutils.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/commandbutton.h>
|
||||
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditoractionhandler.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
||||
namespace Python {
|
||||
namespace Internal {
|
||||
|
||||
static QAction *createAction(QObject *parent, ReplType type)
|
||||
{
|
||||
QAction *action = new QAction(parent);
|
||||
switch (type) {
|
||||
case ReplType::Unmodified:
|
||||
action->setText(QCoreApplication::translate("Python", "REPL"));
|
||||
action->setToolTip(QCoreApplication::translate("Python", "Open interactive Python."));
|
||||
break;
|
||||
case ReplType::Import:
|
||||
action->setText(QCoreApplication::translate("Python", "REPL Import File"));
|
||||
action->setToolTip(
|
||||
QCoreApplication::translate("Python", "Open interactive Python and import file."));
|
||||
break;
|
||||
case ReplType::ImportToplevel:
|
||||
action->setText(QCoreApplication::translate("Python", "REPL Import *"));
|
||||
action->setToolTip(
|
||||
QCoreApplication::translate("Python",
|
||||
"Open interactive Python and import * from file."));
|
||||
break;
|
||||
}
|
||||
|
||||
QObject::connect(action, &QAction::triggered, parent, [type] {
|
||||
Core::IDocument *doc = Core::EditorManager::currentDocument();
|
||||
openPythonRepl(doc ? doc->filePath() : Utils::FilePath(), type);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
static void registerReplAction(QObject *parent)
|
||||
{
|
||||
Core::ActionManager::registerAction(createAction(parent, ReplType::Unmodified),
|
||||
Constants::PYTHON_OPEN_REPL);
|
||||
Core::ActionManager::registerAction(createAction(parent, ReplType::Import),
|
||||
Constants::PYTHON_OPEN_REPL_IMPORT);
|
||||
Core::ActionManager::registerAction(createAction(parent, ReplType::ImportToplevel),
|
||||
Constants::PYTHON_OPEN_REPL_IMPORT_TOPLEVEL);
|
||||
}
|
||||
|
||||
static QWidget *createEditorWidget()
|
||||
{
|
||||
auto widget = new TextEditor::TextEditorWidget;
|
||||
auto replButton = new QToolButton(widget);
|
||||
replButton->setProperty("noArrow", true);
|
||||
replButton->setText(QCoreApplication::translate("Python", "REPL"));
|
||||
replButton->setPopupMode(QToolButton::InstantPopup);
|
||||
auto menu = new QMenu(replButton);
|
||||
replButton->setMenu(menu);
|
||||
menu->addAction(Core::ActionManager::command(Constants::PYTHON_OPEN_REPL)->action());
|
||||
menu->addSeparator();
|
||||
menu->addAction(Core::ActionManager::command(Constants::PYTHON_OPEN_REPL_IMPORT)->action());
|
||||
menu->addAction(
|
||||
Core::ActionManager::command(Constants::PYTHON_OPEN_REPL_IMPORT_TOPLEVEL)->action());
|
||||
widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, replButton);
|
||||
return widget;
|
||||
}
|
||||
|
||||
PythonEditorFactory::PythonEditorFactory()
|
||||
{
|
||||
registerReplAction(this);
|
||||
|
||||
setId(Constants::C_PYTHONEDITOR_ID);
|
||||
setDisplayName(
|
||||
QCoreApplication::translate("OpenWith::Editors", Constants::C_EDITOR_DISPLAY_NAME));
|
||||
@@ -48,6 +113,7 @@ PythonEditorFactory::PythonEditorFactory()
|
||||
| TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
|
||||
|
||||
setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_PYTHONEDITOR_ID); });
|
||||
setEditorWidgetCreator(createEditorWidget);
|
||||
setIndenterCreator([](QTextDocument *doc) { return new PythonIndenter(doc); });
|
||||
setSyntaxHighlighterCreator([] { return new PythonHighlighter; });
|
||||
setCommentDefinition(Utils::CommentDefinition::HashStyle);
|
||||
|
@@ -43,6 +43,8 @@
|
||||
|
||||
#include <texteditor/textdocument.h>
|
||||
|
||||
#include <utils/consoleprocess.h>
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
@@ -170,7 +172,9 @@ static FilePath detectPython(const FilePath &documentPath)
|
||||
{
|
||||
FilePath python;
|
||||
|
||||
PythonProject *project = qobject_cast<PythonProject *>(
|
||||
PythonProject *project = documentPath.isEmpty()
|
||||
? nullptr
|
||||
: qobject_cast<PythonProject *>(
|
||||
ProjectExplorer::SessionManager::projectForFile(documentPath));
|
||||
if (!project)
|
||||
project = qobject_cast<PythonProject *>(ProjectExplorer::SessionManager::startupProject());
|
||||
@@ -480,6 +484,55 @@ PyLSConfigureAssistant::PyLSConfigureAssistant(QObject *parent)
|
||||
});
|
||||
}
|
||||
|
||||
static QStringList replImportArgs(const FilePath &pythonFile, ReplType type)
|
||||
{
|
||||
using MimeTypes = QList<MimeType>;
|
||||
const MimeTypes mimeTypes = pythonFile.isEmpty() || type == ReplType::Unmodified
|
||||
? MimeTypes()
|
||||
: mimeTypesForFileName(pythonFile.toString());
|
||||
const bool isPython = Utils::anyOf(mimeTypes, [](const MimeType &mt) {
|
||||
return mt.inherits("text/x-python") || mt.inherits("text/x-python3");
|
||||
});
|
||||
if (type == ReplType::Unmodified || !isPython)
|
||||
return {};
|
||||
const auto import = type == ReplType::Import
|
||||
? QString("import %1").arg(pythonFile.toFileInfo().completeBaseName())
|
||||
: QString("from %1 import *")
|
||||
.arg(pythonFile.toFileInfo().completeBaseName());
|
||||
return {"-c", QString("%1; print('Running \"%1\"')").arg(import)};
|
||||
}
|
||||
|
||||
void openPythonRepl(const FilePath &file, ReplType type)
|
||||
{
|
||||
static const auto workingDir = [](const FilePath &file) {
|
||||
if (file.isEmpty()) {
|
||||
if (ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject())
|
||||
return project->projectDirectory().toFileInfo().filePath();
|
||||
return QDir::currentPath();
|
||||
}
|
||||
return file.toFileInfo().path();
|
||||
};
|
||||
|
||||
const auto args = QStringList{"-i"} + replImportArgs(file, type);
|
||||
auto process = new ConsoleProcess;
|
||||
const FilePath pythonCommand = detectPython(file);
|
||||
process->setCommand({pythonCommand, args});
|
||||
process->setWorkingDirectory(workingDir(file));
|
||||
const QString commandLine = process->command().toUserOutput();
|
||||
QObject::connect(process,
|
||||
&ConsoleProcess::processError,
|
||||
process,
|
||||
[process, commandLine](const QString &errorString) {
|
||||
Core::MessageManager::write(
|
||||
QCoreApplication::translate("Python",
|
||||
"Failed to run Python (%1): \"%2\".")
|
||||
.arg(commandLine, errorString));
|
||||
process->deleteLater();
|
||||
});
|
||||
QObject::connect(process, &ConsoleProcess::stubStopped, process, &QObject::deleteLater);
|
||||
process->start();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Python
|
||||
|
||||
|
@@ -40,6 +40,10 @@ namespace TextEditor { class TextDocument; }
|
||||
namespace Python {
|
||||
namespace Internal {
|
||||
|
||||
enum class ReplType { Unmodified, Import, ImportToplevel };
|
||||
|
||||
void openPythonRepl(const Utils::FilePath &file, ReplType type);
|
||||
|
||||
struct PythonLanguageServerState;
|
||||
|
||||
class PyLSConfigureAssistant : public QObject
|
||||
|
Reference in New Issue
Block a user