Index: kate/completion/katecompletionwidget.h
===================================================================
--- kate/completion/katecompletionwidget.h	(Revision 890846)
+++ kate/completion/katecompletionwidget.h	(Arbeitskopie)
@@ -61,6 +61,7 @@
     KateCompletionTree* treeView() const;
 
     bool isCompletionActive() const;
+    void startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType);
     void startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model, KTextEditor::CodeCompletionModel::InvocationType invocationType = KTextEditor::CodeCompletionModel::ManualInvocation);
     void userInvokedCompletion();
 
@@ -82,7 +83,8 @@
     int automaticInvocationDelay() const;
     void setAutomaticInvocationDelay(int delay);
     
-    KateSmartRange* completionRange() const;
+    KateSmartRange* completionRange(KTextEditor::CodeCompletionModel* model = 0) const;
+    QMap<KTextEditor::CodeCompletionModel*, KateSmartRange*> completionRanges( ) const;
 
     // Navigation
     void pageDown();
@@ -155,11 +157,12 @@
     //Switch cursor between argument-hint list / completion-list
     void switchList();
     KTextEditor::Range determineRange() const;
+    void completionRangeChanged(KTextEditor::CodeCompletionModel*, const KTextEditor::Range& word);
 
     QList<KTextEditor::CodeCompletionModel*> m_sourceModels;
     KateCompletionModel* m_presentationModel;
 
-    KateSmartRange* m_completionRange;
+    QMap<KTextEditor::CodeCompletionModel*, KateSmartRange*> m_completionRanges;
     KTextEditor::Cursor m_lastCursorPosition;
 
     KateCompletionTree* m_entryList;
Index: kate/completion/katecompletionwidget.cpp
===================================================================
--- kate/completion/katecompletionwidget.cpp	(Revision 890846)
+++ kate/completion/katecompletionwidget.cpp	(Arbeitskopie)
@@ -36,6 +36,7 @@
 #include <kdialog.h>
 
 #include <ktexteditor/cursorfeedback.h>
+#include <ktexteditor/codecompletionmodelcontrollerinterface.h>
 
 #include "kateview.h"
 #include "katesmartmanager.h"
@@ -56,7 +57,6 @@
 KateCompletionWidget::KateCompletionWidget(KateView* parent)
   : QFrame(parent, Qt::ToolTip)
   , m_presentationModel(new KateCompletionModel(this))
-  , m_completionRange(0L)
   , m_entryList(new KateCompletionTree(this))
   , m_argumentHintModel(new KateArgumentHintModel(this))
   , m_argumentHintTree(new KateArgumentHintTree(this))
@@ -194,8 +194,13 @@
     updateArgumentHintGeometry();
 }
 
-void KateCompletionWidget::startCompletion( const KTextEditor::Range & word, KTextEditor::CodeCompletionModel * model, KTextEditor::CodeCompletionModel::InvocationType invocationType)
+void KateCompletionWidget::startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType)
 {
+  startCompletion(KTextEditor::Range(KTextEditor::Cursor(-1, -1), KTextEditor::Cursor(-1, -1)), 0, invocationType);
+}
+
+void KateCompletionWidget::startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model, KTextEditor::CodeCompletionModel::InvocationType invocationType)
+{
   m_isSuspended = false;
   m_inCompletionList = true; //Always start at the top of the completion-list
   m_needShow = true;
@@ -204,13 +209,13 @@
 
   m_dontShowArgumentHints = true;
 
-  if (!word.isValid()) {
-    kWarning(13035) << "Invalid range given to start code completion!";
-    return;
+  QList<KTextEditor::CodeCompletionModel*> models;
+  if (model) {
+    models << model;
+  } else {
+    models = m_sourceModels;
   }
 
-  kDebug(13035) << word << " " << model;
-
   if (!m_filterInstalled) {
     if (!QApplication::activeWindow()) {
       kWarning(13035) << "No active window to install event filter on!!";
@@ -224,32 +229,42 @@
   if (isCompletionActive())
     abortCompletion();
 
-  m_completionRange = view()->doc()->smartManager()->newSmartRange(word);
-  m_completionRange->setInsertBehavior(KTextEditor::SmartRange::ExpandRight | KTextEditor::SmartRange::ExpandLeft);
-  if(!m_completionRange->isValid()) {
-    kWarning(13035) << "Could not construct valid smart-range from" << word << "instead got" << *m_completionRange;
-    abortCompletion();
-    return;
-  }
+  m_presentationModel->setCompletionModels(m_sourceModels);
 
-  connect(m_completionRange->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)), SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool)));
+  foreach (KTextEditor::CodeCompletionModel* model, models) {
+    KTextEditor::CodeCompletionModelControllerInterface* modelController
+        = dynamic_cast<KTextEditor::CodeCompletionModelControllerInterface*>(model);
 
-  cursorPositionChanged();
+    KTextEditor::Range range;
+    if (word.isValid()) {
+      range = word;
+    } else {
+      if (modelController) {
+        range = modelController->completionRange(view(), view()->cursorPosition());
+      } else {
+        range = determineRange();
+      }
+    }
+    model->completionInvoked(view(), range, invocationType);
+    m_completionRanges[model] = view()->doc()->smartManager()->newSmartRange(range);
+    m_completionRanges[model]->setInsertBehavior(KTextEditor::SmartRange::ExpandRight | KTextEditor::SmartRange::ExpandLeft);
+    if(!m_completionRanges[model]->isValid()) {
+      kWarning(13035) << "Could not construct valid smart-range from" << range << "instead got" << *m_completionRanges[model];
+      abortCompletion();
+      return;
+    }
+    connect(m_completionRanges[model]->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)),
+              SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool)));
 
-  if (model)
-    model->completionInvoked(view(), word, invocationType);
-  else
-    foreach (KTextEditor::CodeCompletionModel* model, m_sourceModels)
-      model->completionInvoked(view(), word, invocationType);
+    cursorPositionChanged();
 
-  kDebug( 13035 ) << "msdofjdsoifdsflkdsjf";
-  if (model)
-    m_presentationModel->setCompletionModel(model);
-  else
-    m_presentationModel->setCompletionModels(m_sourceModels);
-
-  if (!m_presentationModel->completionModels().isEmpty()) {
-    m_presentationModel->setCurrentCompletion(view()->doc()->text(KTextEditor::Range(m_completionRange->start(), view()->cursorPosition())));
+    QString currentCompletion;
+    if (modelController) {
+      currentCompletion = modelController->filterString(view(), *m_completionRanges[model], view()->cursorPosition());
+    } else {
+      currentCompletion = view()->doc()->text(KTextEditor::Range(m_completionRanges[model]->start(), view()->cursorPosition()));
+    }
+    m_presentationModel->setCurrentCompletion(model, currentCompletion);
   }
 
   connect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
@@ -292,7 +307,10 @@
     return false;
 //   kDebug() << "updating from" << geometry() << m_entryList->geometry();
 
-  QPoint cursorPosition = view()->cursorToCoordinate(m_completionRange->start());
+  if (!completionRange()) {
+    return false;
+  }
+  QPoint cursorPosition = view()->cursorToCoordinate(completionRange()->start());
   if (cursorPosition == QPoint(-1,-1)) {
     // Start of completion range is now off-screen -> abort
     abortCompletion();
@@ -404,29 +422,56 @@
 void KateCompletionWidget::cursorPositionChanged( )
 {
   if (!isCompletionActive())  {
-//    m_presentationModel->setCurrentCompletion(QString());
     return;
   }
 
   KTextEditor::Cursor cursor = view()->cursorPosition();
 
-  if (m_completionRange->start() > cursor || m_completionRange->end() < cursor)
-    return abortCompletion();
+  QMutableMapIterator<KTextEditor::CodeCompletionModel*, KateSmartRange*> i(m_completionRanges);
+  while (i.hasNext()) {
+      i.next();
+      KTextEditor::CodeCompletionModel *model = i.key();
+      KateSmartRange* range = i.value();
+      KTextEditor::CodeCompletionModelControllerInterface* modelController
+          = dynamic_cast<KTextEditor::CodeCompletionModelControllerInterface*>(model);
+      bool abort;
+      QString currentCompletion;
+      if (modelController) {
+        modelController->updateCompletionRange(view(), *range);
+        currentCompletion = modelController->filterString(view(), *range, view()->cursorPosition());
+        abort = modelController->shouldAbortCompletion(view(), *range, currentCompletion);
+      } else {
+        currentCompletion = view()->doc()->text(KTextEditor::Range(range->start(), view()->cursorPosition()));
+        static const QRegExp allowedText("^(\\w*)");
+        abort = !allowedText.exactMatch(currentCompletion);
+      }
+      if (range->start() > cursor || range->end() < cursor)
+        abort = true;
 
-  QString currentCompletion = view()->doc()->text(KTextEditor::Range(m_completionRange->start(), view()->cursorPosition()));
+      if (abort) {
+        kDebug() << "abort" << model << m_sourceModels;
+        if (m_sourceModels.count() == 1) {
+          //last model - abort whole completion
+          abortCompletion();
+          return;
+        } else {
+          i.remove();
+          delete range;
+          m_sourceModels.removeOne(model);
+          m_presentationModel->removeCompletionModel(model);
+        }
+      } else {
+        kDebug() << model << *range << currentCompletion;
+        m_presentationModel->setCurrentCompletion(model, currentCompletion);
+      }
+  }
 
-  // FIXME - allow client to specify this?
-  static QRegExp allowedText("^(\\w*)");
-  if (!allowedText.exactMatch(currentCompletion))
-    return abortCompletion();
-
-  m_presentationModel->setCurrentCompletion(currentCompletion);
   m_entryList->scheduleUpdate();
 }
 
 bool KateCompletionWidget::isCompletionActive( ) const
 {
-  return m_completionRange && isVisible();
+  return !m_completionRanges.isEmpty() && isVisible();
 }
 
 void KateCompletionWidget::abortCompletion( )
@@ -442,9 +487,6 @@
   if(isVisible())
     hide();
 
-  delete m_completionRange;
-  m_completionRange = 0L;
-
   if (wasActive)
     view()->sendCompletionAborted();
 }
@@ -453,6 +495,8 @@
   m_presentationModel->clearCompletionModels();
   m_argumentHintTree->clearCompletion();
   m_argumentHintModel->clear();
+  qDeleteAll(m_completionRanges);
+  m_completionRanges.clear();
 }
 
 bool KateCompletionWidget::navigateAccept() {
@@ -495,8 +539,6 @@
     return abortCompletion();
   }
 
-  KTextEditor::Cursor start = m_completionRange->start();
-
   // encapsulate all editing as being from the code completion, and undo-able in one step.
   view()->doc()->editStart(true, Kate::CodeCompletionEdit);
 
@@ -505,17 +547,19 @@
 
   KTextEditor::CodeCompletionModel2* model2 = dynamic_cast<KTextEditor::CodeCompletionModel2*>(model);
 
+  KTextEditor::Cursor start = m_completionRanges[model]->start();
+
   //editStart locks the smart-mutex, but it must not be locked when calling external functions,
   //else we may get deadlock-issues.
   view()->doc()->smartMutex()->unlock();
   
   if(model2)
-    model2->executeCompletionItem2(view()->document(), *m_completionRange, toExecute);
+    model2->executeCompletionItem2(view()->document(), *m_completionRanges[model], toExecute);
   else if(toExecute.parent().isValid())
     //The normale CodeCompletionInterface cannot handle feedback for hierarchical models, so just do the replacement
-    view()->document()->replaceText(*m_completionRange, model->data(toExecute.sibling(toExecute.row(), KTextEditor::CodeCompletionModel::Name)).toString());
+    view()->document()->replaceText(*m_completionRanges[model], model->data(toExecute.sibling(toExecute.row(), KTextEditor::CodeCompletionModel::Name)).toString());
   else
-    model->executeCompletionItem(view()->document(), *m_completionRange, toExecute.row());
+    model->executeCompletionItem(view()->document(), *m_completionRanges[model], toExecute.row());
   
   //Relock, because editEnd expects it to be locked
   view()->doc()->smartMutex()->lock();
@@ -551,11 +595,20 @@
     abortCompletion();
 }
 
-KateSmartRange * KateCompletionWidget::completionRange( ) const
+KateSmartRange * KateCompletionWidget::completionRange(KTextEditor::CodeCompletionModel* model) const
 {
-  return m_completionRange;
+  if (!model) {
+    if (m_sourceModels.isEmpty()) return 0;
+    model = m_sourceModels.first();
+  }
+  return m_completionRanges[model];
 }
 
+QMap<KTextEditor::CodeCompletionModel*, KateSmartRange*> KateCompletionWidget::completionRanges( ) const
+{
+  return m_completionRanges;
+}
+
 void KateCompletionWidget::modelReset( )
 {
   setUpdatesEnabled(false);
@@ -850,25 +903,30 @@
 
 void KateCompletionWidget::automaticInvocation()
 {
-  if(m_automaticInvocationLine.isEmpty())
-    return;
-
-  QString lastLine = m_automaticInvocationLine;
-  QChar lastChar = lastLine.at(lastLine.count() - 1);
-
-  if (lastChar.isLetter() || lastChar.isNumber() || lastChar == '.' || lastChar == '_' || lastChar == '>') {
+  bool start = false;
+  foreach (KTextEditor::CodeCompletionModel *model, m_sourceModels) {
+      KTextEditor::CodeCompletionModelControllerInterface* modelController
+          = dynamic_cast<KTextEditor::CodeCompletionModelControllerInterface*>(model);
+      if (modelController) {
+        start = modelController->shouldStartCompletion(view(), m_automaticInvocationLine, view()->cursorPosition());
+      } else {
+        if(m_automaticInvocationLine.isEmpty()) continue;
+        QChar lastChar = m_automaticInvocationLine.at(m_automaticInvocationLine.count() - 1);
+        if (lastChar.isLetter() || lastChar.isNumber() || lastChar == '.' || lastChar == '_' || lastChar == '>') {
+          start = true;
+        }
+      }
+      if (start) break;
+  }
+  if (start) {
     // Start automatic code completion
-    KTextEditor::Range range = determineRange();
-    if (range.isValid())
-      startCompletion(range, 0, KTextEditor::CodeCompletionModel::AutomaticInvocation);
-    else
-      kWarning(13035) << "Completion range was invalid even though it was expected to be valid.";
+    startCompletion(KTextEditor::CodeCompletionModel::AutomaticInvocation);
   }
 }
 
 void KateCompletionWidget::userInvokedCompletion()
 {
-  startCompletion(determineRange(), 0, KTextEditor::CodeCompletionModel::UserInvocation);
+  startCompletion(KTextEditor::CodeCompletionModel::UserInvocation);
 }
 
 KTextEditor::Range KateCompletionWidget::determineRange() const
Index: kate/completion/katecompletionmodel.h
===================================================================
--- kate/completion/katecompletionmodel.h	(Revision 890846)
+++ kate/completion/katecompletionmodel.h	(Arbeitskopie)
@@ -61,8 +61,8 @@
     KateView* view() const;
     KateCompletionWidget* widget() const;
 
-    const QString& currentCompletion() const;
-    void setCurrentCompletion(const QString& completion);
+    QString currentCompletion(KTextEditor::CodeCompletionModel* model) const;
+    void setCurrentCompletion(KTextEditor::CodeCompletionModel* model, const QString& completion);
 
     Qt::CaseSensitivity matchCaseSensitivity() const;
     void setMatchCaseSensitivity( Qt::CaseSensitivity cs );
@@ -209,7 +209,7 @@
         bool isMatching() const;
 
         bool filter();
-        bool match(const QString& newCompletion = QString());
+        bool match();
 
         const ModelRow& sourceRow() const;
 
@@ -293,7 +293,7 @@
     };
 
 
-    void changeCompletions(Group* g, const QString& newCompletion, changeTypes changeType);
+    void changeCompletions(Group* g, changeTypes changeType);
 
     void deleteRows(Group* g, QMutableListIterator<Item>& filtered, int countBackwards, int startRow);
     void addRows(Group* g, QMutableListIterator<Item>& filtered, int startRow, const QList<Item>& newItems);
@@ -314,7 +314,7 @@
     // ### Runtime state
     // General
     QList<KTextEditor::CodeCompletionModel*> m_completionModels;
-    QString m_currentMatch;
+    QMap<KTextEditor::CodeCompletionModel*, QString> m_currentMatch;
     Qt::CaseSensitivity m_matchCaseSensitivity;
 
     // Column merging
Index: kate/completion/katecompletionmodel.cpp
===================================================================
--- kate/completion/katecompletionmodel.cpp	(Revision 890846)
+++ kate/completion/katecompletionmodel.cpp	(Arbeitskopie)
@@ -545,13 +545,13 @@
   clearGroups();
 
   bool has_groups=false;
-
   foreach (CodeCompletionModel* sourceModel, m_completionModels) {
     has_groups|=sourceModel->hasGroups();
     for (int i = 0; i < sourceModel->rowCount(); ++i)
       createItems(HierarchicalModelHandler(sourceModel), sourceModel->index(i, 0));
   }
   m_hasGroups=has_groups;
+
   //debugStats();
 
   foreach (Group* g, m_rowTable)
@@ -559,10 +559,10 @@
 
   foreach (Group* g, m_emptyGroups)
     hideOrShowGroup(g);
-  
+
   reset();
+
   updateBestMatches();
-  
   emit contentGeometryChanged();
 }
 
@@ -586,7 +586,7 @@
   Item item = Item(this, handler, ModelRow(handler.model(), QPersistentModelIndex(sourceIndex)));
 
   if(g != m_argumentHints)
-    item.match(m_currentMatch);
+    item.match();
 
   g->addItem(item, notifyModel);
 
@@ -780,7 +780,7 @@
     int row = m_rowTable.indexOf(g);
 
     if (row == -1) {
-      kWarning() << k_funcinfo << "Couldn't find parent for index" << index;
+      kWarning() << "Couldn't find parent for index" << index;
       return QModelIndex();
     }
 
@@ -859,44 +859,47 @@
   return QModelIndex();
 }
 
-void KateCompletionModel::setCurrentCompletion( const QString & completion )
+void KateCompletionModel::setCurrentCompletion( KTextEditor::CodeCompletionModel* model, const QString & completion )
 {
-  if (m_currentMatch == completion)
+  if (m_currentMatch[model] == completion)
     return;
 
   if (!hasCompletionModel()) {
-    m_currentMatch = completion;
+    m_currentMatch[model] = completion;
     return;
   }
 
   changeTypes changeType = Change;
 
-  if (m_currentMatch.length() > completion.length() && m_currentMatch.startsWith(completion, m_matchCaseSensitivity)) {
+  if (m_currentMatch[model].length() > completion.length() && m_currentMatch[model].startsWith(completion, m_matchCaseSensitivity)) {
     // Filter has been broadened
     changeType = Broaden;
 
-  } else if (m_currentMatch.length() < completion.length() && completion.startsWith(m_currentMatch, m_matchCaseSensitivity)) {
+  } else if (m_currentMatch[model].length() < completion.length() && completion.startsWith(m_currentMatch[model], m_matchCaseSensitivity)) {
     // Filter has been narrowed
     changeType = Narrow;
   }
 
-  kDebug( 13035 ) << "Old match: " << m_currentMatch << ", new: " << completion << ", type: " << changeType;
+  kDebug( 13035 ) << model << "Old match: " << m_currentMatch[model] << ", new: " << completion << ", type: " << changeType;
 
-  if (!hasGroups())
-    changeCompletions(m_ungrouped, completion, changeType);
-  else {
-    foreach (Group* g, m_rowTable)
+  m_currentMatch[model] = completion;
+
+  if (!hasGroups()) {
+    changeCompletions(m_ungrouped, changeType);
+  } else {
+    foreach (Group* g, m_rowTable) {
       if(g != m_argumentHints)
-        changeCompletions(g, completion, changeType);
-    foreach (Group* g, m_emptyGroups)
+        changeCompletions(g, changeType);
+    }
+    foreach (Group* g, m_emptyGroups) {
       if(g != m_argumentHints)
-        changeCompletions(g, completion, changeType);
+        changeCompletions(g, changeType);
+    }
 
     updateBestMatches();
   }
 
   clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering.
-  m_currentMatch = completion;
 
   emit contentGeometryChanged();
 }
@@ -904,16 +907,16 @@
 void KateCompletionModel::rematch()
 {
   if (!hasGroups()) {
-    changeCompletions(m_ungrouped, m_currentMatch, Change);
+    changeCompletions(m_ungrouped, Change);
 
   } else {
     foreach (Group* g, m_rowTable)
       if(g != m_argumentHints)
-        changeCompletions(g, m_currentMatch, Change);
+        changeCompletions(g, Change);
 
     foreach (Group* g, m_emptyGroups)
       if(g != m_argumentHints)
-        changeCompletions(g, m_currentMatch, Change);
+        changeCompletions(g, Change);
 
     updateBestMatches();
   }
@@ -940,7 +943,7 @@
     rowAdd.clear(); \
   }
 
-void KateCompletionModel::changeCompletions( Group * g, const QString & newCompletion, changeTypes changeType )
+void KateCompletionModel::changeCompletions( Group * g, changeTypes changeType )
 {
   QMutableListIterator<Item> filtered = g->filtered;
   QMutableListIterator<Item> prefilter = g->prefilter;
@@ -956,7 +959,7 @@
       if (filtered.peekNext().sourceRow() == prefilter.peekNext().sourceRow()) {
         // Currently being displayed
         if (changeType != Broaden) {
-          if (prefilter.peekNext().match(newCompletion)) {
+          if (prefilter.peekNext().match()) {
             // no change required to this item
             COMPLETE_DELETE
             COMPLETE_ADD
@@ -981,7 +984,7 @@
       } else {
         // Currently hidden
         if (changeType != Narrow) {
-          if (prefilter.peekNext().match(newCompletion)) {
+          if (prefilter.peekNext().match()) {
             // needs to be made visible
             COMPLETE_DELETE
 
@@ -1005,7 +1008,7 @@
     } else {
       // Currently hidden
       if (changeType != Narrow) {
-        if (prefilter.peekNext().match(newCompletion)) {
+        if (prefilter.peekNext().match()) {
           // needs to be made visible
           COMPLETE_DELETE
 
@@ -1091,6 +1094,7 @@
     }
 
   } else {
+
     if (!g->filtered.isEmpty()) {
       // Move off empty group list
       g->isEmpty = false;
@@ -1716,20 +1720,19 @@
   return matchFilters;
 }
 
-bool KateCompletionModel::Item::match(const QString& newCompletion)
+bool KateCompletionModel::Item::match()
 {
-  // Hehe, everything matches nothing! (ie. everything matches a blank string)
-  if (newCompletion.isEmpty())
-    return true;
-
   // Check to see if the item is matched by the current completion string
   QModelIndex sourceIndex = m_sourceRow.second.sibling(m_sourceRow.second.row(), CodeCompletionModel::Name);
 
-  QString match = newCompletion;
-  if (match.isEmpty())
-    match = model->currentCompletion();
+  QString match = model->currentCompletion(m_sourceRow.first);
 
+   // Hehe, everything matches nothing! (ie. everything matches a blank string)
+   if (match.isEmpty())
+     return true;
+
   matchCompletion = m_nameColumn.startsWith(match, model->matchCaseSensitivity());
+
   return matchCompletion;
 }
 
@@ -1827,9 +1830,9 @@
   return m_sourceRow;
 }
 
-const QString & KateCompletionModel::currentCompletion( ) const
+QString KateCompletionModel::currentCompletion( KTextEditor::CodeCompletionModel* model ) const
 {
-  return m_currentMatch;
+  return m_currentMatch.value(model);
 }
 
 Qt::CaseSensitivity KateCompletionModel::matchCaseSensitivity( ) const
@@ -1884,19 +1887,21 @@
 
 void KateCompletionModel::removeCompletionModel(CodeCompletionModel * model)
 {
-  if (!model || m_completionModels.contains(model))
+  if (!model || !m_completionModels.contains(model))
     return;
 
+  m_currentMatch.remove(model);
+
   clearGroups();
 
   model->disconnect(this);
 
   m_completionModels.removeAll(model);
 
-  if (!m_completionModels.isEmpty())
+  if (!m_completionModels.isEmpty()) {
+    // This performs the reset
     createGroups();
-
-  reset();
+  }
 }
 
 //Updates the best-matches group
@@ -1979,6 +1984,8 @@
 
   m_completionModels.clear();
 
+  m_currentMatch.clear();
+
   clearGroups();
 
   reset();
Index: interfaces/ktexteditor/codecompletionmodelcontrollerinterface.h
===================================================================
--- interfaces/ktexteditor/codecompletionmodelcontrollerinterface.h	(Revision 0)
+++ interfaces/ktexteditor/codecompletionmodelcontrollerinterface.h	(Revision 0)
@@ -0,0 +1,100 @@
+/* This file is part of the KDE libraries
+   Copyright (C) 2008 Niko Sams <niko.sams@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDELIBS_KTEXTEDITOR_CODECOMPLETIONMODELCONTROLLERINTERFACE_H
+#define KDELIBS_KTEXTEDITOR_CODECOMPLETIONMODELCONTROLLERINTERFACE_H
+
+#include <ktexteditor/ktexteditor_export.h>
+#include <ktexteditor/smartrange.h>
+#include <ktexteditor/cursor.h>
+
+namespace KTextEditor {
+class View;
+
+/**
+ * \short An interface CodeCompletionModel can implement to have more control
+ *        over code completion
+ */
+class KTEXTEDITOR_EXPORT CodeCompletionModelControllerInterface
+{
+public:
+    virtual ~CodeCompletionModelControllerInterface();
+
+
+    /**
+     * This function decides if the automatic completion should be started when
+     * the user entered some text.
+     *
+     * The default implementation will work for most languages.
+     *
+     * @param view The view to generate completions for
+     * @param insertedText The text that was inserted by the user
+     * @param position Current cursor position
+     */
+    virtual bool shouldStartCompletion(View* view, const QString &insertedText, const Cursor &position);
+
+    /**
+     * This function returns the initial range used for filtering the completion list.
+     *
+     * The default implementation will work for most languages that don't have
+     * special chars in identifiers.
+     *
+     * @param view The view to generate completions for
+     * @param position Current cursor position
+     */
+    virtual Range completionRange(View* view, const Cursor &position);
+
+    /**
+     * This function lets the CompletionModel dynamically modify the range.
+     * Called after every change to the range (eg. when user entered text)
+     *
+     * The default implementation does nothing.
+     *
+     * @param view The view to generate completions for
+     * @param range Reference to the current range
+     */
+    virtual void updateCompletionRange(View* view, SmartRange& range);
+
+    /**
+     * This function returns the filter-text used for the current completion.
+     * Can return an empty string to disable filtering.
+     *
+     * The default implementation will return the text from range start to the cursor position.
+     *
+     * @param view The view to generate completions for
+     * @param range The completion range
+     * @param position Current cursor position
+     */
+    virtual QString filterString(View* view, const SmartRange& range, const Cursor &position);
+
+    /**
+     * This function decides if the completion should be aborted.
+     * Called after every change to the range (eg. when user entered text)
+     *
+     * The default implementation will return false when any special character was entered
+     *
+     * @param view The view to generate completions for
+     * @param range The completion range
+     * @param position Current cursor position
+     */
+    virtual bool shouldAbortCompletion(View* view, const SmartRange& range, const QString &currentCompletion);
+};
+
+}
+
+#endif // KDELIBS_KTEXTEDITOR_CODECOMPLETIONMODELCONTROLLERINTERFACE_H
Index: interfaces/ktexteditor/CMakeLists.txt
===================================================================
--- interfaces/ktexteditor/CMakeLists.txt	(Revision 890846)
+++ interfaces/ktexteditor/CMakeLists.txt	(Arbeitskopie)
@@ -15,6 +15,7 @@
    smartrange.cpp
    codecompletioninterface.cpp
    codecompletionmodel.cpp
+   codecompletionmodelcontrollerinterface.cpp
    configinterface.cpp
    smartinterface.cpp
    )
@@ -61,6 +62,7 @@
     smartrange.h
     codecompletioninterface.h
     codecompletionmodel.h
+    codecompletionmodelcontrollerinterface.h
     configinterface.h
     containerinterface.h
     annotationinterface.h
Index: interfaces/ktexteditor/codecompletionmodelcontrollerinterface.cpp
===================================================================
--- interfaces/ktexteditor/codecompletionmodelcontrollerinterface.cpp	(Revision 0)
+++ interfaces/ktexteditor/codecompletionmodelcontrollerinterface.cpp	(Revision 0)
@@ -0,0 +1,82 @@
+/* This file is part of the KDE libraries
+   Copyright (C) 2008 Niko Sams <niko.sams@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+#include "codecompletionmodelcontrollerinterface.h"
+
+#include <ktexteditor/view.h>
+#include <ktexteditor/document.h>
+
+namespace KTextEditor {
+
+CodeCompletionModelControllerInterface::~CodeCompletionModelControllerInterface()
+{
+}
+
+bool CodeCompletionModelControllerInterface::shouldStartCompletion(View* view, const QString &insertedText, const Cursor &position)
+{
+    Q_UNUSED(view);
+    Q_UNUSED(position);
+    if(insertedText.isEmpty())
+        return false;
+
+    QChar lastChar = insertedText.at(insertedText.count() - 1);
+    if (lastChar.isLetter() || lastChar.isNumber() || lastChar == '.' || lastChar == '_' || lastChar == '>') {
+        return true;
+    }
+    return false;
+}
+
+Range CodeCompletionModelControllerInterface::completionRange(View* view, const Cursor &position)
+{
+    Cursor end = position;
+
+    QString text = view->document()->line(end.line());
+
+    static QRegExp findWordStart( "\\b([_\\w]+)$" );
+    static QRegExp findWordEnd( "^([_\\w]*)\\b" );
+
+    Cursor start = end;
+
+    if (findWordStart.lastIndexIn(text.left(end.column())) >= 0)
+        start.setColumn(findWordStart.pos(1));
+
+    if (findWordEnd.indexIn(text.mid(end.column())) >= 0)
+        end.setColumn(end.column() + findWordEnd.cap(1).length());
+
+    return Range(start, end);
+}
+
+void CodeCompletionModelControllerInterface::updateCompletionRange(View* view, SmartRange& range)
+{
+    Q_UNUSED(view);
+    Q_UNUSED(range);
+}
+
+QString CodeCompletionModelControllerInterface::filterString(View* view, const SmartRange &range, const Cursor &position)
+{
+    return view->document()->text(KTextEditor::Range(range.start(), position));
+}
+
+bool CodeCompletionModelControllerInterface::shouldAbortCompletion(View* view, const SmartRange &range, const QString &currentCompletion)
+{
+    Q_UNUSED(view);
+    Q_UNUSED(range);
+    static const QRegExp allowedText("^(\\w*)");
+    return !allowedText.exactMatch(currentCompletion);
+}
+
+}
