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.
909 lines
32 KiB
909 lines
32 KiB
#include "rubysupport_part.h"
|
|
#include "rubyconfigwidget.h"
|
|
#include "domutil.h"
|
|
|
|
#include "qtdesignerrubyintegration.h"
|
|
#include "rubyimplementationwidget.h"
|
|
|
|
#include "kdevcore.h"
|
|
#include "kdevmainwindow.h"
|
|
#include "kdevlanguagesupport.h"
|
|
#include "kdevpartcontroller.h"
|
|
#include "kdevproject.h"
|
|
#include "kdevappfrontend.h"
|
|
#include "kdevplugininfo.h"
|
|
#include "kdevshellwidget.h"
|
|
#include "kdevquickopen.h"
|
|
|
|
#include <tqdir.h>
|
|
#include <tqwhatsthis.h>
|
|
#include <tqtimer.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <kiconloader.h>
|
|
#include <klocale.h>
|
|
#include <kdevgenericfactory.h>
|
|
#include <kprocess.h>
|
|
#include <kdebug.h>
|
|
#include <kaction.h>
|
|
#include <kparts/part.h>
|
|
#include <kdialogbase.h>
|
|
#include <kapplication.h>
|
|
#include <klibloader.h>
|
|
|
|
#include <ktexteditor/viewcursorinterface.h>
|
|
|
|
#include <codemodel_utils.h>
|
|
|
|
typedef KDevGenericFactory<RubySupportPart> RubySupportFactory;
|
|
static const KDevPluginInfo data("kdevrubysupport");
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevrubysupport, RubySupportFactory( data ) )
|
|
|
|
RubySupportPart::RubySupportPart(TQObject *parent, const char *name, const TQStringList& )
|
|
: KDevLanguageSupport (&data, parent, name ? name : "RubySupportPart" )
|
|
{
|
|
setInstance(RubySupportFactory::instance());
|
|
setXMLFile("kdevrubysupport.rc");
|
|
|
|
KAction *action;
|
|
action = new KAction( i18n("&Run"), "exec", SHIFT + Key_F9,
|
|
this, TQT_SLOT(slotRun()),
|
|
actionCollection(), "build_execute" );
|
|
action->setToolTip(i18n("Run"));
|
|
action->setWhatsThis(i18n("<b>Run</b><p>Starts an application."));
|
|
action->setIcon("ruby_run.png");
|
|
|
|
action = new KAction( i18n("Run Test Under Cursor"), "exec", ALT + Key_F9,
|
|
this, TQT_SLOT(slotRunTestUnderCursor()),
|
|
actionCollection(), "build_execute_test_function" );
|
|
action->setToolTip(i18n("Run Test Under Cursor"));
|
|
action->setWhatsThis(i18n("<b>Run Test Under Cursor</b><p>Runs the function under the cursor as test."));
|
|
|
|
action = new KAction( i18n("Launch Browser"), "network", 0, this, TQT_SLOT(slotBrowse()), actionCollection(), "build_launch_browser" );
|
|
action->setToolTip(i18n("Launch Browser"));
|
|
action->setWhatsThis(i18n("<b>Launch Browser</b><p>Opens a web browser pointing to the Ruby Rails server") );
|
|
|
|
action = new KAction( i18n("Switch To Controller"), 0, CTRL+ALT+Key_1, this, TQT_SLOT(slotSwitchToController()), actionCollection(), "switch_to_controller" );
|
|
action = new KAction( i18n("Switch To Model"), 0, CTRL+ALT+Key_2, this, TQT_SLOT(slotSwitchToModel()), actionCollection(), "switch_to_model" );
|
|
action = new KAction( i18n("Switch To View"), 0, CTRL+ALT+Key_3, this, TQT_SLOT(slotSwitchToView()), actionCollection(), "switch_to_view" );
|
|
action = new KAction( i18n("Switch To Test"), 0, CTRL+ALT+Key_4, this, TQT_SLOT(slotSwitchToTest()), actionCollection(), "switch_to_test" );
|
|
|
|
kdDebug() << "Creating RubySupportPart" << endl;
|
|
|
|
m_shellWidget = new KDevShellWidget( 0, "irb console");
|
|
m_shellWidget->setIcon( SmallIcon("ruby_config.png", KIcon::SizeMedium, KIcon::DefaultState, RubySupportPart::instance()));
|
|
m_shellWidget->setCaption(i18n("Ruby Shell"));
|
|
mainWindow()->embedOutputView( m_shellWidget, i18n("Ruby Shell"), i18n("Ruby Shell"));
|
|
mainWindow()->raiseView( m_shellWidget );
|
|
|
|
connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()) );
|
|
connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) );
|
|
connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
|
|
this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) );
|
|
connect( partController(), TQT_SIGNAL(savedFile(const KURL&)),
|
|
this, TQT_SLOT(savedFile(const KURL&)) );
|
|
connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)),
|
|
this, TQT_SLOT(projectConfigWidget(KDialogBase*)) );
|
|
}
|
|
|
|
|
|
RubySupportPart::~RubySupportPart()
|
|
{
|
|
if ( m_shellWidget )
|
|
mainWindow()->removeView( m_shellWidget );
|
|
delete m_shellWidget;
|
|
}
|
|
|
|
|
|
void RubySupportPart::projectConfigWidget(KDialogBase *dlg)
|
|
{
|
|
TQVBox *vbox = dlg->addVBoxPage(i18n("Ruby"), i18n("Ruby"), BarIcon("ruby_config.png", KIcon::SizeMedium, KIcon::DefaultState, RubySupportPart::instance()));
|
|
RubyConfigWidget *w = new RubyConfigWidget(*projectDom(), (TQWidget *)vbox, "ruby config widget");
|
|
connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
|
|
}
|
|
|
|
void RubySupportPart::projectOpened()
|
|
{
|
|
kdDebug() << "projectOpened()" << endl;
|
|
|
|
TQStrList l;
|
|
l.append( shell().latin1() ) ;
|
|
m_shellWidget->setShell( shell().latin1(), l );
|
|
m_shellWidget->activate();
|
|
m_shellWidget->setAutoReactivateOnClose( true );
|
|
|
|
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 &)) );
|
|
|
|
TQFileInfo program(mainProgram());
|
|
|
|
// If it's a Rails project, create the project files if they're missing
|
|
if (mainProgram().endsWith("script/server")) {
|
|
TQString cmd;
|
|
TQFileInfo server(project()->projectDirectory() + "/script/server");
|
|
if (! server.exists()) {
|
|
cmd += "rails " + project()->projectDirectory();
|
|
if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("TDevelop/AppFrontend"))
|
|
appFrontend->startAppCommand(project()->projectDirectory(), cmd, false);
|
|
}
|
|
}
|
|
|
|
// We want to parse only after all components have been
|
|
// properly initialized
|
|
TQTimer::singleShot(0, this, TQT_SLOT(initialParse()));
|
|
}
|
|
|
|
void RubySupportPart::maybeParse(const TQString fileName)
|
|
{
|
|
TQFileInfo fi(fileName);
|
|
|
|
if (fi.extension() == "rb") {
|
|
if (codeModel()->hasFile(fileName)) {
|
|
emit aboutToRemoveSourceInfo(fileName);
|
|
codeModel()->removeFile(codeModel()->fileByName(fileName));
|
|
}
|
|
|
|
parse(fileName);
|
|
emit addedSourceInfo( fileName );
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::initialParse()
|
|
{
|
|
kdDebug() << "initialParse()" << endl;
|
|
|
|
if (project()) {
|
|
kapp->setOverrideCursor(waitCursor);
|
|
TQStringList files = project()->allFiles();
|
|
for (TQStringList::Iterator it = files.begin(); it != files.end() ;++it) {
|
|
kdDebug() << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
|
|
maybeParse(project()->projectDirectory() + "/" + *it);
|
|
}
|
|
|
|
emit updatedSourceInfo();
|
|
kapp->restoreOverrideCursor();
|
|
} else {
|
|
kdDebug() << "No project" << endl;
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::addedFilesToProject(const TQStringList &fileList)
|
|
{
|
|
kdDebug() << "addedFilesToProject()" << endl;
|
|
|
|
TQStringList::ConstIterator it;
|
|
|
|
for ( it = fileList.begin(); it != fileList.end(); ++it )
|
|
{
|
|
TQString fileName = project()->projectDirectory() + "/" + ( *it );
|
|
maybeParse( fileName );
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::removedFilesFromProject(const TQStringList &fileList)
|
|
{
|
|
kdDebug() << "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) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::savedFile(const KURL &fileName)
|
|
{
|
|
kdDebug() << "savedFile()" << endl;
|
|
|
|
if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) {
|
|
maybeParse(fileName.path());
|
|
emit addedSourceInfo( fileName.path() );
|
|
}
|
|
}
|
|
|
|
KDevLanguageSupport::Features RubySupportPart::features()
|
|
{
|
|
return Features(Classes | Functions | Variables | Declarations | Signals | Slots);
|
|
}
|
|
|
|
void RubySupportPart::parse(const TQString &fileName)
|
|
{
|
|
TQFile f(TQFile::encodeName(fileName));
|
|
if (!f.open(IO_ReadOnly))
|
|
return;
|
|
TQTextStream stream(&f);
|
|
|
|
TQRegExp classre("^\\s*(class|module)\\s+([A-Z][A-Za-z0-9_]+::)*([A-Z][A-Za-z0-9_]+)\\s*(<\\s*([A-Z][A-Za-z0-9_:]+))?$");
|
|
TQRegExp methodre("^(\\s*)def\\s+(([A-Z][A-Za-z0-9_:]+|self)\\.)?([A-Za-z0-9_]+[!?=]?|\\[\\]=?|\\*\\*||\\-|[!~+*/%&|><^]|>>|<<||<=>|<=|>=|==|===|!=|=~|!~).*$");
|
|
TQRegExp accessre("^\\s*(private|protected|public)\\s*((:([A-Za-z0-9_]+[!?=]?|\\[\\]=?|\\*\\*||\\-|[!~+*/%&|><^]|>>|<<||<=>|<=|>=|==|===|!=|=~|!~),?\\s*)*)$");
|
|
TQRegExp attr_accessorre("^\\s*(attr_accessor|attr_reader|attr_writer)\\s*((:([A-Za-z0-9_]+),?\\s*)*)$");
|
|
TQRegExp symbolre(":([^,]+),?");
|
|
TQRegExp line_contre(",\\s*$");
|
|
TQRegExp slot_signalre("^\\s*(slots|signals|k_dcop|k_dcop_signals)\\s*(('[^)]+\\)',?\\s*)*)$");
|
|
TQRegExp memberre("'([A-Za-z0-9_ &*]+\\s)?([A-Za-z0-9_]+)\\([^)]*\\)',?");
|
|
TQRegExp begin_commentre("^*=begin");
|
|
TQRegExp end_commentre("^*=end");
|
|
TQRegExp variablere("(@@?[A-Za-z0-9_]+)\\s*=\\s*((?:([A-Za-z0-9_:.]+)\\.new)|[\\[\"'%:/\\?\\{]|%r|<<|true|false|^\\?|0[0-7]+|[-+]?0b[01]+|[-+]?0x[1-9a-fA-F]+|[-+]?[0-9_\\.e]+|nil)?");
|
|
TQRegExp endre("^(\\s*)end\\s*$");
|
|
|
|
FileDom m_file = codeModel()->create<FileModel>();
|
|
m_file->setName(fileName);
|
|
|
|
ClassDom lastClass;
|
|
FunctionDom lastMethod;
|
|
TQString lastMethodIndentation;
|
|
int lastAccess = CodeModelItem::Public;
|
|
TQString rawline;
|
|
TQCString line;
|
|
int lineNo = 0;
|
|
|
|
while (!stream.atEnd()) {
|
|
rawline = stream.readLine();
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
if (classre.search(line) != -1) {
|
|
if (m_file->hasClass(classre.cap(3))) {
|
|
lastClass = m_file->classByName( classre.cap(3) )[ 0 ];
|
|
} else {
|
|
lastClass = codeModel()->create<ClassModel>();
|
|
lastClass->setName(classre.cap(3));
|
|
lastClass->setFileName( fileName );
|
|
lastClass->setStartPosition( lineNo, 0 );
|
|
m_file->addClass( lastClass );
|
|
}
|
|
|
|
TQString parent = classre.cap(5);
|
|
if (!parent.isEmpty())
|
|
{
|
|
kdDebug() << "Add parent " << parent << endl;
|
|
lastClass->addBaseClass( parent );
|
|
}
|
|
|
|
lastAccess = CodeModelItem::Public;
|
|
} else if (methodre.search(line) != -1) {
|
|
FunctionDom methodDecl;
|
|
if ( lastClass != 0 && lastClass->hasFunction( methodre.cap(4) ) ) {
|
|
FunctionList methods = lastClass->functionByName( methodre.cap(4) );
|
|
methodDecl = methods[0];
|
|
} else {
|
|
methodDecl = codeModel()->create<FunctionModel>();
|
|
methodDecl->setFileName( fileName );
|
|
methodDecl->setStartPosition( lineNo, 0 );
|
|
methodDecl->setName(methodre.cap(4));
|
|
}
|
|
FunctionDefinitionDom method = codeModel()->create<FunctionDefinitionModel>();
|
|
method->setName(methodre.cap(4));
|
|
kdDebug() << "Add method: " << method->name() << endl;
|
|
method->setFileName( fileName );
|
|
method->setStartPosition( lineNo, 0 );
|
|
if (methodDecl->name() == "initialize") {
|
|
// Ruby constructors are alway private
|
|
methodDecl->setAccess( CodeModelItem::Private );
|
|
} else {
|
|
methodDecl->setAccess( lastAccess );
|
|
}
|
|
if (methodre.cap(2) != "") {
|
|
// A ruby class/singleton method of the form <classname>.<methodname>
|
|
methodDecl->setStatic( true );
|
|
}
|
|
|
|
lastMethodIndentation = methodre.cap(1);
|
|
|
|
lastMethod = method;
|
|
|
|
if (lastClass != 0) {
|
|
TQStringList scope( lastClass->name() );
|
|
method->setScope( scope );
|
|
methodDecl->setScope( scope );
|
|
if( !lastClass->hasFunction(methodDecl->name()) ) {
|
|
lastClass->addFunction( methodDecl );
|
|
}
|
|
if( !lastClass->hasFunctionDefinition(method->name()) ) {
|
|
lastClass->addFunctionDefinition( method );
|
|
}
|
|
} else if( !m_file->hasFunctionDefinition(method->name()) ){
|
|
m_file->addFunction( methodDecl );
|
|
m_file->addFunctionDefinition( method );
|
|
lastClass = 0;
|
|
}
|
|
} else if (endre.search(line) != -1 && lastMethod != 0 && endre.cap(1) == lastMethodIndentation ) {
|
|
int endCol, endLine;
|
|
lastMethod->getEndPosition(&endCol, &endLine);
|
|
if (endLine == 0) {
|
|
//hack to set end position of the previous method to the line
|
|
//where its corresponding "end" is found
|
|
//there's an assumption that method's "def" statement will have the same
|
|
//indentation level as method's "end"
|
|
lastMethod->setEndPosition(lineNo, 0);
|
|
}
|
|
}
|
|
else if (accessre.search(line) != -1 && lastClass != 0) {
|
|
int currentAccess = lastAccess;
|
|
if (accessre.cap(1) == "public") {
|
|
currentAccess = CodeModelItem::Public;
|
|
} else if (accessre.cap(1) == "protected") {
|
|
currentAccess = CodeModelItem::Protected;
|
|
} else if (accessre.cap(1) == "private") {
|
|
currentAccess = CodeModelItem::Private;
|
|
}
|
|
|
|
if (accessre.cap(2) == "") {
|
|
lastAccess = currentAccess;
|
|
} else {
|
|
TQString symbolList( accessre.cap(2) );
|
|
int pos = 0;
|
|
|
|
while ( pos >= 0 ) {
|
|
pos = symbolre.search( symbolList, pos );
|
|
if (pos == -1) {
|
|
if (line_contre.search(line) != -1) {
|
|
rawline = stream.readLine();
|
|
if (!stream.atEnd()) {
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
++lineNo;
|
|
symbolList = line;
|
|
pos = 0;
|
|
}
|
|
}
|
|
} else {
|
|
if ( lastClass->hasFunction( symbolre.cap(1) ) ) {
|
|
FunctionList methods = lastClass->functionByName( symbolre.cap(1) );
|
|
methods[0]->setAccess( currentAccess );
|
|
}
|
|
pos += symbolre.matchedLength();
|
|
}
|
|
}
|
|
}
|
|
} else if (slot_signalre.search(line) != -1 && lastClass != 0) {
|
|
TQString memberList( slot_signalre.cap(2) );
|
|
int pos = 0;
|
|
|
|
while ( pos >= 0 ) {
|
|
pos = memberre.search( memberList, pos );
|
|
if (pos == -1) {
|
|
if (line_contre.search(line) != -1) {
|
|
rawline = stream.readLine();
|
|
if (!stream.atEnd()) {
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
++lineNo;
|
|
memberList = line;
|
|
pos = 0;
|
|
}
|
|
}
|
|
} else {
|
|
FunctionDom method;
|
|
if ( lastClass->hasFunction( memberre.cap(2) ) ) {
|
|
FunctionList methods = lastClass->functionByName( memberre.cap(2) );
|
|
method = methods[0];
|
|
} else {
|
|
method = codeModel()->create<FunctionModel>();
|
|
}
|
|
TQStringList scope( lastClass->name() );
|
|
method->setScope( scope );
|
|
method->setName(memberre.cap(2));
|
|
method->setFileName( fileName );
|
|
method->setStartPosition( lineNo, 0 );
|
|
|
|
if (slot_signalre.cap(1) == "slots" || slot_signalre.cap(1) == "k_dcop") {
|
|
method->setSlot( true );
|
|
} else {
|
|
method->setSignal( true );
|
|
}
|
|
if ( !lastClass->hasFunction(method->name()) ) {
|
|
lastClass->addFunction( method );
|
|
}
|
|
pos += memberre.matchedLength();
|
|
}
|
|
}
|
|
} else if (attr_accessorre.search(line) != -1 && lastClass != 0) {
|
|
TQString attr( attr_accessorre.cap(1) );
|
|
TQString symbolList( attr_accessorre.cap(2) );
|
|
int pos = 0;
|
|
|
|
while ( pos >= 0 ) {
|
|
pos = symbolre.search( symbolList, pos );
|
|
if (pos == -1) {
|
|
if (line_contre.search(line) != -1) {
|
|
rawline = stream.readLine();
|
|
if (!stream.atEnd()) {
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
++lineNo;
|
|
symbolList = line;
|
|
pos = 0;
|
|
}
|
|
}
|
|
} else {
|
|
TQStringList scope( lastClass->name() );
|
|
if ( !lastClass->hasFunction(symbolre.cap(1))
|
|
&& (attr == "attr_accessor" || attr == "attr_reader") )
|
|
{
|
|
FunctionDefinitionDom method = codeModel()->create<FunctionDefinitionModel>();
|
|
method->setName(symbolre.cap(1));
|
|
kdDebug() << "Add method: " << method->name() << endl;
|
|
method->setFileName( fileName );
|
|
method->setStartPosition( lineNo, 0 );
|
|
method->setScope(scope);
|
|
lastClass->addFunction( model_cast<FunctionDom>(method) );
|
|
lastClass->addFunctionDefinition( method );
|
|
}
|
|
|
|
if ( !lastClass->hasFunction(symbolre.cap(1) + "=")
|
|
&& (attr == "attr_accessor" || attr == "attr_writer") )
|
|
{
|
|
FunctionDefinitionDom method = codeModel()->create<FunctionDefinitionModel>();
|
|
method->setName(symbolre.cap(1) + "=");
|
|
kdDebug() << "Add method: " << method->name() << endl;
|
|
method->setFileName( fileName );
|
|
method->setStartPosition( lineNo, 0 );
|
|
method->setScope(scope);
|
|
lastClass->addFunction( model_cast<FunctionDom>(method) );
|
|
lastClass->addFunctionDefinition( method );
|
|
}
|
|
|
|
pos += symbolre.matchedLength();
|
|
}
|
|
}
|
|
} else if (variablere.search(line) != -1 && lastClass != 0) {
|
|
VariableDom attr;
|
|
if ( lastClass->hasVariable( variablere.cap(1) ) ) {
|
|
attr = lastClass->variableByName( variablere.cap(1) );
|
|
} else {
|
|
attr = codeModel()->create<VariableModel>();
|
|
attr->setName( variablere.cap(1) );
|
|
attr->setFileName( fileName );
|
|
attr->setStartPosition( lineNo, 0 );
|
|
attr->setAccess( CodeModelItem::Private );
|
|
if (TQRegExp("^@@").search(attr->name()) != -1) {
|
|
attr->setStatic( true );
|
|
}
|
|
lastClass->addVariable( attr );
|
|
}
|
|
|
|
// Give priority to any variable initialized in the constructor
|
|
// Otherwise, take the first one found in the source file
|
|
if (lastMethod != 0 && lastMethod->name() == "initialize") {
|
|
attr->setFileName( fileName );
|
|
attr->setStartPosition( lineNo, 0 );
|
|
}
|
|
|
|
if (TQRegExp("^(/|%r)").search(variablere.cap(2)) != -1) {
|
|
attr->setType( "Regexp" );
|
|
} else if (TQRegExp("^[\"'%<]").search(variablere.cap(2)) != -1) {
|
|
attr->setType( "String" );
|
|
} else if (TQRegExp("^\\[").search(variablere.cap(2)) != -1) {
|
|
attr->setType( "Array" );
|
|
} else if (TQRegExp("^\\{").search(variablere.cap(2)) != -1) {
|
|
attr->setType( "Hash" );
|
|
} else if (TQRegExp("^:").search(variablere.cap(2)) != -1) {
|
|
attr->setType( "Symbol" );
|
|
} else if (TQRegExp("\\.\\.").search(variablere.cap(2)) != -1) {
|
|
attr->setType( "Range" );
|
|
} else if (variablere.cap(2) == "true" || variablere.cap(2) == "false") {
|
|
attr->setType( "Boolean" );
|
|
} else if ( TQRegExp("^[-+]?[0-9_]+").exactMatch(variablere.cap(2))
|
|
|| TQRegExp("^[-+]?(0x|0|0b|\\?)").search(variablere.cap(2)) != -1 )
|
|
{
|
|
attr->setType( "Integer" );
|
|
} else if (TQRegExp("[0-9._]+(e[-+0-9]+)?").exactMatch(variablere.cap(2))) {
|
|
attr->setType( "Float" );
|
|
} else if (variablere.cap(2) != "nil" && variablere.cap(3) != "") {
|
|
attr->setType( variablere.cap(3) );
|
|
}
|
|
} else if (begin_commentre.search(line) != -1) {
|
|
while (!stream.atEnd() && end_commentre.search(line) == -1) {
|
|
rawline = stream.readLine();
|
|
line = rawline.stripWhiteSpace().local8Bit();
|
|
++lineNo;
|
|
}
|
|
}
|
|
|
|
++lineNo;
|
|
}
|
|
|
|
f.close();
|
|
|
|
codeModel()->addFile( m_file );
|
|
}
|
|
|
|
|
|
void RubySupportPart::slotRun ()
|
|
{
|
|
// if we can't save all parts, then the user canceled
|
|
if ( partController()->saveAllFiles() == false )
|
|
return;
|
|
TQFileInfo program(mainProgram());
|
|
if (mainProgram().endsWith("script/server")) {
|
|
TQString cmd;
|
|
TQFileInfo server(project()->projectDirectory() + "/script/server");
|
|
|
|
// Starting WEBrick for a Rails app. Translate a SIGTERM signal sent by KDevelop
|
|
// to a SIGINT expected by WEBrick (ie control&c) to terminate it.
|
|
cmd += "script/server& \n trap \"kill -s SIGINT $!\" TERM \n wait \n exit 0";
|
|
if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("TDevelop/AppFrontend"))
|
|
appFrontend->startAppCommand(project()->projectDirectory(), cmd, false);
|
|
} else {
|
|
TQString cmd = TQString("%1 -K%2 -C\"%3\" -I\"%4\" \"%5\" %6")
|
|
.arg(interpreter())
|
|
.arg(characterCoding())
|
|
.arg(runDirectory())
|
|
.arg(program.dirPath())
|
|
.arg(program.fileName())
|
|
.arg(programArgs());
|
|
startApplication(cmd);
|
|
}
|
|
}
|
|
|
|
TQString RubySupportPart::interpreter() {
|
|
TQString prog = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/interpreter");
|
|
if (prog.isEmpty()) prog = "ruby";
|
|
return prog;
|
|
}
|
|
|
|
TQString RubySupportPart::shell() {
|
|
TQString shell = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/shell");
|
|
if (shell.isEmpty()) shell = "irb";
|
|
return shell;
|
|
}
|
|
|
|
TQString RubySupportPart::mainProgram() {
|
|
TQString prog;
|
|
int runMainProgram = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/runmainprogram");
|
|
|
|
if (runMainProgram == 0) {
|
|
prog = project()->projectDirectory() + "/" + DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/mainprogram");
|
|
} else {
|
|
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
|
|
if (ro_part != 0) {
|
|
prog = ro_part->url().path();
|
|
}
|
|
}
|
|
|
|
return prog;
|
|
}
|
|
|
|
TQString RubySupportPart::runDirectory() {
|
|
TQString cwd = DomUtil::readEntry(*projectDom(), "/kdevscriptproject/run/globalcwd");
|
|
if (cwd.isEmpty())
|
|
{
|
|
TQString mainProg = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/mainprogram");
|
|
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
|
|
if (mainProg.isEmpty() && ro_part)
|
|
cwd = ro_part->url().directory();
|
|
else
|
|
cwd = project()->buildDirectory();
|
|
}
|
|
return cwd;
|
|
}
|
|
|
|
TQString RubySupportPart::programArgs() {
|
|
TQString args = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/programargs");
|
|
return args;
|
|
}
|
|
|
|
TQString RubySupportPart::characterCoding() {
|
|
int coding = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/charactercoding");
|
|
TQString code("A");
|
|
|
|
switch (coding) {
|
|
case 0:
|
|
code = "A";
|
|
break;
|
|
case 1:
|
|
code = "E";
|
|
break;
|
|
case 2:
|
|
code = "S";
|
|
break;
|
|
case 3:
|
|
code = "U";
|
|
break;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
|
|
void RubySupportPart::startApplication(const TQString &program) {
|
|
bool inTerminal = DomUtil::readBoolEntry(*projectDom(), "/kdevrubysupport/run/terminal");
|
|
if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("TDevelop/AppFrontend"))
|
|
appFrontend->startAppCommand(TQString(), program, inTerminal);
|
|
}
|
|
|
|
|
|
KMimeType::List RubySupportPart::mimeTypes( )
|
|
{
|
|
KMimeType::List list;
|
|
KMimeType::Ptr mime = KMimeType::mimeType( "text/x-ruby" );
|
|
if( mime )
|
|
list << mime;
|
|
return list;
|
|
}
|
|
|
|
KDevDesignerIntegration *RubySupportPart::designer(KInterfaceDesigner::DesignerType type)
|
|
{
|
|
KDevDesignerIntegration *des = 0;
|
|
switch (type)
|
|
{
|
|
case KInterfaceDesigner::TQtDesigner:
|
|
des = m_designers[type];
|
|
if (des == 0)
|
|
{
|
|
RubyImplementationWidget *impl = new RubyImplementationWidget(this);
|
|
des = new QtDesignerRubyIntegration(this, impl);
|
|
des->loadSettings(*project()->projectDom(),
|
|
"kdevrubysupport/designerintegration");
|
|
m_designers[type] = des;
|
|
}
|
|
break;
|
|
case KInterfaceDesigner::Glade:
|
|
break;
|
|
}
|
|
return des;
|
|
}
|
|
|
|
void RubySupportPart::projectClosed( )
|
|
{
|
|
for (TQMap<KInterfaceDesigner::DesignerType, KDevDesignerIntegration*>::const_iterator it = m_designers.begin();
|
|
it != m_designers.end(); ++it)
|
|
{
|
|
kdDebug() << "calling save settings fro designer integration" << endl;
|
|
it.data()->saveSettings(*project()->projectDom(), "kdevrubysupport/designerintegration");
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::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->setWhatsThis(id, i18n("<b>Create or select implementation</b><p>Creates or selects a subclass of selected form for use with integrated KDevDesigner."));
|
|
}
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::slotCreateSubclass()
|
|
{
|
|
TQFileInfo fi(m_contextFileName);
|
|
if (fi.extension(false) != "ui")
|
|
return;
|
|
QtDesignerRubyIntegration *des = dynamic_cast<QtDesignerRubyIntegration*>(designer(KInterfaceDesigner::TQtDesigner));
|
|
if (des)
|
|
des->selectImplementation(m_contextFileName);
|
|
}
|
|
|
|
void RubySupportPart::slotBrowse()
|
|
{
|
|
kapp->invokeBrowser("http://localhost:3000/");
|
|
}
|
|
|
|
void RubySupportPart::slotSwitchToController()
|
|
{
|
|
KParts::Part *activePart = partController()->activePart();
|
|
if (!activePart)
|
|
return;
|
|
KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
|
|
if (!ropart)
|
|
return;
|
|
TQFileInfo file(ropart->url().path());
|
|
if (!file.exists())
|
|
return;
|
|
TQString ext = file.extension();
|
|
TQString name = file.baseName();
|
|
TQString switchTo = "";
|
|
if ((ext == "rb") && !name.endsWith("_controller"))
|
|
{
|
|
if (name.endsWith("_test"))
|
|
{
|
|
switchTo = name.remove(TQRegExp("_test$")); //the file is the test
|
|
switchTo = name.remove(TQRegExp("_controller$")); //remove functional test name parts
|
|
}
|
|
else
|
|
switchTo = name;
|
|
}
|
|
else if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
|
|
{
|
|
//this is a view, we need to find the directory of this view and try to find
|
|
//the controller basing on the directory information
|
|
switchTo = file.dir().dirName();
|
|
}
|
|
TQString controllersDir = project()->projectDirectory() + "/app/controllers/";
|
|
if (!switchTo.isEmpty())
|
|
{
|
|
if (switchTo.endsWith("s"))
|
|
switchTo = switchTo.mid(0, switchTo.length()-1);
|
|
TQString singular = controllersDir + switchTo + "_controller.rb";
|
|
TQString plural = controllersDir + switchTo + "s_controller.rb";
|
|
KURL url = KURL::fromPathOrURL(TQFile::exists(singular) ? singular : plural);
|
|
partController()->editDocument(url);
|
|
}
|
|
}
|
|
|
|
void RubySupportPart::slotSwitchToTest()
|
|
{
|
|
KParts::Part *activePart = partController()->activePart();
|
|
if (!activePart)
|
|
return;
|
|
KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
|
|
if (!ropart)
|
|
return;
|
|
TQFileInfo file(ropart->url().path());
|
|
if (!file.exists())
|
|
return;
|
|
TQString ext = file.extension();
|
|
TQString name = file.baseName();
|
|
TQString switchTo = "";
|
|
|
|
if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
|
|
{
|
|
//this is a view already, let's show the list of all views for this model
|
|
switchTo = file.dir().dirName();
|
|
}
|
|
else if (ext == "rb")
|
|
switchTo = name.remove(TQRegExp("_controller$")).remove(TQRegExp("_controller_test$")).remove(TQRegExp("_test$"));
|
|
|
|
if (switchTo.isEmpty())
|
|
return;
|
|
|
|
if (switchTo.endsWith("s"))
|
|
switchTo = switchTo.mid(0, switchTo.length() - 1);
|
|
|
|
KURL::List urls;
|
|
TQString testDir = project()->projectDirectory() + "/test/";
|
|
TQString functionalTestS = testDir + "functional/" + switchTo + "_controller_test.rb";
|
|
TQString functionalTestP = testDir + "functional/" + switchTo + "s_controller_test.rb";
|
|
TQString integrationTestS = testDir + "integration/" + switchTo + "_test.rb";
|
|
TQString integrationTestP = testDir + "integration/" + switchTo + "s_test.rb";
|
|
TQString unitTestS = testDir + "unit/" + switchTo + "_test.rb";
|
|
TQString unitTestP = testDir + "unit/" + switchTo + "s_test.rb";
|
|
if (TQFile::exists(functionalTestP)) urls << KURL::fromPathOrURL(functionalTestP);
|
|
if (TQFile::exists(integrationTestP)) urls << KURL::fromPathOrURL(integrationTestP);
|
|
if (TQFile::exists(unitTestP)) urls << KURL::fromPathOrURL(unitTestP);
|
|
if (TQFile::exists(functionalTestS)) urls << KURL::fromPathOrURL(functionalTestS);
|
|
if (TQFile::exists(integrationTestS)) urls << KURL::fromPathOrURL(integrationTestS);
|
|
if (TQFile::exists(unitTestS)) urls << KURL::fromPathOrURL(unitTestS);
|
|
|
|
KDevQuickOpen *qo = extension<KDevQuickOpen>("TDevelop/QuickOpen");
|
|
if (qo && !urls.isEmpty())
|
|
qo->quickOpenFile(urls);
|
|
}
|
|
|
|
void RubySupportPart::slotSwitchToModel()
|
|
{
|
|
KParts::Part *activePart = partController()->activePart();
|
|
if (!activePart)
|
|
return;
|
|
KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
|
|
if (!ropart)
|
|
return;
|
|
TQFileInfo file(ropart->url().path());
|
|
if (!file.exists())
|
|
return;
|
|
TQString ext = file.extension();
|
|
TQString name = file.baseName();
|
|
TQString switchTo = "";
|
|
|
|
if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
|
|
{
|
|
//this is a view already, let's show the list of all views for this model
|
|
switchTo = file.dir().dirName();
|
|
}
|
|
else if (ext == "rb" && (name.endsWith("_controller") || name.endsWith("_test")))
|
|
{
|
|
switchTo = name.remove(TQRegExp("_controller$")).remove(TQRegExp("_controller_test$")).remove(TQRegExp("_test$"));
|
|
}
|
|
|
|
if (switchTo.isEmpty())
|
|
return;
|
|
|
|
if (switchTo.endsWith("s"))
|
|
switchTo = switchTo.mid(0, switchTo.length() - 1);
|
|
|
|
TQString modelsDir = project()->projectDirectory() + "/app/models/";
|
|
TQString singular = modelsDir + switchTo + "_controller.rb";
|
|
TQString plural = modelsDir + switchTo + "s_controller.rb";
|
|
KURL url = KURL::fromPathOrURL(TQFile::exists(singular) ? singular : plural);
|
|
|
|
partController()->editDocument(KURL::fromPathOrURL(modelsDir + switchTo + ".rb"));
|
|
}
|
|
|
|
void RubySupportPart::slotSwitchToView()
|
|
{
|
|
KParts::Part *activePart = partController()->activePart();
|
|
if (!activePart)
|
|
return;
|
|
KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
|
|
if (!ropart)
|
|
return;
|
|
TQFileInfo file(ropart->url().path());
|
|
if (!file.exists())
|
|
return;
|
|
TQString ext = file.extension();
|
|
TQString name = file.baseName();
|
|
TQString switchTo = "";
|
|
|
|
if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
|
|
{
|
|
//this is a view already, let's show the list of all views for this model
|
|
switchTo = file.dir().dirName();
|
|
}
|
|
else if (ext == "rb")
|
|
switchTo = name.remove(TQRegExp("_controller$")).remove(TQRegExp("_controller_test$")).remove(TQRegExp("_test$"));
|
|
|
|
if (switchTo.isEmpty())
|
|
return;
|
|
|
|
if (switchTo.endsWith("s"))
|
|
switchTo = switchTo.mid(0, switchTo.length() - 1);
|
|
|
|
KURL::List urls;
|
|
TQDir viewsDir;
|
|
TQDir viewsDirS = TQDir(project()->projectDirectory() + "/app/views/" + switchTo);
|
|
TQDir viewsDirP = TQDir(project()->projectDirectory() + "/app/views/" + switchTo + "s");
|
|
if (viewsDirS.exists())
|
|
viewsDir = viewsDirS;
|
|
else if (viewsDirP.exists())
|
|
viewsDir = viewsDirP;
|
|
else
|
|
return;
|
|
|
|
TQStringList views = viewsDir.entryList();
|
|
|
|
for (TQStringList::const_iterator it = views.begin(); it != views.end(); ++it)
|
|
{
|
|
TQString viewName = *it;
|
|
if ( !(viewName.endsWith("~") || viewName == "." || viewName == "..") )
|
|
urls << KURL::fromPathOrURL(viewsDir.absPath() + "/" + viewName);
|
|
}
|
|
KDevQuickOpen *qo = extension<KDevQuickOpen>("TDevelop/QuickOpen");
|
|
if (qo)
|
|
qo->quickOpenFile(urls);
|
|
}
|
|
|
|
void RubySupportPart::slotRunTestUnderCursor()
|
|
{
|
|
// if we can't save all parts, then the user canceled
|
|
if ( partController()->saveAllFiles() == false )
|
|
return;
|
|
|
|
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
|
|
TQString prog;
|
|
if (ro_part != 0) {
|
|
prog = ro_part->url().path();
|
|
} else
|
|
return;
|
|
|
|
KTextEditor::ViewCursorInterface* activeViewCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>( ro_part->widget() );
|
|
if (!activeViewCursor) return;
|
|
|
|
unsigned int line, column;
|
|
activeViewCursor->cursorPositionReal(&line, &column);
|
|
CodeModelUtils::CodeModelHelper hlp(codeModel(), codeModel()->fileByName(prog));
|
|
FunctionDom fun = hlp.functionAt(line, column);
|
|
if (fun == 0) return;
|
|
|
|
TQFileInfo program(prog);
|
|
TQString cmd = TQString("%1 -K%2 -C\"%3\" -I\"%4\" \"%5\" %6")
|
|
.arg(interpreter())
|
|
.arg(characterCoding())
|
|
.arg(runDirectory())
|
|
.arg(program.dirPath())
|
|
.arg(program.fileName())
|
|
.arg(" -n " + fun->name());
|
|
startApplication(cmd);
|
|
}
|
|
|
|
#include "rubysupport_part.moc"
|