00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "ctagspart.h"
00013
00014 #include <qapplication.h>
00015 #include <qfileinfo.h>
00016 #include <qpopupmenu.h>
00017 #include <klocale.h>
00018 #include <kdevgenericfactory.h>
00019 #include <kaction.h>
00020 #include <kdebug.h>
00021 #include <kmainwindow.h>
00022 #include <kmessagebox.h>
00023 #include <kprocess.h>
00024 #include <qregexp.h>
00025 #include <ktempfile.h>
00026 #include <kstringhandler.h>
00027
00028 #include "kdevcore.h"
00029 #include "kdevproject.h"
00030 #include "kdevmainwindow.h"
00031 #include "kdevpartcontroller.h"
00032 #include "ctagskinds.h"
00033 #include "ctagsdlg.h"
00034
00035
00036 typedef KDevGenericFactory<CTagsPart> CTagsFactory;
00037 static const KAboutData data("kdevctags", I18N_NOOP("CTags..."), "1.0");
00038 K_EXPORT_COMPONENT_FACTORY( libkdevctags, CTagsFactory( &data ) )
00039
00040 CTagsPart::CTagsPart( QObject *parent, const char *name, const QStringList & )
00041 : KDevPlugin("CTags", "ctags", parent, name ? name : "CTagsPart")
00042 {
00043 setInstance(CTagsFactory::instance());
00044 setXMLFile("kdevctags.rc");
00045
00046 KAction *action;
00047
00048 action = new KAction( i18n("CTags..."), 0,
00049 this, SLOT(slotSearchTags()),
00050 actionCollection(), "tools_ctags" );
00051 action->setToolTip(i18n("CTags dialog"));
00052 action->setWhatsThis(i18n("<b>CTags</b><p>Creates a tags database and provides a dialog to search it."));
00053
00054 mOccuresTagsDlg = 0;
00055 mOccuresTagsDlg = new OccuresTagsDlg;
00056 mOccuresTagsDlg->hide();
00057
00058 connect( mOccuresTagsDlg->mOcurresList, SIGNAL(clicked( QListBoxItem * )),
00059 this, SLOT(slotGotoTag( QListBoxItem * )) );
00060 connect( core(), SIGNAL(projectClosed()),
00061 this, SLOT(projectClosed()) );
00062 connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
00063 this, SLOT(contextMenu(QPopupMenu *, const Context *)) );
00064
00065 m_tags = 0;
00066 m_dialog = 0;
00067 }
00068
00069
00070 CTagsPart::~CTagsPart()
00071 {
00072 delete m_dialog;
00073 delete m_tags;
00074 delete mOccuresTagsDlg;
00075 }
00076
00077
00078 void CTagsPart::projectClosed()
00079 {
00080 delete m_dialog;
00081 delete m_tags;
00082 delete mOccuresTagsDlg;
00083 m_dialog = 0;
00084 m_tags = 0;
00085 mOccuresTagsDlg = 0;
00086 }
00087
00088
00089 void CTagsPart::contextMenu(QPopupMenu *popup, const Context *context)
00090 {
00091 if (!context->hasType( Context::EditorContext ))
00092 return;
00093
00094 const EditorContext *econtext = static_cast<const EditorContext*>(context);
00095 QString ident = econtext->currentWord();
00096 if (ident.isEmpty())
00097 return;
00098
00099 m_contextString = ident;
00100 QString squeezed = KStringHandler::csqueeze(ident, 30);
00101 int id = popup->insertItem( i18n("Go to ctags Declaration: %1").arg(squeezed),
00102 this, SLOT(slotGotoDeclaration()) );
00103 popup->setWhatsThis(id, i18n("<b>Go to ctags declaration</b><p>Searches in the tags database for a symbol "
00104 "under the cursor and opens a file that contains the symbol declaration."));
00105 id = popup->insertItem( i18n("Go to ctags Definition: %1").arg(squeezed),
00106 this, SLOT(slotGotoDefinition()) );
00107 popup->setWhatsThis(id, i18n("<b>Go to ctags definition</b><p>Searches in the tags database for a symbol "
00108 "under the cursor and opens a file that contains the symbol definition."));
00109 }
00110
00111
00112 void CTagsPart::gotoTag(const QString &tag, const QString &kindChars)
00113 {
00114 if (!ensureTagsLoaded())
00115 return;
00116
00117 QString fileName, pattern;
00118 QStringList occuresList;
00119
00120 CTagsMapIterator result = m_tags->find(tag);
00121 if (result != m_tags->end()) {
00122 CTagsTagInfoListConstIterator it;
00123 for (it = (*result).begin(); it != (*result).end(); ++it)
00124 {
00125 if (kindChars.find((*it).kind) != -1) {
00126 fileName = (*it).fileName;
00127 pattern = (*it).pattern;
00128 occuresList.append( fileName+":"+pattern );
00129 }
00130 }
00131 }
00132
00133 if (fileName.isNull()) {
00134 KMessageBox::sorry(0, i18n("Tag not found"));
00135 return;
00136 }
00137
00138 if ( occuresList.count() > 1 ) {
00139 mOccuresTagsDlg->mOcurresList->clear();
00140 mOccuresTagsDlg->mOcurresList->insertStringList( occuresList );
00141 mOccuresTagsDlg->show();
00142 }
00143 else
00144 gotoFinalTag( occuresList[0] );
00145 }
00146
00147 void CTagsPart::slotGotoTag( QListBoxItem *item )
00148 {
00149 if ( item )
00150 gotoFinalTag( item->text() );
00151 }
00152
00153 void CTagsPart::gotoFinalTag( const QString & contextStr )
00154 {
00155 mOccuresTagsDlg->hide();
00156
00157 QString fileName = contextStr.section( ':', 0, 0 );
00158 QString pattern = contextStr.section( ':', -1 );
00159
00160 bool ok;
00161 int lineNum = pattern.toInt(&ok);
00162 if (!ok) {
00163 KMessageBox::sorry(0, i18n("Currently, only tags with line numbers (option -n) are supported"));
00164 return;
00165 }
00166
00167 partController()->editDocument(KURL::fromPathOrURL( fileName ), lineNum-1);
00168 }
00169
00170
00171 void CTagsPart::slotGotoDeclaration()
00172 {
00173 gotoTag(m_contextString, "Lcegmnpsux");
00174 }
00175
00176
00177 void CTagsPart::slotGotoDefinition()
00178 {
00179 gotoTag(m_contextString, "Sdftv");
00180 }
00181
00182
00183 void CTagsPart::slotSearchTags()
00184 {
00185 if (!m_dialog) {
00186 if( ensureTagsLoaded() )
00187 m_dialog = new CTagsDialog(this);
00188 }
00189
00190 if (m_dialog)
00191 m_dialog->show();
00192 }
00193
00194
00195 bool CTagsPart::ensureTagsLoaded()
00196 {
00197 if (m_tags)
00198 return true;
00199 if (!project())
00200 return false;
00201
00202 kdDebug(9022) << "create/load tags" << endl;
00203
00204 QFileInfo fi(project()->projectDirectory() + "/tags");
00205 if (!fi.exists()) {
00206 int r = KMessageBox::questionYesNo(mainWindow()->main(), i18n("A ctags file for this project does not exist yet. Create it now?"));
00207 if (r != KMessageBox::Yes)
00208 return false;
00209 if (!createTagsFile()) {
00210 KMessageBox::sorry(mainWindow()->main(), i18n("Could not create tags file.\n\nPlease make sure 'ctags' can be found in your PATH."));
00211 return false;
00212 }
00213 }
00214
00215 kdDebug(9022) << "load tags from " << endl;
00216 return loadTagsFile();
00217 }
00218
00219
00220 bool CTagsPart::loadTagsFile()
00221 {
00222 kdDebug(9022) << "load tags file" << endl;
00223
00224 QFile f(project()->projectDirectory() + "/tags");
00225 if (!f.open(IO_ReadOnly))
00226 return false;
00227
00228 if (m_tags)
00229 m_tags->clear();
00230 else
00231 m_tags = new CTagsMap;
00232 m_kindStrings.clear();
00233
00234 QTextStream stream(&f);
00235 QRegExp re("^([^\t]*)\t([^\t]*)\t([^;]*);\"\t(.*)$");
00236
00237 QString line;
00238 while (!stream.atEnd()) {
00239 line = stream.readLine().latin1();
00240
00241 if (re.search(line) == -1)
00242 continue;
00243
00244
00245 QString tag = re.cap(1);
00246 QString file = re.cap(2);
00247 QString pattern = re.cap(3);
00248 QString extfield = re.cap(4);
00249
00250
00251 CTagsMapIterator tiit = m_tags->find(tag);
00252 if (tiit == m_tags->end())
00253 tiit = m_tags->insert(tag, CTagsTagInfoList());
00254
00255 CTagsTagInfo ti;
00256 ti.fileName = re.cap(2);
00257 ti.pattern = re.cap(3);
00258 ti.kind = re.cap(4)[0].latin1();
00259 (*tiit).append(ti);
00260
00261
00262 QString extension;
00263 if (ti.fileName.right(9) == "/Makefile")
00264 extension = "mak";
00265 else {
00266 int pos = ti.fileName.findRev('.');
00267 if (pos > 0)
00268 extension = ti.fileName.mid(pos+1);
00269 }
00270 if (extension.isNull())
00271 continue;
00272
00273 QString kindString = CTagsKinds::findKind(ti.kind, extension);
00274 if (kindString.isNull())
00275 continue;
00276
00277 if (!m_kindStrings.contains(kindString))
00278 m_kindStrings.append(kindString);
00279 }
00280
00281 f.close();
00282
00283 return true;
00284
00285 #if 0
00286 QDictIterator<CTagsTagInfoList> it(tags);
00287 for (; it.current(); ++it) {
00288 kdDebug() << "Id: " << it.currentKey() << endl;
00289 CTagsTagInfoList *l = it.current();
00290 QValueList<CTagsTagInfo>::ConstIterator it2;
00291 for (it2 = l->begin(); it2 != l->end(); ++it2)
00292 kdDebug() << "at " << (*it2).fileName << "," << (*it2).pattern << endl;
00293 }
00294 #endif
00295 }
00296
00297
00298 bool CTagsPart::createTagsFile()
00299 {
00300 kdDebug(9022) << "create tags file" << endl;
00301
00302 KProcess proc;
00303 proc.setWorkingDirectory( project()->projectDirectory() );
00304
00305 QStringList l = project()->allFiles();
00306
00307 KTempFile ifile;
00308 QTextStream& is = *ifile.textStream();
00309 is << l.join( "\n" );
00310 is << "\n";
00311 ifile.close();
00312
00313 proc << "ctags";
00314 proc << "-n";
00315 proc << "--c++-types=+px";
00316 proc << "-L" << ifile.name();
00317
00318 QApplication::setOverrideCursor(Qt::waitCursor);
00319 bool success = proc.start(KProcess::Block);
00320 QApplication::restoreOverrideCursor();
00321
00322 return success;
00323 }
00324
00325 #include "ctagspart.moc"