summaryrefslogtreecommitdiffstats
path: root/languages/ruby/rubysupport_part.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'languages/ruby/rubysupport_part.cpp')
-rw-r--r--languages/ruby/rubysupport_part.cpp908
1 files changed, 908 insertions, 0 deletions
diff --git a/languages/ruby/rubysupport_part.cpp b/languages/ruby/rubysupport_part.cpp
new file mode 100644
index 00000000..c116c05d
--- /dev/null
+++ b/languages/ruby/rubysupport_part.cpp
@@ -0,0 +1,908 @@
+#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 <qdir.h>
+#include <qwhatsthis.h>
+#include <qtimer.h>
+#include <qfileinfo.h>
+#include <qpopupmenu.h>
+#include <qregexp.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(QObject *parent, const char *name, const QStringList& )
+ : KDevLanguageSupport (&data, parent, name ? name : "RubySupportPart" )
+{
+ setInstance(RubySupportFactory::instance());
+ setXMLFile("kdevrubysupport.rc");
+
+ KAction *action;
+ action = new KAction( i18n("&Run"), "exec", SHIFT + Key_F9,
+ this, 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, 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, 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, SLOT(slotSwitchToController()), actionCollection(), "switch_to_controller" );
+ action = new KAction( i18n("Switch To Model"), 0, CTRL+ALT+Key_2, this, SLOT(slotSwitchToModel()), actionCollection(), "switch_to_model" );
+ action = new KAction( i18n("Switch To View"), 0, CTRL+ALT+Key_3, this, SLOT(slotSwitchToView()), actionCollection(), "switch_to_view" );
+ action = new KAction( i18n("Switch To Test"), 0, CTRL+ALT+Key_4, this, 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(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
+ connect( core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()) );
+ connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
+ this, SLOT(contextMenu(QPopupMenu *, const Context *)) );
+ connect( partController(), SIGNAL(savedFile(const KURL&)),
+ this, SLOT(savedFile(const KURL&)) );
+ connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
+ this, SLOT(projectConfigWidget(KDialogBase*)) );
+}
+
+
+RubySupportPart::~RubySupportPart()
+{
+ if ( m_shellWidget )
+ mainWindow()->removeView( m_shellWidget );
+ delete m_shellWidget;
+}
+
+
+void RubySupportPart::projectConfigWidget(KDialogBase *dlg)
+{
+ QVBox *vbox = dlg->addVBoxPage(i18n("Ruby"), i18n("Ruby"), BarIcon("ruby_config.png", KIcon::SizeMedium, KIcon::DefaultState, RubySupportPart::instance()));
+ RubyConfigWidget *w = new RubyConfigWidget(*projectDom(), (QWidget *)vbox, "ruby config widget");
+ connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
+}
+
+void RubySupportPart::projectOpened()
+{
+ kdDebug() << "projectOpened()" << endl;
+
+ QStrList l;
+ l.append( shell().latin1() ) ;
+ m_shellWidget->setShell( shell().latin1(), l );
+ m_shellWidget->activate();
+ m_shellWidget->setAutoReactivateOnClose( true );
+
+ connect( project(), SIGNAL(addedFilesToProject(const QStringList &)),
+ this, SLOT(addedFilesToProject(const QStringList &)) );
+ connect( project(), SIGNAL(removedFilesFromProject(const QStringList &)),
+ this, SLOT(removedFilesFromProject(const QStringList &)) );
+
+ QFileInfo program(mainProgram());
+
+ // If it's a Rails project, create the project files if they're missing
+ if (mainProgram().endsWith("script/server")) {
+ QString cmd;
+ QFileInfo server(project()->projectDirectory() + "/script/server");
+ if (! server.exists()) {
+ cmd += "rails " + project()->projectDirectory();
+ if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("KDevelop/AppFrontend"))
+ appFrontend->startAppCommand(project()->projectDirectory(), cmd, false);
+ }
+ }
+
+ // We want to parse only after all components have been
+ // properly initialized
+ QTimer::singleShot(0, this, SLOT(initialParse()));
+}
+
+void RubySupportPart::maybeParse(const QString fileName)
+{
+ QFileInfo 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);
+ QStringList files = project()->allFiles();
+ for (QStringList::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 QStringList &fileList)
+{
+ kdDebug() << "addedFilesToProject()" << endl;
+
+ QStringList::ConstIterator it;
+
+ for ( it = fileList.begin(); it != fileList.end(); ++it )
+ {
+ QString fileName = project()->projectDirectory() + "/" + ( *it );
+ maybeParse( fileName );
+ }
+}
+
+void RubySupportPart::removedFilesFromProject(const QStringList &fileList)
+{
+ kdDebug() << "removedFilesFromProject()" << endl;
+
+ QStringList::ConstIterator it;
+
+ for ( it = fileList.begin(); it != fileList.end(); ++it )
+ {
+ QString 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 QString &fileName)
+{
+ QFile f(QFile::encodeName(fileName));
+ if (!f.open(IO_ReadOnly))
+ return;
+ QTextStream stream(&f);
+
+ QRegExp 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_:]+))?$");
+ QRegExp methodre("^(\\s*)def\\s+(([A-Z][A-Za-z0-9_:]+|self)\\.)?([A-Za-z0-9_]+[!?=]?|\\[\\]=?|\\*\\*||\\-|[!~+*/%&|><^]|>>|<<||<=>|<=|>=|==|===|!=|=~|!~).*$");
+ QRegExp accessre("^\\s*(private|protected|public)\\s*((:([A-Za-z0-9_]+[!?=]?|\\[\\]=?|\\*\\*||\\-|[!~+*/%&|><^]|>>|<<||<=>|<=|>=|==|===|!=|=~|!~),?\\s*)*)$");
+ QRegExp attr_accessorre("^\\s*(attr_accessor|attr_reader|attr_writer)\\s*((:([A-Za-z0-9_]+),?\\s*)*)$");
+ QRegExp symbolre(":([^,]+),?");
+ QRegExp line_contre(",\\s*$");
+ QRegExp slot_signalre("^\\s*(slots|signals|k_dcop|k_dcop_signals)\\s*(('[^)]+\\)',?\\s*)*)$");
+ QRegExp memberre("'([A-Za-z0-9_ &*]+\\s)?([A-Za-z0-9_]+)\\([^)]*\\)',?");
+ QRegExp begin_commentre("^*=begin");
+ QRegExp end_commentre("^*=end");
+ QRegExp 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)?");
+ QRegExp endre("^(\\s*)end\\s*$");
+
+ FileDom m_file = codeModel()->create<FileModel>();
+ m_file->setName(fileName);
+
+ ClassDom lastClass;
+ FunctionDom lastMethod;
+ QString lastMethodIndentation;
+ int lastAccess = CodeModelItem::Public;
+ QString rawline;
+ QCString 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 );
+ }
+
+ QString 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) {
+ QStringList 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 {
+ QString 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) {
+ QString 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>();
+ }
+ QStringList 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) {
+ QString attr( attr_accessorre.cap(1) );
+ QString 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 {
+ QStringList 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 (QRegExp("^@@").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 (QRegExp("^(/|%r)").search(variablere.cap(2)) != -1) {
+ attr->setType( "Regexp" );
+ } else if (QRegExp("^[\"'%<]").search(variablere.cap(2)) != -1) {
+ attr->setType( "String" );
+ } else if (QRegExp("^\\[").search(variablere.cap(2)) != -1) {
+ attr->setType( "Array" );
+ } else if (QRegExp("^\\{").search(variablere.cap(2)) != -1) {
+ attr->setType( "Hash" );
+ } else if (QRegExp("^:").search(variablere.cap(2)) != -1) {
+ attr->setType( "Symbol" );
+ } else if (QRegExp("\\.\\.").search(variablere.cap(2)) != -1) {
+ attr->setType( "Range" );
+ } else if (variablere.cap(2) == "true" || variablere.cap(2) == "false") {
+ attr->setType( "Boolean" );
+ } else if ( QRegExp("^[-+]?[0-9_]+").exactMatch(variablere.cap(2))
+ || QRegExp("^[-+]?(0x|0|0b|\\?)").search(variablere.cap(2)) != -1 )
+ {
+ attr->setType( "Integer" );
+ } else if (QRegExp("[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;
+ QFileInfo program(mainProgram());
+ if (mainProgram().endsWith("script/server")) {
+ QString cmd;
+ QFileInfo 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>("KDevelop/AppFrontend"))
+ appFrontend->startAppCommand(project()->projectDirectory(), cmd, false);
+ } else {
+ QString cmd = QString("%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);
+ }
+}
+
+QString RubySupportPart::interpreter() {
+ QString prog = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/interpreter");
+ if (prog.isEmpty()) prog = "ruby";
+ return prog;
+}
+
+QString RubySupportPart::shell() {
+ QString shell = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/shell");
+ if (shell.isEmpty()) shell = "irb";
+ return shell;
+}
+
+QString RubySupportPart::mainProgram() {
+ QString 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;
+}
+
+QString RubySupportPart::runDirectory() {
+ QString cwd = DomUtil::readEntry(*projectDom(), "/kdevscriptproject/run/globalcwd");
+ if (cwd.isEmpty())
+ {
+ QString 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;
+}
+
+QString RubySupportPart::programArgs() {
+ QString args = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/programargs");
+ return args;
+}
+
+QString RubySupportPart::characterCoding() {
+ int coding = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/charactercoding");
+ QString 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 QString &program) {
+ bool inTerminal = DomUtil::readBoolEntry(*projectDom(), "/kdevrubysupport/run/terminal");
+ if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("KDevelop/AppFrontend"))
+ appFrontend->startAppCommand(QString::QString(), 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::QtDesigner:
+ 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 (QMap<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( QPopupMenu * 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, 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()
+{
+ QFileInfo fi(m_contextFileName);
+ if (fi.extension(false) != "ui")
+ return;
+ QtDesignerRubyIntegration *des = dynamic_cast<QtDesignerRubyIntegration*>(designer(KInterfaceDesigner::QtDesigner));
+ 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;
+ QFileInfo file(ropart->url().path());
+ if (!file.exists())
+ return;
+ QString ext = file.extension();
+ QString name = file.baseName();
+ QString switchTo = "";
+ if ((ext == "rb") && !name.endsWith("_controller"))
+ {
+ if (name.endsWith("_test"))
+ {
+ switchTo = name.remove(QRegExp("_test$")); //the file is the test
+ switchTo = name.remove(QRegExp("_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();
+ }
+ QString controllersDir = project()->projectDirectory() + "/app/controllers/";
+ if (!switchTo.isEmpty())
+ {
+ if (switchTo.endsWith("s"))
+ switchTo = switchTo.mid(0, switchTo.length()-1);
+ QString singular = controllersDir + switchTo + "_controller.rb";
+ QString plural = controllersDir + switchTo + "s_controller.rb";
+ KURL url = KURL::fromPathOrURL(QFile::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;
+ QFileInfo file(ropart->url().path());
+ if (!file.exists())
+ return;
+ QString ext = file.extension();
+ QString name = file.baseName();
+ QString 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(QRegExp("_controller$")).remove(QRegExp("_controller_test$")).remove(QRegExp("_test$"));
+
+ if (switchTo.isEmpty())
+ return;
+
+ if (switchTo.endsWith("s"))
+ switchTo = switchTo.mid(0, switchTo.length() - 1);
+
+ KURL::List urls;
+ QString testDir = project()->projectDirectory() + "/test/";
+ QString functionalTestS = testDir + "functional/" + switchTo + "_controller_test.rb";
+ QString functionalTestP = testDir + "functional/" + switchTo + "s_controller_test.rb";
+ QString integrationTestS = testDir + "integration/" + switchTo + "_test.rb";
+ QString integrationTestP = testDir + "integration/" + switchTo + "s_test.rb";
+ QString unitTestS = testDir + "unit/" + switchTo + "_test.rb";
+ QString unitTestP = testDir + "unit/" + switchTo + "s_test.rb";
+ if (QFile::exists(functionalTestP)) urls << KURL::fromPathOrURL(functionalTestP);
+ if (QFile::exists(integrationTestP)) urls << KURL::fromPathOrURL(integrationTestP);
+ if (QFile::exists(unitTestP)) urls << KURL::fromPathOrURL(unitTestP);
+ if (QFile::exists(functionalTestS)) urls << KURL::fromPathOrURL(functionalTestS);
+ if (QFile::exists(integrationTestS)) urls << KURL::fromPathOrURL(integrationTestS);
+ if (QFile::exists(unitTestS)) urls << KURL::fromPathOrURL(unitTestS);
+
+ KDevQuickOpen *qo = extension<KDevQuickOpen>("KDevelop/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;
+ QFileInfo file(ropart->url().path());
+ if (!file.exists())
+ return;
+ QString ext = file.extension();
+ QString name = file.baseName();
+ QString 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(QRegExp("_controller$")).remove(QRegExp("_controller_test$")).remove(QRegExp("_test$"));
+ }
+
+ if (switchTo.isEmpty())
+ return;
+
+ if (switchTo.endsWith("s"))
+ switchTo = switchTo.mid(0, switchTo.length() - 1);
+
+ QString modelsDir = project()->projectDirectory() + "/app/models/";
+ QString singular = modelsDir + switchTo + "_controller.rb";
+ QString plural = modelsDir + switchTo + "s_controller.rb";
+ KURL url = KURL::fromPathOrURL(QFile::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;
+ QFileInfo file(ropart->url().path());
+ if (!file.exists())
+ return;
+ QString ext = file.extension();
+ QString name = file.baseName();
+ QString 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(QRegExp("_controller$")).remove(QRegExp("_controller_test$")).remove(QRegExp("_test$"));
+
+ if (switchTo.isEmpty())
+ return;
+
+ if (switchTo.endsWith("s"))
+ switchTo = switchTo.mid(0, switchTo.length() - 1);
+
+ KURL::List urls;
+ QDir viewsDir;
+ QDir viewsDirS = QDir(project()->projectDirectory() + "/app/views/" + switchTo);
+ QDir viewsDirP = QDir(project()->projectDirectory() + "/app/views/" + switchTo + "s");
+ if (viewsDirS.exists())
+ viewsDir = viewsDirS;
+ else if (viewsDirP.exists())
+ viewsDir = viewsDirP;
+ else
+ return;
+
+ QStringList views = viewsDir.entryList();
+
+ for (QStringList::const_iterator it = views.begin(); it != views.end(); ++it)
+ {
+ QString viewName = *it;
+ if ( !(viewName.endsWith("~") || viewName == "." || viewName == "..") )
+ urls << KURL::fromPathOrURL(viewsDir.absPath() + "/" + viewName);
+ }
+ KDevQuickOpen *qo = extension<KDevQuickOpen>("KDevelop/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());
+ QString 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;
+
+ QFileInfo program(prog);
+ QString cmd = QString("%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"