2
Copyright (C) 2005 by Nicolas Escuder <n.escuder@intra-links.com>
3
Copyright (C) 2001 by smeier@kdevelop.org
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
version 2, License as published by the Free Software Foundation.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
17
Boston, MA 02110-1301, USA.
20
#include "phpsupportpart.h"
25
#include <qfileinfo.h>
26
#include <qpopupmenu.h>
27
#include <qprogressbar.h>
28
#include <qstringlist.h>
29
#include <qtextstream.h>
32
#include <qwhatsthis.h>
36
#include <kapplication.h>
38
#include <khtmlview.h>
40
#include <kmessagebox.h>
43
#include <kstatusbar.h>
44
#include <kparts/browserextension.h>
47
#include <kdevproject.h>
48
#include <kdevmainwindow.h>
49
#include <kdevpartcontroller.h>
50
#include <codemodel.h>
52
#include <kdevplugininfo.h>
53
#include <kiconloader.h>
55
#include "phpconfigdata.h"
56
#include "phpconfigwidget.h"
57
#include "phpcodecompletion.h"
58
#include "phpparser.h"
59
#include "phpnewclassdlg.h"
62
#include "phphtmlview.h"
63
#include "phperrorview.h"
65
#include "phpsupport_event.h"
69
static const KDevPluginInfo data("kdevphpsupport");
70
K_EXPORT_COMPONENT_FACTORY( libkdevphpsupport, PHPSupportFactory( data ) )
72
PHPSupportPart::PHPSupportPart(QObject *parent, const char *name, const QStringList &)
73
: KDevLanguageSupport(&data, parent, name ? name : "PHPSupportPart")
78
setInstance(PHPSupportFactory::instance());
80
setXMLFile("kdevphpsupport.rc");
82
connect( core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
83
connect( core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()) );
84
connect( partController(), SIGNAL(savedFile(const KURL&)),
85
this, SLOT(savedFile(const KURL&)) );
86
connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
87
this, SLOT(projectConfigWidget(KDialogBase*)) );
91
action = new KAction( i18n("&Run"), "exec",Key_F9,
92
this, SLOT(slotRun()),
93
actionCollection(), "build_execute" );
94
action->setToolTip(i18n("Run"));
95
action->setWhatsThis(i18n("<b>Run</b><p>Executes script on a terminal or a webserver."));
97
action = new KAction( i18n("&New Class..."),0,
98
this, SLOT(slotNewClass()),
99
actionCollection(), "project_new_class" );
100
action->setToolTip(i18n("New class"));
101
action->setWhatsThis(i18n("<b>New class</b><p>Runs New Class wizard."));
103
m_phpErrorView = new PHPErrorView(this, 0, "phpErrorWidget");
104
m_phpErrorView->setIcon( SmallIcon("info") );
106
QWhatsThis::add(m_phpErrorView, i18n("<b>PHP problems</b><p>This view shows PHP parser warnings, errors, and fatal errors."));
107
mainWindow()->embedOutputView(m_phpErrorView, i18n("Problems"), i18n("Problems"));
109
phpExeProc = new KShellProcess("/bin/sh");
110
connect( phpExeProc, SIGNAL(receivedStdout (KProcess*, char*, int)),
111
this, SLOT(slotReceivedPHPExeStdout (KProcess*, char*, int)));
112
connect( phpExeProc, SIGNAL(receivedStderr (KProcess*, char*, int)),
113
this, SLOT(slotReceivedPHPExeStderr (KProcess*, char*, int)));
114
connect( phpExeProc, SIGNAL(processExited(KProcess*)),
115
this, SLOT(slotPHPExeExited(KProcess*)));
117
m_htmlView = new PHPHTMLView(this);
118
mainWindow()->embedOutputView(m_htmlView->view(), i18n("PHP"), i18n("PHP"));
119
connect( m_htmlView, SIGNAL(started(KIO::Job*)),
120
this, SLOT(slotWebJobStarted(KIO::Job*)));
122
configData = new PHPConfigData(projectDom());
123
connect( configData, SIGNAL(configStored()),
124
this, SLOT(slotConfigStored()));
126
m_codeCompletion = new PHPCodeCompletion(this, configData);
128
new KAction(i18n("Complete Text"), CTRL+Key_Space, m_codeCompletion, SLOT(cursorPositionChanged()), actionCollection(), "edit_complete_text");
130
connect( partController(), SIGNAL(activePartChanged(KParts::Part*)),
131
this, SLOT(slotActivePartChanged(KParts::Part *)));
132
connect( this, SIGNAL(fileParsed( PHPFile* )), this, SLOT(slotfileParsed( PHPFile* )));
135
PHPSupportPart::~PHPSupportPart()
147
if ( m_phpErrorView ) {
148
mainWindow()->removeView( m_phpErrorView );
149
delete( m_phpErrorView );
150
m_phpErrorView = NULL;
153
kdDebug(9018) << "remove codeCompletition" << endl;
154
if ( m_codeCompletion )
155
delete( m_codeCompletion );
157
kdDebug(9018) << "remove configData" << endl;
159
delete( configData );
162
kdDebug(9018) << "remove htmlView" << endl;
163
mainWindow()->removeView( m_htmlView->view() );
164
delete( m_htmlView );
168
kdDebug(9018) << "remove phpExec" << endl;
170
delete( phpExeProc );
172
kdDebug(9018) << "finish" << endl;
175
void PHPSupportPart::slotActivePartChanged(KParts::Part *part) {
176
kdDebug(9018) << "enter slotActivePartChanged" << endl;
177
if (!part || !part->widget())
179
m_editInterface = dynamic_cast<KTextEditor::EditInterface*>(part);
180
if (m_editInterface) { // connect to the editor
181
disconnect(part, 0, this, 0 ); // to make sure that it is't connected twice
182
if (configData->getRealtimeParsing()) {
183
connect(part,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
185
m_codeCompletion->setActiveEditorPart(part);
187
kdDebug(9018) << "exit slotActivePartChanged" << endl;
190
void PHPSupportPart::slotTextChanged() {
191
kdDebug(9018) << "enter text changed" << endl;
193
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
197
QString fileName = ro_part->url().directory() + "/" + ro_part->url().fileName();
200
if (m_parser->hasFile( fileName ))
201
m_parser->reparseFile( fileName );
205
void PHPSupportPart::slotConfigStored() {
206
// fake a changing, this will read the configuration again and install the connects
207
slotActivePartChanged(partController()->activePart());
210
void PHPSupportPart::projectConfigWidget(KDialogBase *dlg) {
211
QVBox *vbox = dlg->addVBoxPage(i18n( "PHP Specific" ), i18n("PHP Settings"), BarIcon( "source", KIcon::SizeMedium ));
212
PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget");
213
connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
216
void PHPSupportPart::slotNewClass() {
217
QStringList classNames = sortedNameList( codeModel()->globalNamespace()->classList() );
218
PHPNewClassDlg dlg(classNames,project()->projectDirectory());
222
void PHPSupportPart::slotRun() {
223
configData = new PHPConfigData(projectDom());
224
if (validateConfig()) {
225
mainWindow()->raiseView(m_htmlView->view());
226
PHPConfigData::InvocationMode mode = configData->getInvocationMode() ;
227
if (mode == PHPConfigData::Web) {
228
executeOnWebserver();
230
else if (mode == PHPConfigData::Shell) {
236
bool PHPSupportPart::validateConfig() {
237
if (!configData->validateConfig()) {
238
KMessageBox::information(0,i18n("There is no configuration for executing a PHP file.\nPlease set the correct values in the next dialog."));
239
KDialogBase dlg(KDialogBase::TreeList, i18n("Customize PHP Mode"),
240
KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, 0,
241
"php config dialog");
243
QVBox *vbox = dlg.addVBoxPage(i18n("PHP Settings"));
244
PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget");
245
connect( &dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
248
if (configData->validateConfig()) {
254
void PHPSupportPart::executeOnWebserver() {
255
// Save all files once
256
if (partController()->saveAllFiles()==false)
257
return; //user cancelled
259
// Figure out the name of the remote file
260
QString weburl = configData->getWebURL();
261
QString file = getExecuteFile();
263
// Force KHTMLPart to reload the page
264
KParts::BrowserExtension* be = m_htmlView->browserExtension();
266
KParts::URLArgs urlArgs( be->urlArgs() );
267
urlArgs.reload = true;
268
be->setURLArgs( urlArgs );
271
// Acutally do the request
273
m_htmlView->openURL(KURL(weburl + file));
277
QString PHPSupportPart::getExecuteFile() {
279
PHPConfigData::StartupFileMode mode = configData->getStartupFileMode();
281
QString weburl = configData->getWebURL();
282
if (mode == PHPConfigData::Current) {
283
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
285
if ( configData->getInvocationMode() == PHPConfigData::Web )
286
file = URLUtil::relativePath( project()->projectDirectory(), ro_part->url().path() );
288
file = ro_part->url().path();
291
if (mode == PHPConfigData::Default) {
292
file = configData->getStartupFile();
297
void PHPSupportPart::slotWebJobStarted(KIO::Job* job) {
298
if (job && job->className() == QString("KIO::TransferJob")) {
299
kdDebug(9018) << endl << "job started" << job->progressId();
300
KIO::TransferJob *tjob = static_cast<KIO::TransferJob*>(job);
301
connect( tjob, SIGNAL(data(KIO::Job*, const QByteArray&)),
302
this, SLOT(slotWebData(KIO::Job*, const QByteArray&)));
303
connect( tjob, SIGNAL(result(KIO::Job*)),
304
this, SLOT(slotWebResult(KIO::Job*)));
308
void PHPSupportPart::slotWebData(KIO::Job* /*job*/,const QByteArray& data) {
309
kdDebug(9018) << "slotWebData()" << endl;
310
QString strData(data);
311
m_phpExeOutput += strData;
314
void PHPSupportPart::slotWebResult(KIO::Job* /*job*/) {
315
kdDebug(9018) << "slotWebResult()" << endl;
316
QString file = getExecuteFile();
317
PHPFile *pfile = new PHPFile(this, file);
318
pfile->ParseStdout(m_phpExeOutput);
322
void PHPSupportPart::executeInTerminal() {
323
kdDebug(9018) << "slotExecuteInTerminal()" << endl;
325
// Save all files once
326
if (partController()->saveAllFiles()==false)
327
return; //user cancelled
329
QString file = getExecuteFile();
331
if (m_htmlView == 0) {
332
m_htmlView = new PHPHTMLView(this);
333
mainWindow()->embedOutputView(m_htmlView->view(), i18n("PHP"), i18n("PHP") );
340
phpExeProc->clearArguments();
341
*phpExeProc << configData->getPHPExecPath();
344
*phpExeProc << KShellProcess::quote(file);
345
kdDebug(9018) << "" << file.latin1() << endl;
346
phpExeProc->start(KProcess::NotifyOnExit,KProcess::All);
348
// core()->gotoDocumentationFile(KURL("http://www.php.net"));
351
void PHPSupportPart::slotReceivedPHPExeStdout (KProcess* /*proc*/, char* buffer, int buflen) {
352
kdDebug(9018) << "slotPHPExeStdout()" << endl;
353
m_phpExeOutput += QString::fromLocal8Bit(buffer,buflen+1);
355
QString buf = buffer;
356
if (configData->getInvocationMode() == PHPConfigData::Shell)
357
buf.replace("\n", "<br>");
358
m_htmlView->write(buf);
361
void PHPSupportPart::slotReceivedPHPExeStderr (KProcess* /*proc*/, char* buffer, int buflen) {
362
kdDebug(9018) << "slotPHPExeStderr()" << endl;
363
m_phpExeOutput += QString::fromLocal8Bit(buffer,buflen+1);
365
QString buf = buffer;
366
if (configData->getInvocationMode() == PHPConfigData::Shell)
367
buf.replace("\n", "<br>");
368
m_htmlView->write(buf);
371
void PHPSupportPart::slotPHPExeExited (KProcess* /*proc*/) {
372
kdDebug(9018) << "slotPHPExeExited()" << endl;
374
QString file = getExecuteFile();
375
PHPFile *pfile = new PHPFile(this, file);
376
pfile->ParseStdout(m_phpExeOutput);
380
void PHPSupportPart::projectOpened()
382
kdDebug(9018) << "projectOpened()" << endl;
384
connect( project(), SIGNAL(addedFilesToProject(const QStringList &)),
385
this, SLOT(addedFilesToProject(const QStringList &)) );
386
connect( project(), SIGNAL(removedFilesFromProject(const QStringList &)),
387
this, SLOT(removedFilesFromProject(const QStringList &)) );
390
m_parser = new PHPParser( this );
394
// We want to parse only after all components have been
395
// properly initialized
396
QTimer::singleShot(500, this, SLOT( initialParse() ) );
399
void PHPSupportPart::initialParse( )
405
kdDebug( 9018 ) << "No project" << endl;
413
void PHPSupportPart::projectClosed()
415
kdDebug(9018) << "projectClosed()" << endl;
424
bool PHPSupportPart::parseProject()
426
kdDebug(9018) << "parseProject()" << endl;
427
mainWindow() ->statusBar() ->message( i18n( "Updating..." ) );
429
kapp->setOverrideCursor( waitCursor );
433
_jd->files = project()->allFiles();
435
QProgressBar* bar = new QProgressBar( _jd->files.count( ), mainWindow( ) ->statusBar( ) );
436
bar->setMinimumWidth( 120 );
437
bar->setCenterIndicator( true );
438
mainWindow()->statusBar()->addWidget( bar );
441
_jd->progressBar = bar;
442
_jd->it = _jd->files.begin();
443
_jd->dir.setPath( project()->projectDirectory() );
445
QTimer::singleShot( 0, this, SLOT( slotParseFiles() ) );
449
void PHPSupportPart::slotParseFiles()
451
kdDebug(9018) << "slotParseFiles()" << endl;
455
if ( _jd->it != _jd->files.end() )
457
_jd->progressBar->setProgress( _jd->progressBar->progress() + 1 );
459
QFileInfo fileInfo( _jd->dir, *( _jd->it ) );
461
if ( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() )
463
QString absFilePath = URLUtil::canonicalPath( fileInfo.absFilePath() );
465
// if ( isValidSource( absFilePath ) )
468
m_parser->addFile( absFilePath );
473
QTimer::singleShot( 0, this, SLOT( slotParseFiles() ) );
475
else // finished or interrupted
477
kapp->restoreOverrideCursor();
478
mainWindow()->statusBar()->removeWidget( _jd->progressBar );
479
mainWindow()->statusBar()->message( i18n( "Done" ), 2000 );
481
emit updatedSourceInfo();
483
m_parser->startParse();
492
void PHPSupportPart::addedFilesToProject(const QStringList &fileList)
494
kdDebug(9018) << "addedFilesToProject()" << endl;
496
QStringList::ConstIterator it;
498
for ( it = fileList.begin(); it != fileList.end(); ++it )
500
QFileInfo fileInfo( project()->projectDirectory(), *it );
502
m_parser->addFile( fileInfo.absFilePath() );
503
emit addedSourceInfo( fileInfo.absFilePath() );
508
void PHPSupportPart::removedFilesFromProject(const QStringList &fileList)
510
kdDebug(9018) << "removedFilesFromProject()" << endl;
512
QStringList::ConstIterator it;
514
for ( it = fileList.begin(); it != fileList.end(); ++it )
516
QFileInfo fileInfo( project()->projectDirectory(), *it );
517
QString path = fileInfo.absFilePath();
518
if ( codeModel()->hasFile(path) ) {
519
emit aboutToRemoveSourceInfo( path );
520
codeModel()->removeFile( codeModel()->fileByName(path) );
525
void PHPSupportPart::savedFile(const KURL &fileName)
527
kdDebug(9018) << "savedFile()" << fileName.fileName() << endl;
528
/// @fixme when activated could cause stop
531
if (m_parser->hasFile( fileName.path() )) {
532
m_parser->reparseFile( fileName.path() );
538
QString PHPSupportPart::getIncludePath()
540
return configData->getPHPIncludePath();
543
QString PHPSupportPart::getExePath()
545
return configData->getPHPExecPath();
548
KDevLanguageSupport::Features PHPSupportPart::features()
550
return Features(Classes | Functions);
553
KMimeType::List PHPSupportPart::mimeTypes( )
555
KMimeType::List list;
556
KMimeType::Ptr mime = KMimeType::mimeType( "application/x-php" );
560
mime = KMimeType::mimeType( "text/plain" );
566
void PHPSupportPart::customEvent( QCustomEvent* ev )
568
// kdDebug(9018) << "phpSupportPart::customEvent(" << ev->type() << ") " << QThread::currentThread() << endl;
570
if ( ev->type() < Event_AddFile || ev->type() > Event_AddFixme )
575
FileParseEvent* event = (FileParseEvent*) ev;
576
NamespaceDom ns = codeModel()->globalNamespace();
577
FileDom m_file = codeModel()->fileByName( event->fileName() );
580
m_file = codeModel()->create<FileModel>();
581
m_file->setName( event->fileName() );
582
codeModel()->addFile( m_file );
585
switch (int(ev->type())) {
587
m_parser->addFile( event->fileName() );
590
case Event_StartParse:
591
// kdDebug(9018) << "StartParse " << event->fileName() << endl;
595
if ( codeModel()->hasFile( event->fileName() ) ) {
596
emit aboutToRemoveSourceInfo( event->fileName() );
597
codeModel()->removeFile( codeModel()->fileByName( event->fileName() ) );
598
emit removedSourceInfo( event->fileName() );
600
ErrorView()->removeAllProblems( event->fileName() );
605
// kdDebug(9018) << "AddClass " << event->name() << endl;
606
ClassDom nClass = codeModel()->create<ClassModel>();
607
nClass->setFileName( event->fileName() );
608
nClass->setName( event->name() );
609
nClass->setStartPosition( event->posititon(), 0);
611
m_file->addClass( nClass );
612
if ( event->arguments().isEmpty() != TRUE )
613
nClass->addBaseClass( event->arguments() );
615
ns->addClass( nClass );
620
case Event_CloseClass:
621
if ( LastClass != NULL ) {
622
// kdDebug(9018) << "CloseClass " << LastClass->name() << endl;
623
LastClass->setEndPosition( event->posititon(), 0 );
630
case Event_AddFunction:
632
// kdDebug(9018) << "AddFunction " << event->name() << endl;
633
FunctionDom nMethod = codeModel()->create<FunctionModel>();
634
nMethod->setFileName( event->fileName() );
635
nMethod->setName( event->name() );
636
nMethod->setStartPosition( event->posititon(), 0 );
638
ArgumentDom nArgument;
639
nArgument = codeModel()->create<ArgumentModel>();
640
nArgument->setType(event->arguments().stripWhiteSpace().local8Bit());
641
nMethod->addArgument( nArgument );
643
if (LastClass != NULL) {
644
LastClass->addFunction(nMethod);
646
ns->addFunction(nMethod);
648
LastMethod = nMethod;
652
case Event_SetFunction:
653
if ( LastMethod != NULL ) {
654
// kdDebug(9018) << "SetFunction " << LastMethod->name() << " " << event->name() << endl;
655
if ( event->name() == "static" )
656
LastMethod->setStatic(true);
657
else if ( event->name() == "abstract" )
658
LastMethod->setAbstract(true);
659
else if ( event->name() == "private" )
660
LastMethod->setAccess(FunctionModel::Private);
661
else if ( event->name() == "public" )
662
LastMethod->setAccess(FunctionModel::Public);
663
else if ( event->name() == "protected" )
664
LastMethod->setAccess(FunctionModel::Protected);
665
else if ( event->name() == "result" ) {
667
if (event->arguments().lower() == "$this" && LastClass ) {
668
ret = LastClass->name();
670
LastMethod->setResultType(ret);
675
case Event_CloseFunction:
676
if ( LastMethod != NULL ) {
677
// kdDebug(9018) << "CloseFunction " << LastMethod->name() << endl;
678
LastMethod->setEndPosition( event->posititon(), 0 );
684
case Event_AddVariable:
686
VariableDom nVariable = codeModel()->create<VariableModel>();
688
nVariable->setFileName( event->fileName() );
689
nVariable->setName( event->name() );
690
nVariable->setStartPosition( event->posititon(), 0 );
691
nVariable->setAccess(VariableModel::Public);
693
if ( event->arguments().isEmpty() != TRUE )
694
nVariable->setType( event->arguments() );
696
if ( LastClass != NULL && ( LastMethod == NULL || event->global() == TRUE ) ) {
697
// kdDebug(9018) << "AddVariable To Class " << LastClass->name() << " " << nVariable->name() << endl;
698
LastClass->addVariable(nVariable);
700
if ( LastMethod != NULL ) {
701
kdDebug(9018) << "AddVariable " << LastMethod->name() << " " << nVariable->name() << endl;
703
ns->addVariable(nVariable);
706
LastVariable = nVariable;
710
case Event_SetVariable:
711
if ( LastVariable != NULL ) {
712
// kdDebug(9018) << "SetVariable " << LastVariable->name() << " " << event->arguments() << endl;
713
if ( event->arguments() == "static" )
714
LastVariable->setStatic(true);
715
else if ( event->arguments() == "private" )
716
LastVariable->setAccess(FunctionModel::Private);
717
else if ( event->arguments() == "public" )
718
LastVariable->setAccess(FunctionModel::Public);
719
else if ( event->arguments() == "protected" )
720
LastVariable->setAccess(FunctionModel::Protected);
725
ErrorView()->reportProblem(Todo, event->fileName(), event->posititon(), event->arguments());
729
ErrorView()->reportProblem(Fixme, event->fileName(), event->posititon(), event->arguments());
733
// kdDebug(9018) << "EndParse " << event->fileName() << endl;
734
emit addedSourceInfo( event->fileName() );
740
kapp->processEvents();
743
PHPErrorView *PHPSupportPart::ErrorView( ) {
744
return m_phpErrorView;
747
PHPParser *PHPSupportPart::Parser( ) {
751
#include "phpsupportpart.moc"