Skip to content

Commit

Permalink
Auto-completion minor code refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
zufuliu committed Dec 7, 2024
1 parent 1c2360f commit 9082a2e
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 123 deletions.
114 changes: 47 additions & 67 deletions scintilla/src/AutoComplete.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,7 @@ using namespace Scintilla;
using namespace Scintilla::Internal;

AutoComplete::AutoComplete() :
active(false),
separator('\n'),
typesep('\t'),
ignoreCase(false),
chooseSingle(false),
options(AutoCompleteOption::Normal),
posStart(0),
startLen(0),
cancelAtStartPos(true),
autoHide(true),
dropRestOfWord(false),
ignoreCaseBehaviour(CaseInsensitiveBehaviour::RespectCase),
widthLBDefault(100),
heightLBDefault(100),
autoSort(Ordering::PreSorted) {
lb = ListBox::Allocate();
lb{ListBox::Allocate()} {
}

AutoComplete::~AutoComplete() {
Expand Down Expand Up @@ -88,30 +73,14 @@ bool AutoComplete::IsFillUpChar(char ch) const noexcept {
return ch && (fillUpChars.find(ch) != std::string::npos);
}

void AutoComplete::SetSeparator(char separator_) noexcept {
separator = separator_;
}

char AutoComplete::GetSeparator() const noexcept {
return separator;
}

void AutoComplete::SetTypesep(char separator_) noexcept {
typesep = separator_;
}

char AutoComplete::GetTypesep() const noexcept {
return typesep;
}

namespace {

struct Sorter {
const AutoComplete *ac;
const bool ignoreCase;
const char *list;
std::vector<int> indices;

Sorter(const AutoComplete *ac_, const char *list_) : ac(ac_), list(list_) {
Sorter(const AutoComplete *ac, const char *list_) : ignoreCase{ac->ignoreCase}, list(list_) {
int i = 0;
if (!list[i]) {
// Empty list has a single empty member
Expand Down Expand Up @@ -144,69 +113,80 @@ struct Sorter {
}

bool operator()(int a, int b) const noexcept {
const int lenA = indices[a * 2 + 1] - indices[a * 2];
const int lenB = indices[b * 2 + 1] - indices[b * 2];
const unsigned indexA = a * 2;
const unsigned indexB = b * 2;
const int lenA = indices[indexA + 1] - indices[indexA];
const int lenB = indices[indexB + 1] - indices[indexB];
const int len = std::min(lenA, lenB);
int cmp;
if (ac->ignoreCase)
cmp = CompareNCaseInsensitive(list + indices[a * 2], list + indices[b * 2], len);
if (ignoreCase)
cmp = CompareNCaseInsensitive(list + indices[indexA], list + indices[indexB], len);
else
cmp = strncmp(list + indices[a * 2], list + indices[b * 2], len);
cmp = strncmp(list + indices[indexA], list + indices[indexB], len);
if (cmp == 0)
cmp = lenA - lenB;
return cmp < 0;
}
};

void FillSortMatrix(std::vector<int> &sortMatrix, unsigned itemCount) {
#if 1
sortMatrix.clear();
for (unsigned i = 0; i < itemCount; i++) {
sortMatrix.push_back(i);
}
#else
sortMatrix.resize(itemCount);
itemCount = 0;
for (auto &it: sortMatrix) {
it = itemCount++;
}
#endif
}

}

void AutoComplete::SetList(const char *list) {
if (autoSort == Ordering::PreSorted) {
lb->SetList(list, separator, typesep);
sortMatrix.resize(lb->Length());
for (int i = 0; i < static_cast<int>(sortMatrix.size()); ++i) {
sortMatrix[i] = i;
}
FillSortMatrix(sortMatrix, lb->Length());
return;
}

Sorter IndexSort(this, list);
sortMatrix.resize(IndexSort.indices.size() / 2);
for (int i = 0; i < static_cast<int>(sortMatrix.size()); ++i) {
sortMatrix[i] = i;
}
const Sorter IndexSort(this, list);
FillSortMatrix(sortMatrix, static_cast<unsigned>(IndexSort.indices.size() / 2));
std::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort);
if (autoSort == Ordering::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];
if (wordLen > maxItemLen - 2)
wordLen = maxItemLen - 2;
memcpy(item, list + IndexSort.indices[sortMatrix[i] * 2], wordLen);
if ((i + 1) == sortMatrix.size()) {
const size_t itemCount = sortMatrix.size();
const std::unique_ptr<char[]> sortedList(new char[itemCount + IndexSort.indices.back() + 1]);
char *back = sortedList.get();
for (size_t i = 0; i < itemCount; ++i) {
const unsigned index = sortMatrix[i] * 2;
sortMatrix[i] = static_cast<int>(i);
// word length include trailing typesep and separator
const unsigned wordLen = IndexSort.indices[index + 2] - IndexSort.indices[index];
const char * const item = list + IndexSort.indices[index];
memcpy(back, item, wordLen);
back += wordLen;
if ((i + 1) == itemCount) {
// Last item so remove separator if present
if ((wordLen > 0) && (item[wordLen - 1] == separator))
wordLen--;
if (wordLen != 0 && item[wordLen - 1] == separator) {
--back;
}
} else {
// Item before last needs a separator
if ((wordLen == 0) || (item[wordLen - 1] != separator)) {
item[wordLen] = separator;
wordLen++;
if (wordLen == 0 || item[wordLen - 1] != separator) {
*back++ = separator;
}
}
item[wordLen] = '\0';
sortedList += item;
}
for (int i = 0; i < static_cast<int>(sortMatrix.size()); ++i) {
sortMatrix[i] = i;
}
lb->SetList(sortedList.c_str(), separator, typesep);
*back = '\0';
lb->SetList(sortedList.get(), separator, typesep);
}

int AutoComplete::GetSelection() const noexcept {
Expand Down
51 changes: 28 additions & 23 deletions scintilla/src/AutoComplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,33 @@ namespace Scintilla::Internal {
/**
*/
class AutoComplete {
bool active;
char separator;
char typesep; // Type separator
bool active = false;
char separator = '\n';
char typesep = '\t'; // Type separator
std::string stopChars;
std::string fillUpChars;
enum {
maxItemLen = 128
};
std::vector<int> sortMatrix;

public:

bool ignoreCase;
bool chooseSingle;
AutoCompleteOption options;
std::unique_ptr<ListBox> lb;
Sci::Position posStart;
Sci::Position startLen;
bool ignoreCase = false;
bool chooseSingle = false;
AutoCompleteOption options = Scintilla::AutoCompleteOption::Normal;
const std::unique_ptr<ListBox> lb;
Sci::Position posStart = 0;
Sci::Position startLen = 0;
/// Should autocompletion be cancelled if editor's currentPos <= startPos?
bool cancelAtStartPos;
bool autoHide;
bool dropRestOfWord;
Scintilla::CaseInsensitiveBehaviour ignoreCaseBehaviour;
int widthLBDefault;
int heightLBDefault;
bool cancelAtStartPos = true;
bool autoHide = true;
bool dropRestOfWord = false;
Scintilla::CaseInsensitiveBehaviour ignoreCaseBehaviour = Scintilla::CaseInsensitiveBehaviour::RespectCase;
int widthLBDefault = 100;
int heightLBDefault = 100;
/** Ordering::PreSorted: Assume the list is presorted; selection will fail if it is not alphabetical<br />
* Ordering::PerformSort: Sort the list alphabetically; start up performance cost for sorting<br />
* Ordering::Custom: Handle non-alphabetical entries; start up performance cost for generating a sorted lookup table
*/
Scintilla::Ordering autoSort;
Scintilla::Ordering autoSort = Scintilla::Ordering::PreSorted;

AutoComplete();
// Deleted so AutoComplete objects can not be copied.
Expand Down Expand Up @@ -69,12 +66,20 @@ class AutoComplete {
bool IsFillUpChar(char ch) const noexcept;

/// The separator character is used when interpreting the list in SetList
void SetSeparator(char separator_) noexcept;
char GetSeparator() const noexcept;
void SetSeparator(char separator_) noexcept {
separator = separator_;
}
char GetSeparator() const noexcept {
return separator;
}

/// The typesep character is used for separating the word from the type
void SetTypesep(char separator_) noexcept;
char GetTypesep() const noexcept;
void SetTypesep(char separator_) noexcept {
typesep = separator_;
}
char GetTypesep() const noexcept {
return typesep;
}

/// The list string contains a sequence of words separated by the separator character
void SetList(const char *list);
Expand Down
67 changes: 34 additions & 33 deletions src/EditAutoC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,37 @@ struct IUnknown;

#define NP2_AUTOC_CACHE_SORT_KEY 1
#define NP2_AUTOC_USE_WORD_POINTER 0 // used for debug
// scintilla/src/AutoComplete.h AutoComplete::maxItemLen
#define NP2_AUTOC_MAX_WORD_LENGTH (128 - 3 - 1) // SP + '(' + ')' + '\0'
#define NP2_AUTOC_WORD_BUFFER_SIZE 128
#define NP2_AUTOC_INIT_BUFFER_SIZE (4096)

// optimization for small string
template <size_t StackSize = 32>
class CharBuffer {
char *ptr;
char buffer[StackSize];
public:
explicit CharBuffer(size_t size) noexcept {
if (size <= StackSize) {
ptr = buffer;
memset(buffer, '\0', StackSize);
} else {
ptr = static_cast<char *>(NP2HeapAlloc(size));
}
}
char *data() noexcept {
return ptr;
}
char& operator[](size_t index) noexcept {
return ptr[index];
}
~CharBuffer() {
if (ptr != buffer) {
NP2HeapFree(ptr);
}
}
};

// memory buffer
struct WordListBuffer {
WordListBuffer *next;
Expand Down Expand Up @@ -798,26 +824,17 @@ static void AutoC_AddDocWord(WordList &pWList, const uint32_t (&ignoredStyleMask
LPCSTR const pRoot = pWList.pWordStart;
const int iRootLen = pWList.iStartLen;

// optimization for small string
char onStack[64];
char *pFind;
if (iRootLen + 2 <= static_cast<int>(sizeof(onStack))) {
memset(onStack, 0, sizeof(onStack));
pFind = onStack;
} else {
pFind = static_cast<char *>(NP2HeapAlloc(iRootLen + 2));
}

CharBuffer pFind(iRootLen + 2);
pFind[0] = prefix;
memcpy(pFind + (prefix != '\0'), pRoot, iRootLen);
memcpy(pFind.data() + (prefix != '\0'), pRoot, iRootLen);
int findFlag = (bIgnoreCase ? SCFIND_NONE : SCFIND_MATCHCASE) | SCFIND_MATCH_TO_WORD_END;
if (IsDefaultWordChar(static_cast<uint8_t>(pRoot[0]))) {
findFlag |= SCFIND_WORDSTART;
}

const Sci_Position iCurrentPos = SciCall_GetCurrentPos() - iRootLen - (prefix ? 1 : 0);
const Sci_Position iDocLen = SciCall_GetLength();
Sci_TextToFindFull ft = { { 0, iDocLen }, pFind, { 0, 0 } };
Sci_TextToFindFull ft = { { 0, iDocLen }, pFind.data(), { 0, 0 } };

Sci_Position iPosFind = SciCall_FindTextFull(findFlag, &ft);
HANDLE timer = idleTaskTimer;
Expand Down Expand Up @@ -947,10 +964,6 @@ static void AutoC_AddDocWord(WordList &pWList, const uint32_t (&ignoredStyleMask
ft.chrg.cpMin = wordEnd;
iPosFind = SciCall_FindTextFull(findFlag, &ft);
}

if (pFind != onStack) {
NP2HeapFree(pFind);
}
}

static void AutoC_AddKeyword(WordList &pWList, int iCurrentStyle) noexcept {
Expand Down Expand Up @@ -1584,19 +1597,10 @@ static bool EditCompleteWordCore(int iCondition, bool autoInsert) noexcept {
chPrev = chPrev2;
}

// optimization for small string
char onStack[64];
char *pRoot;
if (iCurrentPos - iStartWordPos + 1 < static_cast<Sci_Position>(sizeof(onStack))) {
memset(onStack, 0, sizeof(onStack));
pRoot = onStack;
} else {
pRoot = static_cast<char *>(NP2HeapAlloc(iCurrentPos - iStartWordPos + 1));
}

const Sci_TextRangeFull tr = { { iStartWordPos, iCurrentPos }, pRoot };
CharBuffer pRoot(iCurrentPos - iStartWordPos + 1);
const Sci_TextRangeFull tr = { { iStartWordPos, iCurrentPos }, pRoot.data() };
SciCall_GetTextRangeFull(&tr);
iRootLen = static_cast<int>(strlen(pRoot));
iRootLen = static_cast<int>(strlen(pRoot.data()));

#if 0
StopWatch watch;
Expand All @@ -1607,7 +1611,7 @@ static bool EditCompleteWordCore(int iCondition, bool autoInsert) noexcept {
|| (pRoot[0] >= '0' && pRoot[0] <= '9'); // number
const bool bIgnoreCase = bIgnoreLexer || autoCompletionConfig.bIgnoreCase;
WordList pWList;
pWList.Init(pRoot, iRootLen, bIgnoreCase);
pWList.Init(pRoot.data(), iRootLen, bIgnoreCase);
bool bIgnoreDoc = false;
char prefix = '\0';

Expand Down Expand Up @@ -1738,9 +1742,6 @@ static bool EditCompleteWordCore(int iCondition, bool autoInsert) noexcept {
NP2HeapFree(pList);
}

if (pRoot != onStack) {
NP2HeapFree(pRoot);
}
pWList.Free();
return bShow;
}
Expand Down

0 comments on commit 9082a2e

Please sign in to comment.