File indexing completed on 2024-04-14 14:48:56

0001 /*
0002     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0003     SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com>
0004     SPDX-FileCopyrightText: 2012-2016 Sven Brauch <svenbrauch@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "pythonlanguagesupport.h"
0010 
0011 #include <QMutexLocker>
0012 #include <QReadWriteLock>
0013 
0014 #include <KPluginFactory>
0015 #include <KPluginLoader>
0016 
0017 #include <interfaces/icore.h>
0018 #include <interfaces/ilanguagecontroller.h>
0019 #include <interfaces/iplugincontroller.h>
0020 #include <interfaces/idocument.h>
0021 #include <interfaces/isourceformatter.h>
0022 #include <interfaces/idocumentcontroller.h>
0023 #include <interfaces/context.h>
0024 #include <interfaces/contextmenuextension.h>
0025 #include <interfaces/iprojectcontroller.h>
0026 #include <interfaces/iproject.h>
0027 #include <interfaces/isession.h>
0028 #include <language/assistant/renameassistant.h>
0029 #include <language/assistant/staticassistantsmanager.h>
0030 #include <language/interfaces/editorcontext.h>
0031 #include <language/duchain/duchain.h>
0032 #include <language/duchain/duchainlock.h>
0033 #include <language/codecompletion/codecompletion.h>
0034 #include <language/codecompletion/codecompletionmodel.h>
0035 
0036 #include "pythonparsejob.h"
0037 #include "pythonhighlighting.h"
0038 #include "duchain/pythoneditorintegrator.h"
0039 #include "codecompletion/model.h"
0040 #include "codegen/refactoring.h"
0041 #include "codegen/correctionfilegenerator.h"
0042 #include "kdevpythonversion.h"
0043 #include "pep8kcm/kcm_pep8.h"
0044 #include "projectconfig/projectconfigpage.h"
0045 #include "docfilekcm/kcm_docfiles.h"
0046 #include "pythonstylechecking.h"
0047 #include "helpers.h"
0048 
0049 #include <QDebug>
0050 #include <QProcess>
0051 #include "pythondebug.h"
0052 
0053 using namespace KDevelop;
0054 
0055 K_PLUGIN_FACTORY_WITH_JSON( KDevPythonSupportFactory, "kdevpythonsupport.json", registerPlugin<Python::LanguageSupport>(); )
0056 
0057 namespace Python
0058 {
0059 LanguageSupport* LanguageSupport::m_self = nullptr;
0060 
0061 ContextMenuExtension LanguageSupport::contextMenuExtension(Context* context, QWidget* parent)
0062 {
0063     ContextMenuExtension cm;
0064     EditorContext *ec = dynamic_cast<KDevelop::EditorContext *>(context);
0065 
0066     if (ec && ICore::self()->languageController()->languagesForUrl(ec->url()).contains(this)) {
0067         // It's a Python file, let's add our context menu.
0068         m_refactoring->fillContextMenu(cm, context, parent);
0069         TypeCorrection::self().doContextMenu(cm, context);
0070     }
0071     return cm;
0072 }
0073 
0074 LanguageSupport::LanguageSupport( QObject* parent, const QVariantList& /*args*/ )
0075     : KDevelop::IPlugin("pythonlanguagesupport", parent )
0076     , KDevelop::ILanguageSupport()
0077     , m_highlighting( new Highlighting( this ) )
0078     , m_refactoring( new Refactoring( this ) )
0079     , m_styleChecking( new StyleChecking( this ) )
0080 {
0081     m_self = this;
0082 
0083     PythonCodeCompletionModel* codeCompletion = new PythonCodeCompletionModel(this);
0084     new KDevelop::CodeCompletion(this, codeCompletion, "Python");
0085 
0086     auto assistantsManager = core()->languageController()->staticAssistantsManager();
0087     assistantsManager->registerAssistant(StaticAssistant::Ptr(new RenameAssistant(this)));
0088 
0089     QObject::connect(ICore::self()->documentController(), &IDocumentController::documentOpened,
0090                      this, &LanguageSupport::documentOpened);
0091 }
0092 
0093 void LanguageSupport::documentOpened(IDocument* doc)
0094 {
0095     if ( ! ICore::self()->languageController()->languagesForUrl(doc->url()).contains(this) ) {
0096         // not a python file
0097         return;
0098     }
0099 
0100     DUChainReadLocker lock;
0101     ReferencedTopDUContext top = DUChain::self()->chainForDocument(doc->url());
0102     lock.unlock();
0103     updateStyleChecking(top);
0104 }
0105 
0106 void LanguageSupport::updateStyleChecking(KDevelop::ReferencedTopDUContext top)
0107 {
0108     m_styleChecking->updateStyleChecking(top);
0109 }
0110 
0111 LanguageSupport::~LanguageSupport()
0112 {
0113     parseLock()->lockForWrite();
0114     // By locking the parse-mutexes, we make sure that parse jobs get a chance to finish in a good state
0115     parseLock()->unlock();
0116 
0117     delete m_highlighting;
0118     m_highlighting = nullptr;
0119 }
0120 
0121 KDevelop::ParseJob *LanguageSupport::createParseJob( const IndexedString& url )
0122 {
0123     return new ParseJob(url, this);
0124 }
0125 
0126 QString LanguageSupport::name() const
0127 {
0128     return "Python";
0129 }
0130 
0131 LanguageSupport* LanguageSupport::self()
0132 {
0133     return m_self;
0134 }
0135 
0136 SourceFormatterItemList LanguageSupport::sourceFormatterItems() const
0137 {
0138     SourceFormatterStyle autopep8("autopep8");
0139     autopep8.setCaption("autopep8");
0140     autopep8.setDescription(i18n("Format source with the autopep8 formatter."));
0141     autopep8.setOverrideSample("class klass:\n def method(arg1,arg2):\n  a=3+5\n"
0142                                "def function(arg,*vararg,**kwargs): return arg+kwarg[0]\nfunction(3, 5, 7)");
0143     using P = SourceFormatterStyle::MimeHighlightPair;
0144     autopep8.setMimeTypes(SourceFormatterStyle::MimeList{ P{"text/x-python", "Python"},
0145                                                           P{"text/x-python3", "Python 3"} });
0146     QString autopep8path = QStandardPaths::findExecutable("autopep8");
0147     if (autopep8path.isEmpty()) {
0148         // TODO: proper error handling/user notification
0149         qCDebug(KDEV_PYTHON) << "Could not find the autopep8 executable";
0150         autopep8path = "/usr/bin/autopep8";
0151     }
0152     autopep8.setContent(autopep8path + " -i $TMPFILE");
0153 
0154     return SourceFormatterItemList{SourceFormatterStyleItem{"customscript", autopep8}};
0155 }
0156 
0157 KDevelop::ICodeHighlighting* LanguageSupport::codeHighlighting() const
0158 {
0159     return m_highlighting;
0160 }
0161 
0162 BasicRefactoring* LanguageSupport::refactoring() const
0163 {
0164     return m_refactoring;
0165 }
0166 
0167 int LanguageSupport::suggestedReparseDelayForChange(KTextEditor::Document* doc, const KTextEditor::Range& changedRange,
0168                                                     const QString& changedText, bool /*removal*/) const
0169 {
0170     if ( changedRange.start().line() != changedRange.end().line() ) {
0171         // instant update
0172         return 0;
0173     }
0174     if ( std::all_of(changedText.begin(), changedText.end(), [](const QChar& c) { return c.isSpace(); }) ) {
0175         qCDebug(KDEV_PYTHON) << changedText << changedRange.end().column() << doc->lineLength(changedRange.end().line());
0176         if ( changedRange.end().column()-1 == doc->lineLength(changedRange.end().line()) ) {
0177             return ILanguageSupport::NoUpdateRequired;
0178         }
0179     }
0180     return ILanguageSupport::DefaultDelay;
0181 }
0182 
0183 
0184 QList<ILanguageCheck*> LanguageSupport::providedChecks()
0185 {
0186     return {};
0187 }
0188 
0189 int LanguageSupport::configPages() const
0190 {
0191     return 2;
0192 }
0193 
0194 KDevelop::ConfigPage* LanguageSupport::configPage(int number, QWidget* parent)
0195 {
0196     if (number == 0) {
0197         return new PEP8KCModule(this, parent);
0198     } else if (number == 1) {
0199         return new DocfilesKCModule(this, parent);
0200     }
0201     return nullptr;
0202 }
0203 
0204 int LanguageSupport::perProjectConfigPages() const
0205 {
0206     return 1;
0207 }
0208 
0209 KDevelop::ConfigPage* LanguageSupport::perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent)
0210 {
0211     if ( number == 0 ) {
0212         return new Python::ProjectConfigPage(this, options, parent);
0213     }
0214     return nullptr;
0215 }
0216 
0217 }
0218 
0219 #include "pythonlanguagesupport.moc"
0220 
0221 #include "moc_pythonlanguagesupport.cpp"