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:
parent
0c5a42153b
commit
455fcb2da4
@ -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;
|
||||
|
@ -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},
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user