Add "Multi-Select Next Occurence" feature

* Added Ctrl+Shift+D to select next occurence of selection
* Left and right movement works for multiple cursors
* Enter/return a new line works for multiple cursors

Close #5322, close #5399
This commit is contained in:
jmbockhorst 2019-03-08 10:20:47 -06:00 committed by Don HO
parent 0c5a42153b
commit 455fcb2da4
No known key found for this signature in database
GPG Key ID: 6C429F1D8D84F46E
5 changed files with 212 additions and 5 deletions

View File

@ -1570,6 +1570,103 @@ void Notepad_plus::command(int id)
}
break;
case IDM_EDIT_SELECTNEXTOCCURENCE:
{
// Get the selected text length of the first selection
int textLen = abs(int(_pEditView->execute(SCI_GETSELECTIONNANCHOR, 0)) - int(_pEditView->execute(SCI_GETSELECTIONNCARET, 0)));
if (!textLen)
return;
// Get selected text
char* selectedText = new char[textLen + 1];
_pEditView->getSelectedText(selectedText, textLen + 1);
// Get current position of the last selection
int numSelections = int(_pEditView->execute(SCI_GETSELECTIONS));
int currentPos = int(_pEditView->execute(SCI_GETSELECTIONNCARET, numSelections - 1));
int selectonEnd = max(int(_pEditView->execute(SCI_GETSELECTIONNANCHOR, numSelections - 1)), currentPos);
int docLength = int(_pEditView->execute(SCI_GETLENGTH));
// Convert char to TCHAR
WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance();
UINT cp = static_cast<UINT>(_pEditView->execute(SCI_GETCODEPAGE));
const TCHAR* textToFind = wmc->char2wchar(selectedText, cp);
// Find position of next occurence
int posFound = _pEditView->searchInTarget(textToFind, textLen, currentPos + 1, docLength);
if (posFound > currentPos)
{
int start = posFound + textLen;
int end = posFound;
// Detect if selection happened the other way
if (currentPos != selectonEnd)
{
start = posFound;
end = posFound + textLen;
}
_pEditView->execute(SCI_ADDSELECTION, start, end);
// Keep the main selection the first one
_pEditView->execute(SCI_SETMAINSELECTION, 0);
}
}
break;
case IDM_EDIT_MULTICHARLEFT:
_pEditView->multiCursorLeftOrRight(-1);
break;
case IDM_EDIT_MULTICHARLEFTEXTEND:
_pEditView->multiCursorLeftOrRight(-1, true);
break;
case IDM_EDIT_MULTICHARRIGHT:
_pEditView->multiCursorLeftOrRight(1);
break;
case IDM_EDIT_MULTICHARRIGHTEXTEND:
_pEditView->multiCursorLeftOrRight(1, true);
break;
case IDM_EDIT_MULTINEWLINE:
{
// Get the number of selection
int numSelections = int(_pEditView->execute(SCI_GETSELECTIONS));
// Keep an array of the new positions
std::vector<int> selectionPositions;
// Loop through each selection
for (int i = 0; i < numSelections; ++i)
{
// Get the current caret position of the seleciton
int currentPos = int(_pEditView->execute(SCI_GETSELECTIONNCARET, i));
// Add the new position to the array
selectionPositions.push_back(currentPos + i * 2 + 2);
// New line caret adjustment
_pEditView->execute(SCI_SETSELECTIONNCARET, i, currentPos + i * 2);
_pEditView->execute(SCI_SETSELECTIONNANCHOR, i, currentPos + i * 2);
// Set the current selection to main
_pEditView->execute(SCI_SETMAINSELECTION, i);
// Execute the new line command on the selection
_pEditView->execute(SCI_NEWLINE);
}
// Add all of the cursors back where they should be
for (int i = 0; i < (static_cast<signed int>(selectionPositions.size()) - 1); ++i)
{
_pEditView->execute(SCI_ADDSELECTION, selectionPositions[i], selectionPositions[i]);
}
}
break;
case IDM_SEARCH_CUTMARKEDLINES :
cutMarkedLines();
break;

View File

@ -141,6 +141,12 @@ static const WinMenuKeyDefinition winKeyDefs[] =
{ VK_NULL, IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING, false, false, false, nullptr },
{ VK_NULL, IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING, false, false, false, nullptr },
{ VK_NULL, IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING, false, false, false, nullptr },
{ VK_D, IDM_EDIT_SELECTNEXTOCCURENCE, true, false, true, nullptr },
{ VK_LEFT, IDM_EDIT_MULTICHARLEFT, false, false, false, nullptr },
{ VK_LEFT, IDM_EDIT_MULTICHARLEFTEXTEND, false, false, true, nullptr },
{ VK_RIGHT, IDM_EDIT_MULTICHARRIGHT, false, false, false, nullptr },
{ VK_RIGHT, IDM_EDIT_MULTICHARRIGHTEXTEND, false, false, true, nullptr },
{ VK_RETURN, IDM_EDIT_MULTINEWLINE, false, false, false, nullptr },
{ VK_Q, IDM_EDIT_BLOCK_COMMENT, true, false, false, nullptr },
{ VK_K, IDM_EDIT_BLOCK_COMMENT_SET, true, false, false, nullptr },
{ VK_K, IDM_EDIT_BLOCK_UNCOMMENT, true, false, true, nullptr },
@ -420,7 +426,6 @@ static const ScintillaKeyDefinition scintKeyDefs[] =
{TEXT(""), SCI_UNDO, false, true, false, VK_BACK, 0},
{TEXT("SCI_REDO"), SCI_REDO, true, false, false, VK_Y, IDM_EDIT_REDO},
{TEXT(""), SCI_REDO, true, false, true, VK_Z, 0},
{TEXT("SCI_NEWLINE"), SCI_NEWLINE, false, false, false, VK_RETURN, 0},
{TEXT(""), SCI_NEWLINE, false, false, true, VK_RETURN, 0},
{TEXT("SCI_TAB"), SCI_TAB, false, false, false, VK_TAB, IDM_EDIT_INS_TAB},
{TEXT("SCI_BACKTAB"), SCI_BACKTAB, false, false, true, VK_TAB, IDM_EDIT_RMV_TAB},
@ -445,11 +450,7 @@ static const ScintillaKeyDefinition scintKeyDefs[] =
{TEXT("SCI_PARADOWNEXTEND"), SCI_PARADOWNEXTEND, true, false, true, VK_OEM_6, 0},
{TEXT("SCI_PARAUP"), SCI_PARAUP, true, false, false, VK_OEM_4, 0},
{TEXT("SCI_PARAUPEXTEND"), SCI_PARAUPEXTEND, true, false, true, VK_OEM_4, 0},
{TEXT("SCI_CHARLEFT"), SCI_CHARLEFT, false, false, false, VK_LEFT, 0},
{TEXT("SCI_CHARLEFTEXTEND"), SCI_CHARLEFTEXTEND, false, false, true, VK_LEFT, 0},
{TEXT("SCI_CHARLEFTRECTEXTEND"), SCI_CHARLEFTRECTEXTEND, false, true, true, VK_LEFT, 0},
{TEXT("SCI_CHARRIGHT"), SCI_CHARRIGHT, false, false, false, VK_RIGHT, 0},
{TEXT("SCI_CHARRIGHTEXTEND"), SCI_CHARRIGHTEXTEND, false, false, true, VK_RIGHT, 0},
{TEXT("SCI_CHARRIGHTRECTEXTEND"), SCI_CHARRIGHTRECTEXTEND, false, true, true, VK_RIGHT, 0},
{TEXT("SCI_WORDLEFT"), SCI_WORDLEFT, true, false, false, VK_LEFT, 0},
{TEXT("SCI_WORDLEFTEXTEND"), SCI_WORDLEFTEXTEND, true, false, true, VK_LEFT, 0},

View File

@ -2589,6 +2589,108 @@ void ScintillaEditView::currentLinesDown() const
execute(SCI_SCROLLRANGE, execute(SCI_GETSELECTIONEND), execute(SCI_GETSELECTIONSTART));
}
void ScintillaEditView::multiCursorLeftOrRight(int direction, bool shouldExt) const
{
const int NUM_SELECTIONS_FOR_DEFAULT = 1;
// Get the number of selection
int numSelections = int(execute(SCI_GETSELECTIONS));
// Use default functions when there is one selection
if (numSelections == NUM_SELECTIONS_FOR_DEFAULT)
{
if (direction < 0 && !shouldExt)
{
execute(SCI_CHARLEFT);
}
else if (direction < 0 && shouldExt)
{
execute(SCI_CHARLEFTEXTEND);
}
else if (direction > 0 && !shouldExt)
{
execute(SCI_CHARRIGHT);
}
else if (direction > 0 && shouldExt)
{
execute(SCI_CHARRIGHTEXTEND);
}
return;
}
// Stop the caret blinking so it stays on
execute(SCI_SETCARETPERIOD, 0);
// Loop through each selection
for (int i = 0; i < numSelections; ++i)
{
// Get the current caret position of the seleciton
int currentPos = int(execute(SCI_GETSELECTIONNCARET, i));
int docEnd = getCurrentDocLen();
int newPos = currentPos + direction;
// Cursor is at end, use virtual space
if ((currentPos >= docEnd && direction > 0))
{
if (i == 0)
{
break;
}
// Add virtual space
execute(SCI_SETSELECTIONNCARETVIRTUALSPACE, i, execute(SCI_GETSELECTIONNCARETVIRTUALSPACE, i) + 1);
if (!shouldExt)
{
execute(SCI_SETSELECTIONNANCHORVIRTUALSPACE, i, execute(SCI_GETSELECTIONNANCHORVIRTUALSPACE, i) + 1);
}
}
else if (currentPos > docEnd)
{
// Moving back, remove virtual space
execute(SCI_SETSELECTIONNCARETVIRTUALSPACE, i, execute(SCI_GETSELECTIONNCARETVIRTUALSPACE, i) - 1);
if (!shouldExt)
{
execute(SCI_SETSELECTIONNANCHORVIRTUALSPACE, i, execute(SCI_GETSELECTIONNANCHORVIRTUALSPACE, i) - 1);
}
}
else if (currentPos <= 0 && direction < 0)
{
break;
}
// Get the starting and ending position of the line
int lineStart = int(execute(SCI_POSITIONFROMLINE, int(execute(SCI_LINEFROMPOSITION, currentPos))));
int lineEnd = int(execute(SCI_POSITIONFROMLINE, int(execute(SCI_LINEFROMPOSITION, currentPos)) + 1)) - 1;
// Go back or forward another position because of new line
if (newPos < lineStart && direction < 0)
{
newPos--;
}
else if (newPos >= lineEnd && lineEnd + 1 != docEnd && direction > 0)
{
newPos++;
}
// Set the current caret position to the new position
execute(SCI_SETSELECTIONNCARET, i, newPos);
if (!shouldExt)
{
execute(SCI_SETSELECTIONNANCHOR, i, newPos);
}
}
const NppGUI& nppGUI = _pParameter->getNppGUI();
// Reset the caret period so it start blinking again
execute(SCI_SETCARETPERIOD, nppGUI._caretBlinkRate);
}
void ScintillaEditView::changeCase(__inout wchar_t * const strWToConvert, const int & nbChars, const TextCase & caseToConvert) const
{
if (strWToConvert == nullptr || nbChars == NULL)

View File

@ -538,6 +538,7 @@ public:
std::pair<int, int> getSelectionLinesRange() const;
void currentLinesUp() const;
void currentLinesDown() const;
void multiCursorLeftOrRight(int direction, bool shouldExt = false) const;
void changeCase(__inout wchar_t * const strWToConvert, const int & nbChars, const TextCase & caseToConvert) const;
void convertSelectedTextTo(const TextCase & caseToConvert);

View File

@ -130,6 +130,12 @@
#define IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING (IDM_EDIT + 64)
#define IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING (IDM_EDIT + 65)
#define IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING (IDM_EDIT + 66)
#define IDM_EDIT_SELECTNEXTOCCURENCE (IDM_EDIT + 78)
#define IDM_EDIT_MULTICHARLEFT (IDM_EDIT + 79)
#define IDM_EDIT_MULTICHARLEFTEXTEND (IDM_EDIT + 80)
#define IDM_EDIT_MULTICHARRIGHT (IDM_EDIT + 81)
#define IDM_EDIT_MULTICHARRIGHTEXTEND (IDM_EDIT + 82)
#define IDM_EDIT_MULTINEWLINE (IDM_EDIT + 83)
#define IDM_EDIT_OPENASFILE (IDM_EDIT + 73)
#define IDM_EDIT_OPENINFOLDER (IDM_EDIT + 74)