/*************************************************************************** * Copyright (C) 2004 by Jens Dagerbo * * jens.dagerbo@swipnet.se * * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tdevappfrontend.h" #include #include #include #include #include #include #include "configwidgetproxy.h" #include "domutil.h" #include "tdeveditorutil.h" #include "ctags2_settingswidget.h" #include "ctags2_widget.h" #include "ctags2_part.h" #include "tags.h" #define CTAGSSETTINGSPAGE 1 namespace ctags { #include "readtags.h" } typedef TDevGenericFactory CTags2Factory; static const TDevPluginInfo data("tdevctags2"); K_EXPORT_COMPONENT_FACTORY( libtdevctags2, CTags2Factory( data ) ) CTags2Part::CTags2Part(TQObject *parent, const char *name, const TQStringList& ) : TDevPlugin(&data, parent, name ? name : "ctags2Part" ) { setInstance(CTags2Factory::instance()); setXMLFile("tdevpart_ctags2.rc"); TQDomDocument & dom = *projectDom(); TQString customTagFile = DomUtil::readEntry( dom, "/ctagspart/customTagfilePath" ); if ( customTagFile.isEmpty() ) { customTagFile = project()->projectDirectory() + "/tags"; } TQStringList tagFiles = DomUtil::readListEntry(dom, "/ctagspart/activeTagsFiles", "file"); tagFiles.push_front(customTagFile); Tags::setTagFiles(tagFiles); m_widget = new CTags2Widget(this); TQWhatsThis::add(m_widget, i18n("CTags

Result view for a tag lookup. Click a line to go to the corresponding place in the code.")); m_widget->setCaption(i18n("CTags Lookup")); mainWindow()->embedOutputView( m_widget, i18n( "CTags" ), i18n( "CTags lookup results" ) ); connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)), this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) ); _configProxy = new ConfigWidgetProxy( core() ); _configProxy->createProjectConfigPage( i18n("CTags"), CTAGSSETTINGSPAGE, info()->icon() ); connect( _configProxy, TQT_SIGNAL(insertConfigWidget(const KDialogBase*, TQWidget*, unsigned int )), this, TQT_SLOT(insertConfigWidget(const KDialogBase*, TQWidget*, unsigned int )) ); new TDEAction( i18n("Lookup Current Text"), 0, CTRL+Key_Underscore, this, TQT_SLOT(slotLookup()), actionCollection(), "ctags_lookup_shortcut"); new TDEAction( i18n("Lookup Current Text as Declaration"), 0, CTRL+Key_Semicolon, this, TQT_SLOT(slotLookupDeclaration()), actionCollection(), "ctags_declaration_shortcut"); new TDEAction( i18n("Lookup Current Text as Definition"), 0, CTRL+Key_Colon, this, TQT_SLOT(slotLookupDefinition()), actionCollection(), "ctags_definition_shortcut"); new TDEAction( i18n("Jump to Next Match"), 0, 0, this, TQT_SLOT(slotGoToNext()), actionCollection(), "ctags_jump_to_next"); new TDEAction( i18n("Open Lookup Dialog"), 0, 0, this, TQT_SLOT(slotOpenLookup()), actionCollection(), "ctags_input_shortcut"); } CTags2Part::~CTags2Part() { if ( m_widget ) { mainWindow()->removeView( m_widget ); } delete m_widget; delete _configProxy; } void CTags2Part::insertConfigWidget( const KDialogBase * dlg, TQWidget * page, unsigned int pagenumber ) { if ( pagenumber == CTAGSSETTINGSPAGE ) { CTags2SettingsWidget * w = new CTags2SettingsWidget( this, page ); connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(slotAccept()) ); connect( w, TQT_SIGNAL(newTagsfileName(const TQString& )), this, TQT_SLOT(updateTagsfileName(const TQString& )) ); } } void CTags2Part::updateTagsfileName( const TQString & ) { m_widget->updateDBDateLabel(); } // wrapper for creating a tag file for the current project bool CTags2Part::createTagsFile() { // check if user specified a custom tag file name TQDomDocument & dom = *projectDom(); TQString tagsFileCustom = DomUtil::readEntry( dom, "/ctagspart/customTagfilePath" ).stripWhiteSpace(); return createTagsFile(tagsFileCustom, project()->projectDirectory()); } // creates a new tag file with the specified name for the specified source directory bool CTags2Part::createTagsFile(const TQString& tagFile, const TQString& dir) { /* TDEProcess proc; proc.setWorkingDirectory( project()->projectDirectory() ); proc << "ctags"; proc << "-R" << "--c++-types=+px" << "--excmd=pattern" << "--exclude=Makefile"; bool success = proc.start(TDEProcess::Block); return success; */ // get name of the ctags binary TDEConfig * config = kapp->config(); config->setGroup( "CTAGS" ); TQString ctagsBinary = config->readEntry( "ctags binary" ).stripWhiteSpace(); if ( ctagsBinary.isEmpty() ) { ctagsBinary = "ctags"; } // set a default argument list TQString argsDefault = "-R --c++-types=+px --excmd=pattern --exclude=Makefile --exclude=."; TQDomDocument & dom = *projectDom(); TQString argsCustom = DomUtil::readEntry( dom, "/ctagspart/customArguments" ).stripWhiteSpace(); TQString commandline = ctagsBinary + " " + ( argsCustom.isEmpty() ? argsDefault : argsCustom ) + ( tagFile.isEmpty() ? "" : " -f " + tagFile ); commandline += " "; commandline += dir; if (TDevAppFrontend *appFrontend = extension("TDevelop/AppFrontend")) appFrontend->startAppCommand(dir, commandline, false); return true; } void CTags2Part::contextMenu(TQPopupMenu *popup, const Context *context) { if (!context->hasType( Context::EditorContext )) return; const EditorContext *econtext = static_cast(context); TQString ident = econtext->currentWord(); if (ident.isEmpty()) return; TDEConfig * config = kapp->config(); config->setGroup( "CTAGS" ); bool showDeclaration = config->readBoolEntry( "ShowDeclaration", true ); bool showDefinition = config->readBoolEntry( "ShowDefinition", true ); bool showLookup = config->readBoolEntry( "ShowLookup", true ); if ( Tags::hasTag( ident ) && ( showDefinition || showDeclaration || showLookup ) ) { m_contextString = ident; TQString squeezed = KStringHandler::csqueeze(ident, 30); popup->insertSeparator(); if ( showDeclaration ) popup->insertItem( i18n("CTags - Go to Declaration: %1").arg(squeezed), this, TQT_SLOT(slotGotoDeclaration()) ); if ( showDefinition ) popup->insertItem( i18n("CTags - Go to Definition: %1").arg(squeezed), this, TQT_SLOT(slotGotoDefinition()) ); if ( showLookup ) popup->insertItem( i18n("CTags - Lookup: %1").arg(squeezed), this, TQT_SLOT(slotGotoTag()) ); } } void CTags2Part::showHits( Tags::TagList const & tags ) { m_widget->displayHitsAndClear( tags ); mainWindow()->raiseView( m_widget ); m_widget->output_view->setFocus(); } void CTags2Part::slotGotoTag( ) { showHits( Tags::getExactMatches( m_contextString ) ); } void CTags2Part::gotoTagForTypes( TQStringList const & types ) { Tags::TagList list = Tags::getMatches( m_contextString, false, types ); if ( list.count() < 1 ) return; TDEConfig * config = kapp->config(); config->setGroup("CTAGS"); bool jumpToFirst = config->readBoolEntry( "JumpToFirst", false ); if ( list.count() == 1 || jumpToFirst ) { Tags::TagEntry tag = list.first(); KURL url; TQString fileWithTagInside; // assume relative path to project directory if path does not start with slash if (tag.file[0] != '/') { fileWithTagInside = project()->projectDirectory() + "/" + tag.file; } else { fileWithTagInside = tag.file; } url.setPath(fileWithTagInside); partController()->editDocument( url, getFileLineFromPattern( url, tag.pattern ) ); m_widget->displayHitsAndClear( list ); } else { showHits( list ); } } void CTags2Part::slotGotoDefinition( ) { TQStringList types; types << "S" << "d" << "f" << "t" << "v"; gotoTagForTypes( types ); } void CTags2Part::slotGotoDeclaration( ) { TQStringList types; types << "L" << "c" << "e" << "g" << "m" << "n" << "p" << "s" << "u" << "x"; gotoTagForTypes( types ); } int CTags2Part::getFileLineFromStream( TQTextStream & istream, TQString const & pattern ) { if ( pattern.isEmpty() ) return -1; // ctags interestingly escapes "/", but apparently nothing else. lets revert that TQString unescaped = pattern; unescaped.replace( "\\/", "/" ); // most of the time, the ctags pattern has the form /^foo$/ // but this isn't true for some macro definitions // where the form is only /^foo/ // I have no idea if this is a ctags bug or not, but we have to deal with it TQString reduced, escaped, re_string; if ( unescaped.endsWith( "$/" ) ) { reduced = unescaped.mid( 2, unescaped.length() -4 ); escaped = TQRegExp::escape( reduced ); re_string = TQString( "^" + escaped + "$" ); } else { reduced = unescaped.mid( 2, unescaped.length() -3 ); escaped = TQRegExp::escape( reduced ); re_string = TQString( "^" + escaped ); } TQRegExp re( re_string ); int n = 0; while ( !istream.atEnd() ) { if ( re.search( istream.readLine() ) > -1 ) { return n; } n++; } return -1; } int CTags2Part::getFileLineFromPattern( KURL const & url, TQString const & pattern ) { // if the file is open - get the line from the editor buffer if ( KTextEditor::EditInterface * ei = dynamic_cast( partController()->partForURL( url ) ) ) { TQString ibuffer = ei->text(); TQTextStream istream( &ibuffer, IO_ReadOnly ); return getFileLineFromStream( istream, pattern ); } else // else the file is not open - get the line from the file on disk { TQFile file( url.path() ); TQString buffer; if ( file.open( IO_ReadOnly ) ) { TQTextStream istream( &file ); return getFileLineFromStream( istream, pattern ); } } return -1; } void CTags2Part::slotLookupDeclaration( ) { m_contextString = TDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( !m_contextString.isEmpty() ) { slotGotoDeclaration(); } } void CTags2Part::slotLookupDefinition( ) { m_contextString = TDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( !m_contextString.isEmpty() ) { slotGotoDefinition(); } } void CTags2Part::slotLookup( ) { m_contextString = TDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( !m_contextString.isEmpty() ) { slotGotoTag(); } } void CTags2Part::slotOpenLookup( ) { mainWindow()->raiseView( m_widget ); m_widget->input_edit->setFocus(); } void CTags2Part::slotGoToNext( ) { m_widget->goToNext(); } #include "ctags2_part.moc" // kate: space-indent off; indent-width 4; tab-width 4; show-tabs off;