You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3184 lines
100 KiB
3184 lines
100 KiB
/***************************************************************************
|
|
* Copyright (C) 1999 by Jonas Nordin *
|
|
* jonas.nordin@syncom.se *
|
|
* Copyright (C) 2000-2001 by Bernd Gehrmann *
|
|
* bernd@kdevelop.org *
|
|
* Copyright (C) 2002-2003 by Roberto Raggi *
|
|
* roberto@kdevelop.org *
|
|
* Copyright (C) 2003-2004 by Alexander Dymo *
|
|
* adymo@mksat.net *
|
|
* *
|
|
* 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 "cppsupportpart.h"
|
|
#include "cppsupport_events.h"
|
|
#include "problemreporter.h"
|
|
#include "backgroundparser.h"
|
|
#include "store_walker.h"
|
|
#include "ast.h"
|
|
#include "ast_utils.h"
|
|
#include "cppcodecompletion.h"
|
|
#include "ccconfigwidget.h"
|
|
#include "KDevCppSupportIface.h"
|
|
#include "cppsupportfactory.h"
|
|
#include "catalog.h"
|
|
#include "cpp_tags.h"
|
|
#include "kdevdriver.h"
|
|
#include "cppcodecompletionconfig.h"
|
|
#include "cppsplitheadersourceconfig.h"
|
|
#include "tag_creator.h"
|
|
#include "cppsupport_utils.h"
|
|
#include "classgeneratorconfig.h"
|
|
#include "urlutil.h"
|
|
#include "creategettersetterconfiguration.h"
|
|
#include "kdevsourceformatter.h"
|
|
#include "kdevcreatefile.h"
|
|
#include "qtbuildconfig.h"
|
|
#include "kdeveditorutil.h"
|
|
#include <tdetexteditor/viewcursorinterface.h>
|
|
#include <tdepopupmenu.h>
|
|
// wizards
|
|
#include "cppnewclassdlg.h"
|
|
#include "subclassingdlg.h"
|
|
#include "addmethoddialog.h"
|
|
#include "addattributedialog.h"
|
|
#include "creategettersetterdialog.h"
|
|
// designer integration
|
|
#include "qtdesignercppintegration.h"
|
|
#include "cppimplementationwidget.h"
|
|
#include "configproblemreporter.h"
|
|
#include "codeinformationrepository.h"
|
|
|
|
#include <tqeventloop.h>
|
|
#include <tqheader.h>
|
|
#include <tqdir.h>
|
|
#include <tqdom.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqguardedptr.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqprogressdialog.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtimer.h>
|
|
#include <tqstatusbar.h>
|
|
#include <tqprogressbar.h>
|
|
#include <tqregexp.h>
|
|
#include <tqlabel.h>
|
|
#include <tqvbox.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tdeaction.h>
|
|
#include <tdeapplication.h>
|
|
#include <kdebug.h>
|
|
#include <kdialogbase.h>
|
|
#include <kgenericfactory.h>
|
|
#include <tdelocale.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tdemainwindow.h>
|
|
#include <kstatusbar.h>
|
|
#include <tdeconfig.h>
|
|
#include <tdeversion.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kiconloader.h>
|
|
|
|
#include <tdetexteditor/document.h>
|
|
#include <tdetexteditor/editinterface.h>
|
|
#include <tdetexteditor/view.h>
|
|
#include <tdetexteditor/selectioninterface.h>
|
|
#include <tdetexteditor/viewcursorinterface.h>
|
|
#include <tdetexteditor/clipboardinterface.h>
|
|
#include <tdetexteditor/texthintinterface.h>
|
|
|
|
#include <kdevcore.h>
|
|
#include <kdevproject.h>
|
|
#include <kdevmainwindow.h>
|
|
#include <kdevpartcontroller.h>
|
|
#include <kdevmakefrontend.h>
|
|
#include <kdevcoderepository.h>
|
|
#include <codemodel_utils.h>
|
|
#include <kdevplugininfo.h>
|
|
|
|
#include <domutil.h>
|
|
#include <config.h>
|
|
|
|
const bool alwaysParseInBackground = true;
|
|
|
|
enum { KDEV_DB_VERSION = 21 };
|
|
enum { KDEV_PCS_VERSION = 18 };
|
|
|
|
TQStringList CppSupportPart::m_sourceMimeTypes = TQStringList() << "text/x-csrc" << "text/x-c++src";
|
|
TQStringList CppSupportPart::m_headerMimeTypes = TQStringList() << "text/x-chdr" << "text/x-c++hdr";
|
|
|
|
TQStringList CppSupportPart::m_sourceExtensions = TQStringList::split( ",", "c,C,cc,cpp,c++,cxx,m,mm,M" );
|
|
TQStringList CppSupportPart::m_headerExtensions = TQStringList::split( ",", "h,H,hh,h++,hxx,hpp,inl,tlh,diff,ui.h" );
|
|
|
|
class CppDriver: public KDevDriver
|
|
{
|
|
public:
|
|
CppDriver( CppSupportPart* cppSupport ) : KDevDriver( cppSupport, true )
|
|
{}
|
|
|
|
void fileParsed( ParsedFile& fileName )
|
|
{
|
|
//kdDebug(9007) << "-----> file " << fileName << " parsed!" << endl;
|
|
|
|
ParsedFilePointer ast = takeTranslationUnit( fileName.fileName() );
|
|
|
|
if ( cppSupport() ->problemReporter() )
|
|
{
|
|
cppSupport() ->problemReporter() ->removeAllProblems( fileName.fileName() );
|
|
|
|
TQValueList<Problem> pl = problems( fileName.fileName() );
|
|
TQValueList<Problem>::ConstIterator it = pl.begin();
|
|
while ( it != pl.end() )
|
|
{
|
|
const Problem & p = *it++;
|
|
cppSupport() ->problemReporter() ->reportProblem( fileName.fileName(), p );
|
|
}
|
|
}
|
|
|
|
StoreWalker walker( fileName.fileName(), cppSupport() ->codeModel() );
|
|
|
|
if ( cppSupport() ->codeModel() ->hasFile( fileName.fileName() ) )
|
|
{
|
|
FileDom file = cppSupport() ->codeModel() ->fileByName( fileName.fileName() );
|
|
cppSupport() ->removeWithReferences( fileName.fileName() );
|
|
}
|
|
|
|
walker.parseTranslationUnit( *ast );
|
|
cppSupport() ->codeModel() ->addFile( walker.file() );
|
|
remove
|
|
( fileName.fileName() );
|
|
|
|
if( cppSupport()->_jd ) {
|
|
cppSupport()->_jd->backgroundState ++;
|
|
cppSupport()->_jd->lastParse = TQTime::currentTime();
|
|
}
|
|
|
|
TQFileInfo fileInfo( fileName.fileName() );
|
|
TQString path = URLUtil::canonicalPath( fileName.fileName() );
|
|
|
|
cppSupport()->m_timestamp[ path ] = fileInfo.lastModified();
|
|
|
|
cppSupport()->emitSynchronousParseReady( fileName.fileName(), ast );
|
|
}
|
|
};
|
|
|
|
// ProblemReporter doesn't really depend on background parsing, so it's a bit of a mixup to
|
|
// handle them together, but it's the same config widget so...
|
|
class BackgroundParserConfig
|
|
{
|
|
bool m_useProblemReporter;
|
|
bool m_useBackgroundParser;
|
|
int m_backgroundParseDelay;
|
|
public:
|
|
void readConfig()
|
|
{
|
|
TDEConfig* config = kapp->config();
|
|
config->setGroup( "General Options" );
|
|
m_useProblemReporter = config->readBoolEntry( "EnableProblemReporter", true );
|
|
m_useBackgroundParser = config->readBoolEntry( "EnableCppBgParser", true );
|
|
m_backgroundParseDelay = config->readNumEntry( "BgParserDelay", 500 );
|
|
}
|
|
|
|
bool useProblemReporter() { return m_useProblemReporter; }
|
|
bool useBackgroundParser() { return m_useBackgroundParser; }
|
|
int backgroudParseDelay() { return m_backgroundParseDelay; }
|
|
};
|
|
|
|
|
|
CppSupportPart::CppSupportPart( TQObject *parent, const char *name, const TQStringList &args )
|
|
: KDevLanguageSupport( CppSupportFactory::info(), parent, name ? name : "KDevCppSupport" ), m_backgroundParser(0),
|
|
m_activeDocument( 0 ), m_activeView( 0 ), m_activeSelection( 0 ), m_activeEditor( 0 ), m_activeViewCursor( 0 ),
|
|
m_projectClosed( true ), m_projectClosing( false ), m_valid( false ), m_isTyping( false ), m_hadErrors( false ),
|
|
_jd(0)
|
|
{
|
|
setInstance( CppSupportFactory::instance() );
|
|
|
|
m_pCompletionConfig = new CppCodeCompletionConfig( this, projectDom() );
|
|
m_pSplitHeaderSourceConfig = new CppSplitHeaderSourceConfig( this, projectDom() );
|
|
m_pCreateGetterSetterConfiguration = new CreateGetterSetterConfiguration( this ); connect( m_pSplitHeaderSourceConfig, TQT_SIGNAL( stored() ),
|
|
this, TQT_SLOT( splitHeaderSourceConfigStored() ) );
|
|
connect( m_pCompletionConfig, TQT_SIGNAL( stored() ),
|
|
this, TQT_SLOT( codeCompletionConfigStored() ) );
|
|
m_qtBuildConfig = new QtBuildConfig( this, projectDom() );
|
|
m_qtBuildConfig->store();
|
|
|
|
m_backgroundParserConfig = new BackgroundParserConfig;
|
|
m_backgroundParserConfig->readConfig();
|
|
|
|
m_driver = new CppDriver( this );
|
|
m_problemReporter = 0;
|
|
|
|
m_textChangedTimer = new TQTimer( this );
|
|
connect( m_textChangedTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotParseCurrentFile()) );
|
|
|
|
m_cursorMovedTimer = new TQTimer( this );
|
|
connect( m_cursorMovedTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotCursorPositionChanged()) );
|
|
|
|
|
|
// m_deleteParserStoreTimer = new TQTimer( this );
|
|
m_saveMemoryTimer = new TQTimer( this );
|
|
m_buildSafeFileSetTimer = new TQTimer( this );
|
|
// m_functionHintTimer = new TQTimer( this );
|
|
connect( m_buildSafeFileSetTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(buildSafeFileSet()) );
|
|
connect( m_saveMemoryTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotSaveMemory()) );
|
|
// connect( m_deleteParserStoreTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotDeleteParserStore()) );
|
|
resetParserStoreTimer();
|
|
m_saveMemoryTimer->start( 240000, false ); //Free some memory every 4 minutes
|
|
// connect( m_functionHintTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotFunctionHint()) );
|
|
|
|
setXMLFile( "kdevcppsupport.rc" );
|
|
|
|
m_catalogList.setAutoDelete( true );
|
|
|
|
connect( core(), TQT_SIGNAL( projectOpened() ), this, TQT_SLOT( projectOpened() ) );
|
|
connect( core(), TQT_SIGNAL( projectClosed() ), this, TQT_SLOT( projectClosed() ) );
|
|
connect( core(), TQT_SIGNAL( languageChanged() ), this, TQT_SLOT( projectOpened() ) );
|
|
connect( partController(), TQT_SIGNAL( savedFile( const KURL& ) ),
|
|
this, TQT_SLOT( savedFile( const KURL& ) ) );
|
|
connect( core(), TQT_SIGNAL( contextMenu( TQPopupMenu *, const Context * ) ),
|
|
this, TQT_SLOT( contextMenu( TQPopupMenu *, const Context * ) ) );
|
|
connect( partController(), TQT_SIGNAL( activePartChanged( KParts::Part* ) ),
|
|
this, TQT_SLOT( activePartChanged( KParts::Part* ) ) );
|
|
connect( partController(), TQT_SIGNAL( partRemoved( KParts::Part* ) ),
|
|
this, TQT_SLOT( partRemoved( KParts::Part* ) ) );
|
|
|
|
connect( core(), TQT_SIGNAL( configWidget( KDialogBase* ) ),
|
|
this, TQT_SLOT( configWidget( KDialogBase* ) ) );
|
|
|
|
m_switchHeaderSourceAction = new TDEAction( i18n( "Switch Header/Implementation" ), SHIFT + Key_F12,
|
|
this, TQT_SLOT( slotSwitchHeader() ),
|
|
actionCollection(), "edit_switchheader" );
|
|
m_switchHeaderSourceAction->setToolTip( i18n( "Switch between header and implementation files" ) );
|
|
m_switchHeaderSourceAction->setWhatsThis( i18n( "<b>Switch Header/Implementation</b><p>"
|
|
"If you are currently looking at a header file, this "
|
|
"brings you to the corresponding implementation file. "
|
|
"If you are looking at an implementation file (.cpp etc.), "
|
|
"this brings you to the corresponding header file." ) );
|
|
m_switchHeaderSourceAction->setEnabled( false );
|
|
|
|
TDEAction *action;
|
|
|
|
action = new TDEAction( i18n( "Complete Text" ), CTRL + Key_Space,
|
|
this, TQT_SLOT( slotCompleteText() ),
|
|
actionCollection(), "edit_complete_text" );
|
|
action->setToolTip( i18n( "Complete current expression" ) );
|
|
action->setWhatsThis( i18n( "<b>Complete Text</p><p>Completes current expression using "
|
|
"memory class store for the current project and persistent class stores "
|
|
"for external libraries." ) );
|
|
action->setEnabled( false );
|
|
|
|
m_createGetterSetterAction = new TDEAction( i18n( "Create Accessor Methods" ), 0,
|
|
this, TQT_SLOT( slotCreateAccessMethods() ), actionCollection(),
|
|
"edit_create_getter_setter" );
|
|
|
|
action = new TDEAction( i18n( "Make Member" ), 0, Key_F2,
|
|
this, TQT_SLOT( slotMakeMember() ),
|
|
actionCollection(), "edit_make_member" );
|
|
action->setToolTip( i18n( "Make member" ) );
|
|
action->setWhatsThis( i18n( "<b>Make member</b><p>Creates a class member function in implementation file "
|
|
"based on the member declaration at the current line." ) );
|
|
action->plug( &m_DummyActionWidget );
|
|
|
|
action = new TDEAction( i18n( "Navigation Menu" ), 0, CTRL + ALT + Key_Space,
|
|
this, TQT_SLOT( slotNavigate() ),
|
|
actionCollection(), "edit_navigate" );
|
|
action->setToolTip( i18n( "Show the navigation-menu" ) );
|
|
action->setWhatsThis( i18n( "<b>Navigate</b><p>Shows a navigation-menu based on the type-evaluation of the item under the cursor." ) );
|
|
action->plug( &m_DummyActionWidget );
|
|
|
|
|
|
action = new TDEAction( i18n( "New Class..." ), "classnew", 0,
|
|
this, TQT_SLOT( slotNewClass() ),
|
|
actionCollection(), "project_newclass" );
|
|
action->setToolTip( i18n( "Generate a new class" ) );
|
|
action->setWhatsThis( i18n( "<b>New Class</b><p>Calls the <b>New Class</b> wizard." ) );
|
|
|
|
m_pCompletion = 0;
|
|
|
|
withcpp = false;
|
|
if ( args.count() == 1 && args[ 0 ] == "Cpp" )
|
|
withcpp = true;
|
|
|
|
// daniel
|
|
connect( core( ), TQT_SIGNAL( projectConfigWidget( KDialogBase* ) ), this,
|
|
TQT_SLOT( projectConfigWidget( KDialogBase* ) ) );
|
|
|
|
new KDevCppSupportIface( this );
|
|
//(void) dcopClient();
|
|
|
|
m_lockupTester = new UIBlockTester( 100 );
|
|
}
|
|
|
|
|
|
CppSupportPart::~CppSupportPart()
|
|
{
|
|
delete m_lockupTester;
|
|
|
|
if ( !m_projectClosed )
|
|
projectClosed();
|
|
|
|
delete( m_driver );
|
|
m_driver = 0;
|
|
|
|
if ( m_backgroundParser )
|
|
{
|
|
m_backgroundParser->close();
|
|
// m_backgroundParser->wait();
|
|
delete m_backgroundParser;
|
|
m_backgroundParser = 0;
|
|
}
|
|
|
|
codeRepository() ->setMainCatalog( 0 );
|
|
|
|
TQPtrListIterator<Catalog> it( m_catalogList );
|
|
while ( Catalog * catalog = it.current() )
|
|
{
|
|
++it;
|
|
codeRepository() ->unregisterCatalog( catalog );
|
|
}
|
|
|
|
|
|
delete m_backgroundParserConfig;
|
|
m_backgroundParserConfig = 0;
|
|
|
|
delete m_pCompletion;
|
|
m_pCompletion = 0;
|
|
|
|
/* mainWindow()->removeView( m_problemReporter );
|
|
delete m_problemReporter;
|
|
m_problemReporter = 0;
|
|
*/
|
|
delete _jd;
|
|
_jd = 0;
|
|
|
|
kdDebug( 9007 ) << k_funcinfo << endl;
|
|
}
|
|
|
|
|
|
void CppSupportPart::customEvent( TQCustomEvent* ev )
|
|
{
|
|
kdDebug( 9007 ) << "CppSupportPart::customEvent(" << ev->type() << ")" << endl;
|
|
|
|
TQTime t;
|
|
t.start();
|
|
bool fromDisk = false;
|
|
|
|
if ( ev->type() == int( Event_FileParsed ) )
|
|
{
|
|
resetParserStoreTimer();
|
|
|
|
FileParsedEvent * event = ( FileParsedEvent* ) ev;
|
|
fromDisk = event->fromDisk();
|
|
TQString fileName = event->fileName();
|
|
bool hasErrors = false;
|
|
if ( m_problemReporter )
|
|
{
|
|
m_problemReporter->removeAllProblems( fileName );
|
|
|
|
TQValueList<Problem> problems = event->problems();
|
|
TQValueList<Problem>::ConstIterator it = problems.begin();
|
|
while ( it != problems.end() )
|
|
{
|
|
const Problem & p = *it++;
|
|
if ( p.level() == Problem::Level_Error )
|
|
hasErrors = true;
|
|
|
|
m_problemReporter->reportProblem( fileName, p );
|
|
}
|
|
}
|
|
ParsedFilePointer p = m_backgroundParser->translationUnit( fileName );
|
|
if( p && !p->includedFrom().isEmpty() ) {
|
|
kdDebug( 9007 ) << "customEvent() parsed included file \"" << fileName << "\" included from \"" << p->includedFrom() << "\"" << endl;
|
|
} else {
|
|
kdDebug( 9007 ) << "customEvent() parsed file \"" << fileName << "\"" << endl;
|
|
}
|
|
|
|
if( p && !p->includedFrom().isEmpty() ) {
|
|
if( !project()->isProjectFile( fileName ) ) {
|
|
//The file was parsed to resolve a dependency, and is not a project file
|
|
addToRepository( p );
|
|
} else {
|
|
//It is a project-file that was parsed for whatever reason to resolve a dependency(currently it isn't handled this way)
|
|
}
|
|
} else if( !project()->isProjectFile( fileName ) || !m_parseEmitWaiting.reject( fileName ) ) {
|
|
ParseEmitWaiting::Processed p = m_parseEmitWaiting.processFile( fileName, ( !m_hadErrors && hasErrors && !fromDisk && m_isTyping && fileName == m_activeFileName ) ? ParseEmitWaiting::HadErrors : ParseEmitWaiting::None );
|
|
parseEmit( p );
|
|
|
|
//Increase status-bar
|
|
if( p.hasFlag( ParseEmitWaiting::Silent ) && _jd ) {
|
|
_jd->backgroundState ++;
|
|
_jd->lastParse = TQTime::currentTime();
|
|
}
|
|
|
|
} else {
|
|
ParseEmitWaiting::Processed p = m_fileParsedEmitWaiting.processFile( fileName );
|
|
if( !p.hasFlag( ParseEmitWaiting::Silent ) )
|
|
emitFileParsed( p );
|
|
|
|
//Increase status-bar
|
|
if( p.hasFlag( ParseEmitWaiting::Silent ) && _jd ) {
|
|
_jd->backgroundState ++;
|
|
_jd->lastParse = TQTime::currentTime();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CppSupportPart::projectConfigWidget( KDialogBase* dlg )
|
|
{
|
|
TQVBox * vbox = 0;
|
|
|
|
vbox = dlg->addVBoxPage( i18n( "C++ Support" ), i18n( "C++ Support" ),
|
|
BarIcon( info() ->icon(), TDEIcon::SizeMedium ) );
|
|
CCConfigWidget* w = new CCConfigWidget( this, vbox );
|
|
connect( dlg, TQT_SIGNAL( okClicked( ) ), w, TQT_SLOT( accept( ) ) );
|
|
}
|
|
|
|
void CppSupportPart::configWidget( KDialogBase *dlg )
|
|
{
|
|
TQVBox * vbox = dlg->addVBoxPage( i18n( "C++ Class Generator" ), i18n( "C++ Class Generator" ),
|
|
BarIcon( info() ->icon(), TDEIcon::SizeMedium ) );
|
|
ClassGeneratorConfig *w = new ClassGeneratorConfig( vbox, "classgenerator config widget" );
|
|
connect( dlg, TQT_SIGNAL( okClicked() ), w, TQT_SLOT( storeConfig() ) );
|
|
|
|
vbox = dlg->addVBoxPage(i18n("C++ Parsing"), i18n("C++ Parsing"),
|
|
BarIcon( "text-x-c++src", TDEIcon::SizeMedium) );
|
|
ConfigureProblemReporter* ww = new ConfigureProblemReporter( vbox );
|
|
ww->setPart( this );
|
|
connect(dlg, TQT_SIGNAL(okClicked()), ww, TQT_SLOT(accept()));
|
|
}
|
|
|
|
void CppSupportPart::activePartChanged( KParts::Part *part )
|
|
{
|
|
kdDebug( 9032 ) << "CppSupportPart::activePartChanged()" << endl;
|
|
|
|
bool enabled = false;
|
|
|
|
// m_functionHintTimer->stop();
|
|
|
|
if ( m_activeView )
|
|
{
|
|
disconnect( m_activeView, TQT_SIGNAL( cursorPositionChanged() ), this, 0 );
|
|
}
|
|
if ( m_activeDocument )
|
|
{
|
|
disconnect( m_activeDocument, TQT_SIGNAL(textChanged()), this, 0 );
|
|
}
|
|
|
|
m_isTyping = false;
|
|
m_hadErrors = true;
|
|
m_activeDocument = dynamic_cast<KTextEditor::Document*>( part );
|
|
m_activeView = part ? dynamic_cast<KTextEditor::View*>( part->widget() ) : 0;
|
|
m_activeEditor = dynamic_cast<KTextEditor::EditInterface*>( part );
|
|
m_activeSelection = dynamic_cast<KTextEditor::SelectionInterface*>( part );
|
|
m_activeViewCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>( m_activeView );
|
|
|
|
m_activeFileName = TQString();
|
|
|
|
if ( m_activeDocument )
|
|
{
|
|
m_activeFileName = URLUtil::canonicalPath( m_activeDocument->url().path() );
|
|
TQFileInfo fi( m_activeFileName );
|
|
TQString ext = fi.extension();
|
|
if ( isSource( m_activeFileName ) || isHeader( m_activeFileName ) )
|
|
enabled = true;
|
|
}
|
|
|
|
actionCollection() ->action( "edit_switchheader" ) ->setEnabled( enabled );
|
|
actionCollection() ->action( "edit_complete_text" ) ->setEnabled( enabled );
|
|
actionCollection() ->action( "edit_make_member" ) ->setEnabled( enabled );
|
|
|
|
if ( !part || !part->widget() )
|
|
return ;
|
|
|
|
if ( m_activeDocument )
|
|
{
|
|
connect( m_activeDocument, TQT_SIGNAL(textChanged()), this, TQT_SLOT(slotTextChanged()) );
|
|
m_textChangedTimer->start( 250, true ); // kick the parse timer, we might want to parse the current file
|
|
}
|
|
|
|
if ( m_activeViewCursor )
|
|
{
|
|
connect( m_activeView, TQT_SIGNAL( cursorPositionChanged() ), this, TQT_SLOT(slotCursorMoved()) );
|
|
// this, TQT_SLOT( slotCursorPositionChanged() ) );
|
|
}
|
|
|
|
|
|
#if 0
|
|
KTextEditor::TextHintInterface* textHintIface = dynamic_cast<KTextEditor::TextHintInterface*>( m_activeView );
|
|
if ( !textHintIface )
|
|
return ;
|
|
|
|
connect( view, TQT_SIGNAL( needTextHint( int, int, TQString& ) ),
|
|
this, TQT_SLOT( slotNeedTextHint( int, int, TQString& ) ) );
|
|
|
|
textHintIface->enableTextHints( 1000 );
|
|
#endif
|
|
}
|
|
|
|
|
|
void CppSupportPart::setTyping( bool typing ) {
|
|
m_isTyping = typing;
|
|
if( m_problemReporter) {
|
|
m_hadErrors &= m_problemReporter->hasErrors(m_activeFileName);///m_hadErrors generally stores whether there was an error-free state of the file.
|
|
}
|
|
}
|
|
|
|
|
|
void CppSupportPart::projectOpened( )
|
|
{
|
|
kdDebug( 9007 ) << "projectOpened( )" << endl;
|
|
|
|
m_backgroundParser = new BackgroundParser( this, &m_eventConsumed );
|
|
m_backgroundParser->start( TQThread::IdlePriority );
|
|
|
|
// setup the driver
|
|
TQString conf_file_name = specialHeaderName();
|
|
if ( TQFile::exists( conf_file_name ) )
|
|
m_driver->parseFile( conf_file_name, true, true, true );
|
|
|
|
m_projectDirectory = URLUtil::canonicalPath( project() ->projectDirectory() );
|
|
m_projectFileList = project() ->allFiles();
|
|
|
|
setupCatalog();
|
|
|
|
embedProblemReporter();
|
|
|
|
connect( core(), TQT_SIGNAL( configWidget( KDialogBase* ) ),
|
|
m_problemReporter, TQT_SLOT( configWidget( KDialogBase* ) ) );
|
|
|
|
connect( project( ), TQT_SIGNAL( addedFilesToProject( const TQStringList & ) ),
|
|
this, TQT_SLOT( addedFilesToProject( const TQStringList & ) ) );
|
|
connect( project( ), TQT_SIGNAL( removedFilesFromProject( const TQStringList & ) ),
|
|
this, TQT_SLOT( removedFilesFromProject( const TQStringList & ) ) );
|
|
connect( project( ), TQT_SIGNAL( changedFilesInProject( const TQStringList & ) ),
|
|
this, TQT_SLOT( changedFilesInProject( const TQStringList & ) ) );
|
|
connect( project(), TQT_SIGNAL( projectCompiled() ),
|
|
this, TQT_SLOT( slotProjectCompiled() ) );
|
|
|
|
m_timestamp.clear();
|
|
m_parseEmitWaiting.clear();
|
|
m_fileParsedEmitWaiting.clear();
|
|
|
|
m_pCompletion = new CppCodeCompletion( this );
|
|
m_projectClosed = false;
|
|
|
|
m_buildSafeFileSetTimer->start( 500, true );
|
|
updateParserConfiguration(); //Necessary to respect custom include-paths and such
|
|
|
|
TQTimer::singleShot( 500, this, TQT_SLOT( initialParse( ) ) );
|
|
}
|
|
|
|
void CppSupportPart::embedProblemReporter( bool force )
|
|
{
|
|
if ( force || m_backgroundParserConfig->useProblemReporter() )
|
|
{
|
|
m_problemReporter = new ProblemReporter( this, 0, "problemReporterWidget" );
|
|
m_problemReporter->setIcon( SmallIcon( "application-vnd.tde.info" ) );
|
|
m_problemReporter->setCaption( i18n( "Problem Reporter" ) );
|
|
mainWindow( ) ->embedOutputView( m_problemReporter, i18n( "Problems" ), i18n( "Problem reporter" ) );
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::removeProblemReporter()
|
|
{
|
|
mainWindow()->removeView( m_problemReporter );
|
|
delete m_problemReporter;
|
|
m_problemReporter = 0;
|
|
}
|
|
|
|
|
|
void CppSupportPart::projectClosed( )
|
|
{
|
|
kdDebug( 9007 ) << "projectClosed( )" << endl;
|
|
|
|
m_projectClosing = true;
|
|
|
|
TQStringList enabledPCSs;
|
|
TQValueList<Catalog*> catalogs = codeRepository() ->registeredCatalogs();
|
|
for ( TQValueList<Catalog*>::Iterator it = catalogs.begin(); it != catalogs.end(); ++it )
|
|
{
|
|
Catalog* c = *it;
|
|
if ( c->enabled() )
|
|
enabledPCSs.push_back( TQFileInfo( c->dbName() ).baseName(true) );
|
|
}
|
|
DomUtil::writeListEntry( *project() ->projectDom(), "kdevcppsupport/references", "pcs", enabledPCSs );
|
|
|
|
for ( TQMap<KInterfaceDesigner::DesignerType, KDevDesignerIntegration*>::const_iterator it = m_designers.begin();
|
|
it != m_designers.end(); ++it )
|
|
{
|
|
kdDebug( 9007 ) << "calling save settings fro designer integration" << endl;
|
|
it.data() ->saveSettings( *project() ->projectDom(), "kdevcppsupport/designerintegration" );
|
|
}
|
|
|
|
saveProjectSourceInfo();
|
|
|
|
m_pCompletionConfig->store();
|
|
|
|
delete _jd;
|
|
_jd = 0;
|
|
|
|
removeProblemReporter();
|
|
|
|
delete m_pCompletion;
|
|
m_parseEmitWaiting.clear();
|
|
m_fileParsedEmitWaiting.clear();
|
|
m_pCompletion = 0;
|
|
m_projectClosed = true;
|
|
m_projectClosing = false;
|
|
}
|
|
|
|
|
|
void CppSupportPart::slotNavigate() {
|
|
if( codeCompletion() && m_activeView && m_activeViewCursor ) {
|
|
unsigned int curLine = 0, curCol = 0;
|
|
m_activeViewCursor->cursorPositionReal( &curLine, &curCol );
|
|
|
|
if( m_navigationMenu ) delete (TDEPopupMenu*)m_navigationMenu;
|
|
|
|
m_navigationMenu = new TDEPopupMenu( m_activeView );
|
|
|
|
codeCompletion()->contextEvaluationMenus( m_navigationMenu, 0, curLine, curCol );
|
|
|
|
m_navigationMenu->move( m_activeView->mapToGlobal( m_activeViewCursor->cursorCoordinates() ) );
|
|
if ( m_navigationMenu->count() > 0 )
|
|
{
|
|
m_navigationMenu->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CppSupportPart::contextMenu( TQPopupMenu *popup, const Context *context )
|
|
{
|
|
m_activeClass = 0;
|
|
m_activeFunction = 0;
|
|
m_activeVariable = 0;
|
|
m_curAttribute = 0;
|
|
m_curClass = 0;
|
|
|
|
if ( context->hasType( Context::EditorContext ) )
|
|
{
|
|
int id;
|
|
|
|
m_switchHeaderSourceAction->plug( popup );
|
|
|
|
// CodeModelItemContext
|
|
if ( context->type() == Context::EditorContext )
|
|
{
|
|
m_curClass = currentClass();
|
|
if ( m_curClass != 0 )
|
|
{
|
|
m_curAttribute = currentAttribute( m_curClass );
|
|
if ( m_curAttribute != 0 )
|
|
m_createGetterSetterAction->plug( popup );
|
|
}
|
|
}
|
|
|
|
TQString text;
|
|
int atline, atcol;
|
|
MakeMemberHelper( text, atline, atcol );
|
|
if ( !text.isEmpty() )
|
|
{
|
|
id = popup->insertItem( i18n( "Make Member" ), this, TQT_SLOT( slotMakeMember() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Make member</b><p>Creates a class member function in implementation file "
|
|
"based on the member declaration at the current line." ) );
|
|
}
|
|
|
|
kdDebug( 9007 ) << "======> code model has the file: " << m_activeFileName << " = " << codeModel() ->hasFile( m_activeFileName ) << endl;
|
|
|
|
bool showContextMenuExplosion = false;
|
|
bool showContextTypeEvaluation = false;
|
|
TDEConfig *config = CppSupportFactory::instance() ->config();
|
|
if ( config )
|
|
{
|
|
config->setGroup( "General" );
|
|
showContextMenuExplosion = config->readBoolEntry( "ShowContextMenuExplosion", false );
|
|
config->setGroup( "General" );
|
|
showContextTypeEvaluation = config->readBoolEntry( "ShowContextTypeEvaluation", true );
|
|
}
|
|
|
|
|
|
if( codeModel() ->hasFile( m_activeFileName ) ) {
|
|
|
|
if( showContextTypeEvaluation && m_activeViewCursor != 0 ) {
|
|
if( codeCompletion() ) {
|
|
unsigned int curLine = 0, curCol = 0;
|
|
m_activeViewCursor->cursorPositionReal( &curLine, &curCol );
|
|
|
|
codeCompletion()->contextEvaluationMenus( popup, context, curLine, curCol );
|
|
}
|
|
}
|
|
|
|
|
|
if ( showContextMenuExplosion )
|
|
{
|
|
//kdDebug( 9007 ) << "CppSupportPart::contextMenu 1" << endl;
|
|
TQString candidate;
|
|
if ( isSource( m_activeFileName ) )
|
|
candidate = sourceOrHeaderCandidate();
|
|
else
|
|
candidate = m_activeFileName;
|
|
|
|
unsigned int curLine = 0, curCol = 0;
|
|
if ( m_activeViewCursor != 0 )
|
|
m_activeViewCursor->cursorPositionReal( &curLine, &curCol );
|
|
|
|
//kdDebug( 9007 ) << "CppSupportPart::contextMenu 2: candidate: " << candidate << endl;
|
|
|
|
if ( !candidate.isEmpty() && codeModel() ->hasFile( candidate ) )
|
|
{
|
|
TQPopupMenu * m2 = new TQPopupMenu( popup );
|
|
id = popup->insertItem( i18n( "Go to Declaration" ), m2 );
|
|
popup->setWhatsThis( id, i18n( "<b>Go to declaration</b><p>Provides a menu to select available function declarations "
|
|
"in the current file and in the corresponding header (if the current file is an implementation) or source (if the current file is a header) file." ) );
|
|
|
|
FileDom file2 = codeModel() ->fileByName( candidate );
|
|
//kdDebug( 9007 ) << "CppSupportPart::contextMenu 3: " << file2->name() << endl;
|
|
|
|
FunctionList functionList2 = CodeModelUtils::allFunctions( file2 );
|
|
for ( FunctionList::ConstIterator it = functionList2.begin(); it != functionList2.end(); ++it )
|
|
{
|
|
TQString text = ( *it ) ->scope().join( "::" );
|
|
//kdDebug( 9007 ) << "CppSupportPart::contextMenu 3 text: " << text << endl;
|
|
if ( !text.isEmpty() )
|
|
{
|
|
text += "::";
|
|
}
|
|
text += formatModelItem( *it, true );
|
|
text = text.replace( TQString::fromLatin1( "&" ), TQString::fromLatin1( "&&" ) );
|
|
int id = m2->insertItem( text, this, TQT_SLOT( gotoDeclarationLine( int ) ) );
|
|
int line, column;
|
|
( *it ) ->getStartPosition( &line, &column );
|
|
m2->setItemParameter( id, line );
|
|
}
|
|
|
|
if ( m2->count() == 0 )
|
|
{
|
|
popup->removeItem( id );
|
|
}
|
|
//kdDebug( 9007 ) << "CppSupportPart::contextMenu 4" << endl;
|
|
}
|
|
|
|
TQString candidate1;
|
|
if ( isHeader( m_activeFileName ) )
|
|
{
|
|
candidate1 = sourceOrHeaderCandidate();
|
|
}
|
|
else
|
|
{
|
|
candidate1 = m_activeFileName;
|
|
}
|
|
//kdDebug( 9007 ) << "CppSupportPart::go to definition in " << candidate1 << endl;
|
|
if ( codeModel() ->hasFile( candidate1 ) )
|
|
{
|
|
TQPopupMenu * m = new TQPopupMenu( popup );
|
|
id = popup->insertItem( i18n( "Go to Definition" ), m );
|
|
popup->setWhatsThis( id, i18n( "<b>Go to definition</b><p>Provides a menu to select available function definitions "
|
|
"in the current file and in the corresponding header (if the current file is an implementation) or source (if the current file is a header) file." ) );
|
|
|
|
const FileDom file = codeModel() ->fileByName( candidate1 );
|
|
const FunctionDefinitionList functionDefinitionList = CodeModelUtils::allFunctionDefinitionsDetailed( file ).functionList;
|
|
for ( FunctionDefinitionList::ConstIterator it = functionDefinitionList.begin(); it != functionDefinitionList.end(); ++it )
|
|
{
|
|
TQString text = ( *it ) ->scope().join( "::" );
|
|
if ( !text.isEmpty() )
|
|
{
|
|
text += "::";
|
|
}
|
|
text += formatModelItem( *it, true );
|
|
text = text.replace( TQString::fromLatin1( "&" ), TQString::fromLatin1( "&&" ) );
|
|
int id = m->insertItem( text, this, TQT_SLOT( gotoLine( int ) ) );
|
|
int line, column;
|
|
( *it ) ->getStartPosition( &line, &column );
|
|
m->setItemParameter( id, line );
|
|
}
|
|
if ( m->count() == 0 )
|
|
{
|
|
popup->removeItem( id );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
const EditorContext *econtext = static_cast<const EditorContext*>( context );
|
|
TQString str = econtext->currentLine();
|
|
if ( str.isEmpty() )
|
|
return ;
|
|
}
|
|
else if ( context->hasType( Context::CodeModelItemContext ) )
|
|
{
|
|
const CodeModelItemContext * mcontext = static_cast<const CodeModelItemContext*>( context );
|
|
|
|
if ( mcontext->item() ->isClass() )
|
|
{
|
|
m_activeClass = ( ClassModel* ) mcontext->item();
|
|
int id = popup->insertItem( i18n( "Extract Interface..." ), this, TQT_SLOT( slotExtractInterface() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Extract interface</b><p>Extracts interface from the selected class and creates a new class with this interface. "
|
|
"No implementation code is extracted and no implementation code is created." ) );
|
|
}
|
|
else if ( mcontext->item() ->isFunction() )
|
|
{
|
|
m_activeFunction = ( FunctionModel* ) mcontext->item();
|
|
}
|
|
}
|
|
else if ( context->hasType( Context::FileContext ) )
|
|
{
|
|
const FileContext * fc = static_cast<const FileContext*>( context );
|
|
//this is a .ui file and only selection contains only one such file
|
|
KURL url = fc->urls().first();
|
|
kdDebug( 9007 ) << "file context with " << url.path() << endl;
|
|
if ( url.fileName().endsWith( ".ui" ) )
|
|
{
|
|
m_contextFileName = url.path();
|
|
int id = popup->insertItem( i18n( "Create or Select Implementation..." ), this, TQT_SLOT( slotCreateSubclass() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Create or select implementation</b><p>Creates or selects a subclass of selected form for use with integrated KDevDesigner." ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TQStringList makeListUnique( const TQStringList& rhs ) {
|
|
TQMap<TQString, bool> map;
|
|
TQStringList ret;
|
|
for( TQStringList::const_iterator it = rhs.begin(); it != rhs.end(); ++it ) {
|
|
if( map.find( *it ) == map.end() ) {
|
|
ret << *it;
|
|
map.insert( *it, true );
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Makes sure that header files come first
|
|
TQStringList CppSupportPart::reorder( const TQStringList &list )
|
|
{
|
|
TQStringList headers, others;
|
|
|
|
TQStringList headerExtensions = TQStringList::split( ",", "h,H,hh,hxx,hpp,tlh" );
|
|
|
|
TQString projectPath = project()->projectDirectory();
|
|
|
|
TQStringList::ConstIterator it;
|
|
for ( it = list.begin(); it != list.end(); ++it )
|
|
{
|
|
TQString filePath = *it;
|
|
// brilliant stuff.. this method is apparently called both with
|
|
// relative and absolute paths..
|
|
if ( !filePath.startsWith("/") )
|
|
{
|
|
filePath = projectPath + "/" + filePath;
|
|
}
|
|
if( !isValidSource( filePath ) ) continue;
|
|
if ( headerExtensions.contains( TQFileInfo( filePath ).extension() ) )
|
|
headers << ( filePath );
|
|
else
|
|
others << ( filePath );
|
|
}
|
|
|
|
return makeListUnique( headers + others );
|
|
}
|
|
|
|
void CppSupportPart::addedFilesToProject( const TQStringList &fileList )
|
|
{
|
|
m_projectFileList = project() ->allFiles();
|
|
TQStringList files = reorder( fileList );
|
|
|
|
for ( TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
|
|
{
|
|
TQString path = *it;
|
|
if (!path.startsWith("/"))
|
|
path = URLUtil::canonicalPath( m_projectDirectory + "/" + ( *it ) );
|
|
|
|
maybeParse( path );
|
|
//emit addedSourceInfo( path );
|
|
}
|
|
m_buildSafeFileSetTimer->start( 500, true );
|
|
}
|
|
|
|
void CppSupportPart::removedFilesFromProject( const TQStringList &fileList )
|
|
{
|
|
m_projectFileList = project() ->allFiles();
|
|
for ( TQStringList::ConstIterator it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
TQString path = URLUtil::canonicalPath( m_projectDirectory + "/" + *it );
|
|
kdDebug( 9007 ) << "=====================> remove file: " << path << endl;
|
|
|
|
removeWithReferences( path );
|
|
m_backgroundParser->removeFile( path );
|
|
}
|
|
m_buildSafeFileSetTimer->start( 500, true );
|
|
}
|
|
|
|
void CppSupportPart::changedFilesInProject( const TQStringList & fileList )
|
|
{
|
|
TQStringList files = reorder( fileList );
|
|
|
|
for ( TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
|
|
{
|
|
TQString path = URLUtil::canonicalPath( m_projectDirectory + "/" + *it );
|
|
|
|
maybeParse( path );
|
|
//emit addedSourceInfo( path );
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::savedFile( const KURL &file )
|
|
{
|
|
if( file.path() == m_activeFileName ) {
|
|
m_isTyping = false;
|
|
m_hadErrors = false;
|
|
maybeParse( file.path() );
|
|
}
|
|
|
|
Q_UNUSED( file.path() );
|
|
|
|
#if 0 // not needed anymore
|
|
|
|
kdDebug( 9007 ) << "savedFile(): " << fileName.mid ( m_projectDirectory.length() + 1 ) << endl;
|
|
|
|
if ( m_projectFileList.contains( fileName.mid ( m_projectDirectory.length() + 1 ) ) )
|
|
{
|
|
maybeParse( fileName );
|
|
emit addedSourceInfo( fileName );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
TQString CppSupportPart::findSourceFile()
|
|
{
|
|
// get the path of the currently active document
|
|
TQFileInfo fi( m_activeFileName );
|
|
TQString path = fi.filePath();
|
|
TQString ext = fi.extension();
|
|
// extract the base path (full path without '.' and extension)
|
|
TQString base = path.left( path.length() - ext.length() - 1 );
|
|
TQStringList candidates;
|
|
if ( TQStringList::split( ',', "h,H,hh,hxx,hpp,tlh" ).contains( ext ) )
|
|
{
|
|
candidates << ( base + ".c" );
|
|
candidates << ( base + ".cc" );
|
|
candidates << ( base + ".cpp" );
|
|
candidates << ( base + ".c++" );
|
|
candidates << ( base + ".cxx" );
|
|
candidates << ( base + ".C" );
|
|
candidates << ( base + ".m" );
|
|
candidates << ( base + ".mm" );
|
|
candidates << ( base + ".M" );
|
|
candidates << ( base + ".inl" );
|
|
candidates << ( base + "_impl.h" );
|
|
}
|
|
|
|
TQStringList::ConstIterator it;
|
|
for ( it = candidates.begin(); it != candidates.end(); ++it )
|
|
{
|
|
kdDebug( 9007 ) << "Trying " << ( *it ) << endl;
|
|
if ( TQFileInfo( *it ).exists() )
|
|
{
|
|
return * it;
|
|
}
|
|
}
|
|
return m_activeFileName;
|
|
}
|
|
|
|
TQString CppSupportPart::sourceOrHeaderCandidate( const KURL &url )
|
|
{
|
|
TQString urlPath;
|
|
if ( url.isEmpty() )
|
|
{
|
|
KTextEditor::Document * doc =
|
|
dynamic_cast<KTextEditor::Document*>( partController() ->activePart() );
|
|
if ( !doc )
|
|
return TQString();
|
|
urlPath = doc->url().path();
|
|
}
|
|
else
|
|
{
|
|
urlPath = url.path();
|
|
}
|
|
// get the path of the currently active document
|
|
TQFileInfo fi( urlPath );
|
|
TQString path = fi.filePath();
|
|
// extract the exension
|
|
TQString ext = fi.extension();
|
|
if ( ext.isEmpty() )
|
|
return TQString();
|
|
// extract the base path (full path without '.' and extension)
|
|
TQString base = path.left( path.length() - ext.length() - 1 );
|
|
//kdDebug( 9007 ) << "base: " << base << ", ext: " << ext << endl;
|
|
// just the filename without the extension
|
|
TQString fileNameWoExt = fi.fileName();
|
|
if ( !ext.isEmpty() )
|
|
fileNameWoExt.replace( "." + ext, "" );
|
|
TQString possibleExts;
|
|
// depending on the current extension assemble a list of
|
|
// candidate files to look for
|
|
TQStringList candidates;
|
|
// special case for template classes created by the new class dialog
|
|
if ( path.endsWith( "_impl.h" ) )
|
|
{
|
|
TQString headerpath = path;
|
|
headerpath.replace( "_impl.h", ".h" );
|
|
candidates << headerpath;
|
|
fileNameWoExt.replace( "_impl", "" );
|
|
possibleExts = "h";
|
|
}
|
|
// if file is a header file search for implementation file
|
|
else if ( TQStringList::split( ',', "h,H,hh,hxx,hpp,tlh" ).contains( ext ) )
|
|
{
|
|
candidates << ( base + ".c" );
|
|
candidates << ( base + ".cc" );
|
|
candidates << ( base + ".cpp" );
|
|
candidates << ( base + ".c++" );
|
|
candidates << ( base + ".cxx" );
|
|
candidates << ( base + ".C" );
|
|
candidates << ( base + ".m" );
|
|
candidates << ( base + ".mm" );
|
|
candidates << ( base + ".M" );
|
|
candidates << ( base + ".inl" );
|
|
candidates << ( base + "_impl.h" );
|
|
possibleExts = "c,cc,cpp,c++,cxx,C,m,mm,M,inl,_impl.h";
|
|
}
|
|
// if file is an implementation file, search for header file
|
|
else if ( TQStringList::split( ',', "c,cc,cpp,c++,cxx,C,m,mm,M,inl" ).contains( ext ) )
|
|
{
|
|
candidates << ( base + ".h" );
|
|
candidates << ( base + ".H" );
|
|
candidates << ( base + ".hh" );
|
|
candidates << ( base + ".hxx" );
|
|
candidates << ( base + ".hpp" );
|
|
candidates << ( base + ".tlh" );
|
|
possibleExts = "h,H,hh,hxx,hpp,tlh";
|
|
}
|
|
// search for files from the assembled candidate lists, return the first
|
|
// candidate file that actually exists or TQString() if nothing is found.
|
|
TQStringList::ConstIterator it;
|
|
for ( it = candidates.begin(); it != candidates.end(); ++it )
|
|
{
|
|
//kdDebug( 9007 ) << "Trying " << ( *it ) << endl;
|
|
if ( TQFileInfo( *it ).exists() )
|
|
{
|
|
kdDebug( 9007 ) << "using: " << *it << endl;
|
|
return * it;
|
|
}
|
|
}
|
|
//kdDebug( 9007 ) << "Now searching in project files." << endl;
|
|
// Our last resort: search the project file list for matching files
|
|
TQStringList::iterator fileIt;
|
|
TQFileInfo candidateFileWoExt;
|
|
TQString candidateFileWoExtString;
|
|
TQStringList possibleExtsList = TQStringList::split( ',', possibleExts );
|
|
for ( fileIt = m_projectFileList.begin(); fileIt != m_projectFileList.end(); ++fileIt )
|
|
{
|
|
candidateFileWoExt.setFile(*fileIt);
|
|
//kdDebug( 9007 ) << "candidate file: " << *fileIt << endl;
|
|
if( !candidateFileWoExt.extension().isEmpty() )
|
|
candidateFileWoExtString = candidateFileWoExt.fileName().replace( "." + candidateFileWoExt.extension(), "" );
|
|
if ( candidateFileWoExtString == fileNameWoExt )
|
|
{
|
|
if ( possibleExtsList.contains( candidateFileWoExt.extension() ) || candidateFileWoExt.extension().isEmpty() )
|
|
{
|
|
//kdDebug( 9007 ) << "checking if " << *fileIt << " exists" << endl;
|
|
if ( TQFileInfo( *fileIt ).exists() )
|
|
kdDebug( 9007 ) << "using: " << *fileIt << endl;
|
|
return *fileIt;
|
|
}
|
|
}
|
|
}
|
|
return TQString();
|
|
}
|
|
|
|
void CppSupportPart::slotSaveMemory() {
|
|
if( m_backgroundParser ) {
|
|
///This is done so the caches are completely empty after tdevelop was idle for some time(else it would be waste of memory). The background-parsers internal lexer-cache-manager just cares about keeping the count of cached files under a specific count, but doesn't decrease that count when tdevelop is idle.
|
|
m_backgroundParser->lock();
|
|
m_backgroundParser->saveMemory();
|
|
m_backgroundParser->unlock();
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::slotSwitchHeader( bool scrollOnly )
|
|
{
|
|
bool attemptMatch = true;
|
|
TDEConfig *config = CppSupportFactory::instance() ->config();
|
|
if ( config )
|
|
{
|
|
config->setGroup( "General" );
|
|
attemptMatch = config->readBoolEntry( "SwitchShouldMatch", true );
|
|
}
|
|
|
|
// ok, both files exist. Do the codemodel have them?
|
|
if ( codeModel() ->hasFile( m_activeFileName ) && m_activeViewCursor && attemptMatch )
|
|
{
|
|
unsigned int currentline, column;
|
|
m_activeViewCursor->cursorPositionReal( ¤tline, &column );
|
|
|
|
if ( switchHeaderImpl( m_activeFileName, currentline, column, scrollOnly ) )
|
|
return;
|
|
}
|
|
|
|
// last chance
|
|
KURL url;
|
|
url.setPath( sourceOrHeaderCandidate() );
|
|
|
|
if ( scrollOnly )
|
|
return;
|
|
else if ( !splitHeaderSourceConfig()->splitEnabled() )
|
|
partController() ->editDocument( url );
|
|
else
|
|
partController() ->splitCurrentDocument( url );
|
|
}
|
|
|
|
bool CppSupportPart::switchHeaderImpl( const TQString& file, int line, int col, bool scrollOnly )
|
|
{
|
|
bool handled = false;
|
|
|
|
FunctionDom d;
|
|
FileDom fd = codeModel() ->fileByName( file );
|
|
if ( fd ) {
|
|
CodeModelUtils::CodeModelHelper h( codeModel(), fd );
|
|
d = h.functionAt( line, col );
|
|
}
|
|
if ( d ) {
|
|
if( d->isFunctionDefinition() ) {
|
|
FunctionDom decl = findFunction( d );
|
|
if ( decl ) {
|
|
if ( (void*)&decl != (void*)d.data() && ( !scrollOnly || decl->fileName() != file ) ) {
|
|
jumpToCodeModelItem( model_cast<ItemDom>(decl), scrollOnly );
|
|
handled = true;
|
|
}
|
|
}
|
|
} else {
|
|
FunctionDom def = findFunctionDefinition( d );
|
|
if ( def ) {
|
|
if ( def != d && ( !scrollOnly || def->fileName() != file ) ) {
|
|
jumpToCodeModelItem( model_cast<ItemDom>(def), scrollOnly );
|
|
handled = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
FunctionDom CppSupportPart::findFunction( const FunctionDom& def )
|
|
{
|
|
// We have a definition so we're looking for a declaration. The declaration will either be the child of a namespace node (non class members)
|
|
// or the child of a class node (class member). Search recursively until we find a declaration that matches.
|
|
FunctionDom bestMatch;
|
|
FunctionDom decl = findFunctionInNamespace( codeModel()->globalNamespace(), def, codeModel()->globalNamespace()->namespaceImports(),
|
|
sourceOrHeaderCandidate( def->fileName() ), 0, bestMatch );
|
|
return decl ? decl : bestMatch;
|
|
}
|
|
|
|
FunctionDom CppSupportPart::findFunctionInNamespace( const NamespaceDom& ns, const FunctionDom& def, const std::set<NamespaceImportModel>& nsImports,
|
|
const TQString& candidateFile, int scopeIndex, FunctionDom& bestMatch )
|
|
{
|
|
FunctionDom d;
|
|
TQStringList scope = def->scope();
|
|
if ( !(scopeIndex >= (signed) scope.size()) ) {
|
|
NamespaceDom ns_next = ns->namespaceByName( scope[ scopeIndex ] );
|
|
if ( ns_next ) {
|
|
d = findFunctionInNamespace( ns_next, def, ns_next->namespaceImports(), candidateFile, scopeIndex+1, bestMatch );
|
|
}
|
|
if ( !d ) {
|
|
for ( std::set<NamespaceImportModel>::const_iterator it_ns = nsImports.begin(); it_ns != nsImports.end(); ++it_ns ) {
|
|
if ( (*it_ns).fileName().str() == def->fileName() ) {
|
|
ns_next = ns->namespaceByName( (*it_ns).name() );
|
|
if ( ns_next ) {
|
|
if ( d = findFunctionInNamespace( ns_next, def, nsImports, candidateFile, scopeIndex, bestMatch ) ) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( !d ) {
|
|
ClassList classList = ns->classByName( scope[ scopeIndex ] );
|
|
for ( ClassList::ConstIterator it_cs = classList.begin(); it_cs != classList.end(); ) {
|
|
if ( d = findFunctionInClass( *(it_cs++), def, nsImports, candidateFile, scopeIndex+1, bestMatch ) ) break;
|
|
}
|
|
}
|
|
}
|
|
if ( !d ) {
|
|
FunctionList functionList = ns->functionByName( def->name() );
|
|
for ( FunctionList::ConstIterator it_decl = functionList.begin(); it_decl != functionList.end(); ++it_decl ) {
|
|
if ( CodeModelUtils::compareDeclarationToDefinition( *it_decl, (FunctionDefinitionModel*) def.data(), nsImports ) ) {
|
|
ParsedFile* p = dynamic_cast<ParsedFile*>( def->file()->parseResult().data() );
|
|
if ( p ) {
|
|
if ( p->includeFiles()[ (*it_decl)->fileName() ] ) {
|
|
d = *it_decl;
|
|
break;
|
|
} else if ( (*it_decl)->fileName() == candidateFile ) {
|
|
d = *it_decl;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bestMatch ) {
|
|
bestMatch = *it_decl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
FunctionDom CppSupportPart::findFunctionInClass( const ClassDom& cs, const FunctionDom& def, const std::set<NamespaceImportModel>& nsImports,
|
|
const TQString& candidateFile, int scopeIndex, FunctionDom& bestMatch )
|
|
{
|
|
FunctionDom d;
|
|
TQStringList scope = def->scope();
|
|
if ( !(scopeIndex >= (signed) scope.size()) ) {
|
|
ClassList classList = cs->classByName( scope[ scopeIndex ] );
|
|
for ( ClassList::ConstIterator it_cs = classList.begin(); it_cs != classList.end(); ) {
|
|
if ( d = findFunctionInClass( *(it_cs++), def, nsImports, candidateFile, scopeIndex+1, bestMatch ) ) break;
|
|
}
|
|
}
|
|
if ( !d ) {
|
|
FunctionList functionList = cs->functionByName( def->name() );
|
|
for ( FunctionList::ConstIterator it_decl = functionList.begin(); it_decl != functionList.end(); ++it_decl ) {
|
|
if ( CodeModelUtils::compareDeclarationToDefinition( *it_decl, (FunctionDefinitionModel*) def.data(), nsImports ) ) {
|
|
ParsedFile* p = dynamic_cast<ParsedFile*>( def->file()->parseResult().data() );
|
|
if ( p ) {
|
|
if ( p->includeFiles()[ (*it_decl)->fileName() ] ) {
|
|
d = *it_decl;
|
|
break;
|
|
} else if ( (*it_decl)->fileName() == candidateFile ) {
|
|
d = *it_decl;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bestMatch ) {
|
|
bestMatch = *it_decl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
FunctionDom CppSupportPart::findFunctionDefinition( const FunctionDom& decl )
|
|
{
|
|
// We have a declaration so we're looking for a definition. The definition will be the child of some namespace node (never a class node).
|
|
// Since the definition can be the child of any namespace in its scope depending on syntax, we have to check every one.
|
|
FunctionDom def, bestMatch;
|
|
NamespaceDom ns = codeModel()->globalNamespace();
|
|
TQString candidateFile = sourceOrHeaderCandidate( decl->fileName() );
|
|
FunctionDefinitionList functionList = ns->functionDefinitionByName( decl->name() );
|
|
for ( FunctionDefinitionList::ConstIterator it_def = functionList.begin(); it_def != functionList.end() && !def; ++it_def ) {
|
|
if ( CodeModelUtils::compareDeclarationToDefinition( decl, *it_def, ns->namespaceImports() ) ) {
|
|
ParsedFile* p = dynamic_cast<ParsedFile*>( (*it_def)->file()->parseResult().data() );
|
|
if ( p ) {
|
|
if ( p->includeFiles()[ decl->fileName() ] ) {
|
|
def = *it_def;
|
|
} else if ( (*it_def)->fileName() == candidateFile ) {
|
|
def = *it_def;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bestMatch ) {
|
|
bestMatch = *it_def;
|
|
}
|
|
}
|
|
}
|
|
TQStringList scope = decl->scope();
|
|
for ( TQStringList::ConstIterator it_scope = scope.begin(); it_scope != scope.end() && !def; ++it_scope ) {
|
|
NamespaceDom ns_next = ns->namespaceByName( *it_scope );
|
|
if ( ns_next ) {
|
|
ns = ns_next;
|
|
FunctionDefinitionList functionList = ns->functionDefinitionByName( decl->name() );
|
|
for ( FunctionDefinitionList::ConstIterator it_def = functionList.begin(); it_def != functionList.end() && !def; ++it_def ) {
|
|
if ( CodeModelUtils::compareDeclarationToDefinition( decl, *it_def, ns->namespaceImports() ) ) {
|
|
ParsedFile* p = dynamic_cast<ParsedFile*>( (*it_def)->file()->parseResult().data() );
|
|
if ( p ) {
|
|
if ( p->includeFiles()[ decl->fileName() ] ) {
|
|
def = *it_def;
|
|
} else if ( (*it_def)->fileName() == candidateFile ) {
|
|
def = *it_def;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bestMatch ) {
|
|
bestMatch = *it_def;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return def ? def : bestMatch;
|
|
}
|
|
|
|
void CppSupportPart::jumpToCodeModelItem( const ItemDom& item, bool scrollOnly )
|
|
{
|
|
static KURL lastSyncedUrl;
|
|
static int lastSyncedLine = -1;
|
|
|
|
int line, col;
|
|
item->getStartPosition( &line, &col );
|
|
|
|
KURL url( item->fileName() );
|
|
|
|
if ( scrollOnly ) {
|
|
KParts::ReadOnlyPart* part = partController()->partForURL( url );
|
|
int currentLine = lastSyncedLine;
|
|
if ( part ) {
|
|
KTextEditor::ViewCursorInterface *iface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
|
|
if( iface )
|
|
iface->cursorPosition( (uint*) ¤tLine, (uint*) &col );
|
|
}
|
|
partController() ->scrollToLineColumn( url, line, -1, lastSyncedLine != currentLine || lastSyncedUrl != url );
|
|
} else if ( !splitHeaderSourceConfig()->splitEnabled() )
|
|
partController() ->editDocument( url, line );
|
|
else
|
|
partController() ->splitCurrentDocument( url, line );
|
|
lastSyncedLine = line;
|
|
lastSyncedUrl = url;
|
|
}
|
|
|
|
KDevLanguageSupport::Features CppSupportPart::features()
|
|
{
|
|
if ( withcpp )
|
|
return Features( Classes | Structs | Functions | Variables | Namespaces | Declarations
|
|
| Signals | Slots | AddMethod | AddAttribute | NewClass | CreateAccessMethods );
|
|
else
|
|
return Features ( Structs | Functions | Variables | Declarations );
|
|
}
|
|
|
|
TQString CppSupportPart::formatClassName( const TQString &name )
|
|
{
|
|
TQString n = name;
|
|
return n.replace( ".", "::" );
|
|
}
|
|
|
|
TQString CppSupportPart::unformatClassName( const TQString &name )
|
|
{
|
|
TQString n = name;
|
|
return n.replace( "::", "." );
|
|
}
|
|
|
|
bool CppSupportPart::shouldSplitDocument(const KURL &url)
|
|
{
|
|
if ( !splitHeaderSourceConfig()->splitEnabled() )
|
|
return false;
|
|
|
|
KURL::List list = partController()->openURLs();
|
|
KURL::List::ConstIterator it = list.begin();
|
|
while ( it != list.end() )
|
|
{
|
|
TQString candidate = sourceOrHeaderCandidate( ( *it ) );
|
|
if ( candidate.isEmpty() )
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
KURL urlCandidate;
|
|
urlCandidate.setPath( candidate );
|
|
if ( url == urlCandidate )
|
|
{
|
|
// It is already open, so switch to it so
|
|
// our split view will open with it
|
|
partController() ->editDocument( ( *it ) );
|
|
return true;
|
|
}
|
|
++it;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Qt::Orientation CppSupportPart::splitOrientation() const
|
|
{
|
|
TQString o = splitHeaderSourceConfig()->orientation();
|
|
if ( o == "Vertical" )
|
|
return Qt::Vertical;
|
|
else
|
|
return Qt::Horizontal;
|
|
}
|
|
|
|
void CppSupportPart::slotNewClass()
|
|
{
|
|
CppNewClassDialog dlg( this );
|
|
dlg.exec();
|
|
}
|
|
|
|
void CppSupportPart::addMethod( ClassDom klass )
|
|
{
|
|
if ( !klass )
|
|
{
|
|
KMessageBox::error( 0, i18n( "Please select a class." ), i18n( "Error" ) );
|
|
return ;
|
|
}
|
|
|
|
AddMethodDialog dlg( this, klass, mainWindow() ->main() );
|
|
dlg.exec();
|
|
}
|
|
|
|
void CppSupportPart::addAttribute( ClassDom klass )
|
|
{
|
|
if ( !klass )
|
|
{
|
|
KMessageBox::error( 0, i18n( "Please select a class." ), i18n( "Error" ) );
|
|
return ;
|
|
}
|
|
|
|
AddAttributeDialog dlg( this, klass, mainWindow() ->main() );
|
|
dlg.exec();
|
|
}
|
|
|
|
void CppSupportPart::slotCompleteText()
|
|
{
|
|
if ( !m_pCompletion )
|
|
return ;
|
|
m_pCompletion->completeText( true );
|
|
}
|
|
|
|
/**
|
|
* parsing stuff for project persistent classstore and code completion
|
|
*/
|
|
void CppSupportPart::initialParse( )
|
|
{
|
|
// For debugging
|
|
if ( !project( ) )
|
|
{
|
|
// messagebox ?
|
|
kdDebug( 9007 ) << "No project" << endl;
|
|
return ;
|
|
}
|
|
|
|
parseProject( );
|
|
m_valid = true;
|
|
return ;
|
|
}
|
|
|
|
bool CppSupportPart::parseProject( bool force )
|
|
{
|
|
if( _jd )
|
|
delete _jd->progressBar; ///Make sure the progress-bar is open
|
|
|
|
mainWindow() ->statusBar() ->message( i18n( "Updating..." ) );
|
|
|
|
kapp->setOverrideCursor( waitCursor );
|
|
|
|
_jd = new JobData;
|
|
if( TQFileInfo( project() ->projectDirectory() + "/" + project()->projectName().lower()
|
|
+ ".kdevelop.pcs" ).exists())
|
|
{
|
|
TQDir d( project() ->projectDirectory());
|
|
d.rename(project() ->projectName().lower() + ".kdevelop.pcs",
|
|
project() ->projectName() +".kdevelop.pcs");
|
|
}
|
|
_jd->file.setName( project() ->projectDirectory() + "/" + project()->projectName()
|
|
+ ".kdevelop.pcs" );
|
|
|
|
TQString skip_file_name = project() ->projectDirectory() + "/" +
|
|
project() ->projectName() + ".kdevelop.ignore_pcs";
|
|
TQString skip_lower_file_name = project() ->projectDirectory() + "/" +
|
|
project() ->projectName().lower() + ".kdevelop.ignore_pcs";
|
|
|
|
if ( !force && !TQFile::exists( skip_file_name ) &&
|
|
!TQFile::exists( skip_lower_file_name ) && _jd->file.open( IO_ReadOnly ) )
|
|
{
|
|
_jd->stream.setDevice( &( _jd->file ) );
|
|
|
|
createIgnorePCSFile();
|
|
|
|
TQString sig;
|
|
int pcs_version = 0;
|
|
_jd->stream >> sig >> pcs_version;
|
|
if ( sig == "PCS" && pcs_version == KDEV_PCS_VERSION )
|
|
{
|
|
|
|
int numFiles = 0;
|
|
_jd->stream >> numFiles;
|
|
kdDebug( 9007 ) << "Read " << numFiles << " files from pcs" << endl;
|
|
|
|
for ( int i = 0; i < numFiles; ++i )
|
|
{
|
|
TQString fn;
|
|
uint ts;
|
|
uint offset;
|
|
|
|
_jd->stream >> fn >> ts >> offset;
|
|
_jd->pcs[ fn ] = tqMakePair( ts, offset );
|
|
}
|
|
}
|
|
}
|
|
|
|
_jd->files = reorder( modifiedFileList() );
|
|
|
|
TQProgressBar* bar = new TQProgressBar( _jd->files.count( ), mainWindow( ) ->statusBar( ) );
|
|
bar->setMinimumWidth( 120 );
|
|
bar->setCenterIndicator( true );
|
|
mainWindow( ) ->statusBar( ) ->addWidget( bar );
|
|
bar->show( );
|
|
|
|
_jd->progressBar = bar;
|
|
_jd->dir.setPath( m_projectDirectory );
|
|
_jd->it = _jd->files.begin();
|
|
_jd->reparseList = TQStringList();
|
|
_jd->backgroundCount = 0;
|
|
_jd->cycle = 0;
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) );
|
|
|
|
m_saveMemoryTimer->stop(); //Do not regularly remove cached files that may still be needed while parsing(the cache anyway be full for the whole parsing-process)
|
|
return true;
|
|
}
|
|
|
|
void CppSupportPart::slotParseFiles()
|
|
{
|
|
// NOTE: The checking for m_projectClosed is actually (currently) not needed.
|
|
// When the project is closed, the language support plugin is destroyed
|
|
// and as a consequence, the timer job signal never arrives at this method
|
|
|
|
if ( !_jd ) return; // how can this possibly happen?!
|
|
|
|
if ( _jd->cycle == 0 && !m_projectClosed && _jd->it != _jd->files.end() )
|
|
{
|
|
_jd->progressBar->setProgress( _jd->progressBar->progress() + 1 );
|
|
|
|
TQFileInfo fileInfo( _jd->dir, *( _jd->it ) );
|
|
|
|
if ( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() )
|
|
{
|
|
TQString absFilePath = URLUtil::canonicalPath( fileInfo.absFilePath() );
|
|
|
|
if ( isValidSource( absFilePath ) )
|
|
{
|
|
TQDateTime t = fileInfo.lastModified();
|
|
|
|
if ( ! ( m_timestamp.contains( absFilePath ) && m_timestamp[ absFilePath ] == t ) )
|
|
{
|
|
if ( _jd->pcs.contains( absFilePath ) )
|
|
{
|
|
_jd->stream.device() ->at( _jd->pcs[ absFilePath ].second );
|
|
FileDom file = codeModel() ->create<FileModel>();
|
|
file->read( _jd->stream );
|
|
codeModel() ->addFile( file );
|
|
|
|
if( t.toTime_t() != _jd->pcs[ absFilePath ].first ) {
|
|
///The FileDom had to be created first, so the dependencies are known
|
|
_jd->reparseList << file->name();
|
|
/* kdDebug( 9007 ) << "File timestamp: " << ": " << t.toTime_t() << endl;
|
|
kdDebug( 9007 ) << "Stored timestamp: " << ": " << _jd->pcs[ absFilePath ].first << endl;*/
|
|
} else {
|
|
m_timestamp[ absFilePath ] = t;
|
|
/* kdDebug( 9007 ) << "timestamp ok" << endl;*/
|
|
}
|
|
} else {
|
|
_jd->reparseList << absFilePath;
|
|
/* kdDebug( 9007 ) << absFilePath << " put into reparse-list" << endl;
|
|
*/ }
|
|
} else {
|
|
/* kdDebug( 9007 ) << absFilePath << " is already in code-model" << endl;*/
|
|
}
|
|
}
|
|
}
|
|
|
|
++( _jd->it );
|
|
TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) );
|
|
|
|
if( _jd->it == _jd->files.end()) {
|
|
if( _jd->reparseList.isEmpty() ) {
|
|
_jd->backgroundCount = 0;
|
|
} else {
|
|
if( alwaysParseInBackground ) {
|
|
_jd->backgroundCount = parseFilesAndDependencies( _jd->reparseList, true, false, true );
|
|
} else {
|
|
_jd->reparseList = reorder( _jd->reparseList );
|
|
_jd->it = _jd->reparseList.begin();
|
|
_jd->backgroundCount = _jd->reparseList.count();
|
|
}
|
|
_jd->progressBar->setProgress( 0 ); ///restart progress-bar for reparsing
|
|
_jd->progressBar->setTotalSteps( _jd->backgroundCount );
|
|
}
|
|
|
|
_jd->lastBackgroundState = -1;
|
|
_jd->backgroundState = 0;
|
|
_jd->cycle = 1;
|
|
_jd->lastParse = TQTime::currentTime();
|
|
kapp->restoreOverrideCursor( );
|
|
}
|
|
}
|
|
else // finished or interrupted
|
|
{
|
|
if( _jd->backgroundCount <= _jd->backgroundState || m_projectClosed ) {
|
|
mainWindow( ) ->statusBar( ) ->removeWidget( _jd->progressBar );
|
|
|
|
if ( !m_projectClosed )
|
|
{
|
|
kdDebug( 9007 ) << "updating sourceinfo" << endl;
|
|
kapp->restoreOverrideCursor( );
|
|
emit updatedSourceInfo();
|
|
mainWindow( ) ->statusBar( ) ->message( i18n( "Done" ), 2000 );
|
|
TQFile::remove( project() ->projectDirectory()
|
|
+ "/" + project() ->projectName()
|
|
+ ".kdevelop.ignore_pcs" );
|
|
TQFile::remove( project() ->projectDirectory()
|
|
+ "/" + project() ->projectName().lower()
|
|
+ ".kdevelop.ignore_pcs" );
|
|
|
|
}
|
|
else
|
|
{
|
|
kdDebug( 9007 ) << "ABORT" << endl;
|
|
}
|
|
|
|
delete _jd;
|
|
_jd = 0;
|
|
m_saveMemoryTimer->start( 240000, false );
|
|
} else {
|
|
_jd->progressBar->setProgress( _jd->backgroundState ); ///restart
|
|
_jd->progressBar->setTotalSteps( _jd->backgroundCount );
|
|
if( _jd->lastParse.msecsTo( TQTime::currentTime()) > 60000 && !m_backgroundParser->filesInQueue()) {
|
|
_jd->backgroundCount = _jd->backgroundState; ///Stop waiting if there is no progress and no file in the background-parser
|
|
TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) );
|
|
} else {
|
|
int timeStep = 0;
|
|
if( alwaysParseInBackground ) {
|
|
TQTimer::singleShot( 10, this, TQT_SLOT( slotParseFiles() ) );
|
|
} else {
|
|
if( _jd->it == _jd->reparseList.end() ) {
|
|
/*_jd->it = _jd->files.end();
|
|
_jd->backgroundCount = _jd->backgroundState; ///finish processing*/
|
|
timeStep = 1;
|
|
} else {
|
|
/*///Parse the files one by one
|
|
if( _jd->lastParse.msecsTo( TQTime::currentTime()) > 100 || _jd->backgroundState != _jd->lastBackgroundState ) {*/
|
|
maybeParse( *_jd->it, false );
|
|
++(_jd->it);
|
|
_jd->lastBackgroundState = _jd->backgroundState;
|
|
/*}else{
|
|
timeStep = 1;
|
|
}*/
|
|
}
|
|
TQTimer::singleShot( timeStep, this, TQT_SLOT( slotParseFiles() ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::maybeParse( const TQString& fn, bool background )
|
|
{
|
|
if ( !isValidSource( fn ) )
|
|
return ;
|
|
|
|
TQFileInfo fileInfo( fn );
|
|
TQString path = URLUtil::canonicalPath( fn );
|
|
TQDateTime t = fileInfo.lastModified();
|
|
|
|
if ( !fileInfo.exists() )
|
|
return;
|
|
|
|
TQMap<TQString, TQDateTime>::Iterator it = m_timestamp.find( path );
|
|
if ( codeModel()->hasFile( fn ) && it != m_timestamp.end() && *it == t )
|
|
return;
|
|
|
|
TQStringList l;
|
|
l << fn;
|
|
parseFilesAndDependencies( l, background );
|
|
}
|
|
|
|
bool CppSupportPart::isQueued( const TQString& file ) const {
|
|
//int c = m_backgroundParser->countInQueue( file );
|
|
//if( c == 0 ) return false;
|
|
return m_parseEmitWaiting.waiting( file, ParseEmitWaiting::Silent, 2 ); //Since it may be possible that the background-parser is currently parsing the file(in an obselete state), it is allowed to have the file in the queue twice.
|
|
}
|
|
|
|
void CppSupportPart::slotNeedTextHint( int line, int column, TQString& textHint )
|
|
{
|
|
if ( 1 || !m_activeEditor )
|
|
return ;
|
|
|
|
m_backgroundParser->lock();
|
|
TranslationUnitAST* ast = *m_backgroundParser->translationUnit( m_activeFileName );
|
|
AST* node = 0;
|
|
if ( ast && ( node = findNodeAt( ast, line, column ) ) )
|
|
{
|
|
|
|
while ( node && node->nodeType() != NodeType_FunctionDefinition )
|
|
node = node->parent();
|
|
|
|
if ( node )
|
|
{
|
|
int startLine, startColumn;
|
|
int endLine, endColumn;
|
|
node->getStartPosition( &startLine, &startColumn );
|
|
node->getEndPosition( &endLine, &endColumn );
|
|
|
|
if ( !node->text().isNull() )
|
|
textHint = node->text();
|
|
else
|
|
textHint = m_activeEditor->textLine( startLine ).simplifyWhiteSpace();
|
|
}
|
|
}
|
|
m_backgroundParser->unlock();
|
|
}
|
|
|
|
void CppSupportPart::MakeMemberHelper( TQString& text, int& atLine, int& atColumn )
|
|
{
|
|
if ( !m_activeViewCursor || !m_valid )
|
|
return ;
|
|
|
|
atLine = -2;
|
|
atColumn = 0;
|
|
|
|
TQString implFile = findSourceFile();
|
|
|
|
m_backgroundParser->lock();
|
|
TranslationUnitAST* translationUnit = *m_backgroundParser->translationUnit( m_activeFileName );
|
|
if ( translationUnit )
|
|
{
|
|
bool fail = false;
|
|
unsigned int line, column;
|
|
m_activeViewCursor->cursorPositionReal( &line, &column );
|
|
|
|
AST* currentNode = findNodeAt( translationUnit, line, column );
|
|
DeclaratorAST* declarator = 0;
|
|
while ( currentNode && currentNode->nodeType() != NodeType_SimpleDeclaration )
|
|
{
|
|
if ( currentNode->nodeType() == NodeType_Declarator )
|
|
declarator = ( DeclaratorAST* ) currentNode;
|
|
currentNode = currentNode->parent();
|
|
}
|
|
SimpleDeclarationAST* decl = currentNode ? ( SimpleDeclarationAST* ) currentNode : 0;
|
|
|
|
if ( decl && decl->storageSpecifier() && decl->storageSpecifier()->text().contains("friend") )
|
|
{
|
|
kdDebug(9007) << "this is a friend declaration, don't create any definition" << endl;
|
|
fail = true;
|
|
}
|
|
|
|
if ( !fail && decl && decl->initDeclaratorList() && !declarator )
|
|
{
|
|
InitDeclaratorAST * i = decl->initDeclaratorList() ->initDeclaratorList().at( 0 );
|
|
if ( i )
|
|
declarator = i->declarator();
|
|
}
|
|
|
|
if ( !fail && decl && declarator && declarator->parameterDeclarationClause() )
|
|
{
|
|
|
|
TQStringList scope;
|
|
scopeOfNode( decl, scope );
|
|
|
|
TQString scopeStr = scope.join( "::" );
|
|
if ( !scopeStr.isEmpty() )
|
|
scopeStr += "::";
|
|
|
|
TQString declStr = declaratorToString( declarator, scopeStr ).simplifyWhiteSpace();
|
|
if ( declarator->exceptionSpecification() )
|
|
{
|
|
declStr += TQString::fromLatin1( " throw( " );
|
|
TQPtrList<AST> l = declarator->exceptionSpecification() ->nodeList();
|
|
TQPtrListIterator<AST> type_it( l );
|
|
while ( type_it.current() )
|
|
{
|
|
declStr += type_it.current() ->text();
|
|
++type_it;
|
|
|
|
if ( type_it.current() )
|
|
declStr += TQString::fromLatin1( ", " );
|
|
}
|
|
|
|
declStr += TQString::fromLatin1( " )" );
|
|
}
|
|
|
|
text += "\n\n";
|
|
TQString type = typeSpecToString( decl->typeSpec() );
|
|
text += type;
|
|
if ( !type.isNull() )
|
|
text += + " ";
|
|
|
|
text += declStr + "\n{\n}";
|
|
}
|
|
|
|
if ( !fail )
|
|
{
|
|
translationUnit = *m_backgroundParser->translationUnit( implFile );
|
|
if ( translationUnit )
|
|
translationUnit->getEndPosition( &atLine, &atColumn );
|
|
}
|
|
|
|
kdDebug( 9007 ) << "at line in mm: " << atLine << endl;
|
|
}
|
|
m_backgroundParser->unlock();
|
|
}
|
|
|
|
void CppSupportPart::slotMakeMember()
|
|
{
|
|
TQString text;
|
|
int atColumn, atLine;
|
|
MakeMemberHelper( text, atLine, atColumn );
|
|
|
|
if ( !text.isEmpty() )
|
|
{
|
|
TQString implFile = findSourceFile();
|
|
|
|
if ( !implFile.isEmpty() )
|
|
{
|
|
partController() ->editDocument( KURL( implFile ) );
|
|
kapp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput | TQEventLoop::ExcludeSocketNotifiers, 500 );
|
|
}
|
|
if ( atLine == -2 )
|
|
atLine = m_activeEditor->numLines() - 1;
|
|
|
|
m_backgroundParser->lock ()
|
|
;
|
|
|
|
kdDebug( 9007 ) << "at line in mm: " << atLine << " atCol: " << atColumn << endl;
|
|
kdDebug( 9007 ) << "text: " << text << endl;
|
|
if ( m_activeEditor )
|
|
m_activeEditor->insertText( atLine, atColumn, text );
|
|
if ( m_activeViewCursor )
|
|
m_activeViewCursor->setCursorPositionReal( atLine + 3, 1 );
|
|
|
|
m_backgroundParser->unlock();
|
|
}
|
|
}
|
|
|
|
TQStringList CppSupportPart::subclassWidget( const TQString& formName )
|
|
{
|
|
TQStringList newFileNames;
|
|
SubclassingDlg *dlg = new SubclassingDlg( this, formName, newFileNames );
|
|
dlg->exec();
|
|
return newFileNames;
|
|
}
|
|
|
|
TQStringList CppSupportPart::updateWidget( const TQString& formName, const TQString& fileName )
|
|
{
|
|
TQStringList dummy;
|
|
SubclassingDlg *dlg = new SubclassingDlg( this, formName, fileName, dummy );
|
|
dlg->exec();
|
|
return dummy;
|
|
}
|
|
|
|
void CppSupportPart::partRemoved( KParts::Part* part )
|
|
{
|
|
kdDebug( 9032 ) << "CppSupportPart::partRemoved()" << endl;
|
|
|
|
if ( KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( part ) )
|
|
{
|
|
|
|
TQString fileName = doc->url().path();
|
|
if ( !isValidSource( fileName ) )
|
|
return ;
|
|
|
|
TQString canonicalFileName = URLUtil::canonicalPath( fileName );
|
|
m_backgroundParser->removeFile( canonicalFileName );
|
|
m_backgroundParser->addFile( canonicalFileName, true );
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::slotProjectCompiled()
|
|
{
|
|
kdDebug( 9007 ) << "CppSupportPart::slotProjectCompiled()" << endl;
|
|
parseProject();
|
|
}
|
|
|
|
TQStringList CppSupportPart::modifiedFileList()
|
|
{
|
|
TQStringList lst;
|
|
|
|
TQStringList fileList = m_projectFileList;
|
|
TQStringList::Iterator it = fileList.begin();
|
|
while ( it != fileList.end() )
|
|
{
|
|
TQString fileName = *it;
|
|
++it;
|
|
|
|
TQFileInfo fileInfo( m_projectDirectory, fileName );
|
|
TQString path = URLUtil::canonicalPath( fileInfo.absFilePath() );
|
|
|
|
if ( !( isSource( path ) || isHeader( path ) ) )
|
|
continue;
|
|
|
|
TQDateTime t = fileInfo.lastModified();
|
|
|
|
TQMap<TQString, TQDateTime>::Iterator dictIt = m_timestamp.find( path );
|
|
if ( fileInfo.exists() && dictIt != m_timestamp.end() && *dictIt == t )
|
|
continue;
|
|
|
|
lst << fileName;
|
|
}
|
|
|
|
return lst;
|
|
}
|
|
|
|
KTextEditor::Document * CppSupportPart::findDocument( const KURL & url )
|
|
{
|
|
if ( !partController() ->parts() )
|
|
return 0;
|
|
|
|
TQPtrList<KParts::Part> parts( *partController() ->parts() );
|
|
TQPtrListIterator<KParts::Part> it( parts );
|
|
while ( KParts::Part * part = it.current() )
|
|
{
|
|
KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( part );
|
|
if ( doc && doc->url() == url )
|
|
return doc;
|
|
++it;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CppSupportPart::setupCatalog( )
|
|
{
|
|
kdDebug( 9007 ) << "CppSupportPart::setupCatalog()" << endl;
|
|
|
|
TDEStandardDirs *dirs = CppSupportFactory::instance() ->dirs();
|
|
TQStringList pcsList = dirs->findAllResources( "pcs", "*.db", false, true );
|
|
TQStringList pcsIdxList = dirs->findAllResources( "pcs", "*.idx", false, true );
|
|
|
|
TQStringList enabledPCSs;
|
|
if ( DomUtil::elementByPath( *project() ->projectDom(), "kdevcppsupport/references" ).isNull() )
|
|
{
|
|
for ( TQStringList::Iterator it = pcsList.begin(); it != pcsList.end(); ++it )
|
|
{
|
|
kdDebug( 9007 ) << "CppSupportPart::setupCatalog()1 " << *it << endl;
|
|
enabledPCSs.push_back( TQFileInfo( *it ).baseName(true) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
enabledPCSs = DomUtil::readListEntry( *project() ->projectDom(), "kdevcppsupport/references", "pcs" );
|
|
}
|
|
|
|
TQStringList indexList = TQStringList() << "kind" << "name" << "scope" << "fileName" << "prefix";
|
|
|
|
if ( pcsList.size() && pcsVersion() < KDEV_DB_VERSION )
|
|
{
|
|
TQStringList l = pcsList + pcsIdxList;
|
|
int rtn = KMessageBox::questionYesNoList( 0, i18n( "Persistent class store will be disabled: you have a wrong version of pcs installed.\nRemove old pcs files?" ), l, i18n( "C++ Support" ), KStdGuiItem::del(), KStdGuiItem::cancel() );
|
|
if ( rtn == KMessageBox::Yes )
|
|
{
|
|
TQStringList::Iterator it = l.begin();
|
|
while ( it != l.end() )
|
|
{
|
|
TQFile::remove
|
|
( *it );
|
|
++it;
|
|
}
|
|
// @todo regenerate the pcs list
|
|
pcsList.clear();
|
|
}
|
|
else
|
|
{
|
|
return ;
|
|
}
|
|
}
|
|
|
|
TQStringList::Iterator it = pcsList.begin();
|
|
while ( it != pcsList.end() )
|
|
{
|
|
kdDebug( 9007 ) << "CppSupportPart::setupCatalog()2 " << *it << endl;
|
|
Catalog * catalog = new Catalog();
|
|
catalog->open( *it );
|
|
catalog->setEnabled( enabledPCSs.contains( TQFileInfo( *it ).baseName(true) ) );
|
|
++it;
|
|
|
|
for ( TQStringList::Iterator idxIt = indexList.begin(); idxIt != indexList.end(); ++idxIt )
|
|
catalog->addIndex( ( *idxIt ).utf8() );
|
|
|
|
m_catalogList.append( catalog );
|
|
codeRepository() ->registerCatalog( catalog );
|
|
}
|
|
|
|
setPcsVersion( KDEV_DB_VERSION );
|
|
}
|
|
|
|
KMimeType::List CppSupportPart::mimeTypes( )
|
|
{
|
|
TQStringList mimeList;
|
|
mimeList += m_headerMimeTypes;
|
|
mimeList += m_sourceMimeTypes;
|
|
|
|
KMimeType::List list;
|
|
for ( TQStringList::Iterator it = mimeList.begin(); it != mimeList.end(); ++it )
|
|
{
|
|
if ( KMimeType::Ptr mime = KMimeType::mimeType( *it ) )
|
|
list << mime;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
int CppSupportPart::pcsVersion()
|
|
{
|
|
TDEConfig * config = CppSupportFactory::instance() ->config();
|
|
TDEConfigGroupSaver cgs( config, "PCS" );
|
|
return config->readNumEntry( "Version", 0 );
|
|
}
|
|
|
|
void CppSupportPart::setPcsVersion( int version )
|
|
{
|
|
TDEConfig * config = CppSupportFactory::instance() ->config();
|
|
TDEConfigGroupSaver cgs( config, "PCS" );
|
|
config->writeEntry( "Version", version );
|
|
config->sync();
|
|
}
|
|
|
|
TQString CppSupportPart::formatTag( const Tag & inputTag )
|
|
{
|
|
Tag tag = inputTag;
|
|
|
|
switch ( tag.kind() )
|
|
{
|
|
case Tag::Kind_Namespace:
|
|
return TQString::fromLatin1( "namespace " ) + tag.name();
|
|
|
|
case Tag::Kind_Class:
|
|
return TQString::fromLatin1( "class " ) + tag.name();
|
|
|
|
case Tag::Kind_Function:
|
|
case Tag::Kind_FunctionDeclaration:
|
|
{
|
|
CppFunction<Tag> tagInfo( tag );
|
|
return tagInfo.name() + "( " + tagInfo.arguments().join( ", " ) + " ) : " + tagInfo.type();
|
|
}
|
|
break;
|
|
|
|
case Tag::Kind_Variable:
|
|
case Tag::Kind_VariableDeclaration:
|
|
{
|
|
CppVariable<Tag> tagInfo( tag );
|
|
return tagInfo.name() + " : " + tagInfo.type();
|
|
}
|
|
break;
|
|
}
|
|
return tag.name();
|
|
}
|
|
|
|
void CppSupportPart::codeCompletionConfigStored( )
|
|
{
|
|
if ( m_projectClosing ) return;
|
|
updateParserConfiguration();
|
|
/*
|
|
m_backgroundParser->updateParserConfiguration();
|
|
|
|
KDevDriver* d = dynamic_cast<KDevDriver*>( m_driver ); //The foreground-parse isn't used anymore, and could be removed
|
|
if( d ) {
|
|
d->setup();
|
|
d->makeMacrosPersistent();
|
|
}*/
|
|
partController() ->setActivePart( partController()->activePart() );
|
|
}
|
|
|
|
void CppSupportPart::splitHeaderSourceConfigStored( )
|
|
{
|
|
TQString o = splitHeaderSourceConfig()->orientation();
|
|
if ( o == "Vertical" )
|
|
emit splitOrientationChanged( Qt::Vertical );
|
|
else if ( o == "Horizontal" )
|
|
emit splitOrientationChanged( Qt::Horizontal );
|
|
}
|
|
|
|
void CppSupportPart::removeWithReferences( const TQString & fileName )
|
|
{
|
|
kdDebug( 9007 ) << "remove with references: " << fileName << endl;
|
|
m_timestamp.remove( fileName );
|
|
if ( !codeModel() ->hasFile( fileName ) )
|
|
return ;
|
|
|
|
emit aboutToRemoveSourceInfo( fileName );
|
|
|
|
codeModel() ->removeFile( codeModel() ->fileByName( fileName ) );
|
|
}
|
|
|
|
bool CppSupportPart::isValidSource( const TQString& fileName ) const
|
|
{
|
|
TQFileInfo fileInfo( fileName );
|
|
TQString path = URLUtil::canonicalPath( fileInfo.absFilePath() );
|
|
|
|
return /*project() && project() ->isProjectFile( path )
|
|
&&*/ ( isSource( path ) || isHeader( path ) )
|
|
&& !TQFile::exists( fileInfo.dirPath( true ) + "/.tdev_ignore" );
|
|
}
|
|
|
|
TQString CppSupportPart::formatModelItem( const CodeModelItem *item, bool shortDescription )
|
|
{
|
|
if ( item->isFunction() || item->isFunctionDefinition() )
|
|
{
|
|
const FunctionModel * model = static_cast<const FunctionModel*>( item );
|
|
TQString function;
|
|
TQString args;
|
|
ArgumentList argumentList = model->argumentList();
|
|
for ( ArgumentList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it )
|
|
{
|
|
args.isEmpty() ? args += "" : args += ", " ;
|
|
args += formatModelItem( ( *it ).data() );
|
|
}
|
|
if ( !shortDescription )
|
|
function += ( model->isVirtual() ? TQString( "virtual " ) : TQString( "" ) ) + model->resultType() + " ";
|
|
|
|
function += model->name() + "(" + args + ")" + ( model->isConstant() ? TQString( " const" ) : TQString( "" ) ) +
|
|
( model->isAbstract() ? TQString( " = 0" ) : TQString( "" ) );
|
|
|
|
return function;
|
|
}
|
|
else if ( item->isVariable() )
|
|
{
|
|
const VariableModel * model = static_cast<const VariableModel*>( item );
|
|
if ( shortDescription )
|
|
return model->name();
|
|
return model->type() + " " + model->name();
|
|
}
|
|
else if ( item->isArgument() )
|
|
{
|
|
const ArgumentModel * model = static_cast<const ArgumentModel*>( item );
|
|
TQString arg;
|
|
if ( !shortDescription )
|
|
arg += model->type() + " ";
|
|
arg += model->name();
|
|
if ( !shortDescription )
|
|
arg += model->defaultValue().isEmpty() ? TQString( "" ) : TQString( " = " ) + model->defaultValue();
|
|
return arg.stripWhiteSpace();
|
|
}
|
|
else
|
|
return KDevLanguageSupport::formatModelItem( item, shortDescription );
|
|
}
|
|
|
|
void CppSupportPart::addClass()
|
|
{
|
|
slotNewClass();
|
|
}
|
|
|
|
void CppSupportPart::saveProjectSourceInfo()
|
|
{
|
|
const FileList fileList = codeModel() ->fileList();
|
|
|
|
if ( !project() || fileList.isEmpty() )
|
|
return ;
|
|
|
|
TQFile f( project() ->projectDirectory() + "/"
|
|
+ project() ->projectName() + ".kdevelop.pcs" );
|
|
if ( !f.open( IO_WriteOnly ) )
|
|
return ;
|
|
|
|
m_backgroundParser->lock();
|
|
|
|
createIgnorePCSFile();
|
|
|
|
TQDataStream stream( &f );
|
|
TQMap<TQString, uint> offsets;
|
|
|
|
TQString pcs( "PCS" );
|
|
stream << pcs << KDEV_PCS_VERSION;
|
|
|
|
stream << int( fileList.size() );
|
|
for ( FileList::ConstIterator it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
const FileDom dom = ( *it );
|
|
stream << dom->name() << m_timestamp[ dom->name() ].toTime_t();
|
|
if( m_timestamp.find( dom->name() ) == m_timestamp.end() ) {
|
|
kdDebug( 9007 ) << dom->name() << ": timestamp is missing " << endl;
|
|
}
|
|
offsets.insert( dom->name(), stream.device() ->at() );
|
|
stream << ( uint ) 0; // dummy offset
|
|
}
|
|
|
|
for ( FileList::ConstIterator it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
const FileDom dom = ( *it );
|
|
int offset = stream.device() ->at();
|
|
|
|
dom->write( stream );
|
|
|
|
int end = stream.device() ->at();
|
|
|
|
stream.device() ->at( offsets[ dom->name() ] );
|
|
stream << offset;
|
|
stream.device() ->at( end );
|
|
}
|
|
|
|
TQFile::remove( project() ->projectDirectory() + "/"
|
|
+ project() ->projectName() + ".kdevelop.ignore_pcs" );
|
|
TQFile::remove( project() ->projectDirectory() + "/"
|
|
+ project() ->projectName().lower() + ".kdevelop.ignore_pcs" );
|
|
|
|
m_backgroundParser->unlock();
|
|
}
|
|
|
|
TQString CppSupportPart::extractInterface( const ClassDom& klass )
|
|
{
|
|
TQString txt;
|
|
TQTextStream stream( &txt, IO_WriteOnly );
|
|
|
|
TQString name = klass->name() + "Interface";
|
|
TQString ind;
|
|
ind.fill( TQChar( ' ' ), 4 );
|
|
|
|
stream
|
|
<< "class " << name << "\n"
|
|
<< "{" << "\n"
|
|
<< "public:" << "\n"
|
|
<< ind << name << "() {}" << "\n"
|
|
<< ind << "virtual ~" << name << "() {}" << "\n"
|
|
<< "\n";
|
|
|
|
const FunctionList functionList = klass->functionList();
|
|
for ( FunctionList::ConstIterator it = functionList.begin(); it != functionList.end(); ++it )
|
|
{
|
|
const FunctionDom& fun = *it;
|
|
|
|
if ( !fun->isVirtual() || fun->name().startsWith( "~" ) )
|
|
continue;
|
|
|
|
stream << ind << formatModelItem( fun );
|
|
if ( !fun->isAbstract() )
|
|
stream << " = 0";
|
|
|
|
stream << ";\n";
|
|
}
|
|
|
|
stream
|
|
<< "\n"
|
|
<< "private:" << "\n"
|
|
<< ind << name << "( const " << name << "& source );" << "\n"
|
|
<< ind << "void operator = ( const " << name << "& source );" << "\n"
|
|
<< "};" << "\n\n";
|
|
|
|
return txt;
|
|
}
|
|
|
|
void CppSupportPart::slotExtractInterface( )
|
|
{
|
|
if ( !m_activeClass )
|
|
return ;
|
|
|
|
TQFileInfo fileInfo( m_activeClass->fileName() );
|
|
TQString ifaceFileName = fileInfo.dirPath( true ) + "/" + m_activeClass->name().lower() + "_interface.h";
|
|
if ( TQFile::exists( ifaceFileName ) )
|
|
{
|
|
KMessageBox::error( mainWindow() ->main(), i18n( "File %1 already exists" ).arg( ifaceFileName ),
|
|
i18n( "C++ Support" ) );
|
|
}
|
|
else
|
|
{
|
|
TQString text = extractInterface( m_activeClass );
|
|
|
|
TQFile f( ifaceFileName );
|
|
if ( f.open( IO_WriteOnly ) )
|
|
{
|
|
TQTextStream stream( &f );
|
|
stream
|
|
<< "#ifndef __" << m_activeClass->name().upper() << "_INTERFACE_H" << "\n"
|
|
<< "#define __" << m_activeClass->name().upper() << "_INTERFACE_H" << "\n"
|
|
<< "\n"
|
|
<< extractInterface( m_activeClass )
|
|
<< "\n"
|
|
<< "#endif // __" << m_activeClass->name().upper() << "_INTERFACE_H" << "\n";
|
|
f.close();
|
|
|
|
project() ->addFile( ifaceFileName );
|
|
}
|
|
}
|
|
|
|
m_activeClass = 0;
|
|
}
|
|
|
|
void CppSupportPart::gotoLine( int line )
|
|
{
|
|
if ( isHeader( m_activeFileName ) )
|
|
{
|
|
KURL url;
|
|
url.setPath( sourceOrHeaderCandidate() );
|
|
partController() ->editDocument( url, line );
|
|
}
|
|
else
|
|
m_activeViewCursor->setCursorPositionReal( line, 0 );
|
|
}
|
|
|
|
FileDom CppSupportPart::fileByName( const TQString& name) {
|
|
return codeModel()->fileByName( name );
|
|
}
|
|
|
|
|
|
int CppSupportPart::parseFilesAndDependencies( TQStringList files, bool background, bool parseFirst, bool silent ) {
|
|
TQMap<TQString, int> fileGroups;
|
|
int nextGroup = 0;
|
|
|
|
for( TQStringList::iterator it = files.begin(); it != files.end(); ++it ) {
|
|
FileDom d = fileByName( *it );
|
|
|
|
TQStringList lst;
|
|
if( !d ) {
|
|
lst << *it;
|
|
}else{
|
|
lst = codeModel()->getGroupStrings( d->groupId() );
|
|
/* kdDebug( 9007 ) << "adding group of: " << *it << ":\n" << " which is " << lst.join("\n") << "\n\n";*/
|
|
if( lst.count() > 10 ) {
|
|
lst = codeModel()->getGroupStrings( d->groupId() );
|
|
}
|
|
}
|
|
int cgroup = nextGroup;
|
|
nextGroup++;
|
|
|
|
if( fileGroups.find( *it ) != fileGroups.end() )
|
|
cgroup = fileGroups[*it];
|
|
|
|
for( TQStringList::iterator lit = lst.begin(); lit != lst.end(); ++lit )
|
|
fileGroups[*lit] = cgroup;
|
|
}
|
|
|
|
TQValueVector<TQStringList> groups;
|
|
groups.resize( nextGroup );
|
|
|
|
///put the groups together
|
|
for( TQMap<TQString, int>::iterator it = fileGroups.begin(); it != fileGroups.end(); ++it ) {
|
|
groups[*it] << it.key();
|
|
}
|
|
|
|
for( int a = 0; a < nextGroup; a++ ) {
|
|
TQStringList group = reorder( groups[a] );
|
|
|
|
|
|
/* kdDebug( 9007 ) << "reparsing the following group: " << ":\n" << group.join("\n") << "\n\n";*/
|
|
if( background ) {
|
|
|
|
m_backgroundParser->lock();
|
|
|
|
if( !group.isEmpty() ) {
|
|
if( !parseFirst )
|
|
m_parseEmitWaiting.addGroup( group, silent ? ParseEmitWaiting::Silent : ParseEmitWaiting::None );
|
|
else
|
|
m_parseEmitWaiting.addGroupFront( group, silent ? ParseEmitWaiting::Silent : ParseEmitWaiting::None );
|
|
if( !silent ) {
|
|
if( !parseFirst )
|
|
m_fileParsedEmitWaiting.addGroup( group, silent ? ParseEmitWaiting::Silent : ParseEmitWaiting::None );
|
|
else
|
|
m_fileParsedEmitWaiting.addGroupFront( group, silent ? ParseEmitWaiting::Silent : ParseEmitWaiting::None );
|
|
}
|
|
}
|
|
|
|
if( parseFirst && !group.empty() ) {
|
|
for(TQStringList::iterator it = --group.end(); it != group.end(); ) {
|
|
backgroundParser()->addFileFront(*it);
|
|
if( it == group.begin() ) {
|
|
it = group.end();
|
|
} else {
|
|
--it;
|
|
}
|
|
}
|
|
} else {
|
|
for(TQStringList::iterator it = group.begin(); it != group.end(); ++it) {
|
|
backgroundParser()->addFile(*it);
|
|
}
|
|
}
|
|
|
|
m_backgroundParser->unlock();
|
|
|
|
} else {
|
|
for(TQStringList::iterator it = group.begin(); it != group.end(); ++it) {
|
|
m_driver->parseFile( *it );
|
|
}
|
|
}
|
|
}
|
|
|
|
return fileGroups.count();
|
|
}
|
|
|
|
int CppSupportPart::parseFileAndDependencies( const TQString & fileName, bool background, bool parseFirst, bool silent ) {
|
|
if(! isValidSource( fileName ) ) return 0;
|
|
|
|
// kdDebug( 9007 ) << "reparsing dependencies of " << fileName << "\n";
|
|
|
|
return parseFilesAndDependencies( fileName, background, parseFirst, silent );
|
|
}
|
|
|
|
void CppSupportPart::parseEmit( ParseEmitWaiting::Processed files ) {
|
|
if( files.res.isEmpty() ) return;
|
|
|
|
bool modelHasFiles = true;
|
|
|
|
for( TQStringList::iterator it = files.res.begin(); it != files.res.end(); ++it ) {
|
|
if( !codeModel()->hasFile( *it ) ) modelHasFiles = false;
|
|
}
|
|
|
|
int oldFileCount = codeModel()->fileList().count();
|
|
|
|
if( (files.flag & ParseEmitWaiting::HadErrors) && modelHasFiles && !files.hasFlag( ParseEmitWaiting::Silent ) ) {
|
|
mainWindow() ->statusBar() ->message( "File parsed, but not updating code-model because of errors", 2000 );
|
|
kdDebug( 9007 ) << "not updating code-model because at least one file has errors" << endl;
|
|
// for( TQStringList::iterator it = files.res.begin(); it != files.res.end(); ++it )
|
|
// m_backgroundParser->removeFile( *it );
|
|
} else {
|
|
///update timestamps
|
|
for( TQStringList::iterator it = files.res.begin(); it != files.res.end(); ++it ) {
|
|
if( !codeModel()->hasFile( *it ) ) modelHasFiles = false;
|
|
TQString& fileName = *it;
|
|
|
|
TQFileInfo fileInfo( fileName );
|
|
TQString path = URLUtil::canonicalPath( fileName );
|
|
|
|
if ( !fileInfo.exists() ) {
|
|
removeWithReferences( path );
|
|
continue ;
|
|
}
|
|
|
|
m_timestamp[ path ] = fileInfo.lastModified();
|
|
}
|
|
|
|
if( files.hasFlag( ParseEmitWaiting::Silent ) && !alwaysParseInBackground )
|
|
return;
|
|
|
|
m_backgroundParser->lock();
|
|
|
|
TQStringList l = files.res;
|
|
|
|
TQMap<TQString, bool> wholeResult;
|
|
TQStringList missing;
|
|
|
|
TQMap<TQString, FileDom> newFiles;
|
|
|
|
while(!l.isEmpty() ) {
|
|
TQString fileName = l.front();
|
|
|
|
if( !m_backgroundParser->hasTranslationUnit( fileName ) ) {
|
|
kdDebug( 9007 ) << "error: translation-unit is missing: " << fileName << endl;
|
|
missing << fileName;
|
|
} else {
|
|
if ( ParsedFilePointer ast = m_backgroundParser->translationUnit( fileName ) )
|
|
{
|
|
if ( true /*!hasErrors*/ )
|
|
{
|
|
FileDom oldFile = codeModel()->fileByName( fileName );
|
|
|
|
StoreWalker walker( fileName, codeModel() );
|
|
walker.setOverrides( newFiles );
|
|
|
|
walker.parseTranslationUnit( *ast );
|
|
|
|
if( oldFile ) {
|
|
newFiles[fileName] = walker.file();
|
|
|
|
///update timestamps
|
|
TQFileInfo fileInfo( fileName );
|
|
TQString path = URLUtil::canonicalPath( fileName );
|
|
|
|
m_timestamp[ path ] = fileInfo.lastModified();
|
|
} else {
|
|
codeModel() ->addFile( walker.file() );
|
|
}
|
|
|
|
if( walker.file() ) {
|
|
TQStringList grp = walker.file()->wholeGroupStrings();
|
|
for( TQStringList::const_iterator it = grp.begin(); it != grp.end(); ++it )
|
|
wholeResult[*it] = true;
|
|
}
|
|
}
|
|
} else {
|
|
kdDebug( 9007 ) << "failed to parse " << fileName << endl;
|
|
}
|
|
}
|
|
|
|
|
|
l.pop_front();
|
|
}
|
|
|
|
bool canUpdate = true;
|
|
for( TQMap<TQString, FileDom>::const_iterator it = newFiles.begin(); it != newFiles.end(); ++it ) {
|
|
FileDom oldFile = codeModel()->fileByName( it.key() );
|
|
|
|
if( !oldFile || !oldFile->canUpdate( *it ) ) {
|
|
canUpdate = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( canUpdate ) {
|
|
///Update the code-model
|
|
for( TQMap<TQString, FileDom>::const_iterator it = newFiles.begin(); it != newFiles.end(); ++it ) {
|
|
FileDom oldFile = codeModel()->fileByName( it.key() );
|
|
oldFile->update( *it );
|
|
codeModel()->mergeGroups( oldFile->groupId(), (*it)->groupId() ); ///Merge parsing-groups together
|
|
}
|
|
} else {
|
|
///Remove the current files and replace them with the new ones
|
|
for( TQMap<TQString, FileDom>::const_iterator it = newFiles.begin(); it != newFiles.end(); ++it ) {
|
|
removeWithReferences( it.key() );
|
|
codeModel()->addFile( *it );
|
|
}
|
|
}
|
|
/*
|
|
///make the list unique
|
|
|
|
l.clear();
|
|
for( TQMap<TQString, bool>::const_iterator it = wholeResult.begin(); it != wholeResult.end(); ++it )
|
|
l << it.key();*/
|
|
|
|
m_backgroundParser->unlock();
|
|
|
|
if( !missing.isEmpty() ) {
|
|
kdDebug( 9007 ) << "error: translation-units were missing: " << missing << endl;
|
|
//don't reparse missing units, because it may cause the whole project to be reparsed
|
|
// parseFilesAndDependencies( missing, true, false, files.hasFlag( ParseEmitWaiting::Silent ) );
|
|
}
|
|
|
|
if( files.hasFlag( ParseEmitWaiting::Silent ) ) {
|
|
if( alwaysParseInBackground )
|
|
for( TQStringList::iterator it = files.res.begin(); it != files.res.end(); ++it )
|
|
m_backgroundParser->removeFile( *it );
|
|
} else {
|
|
if( !canUpdate ) { ///If the current model could be updated, do not emit addedSourceInfo(..) and remove the units from the parser, because nobody will be using them
|
|
TQStringList l = files.res;
|
|
while(!l.isEmpty() ) {
|
|
emit aboutToRemoveSourceInfo( l.front() );
|
|
emit removedSourceInfo( l.front() );
|
|
emit addedSourceInfo( l.front() );
|
|
l.pop_front();
|
|
}
|
|
|
|
if( !files.hasFlag( ParseEmitWaiting::Silent ) )
|
|
emitFileParsed( files );
|
|
} else {
|
|
TQStringList l = files.res;
|
|
while( !l.isEmpty() ) {
|
|
emit codeModelUpdated( l.front() );
|
|
emit aboutToRemoveSourceInfo( l.front() );
|
|
emit removedSourceInfo( l.front() );
|
|
emit addedSourceInfo( l.front() );
|
|
l.pop_front();
|
|
}
|
|
}
|
|
}
|
|
kdDebug( 9007 ) << "files in code-model after parseEmit: " << codeModel()->fileList().count() << " before: " << oldFileCount << endl;
|
|
}
|
|
}
|
|
|
|
/*void CppSupportPart::recomputeCodeModel( const TQString& fileName )
|
|
{*/
|
|
|
|
//}
|
|
|
|
void CppSupportPart::emitSynchronousParseReady( const TQString& file, ParsedFilePointer unit ) {
|
|
emit synchronousParseReady( file, unit );
|
|
}
|
|
|
|
void CppSupportPart::emitFileParsed( TQStringList l )
|
|
{
|
|
while( !l.isEmpty() ) {
|
|
emit fileParsed( l.front() );
|
|
l.pop_front();
|
|
}
|
|
}
|
|
|
|
bool CppSupportPart::isHeader( const TQString& fileName ) const
|
|
{
|
|
/*KMimeType::Ptr ptr = KMimeType::findByPath( fileName );
|
|
if ( ptr && m_headerMimeTypes.contains( ptr->name() ) )
|
|
return true;*/
|
|
|
|
return ( m_headerExtensions.findIndex( TQFileInfo( fileName ).extension() ) != -1 );
|
|
}
|
|
|
|
bool CppSupportPart::isSource( const TQString& fileName ) const
|
|
{
|
|
/*KMimeType::Ptr ptr = KMimeType::findByPath( fileName );
|
|
if ( ptr && m_sourceMimeTypes.contains( ptr->name() ) )
|
|
return true;*/
|
|
|
|
return ( m_sourceExtensions.findIndex( TQFileInfo( fileName ).extension() ) != -1 );
|
|
}
|
|
|
|
void CppSupportPart::gotoDeclarationLine( int line )
|
|
{
|
|
if ( isHeader( m_activeFileName ) )
|
|
m_activeViewCursor->setCursorPositionReal( line, 0 );
|
|
else
|
|
{
|
|
KURL url;
|
|
url.setPath( sourceOrHeaderCandidate() );
|
|
partController() ->editDocument( url, line );
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::removeCatalog( const TQString & dbName )
|
|
{
|
|
if ( !TQFile::exists( dbName ) )
|
|
return ;
|
|
|
|
TQValueList<Catalog*> catalogs = codeRepository() ->registeredCatalogs();
|
|
Catalog* c = 0;
|
|
for ( TQValueList<Catalog*>::Iterator it = catalogs.begin(); it != catalogs.end(); ++it )
|
|
{
|
|
if ( ( *it ) ->dbName() == dbName )
|
|
{
|
|
c = *it;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( c )
|
|
{
|
|
codeRepository() ->unregisterCatalog( c );
|
|
m_catalogList.remove( c );
|
|
}
|
|
|
|
TQFileInfo fileInfo( dbName );
|
|
TQDir dir( fileInfo.dir( true ) );
|
|
TQStringList indexList = TQStringList() << "kind" << "name" << "scope" << "fileName" << "prefix";
|
|
for(TQStringList::Iterator iter = indexList.begin(); iter != indexList.end(); iter++)
|
|
{
|
|
TQStringList fileList = dir.entryList( fileInfo.baseName(true) +"." +(*iter) + ".idx" );
|
|
for ( TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
TQString idxName = fileInfo.dirPath( true ) + "/" + *it;
|
|
kdDebug( 9007 ) << "=========> remove db index: " << idxName << endl;
|
|
dir.remove( *it );
|
|
}
|
|
}
|
|
dir.remove( fileInfo.fileName() );
|
|
}
|
|
|
|
void CppSupportPart::addCatalog( Catalog * catalog )
|
|
{
|
|
m_catalogList.append( catalog );
|
|
codeRepository() ->registerCatalog( catalog );
|
|
}
|
|
|
|
FunctionDefinitionDom CppSupportPart::functionDefinitionAt( int line, int column )
|
|
{
|
|
if ( !codeModel() ->hasFile( m_activeFileName ) )
|
|
return FunctionDefinitionDom();
|
|
|
|
CodeModelUtils::CodeModelHelper h( codeModel(), codeModel()->fileByName( m_activeFileName ) );
|
|
|
|
FunctionDom d = h.functionAt( line, column, CodeModelUtils::CodeModelHelper::Definition );
|
|
if( d ) {
|
|
FunctionDefinitionModel* m = dynamic_cast<FunctionDefinitionModel*>( d.data() );
|
|
if( m ) return FunctionDefinitionDom( m );
|
|
}
|
|
return FunctionDefinitionDom();
|
|
}
|
|
|
|
FunctionDefinitionDom CppSupportPart::currentFunctionDefinition( )
|
|
{
|
|
if ( !this->m_activeViewCursor )
|
|
return FunctionDefinitionDom();
|
|
|
|
unsigned int line, column;
|
|
this->m_activeViewCursor->cursorPositionReal( &line, &column );
|
|
return functionDefinitionAt( line, column );
|
|
}
|
|
|
|
void CppSupportPart::slotCursorPositionChanged()
|
|
{
|
|
if ( codeCompletion() )
|
|
{
|
|
unsigned int line = 0;
|
|
unsigned int column = 0;
|
|
if ( KDevEditorUtil::currentPositionReal( &line, &column, dynamic_cast<KTextEditor::Document*>( partController()->activePart() ) ) )
|
|
{
|
|
TQString typeInfoString = codeCompletion()->createTypeInfoString( line, column );
|
|
mainWindow()->statusBar()->message( typeInfoString );
|
|
}
|
|
}
|
|
|
|
// m_functionHintTimer->changeInterval( 1000 );
|
|
if ( splitHeaderSourceConfig()->splitEnabled()
|
|
&& splitHeaderSourceConfig()->autoSync() )
|
|
slotSwitchHeader( true );
|
|
}
|
|
|
|
/*
|
|
void CppSupportPart::slotFunctionHint( )
|
|
{
|
|
kdDebug( 9007 ) << "=======> compute current function definition" << endl;
|
|
// m_functionHintTimer->stop();
|
|
if ( FunctionDefinitionDom fun = currentFunctionDefinition() )
|
|
{
|
|
TQStringList scope = fun->scope();
|
|
TQString funName = scope.join( "::" );
|
|
if ( !funName.isEmpty() )
|
|
funName += "::";
|
|
|
|
funName += formatModelItem( fun, true );
|
|
|
|
mainWindow() ->statusBar() ->message( funName, 2000 );
|
|
}
|
|
}
|
|
*/
|
|
|
|
void CppSupportPart::createIgnorePCSFile( )
|
|
{
|
|
static TQCString skip_me( "ignore me\n" );
|
|
|
|
TQString skip_file_name = project() ->projectDirectory() + "/"
|
|
+ project() ->projectName() + ".kdevelop.ignore_pcs";
|
|
TQFile skip_pcs_file( skip_file_name );
|
|
if ( skip_pcs_file.open( IO_WriteOnly ) )
|
|
{
|
|
skip_pcs_file.writeBlock( skip_me );
|
|
skip_pcs_file.close();
|
|
}
|
|
}
|
|
|
|
TQString CppSupportPart::specialHeaderName( bool local ) const
|
|
{
|
|
if ( local )
|
|
return ::locateLocal( "data", "kdevcppsupport/configuration", CppSupportFactory::instance() );
|
|
|
|
return ::locate( "data", "kdevcppsupport/configuration", CppSupportFactory::instance() );
|
|
}
|
|
|
|
void CppSupportPart::updateParserConfiguration()
|
|
{
|
|
m_backgroundParser->updateParserConfiguration();
|
|
|
|
TQString conf_file_name = specialHeaderName();
|
|
|
|
m_driver->removeAllMacrosInFile( conf_file_name );
|
|
dynamic_cast<KDevDriver*>(m_driver)->setup();
|
|
m_driver->parseFile( conf_file_name, true, true, true );
|
|
|
|
m_buildSafeFileSetTimer->start( 500, true );
|
|
parseProject( true );
|
|
}
|
|
|
|
const Driver* CppSupportPart::driver() const {
|
|
return m_driver;
|
|
}
|
|
|
|
Driver* CppSupportPart::driver() {
|
|
return m_driver;
|
|
}
|
|
|
|
KDevDesignerIntegration * CppSupportPart::designer( KInterfaceDesigner::DesignerType type )
|
|
{
|
|
KDevDesignerIntegration * des = 0;
|
|
switch ( type )
|
|
{
|
|
case KInterfaceDesigner::Glade:
|
|
case KInterfaceDesigner::TQtDesigner:
|
|
des = m_designers[ type ];
|
|
if ( des == 0 )
|
|
{
|
|
CppImplementationWidget * impl = new CppImplementationWidget( this );
|
|
des = new QtDesignerCppIntegration( this, impl );
|
|
des->loadSettings( *project() ->projectDom(), "kdevcppsupport/designerintegration" );
|
|
m_designers[ type ] = des;
|
|
}
|
|
break;
|
|
}
|
|
return des;
|
|
}
|
|
|
|
|
|
void CppSupportPart::resetParserStoreTimer() {
|
|
// m_deleteParserStoreTimer->start(10000); ///try to empty the store regularly
|
|
}
|
|
|
|
void CppSupportPart::slotDeleteParserStore() {
|
|
/* if( !m_backgroundParser->filesInQueue() )
|
|
m_backgroundParser->removeAllFiles();
|
|
else
|
|
resetParserStoreTimer();*/
|
|
}
|
|
|
|
|
|
void CppSupportPart::slotCreateSubclass()
|
|
{
|
|
TQFileInfo fi( m_contextFileName );
|
|
if ( fi.extension( false ) != "ui" )
|
|
return ;
|
|
QtDesignerCppIntegration *des = dynamic_cast<QtDesignerCppIntegration*>( designer( KInterfaceDesigner::TQtDesigner ) );
|
|
if ( des )
|
|
des->selectImplementation( m_contextFileName );
|
|
}
|
|
|
|
void CppSupportPart::addMethod( ClassDom aClass, const TQString& name, const TQString type,
|
|
const TQString& parameters, CodeModelItem::Access accessType,
|
|
bool isConst, bool isInline, bool isVirtual, bool isPureVirtual,
|
|
const TQString& implementation )
|
|
{
|
|
partController() ->editDocument( KURL( aClass->fileName() ) );
|
|
KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( partController() ->activePart() );
|
|
if ( !editIface )
|
|
{
|
|
/// @fixme show messagebox
|
|
return ;
|
|
}
|
|
TQString declarationString = type + " " + name + "(" + parameters + ")" + ( isConst ? " const" : "" );
|
|
|
|
KDevSourceFormatter* sourceFormatter = extension<KDevSourceFormatter>( "TDevelop/SourceFormatter" );
|
|
|
|
TQString finalDeclaration = ( ( isVirtual || isPureVirtual ) ? "\nvirtual " : "\n" + declarationString +
|
|
( isPureVirtual ? " = 0 " : "" ) +
|
|
( isInline ? "\n{\n" + implementation + "\n}\n" : ";" ) );
|
|
|
|
if ( sourceFormatter != 0 )
|
|
finalDeclaration = sourceFormatter->formatSource( finalDeclaration );
|
|
|
|
TQString indentString = "\t";
|
|
|
|
if ( sourceFormatter != 0 )
|
|
indentString = sourceFormatter->indentString();
|
|
|
|
editIface->insertText( findInsertionLineMethod( aClass, accessType ), 0,
|
|
finalDeclaration.replace( "\n", "\n\t" ) + "\n" );
|
|
|
|
backgroundParser() ->addFile( aClass->fileName() );
|
|
if ( isInline || isPureVirtual )
|
|
return ;
|
|
|
|
// construct fully qualified name for method definition
|
|
TQString fullyQualifiedName = aClass->scope().join("::");
|
|
if (! fullyQualifiedName.isEmpty())
|
|
{
|
|
fullyQualifiedName += "::";
|
|
}
|
|
fullyQualifiedName += aClass->name() + "::" + name;
|
|
|
|
TQString definitionString = "\n" + type + " " + fullyQualifiedName + "(" + parameters + ")" + ( isConst ? " const" : "" ) + "\n{\n" + implementation + "\n}\n";
|
|
|
|
if ( sourceFormatter != 0 )
|
|
definitionString = sourceFormatter->formatSource( definitionString );
|
|
|
|
TQFileInfo info( aClass->fileName() );
|
|
TQString implementationFile = info.dirPath( true ) + "/" + info.baseName() + ".cpp" ;
|
|
TQFileInfo fileInfo( implementationFile );
|
|
KDevCreateFile* createFileSupport = extension<KDevCreateFile>( "TDevelop/CreateFile" );
|
|
if ( !TQFile::exists( fileInfo.absFilePath() ) && createFileSupport != 0 )
|
|
createFileSupport->createNewFile( fileInfo.extension(), fileInfo.dirPath( true ), fileInfo.baseName() );
|
|
|
|
partController() ->editDocument( KURL( implementationFile ) );
|
|
editIface = dynamic_cast<KTextEditor::EditInterface*>( partController() ->activePart() );
|
|
if ( !editIface )
|
|
return ; //@fixme errorverdoedelung
|
|
|
|
editIface->insertLine( editIface->numLines(), TQString::fromLatin1( "" ) );
|
|
editIface->insertText( editIface->numLines() - 1, 0, definitionString );
|
|
backgroundParser() ->addFile( implementationFile );
|
|
}
|
|
|
|
ClassDom CppSupportPart::currentClass( ) const
|
|
{
|
|
FileDom file = codeModel() ->fileByName( m_activeFileName );
|
|
if ( file == 0 || m_activeViewCursor == 0 )
|
|
return 0;
|
|
|
|
unsigned int curLine, curCol;
|
|
m_activeViewCursor->cursorPositionReal( &curLine, &curCol );
|
|
|
|
CodeModelUtils::CodeModelHelper h( codeModel(), file );
|
|
|
|
return h.classAt( curLine, curCol );
|
|
}
|
|
|
|
VariableDom CppSupportPart::currentAttribute( ClassDom curClass ) const
|
|
{
|
|
if ( m_activeViewCursor == 0 || curClass == 0 )
|
|
return 0;
|
|
|
|
unsigned int line, col;
|
|
m_activeViewCursor->cursorPositionReal( &line, &col );
|
|
|
|
VariableList vars = curClass->variableList();
|
|
|
|
for ( VariableList::iterator i = vars.begin(); i != vars.end(); ++i )
|
|
{
|
|
int startLine, startCol;
|
|
( *i ) ->getStartPosition( &startLine, &startCol );
|
|
if ( startLine < (int)line || ( startLine == (int)line && startCol <= (int)col ) )
|
|
{
|
|
int endLine, endCol;
|
|
( *i ) ->getEndPosition( &endLine, &endCol );
|
|
if ( endLine > (int)line || ( endLine == (int)line && endCol >= (int)col ) )
|
|
return * i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CppSupportPart::slotCreateAccessMethods( )
|
|
{
|
|
if ( m_curAttribute == 0 || m_curClass == 0 )
|
|
return ;
|
|
|
|
CreateGetterSetterDialog dlg ( this, m_curClass, m_curAttribute );
|
|
dlg.exec();
|
|
}
|
|
|
|
int CppSupportPart::findInsertionLineMethod( ClassDom aClass, CodeModelItem::Access access )
|
|
{
|
|
int line, column;
|
|
aClass->getEndPosition( &line, &column );
|
|
|
|
int point = CodeModelUtils::findLastMethodLine( aClass, access );
|
|
|
|
if ( point == -1 )
|
|
{
|
|
KTextEditor::EditInterface * editIface = dynamic_cast<KTextEditor::EditInterface*>( partController() ->activePart() );
|
|
if ( !editIface )
|
|
return -1;
|
|
|
|
editIface->insertLine( line - 1, CodeModelUtils::accessSpecifierToString( access ) + ":\n" );
|
|
return line;
|
|
}
|
|
|
|
return point + 1;
|
|
}
|
|
|
|
int CppSupportPart::findInsertionLineVariable( ClassDom aClass, CodeModelItem::Access access )
|
|
{
|
|
int line, column;
|
|
aClass->getEndPosition( &line, &column );
|
|
|
|
int point = CodeModelUtils::findLastVariableLine( aClass, access );
|
|
|
|
if ( point == -1 )
|
|
{
|
|
KTextEditor::EditInterface * editIface = dynamic_cast<KTextEditor::EditInterface*>( partController() ->activePart() );
|
|
if ( !editIface )
|
|
return -1;
|
|
|
|
editIface->insertLine( line - 1, CodeModelUtils::accessSpecifierToString( access ) + ":\n" );
|
|
return line;
|
|
}
|
|
|
|
return point;
|
|
}
|
|
|
|
void CppSupportPart::createAccessMethods( ClassDom theClass, VariableDom theVariable )
|
|
{
|
|
m_curClass = theClass;
|
|
m_curAttribute = theVariable;
|
|
|
|
slotCreateAccessMethods();
|
|
}
|
|
|
|
void CppSupportPart::slotCursorMoved()
|
|
{
|
|
m_cursorMovedTimer->start( 250, true );
|
|
}
|
|
|
|
void CppSupportPart::slotTextChanged()
|
|
{
|
|
setTyping( true ); ///@todo check if this is really needed
|
|
|
|
if ( m_backgroundParserConfig->useBackgroundParser() )
|
|
{
|
|
m_textChangedTimer->start( m_backgroundParserConfig->backgroudParseDelay(), true );
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::slotParseCurrentFile()
|
|
{
|
|
if( isValid() && !isQueued( m_activeFileName ) )
|
|
{
|
|
parseFileAndDependencies( m_activeFileName, true, true );
|
|
}
|
|
}
|
|
|
|
void CppSupportPart::updateBackgroundParserConfig()
|
|
{
|
|
BackgroundParserConfig config;
|
|
config.readConfig();
|
|
|
|
if ( m_backgroundParserConfig->useProblemReporter() && !config.useProblemReporter() )
|
|
{
|
|
removeProblemReporter();
|
|
}
|
|
else if ( !m_backgroundParserConfig->useProblemReporter() && config.useProblemReporter() )
|
|
{
|
|
embedProblemReporter( true );
|
|
}
|
|
|
|
*m_backgroundParserConfig = config;
|
|
}
|
|
|
|
const SynchronizedFileSet& CppSupportPart::safeFileSet() const {
|
|
return m_safeProjectFiles;
|
|
}
|
|
|
|
SynchronizedFileSet& CppSupportPart::safeFileSet() {
|
|
return m_safeProjectFiles;
|
|
}
|
|
|
|
void CppSupportPart::buildSafeFileSet() {
|
|
if( codeCompletion() == 0 ) //probably the project has already been closed
|
|
return;
|
|
SynchronizedFileSet::SetType files; //everything that goes into this set must be deep-copied
|
|
|
|
kdDebug( 9007 ) << "CppSupportPart:: rebuilding safe-file-set" << endl;
|
|
for( TQStringList::const_iterator it = m_projectFileList.begin(); it != m_projectFileList.end(); ++it ) {
|
|
TQFileInfo fi( *it );
|
|
TQString file = *it;
|
|
if( fi.isRelative() ) {
|
|
fi.setFile( TQDir(m_projectDirectory), *it );
|
|
file = fi.absFilePath();
|
|
}
|
|
|
|
//deep-copy
|
|
files.insert( TQString::fromUtf8(file.utf8()) );
|
|
}
|
|
|
|
///Now get all translation-units from the code-repository
|
|
TQValueList<Catalog::QueryArgument> args;
|
|
|
|
args << Catalog::QueryArgument( "kind", Tag::Kind_TranslationUnit );
|
|
|
|
TQValueList<Tag> tags( codeCompletion()->repository()->query( args ) );
|
|
|
|
for( TQValueList<Tag>::const_iterator it = tags.begin(); it != tags.end(); ++it ) {
|
|
files.insert( (*it).fileName() + "||" + (*it).attribute("macroValueHash").toString() + "||" + (*it).attribute("macroIdHash").toString() );
|
|
}
|
|
m_safeProjectFiles.setFiles( files );
|
|
}
|
|
|
|
void CppSupportPart::addToRepository( ParsedFilePointer file ) {
|
|
TQString catalogString( "automatic_" + KURL::encode_string_no_slash(m_projectDirectory) );
|
|
|
|
TDEStandardDirs *dirs = CppSupportFactory::instance() ->dirs();
|
|
|
|
TQString dbName = dirs->saveLocation( "data", "kdevcppsupport/pcs" ) + catalogString + ".db";
|
|
|
|
Catalog* catalog = 0;
|
|
///First check if the catalog is already there
|
|
TQValueList<Catalog*> catalogs = codeRepository()->registeredCatalogs();
|
|
for( TQValueList<Catalog*>::const_iterator it = catalogs.begin(); it != catalogs.end(); ++it ) {
|
|
if( (*it)->dbName() == dbName ) {
|
|
catalog = *it;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !catalog ) {
|
|
kdDebug( 9007 ) << "creating new catalog named " << catalogString << " for automatic filling" << endl;
|
|
//TQStringList indexList = TQStringList() << "kind" << "name" << "scope" << "fileName" << "prefix";
|
|
catalog = new Catalog;
|
|
catalog->open( dbName );
|
|
catalog->addIndex( "kind" );
|
|
catalog->addIndex( "name" );
|
|
catalog->addIndex( "scope" );
|
|
catalog->addIndex( "prefix" );
|
|
catalog->addIndex( "fileName" );
|
|
/*
|
|
for ( TQStringList::Iterator idxIt = indexList.begin(); idxIt != indexList.end(); ++idxIt )
|
|
catalog->addIndex( ( *idxIt ).utf8() );*/
|
|
addCatalog( catalog );
|
|
}
|
|
catalog->setEnabled( true );
|
|
|
|
///Now check if the file was already parsed with the same parameters, if yes don't parse again(auto-update is currently not supported, when major changes have been done in the libraries, the repository should be deleted)
|
|
TQValueList<Catalog::QueryArgument> args;
|
|
|
|
bool compatibleParsed = false;
|
|
Tag compatibleParsedTag;
|
|
|
|
args << Catalog::QueryArgument( "kind", Tag::Kind_TranslationUnit );
|
|
args << Catalog::QueryArgument( "fileName", file->fileName() );
|
|
TQValueList<Tag> tags( catalog->query( args ) );
|
|
if( !tags.isEmpty() ) {
|
|
for( TQValueList<Tag>::const_iterator it = tags.begin(); it != tags.end(); ++it ) {
|
|
if( (*it).hasAttribute( "cppparsedfile" ) ) {
|
|
TQVariant v = (*it).attribute( "cppparsedfile" );
|
|
///@todo reenable this
|
|
/*TQByteArray b = v.toByteArray();
|
|
if( !b.isEmpty() ) {
|
|
//Would be much more efficient not to do this deserialization
|
|
ParsedFile f(b);
|
|
if( f.usedMacros().valueHash() == file->usedMacros().valueHash() && f.usedMacros().idHash() == file->usedMacros().idHash() && f.includeFiles().hash() == file->includeFiles().hash() ) {
|
|
///Do not reparse the file, it seems to already be in the repository in a similar state
|
|
if( (*it).attribute( "includedFrom" ).toString() == file->includedFrom() ) return;
|
|
|
|
///It is probable that the same state has already been parsed, but there seems to be no such tag yet(the tag will be added)
|
|
compatibleParsed = true;
|
|
compatibleParsedTag = *it;
|
|
break;
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
}
|
|
|
|
if( compatibleParsed ) {
|
|
///Add a Tag that makes sure that the file will not be parsed again
|
|
compatibleParsedTag.setAttribute( "includedFrom", file->includedFrom() );
|
|
TQByteArray data;
|
|
TQDataStream s( data, IO_WriteOnly );
|
|
file->write( s );
|
|
compatibleParsedTag.setAttribute( "cppparsedfile", data );
|
|
catalog->addItem( compatibleParsedTag );
|
|
return;
|
|
}
|
|
|
|
kdDebug( 9007 ) << "parsing translation-unit " << file->fileName() << " into catalog " << catalogString << endl;
|
|
TagCreator w( file->fileName(), catalog );
|
|
w.parseTranslationUnit( *file );
|
|
codeRepository()->touchCatalog( catalog );
|
|
|
|
m_safeProjectFiles.insert( file->fileName() + "||" + TQString("%1").arg(file->usedMacros().valueHash()) + "||" + TQString("%1").arg(file->usedMacros().idHash()) );
|
|
}
|
|
|
|
TQString CppSupportPart::findHeaderSimple( const TQString &header )
|
|
{
|
|
TQStringList::ConstIterator it;
|
|
for ( it = m_projectFileList.begin(); it != m_projectFileList.end(); ++it )
|
|
{
|
|
TQString s = *it;
|
|
if (s == header)
|
|
return s;
|
|
if ( ( s.right( header.length() ) == header ) && ( s[s.length() - header.length() - 1] == '/' ) )
|
|
return s;
|
|
}
|
|
|
|
return TQString();
|
|
}
|
|
|
|
UIBlockTester::UIBlockTesterThread::UIBlockTesterThread( UIBlockTester& parent ) : TQThread(), m_parent( parent ), m_stop(false) {
|
|
}
|
|
|
|
void UIBlockTester::UIBlockTesterThread::run() {
|
|
while(!m_stop) {
|
|
msleep( m_parent.m_msecs / 10 );
|
|
m_parent.m_timeMutex.lock();
|
|
TQDateTime t = TQDateTime::currentDateTime();
|
|
uint msecs = m_parent.m_lastTime.time().msecsTo( t.time() );
|
|
if( msecs > m_parent.m_msecs ) {
|
|
m_parent.lockup();
|
|
m_parent.m_lastTime = t;
|
|
}
|
|
m_parent.m_timeMutex.unlock();
|
|
}
|
|
}
|
|
|
|
void UIBlockTester::UIBlockTesterThread::stop() {
|
|
m_stop = true;
|
|
}
|
|
|
|
UIBlockTester::UIBlockTester( uint milliseconds ) : m_thread( *this ), m_msecs( milliseconds ) {
|
|
m_timer = new TQTimer( this );
|
|
m_timer->start( milliseconds/10 );
|
|
connect( m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(timer()) );
|
|
timer();
|
|
m_thread.start();
|
|
}
|
|
UIBlockTester::~UIBlockTester() {
|
|
m_thread.stop();
|
|
m_thread.wait();
|
|
}
|
|
|
|
void UIBlockTester::timer() {
|
|
m_timeMutex.lock();
|
|
m_lastTime = TQDateTime::currentDateTime();
|
|
m_timeMutex.unlock();
|
|
}
|
|
|
|
void UIBlockTester::lockup() {
|
|
//std::cout << "UIBlockTester: lockup of the UI for " << m_msecs << endl; ///kdDebug(..) is not thread-safe..
|
|
int a = 1; ///Place breakpoint here
|
|
}
|
|
|
|
#include "cppsupportpart.moc"
|