00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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) ) {
00055 QTextStream t( &f );
00056 QString s;
00057 while ( !t.eof() ) {
00058 s = t.readLine();
00059 if(lineReg.search(s.local8Bit()) != -1){
00060 e.prefix = lineReg.cap(1);
00061 e.text = lineReg.cap(2);
00062
00063 e.postfix ="()";
00064
00065
00066
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;
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) {
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 );
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;
00142
00143
00144
00145
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
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 ){
00192 kdDebug(9018) << "enter checkForMethodArgHint" << endl;
00193 if(m_argWidgetShow){
00194 return false;
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
00202 return false;
00203 }
00204 QString variableLine = methodStart.mid(varStart+1);
00205 if(variableLine.isNull()){ return false;}
00206
00207 QString className = "";
00208 QStringList vars = QStringList::split("->",variableLine);
00209 QString methodName = vars.last();
00210
00211 vars.remove(vars.fromLast());
00212 for ( QStringList::Iterator it = vars.begin(); it != vars.end(); ++it ) {
00213 className = this->getClassName("$" + (*it),className);
00214 }
00215
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 ){
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
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) {
00298
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) {
00313 return classre.cap(1);
00314 }
00315 }
00316 }
00317 return QString::null;
00318 }
00319
00320 bool PHPCodeCompletion::checkForGlobalFunctionArgHint(QString lineStr,int col,int ){
00321 kdDebug(9018) << "enter checkForGlobalFunctionArgHint" << endl;
00322 if(m_argWidgetShow){
00323 return false;
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;
00334 if(rightBracket>leftBracket) return false;
00335 methodStart = methodStart.left(leftBracket+1);
00336
00337 QRegExp functionre("([A-Za-z_]+)[ \t]*\\(");
00338 if(functionre.search(methodStart.local8Bit()) != -1){
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
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
00381 QString startStr =lineStr.mid(col-3,3);
00382 if(startStr.isNull()){
00383 kdDebug(9018) << "not enough letters" << endl;
00384 return false;
00385 }
00386
00387 QString extraChar = " \t+-=/*;)(}{";
00388 if(extraChar.find( startStr[0] ) != -1){
00389 methodStart = startStr.right(2);
00390 }
00391
00392
00393 if(!methodStart.isEmpty()){
00394 return doGlobalMethodCompletion(methodStart);
00395 }
00396 return false;
00397 }
00398
00399 bool PHPCodeCompletion::doGlobalMethodCompletion(QString methodStart){
00400
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 ){
00433
00434 if(m_argWidgetShow){
00435 return false;
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;
00443 if(leftBracket == -1) return false;
00444 if(rightBracket>leftBracket) return false;
00445 start = start.mid(equal,leftBracket-equal+1);
00446
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)) ){
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 ){
00470
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
00519
00520 e.postfix ="()";
00521
00522
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"