diff --git a/phplanguagesupport.cpp b/phplanguagesupport.cpp index 390699b..4e5e21d 100644 --- a/phplanguagesupport.cpp +++ b/phplanguagesupport.cpp @@ -1,196 +1,197 @@ /***************************************************************************** * Copyright (c) 2007 Piyush verma * * Copyright (c) 2009 Niko Sams * * Copyright (c) 2010 Milian Wolff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ #include "phplanguagesupport.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phpparsejob.h" #include "phphighlighting.h" #include "kdevphpversion.h" #include "phpdebug.h" #include "codegen/refactoring.h" #include #include #include "completion/model.h" #include "completion/worker.h" #include "navigation/navigationwidget.h" #include #include "duchain/helper.h" #include using namespace KTextEditor; using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(KDevPhpSupportFactory, "kdevphpsupport.json", registerPlugin(); ) namespace Php { LanguageSupport::LanguageSupport(QObject* parent, const QVariantList& /*args*/) : KDevelop::IPlugin(QStringLiteral("kdevphpsupport"), parent), KDevelop::ILanguageSupport() { Q_ASSERT(internalFunctionFile().toUrl().isValid()); m_highlighting = new Php::Highlighting(this); m_refactoring = new Php::Refactoring(this); auto* ccModel = new CodeCompletionModel(this); new KDevelop::CodeCompletion(this, ccModel, name()); } LanguageSupport::~LanguageSupport() { parseLock()->lockForWrite(); //By locking the parse-mutexes, we make sure that parse- and preprocess-jobs //get a chance to finish in a good state parseLock()->unlock(); } KDevelop::ParseJob *LanguageSupport::createParseJob(const IndexedString &url) { auto *job = new ParseJob(url, this); // bypass the 5 MB maximum file size limit for the internal file if (url == internalFunctionFile()) { job->setMaximumFileSize(std::numeric_limits::max()); + job->setMinimumFeatures(TopDUContext::AllDeclarationsAndContexts); } return job; } QString LanguageSupport::name() const { return QStringLiteral("Php"); } KDevelop::ICodeHighlighting* LanguageSupport::codeHighlighting() const { return m_highlighting; } ContextMenuExtension LanguageSupport::contextMenuExtension(Context* context, QWidget* parent) { ContextMenuExtension cm; EditorContext *ed = dynamic_cast(context); if (ed && ICore::self()->languageController()->languagesForUrl(ed->url()).contains(this)) { // It's safe to add our own ContextMenuExtension. m_refactoring->fillContextMenu(cm, context, parent); } return cm; } QPair LanguageSupport::wordUnderCursor(const QUrl& url, const Cursor& position) { KDevelop::IDocument* doc = core()->documentController()->documentForUrl(url); if(!doc || !doc->textDocument()) return {}; int lineNumber = position.line(); int lineLength = doc->textDocument()->lineLength(lineNumber); QString line = doc->textDocument()->text(Range(lineNumber, 0, lineNumber, lineLength)); int startCol = position.column(); for ( ; startCol >= 0; --startCol ) { if ( !line[startCol].isLetter() && line[startCol] != '_' ) { // don't include the wrong char if ( startCol != position.column() ) { ++startCol; } break; } } int endCol = position.column(); for ( ; endCol <= lineLength; ++endCol ) { if ( !line[endCol].isLetter() && line[endCol] != '_' ) { break; } } QString word = line.mid(startCol, endCol - startCol); Range range(lineNumber, startCol, lineNumber, endCol); return qMakePair(word, range); } bool isMagicConstant(QPair word) { if ( word.second.isValid() && !word.second.isEmpty() ) { if ( word.first == QLatin1String("__FILE__") || word.first == QLatin1String("__LINE__") || word.first == QLatin1String("__METHOD__") || word.first == QLatin1String("__CLASS__") || word.first == QLatin1String("__FUNCTION__") || word.first == QLatin1String("__NAMESPACE__") || word.first == QLatin1String("__DIR__") || word.first == QLatin1String("__TRAIT__") ) { ///TODO: maybe we should use the tokenizer to really make sure this is such a token /// and we are not inside a string, comment or similar /// otoh, it doesn't hurt imo return true; } } return false; } QPair LanguageSupport::specialLanguageObjectNavigationWidget(const QUrl& url, const Cursor& position) { QPair word = wordUnderCursor(url, position); if ( isMagicConstant(word) ) { DUChainReadLocker lock; if (TopDUContext* top = standardContext(url)) { return {new NavigationWidget(TopDUContextPointer(top), position, word.first), word.second}; } else { return {nullptr, Range::invalid()}; } } return ILanguageSupport::specialLanguageObjectNavigationWidget(url, position); } Range LanguageSupport::specialLanguageObjectRange(const QUrl& url, const Cursor& position) { QPair word = wordUnderCursor(url, position); if ( isMagicConstant(word) ) { return word.second; } return ILanguageSupport::specialLanguageObjectRange(url, position); } } #include "phplanguagesupport.moc" diff --git a/phpparsejob.cpp b/phpparsejob.cpp index ddef210..4fb4107 100644 --- a/phpparsejob.cpp +++ b/phpparsejob.cpp @@ -1,250 +1,250 @@ /***************************************************************************** * Copyright (c) 2007 Piyush verma * * Copyright (c) 2008 Niko Sams * * Copyright (c) 2010 Milian Wolff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ #include "phpparsejob.h" #include #include #include #include #include #include #include #include #include #include #include #include "editorintegrator.h" #include "parsesession.h" #include "phplanguagesupport.h" #include "phpdebugvisitor.h" #include "duchain/builders/declarationbuilder.h" #include "duchain/builders/usebuilder.h" #include "duchain/helper.h" #include "phpducontext.h" #include "phpdebug.h" #include #include #include #include +#include using namespace KDevelop; namespace Php { ParseJob::ParseJob(const IndexedString& url, ILanguageSupport* languageSupport) : KDevelop::ParseJob(url, languageSupport) , m_parentJob(nullptr) { } ParseJob::~ParseJob() { } LanguageSupport* ParseJob::php() const { return dynamic_cast(languageSupport()); } void ParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread * /*thread*/) { if (document() != internalFunctionFile()) { // make sure we loaded the internal file already const auto &phpSupport = languageSupport(); static std::once_flag once; std::call_once(once, [phpSupport] { qCDebug(PHP) << "Initializing internal function file" << internalFunctionFile(); - ParseJob internalJob(internalFunctionFile(), phpSupport); - internalJob.setMinimumFeatures(TopDUContext::AllDeclarationsAndContexts); - internalJob.run({}, nullptr); - Q_ASSERT(internalJob.success()); + auto internalJob = std::unique_ptr(static_cast(phpSupport->createParseJob(internalFunctionFile()))); + internalJob->run({}, nullptr); + Q_ASSERT(internalJob->success()); }); } UrlParseLock urlLock(document()); if (!(minimumFeatures() & Rescheduled) && !isUpdateRequired(phpLanguageString())) { return; } qCDebug(PHP) << "parsing" << document().str(); KDevelop::ProblemPointer p = readContents(); if (p) { //TODO: associate problem with topducontext return abortJob();; } ParseSession session; //TODO: support different charsets session.setContents(QString::fromUtf8(contents().contents)); session.setCurrentDocument(document()); // 2) parse StartAst* ast = nullptr; bool matched = session.parse(&ast); if (abortRequested() || ICore::self()->shuttingDown()) { return abortJob(); } KDevelop::ReferencedTopDUContext toUpdate; { KDevelop::DUChainReadLocker duchainlock(KDevelop::DUChain::lock()); toUpdate = KDevelop::DUChainUtils::standardContextForUrl(document().toUrl()); } KDevelop::TopDUContext::Features newFeatures = minimumFeatures(); if (toUpdate) newFeatures = (KDevelop::TopDUContext::Features)(newFeatures | toUpdate->features()); //Remove update-flags like 'Recursive' or 'ForceUpdate' newFeatures = static_cast(newFeatures & KDevelop::TopDUContext::AllDeclarationsContextsUsesAndAST); if (matched) { if (abortRequested()) { return abortJob(); } EditorIntegrator editor(&session); QReadLocker parseLock(php()->parseLock()); DeclarationBuilder builder(&editor); KDevelop::ReferencedTopDUContext chain = builder.build(document(), ast, toUpdate); if (abortRequested()) { return abortJob(); } setDuChain(chain); bool hadUnresolvedIdentifiers = builder.hadUnresolvedIdentifiers(); if ( newFeatures & TopDUContext::AllDeclarationsContextsAndUses && document() != internalFunctionFile() ) { UseBuilder useBuilder(&editor); useBuilder.buildUses(ast); if (useBuilder.hadUnresolvedIdentifiers()) hadUnresolvedIdentifiers = true; } if (hadUnresolvedIdentifiers) { if (!(minimumFeatures() & Rescheduled)) { // Need to create new parse job with lower priority qCDebug(PHP) << "Reschedule file " << document().str() << "for parsing"; KDevelop::TopDUContext::Features feat = static_cast( minimumFeatures() | KDevelop::TopDUContext::VisibleDeclarationsAndContexts | Rescheduled ); int priority = qMin(parsePriority()+100, (int)KDevelop::BackgroundParser::WorstPriority); KDevelop::ICore::self()->languageController()->backgroundParser() ->addDocument(document(), feat, priority); } else { // We haven't resolved all identifiers, but by now, we don't expect to qCDebug(PHP) << "Builder found unresolved identifiers when they should have been resolved! (if there was no coding error)"; } } if (abortRequested()) { return abortJob(); } if (abortRequested()) { return abortJob(); } { DUChainWriteLocker lock(DUChain::lock()); foreach(const ProblemPointer &p, session.problems()) { chain->addProblem(p); } chain->setFeatures(newFeatures); ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile(); file->setModificationRevision(contents().modification); DUChain::self()->updateContextEnvironment( chain->topContext(), file.data() ); } highlightDUChain(); } else { ReferencedTopDUContext top; DUChainWriteLocker lock; { top = DUChain::self()->chainForDocument(document()); } if (top) { ///NOTE: if we clear the imported parent contexts, autocompletion of built-in PHP stuff won't work! //top->clearImportedParentContexts(); top->parsingEnvironmentFile()->clearModificationRevisions(); top->clearProblems(); } else { ParsingEnvironmentFile *file = new ParsingEnvironmentFile(document()); file->setLanguage(phpLanguageString()); top = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file); DUChain::self()->addDocumentChain(top); } foreach(const ProblemPointer &p, session.problems()) { top->addProblem(p); } setDuChain(top); qCDebug(PHP) << "===Failed===" << document().str(); } DUChain::self()->emitUpdateReady(document(), duChain()); } void ParseJob::setParentJob(ParseJob *job) { m_parentJob = job; } bool ParseJob::hasParentDocument(const IndexedString &doc) { if (document() == doc) return true; if (!m_parentJob) return false; if (m_parentJob->document() == doc) return true; return m_parentJob->hasParentDocument(doc); } ProblemPointer ParseJob::createProblem(const QString &description, AstNode* node, EditorIntegrator * editor, IProblem::Source source, IProblem::Severity severity) { ProblemPointer p(new Problem()); p->setSource(source); p->setSeverity(severity); p->setDescription(description); p->setFinalLocation(DocumentRange(document(), editor->findRange(node).castToSimpleRange())); qCDebug(PHP) << p->description(); return p; } } // kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on