diff --git a/scintilla/src/AutoComplete.cxx b/scintilla/src/AutoComplete.cxx index c2b99d5f8a..fb12b5668e 100644 --- a/scintilla/src/AutoComplete.cxx +++ b/scintilla/src/AutoComplete.cxx @@ -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() { @@ -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 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 @@ -144,37 +113,48 @@ 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 &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(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(sortMatrix.size()); ++i) { - sortMatrix[i] = i; - } + const Sorter IndexSort(this, list); + FillSortMatrix(sortMatrix, static_cast(IndexSort.indices.size() / 2)); std::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort); if (autoSort == Ordering::Custom || sortMatrix.size() < 2) { lb->SetList(list, separator, typesep); @@ -182,31 +162,31 @@ void AutoComplete::SetList(const char *list) { 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 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(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(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 { diff --git a/scintilla/src/AutoComplete.h b/scintilla/src/AutoComplete.h index b83691bd5a..bb005dfe57 100644 --- a/scintilla/src/AutoComplete.h +++ b/scintilla/src/AutoComplete.h @@ -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 sortMatrix; public: - bool ignoreCase; - bool chooseSingle; - AutoCompleteOption options; - std::unique_ptr lb; - Sci::Position posStart; - Sci::Position startLen; + bool ignoreCase = false; + bool chooseSingle = false; + AutoCompleteOption options = Scintilla::AutoCompleteOption::Normal; + const std::unique_ptr 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
* Ordering::PerformSort: Sort the list alphabetically; start up performance cost for sorting
* 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. @@ -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); diff --git a/src/EditAutoC.cpp b/src/EditAutoC.cpp index 99ca302dc2..e71cd21928 100644 --- a/src/EditAutoC.cpp +++ b/src/EditAutoC.cpp @@ -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 +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(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; @@ -798,18 +824,9 @@ 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(sizeof(onStack))) { - memset(onStack, 0, sizeof(onStack)); - pFind = onStack; - } else { - pFind = static_cast(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(pRoot[0]))) { findFlag |= SCFIND_WORDSTART; @@ -817,7 +834,7 @@ static void AutoC_AddDocWord(WordList &pWList, const uint32_t (&ignoredStyleMask 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; @@ -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 { @@ -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(sizeof(onStack))) { - memset(onStack, 0, sizeof(onStack)); - pRoot = onStack; - } else { - pRoot = static_cast(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(strlen(pRoot)); + iRootLen = static_cast(strlen(pRoot.data())); #if 0 StopWatch watch; @@ -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'; @@ -1738,9 +1742,6 @@ static bool EditCompleteWordCore(int iCondition, bool autoInsert) noexcept { NP2HeapFree(pList); } - if (pRoot != onStack) { - NP2HeapFree(pRoot); - } pWList.Free(); return bShow; }