/*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 = ""; const char* destructorPrefix = ""; ///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 TQString& str, int time ) { CppCodeCompletion* c = CppCodeCompletion::instance(); if( c ) c->addStatusText( str, time ); } TypePointer CppCodeCompletion::createGlobalNamespace() { TDESharedPtr n = new SimpleTypeCachedNamespace( TQStringList(), TQStringList() ); n->addAliases(m_pSupport->codeCompletionConfig()->namespaceAliases() ); return n.data(); } template 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( i->file()->parseResult().data()); } SafetyCounter safetyCounter; CppCodeCompletion* cppCompletionInstance = 0; //file global functions, must be before any "using namespace" TQString cleanForMenu( TQString txt ) { return txt.replace( "&", "&&" ).replace( " ", " " ); } TQString buildSignature( TypePointer currType ) { SimpleTypeFunctionInterface * f = currType->asFunction(); if ( !f ) return ""; TQString 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(); TQString 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 */ TQStringList maximumLength( const TQStringList& in, int length ) { TQStringList ret; uint firstNonSpace = 50000; for ( TQStringList::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 TQStringList(); bool hadEmptyLine = false; for ( TQStringList::const_iterator it = in.begin(); it != in.end(); ++it ) { if ( ( *it ).length() <= firstNonSpace ) { if ( !hadEmptyLine ) ret << " "; hadEmptyLine = true; } else { hadEmptyLine = false; TQString 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; } TQStringList prepareTextForMenu( const TQString& comment, int maxLines, int maxLength ) { TQStringList in = TQStringList::split( "\n", comment ); TQStringList out; for ( TQStringList::iterator it = in.begin(); it != in.end(); ++it ) { out << cleanForMenu( *it ); if ( (int)out.count() >= maxLines ) { out << "[...]"; break; } } return maximumLength( out, maxLength ); } TQStringList formatComment( const TQString& comment, int maxCols = 120 ) { TQStringList ret; SafetyCounter s( 14 ); ///maximum of 14 lines TQStringList lines = TQStringList::split( "\n", comment ); for ( TQStringList::iterator it = lines.begin(); it != lines.end(); ++it ) { TQStringList words = TQStringList::split( " ", *it ); while ( !words.isEmpty() && s ) { TQString 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 static TQValueList unique( const TQValueList& entryList ) { TQValueList< ItemType > l; TQMap map; typename TQValueList< ItemType >::ConstIterator it = entryList.begin(); while ( it != entryList.end() ) { CodeCompletionEntry e = *it++; TQString 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 TQStringList unique( const TQStringList& entryList ) { TQStringList l; TQMap map; TQStringList::ConstIterator it = entryList.begin(); while ( it != entryList.end() ) { TQString e = *it++; if ( map.find( e ) == map.end() ) { map[ e ] = TRUE; l << e; } } return l; } static TQStringList unique( const TQValueList& entryList ) { TQStringList l; TQMap map; TQValueList::ConstIterator it = entryList.begin(); while ( it != entryList.end() ) { TQStringList li = ( *it++ ); TQString e = li.join( "\n" ); if ( map.find( e ) == map.end() ) { map[ e ] = TRUE; l += li; } } return l; } bool tokenAt( const TQString& text, const TQString& 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 ] ) { TQChar 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; } TQMap m_namespacePopupCache; void insertItem( TQPopupMenu* parent, SimpleTypeImpl::MemberInfo d , TQString prefix ) { Q_UNUSED(prefix); TQString memType = d.memberTypeToString(); if ( d.memberType == SimpleTypeImpl::MemberInfo::Typedef && d.type->fullName() == "const int" ) memType = "enum"; TQString txt = i18n( "Jump to %1 %2" ).arg( memType ).arg( cleanForMenu( d.name ) ); int id = parent->insertItem( txt, receiver, TQ_SLOT( popupAction( int ) ) ); receiver->m_popupActions.insert( id, d.decl ); } void insertItem ( TQPopupMenu* parent, TypeDesc d , TQString prefix ) { Debug dbg( "#insert# ", 10 ); TQString txt1, txt2; if ( d.resolved() && d.resolved() ->isNamespace() ) { SimpleTypeCachedNamespace * ns = dynamic_cast( 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( ( *it ).first.first.resolved().data() ); if ( cm && cm->item() ) { TQPopupMenu * m = PopupTracker::createPopup( parent ); TQString scope = cm->scope().join("::"); TQMap< TQString, TQPopupMenu* >::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( ( *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( d.resolved().data() ) ) { ///(1) if ( item->item() && item->item() ->isNamespace() ) { NamespaceModel* ns = dynamic_cast( item->item().data() ); TQStringList wholeScope = ns->scope(); wholeScope << ns->name(); for( FileList::iterator it = files.begin(); it != files.end(); ++it ) { // if( !safetyCounter ) break; NamespaceModel* ns = (*it).data(); for( TQStringList::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() + TQString(" (%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, TQ_SLOT( popupAction( int ) ) ); if ( d.resolved() ) receiver->m_popupActions.insert( id, d.resolved() ->getDeclarationInfo() ); if ( !txt2.isEmpty() ) { int id2 = parent->insertItem( txt2, receiver, TQ_SLOT( popupDefinitionAction( int ) ) ); if ( d.resolved() ) receiver->m_popupDefinitionActions.insert( id2, d.resolved() ->getDeclarationInfo() ); } } }; ItemDom itemFromScope( const TQStringList& scope, NamespaceDom startNamespace ) { if ( scope.isEmpty() ) return ItemDom(); NamespaceDom glob = startNamespace; if ( !glob ) return ItemDom(); ClassModel* curr = glob ; TQStringList::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( l.front() ); VariableDom v = curr->variableByName( *mit ); if ( v ) return model_cast( v ); ClassList c = curr->classByName( *mit ); if ( !c.isEmpty() ) return model_cast( c.front() ); EnumDom en = curr->enumByName( *mit ); if ( en ) return model_cast( en ); FunctionList f = curr->functionByName( *mit ); if ( !f.isEmpty() ) return model_cast( f.front() ); FunctionDefinitionList fd = curr->functionDefinitionByName( *mit ); if ( !fd.isEmpty() ) return model_cast( 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( TQPopupMenu* parent, SimpleTypeImpl::MemberInfo d , TQString prefix ) { Q_UNUSED(prefix); FileDom f = receiver->m_pSupport->codeModel() ->fileByName( d.decl.file ); if ( !f ) return ; ItemDom dom = itemFromScope( TQStringList::split( "::", d.name ), model_cast( f ) ); TQString memType = d.memberTypeToString(); if ( d.memberType == SimpleTypeImpl::MemberInfo::Typedef && d.type->fullName() == "const int" ) memType = "enum"; TQString txt = i18n( "Show %1 %2" ).arg( memType ).arg( cleanForMenu( d.name ) ); int id = parent->insertItem( txt, receiver, TQ_SLOT( popupClassViewAction( int ) ) ); receiver->m_popupClassViewActions.insert( id, dom ); } void insertItem ( TQPopupMenu* parent, TypeDesc d , TQString prefix ) { Debug dbg( "#insert# ", 10 ); TQString txt; if ( !d.resolved() ) return ; ItemDom dom; if ( d.resolved() ) { SimpleTypeCodeModel * cm = dynamic_cast( d.resolved().data() ); if ( cm ) dom = cm->item(); } if ( d.resolved() ) { if ( !dom && d.resolved() ->isNamespace() ) { SimpleTypeCachedNamespace * ns = dynamic_cast( 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( ( *it ).first.first.resolved().data() ); if ( cm && cm->item() ) { insertItem( parent, ( new SimpleTypeCachedCodeModel( cm->item() ) ) ->desc(), prefix ); } else { SimpleTypeNamespace* cn = dynamic_cast( ( *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 ) { TQString n = d.resolved() ->scope().join( "::" ); //TQString 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, TQ_SLOT( popupClassViewAction( int ) ) ); if ( dom ) receiver->m_popupClassViewActions.insert( id, dom ); } }; template class PopupFiller { HelpStruct struk; TQString depthAdd; SafetyCounter s; public: PopupFiller( HelpStruct str , TQString dAdd, int maxCount = 100 ) : struk( str ), depthAdd( dAdd ), s( maxCount ) {} void fillIncludes( const DeclarationInfo& decl, TQPopupMenu* parent, bool& needSeparator ) { if( !struk.receiver->getIncludeFiles()[ HashedString( decl.file ) ] ) { TQString file = decl.file; //The include-file seems to be missing if( needSeparator ) { needSeparator = false; parent->insertSeparator(); } TQString includeFile = file; TQFileInfo info( file ); Driver* driver = struk.receiver->cppSupport()->driver(); if( driver ) { TQStringList elements = TQStringList::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, TQ_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( TQPopupMenu * parent, LocateResult d, TQString 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, TQ_SLOT( popupAction( int ) ) ); m_popupActions.insert( id, type.sourceVariable );*/ struk.insertItem( parent, f, prefix ); parent->insertSeparator(); if ( !sourceVariable.comment.isEmpty() ) { TQPopupMenu * m = PopupTracker::createPopup( parent ); parent->insertItem( i18n( "Comment on %1" ).arg( sourceVariable.name ), m ); TQStringList ls = prepareTextForMenu( sourceVariable.comment, 15, 100 ); for ( TQStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { m->insertItem( *it, 0, TQ_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 ) { TQPopupMenu * 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() ) { TQPopupMenu * 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 ) { TQPopupMenu * m = PopupTracker::createPopup( parent ); parent->insertItem( i18n( "Return-type \"%1\"" ).arg( cleanForMenu( rt->fullNameChain() ) ), m ); fill( m, rt ); } TQValueList args = d->resolved() ->asFunction() ->getArgumentTypes(); TQStringList argNames = d->resolved() ->asFunction() ->getArgumentNames(); if ( !args.isEmpty() ) { TQPopupMenu * m = PopupTracker::createPopup( parent ); parent->insertItem( i18n( "Argument-types" ), m ); TQStringList::iterator it2 = argNames.begin(); for ( TQValueList::iterator it = args.begin(); it != args.end(); ++it ) { LocateResult at = d->resolved() ->locateDecType( *it ); TQString name = ""; if ( it2 != argNames.end() ) { name = *it2; ++it2; } TQPopupMenu * mo = PopupTracker::createPopup( m ); m->insertItem( i18n( "Argument \"%1\"" ).arg( cleanForMenu( at->fullNameChain() + " " + name ) ), mo ); fill( mo, at ); } } } } #ifndef DISABLE_TRACING if ( d.trace() ) { TQValueList > trace = d.trace() ->trace(); if ( !trace.isEmpty() ) { TQPopupMenu * m = PopupTracker::createPopup( parent ); parent->insertItem( i18n( "Trace" ), m ); for ( TQValueList >::iterator it = trace.begin(); it != trace.end(); ++it ) { TQPopupMenu * mo = PopupTracker::createPopup( m ); TQString 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(); TQPopupMenu * m = PopupTracker::createPopup( mo ); mo->insertItem( i18n( "Comment" ), m ); TQStringList ls = prepareTextForMenu( ( *it ).first.decl.comment, 15, 100 ); for ( TQStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { m->insertItem( *it, 0, TQ_SLOT( popupClassViewAction( int ) ) ); } } /*bool needSeparator = true; if( struk.shouldShowIncludeMenu() && struk.receiver->cppSupport()->codeCompletionConfig()->preProcessAllHeaders() && !(*it).first.decl.file.operator TQString().isEmpty() ) fillIncludes( (*it).first.decl, mo, needSeparator );*/ } } } #endif if ( d->resolved() ) { TQValueList bases = d->resolved() ->getBases(); for ( TQValueList::iterator it = bases.begin(); it != bases.end(); ++it ) { TQPopupMenu * 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() ) { TQPopupMenu * 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(); TQPopupMenu * m = PopupTracker::createPopup( parent ); parent->insertItem( i18n( "Comment on %1" ).arg( cleanForMenu( d->name() ) ), m ); TQStringList ls = prepareTextForMenu( d->resolved() ->comment(), 15, 100 ); for ( TQStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { m->insertItem( *it, 0, TQ_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() ) { TQValueList > trace = d.trace() ->trace(); if ( !trace.isEmpty() ) { for ( TQValueList >::iterator it = trace.begin(); it != trace.end(); ++it ) { if( struk.shouldShowIncludeMenu() && struk.receiver->cppSupport()->codeCompletionConfig()->preProcessAllHeaders() && !(*it).first.decl.file.operator TQString().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 TQString parentType() { return m_scope->fullType(); } virtual TQString processType( const TQString& type ) { if ( !m_processArguments ) return type; LocateResult t = m_scope->locateDecType( type ); if ( t ) return t->fullNameChain(); else return type; } }; struct CppCodeCompletionData { TQPtrList recoveryPoints; //TQStringList classNameList; CppCodeCompletionData() { recoveryPoints.setAutoDelete( true ); } RecoveryPoint* findRecoveryPoint( int line, int column ) { if ( recoveryPoints.count() == 0 ) return 0; TQPair pt = qMakePair( line, column ); TQPtrListIterator it( recoveryPoints ); RecoveryPoint* recPt = 0; while ( it.current() ) { TQPair startPt = qMakePair( it.current() ->startLine, it.current() ->startColumn ); TQPair 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(), TQ_SIGNAL( stored() ), this, TQ_SLOT( emptyCache() ) ); m_activeCursor = 0; m_activeEditor = 0; m_activeCompletion = 0; m_activeHintInterface = 0; m_activeView = 0; m_ccTimer = new TQTimer( this ); m_showStatusTextTimer = new TQTimer( this ); m_ccLine = 0; m_ccColumn = 0; connect( m_ccTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( slotTimeout() ) ); connect( m_showStatusTextTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( slotStatusTextTimeout() ) ); computeFileEntryList(); CppSupportPart* cppSupport = m_pSupport; connect( cppSupport->project(), TQ_SIGNAL( addedFilesToProject( const TQStringList& ) ), this, TQ_SLOT( computeFileEntryList() ) ); connect( cppSupport->project(), TQ_SIGNAL( removedFilesFromProject( const TQStringList& ) ), this, TQ_SLOT( computeFileEntryList() ) ); connect( cppSupport, TQ_SIGNAL( synchronousParseReady( const TQString&, ParsedFilePointer ) ), this, TQ_SLOT( synchronousParseReady( const TQString&, 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(), TQ_SIGNAL(catalogRegistered( Catalog* )), this, TQ_SLOT( emptyCache() ) ); connect( cppSupport->codeRepository(), TQ_SIGNAL(catalogUnregistered( Catalog* )), this, TQ_SLOT( emptyCache() ) ); connect( cppSupport->codeRepository(), TQ_SIGNAL(catalogChanged( Catalog* )), this, TQ_SLOT( emptyCache() ) ); setupCodeInformationRepository(); if ( part->partController() ->parts() ) { TQPtrListIterator it( *part->partController() ->parts() ); while ( KParts::Part * part = it.current() ) { integratePart( part ); ++it; } } if ( part->partController() ->activePart() ) slotActivePartChanged( part->partController() ->activePart() ); connect( part->partController( ), TQ_SIGNAL( partAdded( KParts::Part* ) ), this, TQ_SLOT( slotPartAdded( KParts::Part* ) ) ); connect( part->partController( ), TQ_SIGNAL( activePartChanged( KParts::Part* ) ), this, TQ_SLOT( slotActivePartChanged( KParts::Part* ) ) ); connect( part, TQ_SIGNAL( fileParsed( const TQString& ) ), this, TQ_SLOT( slotFileParsed( const TQString& ) ) ); connect( part, TQ_SIGNAL( codeModelUpdated( const TQString& ) ), this, TQ_SLOT( slotCodeModelUpdated( const TQString& ) ) ); TDEAction * action = new TDEAction( i18n("Jump to declaration under cursor"), 0, CTRL + Key_Comma, this, TQ_SLOT(slotJumpToDeclCursorContext()), part->actionCollection(), "jump_to_declaration_cursor_context" ); action->plug( &m_DummyActionWidget ); action = new TDEAction( i18n("Jump to definition under cursor"), 0, CTRL + Key_Period, this, TQ_SLOT(slotJumpToDefCursorContext()), part->actionCollection(), "jump_to_defintion_cursor_context" ); action->plug( &m_DummyActionWidget ); } CppCodeCompletion::~CppCodeCompletion( ) { delete m_repository; delete d; } void CppCodeCompletion::addStatusText( TQString text, int timeout ) { m_statusTextList.append( TQPair( 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 ; TQString textLine = m_activeEditor->textLine( nLine ); TQChar 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( 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, TQ_SIGNAL( textChanged() ), this, TQ_SLOT( slotTextChanged() ) ); connect( part->widget(), TQ_SIGNAL( completionDone() ), this, TQ_SLOT( slotCompletionBoxHidden() ) ); connect( part->widget(), TQ_SIGNAL( completionAborted() ), this, TQ_SLOT( slotCompletionBoxHidden() ) ); connect( part->widget(), TQ_SIGNAL( argHintHidden() ), this, TQ_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 , TQ_SIGNAL( needTextHint( int, int, TQString & ) ), this, TQ_SLOT( slotTextHint( int, int, TQString& ) ) ); m_activeHintInterface = 0; } if ( !part ) return ; kdDebug( 9007 ) << k_funcinfo << endl; m_activeFileName = TQString(); KTextEditor::Document* doc = dynamic_cast( part ); if ( !doc ) return ; m_activeFileName = doc->url().path(); // if the interface stuff fails we should disable codecompletion automatically m_activeEditor = dynamic_cast( part ); if ( !m_activeEditor ) { kdDebug( 9007 ) << "Editor doesn't support the EditDocumentIface" << endl; return ; } m_activeCursor = dynamic_cast( part->widget() ); if ( !m_activeCursor ) { kdDebug( 9007 ) << "The editor doesn't support the CursorDocumentIface!" << endl; return ; } m_activeCompletion = dynamic_cast( part->widget() ); if ( !m_activeCompletion ) { kdDebug( 9007 ) << "Editor doesn't support the CompletionIface" << endl; return ; } m_activeView = part ? dynamic_cast( part->widget() ) : 0; if ( m_activeView ) m_activeHintInterface = dynamic_cast( m_activeView ); char* q = 0; kdDebug() << q << endl; if ( m_activeHintInterface ) { #ifndef DISABLETOOLTIPS m_activeHintInterface->enableTextHints( 500 ); connect( m_activeView, TQ_SIGNAL( needTextHint( int, int, TQString & ) ), this, TQ_SLOT( slotTextHint( int, int, TQString& ) ) ); #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 ); TQString strCurLine = m_activeEditor->textLine( nLine ); TQString ch = strCurLine.mid( nCol - 1, 1 ); TQString 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 ) { TQValueList 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( 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( 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 }; TQString CppCodeCompletion::replaceCppComments( const TQString& contents ) { TQString text = contents; int pos = 0; while ( ( pos = m_cppCodeCommentsRx.search( text, pos ) ) != -1 ) { if ( m_cppCodeCommentsRx.cap( 1 ).startsWith( "//" ) ) { TQString before = m_cppCodeCommentsRx.cap( 1 ); TQString 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 TQString& 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. */ TQString text = clearComments( contents ); int last = T_UNKNOWN; int start = index; --index; while ( index > 0 ) { while ( index > 0 && text[ index ].isSpace() ) { --index; } TQChar ch = text[ index ]; TQString 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 ) { TQChar 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 ) { TQChar 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 ) { TQChar 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; } TQStringList CppCodeCompletion::splitExpression( const TQString& text ) { #define ADD_CURRENT()\ if( current.length() ) { l << current; /*kdDebug(9007) << "add word " << current << endl;*/ current = ""; } TQStringList l; uint index = 0; TQString current; while ( index < text.length() ) { TQChar ch = text[ index ]; TQString ch2 = text.mid( index, 2 ); if ( ch == '.' ) { current += ch; ADD_CURRENT(); ++index; } else if ( ch == '(' ) { int count = 0; while ( index < text.length() ) { TQChar 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() ) { TQChar 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(); { TQString 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(); TQString 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() ) { TQString fileName = ( *it ).file == "current_file" ? m_activeFileName : ( *it ).file.operator TQString(); if( (*it).startLine == -1 ) { //startLine -1 indicates that the file should be added to the include-files m_activeEditor->insertLine( 0, TQString("#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() ) { TQString fileName = ( *it ).file == "current_file" ? m_activeFileName : ( *it ).file.operator TQString(); 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 ( TQPopupMenu *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 ) { TQPopupMenu * 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, TQ_SLOT( popupAction( int ) ) ); TQPopupMenu * 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 ); TQStringList ls = prepareTextForMenu( type.macro.body(), 20, 100 ); for ( TQStringList::iterator it = ls.begin(); it != ls.end(); ++it ) { b->insertItem( *it, 0, TQ_SLOT( popupClassViewAction( int ) ) ); } } ///Test if it is an include-directive TQString includeFileName, includeFilePath; bool simpleAlgorithm = false; bool isIncludeDirective = getIncludeInfo( line, includeFileName, includeFilePath, simpleAlgorithm ); if( isIncludeDirective ) { ///Add menu entry if( !includeFilePath.isEmpty() ) { int gid; TQPopupMenu * 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, TQ_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 ; TQString 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 filler( h, "" ); TQPopupMenu * 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( "Navigation

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, TQ_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 { TQPopupMenu * 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( "Navigation

Provides a menu to show involved items in the class-view " ) ); PopupClassViewFillerHelpStruct h( this ); PopupFiller filler( h, "" ); filler.fill( m, type ); } } if ( contextMenuEntriesAtTop ) popup->insertSeparator( cpos ); } void CppCodeCompletion::slotTextHint( int line, int column, TQString &text ) { if ( ! m_pSupport->codeCompletionConfig() ->statusBarTypeEvaluation() ) return ; kdDebug( 9007 ) << "CppCodeCompletion::slotTextHint()" << endl; clearStatusText(); if ( m_lastHintTime.msecsTo( TQTime::currentTime() ) < 300 ) { kdDebug( 9007 ) << "slotNeedTextHint called too often" << endl; return ; } m_lastHintTime = TQTime::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 { TQValueList trace = type.resultType->trace(); if( !trace.isEmpty() ) { for( TQValueList::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 TQString& expr ) { TypeDesc d( expr ); if ( !d.isValidType() ) return false; TQString ex = d.fullNameChain(); TQStringList lex = TQStringList::split( " ", ex ); TQStringList lexpr = TQStringList::split( " ", expr ); return lex.join( " " ) == lexpr.join( " " ); } bool CppCodeCompletion::mayBeTypeTail( int line, int column, TQString& append, bool inFunction ) { TQString 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 TQString& 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; TQString contents = clearComments( getText( startLine, startCol, line, column ) ); int start_expr = expressionAt( contents, contents.length() ); if ( start_expr != int( contents.length() ) ) { TQString 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; TQString 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 TQString 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( 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 ); TQString textLine = m_activeEditor->textLine( modelStartLine ); kdDebug( 9007 ) << "startLine = " << textLine << endl; TQString 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( recoveredDecl.get() ); SimpleContext* ctx = computeContext( def, endLine, endColumn, modelStartLine, modelStartColumn ); if ( !ctx ) return 0; TQStringList scope = f->scope(); if ( !scope.isEmpty() ) { SimpleType parentType; /* if( !m_cachedFromContext ) { TypePointer t = SimpleType(TQStringList())->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( &(*global) ) ) { SimpleTypeNamespace* globalNs = static_cast ( &(*global) ); TQValueList > localImports = ctx->imports(); for( TQValueList >::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 ); TQString 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 ); TQString 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 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 ); TQString strCurLine = m_activeEditor->textLine( nLine ); TQString ch = strCurLine.mid( nCol - 1, 1 ); TQString 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; TQChar 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; } TQString word; { ExpressionInfo exp_ = findExpressionAt( line, column , line, 0 ); if( file->parseResult() ) { ParsedFilePointer p = dynamic_cast( 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 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 ) ) { TQString 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 ); TQStringList 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( TQStringList(), 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 TQString& str, int column ) { TQStringList keywords; keywords << "new"; keywords << "throw"; keywords << "return"; keywords << "emit"; ///This could be done even better by only showing signals for completion.. for ( TQStringList::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 ); TQString pre = getText( surroundingStartLine, 0, line, column ); int pos = pre.length(); pre += getText( line, column, surroundingEndLine, surroundingEndCol ); TQString 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; TQString strCurLine = clearComments( m_activeEditor->textLine( nLine ) ); TQString ch = strCurLine.mid( nCol - 1, 1 ); TQString 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; TQChar 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 "TQString name("David");" nCol = column; } } showArguments = TRUE; } EvaluationResult type; SimpleType this_type; TQString expr, word; DeclarationAST::Node recoveredDecl; TypeSpecifierAST::Node recoveredTypeSpec; SimpleContext* ctx = 0; SimpleTypeConfiguration conf( m_activeFileName ); if ( !m_cachedFromContext ) conf.setGlobalNamespace( createGlobalNamespace() ); ItemLocker block( *m_pSupport->backgroundParser() ); FunctionDom currentFunction = fileModel.functionAt( line, column ); RecoveryPoint * recoveryPoint = d->findRecoveryPoint( line, column ); if ( recoveryPoint || currentFunction ) { contextItem = currentFunction.data(); TQStringList 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; } TQString textLine = m_activeEditor->textLine( startLine ); kdDebug( 9007 ) << "startLine = " << textLine << endl; if ( currentFunction || recoveryPoint->kind == NodeType_FunctionDefinition ) { TQString 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( recoveredDecl.get() ); /// @todo remove code duplication TQString 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( "TQ_SIGNAL" ) || expr.startsWith( "TQ_SLOT" ) ) { m_completionMode = expr.startsWith( "TQ_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 = TQString(); } 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(); TQStringList nested; TQPtrList l; if ( name ) { l = name->classOrNamespaceNameList(); } // TQPtrList l = name->classOrNamespaceNameList(); TQPtrListIterator 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( SimpleType::globalNamespace().data() ) ) { SimpleTypeNamespace* globalNs = static_cast ( SimpleType::globalNamespace().data() ); TQValueList > localImports = ctx->imports(); for( TQValueList >::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( t.data() ); if ( !n ) { TQString str = TQString( "the global namespace was not resolved correctly , real type: " ) + typeid( n ).name() + TQString( " 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( TQStringList(), 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 ) { TQString 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( recoveredTypeSpec.get() ); TQString keyword = getText( line, 0, line, column ).simplifyWhiteSpace(); kdDebug( 9007 ) << "===========================> keyword is: " << keyword << endl; if ( keyword == "virtual" ) { /* BaseClauseAST *baseClause = clazz->baseClause(); if ( baseClause ) { TQPtrList baseList = baseClause->baseSpecifierList(); TQPtrList::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 ( TQString( "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 ); TQString 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( "TQ_SIGNAL" ) || expr.startsWith( "TQ_SLOT" ) ) { m_completionMode = expr.startsWith( "TQ_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 = TQString(); } 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 ); TQStringList 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 == "::" ) { TQString str = clearComments( expr ); if ( !str.contains( '.' ) && !str.contains( "->" ) ) ///Necessary, because the expression may also be like user->BaseUser:: isInstance = false; } TQString 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 ) { TQValueList 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, TQStringList(), false ); computeCompletionEntryList( type, entryList, TQStringList(), 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 ); } TQStringList trueMatches; if ( invokedOnDemand ) { // find matching words TQValueList::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( *( ( TQValueList* ) ( &entryList ) ), word.length() ); } } else { TQValueList signatureList; signatureList = computeSignatureList( type ); TQString 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; } } TQValueList 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 } TQValueList 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 ) { TQStringList lst; TQString sig = buildSignature( currType.get() ); TQString comment = currType->comment(); TQStringList 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 TQString& file, ParsedFilePointer unit ) { if ( file == m_activeFileName ) { computeRecoveryPoints( unit ); } } void CppCodeCompletion::slotCodeModelUpdated( const TQString& 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 TQString& 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() ) { TQPtrList l( params->parameterList() ); TQPtrListIterator it( l ); while ( it.current() ) { ParameterDeclarationAST * param = it.current(); ++it; SimpleVariable var; TQStringList ptrList; TQPtrList ptrOpList = param->declarator() ->ptrOpList(); TQPtrList::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(), TQString(), 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( stmt ), line, col ); break; case NodeType_WhileStatement: computeContext( ctx, static_cast( stmt ), line, col ); break; case NodeType_DoStatement: computeContext( ctx, static_cast( stmt ), line, col ); break; case NodeType_ForStatement: computeContext( ctx, static_cast( stmt ), line, col ); break; case NodeType_SwitchStatement: computeContext( ctx, static_cast( stmt ), line, col ); break; case NodeType_TryBlockStatement: computeContext( ctx, static_cast( stmt ), line, col ); break; case NodeType_DeclarationStatement: computeContext( ctx, static_cast( stmt ), line, col ); break; case NodeType_StatementList: computeContext( ctx, static_cast( 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 ; TQPtrList l( ast->statementList() ); TQPtrListIterator 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;*/ TQPtrList l( ast->statementList() ); TQPtrListIterator 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( ast->declaration() ); TQString name; if( usingDecl->name() ) { name = usingDecl->name()->text(); if( !name.isNull() ) ctx->addImport( TQPair( "", name ) ); } } if( ast->declaration() && ast->declaration() ->nodeType() == NodeType_NamespaceAlias ) { NamespaceAliasAST* namespaceAlias = static_cast( ast->declaration() ); TQString name; if( namespaceAlias ->namespaceName() && namespaceAlias->aliasName() ) { ctx->addImport( TQPair( 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( ast->declaration() ); TypeSpecifierAST* typeSpec = simpleDecl->typeSpec(); InitDeclaratorListAST* initDeclListAST = simpleDecl->initDeclaratorList(); if ( !initDeclListAST ) return ; TQPtrList l = initDeclListAST->initDeclaratorList(); TQPtrListIterator it( l ); while ( it.current() ) { DeclaratorAST * d = it.current() ->declarator(); ++it; if ( d->declaratorId() ) { SimpleVariable var; TQStringList ptrList; TQPtrList ptrOpList = d->ptrOpList(); TQPtrList::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; TQStringList ptrList; TQPtrList ptrOpList = ast->declarator() ->ptrOpList(); TQPtrList::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( node ); node = node->parent(); } return 0; } TQString CppCodeCompletion::getText( int startLine, int startColumn, int endLine, int endColumn, int omitLine ) { if ( startLine == endLine ) { TQString textLine = m_activeEditor->textLine( startLine ); return textLine.mid( startColumn, endColumn - startColumn ); } TQStringList contents; for ( int line = startLine; line <= endLine; ++line ) { if ( line == omitLine ) continue; TQString 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 ); } TQString 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 TQString CppCodeCompletion::commentFromItem( const SimpleType& parent, const ItemDom& item ) { --m_maxComments; static TQString maxReached = " "; if( m_maxComments < 0 ) { return maxReached; } TQString 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( 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( item.data() ); ret += "\nKind: Function"; if ( f ) { TQString 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( 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( 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( 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 += TQString( "\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; } TQString CppCodeCompletion::commentFromTag( const SimpleType& parent, Tag& tag ) { --m_maxComments; static TQString maxReached = " "; if( m_maxComments < 0 ) { return maxReached; } int line, col; tag.getStartPosition( &line, &col ); TQString 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( 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 function( tag ); ret += "\nKind: Function"; TQString 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( 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( item.data() ); ret += "\nKind: Variable"; if( f ) { ret += "\nAccess: " + codeModelAccessToString( (CodeModelItem::Access)f->access() ); } } }*/ if ( tag.kind() == Tag::Kind_Enum ) { CppVariable var( tag ); ret += "\nKind: Enum"; } if ( tag.kind() == Tag::Kind_Enumerator ) { CppVariable 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 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 += TQString( "\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, TQValueList& entryList, const TQStringList& type, SimpleTypeNamespace* ns, std::set& 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( (*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, TQValueList< CodeCompletionEntry > & entryList, const TQStringList & type, bool isInstance, int depth ) { dbgState.setState( disableVerboseForCompletionList ); Debug d( "#cel#" ); if ( !safetyCounter || !d ) return ; SimpleTypeImpl* m = &( *typeR ) ; if ( SimpleTypeNamespace * ns = dynamic_cast( m ) ) { std::set ignore; computeCompletionEntryList( typeR, entryList, type, ns, ignore, isInstance, depth ); } else if ( dynamic_cast( m ) ) { ItemDom item = ( dynamic_cast( m ) ) ->item(); if ( item ) if ( ClassModel * mod = dynamic_cast ( &( *item ) ) ) computeCompletionEntryList( typeR, entryList, ClassDom( mod ) , isInstance, depth ); } else { TQValueList args; TQValueList tags; TQStringList ts = type; if( !ts.isEmpty() ) { TQString 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 ); TQString fullname = type.join( "::" )+typeR->specialization(); /* if( fullname.length() >=2 ) args << Catalog::QueryArgument( "prefix", fullname.left(2) );*/ args << Catalog::QueryArgument( "name", fullname ); TQValueList parents = typeR->getBases( ); for ( TQValueList::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, TQValueList< CodeCompletionEntry > & entryList, TQValueList< Tag > & tags, bool isInstance, int depth ) { Debug d( "#cel#" ); if ( !safetyCounter || !d ) return ; TQString 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(); TQValueList::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 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; TQString 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 ).isStatic() && !isNs ) continue; break; case Tag::Kind_FunctionDeclaration: case Tag::Kind_Function: sortPosition = 1; if ( !isInstance && !CppFunction( tag ).isStatic() && !isNs ) continue; break; case Tag::Kind_Typedef: sortPosition = 6; if ( isInstance ) continue; break; } e.userdata = TQString( "%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() + ")"; } } TQString 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, TQValueList< 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; TQStringList 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 it3 = args.begin(); ArgumentList::iterator it2 = fArgs.begin(); bool hit = true; while ( it3 != args.end() ) { if ( ( *it3 ) ->type() != ( *it2 ) ->type() ) { hit = false; break; } ++it3; ++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 ); } TQValueList parents = type->getBases( ); for ( TQValueList::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 ( i ); if ( m ) { ItemDom item = m->item(); ClassModel* kl = dynamic_cast ( &( *item ) ); if ( kl ) { computeCompletionEntryList( SimpleType( ( *it ) ->resolved() ), entryList, ClassDom ( kl ), isInstance, depth + 1 ); } }*/ } } void CppCodeCompletion::computeCompletionEntryList( SimpleType type, TQValueList< 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( TQString parent, SimpleType type, TQValueList< 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 = TQString( "%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( TQString parent, SimpleType type, TQValueList< 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 = TQString( "%1%2%3%4%5" ).arg( CodeModelItem::Public ).arg( depth ).arg( parent ).arg( 5 ); entryList << entry; } } void CppCodeCompletion::computeCompletionEntryList( SimpleType type, TQValueList< 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, TQValueList< CodeCompletionEntry > & entryList, const FunctionList & methods, bool isInstance, int depth ) { Debug d( "#cel#" ); if ( !safetyCounter || !d ) return ; TQString 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( meth ) ); if ( ! resolve ) { entry.prefix = meth->resultType(); } else { TQString 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(); TQString 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 += TQString( " " ) + 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 += TQString( "%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, TQValueList< CodeCompletionEntry > & entryList, const VariableList & attributes, bool isInstance, int depth ) { Debug d( "#cel#" ); TQString 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( attr ) ); entry.userdata += TQString( "%1%2%3%4" ).arg( attr->access() ).arg( depth ).arg( className ).arg( 2 ); if ( !attr->isEnumeratorVariable() ) { if ( ! resolve ) { entry.prefix = attr->type(); } else { TQString 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( TQValueList< CodeCompletionEntry > & entryList, SimpleContext * ctx, bool /*isInstance*/, int depth ) { Debug d( "#cel#" ); if ( !safetyCounter || !d ) return ; while ( ctx ) { TQValueList vars = ctx->vars(); TQValueList::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(); TQString 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(); TQStringList fileList = m_pSupport->project() ->allFiles(); for ( TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it ) { if ( !m_pSupport->isHeader( *it ) ) continue; CodeCompletionEntry entry; entry.text = TQFileInfo( *it ).fileName(); m_fileEntryList.push_back( entry ); } m_fileEntryList = unique( m_fileEntryList ); } HashedStringSet CppCodeCompletion::getIncludeFiles( const TQString& fi ) { TQString 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( 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; TQString 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( type->resolved().data() ); if ( ns ) { SimpleTypeNamespace::SlaveList slaves = ns->getSlaves( getIncludeFiles() ); if ( slaves.begin() != slaves.end() ) { SimpleTypeCachedCodeModel * item = dynamic_cast( ( *slaves.begin() ).first.first.resolved().data() ); if ( item && item->item() && item->item()->isNamespace() ) { NamespaceModel* ns = dynamic_cast( item->item().data() ); TQStringList 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 ( TQStringList::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() ) { TQValueList< TQPair > trace = type.trace()->trace(); if ( !trace.isEmpty() ) { if ( trace.begin() != trace.end() ) { d = ( *trace.begin() ).first.decl; } } } } if ( d ) { TQString fileName = d.file == "current_file" ? m_activeFileName : d.file.operator TQString(); if ( f == Definition && cppSupport()->switchHeaderImpl( fileName, d.startLine, d.startCol ) ) return; cppSupport()->partController()->editDocument( fileName, d.startLine ); } } TQString CppCodeCompletion::createTypeInfoString( int line, int column ) { TQString typeInfoString; SimpleTypeConfiguration conf( m_activeFileName ); EvaluationResult type = evaluateExpressionAt( line, column, conf ); if ( type.expr.expr().stripWhiteSpace().isEmpty() ) return typeInfoString; typeInfoString += type.expr.expr() + TQString(" : " ); if ( type->resolved() ) { TQString scope = type->resolved()->scope().join("::"); int pos = scope.findRev("::"); if ( scope.isEmpty() || pos == -1 ) { scope = "::"; } else { scope.truncate( pos + 2 ); } typeInfoString += scope + type->fullNameChain() + TQString( i18n(" (resolved) ") ); } else { if ( type ) { if( !BuiltinTypes::isBuiltin( type.resultType ) ) { typeInfoString += type->fullNameChain() + TQString( i18n(" (unresolved) ") ); } else { typeInfoString += type->fullNameChain() + ", " + BuiltinTypes::comment( type.resultType ) + TQString( i18n(" (builtin type) ") ); } } else { typeInfoString += TQString( 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, TQString& includeFileName, TQString& includeFilePath, bool& usedProjectFiles ) { bool isIncludeDirective = false; TQString lineText = getText( line, 0, line+1, 0 ); TQRegExp 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; TQStringList 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"