KDevelop API Documentation

phpcodecompletion.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           phpcodecompletion.cpp  -  description
00003                              -------------------
00004     begin                : Tue Jul 17 2001
00005     copyright            : (C) 2001 by Sandy Meier
00006     email                : smeier@kdevelop.org
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "phpcodecompletion.h"
00019 #include "phpsupportpart.h"
00020 #include "phpconfigdata.h"
00021 
00022 #include <kdevcore.h>
00023 #include <kinstance.h>
00024 #include <kstandarddirs.h>
00025 #include <kdebug.h>
00026 
00027 #include <qfile.h>
00028 #include <qtextstream.h>
00029 #include <qregexp.h>
00030 
00031 #include <iostream>
00032 
00033 using namespace std;
00034 
00035 PHPCodeCompletion::PHPCodeCompletion(PHPConfigData *config,KDevCore* core,CodeModel* model){
00036   m_config = config;
00037   m_core = core;
00038   m_model = model;
00039   m_argWidgetShow = false;
00040   m_completionBoxShow=false;
00041 
00042   readGlobalPHPFunctionsFile();
00043 }
00044 
00045 PHPCodeCompletion::~PHPCodeCompletion(){
00046 }
00047 
00048 void PHPCodeCompletion::readGlobalPHPFunctionsFile(){
00049   KStandardDirs *dirs = PHPSupportFactory::instance()->dirs();
00050   QString phpFuncFile = dirs->findResource("data","kdevphpsupport/phpfunctions");
00051   QRegExp lineReg(":([0-9A-Za-z_]+) ([0-9A-Za-z_]+)(\\(.*\\))");
00052   FunctionCompletionEntry e;
00053   QFile f(phpFuncFile);
00054   if ( f.open(IO_ReadOnly) ) {    // file opened successfully
00055       QTextStream t( &f );        // use a text stream
00056       QString s;
00057       while ( !t.eof() ) {        // until end of file...
00058       s = t.readLine();       // line of text excluding '\n'
00059       if(lineReg.search(s.local8Bit()) != -1){
00060           e.prefix = lineReg.cap(1);
00061           e.text = lineReg.cap(2);
00062           //          if(QString(lineReg.cap(3)) == "void"){
00063         e.postfix ="()";
00064         //        }
00065         //        else{
00066         //        e.postfix ="(...)";
00067         //        }
00068           e.prototype = QString(lineReg.cap(1)) + " " + QString(lineReg.cap(2)) +
00069           "(" + QString(lineReg.cap(3)) + ")";
00070           m_globalFunctions.append(e);
00071       }
00072 
00073       }
00074       f.close();
00075   }
00076 
00077 }
00078 void PHPCodeCompletion::argHintHided(){
00079   kdDebug(9018) << "PHPCodeCompletion::argHintHided"  << endl;
00080   m_argWidgetShow = false;
00081 }
00082 void PHPCodeCompletion::completionBoxHided(){
00083   kdDebug(9018) << "PHPCodeCompletion::completionBoxHided()" << endl;
00084   m_completionBoxShow=false;
00085 }
00086 
00087 void PHPCodeCompletion::setActiveEditorPart(KParts::Part *part)
00088 {
00089   if (!part || !part->widget())
00090     return;
00091 
00092   kdDebug(9018) << "PHPCodeCompletion::setActiveEditorPart"  << endl;
00093 
00094   if(!(m_config->getCodeCompletion() || m_config->getCodeHinting())){
00095     return; // no help
00096   }
00097 
00098   m_editInterface = dynamic_cast<KTextEditor::EditInterface*>(part);
00099   if (!m_editInterface)
00100   {
00101     kdDebug(9018) << "editor doesn't support the EditDocumentIface" << endl;
00102     return;
00103   }
00104 
00105   m_cursorInterface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
00106   if (!m_cursorInterface)
00107   {
00108     kdDebug(9018) << "editor does not support the ViewCursorInterface" << endl;
00109     return;
00110   }
00111 
00112   m_codeInterface = dynamic_cast<KTextEditor::CodeCompletionInterface*>(part->widget());
00113   if (!m_codeInterface) { // no CodeCompletionDocument available
00114     kdDebug(9018) << "editor doesn't support the CodeCompletionDocumentIface" << endl;
00115     return;
00116   }
00117 
00118   m_selectionInterface = dynamic_cast<KTextEditor::SelectionInterface*>(part);
00119   if(!m_selectionInterface) {
00120     kdDebug(9018) << "editor doesn't support the SelectionInterface" << endl;
00121     return;
00122   }
00123 
00124 
00125   disconnect(part->widget(), 0, this, 0 ); // to make sure that it is't connected twice
00126   connect(part->widget(), SIGNAL(cursorPositionChanged()),
00127           this, SLOT(cursorPositionChanged()));
00128   connect(part->widget(), SIGNAL(argHintHidden()), this, SLOT(argHintHided()));
00129   connect(part->widget(), SIGNAL(completionAborted()), this, SLOT(completionBoxHided()));
00130   connect(part->widget(), SIGNAL(completionDone()), this, SLOT(completionBoxHided()));
00131 
00132 }
00133 
00134 void PHPCodeCompletion::cursorPositionChanged(){
00135   uint line, col;
00136   m_cursorInterface->cursorPositionReal(&line, &col);
00137   kdDebug(9018) << "PHPCodeCompletion::cursorPositionChanged:" << line << ":" << col  << endl;
00138 
00139   m_currentLine = line;
00140   QString lineStr = m_editInterface->textLine(line);
00141   if(lineStr.isNull() || lineStr.isEmpty()) return; // nothing to do
00142   //kdDebug(9018) << "ZEILE:" << lineStr <<":" << endl;
00143   //  kdDebug(9018) << "Length:" << lineStr.length() <<":" << endl;
00144   //  lineStr.replace(QRegExp("\t"),"_");
00145   //  kdDebug(9018) << "ZEILEohneTAB:" << lineStr <<":" << endl;
00146 
00147   if(m_selectionInterface->hasSelection()){
00148     kdDebug(9018) << "No CodeCompletion/ArgHinting at the moment, because text is selected" << endl;
00149     return;
00150   }
00151 
00152   if(m_config->getCodeHinting()){
00153     if(checkForNewInstanceArgHint(lineStr,col,line)){
00154       return;
00155     }
00156 
00157     if(checkForMethodArgHint(lineStr,col,line)){
00158       return;
00159     }
00160 
00161     if(checkForGlobalFunctionArgHint(lineStr,col,line)){
00162       return;
00163     }
00164   }
00165 
00166   if(m_config->getCodeCompletion()){
00167     QString restLine = lineStr.mid(col);
00168     if(restLine.left(1) != " " && restLine.left(1) != "\t" && !restLine.isNull()){
00169       kdDebug(9018) << "no codecompletion because no empty character after cursor:" << restLine << ":" << endl;
00170       return;
00171     }
00172 
00173     if(checkForVariable(lineStr,col,line)){
00174       return;
00175     }
00176 
00177     // $test = new XXX
00178     if(checkForNewInstance(lineStr,col,line)){
00179       return;
00180     }
00181 
00182 
00183     if(checkForGlobalFunction(lineStr,col)) {
00184       return;
00185     }
00186   }
00187 
00188 
00189 }
00190 
00191 bool PHPCodeCompletion::checkForMethodArgHint(QString lineStr,int col,int /*line*/){
00192   kdDebug(9018) << "enter checkForMethodArgHint" << endl;
00193   if(m_argWidgetShow){
00194     return false; //nothing to do
00195   }
00196   QString methodStart = lineStr.left(col);
00197   int leftBracket = methodStart.findRev("(");
00198   methodStart = methodStart.left(leftBracket);
00199   int varStart = methodStart.findRev("$");
00200   if(varStart ==-1){
00201     //cerr << "checkForMethodArgHint: no '$' (variable start) found" << endl;
00202     return false;
00203   }
00204   QString variableLine = methodStart.mid(varStart+1);
00205   if(variableLine.isNull()){ return false;}
00206   //  cerr << "VarLine:" << variableLine << endl;
00207   QString className = "";
00208   QStringList vars = QStringList::split("->",variableLine);
00209   QString methodName = vars.last();
00210   //cerr << "methodname:" << methodName << endl;
00211   vars.remove(vars.fromLast()); // remove the methodname
00212   for ( QStringList::Iterator it = vars.begin(); it != vars.end(); ++it ) {
00213     className = this->getClassName("$" + (*it),className);
00214   }
00215   //  cerr << "Classname:" << className << endl;
00216 
00217   if( m_model->globalNamespace()->hasClass(className) ){
00218     ClassDom pClass =  m_model->globalNamespace()->classByName(className)[ 0 ];
00219     FunctionList methodList = pClass->functionList();
00220     FunctionList::Iterator methodIt;
00221 
00222     for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) {
00223       if ((*methodIt)->name() == methodName){
00224     ArgumentDom pArg = (*methodIt)->argumentList().first();
00225     m_argWidgetShow = true;
00226     QValueList <QString> functionList;
00227     if(pArg){
00228       functionList.append(methodName + "(" + pArg->type() +")");
00229     }
00230     m_codeInterface->showArgHint ( functionList, "()", "," );
00231     return true;
00232       }
00233     }
00234   }
00235 
00236   return false;
00237 }
00238 bool PHPCodeCompletion::checkForVariable(QString lineStr,int col,int /*line*/){
00239   kdDebug(9018)  << "enter checkForVariable()" << endl;
00240   QString methodStart = lineStr.left(col);
00241   if(methodStart.right(2) != "->"){
00242     kdDebug(9018)  << "checkForVariable: no '->' found" << endl;
00243     return false;
00244   }
00245   int varStart = methodStart.findRev("$");
00246   if(varStart ==-1){
00247     kdDebug(9018) << "checkForVariable: no '$' (variable start) found" << endl;
00248     return false;
00249   }
00250   QString variableLine = methodStart.mid(varStart+1);
00251   kdDebug(9018) << "VarLine:" << variableLine << ":" << endl;
00252   QString className ="";
00253   QStringList vars = QStringList::split("->",variableLine);
00254   for ( QStringList::Iterator it = vars.begin(); it != vars.end(); ++it ) {
00255     className = this->getClassName("$" + (*it),className);
00256   }
00257   kdDebug(9018) << "Classname:" << className << endl;
00258 
00259   QValueList<KTextEditor::CompletionEntry> list = this->getClassMethodsAndVariables(className);
00260   if(list.count()>0){
00261     m_completionBoxShow=true;
00262     m_codeInterface->showCompletionBox(list);
00263     return true;
00264   }
00265   return false;
00266 }
00267 
00268 QString PHPCodeCompletion::getClassName(QString varName,QString maybeInstanceOf){
00269   kdDebug(9018) << "enter PHPCodeCompletion::getClassName:" << varName << ":" << maybeInstanceOf << ":" << endl;
00270   if(varName == "$this"){
00271     return this->searchCurrentClassName();
00272   }
00273   if(maybeInstanceOf.isEmpty()){
00274     // ok, we need to search it
00275     return this->searchClassNameForVariable(varName);
00276   }
00277   if(m_model->globalNamespace()->hasClass(maybeInstanceOf) !=0){
00278     ClassDom pClass = m_model->globalNamespace()->classByName(maybeInstanceOf)[ 0 ];
00279     VariableList attrList = pClass->variableList();
00280     VariableList::Iterator attrIt;
00281 
00282     for (attrIt = attrList.begin(); attrIt != attrList.end(); ++attrIt) {
00283       if ((*attrIt)->name() == varName){
00284     return (*attrIt)->type();
00285       }
00286     }
00287   }
00288   return "";
00289 }
00290 
00291 QString PHPCodeCompletion::searchClassNameForVariable(QString varName){
00292   kdDebug(9018) << "enter PHPCodeCompletion::searchClassNameForVariable:" << varName << ":" << endl;
00293   QRegExp createVarRe(QString("\\$" + varName.mid(1) + "[ \t]*=[& \t]*new[ \t]+([0-9A-Za-z_]+)").local8Bit());
00294   for(int i=m_currentLine;i>=0;i--){
00295     QString lineStr = m_editInterface->textLine(i);
00296     if(!lineStr.isNull()){
00297       if(createVarRe.search(lineStr.local8Bit()) != -1) { // ok found
00298     //      cerr << endl << "match in searchClassNameForVariable:";
00299     return createVarRe.cap(1);
00300       }
00301     }
00302   }
00303   return QString::null;
00304 }
00305 
00306 QString PHPCodeCompletion::searchCurrentClassName(){
00307   kdDebug(9018) << "enter PHPCodeCompletion::searchCurrentClassName:" << endl;
00308   QRegExp classre("^[ \t]*class[ \t]+([A-Za-z_]+)[ \t]*(extends[ \t]*([A-Za-z_]+))?.*$");
00309   for(int i=m_currentLine;i>=0;i--){
00310     QString lineStr = m_editInterface->textLine(i);
00311     if(!lineStr.isNull()){
00312       if(classre.search(lineStr.local8Bit()) != -1) { // ok found
00313           return classre.cap(1);
00314       }
00315     }
00316   }
00317   return QString::null;
00318 }
00319 
00320 bool PHPCodeCompletion::checkForGlobalFunctionArgHint(QString lineStr,int col,int /*line*/){
00321   kdDebug(9018) << "enter checkForGlobalFunctionArgHint" << endl;
00322   if(m_argWidgetShow){
00323     return false; //nothing to do
00324   }
00325 
00326   QString methodStart = lineStr.left(col);
00327   int leftBracket = methodStart.findRev("(");
00328   int rightBracket = methodStart.findRev(")");
00329   kdDebug(9018)   << "col: " << col << endl;
00330   kdDebug(9018)   << "leftBracket: " << leftBracket << endl;
00331   kdDebug(9018)   << "rightBracket: " << rightBracket << endl;
00332   kdDebug(9018)   << "methodStart: " << methodStart.latin1() << endl;
00333   if(leftBracket == -1) return false; // ok not found
00334   if(rightBracket>leftBracket) return false; // we are out of (..)
00335   methodStart = methodStart.left(leftBracket+1);
00336   //  cerr << methodStart << endl;
00337   QRegExp functionre("([A-Za-z_]+)[ \t]*\\(");
00338   if(functionre.search(methodStart.local8Bit()) != -1){ // check for global functions
00339     QString name = functionre.cap(1);
00340     int startMethod = lineStr.findRev(name,col);
00341     QString startString = lineStr.mid(0,startMethod);
00342     if(startString.right(2) != "->"){
00343       QValueList <QString> functionList;
00344       //cerr << "PHPCodeCompletion::checkForArgHint() found global function" << endl ;
00345       QValueList<FunctionCompletionEntry>::Iterator it;
00346       for( it = m_globalFunctions.begin(); it != m_globalFunctions.end(); ++it ){
00347     if((*it).text == name){
00348       functionList.append((*it).prototype);
00349     }
00350       }
00351       FunctionList methodList = m_model->globalNamespace()->functionList();
00352       FunctionList::Iterator methodIt;
00353       for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) {
00354     if((*methodIt)->name() == name){
00355       ArgumentDom pArg = (*methodIt)->argumentList().first();
00356       functionList.append(name+"("+ pArg->type()+")");
00357     }
00358       }
00359       if(functionList.count() >0){
00360     m_argWidgetShow = true;
00361     if (m_codeInterface)
00362       m_codeInterface->showArgHint ( functionList, "()", "," );
00363     return true;
00364       }
00365     }
00366   }
00367   return false;
00368 }
00369 bool PHPCodeCompletion::checkForGlobalFunction(QString lineStr,int col){
00370   kdDebug(9018)   << "enter checkForGlobalFunction(" + lineStr + "," << col << endl;
00371   QString methodStart ="";
00372   if(lineStr.length()==2){
00373     return doGlobalMethodCompletion(lineStr);
00374   }
00375   if(col==2){
00376     QString startStr =lineStr.mid(col-2,2);
00377     return doGlobalMethodCompletion(startStr);
00378   }
00379 
00380   // normal case
00381   QString startStr =lineStr.mid(col-3,3);
00382   if(startStr.isNull()){
00383     kdDebug(9018)   << "not enough letters" << endl;
00384     return false; // not enough letters
00385   }
00386   //dDebug(9018)  << "StartStr:" << startStr  << ":" << endl;
00387   QString extraChar = " \t+-=/*;)(}{";
00388   if(extraChar.find( startStr[0] ) != -1){
00389     methodStart = startStr.right(2);
00390   }
00391 
00392   //kdDebug(9018)  << "Methodstart:" << methodStart  << ":" << endl;
00393   if(!methodStart.isEmpty()){
00394     return doGlobalMethodCompletion(methodStart);
00395   }
00396   return false;
00397 }
00398 
00399 bool PHPCodeCompletion::doGlobalMethodCompletion(QString methodStart){
00400   //kdDebug(9018)  << "doGlobalMethodCompletion:" << methodStart  << ":" << endl;
00401   QValueList<KTextEditor::CompletionEntry> list;
00402   QValueList<FunctionCompletionEntry>::Iterator it;
00403   for( it = m_globalFunctions.begin(); it != m_globalFunctions.end(); ++it ){
00404     if((*it).text.startsWith(methodStart)){
00405       KTextEditor::CompletionEntry e;
00406       e = (*it);
00407       list.append(e);
00408     }
00409   }
00410 
00411   FunctionList methodList = m_model->globalNamespace()->functionList();
00412   FunctionList::Iterator methodIt;
00413   for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) {
00414     if ((*methodIt)->name().startsWith(methodStart)){
00415       KTextEditor::CompletionEntry e;
00416       e.text = (*methodIt)->name();
00417       e.postfix ="()";
00418       list.append(e);
00419     }
00420   }
00421 
00422   if(list.count() >0){
00423     m_completionBoxShow=true;
00424     m_codeInterface->showCompletionBox(list,2);
00425     return true;
00426   }
00427   return false;
00428 }
00429 
00430 
00431 
00432 bool PHPCodeCompletion::checkForNewInstanceArgHint(QString lineStr,int col,int /*line*/){
00433   //  cerr  << "enter checkForNewInstanceArgHint" << endl;
00434   if(m_argWidgetShow){
00435     return false; //nothing to do
00436   }
00437 
00438   QString start = lineStr.left(col);
00439   int leftBracket = start.findRev("(");
00440   int rightBracket = start.findRev(")");
00441   int equal = start.findRev("=");
00442   if(equal == -1) return false; // ok not found
00443   if(leftBracket == -1) return false; // ok not found
00444   if(rightBracket>leftBracket) return false; // we are out of (..)
00445   start = start.mid(equal,leftBracket-equal+1);
00446   //  cerr << "NEW: " << start << endl;
00447   QRegExp newre("=[& \t]*new[ \t]+([A-Za-z_]+)[ \t]*\\(");
00448   if(newre.search(start.local8Bit()) != -1){
00449     if( m_model->globalNamespace()->hasClass(newre.cap(1)) ){ // exists this class?
00450       ClassDom pClass = m_model->globalNamespace()->classByName(newre.cap(1))[ 0 ];
00451       FunctionList methodList = pClass->functionList();
00452       FunctionList::Iterator methodIt;
00453       for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) {
00454         if((*methodIt)->name() == newre.cap(1)){
00455       ArgumentDom pArg = (*methodIt)->argumentList().first();
00456       m_argWidgetShow = true;
00457       QValueList <QString> functionList;
00458       if(pArg){
00459         functionList.append((*methodIt)->name()+"("+ pArg->type()+")");
00460       }
00461       m_codeInterface->showArgHint ( functionList, "()", "," );
00462       return true;
00463     }
00464       }
00465     }
00466   }
00467   return false;
00468 }
00469 bool PHPCodeCompletion::checkForNewInstance(QString lineStr,int col,int /*line*/){
00470   //  cerr  << "enter checkForNewInstance" << endl;
00471   QString start = lineStr.left(col);
00472   QRegExp newre("=[& \t]*new[ \t]+([A-Za-z_]+)");
00473   if(newre.search(start.local8Bit()) != -1){
00474     QString classStart = newre.cap(1);
00475     if(start.right(2) == classStart){
00476       QValueList<KTextEditor::CompletionEntry> list;
00477 
00478       ClassList classList = m_model->globalNamespace()->classList();
00479       ClassList::Iterator classIt;
00480       for (classIt = classList.begin(); classIt != classList.end(); ++classIt) {
00481     if((*classIt)->name().startsWith(classStart)){
00482       KTextEditor::CompletionEntry e;
00483       e.text = (*classIt)->name();
00484       list.append(e);
00485     }
00486       }
00487       if(classStart == "ob") {
00488     KTextEditor::CompletionEntry e;
00489     e.text = "object";
00490     list.append(e);
00491       }
00492       if(classStart == "ar") {
00493     KTextEditor::CompletionEntry e;
00494     e.text = "array";
00495     list.append(e);
00496       }
00497       if(list.count() >0){
00498     m_completionBoxShow=true;
00499     m_codeInterface->showCompletionBox(list,2);
00500     return true;
00501       }
00502     }
00503   }
00504   return false;
00505 }
00506 
00507 QValueList<KTextEditor::CompletionEntry> PHPCodeCompletion::getClassMethodsAndVariables(QString className){
00508   QValueList<KTextEditor::CompletionEntry> list;
00509   ClassDom pClass;
00510   do {
00511     if(m_model->globalNamespace()->hasClass(className) ){
00512       pClass = m_model->globalNamespace()->classByName(className)[ 0 ];
00513       FunctionList methodList = pClass->functionList();
00514       FunctionList::Iterator methodIt;
00515       for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) {
00516     KTextEditor::CompletionEntry e;
00517     e.text = (*methodIt)->name();
00518     //  ParsedArgument* pArg = pMethod->arguments.first();
00519     //  if(pArg->type() == ""){
00520     e.postfix ="()";
00521     //  }else{
00522     //      e.postfix ="(...)";
00523     //  }
00524     list.append(e);
00525       }
00526       VariableList attrList = pClass->variableList();
00527       VariableList::Iterator attrIt;
00528       for (attrIt = attrList.begin(); attrIt != attrList.end(); ++attrIt) {
00529     KTextEditor::CompletionEntry e;
00530     QString name = (*attrIt)->name();
00531     e.text = name;
00532     e.postfix ="";
00533     list.append(e);
00534       }
00535 
00536 
00537       if(pClass->baseClassList().count() !=0){
00538     className = pClass->baseClassList().first();
00539       }
00540       else{
00541     className ="";
00542       }
00543     } else {
00544         pClass = 0;
00545     }
00546   } while (pClass != 0);
00547   return list;
00548 }
00549 
00550 #include "phpcodecompletion.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 22 09:22:33 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003