2009-04-24 23:35:41 +00:00
|
|
|
// Scintilla source code edit control
|
|
|
|
/** @file AutoComplete.cxx
|
|
|
|
** Defines the auto completion list box.
|
|
|
|
**/
|
|
|
|
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
|
|
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2010-08-21 23:59:56 +00:00
|
|
|
#include <assert.h>
|
2009-04-24 23:35:41 +00:00
|
|
|
|
2013-08-28 00:44:27 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2009-04-24 23:35:41 +00:00
|
|
|
#include "Platform.h"
|
|
|
|
|
2010-08-21 23:59:56 +00:00
|
|
|
#include "CharacterSet.h"
|
2009-04-24 23:35:41 +00:00
|
|
|
#include "AutoComplete.h"
|
2013-08-28 00:44:27 +00:00
|
|
|
#include "Scintilla.h"
|
2009-04-24 23:35:41 +00:00
|
|
|
|
|
|
|
#ifdef SCI_NAMESPACE
|
|
|
|
using namespace Scintilla;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
AutoComplete::AutoComplete() :
|
|
|
|
active(false),
|
|
|
|
separator(' '),
|
|
|
|
typesep('?'),
|
|
|
|
ignoreCase(false),
|
|
|
|
chooseSingle(false),
|
|
|
|
lb(0),
|
|
|
|
posStart(0),
|
|
|
|
startLen(0),
|
|
|
|
cancelAtStartPos(true),
|
|
|
|
autoHide(true),
|
2013-08-28 00:44:27 +00:00
|
|
|
dropRestOfWord(false),
|
|
|
|
ignoreCaseBehaviour(SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE),
|
|
|
|
widthLBDefault(100),
|
|
|
|
heightLBDefault(100),
|
|
|
|
autoSort(SC_ORDER_PRESORTED) {
|
2009-04-24 23:35:41 +00:00
|
|
|
lb = ListBox::Allocate();
|
|
|
|
stopChars[0] = '\0';
|
|
|
|
fillUpChars[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoComplete::~AutoComplete() {
|
|
|
|
if (lb) {
|
|
|
|
lb->Destroy();
|
|
|
|
delete lb;
|
|
|
|
lb = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
bool AutoComplete::Active() const {
|
2009-04-24 23:35:41 +00:00
|
|
|
return active;
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
void AutoComplete::Start(Window &parent, int ctrlID,
|
|
|
|
int position, Point location, int startLen_,
|
2013-08-28 00:44:27 +00:00
|
|
|
int lineHeight, bool unicodeMode, int technology) {
|
2009-04-24 23:35:41 +00:00
|
|
|
if (active) {
|
|
|
|
Cancel();
|
|
|
|
}
|
2013-08-28 00:44:27 +00:00
|
|
|
lb->Create(parent, ctrlID, location, lineHeight, unicodeMode, technology);
|
2009-04-24 23:35:41 +00:00
|
|
|
lb->Clear();
|
|
|
|
active = true;
|
|
|
|
startLen = startLen_;
|
|
|
|
posStart = position;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::SetStopChars(const char *stopChars_) {
|
|
|
|
strncpy(stopChars, stopChars_, sizeof(stopChars));
|
|
|
|
stopChars[sizeof(stopChars) - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AutoComplete::IsStopChar(char ch) {
|
|
|
|
return ch && strchr(stopChars, ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::SetFillUpChars(const char *fillUpChars_) {
|
|
|
|
strncpy(fillUpChars, fillUpChars_, sizeof(fillUpChars));
|
|
|
|
fillUpChars[sizeof(fillUpChars) - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AutoComplete::IsFillUpChar(char ch) {
|
|
|
|
return ch && strchr(fillUpChars, ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::SetSeparator(char separator_) {
|
|
|
|
separator = separator_;
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
char AutoComplete::GetSeparator() const {
|
2009-04-24 23:35:41 +00:00
|
|
|
return separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::SetTypesep(char separator_) {
|
|
|
|
typesep = separator_;
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
char AutoComplete::GetTypesep() const {
|
2009-04-24 23:35:41 +00:00
|
|
|
return typesep;
|
|
|
|
}
|
|
|
|
|
2013-08-28 00:44:27 +00:00
|
|
|
struct Sorter {
|
|
|
|
AutoComplete *ac;
|
|
|
|
const char *list;
|
|
|
|
std::vector<int> indices;
|
|
|
|
|
|
|
|
Sorter(AutoComplete *ac_, const char *list_) : ac(ac_), list(list_) {
|
|
|
|
int i = 0;
|
|
|
|
while (list[i]) {
|
|
|
|
indices.push_back(i); // word start
|
|
|
|
while (list[i] != ac->GetTypesep() && list[i] != ac->GetSeparator() && list[i])
|
|
|
|
++i;
|
|
|
|
indices.push_back(i); // word end
|
|
|
|
if (list[i] == ac->GetTypesep()) {
|
|
|
|
while (list[i] != ac->GetSeparator() && list[i])
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (list[i] == ac->GetSeparator()) {
|
|
|
|
++i;
|
|
|
|
// preserve trailing separator as blank entry
|
|
|
|
if (!list[i]) {
|
|
|
|
indices.push_back(i);
|
|
|
|
indices.push_back(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
indices.push_back(i); // index of last position
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator()(int a, int b) {
|
|
|
|
int lenA = indices[a * 2 + 1] - indices[a * 2];
|
|
|
|
int lenB = indices[b * 2 + 1] - indices[b * 2];
|
|
|
|
int len = std::min(lenA, lenB);
|
|
|
|
int cmp;
|
|
|
|
if (ac->ignoreCase)
|
|
|
|
cmp = CompareNCaseInsensitive(list + indices[a * 2], list + indices[b * 2], len);
|
|
|
|
else
|
|
|
|
cmp = strncmp(list + indices[a * 2], list + indices[b * 2], len);
|
|
|
|
if (cmp == 0)
|
|
|
|
cmp = lenA - lenB;
|
|
|
|
return cmp < 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-04-24 23:35:41 +00:00
|
|
|
void AutoComplete::SetList(const char *list) {
|
2013-08-28 00:44:27 +00:00
|
|
|
if (autoSort == SC_ORDER_PRESORTED) {
|
|
|
|
lb->SetList(list, separator, typesep);
|
|
|
|
sortMatrix.clear();
|
|
|
|
for (int i = 0; i < lb->Length(); ++i)
|
|
|
|
sortMatrix.push_back(i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Sorter IndexSort(this, list);
|
|
|
|
sortMatrix.clear();
|
|
|
|
for (int i = 0; i < (int)IndexSort.indices.size() / 2; ++i)
|
|
|
|
sortMatrix.push_back(i);
|
|
|
|
std::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort);
|
|
|
|
if (autoSort == SC_ORDER_CUSTOM || sortMatrix.size() < 2) {
|
|
|
|
lb->SetList(list, separator, typesep);
|
|
|
|
PLATFORM_ASSERT(lb->Length() == static_cast<int>(sortMatrix.size()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string sortedList;
|
|
|
|
char item[maxItemLen];
|
|
|
|
for (size_t i = 0; i < sortMatrix.size(); ++i) {
|
|
|
|
int wordLen = IndexSort.indices[sortMatrix[i] * 2 + 2] - IndexSort.indices[sortMatrix[i] * 2];
|
|
|
|
strncpy(item, list + IndexSort.indices[sortMatrix[i] * 2], wordLen);
|
|
|
|
if ((i+1) == sortMatrix.size()) {
|
|
|
|
// Last item so remove separator if present
|
|
|
|
if ((wordLen > 0) && (item[wordLen-1] == separator))
|
|
|
|
wordLen--;
|
|
|
|
} else {
|
|
|
|
// Item before last needs a separator
|
|
|
|
if ((wordLen == 0) || (item[wordLen-1] != separator)) {
|
|
|
|
item[wordLen] = separator;
|
|
|
|
wordLen++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
item[wordLen] = '\0';
|
|
|
|
sortedList += item;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < (int)sortMatrix.size(); ++i)
|
|
|
|
sortMatrix[i] = i;
|
|
|
|
lb->SetList(sortedList.c_str(), separator, typesep);
|
|
|
|
}
|
|
|
|
|
|
|
|
int AutoComplete::GetSelection() const {
|
|
|
|
return lb->GetSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AutoComplete::GetValue(int item) const {
|
|
|
|
char value[maxItemLen];
|
|
|
|
lb->GetValue(item, value, sizeof(value));
|
|
|
|
return std::string(value);
|
2009-04-24 23:35:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::Show(bool show) {
|
|
|
|
lb->Show(show);
|
|
|
|
if (show)
|
|
|
|
lb->Select(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::Cancel() {
|
|
|
|
if (lb->Created()) {
|
|
|
|
lb->Clear();
|
|
|
|
lb->Destroy();
|
|
|
|
active = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AutoComplete::Move(int delta) {
|
|
|
|
int count = lb->Length();
|
|
|
|
int current = lb->GetSelection();
|
|
|
|
current += delta;
|
|
|
|
if (current >= count)
|
|
|
|
current = count - 1;
|
|
|
|
if (current < 0)
|
|
|
|
current = 0;
|
|
|
|
lb->Select(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AutoComplete::Select(const char *word) {
|
|
|
|
size_t lenWord = strlen(word);
|
|
|
|
int location = -1;
|
|
|
|
int start = 0; // lower bound of the api array block to search
|
|
|
|
int end = lb->Length() - 1; // upper bound of the api array block to search
|
|
|
|
while ((start <= end) && (location == -1)) { // Binary searching loop
|
|
|
|
int pivot = (start + end) / 2;
|
2011-03-22 00:16:49 +00:00
|
|
|
char item[maxItemLen];
|
2013-08-28 00:44:27 +00:00
|
|
|
lb->GetValue(sortMatrix[pivot], item, maxItemLen);
|
2009-04-24 23:35:41 +00:00
|
|
|
int cond;
|
|
|
|
if (ignoreCase)
|
|
|
|
cond = CompareNCaseInsensitive(word, item, lenWord);
|
|
|
|
else
|
|
|
|
cond = strncmp(word, item, lenWord);
|
|
|
|
if (!cond) {
|
|
|
|
// Find first match
|
|
|
|
while (pivot > start) {
|
2013-08-28 00:44:27 +00:00
|
|
|
lb->GetValue(sortMatrix[pivot-1], item, maxItemLen);
|
2009-04-24 23:35:41 +00:00
|
|
|
if (ignoreCase)
|
|
|
|
cond = CompareNCaseInsensitive(word, item, lenWord);
|
|
|
|
else
|
|
|
|
cond = strncmp(word, item, lenWord);
|
|
|
|
if (0 != cond)
|
|
|
|
break;
|
|
|
|
--pivot;
|
|
|
|
}
|
|
|
|
location = pivot;
|
2013-08-28 00:44:27 +00:00
|
|
|
if (ignoreCase
|
|
|
|
&& ignoreCaseBehaviour == SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE) {
|
2009-04-24 23:35:41 +00:00
|
|
|
// Check for exact-case match
|
|
|
|
for (; pivot <= end; pivot++) {
|
2013-08-28 00:44:27 +00:00
|
|
|
lb->GetValue(sortMatrix[pivot], item, maxItemLen);
|
2009-04-24 23:35:41 +00:00
|
|
|
if (!strncmp(word, item, lenWord)) {
|
|
|
|
location = pivot;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (CompareNCaseInsensitive(word, item, lenWord))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (cond < 0) {
|
|
|
|
end = pivot - 1;
|
|
|
|
} else if (cond > 0) {
|
|
|
|
start = pivot + 1;
|
|
|
|
}
|
|
|
|
}
|
2013-08-28 00:44:27 +00:00
|
|
|
if (location == -1) {
|
|
|
|
if (autoHide)
|
|
|
|
Cancel();
|
|
|
|
else
|
|
|
|
lb->Select(-1);
|
|
|
|
} else {
|
|
|
|
if (autoSort == SC_ORDER_CUSTOM) {
|
|
|
|
// Check for a logically earlier match
|
|
|
|
char item[maxItemLen];
|
|
|
|
for (int i = location + 1; i <= end; ++i) {
|
|
|
|
lb->GetValue(sortMatrix[i], item, maxItemLen);
|
|
|
|
if (CompareNCaseInsensitive(word, item, lenWord))
|
|
|
|
break;
|
|
|
|
if (sortMatrix[i] < sortMatrix[location] && !strncmp(word, item, lenWord))
|
|
|
|
location = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lb->Select(sortMatrix[location]);
|
|
|
|
}
|
2009-04-24 23:35:41 +00:00
|
|
|
}
|
|
|
|
|