/* Copyright (C) 2005 by Nicolas Escuder Copyright (C) 2001 by smeier@kdevelop.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 "phpcodecompletion.h" #include "phpsupportpart.h" #include "phpconfigdata.h" #include #include #include #include #include #include #include #include #include "phpfile.h" using namespace std; PHPCodeCompletion::PHPCodeCompletion(PHPSupportPart *phpSupport, PHPConfigData *config) : TQObject(), m_cursorInterface(0), m_codeInterface(0), m_editInterface(0), m_selectionInterface(0) { m_phpSupport = phpSupport; m_config = config; m_model = phpSupport->codeModel(); m_argWidgetShow = false; m_completionBoxShow = false; readGlobalPHPFunctionsFile(); } PHPCodeCompletion::~PHPCodeCompletion(){ } void PHPCodeCompletion::readGlobalPHPFunctionsFile(){ TDEStandardDirs *dirs = PHPSupportFactory::instance()->dirs(); TQString phpFuncFile = dirs->findResource("data","kdevphpsupport/phpfunctions"); TQRegExp lineReg(":([0-9A-Za-z_]+) ([0-9A-Za-z_]+)\\((.*)\\)"); FunctionCompletionEntry e; TQFile f(phpFuncFile); if ( f.open(IO_ReadOnly) ) { // file opened successfully TQTextStream t( &f ); // use a text stream TQString s; while ( !t.eof() ) { // until end of file... s = t.readLine(); // line of text excluding '\n' if (lineReg.search(s.local8Bit()) != -1) { e.prefix = lineReg.cap(1); e.text = lineReg.cap(2); e.postfix = "(" + TQString(lineReg.cap(3)) + ")"; e.prototype = TQString(lineReg.cap(1)) + " " + TQString(lineReg.cap(2)) + "(" + TQString(lineReg.cap(3)) + ")"; m_globalFunctions.append(e); } } f.close(); } } void PHPCodeCompletion::argHintHided(){ kdDebug(9018) << "PHPCodeCompletion::argHintHided" << endl; m_argWidgetShow = false; } void PHPCodeCompletion::completionBoxHided(){ kdDebug(9018) << "PHPCodeCompletion::completionBoxHided()" << endl; m_completionBoxShow = false; } void PHPCodeCompletion::setActiveEditorPart(KParts::Part *part) { if (!part || !part->widget()) return; kdDebug(9018) << "PHPCodeCompletion::setActiveEditorPart" << endl; if (!(m_config->getCodeCompletion() || m_config->getCodeHinting())) return; // no help m_editInterface = dynamic_cast(part); if (!m_editInterface) { kdDebug(9018) << "editor doesn't support the EditDocumentIface" << endl; return; } m_cursorInterface = dynamic_cast(part->widget()); if (!m_cursorInterface) { kdDebug(9018) << "editor does not support the ViewCursorInterface" << endl; return; } m_codeInterface = dynamic_cast(part->widget()); if (!m_codeInterface) { // no CodeCompletionDocument available kdDebug(9018) << "editor doesn't support the CodeCompletionDocumentIface" << endl; return; } m_selectionInterface = dynamic_cast(part); if (!m_selectionInterface) { kdDebug(9018) << "editor doesn't support the SelectionInterface" << endl; return; } disconnect(part->widget(), 0, this, 0 ); // to make sure that it is't connected twice // connect(part->widget(), TQT_SIGNAL(cursorPositionChanged()), this, TQT_SLOT(cursorPositionChanged())); connect( part, TQT_SIGNAL(textChanged()), this, TQT_SLOT(cursorPositionChanged()) ); connect(part->widget(), TQT_SIGNAL(argHintHidden()), this, TQT_SLOT(argHintHided())); connect(part->widget(), TQT_SIGNAL(completionAborted()), this, TQT_SLOT(completionBoxHided())); connect(part->widget(), TQT_SIGNAL(completionDone()), this, TQT_SLOT(completionBoxHided())); } void PHPCodeCompletion::cursorPositionChanged(){ uint line, col; if( !m_cursorInterface || !m_selectionInterface || !m_codeInterface || !m_editInterface ) return; m_cursorInterface->cursorPositionReal(&line, &col); kdDebug(9018) << "cursorPositionChanged:" << line << ":" << col << endl; m_currentLine = line; TQString lineStr = m_editInterface->textLine(line); if (lineStr.isNull() || lineStr.isEmpty()) { return; } if (m_selectionInterface->hasSelection()) { kdDebug(9018) << "No CodeCompletion/ArgHinting at the moment, because text is selected" << endl; return; } if (m_config->getCodeHinting()) { int pos1 = lineStr.findRev("(", col - 1); int pos2 = lineStr.findRev(TQRegExp("[ \\t=;\\$\\.\\(\\)]"), pos1 - 1); int pos3 = lineStr.findRev(")", col); if (pos1 > pos2 && pos1 != -1 && pos3 < pos1) { TQString line = lineStr.mid(pos2 + 1, pos1 - pos2 - 1).stripWhiteSpace(); checkForArgHint(line, col); kdDebug(9018) << "end checkForArgHint" << endl; } } if (m_config->getCodeCompletion()) { if (m_completionBoxShow == true) { return; } int pos = lineStr.findRev(TQRegExp("[ \\t=;\\$\\.\\(\\)]"), col - 1); TQString line = lineStr.mid(pos + 1, col - pos).stripWhiteSpace(); if (checkForVariable(line, col)) { kdDebug(9018) << "end checkForVariable" << endl; return; } if (checkForStaticFunction(line, col)) { kdDebug(9018) << "end checkForStaticFunction" << endl; return; } if(checkForGlobalFunction(line, col)) { kdDebug(9018) << "end checkForGlobalFunction" << endl; return; } pos = lineStr.stripWhiteSpace().findRev(TQRegExp("[ \\t=;\\$\\.\\(\\)]"), col - 1); line = lineStr.mid(pos + 1, col - pos); if (checkForNew(line, col)) { kdDebug(9018) << "end checkForNew" << endl; return; } if (checkForExtends(line, col)) { kdDebug(9018) << "end checkForExtends" << endl; return; } kdDebug(9018) << "end checkFor" << endl; } } bool PHPCodeCompletion::showCompletionBox(TQValueList list, unsigned long max) { if (list.count() > 0) { if (list.count() == 1) { KTextEditor::CompletionEntry e = list.first(); if (e.text.length() == max) return false; } m_completionBoxShow = true; m_codeInterface->showCompletionBox(list, max, FALSE); return true; } return false; } bool PHPCodeCompletion::checkForStaticFunction(TQString line, int col) { kdDebug(9018) << "checkForStaticFunction" << endl; TQValueList list; if (line.find("::") == -1) return false; TQRegExp Class("([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)::([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|)"); Class.setCaseSensitive(FALSE); if (Class.search(line) != -1) { TQString classname = Class.cap(1); TQString function = Class.cap(2); ClassList classList = getClassByName(classname); ClassList::Iterator classIt; for (classIt = classList.begin(); classIt != classList.end(); ++classIt) { ClassDom nClass = *classIt; FunctionList funcList = nClass->functionList(); FunctionList::Iterator funcIt; for (funcIt = funcList.begin(); funcIt != funcList.end(); ++funcIt) { FunctionDom nFunc = *funcIt; if ((function.isEmpty() || nFunc->name().startsWith(function, FALSE)) && nFunc->isStatic()) { KTextEditor::CompletionEntry e; e.prefix = nClass->name() + " ::"; e.text = nFunc->name(); ArgumentDom pArg = (*funcIt)->argumentList().first(); if (pArg) e.postfix = "(" + pArg->type() +")"; else e.postfix = "()"; list.append(e); } } if (nClass->baseClassList().count() != 0) { TQStringList base = nClass->baseClassList(); TQStringList::Iterator nameIt; for (nameIt = base.begin(); nameIt != base.end(); ++nameIt) { ClassList baseList = getClassByName(*nameIt); ClassList::Iterator baseIt; for (baseIt = baseList.begin(); baseIt != baseList.end(); ++baseIt) classList.append(*baseIt); } } } return showCompletionBox(list, Class.cap(2).length()); } return false; } bool PHPCodeCompletion::checkForNew(TQString line, int col){ kdDebug(9018) << "checkForNew" << endl; TQValueList list; if (line.find("new ", 0, FALSE) == -1) return false; TQRegExp New("[& \t]*new[ \t]+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|)"); New.setCaseSensitive(FALSE); if (New.search(line) != -1) { list = getClasses( New.cap(1) ); if (New.cap(1).lower() == "ob") { KTextEditor::CompletionEntry e; e.text = "object"; list.append(e); } if (New.cap(1).lower() == "ar") { KTextEditor::CompletionEntry e; e.text = "array"; list.append(e); } return showCompletionBox(list, New.cap(1).length()); } return false; } bool PHPCodeCompletion::checkForExtends(TQString line, int col){ kdDebug(9018) << "checkForExtends" << endl; TQValueList list; if (line.find("extends", 0, FALSE) == -1) return false; TQRegExp extends("[ \t]*extends[ \t]+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|)"); extends.setCaseSensitive(FALSE); if (extends.search(line) != -1) { list = getClasses(extends.cap(1)); return showCompletionBox(list, extends.cap(1).length()); } return false; } bool PHPCodeCompletion::checkForVariable(TQString line, int col){ kdDebug(9018) << "checkForVariable" << endl; TQValueList list; TQString args; if (line.find("->") == -1) { return false; } if (line.left(2) != "->") { int pos = line.findRev("->"); args = line.mid(pos + 2, line.length() - pos); line = line.mid(0, pos); } TQStringList vars = TQStringList::split("->", line); TQString classname; for ( TQStringList::Iterator it = vars.begin(); it != vars.end(); ++it ) { classname = getClassName(*it, classname); } if (classname.isEmpty()) { return false; } this->setStatusBar(line, classname); list = this->getFunctionsAndVars(classname, args); return showCompletionBox(list, args.length()); } bool PHPCodeCompletion::checkForGlobalFunction(TQString line, int col) { kdDebug(9018) << "checkForGlobalFunction(" + line + "," << col << endl; TQValueList list; if (line.length() < 3) return false; list = this->getFunctionsAndVars("", line); return showCompletionBox(list, line.length()); } TQValueList PHPCodeCompletion::getClasses(TQString name) { TQValueList list; TQStringList added; ClassList classList = m_model->globalNamespace()->classList(); ClassList::Iterator classIt; for (classIt = classList.begin(); classIt != classList.end(); ++classIt) { ClassDom nClass = *classIt; if (name == NULL || name.isEmpty() || nClass->name().startsWith(name, FALSE)) { KTextEditor::CompletionEntry e; TQStringList::Iterator it = added.find(nClass->name()); if (it == added.end()) { e.text = nClass->name(); list.append(e); added.append(nClass->name()); } } } return list; } TQValueList PHPCodeCompletion::getFunctionsAndVars(TQString classname, TQString function) { kdDebug(9018) << "getFunctionsAndVars " << classname << endl; TQValueList list; if (classname.isEmpty()) { TQValueList::Iterator it; for( it = m_globalFunctions.begin(); it != m_globalFunctions.end(); ++it ) { if((*it).text.startsWith(function, FALSE)){ KTextEditor::CompletionEntry e; e = (*it); list.append(e); } } FunctionList methodList = m_model->globalNamespace()->functionList(); FunctionList::Iterator methodIt; for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) { if ((*methodIt)->name().startsWith(function, FALSE)){ KTextEditor::CompletionEntry e; e.text = (*methodIt)->name(); ArgumentDom pArg = (*methodIt)->argumentList().first(); if (pArg) e.postfix = "(" + pArg->type() +")"; else e.postfix = "()"; list.append(e); } } return list; } ClassList classList = getClassByName(classname); ClassList::Iterator classIt; for (classIt = classList.begin(); classIt != classList.end(); ++classIt) { ClassDom nClass = *classIt; FunctionList methodList = nClass->functionList(); FunctionList::Iterator methodIt; for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) { FunctionDom pMethod = *methodIt; if (function.isEmpty() || pMethod->name().startsWith(function, FALSE)) { KTextEditor::CompletionEntry e; ArgumentDom arg = pMethod->argumentList().first(); e.prefix = nClass->name() + " ::"; e.text = pMethod->name(); e.postfix = "(" + arg->type() + ")"; list.append(e); } } VariableList attrList = nClass->variableList(); VariableList::Iterator attrIt; for (attrIt = attrList.begin(); attrIt != attrList.end(); ++attrIt) { VariableDom pVar = *attrIt; if (function.isEmpty() || pVar->name().startsWith(function, FALSE)) { KTextEditor::CompletionEntry e; e.prefix = nClass->name() + " ::"; e.text = pVar->name(); e.postfix = ""; list.append(e); } } if (nClass->baseClassList().count() != 0) { TQStringList base = nClass->baseClassList(); TQStringList::Iterator nameIt; for (nameIt = base.begin(); nameIt != base.end(); ++nameIt) { ClassList baseList = getClassByName(*nameIt); ClassList::Iterator baseIt; for (baseIt = baseList.begin(); baseIt != baseList.end(); ++baseIt) classList.append(*baseIt); } } } return list; } TQStringList PHPCodeCompletion::getArguments(TQString classname, TQString function) { kdDebug(9018) << "getArguments " << function << endl; TQStringList list; if (classname.isEmpty()) { TQValueList::Iterator it; for( it = m_globalFunctions.begin(); it != m_globalFunctions.end(); ++it ) { if((*it).text.lower() == function.lower()){ KTextEditor::CompletionEntry e = (*it); list.append(e.text + e.postfix); } } FunctionList methodList = m_model->globalNamespace()->functionList(); FunctionList::Iterator methodIt; for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) { if ((*methodIt)->name().lower() == function.lower()){ KTextEditor::CompletionEntry e; ArgumentDom pArgs; TQString args = "()"; ArgumentDom pArg = (*methodIt)->argumentList().first(); if (pArgs) args = "(" + pArg->type() +")"; list.append((*methodIt)->name() + "(" + args +")"); } } return list; } ClassList classList = getClassByName(classname); ClassList::Iterator classIt; for (classIt = classList.begin(); classIt != classList.end(); ++classIt) { ClassDom nClass = *classIt; FunctionList methodList = nClass->functionList(); FunctionList::Iterator methodIt; for (methodIt = methodList.begin(); methodIt != methodList.end(); ++methodIt) { if ((*methodIt)->name().lower() == function.lower()) { ArgumentDom pArg = (*methodIt)->argumentList().first(); if (pArg) list.append(nClass->name() + "::" + function + "(" + pArg->type() +")"); } } if (nClass->baseClassList().count() != 0) { TQStringList base = nClass->baseClassList(); TQStringList::Iterator nameIt; for (nameIt = base.begin(); nameIt != base.end(); ++nameIt) { ClassList baseList = getClassByName(*nameIt); ClassList::Iterator baseIt; for (baseIt = baseList.begin(); baseIt != baseList.end(); ++baseIt) classList.append(*baseIt); } } } return list; } TQString PHPCodeCompletion::getCurrentClassName() { kdDebug(9018) << "getCurrentClassName" << endl; TQRegExp Class("^[ \t]*(abstract|final|)[ \t]*class[ \t]+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[ \t]*(extends[ \t]*([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))?.*$"); Class.setCaseSensitive(FALSE); for(int i = m_currentLine; i >= 0; i--){ TQString line = m_editInterface->textLine(i); if (!line.isNull()) { if (Class.search(line) != -1) return Class.cap(2); } } return TQString(); } TQString PHPCodeCompletion::getClassName(TQString varName, TQString classname) { kdDebug(9018) << "getClassName " << varName << "::" << classname << endl; if (varName.find("$") == 0) varName = varName.mid(1); if (varName.lower() == "this") return this->getCurrentClassName(); if (classname.isEmpty()) { VariableList attrList = m_model->globalNamespace()->variableList(); VariableList::Iterator attrIt; for (attrIt = attrList.begin(); attrIt != attrList.end(); ++attrIt) { if ((*attrIt)->name().lower() == varName.lower()) return (*attrIt)->type(); } } ClassList classList = getClassByName( classname ); ClassList::Iterator classIt; for (classIt = classList.begin(); classIt != classList.end(); ++classIt) { ClassDom pClass = *classIt; FunctionList funcList = pClass->functionList(); FunctionList::Iterator funcIt; for (funcIt = funcList.begin(); funcIt != funcList.end(); ++funcIt) { if (TQString((*funcIt)->name().lower() + "(") == varName.lower()) return (*funcIt)->resultType(); } VariableList attrList = pClass->variableList(); VariableList::Iterator attrIt; for (attrIt = attrList.begin(); attrIt != attrList.end(); ++attrIt) { if ((*attrIt)->name().lower() == varName.lower()) return (*attrIt)->type(); } } kdDebug(9018) << "Need " << classname << " " << varName << endl; /* /// @fixme peut devenir recursif voir xbutton.php ligne 204 TQRegExp createmember("\\" + varName + "[ \t]*=[ \t]*(.*)[ \t]*;"); for(int i = m_currentLine; i >= 0; i--){ TQString line = m_editInterface->textLine(i); if (!line.isNull() && line.find(varName,0 , FALSE) != -1) { if (createmember.search(line) != -1) { TQString right = createmember.cap(1).stripWhiteSpace(); TQStringList vars = TQStringList::split("->", right); for ( TQStringList::Iterator it = vars.begin(); it != vars.end(); ++it ) { TQString objet = *it; ++it; if (it == vars.end()) break; TQString var = *it; if (objet.lower() == "$this") objet = this->getCurrentClassName(); classname = getClassName(var, objet); NamespaceDom varns = m_model->globalNamespace()->namespaceByName("varsns"); TQString fromns; if (varns) { TQString name = objet + "::" + var; kdDebug(9018) << name << endl; VariableDom nVar = varns->variableByName(name); fromns = nVar->type(); } kdDebug(9018) << "Need " << objet << " " << var << " " << fromns << " " << vars.size() << endl; } kdDebug(9018) << "Dehors" << " " << classname << endl; return classname; } } } */ return ""; } TQValueList PHPCodeCompletion::getClassByName(TQString classname) { TQValueList CList; ClassList classList = m_model->globalNamespace()->classList(); ClassList::Iterator classIt; for (classIt = classList.begin(); classIt != classList.end(); ++classIt) { ClassDom nClass = *classIt; if (nClass->name().lower() == classname.lower()) CList.append( nClass ); } return CList; } bool PHPCodeCompletion::checkForArgHint(TQString line, int col) { kdDebug(9018) << "checkForArgHint" << endl; TQValueList list; TQStringList argsList; if (m_argWidgetShow == true) return false; if (line.find("::") != -1) { TQRegExp Static("([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)::([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)"); Static.setCaseSensitive(FALSE); if (Static.search(line) != -1) { TQString classname = Static.cap(1); TQString function = Static.cap(2); argsList = getArguments(classname, function); if (argsList.count() > 0) { m_argWidgetShow = true; m_codeInterface->showArgHint ( argsList, "()", "," ); return true; } } } if (line.findRev("->") != -1) { int pos1 = line.findRev("->"); TQString classname; TQString function = line.mid(pos1 + 2); line = line.mid(0, pos1); kdDebug(9018) << "checkForArgHint 2 " << line << endl; TQStringList vars = TQStringList::split("->", line); for ( TQStringList::Iterator it = vars.begin(); it != vars.end(); ++it ) { kdDebug(9018) << "for " << line << endl; classname = getClassName(*it, classname); kdDebug(9018) << "next " << line << endl; } kdDebug(9018) << "checkForArgHint 4 " << line << endl; argsList = getArguments(classname, function); if (argsList.count() > 0) { m_argWidgetShow = true; m_codeInterface->showArgHint ( argsList, "()", "," ); return true; } } kdDebug(9018) << "checkForArgHint 0 " << line << endl; argsList = getArguments("", line); if (argsList.count() > 0) { m_argWidgetShow = true; m_codeInterface->showArgHint ( argsList, "()", "," ); return true; } argsList = getArguments(line, line); if (argsList.count() > 0) { m_argWidgetShow = true; m_codeInterface->showArgHint ( argsList, "()", "," ); return true; } return false; } void PHPCodeCompletion::setStatusBar(TQString expr, TQString type) { m_phpSupport->mainWindow()->statusBar()->message( i18n("Type of %1 is %2").arg(expr).arg(type), 1000 ); } #include "phpcodecompletion.moc"