1
/***************************************************************************
2
* Copyright (C) 2002 Roberto Raggi *
3
* roberto@kdevelop.org *
4
* Copyright (C) 2002 by Bernd Gehrmann *
6
* Copyright (C) 2003 by Alexander Dymo *
7
* cloudtemple@mksat.net *
9
* This program is free software; you can redistribute it and/or modify *
10
* it under the terms of the GNU General Public License as published by *
11
* the Free Software Foundation; either version 2 of the License, or *
12
* (at your option) any later version. *
14
***************************************************************************/
16
#include "abbrevpart.h"
19
#include <qfileinfo.h>
23
#include <kdialogbase.h>
25
#include <kparts/part.h>
26
#include <kstandarddirs.h>
27
#include <kdevgenericfactory.h>
30
#include <kio/netaccess.h>
31
#include <kiconloader.h>
32
#include <kdevplugininfo.h>
34
#include <ktexteditor/document.h>
35
#include <ktexteditor/editinterface.h>
36
#include <ktexteditor/viewcursorinterface.h>
37
#include <ktexteditor/codecompletioninterface.h>
40
#include "kdevpartcontroller.h"
41
#include "abbrevconfigwidget.h"
42
#include "kdeveditorutil.h"
44
static const KDevPluginInfo data("kdevabbrev");
46
class AbbrevFactory : public KDevGenericFactory<AbbrevPart>
50
: KDevGenericFactory<AbbrevPart>( data )
53
virtual KInstance *createInstance()
55
KInstance *instance = KDevGenericFactory<AbbrevPart>::createInstance();
56
KStandardDirs *dirs = instance->dirs();
57
dirs->addResourceType( "codetemplates",
58
KStandardDirs::kde_default( "data" ) + "kdevabbrev/templates/" );
59
dirs->addResourceType( "sources",
60
KStandardDirs::kde_default( "data" ) + "kdevabbrev/sources" );
66
K_EXPORT_COMPONENT_FACTORY( libkdevabbrev, AbbrevFactory )
68
AbbrevPart::AbbrevPart(QObject *parent, const char *name, const QStringList &)
69
: KDevPlugin(&data, parent, name ? name : "AbbrevPart")
71
setInstance(AbbrevFactory::instance());
72
setXMLFile("kdevabbrev.rc");
74
connect(partController(), SIGNAL(activePartChanged(KParts::Part*)),
75
this, SLOT(slotActivePartChanged(KParts::Part*)) );
77
connect(core(), SIGNAL(configWidget(KDialogBase*)), this, SLOT(configWidget(KDialogBase*)));
80
action = new KAction( i18n("Expand Text"), CTRL + Key_J,
81
this, SLOT(slotExpandText()),
82
actionCollection(), "edit_expandtext" );
83
action->setToolTip( i18n("Expand current word") );
84
action->setWhatsThis( i18n("<b>Expand current word</b><p>Current word can be completed using the list of similar words in source files.") );
86
action = new KAction( i18n("Expand Abbreviation"), CTRL + Key_L,
87
this, SLOT(slotExpandAbbrev()),
88
actionCollection(), "edit_expandabbrev" );
89
action->setToolTip( i18n("Expand abbreviation") );
90
action->setWhatsThis( i18n("<b>Expand abbreviation</b><p>Enable and configure abbreviations in <b>KDevelop Settings</b>, <b>Abbreviations</b> tab.") );
94
m_inCompletion = false;
102
m_sequenceLength = 0;
104
KConfig* config = AbbrevFactory::instance()->config();
105
KConfigGroupSaver group( config, "General" );
106
m_autoWordCompletionEnabled = config->readBoolEntry( "AutoWordCompletion", false );
110
slotActivePartChanged( partController()->activePart() );
114
AbbrevPart::~AbbrevPart()
119
bool AbbrevPart::autoWordCompletionEnabled() const
121
return m_autoWordCompletionEnabled;
124
void AbbrevPart::setAutoWordCompletionEnabled( bool enabled )
126
if( enabled == m_autoWordCompletionEnabled )
129
KConfig* config = AbbrevFactory::instance()->config();
130
KConfigGroupSaver group( config, "General" );
132
m_autoWordCompletionEnabled = enabled;
133
config->writeEntry( "AutoWordCompletion", m_autoWordCompletionEnabled );
136
if( !docIface || !docIface->widget() )
139
disconnect( docIface, 0, this, 0 );
140
disconnect( docIface->widget(), 0, this, 0 );
142
if( m_autoWordCompletionEnabled ){
143
connect( docIface->widget(), SIGNAL(completionAborted()),
144
this, SLOT(slotCompletionAborted()) );
145
connect( docIface->widget(), SIGNAL(completionDone()),
146
this, SLOT(slotCompletionDone()) );
147
connect( docIface->widget(), SIGNAL(aboutToShowCompletionBox()),
148
this, SLOT(slotAboutToShowCompletionBox()) );
150
connect( docIface, SIGNAL(textChanged()), this, SLOT(slotTextChanged()) );
153
void AbbrevPart::load()
155
KStandardDirs *dirs = AbbrevFactory::instance()->dirs();
156
QString localTemplatesFile = locateLocal("codetemplates", "templates", AbbrevFactory::instance());
158
if (QFileInfo(localTemplatesFile).exists())
159
files << localTemplatesFile;
161
files = dirs->findAllResources("codetemplates", QString::null, false, true);
163
QString localSourcesFile = locateLocal("sources", "sources", AbbrevFactory::instance());
164
QStringList sourceFiles;
165
if (QFileInfo(localSourcesFile).exists())
166
sourceFiles << localSourcesFile;
168
sourceFiles = dirs->findAllResources("sources", QString::null, false, true);
169
kdDebug(9028) << "=========> sourceFiles: " << sourceFiles.join(" ") << endl;
171
this->m_completionFile = QString::null;
172
for( QStringList::Iterator it=sourceFiles.begin(); it!=sourceFiles.end(); ++it ) {
174
kdDebug(9028) << "===> load file: " << fn << endl;
176
if ( f.open(IO_ReadOnly) ) {
177
QTextStream stream( &f );
178
m_completionFile += ( stream.read() + QString("\n") );
183
QStringList::ConstIterator it;
184
for (it = files.begin(); it != files.end(); ++it) {
186
kdDebug(9028) << "fn = " << fn << endl;
188
if ( f.open(IO_ReadOnly) ) {
190
doc.setContent( &f );
191
QDomElement root = doc.firstChild().toElement();
192
QDomElement e = root.firstChild().toElement();
193
while ( !e.isNull() ){
194
addTemplate( e.attribute("name"),
195
e.attribute("description"),
196
e.attribute("suffixes"),
197
e.attribute("code") );
198
e = e.nextSibling().toElement();
206
void AbbrevPart::save()
208
QString fn = AbbrevFactory::instance()->dirs()->saveLocation("codetemplates", "", true);
209
kdDebug(9028) << "fn = " << fn << endl;
211
QDomDocument doc( "Templates" );
212
QDomElement root = doc.createElement( "Templates" );
213
doc.appendChild( root );
215
QPtrList<CodeTemplate> templates = m_templates.allTemplates();
217
for (templ = templates.first(); templ; templ = templates.next())
219
QDomElement e = doc.createElement( "Template" );
220
e.setAttribute( "name", templ->name );
221
e.setAttribute( "description", templ->description );
222
e.setAttribute( "suffixes", templ->suffixes );
223
e.setAttribute( "code", templ->code );
224
root.appendChild( e );
227
QFile f( fn + "templates" );
228
if( f.open(IO_WriteOnly) ){
229
QTextStream stream( &f );
230
stream << doc.toString();
236
QString AbbrevPart::currentWord() const
238
return KDevEditorUtil::currentWord( dynamic_cast<KTextEditor::Document*>( partController()->activePart() ) );
242
void AbbrevPart::configWidget(KDialogBase *dlg)
244
QVBox *vbox = dlg->addVBoxPage(i18n("Abbreviations"), i18n("Abbreviations"), BarIcon( info()->icon(), KIcon::SizeMedium) );
245
AbbrevConfigWidget *w = new AbbrevConfigWidget(this, vbox, "abbrev config widget");
246
connect(dlg, SIGNAL(okClicked()), w, SLOT(accept()));
250
void AbbrevPart::slotExpandText()
252
if( !editIface || !completionIface || !viewCursorIface )
255
QString word = currentWord();
259
QValueList<KTextEditor::CompletionEntry> entries = findAllWords(editIface->text(), word);
260
if (entries.count() == 0) {
261
; // some statusbar message?
262
// } else if (entries.count() == 1) {
264
// viewCursorIface->cursorPositionReal(&line, &col);
265
// QString txt = entries[0].text.mid(word.length());
266
// editIface->insertText( line, col, txt );
267
// viewCursorIface->setCursorPositionReal( line, col + txt.length() );
269
m_inCompletion = true;
270
completionIface->showCompletionBox(entries, word.length());
275
QValueList<KTextEditor::CompletionEntry> AbbrevPart::findAllWords(const QString &text, const QString &prefix)
277
QValueList<KTextEditor::CompletionEntry> entries;
279
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
280
QWidget *view = partController()->activeWidget();
281
if (!part || !view) {
282
kdDebug(9028) << "no rw part" << endl;
286
QString suffix = part->url().url();
287
int pos = suffix.findRev('.');
289
suffix.remove(0, pos+1);
290
kdDebug(9028) << "AbbrevPart::findAllWords with suffix " << suffix << endl;
292
QMap<QString, bool> map;
293
QRegExp rx( QString("\\b") + prefix + "[a-zA-Z0-9_]+\\b" );
298
while ( (pos = rx.search(text, idx)) != -1 ) {
299
len = rx.matchedLength();
300
QString word = text.mid(pos, len);
301
if (map.find(word) == map.end()) {
302
KTextEditor::CompletionEntry e;
313
while ( (pos = rx.search(m_completionFile, idx)) != -1 ) {
314
len = rx.matchedLength();
315
QString word = m_completionFile.mid(pos, len);
316
if (map.find(word) == map.end()) {
317
KTextEditor::CompletionEntry e;
326
QMap<QString, CodeTemplate*> m = m_templates[suffix];
327
for (QMap<QString, CodeTemplate*>::const_iterator it = m.begin(); it != m.end() ; ++it) {
328
KTextEditor::CompletionEntry e;
329
e.text = it.data()->description + " <abbrev>";
330
e.userdata = it.key();
338
void AbbrevPart::slotExpandAbbrev()
340
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
341
QWidget *view = partController()->activeWidget();
342
if (!part || !view) {
343
kdDebug(9028) << "no rw part" << endl;
347
QString suffix = part->url().url();
348
int pos = suffix.findRev('.');
350
suffix.remove(0, pos+1);
352
KTextEditor::EditInterface *editiface
353
= dynamic_cast<KTextEditor::EditInterface*>(part);
355
kdDebug(9028) << "no editiface" << endl;
358
KTextEditor::ViewCursorInterface *cursoriface
359
= dynamic_cast<KTextEditor::ViewCursorInterface*>(view);
361
kdDebug(9028) << "no viewcursoriface" << endl;
365
QString word = currentWord();
366
kdDebug(9028) << "Expanding word " << word << " with suffix " << suffix << "." << endl;
368
QMap<QString, CodeTemplate*> m = m_templates[suffix];
369
for (QMap<QString, CodeTemplate*>::const_iterator it = m.begin(); it != m.end() ; ++it) {
370
if (it.key() != word)
374
cursoriface->cursorPositionReal(&line, &col);
376
QString linestr = editIface->textLine(line);
377
int startPos = QMAX( QMIN( (int)col, (int)linestr.length()-1 ), 0 );
378
int endPos = startPos;
380
while (startPos >= 0 && ( linestr[startPos].isLetterOrNumber() || linestr[startPos] == '_' || linestr[startPos] == '~') )
382
while (endPos < (int)linestr.length() && ( linestr[endPos].isLetterOrNumber() || linestr[endPos] == '_' ) )
385
editiface->removeText( line, startPos+1, line, endPos );
386
insertChars(it.data()->code );
391
void AbbrevPart::insertChars( const QString &chars )
393
unsigned line=0, col=0;
394
viewCursorIface->cursorPositionReal( &line, &col );
396
unsigned int currentLine=line, currentCol=col;
399
QString s = editIface->textLine( currentLine );
401
while( i<s.length() && s[ i ].isSpace() ){
406
bool foundPipe = false;
408
QTextStream stream( &str, IO_WriteOnly );
409
QStringList lines = QStringList::split( "\n", chars );
410
QStringList::Iterator it = lines.begin();
412
while( it != lines.end() ){
413
QString lineText = *it;
414
if( it != lines.begin() ){
417
currentCol += spaces.length();
420
int idx = lineText.find( '|' );
422
stream << lineText.left( idx ) << lineText.mid( idx+1 );
425
currentCol += lineText.left( idx ).length();
426
kdDebug(9007) << "found pipe at " << currentLine << ", " << currentCol << endl;
434
if( it != lines.end() ){
442
editIface->insertText( line, col, str );
443
kdDebug(9007) << "go to " << currentLine << ", " << currentCol << endl;
444
viewCursorIface->setCursorPositionReal( currentLine, currentCol );
447
void AbbrevPart::addTemplate( const QString& templ,
448
const QString& descr,
449
const QString& suffixes,
452
m_templates.insert(templ, descr, code, suffixes);
456
void AbbrevPart::removeTemplate( const QString &suffixes, const QString &name )
458
m_templates.remove( suffixes, name );
462
void AbbrevPart::clearTemplates()
467
CodeTemplateList AbbrevPart::templates() const
472
void AbbrevPart::slotActivePartChanged( KParts::Part* part )
474
kdDebug(9028) << "AbbrevPart::slotActivePartChanged()" << endl;
475
KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part );
477
if( !doc || !part->widget() || doc == docIface )
479
actionCollection()->action( "edit_expandtext" )->setEnabled( false );
480
actionCollection()->action( "edit_expandabbrev" )->setEnabled( false );
493
editIface = dynamic_cast<KTextEditor::EditInterface*>( part );
494
viewCursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
495
completionIface = dynamic_cast<KTextEditor::CodeCompletionInterface*>( part->widget() );
499
if( !editIface || !viewCursorIface || !completionIface )
502
disconnect( part->widget(), 0, this, 0 );
503
disconnect( doc, 0, this, 0 );
505
connect( part->widget(), SIGNAL(filterInsertString(KTextEditor::CompletionEntry*, QString*)),
506
this, SLOT(slotFilterInsertString(KTextEditor::CompletionEntry*, QString*)) );
508
if( autoWordCompletionEnabled() ){
509
connect( part->widget(), SIGNAL(completionAborted()), this, SLOT(slotCompletionAborted()) );
510
connect( part->widget(), SIGNAL(completionDone()), this, SLOT(slotCompletionDone()) );
511
connect( part->widget(), SIGNAL(aboutToShowCompletionBox()), this, SLOT(slotAboutToShowCompletionBox()) );
512
connect( doc, SIGNAL(textChanged()), this, SLOT(slotTextChanged()) );
517
m_sequenceLength = 0;
518
kdDebug(9028) << "AbbrevPart::slotActivePartChanged() -- OK" << endl;
521
void AbbrevPart::slotTextChanged()
526
unsigned int line, col;
527
viewCursorIface->cursorPositionReal( &line, &col );
529
if( m_prevLine != int(line) || m_prevColumn+1 != int(col) || col == 0 ){
532
m_sequenceLength = 1;
536
QString textLine = editIface->textLine( line );
537
QChar ch = textLine[ col-1 ];
538
QChar currentChar = textLine[ col ];
540
if( currentChar.isLetterOrNumber() || currentChar == QChar('_') || !(ch.isLetterOrNumber() || ch == QChar('_')) ){
546
if( m_sequenceLength >= 3 )
554
void AbbrevPart::slotFilterInsertString( KTextEditor::CompletionEntry* entry, QString* text )
556
kdDebug(9028) << "AbbrevPart::slotFilterInsertString()" << endl;
557
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
558
QWidget *view = partController()->activeWidget();
559
if (!part || !view) {
560
kdDebug(9028) << "no rw part" << endl;
564
QString suffix = part->url().url();
565
int pos = suffix.findRev('.');
567
suffix.remove(0, pos+1);
568
kdDebug(9028) << "AbbrevPart::slotFilterInsertString with suffix " << suffix << endl;
570
if( !entry || !text || !viewCursorIface || !editIface )
573
QString expand( " <abbrev>" );
574
if( !entry->userdata.isNull() && entry->text.endsWith(expand) ){
575
QString macro = entry->text.left( entry->text.length() - expand.length() );
578
viewCursorIface->cursorPositionReal( &line, &col );
579
editIface->removeText( line, col-currentWord().length(), line, col );
580
insertChars( m_templates[suffix][entry->userdata]->code );
584
void AbbrevPart::updateActions()
586
actionCollection()->action( "edit_expandtext" )->setEnabled( docIface != 0 );
587
actionCollection()->action( "edit_expandabbrev" )->setEnabled( docIface != 0 );
590
void AbbrevPart::slotCompletionAborted()
592
kdDebug(9028) << "AbbrevPart::slotCompletionAborted()" << endl;
593
m_inCompletion = false;
596
void AbbrevPart::slotCompletionDone()
598
kdDebug(9028) << "AbbrevPart::slotCompletionDone()" << endl;
599
m_inCompletion = false;
602
void AbbrevPart::slotAboutToShowCompletionBox()
604
kdDebug(9028) << "AbbrevPart::slotAboutToShowCompletionBox()" << endl;
605
m_inCompletion = true;
608
CodeTemplateList::CodeTemplateList( )
610
allCodeTemplates.setAutoDelete(true);
613
CodeTemplateList::~ CodeTemplateList( )
617
QMap< QString, CodeTemplate * > CodeTemplateList::operator [ ]( QString suffix )
619
kdDebug(9028) << "CodeTemplateList::operator []" << endl;
620
QMap< QString, CodeTemplate * > selectedTemplates;
621
for (QMap<QString, QMap<QString, CodeTemplate* > >::const_iterator it = templates.begin(); it != templates.end(); ++it)
623
kdDebug(9028) << "CodeTemplateList::operator [] - suffixes " << it.key() << endl;
624
if (QStringList::split(",", it.key()).contains(suffix))
626
kdDebug(9028) << "CodeTemplateList::operator [] - suffixes " << it.key() << " contains " << suffix << endl;
628
QMap<QString, CodeTemplate* > m = it.data();
629
for (QMap<QString, CodeTemplate* >::const_iterator itt = m.begin(); itt != m.end(); ++itt)
631
kdDebug(9028) << "x" << endl;
632
selectedTemplates[itt.key()] = itt.data();
636
return selectedTemplates;
639
void CodeTemplateList::insert( QString name, QString description, QString code, QString suffixes )
641
QString origSuffixes = suffixes;
642
// QStringList suffixList;
643
int pos = suffixes.find('(');
646
suffixes.remove(0, pos+1);
647
pos = suffixes.find(')');
650
suffixes.remove(pos, suffixes.length()-pos);
651
// suffixList = QStringList::split(",", suffixes);
654
if (templates.contains(suffixes) && templates[suffixes].contains(name))
656
kdDebug(9028) << "found template for suffixes " << suffixes << " and name " << name << endl;
657
t = templates[suffixes][name];
661
kdDebug(9028) << "creating template for suffixes " << suffixes << " and name " << name << endl;
662
t = new CodeTemplate();
663
allCodeTemplates.append(t);
664
templates[suffixes][name] = t;
667
t->description = description;
669
t->suffixes = origSuffixes;
670
if (!m_suffixes.contains(origSuffixes))
671
m_suffixes.append(origSuffixes);
674
QPtrList< CodeTemplate > CodeTemplateList::allTemplates( ) const
676
return allCodeTemplates;
679
void CodeTemplateList::remove( const QString & suffixes, const QString & name )
681
allCodeTemplates.remove(templates[suffixes][name]);
682
templates[suffixes].remove(name);
685
void CodeTemplateList::clear( )
688
allCodeTemplates.clear();
691
QStringList CodeTemplateList::suffixes( )
696
#include "abbrevpart.moc"