You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/languages/php/phpsupportpart.cpp

752 lines
23 KiB

/*
Copyright (C) 2005 by Nicolas Escuder <n.escuder@intra-links.com>
Copyright (C) 2001 by smeier@tdevelop.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
version 2, License as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "phpsupportpart.h"
#include <iostream>
#include <tqdir.h>
#include <tqfileinfo.h>
#include <tqpopupmenu.h>
#include <tqprogressbar.h>
#include <tqstringlist.h>
#include <tqtextstream.h>
#include <tqtimer.h>
#include <tqvbox.h>
#include <tqwhatsthis.h>
#include <tqthread.h>
#include <kaction.h>
#include <kapplication.h>
#include <kdebug.h>
#include <khtmlview.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kprocess.h>
#include <kregexp.h>
#include <kstatusbar.h>
#include <kparts/browserextension.h>
#include <kdevcore.h>
#include <kdevproject.h>
#include <kdevmainwindow.h>
#include <kdevpartcontroller.h>
#include <codemodel.h>
#include <domutil.h>
#include <kdevplugininfo.h>
#include <kiconloader.h>
#include "phpconfigdata.h"
#include "phpconfigwidget.h"
#include "phpcodecompletion.h"
#include "phpparser.h"
#include "phpnewclassdlg.h"
#include "phphtmlview.h"
#include "phperrorview.h"
#include "phpsupport_event.h"
using namespace std;
static const KDevPluginInfo data("kdevphpsupport");
K_EXPORT_COMPONENT_FACTORY( libkdevphpsupport, PHPSupportFactory( data ) )
PHPSupportPart::PHPSupportPart(TQObject *parent, const char *name, const TQStringList &)
: KDevLanguageSupport(&data, parent, name ? name : "PHPSupportPart")
{
m_htmlView = 0;
m_parser = 0;
phpExeProc = 0;
setInstance(PHPSupportFactory::instance());
setXMLFile("kdevphpsupport.rc");
connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()) );
connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) );
connect( partController(), TQT_SIGNAL(savedFile(const KURL&)),
this, TQT_SLOT(savedFile(const KURL&)) );
connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)),
this, TQT_SLOT(projectConfigWidget(KDialogBase*)) );
KAction *action;
action = new KAction( i18n("&Run"), "exec",Key_F9,
this, TQT_SLOT(slotRun()),
actionCollection(), "build_execute" );
action->setToolTip(i18n("Run"));
action->setWhatsThis(i18n("<b>Run</b><p>Executes script on a terminal or a webserver."));
action = new KAction( i18n("&New Class..."),0,
this, TQT_SLOT(slotNewClass()),
actionCollection(), "project_new_class" );
action->setToolTip(i18n("New class"));
action->setWhatsThis(i18n("<b>New class</b><p>Runs New Class wizard."));
m_phpErrorView = new PHPErrorView(this, 0, "phpErrorWidget");
m_phpErrorView->setIcon( SmallIcon("info") );
TQWhatsThis::add(m_phpErrorView, i18n("<b>PHP problems</b><p>This view shows PHP parser warnings, errors, and fatal errors."));
mainWindow()->embedOutputView(m_phpErrorView, i18n("Problems"), i18n("Problems"));
phpExeProc = new KShellProcess("/bin/sh");
connect( phpExeProc, TQT_SIGNAL(receivedStdout (KProcess*, char*, int)),
this, TQT_SLOT(slotReceivedPHPExeStdout (KProcess*, char*, int)));
connect( phpExeProc, TQT_SIGNAL(receivedStderr (KProcess*, char*, int)),
this, TQT_SLOT(slotReceivedPHPExeStderr (KProcess*, char*, int)));
connect( phpExeProc, TQT_SIGNAL(processExited(KProcess*)),
this, TQT_SLOT(slotPHPExeExited(KProcess*)));
m_htmlView = new PHPHTMLView(this);
mainWindow()->embedOutputView(m_htmlView->view(), i18n("PHP"), i18n("PHP"));
connect( m_htmlView, TQT_SIGNAL(started(KIO::Job*)),
this, TQT_SLOT(slotWebJobStarted(KIO::Job*)));
configData = new PHPConfigData(projectDom());
connect( configData, TQT_SIGNAL(configStored()),
this, TQT_SLOT(slotConfigStored()));
m_codeCompletion = new PHPCodeCompletion(this, configData);
new KAction(i18n("Complete Text"), CTRL+Key_Space, m_codeCompletion, TQT_SLOT(cursorPositionChanged()), actionCollection(), "edit_complete_text");
connect( partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)),
this, TQT_SLOT(slotActivePartChanged(KParts::Part *)));
connect( this, TQT_SIGNAL(fileParsed( PHPFile* )), this, TQT_SLOT(slotfileParsed( PHPFile* )));
}
PHPSupportPart::~PHPSupportPart()
{
LastClass = NULL;
LastMethod = NULL;
LastVariable = NULL;
if ( m_parser ) {
m_parser->close() ;
delete( m_parser );
m_parser = NULL;
}
if ( m_phpErrorView ) {
mainWindow()->removeView( m_phpErrorView );
delete( m_phpErrorView );
m_phpErrorView = NULL;
}
kdDebug(9018) << "remove codeCompletition" << endl;
if ( m_codeCompletion )
delete( m_codeCompletion );
kdDebug(9018) << "remove configData" << endl;
if ( configData )
delete( configData );
if ( m_htmlView ) {
kdDebug(9018) << "remove htmlView" << endl;
mainWindow()->removeView( m_htmlView->view() );
delete( m_htmlView );
m_htmlView = NULL;
}
kdDebug(9018) << "remove phpExec" << endl;
if ( phpExeProc )
delete( phpExeProc );
kdDebug(9018) << "finish" << endl;
}
void PHPSupportPart::slotActivePartChanged(KParts::Part *part) {
kdDebug(9018) << "enter slotActivePartChanged" << endl;
if (!part || !part->widget())
return;
m_editInterface = dynamic_cast<KTextEditor::EditInterface*>(part);
if (m_editInterface) { // connect to the editor
disconnect(part, 0, this, 0 ); // to make sure that it is't connected twice
if (configData->getRealtimeParsing()) {
connect(part,TQT_SIGNAL(textChanged()),this,TQT_SLOT(slotTextChanged()));
}
m_codeCompletion->setActiveEditorPart(part);
}
kdDebug(9018) << "exit slotActivePartChanged" << endl;
}
void PHPSupportPart::slotTextChanged() {
kdDebug(9018) << "enter text changed" << endl;
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
if (!ro_part)
return;
TQString fileName = ro_part->url().directory() + "/" + ro_part->url().fileName();
if (m_parser) {
if (m_parser->hasFile( fileName ))
m_parser->reparseFile( fileName );
}
}
void PHPSupportPart::slotConfigStored() {
// fake a changing, this will read the configuration again and install the connects
slotActivePartChanged(partController()->activePart());
}
void PHPSupportPart::projectConfigWidget(KDialogBase *dlg) {
TQVBox *vbox = dlg->addVBoxPage(i18n( "PHP Specific" ), i18n("PHP Settings"), BarIcon( "source", KIcon::SizeMedium ));
PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget");
connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
}
void PHPSupportPart::slotNewClass() {
TQStringList classNames = sortedNameList( codeModel()->globalNamespace()->classList() );
PHPNewClassDlg dlg(classNames,project()->projectDirectory());
dlg.exec();
}
void PHPSupportPart::slotRun() {
configData = new PHPConfigData(projectDom());
if (validateConfig()) {
mainWindow()->raiseView(m_htmlView->view());
PHPConfigData::InvocationMode mode = configData->getInvocationMode() ;
if (mode == PHPConfigData::Web) {
executeOnWebserver();
}
else if (mode == PHPConfigData::Shell) {
executeInTerminal();
}
}
}
bool PHPSupportPart::validateConfig() {
if (!configData->validateConfig()) {
KMessageBox::information(0,i18n("There is no configuration for executing a PHP file.\nPlease set the correct values in the next dialog."));
KDialogBase dlg(KDialogBase::TreeList, i18n("Customize PHP Mode"),
KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, 0,
"php config dialog");
TQVBox *vbox = dlg.addVBoxPage(i18n("PHP Settings"));
PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget");
connect( &dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
dlg.exec();
}
if (configData->validateConfig()) {
return true;
}
return false;
}
void PHPSupportPart::executeOnWebserver() {
// Save all files once
if (partController()->saveAllFiles()==false)
return; //user cancelled
// Figure out the name of the remote file
TQString weburl = configData->getWebURL();
TQString file = getExecuteFile();
// Force KHTMLPart to reload the page
KParts::BrowserExtension* be = m_htmlView->browserExtension();
if (be) {
KParts::URLArgs urlArgs( be->urlArgs() );
urlArgs.reload = true;
be->setURLArgs( urlArgs );
}
// Acutally do the request
m_phpExeOutput="";
m_htmlView->openURL(KURL(weburl + file));
m_htmlView->show();
}
TQString PHPSupportPart::getExecuteFile() {
TQString file;
PHPConfigData::StartupFileMode mode = configData->getStartupFileMode();
TQString weburl = configData->getWebURL();
if (mode == PHPConfigData::Current) {
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
if (ro_part) {
if ( configData->getInvocationMode() == PHPConfigData::Web )
file = URLUtil::relativePath( project()->projectDirectory(), ro_part->url().path() );
else
file = ro_part->url().path();
}
}
if (mode == PHPConfigData::Default) {
file = configData->getStartupFile();
}
return file;
}
void PHPSupportPart::slotWebJobStarted(KIO::Job* job) {
if (job && job->className() == TQString("KIO::TransferJob")) {
kdDebug(9018) << endl << "job started" << job->progressId();
KIO::TransferJob *tjob = static_cast<KIO::TransferJob*>(job);
connect( tjob, TQT_SIGNAL(data(KIO::Job*, const TQByteArray&)),
this, TQT_SLOT(slotWebData(KIO::Job*, const TQByteArray&)));
connect( tjob, TQT_SIGNAL(result(KIO::Job*)),
this, TQT_SLOT(slotWebResult(KIO::Job*)));
}
}
void PHPSupportPart::slotWebData(KIO::Job* /*job*/,const TQByteArray& data) {
kdDebug(9018) << "slotWebData()" << endl;
TQString strData(data);
m_phpExeOutput += strData;
}
void PHPSupportPart::slotWebResult(KIO::Job* /*job*/) {
kdDebug(9018) << "slotWebResult()" << endl;
TQString file = getExecuteFile();
PHPFile *pfile = new PHPFile(this, file);
pfile->ParseStdout(m_phpExeOutput);
delete pfile;
}
void PHPSupportPart::executeInTerminal() {
kdDebug(9018) << "slotExecuteInTerminal()" << endl;
// Save all files once
if (partController()->saveAllFiles()==false)
return; //user cancelled
TQString file = getExecuteFile();
if (m_htmlView == 0) {
m_htmlView = new PHPHTMLView(this);
mainWindow()->embedOutputView(m_htmlView->view(), i18n("PHP"), i18n("PHP") );
}
m_htmlView->show();
m_htmlView->begin();
m_phpExeOutput = "";
phpExeProc->clearArguments();
*phpExeProc << configData->getPHPExecPath();
*phpExeProc << "-f";
*phpExeProc << KShellProcess::quote(file);
kdDebug(9018) << "" << file.latin1() << endl;
phpExeProc->start(KProcess::NotifyOnExit,KProcess::All);
// core()->gotoDocumentationFile(KURL("http://www.php.net"));
}
void PHPSupportPart::slotReceivedPHPExeStdout (KProcess* /*proc*/, char* buffer, int buflen) {
kdDebug(9018) << "slotPHPExeStdout()" << endl;
m_phpExeOutput += TQString::fromLocal8Bit(buffer,buflen+1);
TQString buf = buffer;
if (configData->getInvocationMode() == PHPConfigData::Shell)
buf.replace("\n", "<br>");
m_htmlView->write(buf);
}
void PHPSupportPart::slotReceivedPHPExeStderr (KProcess* /*proc*/, char* buffer, int buflen) {
kdDebug(9018) << "slotPHPExeStderr()" << endl;
m_phpExeOutput += TQString::fromLocal8Bit(buffer,buflen+1);
TQString buf = buffer;
if (configData->getInvocationMode() == PHPConfigData::Shell)
buf.replace("\n", "<br>");
m_htmlView->write(buf);
}
void PHPSupportPart::slotPHPExeExited (KProcess* /*proc*/) {
kdDebug(9018) << "slotPHPExeExited()" << endl;
m_htmlView->end();
TQString file = getExecuteFile();
PHPFile *pfile = new PHPFile(this, file);
pfile->ParseStdout(m_phpExeOutput);
delete pfile;
}
void PHPSupportPart::projectOpened()
{
kdDebug(9018) << "projectOpened()" << endl;
connect( project(), TQT_SIGNAL(addedFilesToProject(const TQStringList &)),
this, TQT_SLOT(addedFilesToProject(const TQStringList &)) );
connect( project(), TQT_SIGNAL(removedFilesFromProject(const TQStringList &)),
this, TQT_SLOT(removedFilesFromProject(const TQStringList &)) );
if (!m_parser) {
m_parser = new PHPParser( this );
m_parser->start();
}
// We want to parse only after all components have been
// properly initialized
TQTimer::singleShot(500, this, TQT_SLOT( initialParse() ) );
}
void PHPSupportPart::initialParse( )
{
// For debugging
if ( !project( ) )
{
// messagebox ?
kdDebug( 9018 ) << "No project" << endl;
return ;
}
parseProject( );
return ;
}
void PHPSupportPart::projectClosed()
{
kdDebug(9018) << "projectClosed()" << endl;
if (m_parser) {
m_parser->close() ;
delete( m_parser );
m_parser = 0;
}
}
bool PHPSupportPart::parseProject()
{
kdDebug(9018) << "parseProject()" << endl;
mainWindow() ->statusBar() ->message( i18n( "Updating..." ) );
kapp->setOverrideCursor( waitCursor );
_jd = new JobData;
_jd->files = project()->allFiles();
TQProgressBar* bar = new TQProgressBar( _jd->files.count( ), mainWindow( ) ->statusBar( ) );
bar->setMinimumWidth( 120 );
bar->setCenterIndicator( true );
mainWindow()->statusBar()->addWidget( bar );
bar->show();
_jd->progressBar = bar;
_jd->it = _jd->files.begin();
_jd->dir.setPath( project()->projectDirectory() );
TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) );
return TRUE;
}
void PHPSupportPart::slotParseFiles()
{
kdDebug(9018) << "slotParseFiles()" << endl;
kapp->lock();
if ( _jd->it != _jd->files.end() )
{
_jd->progressBar->setProgress( _jd->progressBar->progress() + 1 );
TQFileInfo fileInfo( _jd->dir, *( _jd->it ) );
if ( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() )
{
TQString absFilePath = URLUtil::canonicalPath( fileInfo.absFilePath() );
// if ( isValidSource( absFilePath ) )
{
if (m_parser)
m_parser->addFile( absFilePath );
}
++( _jd->it );
}
TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) );
}
else // finished or interrupted
{
kapp->restoreOverrideCursor();
mainWindow()->statusBar()->removeWidget( _jd->progressBar );
mainWindow()->statusBar()->message( i18n( "Done" ), 2000 );
emit updatedSourceInfo();
if (m_parser)
m_parser->startParse();
delete _jd;
_jd = 0;
}
kapp->unlock();
}
void PHPSupportPart::addedFilesToProject(const TQStringList &fileList)
{
kdDebug(9018) << "addedFilesToProject()" << endl;
TQStringList::ConstIterator it;
for ( it = fileList.begin(); it != fileList.end(); ++it )
{
TQFileInfo fileInfo( project()->projectDirectory(), *it );
if (m_parser) {
m_parser->addFile( fileInfo.absFilePath() );
emit addedSourceInfo( fileInfo.absFilePath() );
}
}
}
void PHPSupportPart::removedFilesFromProject(const TQStringList &fileList)
{
kdDebug(9018) << "removedFilesFromProject()" << endl;
TQStringList::ConstIterator it;
for ( it = fileList.begin(); it != fileList.end(); ++it )
{
TQFileInfo fileInfo( project()->projectDirectory(), *it );
TQString path = fileInfo.absFilePath();
if ( codeModel()->hasFile(path) ) {
emit aboutToRemoveSourceInfo( path );
codeModel()->removeFile( codeModel()->fileByName(path) );
}
}
}
void PHPSupportPart::savedFile(const KURL &fileName)
{
kdDebug(9018) << "savedFile()" << fileName.fileName() << endl;
/// @fixme when activated could cause stop
/*
if (m_parser) {
if (m_parser->hasFile( fileName.path() )) {
m_parser->reparseFile( fileName.path() );
}
}
*/
}
TQString PHPSupportPart::getIncludePath()
{
return configData->getPHPIncludePath();
}
TQString PHPSupportPart::getExePath()
{
return configData->getPHPExecPath();
}
KDevLanguageSupport::Features PHPSupportPart::features()
{
return Features(Classes | Functions);
}
KMimeType::List PHPSupportPart::mimeTypes( )
{
KMimeType::List list;
KMimeType::Ptr mime = KMimeType::mimeType( "application/x-php" );
if ( mime )
list << mime;
mime = KMimeType::mimeType( "text/plain" );
if ( mime )
list << mime;
return list;
}
void PHPSupportPart::customEvent( TQCustomEvent* ev )
{
// kdDebug(9018) << "phpSupportPart::customEvent(" << ev->type() << ") " << TQThread::currentThread() << endl;
if ( ev->type() < Event_AddFile || ev->type() > Event_AddFixme )
return;
kapp->lock();
FileParseEvent* event = (FileParseEvent*) ev;
NamespaceDom ns = codeModel()->globalNamespace();
FileDom m_file = codeModel()->fileByName( event->fileName() );
if (!m_file) {
m_file = codeModel()->create<FileModel>();
m_file->setName( event->fileName() );
codeModel()->addFile( m_file );
}
switch (int(ev->type())) {
case Event_AddFile:
m_parser->addFile( event->fileName() );
break;
case Event_StartParse:
// kdDebug(9018) << "StartParse " << event->fileName() << endl;
LastClass = NULL;
LastMethod = NULL;
LastVariable = NULL;
if ( codeModel()->hasFile( event->fileName() ) ) {
emit aboutToRemoveSourceInfo( event->fileName() );
codeModel()->removeFile( codeModel()->fileByName( event->fileName() ) );
emit removedSourceInfo( event->fileName() );
}
ErrorView()->removeAllProblems( event->fileName() );
break;
case Event_AddClass:
{
// kdDebug(9018) << "AddClass " << event->name() << endl;
ClassDom nClass = codeModel()->create<ClassModel>();
nClass->setFileName( event->fileName() );
nClass->setName( event->name() );
nClass->setStartPosition( event->posititon(), 0);
m_file->addClass( nClass );
if ( event->arguments().isEmpty() != TRUE )
nClass->addBaseClass( event->arguments() );
ns->addClass( nClass );
LastClass = nClass;
}
break;
case Event_CloseClass:
if ( LastClass != NULL ) {
// kdDebug(9018) << "CloseClass " << LastClass->name() << endl;
LastClass->setEndPosition( event->posititon(), 0 );
LastClass = NULL;
LastMethod = NULL;
LastVariable = NULL;
}
break;
case Event_AddFunction:
{
// kdDebug(9018) << "AddFunction " << event->name() << endl;
FunctionDom nMethod = codeModel()->create<FunctionModel>();
nMethod->setFileName( event->fileName() );
nMethod->setName( event->name() );
nMethod->setStartPosition( event->posititon(), 0 );
ArgumentDom nArgument;
nArgument = codeModel()->create<ArgumentModel>();
nArgument->setType(event->arguments().stripWhiteSpace().local8Bit());
nMethod->addArgument( nArgument );
if (LastClass != NULL) {
LastClass->addFunction(nMethod);
} else {
ns->addFunction(nMethod);
}
LastMethod = nMethod;
}
break;
case Event_SetFunction:
if ( LastMethod != NULL ) {
// kdDebug(9018) << "SetFunction " << LastMethod->name() << " " << event->name() << endl;
if ( event->name() == "static" )
LastMethod->setStatic(true);
else if ( event->name() == "abstract" )
LastMethod->setAbstract(true);
else if ( event->name() == "private" )
LastMethod->setAccess(FunctionModel::Private);
else if ( event->name() == "public" )
LastMethod->setAccess(FunctionModel::Public);
else if ( event->name() == "protected" )
LastMethod->setAccess(FunctionModel::Protected);
else if ( event->name() == "result" ) {
TQString ret = "";
if (event->arguments().lower() == "$this" && LastClass ) {
ret = LastClass->name();
}
LastMethod->setResultType(ret);
}
}
break;
case Event_CloseFunction:
if ( LastMethod != NULL ) {
// kdDebug(9018) << "CloseFunction " << LastMethod->name() << endl;
LastMethod->setEndPosition( event->posititon(), 0 );
LastMethod = NULL;
LastVariable = NULL;
}
break;
case Event_AddVariable:
{
VariableDom nVariable = codeModel()->create<VariableModel>();
nVariable->setFileName( event->fileName() );
nVariable->setName( event->name() );
nVariable->setStartPosition( event->posititon(), 0 );
nVariable->setAccess(VariableModel::Public);
if ( event->arguments().isEmpty() != TRUE )
nVariable->setType( event->arguments() );
if ( LastClass != NULL && ( LastMethod == NULL || event->global() == TRUE ) ) {
// kdDebug(9018) << "AddVariable To Class " << LastClass->name() << " " << nVariable->name() << endl;
LastClass->addVariable(nVariable);
} else {
if ( LastMethod != NULL ) {
kdDebug(9018) << "AddVariable " << LastMethod->name() << " " << nVariable->name() << endl;
} else {
ns->addVariable(nVariable);
}
}
LastVariable = nVariable;
}
break;
case Event_SetVariable:
if ( LastVariable != NULL ) {
// kdDebug(9018) << "SetVariable " << LastVariable->name() << " " << event->arguments() << endl;
if ( event->arguments() == "static" )
LastVariable->setStatic(true);
else if ( event->arguments() == "private" )
LastVariable->setAccess(FunctionModel::Private);
else if ( event->arguments() == "public" )
LastVariable->setAccess(FunctionModel::Public);
else if ( event->arguments() == "protected" )
LastVariable->setAccess(FunctionModel::Protected);
}
break;
case Event_AddTodo:
ErrorView()->reportProblem(Todo, event->fileName(), event->posititon(), event->arguments());
break;
case Event_AddFixme:
ErrorView()->reportProblem(Fixme, event->fileName(), event->posititon(), event->arguments());
break;
case Event_EndParse:
// kdDebug(9018) << "EndParse " << event->fileName() << endl;
emit addedSourceInfo( event->fileName() );
break;
}
kapp->unlock();
kapp->processEvents();
}
PHPErrorView *PHPSupportPart::ErrorView( ) {
return m_phpErrorView;
}
PHPParser *PHPSupportPart::Parser( ) {
return m_parser;
}
#include "phpsupportpart.moc"