1
/***************************************************************************
2
* This file is part of KDevelop *
3
* Copyright 2008 Niko Sams <niko.sams@gmail.com> *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU Library General Public License as *
7
* published by the Free Software Foundation; either version 2 of the *
8
* License, or (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU Library General Public *
16
* License along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19
***************************************************************************/
22
#include <KParts/MainWindow>
23
#include <KLocalizedString>
25
#include <language/duchain/ducontext.h>
26
#include <language/duchain/duchainlock.h>
27
#include <language/duchain/persistentsymboltable.h>
28
#include <language/duchain/duchain.h>
29
#include <language/duchain/stringhelpers.h>
30
#include <language/duchain/parsingenvironment.h>
31
#include <interfaces/icore.h>
32
#include <interfaces/iprojectcontroller.h>
33
#include <interfaces/iuicontroller.h>
34
#include <interfaces/iproject.h>
35
#include <project/projectmodel.h>
37
#include "editorintegrator.h"
38
#include "../parser/parsesession.h"
40
#include "phpdefaultvisitor.h"
41
#include "classdeclaration.h"
42
#include "classmethoddeclaration.h"
43
#include "functiondeclaration.h"
48
using namespace KDevelop;
53
bool isMatch(Declaration* declaration, DeclarationType declarationType)
55
if (declarationType == ClassDeclarationType
56
&& dynamic_cast<ClassDeclaration*>(declaration)
59
} else if (declarationType == FunctionDeclarationType
60
&& dynamic_cast<FunctionDeclaration*>(declaration)
63
} else if (declarationType == ConstantDeclarationType
64
&& declaration->abstractType() && declaration->abstractType()->modifiers() & AbstractType::ConstModifier
65
&& (!declaration->context() || declaration->context()->type() != DUContext::Class)
68
} else if (declarationType == GlobalVariableDeclarationType
69
&& declaration->kind() == Declaration::Instance
70
&& !(declaration->abstractType() && declaration->abstractType()->modifiers() & AbstractType::ConstModifier)
77
Declaration* findDeclarationImportHelper(DUContext* currentContext, QualifiedIdentifier id,
78
DeclarationType declarationType, AstNode* node, EditorIntegrator* editor)
80
ifDebug(kDebug() << id.toString() << declarationType;)
81
if (declarationType == ClassDeclarationType && id == QualifiedIdentifier("self")) {
82
DUChainReadLocker lock(DUChain::lock());
83
if (currentContext->parentContext()) {
84
Declaration* declaration = currentContext->parentContext()->owner();
87
} else if (declarationType == ClassDeclarationType && id == QualifiedIdentifier("parent")) {
88
//there can be just one Class-Context imported
89
DUChainReadLocker lock(DUChain::lock());
90
if (currentContext->parentContext()) {
91
foreach(const DUContext::Import &i, currentContext->parentContext()->importedParentContexts()) {
92
if (i.context(currentContext->topContext())->type() == DUContext::Class) {
93
return i.context(currentContext->topContext())->owner();
98
QList<Declaration*> foundDeclarations;
99
DUChainReadLocker lock(DUChain::lock());
100
foundDeclarations = currentContext->topContext()->findDeclarations(id);
101
foreach(Declaration *declaration, foundDeclarations) {
102
if (isMatch(declaration, declarationType)) {
106
if ( currentContext->url() == IndexedString("InternalFunctions.php") ) {
107
// when compiling php internal functions, we don't need to ask the persistent symbol table for anything
110
if (declarationType != GlobalVariableDeclarationType) {
111
ifDebug(kDebug() << "No declarations found with findDeclarations, trying through PersistentSymbolTable" << id.toString();)
113
const IndexedDeclaration* declarations = 0;
114
PersistentSymbolTable::self().declarations(id, nr, declarations);
115
ifDebug(kDebug() << "found declarations:" << nr;)
118
DUChainWriteLocker wlock(DUChain::lock());
119
for (uint i = 0; i < nr; ++i) {
120
ParsingEnvironmentFilePointer env = DUChain::self()->environmentFileForDocument(declarations[i].indexedTopContext());
122
ifDebug(kDebug() << "skipping declaration, missing meta-data";)
125
if(env->language() != IndexedString("Php")) {
126
ifDebug(kDebug() << "skipping declaration, invalid language" << env->language().str();)
130
if (!declarations[i].declaration()) {
131
ifDebug(kDebug() << "skipping declaration, doesn't have declaration";)
133
} else if (!isMatch(declarations[i].declaration(), declarationType)) {
134
ifDebug(kDebug() << "skipping declaration, doesn't match with declarationType";)
137
TopDUContext* top = declarations[i].declaration()->context()->topContext();
139
if (top->url() != IndexedString("InternalFunctions.php") && ICore::self()) {
140
bool loadedProjectContainsUrl = false;
141
foreach(IProject *project, ICore::self()->projectController()->projects()) {
142
if (project->fileSet().contains(top->url())) {
143
loadedProjectContainsUrl = true;
147
if (!loadedProjectContainsUrl) {
148
ifDebug(kDebug() << "skipping declaration, not in loaded project";)
152
currentContext->topContext()->addImportedParentContext(top);
153
currentContext->topContext()->parsingEnvironmentFile()
154
->addModificationRevisions(top->parsingEnvironmentFile()->allModificationRevisions());
155
ifDebug(kDebug() << "using" << declarations[i].declaration()->toString() << top->url().str();)
156
return declarations[i].declaration();
164
QByteArray formatComment(AstNode* node, EditorIntegrator* editor)
166
return KDevelop::formatComment(editor->parseSession()->docComment(node->startToken).toUtf8());
169
//Helper visitor to extract a commonScalar node
170
//used to get the value of an function call argument
171
class ScalarExpressionVisitor : public DefaultVisitor
174
ScalarExpressionVisitor() : m_node(0) {}
175
CommonScalarAst* node() const {
179
virtual void visitCommonScalar(CommonScalarAst* node) {
182
CommonScalarAst* m_node;
185
CommonScalarAst* findCommonScalar(AstNode* node)
187
ScalarExpressionVisitor visitor;
188
visitor.visitNode(node);
189
return visitor.node();
192
bool includeExists(const KUrl &url)
195
DUChainReadLocker lock(DUChain::lock());
196
///TODO: this may load the chain into memory, do we really want that here?
197
if (DUChain::self()->chainForDocument(url)) {
201
if ( url.isLocalFile() ) {
202
return QFile::exists(url.toLocalFile());
208
KUrl getUrlForBase(const QString &includeFile, const KUrl &baseUrl) {
209
if ( includeFile.isEmpty() ) {
213
if ( includeFile[0] == '/' ) {
214
url.setPath(includeFile);
216
url.addPath(includeFile);
222
IndexedString findIncludeFileUrl(const QString &includeFile, const KUrl ¤tUrl)
224
if ( includeFile.isEmpty() ) {
225
return IndexedString();
228
// check remote files
229
if ( includeFile.startsWith(QLatin1String("http://"), Qt::CaseInsensitive)
230
|| includeFile.startsWith(QLatin1String("ftp://"), Qt::CaseInsensitive) ) {
231
// always expect remote includes to exist
232
return IndexedString(includeFile);
237
// look for file relative to current url
238
url = getUrlForBase(includeFile, currentUrl.upUrl());
239
if ( ICore::self()->projectController()->findProjectForUrl(url) || includeExists(url) ) {
240
return IndexedString(url);
243
// look for file relative to current project base
244
IProject* ownProject = ICore::self()->projectController()->findProjectForUrl(currentUrl);
246
url = getUrlForBase(includeFile, ownProject->folder());
247
if ( ownProject->inProject(url) || includeExists(url) ) {
248
return IndexedString(url);
252
// now look in all other projects
253
foreach(IProject* project, ICore::self()->projectController()->projects()) {
254
if ( project == ownProject ) {
257
url = getUrlForBase(includeFile, project->folder());
258
if ( project->inProject(url) || includeExists(url) ) {
259
return IndexedString(url);
263
//TODO configurable include paths
265
return IndexedString();
268
IndexedString getIncludeFileForNode(UnaryExpressionAst* node, EditorIntegrator* editor) {
269
if ( node->includeExpression ) {
270
//find name of the constant (first argument of the function call)
271
CommonScalarAst* scalar = findCommonScalar(node->includeExpression);
272
if (scalar && scalar->string != -1) {
273
QString str = editor->parseSession()->symbol(scalar->string);
274
str = str.mid(1, str.length() - 2);
275
if ( str == "." || str == ".." || str.endsWith('/') ) {
276
return IndexedString();
278
return findIncludeFileUrl(str, editor->currentUrl().toUrl());
282
return IndexedString();
285
QString prettyName(Declaration* dec) {
286
if ( ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec) ) {
287
return classDec->prettyName();
288
} else if ( ClassMethodDeclaration* classMember = dynamic_cast<ClassMethodDeclaration*>(dec) ) {
289
return classMember->prettyName();
290
} else if ( FunctionDeclaration* func = dynamic_cast<FunctionDeclaration*>(dec) ) {
291
return func->prettyName();
293
return dec->identifier().toString();