From 1f4a1fb2e727a2dbedf1999818efca2734edc04d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20J=C3=B6nsson?= Date: Fri, 8 May 2015 23:27:21 +0200 Subject: [PATCH 1/2] Optimize sort. Remove custom, recursive implementation of quicksort which becomes too slow to use after a couple of hundred lines. --- PowerEditor/src/MISC/Common/Common.cpp | 31 ++ PowerEditor/src/MISC/Common/Common.h | 2 + PowerEditor/src/Notepad_plus.rc | 4 +- PowerEditor/src/NppCommands.cpp | 10 +- PowerEditor/src/Parameters.cpp | 4 +- .../ScitillaComponent/ScintillaEditView.cpp | 271 +++--------------- .../src/ScitillaComponent/ScintillaEditView.h | 11 +- PowerEditor/src/menuCmdID.h | 4 +- 8 files changed, 91 insertions(+), 246 deletions(-) diff --git a/PowerEditor/src/MISC/Common/Common.cpp b/PowerEditor/src/MISC/Common/Common.cpp index 11db3f3b..9578cf72 100644 --- a/PowerEditor/src/MISC/Common/Common.cpp +++ b/PowerEditor/src/MISC/Common/Common.cpp @@ -716,4 +716,35 @@ generic_string stringReplace(generic_string subject, const generic_string& searc pos += replace.length(); } return subject; +} + +std::vector stringSplit(const generic_string& input, generic_string delimiter) +{ + auto start = 0U; + auto end = input.find(delimiter); + std::vector output; + const size_t delimiterLength = delimiter.length(); + while (end != std::string::npos) + { + output.push_back(input.substr(start, end - start)); + start = end + delimiterLength; + end = input.find(delimiter, start); + } + output.push_back(input.substr(start, end)); + return output; +} + +generic_string stringJoin(const std::vector &strings, generic_string separator) +{ + generic_string joined; + size_t length = strings.size(); + for (size_t i = 0; i < length; ++i) + { + joined += strings.at(i); + if (i != length - 1) + { + joined += separator; + } + } + return joined; } \ No newline at end of file diff --git a/PowerEditor/src/MISC/Common/Common.h b/PowerEditor/src/MISC/Common/Common.h index 5164c952..43bc0e6a 100644 --- a/PowerEditor/src/MISC/Common/Common.h +++ b/PowerEditor/src/MISC/Common/Common.h @@ -188,5 +188,7 @@ generic_string PathAppend(generic_string &strDest, const generic_string & str2ap COLORREF getCtrlBgColor(HWND hWnd); generic_string stringToUpper(generic_string strToConvert); generic_string stringReplace(generic_string subject, const generic_string& search, const generic_string& replace); +std::vector stringSplit(const generic_string& input, generic_string delimiter); +generic_string stringJoin(const std::vector &strings, generic_string separator); #endif //M30_IDE_COMMUN_H diff --git a/PowerEditor/src/Notepad_plus.rc b/PowerEditor/src/Notepad_plus.rc index 12ad0c7a..292bf99d 100644 --- a/PowerEditor/src/Notepad_plus.rc +++ b/PowerEditor/src/Notepad_plus.rc @@ -274,8 +274,8 @@ BEGIN MENUITEM "Duplicate Current Line", IDM_EDIT_DUP_LINE MENUITEM "Split Lines", IDM_EDIT_SPLIT_LINES MENUITEM "Join Lines", IDM_EDIT_JOIN_LINES - MENUITEM "Sort Lines in Ascending Order", IDM_EDIT_SORTLINES - MENUITEM "Sort Lines in Descending Order", IDM_EDIT_SORTLINESREVERSE + MENUITEM "Sort Lines in Ascending Order", IDM_EDIT_SORTLINES_ASCENDING + MENUITEM "Sort Lines in Descending Order", IDM_EDIT_SORTLINES_DESCENDING MENUITEM "Move Up Current Line", IDM_EDIT_LINE_UP MENUITEM "Move Down Current Line", IDM_EDIT_LINE_DOWN MENUITEM "Remove Empty Lines", IDM_EDIT_REMOVEEMPTYLINES diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index 9eb78abb..f852e7fd 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -344,8 +344,8 @@ void Notepad_plus::command(int id) } break; - case IDM_EDIT_SORTLINES: - case IDM_EDIT_SORTLINESREVERSE: + case IDM_EDIT_SORTLINES_ASCENDING: + case IDM_EDIT_SORTLINES_DESCENDING: { // default: no selection size_t fromLine = 0; @@ -375,7 +375,7 @@ void Notepad_plus::command(int id) } _pEditView->execute(SCI_BEGINUNDOACTION); - _pEditView->quickSortLines(fromLine, toLine, id == IDM_EDIT_SORTLINESREVERSE); + _pEditView->quickSortLines(fromLine, toLine, id == IDM_EDIT_SORTLINES_DESCENDING); _pEditView->execute(SCI_ENDUNDOACTION); if (hasSelection) // there was 1 selection, so we restore it @@ -2598,8 +2598,8 @@ void Notepad_plus::command(int id) case IDM_EDIT_RTL : case IDM_EDIT_LTR : case IDM_EDIT_BEGINENDSELECT: - case IDM_EDIT_SORTLINES: - case IDM_EDIT_SORTLINESREVERSE: + case IDM_EDIT_SORTLINES_ASCENDING: + case IDM_EDIT_SORTLINES_DESCENDING: case IDM_EDIT_BLANKLINEABOVECURRENT: case IDM_EDIT_BLANKLINEBELOWCURRENT: case IDM_VIEW_FULLSCREENTOGGLE : diff --git a/PowerEditor/src/Parameters.cpp b/PowerEditor/src/Parameters.cpp index ed804a49..23a07ebc 100644 --- a/PowerEditor/src/Parameters.cpp +++ b/PowerEditor/src/Parameters.cpp @@ -127,8 +127,8 @@ WinMenuKeyDefinition winKeyDefs[] = { {VK_SPACE, IDM_EDIT_FUNCCALLTIP, true, false, true, NULL}, {VK_R, IDM_EDIT_RTL, true, true, false, NULL}, {VK_L, IDM_EDIT_LTR, true, true, false, NULL}, - {VK_NULL, IDM_EDIT_SORTLINES, false, false, false, NULL}, - {VK_NULL, IDM_EDIT_SORTLINESREVERSE, false, false, false, NULL}, + {VK_NULL, IDM_EDIT_SORTLINES_ASCENDING, false, false, false, NULL}, + {VK_NULL, IDM_EDIT_SORTLINES_DESCENDING, false, false, false, NULL}, {VK_RETURN, IDM_EDIT_BLANKLINEABOVECURRENT, true, true, false, NULL}, {VK_RETURN, IDM_EDIT_BLANKLINEBELOWCURRENT, true, true, true, NULL}, {VK_F, IDM_SEARCH_FIND, true, false, false, NULL}, diff --git a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp index e15ac336..269d3586 100644 --- a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp +++ b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp @@ -1699,6 +1699,17 @@ void ScintillaEditView::getText(char *dest, int start, int end) const execute(SCI_GETTEXTRANGE, 0, reinterpret_cast(&tr)); } +generic_string ScintillaEditView::getGenericTextAsString(int start, int end) const +{ + assert(end > start); + const int bufSize = end - start + 1; + _TCHAR *buf = new _TCHAR[bufSize]; + getGenericText(buf, bufSize, start, end); + generic_string text = buf; + delete[] buf; + return text; +} + void ScintillaEditView::getGenericText(TCHAR *dest, size_t destlen, int start, int end) const { WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance(); @@ -2900,16 +2911,7 @@ void ScintillaEditView::setTabSettings(Lang *lang) void ScintillaEditView::insertNewLineAboveCurrentLine() { - const int eol_mode = int(execute(SCI_GETEOLMODE)); - - generic_string newline; - if(eol_mode == SC_EOL_CRLF) - newline = TEXT("\r\n"); - else if(eol_mode == SC_EOL_LF) - newline = TEXT("\n"); - else - newline = TEXT("\r"); - + generic_string newline = getEOLString(); const int current_line = getCurrentLineNumber(); if(current_line == 0) { @@ -2918,7 +2920,7 @@ void ScintillaEditView::insertNewLineAboveCurrentLine() } else { - const int eol_length = eol_mode == SC_EOL_CRLF ? 2 : 1; + const int eol_length = newline.length(); const long position = execute(SCI_POSITIONFROMLINE, current_line) - eol_length; insertGenericTextFrom(position, newline.c_str()); } @@ -2928,16 +2930,7 @@ void ScintillaEditView::insertNewLineAboveCurrentLine() void ScintillaEditView::insertNewLineBelowCurrentLine() { - const int eol_mode = int(execute(SCI_GETEOLMODE)); - - generic_string newline; - if(eol_mode == SC_EOL_CRLF) - newline = TEXT("\r\n"); - else if(eol_mode == SC_EOL_LF) - newline = TEXT("\n"); - else - newline = TEXT("\r"); - + generic_string newline = getEOLString(); const int line_count = execute(SCI_GETLINECOUNT); const int current_line = getCurrentLineNumber(); if(current_line == line_count - 1) @@ -2947,146 +2940,38 @@ void ScintillaEditView::insertNewLineBelowCurrentLine() } else { - const int eol_length = eol_mode == SC_EOL_CRLF ? 2 : 1; + const int eol_length = newline.length(); const long position = eol_length + execute(SCI_GETLINEENDPOSITION, current_line); insertGenericTextFrom(position, newline.c_str()); } execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line + 1)); } -// Get the first left index, in which the value greater/equal or smaller/equal than pivot's one -// If not found, then pivot's index will be returned -size_t ScintillaEditView::getLeftLineIndex(size_t leftIndex, size_t pivotIndex, bool isReverse) -{ - size_t i = leftIndex; - while (i < pivotIndex) - { - if (!isReverse) - { - size_t iLine = getGreaterLineBetween(i, pivotIndex); - if (iLine == pivotIndex) // pivotIndex > i - ++i; - else - break; // Bingo! - } - else - { - size_t iLine = getGreaterLineBetween(i, pivotIndex); - if (iLine == pivotIndex) // pivotIndex < i - break; // Bingo! - else - ++i; - } - } - return i; -} - -// Get the first right index, in which the value smaller/equal or greater/equal than pivot's one -// If not found, then pivot's index will be returned -size_t ScintillaEditView::getRightLineIndex(size_t rightIndex, size_t pivotIndex, bool isReverse) -{ - size_t i = rightIndex; - while (i > pivotIndex) - { - if (!isReverse) - { - size_t iLine = getGreaterLineBetween(i, pivotIndex); - if (iLine == i) // pivotIndex > i - i--; - else - break; // Bingo! - } - else - { - size_t iLine = getGreaterLineBetween(i, pivotIndex); - if (iLine == i) // pivotIndex < i - break; // Bingo! - else - i--; - } - } - return i; -} - -size_t ScintillaEditView::getGreaterLineBetween(size_t l1, size_t l2) -{ - int line1Len = execute(SCI_LINELENGTH, l1); - int line2Len = execute(SCI_LINELENGTH, l2); - - char *line1text = new char[line1Len + 1]; - char *line2text = new char[line2Len + 1]; - execute(SCI_GETLINE, l1, (LPARAM)line1text); - line1text[line1Len] = '\0'; - execute(SCI_GETLINE, l2, (LPARAM)line2text); - line2text[line2Len] = '\0'; - - string s1 = line1text; - string s2 = line2text; - - size_t res; - if (s1.compare(s2) > 0) - res = l1; - else - res = l2; - - delete[] line1text; - delete[] line2text; - - return res; -} - -size_t ScintillaEditView::getRandomPivot(size_t fromLine, size_t toLine) -{ - srand((unsigned int)time(NULL)); - return rand() % (toLine - fromLine) + fromLine; -} - - -void ScintillaEditView::quickSortLines(size_t fromLine, size_t toLine, bool isReverse) +void ScintillaEditView::quickSortLines(size_t fromLine, size_t toLine, bool isDescending) { if (fromLine >= toLine) - return; - - // choose the pivot - size_t pivotIndex = getRandomPivot(fromLine, toLine); - - // comparing right with left - size_t leftIndex = fromLine; - size_t rightIndex = toLine; - - while (rightIndex > leftIndex) { - leftIndex = getLeftLineIndex(leftIndex, pivotIndex, isReverse); // get the first left index, in which the value greater or equal than pivot's one - rightIndex = getRightLineIndex(rightIndex, pivotIndex, isReverse); // get the first right index, in which the value smaller or equal than pivot's one - - if ((leftIndex != rightIndex) && swapLines(leftIndex, rightIndex)) - { - if (leftIndex == pivotIndex) - { - pivotIndex = rightIndex; - ++leftIndex; - } - else if (rightIndex == pivotIndex) - { - pivotIndex = leftIndex; - --rightIndex; - } - else - { - ++leftIndex; - --rightIndex; - } - } - + return; } - // check the left side recursively - if (pivotIndex != fromLine) - quickSortLines(fromLine, pivotIndex - 1, isReverse); + const int startPos = execute(SCI_POSITIONFROMLINE, fromLine); + const int endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine); - // check the right side recursively - if (pivotIndex != toLine) - quickSortLines(pivotIndex + 1, toLine, isReverse); + generic_string text = getGenericTextAsString(startPos, endPos); + std::vector splitText = stringSplit(text, getEOLString()); + std::sort(splitText.begin(), splitText.end(), [isDescending](generic_string a, generic_string b) + { + if (isDescending) + { + return a.compare(b) > 0; + } + else + { + return a.compare(b) < 0; + } + }); + generic_string joined = stringJoin(splitText, getEOLString()); + replaceTarget(joined.c_str(), startPos, endPos); } bool ScintillaEditView::isTextDirectionRTL() const @@ -3102,88 +2987,20 @@ void ScintillaEditView::changeTextDirection(bool isRTL) ::SetWindowLongPtr(_hSelf, GWL_EXSTYLE, exStyle); } -bool ScintillaEditView::swapLines(size_t line1, size_t line2) +generic_string ScintillaEditView::getEOLString() { - size_t lowerLine = line1; - size_t higherLine = line2; - - if (lowerLine == higherLine) - return false; - - if (line1 > line2) + const int eol_mode = int(execute(SCI_GETEOLMODE)); + string newline; + if (eol_mode == SC_EOL_CRLF) { - lowerLine = line2; - higherLine = line1; + return TEXT("\r\n"); } - - size_t nbLine = execute(SCI_GETLINECOUNT); - if (higherLine + 1 > nbLine) - return false; - - bool isLastLine = false; - int eol_mode = SC_EOL_CRLF; - size_t extraEOLLength = 0; - if (higherLine + 1 == nbLine) + else if (eol_mode == SC_EOL_LF) { - isLastLine = true; - - eol_mode = int(execute(SCI_GETEOLMODE)); - - if(eol_mode == SC_EOL_CRLF) - extraEOLLength = 2; - else if(eol_mode == SC_EOL_LF) - extraEOLLength = 1; - else // SC_EOL_CR - extraEOLLength = 1; - } - - int line1Len = execute(SCI_LINELENGTH, lowerLine); - int line2Len = execute(SCI_LINELENGTH, higherLine); - - char *line1text = new char[line1Len + 1 ]; - char *line2text = new char[line2Len + 1 + extraEOLLength]; - execute(SCI_GETLINE, lowerLine, (LPARAM)line1text); - line1text[line1Len - extraEOLLength] = '\0'; - execute(SCI_GETLINE, higherLine, (LPARAM)line2text); - if (isLastLine) - { - if (eol_mode == SC_EOL_CRLF) - { - line2text[line2Len] = '\r'; - line2text[line2Len + 1] = '\n'; - line2text[line2Len + 2] = '\0'; - } - else if (eol_mode == SC_EOL_LF) - { - line2text[line2Len] = '\n'; - line2text[line2Len + 1] = '\0'; - } - else // SC_EOL_CR - { - line2text[line2Len] = '\r'; - line2text[line2Len + 1] = '\0'; - } - + return TEXT("\n"); } else - line2text[line2Len] = '\0'; - - size_t posFrom1, posTo1, posFrom2, posTo2; - posFrom1 = execute(SCI_POSITIONFROMLINE, lowerLine); - posFrom2 = execute(SCI_POSITIONFROMLINE, higherLine); - posTo1 = posFrom1 + line1Len; - posTo2 = posFrom2 + line2Len; - - execute(SCI_SETTARGETSTART, posFrom2); - execute(SCI_SETTARGETEND, posTo2); - execute(SCI_REPLACETARGET, (WPARAM)-1, (LPARAM)line1text); - - execute(SCI_SETTARGETSTART, posFrom1); - execute(SCI_SETTARGETEND, posTo1); - execute(SCI_REPLACETARGET, (WPARAM)-1, (LPARAM)line2text); - - delete[] line1text; - delete[] line2text; - - return true; + { + return TEXT("\r"); + } } \ No newline at end of file diff --git a/PowerEditor/src/ScitillaComponent/ScintillaEditView.h b/PowerEditor/src/ScitillaComponent/ScintillaEditView.h index 666f5405..e296ba64 100644 --- a/PowerEditor/src/ScitillaComponent/ScintillaEditView.h +++ b/PowerEditor/src/ScitillaComponent/ScintillaEditView.h @@ -247,6 +247,7 @@ public: void getText(char *dest, int start, int end) const; void getGenericText(TCHAR *dest, size_t destlen, int start, int end) const; void getGenericText(TCHAR *dest, size_t deslen, int start, int end, int *mstart, int *mend) const; + generic_string getGenericTextAsString(int start, int end) const; void insertGenericTextFrom(int position, const TCHAR *text2insert) const; void replaceSelWith(const char * replaceText); @@ -634,8 +635,8 @@ public: (_codepage == CP_JAPANESE) || (_codepage == CP_KOREAN)); }; void scrollPosToCenter(int pos); - bool swapLines(size_t line1, size_t line2); - void quickSortLines(size_t fromLine, size_t toLine, bool isReverse = false); + generic_string getEOLString(); + void quickSortLines(size_t fromLine, size_t toLine, bool isDescending); void changeTextDirection(bool isRTL); bool isTextDirectionRTL() const; @@ -911,12 +912,6 @@ protected: pair getWordRange(); bool expandWordSelection(); - - // For the quicksort on lines - size_t getLeftLineIndex(size_t leftIndex, size_t pivotIndex, bool isReverse); - size_t getRightLineIndex(size_t rightIndex, size_t pivotIndex, bool isReverse); - size_t getGreaterLineBetween(size_t l1, size_t l2); - size_t getRandomPivot(size_t fromLine, size_t toLine); }; #endif //SCINTILLA_EDIT_VIEW_H diff --git a/PowerEditor/src/menuCmdID.h b/PowerEditor/src/menuCmdID.h index 4e659461..7ca35376 100644 --- a/PowerEditor/src/menuCmdID.h +++ b/PowerEditor/src/menuCmdID.h @@ -112,8 +112,8 @@ #define IDM_EDIT_REMOVEEMPTYLINESWITHBLANK (IDM_EDIT + 56) #define IDM_EDIT_BLANKLINEABOVECURRENT (IDM_EDIT + 57) #define IDM_EDIT_BLANKLINEBELOWCURRENT (IDM_EDIT + 58) - #define IDM_EDIT_SORTLINES (IDM_EDIT + 59) - #define IDM_EDIT_SORTLINESREVERSE (IDM_EDIT + 60) + #define IDM_EDIT_SORTLINES_ASCENDING (IDM_EDIT + 59) + #define IDM_EDIT_SORTLINES_DESCENDING (IDM_EDIT + 60) // Menu macro #define IDM_MACRO_STARTRECORDINGMACRO (IDM_EDIT + 18) From 4f7752e4a389c3a5eb879bdf84fe30154a606c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20J=C3=B6nsson?= Date: Sat, 9 May 2015 00:17:18 +0200 Subject: [PATCH 2/2] Add more const. --- PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp index 269d3586..ed0f9cc4 100644 --- a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp +++ b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp @@ -2956,8 +2956,7 @@ void ScintillaEditView::quickSortLines(size_t fromLine, size_t toLine, bool isDe const int startPos = execute(SCI_POSITIONFROMLINE, fromLine); const int endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine); - - generic_string text = getGenericTextAsString(startPos, endPos); + const generic_string text = getGenericTextAsString(startPos, endPos); std::vector splitText = stringSplit(text, getEOLString()); std::sort(splitText.begin(), splitText.end(), [isDescending](generic_string a, generic_string b) { @@ -2970,7 +2969,7 @@ void ScintillaEditView::quickSortLines(size_t fromLine, size_t toLine, bool isDe return a.compare(b) < 0; } }); - generic_string joined = stringJoin(splitText, getEOLString()); + const generic_string joined = stringJoin(splitText, getEOLString()); replaceTarget(joined.c_str(), startPos, endPos); }