OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / texteditor / icompletioncollector.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "icompletioncollector.h"
35
36 #include "completionsettings.h"
37 #include "itexteditable.h"
38
39 #include <QtCore/QRegExp>
40 #include <algorithm>
41
42 using namespace TextEditor;
43 using namespace TextEditor::Internal;
44
45 namespace TextEditor {
46 namespace Internal {
47
48 class ICompletionCollectorPrivate
49 {
50 public:
51     CompletionSettings m_completionSettings;
52 };
53
54 } // namespace Internal
55 } // namespace TextEditor
56
57 bool ICompletionCollector::compareChar(const QChar &l, const QChar &r)
58 {
59     if (l == QLatin1Char('_'))
60         return false;
61     else if (r == QLatin1Char('_'))
62         return true;
63     else
64         return l < r;
65 }
66
67 bool ICompletionCollector::lessThan(const QString &l, const QString &r)
68 {
69     return std::lexicographical_compare(l.begin(), l.end(),
70                                         r.begin(), r.end(),
71                                         compareChar);
72 }
73
74 bool ICompletionCollector::completionItemLessThan(const CompletionItem &i1, const CompletionItem &i2)
75 {
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();
79     if (lower1 == lower2)
80         return lessThan(i1.text, i2.text);
81     else
82         return lessThan(lower1, lower2);
83 }
84
85 ICompletionCollector::ICompletionCollector(QObject *parent)
86     : QObject(parent)
87     , m_d(new Internal::ICompletionCollectorPrivate)
88 {
89 }
90
91 ICompletionCollector::~ICompletionCollector()
92 {
93     delete m_d;
94 }
95
96 void ICompletionCollector::setCompletionSettings(const CompletionSettings &settings)
97 {
98     m_d->m_completionSettings = settings;
99 }
100
101 const CompletionSettings &ICompletionCollector::completionSettings() const
102 {
103     return m_d->m_completionSettings;
104 }
105
106 QList<CompletionItem> ICompletionCollector::getCompletions()
107 {
108     QList<CompletionItem> completionItems;
109
110     completions(&completionItems);
111
112     qStableSort(completionItems.begin(), completionItems.end(), completionItemLessThan);
113
114     // Remove duplicates
115     QString lastKey;
116     QList<CompletionItem> uniquelist;
117
118     foreach (const CompletionItem &item, completionItems) {
119         if (item.text != lastKey) {
120             uniquelist.append(item);
121             lastKey = item.text;
122         } else {
123             uniquelist.last().duplicateCount++;
124         }
125     }
126
127     return uniquelist;
128 }
129
130 bool ICompletionCollector::partiallyComplete(const QList<TextEditor::CompletionItem> &items)
131 {
132     if (! m_d->m_completionSettings.m_partiallyComplete)
133         return false;
134     if (items.size() >= 100)
135         return false;
136
137     QList<TextEditor::CompletionItem> completionItems = items;
138     sortCompletion(completionItems);
139
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);
146
147     while (firstKey != lastKey) {
148         firstKey.chop(1);
149         lastKey.chop(1);
150     }
151
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);
157         }
158     }
159
160     return false;
161 }
162
163 void ICompletionCollector::sortCompletion(QList<TextEditor::CompletionItem> &completionItems)
164 {
165     qStableSort(completionItems.begin(), completionItems.end(), &ICompletionCollector::completionItemLessThan);
166 }
167
168 void ICompletionCollector::filter(const QList<TextEditor::CompletionItem> &items,
169                                   QList<TextEditor::CompletionItem> *filteredItems,
170                                   const QString &key)
171 {
172     const TextEditor::CaseSensitivity caseSensitivity = m_d->m_completionSettings.m_caseSensitivity;
173
174     /*
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:
177      *
178      *   A => [a-z0-9_]*A (for any but the first capital letter)
179      *
180      * Meaning it allows any sequence of lower-case characters to preceed an
181      * upper-case character. So for example gAC matches getActionController.
182      *
183      * It also implements the first-letter-only case sensitivity.
184      */
185     QString keyRegExp;
186     keyRegExp += QLatin1Char('^');
187     bool first = true;
188     const QLatin1String wordContinuation("[a-z0-9_]*");
189     foreach (const QChar &c, key) {
190         if (caseSensitivity == TextEditor::CaseInsensitive ||
191             (caseSensitivity == TextEditor::FirstLetterCaseSensitive && !first)) {
192
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(')');
200         } else {
201             if (c.isUpper() && !first)
202                 keyRegExp += wordContinuation;
203             keyRegExp += QRegExp::escape(c);
204         }
205
206         first = false;
207     }
208     const QRegExp regExp(keyRegExp);
209
210     const bool hasKey = !key.isEmpty();
211     foreach (TextEditor::CompletionItem item, items) {
212         if (regExp.indexIn(item.text) == 0) {
213             if (hasKey) {
214                 if (item.text.startsWith(key, Qt::CaseSensitive)) {
215                     item.relevance = 2;
216                 } else if (caseSensitivity != TextEditor::CaseSensitive
217                            && item.text.startsWith(key, Qt::CaseInsensitive)) {
218                     item.relevance = 1;
219                 }
220             }
221             filteredItems->append(item);
222         }
223     }
224 }
225
226 bool ICompletionCollector::shouldRestartCompletion()
227 {
228     return true;
229 }