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

Kate

katewordcompletion.cpp

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