1 /*
2 SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com>
3 SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com>
4 SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "phplanguagesupport.h"
10
11 #include <QMutexLocker>
12 #include <QReadWriteLock>
13
14 #include <kpluginfactory.h>
15 #include <kpluginloader.h>
16 #include <KTextEditor/Document>
17
18 #include <interfaces/icore.h>
19 #include <interfaces/ilanguagecontroller.h>
20 #include <interfaces/iplugincontroller.h>
21 #include <interfaces/idocument.h>
22 #include <interfaces/iproject.h>
23 #include <language/backgroundparser/backgroundparser.h>
24 #include <language/duchain/duchain.h>
25 #include <language/duchain/duchainlock.h>
26 #include <interfaces/idocumentcontroller.h>
27 #include <interfaces/contextmenuextension.h>
28 #include <language/interfaces/editorcontext.h>
29
30 #include "phpparsejob.h"
31 #include "phphighlighting.h"
32 #include "kdevphpversion.h"
33 #include "phpdebug.h"
34 #include "codegen/refactoring.h"
35
36 #include <language/codecompletion/codecompletion.h>
37 #include <language/codecompletion/codecompletionmodel.h>
38
39 #include "completion/model.h"
40 #include "completion/worker.h"
41
42 #include "navigation/navigationwidget.h"
43 #include <language/duchain/parsingenvironment.h>
44
45 #include "duchain/helper.h"
46 #include <QTimer>
47
48 using namespace KTextEditor;
49 using namespace KDevelop;
50
51 K_PLUGIN_FACTORY_WITH_JSON(KDevPhpSupportFactory, "kdevphpsupport.json", registerPlugin<Php::LanguageSupport>(); )
52
53 namespace Php
54 {
55
LanguageSupport(QObject * parent,const QVariantList &)56 LanguageSupport::LanguageSupport(QObject* parent, const QVariantList& /*args*/)
57 : KDevelop::IPlugin(QStringLiteral("kdevphpsupport"), parent),
58 KDevelop::ILanguageSupport()
59 {
60 Q_ASSERT(internalFunctionFile().toUrl().isValid());
61
62 m_highlighting = new Php::Highlighting(this);
63 m_refactoring = new Php::Refactoring(this);
64
65 auto* ccModel = new CodeCompletionModel(this);
66 new KDevelop::CodeCompletion(this, ccModel, name());
67 }
68
~LanguageSupport()69 LanguageSupport::~LanguageSupport()
70 {
71 parseLock()->lockForWrite();
72 //By locking the parse-mutexes, we make sure that parse- and preprocess-jobs
73 //get a chance to finish in a good state
74 parseLock()->unlock();
75 }
76
createParseJob(const IndexedString & url)77 KDevelop::ParseJob *LanguageSupport::createParseJob(const IndexedString &url)
78 {
79 auto *job = new ParseJob(url, this);
80
81 // bypass the 5 MB maximum file size limit for the internal file
82 if (url == internalFunctionFile()) {
83 job->setMaximumFileSize(std::numeric_limits<qint64>::max());
84 job->setMinimumFeatures(TopDUContext::AllDeclarationsAndContexts);
85 }
86
87 return job;
88 }
89
name() const90 QString LanguageSupport::name() const
91 {
92 return QStringLiteral("Php");
93 }
94
codeHighlighting() const95 KDevelop::ICodeHighlighting* LanguageSupport::codeHighlighting() const
96 {
97 return m_highlighting;
98 }
99
contextMenuExtension(Context * context,QWidget * parent)100 ContextMenuExtension LanguageSupport::contextMenuExtension(Context* context, QWidget* parent)
101 {
102 ContextMenuExtension cm;
103 EditorContext *ed = dynamic_cast<KDevelop::EditorContext *>(context);
104
105 if (ed && ICore::self()->languageController()->languagesForUrl(ed->url()).contains(this)) {
106 // It's safe to add our own ContextMenuExtension.
107 m_refactoring->fillContextMenu(cm, context, parent);
108 }
109 return cm;
110 }
111
wordUnderCursor(const QUrl & url,const Cursor & position)112 QPair<QString, Range> LanguageSupport::wordUnderCursor(const QUrl& url, const Cursor& position)
113 {
114 KDevelop::IDocument* doc = core()->documentController()->documentForUrl(url);
115 if(!doc || !doc->textDocument())
116 return {};
117
118 int lineNumber = position.line();
119 int lineLength = doc->textDocument()->lineLength(lineNumber);
120
121 QString line = doc->textDocument()->text(Range(lineNumber, 0, lineNumber, lineLength));
122
123 int startCol = position.column();
124 for ( ; startCol >= 0; --startCol ) {
125 if ( !line[startCol].isLetter() && line[startCol] != '_' ) {
126 // don't include the wrong char
127 if ( startCol != position.column() ) {
128 ++startCol;
129 }
130 break;
131 }
132 }
133 int endCol = position.column();
134 for ( ; endCol <= lineLength; ++endCol ) {
135 if ( !line[endCol].isLetter() && line[endCol] != '_' ) {
136 break;
137 }
138 }
139 QString word = line.mid(startCol, endCol - startCol);
140 Range range(lineNumber, startCol, lineNumber, endCol);
141 return qMakePair(word, range);
142 }
143
isMagicConstant(QPair<QString,Range> word)144 bool isMagicConstant(QPair<QString, Range> word) {
145 if ( word.second.isValid() && !word.second.isEmpty() ) {
146 if ( word.first == QLatin1String("__FILE__") || word.first == QLatin1String("__LINE__") ||
147 word.first == QLatin1String("__METHOD__") || word.first == QLatin1String("__CLASS__") ||
148 word.first == QLatin1String("__FUNCTION__") || word.first == QLatin1String("__NAMESPACE__") ||
149 word.first == QLatin1String("__DIR__") || word.first == QLatin1String("__TRAIT__")
150 )
151 {
152 ///TODO: maybe we should use the tokenizer to really make sure this is such a token
153 /// and we are not inside a string, comment or similar
154 /// otoh, it doesn't hurt imo
155 return true;
156 }
157 }
158 return false;
159 }
160
specialLanguageObjectNavigationWidget(const QUrl & url,const Cursor & position)161 QPair<QWidget*, Range> LanguageSupport::specialLanguageObjectNavigationWidget(const QUrl& url, const Cursor& position)
162 {
163 QPair<QString, Range> word = wordUnderCursor(url, position);
164 if ( isMagicConstant(word) ) {
165 DUChainReadLocker lock;
166 if (TopDUContext* top = standardContext(url)) {
167 return {new NavigationWidget(TopDUContextPointer(top), position, word.first), word.second};
168 } else {
169 return {nullptr, Range::invalid()};
170 }
171 }
172 return ILanguageSupport::specialLanguageObjectNavigationWidget(url, position);
173 }
174
specialLanguageObjectRange(const QUrl & url,const Cursor & position)175 Range LanguageSupport::specialLanguageObjectRange(const QUrl& url, const Cursor& position)
176 {
177 QPair<QString, Range> word = wordUnderCursor(url, position);
178 if ( isMagicConstant(word) ) {
179 return word.second;
180 }
181 return ILanguageSupport::specialLanguageObjectRange(url, position);
182 }
183
184 }
185
186 #include "phplanguagesupport.moc"
187