/* * Copyright (C) 2003 Ian Reinhart Geiser */ #include "bashsupport_part.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef KDevGenericFactory BashSupportFactory; static const KDevPluginInfo data("kdevbashsupport"); K_EXPORT_COMPONENT_FACTORY( libkdevbashsupport, BashSupportFactory( data ) ) BashSupportPart::BashSupportPart(TQObject *tqparent, const char *name, const TQStringList& ) : KDevLanguageSupport (&data, tqparent, name ? name : "BashSupportPart" ) { setInstance(BashSupportFactory::instance()); setXMLFile("kdevbashsupport.rc"); KAction *action; action = new KAction( i18n("&Run"), "exec",Key_F9,this, TQT_SLOT(slotRun()),actionCollection(), "build_execute" ); action->setToolTip(i18n("Run")); action->setWhatsThis(i18n("Run

Starts an application.")); kdDebug() << "Creating BashSupportPart" << 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 KURL&)), this, TQT_SLOT(savedFile(const KURL&)) ); connect(partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)), this, TQT_SLOT(slotActivePartChanged(KParts::Part *))); m_cc = new BashCodeCompletion(); } BashSupportPart::~BashSupportPart() { delete( m_cc ); m_cc = 0; } void BashSupportPart::projectConfigWidget(KDialogBase *dlg) { Q_UNUSED( dlg ); // TQVBox *vbox = dlg->addVBoxPage(i18n("Bash")); // RubyConfigWidget *w = new RubyConfigWidget(*projectDom(), (TQWidget *)vbox, "Bash config widget"); // connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) ); } void BashSupportPart::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 BashSupportPart::projectClosed() { } void BashSupportPart::slotRun () { TQString file; KParts::ReadOnlyPart *ro_part = dynamic_cast(partController()->activePart()); if(ro_part) file = ro_part->url().path(); TQString cmd = interpreter() + " " + file; startApplication(cmd); } TQString BashSupportPart::interpreter() { TQString prog = DomUtil::readEntry(*projectDom(), "/kdevrbashsupport/run/interpreter"); if (prog.isEmpty()) prog = "bash"; return prog; } void BashSupportPart::parse() { kdDebug(9014) << "initialParse()" << endl; if (project()) { kapp->setOverrideCursor(waitCursor); TQStringList files = project()->allFiles(); for (TQStringList::Iterator it = files.begin(); it != files.end() ;++it) { kdDebug(9014) << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl; parse(project()->projectDirectory() + "/" + *it); } emit updatedSourceInfo(); kapp->restoreOverrideCursor(); } else { kdDebug(9014) << "No project" << endl; } } void BashSupportPart::addedFilesToProject(const TQStringList &fileList) { kdDebug(9014) << "addedFilesToProject()" << endl; TQStringList::ConstIterator it; for ( it = fileList.begin(); it != fileList.end(); ++it ) { parse(project()->projectDirectory() + "/" + ( *it ) ); } emit updatedSourceInfo(); } void BashSupportPart::removedFilesFromProject(const TQStringList &fileList) { kdDebug(9014) << "removedFilesFromProject()" << endl; TQStringList::ConstIterator it; for ( it = fileList.begin(); it != fileList.end(); ++it ) { TQString fileName = project()->projectDirectory() + "/" + ( *it ); if( codeModel()->hasFile(fileName) ){ emit aboutToRemoveSourceInfo( fileName ); codeModel()->removeFile( codeModel()->fileByName(fileName) ); } } //emit updatedSourceInfo(); } void BashSupportPart::savedFile(const KURL &fileName) { kdDebug(9014) << "savedFile()" << endl; if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) { parse(fileName.path()); emit addedSourceInfo( fileName.path() ); } } void BashSupportPart::startApplication(const TQString &program) { kdDebug() << "starting application" << program << endl; if (KDevAppFrontend *appFrontend = extension("KDevelop/AppFrontend")) appFrontend->startAppCommand(TQString(), program, TRUE); } KDevLanguageSupport::Features BashSupportPart::features() { return Features(Variables | Functions); } void BashSupportPart::parse(const TQString &fileName) { TQFileInfo fi(fileName); m_vars.clear(); if (fi.extension() == "sh") { if( codeModel()->hasFile(fileName) ){ emit aboutToRemoveSourceInfo( fileName ); codeModel()->removeFile( codeModel()->fileByName(fileName) ); } FileDom m_file = codeModel()->create(); m_file->setName( fileName ); m_vars.clear(); TQFile f(TQFile::encodeName(fileName)); if (!f.open(IO_ReadOnly)) return; TQString rawline; TQString line; uint lineNo = 0; //KRegExp methodre("\\b([\\d\\w]+[\\s]*)\\([\\s]*\\)"); TQRegExp methodre("^\\s*(\\w+)\\s*\\(\\s*\\)"); TQRegExp varre( "^\\s*(\\w+)[=]" ); TQRegExp expvarre( "^export\\s*(\\w+)[=]" ); TQRegExp forvarre("\\bfor[\\s]+([\\d\\w]+)[\\s]+in[\\s]+"); TQTextStream stream(&f); while (!stream.atEnd()) { rawline = stream.readLine(); line = rawline.stripWhiteSpace().local8Bit(); kdDebug() << "Trying line: " << line << endl; if (methodre.search(line) != -1) { FunctionDom method = codeModel()->create(); method->setName(methodre.cap(1)); method->setFileName(fileName); method->setStartPosition(lineNo, 0); if( !m_file->hasFunction(method->name()) ){ kdDebug() << "Add global method " << method->name() << endl; m_file->addFunction( method ); } } else if(varre.search(line) != -1) { addAttribute(varre.cap(1), m_file, lineNo); } else if(expvarre.search(line) != -1) { addAttribute(expvarre.cap(1), m_file, lineNo); } else if(forvarre.search(line) != -1) { addAttribute(forvarre.cap(1), m_file, lineNo); } ++lineNo; } f.close(); kdDebug() << "Trying to add list..." << endl; codeModel()->addFile( m_file ); VariableList attrList = codeModel()->globalNamespace()->variableList(); for (VariableList::Iterator it = attrList.begin(); it != attrList.end(); ++it) { kdDebug() << "Adding " << (*it)->name() << endl; m_vars.append((*it)->name()); } m_cc->setVars(m_vars); codeModel()->addFile( m_file ); } } void BashSupportPart::slotActivePartChanged(KParts::Part *part) { kdDebug() << "Changeing part..." << endl; m_cc->setActiveEditorPart(part); } void BashSupportPart::addAttribute(const TQString &name, FileDom file, uint lineNo) { VariableDom var = codeModel()->create(); var->setName(name); var->setFileName(file->name()); var->setStartPosition( lineNo, 0 ); var->setType(i18n("Variable")); if( !file->hasVariable(var->name()) ){ kdDebug() << "Add global attribute " << var->name() << endl; file->addVariable(var); } } BashCodeCompletion::BashCodeCompletion() { m_argWidgetShow = false; m_completionBoxShow=false; } BashCodeCompletion::~BashCodeCompletion() { } void BashCodeCompletion::setActiveEditorPart(KParts::Part *part) { if (!part || !part->widget()) return; kdDebug() << "BashCodeCompletion::setActiveEditorPart" << endl; // We need to think about this // if(!(m_config->getCodeCompletion() || m_config->getCodeHinting())){ // return; // no help // } m_editInterface = dynamic_cast(part); if (!m_editInterface) { kdDebug() << "editor doesn't support the EditDocumentIface" << endl; return; } m_cursorInterface = dynamic_cast(part->widget()); if (!m_cursorInterface) { kdDebug() << "editor does not support the ViewCursorInterface" << endl; return; } m_codeInterface = dynamic_cast(part->widget()); if (!m_codeInterface) { // no CodeCompletionDocument available kdDebug() << "editor doesn't support the CodeCompletionDocumentIface" << endl; return; } disconnect(part->widget(), 0, this, 0 ); // to make sure that it is't connected twice connect(part->widget(), TQT_SIGNAL(cursorPositionChanged()), this, TQT_SLOT(cursorPositionChanged())); connect(part->widget(), TQT_SIGNAL(argHintHidden()), this, TQT_SLOT(argHintHidden())); connect(part->widget(), TQT_SIGNAL(completionAborted()), this, TQT_SLOT(completionBoxAbort())); connect(part->widget(), TQT_SIGNAL(completionDone()), this, TQT_SLOT(completionBoxHidden())); } void BashCodeCompletion::setVars(TQStringList lst) { m_vars = lst; } TQValueList BashCodeCompletion::getVars(const TQString &startText) { kdDebug() << "getVars for " << startText << endl; TQValueList varList; TQValueList::ConstIterator it; for (it = m_vars.begin(); it != m_vars.end(); ++it) { TQString var = "$" + (*it); kdDebug() << "Compair " << var << endl; if( var.startsWith( startText )) { KTextEditor::CompletionEntry e; e.text = var; //e.postfix =""; //e.prefix =""; kdDebug() << "getVar: " << var << endl; varList.append(e); } } return varList; } void BashCodeCompletion::cursorPositionChanged() { uint line, col; m_cursorInterface->cursorPositionReal(&line, &col); kdDebug() << "BashCodeCompletion::cursorPositionChanged:" << line << ":" << col << endl; TQString lineStr = m_editInterface->textLine(line); if(lineStr.isNull() || lineStr.isEmpty()){ kdDebug() << "No Text..." << endl; return; // nothing to do } // if(m_config->getCodeCompletion()) // { TQString restLine = lineStr.mid(col); TQString prevText = lineStr.mid(0,col); if(restLine.left(1) != " " && restLine.left(1) != "\t" && !restLine.isNull()) { kdDebug() << "no codecompletion because no empty character after cursor:" << restLine << ":" << endl; return; } TQRegExp prevReg("[$][\\d\\w]*\\b$"); int pos = prevReg.search( prevText ); if (pos > -1 ) { // We are in completion mode TQString startMatch = prevReg.cap(0); kdDebug() << "Matching: " << startMatch << endl; m_completionBoxShow=true; m_codeInterface->showCompletionBox(getVars(startMatch),2); } else { kdDebug() << "no vars in: " << prevText << endl; return; } // } } void BashCodeCompletion::completionBoxHidden() { kdDebug() << "Complete..." << endl; m_completionBoxShow=false; /* uint line, col, start; m_cursorInterface->cursorPositionReal(&line, &col); TQString lineStr = m_editInterface->textLine(line); start = lineStr.findRev(TQRegExp("[$][\\d\\w]*\\b$")); m_editInterface->removeText ( start, col, line, col ); */ } void BashCodeCompletion::completionBoxAbort() { kdDebug() << "aborted..." << endl; m_completionBoxShow=false; } KMimeType::List BashSupportPart::mimeTypes( ) { KMimeType::List list; KMimeType::Ptr mime = KMimeType::mimeType( "application/x-shellscript" ); if( mime ) list << mime; return list; } #include "bashsupport_part.moc"