Fix typos
[TortoiseGit.git] / ext / scintilla / src / AutoComplete.cxx
blob397480f3971de28cd0d1d467b7169d822a3c7d92
1 // Scintilla source code edit control
2 /** @file AutoComplete.cxx
3 ** Defines the auto completion list box.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cassert>
11 #include <cstring>
12 #include <cstdio>
14 #include <stdexcept>
15 #include <string>
16 #include <string_view>
17 #include <vector>
18 #include <optional>
19 #include <algorithm>
20 #include <memory>
22 #include "ScintillaTypes.h"
23 #include "ScintillaMessages.h"
25 #include "Debugging.h"
26 #include "Geometry.h"
27 #include "Platform.h"
29 #include "CharacterType.h"
30 #include "Position.h"
31 #include "AutoComplete.h"
33 using namespace Scintilla;
34 using namespace Scintilla::Internal;
36 AutoComplete::AutoComplete() :
37 active(false),
38 separator(' '),
39 typesep('?'),
40 ignoreCase(false),
41 chooseSingle(false),
42 options(AutoCompleteOption::Normal),
43 posStart(0),
44 startLen(0),
45 cancelAtStartPos(true),
46 autoHide(true),
47 dropRestOfWord(false),
48 ignoreCaseBehaviour(CaseInsensitiveBehaviour::RespectCase),
49 widthLBDefault(100),
50 heightLBDefault(100),
51 autoSort(Ordering::PreSorted) {
52 lb = ListBox::Allocate();
55 AutoComplete::~AutoComplete() {
56 if (lb) {
57 lb->Destroy();
61 bool AutoComplete::Active() const noexcept {
62 return active;
65 void AutoComplete::Start(Window &parent, int ctrlID,
66 Sci::Position position, Point location, Sci::Position startLen_,
67 int lineHeight, bool unicodeMode, Technology technology, ListOptions listOptions) {
68 if (active) {
69 Cancel();
71 lb->SetOptions(listOptions);
72 lb->Create(parent, ctrlID, location, lineHeight, unicodeMode, technology);
73 lb->Clear();
74 active = true;
75 startLen = startLen_;
76 posStart = position;
79 void AutoComplete::SetStopChars(const char *stopChars_) {
80 stopChars = stopChars_;
83 bool AutoComplete::IsStopChar(char ch) const noexcept {
84 return ch && (stopChars.find(ch) != std::string::npos);
87 void AutoComplete::SetFillUpChars(const char *fillUpChars_) {
88 fillUpChars = fillUpChars_;
91 bool AutoComplete::IsFillUpChar(char ch) const noexcept {
92 return ch && (fillUpChars.find(ch) != std::string::npos);
95 void AutoComplete::SetSeparator(char separator_) {
96 separator = separator_;
99 char AutoComplete::GetSeparator() const noexcept {
100 return separator;
103 void AutoComplete::SetTypesep(char separator_) {
104 typesep = separator_;
107 char AutoComplete::GetTypesep() const noexcept {
108 return typesep;
111 struct Sorter {
112 AutoComplete *ac;
113 const char *list;
114 std::vector<int> indices;
116 Sorter(AutoComplete *ac_, const char *list_) : ac(ac_), list(list_) {
117 int i = 0;
118 if (!list[i]) {
119 // Empty list has a single empty member
120 indices.push_back(i); // word start
121 indices.push_back(i); // word end
123 while (list[i]) {
124 indices.push_back(i); // word start
125 while (list[i] != ac->GetTypesep() && list[i] != ac->GetSeparator() && list[i])
126 ++i;
127 indices.push_back(i); // word end
128 if (list[i] == ac->GetTypesep()) {
129 while (list[i] != ac->GetSeparator() && list[i])
130 ++i;
132 if (list[i] == ac->GetSeparator()) {
133 ++i;
134 // preserve trailing separator as blank entry
135 if (!list[i]) {
136 indices.push_back(i);
137 indices.push_back(i);
141 indices.push_back(i); // index of last position
144 bool operator()(int a, int b) noexcept {
145 const int lenA = indices[a * 2 + 1] - indices[a * 2];
146 const int lenB = indices[b * 2 + 1] - indices[b * 2];
147 const int len = std::min(lenA, lenB);
148 int cmp;
149 if (ac->ignoreCase)
150 cmp = CompareNCaseInsensitive(list + indices[a * 2], list + indices[b * 2], len);
151 else
152 cmp = strncmp(list + indices[a * 2], list + indices[b * 2], len);
153 if (cmp == 0)
154 cmp = lenA - lenB;
155 return cmp < 0;
159 void AutoComplete::SetList(const char *list) {
160 if (autoSort == Ordering::PreSorted) {
161 lb->SetList(list, separator, typesep);
162 sortMatrix.clear();
163 for (int i = 0; i < lb->Length(); ++i)
164 sortMatrix.push_back(i);
165 return;
168 Sorter IndexSort(this, list);
169 sortMatrix.clear();
170 for (int i = 0; i < static_cast<int>(IndexSort.indices.size()) / 2; ++i)
171 sortMatrix.push_back(i);
172 std::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort);
173 if (autoSort == Ordering::Custom || sortMatrix.size() < 2) {
174 lb->SetList(list, separator, typesep);
175 PLATFORM_ASSERT(lb->Length() == static_cast<int>(sortMatrix.size()));
176 return;
179 std::string sortedList;
180 char item[maxItemLen];
181 for (size_t i = 0; i < sortMatrix.size(); ++i) {
182 int wordLen = IndexSort.indices[sortMatrix[i] * 2 + 2] - IndexSort.indices[sortMatrix[i] * 2];
183 if (wordLen > maxItemLen-2)
184 wordLen = maxItemLen - 2;
185 memcpy(item, list + IndexSort.indices[sortMatrix[i] * 2], wordLen);
186 if ((i+1) == sortMatrix.size()) {
187 // Last item so remove separator if present
188 if ((wordLen > 0) && (item[wordLen-1] == separator))
189 wordLen--;
190 } else {
191 // Item before last needs a separator
192 if ((wordLen == 0) || (item[wordLen-1] != separator)) {
193 item[wordLen] = separator;
194 wordLen++;
197 item[wordLen] = '\0';
198 sortedList += item;
200 for (int i = 0; i < static_cast<int>(sortMatrix.size()); ++i)
201 sortMatrix[i] = i;
202 lb->SetList(sortedList.c_str(), separator, typesep);
205 int AutoComplete::GetSelection() const {
206 return lb->GetSelection();
209 std::string AutoComplete::GetValue(int item) const {
210 return lb->GetValue(item);
213 void AutoComplete::Show(bool show) {
214 lb->Show(show);
215 if (show)
216 lb->Select(0);
219 void AutoComplete::Cancel() noexcept {
220 if (lb->Created()) {
221 lb->Clear();
222 lb->Destroy();
223 active = false;
228 void AutoComplete::Move(int delta) {
229 const int count = lb->Length();
230 int current = lb->GetSelection();
231 current += delta;
232 if (current >= count)
233 current = count - 1;
234 if (current < 0)
235 current = 0;
236 lb->Select(current);
239 void AutoComplete::Select(const char *word) {
240 const size_t lenWord = strlen(word);
241 int location = -1;
242 int start = 0; // lower bound of the api array block to search
243 int end = lb->Length() - 1; // upper bound of the api array block to search
244 while ((start <= end) && (location == -1)) { // Binary searching loop
245 int pivot = (start + end) / 2;
246 std::string item = GetValue(sortMatrix[pivot]);
247 int cond;
248 if (ignoreCase)
249 cond = CompareNCaseInsensitive(word, item.c_str(), lenWord);
250 else
251 cond = strncmp(word, item.c_str(), lenWord);
252 if (!cond) {
253 // Find first match
254 while (pivot > start) {
255 item = lb->GetValue(sortMatrix[pivot-1]);
256 if (ignoreCase)
257 cond = CompareNCaseInsensitive(word, item.c_str(), lenWord);
258 else
259 cond = strncmp(word, item.c_str(), lenWord);
260 if (0 != cond)
261 break;
262 --pivot;
264 location = pivot;
265 if (ignoreCase
266 && ignoreCaseBehaviour == CaseInsensitiveBehaviour::RespectCase) {
267 // Check for exact-case match
268 for (; pivot <= end; pivot++) {
269 item = lb->GetValue(sortMatrix[pivot]);
270 if (!strncmp(word, item.c_str(), lenWord)) {
271 location = pivot;
272 break;
274 if (CompareNCaseInsensitive(word, item.c_str(), lenWord))
275 break;
278 } else if (cond < 0) {
279 end = pivot - 1;
280 } else { // cond > 0
281 start = pivot + 1;
284 if (location == -1) {
285 if (autoHide)
286 Cancel();
287 else
288 lb->Select(-1);
289 } else {
290 if (autoSort == Ordering::Custom) {
291 // Check for a logically earlier match
292 for (int i = location + 1; i <= end; ++i) {
293 std::string item = lb->GetValue(sortMatrix[i]);
294 if (CompareNCaseInsensitive(word, item.c_str(), lenWord))
295 break;
296 if (sortMatrix[i] < sortMatrix[location] && !strncmp(word, item.c_str(), lenWord))
297 location = i;
300 lb->Select(sortMatrix[location]);