You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
13 KiB
411 lines
13 KiB
/***************************************************************************
|
|
* Copyright (C) 2001-2002 by Bernd Gehrmann *
|
|
* bernd@kdevelop.org *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "pythonsupportpart.h"
|
|
#include "pythonconfigwidget.h"
|
|
|
|
#include <kdevcore.h>
|
|
#include <kdevproject.h>
|
|
#include <kdevappfrontend.h>
|
|
#include <kdevpartcontroller.h>
|
|
#include <codemodel.h>
|
|
#include <domutil.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqtimer.h>
|
|
#include <tqvbox.h>
|
|
#include <kaction.h>
|
|
#include <kapplication.h>
|
|
#include <kdebug.h>
|
|
#include <kdialogbase.h>
|
|
#include <kdevgenericfactory.h>
|
|
#include <kdevplugininfo.h>
|
|
#include <kinputdialog.h>
|
|
#include <klocale.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include "qtdesignerpythonintegration.h"
|
|
#include "pythonimplementationwidget.h"
|
|
|
|
typedef KDevGenericFactory<PythonSupportPart> PythonSupportFactory;
|
|
static const KDevPluginInfo data("kdevpythonsupport");
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevpythonsupport, PythonSupportFactory( data ) )
|
|
|
|
PythonSupportPart::PythonSupportPart(TQObject *parent, const char *name, const TQStringList &)
|
|
: KDevLanguageSupport(&data, parent, name ? name : "PythonSupportPart")
|
|
{
|
|
setInstance(PythonSupportFactory::instance());
|
|
|
|
setXMLFile("kdevpythonsupport.rc");
|
|
|
|
connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()) );
|
|
connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) );
|
|
connect( partController(), TQT_SIGNAL(savedFile(const KURL&)),
|
|
this, TQT_SLOT(savedFile(const KURL&)) );
|
|
connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)),
|
|
this, TQT_SLOT(projectConfigWidget(KDialogBase*)) );
|
|
connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
|
|
this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) );
|
|
|
|
KAction *action;
|
|
|
|
action = new KAction( i18n("Execute Program"), "exec", 0,
|
|
this, TQT_SLOT(slotExecute()),
|
|
actionCollection(), "build_exec" );
|
|
action->setToolTip( i18n("Execute program") );
|
|
action->setWhatsThis(i18n("<b>Execute program</b><p>Runs the Python program."));
|
|
|
|
action = new KAction( i18n("Execute String..."), "exec", 0,
|
|
this, TQT_SLOT(slotExecuteString()),
|
|
actionCollection(), "build_execstring" );
|
|
action->setToolTip( i18n("Execute string") );
|
|
action->setWhatsThis(i18n("<b>Execute String</b><p>Executes a string as Python code."));
|
|
|
|
action = new KAction( i18n("Start Python Interpreter"), "exec", 0,
|
|
this, TQT_SLOT(slotStartInterpreter()),
|
|
actionCollection(), "build_runinterpreter" );
|
|
action->setToolTip( i18n("Start Python interpreter") );
|
|
action->setWhatsThis(i18n("<b>Start python interpreter</b><p>Starts the Python interpreter without a program"));
|
|
|
|
action = new KAction( i18n("Python Documentation..."), 0,
|
|
this, TQT_SLOT(slotPydoc()),
|
|
actionCollection(), "help_pydoc" );
|
|
action->setToolTip( i18n("Python documentation") );
|
|
action->setWhatsThis(i18n("<b>Python documentation</b><p>Shows a Python documentation page."));
|
|
}
|
|
|
|
|
|
PythonSupportPart::~PythonSupportPart()
|
|
{}
|
|
|
|
|
|
void PythonSupportPart::contextMenu(TQPopupMenu *popup, const Context *context)
|
|
{
|
|
if (context->hasType(Context::FileContext)){
|
|
const FileContext *fc = static_cast<const FileContext*>(context);
|
|
//this is a .ui file and only selection contains only one such file
|
|
KURL url = fc->urls().first();
|
|
if (url.fileName().endsWith(".ui"))
|
|
{
|
|
m_contextFileName = url.fileName();
|
|
int id = popup->insertItem(i18n("Create or Select Implementation..."), this, TQT_SLOT(slotCreateSubclass()));
|
|
popup->tqsetWhatsThis(id, i18n("<b>Create or select implementation</b><p>Creates or selects a subclass of selected form for use with integrated KDevDesigner."));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void PythonSupportPart::projectConfigWidget(KDialogBase *dlg)
|
|
{
|
|
TQVBox *vbox = dlg->addVBoxPage(i18n("Python"));
|
|
PythonConfigWidget *w = new PythonConfigWidget(*projectDom(), vbox, "python config widget");
|
|
connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
|
|
}
|
|
|
|
|
|
void PythonSupportPart::projectOpened()
|
|
{
|
|
kdDebug(9014) << "projectOpened()" << endl;
|
|
|
|
connect( project(), TQT_SIGNAL(addedFilesToProject(const TQStringList &)),
|
|
this, TQT_SLOT(addedFilesToProject(const TQStringList &)) );
|
|
connect( project(), TQT_SIGNAL(removedFilesFromProject(const TQStringList &)),
|
|
this, TQT_SLOT(removedFilesFromProject(const TQStringList &)) );
|
|
|
|
// We want to parse only after all components have been
|
|
// properly initialized
|
|
TQTimer::singleShot(0, this, TQT_SLOT(initialParse()));
|
|
}
|
|
|
|
|
|
void PythonSupportPart::projectClosed()
|
|
{
|
|
}
|
|
|
|
|
|
void PythonSupportPart::maybeParse(const TQString fileName)
|
|
{
|
|
TQFileInfo fi(fileName);
|
|
if (fi.extension() == "py") {
|
|
|
|
if( codeModel()->hasFile(fileName) ){
|
|
emit aboutToRemoveSourceInfo( fileName );
|
|
codeModel()->removeFile( codeModel()->fileByName(fileName) );
|
|
}
|
|
|
|
parse( fileName );
|
|
}
|
|
}
|
|
|
|
|
|
void PythonSupportPart::initialParse()
|
|
{
|
|
kdDebug(9014) << "initialParse()" << endl;
|
|
|
|
if (project()) {
|
|
kapp->setOverrideCursor(waitCursor);
|
|
TQStringList files = project()->allFiles();
|
|
for (TQStringList::Iterator it = files.begin(); it != files.end() ;++it) {
|
|
kdDebug(9014) << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
|
|
maybeParse(project()->projectDirectory() + "/" + *it);
|
|
}
|
|
|
|
emit updatedSourceInfo();
|
|
kapp->restoreOverrideCursor();
|
|
} else {
|
|
kdDebug(9014) << "No project" << endl;
|
|
}
|
|
}
|
|
|
|
|
|
void PythonSupportPart::addedFilesToProject(const TQStringList &fileList)
|
|
{
|
|
kdDebug(9014) << "addedFilesToProject()" << endl;
|
|
|
|
TQStringList::ConstIterator it;
|
|
|
|
for ( it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
TQString fileName = project()->projectDirectory() + "/" + ( *it );
|
|
maybeParse( fileName );
|
|
emit addedSourceInfo( fileName );
|
|
}
|
|
|
|
//emit updatedSourceInfo();
|
|
}
|
|
|
|
|
|
void PythonSupportPart::removedFilesFromProject(const TQStringList &fileList)
|
|
{
|
|
kdDebug(9014) << "removedFilesFromProject()" << endl;
|
|
|
|
TQStringList::ConstIterator it;
|
|
|
|
for ( it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
TQString fileName = project()->projectDirectory() + "/" + ( *it );
|
|
|
|
if( codeModel()->hasFile(fileName) ){
|
|
emit aboutToRemoveSourceInfo( fileName );
|
|
codeModel()->removeFile( codeModel()->fileByName(fileName) );
|
|
}
|
|
}
|
|
|
|
//emit updatedSourceInfo();
|
|
}
|
|
|
|
|
|
void PythonSupportPart::savedFile(const KURL &fileName)
|
|
{
|
|
kdDebug(9014) << "savedFile()" << endl;
|
|
|
|
if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) {
|
|
maybeParse(fileName.path());
|
|
emit addedSourceInfo( fileName.path() );
|
|
}
|
|
}
|
|
|
|
|
|
KDevLanguageSupport::Features PythonSupportPart::features()
|
|
{
|
|
return Features(Classes | Functions);
|
|
}
|
|
|
|
|
|
KMimeType::List PythonSupportPart::mimeTypes( )
|
|
{
|
|
KMimeType::List list;
|
|
KMimeType::Ptr mime = KMimeType::mimeType( "text/x-python" );
|
|
if( mime )
|
|
list << mime;
|
|
|
|
mime = KMimeType::mimeType( "application/x-python" );
|
|
if( mime )
|
|
list << mime;
|
|
|
|
return list;
|
|
}
|
|
|
|
void PythonSupportPart::parse(const TQString &fileName)
|
|
{
|
|
TQFile f(TQFile::encodeName(fileName));
|
|
if (!f.open(IO_ReadOnly))
|
|
return;
|
|
TQTextStream stream(&f);
|
|
|
|
TQRegExp classre("^[ \t]*class[ \t]+([A-Za-z0-9_]+)[ \t]*(\\(([A-Za-z0-9_, \t]+)\\))?.*$");
|
|
TQRegExp methodre("^[ \t]*def[ \t]+([A-Za-z0-9_]+).*$");
|
|
|
|
FileDom m_file = codeModel()->create<FileModel>();
|
|
m_file->setName( fileName );
|
|
|
|
ClassDom lastClass;
|
|
TQString rawline;
|
|
TQCString line;
|
|
int lineNo = 0;
|
|
while (!stream.atEnd()) {
|
|
rawline = stream.readLine();
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
if (classre.search(line) != -1) {
|
|
|
|
lastClass = codeModel()->create<ClassModel>();
|
|
lastClass->setName(classre.cap(1));
|
|
lastClass->setFileName( fileName );
|
|
lastClass->setStartPosition( lineNo, 0 );
|
|
|
|
TQStringList parentList = TQStringList::split(",", classre.cap(3));
|
|
TQStringList::ConstIterator it;
|
|
for (it = parentList.begin(); it != parentList.end(); ++it) {
|
|
TQString baseName = (*it).stripWhiteSpace();
|
|
kdDebug(9014) << "Add parent" << baseName << endl;
|
|
lastClass->addBaseClass( baseName );
|
|
}
|
|
|
|
if (m_file->hasClass(lastClass->name())) {
|
|
ClassDom old = m_file->classByName( lastClass->name() )[ 0 ];
|
|
old->setFileName( lastClass->fileName() );
|
|
|
|
int line, col;
|
|
lastClass->getStartPosition( &line, &col );
|
|
old->setStartPosition( line, col );
|
|
|
|
lastClass = old;
|
|
} else {
|
|
kdDebug(9014) << "Add class " << lastClass->name() << endl;
|
|
m_file->addClass( lastClass );
|
|
}
|
|
|
|
} else if (methodre.search(line) != -1 ) {
|
|
|
|
FunctionDom method = codeModel()->create<FunctionModel>();
|
|
method->setName(methodre.cap(1));
|
|
method->setFileName( fileName );
|
|
method->setStartPosition( lineNo, 0 );
|
|
|
|
if (lastClass && rawline.left(3) != "def") {
|
|
if( !lastClass->hasFunction(method->name()) )
|
|
lastClass->addFunction( method );
|
|
TQStringList scope;
|
|
scope << lastClass->name();
|
|
method->setScope( scope );
|
|
|
|
} else if( !m_file->hasFunction(method->name()) ){
|
|
m_file->addFunction( method );
|
|
lastClass = 0;
|
|
}
|
|
}
|
|
++lineNo;
|
|
}
|
|
|
|
f.close();
|
|
|
|
codeModel()->addFile( m_file );
|
|
}
|
|
|
|
|
|
TQString PythonSupportPart::interpreter()
|
|
{
|
|
TQString prog = DomUtil::readEntry(*projectDom(), "/kdevpythonsupport/run/interpreter");
|
|
if (prog.isEmpty())
|
|
prog = "python";
|
|
|
|
return prog;
|
|
}
|
|
|
|
|
|
void PythonSupportPart::startApplication(const TQString &program)
|
|
{
|
|
bool inTerminal = DomUtil::readBoolEntry(*projectDom(), "/kdevpythonsupport/run/terminal");
|
|
if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("KDevelop/AppFrontend"))
|
|
appFrontend->startAppCommand(TQString(), program, inTerminal);
|
|
}
|
|
|
|
|
|
void PythonSupportPart::slotExecute()
|
|
{
|
|
TQString program = project()->mainProgram();
|
|
TQString cmd = interpreter() + " " + program;
|
|
startApplication(cmd);
|
|
}
|
|
|
|
|
|
void PythonSupportPart::slotStartInterpreter()
|
|
{
|
|
startApplication(interpreter());
|
|
}
|
|
|
|
|
|
void PythonSupportPart::slotExecuteString()
|
|
{
|
|
bool ok;
|
|
TQString cmd = KInputDialog::getText(i18n("String to Execute"), i18n("String to execute:"), TQString(), &ok, 0);
|
|
if (ok) {
|
|
cmd.prepend("'");
|
|
cmd.append("'");
|
|
startApplication(cmd);
|
|
}
|
|
}
|
|
|
|
|
|
void PythonSupportPart::slotPydoc()
|
|
{
|
|
bool ok;
|
|
TQString key = KInputDialog::getText(i18n("Show Python Documentation"), i18n("Show Python documentation on keyword:"), "", &ok, 0);
|
|
if (ok && !key.isEmpty()) {
|
|
TQString url = "pydoc:";
|
|
url += key;
|
|
partController()->showDocument(KURL(url));
|
|
}
|
|
}
|
|
|
|
KDevDesignerIntegration *PythonSupportPart::designer(KInterfaceDesigner::DesignerType type)
|
|
{
|
|
KDevDesignerIntegration *des = 0;
|
|
switch (type)
|
|
{
|
|
case KInterfaceDesigner::TQtDesigner:
|
|
des = m_designers[type];
|
|
if (des == 0)
|
|
{
|
|
PythonImplementationWidget *impl = new PythonImplementationWidget(this);
|
|
des = new QtDesignerPythonIntegration(this, impl);
|
|
des->loadSettings(*project()->projectDom(),
|
|
"kdevpythonsupport/designerintegration");
|
|
m_designers[type] = des;
|
|
}
|
|
break;
|
|
case KInterfaceDesigner::Glade:
|
|
break;
|
|
}
|
|
return des;
|
|
|
|
}
|
|
|
|
void PythonSupportPart::slotCreateSubclass()
|
|
{
|
|
TQFileInfo fi(m_contextFileName);
|
|
kdDebug(9014) << k_funcinfo << " file: " << m_contextFileName << " ext: " << fi.extension(false) << endl;
|
|
if (fi.extension(false) != "ui")
|
|
return;
|
|
QtDesignerPythonIntegration *des = dynamic_cast<QtDesignerPythonIntegration*>(designer(KInterfaceDesigner::TQtDesigner));
|
|
if (des)
|
|
{
|
|
kdDebug(9014) << "ok: " << des << endl;
|
|
kdDebug(9014) << "have impl: " << des->selectImplementation(m_contextFileName);
|
|
}
|
|
kdDebug(9014) << "end: " << des << endl;
|
|
}
|
|
|
|
#include "pythonsupportpart.moc"
|