Index: interfaces/ktexteditor/codecompletionmodel.h =================================================================== --- interfaces/ktexteditor/codecompletionmodel.h (Revision 884497) +++ interfaces/ktexteditor/codecompletionmodel.h (Arbeitskopie) @@ -421,6 +421,7 @@ Q_SIGNALS: void hasGroupsChanged(KTextEditor::CodeCompletionModel *model,bool hasGroups); + void modifyCompletionRange(const KTextEditor::Range& word, const QRegExp& allowedText); protected: void setHasGroups(bool hasGroups); Index: kate/completion/katecompletionmodel.cpp =================================================================== --- kate/completion/katecompletionmodel.cpp (Revision 884497) +++ kate/completion/katecompletionmodel.cpp (Arbeitskopie) @@ -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); @@ -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 filtered = g->filtered; QMutableListIterator 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 @@ -1716,19 +1719,17 @@ 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 +1828,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 @@ -1887,6 +1888,8 @@ if (!model || m_completionModels.contains(model)) return; + m_currentMatch.remove(model); + clearGroups(); model->disconnect(this); @@ -1979,6 +1982,8 @@ m_completionModels.clear(); + m_currentMatch.clear(); + clearGroups(); reset(); Index: kate/completion/katecompletionmodel.h =================================================================== --- kate/completion/katecompletionmodel.h (Revision 884497) +++ 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& filtered, int countBackwards, int startRow); void addRows(Group* g, QMutableListIterator& filtered, int startRow, const QList& newItems); @@ -314,7 +314,7 @@ // ### Runtime state // General QList m_completionModels; - QString m_currentMatch; + QMap m_currentMatch; Qt::CaseSensitivity m_matchCaseSensitivity; // Column merging Index: kate/completion/katecompletionwidget.cpp =================================================================== --- kate/completion/katecompletionwidget.cpp (Revision 884497) +++ kate/completion/katecompletionwidget.cpp (Arbeitskopie) @@ -56,7 +56,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)) @@ -202,7 +201,12 @@ return; } - kDebug(13035) << word << " " << model; + QList models; + if (model) { + models << model; + } else { + models = m_sourceModels; + } if (!m_filterInstalled) { if (!QApplication::activeWindow()) { @@ -217,32 +221,14 @@ 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(models); - connect(m_completionRange->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)), SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool))); - - cursorPositionChanged(); - - if (model) + foreach (KTextEditor::CodeCompletionModel* model, models) { + connect(model, SIGNAL(modifyCompletionRange(KTextEditor::Range, QRegExp)), SLOT(modifyCompletionRange(KTextEditor::Range, QRegExp))); model->completionInvoked(view(), word, invocationType); - else - foreach (KTextEditor::CodeCompletionModel* model, m_sourceModels) - model->completionInvoked(view(), word, invocationType); - - 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()))); + if (!m_completionRanges.contains(model)) { //completionInvoked might emit modifyCompletionRange + completionRangeChanged(model, word); + } } connect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged())); @@ -250,6 +236,40 @@ modelContentChanged(); } +void KateCompletionWidget::modifyCompletionRange(const KTextEditor::Range& word, const QRegExp& allowedText) +{ + KTextEditor::CodeCompletionModel* model = qobject_cast(sender()); + Q_ASSERT(model); + kDebug() << model << word << allowedText; + m_allowedText[model] = allowedText; + completionRangeChanged(model, word); +} + +void KateCompletionWidget::completionRangeChanged(KTextEditor::CodeCompletionModel* model, const KTextEditor::Range& word) +{ + if (m_completionRanges.contains(model)) { + if (*m_completionRanges[model] == word) { + kDebug() << "ignoring, range not changed"; + return; + } + delete m_completionRanges[model]; + } + + m_completionRanges[model] = view()->doc()->smartManager()->newSmartRange(word); + m_completionRanges[model]->setInsertBehavior(KTextEditor::SmartRange::ExpandRight | KTextEditor::SmartRange::ExpandLeft); + if(!m_completionRanges[model]->isValid()) { + kWarning(13035) << "Could not construct valid smart-range from" << word << "instead got" << *m_completionRanges[model]; + abortCompletion(); + return; + } + connect(m_completionRanges[model]->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)), + SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool))); + + cursorPositionChanged(); + + m_presentationModel->setCurrentCompletion(model, view()->doc()->text(KTextEditor::Range(m_completionRanges[model]->start(), view()->cursorPosition()))); +} + void KateCompletionWidget::updateAndShow() { setUpdatesEnabled(false); @@ -285,7 +305,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(); @@ -403,23 +426,33 @@ KTextEditor::Cursor cursor = view()->cursorPosition(); - if (m_completionRange->start() > cursor || m_completionRange->end() < cursor) - return abortCompletion(); + foreach (KateSmartRange* range, m_completionRanges) { + if (range->start() > cursor || range->end() < cursor) + return abortCompletion(); + } - QString currentCompletion = view()->doc()->text(KTextEditor::Range(m_completionRange->start(), view()->cursorPosition())); + QMapIterator i(m_completionRanges); + while (i.hasNext()) { + i.next(); + QString currentCompletion = view()->doc()->text(KTextEditor::Range(i.value()->start(), view()->cursorPosition())); - // FIXME - allow client to specify this? - static QRegExp allowedText("^(\\w*)"); - if (!allowedText.exactMatch(currentCompletion)) - return abortCompletion(); + QRegExp allowedText("^(\\w*)"); + if (m_allowedText.contains(i.key())) { + allowedText = m_allowedText[i.key()]; + } + kDebug() << i.key() << i.value() << currentCompletion << allowedText; + if (!allowedText.exactMatch(currentCompletion)) + return abortCompletion(); - m_presentationModel->setCurrentCompletion(currentCompletion); + m_presentationModel->setCurrentCompletion(i.key(), currentCompletion); + } + m_entryList->scheduleUpdate(); } bool KateCompletionWidget::isCompletionActive( ) const { - return m_completionRange && isVisible(); + return !m_completionRanges.isEmpty() && isVisible(); } void KateCompletionWidget::abortCompletion( ) @@ -435,9 +468,6 @@ if(isVisible()) hide(); - delete m_completionRange; - m_completionRange = 0L; - if (wasActive) view()->sendCompletionAborted(); } @@ -446,6 +476,8 @@ m_presentationModel->clearCompletionModels(); m_argumentHintTree->clearCompletion(); m_argumentHintModel->clear(); + qDeleteAll(m_completionRanges); + m_completionRanges.clear(); } bool KateCompletionWidget::embeddedWidgetAccept() { @@ -491,8 +523,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); @@ -501,17 +531,19 @@ KTextEditor::CodeCompletionModel2* model2 = dynamic_cast(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(); @@ -547,11 +579,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 KateCompletionWidget::completionRanges( ) const +{ + return m_completionRanges; +} + void KateCompletionWidget::modelReset( ) { setUpdatesEnabled(false); Index: kate/completion/katecompletionwidget.h =================================================================== --- kate/completion/katecompletionwidget.h (Revision 884497) +++ kate/completion/katecompletionwidget.h (Arbeitskopie) @@ -83,7 +83,8 @@ int automaticInvocationDelay() const; void setAutomaticInvocationDelay(int delay); - KateSmartRange* completionRange() const; + KateSmartRange* completionRange(KTextEditor::CodeCompletionModel* model = 0) const; + QMap completionRanges( ) const; // Navigation void pageDown(); @@ -144,6 +145,7 @@ void startCharacterDeleted(KTextEditor::SmartCursor* cursor, bool deletedBefore); void rowsInserted(const QModelIndex& parent, int row, int rowEnd); void viewFocusOut(); + void modifyCompletionRange(const KTextEditor::Range& word, const QRegExp& allowedText); private: void updateAndShow(); void updateArgumentHintGeometry(); @@ -153,11 +155,13 @@ //Switch cursor between argument-hint list / completion-list void switchList(); KTextEditor::Range determineRange() const; + void completionRangeChanged(KTextEditor::CodeCompletionModel*, const KTextEditor::Range& word); QList m_sourceModels; KateCompletionModel* m_presentationModel; - KateSmartRange* m_completionRange; + QMap m_completionRanges; + QMap m_allowedText; KTextEditor::Cursor m_lastCursorPosition; KateCompletionTree* m_entryList;