• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katewordcompletion.cpp

Go to the documentation of this file.
00001 /*
00002     This library is free software; you can redistribute it and/or
00003     modify it under the terms of the GNU Library General Public
00004     License version 2 as published by the Free Software Foundation.
00005 
00006     This library is distributed in the hope that it will be useful,
00007     but WITHOUT ANY WARRANTY; without even the implied warranty of
00008     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00009     Library General Public License for more details.
00010 
00011     You should have received a copy of the GNU Library General Public License
00012     along with this library; see the file COPYING.LIB.  If not, write to
00013     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00014     Boston, MA 02110-1301, USA.
00015 
00016     ---
00017     file: docwordcompletion.cpp
00018 
00019     KTextEditor plugin to autocompletion with document words.
00020     Copyright Anders Lund <anders.lund@lund.tdcadsl.dk>, 2003
00021 
00022     The following completion methods are supported:
00023     * Completion with bigger matching words in
00024       either direction (backward/forward).
00025     * NOT YET Pop up a list of all bigger matching words in document
00026 
00027 */
00028 
00029 //BEGIN includes
00030 #include "katewordcompletion.h"
00031 #include "kateview.h"
00032 #include "katedocument.h"
00033 #include "kateglobal.h"
00034 #include "katesmartrange.h"
00035 
00036 #include <ktexteditor/variableinterface.h>
00037 #include <ktexteditor/smartinterface.h>
00038 #include <ktexteditor/smartrange.h>
00039 #include <ktexteditor/rangefeedback.h>
00040 
00041 #include <kconfig.h>
00042 #include <kdialog.h>
00043 #include <kgenericfactory.h>
00044 #include <klocale.h>
00045 #include <kaction.h>
00046 #include <kactioncollection.h>
00047 #include <knotification.h>
00048 #include <kparts/part.h>
00049 #include <kiconloader.h>
00050 #include <kpagedialog.h>
00051 #include <kpagewidgetmodel.h>
00052 #include <ktoggleaction.h>
00053 #include <kconfiggroup.h>
00054 #include <kcolorscheme.h>
00055 #include <kaboutdata.h>
00056 
00057 #include <QtCore/QRegExp>
00058 #include <QtCore/QString>
00059 #include <QtCore/QSet>
00060 #include <QtGui/QSpinBox>
00061 #include <QtGui/QLabel>
00062 #include <QtGui/QLayout>
00063 
00064 #include <kvbox.h>
00065 #include <QtGui/QCheckBox>
00066 
00067 #include <kdebug.h>
00068 //END
00069 
00070 //BEGIN KateWordCompletionModel
00071 KateWordCompletionModel::KateWordCompletionModel( QObject *parent )
00072   : CodeCompletionModel( parent )
00073 {
00074   setHasGroups(false);
00075 }
00076 
00077 KateWordCompletionModel::~KateWordCompletionModel()
00078 {
00079 }
00080 
00081 void KateWordCompletionModel::saveMatches( KTextEditor::View* view,
00082                         const KTextEditor::Range& range)
00083 {
00084   m_matches = allMatches( view, range, 2 );
00085   m_matches.sort();
00086 }
00087 
00088 QVariant KateWordCompletionModel::data(const QModelIndex& index, int role) const
00089 {
00090   if( role == InheritanceDepth )
00091     return 10000; //Very high value, so the word-completion group and items are shown behind any other groups/items if there is multiple
00092   
00093   if( !index.parent().isValid() ) {
00094     //It is the group header
00095     switch ( role )
00096     {
00097       case Qt::DisplayRole:
00098         return i18n("Auto Word Completion");
00099       case GroupRole:
00100         return Qt::DisplayRole;
00101     }
00102   }
00103   
00104   if( index.column() == KTextEditor::CodeCompletionModel::Name && role == Qt::DisplayRole )
00105     return m_matches.at( index.row() );
00106 
00107   if( index.column() == KTextEditor::CodeCompletionModel::Icon && role == Qt::DecorationRole ) {
00108     static QIcon icon(KIcon("insert-text").pixmap(QSize(16, 16)));
00109     return icon;
00110   }
00111   
00112   return QVariant();
00113 }
00114 
00115 QModelIndex KateWordCompletionModel::parent(const QModelIndex& index) const
00116 {
00117   if(index.internalId())
00118     return createIndex(0, 0, 0);
00119   else
00120     return QModelIndex();
00121 }
00122 
00123 QModelIndex KateWordCompletionModel::index(int row, int column, const QModelIndex& parent) const
00124 {
00125   if( !parent.isValid()) {
00126     if(row == 0)
00127       return createIndex(row, column, 0);
00128     else
00129       return QModelIndex();
00130 
00131   }else if(parent.parent().isValid())
00132     return QModelIndex();
00133 
00134   
00135   if (row < 0 || row >= m_matches.count() || column < 0 || column >= ColumnCount )
00136     return QModelIndex();
00137 
00138   return createIndex(row, column, 1);
00139 }
00140 
00141 int KateWordCompletionModel::rowCount ( const QModelIndex & parent ) const
00142 {
00143   if( !parent.isValid() && !m_matches.isEmpty() )
00144     return 1; //One root node to define the custom group
00145   else if(parent.parent().isValid())
00146     return 0; //Completion-items have no children
00147   else
00148     return m_matches.count();
00149 }
00150 
00151 void KateWordCompletionModel::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType it)
00152 {
00156   if (it==AutomaticInvocation) {
00157       KateView *v = qobject_cast<KateView*> (view);
00158 
00159       if (range.columnWidth() >= v->config()->wordCompletionMinimalWordLength())
00160         saveMatches( view, range );
00161       else
00162         m_matches.clear();
00163 
00164       // done here...
00165       return;
00166   }
00167 
00168   // normal case ;)
00169   saveMatches( view, range );
00170 }
00171 
00172 
00173 // Scan throughout the entire document for possible completions,
00174 // ignoring any dublets
00175 const QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range, int minAdditionalLength ) const
00176 {
00177   QStringList l;
00178 
00179   // we complete words on a single line, that has a length
00180   if ( range.numberOfLines() || ! range.columnWidth() )
00181     return l;
00182 
00183   int i( 0 );
00184   int pos( 0 );
00185   KTextEditor::Document *doc = view->document();
00186   QRegExp re( "\\b(" + doc->text( range ) + "\\w{" + QString::number(minAdditionalLength) + ",})" );
00187   QString s, m;
00188   QSet<QString> seen;
00189 
00190   while( i < doc->lines() )
00191   {
00192     s = doc->line( i );
00193     pos = 0;
00194     while ( pos >= 0 )
00195     {
00196       pos = re.indexIn( s, pos );
00197       if ( pos >= 0 )
00198       {
00199         // typing in the middle of a word
00200         if ( ! ( i == range.start().line() && pos == range.start().column() ) )
00201         {
00202           m = re.cap( 1 );
00203           if ( ! seen.contains( m ) ) {
00204             seen.insert( m );
00205             l << m;
00206           }
00207         }
00208         pos += re.matchedLength();
00209       }
00210     }
00211     i++;
00212   }
00213   return l;
00214 }
00215 
00216 //END KateWordCompletionModel
00217 
00218 
00219 //BEGIN KateWordCompletionView
00220 struct KateWordCompletionViewPrivate
00221 {
00222   KTextEditor::SmartRange* liRange;       // range containing last inserted text
00223   KTextEditor::Range dcRange;  // current range to be completed by directional completion
00224   KTextEditor::Cursor dcCursor;     // directional completion search cursor
00225   QRegExp re;           // hrm
00226   int directionalPos;   // be able to insert "" at the correct time
00227   bool isCompleting; // true when the directional completion is doing a completion
00228 };
00229 
00230 KateWordCompletionView::KateWordCompletionView( KTextEditor::View *view, KActionCollection* ac )
00231   : QObject( view ),
00232     m_view( view ),
00233     m_dWCompletionModel( KateGlobal::self()->wordCompletionModel() ),
00234     d( new KateWordCompletionViewPrivate )
00235 {
00236   d->isCompleting = false;
00237   d->dcRange = KTextEditor::Range::invalid();
00238   KTextEditor::SmartInterface *si =
00239      qobject_cast<KTextEditor::SmartInterface*>( m_view->document() );
00240 
00241   if( ! si )
00242     return;
00243 
00244   d->liRange = si->newSmartRange();
00245   // (dh) guard the smart range to not become a dangling pointer on document reload
00246   static_cast<KateSmartRange*>(d->liRange)->setInternal();
00247 
00248   KColorScheme colors(QPalette::Active);
00249   KTextEditor::Attribute::Ptr a = KTextEditor::Attribute::Ptr( new KTextEditor::Attribute() );
00250   a->setBackground( colors.background(KColorScheme::ActiveBackground) );
00251   a->setForeground( colors.foreground(KColorScheme::ActiveText) ); // ### this does 0
00252   d->liRange->setAttribute( a );
00253 
00254   KTextEditor::CodeCompletionInterface *cci = qobject_cast<KTextEditor::CodeCompletionInterface *>(view);
00255 
00256   KAction *action;
00257 
00258   if (cci)
00259   {
00260     cci->registerCompletionModel( m_dWCompletionModel );
00261 
00262     action = new KAction( i18n("Shell Completion"), this );
00263     ac->addAction( "doccomplete_sh", action );
00264     connect( action, SIGNAL( triggered() ), this, SLOT(shellComplete()) );
00265   }
00266 
00267 
00268   action = new KAction( i18n("Reuse Word Above"), this );
00269   ac->addAction( "doccomplete_bw", action );
00270   action->setShortcut( Qt::CTRL+Qt::Key_8 );
00271   connect( action, SIGNAL( triggered() ), this, SLOT(completeBackwards()) );
00272 
00273   action = new KAction( i18n("Reuse Word Below"), this );
00274   ac->addAction( "doccomplete_fw", action );
00275   action->setShortcut( Qt::CTRL+Qt::Key_9 );
00276   connect( action, SIGNAL( triggered() ), this, SLOT(completeForwards()) );
00277 }
00278 
00279 KateWordCompletionView::~KateWordCompletionView()
00280 {
00281   KTextEditor::CodeCompletionInterface *cci = qobject_cast<KTextEditor::CodeCompletionInterface *>(m_view);
00282 
00283   if (cci) cci->unregisterCompletionModel(m_dWCompletionModel);
00284 
00285   delete d;
00286 }
00287 
00288 void KateWordCompletionView::completeBackwards()
00289 {
00290   complete( false );
00291 }
00292 
00293 void KateWordCompletionView::completeForwards()
00294 {
00295   complete();
00296 }
00297 
00298 // Pop up the editors completion list if applicable
00299 void KateWordCompletionView::popupCompletionList()
00300 {
00301   kDebug( 13040 ) << "entered ...";
00302   KTextEditor::Range r = range();
00303 
00304   if ( r.isEmpty() )
00305     return;
00306 
00307   KTextEditor::CodeCompletionInterface *cci = qobject_cast<KTextEditor::CodeCompletionInterface *>( m_view );
00308   if(!cci || cci->isCompletionActive())
00309     return;
00310 
00311   m_dWCompletionModel->saveMatches( m_view, r );
00312 
00313   kDebug( 13040 ) << "after save matches ...";
00314 
00315   if ( ! m_dWCompletionModel->rowCount(QModelIndex()) ) return;
00316 
00317   cci->startCompletion( r, m_dWCompletionModel );
00318 }
00319 
00320 // Contributed by <brain@hdsnet.hu>
00321 void KateWordCompletionView::shellComplete()
00322 {
00323   KTextEditor::Range r = range();
00324   if (r.isEmpty())
00325     return;
00326 
00327   QStringList matches = m_dWCompletionModel->allMatches( m_view, r );
00328 
00329   if (matches.size() == 0)
00330     return;
00331 
00332   QString partial = findLongestUnique( matches, r.columnWidth() );
00333 
00334   if ( ! partial.length() )
00335     popupCompletionList();
00336 
00337   else
00338   {
00339     m_view->document()->insertText( r.end(), partial.mid( r.columnWidth() ) );
00340     KTextEditor::SmartInterface *si = qobject_cast<KTextEditor::SmartInterface*>( m_view->document() );
00341     if ( si ) {
00342       si->addHighlightToView( m_view, d->liRange, true );
00343       d->liRange->setRange( KTextEditor::Range( r.end(), partial.length() - r.columnWidth() ) );
00344       connect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(slotCursorMoved()) );
00345     }
00346   }
00347 }
00348 
00349 // Do one completion, searching in the desired direction,
00350 // if possible
00351 void KateWordCompletionView::complete( bool fw )
00352 {
00353   KTextEditor::Range r = range();
00354   if ( r.isEmpty() )
00355     return;
00356 
00357   int inc = fw ? 1 : -1;
00358   KTextEditor::Document *doc = m_view->document();
00359 
00360   if ( d->dcRange.isValid() )
00361   {
00362     //kDebug( 13040 )<<"CONTINUE "<<d->dcRange;
00363     // this is a repeted activation
00364 
00365     // if we are back to where we started, reset.
00366     if ( ( fw && d->directionalPos == -1 ) ||
00367          ( !fw && d->directionalPos == 1 ) )
00368     {
00369       if ( d->liRange->columnWidth() )
00370         doc->removeText( *d->liRange );
00371 
00372       d->liRange->setRange( KTextEditor::Range( d->liRange->start(), 0 )  );
00373       d->dcCursor = r.end();
00374       d->directionalPos = 0;
00375 
00376       return;
00377     }
00378 
00379     if ( fw )
00380       d->dcCursor.setColumn( d->dcCursor.column() + d->liRange->columnWidth() );
00381 
00382     d->directionalPos += inc;
00383   }
00384   else // new completion, reset all
00385   {
00386     //kDebug( 13040 )<<"RESET FOR NEW";
00387     d->dcRange = r;
00388     d->liRange->setRange( KTextEditor::Range( r.end(), 0 ) );
00389     d->dcCursor = r.start();
00390     d->directionalPos = inc;
00391 
00392   KTextEditor::SmartInterface *si =
00393      qobject_cast<KTextEditor::SmartInterface*>( m_view->document() );
00394   if ( si )
00395     si->addHighlightToView( m_view, d->liRange, true );
00396 
00397     connect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(slotCursorMoved()) );
00398 
00399   }
00400 
00401   d->re.setPattern( "\\b" + doc->text( d->dcRange ) + "(\\w+)" );
00402   int pos ( 0 );
00403   QString ln = doc->line( d->dcCursor.line() );
00404 
00405   while ( true )
00406   {
00407     //kDebug( 13040 )<<"SEARCHING FOR "<<d->re.pattern()<<" "<<ln<<" at "<<d->dcCursor;
00408     pos = fw ?
00409       d->re.indexIn( ln, d->dcCursor.column() ) :
00410       d->re.lastIndexIn( ln, d->dcCursor.column() );
00411 
00412     if ( pos > -1 ) // we matched a word
00413     {
00414       //kDebug( 13040 )<<"USABLE MATCH";
00415       QString m = d->re.cap( 1 );
00416       if ( m != doc->text( *d->liRange ) && (d->dcCursor.line() != d->dcRange.start().line() || pos != d->dcRange.start().column() ) )
00417       {
00418         // we got good a match! replace text and return.
00419         d->isCompleting = true;
00420         doc->replaceText( *d->liRange, m );
00421         d->liRange->setRange( KTextEditor::Range( d->dcRange.end(), m.length() ) );
00422 
00423         d->dcCursor.setColumn( pos ); // for next try
00424 
00425         d->isCompleting = false;
00426         return;
00427       }
00428 
00429       // equal to last one, continue
00430       else
00431       {
00432         //kDebug( 13040 )<<"SKIPPING, EQUAL MATCH";
00433         d->dcCursor.setColumn( pos ); // for next try
00434 
00435         if ( fw )
00436           d->dcCursor.setColumn( pos + m.length() );
00437 
00438         else
00439         {
00440           if ( pos == 0 )
00441           {
00442             if ( d->dcCursor.line() > 0 )
00443             {
00444               int l = d->dcCursor.line() + inc;
00445               ln = doc->line( l );
00446               d->dcCursor.setPosition( l, ln.length() );
00447             }
00448             else
00449             {
00450               KNotification::beep();
00451               return;
00452             }
00453           }
00454 
00455           else
00456             d->dcCursor.setColumn( d->dcCursor.column()-1 );
00457         }
00458       }
00459     }
00460 
00461     else  // no match
00462     {
00463       //kDebug( 13040 )<<"NO MATCH";
00464       if ( (! fw && d->dcCursor.line() == 0 ) || ( fw && d->dcCursor.line() >= doc->lines() ) )
00465       {
00466         KNotification::beep();
00467         return;
00468       }
00469 
00470       int l = d->dcCursor.line() + inc;
00471       ln = doc->line( l );
00472       d->dcCursor.setPosition( l, fw ? 0 : ln.length() );
00473     }
00474   } // while true
00475 }
00476 
00477 void KateWordCompletionView::slotCursorMoved()
00478 {
00479   if ( d->isCompleting) return;
00480 
00481   d->dcRange = KTextEditor::Range::invalid();
00482 
00483   disconnect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(slotCursorMoved()) );
00484 
00485   KTextEditor::SmartInterface *si =
00486      qobject_cast<KTextEditor::SmartInterface*>( m_view->document() );
00487   if ( si )
00488     si->removeHighlightFromView( m_view, d->liRange );
00489 }
00490 
00491 // Contributed by <brain@hdsnet.hu> FIXME
00492 QString KateWordCompletionView::findLongestUnique( const QStringList &matches, int lead ) const
00493 {
00494   QString partial = matches.first();
00495 
00496   QStringListIterator it( matches );
00497   QString current;
00498   while ( it.hasNext() )
00499   {
00500     current = it.next();
00501     if ( !current.startsWith( partial ) )
00502     {
00503       while( partial.length() > lead )
00504       {
00505         partial.remove( partial.length() - 1, 1 );
00506         if ( current.startsWith( partial ) )
00507           break;
00508       }
00509 
00510       if ( partial.length() == lead )
00511         return QString();
00512     }
00513   }
00514 
00515   return partial;
00516 }
00517 
00518 // Return the string to complete (the letters behind the cursor)
00519 const QString KateWordCompletionView::word() const
00520 {
00521   return m_view->document()->text( range() );
00522 }
00523 
00524 // Return the range containing the word behind the cursor
00525 const KTextEditor::Range KateWordCompletionView::range() const
00526 {
00527   KTextEditor::Cursor end = m_view->cursorPosition();
00528 
00529   if ( ! end.column() ) return KTextEditor::Range(); // no word
00530   int line = end.line();
00531   int col = end.column();
00532 
00533   KTextEditor::Document *doc = m_view->document();
00534   while ( col > 0 )
00535   {
00536     QChar c = ( doc->character( KTextEditor::Cursor( line, col-1 ) ) );
00537     if ( c.isLetterOrNumber() || c.isMark() || c == '_' )
00538     {
00539       col--;
00540       continue;
00541     }
00542 
00543     break;
00544   }
00545 
00546   return KTextEditor::Range( KTextEditor::Cursor( line, col ), end );
00547 }
00548 //END
00549 
00550 #include "katewordcompletion.moc"
00551 // kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal