File indexing completed on 2024-04-28 12:23:09
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"