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.
tdevelop/parts/abbrev/abbrevpart.cpp

697 lines
22 KiB

/***************************************************************************
* Copyright (C) 2002 Roberto Raggi *
* roberto@tdevelop.org *
* Copyright (C) 2002 by Bernd Gehrmann *
* bernd@tdevelop.org *
* Copyright (C) 2003 by Alexander Dymo *
* cloudtemple@mksat.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "abbrevpart.h"
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqregexp.h>
#include <tqvbox.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <klocale.h>
#include <kparts/part.h>
#include <kstandarddirs.h>
#include <kdevgenericfactory.h>
#include <kaction.h>
#include <kconfig.h>
#include <kio/netaccess.h>
#include <kiconloader.h>
#include <kdevplugininfo.h>
#include <ktexteditor/document.h>
#include <ktexteditor/editinterface.h>
#include <ktexteditor/viewcursorinterface.h>
#include <ktexteditor/codecompletioninterface.h>
#include "kdevcore.h"
#include "kdevpartcontroller.h"
#include "abbrevconfigwidget.h"
#include "kdeveditorutil.h"
static const KDevPluginInfo data("kdevabbrev");
class AbbrevFactory : public KDevGenericFactory<AbbrevPart>
{
public:
AbbrevFactory()
: KDevGenericFactory<AbbrevPart>( data )
{ }
virtual KInstance *createInstance()
{
KInstance *instance = KDevGenericFactory<AbbrevPart>::createInstance();
KStandardDirs *dirs = instance->dirs();
dirs->addResourceType( "codetemplates",
KStandardDirs::kde_default( "data" ) + "kdevabbrev/templates/" );
dirs->addResourceType( "sources",
KStandardDirs::kde_default( "data" ) + "kdevabbrev/sources" );
return instance;
}
};
K_EXPORT_COMPONENT_FACTORY( libkdevabbrev, AbbrevFactory )
AbbrevPart::AbbrevPart(TQObject *parent, const char *name, const TQStringList &)
: KDevPlugin(&data, parent, name ? name : "AbbrevPart")
{
setInstance(AbbrevFactory::instance());
setXMLFile("kdevabbrev.rc");
connect(partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)),
this, TQT_SLOT(slotActivePartChanged(KParts::Part*)) );
connect(core(), TQT_SIGNAL(configWidget(KDialogBase*)), this, TQT_SLOT(configWidget(KDialogBase*)));
KAction *action;
action = new KAction( i18n("Expand Text"), CTRL + Key_J,
this, TQT_SLOT(slotExpandText()),
actionCollection(), "edit_expandtext" );
action->setToolTip( i18n("Expand current word") );
action->setWhatsThis( i18n("<b>Expand current word</b><p>Current word can be completed using the list of similar words in source files.") );
action = new KAction( i18n("Expand Abbreviation"), CTRL + Key_L,
this, TQT_SLOT(slotExpandAbbrev()),
actionCollection(), "edit_expandabbrev" );
action->setToolTip( i18n("Expand abbreviation") );
action->setWhatsThis( i18n("<b>Expand abbreviation</b><p>Enable and configure abbreviations in <b>TDevelop Settings</b>, <b>Abbreviations</b> tab.") );
load();
m_inCompletion = false;
docIface = 0;
editIface = 0;
viewCursorIface = 0;
completionIface = 0;
m_prevLine = -1;
m_prevColumn = -1;
m_sequenceLength = 0;
KConfig* config = AbbrevFactory::instance()->config();
KConfigGroupSaver group( config, "General" );
m_autoWordCompletionEnabled = config->readBoolEntry( "AutoWordCompletion", false );
updateActions();
slotActivePartChanged( partController()->activePart() );
}
AbbrevPart::~AbbrevPart()
{
save();
}
bool AbbrevPart::autoWordCompletionEnabled() const
{
return m_autoWordCompletionEnabled;
}
void AbbrevPart::setAutoWordCompletionEnabled( bool enabled )
{
if( enabled == m_autoWordCompletionEnabled )
return;
KConfig* config = AbbrevFactory::instance()->config();
KConfigGroupSaver group( config, "General" );
m_autoWordCompletionEnabled = enabled;
config->writeEntry( "AutoWordCompletion", m_autoWordCompletionEnabled );
config->sync();
if( !docIface || !docIface->widget() )
return;
disconnect( docIface, 0, this, 0 );
disconnect( docIface->widget(), 0, this, 0 );
if( m_autoWordCompletionEnabled ){
connect( docIface->widget(), TQT_SIGNAL(completionAborted()),
this, TQT_SLOT(slotCompletionAborted()) );
connect( docIface->widget(), TQT_SIGNAL(completionDone()),
this, TQT_SLOT(slotCompletionDone()) );
connect( docIface->widget(), TQT_SIGNAL(aboutToShowCompletionBox()),
this, TQT_SLOT(slotAboutToShowCompletionBox()) );
connect( docIface, TQT_SIGNAL(textChanged()), this, TQT_SLOT(slotTextChanged()) );
}
}
void AbbrevPart::load()
{
KStandardDirs *dirs = AbbrevFactory::instance()->dirs();
TQString localTemplatesFile = locateLocal("codetemplates", "templates", AbbrevFactory::instance());
TQStringList files;
if (TQFileInfo(localTemplatesFile).exists())
files << localTemplatesFile;
else
files = dirs->findAllResources("codetemplates", TQString(), false, true);
TQString localSourcesFile = locateLocal("sources", "sources", AbbrevFactory::instance());
TQStringList sourceFiles;
if (TQFileInfo(localSourcesFile).exists())
sourceFiles << localSourcesFile;
else
sourceFiles = dirs->findAllResources("sources", TQString(), false, true);
kdDebug(9028) << "=========> sourceFiles: " << sourceFiles.join(" ") << endl;
this->m_completionFile = TQString();
for( TQStringList::Iterator it=sourceFiles.begin(); it!=sourceFiles.end(); ++it ) {
TQString fn = *it;
kdDebug(9028) << "===> load file: " << fn << endl;
TQFile f( fn );
if ( f.open(IO_ReadOnly) ) {
TQTextStream stream( &f );
m_completionFile += ( stream.read() + TQString("\n") );
f.close();
}
}
TQStringList::ConstIterator it;
for (it = files.begin(); it != files.end(); ++it) {
TQString fn = *it;
kdDebug(9028) << "fn = " << fn << endl;
TQFile f( fn );
if ( f.open(IO_ReadOnly) ) {
TQDomDocument doc;
doc.setContent( &f );
TQDomElement root = doc.firstChild().toElement();
TQDomElement e = root.firstChild().toElement();
while ( !e.isNull() ){
addTemplate( e.attribute("name"),
e.attribute("description"),
e.attribute("suffixes"),
e.attribute("code") );
e = e.nextSibling().toElement();
}
f.close();
}
}
}
void AbbrevPart::save()
{
TQString fn = AbbrevFactory::instance()->dirs()->saveLocation("codetemplates", "", true);
kdDebug(9028) << "fn = " << fn << endl;
TQDomDocument doc( "Templates" );
TQDomElement root = doc.createElement( "Templates" );
doc.appendChild( root );
TQPtrList<CodeTemplate> templates = m_templates.allTemplates();
CodeTemplate *templ;
for (templ = templates.first(); templ; templ = templates.next())
{
TQDomElement e = doc.createElement( "Template" );
e.setAttribute( "name", templ->name );
e.setAttribute( "description", templ->description );
e.setAttribute( "suffixes", templ->suffixes );
e.setAttribute( "code", templ->code );
root.appendChild( e );
}
TQFile f( fn + "templates" );
if( f.open(IO_WriteOnly) ){
TQTextStream stream( &f );
stream << doc.toString();
f.close();
}
}
TQString AbbrevPart::currentWord() const
{
return KDevEditorUtil::currentWord( dynamic_cast<KTextEditor::Document*>( partController()->activePart() ) );
}
void AbbrevPart::configWidget(KDialogBase *dlg)
{
TQVBox *vbox = dlg->addVBoxPage(i18n("Abbreviations"), i18n("Abbreviations"), BarIcon( info()->icon(), KIcon::SizeMedium) );
AbbrevConfigWidget *w = new AbbrevConfigWidget(this, vbox, "abbrev config widget");
connect(dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()));
}
void AbbrevPart::slotExpandText()
{
if( !editIface || !completionIface || !viewCursorIface )
return;
TQString word = currentWord();
if (word.isEmpty())
return;
TQValueList<KTextEditor::CompletionEntry> entries = findAllWords(editIface->text(), word);
if (entries.count() == 0) {
; // some statusbar message?
// } else if (entries.count() == 1) {
// uint line, col;
// viewCursorIface->cursorPositionReal(&line, &col);
// TQString txt = entries[0].text.mid(word.length());
// editIface->insertText( line, col, txt );
// viewCursorIface->setCursorPositionReal( line, col + txt.length() );
} else {
m_inCompletion = true;
completionIface->showCompletionBox(entries, word.length());
}
}
TQValueList<KTextEditor::CompletionEntry> AbbrevPart::findAllWords(const TQString &text, const TQString &prefix)
{
TQValueList<KTextEditor::CompletionEntry> entries;
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
TQWidget *view = partController()->activeWidget();
if (!part || !view) {
kdDebug(9028) << "no rw part" << endl;
return entries;
}
TQString suffix = part->url().url();
int pos = suffix.findRev('.');
if (pos != -1)
suffix.remove(0, pos+1);
kdDebug(9028) << "AbbrevPart::findAllWords with suffix " << suffix << endl;
TQMap<TQString, bool> map;
TQRegExp rx( TQString("\\b") + prefix + "[a-zA-Z0-9_]+\\b" );
int idx = 0;
pos = 0;
int len = 0;
while ( (pos = rx.search(text, idx)) != -1 ) {
len = rx.matchedLength();
TQString word = text.mid(pos, len);
if (map.find(word) == map.end()) {
KTextEditor::CompletionEntry e;
e.text = word;
entries << e;
map[ word ] = TRUE;
}
idx = pos + len + 1;
}
idx = 0;
pos = 0;
len = 0;
while ( (pos = rx.search(m_completionFile, idx)) != -1 ) {
len = rx.matchedLength();
TQString word = m_completionFile.mid(pos, len);
if (map.find(word) == map.end()) {
KTextEditor::CompletionEntry e;
e.text = word;
entries << e;
map[ word ] = TRUE;
}
idx = pos + len + 1;
}
TQMap<TQString, CodeTemplate*> m = m_templates[suffix];
for (TQMap<TQString, CodeTemplate*>::const_iterator it = m.begin(); it != m.end() ; ++it) {
KTextEditor::CompletionEntry e;
e.text = it.data()->description + " <abbrev>";
e.userdata = it.key();
entries << e;
}
return entries;
}
void AbbrevPart::slotExpandAbbrev()
{
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
TQWidget *view = partController()->activeWidget();
if (!part || !view) {
kdDebug(9028) << "no rw part" << endl;
return;
}
TQString suffix = part->url().url();
int pos = suffix.findRev('.');
if (pos != -1)
suffix.remove(0, pos+1);
KTextEditor::EditInterface *editiface
= dynamic_cast<KTextEditor::EditInterface*>(part);
if (!editiface) {
kdDebug(9028) << "no editiface" << endl;
return;
}
KTextEditor::ViewCursorInterface *cursoriface
= dynamic_cast<KTextEditor::ViewCursorInterface*>(view);
if (!cursoriface) {
kdDebug(9028) << "no viewcursoriface" << endl;
return;
}
TQString word = currentWord();
kdDebug(9028) << "Expanding word " << word << " with suffix " << suffix << "." << endl;
TQMap<TQString, CodeTemplate*> m = m_templates[suffix];
for (TQMap<TQString, CodeTemplate*>::const_iterator it = m.begin(); it != m.end() ; ++it) {
if (it.key() != word)
continue;
uint line, col;
cursoriface->cursorPositionReal(&line, &col);
TQString linestr = editIface->textLine(line);
int startPos = TQMAX( TQMIN( (int)col, (int)linestr.length()-1 ), 0 );
int endPos = startPos;
startPos--;
while (startPos >= 0 && ( linestr[startPos].isLetterOrNumber() || linestr[startPos] == '_' || linestr[startPos] == '~') )
startPos--;
while (endPos < (int)linestr.length() && ( linestr[endPos].isLetterOrNumber() || linestr[endPos] == '_' ) )
endPos++;
editiface->removeText( line, startPos+1, line, endPos );
insertChars(it.data()->code );
}
}
void AbbrevPart::insertChars( const TQString &chars )
{
unsigned line=0, col=0;
viewCursorIface->cursorPositionReal( &line, &col );
unsigned int currentLine=line, currentCol=col;
TQString spaces;
TQString s = editIface->textLine( currentLine );
uint i=0;
while( i<s.length() && s[ i ].isSpace() ){
spaces += s[ i ];
++i;
}
bool foundPipe = false;
TQString str;
TQTextStream stream( &str, IO_WriteOnly );
TQStringList lines = TQStringList::split( "\n", chars );
TQStringList::Iterator it = lines.begin();
line = currentLine;
while( it != lines.end() ){
TQString lineText = *it;
if( it != lines.begin() ){
stream << spaces;
if( !foundPipe )
currentCol += spaces.length();
}
int idx = lineText.find( '|' );
if( idx != -1 ){
stream << lineText.left( idx ) << lineText.mid( idx+1 );
if( !foundPipe ){
foundPipe = true;
currentCol += lineText.left( idx ).length();
kdDebug(9007) << "found pipe at " << currentLine << ", " << currentCol << endl;
}
} else {
stream << lineText;
}
++it;
if( it != lines.end() ){
stream << "\n";
if( !foundPipe ){
++currentLine;
currentCol = 0;
}
}
}
editIface->insertText( line, col, str );
kdDebug(9007) << "go to " << currentLine << ", " << currentCol << endl;
viewCursorIface->setCursorPositionReal( currentLine, currentCol );
}
void AbbrevPart::addTemplate( const TQString& templ,
const TQString& descr,
const TQString& suffixes,
const TQString& code)
{
m_templates.insert(templ, descr, code, suffixes);
}
void AbbrevPart::removeTemplate( const TQString &suffixes, const TQString &name )
{
m_templates.remove( suffixes, name );
}
void AbbrevPart::clearTemplates()
{
m_templates.clear();
}
CodeTemplateList AbbrevPart::templates() const
{
return m_templates;
}
void AbbrevPart::slotActivePartChanged( KParts::Part* part )
{
kdDebug(9028) << "AbbrevPart::slotActivePartChanged()" << endl;
KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part );
if( !doc || !part->widget() || doc == docIface )
{
actionCollection()->action( "edit_expandtext" )->setEnabled( false );
actionCollection()->action( "edit_expandabbrev" )->setEnabled( false );
return;
}
docIface = doc;
if( !docIface ){
docIface = 0;
editIface = 0;
viewCursorIface = 0;
completionIface = 0;
}
editIface = dynamic_cast<KTextEditor::EditInterface*>( part );
viewCursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
completionIface = dynamic_cast<KTextEditor::CodeCompletionInterface*>( part->widget() );
updateActions();
if( !editIface || !viewCursorIface || !completionIface )
return;
disconnect( part->widget(), 0, this, 0 );
disconnect( doc, 0, this, 0 );
connect( part->widget(), TQT_SIGNAL(filterInsertString(KTextEditor::CompletionEntry*, TQString*)),
this, TQT_SLOT(slotFilterInsertString(KTextEditor::CompletionEntry*, TQString*)) );
if( autoWordCompletionEnabled() ){
connect( part->widget(), TQT_SIGNAL(completionAborted()), this, TQT_SLOT(slotCompletionAborted()) );
connect( part->widget(), TQT_SIGNAL(completionDone()), this, TQT_SLOT(slotCompletionDone()) );
connect( part->widget(), TQT_SIGNAL(aboutToShowCompletionBox()), this, TQT_SLOT(slotAboutToShowCompletionBox()) );
connect( doc, TQT_SIGNAL(textChanged()), this, TQT_SLOT(slotTextChanged()) );
}
m_prevLine = -1;
m_prevColumn = -1;
m_sequenceLength = 0;
kdDebug(9028) << "AbbrevPart::slotActivePartChanged() -- OK" << endl;
}
void AbbrevPart::slotTextChanged()
{
if( m_inCompletion )
return;
unsigned int line, col;
viewCursorIface->cursorPositionReal( &line, &col );
if( m_prevLine != int(line) || m_prevColumn+1 != int(col) || col == 0 ){
m_prevLine = line;
m_prevColumn = col;
m_sequenceLength = 1;
return;
}
TQString textLine = editIface->textLine( line );
TQChar ch = textLine[ col-1 ];
TQChar currentChar = textLine[ col ];
if( currentChar.isLetterOrNumber() || currentChar == TQChar('_') || !(ch.isLetterOrNumber() || ch == TQChar('_')) ){
// reset
m_prevLine = -1;
return;
}
if( m_sequenceLength >= 3 )
slotExpandText();
++m_sequenceLength;
m_prevLine = line;
m_prevColumn = col;
}
void AbbrevPart::slotFilterInsertString( KTextEditor::CompletionEntry* entry, TQString* text )
{
kdDebug(9028) << "AbbrevPart::slotFilterInsertString()" << endl;
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
TQWidget *view = partController()->activeWidget();
if (!part || !view) {
kdDebug(9028) << "no rw part" << endl;
return;
}
TQString suffix = part->url().url();
int pos = suffix.findRev('.');
if (pos != -1)
suffix.remove(0, pos+1);
kdDebug(9028) << "AbbrevPart::slotFilterInsertString with suffix " << suffix << endl;
if( !entry || !text || !viewCursorIface || !editIface )
return;
TQString expand( " <abbrev>" );
if( !entry->userdata.isNull() && entry->text.endsWith(expand) ){
TQString macro = entry->text.left( entry->text.length() - expand.length() );
*text = "";
uint line, col;
viewCursorIface->cursorPositionReal( &line, &col );
editIface->removeText( line, col-currentWord().length(), line, col );
insertChars( m_templates[suffix][entry->userdata]->code );
}
}
void AbbrevPart::updateActions()
{
actionCollection()->action( "edit_expandtext" )->setEnabled( docIface != 0 );
actionCollection()->action( "edit_expandabbrev" )->setEnabled( docIface != 0 );
}
void AbbrevPart::slotCompletionAborted()
{
kdDebug(9028) << "AbbrevPart::slotCompletionAborted()" << endl;
m_inCompletion = false;
}
void AbbrevPart::slotCompletionDone()
{
kdDebug(9028) << "AbbrevPart::slotCompletionDone()" << endl;
m_inCompletion = false;
}
void AbbrevPart::slotAboutToShowCompletionBox()
{
kdDebug(9028) << "AbbrevPart::slotAboutToShowCompletionBox()" << endl;
m_inCompletion = true;
}
CodeTemplateList::CodeTemplateList( )
{
allCodeTemplates.setAutoDelete(true);
}
CodeTemplateList::~ CodeTemplateList( )
{
}
TQMap< TQString, CodeTemplate * > CodeTemplateList::operator [ ]( TQString suffix )
{
kdDebug(9028) << "CodeTemplateList::operator []" << endl;
TQMap< TQString, CodeTemplate * > selectedTemplates;
for (TQMap<TQString, TQMap<TQString, CodeTemplate* > >::const_iterator it = templates.begin(); it != templates.end(); ++it)
{
kdDebug(9028) << "CodeTemplateList::operator [] - suffixes " << it.key() << endl;
if (TQStringList::split(",", it.key()).contains(suffix))
{
kdDebug(9028) << "CodeTemplateList::operator [] - suffixes " << it.key() << " contains " << suffix << endl;
TQMap<TQString, CodeTemplate* > m = it.data();
for (TQMap<TQString, CodeTemplate* >::const_iterator itt = m.begin(); itt != m.end(); ++itt)
{
kdDebug(9028) << "x" << endl;
selectedTemplates[itt.key()] = itt.data();
}
}
}
return selectedTemplates;
}
void CodeTemplateList::insert( TQString name, TQString description, TQString code, TQString suffixes )
{
TQString origSuffixes = suffixes;
// TQStringList suffixList;
int pos = suffixes.find('(');
if (pos == -1)
return;
suffixes.remove(0, pos+1);
pos = suffixes.find(')');
if (pos == -1)
return;
suffixes.remove(pos, suffixes.length()-pos);
// suffixList = TQStringList::split(",", suffixes);
CodeTemplate *t;
if (templates.contains(suffixes) && templates[suffixes].contains(name))
{
kdDebug(9028) << "found template for suffixes " << suffixes << " and name " << name << endl;
t = templates[suffixes][name];
}
else
{
kdDebug(9028) << "creating template for suffixes " << suffixes << " and name " << name << endl;
t = new CodeTemplate();
allCodeTemplates.append(t);
templates[suffixes][name] = t;
}
t->name = name;
t->description = description;
t->code = code;
t->suffixes = origSuffixes;
if (!m_suffixes.contains(origSuffixes))
m_suffixes.append(origSuffixes);
}
TQPtrList< CodeTemplate > CodeTemplateList::allTemplates( ) const
{
return allCodeTemplates;
}
void CodeTemplateList::remove( const TQString & suffixes, const TQString & name )
{
allCodeTemplates.remove(templates[suffixes][name]);
templates[suffixes].remove(name);
}
void CodeTemplateList::clear( )
{
templates.clear();
allCodeTemplates.clear();
}
TQStringList CodeTemplateList::suffixes( )
{
return m_suffixes;
}
#include "abbrevpart.moc"