00001
00002
00003
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
00075
00076
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
00089
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
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
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
00309
00310
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) {
00329 kdDebug() << "editor doesn't support the CodeCompletionDocumentIface" << endl;
00330 return;
00331 }
00332
00333 disconnect(part->widget(), 0, this, 0 );
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
00360
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;
00379 }
00380
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
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
00417
00418
00419
00420
00421
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"