00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "pythonsupportpart.h"
00013 #include "pythonconfigwidget.h"
00014
00015 #include <kdevcore.h>
00016 #include <kdevproject.h>
00017 #include <kdevappfrontend.h>
00018 #include <kdevpartcontroller.h>
00019 #include <codemodel.h>
00020 #include <domutil.h>
00021
00022 #include <qfileinfo.h>
00023 #include <qpopupmenu.h>
00024 #include <qstringlist.h>
00025 #include <qtextstream.h>
00026 #include <qtimer.h>
00027 #include <qvbox.h>
00028 #include <kaction.h>
00029 #include <kapplication.h>
00030 #include <kdebug.h>
00031 #include <kdialogbase.h>
00032 #include <kdevgenericfactory.h>
00033 #include <kinputdialog.h>
00034 #include <klocale.h>
00035 #include <qregexp.h>
00036
00037
00038 typedef KDevGenericFactory<PythonSupportPart> PythonSupportFactory;
00039 static const KAboutData data("kdevpythonsupport", I18N_NOOP("Language"), "1.0");
00040 K_EXPORT_COMPONENT_FACTORY( libkdevpythonsupport, PythonSupportFactory( &data ) )
00041
00042 PythonSupportPart::PythonSupportPart(QObject *parent, const char *name, const QStringList &)
00043 : KDevLanguageSupport("PythonSupport", "python", parent, name ? name : "PythonSupportPart")
00044 {
00045 setInstance(PythonSupportFactory::instance());
00046
00047 setXMLFile("kdevpythonsupport.rc");
00048
00049 connect( core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
00050 connect( core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()) );
00051 connect( partController(), SIGNAL(savedFile(const KURL&)),
00052 this, SLOT(savedFile(const KURL&)) );
00053 connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
00054 this, SLOT(projectConfigWidget(KDialogBase*)) );
00055
00056 KAction *action;
00057
00058 action = new KAction( i18n("Execute Program"), "exec", 0,
00059 this, SLOT(slotExecute()),
00060 actionCollection(), "build_exec" );
00061 action->setToolTip( i18n("Execute program") );
00062 action->setWhatsThis(i18n("<b>Execute program</b><p>Runs the Python program."));
00063
00064 action = new KAction( i18n("Execute String..."), "exec", 0,
00065 this, SLOT(slotExecuteString()),
00066 actionCollection(), "build_execstring" );
00067 action->setToolTip( i18n("Execute string") );
00068 action->setWhatsThis(i18n("<b>Execute String</b><p>Executes a string as Python code."));
00069
00070 action = new KAction( i18n("Start Python Interpreter"), "exec", 0,
00071 this, SLOT(slotStartInterpreter()),
00072 actionCollection(), "build_runinterpreter" );
00073 action->setToolTip( i18n("Start Python interpreter") );
00074 action->setWhatsThis(i18n("<b>Start python interpreter</b><p>Starts the Python interpreter without a program"));
00075
00076 action = new KAction( i18n("Python Documentation..."), 0,
00077 this, SLOT(slotPydoc()),
00078 actionCollection(), "help_pydoc" );
00079 action->setToolTip( i18n("Python documentation") );
00080 action->setWhatsThis(i18n("<b>Python documentation</b><p>Shows a Python documentation page."));
00081 }
00082
00083
00084 PythonSupportPart::~PythonSupportPart()
00085 {}
00086
00087
00088 void PythonSupportPart::projectConfigWidget(KDialogBase *dlg)
00089 {
00090 QVBox *vbox = dlg->addVBoxPage(i18n("Python"));
00091 PythonConfigWidget *w = new PythonConfigWidget(*projectDom(), vbox, "python config widget");
00092 connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00093 }
00094
00095
00096 void PythonSupportPart::projectOpened()
00097 {
00098 kdDebug(9014) << "projectOpened()" << endl;
00099
00100 connect( project(), SIGNAL(addedFilesToProject(const QStringList &)),
00101 this, SLOT(addedFilesToProject(const QStringList &)) );
00102 connect( project(), SIGNAL(removedFilesFromProject(const QStringList &)),
00103 this, SLOT(removedFilesFromProject(const QStringList &)) );
00104
00105
00106
00107 QTimer::singleShot(0, this, SLOT(initialParse()));
00108 }
00109
00110
00111 void PythonSupportPart::projectClosed()
00112 {
00113 }
00114
00115
00116 void PythonSupportPart::maybeParse(const QString fileName)
00117 {
00118 QFileInfo fi(fileName);
00119 if (fi.extension() == "py") {
00120
00121 if( codeModel()->hasFile(fileName) ){
00122 emit aboutToRemoveSourceInfo( fileName );
00123 codeModel()->removeFile( codeModel()->fileByName(fileName) );
00124 }
00125
00126 parse( fileName );
00127 }
00128 }
00129
00130
00131 void PythonSupportPart::initialParse()
00132 {
00133 kdDebug(9014) << "initialParse()" << endl;
00134
00135 if (project()) {
00136 kapp->setOverrideCursor(waitCursor);
00137 QStringList files = project()->allFiles();
00138 for (QStringList::Iterator it = files.begin(); it != files.end() ;++it) {
00139 kdDebug(9014) << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
00140 maybeParse(project()->projectDirectory() + "/" + *it);
00141 }
00142
00143 emit updatedSourceInfo();
00144 kapp->restoreOverrideCursor();
00145 } else {
00146 kdDebug(9014) << "No project" << endl;
00147 }
00148 }
00149
00150
00151 void PythonSupportPart::addedFilesToProject(const QStringList &fileList)
00152 {
00153 kdDebug(9014) << "addedFilesToProject()" << endl;
00154
00155 QStringList::ConstIterator it;
00156
00157 for ( it = fileList.begin(); it != fileList.end(); ++it )
00158 {
00159 QString fileName = project()->projectDirectory() + "/" + ( *it );
00160 maybeParse( fileName );
00161 emit addedSourceInfo( fileName );
00162 }
00163
00164
00165 }
00166
00167
00168 void PythonSupportPart::removedFilesFromProject(const QStringList &fileList)
00169 {
00170 kdDebug(9014) << "removedFilesFromProject()" << endl;
00171
00172 QStringList::ConstIterator it;
00173
00174 for ( it = fileList.begin(); it != fileList.end(); ++it )
00175 {
00176 QString fileName = project()->projectDirectory() + "/" + ( *it );
00177
00178 if( codeModel()->hasFile(fileName) ){
00179 emit aboutToRemoveSourceInfo( fileName );
00180 codeModel()->removeFile( codeModel()->fileByName(fileName) );
00181 }
00182 }
00183
00184
00185 }
00186
00187
00188 void PythonSupportPart::savedFile(const KURL &fileName)
00189 {
00190 kdDebug(9014) << "savedFile()" << endl;
00191
00192 if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) {
00193 maybeParse(fileName.path());
00194 emit addedSourceInfo( fileName.path() );
00195 }
00196 }
00197
00198
00199 KDevLanguageSupport::Features PythonSupportPart::features()
00200 {
00201 return Features(Classes | Functions);
00202 }
00203
00204
00205 KMimeType::List PythonSupportPart::mimeTypes( )
00206 {
00207 KMimeType::List list;
00208 KMimeType::Ptr mime = KMimeType::mimeType( "text/x-python" );
00209 if( mime )
00210 list << mime;
00211
00212 mime = KMimeType::mimeType( "application/x-python" );
00213 if( mime )
00214 list << mime;
00215
00216 return list;
00217 }
00218
00219 void PythonSupportPart::parse(const QString &fileName)
00220 {
00221 QFile f(QFile::encodeName(fileName));
00222 if (!f.open(IO_ReadOnly))
00223 return;
00224 QTextStream stream(&f);
00225
00226 QRegExp classre("^[ \t]*class[ \t]+([A-Za-z0-9_]+)[ \t]*(\\(([A-Za-z0-9_, \t]+)\\))?.*$");
00227 QRegExp methodre("^[ \t]*def[ \t]+([A-Za-z0-9_]+).*$");
00228
00229 FileDom m_file = codeModel()->create<FileModel>();
00230 m_file->setName( fileName );
00231
00232 ClassDom lastClass;
00233 QString rawline;
00234 QCString line;
00235 int lineNo = 0;
00236 while (!stream.atEnd()) {
00237 rawline = stream.readLine();
00238 line = rawline.stripWhiteSpace().local8Bit();
00239 if (classre.search(line) != -1) {
00240
00241 lastClass = codeModel()->create<ClassModel>();
00242 lastClass->setName(classre.cap(1));
00243 lastClass->setFileName( fileName );
00244 lastClass->setStartPosition( lineNo, 0 );
00245
00246 QStringList parentList = QStringList::split(",", classre.cap(3));
00247 QStringList::ConstIterator it;
00248 for (it = parentList.begin(); it != parentList.end(); ++it) {
00249 QString baseName = (*it).stripWhiteSpace();
00250 kdDebug(9014) << "Add parent" << baseName << endl;
00251 lastClass->addBaseClass( baseName );
00252 }
00253
00254 if (m_file->hasClass(lastClass->name())) {
00255 ClassDom old = m_file->classByName( lastClass->name() )[ 0 ];
00256 old->setFileName( lastClass->fileName() );
00257
00258 int line, col;
00259 lastClass->getStartPosition( &line, &col );
00260 old->setStartPosition( line, col );
00261
00262 lastClass = old;
00263 } else {
00264 kdDebug(9014) << "Add class " << lastClass->name() << endl;
00265 m_file->addClass( lastClass );
00266 }
00267
00268 } else if (methodre.search(line) != -1 ) {
00269
00270 FunctionDom method = codeModel()->create<FunctionModel>();
00271 method->setName(methodre.cap(1));
00272 method->setFileName( fileName );
00273 method->setStartPosition( lineNo, 0 );
00274
00275 if (lastClass && rawline.left(3) != "def") {
00276 if( !lastClass->hasFunction(method->name()) )
00277 lastClass->addFunction( method );
00278 } else if( !m_file->hasFunction(method->name()) ){
00279 m_file->addFunction( method );
00280 lastClass = 0;
00281 }
00282 }
00283 ++lineNo;
00284 }
00285
00286 f.close();
00287
00288 codeModel()->addFile( m_file );
00289 }
00290
00291
00292 QString PythonSupportPart::interpreter()
00293 {
00294 QString prog = DomUtil::readEntry(*projectDom(), "/kdevpythonsupport/run/interpreter");
00295 if (prog.isEmpty())
00296 prog = "python";
00297
00298 return prog;
00299 }
00300
00301
00302 void PythonSupportPart::startApplication(const QString &program)
00303 {
00304 bool inTerminal = DomUtil::readBoolEntry(*projectDom(), "/kdevpythonsupport/run/terminal");
00305 appFrontend()->startAppCommand(QString::QString(), program, inTerminal);
00306 }
00307
00308
00309 void PythonSupportPart::slotExecute()
00310 {
00311 QString program = project()->mainProgram();
00312 QString cmd = interpreter() + " " + program;
00313 startApplication(cmd);
00314 }
00315
00316
00317 void PythonSupportPart::slotStartInterpreter()
00318 {
00319 startApplication(interpreter());
00320 }
00321
00322
00323 void PythonSupportPart::slotExecuteString()
00324 {
00325 bool ok;
00326 QString cmd = KInputDialog::getText(i18n("String to Execute"), i18n("String to execute:"), QString::null, &ok, 0);
00327 if (ok) {
00328 cmd.prepend("'");
00329 cmd.append("'");
00330 startApplication(cmd);
00331 }
00332 }
00333
00334
00335 void PythonSupportPart::slotPydoc()
00336 {
00337 bool ok;
00338 QString key = KInputDialog::getText(i18n("Show Python Documentation"), i18n("Show Python documentation on keyword:"), "", &ok, 0);
00339 if (ok && !key.isEmpty()) {
00340 QString url = "pydoc:";
00341 url += key;
00342 partController()->showDocument(KURL(url));
00343 }
00344 }
00345
00346 #include "pythonsupportpart.moc"