diff --git a/PowerEditor/installer/nativeLang/english.xml b/PowerEditor/installer/nativeLang/english.xml
index 581fae80..a89491a6 100644
--- a/PowerEditor/installer/nativeLang/english.xml
+++ b/PowerEditor/installer/nativeLang/english.xml
@@ -112,6 +112,7 @@ The comments are here for explanation, it's not necessary to translate them.
+
diff --git a/PowerEditor/src/MISC/Common/Common.h b/PowerEditor/src/MISC/Common/Common.h
index f479529a..e3299db5 100644
--- a/PowerEditor/src/MISC/Common/Common.h
+++ b/PowerEditor/src/MISC/Common/Common.h
@@ -31,6 +31,8 @@
#include
#include
#include
+#include
+#include
const bool dirUp = true;
@@ -205,3 +207,29 @@ std::string ws2s(const std::wstring& wstr);
bool deleteFileOrFolder(const generic_string& f2delete);
void getFilesInFolder(std::vector& files, const generic_string& extTypeFilter, const generic_string& inFolder);
+
+template size_t vecRemoveDuplicates(std::vector& vec, bool isSorted = false, bool canSort = false)
+{
+ if (!isSorted && canSort)
+ {
+ std::sort(vec.begin(), vec.end());
+ isSorted = true;
+ }
+
+ if (isSorted)
+ {
+ typename std::vector::iterator it;
+ it = std::unique(vec.begin(), vec.end());
+ vec.resize(distance(vec.begin(), it)); // unique() does not shrink the vector
+ }
+ else
+ {
+ std::unordered_set seen;
+ auto newEnd = std::remove_if(vec.begin(), vec.end(), [&seen](const T& value)
+ {
+ return !seen.insert(value).second;
+ });
+ vec.erase(newEnd, vec.end());
+ }
+ return vec.size();
+}
diff --git a/PowerEditor/src/Notepad_plus.rc b/PowerEditor/src/Notepad_plus.rc
index d4cfb149..6d347168 100644
--- a/PowerEditor/src/Notepad_plus.rc
+++ b/PowerEditor/src/Notepad_plus.rc
@@ -308,6 +308,7 @@ BEGIN
POPUP "Line Operations"
BEGIN
MENUITEM "Duplicate Current Line", IDM_EDIT_DUP_LINE
+ MENUITEM "Remove Duplicate Lines", IDM_EDIT_REMOVE_ANY_DUP_LINES
MENUITEM "Remove Consecutive Duplicate Lines", IDM_EDIT_REMOVE_DUP_LINES
MENUITEM "Split Lines", IDM_EDIT_SPLIT_LINES
MENUITEM "Join Lines", IDM_EDIT_JOIN_LINES
diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp
index 84706178..13296122 100644
--- a/PowerEditor/src/NppCommands.cpp
+++ b/PowerEditor/src/NppCommands.cpp
@@ -1546,6 +1546,12 @@ void Notepad_plus::command(int id)
_pEditView->execute(SCI_ENDUNDOACTION);
break;
+ case IDM_EDIT_REMOVE_ANY_DUP_LINES:
+ _pEditView->execute(SCI_BEGINUNDOACTION);
+ _pEditView->removeAnyDuplicateLines();
+ _pEditView->execute(SCI_ENDUNDOACTION);
+ break;
+
case IDM_EDIT_SPLIT_LINES:
{
if (_pEditView->execute(SCI_GETSELECTIONS) == 1)
@@ -3524,6 +3530,7 @@ void Notepad_plus::command(int id)
case IDM_EDIT_RMV_TAB:
case IDM_EDIT_DUP_LINE:
case IDM_EDIT_REMOVE_DUP_LINES:
+ case IDM_EDIT_REMOVE_ANY_DUP_LINES:
case IDM_EDIT_TRANSPOSE_LINE:
case IDM_EDIT_SPLIT_LINES:
case IDM_EDIT_JOIN_LINES:
diff --git a/PowerEditor/src/Parameters.cpp b/PowerEditor/src/Parameters.cpp
index c5901395..9dc5de35 100644
--- a/PowerEditor/src/Parameters.cpp
+++ b/PowerEditor/src/Parameters.cpp
@@ -128,6 +128,7 @@ static const WinMenuKeyDefinition winKeyDefs[] =
{ VK_NULL, IDM_EDIT_INVERTCASE, false, false, false, nullptr },
{ VK_NULL, IDM_EDIT_RANDOMCASE, false, false, false, nullptr },
{ VK_NULL, IDM_EDIT_REMOVE_DUP_LINES, false, false, false, nullptr },
+ { VK_NULL, IDM_EDIT_REMOVE_ANY_DUP_LINES, false, false, false, nullptr },
{ VK_I, IDM_EDIT_SPLIT_LINES, true, false, false, nullptr },
{ VK_J, IDM_EDIT_JOIN_LINES, true, false, false, nullptr },
{ VK_UP, IDM_EDIT_LINE_UP, true, false, true, nullptr },
diff --git a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp
index bd01ef00..d07f6175 100644
--- a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp
+++ b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp
@@ -3871,3 +3871,66 @@ void ScintillaEditView::markedTextToClipboard(int indiStyle, bool doAll /*= fals
str2Clipboard(joined, NULL);
}
}
+
+void ScintillaEditView::removeAnyDuplicateLines()
+{
+ size_t fromLine = 0, toLine = 0;
+ bool hasLineSelection = false;
+
+ auto selStart = execute(SCI_GETSELECTIONSTART);
+ auto selEnd = execute(SCI_GETSELECTIONEND);
+ hasLineSelection = selStart != selEnd;
+
+ if (hasLineSelection)
+ {
+ const pair lineRange = getSelectionLinesRange();
+ // One single line selection is not allowed.
+ if (lineRange.first == lineRange.second)
+ {
+ return;
+ }
+ fromLine = lineRange.first;
+ toLine = lineRange.second;
+ }
+ else
+ {
+ // No selection.
+ fromLine = 0;
+ toLine = execute(SCI_GETLINECOUNT) - 1;
+ }
+
+ if (fromLine >= toLine)
+ {
+ return;
+ }
+
+ const auto startPos = execute(SCI_POSITIONFROMLINE, fromLine);
+ const auto endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine);
+ const generic_string text = getGenericTextAsString(startPos, endPos);
+ std::vector linesVect = stringSplit(text, getEOLString());
+ const size_t lineCount = execute(SCI_GETLINECOUNT);
+
+ const bool doingEntireDocument = toLine == lineCount - 1;
+ if (!doingEntireDocument)
+ {
+ if (linesVect.rbegin()->empty())
+ {
+ linesVect.pop_back();
+ }
+ }
+
+ size_t origSize = linesVect.size();
+ size_t newSize = vecRemoveDuplicates(linesVect);
+ if (origSize != newSize)
+ {
+ generic_string joined = stringJoin(linesVect, getEOLString());
+ if (!doingEntireDocument)
+ {
+ joined += getEOLString();
+ }
+ if (text != joined)
+ {
+ replaceTarget(joined.c_str(), int(startPos), int(endPos));
+ }
+ }
+}
diff --git a/PowerEditor/src/ScitillaComponent/ScintillaEditView.h b/PowerEditor/src/ScitillaComponent/ScintillaEditView.h
index 2fa659d9..bc76f452 100644
--- a/PowerEditor/src/ScitillaComponent/ScintillaEditView.h
+++ b/PowerEditor/src/ScitillaComponent/ScintillaEditView.h
@@ -621,6 +621,7 @@ public:
bool isTextDirectionRTL() const;
void setPositionRestoreNeeded(bool val) { _positionRestoreNeeded = val; };
void markedTextToClipboard(int indiStyle, bool doAll = false);
+ void removeAnyDuplicateLines();
protected:
static HINSTANCE _hLib;
diff --git a/PowerEditor/src/menuCmdID.h b/PowerEditor/src/menuCmdID.h
index e8219484..9bcbbb1c 100644
--- a/PowerEditor/src/menuCmdID.h
+++ b/PowerEditor/src/menuCmdID.h
@@ -106,6 +106,7 @@
#define IDM_EDIT_RMV_TAB (IDM_EDIT + 9)
#define IDM_EDIT_DUP_LINE (IDM_EDIT + 10)
#define IDM_EDIT_REMOVE_DUP_LINES (IDM_EDIT + 77)
+ #define IDM_EDIT_REMOVE_ANY_DUP_LINES (IDM_EDIT + 79)
#define IDM_EDIT_TRANSPOSE_LINE (IDM_EDIT + 11)
#define IDM_EDIT_SPLIT_LINES (IDM_EDIT + 12)
#define IDM_EDIT_JOIN_LINES (IDM_EDIT + 13)