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.
tdevelop/parts/ctags2/ctags2_part.cpp

370 lines
11 KiB

/***************************************************************************
* 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 <tqwhatsthis.h>
#include <tqpopupmenu.h>
#include <tqtextstream.h>
#include <tqfile.h>
#include <tqregexp.h>
#include <klineedit.h>
#include <tdelistview.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdeparts/part.h>
#include <tdetexteditor/document.h>
#include <tdetexteditor/editinterface.h>
#include <tdetexteditor/viewcursorinterface.h>
#include <kprocess.h>
#include <kdebug.h>
#include <kstringhandler.h>
#include <kdialogbase.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <tdeaction.h>
#include "kdevappfrontend.h"
#include <kdevgenericfactory.h>
#include <kdevcore.h>
#include <kdevmainwindow.h>
#include <kdevproject.h>
#include <kdevpartcontroller.h>
#include <kdevplugininfo.h>
#include "configwidgetproxy.h"
#include "domutil.h"
#include "kdeveditorutil.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 KDevGenericFactory<CTags2Part> CTags2Factory;
static const KDevPluginInfo pluginData("kdevctags2");
K_EXPORT_COMPONENT_FACTORY( libkdevctags2, CTags2Factory( pluginData ) )
CTags2Part::CTags2Part(TQObject *parent, const char *name, const TQStringList& )
: KDevPlugin(&pluginData, parent, name ? name : "ctags2Part" )
{
setInstance(CTags2Factory::instance());
setXMLFile("kdevpart_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("<b>CTags</b><p>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 (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("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<const EditorContext*>(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<KTextEditor::EditInterface*>( 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 = KDevEditorUtil::currentWord( dynamic_cast<KTextEditor::Document*>( partController()->activePart() ) );
if ( !m_contextString.isEmpty() )
{
slotGotoDeclaration();
}
}
void CTags2Part::slotLookupDefinition( )
{
m_contextString = KDevEditorUtil::currentWord( dynamic_cast<KTextEditor::Document*>( partController()->activePart() ) );
if ( !m_contextString.isEmpty() )
{
slotGotoDefinition();
}
}
void CTags2Part::slotLookup( )
{
m_contextString = KDevEditorUtil::currentWord( dynamic_cast<KTextEditor::Document*>( 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"