/*************************************************************************** * Copyright (C) 2002 Roberto Raggi * * roberto@kdevelop.org * * Copyright (C) 2002 by Bernd Gehrmann * * bernd@kdevelop.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdevcore.h" #include "kdevpartcontroller.h" #include "abbrevconfigwidget.h" #include "kdeveditorutil.h" static const KDevPluginInfo data("kdevabbrev"); class AbbrevFactory : public KDevGenericFactory { public: AbbrevFactory() : KDevGenericFactory( data ) { } virtual KInstance *createInstance() { KInstance *instance = KDevGenericFactory::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("Expand current word

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("Expand abbreviation

Enable and configure abbreviations in KDevelop Settings, Abbreviations 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::null, false, true); TQString localSourcesFile = locateLocal("sources", "sources", AbbrevFactory::instance()); TQStringList sourceFiles; if (TQFileInfo(localSourcesFile).exists()) sourceFiles << localSourcesFile; else sourceFiles = dirs->findAllResources("sources", TQString::null, false, true); kdDebug(9028) << "=========> sourceFiles: " << sourceFiles.join(" ") << endl; this->m_completionFile = TQString::null; 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 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( 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 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 AbbrevPart::findAllWords(const TQString &text, const TQString &prefix) { TQValueList entries; KParts::ReadWritePart *part = dynamic_cast(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 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 m = m_templates[suffix]; for (TQMap::const_iterator it = m.begin(); it != m.end() ; ++it) { KTextEditor::CompletionEntry e; e.text = it.data()->description + " "; e.userdata = it.key(); entries << e; } return entries; } void AbbrevPart::slotExpandAbbrev() { KParts::ReadWritePart *part = dynamic_cast(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(part); if (!editiface) { kdDebug(9028) << "no editiface" << endl; return; } KTextEditor::ViewCursorInterface *cursoriface = dynamic_cast(view); if (!cursoriface) { kdDebug(9028) << "no viewcursoriface" << endl; return; } TQString word = currentWord(); kdDebug(9028) << "Expanding word " << word << " with suffix " << suffix << "." << endl; TQMap m = m_templates[suffix]; for (TQMap::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 = QMAX( QMIN( (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( iinsertText( 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( 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( part ); viewCursorIface = dynamic_cast( part->widget() ); completionIface = dynamic_cast( 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(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( " " ); 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 >::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 m = it.data(); for (TQMap::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"