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.
372 lines
11 KiB
372 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 data("kdevctags2");
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevctags2, CTags2Factory( data ) )
|
|
|
|
CTags2Part::CTags2Part(TQObject *parent, const char *name, const TQStringList& )
|
|
: KDevPlugin(&data, 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"
|
|
|
|
// kate: space-indent off; indent-width 4; tab-width 4; show-tabs off;
|