KDevelop API Documentation

bashsupport_part.cpp

Go to the documentation of this file.
00001 /*
00002 * $Id: bashsupport_part.cpp,v 1.18 2004/06/21 17:36:30 tobgle Exp $
00003 *  Copyright (C) 2003 Ian Reinhart Geiser <geiseri@kde.org>
00004 */
00005 #include "bashsupport_part.h"
00006 
00007 #include <qwhatsthis.h>
00008 
00009 #include <qfileinfo.h>
00010 #include <qstringlist.h>
00011 #include <qtextstream.h>
00012 #include <qtimer.h>
00013 #include <kapplication.h>
00014 #include <qregexp.h>
00015 
00016 #include <kiconloader.h>
00017 #include <klocale.h>
00018 #include <kdevgenericfactory.h>
00019 #include <kprocess.h>
00020 #include <kdebug.h>
00021 #include <kaction.h>
00022 #include <kparts/part.h>
00023 #include <kdialogbase.h>
00024 
00025 
00026 #include <kdevcore.h>
00027 #include <kdevmainwindow.h>
00028 #include <kdevlanguagesupport.h>
00029 #include <kdevpartcontroller.h>
00030 #include <kdevproject.h>
00031 #include <kdevappfrontend.h>
00032 #include <domutil.h>
00033 #include <codemodel.h>
00034 
00035 typedef KDevGenericFactory<BashSupportPart> BashSupportFactory;
00036 static const KAboutData data("kdevbashsupport", I18N_NOOP("Language"), "1.0");
00037 K_EXPORT_COMPONENT_FACTORY( libkdevbashsupport, BashSupportFactory( &data ) )
00038 
00039 BashSupportPart::BashSupportPart(QObject *parent, const char *name, const QStringList& )
00040 : KDevLanguageSupport ("KDevPart", "kdevpart", parent, name ? name : "BashSupportPart" )
00041 {
00042     setInstance(BashSupportFactory::instance());
00043     setXMLFile("kdevbashsupport.rc");
00044 
00045     KAction *action;
00046     action = new KAction( i18n("&Run"), "exec",Key_F9,this, SLOT(slotRun()),actionCollection(), "build_execute" );
00047     action->setToolTip(i18n("Run"));
00048     action->setWhatsThis(i18n("<b>Run</b><p>Starts an application."));
00049 
00050     kdDebug() << "Creating BashSupportPart" << endl;
00051 
00052     connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
00053         this, SLOT(projectConfigWidget(KDialogBase*)) );
00054     connect( core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
00055     connect( core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()) );
00056     connect( partController(), SIGNAL(savedFile(const KURL&)), this, SLOT(savedFile(const KURL&)) );
00057     connect(partController(), SIGNAL(activePartChanged(KParts::Part*)),
00058         this, SLOT(slotActivePartChanged(KParts::Part *)));
00059 
00060     m_cc = new BashCodeCompletion();
00061 }
00062 
00063 
00064 BashSupportPart::~BashSupportPart()
00065 {
00066     delete( m_cc );
00067     m_cc = 0;
00068 }
00069 
00070 
00071 void BashSupportPart::projectConfigWidget(KDialogBase *dlg)
00072 {
00073     Q_UNUSED( dlg );
00074 //  QVBox *vbox = dlg->addVBoxPage(i18n("Bash"));
00075 //  RubyConfigWidget *w = new RubyConfigWidget(*projectDom(), (QWidget *)vbox, "Bash config widget");
00076 //  connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00077 }
00078 
00079 void BashSupportPart::projectOpened()
00080 {
00081     kdDebug(9014) << "projectOpened()" << endl;
00082 
00083     connect( project(), SIGNAL(addedFilesToProject(const QStringList &)),
00084         this, SLOT(addedFilesToProject(const QStringList &)) );
00085     connect( project(), SIGNAL(removedFilesFromProject(const QStringList &)),
00086         this, SLOT(removedFilesFromProject(const QStringList &)) );
00087 
00088     // We want to parse only after all components have been
00089     // properly initialized
00090     QTimer::singleShot(0, this, SLOT(parse()));
00091 }
00092 
00093 
00094 void BashSupportPart::projectClosed()
00095 {
00096 
00097 }
00098 
00099 void BashSupportPart::slotRun ()
00100 {
00101     QString file;
00102     KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
00103     if(ro_part)
00104         file = ro_part->url().path();
00105 
00106     QString cmd = interpreter() + " " + file;
00107     startApplication(cmd);
00108 }
00109 
00110 QString BashSupportPart::interpreter()
00111 {
00112     QString prog = DomUtil::readEntry(*projectDom(), "/kdevrbashsupport/run/interpreter");
00113     if (prog.isEmpty())
00114         prog = "bash";
00115     return prog;
00116 }
00117 
00118 void BashSupportPart::parse()
00119 {
00120     kdDebug(9014) << "initialParse()" << endl;
00121 
00122     if (project())
00123     {
00124         kapp->setOverrideCursor(waitCursor);
00125         QStringList files = project()->allFiles();
00126         for (QStringList::Iterator it = files.begin(); it != files.end() ;++it)
00127         {
00128             kdDebug(9014) << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
00129             parse(project()->projectDirectory() + "/" + *it);
00130         }
00131         emit updatedSourceInfo();
00132         kapp->restoreOverrideCursor();
00133     } else {
00134         kdDebug(9014) << "No project" << endl;
00135     }
00136 }
00137 
00138 void BashSupportPart::addedFilesToProject(const QStringList &fileList)
00139 {
00140 kdDebug(9014) << "addedFilesToProject()" << endl;
00141 
00142     QStringList::ConstIterator it;
00143 
00144     for ( it = fileList.begin(); it != fileList.end(); ++it )
00145     {
00146         parse(project()->projectDirectory() + "/" + ( *it ) );
00147     }
00148 
00149     emit updatedSourceInfo();
00150 }
00151 
00152 
00153 void BashSupportPart::removedFilesFromProject(const QStringList &fileList)
00154 {
00155     kdDebug(9014) << "removedFilesFromProject()" << endl;
00156 
00157     QStringList::ConstIterator it;
00158 
00159     for ( it = fileList.begin(); it != fileList.end(); ++it )
00160     {
00161         QString fileName = project()->projectDirectory() + "/" + ( *it );
00162         if( codeModel()->hasFile(fileName) ){
00163             emit aboutToRemoveSourceInfo( fileName );
00164             codeModel()->removeFile( codeModel()->fileByName(fileName) );
00165         }
00166     }
00167 
00168     //emit updatedSourceInfo();
00169 }
00170 
00171 void BashSupportPart::savedFile(const KURL &fileName)
00172 {
00173     kdDebug(9014) << "savedFile()" << endl;
00174 
00175     if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 )))
00176     {
00177         parse(fileName.path());
00178         emit addedSourceInfo( fileName.path() );
00179     }
00180 }
00181 
00182 void BashSupportPart::startApplication(const QString &program)
00183 {
00184     kdDebug() << "starting application" << program << endl;
00185     appFrontend()->startAppCommand(QString::QString(), program, TRUE);
00186 }
00187 
00188 
00189 KDevLanguageSupport::Features BashSupportPart::features()
00190 {
00191     return Features(Variables | Functions);
00192 }
00193 
00194 void BashSupportPart::parse(const QString &fileName)
00195 {
00196     QFileInfo fi(fileName);
00197     m_vars.clear();
00198     if (fi.extension() == "sh")
00199     {
00200         if( codeModel()->hasFile(fileName) ){
00201             emit aboutToRemoveSourceInfo( fileName );
00202             codeModel()->removeFile( codeModel()->fileByName(fileName) );
00203         }
00204 
00205         FileDom m_file = codeModel()->create<FileModel>();
00206         m_file->setName( fileName );
00207 
00208         m_vars.clear();
00209         QFile f(QFile::encodeName(fileName));
00210         if (!f.open(IO_ReadOnly))
00211             return;
00212         QString rawline;
00213         QString line;
00214         uint lineNo = 0;
00215         //KRegExp methodre("\\b([\\d\\w]+[\\s]*)\\([\\s]*\\)");
00216         QRegExp methodre("^\\s*(\\w+)\\s*\\(\\s*\\)");
00217         QRegExp varre( "^\\s*(\\w+)[=]" );
00218         QRegExp expvarre( "^export\\s*(\\w+)[=]" );
00219         QRegExp forvarre("\\bfor[\\s]+([\\d\\w]+)[\\s]+in[\\s]+");
00220 
00221         QTextStream stream(&f);
00222         while (!stream.atEnd())
00223         {
00224              rawline = stream.readLine();
00225                  line = rawline.stripWhiteSpace().local8Bit();
00226              kdDebug() << "Trying line: " << line << endl;
00227              if (methodre.search(line) != -1)
00228              {
00229                 FunctionDom method = codeModel()->create<FunctionModel>();
00230                 method->setName(methodre.cap(1));
00231                 method->setFileName(fileName);
00232                 method->setStartPosition(lineNo, 0);
00233 
00234                 if( !m_file->hasFunction(method->name()) ){
00235                     kdDebug() << "Add global method " << method->name() << endl;
00236                     m_file->addFunction( method );
00237                 }
00238             }
00239             else if(varre.search(line) != -1)
00240             {
00241                 addAttribute(varre.cap(1), m_file, lineNo);
00242             }
00243             else if(expvarre.search(line) != -1)
00244             {
00245                 addAttribute(expvarre.cap(1), m_file, lineNo);
00246             }
00247             else if(forvarre.search(line) != -1)
00248             {
00249                 addAttribute(forvarre.cap(1), m_file, lineNo);
00250             }
00251             ++lineNo;
00252         }
00253         f.close();
00254 
00255         kdDebug() << "Trying to add list..." << endl;
00256         codeModel()->addFile( m_file );
00257         VariableList attrList = codeModel()->globalNamespace()->variableList();
00258         for (VariableList::Iterator it = attrList.begin(); it != attrList.end(); ++it)
00259         {
00260             kdDebug() << "Adding " << (*it)->name() << endl;
00261             m_vars.append((*it)->name());
00262         }
00263         m_cc->setVars(m_vars);
00264 
00265         codeModel()->addFile( m_file );
00266     }
00267 
00268 }
00269 
00270 void BashSupportPart::slotActivePartChanged(KParts::Part *part)
00271 {
00272     kdDebug() << "Changeing part..." << endl;
00273     m_cc->setActiveEditorPart(part);
00274 }
00275 
00276 void BashSupportPart::addAttribute(const QString &name, FileDom file, uint lineNo)
00277 {
00278     VariableDom var = codeModel()->create<VariableModel>();
00279     var->setName(name);
00280     var->setFileName(file->name());
00281     var->setStartPosition( lineNo, 0 );
00282     var->setType(i18n("Variable"));
00283 
00284     if( !file->hasVariable(var->name()) ){
00285         kdDebug() << "Add global attribute " << var->name() << endl;
00286         file->addVariable(var);
00287     }
00288 }
00289 
00290 BashCodeCompletion::BashCodeCompletion()
00291 {
00292     m_argWidgetShow = false;
00293     m_completionBoxShow=false;
00294 }
00295 
00296 BashCodeCompletion::~BashCodeCompletion()
00297 {
00298 
00299 }
00300 
00301 void BashCodeCompletion::setActiveEditorPart(KParts::Part *part)
00302 {
00303     if (!part || !part->widget())
00304         return;
00305 
00306     kdDebug() << "BashCodeCompletion::setActiveEditorPart"  << endl;
00307 
00308 // We need to think about this
00309 //  if(!(m_config->getCodeCompletion() || m_config->getCodeHinting())){
00310 //      return; // no help
00311 //  }
00312 
00313     m_editInterface = dynamic_cast<KTextEditor::EditInterface*>(part);
00314     if (!m_editInterface)
00315     {
00316         kdDebug() << "editor doesn't support the EditDocumentIface" << endl;
00317         return;
00318     }
00319 
00320     m_cursorInterface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
00321     if (!m_cursorInterface)
00322     {
00323         kdDebug() << "editor does not support the ViewCursorInterface" << endl;
00324         return;
00325     }
00326 
00327     m_codeInterface = dynamic_cast<KTextEditor::CodeCompletionInterface*>(part->widget());
00328     if (!m_codeInterface) { // no CodeCompletionDocument available
00329         kdDebug() << "editor doesn't support the CodeCompletionDocumentIface" << endl;
00330         return;
00331     }
00332 
00333     disconnect(part->widget(), 0, this, 0 ); // to make sure that it is't connected twice
00334     connect(part->widget(), SIGNAL(cursorPositionChanged()),
00335         this, SLOT(cursorPositionChanged()));
00336     connect(part->widget(), SIGNAL(argHintHidden()), this, SLOT(argHintHidden()));
00337     connect(part->widget(), SIGNAL(completionAborted()), this, SLOT(completionBoxAbort()));
00338     connect(part->widget(), SIGNAL(completionDone()), this, SLOT(completionBoxHidden()));
00339 
00340 }
00341 
00342 void BashCodeCompletion::setVars(QStringList lst)
00343 {
00344     m_vars = lst;
00345 }
00346 
00347 QValueList<KTextEditor::CompletionEntry> BashCodeCompletion::getVars(const QString &startText)
00348 {
00349     kdDebug() << "getVars for " << startText << endl;
00350     QValueList<KTextEditor::CompletionEntry> varList;
00351     QValueList<QString>::ConstIterator it;
00352     for (it = m_vars.begin(); it != m_vars.end(); ++it) {
00353         QString var = "$" + (*it);
00354         kdDebug() << "Compair " << var << endl;
00355         if( var.startsWith( startText ))
00356         {
00357           KTextEditor::CompletionEntry e;
00358           e.text = var;
00359           //e.postfix ="";
00360           //e.prefix ="";
00361           kdDebug() << "getVar: " << var << endl;
00362           varList.append(e);
00363         }
00364     }
00365 
00366     return varList;
00367 }
00368 
00369 void BashCodeCompletion::cursorPositionChanged()
00370 {
00371     uint line, col;
00372     m_cursorInterface->cursorPositionReal(&line, &col);
00373     kdDebug() << "BashCodeCompletion::cursorPositionChanged:" << line << ":" << col  << endl;
00374 
00375     QString lineStr = m_editInterface->textLine(line);
00376     if(lineStr.isNull() || lineStr.isEmpty()){
00377         kdDebug() << "No Text..." << endl;
00378         return; // nothing to do
00379     }
00380 //  if(m_config->getCodeCompletion())
00381 //  {
00382         QString restLine = lineStr.mid(col);
00383         QString prevText = lineStr.mid(0,col);
00384 
00385         if(restLine.left(1) != " " && restLine.left(1) != "\t" && !restLine.isNull())
00386         {
00387             kdDebug() << "no codecompletion because no empty character after cursor:" << restLine << ":" << endl;
00388             return;
00389         }
00390 
00391         QRegExp prevReg("[$][\\d\\w]*\\b$");
00392 
00393         int pos = prevReg.search( prevText );
00394         if (pos > -1 )
00395         {
00396             // We are in completion mode
00397               QString startMatch = prevReg.cap(0);
00398               kdDebug() << "Matching: " << startMatch << endl;
00399               m_completionBoxShow=true;
00400               m_codeInterface->showCompletionBox(getVars(startMatch),2);
00401         }
00402         else
00403         {
00404             kdDebug() << "no vars in: " << prevText << endl;
00405             return;
00406         }
00407 
00408 //  }
00409 
00410 }
00411 
00412 void BashCodeCompletion::completionBoxHidden()
00413 {
00414     kdDebug() << "Complete..." << endl;
00415     m_completionBoxShow=false;
00416 /*  uint line, col, start;
00417     m_cursorInterface->cursorPositionReal(&line, &col);
00418     QString lineStr = m_editInterface->textLine(line);
00419 
00420     start = lineStr.findRev(QRegExp("[$][\\d\\w]*\\b$"));
00421     m_editInterface->removeText ( start, col, line, col );
00422 */
00423 }
00424 
00425 void BashCodeCompletion::completionBoxAbort()
00426 {
00427     kdDebug() << "aborted..." << endl;
00428     m_completionBoxShow=false;
00429 }
00430 
00431 KMimeType::List BashSupportPart::mimeTypes( )
00432 {
00433     KMimeType::List list;
00434 
00435     KMimeType::Ptr mime = KMimeType::mimeType( "application/x-shellscript" );
00436     if( mime )
00437     list << mime;
00438 
00439     return list;
00440 }
00441 #include "bashsupport_part.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 22 09:22:28 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003