diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 114a878c64ce6f8223cfd22d76a20eb16d177e5e (patch) | |
tree | acaf47eb0fa12142d3896416a69e74cbf5a72242 /languages/cpp/cppcodecompletion.cpp | |
download | tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.tar.gz tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdevelop@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'languages/cpp/cppcodecompletion.cpp')
-rw-r--r-- | languages/cpp/cppcodecompletion.cpp | 4492 |
1 files changed, 4492 insertions, 0 deletions
diff --git a/languages/cpp/cppcodecompletion.cpp b/languages/cpp/cppcodecompletion.cpp new file mode 100644 index 00000000..27cc9009 --- /dev/null +++ b/languages/cpp/cppcodecompletion.cpp @@ -0,0 +1,4492 @@ +/*************************************************************************** + cppcodecompletion.cpp - description + ------------------- +begin : Sat Jul 21 2001 +copyright : (C) 2001 by Victor R�er +email : victor_roeder@gmx.de +copyright : (C) 2002,2003 by Roberto Raggi +email : roberto@kdevelop.org +copyright : (C) 2005 by Adam Treat +email : manyoso@yahoo.com +copyright : (C) 2006,2007 by David Nolden +email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "cppcodecompletion.h" + + +#include "cppcodecompletionconfig.h" +#include "backgroundparser.h" +#include "ast.h" +#include "ast_utils.h" +#include "codeinformationrepository.h" +#include "parser.h" +#include "lexer.h" +#include "tree_parser.h" +#include "cpp_tags.h" +#include "cppsupport_utils.h" +#include "tag_creator.h" + +#include <typeinfo> + +#include <qpopupmenu.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmainwindow.h> +#include <kmessagebox.h> +#include <kparts/part.h> +#include <kstatusbar.h> +#include <ktexteditor/document.h> +#include <kaction.h> + +#include <qdatastream.h> +#include <qfile.h> +#include <qmap.h> +#include <qregexp.h> +#include <qstatusbar.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qpair.h> +#include <qvaluestack.h> + +#include <kdevpartcontroller.h> +#include <kdevmainwindow.h> +#include <kdevproject.h> +#include <kdevcoderepository.h> +#include <codemodel_utils.h> +#include <codemodel.h> +#include <codebrowserfrontend.h> + +#include "codecompletionentry.h" +#include "typedesc.h" +#include "computerecoverypoints.h" +#include "completiondebug.h" +#include "bithelpers.h" +#include "stringhelpers.h" +#include "simpletype.h" +#include "simpletypecachebinder.h" +#include "safetycounter.h" +#include "cppevaluation.h" +#include "simplecontext.h" +#include "simpletypefunction.h" + +//#define DISABLE_TRACING + +CppCodeCompletion* CppCodeCompletion::m_instance = 0; + +const bool disableVerboseForCompletionList = false; +const bool disableVerboseForContextMenu = false; +const bool contextMenuEntriesAtTop = false; +bool showNamespaceAppearances = true; + +const char* constructorPrefix = "<constructor>"; +const char* destructorPrefix = "<destructor>"; +///This enables-disables the automatic processing of the expression under the mouse-cursor +//#define DISABLETOOLTIPS + +/** +-- TODO: Does not yet correctly search for overloaded functions and select the right one +-- TODO: The documentation shown in the calltips looks very bad, a better solution must be found(maybe an additional tooltip) +*/ + + void statusBarText( const QString& str, int time ) { + CppCodeCompletion* c = CppCodeCompletion::instance(); + if( c ) + c->addStatusText( str, time ); + } + +TypePointer CppCodeCompletion::createGlobalNamespace() { + KSharedPtr<SimpleTypeCachedNamespace> n = new SimpleTypeCachedNamespace( QStringList(), QStringList() ); + n->addAliases(m_pSupport->codeCompletionConfig()->namespaceAliases() ); + return n.data(); +} + +template <class Item> +class ItemLocker { + public: + ItemLocker( Item& it ) : item( it ) { + it.lock(); + } + ~ItemLocker() { + item.unlock(); + } + private: + Item& item; +}; + +ParsedFilePointer getParsedFile( CodeModelItem* i ) { + if( !i || !i->file() || !i->file()->parseResult() ) return 0; + return dynamic_cast<ParsedFile*>( i->file()->parseResult().data()); +} + +SafetyCounter safetyCounter; +CppCodeCompletion* cppCompletionInstance = 0; + +//file global functions, must be before any "using namespace" +QString cleanForMenu( QString txt ) { + return txt.replace( "&", "&&" ).replace( " ", " " ); +} + +QString buildSignature( TypePointer currType ) { + SimpleTypeFunctionInterface * f = currType->asFunction(); + if ( !f ) + return ""; + + QString ret; + LocateResult rtt = currType->locateDecType( f->getReturnType() ); + if ( rtt->resolved() || rtt.resolutionCount() > 1 ) + ret = rtt->fullNameChain(); + else + ret = f->getReturnType().fullNameChain(); + + + TypeDesc desc = currType->desc(); + desc.decreaseFunctionDepth(); + + QString sig = ret + " " + desc.fullNameChain() + f->signature(); + if ( f->isConst() ) + sig += " const"; + return sig; +} + +uint PopupTracker::pendingPopups = 0; +PopupTracker* PopupTracker::pt = 0; + +/** Multiple empty lines are reduced to one, too long lines wrapped over, and the beginnings of the lines are normalized +*/ +QStringList maximumLength( const QStringList& in, int length ) { + QStringList ret; + uint firstNonSpace = 50000; + for ( QStringList::const_iterator it = in.begin(); it != in.end(); ++it ) + for ( uint a = 0; a < ( *it ).length(); a++ ) + if ( !( *it ) [ a ].isSpace() ) { + if ( firstNonSpace > a ) + firstNonSpace = a; + break; + } + if ( firstNonSpace == 50000 ) + return QStringList(); + + bool hadEmptyLine = false; + for ( QStringList::const_iterator it = in.begin(); it != in.end(); ++it ) { + if ( ( *it ).length() <= firstNonSpace ) { + if ( !hadEmptyLine ) + ret << " "; + hadEmptyLine = true; + } else { + hadEmptyLine = false; + QString str = ( *it ).mid( firstNonSpace ); + while ( !str.isEmpty() ) { + if ( (int)str.length() < length ) { + ret << str; + break; + } else { + ret << str.left( length ) + "\\"; + str = str.mid( length ); + } + } + } + } + return ret; +} + +QStringList prepareTextForMenu( const QString& comment, int maxLines, int maxLength ) { + QStringList in = QStringList::split( "\n", comment ); + QStringList out; + for ( QStringList::iterator it = in.begin(); it != in.end(); ++it ) { + out << cleanForMenu( *it ); + if ( (int)out.count() >= maxLines ) { + out << "[...]"; + break; + } + } + + return maximumLength( out, maxLength ); +} + +QStringList formatComment( const QString& comment, int maxCols = 120 ) { + QStringList ret; + SafetyCounter s( 14 ); ///maximum of 14 lines + + QStringList lines = QStringList::split( "\n", comment ); + for ( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) { + QStringList words = QStringList::split( " ", *it ); + while ( !words.isEmpty() && s ) { + QString line = "? "; + int len = 0; + while ( !words.isEmpty() && len < maxCols ) { + len += words.front().length(); + line += words.front() + " "; + words.pop_front(); + } + ret << line; + } + } + if ( !s ) + ret << "? comment has too many lines"; + + return ret; +} + +bool operator < ( const CodeCompletionEntry& e1, const CodeCompletionEntry& e2 ) { + return e1.text < e2.text; +} + +template <class ItemType> +static QValueList<ItemType> unique( const QValueList<ItemType>& entryList ) { + + QValueList< ItemType > l; + QMap<QString, bool> map; + typename QValueList< ItemType >::ConstIterator it = entryList.begin(); + while ( it != entryList.end() ) { + CodeCompletionEntry e = *it++; + QString key = ( e.type + " " + + e.prefix + " " + + e.text + " " + + e.postfix + " " ).simplifyWhiteSpace().stripWhiteSpace(); + if ( map.find( key ) == map.end() ) { + map[ key ] = TRUE; + l << e; + } + } + return l; +} + +static QStringList unique( const QStringList& entryList ) { + + QStringList l; + QMap<QString, bool> map; + QStringList::ConstIterator it = entryList.begin(); + while ( it != entryList.end() ) { + QString e = *it++; + if ( map.find( e ) == map.end() ) { + map[ e ] = TRUE; + l << e; + } + } + return l; +} + +static QStringList unique( const QValueList<QStringList>& entryList ) { + + QStringList l; + QMap<QString, bool> map; + QValueList<QStringList>::ConstIterator it = entryList.begin(); + while ( it != entryList.end() ) { + QStringList li = ( *it++ ); + QString e = li.join( "\n" ); + if ( map.find( e ) == map.end() ) { + map[ e ] = TRUE; + l += li; + } + } + + return l; +} + + +bool tokenAt( const QString& text, const QString& token, int textPos ) { + if ( text.isEmpty() ) + return false; + + int tokenPos = token.length() - 1; + if ( tokenPos <= 0 || textPos <= 0 ) + return false; + + while ( text[ textPos ] == token[ tokenPos ] ) { + + --tokenPos; + --textPos; + + if ( tokenPos == 0 || textPos == 0 ) { + if ( tokenPos == 0 ) { + if ( textPos >= 1 && text[ textPos ] == token[ tokenPos ] ) { + QChar c = text[ textPos - 1 ]; + return c.isSpace() || c == '{' || c == '}' || c == ';'; + } else { + return false; + } + } else { + return false; + } + } + } + return false; +} + +CppSupportPart* CppCodeCompletion::cppSupport() const { + return m_pSupport; +} + +using namespace CompletionDebug; +using namespace StringHelpers; +using namespace BitHelpers; +using namespace CppEvaluation; + +struct PopupFillerHelpStruct { + CppCodeCompletion* receiver; + FileList files; + CppCodeCompletion::PopupActions& m_popupActions; + PopupFillerHelpStruct( CppCodeCompletion* rec ) : m_popupActions( rec->m_popupActions ) { + receiver = rec; + files = receiver->cppSupport()->codeModel()->fileList(); + } + + bool shouldShowIncludeMenu() const { + return true; + } + + QMap<QString, QPopupMenu*> m_namespacePopupCache; + + void insertItem( QPopupMenu* parent, SimpleTypeImpl::MemberInfo d , QString prefix ) { + Q_UNUSED(prefix); + + QString memType = d.memberTypeToString(); + + if ( d.memberType == SimpleTypeImpl::MemberInfo::Typedef && d.type->fullName() == "const int" ) + memType = "enum"; + + QString txt = i18n( "Jump to %1 %2" ).arg( memType ).arg( cleanForMenu( d.name ) ); + int id = parent->insertItem( txt, receiver, SLOT( popupAction( int ) ) ); + + receiver->m_popupActions.insert( id, d.decl ); + } + + void insertItem ( QPopupMenu* parent, TypeDesc d , QString prefix ) { + Debug dbg( "#insert# ", 10 ); + + QString txt1, txt2; + + if ( d.resolved() && d.resolved() ->isNamespace() ) { + SimpleTypeCachedNamespace * ns = dynamic_cast<SimpleTypeCachedNamespace*>( d.resolved().data() ); + if ( ns ) { + SimpleTypeNamespace::SlaveList slaves = ns->getSlaves( receiver->getIncludeFiles() ); + for ( SimpleTypeNamespace::SlaveList::iterator it = slaves.begin(); it != slaves.end(); ++it ) { + SimpleTypeCodeModel* cm = dynamic_cast<SimpleTypeCodeModel*>( ( *it ).first.first.resolved().data() ); + if ( cm && cm->item() ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + QString scope = cm->scope().join("::"); + QMap< QString, QPopupMenu* >::iterator it = m_namespacePopupCache.find( scope ); + if( it != m_namespacePopupCache.end() ) { + parent->insertItem( "Imported Namespace " + scope, *it ); + delete m; + } else { + parent->insertItem( "Imported Namespace " + scope, m ); + + insertItem( m, ( new SimpleTypeCachedCodeModel( cm->item() ) ) ->desc(), prefix ); + m_namespacePopupCache.insert( scope, m ); + } + } else { + SimpleTypeNamespace* cn = dynamic_cast<SimpleTypeNamespace*>( ( *it ).first.first.resolved().data() ); + if( cn ) { + TypePointer t = new SimpleTypeNamespace( cn ); //To avoid endless recursion, this needs to be done(the dynamic-cast above fails) + insertItem( parent, t->desc(), prefix ); + } + } + } + return ; + } + } + + if ( d.resolved() && receiver->cppSupport() ->codeCompletionConfig() ->showNamespaceAppearances() ) { + if ( SimpleTypeCachedCodeModel * item = dynamic_cast<SimpleTypeCachedCodeModel*>( d.resolved().data() ) ) { ///(1) + if ( item->item() && item->item() ->isNamespace() ) { + NamespaceModel* ns = dynamic_cast<NamespaceModel*>( item->item().data() ); + QStringList wholeScope = ns->scope(); + wholeScope << ns->name(); + for( FileList::iterator it = files.begin(); it != files.end(); ++it ) { + // if( !safetyCounter ) break; + NamespaceModel* ns = (*it).data(); + + for( QStringList::iterator it2 = wholeScope.begin(); it2 != wholeScope.end(); ++it2 ) { + if( ns->hasNamespace( (*it2) ) ) { + ns = ns->namespaceByName( *it2 ); + if( !ns ) break; + } else { + ns = 0; + break; + } + } + + if( ns ) { + ItemDom i(ns); + int sLine, sCol, eLine, eCol; + i->getStartPosition( &sLine, &sCol ); + i->getEndPosition( &eLine, &eCol ); + insertItem( parent, (new SimpleTypeCodeModel( i ))->desc(), prefix + " " + (*it)->name() + QString(" (%1 Lines): ").arg( eLine - sLine ) ); ///SimpleTypeCodeModel is used instead of SimpleTypeCachedNodeModel, so the detection at (1) does not trigger, this avoids endless recursion. + } + + } + return; + } + } + } + + if ( d.resolved() ) { + if ( d.resolved() ->asFunction() ) { + txt1 = prefix + i18n( "Jump to declaration of %1(...)" ).arg( d.resolved() ->scope().join( "::" ) ); + txt2 = prefix + i18n( "Jump to definition of %1(...)" ).arg( d.resolved() ->scope().join( "::" ) ); + } else { + txt1 = prefix + i18n( "Jump to %1" ).arg( cleanForMenu( d.resolved() ->scope().join( "::" ) ) ); + } + } else { + if( !BuiltinTypes::isBuiltin( d ) ) { + txt1 = prefix + d.name() + i18n( " is unresolved" ); + } else { + txt1 = prefix + d.name() + i18n( " (builtin " ) + BuiltinTypes::comment( d ) + ")"; + } + } + + int id = parent->insertItem( txt1, receiver, SLOT( popupAction( int ) ) ); + if ( d.resolved() ) + receiver->m_popupActions.insert( id, d.resolved() ->getDeclarationInfo() ); + + if ( !txt2.isEmpty() ) { + int id2 = parent->insertItem( txt2, receiver, SLOT( popupDefinitionAction( int ) ) ); + if ( d.resolved() ) + receiver->m_popupDefinitionActions.insert( id2, d.resolved() ->getDeclarationInfo() ); + } + } +}; + +ItemDom itemFromScope( const QStringList& scope, NamespaceDom startNamespace ) { + if ( scope.isEmpty() ) + return ItemDom(); + + NamespaceDom glob = startNamespace; + if ( !glob ) + return ItemDom(); + + ClassModel* curr = glob ; + + QStringList::const_iterator mit = scope.begin(); + + while ( curr->isNamespace() && mit != scope.end() && ( ( NamespaceModel* ) curr ) ->hasNamespace( *mit ) ) { + curr = &( *( ( ( NamespaceModel* ) curr ) ->namespaceByName( *mit ) ) ); + ++mit; + } + + while ( ( curr->isNamespace() || curr->isClass() ) && mit != scope.end() && curr->hasClass( *mit ) ) { + ClassList cl = curr->classByName( *mit ); + curr = &( **cl.begin() ); + ++mit; + } + + if ( mit != --scope.end() ) + return ItemDom(); + + TypeAliasList l = curr->typeAliasByName( *mit ); + if ( !l.isEmpty() ) + return model_cast<ItemDom>( l.front() ); + + VariableDom v = curr->variableByName( *mit ); + if ( v ) + return model_cast<ItemDom>( v ); + + ClassList c = curr->classByName( *mit ); + if ( !c.isEmpty() ) + return model_cast<ItemDom>( c.front() ); + + EnumDom en = curr->enumByName( *mit ); + if ( en ) + return model_cast<ItemDom>( en ); + + FunctionList f = curr->functionByName( *mit ); + if ( !f.isEmpty() ) + return model_cast<ItemDom>( f.front() ); + + FunctionDefinitionList fd = curr->functionDefinitionByName( *mit ); + if ( !fd.isEmpty() ) + return model_cast<ItemDom>( fd.front() ); + + return ItemDom(); +} + +struct PopupClassViewFillerHelpStruct { + CppCodeCompletion* receiver; + CppCodeCompletion::PopupActions& m_popupActions; + PopupClassViewFillerHelpStruct( CppCodeCompletion* rec ) : m_popupActions( rec->m_popupActions ) { + receiver = rec; + } + + bool shouldShowIncludeMenu() const { + return false; + } + + void insertItem( QPopupMenu* parent, SimpleTypeImpl::MemberInfo d , QString prefix ) { + Q_UNUSED(prefix); + FileDom f = receiver->m_pSupport->codeModel() ->fileByName( d.decl.file ); + if ( !f ) + return ; + + ItemDom dom = itemFromScope( QStringList::split( "::", d.name ), model_cast<NamespaceDom>( f ) ); + + QString memType = d.memberTypeToString(); + + if ( d.memberType == SimpleTypeImpl::MemberInfo::Typedef && d.type->fullName() == "const int" ) + memType = "enum"; + + QString txt = i18n( "Show %1 %2" ).arg( memType ).arg( cleanForMenu( d.name ) ); + int id = parent->insertItem( txt, receiver, SLOT( popupClassViewAction( int ) ) ); + + receiver->m_popupClassViewActions.insert( id, dom ); + } + + void insertItem ( QPopupMenu* parent, TypeDesc d , QString prefix ) { + Debug dbg( "#insert# ", 10 ); + + QString txt; + if ( !d.resolved() ) + return ; + + ItemDom dom; + + if ( d.resolved() ) { + SimpleTypeCodeModel * cm = dynamic_cast<SimpleTypeCodeModel*>( d.resolved().data() ); + if ( cm ) + dom = cm->item(); + } + + if ( d.resolved() ) { + if ( !dom && d.resolved() ->isNamespace() ) { + SimpleTypeCachedNamespace * ns = dynamic_cast<SimpleTypeCachedNamespace*>( d.resolved().data() ); + if ( ns ) { + SimpleTypeNamespace::SlaveList slaves = ns->getSlaves( receiver->getIncludeFiles() ); + for ( SimpleTypeNamespace::SlaveList::iterator it = slaves.begin(); it != slaves.end(); ++it ) { + SimpleTypeCodeModel* cm = dynamic_cast<SimpleTypeCodeModel*>( ( *it ).first.first.resolved().data() ); + if ( cm && cm->item() ) { + insertItem( parent, ( new SimpleTypeCachedCodeModel( cm->item() ) ) ->desc(), prefix ); + } else { + SimpleTypeNamespace* cn = dynamic_cast<SimpleTypeNamespace*>( ( *it ).first.first.resolved().data() ); + if( cn ) { + TypePointer t = new SimpleTypeNamespace( cn ); //to avoid endless recursion (caching would be better) + insertItem( parent, t->desc(), prefix ); + } + } + } + return ; + } + } else { + if ( dom ) { + QString n = d.resolved() ->scope().join( "::" ); + //QString n = d.fullNameChain(); + if ( d.resolved() ->asFunction() ) { + n = buildSignature( d.resolved() ); + } + txt = prefix + i18n( "Show %1" ).arg( cleanForMenu( n ) ); + } else { + txt = prefix + d.name() + " not in code-model"; + } + } + } else { + if( !BuiltinTypes::isBuiltin( d ) ) { + txt = prefix + d.name() + i18n( " is unresolved" ); + } else { + txt = prefix + d.name() + i18n( " (builtin " ) + BuiltinTypes::comment( d ) + ")"; + } + } + + int id = parent->insertItem( txt, receiver, SLOT( popupClassViewAction( int ) ) ); + + if ( dom ) + receiver->m_popupClassViewActions.insert( id, dom ); + } +}; + +template <class HelpStruct = PopupFillerHelpStruct> +class PopupFiller { + HelpStruct struk; + QString depthAdd; + SafetyCounter s; + public: + PopupFiller( HelpStruct str , QString dAdd, int maxCount = 100 ) : struk( str ), depthAdd( dAdd ), s( maxCount ) {} + + + void fillIncludes( const DeclarationInfo& decl, QPopupMenu* parent, bool& needSeparator ) { + if( !struk.receiver->getIncludeFiles()[ HashedString( decl.file ) ] ) { + QString file = decl.file; + //The include-file seems to be missing + if( needSeparator ) { + needSeparator = false; + parent->insertSeparator(); + } + + QString includeFile = file; + QFileInfo info( file ); + + Driver* driver = struk.receiver->cppSupport()->driver(); + if( driver ) { + QStringList elements = QStringList::split( "/", file ); + includeFile = elements.back(); + elements.pop_back(); + + Dependence d; + d.first = includeFile; + d.second = Dep_Local; + while( driver->findIncludeFile( d, struk.receiver->activeFileName() ) != file && !elements.empty() ) { + //kdDebug( 9007 ) << "could not find include-file \"" << d.first << "\"" << endl; + includeFile = elements.back() + "/" + includeFile; + d.first = includeFile; + elements.pop_back(); + } + if( elements.empty() ) + includeFile = "/" + includeFile; + + //kdDebug( 9007 ) << "found include-file \"" << includeFile << "\"" << endl; + } + int id = parent->insertItem( i18n( "#include \"%1\" ( defines %2 )" ).arg ( includeFile ).arg( decl.name ), struk.receiver, SLOT( popupAction( int ) ) ); + DeclarationInfo fakeDec; + fakeDec.name = decl.name; + fakeDec.file = includeFile; + fakeDec.startLine = -1; //Use startline -1 to indicate that instead of jumping to the file, the file should be included. + struk.m_popupActions.insert( id, fakeDec ); + } + } + + void fill( QPopupMenu * parent, LocateResult d, QString prefix = "", const DeclarationInfo & sourceVariable = DeclarationInfo() ) { + Debug dbg( "#fl# ", 10 ) + ; + + + if ( !s || !dbg ) { + //dbgMajor() << "safety-counter triggered while filling \"" << d.fullNameChain() << "\"" << endl; + return ; + } + + if ( !sourceVariable.name.isEmpty() && sourceVariable.name != "this" ) { + SimpleTypeImpl::MemberInfo f; + f.decl = sourceVariable; + f.name = sourceVariable.name; + f.type = d.desc(); + f.memberType = SimpleTypeImpl::MemberInfo::Variable; + + /*int id = m->insertItem( i18n("jump to variable-declaration \"%1\"").arg( type.sourceVariable.name ) , this, SLOT( popupAction( int ) ) ); + + m_popupActions.insert( id, type.sourceVariable );*/ + struk.insertItem( parent, f, prefix ); + + parent->insertSeparator(); + + if ( !sourceVariable.comment.isEmpty() ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Comment on %1" ).arg( sourceVariable.name ), m ); + QStringList ls = prepareTextForMenu( sourceVariable.comment, 15, 100 ); + for ( QStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { + m->insertItem( *it, 0, SLOT( popupClassViewAction( int ) ) ); + } + parent->insertSeparator(); + } + } + + struk.insertItem( parent, d, prefix ); + + if( d->resolved() && !d->resolved()->specialization().isEmpty() ) { + SimpleType p = d->resolved()->parent(); + LocateResult r = p->locateDecType( d->name() ); + if( r ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Specialized from \"%1\"" ).arg( cleanForMenu( r->fullNameChain() ) ), m ); + fill( m, r ); + } + } + + TypeDesc::TemplateParams p = d->templateParams(); + for ( TypeDesc::TemplateParams::iterator it = p.begin(); it != p.end(); ++it ) { + //if( (*it)->resolved() ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Template-param \"%1\"" ).arg( cleanForMenu( ( *it ) ->fullNameChain() ) ), m ); + fill( m, **it ); + /*} else { + fill( parent, **it, prefix + depthAdd ); + }*/ + } + + if ( d->resolved() ) { + if ( d->resolved() ->asFunction() ) { + LocateResult rt = d->resolved() ->locateDecType( d->resolved() ->asFunction() ->getReturnType() ); + if ( rt ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Return-type \"%1\"" ).arg( cleanForMenu( rt->fullNameChain() ) ), m ); + fill( m, rt ); + } + + QValueList<TypeDesc> args = d->resolved() ->asFunction() ->getArgumentTypes(); + QStringList argNames = d->resolved() ->asFunction() ->getArgumentNames(); + if ( !args.isEmpty() ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Argument-types" ), m ); + QStringList::iterator it2 = argNames.begin(); + for ( QValueList<TypeDesc>::iterator it = args.begin(); it != args.end(); ++it ) { + LocateResult at = d->resolved() ->locateDecType( *it ); + QString name = ""; + if ( it2 != argNames.end() ) { + name = *it2; + ++it2; + } + QPopupMenu * mo = PopupTracker::createPopup( m ); + m->insertItem( i18n( "Argument \"%1\"" ).arg( cleanForMenu( at->fullNameChain() + " " + name ) ), mo ); + fill( mo, at ); + + } + } + } + } +#ifndef DISABLE_TRACING + if ( d.trace() ) { + QValueList<QPair<SimpleTypeImpl::MemberInfo, TypeDesc> > trace = d.trace() ->trace(); + if ( !trace.isEmpty() ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Trace" ), m ); + + for ( QValueList<QPair<SimpleTypeImpl::MemberInfo, TypeDesc> >::iterator it = trace.begin(); it != trace.end(); ++it ) { + QPopupMenu * mo = PopupTracker::createPopup( m ); + QString tail = ( *it ).second.fullNameChain(); + if ( !tail.isEmpty() ) + tail = "::" + tail; + m->insertItem( i18n( "%1 -> %2" ).arg( cleanForMenu( ( *it ).first.name + tail ) ).arg( cleanForMenu( ( *it ).first.type->fullNameChain() + tail ) ), mo ); + + struk.insertItem( mo, ( *it ).first, prefix ); + + if ( !( *it ).first.decl.comment.isEmpty() ) { + mo->insertSeparator(); + QPopupMenu * m = PopupTracker::createPopup( mo ); + mo->insertItem( i18n( "Comment" ), m ); + QStringList ls = prepareTextForMenu( ( *it ).first.decl.comment, 15, 100 ); + for ( QStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { + m->insertItem( *it, 0, SLOT( popupClassViewAction( int ) ) ); + } + } + + /*bool needSeparator = true; + if( struk.shouldShowIncludeMenu() && struk.receiver->cppSupport()->codeCompletionConfig()->preProcessAllHeaders() && !(*it).first.decl.file.operator QString().isEmpty() ) + fillIncludes( (*it).first.decl, mo, needSeparator );*/ + } + } + } +#endif + + if ( d->resolved() ) { + QValueList<LocateResult> bases = d->resolved() ->getBases(); + for ( QValueList<LocateResult>::iterator it = bases.begin(); it != bases.end(); ++it ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Base-class \"%1\"" ).arg( cleanForMenu( ( *it ) ->fullNameChain() ) ), m ); + fill( m, *it ); + } + + if ( d->resolved() ->parent() && d->resolved() ->parent() ->desc() ) { + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Nested in \"%1\"" ).arg( cleanForMenu( d->resolved() ->parent() ->fullTypeResolved() ) ), m ); + fill( m, d->resolved() ->parent() ->desc() ); + } + + if ( !d->resolved() ->comment().isEmpty() ) { + parent->insertSeparator(); + QPopupMenu * m = PopupTracker::createPopup( parent ); + parent->insertItem( i18n( "Comment on %1" ).arg( cleanForMenu( d->name() ) ), m ); + QStringList ls = prepareTextForMenu( d->resolved() ->comment(), 15, 100 ); + for ( QStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { + m->insertItem( *it, 0, SLOT( popupClassViewAction( int ) ) ); + } + } + } + + //Add entries for including missing include-files + if( struk.shouldShowIncludeMenu() && struk.receiver->cppSupport()->codeCompletionConfig()->preProcessAllHeaders() ) { + bool needSeparator = true; + //Show the include-files for the whole trace, because usually the first in the trace should be the one to include + if ( d.trace() ) { + QValueList<QPair<SimpleTypeImpl::MemberInfo, TypeDesc> > trace = d.trace() ->trace(); + if ( !trace.isEmpty() ) { + for ( QValueList<QPair<SimpleTypeImpl::MemberInfo, TypeDesc> >::iterator it = trace.begin(); it != trace.end(); ++it ) { + if( struk.shouldShowIncludeMenu() && struk.receiver->cppSupport()->codeCompletionConfig()->preProcessAllHeaders() && !(*it).first.decl.file.operator QString().isEmpty() ) + fillIncludes( (*it).first.decl, parent, needSeparator ); + } + } + } + + //Show the include-file for the item itself + if( d->resolved() && !d->resolved()->isNamespace() && struk.receiver->cppSupport() ) { + fillIncludes( d->resolved()->getDeclarationInfo(), parent, needSeparator ); + } + } + } +}; + +struct CompTypeProcessor : public TypeProcessor { + SimpleType m_scope; + bool m_processArguments; + + CompTypeProcessor( SimpleType scope, bool processArguments ) : m_scope( scope ), m_processArguments( processArguments ) {} + + virtual QString parentType() { + return m_scope->fullType(); + } + + virtual QString processType( const QString& type ) { + if ( !m_processArguments ) + return type; + LocateResult t = m_scope->locateDecType( type ); + if ( t ) + return t->fullNameChain(); + else + return type; + } +}; + +struct CppCodeCompletionData { + QPtrList<RecoveryPoint> recoveryPoints; + //QStringList classNameList; + + CppCodeCompletionData() { + recoveryPoints.setAutoDelete( true ); + } + + RecoveryPoint* findRecoveryPoint( int line, int column ) { + if ( recoveryPoints.count() == 0 ) + return 0; + + QPair<int, int> pt = qMakePair( line, column ); + + QPtrListIterator<RecoveryPoint> it( recoveryPoints ); + RecoveryPoint* recPt = 0; + + while ( it.current() ) { + QPair<int, int> startPt = qMakePair( it.current() ->startLine, it.current() ->startColumn ); + QPair<int, int> endPt = qMakePair( it.current() ->endLine, it.current() ->endColumn ); + + if ( pt < startPt ) { + break; + } + + if ( startPt < pt && pt < endPt ) + recPt = it.current(); + + ++it; + } + + return recPt; + } + +}; + +CppCodeCompletion::CppCodeCompletion( CppSupportPart* part ) + : d( new CppCodeCompletionData ), + //Matches on includes + m_includeRx( "^\\s*#\\s*include\\s+[\"<]" ), + //Matches on C++ and C style comments as well as literal strings + m_cppCodeCommentsRx( "(//([^\n]*)(\n|$)|/\\*.*\\*/|\"([^\\\\]|\\\\.)*\")" ), + //Matches on alpha chars and '.' + m_codeCompleteChRx( "([A-Z])|([a-z])|(\\.)" ), + //Matches on "->" and "::" +m_codeCompleteCh2Rx( "(->)|(\\:\\:)" ) { + m_instance = this; + cppCompletionInstance = this; + m_cppCodeCommentsRx.setMinimal( true ); + + m_pSupport = part; + + connect( m_pSupport->codeCompletionConfig(), SIGNAL( stored() ), this, SLOT( emptyCache() ) ); + + m_activeCursor = 0; + m_activeEditor = 0; + m_activeCompletion = 0; + m_activeHintInterface = 0; + m_activeView = 0; + m_ccTimer = new QTimer( this ); + m_showStatusTextTimer = new QTimer( this ); + + m_ccLine = 0; + m_ccColumn = 0; + connect( m_ccTimer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) ); + connect( m_showStatusTextTimer, SIGNAL( timeout() ), this, SLOT( slotStatusTextTimeout() ) ); + + computeFileEntryList(); + + CppSupportPart* cppSupport = m_pSupport; + connect( cppSupport->project(), SIGNAL( addedFilesToProject( const QStringList& ) ), + this, SLOT( computeFileEntryList() ) ); + connect( cppSupport->project(), SIGNAL( removedFilesFromProject( const QStringList& ) ), + this, SLOT( computeFileEntryList() ) ); + connect( cppSupport, SIGNAL( synchronousParseReady( const QString&, ParsedFilePointer ) ), this, SLOT( synchronousParseReady( const QString&, ParsedFilePointer ) ) ); + + m_bArgHintShow = false; + m_bCompletionBoxShow = false; + m_blockForKeyword = false; + m_demandCompletion = false; + m_completionMode = NormalCompletion; + + m_repository = new CodeInformationRepository( cppSupport->codeRepository() ); + + connect( cppSupport->codeRepository(), SIGNAL(catalogRegistered( Catalog* )), this, SLOT( emptyCache() ) ); + connect( cppSupport->codeRepository(), SIGNAL(catalogUnregistered( Catalog* )), this, SLOT( emptyCache() ) ); + connect( cppSupport->codeRepository(), SIGNAL(catalogChanged( Catalog* )), this, SLOT( emptyCache() ) ); + + setupCodeInformationRepository(); + + if ( part->partController() ->parts() ) { + QPtrListIterator<KParts::Part> it( *part->partController() ->parts() ); + while ( KParts::Part * part = it.current() ) { + integratePart( part ); + ++it; + } + } + + if ( part->partController() ->activePart() ) + slotActivePartChanged( part->partController() ->activePart() ); + + connect( part->partController( ), SIGNAL( partAdded( KParts::Part* ) ), + this, SLOT( slotPartAdded( KParts::Part* ) ) ); + connect( part->partController( ), SIGNAL( activePartChanged( KParts::Part* ) ), + this, SLOT( slotActivePartChanged( KParts::Part* ) ) ); + + connect( part, SIGNAL( fileParsed( const QString& ) ), + this, SLOT( slotFileParsed( const QString& ) ) ); + connect( part, SIGNAL( codeModelUpdated( const QString& ) ), + this, SLOT( slotCodeModelUpdated( const QString& ) ) ); + + KAction * action = new KAction( i18n("Jump to declaration under cursor"), 0, CTRL + Key_Comma, + this, SLOT(slotJumpToDeclCursorContext()), part->actionCollection(), "jump_to_declaration_cursor_context" ); + action->plug( &m_DummyActionWidget ); + + action = new KAction( i18n("Jump to definition under cursor"), 0, CTRL + Key_Period, + this, SLOT(slotJumpToDefCursorContext()), part->actionCollection(), "jump_to_defintion_cursor_context" ); + action->plug( &m_DummyActionWidget ); +} + +CppCodeCompletion::~CppCodeCompletion( ) { + delete m_repository; + delete d; +} + +void CppCodeCompletion::addStatusText( QString text, int timeout ) { + m_statusTextList.append( QPair<int, QString>( timeout, text ) ); + if ( !m_showStatusTextTimer->isActive() ) { + slotStatusTextTimeout(); + } +} + +void CppCodeCompletion::clearStatusText() { + m_statusTextList.clear(); + m_showStatusTextTimer->stop(); +} + +void CppCodeCompletion::slotStatusTextTimeout() { + if ( m_statusTextList.isEmpty() || !m_pSupport ) + return ; +// m_pSupport->mainWindow() ->statusBar() ->message( m_statusTextList.front().second, m_statusTextList.front().first ); + m_showStatusTextTimer->start( m_statusTextList.front().first , true ); + m_statusTextList.pop_front(); +} + +void CppCodeCompletion::slotTimeout() { + if ( !m_activeCursor || !m_activeEditor || !m_activeCompletion ) + return ; + + uint nLine, nCol; + m_activeCursor->cursorPositionReal( &nLine, &nCol ); + + if ( nLine != m_ccLine || nCol != m_ccColumn ) + return ; + + QString textLine = m_activeEditor->textLine( nLine ); + QChar ch = textLine[ nCol ]; + if ( ch.isLetterOrNumber() || ch == '_' ) + return ; + + completeText(); +} + +void CppCodeCompletion::slotArgHintHidden() { + //kdDebug(9007) << "CppCodeCompletion::slotArgHintHidden()" << endl; + m_bArgHintShow = false; +} + +void CppCodeCompletion::slotCompletionBoxHidden() { + //kdDebug( 9007 ) << "CppCodeCompletion::slotCompletionBoxHidden()" << endl; + m_bCompletionBoxShow = false; +} + + +void CppCodeCompletion::integratePart( KParts::Part * part ) { + if ( !part || !part->widget() ) + return ; + + KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part ); + if ( doc ) { + kdDebug( 9007 ) << k_funcinfo << "integrate document: " << doc << endl; + + if ( m_pSupport ) { //The slot should connected even when automatic completion is disabled, so it can be enabled any time + kdDebug( 9007 ) << k_funcinfo << "enabling code completion" << endl; + connect( part, SIGNAL( textChanged() ), this, SLOT( slotTextChanged() ) ); + connect( part->widget(), SIGNAL( completionDone() ), this, + SLOT( slotCompletionBoxHidden() ) ); + connect( part->widget(), SIGNAL( completionAborted() ), this, + SLOT( slotCompletionBoxHidden() ) ); + connect( part->widget(), SIGNAL( argHintHidden() ), this, + SLOT( slotArgHintHidden() ) ); + } + } +} + +void CppCodeCompletion::slotPartAdded( KParts::Part * part ) { + integratePart( part ); +} + +void CppCodeCompletion::slotActivePartChanged( KParts::Part * part ) { + emptyCache(); + this->d->recoveryPoints.clear(); + if ( m_activeHintInterface && m_activeView ) { + disconnect( m_activeView , SIGNAL( needTextHint( int, int, QString & ) ), this, SLOT( slotTextHint( int, int, QString& ) ) ); + + m_activeHintInterface = 0; + } + if ( !part ) + return ; + + kdDebug( 9007 ) << k_funcinfo << endl; + + m_activeFileName = QString::null; + + KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part ); + if ( !doc ) + return ; + + m_activeFileName = doc->url().path(); + + // if the interface stuff fails we should disable codecompletion automatically + m_activeEditor = dynamic_cast<KTextEditor::EditInterface*>( part ); + if ( !m_activeEditor ) { + kdDebug( 9007 ) << "Editor doesn't support the EditDocumentIface" << endl; + return ; + } + + m_activeCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() ); + if ( !m_activeCursor ) { + kdDebug( 9007 ) << "The editor doesn't support the CursorDocumentIface!" << endl; + return ; + } + + m_activeCompletion = dynamic_cast<KTextEditor::CodeCompletionInterface*>( part->widget() ); + if ( !m_activeCompletion ) { + kdDebug( 9007 ) << "Editor doesn't support the CompletionIface" << endl; + return ; + } + + m_activeView = part ? dynamic_cast<KTextEditor::View*>( part->widget() ) : 0; + + if ( m_activeView ) + m_activeHintInterface = dynamic_cast<KTextEditor::TextHintInterface*>( m_activeView ); + + char* q = 0; + kdDebug() << q << endl; + + if ( m_activeHintInterface ) { +#ifndef DISABLETOOLTIPS + m_activeHintInterface->enableTextHints( 500 ); + connect( m_activeView, SIGNAL( needTextHint( int, int, QString & ) ), this, SLOT( slotTextHint( int, int, QString& ) ) ); +#endif + + } else { + kdDebug( 9007 ) << "editor has no text-hint-interface" << endl; + } + + kdDebug( 9007 ) << k_funcinfo << "-- end" << endl; +} + +void CppCodeCompletion::slotTextChanged() { + m_ccTimer->stop(); + + if ( !m_activeCursor ) + return ; + + unsigned int nLine, nCol; + m_activeCursor->cursorPositionReal( &nLine, &nCol ); + + QString strCurLine = m_activeEditor->textLine( nLine ); + QString ch = strCurLine.mid( nCol - 1, 1 ); + QString ch2 = strCurLine.mid( nCol - 2, 2 ); + + // Tell the completion box to _go_away_ when the completion char + // becomes empty or whitespace and the box is already showing. + // !!WARNING!! This is very hackish, but KTE doesn't offer a way + // to tell the completion box to _go_away_ + if ( ch.simplifyWhiteSpace().isEmpty() && + !strCurLine.simplifyWhiteSpace().contains( "virtual" ) && + m_bCompletionBoxShow ) { + QValueList<KTextEditor::CompletionEntry> entryList; + m_bCompletionBoxShow = true; + m_activeCompletion->showCompletionBox( entryList, 0 ); + } + + m_ccLine = 0; + m_ccColumn = 0; + + bool argsHint = m_pSupport->codeCompletionConfig() ->automaticArgumentsHint(); + bool codeComplete = m_pSupport->codeCompletionConfig() ->automaticCodeCompletion(); + bool headComplete = codeComplete; //m_pSupport->codeCompletionConfig() ->automaticHeaderCompletion(); + + // m_codeCompleteChRx completes on alpha chars and '.' + // m_codeCompleteCh2Rx completes on "->" and "::" + + if ( ( argsHint && ch == "(" ) || + ( codeComplete && strCurLine.simplifyWhiteSpace().contains( "virtual" ) ) || + ( codeComplete && ( m_codeCompleteChRx.search( ch ) != -1 || + m_codeCompleteCh2Rx.search( ch2 ) != -1 ) ) || + ( headComplete && ( ch == "\"" || ch == "<" ) && m_includeRx.search( strCurLine ) != -1 ) ) { + int time; + m_ccLine = nLine; + m_ccColumn = nCol; + if ( ch == "(" ) + time = m_pSupport->codeCompletionConfig() ->argumentsHintDelay(); + else + time = m_pSupport->codeCompletionConfig() ->codeCompletionDelay(); + m_ccTimer->start( time, true ); + } + + fitContextItem( nLine, nCol ); +} + +void CppCodeCompletion::fitContextItem( int nLine, int nCol ) { + if( !SimpleType::globalNamespace() ) { + kdDebug( 9007 ) << "no global namespace was set, clearing cache" << endl; + emptyCache(); + } + ///Find out whether the cache may be used on, or has to be cleared. + if ( m_cachedFromContext ) { + int sLine, sCol, eLine, eCol; + m_cachedFromContext->getStartPosition( &sLine, &sCol ); + m_cachedFromContext->getEndPosition( &eLine, &eCol ); + + if ( ( nLine < sLine || ( nLine == sLine && nCol < sCol ) ) || ( nLine > eLine || ( nLine == eLine && nCol >= eCol ) ) ) { + ///The stored item was left. First check whether the item was expanded. + FileDom file = m_pSupport->codeModel() ->fileByName( m_activeFileName ); + + if ( file ) { + CodeModelUtils::CodeModelHelper fileModel( m_pSupport->codeModel(), file ); + if ( m_cachedFromContext->isClass() ) { + ClassDom klass = fileModel.classAt( nLine, nCol ); + if ( klass ) { + ClassDom oldClass = dynamic_cast<ClassModel*>( m_cachedFromContext.data() ); + if ( oldClass && oldClass->name() == klass->name() && oldClass->scope() == klass->scope() ) { + m_cachedFromContext = klass.data(); + } else { + emptyCache(); + } + } else { + emptyCache(); + } + } else if ( m_cachedFromContext->isFunction() ) { + FunctionDom function = fileModel.functionAt( nLine, nCol ); + if ( function ) { + FunctionDom oldFunction = dynamic_cast<FunctionModel*>( m_cachedFromContext.data() ); + if ( oldFunction && oldFunction->name() == function->name() && function->scope() == oldFunction->scope() && oldFunction->argumentList().count() == function->argumentList().count() ) { + ArgumentList l1 = oldFunction->argumentList(); + ArgumentList l2 = function->argumentList(); + ArgumentList::iterator it = l1.begin(); + ArgumentList::iterator it2 = l2.begin(); + bool match = true; + while ( it != l1.end() ) { + if ( ( *it ) ->type() != ( *it2 ) ->type() ) { + match = false; + break; + } + ++it; + ++it2; + } + if ( match ) { + m_cachedFromContext = function.data(); + } else { + emptyCache(); + } + + } else { + emptyCache(); + } + } else { + emptyCache(); + } + } else { + emptyCache(); + } + } else { + emptyCache(); + } + } + } +} + +enum { T_ACCESS, T_PAREN, T_BRACKET, T_IDE, T_UNKNOWN, T_TEMP }; + +QString CppCodeCompletion::replaceCppComments( const QString& contents ) { + QString text = contents; + + int pos = 0; + while ( ( pos = m_cppCodeCommentsRx.search( text, pos ) ) != -1 ) { + if ( m_cppCodeCommentsRx.cap( 1 ).startsWith( "//" ) ) { + QString before = m_cppCodeCommentsRx.cap( 1 ); + QString after; + after.fill( ' ', before.length() - 5 ); + after.prepend( "/*" ); + after.append( "*/" ); + text.replace( pos, before.length() - 1, after ); + pos += after.length(); + } else { + pos += m_cppCodeCommentsRx.matchedLength(); + } + } + return text; +} + +int CppCodeCompletion::expressionAt( const QString& contents, int index ) { + kdDebug( 9007 ) << k_funcinfo << endl; + + /* C++ style comments present issues with finding the expr so I'm + matching for them and replacing them with empty C style comments + of the same length for purposes of finding the expr. */ + + QString text = clearComments( contents ); + + int last = T_UNKNOWN; + int start = index; + --index; + + while ( index > 0 ) { + while ( index > 0 && text[ index ].isSpace() ) { + --index; + } + + QChar ch = text[ index ]; + QString ch2 = text.mid( index - 1, 2 ); + if ( ( last != T_IDE ) && ( ch.isLetterOrNumber() || ch == '_' ) ) { + while ( index > 0 && ( text[ index ].isLetterOrNumber() || text[ index ] == '_' ) ) { + --index; + } + last = T_IDE; + } else if ( last != T_IDE && ch == ')' ) { + int count = 0; + while ( index > 0 ) { + QChar ch = text[ index ]; + if ( ch == '(' ) { + ++count; + } else if ( ch == ')' ) { + --count; + } else if ( count == 0 ) { + //index; + last = T_PAREN; + break; + } + --index; + } + } else if ( last != T_IDE && ch == '>' && ch2 != "->" ) { + int count = 0; + while ( index > 0 ) { + QChar ch = text[ index ]; + if ( ch == '<' ) { + ++count; + } else if ( ch == '>' ) { + --count; + } else if ( count == 0 ) { + //--index; + last = T_TEMP; + break; + } + --index; + } + } else if ( ch == ']' ) { + int count = 0; + while ( index > 0 ) { + QChar ch = text[ index ]; + if ( ch == '[' ) { + ++count; + } else if ( ch == ']' ) { + --count; + } else if ( count == 0 ) { + //--index; + last = T_BRACKET; + break; + } + --index; + } + } else if ( ch == '.' ) { + --index; + last = T_ACCESS; + } else if ( ch2 == "::" ) { + index -= 2; + last = T_ACCESS; + } else if ( ch2 == "->" ) { + index -= 2; + last = T_ACCESS; + } else { + if ( start > index ) { + ++index; + } + last = T_UNKNOWN; + break; + } + } + + ///If we're at the first item, the above algorithm cannot be used safely, + ///so just determine whether the sign is valid for the beginning of an expression, if it isn't reject it. + if ( index == 0 && start > index && !( text[ index ].isLetterOrNumber() || text[ index ] == '_' || text[ index ] == ':' ) ) { + ++index; + } + + return index; +} + +QStringList CppCodeCompletion::splitExpression( const QString& text ) { +#define ADD_CURRENT()\ + if( current.length() ) { l << current; /*kdDebug(9007) << "add word " << current << endl;*/ current = ""; } + + QStringList l; + uint index = 0; + QString current; + while ( index < text.length() ) { + QChar ch = text[ index ]; + QString ch2 = text.mid( index, 2 ); + + if ( ch == '.' ) { + current += ch; + ADD_CURRENT(); + ++index; + } else if ( ch == '(' ) { + int count = 0; + while ( index < text.length() ) { + QChar ch = text[ index ]; + if ( ch == '(' ) { + ++count; + } else if ( ch == ')' ) { + --count; + } else if ( count == 0 ) { + break; + } + current += ch; + ++index; + } + } else if ( ch == '[' ) { + int count = 0; + while ( index < text.length() ) { + QChar ch = text[ index ]; + if ( ch == '[' ) { + ++count; + } else if ( ch == ']' ) { + --count; + } else if ( count == 0 ) { + break; + } + current += ch; + ++index; + } + } else if ( ch2 == "->" ) { + current += ch2; + ADD_CURRENT(); + index += 2; + } /*else if ( ch2 == "::" ) + { + current += ch2; + ADD_CURRENT(); + index += 2; + }*/ + else { + current += text[ index ]; + ++index; + } + } + ADD_CURRENT(); + return l; +} + +///Before calling this, a SimpleTypeConfiguration-object should be created, so that the ressources will be freed when that object is destroyed +EvaluationResult CppCodeCompletion::evaluateExpressionAt( int line, int column , SimpleTypeConfiguration& conf, bool ifUnknownSetType ) { + kdDebug( 9007 ) << "CppCodeCompletion::evaluateExpressionAt( " << line << ", " << column << " )" << endl; + + if ( !m_pSupport || !m_activeEditor ) + return EvaluationResult(); + if ( line < 0 || line >= ( int ) m_activeEditor->numLines() ) + return EvaluationResult(); + if ( column < 0 || column >= m_activeEditor->lineLength( line ) ) + return EvaluationResult(); + + { + QString curLine = m_activeEditor->textLine( line ); + + ///move column to the last letter of the pointed word + while ( column + 1 < ( int ) curLine.length() && isValidIdentifierSign( curLine[ column ] ) && isValidIdentifierSign( curLine[ column + 1 ] ) ) + column++; + + //if( column > 0 ) column--; + + if ( column >= ( int ) curLine.length() || curLine[ column ].isSpace() ) + return EvaluationResult(); + + QString expr = curLine.left( column + 1 ); + kdDebug( 9007 ) << "evaluating line \"" << expr.stripWhiteSpace() << "\"" << endl; + + if ( curLine[ column ] == '-' || curLine[ column ] == ';' ) + --column; + + EvaluationResult type = evaluateExpressionType( line, column + 1, conf, ifUnknownSetType ? addFlag( DefaultEvaluationOptions, DefaultAsTypeExpression ) : DefaultEvaluationOptions ); + + kdDebug( 9007 ) << "type: " << type->fullNameChain() << endl; + + return type; + } +} + +void CppCodeCompletion::popupAction( int number ) { + PopupActions::iterator it = m_popupActions.find( number ); + if ( it != m_popupActions.end() ) { + QString fileName = ( *it ).file == "current_file" ? m_activeFileName : ( *it ).file.operator QString(); + if( (*it).startLine == -1 ) { + //startLine -1 indicates that the file should be added to the include-files + m_activeEditor->insertLine( 0, QString("#include \"%1\" /* defines %2 */").arg( fileName ).arg( (*it).name ) ); + } else { + m_pSupport->partController() ->editDocument( fileName, ( *it ).startLine ); + } + } else { + kdDebug( 9007 ) << "error" << endl; + } +} + +void CppCodeCompletion::popupDefinitionAction( int number ) { + PopupActions::iterator it = m_popupDefinitionActions.find( number ); + if ( it != m_popupDefinitionActions.end() ) { + QString fileName = ( *it ).file == "current_file" ? m_activeFileName : ( *it ).file.operator QString(); + if ( !m_pSupport->switchHeaderImpl( fileName, ( *it ).startLine, ( *it ).startCol ) ) + m_pSupport->partController() ->editDocument( fileName, ( *it ).startLine ); + } else { + kdDebug( 9007 ) << "error" << endl; + } +} + +void CppCodeCompletion::selectItem( ItemDom item ) { + Extensions::KDevCodeBrowserFrontend * f = m_pSupport->extension< Extensions::KDevCodeBrowserFrontend > ( "KDevelop/CodeBrowserFrontend" ); + + if ( f != 0 ) { + ItemDom itemDom( &( *item ) ); + f->jumpedToItem( itemDom ); + } else { + kdDebug() << "could not find the proper extension" << endl; + } +} + +void CppCodeCompletion::popupClassViewAction( int number ) { + PopupClassViewActions::iterator it = m_popupClassViewActions.find( number ); + if ( it != m_popupClassViewActions.end() ) { + if ( ( *it ) ) + selectItem( *it ); + } else { + kdDebug( 9007 ) << "error" << endl; + } +} + +void CppCodeCompletion::contextEvaluationMenus ( QPopupMenu *popup, const Context *context, int line, int column ) { + clearStatusText(); + Q_UNUSED(context); + if ( !m_pSupport->codeCompletionConfig() ->showEvaluationContextMenu() ) + return ; + + kdDebug( 9007 ) << "CppCodeCompletion::contextEvaluationMenu()" << endl; + + PopupTracker::print(); + + m_popupActions.clear(); + m_popupDefinitionActions.clear(); + m_popupClassViewActions.clear(); + + if ( !m_pSupport || !m_activeEditor ) + return ; + + struct SetDbgState { + DBGStreamType& st; + bool oldState; + SetDbgState( DBGStreamType& targ, bool state ) : st( targ ) { + oldState = targ.state(); + targ.setState( state ); + } + ~SetDbgState() { + st.setState( oldState ); + } + }; + + int cpos = 0; + + SetDbgState stt( dbgState, disableVerboseForContextMenu ); + + SimpleTypeConfiguration conf( m_activeFileName ); + + EvaluationResult type = evaluateExpressionAt( line, column, conf ); + + ///Test if it is a macro + if( type.isMacro ) { + QPopupMenu * m = PopupTracker::createPopup( popup ); + int gid; + if ( contextMenuEntriesAtTop ) + gid = popup->insertItem( i18n( "Navigate by Macro \"%1\"" ).arg( cleanForMenu( type.macro.name() ) ), m, 5, cpos++ ); + else + gid = popup->insertItem( i18n( "Navigate by Macro \"%1\"" ).arg( cleanForMenu( type.macro.name() ) ), m ); + + int id = m->insertItem( i18n( "Jump to %1" ).arg( cleanForMenu( type.macro.name() ) ), this, SLOT( popupAction( int ) ) ); + QPopupMenu * b = PopupTracker::createPopup( m ); + m->insertItem( i18n( "Body" ), b ); + + DeclarationInfo i; + i.file = type.macro.fileName(); + i.startCol = type.macro.column(); + i.startLine = type.macro.line(); + i.endCol = type.macro.column(); + i.endLine = type.macro.line(); + m_popupActions.insert( id, i ); + + QStringList ls = prepareTextForMenu( type.macro.body(), 20, 100 ); + for ( QStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { + b->insertItem( *it, 0, SLOT( popupClassViewAction( int ) ) ); + } + } + + ///Test if it is an include-directive + QString includeFileName, includeFilePath; + bool simpleAlgorithm = false; + bool isIncludeDirective = getIncludeInfo( line, includeFileName, includeFilePath, simpleAlgorithm ); + if( isIncludeDirective ) { + ///Add menu entry + if( !includeFilePath.isEmpty() ) { + int gid; + QPopupMenu * m = PopupTracker::createPopup( popup ); + if ( contextMenuEntriesAtTop ) + gid = popup->insertItem( i18n( "Goto Include File: %1" ).arg( cleanForMenu( includeFileName ) ), m, 5, cpos++ ); + else + gid = popup->insertItem( i18n( "Goto Include File: %1" ).arg( cleanForMenu( includeFileName ) ), m ); + + int id = m->insertItem( i18n( "Jump to %1" ).arg( cleanForMenu( includeFilePath ) ), this, SLOT( popupAction( int ) ) ); + + DeclarationInfo i; + i.file = includeFilePath; + i.startCol = 0; + i.startLine = 0; + i.endCol = 0; + i.endLine = 0; + m_popupActions.insert( id, i ); + + if( simpleAlgorithm && cppSupport()->codeCompletionConfig()->resolveIncludePaths() ) { + //Add a notification that the correct algorithm failed in finding the include-file correctly + m->insertItem( i18n( "This include-file could not be located regularly, and was selected from the project file list." ) ); + } + } else { + ///Could not find include-file + if ( contextMenuEntriesAtTop ) + popup->insertItem( i18n( "Not Found: \"%1\"" ).arg( includeFileName ), 5, cpos++ ); + else + popup->insertItem( i18n( "Not Found: \"%1\"" ).arg( includeFileName ) ); + } + } + + ///Break if we cannot show additional information + if ( isIncludeDirective || (!type->resolved() && !type.sourceVariable && ( !type.resultType.trace() || type.resultType.trace() ->trace().isEmpty() ) && !BuiltinTypes::isBuiltin( type.resultType ) ) ) + return ; + + QString name = type->fullNameChain(); + if ( type.sourceVariable ) + name += " " + type.sourceVariable.name; + if ( type.resultType->resolved() && type.resultType->resolved() ->asFunction() ) + name = buildSignature( type.resultType->resolved() ); + + ///Fill the jump-menu + { + PopupFillerHelpStruct h( this ); + PopupFiller<PopupFillerHelpStruct> filler( h, "" ); + + QPopupMenu * m = PopupTracker::createPopup( popup ); + int gid; + if ( contextMenuEntriesAtTop ) + gid = popup->insertItem( i18n( "Navigate by \"%1\"" ).arg( cleanForMenu( name ) ), m, 5, cpos++ ); + else + gid = popup->insertItem( i18n( "Navigate by \"%1\"" ).arg( cleanForMenu( name ) ), m ); + + popup->setWhatsThis( gid, i18n( "<b>Navigation</b><p>Provides a menu to navigate to positions of items that are involved in this expression" ) ); + + /*if( type.sourceVariable && type.sourceVariable.name != "this" ) { + int id = m->insertItem( i18n("jump to variable-declaration \"%1\"").arg( type.sourceVariable.name ) , this, SLOT( popupAction( int ) ) ); + + m_popupActions.insert( id, type.sourceVariable ); + }*/ + + filler.fill( m, type, "", type.sourceVariable ); + } + if ( type->resolved() ) { + ///Now fill the class-view-browsing-stuff + { + QPopupMenu * m = PopupTracker::createPopup( popup ); + int gid; + if ( contextMenuEntriesAtTop ) + gid = popup->insertItem( i18n( "Navigate Class-View by \"%1\"" ).arg( cleanForMenu( name ) ), m, 6, cpos++ ); + else + gid = popup->insertItem( i18n( "Navigate Class-View by \"%1\"" ).arg( cleanForMenu( name ) ), m ); + + popup->setWhatsThis( gid, i18n( "<b>Navigation</b><p>Provides a menu to show involved items in the class-view " ) ); + + PopupClassViewFillerHelpStruct h( this ); + PopupFiller<PopupClassViewFillerHelpStruct> filler( h, "" ); + + filler.fill( m, type ); + } + } + + if ( contextMenuEntriesAtTop ) + popup->insertSeparator( cpos ); +} + +void CppCodeCompletion::slotTextHint( int line, int column, QString &text ) { + if ( ! m_pSupport->codeCompletionConfig() ->statusBarTypeEvaluation() ) + return ; + + kdDebug( 9007 ) << "CppCodeCompletion::slotTextHint()" << endl; + + clearStatusText(); + + if ( m_lastHintTime.msecsTo( QTime::currentTime() ) < 300 ) { + kdDebug( 9007 ) << "slotNeedTextHint called too often" << endl; + return ; + } + + m_lastHintTime = QTime::currentTime(); + + clearStatusText(); + text = ""; + if ( !m_pSupport || !m_activeEditor ) + return ; + + SimpleTypeConfiguration conf( m_activeFileName ); + + EvaluationResult type = evaluateExpressionAt( line, column, conf ); + + if ( type.expr.expr().stripWhiteSpace().isEmpty() ) + return ; ///Expression could not be found + + if ( type.sourceVariable ) { + text += type.sourceVariable.toText() + "\n"; + } + + if ( type->resolved() ) { + /*SimpleTypeFunctionInterface* f = type->resolved()->asFunction(); + if( f ) { + text += "function: \"" + buildSignature( type->resolved() ) + "\""; + } else { + QValueList<TypeDesc> trace = type.resultType->trace(); + if( !trace.isEmpty() ) { + for( QValueList<TypeDesc>::iterator it = trace.begin(); it != trace.end(); ++it ) { + text += (*it).fullNameChain() + " --> "; + } + text += "\n"; + } + text += "type: \"" + type.resultType->fullTypeResolved() + "\""; + } + if( type.resultType->parent()) text += "\nnested in: \"" + type.resultType->parent()->fullTypeResolvedWithScope() + "\""; + DeclarationInfo i = type.resultType->getDeclarationInfo(); + if( i ) text += "\n" + i.locationToText(); + + if( !type.resultType->comment().isEmpty() ) text += "\n\n" + type.resultType->comment() + "";*/ + + } else {} + + kdDebug( 9007 ) << "showing: \n" << text << endl; + const int timeout = 2000; + + if ( type->resolved() ) { + addStatusText( i18n( "Type of \"%1\" is \"%2\"" ).arg( type.expr.expr() ).arg( type->fullNameChain() ), timeout ); + if ( type.sourceVariable && !type.sourceVariable.comment.isEmpty() ) { + addStatusText( i18n( "Comment on variable \"%1\": \"%2\"" ).arg( type.sourceVariable.name ).arg( type.sourceVariable.comment ) , 10000 ); + } + if ( !type->resolved() ->comment().isEmpty() ) { + addStatusText( i18n( "Comment on \"%1\": \"%2\"" ).arg( type->name() ).arg( type->resolved() ->comment() ) , 10000 ); + } + if ( type->resolved() ->comment().isEmpty() ) { + addStatusText( i18n( "\"%1\" has no comment" ).arg( type->name() ) , timeout ); + } + } else { + if ( type ) { + if( !BuiltinTypes::isBuiltin( type.resultType ) ) { + addStatusText( i18n( "Type of \"%1\" is unresolved, name: \"%2\"" ).arg( type.expr.expr() ).arg( type->fullNameChain() ), 2 * timeout ); + } else { + addStatusText( i18n( "\"%1\" is of builtin type \"%2\", a %3" ).arg( type.expr.expr() ).arg( type->fullNameChain() ).arg(BuiltinTypes::comment( type.resultType )), 2 * timeout ); + } + + + } else { + addStatusText( i18n( "Type of \"%1\" could not be evaluated: tried to evaluate expression as \"%2\"" ).arg( type.expr.expr() ).arg( type.expr.typeAsString() ), 2 * timeout ); + } + } + + text = ""; ///Don't really use tooltips since those are not implemented in katepart, and don't work right in the qt-designer based part +} + +///not good.. +bool CppCodeCompletion::isTypeExpression( const QString& expr ) { + TypeDesc d( expr ); + if ( !d.isValidType() ) + return false; + + QString ex = d.fullNameChain(); + QStringList lex = QStringList::split( " ", ex ); + QStringList lexpr = QStringList::split( " ", expr ); + return lex.join( " " ) == lexpr.join( " " ); +} + +bool CppCodeCompletion::mayBeTypeTail( int line, int column, QString& append, bool inFunction ) { + QString tail = clearComments( m_activeEditor->text( line, column + 1, line + 10 > ( int ) m_activeEditor->numLines() ? ( int ) m_activeEditor->numLines() : line + 10, 0 ) ); + tail.replace( "\n", " " ); + SafetyCounter s ( 100 ); + bool hadSpace = false; + while ( !tail.isEmpty() && s ) { + if ( tail[ 0 ] == ';' ) { + return false; + } else if ( ( !inFunction && tail[ 0 ] == ',' ) || tail[ 0 ] == '&' || tail[ 0 ] == '*' || tail[ 0 ] == '{' || tail[ 0 ] == ':' ) { + return true; + } else if ( isTypeOpenParen( tail[ 0 ] ) ) { + ///TODO: use findClose to make the whole expression include template-params + int to = findClose( tail, 0 ); + if ( to != -1 ) { + append = tail.left( to + 1 ); + tail = tail.mid( to + 1 ); + } else { + return false; + } + } else if ( isTypeCloseParen( tail[ 0 ] ) ) { + return true; + } else if ( tail[ 0 ].isSpace() ) { + tail = tail.mid( 1 ); + hadSpace = true; + } else if ( tail[ 0 ].isLetter() ) { + return hadSpace; + } else { + break; + } + } + + return false; +} + +bool CppCodeCompletion::canBeTypePrefix( const QString& prefix, bool inFunction ) { + + for ( int p = prefix.length() - 1 ; p >= 0; --p ) { + if ( prefix[ p ].isSpace() ) { + continue; + } + + if ( prefix[ p ] == ';' || prefix[ p ] == '<' || prefix[ p ] == ':' || ( !inFunction && ( prefix[ p ] == '(' || prefix[ p ] == ',' ) ) || prefix[ p ] == '}' || prefix[ p ] == '{' ) { + return true; + } + + ///@todo: make this a simple regex + if ( prefix[ p ].isLetterOrNumber() && ( tokenAt( prefix, "class", p ) || tokenAt( prefix, "struct", p ) || tokenAt( prefix, "const", p ) || tokenAt( prefix, "typedef", p ) || tokenAt( prefix, "public", p ) || tokenAt( prefix, "protected", p ) || tokenAt( prefix, "private", p ) || tokenAt( prefix, "virtual", p ) || tokenAt( prefix, "static", p ) || tokenAt( prefix, "virtual", p ) ) ) + return true; + else { + return false; + } + } + + return true; +} + +///This function is just a litte hack und should be remade, it doesn't work for all cases +ExpressionInfo CppCodeCompletion::findExpressionAt( int line, int column, int startLine, int startCol, bool inFunction ) { + ExpressionInfo ret; + + QString contents = clearComments( getText( startLine, startCol, line, column ) ); + + + int start_expr = expressionAt( contents, contents.length() ); + + if ( start_expr != int( contents.length() ) ) { + QString str = contents.mid( start_expr, contents.length() - start_expr ).stripWhiteSpace(); + if ( str.startsWith( "new " ) ) { + str = str.mid( 4 ).stripWhiteSpace(); + } + ret.setExpr( str ); + if ( !ret.expr().isEmpty() ) + ret.t = ExpressionInfo::NormalExpression; + } + + if ( ret ) { + ///Check whether it may be a type-expression + bool mayBeType = true; + QString append; + if ( !mayBeTypeTail( line, column - 1, append, inFunction ) ) + mayBeType = false; + if ( mayBeType ) { + if ( !canBeTypePrefix( contents.left( start_expr ), inFunction ) ) + mayBeType = false; + } + + //make this a regexp + QString e = ret.expr(); + if ( e.contains( "." ) || e.contains( "->" ) || e.contains( "(" ) || e.contains( ")" ) || e.contains( "=" ) || e.contains( "-" ) ) + mayBeType = false; + + if ( mayBeType ) { + ret.setExpr( ret.expr() + append ); + ret.t = ExpressionInfo::TypeExpression; + } + } + + return ret; +} + +void macrosToDriver( Driver& d, FileDom file ) { + return; //Deactivate this for now, because macros can cause inconsistency of line/column-numbers between processed text and the not-processed text of the buffer + ParseResultPointer p; + if( file ) + p = file->parseResult(); + ParsedFile* pf = dynamic_cast<ParsedFile*>( p.data() ); + if( pf ) { + d.insertMacros( pf->usedMacros() ); ///Add macros + } +} + +SimpleContext* CppCodeCompletion::computeFunctionContext( FunctionDom f, int line, int col, SimpleTypeConfiguration& conf ) { + Q_UNUSED(conf); + if ( !f ) + return 0; + int modelStartLine, modelStartColumn; + int modelEndLine, modelEndColumn; + + f->getStartPosition( &modelStartLine, &modelStartColumn ); + f->getEndPosition( &modelEndLine, &modelEndColumn ); + + QString textLine = m_activeEditor->textLine( modelStartLine ); + kdDebug( 9007 ) << "startLine = " << textLine << endl; + + QString contents = getText( modelStartLine, modelStartColumn, line, col ); + + Driver d; + Lexer lexer( &d ); + macrosToDriver( d, f->file() ); + + lexer.setSource( contents ); + Parser parser( &d, &lexer ); + + DeclarationAST::Node recoveredDecl; + RecoveryPoint* recoveryPoint = this->d->findRecoveryPoint( line, col );///@todo recovery-points are not needed anymore + + parser.parseDeclaration( recoveredDecl ); + if ( recoveredDecl.get() ) { + bool isFunDef = recoveredDecl->nodeType() == NodeType_FunctionDefinition; + kdDebug( 9007 ) << "is function definition= " << isFunDef << endl; + + int startLine, startColumn; + int endLine, endColumn; + recoveredDecl->getStartPosition( &startLine, &startColumn ); + recoveredDecl->getEndPosition( &endLine, &endColumn ); + /*if( startLine != modelStartLine || endLine != modelEndLine || startColumn != modelStartColumn || endColumn != modelEndColumn ) { + kdDebug( 9007 ) << "code-model and real file are out of sync \nfunction-bounds in code-model: " << endl; + kdDebug( 9007 ) << "(l " << modelStartLine << ", c " << modelStartColumn << ") - (l " << modelEndLine << ", c " << modelEndColumn << ") " << "parsed function-bounds: " << endl; + kdDebug( 9007 ) << "(l " << startLine << ", c " << startColumn << ") - (l " << endLine << ", c " << endColumn << ") " << endl; + }*/ + + if ( isFunDef ) { + FunctionDefinitionAST * def = static_cast<FunctionDefinitionAST*>( recoveredDecl.get() ); + + SimpleContext* ctx = computeContext( def, endLine, endColumn, modelStartLine, modelStartColumn ); + if ( !ctx ) + return 0; + + QStringList scope = f->scope(); + + + if ( !scope.isEmpty() ) { + SimpleType parentType; + /* if( !m_cachedFromContext ) { + TypePointer t = SimpleType(QStringList())->locateDecType( scope.join("") ).desc().resolved();; + if( t ) + parentType = SimpleType( t.data() ); + else + parentType = SimpleType( scope ); + } else {*/ + parentType = SimpleType( scope, getIncludeFiles() ); + //} + parentType->descForEdit().setTotalPointerDepth( 1 ); + ctx->setContainer( parentType ); + } + + SimpleType global = ctx->global(); + + if( dynamic_cast<SimpleTypeNamespace*>( &(*global) ) ) { + SimpleTypeNamespace* globalNs = static_cast <SimpleTypeNamespace*>( &(*global) ); + QValueList<QPair<QString, QString> > localImports = ctx->imports(); + for( QValueList<QPair<QString, QString> >::const_iterator it = localImports.begin(); it != localImports.end(); ++it ) + globalNs->addAliasMap( (*it).first, (*it).second ); + } + + /* //Should not be necessary any more + if( !getParsedFile( f->file().data() ) || getParsedFile( f->file().data() )->includeFiles().size() <= 1 ) { + if ( !m_cachedFromContext ) { + conf.setGlobalNamespace( &( *global ) ); + if ( recoveryPoint ) { + recoveryPoint->registerImports( global, m_pSupport->codeCompletionConfig() ->namespaceAliases() ); + } else { + kdDebug( 9007 ) << "no recovery-point, cannot use imports" << endl; + } + } + }*/ + + ///Insert the "this"-type(container) and correctly resolve it using imported namespaces + if ( ctx->container() ) { + if ( !m_cachedFromContext ) { + TypeDesc td = ctx->container() ->desc(); + td.setIncludeFiles( getIncludeFiles() ); + td.makePrivate(); + + td.resetResolved( ); + TypePointer tt = ctx->container() ->locateDecType( td, SimpleTypeImpl::LocateBase ) ->resolved(); + if ( tt ) { + ctx->setContainer( SimpleType( tt ) ); + } else { + kdDebug( 9007 ) << "could not resolve local this-type \"" << td.fullNameChain() << "\"" << endl; + } + } + + SimpleType this_type = ctx->container(); + + this_type->descForEdit().setTotalPointerDepth( 1 ); + + SimpleVariable var; + var.type = this_type->desc(); + var.name = "this"; + var.comment = this_type->comment(); + ctx->add + ( var ); + ctx->setContainer( this_type ); + } + + return ctx; + } else { + kdDebug( 9007 ) << "computeFunctionContext: context is no function-definition" << endl; + } + } else { + kdDebug( 9007 ) << "computeFunctionContext: could not find a valid declaration to recover" << endl; + } + return 0; +} + +bool CppCodeCompletion::functionContains( FunctionDom f , int line, int col ) { + if ( !f ) + return false; + int sl, sc, el, ec; + f->getStartPosition( &sl, &sc ); + f->getEndPosition( &el, &ec ); + QString t = clearComments( getText( sl, sc, el, ec ) ); + if ( t.isEmpty() ) + return false; + + //int i = t.find( '{' ); + int i = t.find( '(' ); //This now includes the argument-list + if ( i == -1 ) + return false; + int lineCols = 0; + for ( int a = 0; a < i; a++ ) { + if ( t[ a ] == '\n' ) { + sl++; + lineCols = 0; + } else { + lineCols++; + } + } + + sc += lineCols; + + return ( line > sl || ( line == sl && col >= sc ) ) && ( line < el || ( line == el && col < ec ) ); +} + +void CppCodeCompletion::getFunctionBody( FunctionDom f , int& line, int& col ) { + if ( !f ) + return; + int sl, sc, el, ec; + f->getStartPosition( &sl, &sc ); + f->getEndPosition( &el, &ec ); + QString t = clearComments( getText( sl, sc, el, ec ) ); + if ( t.isEmpty() ) + return; + + int i = t.find( '{' ); + if ( i == -1 ) + return; + i++; + if( (uint)i >= t.length() ) + return; + int lineCols = 0; + for ( int a = 0; a < i; a++ ) { + if ( t[ a ] == '\n' ) { + sl++; + lineCols = 0; + } else { + lineCols++; + } + } + + sc += lineCols; + + line = sl; + col = sc; +} + +void CppCodeCompletion::emptyCache() { + m_cachedFromContext = 0; + SimpleTypeConfiguration c; ///Will automatically destroy the type-store when the function is closed + kdDebug( 9007 ) << "completion-cache emptied" << endl; +} + +void CppCodeCompletion::needRecoveryPoints() { + + if ( this->d->recoveryPoints.isEmpty() ) { + kdDebug( 9007 ) << "missing recovery-points for file " << m_activeFileName << " they have to be computed now" << endl; + m_pSupport->backgroundParser() ->lock () + ; + + std::vector<CppCodeCompletion> vec; + + TranslationUnitAST * ast = *m_pSupport->backgroundParser() ->translationUnit( m_activeFileName ); + m_pSupport->backgroundParser() ->unlock(); + if ( !ast ) { + kdDebug( 9007 ) << "background-parser is missing the translation-unit. The file needs to be reparsed." << endl; + m_pSupport->parseFileAndDependencies( m_activeFileName, true ); +// m_pSupport->mainWindow() ->statusBar() ->message( i18n( "Background-parser is missing the necessary translation-unit. It will be computed, but this completion will fail." ).arg( m_activeFileName ), 2000 ); + return; + } else { + computeRecoveryPointsLocked(); + } + if ( this->d->recoveryPoints.isEmpty() ) { + kdDebug( 9007 ) << "Failed to compute recovery-points for " << m_activeFileName << endl; +// m_pSupport->mainWindow() ->statusBar() ->message( i18n( "Failed to compute recovery-points for %1" ).arg( m_activeFileName ), 1000 ); + } else { + kdDebug( 9007 ) << "successfully computed recovery-points for " << m_activeFileName << endl; + } + } +} + +EvaluationResult CppCodeCompletion::evaluateExpressionType( int line, int column, SimpleTypeConfiguration& conf, EvaluateExpressionOptions opt ) { + EvaluationResult ret; + safetyCounter.init(); + + FileDom file = m_pSupport->codeModel() ->fileByName( m_activeFileName ); + + if ( !file ) { +// m_pSupport->mainWindow() ->statusBar() ->message( i18n( "File %1 does not exist in the code-model" ).arg( m_activeFileName ), 1000 ); + kdDebug( 9007 ) << "Error: file " << m_activeFileName << " could not be located in the code-model, code-completion stopped\n"; + return SimpleType(); + } + + needRecoveryPoints(); + + CodeModelUtils::CodeModelHelper fileModel( m_pSupport->codeModel(), file ); + ItemDom contextItem; + + int nLine = line, nCol = column; + + // emptyCache(); + fitContextItem( line, column ); + + QString strCurLine = m_activeEditor->textLine( nLine ); + + QString ch = strCurLine.mid( nCol - 1, 1 ); + QString ch2 = strCurLine.mid( nCol - 2, 2 ); + + while ( ch[ 0 ].isSpace() && nCol >= 3 ) { + nCol -= 1; + ch = strCurLine.mid( nCol - 1, 1 ); + ch2 = strCurLine.mid( nCol - 2, 2 ); + } + + if ( ch2 == "->" || ch == "." || ch == "(" ) { + int pos = ch2 == "->" ? nCol - 3 : nCol - 2; + QChar c = strCurLine[ pos ]; + while ( pos > 0 && c.isSpace() ) + c = strCurLine[ --pos ]; + + if ( !( c.isLetterOrNumber() || c == '_' || c == ')' || c == ']' || c == '>' ) ) { + conf.invalidate(); + return SimpleType(); + } + } + bool showArguments = false; + + if ( ch == "(" ) { + --nCol; + while ( nCol > 0 && strCurLine[ nCol ].isSpace() ) + --nCol; + showArguments = true; + } + + QString word; + + { + ExpressionInfo exp_ = findExpressionAt( line, column , line, 0 ); + + if( file->parseResult() ) { + ParsedFilePointer p = dynamic_cast<ParsedFile*>( file->parseResult().data()); + if( p ) { + if( p->usedMacros().hasMacro( exp_.expr() ) ) { + //It is a macro, return it + ret.expr = exp_.expr(); + ret.isMacro = true; + ret.macro = p->usedMacros().macro( exp_.expr() ); + return ret; + } + } + } + } + + if ( !m_cachedFromContext ) + conf.setGlobalNamespace( createGlobalNamespace() ); + + ItemLocker<BackgroundParser> block( *m_pSupport->backgroundParser() ); + + FunctionDom currentFunction = fileModel.functionAt( line, column ); + + bool functionFailed = true; + + if ( opt & SearchInFunctions ) { + //currentFunction = fileModel.functionAt( line, column ); + + if ( currentFunction && functionContains( currentFunction, line, column ) ) { + ///Evaluate the context of the function-body if we're in the argument-list + int realLine = line, realColumn = column; + getFunctionBody( currentFunction, realLine, realColumn ); + if( realLine < line || ( realLine == line && realColumn < column ) ) { + realLine = line; + realColumn = column; + } + + SimpleContext * ctx = computeFunctionContext( currentFunction, realLine, realColumn, conf ); + contextItem = currentFunction.data(); + + if ( ctx ) { + opt = remFlag( opt, SearchInClasses ); + int startLine, endLine; + currentFunction->getStartPosition( &startLine, &endLine ); + ExpressionInfo exp = findExpressionAt( line, column , startLine, endLine, true ); + if ( ( opt & DefaultAsTypeExpression ) && ( !exp.canBeNormalExpression() && !exp.canBeTypeExpression() ) && !exp.expr().isEmpty() ) + exp.t = ExpressionInfo::TypeExpression; + + if ( exp.canBeTypeExpression() ) { + { + if ( ! ( opt & IncludeTypeExpression ) ) { + kdDebug( 9007 ) << "recognized a type-expression, but another expression-type is desired" << endl; + } else { + TypeDesc d( exp.expr() ); + d.setIncludeFiles( getIncludeFiles() ); + ret.resultType = ctx->container() ->locateDecType( d ); + ret.expr = exp; + } + } + } + if ( /*exp.canBeNormalExpression() &&*/ !ret.resultType->resolved() ) { ///It is not cleary possible to recognize the kind of an expression from the syntax as long as it's not written completely + { + if ( ! ( opt & IncludeStandardExpressions ) ) { + kdDebug( 9007 ) << "recognized a standard-expression, but another expression-type is desired" << endl; + } else { + ///Remove the not completely typed last word while normal completion + if ( !showArguments && ( opt & CompletionOption ) ) { + QString e = exp.expr(); + int idx = e.length() - 1; + while ( e[ idx ].isLetterOrNumber() || e[ idx ] == '_' ) + --idx; + + if ( idx != int( e.length() ) - 1 ) { + ++idx; + word = e.mid( idx ).stripWhiteSpace(); + exp.setExpr( e.left( idx ).stripWhiteSpace() ); + } + } + + functionFailed = false; + ret = evaluateExpression( exp, ctx ); + } + } + } + } else { + kdDebug( 9007 ) << "could not compute context" << endl; + } + if ( ctx ) + delete ctx; + } else { + kdDebug( 9007 ) << "could not find context-function in code-model" << endl; + } + } + + if ( ( opt & SearchInClasses ) && !ret->resolved() && functionFailed ) { + ClassDom currentClass = fileModel.classAt( line, column ); + int startLine = 0, startCol = 0; + + RecoveryPoint* recoveryPoint = this->d->findRecoveryPoint( line, column ); + + QStringList scope; + + if ( !currentClass ) { + kdDebug( 9007 ) << "no container-class found" << endl; + if ( !recoveryPoint ) { + kdDebug( 9007 ) << "no recovery-point found" << endl; + } else { + startLine = recoveryPoint->startLine; + startCol = recoveryPoint->startColumn; + scope = recoveryPoint->scope; + } + } else { + contextItem = currentClass.data(); + scope = currentClass->scope(); + scope << currentClass->name(); + currentClass->getStartPosition( &startLine, &startCol ); + } + + SimpleType container; + if ( m_cachedFromContext ) { + TypeDesc d( scope.join( "::" ) ); + d.setIncludeFiles( getIncludeFiles() ); + SimpleTypeImpl * i = SimpleType( QStringList(), getIncludeFiles() ) ->locateDecType( d ).desc().resolved().data(); + if ( i ) + container = i; + else + container = SimpleType( scope, getIncludeFiles() ); + } else { + container = SimpleType( scope, getIncludeFiles() ); + } + + ExpressionInfo exp = findExpressionAt( line, column , startLine, startCol ); + exp.t = ExpressionInfo::TypeExpression; ///Outside of functions, we can only handle type-expressions + ret.expr = exp; + + if ( exp && ( exp.t & ExpressionInfo::TypeExpression ) ) { + kdDebug( 9007 ) << "locating \"" << exp.expr() << "\" in " << container->fullTypeResolvedWithScope() << endl; + TypeDesc d( exp.expr() ); + d.setIncludeFiles( getIncludeFiles() ); + ret.resultType = container->locateDecType( d ); + } else { + if ( exp ) { + kdDebug( 9007 ) << "wrong expression-type recognized" << endl; + } else { + kdDebug( 9007 ) << "expression could not be recognized" << endl; + } + } + } + + CppCodeCompletionConfig * cfg = m_pSupport->codeCompletionConfig(); + if( cfg->usePermanentCaching() && contextItem ) { + conf.invalidate(); + m_cachedFromContext = contextItem; + } + + return ret; +} + +bool isAfterKeyword( const QString& str, int column ) { + QStringList keywords; + keywords << "new"; + keywords << "throw"; + keywords << "return"; + keywords << "emit"; ///This could be done even better by only showing signals for completion.. + for ( QStringList::iterator it = keywords.begin(); it != keywords.end(); ++it ) { + int len = ( *it ).length(); + if ( column >= len && str.mid( column - len, len ) == *it ) + return true; + } + return false; +} + +void CppCodeCompletion::setMaxComments( int count ) { + m_maxComments = count; +} + +///TODO: make this use findExpressionAt etc. (like the other expression-evaluation-stuff) +void CppCodeCompletion::completeText( bool invokedOnDemand /*= false*/ ) { + kdDebug( 9007 ) << "CppCodeCompletion::completeText()" << endl; + clearStatusText(); + + if ( !m_pSupport || !m_activeCursor || !m_activeEditor || !m_activeCompletion ) + return ; + + setMaxComments( 1000 ); + + needRecoveryPoints(); + + CppCodeCompletionConfig * cfg = m_pSupport->codeCompletionConfig(); + m_demandCompletion = invokedOnDemand; + + FileDom file = m_pSupport->codeModel() ->fileByName( m_activeFileName ); + + if ( !file ) { +// m_pSupport->mainWindow() ->statusBar() ->message( i18n( "File %1 does not exist in the code-model" ).arg( m_activeFileName ), 1000 ); + kdDebug( 9007 ) << "Error: file " << m_activeFileName << " could not be located in the code-model, code-completion stopped\n"; + return ; + } + + CodeModelUtils::CodeModelHelper fileModel( m_pSupport->codeModel(), file ); + + ItemDom contextItem; + + unsigned int line, column; + m_activeCursor->cursorPositionReal( &line, &column ); + + fitContextItem( line, column ); + + ///Check whether the cursor is within a comment + int surroundingStartLine = line - 10, surroundingEndLine = line + 10; + if ( surroundingStartLine < 0 ) + surroundingStartLine = 0; + if ( surroundingEndLine > m_activeEditor->numLines() - 1 ) + surroundingEndLine = m_activeEditor->numLines() - 1; + int surroundingEndCol = m_activeEditor->lineLength( surroundingEndLine ); + + QString pre = getText( surroundingStartLine, 0, line, column ); + int pos = pre.length(); + pre += getText( line, column, surroundingEndLine, surroundingEndCol ); + QString cleared = clearComments( pre ); + if ( cleared[ pos ] != pre[ pos ] ) { + kdDebug( 9007 ) << "stopping completion because we're in a coment" << endl; + return ; + } + + int nLine = line, nCol = column; + + QString strCurLine = clearComments( m_activeEditor->textLine( nLine ) ); + + QString ch = strCurLine.mid( nCol - 1, 1 ); + QString ch2 = strCurLine.mid( nCol - 2, 2 ); + + while ( ch[ 0 ].isSpace() && nCol >= 3 ) { + nCol -= 1; + ch = strCurLine.mid( nCol - 1, 1 ); + ch2 = strCurLine.mid( nCol - 2, 2 ); + } + + if ( m_includeRx.search( strCurLine ) != -1 ) { + if ( !m_fileEntryList.isEmpty() ) { + m_bCompletionBoxShow = true; + m_activeCompletion->showCompletionBox( m_fileEntryList, column - m_includeRx.matchedLength() ); + } + return ; + } + + bool showArguments = false; + bool isInstance = true; + m_completionMode = NormalCompletion; + + if ( ch2 == "->" || ch == "." || ch == "(" ) { + int pos = ch2 == "->" ? nCol - 3 : nCol - 2; + QChar c = strCurLine[ pos ]; + while ( pos > 0 && c.isSpace() ) + c = strCurLine[ --pos ]; + + if ( !( c.isLetterOrNumber() || c == '_' || c == ')' || c == ']' || c == '>' ) ) + return ; + } + + if ( ch == "(" ) { + --nCol; + while ( nCol > 0 && strCurLine[ nCol - 1 ].isSpace() ) + --nCol; + + ///check whether it is a value-definition using constructor + int column = nCol; + bool s1 = false, s2 = false; + while ( column > 0 && isValidIdentifierSign( strCurLine[ column - 1 ] ) ) { + column--; + s1 = true; + } + + ///skip white space + while ( column > 0 && strCurLine[ column - 1 ].isSpace() ) { + --column; + s2 = true; + } + + if ( s1 && s2 && isValidIdentifierSign( strCurLine[ column - 1 ] ) ) { + if ( isAfterKeyword( strCurLine, column ) ) { + ///Maybe a constructor using "new", or "throw", "return", ... + } else { + ///it is a local constructor like "QString name("David");" + nCol = column; + } + } + + + + showArguments = TRUE; + } + + EvaluationResult type; + SimpleType this_type; + QString expr, word; + + DeclarationAST::Node recoveredDecl; + TypeSpecifierAST::Node recoveredTypeSpec; + + SimpleContext* ctx = 0; + SimpleTypeConfiguration conf( m_activeFileName ); + + if ( !m_cachedFromContext ) + conf.setGlobalNamespace( createGlobalNamespace() ); + + ItemLocker<BackgroundParser> block( *m_pSupport->backgroundParser() ); + + FunctionDom currentFunction = fileModel.functionAt( line, column ); + + RecoveryPoint * recoveryPoint = d->findRecoveryPoint( line, column ); + if ( recoveryPoint || currentFunction ) { + contextItem = currentFunction.data(); + QStringList scope; + + int startLine, startColumn; + if ( currentFunction ) { ///maybe change the priority of these + kdDebug( 9007 ) << "using code-model for completion" << endl; + currentFunction->getStartPosition( &startLine, &startColumn ); + scope = currentFunction->scope(); + } else { + kdDebug( 9007 ) << "recovery-point, node-kind = " << nodeTypeToString( recoveryPoint->kind ) << endl; + startLine = recoveryPoint->startLine; + startColumn = recoveryPoint->startColumn; + scope = recoveryPoint->scope; + } + + QString textLine = m_activeEditor->textLine( startLine ); + kdDebug( 9007 ) << "startLine = " << textLine << endl; + + if ( currentFunction || recoveryPoint->kind == NodeType_FunctionDefinition ) { + + QString textToReparse = clearComments( getText( startLine, startColumn, line, showArguments ? nCol : column ) ); + + kdDebug( 9007 ) << "-------------> reparse text" << endl << textToReparse << endl + << "--------------------------------------------" << endl; + + Driver d; + Lexer lexer( &d ); + + macrosToDriver( d, file ); + + lexer.setSource( textToReparse ); + Parser parser( &d, &lexer ); + + parser.parseDeclaration( recoveredDecl ); + /* kdDebug(9007) << "recoveredDecl = " << recoveredDecl.get() << endl;*/ + if ( recoveredDecl.get() ) { + + bool isFunDef = recoveredDecl->nodeType() == NodeType_FunctionDefinition; + kdDebug( 9007 ) << "is function definition= " << isFunDef << endl; + + int endLine, endColumn; + recoveredDecl->getEndPosition( &endLine, &endColumn ); + kdDebug( 9007 ) << "endLine = " << endLine << ", endColumn " << endColumn << endl; + + /// @todo check end position + + if ( isFunDef ) { + FunctionDefinitionAST * def = static_cast<FunctionDefinitionAST*>( recoveredDecl.get() ); + + /// @todo remove code duplication + + QString contents = textToReparse; + int start_expr = expressionAt( contents, contents.length() ); + + // kdDebug(9007) << "start_expr = " << start_expr << endl; + if ( start_expr != int( contents.length() ) ) + expr = contents.mid( start_expr, contents.length() - start_expr ).stripWhiteSpace(); + + if ( expr.startsWith( "SIGNAL" ) || expr.startsWith( "SLOT" ) ) { + m_completionMode = expr.startsWith( "SIGNAL" ) ? SignalCompletion : SlotCompletion; + + showArguments = false; + int end_expr = start_expr - 1; + while ( end_expr > 0 && contents[ end_expr ].isSpace() ) + --end_expr; + + if ( contents[ end_expr ] != ',' ) { + expr = QString::null; + } else { + start_expr = expressionAt( contents, end_expr ); + expr = contents.mid( start_expr, end_expr - start_expr ).stripWhiteSpace(); + } + } else { + if ( !showArguments ) { + int idx = expr.length() - 1; + while ( expr[ idx ].isLetterOrNumber() || expr[ idx ] == '_' ) + --idx; + + if ( idx != int( expr.length() ) - 1 ) { + ++idx; + word = expr.mid( idx ).stripWhiteSpace(); + expr = expr.left( idx ).stripWhiteSpace(); + } + } + } + + ctx = computeContext( def, endLine, endColumn, startLine, startColumn ); + DeclaratorAST* d = def->initDeclarator() ->declarator(); + NameAST* name = d->declaratorId(); + + QStringList nested; + + QPtrList<ClassOrNamespaceNameAST> l; + if ( name ) { + l = name->classOrNamespaceNameList(); + } + // QPtrList<ClassOrNamespaceNameAST> l = name->classOrNamespaceNameList(); + QPtrListIterator<ClassOrNamespaceNameAST> nameIt( l ); + while ( nameIt.current() ) { + if ( nameIt.current() ->name() ) { + nested << nameIt.current() ->name() ->text(); + } + ++nameIt; + } + + if ( currentFunction ) { + scope = currentFunction->scope(); + if ( !scope.isEmpty() ) { + //scope.pop_back(); + } else { + kdDebug( 9007 ) << "scope is empty" << endl; + } + if( dynamic_cast<SimpleTypeNamespace*>( SimpleType::globalNamespace().data() ) ) { + SimpleTypeNamespace* globalNs = static_cast <SimpleTypeNamespace*>( SimpleType::globalNamespace().data() ); + QValueList<QPair<QString, QString> > localImports = ctx->imports(); + for( QValueList<QPair<QString, QString> >::const_iterator it = localImports.begin(); it != localImports.end(); ++it ) + globalNs->addAliasMap( (*it).first, (*it).second ); + } + } else { + scope += nested; + } + + if ( !scope.isEmpty() ) { + SimpleType parentType; + + /*if( !m_cachedFromContext ) { + TypePointer t = createGlobalNamespace(); + conf.setGlobalNamespace( t ); + SimpleTypeNamespace * n = dynamic_cast<SimpleTypeNamespace*>( t.data() ); + if ( !n ) { + QString str = QString( "the global namespace was not resolved correctly , real type: " ) + typeid( n ).name() + QString( " name: " ) + n->scope().join( "::" ) + " scope-size: " + n->scope().count(); + kdDebug( 9007 ) << str << endl; + m_pSupport->mainWindow() ->statusBar() ->message( str , 1000 ); + } else { + } + this_type = SimpleType(t); + }*/ + + if ( m_cachedFromContext ) { + TypeDesc d( scope.join( "::" ) ); + d.setIncludeFiles( getIncludeFiles() ); + SimpleTypeImpl * i = SimpleType( QStringList(), getIncludeFiles() ) ->locateDecType( d ).desc().resolved().data(); + if ( i ) { + parentType = i; + } else { + parentType = SimpleType( scope, getIncludeFiles() ); + } + } else { + parentType = SimpleType( scope, getIncludeFiles() ); + } + this_type = parentType; + this_type->descForEdit().setTotalPointerDepth( 1 ); + ctx->setContainer( this_type ); + } + + ///Now locate the local type using the imported namespaces + if ( !scope.isEmpty() ) { + if ( !m_cachedFromContext ) { + TypeDesc td = ctx->container() ->desc(); + td.makePrivate(); + td.resetResolved( ); + td.setIncludeFiles( getIncludeFiles() ); + TypePointer tt = ctx->container() ->locateDecType( td, SimpleTypeImpl::LocateBase ) ->resolved(); + if ( tt ) { + ctx->setContainer( SimpleType( tt ) ); + } else { + kdDebug( 9007 ) << "could not resolve local this-type \"" << td.fullNameChain() << "\"" << endl; + } + } + + SimpleType this_type = ctx->container(); + + this_type->descForEdit().setTotalPointerDepth( 1 ); + + SimpleVariable var; + var.type = this_type->desc(); + var.name = "this"; + var.comment = this_type->comment(); + ctx->add + ( var ); + ctx->setContainer( this_type ); + } + + ExpressionInfo exp( expr ); + exp.t = ( ExpressionInfo::Type ) ( ExpressionInfo::NormalExpression | ExpressionInfo::TypeExpression ); + type = evaluateExpression( exp, ctx ); + } + + } else { + kdDebug( 9007 ) << "no valid declaration to recover!!!" << endl; + } + } else if ( recoveryPoint->kind == NodeType_ClassSpecifier ) { + QString textToReparse = getText( recoveryPoint->startLine, recoveryPoint->startColumn, + recoveryPoint->endLine, recoveryPoint->endColumn, line ); + // kdDebug(9007) << "-------------> please reparse only text" << endl << textToReparse << endl + // << "--------------------------------------------" << endl; + + Driver d; + Lexer lexer( &d ); + macrosToDriver( d, file ); + + lexer.setSource( textToReparse ); + Parser parser( &d, &lexer ); + + parser.parseClassSpecifier( recoveredTypeSpec ); + /* kdDebug(9007) << "recoveredDecl = " << recoveredTypeSpec.get() << endl;*/ + if ( recoveredTypeSpec.get() ) { + + //ClassSpecifierAST * clazz = static_cast<ClassSpecifierAST*>( recoveredTypeSpec.get() ); + + QString keyword = getText( line, 0, line, column ).simplifyWhiteSpace(); + + kdDebug( 9007 ) << "===========================> keyword is: " << keyword << endl; + + if ( keyword == "virtual" ) { /* + BaseClauseAST *baseClause = clazz->baseClause(); + if ( baseClause ) + { + QPtrList<BaseSpecifierAST> baseList = baseClause->baseSpecifierList(); + QPtrList<BaseSpecifierAST>::iterator it = baseList.begin(); + + for ( ; it != baseList.end(); ++it ) + type.append( ( *it )->name()->text() ); + + ctx = new SimpleContext(); + + showArguments = false; + m_completionMode = VirtualDeclCompletion; + + kdDebug(9007) << "------> found virtual keyword for class specifier '" + << clazz->text() << "'" << endl; + }*/ + } else if ( QString( "virtual" ).find( keyword ) != -1 ) + m_blockForKeyword = true; + else + m_blockForKeyword = false; + } + } + } + + ///@todo is all this necessary? + if ( !recoveredDecl.get() && !recoveredTypeSpec.get() ) { + TranslationUnitAST * ast = *m_pSupport->backgroundParser() ->translationUnit( m_activeFileName ); + if ( AST * node = findNodeAt( ast, line, column ) ) { + kdDebug( 9007 ) << "------------------- AST FOUND --------------------" << endl; + kdDebug( 9007 ) << "node-kind = " << nodeTypeToString( node->nodeType() ) << endl; + + if ( FunctionDefinitionAST * def = functionDefinition( node ) ) { + kdDebug( 9007 ) << "------> found a function definition" << endl; + + int startLine, startColumn; + def->getStartPosition( &startLine, &startColumn ); + + QString contents = getText( startLine, startColumn, line, showArguments ? nCol : column ); + + + /// @todo remove code duplication + int start_expr = expressionAt( contents, contents.length() ); + + // kdDebug(9007) << "start_expr = " << start_expr << endl; + if ( start_expr != int( contents.length() ) ) + expr = contents.mid( start_expr, contents.length() - start_expr ).stripWhiteSpace(); + + if ( expr.startsWith( "SIGNAL" ) || expr.startsWith( "SLOT" ) ) { + m_completionMode = expr.startsWith( "SIGNAL" ) ? SignalCompletion : SlotCompletion; + + showArguments = false; + int end_expr = start_expr - 1; + while ( end_expr > 0 && contents[ end_expr ].isSpace() ) + --end_expr; + + if ( contents[ end_expr ] != ',' ) { + expr = QString::null; + } else { + start_expr = expressionAt( contents, end_expr ); + expr = contents.mid( start_expr, end_expr - start_expr ).stripWhiteSpace(); + } + } else { + int idx = expr.length() - 1; + while ( expr[ idx ].isLetterOrNumber() || expr[ idx ] == '_' ) + --idx; + + if ( idx != int( expr.length() ) - 1 ) { + ++idx; + word = expr.mid( idx ).stripWhiteSpace(); + expr = expr.left( idx ).stripWhiteSpace(); + } + } + + ctx = computeContext( def, line, column, startLine, startColumn ); + + QStringList scope; + scopeOfNode( def, scope ); + this_type = SimpleType( scope, getIncludeFiles() ); + + if ( scope.size() ) { /* + SimpleVariable var; + var.type = scope; + var.name = "this"; + ctx->add( var );*/ + //kdDebug(9007) << "add variable " << var.name << " with type " << var.type << endl; + } + + ExpressionInfo exp( expr ); + exp.t = ( ExpressionInfo::Type ) ( ExpressionInfo::NormalExpression | ExpressionInfo::TypeExpression ); + type = evaluateExpression( exp, ctx ); + } + } + } + + if ( !ctx ) + return ; + + if ( ch2 == "::" ) { + QString str = clearComments( expr ); + if ( !str.contains( '.' ) && !str.contains( "->" ) ) ///Necessary, because the expression may also be like user->BaseUser:: + isInstance = false; + } + + QString resolutionType = "(resolved)"; + if( !type->resolved() ) { + if( BuiltinTypes::isBuiltin( type.resultType ) ) { + resolutionType = "(builtin " + BuiltinTypes::comment( type.resultType ) + ")"; + } else { + resolutionType = "(unresolved)"; + } + } + + kdDebug( 9007 ) << "===========================> type is: " << type->fullNameChain() << resolutionType << endl; + kdDebug( 9007 ) << "===========================> word is: " << word << endl; + + if ( !showArguments ) { + QValueList<CodeCompletionEntry> entryList; + + if ( !type && this_type && ( expr.isEmpty() || expr.endsWith( ";" ) ) ) { + + bool alwaysIncludeNamespaces = cfg->alwaysIncludeNamespaces(); + { + SimpleType t = this_type; + ///First, all static data. + bool ready = false; + SafetyCounter cnt( 20 ); + int depth = 0; + while ( !ready & cnt ) { + if ( t->scope().isEmpty() ) { + ready = true; + } + if( !t->isNamespace() || invokedOnDemand || alwaysIncludeNamespaces ) + computeCompletionEntryList( t, entryList, t->scope(), false, depth ); + t = t->parent(); + depth++; + } + } + { + SimpleType t = this_type; + ///Now find non-static(if we have an instance) and global data + bool ready = false; + SafetyCounter cnt( 20 ); + int depth = 0; + bool first = true; + while ( !ready & cnt ) { + if ( t->scope().isEmpty() ) { + ready = true; + } + if ( ( (t->isNamespace() && invokedOnDemand) || alwaysIncludeNamespaces ) || ( first && isInstance ) ) + computeCompletionEntryList( t, entryList, t->scope(), t->isNamespace() ? true : isInstance, depth ); + t = t->parent(); + depth++; + first = false; + } + } + if( ctx ) computeCompletionEntryList( entryList, ctx, isInstance ); + } else if ( type->resolved() && expr.isEmpty() ) { + if( ctx ) computeCompletionEntryList( entryList, ctx, isInstance ); + + // if ( m_pSupport->codeCompletionConfig() ->includeGlobalFunctions() ) + // computeCompletionEntryList( type, entryList, QStringList(), false ); + + computeCompletionEntryList( type, entryList, QStringList(), false ); + + if ( this_type.scope().size() ) + computeCompletionEntryList( this_type, entryList, this_type.scope(), isInstance ); + computeCompletionEntryList( type, entryList, type->resolved() ->scope() , isInstance ); + } else if ( type->resolved() ) { + if ( type->resolved() ) + computeCompletionEntryList( type, entryList, type->resolved() ->scope() , isInstance ); + } + + QStringList trueMatches; + + if ( invokedOnDemand ) { + // find matching words + QValueList<CodeCompletionEntry>::Iterator it; + for ( it = entryList.begin(); it != entryList.end(); ++it ) { + if ( ( *it ).text.startsWith( word ) ) { + trueMatches << ( *it ).text; + + // if more than one entry matches, abort immediately + if ( trueMatches.size() > 1 ) + break; + } + } + } + + if ( invokedOnDemand && trueMatches.size() == 1 ) { + // erbsland: get the cursor position now, because m_ccLine and m_ccColumn + // are not set until the first typed char. + unsigned int nLine, nCol; + m_activeCursor->cursorPositionReal( &nLine, &nCol ); + // there is only one entry -> complete immediately + m_activeEditor->insertText( nLine, nCol, + trueMatches[ 0 ].right( trueMatches[ 0 ].length() - word.length() ) ); + } else if ( entryList.size() ) { + entryList = unique( entryList ); + qHeapSort( entryList ); + + m_bCompletionBoxShow = true; + ///Warning: the conversion is only possible because CodeCompletionEntry is binary compatible with KTextEditor::CompletionEntry, + ///never change that! + m_activeCompletion->showCompletionBox( *( ( QValueList<KTextEditor::CompletionEntry>* ) ( &entryList ) ), word.length() ); + } + } else { + QValueList<QStringList> signatureList; + + signatureList = computeSignatureList( type ); + + QString methodName = type->name(); + + ///Search for variables with ()-operator in the context + if ( ctx ) { + SimpleVariable var = ctx->findVariable( methodName ); + if ( !var.name.isEmpty() ) { + signatureList += computeSignatureList( ctx->container() ->locateDecType( var.type ) ); + } + } + + ///search for fitting methods/classes in the current context + SimpleType t = this_type; + bool ready = false; + SafetyCounter s( 20 ); + do { + if ( !t ) + ready = true; + TypeDesc d( methodName ); + d.setIncludeFiles( getIncludeFiles() ); + SimpleType method = t->typeOf( d ); + if ( method ) + signatureList += computeSignatureList( method ); + if ( t ) + t = t->parent(); + } while ( !ready && s ); + + if ( !signatureList.isEmpty() ) { + //signatureList = unique( signatureList ); + //qHeapSort( signatureList ); + m_bArgHintShow = true; + m_activeCompletion->showArgHint( unique( signatureList ), "()", "," ); + } + } + + delete( ctx ); + ctx = 0; + + if ( cfg->usePermanentCaching() ) { + conf.invalidate(); + m_cachedFromContext = contextItem; + } +} + + +QValueList<QStringList> CppCodeCompletion::computeSignatureList( EvaluationResult result ) { + SimpleType type = result; + + if ( result.expr.t == ExpressionInfo::TypeExpression ) { + TypeDesc d( result->name() ); + d.setIncludeFiles( getIncludeFiles() ); + type = type->typeOf( d, SimpleTypeImpl::MemberInfo::Function ); ///Compute the signature of the constructor + } + + QValueList<QStringList> retList; + SimpleTypeFunctionInterface* f = type->asFunction(); + SimpleType currType = type; + + if ( !f && !type->isNamespace() ) { + SimpleType t = type->typeOf( TypeDesc("operator ( )"), SimpleTypeImpl::MemberInfo::Function ); + + if ( t ) { + f = t->asFunction(); + currType = t; + } + } + + while ( f ) { + QStringList lst; + QString sig = buildSignature( currType.get() ); + QString comment = currType->comment(); + QStringList commentList; + if ( m_pSupport->codeCompletionConfig() ->showCommentWithArgumentHint() ) { + + if ( !comment.isEmpty() ) { + if ( sig.length() + comment.length() < 130 ) { + sig += ": \"" + currType->comment() + "\""; + } else { + commentList = formatComment( comment ); + } + } + } + + lst << sig; + lst += commentList; + + currType = f->nextFunction(); + + ///Maybe try to apply implicit template-params in this place + + retList << lst; + f = currType->asFunction(); + } + return retList; +} + +void CppCodeCompletion::synchronousParseReady( const QString& file, ParsedFilePointer unit ) { + if ( file == m_activeFileName ) { + computeRecoveryPoints( unit ); + } +} + +void CppCodeCompletion::slotCodeModelUpdated( const QString& fileName ) { + if ( fileName != m_activeFileName || !m_pSupport || !m_activeEditor ) + return ; + +// m_pSupport->mainWindow() ->statusBar() ->message( i18n( "Current file updated %1" ).arg( m_activeFileName ), 1000 ); + + computeRecoveryPointsLocked(); +} + +void CppCodeCompletion::slotFileParsed( const QString& fileName ) { + if ( fileName != m_activeFileName || !m_pSupport || !m_activeEditor ) + return ; + +// m_pSupport->mainWindow() ->statusBar() ->message( i18n( "Current file parsed %1 (cache emptied)" ).arg( m_activeFileName ), 1000 ); + + emptyCache(); ///The cache has to be emptied, because the code-model changed. @todo Better: Only refresh the code-model(tell all code-model-types to refresh themselves on demand) + + computeRecoveryPointsLocked(); +} + +void CppCodeCompletion::setupCodeInformationRepository( ) {} + +SimpleContext* CppCodeCompletion::computeContext( FunctionDefinitionAST * ast, int line, int col, int lineOffset, int colOffset ) { + kdDebug( 9007 ) << "CppCodeCompletion::computeContext() -- main" << endl; + + SimpleContext* ctx = new SimpleContext(); + + if ( ast && ast->initDeclarator() && ast->initDeclarator() ->declarator() ) { + DeclaratorAST * d = ast->initDeclarator() ->declarator(); + if ( ParameterDeclarationClauseAST * clause = d->parameterDeclarationClause() ) { + if ( ParameterDeclarationListAST * params = clause->parameterDeclarationList() ) { + QPtrList<ParameterDeclarationAST> l( params->parameterList() ); + QPtrListIterator<ParameterDeclarationAST> it( l ); + while ( it.current() ) { + ParameterDeclarationAST * param = it.current(); + ++it; + + SimpleVariable var; + + QStringList ptrList; + QPtrList<AST> ptrOpList = param->declarator() ->ptrOpList(); + QPtrList<AST>::iterator it = ptrOpList.begin(); + for ( ; it != ptrOpList.end(); ++it ) { + ptrList.append( ( *it ) ->text() ); + } + + var.ptrList = ptrList; + var.type = param->typeSpec() ->text() + ptrList.join( "" ); + var.name = declaratorToString( param->declarator(), QString::null, true ); + var.comment = param->comment(); + param->getStartPosition( &var.startLine, &var.startCol ); + param->getEndPosition( &var.endLine, &var.endCol ); + + if ( var.type ) { + ctx->add + ( var ); + //kdDebug(9007) << "add argument " << var.name << " with type " << var.type << endl; + } + } + } + } + } + + + if ( ast ) + computeContext( ctx, ast->functionBody(), line, col ); + + if ( ctx ) { + ctx->offset( lineOffset, colOffset ); + } + + return ctx; +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, StatementAST* stmt, int line, int col ) { + if ( !stmt ) + return ; + + switch ( stmt->nodeType() ) { + case NodeType_IfStatement: + computeContext( ctx, static_cast<IfStatementAST*>( stmt ), line, col ); + break; + case NodeType_WhileStatement: + computeContext( ctx, static_cast<WhileStatementAST*>( stmt ), line, col ); + break; + case NodeType_DoStatement: + computeContext( ctx, static_cast<DoStatementAST*>( stmt ), line, col ); + break; + case NodeType_ForStatement: + computeContext( ctx, static_cast<ForStatementAST*>( stmt ), line, col ); + break; + case NodeType_SwitchStatement: + computeContext( ctx, static_cast<SwitchStatementAST*>( stmt ), line, col ); + break; + case NodeType_TryBlockStatement: + computeContext( ctx, static_cast<TryBlockStatementAST*>( stmt ), line, col ); + break; + case NodeType_DeclarationStatement: + computeContext( ctx, static_cast<DeclarationStatementAST*>( stmt ), line, col ); + break; + case NodeType_StatementList: + computeContext( ctx, static_cast<StatementListAST*>( stmt ), line, col ); + break; + case NodeType_ExpressionStatement: + break; + } +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, StatementListAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col, false, true ) ) + return ; + + QPtrList<StatementAST> l( ast->statementList() ); + QPtrListIterator<StatementAST> it( l ); + while ( it.current() ) { + StatementAST * stmt = it.current(); + ++it; + + computeContext( ctx, stmt, line, col ); + } +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, IfStatementAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col ) ) + return ; + + computeContext( ctx, ast->condition(), line, col ); + computeContext( ctx, ast->statement(), line, col ); + computeContext( ctx, ast->elseStatement(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, ForStatementAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col ) ) + return ; + + computeContext( ctx, ast->initStatement(), line, col ); + computeContext( ctx, ast->condition(), line, col ); + computeContext( ctx, ast->statement(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, DoStatementAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col ) ) + return ; + + //computeContext( ctx, ast->condition(), line, col ); + computeContext( ctx, ast->statement(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, WhileStatementAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col ) ) + return ; + + computeContext( ctx, ast->condition(), line, col ); + computeContext( ctx, ast->statement(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, SwitchStatementAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col ) ) + return ; + + computeContext( ctx, ast->condition(), line, col ); + computeContext( ctx, ast->statement(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, TryBlockStatementAST* ast, int line, int col ) { + if ( !inContextScope( ast, line, col ) ) + return ; + + computeContext( ctx, ast->statement(), line, col ); + computeContext( ctx, ast->catchStatementList(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, CatchStatementListAST* ast, int line, int col ) { + /*if ( !inContextScope( ast, line, col, false, true ) ) + return;*/ + + QPtrList<CatchStatementAST> l( ast->statementList() ); + QPtrListIterator<CatchStatementAST> it( l ); + while ( it.current() ) { + CatchStatementAST * stmt = it.current(); + ++it; + + computeContext( ctx, stmt, line, col ); + } +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, CatchStatementAST* ast, int line, int col ) { + if ( !ast->statement() ) + return ; + if ( !inContextScope( ast->statement(), line, col ) ) + return ; + + computeContext( ctx, ast->condition(), line, col ); + computeContext( ctx, ast->statement(), line, col ); +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, DeclarationStatementAST* ast, int line, int col ) { + ///@todo respect NodeType_Typedef + if( ast->declaration() && ast->declaration() ->nodeType() == NodeType_UsingDirective ) { + UsingDirectiveAST* usingDecl = static_cast<UsingDirectiveAST*>( ast->declaration() ); + QString name; + if( usingDecl->name() ) { + name = usingDecl->name()->text(); + + if( !name.isNull() ) + ctx->addImport( QPair<QString, QString>( "", name ) ); + } + } + + if( ast->declaration() && ast->declaration() ->nodeType() == NodeType_NamespaceAlias ) { + NamespaceAliasAST* namespaceAlias = static_cast<NamespaceAliasAST*>( ast->declaration() ); + QString name; + + if( namespaceAlias ->namespaceName() && namespaceAlias->aliasName() ) { + ctx->addImport( QPair<QString, QString>( namespaceAlias->namespaceName()->text(), namespaceAlias->aliasName()->text() ) ); + } + } + + if ( !ast->declaration() || ast->declaration() ->nodeType() != NodeType_SimpleDeclaration ) + return ; + + if ( !inContextScope( ast, line, col, true, false ) ) + return ; + + SimpleDeclarationAST* simpleDecl = static_cast<SimpleDeclarationAST*>( ast->declaration() ); + TypeSpecifierAST* typeSpec = simpleDecl->typeSpec(); + + InitDeclaratorListAST* initDeclListAST = simpleDecl->initDeclaratorList(); + if ( !initDeclListAST ) + return ; + + QPtrList<InitDeclaratorAST> l = initDeclListAST->initDeclaratorList(); + QPtrListIterator<InitDeclaratorAST> it( l ); + while ( it.current() ) { + DeclaratorAST * d = it.current() ->declarator(); + ++it; + + if ( d->declaratorId() ) { + SimpleVariable var; + + QStringList ptrList; + QPtrList<AST> ptrOpList = d->ptrOpList(); + QPtrList<AST>::iterator it = ptrOpList.begin(); + for ( ; it != ptrOpList.end(); ++it ) { + ptrList.append( ( *it ) ->text() ); + } + + for( int a = 0; a < d->arrayDimensionList().count(); a++ ) + ptrList.append("*"); + + var.ptrList = ptrList; + var.type = typeSpec->text() + ptrList.join( "" ); + var.name = toSimpleName( d->declaratorId() ); + var.comment = d->comment(); + d->getStartPosition( &var.startLine, &var.startCol ); + d->getEndPosition( &var.endLine, &var.endCol ); + + ctx->add + ( var ); + //kdDebug(9007) << "add variable " << var.name << " with type " << var.type << endl; + } + } +} + +void CppCodeCompletion::computeContext( SimpleContext*& ctx, ConditionAST* ast, int line, int col ) { + if ( !ast->typeSpec() || !ast->declarator() || !ast->declarator() ->declaratorId() ) + return ; + + if ( !inContextScope( ast, line, col, true, false ) ) + return ; + + SimpleVariable var; + + QStringList ptrList; + QPtrList<AST> ptrOpList = ast->declarator() ->ptrOpList(); + QPtrList<AST>::iterator it = ptrOpList.begin(); + for ( ; it != ptrOpList.end(); ++it ) { + ptrList.append( ( *it ) ->text() ); + } + + var.ptrList = ptrList; + var.type = ast->typeSpec() ->text() + ptrList.join( "" ); + var.name = toSimpleName( ast->declarator() ->declaratorId() ); + var.comment = ast->comment(); + ast->getStartPosition( &var.startLine, &var.startCol ); + ast->getEndPosition( &var.endLine, &var.endCol ); + ctx->add + ( var ); + //kdDebug(9007) << "add variable " << var.name << " with type " << var.type << endl; +} + +bool CppCodeCompletion::inContextScope( AST* ast, int line, int col, bool checkStart, bool checkEnd ) { + int startLine, startColumn; + int endLine, endColumn; + ast->getStartPosition( &startLine, &startColumn ); + ast->getEndPosition( &endLine, &endColumn ); + + // kdDebug(9007) << k_funcinfo << endl; + // kdDebug(9007) << "current char line: " << line << " col: " << col << endl; + // + // kdDebug(9007) << nodeTypeToString( ast->nodeType() ) + // << " start line: " << startLine + // << " col: " << startColumn << endl; + // kdDebug(9007) << nodeTypeToString( ast->nodeType() ) + // << " end line: " << endLine + // << " col: " << endColumn << endl; + + bool start = line > startLine || ( line == startLine && col >= startColumn ); + bool end = line < endLine || ( line == endLine && col <= endColumn ); + + if ( checkStart && checkEnd ) + return start && end; + else if ( checkStart ) + return start; + else if ( checkEnd ) + return end; + + return false; +} + +FunctionDefinitionAST * CppCodeCompletion::functionDefinition( AST* node ) { + + while ( node ) { + if ( node->nodeType() == NodeType_FunctionDefinition ) + return static_cast<FunctionDefinitionAST*>( node ); + node = node->parent(); + } + return 0; +} + +QString CppCodeCompletion::getText( int startLine, int startColumn, int endLine, int endColumn, int omitLine ) { + if ( startLine == endLine ) { + QString textLine = m_activeEditor->textLine( startLine ); + return textLine.mid( startColumn, endColumn - startColumn ); + } + + QStringList contents; + + for ( int line = startLine; line <= endLine; ++line ) { + if ( line == omitLine ) + continue; + + QString textLine = m_activeEditor->textLine( line ); + + if ( line == startLine ) + textLine = textLine.mid( startColumn ); + if ( line == endLine ) + textLine = textLine.left( endColumn ); + + contents << textLine; + } + return contents.join( "\n" ); +} + +void CppCodeCompletion::computeRecoveryPointsLocked() { + m_pSupport->backgroundParser() ->lock () + ; + ParsedFilePointer unit = m_pSupport->backgroundParser() ->translationUnit( m_activeFileName ); + computeRecoveryPoints( unit ); + m_pSupport->backgroundParser() ->unlock(); +} + +void CppCodeCompletion::computeRecoveryPoints( ParsedFilePointer unit ) { + if ( m_blockForKeyword ) + return ; + + kdDebug( 9007 ) << "CppCodeCompletion::computeRecoveryPoints" << endl; + + d->recoveryPoints.clear(); + if ( !unit ) + return ; + + ComputeRecoveryPoints walker( d->recoveryPoints ); + walker.parseTranslationUnit( *unit ); +} + +QString codeModelAccessToString( CodeModelItem::Access access ) { + switch ( access ) { + case CodeModelItem::Public: + return "public"; + case CodeModelItem::Protected: + return "protected"; + case CodeModelItem::Private: + return "private"; + default: + return "unknown"; + } +} + +#define MAXCOMMENTCOLUMNS 45 + + +QString CppCodeCompletion::commentFromItem( const SimpleType& parent, const ItemDom& item ) { + --m_maxComments; + static QString maxReached = " "; + if( m_maxComments < 0 ) { + return maxReached; + } + QString ret; + int line, col; + item->getStartPosition( &line, &col ); + + + if ( !parent->scope().isEmpty() ) { + ret += "Container: " + parent->fullTypeResolvedWithScope(); + } + + if ( item->isEnum() ) { + ret += "\nKind: Enum"; + ret += "\nValues:"; + const EnumModel* en = dynamic_cast<const EnumModel*>( item.data() ); + if ( en ) { + EnumeratorList values = en->enumeratorList(); + for ( EnumeratorList::iterator it = values.begin(); it != values.end(); ++it ) { + ret += "\n " + ( *it ) ->name(); + if ( !( *it ) ->value().isEmpty() ) { + ret + " = " + ( *it ) ->value(); + } + } + + ret += "\n\nAccess: " + codeModelAccessToString( ( CodeModelItem::Access ) en->access() ); + } else {} + + } + + if ( item->isFunction() || item->isFunctionDefinition() ) { + const FunctionModel * f = dynamic_cast<const FunctionModel*>( item.data() ); + ret += "\nKind: Function"; + if ( f ) { + QString state; + if ( f->isStatic() ) + state += "static "; + if ( f->isVirtual() ) + state += "virtual "; + if ( f->isAbstract() ) + state += "abstract "; + //if( f->isTemplateable() ) state += "template "; + if ( f->isConstant() ) + state += "const "; + if ( f->isSlot() ) + state += "slot "; + if ( f->isSignal() ) + state += "signal "; + + if ( !state.isEmpty() ) + ret += "\nModifiers: " + state; + + ret += "\nAccess: " + codeModelAccessToString( ( CodeModelItem::Access ) f->access() ); + } + } + + if ( item->isEnumerator() ) { + const EnumeratorModel * f = dynamic_cast<const EnumeratorModel*>( item.data() ); + ret += "\nKind: Enumerator"; + if ( f ) { + if ( !f->value().isEmpty() ) + ret += "\nValue: " + f->value(); + + //ret += "\nAccess: " + codeModelAccessToString( f->() ); + } + } else { + if ( item->isVariable() ) { + const VariableModel * f = dynamic_cast<const VariableModel*>( item.data() ); + if ( f ) { + if ( !f->isEnumeratorVariable() ) { + ret += "\nKind: Variable"; + if ( f->isStatic() ) + ret += "\nModifiers: static"; + } else { + ret += "\nKind: Enumerator"; + ret += "\nEnum: " + f->type(); + } + + ret += "\nAccess: " + codeModelAccessToString( ( CodeModelItem::Access ) f->access() ); + } + } + } + + if ( item->isTypeAlias() ) { + const TypeAliasModel * t = dynamic_cast<const TypeAliasModel*>( item.data() ); + ret += "\nKind: Typedef"; + if ( t ) { + ret += "\nType: " + t->type(); + LocateResult r = parent->locateDecType( t->type() ); + if ( r.desc().resolved() ) + ret += "\nResolved type: " + r.desc().resolved() ->fullTypeResolvedWithScope(); + else + ret += "\nPartially resolved type: " + r.desc().fullNameChain(); + } + } + + if ( item->isClass() ) { + ret += "\nKind: Class"; + } + + ret += QString( "\nFile: %1\nLine: %2 Column: %3" ).arg( prepareTextForMenu( item->fileName(), 3, MAXCOMMENTCOLUMNS ).join( "\n" ) ).arg( line ).arg( col ); + if ( !item->comment().isEmpty() ) + ret += "\n\n" + prepareTextForMenu( item->comment(), 3, MAXCOMMENTCOLUMNS ).join( "\n" ); + return ret; +} + +QString CppCodeCompletion::commentFromTag( const SimpleType& parent, Tag& tag ) { + --m_maxComments; + static QString maxReached = " "; + if( m_maxComments < 0 ) { + return maxReached; + } + + int line, col; + tag.getStartPosition( &line, &col ); + QString ret; // = tag.comment(); + + if ( !parent->scope().isEmpty() ) { + ret += "Container: " + parent->fullTypeResolvedWithScope(); + } + /* + if( tag.kind() == Tag::Kind_Enum ) { + ret += "\nKind: Enum"; + ret += "\nValues:"; + EnumModel* en = dynamic_cast<EnumModel*>( item.data() ); + if( en ) { + EnumeratorList values =en->enumeratorList(); + for( EnumeratorList::iterator it = values.begin(); it != values.end(); ++it ) + { + ret += "\n " + (*it)->name(); + if( !(*it)->value().isEmpty() ) { + ret + " = " + (*it)->value(); + } + } + + ret += "\n\nAccess: " + codeModelAccessToString( (CodeModelItem::Access)en->access() ); + } else { + } + }*/ + + if ( tag.kind() == Tag::Kind_Function || tag.kind() == Tag::Kind_FunctionDeclaration ) { + CppFunction<Tag> function( tag ); + + ret += "\nKind: Function"; + + QString state; + if ( function.isStatic() ) + state += "static "; + if ( function.isVirtual() ) + state += "virtual "; + //if( function.isVolatile() ) state += "volatile "; + if ( function.isConst() ) + state += "const "; + if ( function.isSlot() ) + state += "slot "; + if ( function.isSignal() ) + state += "signal "; + if ( !state.isEmpty() ) + ret += "\nModifiers: " + state; + + ret += "\nAccess: " + TagUtils::accessToString( function.access() ); + } + + /*if( item->isEnumerator() ) { + EnumeratorModel* f = dynamic_cast<EnumeratorModel*>( item.data() ); + ret += "\nKind: Enumerator"; + if( f ) { + if( !f->value().isEmpty() ) + ret += "\nValue: " + f->value(); + + //ret += "\nAccess: " + codeModelAccessToString( f->() ); + } + } else { + if( item->isVariable() ) { + VariableModel* f = dynamic_cast<VariableModel*>( item.data() ); + ret += "\nKind: Variable"; + if( f ) { + ret += "\nAccess: " + codeModelAccessToString( (CodeModelItem::Access)f->access() ); + } + } + }*/ + + if ( tag.kind() == Tag::Kind_Enum ) { + CppVariable<Tag> var( tag ); + + ret += "\nKind: Enum"; + } + + if ( tag.kind() == Tag::Kind_Enumerator ) { + CppVariable<Tag> var( tag ); + + ret += "\nKind: Enumerator"; + if ( tag.hasAttribute( "enum" ) && tag.attribute( "enum" ).asString() != "int" ) + ret += "\nEnum: " + tag.attribute( "enum" ).asString(); + } + + if ( tag.kind() == Tag::Kind_Variable ) { + CppVariable<Tag> var( tag ); + + ret += "\nKind: Variable"; + if ( var.isStatic() ) + ret += "\nModifiers: static"; + ret += "\nAccess: " + TagUtils::accessToString( var.access() ); + } + + if ( tag.kind() == Tag::Kind_Typedef ) { + ret += "\nKind: Typedef"; + ret += "\nType: " + tagType( tag ); + LocateResult r = parent->locateDecType( tagType( tag ) ); + if ( r.desc().resolved() ) + ret += "\nResolved type: " + r.desc().resolved() ->fullTypeResolvedWithScope(); + else + ret += "\nPartially resolved type: " + r.desc().fullNameChain(); + } + + if ( tag.kind() == Tag::Kind_Class ) { + ret += "\nKind: Class"; + } + if ( tag.kind() == Tag::Kind_Struct ) { + ret += "\nKind: Struct"; + } + + ret += QString( "\nFile: %1\nLine: %2 Column: %3" ).arg( prepareTextForMenu( tag.fileName(), 3, MAXCOMMENTCOLUMNS ).join( "\n" ) ).arg( line ).arg( col ); + if ( !tag.comment().isEmpty() ) { + ret += "\n\n" + prepareTextForMenu( tag.comment(), 20, MAXCOMMENTCOLUMNS ).join( "\n" ); + } + return ret; +} + +void CppCodeCompletion::computeCompletionEntryList( SimpleType typeR, QValueList<CodeCompletionEntry>& entryList, const QStringList& type, SimpleTypeNamespace* ns, std::set<HashedString>& ignore, bool isInstance, int depth ) { + HashedString myName = HashedString( ns->scope().join("::") +"%"+typeid(*ns).name() ); + if ( ignore.find( myName ) != ignore.end() ) + return ; + ignore.insert( myName ); + SimpleTypeNamespace::SlaveList slaves = ns->getSlaves( getIncludeFiles() ); + for ( SimpleTypeNamespace::SlaveList::iterator it = slaves.begin(); it != slaves.end(); ++it ) { + SimpleTypeNamespace* nns = dynamic_cast<SimpleTypeNamespace*>( (*it).first.first.resolved().data() ); + if ( !nns ) { + if( ( *it ).first.first.resolved() ) computeCompletionEntryList( SimpleType((*it).first.first.resolved()), entryList, ( *it ).first.first.resolved()->scope(), isInstance, depth ); + } else { + if( ( *it ).first.first.resolved() ) computeCompletionEntryList( SimpleType(( *it ).first.first.resolved()), entryList, ( *it ).first.first.resolved()->scope(), nns, ignore, isInstance, depth ); + } + } +} + +void CppCodeCompletion::computeCompletionEntryList( SimpleType typeR, QValueList< CodeCompletionEntry > & entryList, const QStringList & type, bool isInstance, int depth ) { + dbgState.setState( disableVerboseForCompletionList ); + + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + SimpleTypeImpl* m = &( *typeR ) ; + + if ( SimpleTypeNamespace * ns = dynamic_cast<SimpleTypeNamespace*>( m ) ) { + std::set<HashedString> ignore; + computeCompletionEntryList( typeR, entryList, type, ns, ignore, isInstance, depth ); + } else if ( dynamic_cast<SimpleTypeCodeModel*>( m ) ) { + ItemDom item = ( dynamic_cast<SimpleTypeCodeModel*>( m ) ) ->item(); + if ( item ) + if ( ClassModel * mod = dynamic_cast<ClassModel*> ( &( *item ) ) ) + computeCompletionEntryList( typeR, entryList, ClassDom( mod ) , isInstance, depth ); + } else { + QValueList<Catalog::QueryArgument> args; + QValueList<Tag> tags; + + QStringList ts = type; + if( !ts.isEmpty() ) { + QString s = ts.back() + typeR->specialization(); + ts.pop_back(); + ts.push_back( s ); + } + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_FunctionDeclaration ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Variable ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + + if ( !isInstance ) { + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Enumerator ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Enum ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Typedef ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Class ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Struct ) + << Catalog::QueryArgument( "scope", ts ); + tags = m_repository->query( args ); + computeCompletionEntryList( typeR, entryList, tags, isInstance, depth ); + } + + args.clear(); + args << Catalog::QueryArgument( "kind", Tag::Kind_Base_class ); + QString fullname = type.join( "::" )+typeR->specialization(); + /* if( fullname.length() >=2 ) + args << Catalog::QueryArgument( "prefix", fullname.left(2) );*/ + args << Catalog::QueryArgument( "name", fullname ); + + + QValueList<LocateResult> parents = typeR->getBases( ); + for ( QValueList<LocateResult>::Iterator it = parents.begin(); it != parents.end(); ++it ) { + if ( !( *it ) ->resolved() ) + continue; + SimpleType tp = SimpleType( ( *it ) ->resolved() ); + if ( tp ) + computeCompletionEntryList( tp, entryList, tp.scope(), isInstance, depth + 1 ); + } + } + dbgState.setState( true ); +} + + +void CppCodeCompletion::computeCompletionEntryList( SimpleType type, QValueList< CodeCompletionEntry > & entryList, QValueList< Tag > & tags, bool isInstance, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + QString className = type->desc().name(); + + bool isNs = type->isNamespace(); + + CompTypeProcessor proc( type, m_pSupport->codeCompletionConfig() ->processFunctionArguments() && type->usingTemplates() ); + bool resolve = m_pSupport->codeCompletionConfig() ->processPrimaryTypes() && type->usingTemplates(); + + QValueList<Tag>::Iterator it = tags.begin(); + while ( it != tags.end() ) { + Tag & tag = *it; + ++it; + + int subSorting = 0; + + if ( tag.name().isEmpty() ) { + continue; + } else if ( m_completionMode != NormalCompletion ) { + if ( tag.kind() != Tag::Kind_FunctionDeclaration ) + continue; + } + + if ( tag.kind() == Tag::Kind_Function || tag.kind() == Tag::Kind_FunctionDeclaration ) { + CppFunction<Tag> info( tag ); + + if ( m_completionMode == SlotCompletion && !info.isSlot() ) + continue; + else if ( m_completionMode == SignalCompletion && !info.isSignal() ) + continue; + else if ( m_completionMode == VirtualDeclCompletion && !info.isVirtual() ) + continue; + + if ( info.isConst() ) + subSorting = 1; + if ( info.isSlot() ) + subSorting = 2; + if ( info.isSignal() ) + subSorting = 3; + if ( info.isVirtual() ) + subSorting = 4; + if ( info.isStatic() ) + subSorting = 5; + } + + CodeCompletionEntry e = CodeInformationRepository::toEntry( tag, m_completionMode, &proc ); + + TagFlags fl; + fl.flags = tag.flags(); + int num = fl.data.access; + + QString str = "public"; + if ( num != 0 ) { + str = TagUtils::accessToString( num ); + } else { + num = 0; + } + // 0 = protected, 1 = public, 2 = private + + if ( str == "public" ) + num = 0; + else if ( str == "protected" ) + num = 1; + else if ( str == "private" ) + num = 2; + + int sortPosition = 0; + + switch ( tag.kind() ) { + case Tag::Kind_Enum: + sortPosition = 3; + if ( isInstance ) + continue; + break; + case Tag::Kind_Enumerator: + sortPosition = 4; + if ( isInstance ) + continue; + break; + case Tag::Kind_Struct: + case Tag::Kind_Union: + case Tag::Kind_Class: + sortPosition = 5; + if ( isInstance ) + continue; + break; + case Tag::Kind_VariableDeclaration: + case Tag::Kind_Variable: + sortPosition = 2; + if ( !isInstance && !CppVariable<Tag>( tag ).isStatic() && !isNs ) + continue; + break; + case Tag::Kind_FunctionDeclaration: + case Tag::Kind_Function: + sortPosition = 1; + if ( !isInstance && !CppFunction<Tag>( tag ).isStatic() && !isNs ) + continue; + break; + case Tag::Kind_Typedef: + sortPosition = 6; + if ( isInstance ) + continue; + break; + } + + e.userdata = QString( "%1%2%3%4%5" ).arg( num ).arg( depth ).arg( className ).arg( sortPosition ).arg( subSorting ); + + if ( m_completionMode != SignalCompletion ) { + if ( !type->isNamespace() ) { + if ( num == 1 ) + e.postfix += "; (protected)"; // in " + proc.parentType() + ")"; + if ( num == 2 ) + e.postfix += "; (private)"; // in " + proc.parentType() + ")"; + } + } + + + QString prefix = tagType( tag ).stripWhiteSpace(); + + if ( tag.kind() == Tag::Kind_Enumerator && tag.hasAttribute( "enum" ) ) { + prefix = tag.attribute( "enum" ).asString(); + e.userdata += prefix; ///Sort enumerators together + } else if ( tag.kind() == Tag::Kind_Enum ) { + prefix = "enum"; + } else { + + if ( tag.kind() == Tag::Kind_FunctionDeclaration || tag.kind() == Tag::Kind_Function || tag.kind() == Tag::Kind_Variable || tag.kind() == Tag::Kind_Typedef ) { + if ( !prefix.isEmpty() && resolve ) { + LocateResult et = type->locateDecType( prefix ); + + if ( et ) + prefix = et->fullNameChain(); + } + } + + if ( tag.kind() == Tag::Kind_FunctionDeclaration || tag.kind() == Tag::Kind_Function ) { + if ( prefix.isEmpty() ) { + if ( tag.name() == className ) + prefix = constructorPrefix; + else if ( tag.name().startsWith( "~" ) ) + prefix = destructorPrefix; + } + } + + if ( tag.kind() == Tag::Kind_Class || tag.kind() == Tag::Kind_Function ) + prefix = ""; + } + + e.comment = commentFromTag( type, tag ); + + if ( e.prefix.isEmpty() ) + e.prefix = prefix; + else + e.prefix += " " + prefix; + + e.prefix = e.prefix.stripWhiteSpace(); + e.prefix = stringMult( depth, " " ) + e.prefix.stripWhiteSpace(); + + e.text = e.text.stripWhiteSpace(); + + if ( str != "private" ) + entryList << e; + } +} + +void CppCodeCompletion::computeCompletionEntryList( SimpleType type, QValueList< CodeCompletionEntry > & entryList, ClassDom klass, bool isInstance, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + + computeCompletionEntryList( type, entryList, klass->functionList(), isInstance, depth ); + + ///Find all function-definitions that have no functions. Those may be inlined functions and need to be treated too. + FunctionDefinitionList definitions = klass->functionDefinitionList(); + FunctionList l; + + QStringList classScope = klass->scope(); + classScope << klass->name(); + + for ( FunctionDefinitionList::iterator it = definitions.begin(); it != definitions.end(); ++it ) { + FunctionList fl = klass->functionByName( ( *it ) ->name() ); + + ArgumentList args = ( *it ) ->argumentList(); + + if ( !l.isEmpty() ) { + bool matched = false; + for ( FunctionList::iterator it = fl.begin(); it != fl.end(); ++it ) { + ArgumentList fArgs = ( *it ) ->argumentList(); + if ( fArgs.count() != args.count() ) + continue; + ArgumentList::iterator it = args.begin(); + ArgumentList::iterator it2 = fArgs.begin(); + bool hit = true; + while ( it != args.end() ) { + if ( ( *it ) ->type() != ( *it2 ) ->type() ) { + hit = false; + break; + } + ++it; + ++it2; + } + if ( hit ) { + matched = true; + break; + } + + } + + if ( matched ) + continue; + } + + ///The function-definition belongs to some sub-class + if( (*it)->scope() != classScope && !(*it)->scope().isEmpty() ) continue; + l << ( FunctionModel* ) ( *it ).data(); + } + + if ( !l.isEmpty() ) + computeCompletionEntryList( type, entryList, l, isInstance, depth ); + + if ( m_completionMode == NormalCompletion ) + computeCompletionEntryList( type, entryList, klass->variableList(), isInstance, depth ); + + if ( !isInstance ) { + computeCompletionEntryList( klass->name(), type, entryList, klass->classList(), isInstance, depth ); + computeCompletionEntryList( klass->name(), type, entryList, klass->typeAliasList(), isInstance, depth ); + } + + QValueList<LocateResult> parents = type->getBases( ); + + for ( QValueList<LocateResult>::Iterator it = parents.begin(); it != parents.end(); ++it ) { + if ( !( *it ) ->resolved() ) + continue; + + SimpleTypeImpl* i = ( *it ) ->resolved(); + computeCompletionEntryList( i, entryList, i->scope(), isInstance, depth + 1 ); + /* + SimpleTypeCodeModel* m = dynamic_cast<SimpleTypeCodeModel*> ( i ); + if ( m ) { + ItemDom item = m->item(); + ClassModel* kl = dynamic_cast<ClassModel*> ( &( *item ) ); + if ( kl ) { + computeCompletionEntryList( SimpleType( ( *it ) ->resolved() ), entryList, ClassDom ( kl ), isInstance, depth + 1 ); + } + }*/ + } +} + +void CppCodeCompletion::computeCompletionEntryList( SimpleType type, QValueList< CodeCompletionEntry > & entryList, NamespaceDom scope, bool isInstance, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + + CppCodeCompletionConfig * cfg = m_pSupport->codeCompletionConfig(); + + computeCompletionEntryList( type, entryList, ClassDom( scope.data() ), isInstance, depth ); + if ( !isInstance ) + computeCompletionEntryList( type, entryList, scope->namespaceList(), isInstance, depth ); +} + +void CppCodeCompletion::computeCompletionEntryList( QString parent, SimpleType type, QValueList< CodeCompletionEntry > & entryList, const ClassList & lst, bool isInstance, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + + + ClassList::ConstIterator it = lst.begin(); + while ( it != lst.end() ) { + ClassDom klass = *it; + ++it; + + CodeCompletionEntry entry; + entry.prefix = "class"; + entry.prefix = stringMult( depth, " " ) + entry.prefix.stripWhiteSpace(); + entry.text = klass->name(); + entry.comment = commentFromItem( type, klass.data() ); + if ( isInstance ) + continue; + + entry.userdata = QString( "%1%2%3%4%5" ).arg( CodeModelItem::Public ).arg( depth ).arg( parent ).arg( 6 ); + + entryList << entry; + + + // if ( cfg->includeTypes() ) + /*{ + computeCompletionEntryList( type, entryList, klass->classList(), isInstance, depth ); + }*/ + } +} + +void CppCodeCompletion::computeCompletionEntryList( QString parent, SimpleType type, QValueList< CodeCompletionEntry > & entryList, const TypeAliasList & lst, bool isInstance, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + + + TypeAliasList::ConstIterator it = lst.begin(); + while ( it != lst.end() ) { + TypeAliasDom klass = *it; + ++it; + + CodeCompletionEntry entry; + + LocateResult et = type->locateDecType( klass->type() ); + if ( et ) + entry.prefix = "typedef " + et->fullNameChain(); + else + entry.prefix = "typedef " + klass->type(); + + entry.prefix = stringMult( depth, " " ) + entry.prefix.stripWhiteSpace(); + entry.text = klass->name(); + entry.comment = commentFromItem( type, klass.data() ); + entry.userdata = QString( "%1%2%3%4%5" ).arg( CodeModelItem::Public ).arg( depth ).arg( parent ).arg( 5 ); + entryList << entry; + } +} +void CppCodeCompletion::computeCompletionEntryList( SimpleType type, QValueList< CodeCompletionEntry > & entryList, const NamespaceList & lst, bool /*isInstance*/, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + + NamespaceList::ConstIterator it = lst.begin(); + while ( it != lst.end() ) { + NamespaceDom scope = *it; + ++it; + + CodeCompletionEntry entry; + entry.prefix = "namespace"; + entry.prefix = stringMult( depth, " " ) + entry.prefix.stripWhiteSpace(); + entry.text = scope->name(); + entry.comment = commentFromItem( type, scope.data() ); + entryList << entry; + } +} + +void CppCodeCompletion::computeCompletionEntryList( SimpleType type, QValueList< CodeCompletionEntry > & entryList, const FunctionList & methods, bool isInstance, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + QString className = type->desc().name(); + bool isNs = type->isNamespace(); + + bool resolve = type->usingTemplates() && m_pSupport->codeCompletionConfig() ->processPrimaryTypes(); + + CompTypeProcessor proc( type, m_pSupport->codeCompletionConfig() ->processFunctionArguments() && type->usingTemplates() ); + + FunctionList::ConstIterator it = methods.begin(); + while ( it != methods.end() ) { + FunctionDom meth = *it; + ++it; + + if ( isInstance && meth->isStatic() ) + continue; + else if ( m_completionMode == SignalCompletion && !meth->isSignal() ) + continue; + else if ( m_completionMode == SlotCompletion && !meth->isSlot() ) + continue; + else if ( m_completionMode == VirtualDeclCompletion && !meth->isVirtual() ) + continue; + + if ( !isInstance && !meth->isStatic() && !isNs ) + continue; + + CodeCompletionEntry entry; + + entry.comment = commentFromItem( type, model_cast<ItemDom>( meth ) ); + + if ( ! resolve ) { + entry.prefix = meth->resultType(); + } else { + QString tt = meth->resultType(); + LocateResult t = type->locateDecType( tt ); + if ( t ) { + entry.prefix = t->fullNameChain(); + } else + entry.prefix = meth->resultType(); + } + + if ( entry.prefix.isEmpty() && meth->name() == className ) + entry.prefix = constructorPrefix; + if ( entry.prefix.isEmpty() && meth->name().startsWith( "~" ) ) + entry.prefix = destructorPrefix; + + entry.prefix = stringMult( depth, " " ) + entry.prefix.stripWhiteSpace(); + QString text; + + ArgumentList args = meth->argumentList(); + ArgumentList::Iterator argIt = args.begin(); + /* + if ( m_completionMode == VirtualDeclCompletion ) + { + //Ideally the type info would be a entry.prefix, but we need them to be + //inserted upon completion so they have to be part of entry.text + entry.text = meth->resultType(); + entry.text += " "; + entry.text += meth->name(); + } + else*/ + entry.text = meth->name(); + + entry.text += formattedOpeningParenthesis( args.size() == 0 ); + + while ( argIt != args.end() ) { + ArgumentDom arg = *argIt; + ++argIt; + + text += proc.processType( arg->type() ); + if ( m_completionMode == NormalCompletion || + m_completionMode == VirtualDeclCompletion ) + text += QString( " " ) + arg->name(); + + if ( argIt != args.end() ) + text += ", "; + } + + if ( args.size() == 0 ) { + entry.text += formattedClosingParenthesis( true ); + } else { + text += formattedClosingParenthesis( false ); + } + + int subSorting = 0; + if ( meth->isConstant() ) + subSorting = 1; + if ( meth->isSlot() ) + subSorting = 2; + if ( meth->isSignal() ) + subSorting = 3; + if ( meth->isVirtual() ) + subSorting = 4; + if ( meth->isStatic() ) + subSorting = 5; + + entry.userdata += QString( "%1%2%3%4%5" ).arg( meth->access() ).arg( depth ).arg( className ).arg( 1 ).arg( subSorting ); + + if ( m_completionMode == VirtualDeclCompletion ) + entry.text += text + ";"; + if ( m_completionMode != NormalCompletion ) + entry.text += text; + else + entry.postfix = text; + + if ( meth->isConstant() ) + entry.postfix += " const"; + if ( m_completionMode != SignalCompletion ) { + if ( !type->isNamespace() ) { + if ( meth->access() == CodeModelItem::Protected ) + entry.postfix += "; (protected)"; // in " + type->fullType() + ")"; + if ( meth->access() == CodeModelItem::Private ) + entry.postfix += "; (private)"; // in " + type->fullType() + ")"; + } + } + + entry.text = entry.text.stripWhiteSpace(); + + entryList << entry; + } +} + +void CppCodeCompletion::computeCompletionEntryList( SimpleType type, QValueList< CodeCompletionEntry > & entryList, const VariableList & attributes, bool isInstance, int depth ) { + Debug d( "#cel#" ); + QString className = type->desc().name(); + bool isNs = type->isNamespace(); + + if ( !safetyCounter || !d ) + return ; + + if ( m_completionMode != NormalCompletion ) + return ; + bool resolve = type->usingTemplates() && m_pSupport->codeCompletionConfig() ->processPrimaryTypes(); + + VariableList::ConstIterator it = attributes.begin(); + while ( it != attributes.end() ) { + VariableDom attr = *it; + ++it; + + if ( isInstance && attr->isStatic() ) + continue; + if ( !isInstance && !attr->isStatic() && !isNs ) + continue; + + CodeCompletionEntry entry; + entry.text = attr->name(); + entry.comment = commentFromItem( type, model_cast<ItemDom>( attr ) ); + entry.userdata += QString( "%1%2%3%4" ).arg( attr->access() ).arg( depth ).arg( className ).arg( 2 ); + + + if ( !attr->isEnumeratorVariable() ) { + if ( ! resolve ) { + entry.prefix = attr->type(); + } else { + QString tt = attr->type(); + LocateResult t = type->locateDecType( tt ); + //SimpleType t = type->typeOf( attr->name() ); + if ( t ) + entry.prefix = t->fullNameChain(); + else + entry.prefix = attr->type(); + } + } else { + entry.prefix = attr->type(); + entry.userdata += attr->type(); ///Sort enumerators by their enum + } + if ( attr->access() == CodeModelItem::Protected ) + entry.postfix += "; (protected)"; // in " + type->fullType() + ")"; + if ( attr->access() == CodeModelItem::Private ) + entry.postfix += "; (private)"; // in " + type->fullType() + ")"; + + entry.prefix = stringMult( depth, " " ) + entry.prefix.stripWhiteSpace(); + + entryList << entry; + } +} + +void CppCodeCompletion::computeCompletionEntryList( QValueList< CodeCompletionEntry > & entryList, SimpleContext * ctx, bool /*isInstance*/, int depth ) { + Debug d( "#cel#" ); + if ( !safetyCounter || !d ) + return ; + + while ( ctx ) { + QValueList<SimpleVariable> vars = ctx->vars(); + QValueList<SimpleVariable>::ConstIterator it = vars.begin(); + while ( it != vars.end() ) { + const SimpleVariable & var = *it; + ++it; + + CodeCompletionEntry entry; + entry.prefix = var.type.fullNameChain(); + entry.text = var.name; + entry.userdata = "000"; + entry.comment = "Local variable"; + entryList << entry; + + } + ctx = ctx->prev(); + } + +} + +EvaluationResult CppCodeCompletion::evaluateExpression( ExpressionInfo expr, SimpleContext* ctx ) { + safetyCounter.init(); + + //d->classNameList = typeNameList( m_pSupport->codeModel() ); + + CppEvaluation::ExpressionEvaluation obj( this, expr, AllOperators, getIncludeFiles(), ctx ); + + EvaluationResult res; + res = obj.evaluate(); + + QString resolutionType = "(resolved)"; + if( !res->resolved() ) { + if( BuiltinTypes::isBuiltin( res.resultType ) ) { + resolutionType = "(builtin " + BuiltinTypes::comment( res.resultType ) + ")"; + } else { + resolutionType = "(unresolved)"; + } + } + + addStatusText( i18n( "Type of \"%1\" is \"%2\", %3" ).arg( expr.expr() ).arg( res->fullNameChain() ).arg( resolutionType ), 5000 ); + + return res; +} + +void CppCodeCompletion::computeFileEntryList( ) { + m_fileEntryList.clear(); + + QStringList fileList = m_pSupport->project() ->allFiles(); + for ( QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it ) { + if ( !m_pSupport->isHeader( *it ) ) + continue; + + CodeCompletionEntry entry; + entry.text = QFileInfo( *it ).fileName(); + + m_fileEntryList.push_back( entry ); + } + + m_fileEntryList = unique( m_fileEntryList ); +} + +HashedStringSet CppCodeCompletion::getIncludeFiles( const QString& fi ) { + QString file = fi; + if( file.isEmpty() ) + file = m_activeFileName; + + FileDom f = m_pSupport->codeModel() ->fileByName( file ); + if( f ) { + ParseResultPointer p = f->parseResult(); + if( p ) { + ParsedFilePointer pp = dynamic_cast<ParsedFile*>( p.data() ); + if( pp ) { + return pp->includeFiles(); + } + } + } + return HashedStringSet(); +} + +void CppCodeCompletion::slotJumpToDeclCursorContext() +{ + kdDebug(9007) << k_funcinfo << endl; + jumpCursorContext( Declaration ); +} + +void CppCodeCompletion::slotJumpToDefCursorContext() +{ + kdDebug(9007) << k_funcinfo << endl; + jumpCursorContext( Definition ); +} + +void CppCodeCompletion::jumpCursorContext( FunctionType f ) +{ + if ( !m_activeCursor ) return; + + SimpleTypeConfiguration conf( m_activeFileName ); + + unsigned int line; + unsigned int column; + m_activeCursor->cursorPositionReal( &line, &column ); + + EvaluationResult result = evaluateExpressionAt( line, column, conf ); + + // Determine the declaration info based on the type of item we are dealing with. + DeclarationInfo d; + + QString includeFileName, includeFilePath; + bool unused; + + if ( result.isMacro ) { + d.name = result.macro.name(); + d.file = result.macro.fileName(); + d.startLine = d.endLine = result.macro.line(); + d.startCol = d.endCol = result.macro.column(); + } else if ( getIncludeInfo( line, includeFileName, includeFilePath, unused ) ) { + d.name = includeFileName; + d.file = includeFilePath; + } else { + d = result.sourceVariable; + } + if ( !d ) { + LocateResult type = result.resultType; + if ( type && type->resolved() ) { + // Is it a namespace? + if ( type->resolved()->isNamespace() ) { + SimpleTypeCachedNamespace * ns = dynamic_cast<SimpleTypeCachedNamespace*>( type->resolved().data() ); + if ( ns ) { + SimpleTypeNamespace::SlaveList slaves = ns->getSlaves( getIncludeFiles() ); + if ( slaves.begin() != slaves.end() ) { + SimpleTypeCachedCodeModel * item = dynamic_cast<SimpleTypeCachedCodeModel*>( ( *slaves.begin() ).first.first.resolved().data() ); + if ( item && item->item() && item->item()->isNamespace() ) { + NamespaceModel* ns = dynamic_cast<NamespaceModel*>( item->item().data() ); + QStringList wholeScope = ns->scope(); + wholeScope << ns->name(); + FileList files = cppSupport()->codeModel()->fileList(); + for ( FileList::iterator it = files.begin(); it != files.end(); ++it ) { + NamespaceModel* ns = (*it).data(); + for ( QStringList::iterator it2 = wholeScope.begin(); it2 != wholeScope.end(); ++it2 ) { + if ( ns->hasNamespace( (*it2) ) ) { + ns = ns->namespaceByName( *it2 ); + if ( !ns ) break; + } else { + ns = 0; + break; + } + } + if ( ns ) { + d.name = ns->name(); + ns->getStartPosition( &d.startLine, &d.startCol ); + ns->getEndPosition( &d.endLine, &d.endCol ); + d.file = ns->fileName(); + break; + } + } + } + } + } + } else { + // Not a namespace, we can get the declaration info straight from the type description. + d = type->resolved()->getDeclarationInfo(); + } + } + // Unresolved, maybe its a named enumeration? + else if ( type && type.trace() ) { + QValueList< QPair<SimpleTypeImpl::MemberInfo, TypeDesc> > trace = type.trace()->trace(); + if ( !trace.isEmpty() ) { + if ( trace.begin() != trace.end() ) { + d = ( *trace.begin() ).first.decl; + } + } + } + } + if ( d ) { + QString fileName = d.file == "current_file" ? m_activeFileName : d.file.operator QString(); + if ( f == Definition && cppSupport()->switchHeaderImpl( fileName, d.startLine, d.startCol ) ) + return; + cppSupport()->partController()->editDocument( fileName, d.startLine ); + } +} + +QString CppCodeCompletion::createTypeInfoString( int line, int column ) +{ + QString typeInfoString; + + SimpleTypeConfiguration conf( m_activeFileName ); + EvaluationResult type = evaluateExpressionAt( line, column, conf ); + + if ( type.expr.expr().stripWhiteSpace().isEmpty() ) + return typeInfoString; + + typeInfoString += type.expr.expr() + QString(" : " ); + + if ( type->resolved() ) + { + QString scope = type->resolved()->scope().join("::"); + int pos = scope.findRev("::"); + if ( scope.isEmpty() || pos == -1 ) + { + scope = "::"; + } + else + { + scope.truncate( pos + 2 ); + } + + typeInfoString += scope + type->fullNameChain() + QString( i18n(" (resolved) ") ); + } + else + { + if ( type ) + { + if( !BuiltinTypes::isBuiltin( type.resultType ) ) + { + typeInfoString += type->fullNameChain() + QString( i18n(" (unresolved) ") ); + } + else + { + typeInfoString += type->fullNameChain() + ", " + BuiltinTypes::comment( type.resultType ) + QString( i18n(" (builtin type) ") ); + } + } + else + { + typeInfoString += QString( i18n(" (unresolved) ") ); + } + } + + if( cppSupport() && type->resolved() && cppSupport()->codeCompletionConfig()->preProcessAllHeaders() ) { + DeclarationInfo decl = type->resolved()->getDeclarationInfo(); + if( !getIncludeFiles()[ HashedString( decl.file ) ] ) { + typeInfoString += " [header not included] "; + } + } + + return typeInfoString; +} + +bool CppCodeCompletion::getIncludeInfo( int line, QString& includeFileName, QString& includeFilePath, bool& usedProjectFiles ) +{ + bool isIncludeDirective = false; + QString lineText = getText( line, 0, line+1, 0 ); + QRegExp includeRx( "(?:#include[\\s]*(?:\\\"|\\<))([^\\n]*)(\\\"|\\>)" ); + if( includeRx.search( lineText ) != -1 ) { + //It is an include-directive. The regular expression captures the string, and the closing sign('"' or '>'). + isIncludeDirective = true; + usedProjectFiles = false; + QStringList captured = includeRx.capturedTexts(); + if( captured.size() == 3 ) { + Dependence d; + d.first = captured[1]; + d.second = captured[2] == "\"" ? Dep_Local : Dep_Global; + includeFilePath = cppSupport()->driver()->findIncludeFile( d, activeFileName() ); + if( includeFilePath.isEmpty() ) { + //A simple backup-algorithm that can only find files within the same project + includeFilePath = cppSupport()->findHeaderSimple( d.first ); + usedProjectFiles = true; + } + includeFileName = d.first; + } else { + kdDebug( 9007 ) << "wrong count of captured items" << endl; + } + } + return isIncludeDirective; +} + + +#include "cppcodecompletion.moc" +//kate: indent-mode csands; tab-width 2; space-indent off; + |