1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "icompletioncollector.h"
36 #include "completionsettings.h"
37 #include "itexteditable.h"
39 #include <QtCore/QRegExp>
42 using namespace TextEditor;
43 using namespace TextEditor::Internal;
45 namespace TextEditor {
48 class ICompletionCollectorPrivate
51 CompletionSettings m_completionSettings;
54 } // namespace Internal
55 } // namespace TextEditor
57 bool ICompletionCollector::compareChar(const QChar &l, const QChar &r)
59 if (l == QLatin1Char('_'))
61 else if (r == QLatin1Char('_'))
67 bool ICompletionCollector::lessThan(const QString &l, const QString &r)
69 return std::lexicographical_compare(l.begin(), l.end(),
74 bool ICompletionCollector::completionItemLessThan(const CompletionItem &i1, const CompletionItem &i2)
76 // The order is case-insensitive in principle, but case-sensitive when this would otherwise mean equality
77 const QString lower1 = i1.text.toLower();
78 const QString lower2 = i2.text.toLower();
80 return lessThan(i1.text, i2.text);
82 return lessThan(lower1, lower2);
85 ICompletionCollector::ICompletionCollector(QObject *parent)
87 , m_d(new Internal::ICompletionCollectorPrivate)
91 ICompletionCollector::~ICompletionCollector()
96 void ICompletionCollector::setCompletionSettings(const CompletionSettings &settings)
98 m_d->m_completionSettings = settings;
101 const CompletionSettings &ICompletionCollector::completionSettings() const
103 return m_d->m_completionSettings;
106 QList<CompletionItem> ICompletionCollector::getCompletions()
108 QList<CompletionItem> completionItems;
110 completions(&completionItems);
112 qStableSort(completionItems.begin(), completionItems.end(), completionItemLessThan);
116 QList<CompletionItem> uniquelist;
118 foreach (const CompletionItem &item, completionItems) {
119 if (item.text != lastKey) {
120 uniquelist.append(item);
123 uniquelist.last().duplicateCount++;
130 bool ICompletionCollector::partiallyComplete(const QList<TextEditor::CompletionItem> &items)
132 if (! m_d->m_completionSettings.m_partiallyComplete)
134 if (items.size() >= 100)
137 QList<TextEditor::CompletionItem> completionItems = items;
138 sortCompletion(completionItems);
140 // Compute common prefix
141 QString firstKey = completionItems.first().text;
142 QString lastKey = completionItems.last().text;
143 const int length = qMin(firstKey.length(), lastKey.length());
144 firstKey.truncate(length);
145 lastKey.truncate(length);
147 while (firstKey != lastKey) {
152 if (ITextEditable *ed = editor()) {
153 const int typedLength = ed->position() - startPosition();
154 if (!firstKey.isEmpty() && firstKey.length() > typedLength) {
155 ed->setCurPos(startPosition());
156 ed->replace(typedLength, firstKey);
163 void ICompletionCollector::sortCompletion(QList<TextEditor::CompletionItem> &completionItems)
165 qStableSort(completionItems.begin(), completionItems.end(), &ICompletionCollector::completionItemLessThan);
168 void ICompletionCollector::filter(const QList<TextEditor::CompletionItem> &items,
169 QList<TextEditor::CompletionItem> *filteredItems,
172 const TextEditor::CaseSensitivity caseSensitivity = m_d->m_completionSettings.m_caseSensitivity;
175 * This code builds a regular expression in order to more intelligently match
176 * camel-case style. This means upper-case characters will be rewritten as follows:
178 * A => [a-z0-9_]*A (for any but the first capital letter)
180 * Meaning it allows any sequence of lower-case characters to preceed an
181 * upper-case character. So for example gAC matches getActionController.
183 * It also implements the first-letter-only case sensitivity.
186 keyRegExp += QLatin1Char('^');
188 const QLatin1String wordContinuation("[a-z0-9_]*");
189 foreach (const QChar &c, key) {
190 if (caseSensitivity == TextEditor::CaseInsensitive ||
191 (caseSensitivity == TextEditor::FirstLetterCaseSensitive && !first)) {
193 keyRegExp += QLatin1String("(?:");
194 if (c.isUpper() && !first)
195 keyRegExp += wordContinuation;
196 keyRegExp += QRegExp::escape(c.toUpper());
197 keyRegExp += QLatin1Char('|');
198 keyRegExp += QRegExp::escape(c.toLower());
199 keyRegExp += QLatin1Char(')');
201 if (c.isUpper() && !first)
202 keyRegExp += wordContinuation;
203 keyRegExp += QRegExp::escape(c);
208 const QRegExp regExp(keyRegExp);
210 const bool hasKey = !key.isEmpty();
211 foreach (TextEditor::CompletionItem item, items) {
212 if (regExp.indexIn(item.text) == 0) {
214 if (item.text.startsWith(key, Qt::CaseSensitive)) {
216 } else if (caseSensitivity != TextEditor::CaseSensitive
217 && item.text.startsWith(key, Qt::CaseInsensitive)) {
221 filteredItems->append(item);
226 bool ICompletionCollector::shouldRestartCompletion()