00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00062
00063
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;
00085
00086 if( !index.parent().isValid() ) {
00087
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;
00138 else if(parent.parent().isValid())
00139 return 0;
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
00158 return;
00159 }
00160
00161
00162 saveMatches( view, range );
00163 }
00164
00165
00166
00167
00168 const QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const
00169 {
00170 QStringList l;
00171
00172
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
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
00210
00211
00212
00213 struct KateWordCompletionViewPrivate
00214 {
00215 KTextEditor::SmartRange* liRange;
00216 KTextEditor::Range dcRange;
00217 KTextEditor::Cursor dcCursor;
00218 QRegExp re;
00219 int directionalPos;
00220 bool isCompleting;
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
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) );
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
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
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
00343
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
00356
00357
00358
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
00378 {
00379
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
00401 pos = fw ?
00402 d->re.indexIn( ln, d->dcCursor.column() ) :
00403 d->re.lastIndexIn( ln, d->dcCursor.column() );
00404
00405 if ( pos > -1 )
00406 {
00407
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
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 );
00417
00418 d->isCompleting = false;
00419 return;
00420 }
00421
00422
00423 else
00424 {
00425
00426 d->dcCursor.setColumn( pos );
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
00455 {
00456
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 }
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
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
00512 const QString KateWordCompletionView::word() const
00513 {
00514 return m_view->document()->text( range() );
00515 }
00516
00517
00518 const KTextEditor::Range KateWordCompletionView::range() const
00519 {
00520 KTextEditor::Cursor end = m_view->cursorPosition();
00521
00522 if ( ! end.column() ) return KTextEditor::Range();
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
00542
00543 #include "katewordcompletion.moc"
00544