-- Transpose selections or characters or lines.
-- If there is 1 empty selection at the start or end of a line, then
-- transpose the line with the one above it (the pre-existing Ctrl+T behaviour).
-- If all selections are empty, then transpose the characters on either side of
-- any that are not at the start or end of a line.
-- If there is 1 non-empty selection, rotate its characters forward.
-- If there are 2 or more selections that are not all empty, rotate the selection contents between selections.
-- Useful for rotating the rows in a rectangular/column selection (Alt-select)
local function transposeSelectionsOrCharactersOrLines()
-- According to the Scintilla documentation, there is always at least one selection
local lastIdx = editor.Selections - 1 -- Indexes for multiple selections are zero-based
editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
if editor.SelectionEmpty then -- If all selections are empty, i.e. consist only of an insertion point
if lastIdx == 0 and isStartOrEndOfLine(editor.CurrentPos) then -- If there is only one selection and it's at the start or end of a line
editor:LineTranspose() -- Transpose the current line with the one before
else -- Else multiple empty selections or a single empty selection not at the start or end of a line
-- Transpose the characters on either side of each insertion point that is not at the start or end of a line
for idx = 0, lastIdx do
local pos = editor.SelectionNCaret[idx]
if not isStartOrEndOfLine(pos) then -- If this empty selection is not at the start or end of a line
transposeCharactersAt(pos) -- Transpose the characters either side of the given position
-- The insertion point will now be before both characters
-- Put the insertion point back in between them, allowing for different numbers of bytes per character
pos = editor:PositionAfter(editor.SelectionNCaret[idx])
editor.SelectionNAnchor[idx] = pos
editor.SelectionNCaret[idx] = pos
end
end
end
else -- Else selections are not all empty
if lastIdx == 0 then -- If there is only 1 selection
--[[ Alternative behaviour:
editor:LineTranspose() -- Transpose the current line with the one before
--]]
-- Rotate the characters in the selection (ignores virtual space)
local selStart = editor.SelectionNStart[0]
local selEnd = editor.SelectionNEnd[0]
rotateCharacters(selStart, selEnd)
-- Restore the selection
editor.SelectionNStart[0] = selStart
editor.SelectionNEnd[0] = selEnd
else -- Else there are 2 or more selections
-- Rotate selection contents between selections
local str = getSelectionNTextVirtual(lastIdx) -- Save the last selection including any virtual space
for idx = lastIdx, 1, -1 do -- Shift the other selections along
setSelectionNTextVirtual(idx, getSelectionNTextVirtual(idx-1))
end
setSelectionNTextVirtual(0, str) -- Make the first selection be the saved last selection
end
end
editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
end
-- Rotate selections backwards
-- Useful for rotating the rows backwards in a rectangular/column selection (Alt-select)
local function rotateSelectionsOrCharactersBackwards()
-- According to the Scintilla documentation, there is always at least one selection
local lastIdx = editor.Selections - 1 -- Indexes for multiple selections are zero-based
editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
if lastIdx == 0 then -- If there is only 1 selection
-- Rotate the characters backwards in the selection (ignores virtual space)
local selStart = editor.SelectionNStart[0]
local selEnd = editor.SelectionNEnd[0]
rotateCharactersBackwards(selStart, selEnd)
-- Restore the selection
editor.SelectionNStart[0] = selStart
editor.SelectionNEnd[0] = selEnd
else
-- Rotate selection contents backwards between selections
local str = getSelectionNTextVirtual(0) -- Save the first selection including any virtual space
for idx = 1, lastIdx do -- Shift the other selections along
setSelectionNTextVirtual(idx-1, getSelectionNTextVirtual(idx))
end
setSelectionNTextVirtual(lastIdx, str) -- Make the last selection be the saved first selection
end
editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
end
-- Reverse selections
-- Useful for reversing the order of rows in a rectangular/column selection (Alt-select)
local function reverseSelectionsOrCharacters()
-- According to the Scintilla documentation, there is always at least one selection
local lastIdx = editor.Selections - 1 -- Indexes for multiple selections are zero-based
local lastSwap = editor.Selections // 2 - 1
editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
if lastIdx == 0 then -- If there is only 1 selection
-- Reverse the characters in the selection (ignores virtual space)
local selStart = editor.SelectionNStart[0]
local selEnd = editor.SelectionNEnd[0]
reverseCharacters(selStart, selEnd)
-- Restore the selection
editor.SelectionNStart[0] = selStart
editor.SelectionNEnd[0] = selEnd
else -- Else there are 2 or more selections
-- Swap selection contents between opposite pairs of selections
for idx = 0, lastSwap do
local str = getSelectionNTextVirtual(lastIdx - idx) -- Save the later selection including any virtual space
setSelectionNTextVirtual(lastIdx - idx, getSelectionNTextVirtual(idx)) -- Replace later selection with the opposite earlier selection
setSelectionNTextVirtual(idx, str) -- Make the opposite earlier selection be the saved later selection
end
end
editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
end
-- Add menu entry for transposing or rotating selections, characters or lines.
-- The Ctrl+T shortcut may need to be added manually via
-- Settings -> Shortcut Mapper -> Plugin commands -> Transpose Selections/Characters/Lines -> Modify.
-- You can ignore the conflict with the shortcut for Scintilla SCI_LINETRANSPOSE.
editor:ClearCmdKey(string.byte("T"), SCMOD_CTRL) -- This doesn't seem to work here
npp.AddShortcut("Transpose Selections/Characters/Lines", "Ctrl+T", transposeSelectionsOrCharactersOrLines)
-- Add menu entry for rotating selections backwards.
npp.AddShortcut("Rotate Selections/Characters Backwards", "Ctrl+Alt+T", rotateSelectionsOrCharactersBackwards)
-- Add menu entry for reversing selections.
npp.AddShortcut("Reverse Selections/Characters", "Ctrl+Alt+Shift+T", reverseSelectionsOrCharacters)