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 QString&)),
this, SLOT(savedFile(
const QString&)) );
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 QString &fileName)
00172 {
00173
kdDebug(9014) <<
"savedFile()" <<
endl;
00174
00175
if (
project()->
allFiles().contains(fileName.mid ( project()->projectDirectory().
length() + 1 )))
00176 {
00177
parse(fileName);
00178 emit addedSourceInfo( fileName );
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"