00001 #include <qwhatsthis.h>
00002 #include <qtimer.h>
00003 #include <qfileinfo.h>
00004
00005
00006 #include <kiconloader.h>
00007 #include <klocale.h>
00008 #include <kdevgenericfactory.h>
00009 #include <kprocess.h>
00010 #include <kdebug.h>
00011 #include <kaction.h>
00012 #include <kparts/part.h>
00013 #include <kdialogbase.h>
00014 #include <kapplication.h>
00015 #include <qregexp.h>
00016
00017
00018 #include "kdevcore.h"
00019 #include "kdevmainwindow.h"
00020 #include "kdevlanguagesupport.h"
00021 #include "kdevpartcontroller.h"
00022 #include "kdevproject.h"
00023 #include "kdevappfrontend.h"
00024
00025 #include "rubysupport_part.h"
00026 #include "rubyconfigwidget.h"
00027 #include "domutil.h"
00028
00029 typedef KDevGenericFactory<RubySupportPart> RubySupportFactory;
00030 static const KAboutData data("kdevrubysupport", I18N_NOOP("Language"), "1.0");
00031 K_EXPORT_COMPONENT_FACTORY( libkdevrubysupport, RubySupportFactory( &data ) )
00032
00033 RubySupportPart::RubySupportPart(QObject *parent, const char *name, const QStringList& )
00034 : KDevLanguageSupport ("KDevPart", "kdevpart", parent, name ? name : "RubySupportPart" )
00035 {
00036 setInstance(RubySupportFactory::instance());
00037 setXMLFile("kdevrubysupport.rc");
00038
00039 KAction *action;
00040 action = new KAction( i18n("&Run"), "exec",Key_F9,this, SLOT(slotRun()),actionCollection(), "build_execute" );
00041 action->setToolTip(i18n("Run"));
00042 action->setWhatsThis(i18n("<b>Run</b><p>Starts an application."));
00043
00044 kdDebug() << "Creating RubySupportPart" << endl;
00045
00046 connect( core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
00047 connect( partController(), SIGNAL(savedFile(const KURL&)),
00048 this, SLOT(savedFile(const KURL&)) );
00049 connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
00050 this, SLOT(projectConfigWidget(KDialogBase*)) );
00051 }
00052
00053
00054 RubySupportPart::~RubySupportPart() {
00055 }
00056
00057
00058 void RubySupportPart::projectConfigWidget(KDialogBase *dlg) {
00059 QVBox *vbox = dlg->addVBoxPage(i18n("Ruby"));
00060 RubyConfigWidget *w = new RubyConfigWidget(*projectDom(), (QWidget *)vbox, "ruby config widget");
00061 connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00062 }
00063
00064 void RubySupportPart::projectOpened()
00065 {
00066 kdDebug() << "projectOpened()" << endl;
00067
00068 connect( project(), SIGNAL(addedFilesToProject(const QStringList &)),
00069 this, SLOT(addedFilesToProject(const QStringList &)) );
00070 connect( project(), SIGNAL(removedFilesFromProject(const QStringList &)),
00071 this, SLOT(removedFilesFromProject(const QStringList &)) );
00072
00073
00074
00075 QTimer::singleShot(0, this, SLOT(initialParse()));
00076 }
00077
00078 void RubySupportPart::maybeParse(const QString fileName)
00079 {
00080 QFileInfo fi(fileName);
00081
00082 if (fi.extension() == "rb") {
00083 if (codeModel()->hasFile(fileName)) {
00084 emit aboutToRemoveSourceInfo(fileName);
00085 codeModel()->removeFile(codeModel()->fileByName(fileName));
00086 }
00087
00088 parse(fileName);
00089 }
00090 }
00091
00092 void RubySupportPart::initialParse()
00093 {
00094 kdDebug() << "initialParse()" << endl;
00095
00096 if (project()) {
00097 kapp->setOverrideCursor(waitCursor);
00098 QStringList files = project()->allFiles();
00099 for (QStringList::Iterator it = files.begin(); it != files.end() ;++it) {
00100 kdDebug() << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
00101 maybeParse(project()->projectDirectory() + "/" + *it);
00102 }
00103
00104 emit updatedSourceInfo();
00105 kapp->restoreOverrideCursor();
00106 } else {
00107 kdDebug() << "No project" << endl;
00108 }
00109 }
00110
00111 void RubySupportPart::addedFilesToProject(const QStringList &fileList)
00112 {
00113 kdDebug() << "addedFilesToProject()" << endl;
00114
00115 QStringList::ConstIterator it;
00116
00117 for ( it = fileList.begin(); it != fileList.end(); ++it )
00118 {
00119 QString fileName = project()->projectDirectory() + "/" + ( *it );
00120 maybeParse( fileName );
00121 emit addedSourceInfo( fileName );
00122 }
00123 }
00124
00125 void RubySupportPart::removedFilesFromProject(const QStringList &fileList)
00126 {
00127 kdDebug() << "removedFilesFromProject()" << endl;
00128
00129 QStringList::ConstIterator it;
00130
00131 for ( it = fileList.begin(); it != fileList.end(); ++it )
00132 {
00133 QString fileName = project()->projectDirectory() + "/" + ( *it );
00134
00135 if( codeModel()->hasFile(fileName) ){
00136 emit aboutToRemoveSourceInfo( fileName );
00137 codeModel()->removeFile( codeModel()->fileByName(fileName) );
00138 }
00139 }
00140 }
00141
00142 void RubySupportPart::savedFile(const KURL &fileName)
00143 {
00144 kdDebug() << "savedFile()" << endl;
00145
00146 if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) {
00147 maybeParse(fileName.path());
00148 emit addedSourceInfo( fileName.path() );
00149 }
00150 }
00151
00152 KDevLanguageSupport::Features RubySupportPart::features()
00153 {
00154 return Features(Classes | Functions);
00155 }
00156
00157 void RubySupportPart::parse(const QString &fileName)
00158 {
00159 QFile f(QFile::encodeName(fileName));
00160 if (!f.open(IO_ReadOnly))
00161 return;
00162 QTextStream stream(&f);
00163
00164 QRegExp classre("^\\s*class\\s+([A-Z][A-Za-z0-9_]+)\\s*(<\\s*([A-Z][A-Za-z0-9_:]+))?$");
00165 QRegExp methodre("^\\s*def\\s+([A-Za-z0-9_]+).*$");
00166
00167 FileDom m_file = codeModel()->create<FileModel>();
00168 m_file->setName(fileName);
00169
00170 ClassDom lastClass;
00171 QString rawline;
00172 QCString line;
00173 int lineNo = 0;
00174
00175 while (!stream.atEnd()) {
00176 rawline = stream.readLine();
00177 line = rawline.stripWhiteSpace().local8Bit();
00178 if (classre.search(line) != -1) {
00179 lastClass = codeModel()->create<ClassModel>();
00180 lastClass->setName(classre.cap(1));
00181 lastClass->setFileName( fileName );
00182 lastClass->setStartPosition( lineNo, 0 );
00183
00184 QString parent = classre.cap(3);
00185 if (!parent.isEmpty())
00186 {
00187 kdDebug() << "Add parent " << parent << endl;
00188 lastClass->addBaseClass( parent );
00189 }
00190
00191 if (m_file->hasClass(lastClass->name())) {
00192 ClassDom old = m_file->classByName( lastClass->name() )[ 0 ];
00193 old->setFileName( lastClass->fileName() );
00194
00195 int line, col;
00196 lastClass->getStartPosition( &line, &col );
00197 old->setStartPosition( line, col );
00198
00199 lastClass = old;
00200 } else {
00201 kdDebug() << "Add class " << lastClass->name() << endl;
00202 m_file->addClass( lastClass );
00203 }
00204 } else if (methodre.search(line) != -1) {
00205 FunctionDom method = codeModel()->create<FunctionModel>();
00206 method->setName(methodre.cap(1));
00207 kdDebug() << "Add method: " << method->name() << endl;
00208 method->setFileName( fileName );
00209 method->setStartPosition( lineNo, 0 );
00210
00211 if (lastClass && rawline.left(3) != "def") {
00212 if( !lastClass->hasFunction(method->name()) )
00213 lastClass->addFunction( method );
00214 } else if( !m_file->hasFunction(method->name()) ){
00215 m_file->addFunction( method );
00216 lastClass = 0;
00217 }
00218 }
00219 ++lineNo;
00220 }
00221
00222 f.close();
00223
00224 codeModel()->addFile( m_file );
00225 }
00226
00227
00228 void RubySupportPart::slotRun () {
00229 QString file;
00230 KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
00231 if(ro_part) file = ro_part->url().path();
00232
00233
00234 QString cmd = interpreter() + " " + file;
00235 startApplication(cmd);
00236 }
00237
00238 QString RubySupportPart::interpreter() {
00239 QString prog = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/interpreter");
00240 if (prog.isEmpty()) prog = "ruby";
00241 return prog;
00242 }
00243
00244
00245 void RubySupportPart::startApplication(const QString &program) {
00246 bool inTerminal = DomUtil::readBoolEntry(*projectDom(), "/kdevrubysupport/run/terminal");
00247 appFrontend()->startAppCommand(project()->projectDirectory(), program, inTerminal);
00248 }
00249
00250
00251 KMimeType::List RubySupportPart::mimeTypes( )
00252 {
00253 KMimeType::List list;
00254 KMimeType::Ptr mime = KMimeType::mimeType( "text/x-ruby" );
00255 if( mime )
00256 list << mime;
00257 return list;
00258 }
00259
00260 #include "rubysupport_part.moc"