00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "phpsupportpart.h"
00013
00014 #include <iostream>
00015
00016 #include <qdir.h>
00017 #include <qfileinfo.h>
00018 #include <qpopupmenu.h>
00019 #include <qprogressbar.h>
00020 #include <qstringlist.h>
00021 #include <qtextstream.h>
00022 #include <qtimer.h>
00023 #include <qvbox.h>
00024 #include <qwhatsthis.h>
00025
00026 #include <kaction.h>
00027 #include <kapplication.h>
00028 #include <kdebug.h>
00029 #include <khtmlview.h>
00030 #include <klocale.h>
00031 #include <kmessagebox.h>
00032 #include <kprocess.h>
00033 #include <kregexp.h>
00034 #include <kstatusbar.h>
00035 #include <kparts/browserextension.h>
00036
00037 #include <kdevcore.h>
00038 #include <kdevproject.h>
00039 #include <kdevmainwindow.h>
00040 #include <kdevpartcontroller.h>
00041 #include <codemodel.h>
00042 #include <domutil.h>
00043
00044 #include "phpconfigdata.h"
00045 #include "phpconfigwidget.h"
00046 #include "phpconfigparserwidget.h"
00047 #include "phpcodecompletion.h"
00048 #include "phpparser.h"
00049 #include "phpnewclassdlg.h"
00050
00051
00052 #include "phphtmlview.h"
00053 #include "phperrorview.h"
00054
00055
00056 using namespace std;
00057
00058 static const KAboutData data("kdevphpsupport", I18N_NOOP("Language"), "1.0");
00059 K_EXPORT_COMPONENT_FACTORY( libkdevphpsupport, PHPSupportFactory( &data ) )
00060
00061 PHPSupportPart::PHPSupportPart(QObject *parent, const char *name, const QStringList &)
00062 : KDevLanguageSupport("PHPSupport", "php", parent, name ? name : "PHPSupportPart")
00063 {
00064 m_htmlView=0;
00065 phpExeProc=0;
00066 setInstance(PHPSupportFactory::instance());
00067
00068 setXMLFile("kdevphpsupport.rc");
00069
00070 connect( core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
00071 connect( core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()) );
00072 connect( partController(), SIGNAL(savedFile(const KURL&)),
00073 this, SLOT(savedFile(const KURL&)) );
00074 connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
00075 this, SLOT(projectConfigWidget(KDialogBase*)) );
00076
00077 KAction *action;
00078
00079 action = new KAction( i18n("&Run"), "exec",Key_F9,
00080 this, SLOT(slotRun()),
00081 actionCollection(), "build_execute" );
00082 action->setToolTip(i18n("Run"));
00083 action->setWhatsThis(i18n("<b>Run</b><p>Executes script on a terminal or a webserver."));
00084
00085 action = new KAction( i18n("&New Class..."),0,
00086 this, SLOT(slotNewClass()),
00087 actionCollection(), "project_new_class" );
00088 action->setToolTip(i18n("New class"));
00089 action->setWhatsThis(i18n("<b>New class</b><p>Runs New Class wizard."));
00090
00091 m_phpErrorView = new PHPErrorView(this);
00092 QWhatsThis::add(m_phpErrorView, i18n("<b>PHP problems</b><p>This view shows PHP parser warnings, errors, and fatal errors."));
00093 mainWindow()->embedOutputView(m_phpErrorView, i18n("PHP Problems"), i18n("PHP Problems"));
00094 connect(m_phpErrorView,SIGNAL(fileSelected(const QString&,int)),
00095 this,SLOT(slotErrorMessageSelected(const QString&,int)));
00096
00097 phpExeProc = new KShellProcess("/bin/sh");
00098 connect(phpExeProc, SIGNAL(receivedStdout (KProcess*, char*, int)),
00099 this, SLOT(slotReceivedPHPExeStdout (KProcess*, char*, int)));
00100 connect(phpExeProc, SIGNAL(receivedStderr (KProcess*, char*, int)),
00101 this, SLOT(slotReceivedPHPExeStderr (KProcess*, char*, int)));
00102 connect(phpExeProc, SIGNAL(processExited(KProcess*)),
00103 this, SLOT(slotPHPExeExited(KProcess*)));
00104
00105 m_htmlView = new PHPHTMLView(this);
00106 mainWindow()->embedPartView(m_htmlView->view(), i18n("PHP"), "PHP");
00107 connect(m_htmlView, SIGNAL(started(KIO::Job*)),
00108 this, SLOT(slotWebJobStarted(KIO::Job*)));
00109
00110 configData = new PHPConfigData(projectDom());
00111 connect(configData, SIGNAL(configStored()),
00112 this, SLOT(slotConfigStored()));
00113
00114 m_parser = new PHPParser(core(),codeModel());
00115 m_codeCompletion = new PHPCodeCompletion(configData, core(),codeModel());
00116
00117 connect(partController(), SIGNAL(activePartChanged(KParts::Part*)),
00118 this, SLOT(slotActivePartChanged(KParts::Part *)));
00119 }
00120
00121
00122 PHPSupportPart::~PHPSupportPart()
00123 {
00124 delete( m_parser );
00125 delete( m_codeCompletion );
00126 delete( configData );
00127
00128 if( m_htmlView ){
00129 mainWindow()->removeView( m_htmlView->view() );
00130 delete( m_htmlView );
00131 m_htmlView = 0;
00132 }
00133
00134 delete( phpExeProc );
00135
00136 if(m_phpErrorView){
00137 mainWindow()->removeView( m_phpErrorView );
00138
00139 m_phpErrorView = 0;
00140 }
00141 }
00142
00143 void PHPSupportPart::slotActivePartChanged(KParts::Part *part){
00144 kdDebug(9018) << "enter slotActivePartChanged" << endl;
00145 if (!part || !part->widget())
00146 return;
00147 m_editInterface = dynamic_cast<KTextEditor::EditInterface*>(part);
00148 if(m_editInterface){
00149 disconnect(part, 0, this, 0 );
00150 if(configData->getRealtimeParsing()){
00151 connect(part,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
00152 }
00153 m_codeCompletion->setActiveEditorPart(part);
00154 }
00155 kdDebug(9018) << "exit slotActivePartChanged" << endl;
00156 }
00157
00158 void PHPSupportPart::slotTextChanged(){
00159 kdDebug(9018) << "enter text changed" << endl;
00160
00161 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
00162 if (!ro_part)
00163 return;
00164
00165 QString fileName = ro_part->url().directory() + "/" + ro_part->url().fileName();
00166 kdDebug(9018) << "filename:" << fileName << endl;
00167 int numLines = m_editInterface->numLines();
00168
00169
00170 if (!project()->allFiles().contains(fileName.mid ( project()->projectDirectory().length() + 1 ))) {
00171 kdDebug(9018) << "Not Parsing file " << fileName << ", file is not part of the project" << endl;
00172 return;
00173 }
00174
00175 QStringList lines;
00176 for(int i=0;i<numLines;i++){
00177 lines.append(m_editInterface->textLine(i));
00178 }
00179
00180 if( codeModel()->hasFile(fileName) ){
00181 emit aboutToRemoveSourceInfo( fileName );
00182 codeModel()->removeFile( codeModel()->fileByName(fileName) );
00183 }
00184 m_parser->parseLines(&lines,fileName);
00185
00186 emit addedSourceInfo( fileName );
00187 kdDebug(9018) << "exit text changed" << endl;
00188 }
00189
00190 void PHPSupportPart::slotConfigStored(){
00191
00192 slotActivePartChanged(partController()->activePart());
00193 }
00194
00195
00196 void PHPSupportPart::slotErrorMessageSelected(const QString& filename,int line){
00197 kdDebug(9018) << endl << "slotWebResult()" << filename.latin1() << line;
00198 partController()->editDocument(KURL( filename ),line);
00199 }
00200 void PHPSupportPart::projectConfigWidget(KDialogBase *dlg){
00201 QVBox *vbox = dlg->addVBoxPage(i18n("PHP Settings"));
00202 PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget");
00203 connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00204
00205 vbox = dlg->addVBoxPage(i18n("PHP Parser"));
00206 PHPConfigParserWidget* wp = new PHPConfigParserWidget(configData,vbox, "php parser config widget");
00207 connect( dlg, SIGNAL(okClicked()), wp, SLOT(accept()) );
00208 }
00209
00210 void PHPSupportPart::slotNewClass(){
00211 QStringList classNames = sortedNameList( codeModel()->globalNamespace()->classList() );
00212 PHPNewClassDlg dlg(classNames,project()->projectDirectory());
00213 dlg.exec();
00214 }
00215
00216 void PHPSupportPart::slotRun(){
00217 configData = new PHPConfigData(projectDom());
00218 if(validateConfig()){
00219 mainWindow()->raiseView(m_phpErrorView);
00220 mainWindow()->raiseView(m_htmlView->view());
00221 PHPConfigData::InvocationMode mode = configData->getInvocationMode() ;
00222 if(mode == PHPConfigData::Web){
00223 executeOnWebserver();
00224 }
00225 else if(mode == PHPConfigData::Shell){
00226 executeInTerminal();
00227 }
00228 }
00229 }
00230
00231 bool PHPSupportPart::validateConfig(){
00232 if(!configData->validateConfig()){
00233 KMessageBox::information(0,i18n("There is no configuration for executing a PHP file.\nPlease set the correct values in the next dialog."));
00234 KDialogBase dlg(KDialogBase::TreeList, i18n("Customize PHP Mode"),
00235 KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, 0,
00236 "php config dialog");
00237
00238 QVBox *vbox = dlg.addVBoxPage(i18n("PHP Settings"));
00239 PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget");
00240 connect( &dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00241 dlg.exec();
00242 }
00243 if(configData->validateConfig()){
00244 return true;
00245 }
00246 return false;
00247 }
00248
00249 void PHPSupportPart::executeOnWebserver(){
00250
00251
00252 partController()->saveAllFiles();
00253
00254
00255 QString file;
00256 PHPConfigData::WebFileMode mode = configData->getWebFileMode();
00257 QString weburl = configData->getWebURL();
00258 if(mode == PHPConfigData::Current){
00259 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
00260 if(ro_part){
00261 file = QFileInfo(ro_part->url().url()).fileName();
00262 }
00263 }
00264 if(mode == PHPConfigData::Default){
00265 file = configData->getWebDefaultFile();
00266 }
00267
00268
00269 KParts::BrowserExtension* be = m_htmlView->browserExtension();
00270 if(be){
00271 KParts::URLArgs urlArgs( be->urlArgs() );
00272 urlArgs.reload = true;
00273 be->setURLArgs( urlArgs );
00274 }
00275
00276
00277 m_phpExeOutput="";
00278 m_htmlView->openURL(KURL(weburl + file));
00279 m_htmlView->show();
00280 }
00281
00282 void PHPSupportPart::slotWebJobStarted(KIO::Job* job){
00283 if (job && job->className() == QString("KIO::TransferJob")){
00284 kdDebug(9018) << endl << "job started" << job->progressId();
00285 KIO::TransferJob *tjob = static_cast<KIO::TransferJob*>(job);
00286 connect(tjob, SIGNAL(data(KIO::Job*, const QByteArray&)),
00287 this, SLOT(slotWebData(KIO::Job*, const QByteArray&)));
00288 connect(tjob, SIGNAL(result(KIO::Job*)),
00289 this, SLOT(slotWebResult(KIO::Job*)));
00290 }
00291 }
00292
00293 void PHPSupportPart::slotWebData(KIO::Job* ,const QByteArray& data){
00294 kdDebug(9018) << "slotWebData()" << endl;
00295 QString strData(data);
00296 m_phpExeOutput += strData;
00297 }
00298
00299 void PHPSupportPart::slotWebResult(KIO::Job* ){
00300 kdDebug(9018) << "slotWebResult()" << endl;
00301 m_phpErrorView->parse(m_phpExeOutput);
00302 }
00303
00304 void PHPSupportPart::executeInTerminal(){
00305 kdDebug(9018) << "slotExecuteInTerminal()" << endl;
00306
00307
00308 partController()->saveAllFiles();
00309
00310 QString file;
00311 if(m_htmlView==0){
00312 m_htmlView = new PHPHTMLView(this);
00313 mainWindow()->embedPartView(m_htmlView->view(), i18n("PHP"));
00314 }
00315 m_htmlView->show();
00316 m_htmlView->begin();
00317
00318 m_phpExeOutput="";
00319 phpExeProc->clearArguments();
00320 *phpExeProc << configData->getPHPExecPath();
00321 *phpExeProc << "-f";
00322
00323
00324 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
00325 if(ro_part){
00326 file = ro_part->url().path();
00327 }
00328
00329 *phpExeProc << KShellProcess::quote(file);
00330 kdDebug(9018) << "" << file.latin1() << endl;
00331 phpExeProc->start(KProcess::NotifyOnExit,KProcess::All);
00332
00333
00334
00335
00336 }
00337 void PHPSupportPart::slotReceivedPHPExeStdout (KProcess* , char* buffer, int buflen){
00338 kdDebug(9018) << "slotPHPExeStdout()" << endl;
00339 m_htmlView->write(buffer,buflen+1);
00340 m_phpExeOutput += QString::fromLocal8Bit(buffer,buflen+1);
00341 }
00342
00343 void PHPSupportPart::slotReceivedPHPExeStderr (KProcess* , char* buffer, int buflen){
00344 kdDebug(9018) << "slotPHPExeStderr()" << endl;
00345 m_htmlView->write(buffer,buflen+1);
00346 m_phpExeOutput += QString::fromLocal8Bit(buffer,buflen+1);
00347 }
00348
00349 void PHPSupportPart::slotPHPExeExited (KProcess* ){
00350 kdDebug(9018) << "slotPHPExeExited()" << endl;
00351 m_htmlView->end();
00352 m_phpErrorView->parse(m_phpExeOutput);
00353 }
00354
00355 void PHPSupportPart::projectOpened()
00356 {
00357 kdDebug(9018) << "projectOpened()" << endl;
00358
00359 connect( project(), SIGNAL(addedFilesToProject(const QStringList &)),
00360 this, SLOT(addedFilesToProject(const QStringList &)) );
00361 connect( project(), SIGNAL(removedFilesFromProject(const QStringList &)),
00362 this, SLOT(removedFilesFromProject(const QStringList &)) );
00363
00364
00365
00366 QTimer::singleShot(0, this, SLOT(initialParse()));
00367 }
00368
00369
00370 void PHPSupportPart::projectClosed()
00371 {
00372 }
00373
00374
00375 void PHPSupportPart::maybeParse(const QString fileName)
00376 {
00377
00378 QFileInfo fi(fileName);
00379 QString path = fi.filePath();
00380 if ((fi.extension().contains("inc") || fi.extension().contains("php")
00381 || fi.extension().contains("html")
00382 || fi.extension().contains("php3")) && !fi.extension().contains("~")) {
00383 kdDebug(9018) << "remove and parse" << fileName.latin1() << endl;
00384 if( codeModel()->hasFile(fileName) ){
00385 emit aboutToRemoveSourceInfo( fileName );
00386 codeModel()->removeFile( codeModel()->fileByName(fileName) );
00387 }
00388 m_parser->parseFile(fileName);
00389 }
00390 }
00391
00392
00393 void PHPSupportPart::initialParse(){
00394 kdDebug(9018) << "initialParse()" << endl;
00395
00396 if (project()) {
00397 kdDebug(9018) << "project" << endl;
00398 kapp->setOverrideCursor(waitCursor);
00399 QStringList files = project()->allFiles();
00400 int n = 0;
00401 QProgressBar *bar = new QProgressBar(files.count(), mainWindow()->statusBar());
00402 bar->setMinimumWidth(120);
00403 bar->setCenterIndicator(true);
00404 mainWindow()->statusBar()->addWidget(bar);
00405 bar->show();
00406
00407 for (QStringList::Iterator it = files.begin(); it != files.end() ;++it) {
00408 QFileInfo fileInfo( project()->projectDirectory(), *it );
00409 kdDebug(9018) << "maybe parse " << fileInfo.absFilePath() << endl;
00410 bar->setProgress(n);
00411 kapp->processEvents();
00412 maybeParse( fileInfo.absFilePath() );
00413 ++n;
00414 }
00415 mainWindow()->statusBar()->removeWidget(bar);
00416 delete bar;
00417 emit updatedSourceInfo();
00418 kapp->restoreOverrideCursor();
00419 } else {
00420 kdDebug(9018) << "No project" << endl;
00421 }
00422 }
00423
00424
00425 void PHPSupportPart::addedFilesToProject(const QStringList &fileList)
00426 {
00427 kdDebug(9018) << "addedFilesToProject()" << endl;
00428
00429 QStringList::ConstIterator it;
00430
00431 for ( it = fileList.begin(); it != fileList.end(); ++it )
00432 {
00433 QFileInfo fileInfo( project()->projectDirectory(), *it );
00434 maybeParse( fileInfo.absFilePath() );
00435 emit addedSourceInfo( fileInfo.absFilePath() );
00436 }
00437
00438
00439 }
00440
00441
00442 void PHPSupportPart::removedFilesFromProject(const QStringList &fileList)
00443 {
00444 kdDebug(9018) << "removedFilesFromProject()" << endl;
00445
00446 QStringList::ConstIterator it;
00447
00448 for ( it = fileList.begin(); it != fileList.end(); ++it )
00449 {
00450 QFileInfo fileInfo( project()->projectDirectory(), *it );
00451 QString path = fileInfo.absFilePath();
00452 if( codeModel()->hasFile(path) ){
00453 emit aboutToRemoveSourceInfo( path );
00454 codeModel()->removeFile( codeModel()->fileByName(path) );
00455 }
00456 }
00457
00458
00459 }
00460
00461
00462 void PHPSupportPart::savedFile(const KURL &fileName)
00463 {
00464 kdDebug(9018) << "savedFile()" << endl;
00465
00466 if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) {
00467 maybeParse(fileName.path());
00468 emit addedSourceInfo( fileName.path() );
00469 }
00470 }
00471
00472
00473 KDevLanguageSupport::Features PHPSupportPart::features()
00474 {
00475 return Features(Classes | Functions);
00476 }
00477
00478 KMimeType::List PHPSupportPart::mimeTypes( )
00479 {
00480 KMimeType::List list;
00481 KMimeType::Ptr mime = KMimeType::mimeType( "application/x-php" );
00482 if( mime )
00483 list << mime;
00484
00485 mime = KMimeType::mimeType( "text/plain" );
00486 if( mime )
00487 list << mime;
00488 return list;
00489 }
00490
00491 #include "phpsupportpart.moc"