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.
443 lines
12 KiB
443 lines
12 KiB
/*
|
|
* Copyright (C) 2003 Ian Reinhart Geiser <geiseri@kde.org>
|
|
*/
|
|
#include "bashsupport_part.h"
|
|
|
|
#include <tqwhatsthis.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqtimer.h>
|
|
#include <tdeapplication.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <kiconloader.h>
|
|
#include <tdelocale.h>
|
|
#include <kdevgenericfactory.h>
|
|
#include <tdeprocess.h>
|
|
#include <kdebug.h>
|
|
#include <tdeaction.h>
|
|
#include <tdeparts/part.h>
|
|
#include <kdialogbase.h>
|
|
|
|
|
|
#include <kdevcore.h>
|
|
#include <kdevmainwindow.h>
|
|
#include <kdevlanguagesupport.h>
|
|
#include <kdevpartcontroller.h>
|
|
#include <kdevproject.h>
|
|
#include <kdevappfrontend.h>
|
|
#include <kdevplugininfo.h>
|
|
#include <domutil.h>
|
|
#include <codemodel.h>
|
|
|
|
typedef KDevGenericFactory<BashSupportPart> BashSupportFactory;
|
|
static const KDevPluginInfo pluginData("kdevbashsupport");
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevbashsupport, BashSupportFactory( pluginData ) )
|
|
|
|
BashSupportPart::BashSupportPart(TQObject *parent, const char *name, const TQStringList& )
|
|
: KDevLanguageSupport (&pluginData, parent, name ? name : "BashSupportPart" )
|
|
{
|
|
setInstance(BashSupportFactory::instance());
|
|
setXMLFile("kdevbashsupport.rc");
|
|
|
|
TDEAction *action;
|
|
action = new TDEAction( i18n("&Run"), "application-x-executable",Key_F9,this, TQ_SLOT(slotRun()),actionCollection(), "build_execute" );
|
|
action->setToolTip(i18n("Run"));
|
|
action->setWhatsThis(i18n("<b>Run</b><p>Starts an application."));
|
|
|
|
kdDebug() << "Creating BashSupportPart" << endl;
|
|
|
|
connect( core(), TQ_SIGNAL(projectConfigWidget(KDialogBase*)),
|
|
this, TQ_SLOT(projectConfigWidget(KDialogBase*)) );
|
|
connect( core(), TQ_SIGNAL(projectOpened()), this, TQ_SLOT(projectOpened()) );
|
|
connect( core(), TQ_SIGNAL(projectClosed()), this, TQ_SLOT(projectClosed()) );
|
|
connect( partController(), TQ_SIGNAL(savedFile(const KURL&)), this, TQ_SLOT(savedFile(const KURL&)) );
|
|
connect(partController(), TQ_SIGNAL(activePartChanged(KParts::Part*)),
|
|
this, TQ_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, TQ_SIGNAL(okClicked()), w, TQ_SLOT(accept()) );
|
|
}
|
|
|
|
void BashSupportPart::projectOpened()
|
|
{
|
|
kdDebug(9014) << "projectOpened()" << endl;
|
|
|
|
connect( project(), TQ_SIGNAL(addedFilesToProject(const TQStringList &)),
|
|
this, TQ_SLOT(addedFilesToProject(const TQStringList &)) );
|
|
connect( project(), TQ_SIGNAL(removedFilesFromProject(const TQStringList &)),
|
|
this, TQ_SLOT(removedFilesFromProject(const TQStringList &)) );
|
|
|
|
// We want to parse only after all components have been
|
|
// properly initialized
|
|
TQTimer::singleShot(0, this, TQ_SLOT(parse()));
|
|
}
|
|
|
|
|
|
void BashSupportPart::projectClosed()
|
|
{
|
|
|
|
}
|
|
|
|
void BashSupportPart::slotRun ()
|
|
{
|
|
TQString file;
|
|
KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(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<KDevAppFrontend>("TDevelop/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<FileModel>();
|
|
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<FunctionModel>();
|
|
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<VariableModel>();
|
|
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<KTextEditor::EditInterface*>(part);
|
|
if (!m_editInterface)
|
|
{
|
|
kdDebug() << "editor doesn't support the EditDocumentIface" << endl;
|
|
return;
|
|
}
|
|
|
|
m_cursorInterface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
|
|
if (!m_cursorInterface)
|
|
{
|
|
kdDebug() << "editor does not support the ViewCursorInterface" << endl;
|
|
return;
|
|
}
|
|
|
|
m_codeInterface = dynamic_cast<KTextEditor::CodeCompletionInterface*>(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(), TQ_SIGNAL(cursorPositionChanged()),
|
|
this, TQ_SLOT(cursorPositionChanged()));
|
|
connect(part->widget(), TQ_SIGNAL(argHintHidden()), this, TQ_SLOT(argHintHidden()));
|
|
connect(part->widget(), TQ_SIGNAL(completionAborted()), this, TQ_SLOT(completionBoxAbort()));
|
|
connect(part->widget(), TQ_SIGNAL(completionDone()), this, TQ_SLOT(completionBoxHidden()));
|
|
|
|
}
|
|
|
|
void BashCodeCompletion::setVars(TQStringList lst)
|
|
{
|
|
m_vars = lst;
|
|
}
|
|
|
|
TQValueList<KTextEditor::CompletionEntry> BashCodeCompletion::getVars(const TQString &startText)
|
|
{
|
|
kdDebug() << "getVars for " << startText << endl;
|
|
TQValueList<KTextEditor::CompletionEntry> varList;
|
|
TQValueList<TQString>::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"
|