#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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef KDevGenericFactory 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"); TDEAction *action; action = new TDEAction( i18n("&Run"), "application-x-executable", SHIFT + Key_F9, this, TQT_SLOT(slotRun()), actionCollection(), "build_execute" ); action->setToolTip(i18n("Run")); action->setWhatsThis(i18n("Run

Starts an application.")); action->setIcon("ruby_run.png"); action = new TDEAction( i18n("Run Test Under Cursor"), "application-x-executable", ALT + Key_F9, this, TQT_SLOT(slotRunTestUnderCursor()), actionCollection(), "build_execute_test_function" ); action->setToolTip(i18n("Run Test Under Cursor")); action->setWhatsThis(i18n("Run Test Under Cursor

Runs the function under the cursor as test.")); action = new TDEAction( i18n("Launch Browser"), "network", 0, this, TQT_SLOT(slotBrowse()), actionCollection(), "build_launch_browser" ); action->setToolTip(i18n("Launch Browser")); action->setWhatsThis(i18n("Launch Browser

Opens a web browser pointing to the Ruby Rails server") ); action = new TDEAction( i18n("Switch To Controller"), 0, CTRL+ALT+Key_1, this, TQT_SLOT(slotSwitchToController()), actionCollection(), "switch_to_controller" ); action = new TDEAction( i18n("Switch To Model"), 0, CTRL+ALT+Key_2, this, TQT_SLOT(slotSwitchToModel()), actionCollection(), "switch_to_model" ); action = new TDEAction( i18n("Switch To View"), 0, CTRL+ALT+Key_3, this, TQT_SLOT(slotSwitchToView()), actionCollection(), "switch_to_view" ); action = new TDEAction( 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", TDEIcon::SizeMedium, TDEIcon::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", TDEIcon::SizeMedium, TDEIcon::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("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(); 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(); 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(); methodDecl->setFileName( fileName ); methodDecl->setStartPosition( lineNo, 0 ); methodDecl->setName(methodre.cap(4)); } FunctionDefinitionDom method = codeModel()->create(); 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 . 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(); } 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(); 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(method) ); lastClass->addFunctionDefinition( method ); } if ( !lastClass->hasFunction(symbolre.cap(1) + "=") && (attr == "attr_accessor" || attr == "attr_writer") ) { FunctionDefinitionDom method = codeModel()->create(); 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(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(); 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("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(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(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("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::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(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("Create or select implementation

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(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(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(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("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(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(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("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(partController()->activePart()); TQString prog; if (ro_part != 0) { prog = ro_part->url().path(); } else return; KTextEditor::ViewCursorInterface* activeViewCursor = dynamic_cast( 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"