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.
449 lines
13 KiB
449 lines
13 KiB
/*
|
|
Copyright (C) 2003 ian reinhart geiser <geiseri@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
version 2, License as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <tqwhatsthis.h>
|
|
#include <tqtimer.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqregexp.h>
|
|
#include <tqpopupmenu.h>
|
|
|
|
#include <kiconloader.h>
|
|
#include <klocale.h>
|
|
#include <kgenericfactory.h>
|
|
#include <kdevcore.h>
|
|
#include <kdevpartcontroller.h>
|
|
#include <kdevmainwindow.h>
|
|
#include <domutil.h>
|
|
#include <codemodel.h>
|
|
#include <kparts/part.h>
|
|
|
|
|
|
#include <kdevproject.h>
|
|
#include <kaction.h>
|
|
#include <kdebug.h>
|
|
#include <kapplication.h>
|
|
|
|
#include <kjsembed/kjsembedpart.h>
|
|
#include <kjsembed/jsconsolewidget.h>
|
|
|
|
#include <kdevplugininfo.h>
|
|
|
|
#include "kjssupport_part.h"
|
|
#include "kjsproblems.h"
|
|
#include "jscodecompletion.h"
|
|
#include "subclassingdlg.h"
|
|
|
|
typedef KDevGenericFactory<kjsSupportPart> kjsSupportFactory;
|
|
static const KDevPluginInfo data("kdevkjssupport");
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevkjssupport, kjsSupportFactory( data ) );
|
|
|
|
|
|
class typeProperty
|
|
{
|
|
public:
|
|
TQString type;
|
|
TQString name;
|
|
int depth;
|
|
};
|
|
|
|
kjsSupportPart::kjsSupportPart(TQObject *parent, const char *name, const TQStringList& )
|
|
: KDevLanguageSupport(&data, parent, name ? name : "kjsSupportPart" )
|
|
{
|
|
setInstance(kjsSupportFactory::instance());
|
|
setXMLFile("kdevkjssupport.rc");
|
|
|
|
|
|
m_build = new KAction( i18n("&Run"), "exec",Key_F9,this, TQT_SLOT(slotRun()),actionCollection(), "build_execute" );
|
|
m_build->setStatusText( i18n("Test the active script.") );
|
|
|
|
kdDebug() << "Creating kjssupport Part" << endl;
|
|
|
|
connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)),
|
|
this, TQT_SLOT(projectConfigWidget(KDialogBase*)) );
|
|
connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()) );
|
|
connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) );
|
|
connect( partController(), TQT_SIGNAL(savedFile(const TQString&)), this, TQT_SLOT(savedFile(const TQString&)) );
|
|
connect(partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)),
|
|
this, TQT_SLOT(slotActivePartChanged(KParts::Part *)));
|
|
connect(core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
|
|
this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)));
|
|
|
|
|
|
// Building kjs interpreter.
|
|
m_js = new KJSEmbed::KJSEmbedPart();
|
|
mainWindow()->embedOutputView( m_js->view() , i18n("KJS Console"),i18n("KJS Embed Console") );
|
|
|
|
// get the problem reporter
|
|
m_problemReporter = new KJSProblems( this, 0, "problems" );
|
|
mainWindow( )->embedOutputView( m_problemReporter, i18n("Problems"), i18n("Problem reporter"));
|
|
m_cc = new JSCodeCompletion();
|
|
}
|
|
|
|
|
|
kjsSupportPart::~kjsSupportPart()
|
|
{
|
|
delete m_problemReporter;
|
|
delete m_cc;
|
|
delete m_build;
|
|
delete m_js;
|
|
}
|
|
|
|
KDevLanguageSupport::Features kjsSupportPart::features()
|
|
{
|
|
return Features(Classes | Variables | Functions);
|
|
}
|
|
|
|
KMimeType::List kjsSupportPart::mimeTypes()
|
|
{
|
|
KMimeType::List list;
|
|
|
|
KMimeType::Ptr mime = KMimeType::mimeType( "application/x-javascript" );
|
|
if( mime )
|
|
list << mime;
|
|
|
|
return list;
|
|
}
|
|
void kjsSupportPart::slotRun()
|
|
{
|
|
// Execute the application here.
|
|
|
|
KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( partController()->activePart() );
|
|
if ( ro_part )
|
|
{
|
|
m_js->runFile( ro_part->url().path() );
|
|
}
|
|
|
|
}
|
|
|
|
void kjsSupportPart::projectConfigWidget(KDialogBase *dlg)
|
|
{
|
|
Q_UNUSED( dlg );
|
|
// Create your config dialog here.
|
|
}
|
|
void kjsSupportPart::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(parse()));
|
|
}
|
|
void kjsSupportPart::projectClosed()
|
|
{
|
|
|
|
}
|
|
void kjsSupportPart::savedFile(const TQString &fileName)
|
|
{
|
|
|
|
|
|
if (project()->allFiles().contains(fileName.mid ( project()->projectDirectory().length() + 1 )))
|
|
{
|
|
kdDebug(9014) << "parse file " << fileName << endl;
|
|
parse( fileName );
|
|
emit addedSourceInfo( fileName );
|
|
}
|
|
}
|
|
void kjsSupportPart::addedFilesToProject(const TQStringList &fileList)
|
|
{
|
|
kdDebug(9014) << "addedFilesToProject()" << endl;
|
|
|
|
TQStringList::ConstIterator it;
|
|
|
|
for ( it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
kdDebug(9014) << "maybe parse " << project()->projectDirectory() + "/" + ( *it ) << endl;
|
|
parse( project()->projectDirectory() + "/" + (*it) );
|
|
}
|
|
|
|
emit updatedSourceInfo();
|
|
}
|
|
void kjsSupportPart::removedFilesFromProject(const TQStringList &fileList)
|
|
{
|
|
|
|
|
|
TQStringList::ConstIterator it;
|
|
|
|
for ( it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
TQString fileName = project()->projectDirectory() + "/" + ( *it );
|
|
if( codeModel()->hasFile(fileName) )
|
|
{
|
|
kdDebug(9014) << "removed " << fileName << endl;
|
|
emit aboutToRemoveSourceInfo( fileName );
|
|
codeModel()->removeFile( codeModel()->fileByName(fileName) );
|
|
}
|
|
}
|
|
|
|
}
|
|
void kjsSupportPart::parse()
|
|
{
|
|
kdDebug(9014) << "initialParse()" << endl;
|
|
|
|
if (project())
|
|
{
|
|
kapp->setOverrideCursor(waitCursor);
|
|
TQStringList files = project()->allFiles();
|
|
m_problemReporter->clear();
|
|
|
|
for (TQStringList::Iterator it = files.begin(); it != files.end() ;++it)
|
|
{
|
|
kdDebug(9014) << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
|
|
|
|
parse( project()->projectDirectory() + "/" + (*it) );
|
|
|
|
}
|
|
emit updatedSourceInfo();
|
|
kapp->restoreOverrideCursor();
|
|
} else {
|
|
kdDebug(9014) << "No project" << endl;
|
|
}
|
|
}
|
|
void kjsSupportPart::slotActivePartChanged(KParts::Part *part)
|
|
{
|
|
kdDebug() << "Changeing active part..." << endl;
|
|
m_cc->setActiveEditorPart(part);
|
|
}
|
|
|
|
/*!
|
|
\fn kjsSupportPart::parse(const TQString &fileName)
|
|
*/
|
|
void kjsSupportPart::parse(const TQString &fileName)
|
|
{
|
|
TQFileInfo fi(fileName);
|
|
if (fi.extension() == "js")
|
|
{
|
|
if( codeModel()->hasFile(fileName) )
|
|
{
|
|
emit aboutToRemoveSourceInfo( fileName );
|
|
codeModel()->removeFile( codeModel()->fileByName(fileName) );
|
|
}
|
|
|
|
FileDom m_file = codeModel()->create<FileModel>();
|
|
m_file->setName( fileName );
|
|
m_file->setFileName( fileName );
|
|
|
|
TQFile f(TQFile::encodeName(fileName));
|
|
if (!f.open(IO_ReadOnly))
|
|
return;
|
|
TQString rawline;
|
|
TQString line;
|
|
uint lineNo = 0;
|
|
TQTextStream stream(&f);
|
|
int depth = 0;
|
|
bool inFunction = false;
|
|
TQString lastFunction = "";
|
|
int lastLineNo = 0;
|
|
ClassDom currentClass;
|
|
|
|
TQRegExp varRx("var[\\s]([_a-zA-Z\\d]+)");
|
|
TQRegExp classVarRx("this\\.([_a-zA-Z\\d]+)");
|
|
TQRegExp classMethRx("this\\.([_a-zA-Z\\d]+)[\\s]*=[\\s]*function(\\([^){}\\n\\r]*\\))");
|
|
TQRegExp methRx("function[\\s]+([_a-zA-Z\\d]+[\\s]*\\([^){}\\n\\r]*\\))");
|
|
TQRegExp allocRx("([_\\d\\w]+)[\\s]*=[\\s]*new[\\s]*([_\\d\\w]+)");
|
|
TQRegExp assnRx("var[\\s]+([_\\d\\w]+)[\\s]+[=][\\s]+([_\\d\\w]+)[;]");
|
|
|
|
while (!stream.atEnd())
|
|
{
|
|
rawline = stream.readLine();
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
kdDebug() << "Trying line: " << line << endl;
|
|
|
|
if (methRx.search(line) != -1 && depth == 0)
|
|
{
|
|
if (lastFunction != "" )
|
|
addMethod(lastFunction, m_file, lastLineNo);
|
|
lastFunction = methRx.cap(1);
|
|
lastLineNo = lineNo;
|
|
}
|
|
else if(varRx.search(line) != -1 && depth == 0)
|
|
{
|
|
addAttribute(varRx.cap(1), m_file, lineNo);
|
|
}
|
|
else if(classMethRx.search(line) != -1 && depth > 0)
|
|
{
|
|
if ( lastFunction != "" )
|
|
{
|
|
currentClass = addClass(lastFunction, m_file, lastLineNo );
|
|
lastFunction = "";
|
|
}
|
|
addMethod(classMethRx.cap(1)+classMethRx.cap(2), currentClass, lineNo);
|
|
}
|
|
else if(classVarRx.search(line) != -1 && depth > 0)
|
|
{
|
|
if ( lastFunction != "" )
|
|
{
|
|
currentClass = addClass(lastFunction, m_file, lastLineNo );
|
|
lastFunction = "";
|
|
}
|
|
addAttribute(classVarRx.cap(1), currentClass, lineNo);
|
|
}
|
|
|
|
if( allocRx.search(line) != -1 )
|
|
{
|
|
TQString varName = allocRx.cap(1);
|
|
TQString varType = allocRx.cap(2);
|
|
|
|
typeProperty *type = new typeProperty();
|
|
type->depth = depth;
|
|
type->name = varName;
|
|
type->type = varType;
|
|
|
|
m_typeMap.insert(varName, type);
|
|
kdDebug() << "Adding " << varName << " of type " << varType << " at scope " << depth << endl;
|
|
|
|
}
|
|
|
|
|
|
kdDebug() << "Syntax check..." << endl;
|
|
KJS::UString jsLine( line.latin1() );
|
|
int lineNumber = 0;
|
|
KJS::UString errorMessage;
|
|
|
|
if ( !m_js->interpreter()->checkSyntax( jsLine, &lineNumber, &errorMessage ) )
|
|
{
|
|
kdDebug() << errorMessage.qstring() << " on line " << lineNo << endl;
|
|
m_problemReporter->addLine(m_file->fileName(), lineNo, errorMessage.qstring());
|
|
}
|
|
|
|
if( line.contains("{") )
|
|
++depth;
|
|
|
|
if( line.contains("}") )
|
|
--depth;
|
|
|
|
++lineNo;
|
|
}
|
|
|
|
if (lastFunction != "" )
|
|
addMethod(lastFunction, m_file, lastLineNo);
|
|
|
|
f.close();
|
|
|
|
kdDebug() << "Trying to add list..." << endl;
|
|
|
|
codeModel()->addFile( m_file );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
ClassDom kjsSupportPart::addClass(const TQString &name, FileDom file, uint lineNo)
|
|
{
|
|
ClassDom clazz = codeModel()->create<ClassModel>();
|
|
clazz->setName(name);
|
|
clazz->setFileName(file->fileName());
|
|
clazz->setStartPosition(lineNo, 0);
|
|
|
|
if( !file->hasClass(clazz->name()) ){
|
|
kdDebug() << "Add global class " << clazz->name() << endl;
|
|
file->addClass( clazz );
|
|
}
|
|
return clazz;
|
|
}
|
|
|
|
void kjsSupportPart::addMethod(const TQString &name, ClassDom clazz, uint lineNo)
|
|
{
|
|
FunctionDom method = codeModel()->create<FunctionModel>();
|
|
method->setName(name);
|
|
method->setFileName(clazz->fileName());
|
|
method->setStartPosition(lineNo, 0);
|
|
|
|
if( !clazz->hasFunction(method->name()) ){
|
|
kdDebug() << "Add class method " << method->name() << endl;
|
|
clazz->addFunction( method );
|
|
}
|
|
}
|
|
|
|
void kjsSupportPart::addAttribute(const TQString &name, ClassDom clazz, uint lineNo)
|
|
{
|
|
VariableDom var = codeModel()->create<VariableModel>();
|
|
var->setName(name);
|
|
var->setFileName(clazz->fileName());
|
|
var->setStartPosition( lineNo, 0 );
|
|
var->setType(i18n("Variable"));
|
|
|
|
if( !clazz->hasVariable(var->name()) ){
|
|
kdDebug() << "Add class attribute " << var->name() << endl;
|
|
clazz->addVariable(var);
|
|
}
|
|
}
|
|
|
|
void kjsSupportPart::addMethod(const TQString &name, FileDom file, uint lineNo)
|
|
{
|
|
FunctionDom method = codeModel()->create<FunctionModel>();
|
|
method->setName(name);
|
|
method->setFileName(file->fileName());
|
|
method->setStartPosition(lineNo, 0);
|
|
|
|
if( !file->hasFunction(method->name()) ){
|
|
kdDebug() << "Add global method " << method->name() << endl;
|
|
file->addFunction( method );
|
|
}
|
|
}
|
|
|
|
void kjsSupportPart::addAttribute(const TQString &name, FileDom file, uint lineNo)
|
|
{
|
|
VariableDom var = codeModel()->create<VariableModel>();
|
|
var->setName(name);
|
|
var->setFileName(file->fileName());
|
|
var->setStartPosition( lineNo, 0 );
|
|
var->setType(i18n("Variable"));
|
|
|
|
if( !file->hasVariable(var->name()) ){
|
|
kdDebug() << "Add global attribute " << var->name() << endl;
|
|
file->addVariable(var);
|
|
}
|
|
}
|
|
|
|
void kjsSupportPart::contextMenu(TQPopupMenu * popupMenu, const Context *context)
|
|
{
|
|
kdDebug() << "1" << endl;
|
|
if (!context->hasType( Context::FileContext ))
|
|
return;
|
|
|
|
kdDebug() << "2" << endl;
|
|
const FileContext *fcontext = static_cast<const FileContext*>(context);
|
|
m_selectedUI = fcontext->fileName();
|
|
if (m_selectedUI.right(3).lower() == ".ui")
|
|
int id = popupMenu->insertItem(i18n("Implement Slots"),
|
|
this, TQT_SLOT(implementSlots()));
|
|
else
|
|
m_selectedUI = TQString::null;
|
|
}
|
|
|
|
void kjsSupportPart::implementSlots()
|
|
{
|
|
if (m_selectedUI.isEmpty())
|
|
return;
|
|
|
|
TQStringList newFiles;
|
|
SubclassingDlg *sub = new SubclassingDlg(this, m_selectedUI, newFiles);
|
|
if (sub->exec())
|
|
project()->addFiles(newFiles);
|
|
|
|
delete sub;
|
|
}
|
|
|
|
#include "kjssupport_part.moc"
|