Community
    • Login

    Transpose multiple selections

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    16 Posts 2 Posters 4.7k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • dkeenan7D
      dkeenan7
      last edited by dkeenan7

      I primarily wanted Ctrl+T to swap two selections after you Ctrl-select a second selection. I note that Multi-editing must be enabled in Preferences for Ctrl-select to work. For example, you might decide, after having written it, that some code will read better if you swap the order of operands for some commutative operator, or if you swap the if and else parts of a conditional.

      But then it seems a shame not to make Ctrl+T also do something with more than 2 selections, and with rectangular selections (Alt-select). Rotating them seems the obvious generalisation.

      When there is only 1 selection, Ctrl+T can do what it does now, transpose lines.

      I made a Notepad++ feature request:
      https://github.com/notepad-plus-plus/notepad-plus-plus/issues/6226
      and a Scintilla feature request:
      https://sourceforge.net/p/scintilla/feature-requests/1316/

      Then dinkumoil kindly wrote a Lua script for one way of doing this, which you can see here:
      https://github.com/notepad-plus-plus/notepad-plus-plus/issues/6226#issuecomment-546368744
      and I agree with dinkumoil that any scripts for solving this problem need to be known and discussed here in the community forum.

      Dinkumoil’s script first sorts the multiple selections by their order in the document, and so requires separate commands for rotating forwards and backwards (or down and up). I have simplified it below, to a version that rotates selections in the order they were selected, and so only requires one command. Thanks @dinkumoil! This probably would have taken me several days if I’d had to learn Lua and its Notepad++ and Scintilla APIs and do it myself.

      Can anyone explain how to get the Ctrl+T shortcut to work from the script, or why it doesn’t work as written?

      -- ================================================================================
      -- Add a menu entry (intended for Ctrl+T shortcut) to transpose multiple selections 
      -- by rotating them in the order they were selected.
      -- Modified from a script kindly written by Andreas Heim (dinkumoil), 2019-10-25
      -- ================================================================================
      
      -- Get text of selection with index idx
      local function getSelectionNText(idx)
        local retVal = editor:textrange(editor.SelectionNStart[idx], editor.SelectionNEnd[idx])
        if not retVal then retVal = "" end
      
        return retVal
      end
      
      
      -- Set text of selection with index idx and readjust its span
      local function setSelectionNText(idx, str)
        editor:SetTargetRange(editor.SelectionNStart[idx], editor.SelectionNEnd[idx])
        editor:ReplaceTarget(str)
      
        editor.SelectionNCaret[idx] = editor.SelectionNAnchor[idx] + #str
      end
      
      
      -- Transpose selections
      -- If there are less than 2 selections, the standard Line Transpose function is used
      -- If there are 2 or more selections, they get rotated in the order they were selected
      local function transposeSelections()
        local lastIdx, bufStr
      
        lastIdx = editor.Selections-1
      
        -- Process special case:
        --   Less than 2 selections  -> perform SCI_LINETRANSPOSE
        if lastIdx < 1 then
          npp.SendEditor(SCI_LINETRANSPOSE)
          return
        end
      
        -- Set start of undo sequence
        editor:BeginUndoAction()
      
        -- Perform transpose operation
        bufStr = getSelectionNText(lastIdx)
        for idx = lastIdx, 1, -1 do
          setSelectionNText(idx, getSelectionNText(idx-1))
        end
        setSelectionNText(0, bufStr)
      
        -- Set end of undo sequence
        editor:EndUndoAction()
      end
      
      
      -- Add menu entry for transposing selections or lines.
      -- The Ctrl+T shortcut must be added manually via 
      -- Settings->Shortcut Mapper->Plugin commands->Transpose Selections->Modify.
      -- Ignore the conflict with the shortcut for Scintilla SCI_LINETRANSPOSE.
      npp.AddShortcut("Transpose Selections", "Ctrl+T", function()
        transposeSelections()
      end)
      
      dinkumoilD 1 Reply Last reply Reply Quote 3
      • dinkumoilD
        dinkumoil @dkeenan7
        last edited by dinkumoil

        @dkeenan7

        Back from garden work (ouch, my back!).

        Thank you for creating this topic here in the community forum and for providing an alternative solution.

        Can anyone explain how to get the Ctrl+T shortcut to work from the script, or why it doesn’t work as written?

        The only idea I have is, that you should go to (menu) Settings -> Shortcut Mapper and ensure that CTRL+T is not already assigned to another function of Notepad++, a plugin or Scintilla. Usually CTRL+T is assigned to the the Scintilla command SCI_LINETRANSPOSE (no. 92 of the Scintilla commands at Shortcut Mappers’s Scintilla register card).

        1 Reply Last reply Reply Quote 1
        • dkeenan7D
          dkeenan7
          last edited by dkeenan7

          @dinkumoil Thanks for the suggestion. It wasn’t easy to remove the existing assignment of Ctrl+T to SCI_LINETRANSPOSE. Thanks go to @sasumner for describing the workaround here:
          https://community.notepad-plus-plus.org/topic/13687/bug-unable-to-clear-or-remove-shortcuts-for-scintilla-commands

          But it made no difference. And if I try a shortcut key that was never assigned, e.g.
          npp.AddShortcut("Transpose Selections", "Ctrl+E", transposeSelections)
          that doesn’t work either.

          On another matter: Neil Hodgson mentioned another operation performed by Ctrl+T in some editors. When the selection is empty (only a blinking vertical line representing an insertion point) then Ctrl+T swaps the two characters on either side of the insertion point. This may be useful for correcting common typos.

          Would it be easy for you to add that to your script? I suppose we should also allow for multiple selections, all empty, and do the same to all of them.

          I notice Scintilla has:
          SCI_GETSELECTIONEMPTY → bool
          Return 1 if every selected range is empty else 0.
          which, in Lua, is:
          Editor.SelectionEmpty

          1 Reply Last reply Reply Quote 1
          • dkeenan7D
            dkeenan7
            last edited by dkeenan7

            If an empty selection is at the start or end of a line, then the characters either side of it should not be swapped. If there is only one selection and it is empty and it is at the start or end of a line, then Ctrl+T should revert to transposing lines.

            dinkumoilD 1 Reply Last reply Reply Quote 0
            • dinkumoilD
              dinkumoil @dkeenan7
              last edited by dinkumoil

              @dkeenan7

              I have tried to integrate a slightly modified version of your code above in my LuaScript plugin startup script using CTRL+ALT+0 and Ctrl+Alt+0 (note the different case) as keyboard shortcuts because this shortcut is unused in my Notepad++ installation - it worked. The modifications I made were only to rename the functions to avoid conflicts with my own version of the script (provided in the GitHub issue) I already use. But IMO it should be avoided to set a keyboard shortcut by script anyway because of possible conflicts with existing shortcuts.

              Would it be easy for you to add that to your script?

              Since you seem not to be a novice in programming, how about doing it yourself? Thus you will learn something useful.

              Hint: Visit the >>> documentation site of the LuaScript plugin <<<, there you will find all information you need. Beyond the functions I’ve used in my script I guess the following functions could be useful for that task as well:

              • editor.CurrentPos
              • editor:LineFromPosition
              • editor.LineEndPosition

              Please note:

              1. Don’t confuse Editor and editor. The first one is the class name and the second one is the instance name. Lua is case sensitive.
              2. In Lua calling a method is different from using a property. The first one requires a colon as delimiter between instance and method name (thus editor:LineFromPosition) and the latter one requires a dot as delimiter (thus editor.CurrentPos).
              3. In Lua there is no difference between arrays and lists, both are implemented as dictionaries with the index as the key. The routines of the runtime assume that indices are 1-based. That’s the reason why I wrote a sort function by myself.

              For further information about the Lua language visit the >>> online version of “Programming in Lua” <<< written by the author of the language.

              dkeenan7D 1 Reply Last reply Reply Quote 3
              • dkeenan7D
                dkeenan7 @dinkumoil
                last edited by

                @dinkumoil Ctrl+Alt+0 is also unused in my Notepad++ installation. I tried
                npp.AddShortcut("Transpose Selections", "Ctrl+Alt+0", transposeSelections)
                npp.AddShortcut("Transpose Selections", "CTRL+ALT+0", transposeSelections)
                Neither of them added the keyboard shortcut for me. Are you sure they worked for you? I fooled myself for a while, that “Ctrl+Shift+T” had worked, but it was merely that I had previously set it manually, using Shortcut Mapper.

                But IMO it should be avoided to set a keyboard shortcut by script anyway because of possible conflicts with existing shortcuts.

                In this case, we expect there to be a conflict as we are deliberately extending the function of the likely existing Ctrl+T. The script could first remove the existing assignment of Ctrl+T. Ideally it would only do this if it was the shortcut for SCI_LINETRANSPOSE. But I see no way to determine that. And I tried
                editor:ClearCmdKey(string.byte("T"), SCMOD_CTRL)
                but this had no effect. I also tried substituting editor1, editor2, console and input for editor. Still no effect.

                Yes, I will add the character-transpose function and post the script. Thanks for the hints.

                dinkumoilD 1 Reply Last reply Reply Quote 1
                • dinkumoilD
                  dinkumoil @dkeenan7
                  last edited by dinkumoil

                  @dkeenan7 said in Transpose multiple selections:

                  I tried
                  npp.AddShortcut("Transpose Selections", "Ctrl+Alt+0", transposeSelections)

                  The correct syntax is:

                  npp.AddShortcut("Transpose Selections", "Ctrl+Alt+0", function()
                    transposeSelections()
                  end)
                  

                  The third parameter of AddShortcut is called a closure or anonymous function. It calls the actual handler function transposeSelections. See >>> here <<<.

                  dkeenan7D 1 Reply Last reply Reply Quote 1
                  • dkeenan7D
                    dkeenan7 @dinkumoil
                    last edited by

                    @dinkumoil said in Transpose multiple selections:

                    The third parameter of AddShortcut is called a closure or anonymous function. It calls the actual handler function transposeSelections. See >>> here <<<.

                    Because transposeSelections has no parameters, it works just fine as a closure here without being anonymised. I only have to drop the trailing empty parentheses “()”.

                    1 Reply Last reply Reply Quote 1
                    • dkeenan7D
                      dkeenan7
                      last edited by dkeenan7

                      Here is a Transpose script that includes transposing characters either side of insertion points.

                      You will see that I have also added code to handle virtual space, when transposing the rows of a rectangular selection. If you apply it to sasumner’s example you will see that it does give a visual improvement, but still does not give the result I want. I believe my desired result can be achieved by inserting and removing real spaces. But that’s enough work for one day. :-)

                      -- Startup script for LuaScript plugin
                      -- Changes will take effect once Notepad++ is restarted
                      
                      -- ================================================================================
                      -- Add a menu entry (intended for Ctrl+T shortcut) to transpose multiple selections 
                      -- by rotating them in the order they were selected, 
                      -- and to transpose characters either side of an insertion point, 
                      -- and to transpose lines if neither of the above apply.
                      -- Modified from a script kindly written by Andreas Heim (dinkumoil), 2019-10-25
                      -- ================================================================================
                      
                      -- Get text and virtual space of selection with index idx
                      local function getSelectionNTextVirtual(idx)
                      	local str = editor:textrange(editor.SelectionNStart[idx], editor.SelectionNEnd[idx])
                      	if not str then str = "" end
                      	local aVirt = editor.SelectionNAnchorVirtualSpace[idx]
                      	local cVirt = editor.SelectionNCaretVirtualSpace[idx]
                      
                      	return str, aVirt, cVirt
                      end
                      
                      
                      -- Set text and virtual space of selection with index idx and readjust its span
                      local function setSelectionNTextVirtual(idx, str, aVirt, cVirt)
                      	editor:SetTargetRange(editor.SelectionNStart[idx], editor.SelectionNEnd[idx])
                      	editor:ReplaceTarget(str)
                      
                      	editor.SelectionNCaret[idx] = editor.SelectionNAnchor[idx] + #str
                      	editor.SelectionNAnchorVirtualSpace[idx] = aVirt
                      	editor.SelectionNCaretVirtualSpace[idx] = cVirt
                      end
                      
                      
                      -- Is the given position at the start or end of a line?
                      local function isStartOrEndOfLine(pos)
                      	local line, lineStart, lineEnd
                      	line = editor:LineFromPosition(pos)
                      	lineStart = editor:PositionFromLine(line)
                      	lineEnd = editor.LineEndPosition[line]
                      	return pos == lineStart or pos == lineEnd
                      end
                      
                      
                      -- Transpose the characters either side of the given position
                      local function transposeCharactersAt(pos)
                      	local before = editor:PositionBefore(pos) -- Allow for multi-byte characters
                      	local after = editor:PositionAfter(pos)
                      	local str = editor:textrange(pos, after) -- Save the character to the right of the insertion point
                      	editor:SetTargetRange(pos, after) -- Target the character to the right of the insertion point
                      	editor:ReplaceTarget(editor:textrange(before, pos)) -- Replace the char to the right with the char to the left
                      	editor:SetTargetRange(before, pos) -- Target the character to the left of the insertion point
                      	editor:ReplaceTarget(str) -- Replace the char to the left with the saved char from the right
                      end
                      
                      
                      -- Transpose selections or characters or lines.
                      -- If there is only one selection and it is non-empty or 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 insertion points, then transpose the characters on either side of them 
                      -- unless they are at the start or end of a line
                      -- If there are less than 2 selections, the standard Line Transpose function is used
                      local function transposeSelectionsCharactersLines()
                      	-- According to the Scintilla documentation, there is always at least one selection
                      	local lastIdx = editor.Selections-1 -- Indexes for multiple selections are zero-based
                        
                      	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
                      			editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
                      			
                      			-- 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
                      					-- For some unknown reason, the insertion point will now be to the left of 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
                      			
                      			editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
                      		end
                      	else -- Else selections are not all empty
                      		if lastIdx == 0 then -- If there is only 1 selection
                      			editor:LineTranspose() -- Transpose the current line with the one before
                      		else -- Else there are 2 or more selections
                      			editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
                      			
                      			-- Rotate selection contents between selections
                      			local lastStr, lastAVirt, lastCVirt = 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, lastStr, lastAVirt, lastCVirt) -- Make the first selection be the saved last selection
                      			
                      			editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
                      		end
                      	end
                      end
                      
                      
                      -- Add menu entry for transposing 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)
                      npp.AddShortcut("Transpose Selections/Characters/Lines", "Ctrl+T", transposeSelectionsCharactersLines)
                      
                      dinkumoilD 1 Reply Last reply Reply Quote 2
                      • dinkumoilD
                        dinkumoil @dkeenan7
                        last edited by

                        @dkeenan7

                        Nice!

                        I believe my desired result can be achieved by inserting and removing real spaces.

                        I stay tuned!

                        dkeenan7D 1 Reply Last reply Reply Quote 1
                        • dkeenan7D
                          dkeenan7 @dinkumoil
                          last edited by dinkumoil

                          @dinkumoil @sasumner That was waaay harder than I expected. I was tearing out what little hair I have left, while debugging it. :-) I eventually started over from scratch with a blank sheet of paper on which I mapped out a grid of all the possible combinations of virtual space to the left and right of the selection and the replacement, and what should be done in each case, then simplified it Karnaugh-map style. And it finally does what I expect it to do with rectangular selections that include virtual space.

                          So, please install this script, map it to Ctrl+T, take your most ragged set of line endings, alt-select a rectangle through the middle of them and rotate them to your heart’s content.

                          That was a lot of work for a feature probably no-one will ever use. But I couldn’t let it beat me. :-)

                          But everyone can benefit from this script’s basic Ctrl+T functions of swapping two adjacent characters or swapping two disjoint selections. I used these myself several times while writing, and rewriting, the virtual rectangle code.

                          Thanks again dinkumoil, for introducing me to Lua and scripting of Notepad++. Here’s the final code:

                          -- Startup script for LuaScript plugin
                          -- Changes will take effect once Notepad++ is restarted
                          
                          -- ================================================================================
                          -- Add a menu entry (intended for Ctrl+T shortcut) to transpose multiple selections 
                          -- (disjoint or rectangular) by rotating them in the order they were selected, 
                          -- and to transpose characters either side of an insertion point, 
                          -- and to transpose lines if neither of the above apply.
                          -- Modified from a script kindly written by Andreas Heim (dinkumoil), 2019-10-25
                          -- ================================================================================
                          
                          -- Get text of selection with index idx, converting any trailing virtual space to real spaces
                          local function getSelectionNTextVirtual(idx)
                          	local startVirt = editor.SelectionNAnchorVirtualSpace[idx] -- Number of positions virtual anchor is past real anchor 
                          	local endVirt = editor.SelectionNCaretVirtualSpace[idx] -- Number of positions virtual caret is past real caret
                          	if startVirt > endVirt then startVirt, endVirt = endVirt, startVirt end -- Ensure startVirt < endVirt
                          	
                          	local selStart = editor.SelectionNStart[idx]
                          	local selEnd = editor.SelectionNEnd[idx]
                          	local str = editor:textrange(selStart, selEnd)
                          	if not str then str = "" end
                          	
                          	-- The following line was added to give the behaviour I expect when transposing rectangular selections
                          	-- that contain virtual space. Dave Keenan, 2019-10-30
                          	str = str .. string.rep(" ", endVirt - startVirt) -- Convert virtual space at the end of the selection into real spaces at the end of the string
                          
                          	return str
                          end
                          
                          
                          -- Set text of selection with index idx, converting any trailing real spaces to virtual space
                          local function setSelectionNTextVirtual(idx, str)
                          	local selStart = editor.SelectionNStart[idx] -- The start of the selection
                          	local selEnd = editor.SelectionNEnd[idx] -- The end of the selection
                          	local lineEnd = editor.LineEndPosition[editor:LineFromPosition(selEnd)] -- The end of the line that the selection ends on
                          	
                          	local startVirt = editor.SelectionNAnchorVirtualSpace[idx] -- The number of positions virtual anchor is past real anchor
                          	local endVirt = editor.SelectionNCaretVirtualSpace[idx] -- The number of positions virtual caret is past real caret
                          	if startVirt > endVirt then startVirt = endVirt end -- Ensure startVirt really is the starting virtual space
                          	
                          	-- The following section was added to give the behaviour I expect when transposing rectangular selections
                          	-- that contain virtual space. Dave Keenan, 2019-10-30
                          	-- It has the unavoidable side effect of leaving no trailing spaces on any line that ends with a selection.
                          	-- This (fairly benign) side effect could be eliminated if Rotate Selections was implemented as a Scintilla method.
                          
                          	-- If there is no text after the selection, convert any trailing spaces in the replacement into end virtual space
                          	-- and if the replacement is then empty, convert spaces before the selection into start virtual space, otherwise do the opposite conversion.
                          	endVirt = 0 -- Set end virtual space to a default value of zero
                          	if selEnd == lineEnd then -- If there is no text after the selection
                          		-- Convert any spaces at the end of the replacement string, into virtual space at the end of the new selection
                          		local origStrLen = #str -- Save the original string length
                          		str = string.gsub(str, " +$", "") -- Remove any trailing spaces
                          		endVirt = origStrLen - #str -- Set end virtual to the number of spaces removed. Assumes start virtual will be zero. May be modified below.
                          		-- If the replacement is now empty, convert spaces before the selection into start virtual space, otherwise do the opposite conversion
                          		if #str == 0 then -- If the replacement string is now empty
                          			-- Convert any real spaces before the selection, into virtual space
                          			local before = editor:PositionBefore(selStart) -- Get the character position before the present selection start
                          			while editor:textrange(before, selStart) == " " do -- While the character before the selection is a space
                          				editor:SetTargetRange(before, selStart) -- Target the space
                          				editor:ReplaceTarget("") -- Delete the space
                          				selStart = selStart - 1 -- Shift the real selection start back
                          				selEnd = selEnd - 1 -- Shift the real selection end back
                          				startVirt = startVirt + 1 -- Increment the start virtual space
                          				before = editor:PositionBefore(selStart) -- Get the character position before the new selection start
                          			end
                          			endVirt = endVirt + startVirt -- End virtual previously assumed start virtual was zero
                          		else -- Else the replacement string is not now empty
                          			-- Convert any virtual space before the selection, into real spaces
                          			editor:SetTargetRange(selStart, selStart) -- Target the start of the selection
                          			editor:ReplaceTarget(string.rep(" ", startVirt)) -- Insert a number of spaces corresponding to start virtual
                          			selStart = selStart + startVirt -- New start is old start plus the number of spaces inserted
                          			selEnd = selEnd + startVirt -- New end is old end plus the number of spaces inserted
                          			startVirt = 0 -- New start virtual is zero. End virtual is unchanged.
                          		end
                          	end
                          	
                          	-- Update the text and position and virtual space of the selection
                          	editor:SetTargetRange(selStart, selEnd) -- Target the existing selection position
                          	editor:ReplaceTarget(str) -- Replace the selection contents with the possibly-modified replacement string
                          	editor.SelectionNStart[idx] = selStart -- Set the selection start
                          	selEnd = selStart + #str -- Set the new selection end based on the length of the replacement string
                          	editor.SelectionNEnd[idx] = selEnd -- Set the selection end
                          	editor.SelectionNAnchorVirtualSpace[idx] = startVirt -- Update the selection start virtual space
                          	editor.SelectionNCaretVirtualSpace[idx] = endVirt  -- Update the selection end virtual space
                          end
                          
                          
                          -- Is the given position at the start or end of a line?
                          local function isStartOrEndOfLine(pos)
                          	local line, lineStart, lineEnd
                          	line = editor:LineFromPosition(pos)
                          	lineStart = editor:PositionFromLine(line)
                          	lineEnd = editor.LineEndPosition[line]
                          	return pos == lineStart or pos == lineEnd
                          end
                          
                          
                          -- Transpose the characters either side of the given position
                          local function transposeCharactersAt(pos)
                          	local before = editor:PositionBefore(pos) -- Allow for multi-byte characters
                          	local after = editor:PositionAfter(pos)
                          	local str = editor:textrange(pos, after) -- Save the character after the insertion point
                          	editor:SetTargetRange(pos, after) -- Target the character after the insertion point
                          	editor:ReplaceTarget(editor:textrange(before, pos)) -- Replace the char before with the char after
                          	editor:SetTargetRange(before, pos) -- Target the character before the insertion point
                          	editor:ReplaceTarget(str) -- Replace the char before with the saved char from after
                          end
                          
                          
                          -- Transpose selections or characters or lines.
                          -- If there is only one selection and it is non-empty or 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 insertion points, then transpose the characters on either side of them 
                          -- unless they are at the start or end of a line.
                          -- If there are 2 or more selections that are not all empty, rotate the selection contents between selections.
                          local function transposeSelectionsCharactersLines()
                          	-- According to the Scintilla documentation, there is always at least one selection
                          	local lastIdx = editor.Selections-1 -- Indexes for multiple selections are zero-based
                            
                          	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
                          			editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
                          			
                          			-- 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
                          					-- For some unknown reason, 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
                          			
                          			editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
                          		end
                          	else -- Else selections are not all empty
                          		if lastIdx == 0 then -- If there is only 1 selection
                          			editor:LineTranspose() -- Transpose the current line with the one before
                          		else -- Else there are 2 or more selections
                          			editor:BeginUndoAction() -- Begin the sequence of actions to be undone or redone as a unit
                          			
                          			-- Rotate selection contents between selections
                          			local lastStr = 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, lastStr) -- Make the first selection be the saved last selection
                          			
                          			editor:EndUndoAction() -- End the sequence of actions to be undone or redone as a unit
                          		end
                          	end
                          end
                          
                          
                          -- Add menu entry for transposing 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) -- Doesn't work
                          npp.AddShortcut("Transpose Selections/Characters/Lines", "Ctrl+T", transposeSelectionsCharactersLines)
                          
                          1 Reply Last reply Reply Quote 2
                          • dkeenan7D
                            dkeenan7
                            last edited by

                            I’d be pleased if a moderator would edit the above post so the code is syntax-highlighted as Lua. I learned how to do that a few minutes too late. :-) You could then delete this post. Thanks.

                            dinkumoilD 1 Reply Last reply Reply Quote 0
                            • dkeenan7D
                              dkeenan7
                              last edited by

                              For the general reader:

                              This script lets you correct common character-transposition typos, like “teh” instead of “the”, by clicking between the transposed characters (or arrow-keying back to between them) and hitting Ctrl+T. It also lets you swap two selections. You hold Ctrl to make the second selection. For example, you might want to swap the if and else code in a conditional.

                              The original function of Ctrl+T, i.e. swapping a line with the one above it, is still available by clicking at the start or end of a line, or by making a single non-empty selection anywhere in the line.

                              If you want to install Ctrl+T for “Transpose Selections/Characters/Lines” into Notepad++:

                              1. Install the “LuaScript” plugin using Plugins -> Plugins Admin.
                              2. Choose Plugins -> LuaScript -> Edit Startup Script, and paste in the code from this post:
                                https://community.notepad-plus-plus.org/topic/18415/transpose-multiple-selections/11
                              3. Exit and relaunch Notepad++.
                              4. Use Settings -> Shortcut Mapper -> Plugin commands -> Transpose Selections/Characters/Lines -> Modify, to add Ctrl+T as the shortcut. Ignore the conflict with the shortcut for Scintilla SCI_LINETRANSPOSE.
                              5. Choose Settings -> Preferences -> Editing -> Multi-Edit Settings -> Enable.

                              Many thanks to @dinkumoil @sasumner and Neil Hodgson.

                              1 Reply Last reply Reply Quote 2
                              • dinkumoilD
                                dinkumoil @dkeenan7
                                last edited by

                                @dkeenan7

                                Many thanks for that code and many respect for all the hard work!

                                I was tearing out what little hair I have left, while debugging it.

                                I thought so. ;-)

                                I’d be pleased if a moderator would edit the above post so the code is syntax-highlighted as Lua.

                                If you mean this syntax

                                ```Lua
                                some code
                                ```
                                

                                it doesn’t work, the forum doesn’t support Lua syntax highlighting. Above I’ve changed it to SQL as it uses the same syntax for line comments as Lua, thus it produces a result that is closest as possible to the desired result.

                                1 Reply Last reply Reply Quote 2
                                • dkeenan7D
                                  dkeenan7
                                  last edited by

                                  Although it’s too late to solve the problem that @X had in this thread:
                                  https://community.notepad-plus-plus.org/topic/18448/is-it-possible-to-switch-column-lines
                                  I have added Reverse Selections/Characters Ctrl+Alt+Shift+T to our transpose script. If you Alt-select a column, it will reverse the rows.

                                  We now have:
                                  Transpose Selections/Characters/Lines Ctrl+T (rotate forwards)
                                  Rotate Backwards Selections/Characters Ctrl+Alt+T
                                  Reverse Selections/Characters Ctrl+Alt+Shift+T

                                  In the case of a single selection with 2 or more characters, each of the above will now rotate or reverse the characters in the selection.

                                  It’s such a monster now, that I have to post it in two pieces. It exceeds the limit of 16 768 bytes for a post.

                                  -- Copy into the startup script for the LuaScript plugin
                                  -- Changes will take effect once Notepad++ is restarted
                                  
                                  -- ================================================================================
                                  -- Adds menu entries (intended for Ctrl+mod+T shortcuts) to transpose multiple selections 
                                  -- (disjoint or rectangular) by rotating them in the order they were selected (Ctrl+T), 
                                  -- or rotate them backwards (Ctrl+Alt+T), or reverse them (Ctrl+Alt+Shift+T), 
                                  -- and to do the same with the characters of a single non-empty selection.
                                  -- and to transpose characters either side of an empty selection, 
                                  -- and to transpose lines if none of the above apply.
                                  -- Modified from a script kindly written by Andreas Heim (dinkumoil), 2019-10-25
                                  -- ================================================================================
                                  
                                  
                                  -- Get the number of positions the virtual start is past the real start, for the selection with index idx
                                  local function getSelectionNStartVirtualSpace(idx)
                                  	local anchor = editor.SelectionNAnchor[idx]
                                  	local caret = editor.SelectionNCaret[idx]
                                  	local anchorVirtual = editor.SelectionNAnchorVirtualSpace[idx]
                                  	local caretVirtual = editor.SelectionNCaretVirtualSpace[idx]
                                  	if anchor < caret or (anchor == caret and anchorVirtual < caretVirtual) then
                                  		return anchorVirtual
                                  	else
                                  		return caretVirtual
                                  	end
                                  end
                                  
                                  
                                  -- Get the number of positions the virtual end is past the real end, for the selection with index idx
                                  local function getSelectionNEndVirtualSpace(idx)
                                  	local anchor = editor.SelectionNAnchor[idx]
                                  	local caret = editor.SelectionNCaret[idx]
                                  	local anchorVirtual = editor.SelectionNAnchorVirtualSpace[idx]
                                  	local caretVirtual = editor.SelectionNCaretVirtualSpace[idx]
                                  	if anchor < caret or (anchor == caret and anchorVirtual < caretVirtual) then
                                  		return caretVirtual
                                  	else
                                  		return anchorVirtual
                                  	end
                                  end
                                  
                                  
                                  -- Get text of selection with index idx, converting any trailing virtual space to real spaces
                                  local function getSelectionNTextVirtual(idx)
                                  	local selStart = editor.SelectionNStart[idx]
                                  	local selEnd = editor.SelectionNEnd[idx]
                                  	local startVirtual = getSelectionNStartVirtualSpace(idx) -- Number of positions virtual start is past real start 
                                  	local endVirtual = getSelectionNEndVirtualSpace(idx) -- Number of positions virtual end is past real end
                                  
                                  	local str = editor:textrange(selStart, selEnd) or ""
                                  
                                  	-- The following section was added to give the behaviour I expect when transposing rectangular selections
                                  	-- that contain virtual space. Dave Keenan, 2019-11-01
                                  	if selStart == selEnd then -- If the real part of the selection is empty
                                  		endVirtual = endVirtual - startVirtual -- The virtual part that comes after, is the difference between end and start virtual
                                  	-- else any start virtual is on a different line and so should not be subtracted from end virtual
                                  	end
                                  	str = str .. string.rep(" ", endVirtual) -- Convert virtual space at the end of the selection into real spaces at the end of the string
                                  
                                  	return str
                                  end
                                  
                                  
                                  -- Set text of selection with index idx, converting any trailing real spaces to virtual space
                                  local function setSelectionNTextVirtual(idx, str)
                                  	local selStart = editor.SelectionNStart[idx] -- The start of the selection
                                  	local selEnd = editor.SelectionNEnd[idx] -- The end of the selection
                                  	local lineEnd = editor.LineEndPosition[editor:LineFromPosition(selEnd)] -- The end of the line that the selection ends on
                                  	
                                  	local startVirt = getSelectionNStartVirtualSpace(idx) -- The number of positions virtual start is past real start
                                  	
                                  	-- The following section was added to give the behaviour I expect when transposing rectangular selections
                                  	-- that contain virtual space. Dave Keenan, 2019-11-01
                                  	-- It has the fairly benign side effect of leaving no trailing spaces on any line that ends with a selection.
                                  
                                  	-- If there is no text after the selection, convert any trailing spaces in the replacement into end virtual space
                                  	-- and if the replacement is then empty, convert spaces before the selection into start virtual space, otherwise do the opposite conversion.
                                  	local endVirt = 0 -- Set end virtual space to a default value of zero
                                  	if selEnd == lineEnd then -- If there is no text after the selection
                                  		-- Convert any spaces at the end of the replacement string, into virtual space at the end of the new selection
                                  		local origStrLen = #str -- Save the original string length
                                  		str = string.gsub(str, " +$", "") -- Remove any trailing spaces
                                  		endVirt = origStrLen - #str -- Set end virtual to the number of spaces removed. Assumes start virtual will be zero. May be modified below.
                                  		-- If the replacement is now empty, convert spaces before the selection into start virtual space, otherwise do the opposite conversion
                                  		if #str == 0 then -- If the replacement string is now empty
                                  			-- Convert any real spaces before the selection, into virtual space
                                  			local before = editor:PositionBefore(selStart) -- Get the character position before the present selection start
                                  			while editor:textrange(before, selStart) == " " do -- While the character before the selection is a space
                                  				editor:SetTargetRange(before, selStart) -- Target the space
                                  				editor:ReplaceTarget("") -- Delete the space
                                  				selStart = selStart - 1 -- Shift the real selection start back
                                  				selEnd = selEnd - 1 -- Shift the real selection end back
                                  				startVirt = startVirt + 1 -- Increment the start virtual space
                                  				before = editor:PositionBefore(selStart) -- Get the character position before the new selection start
                                  			end
                                  			endVirt = endVirt + startVirt -- End virtual previously assumed start virtual was zero
                                  		else -- Else the replacement string is not now empty
                                  			-- Convert any virtual space before the selection, into real spaces
                                  			editor:SetTargetRange(selStart, selStart) -- Target the start of the selection
                                  			editor:ReplaceTarget(string.rep(" ", startVirt)) -- Insert a number of spaces corresponding to start virtual
                                  			selStart = selStart + startVirt -- New start is old start plus the number of spaces inserted
                                  			selEnd = selEnd + startVirt -- New end is old end plus the number of spaces inserted
                                  			startVirt = 0 -- New start virtual is zero. End virtual is unchanged.
                                  		end
                                  	end
                                  	
                                  	-- Update the text and position and virtual space of the selection
                                  	editor:SetTargetRange(selStart, selEnd) -- Target the existing selection position
                                  	editor:ReplaceTarget(str) -- Replace the selection contents with the possibly-modified replacement string
                                  	editor.SelectionNStart[idx] = selStart -- Set the selection start
                                  	selEnd = selStart + #str -- Set the new selection end based on the length of the replacement string
                                  	editor.SelectionNEnd[idx] = selEnd -- Set the selection end
                                  	-- Setting SelectionNStart and SelectionNEnd above, will have ensured that the anchor is the start
                                  	-- and the caret is the end, so that the following will be valid.
                                  	editor.SelectionNAnchorVirtualSpace[idx] = startVirt -- Update the selection start virtual space
                                  	editor.SelectionNCaretVirtualSpace[idx] = endVirt  -- Update the selection end virtual space
                                  end
                                  
                                  
                                  -- Is the given position at the start or end of a line?
                                  local function isStartOrEndOfLine(pos)
                                  	local line, lineStart, lineEnd
                                  	line = editor:LineFromPosition(pos)
                                  	lineStart = editor:PositionFromLine(line)
                                  	lineEnd = editor.LineEndPosition[line]
                                  	return pos == lineStart or pos == lineEnd
                                  end
                                  
                                  
                                  -- Transpose the characters either side of the given position
                                  local function transposeCharactersAt(pos)
                                  	local before = editor:PositionBefore(pos) -- Allow for multi-byte characters
                                  	local after = editor:PositionAfter(pos)
                                  	local str = editor:textrange(pos, after) -- Save the character after the insertion point
                                  	editor:SetTargetRange(pos, after) -- Target the character after the insertion point. Doesn't work with different character widths if you go the other way first.
                                  	editor:ReplaceTarget(editor:textrange(before, pos)) -- Replace the char after with the char before. 
                                  	editor:SetTargetRange(before, pos) -- Target the character before the insertion point
                                  	editor:ReplaceTarget(str) -- Replace the char before with the saved char from after
                                  end
                                  
                                  
                                  -- Rotate the characters between the given positions
                                  local function rotateCharacters(start, endd)
                                  	if start == endd then return end
                                  	local after = endd
                                  	local pos = editor:PositionBefore(endd) -- Allow for multi-byte characters
                                  	if pos == start then return end
                                  	local before = editor:PositionBefore(pos)
                                  	local str = editor:textrange(pos, after) -- Save the last character
                                  	while before > start do -- While we haven't reached the start
                                  		editor:SetTargetRange(pos, after) -- Target the after character. Doesn't work with different character widths if you go the other way first.
                                  		editor:ReplaceTarget(editor:textrange(before, pos)) -- Replace the after char with the before character
                                  		-- Move backwards
                                  		pos = before
                                  		before = editor:PositionBefore(pos)
                                  		after = editor:PositionAfter(pos)  -- Can't just assign the previous pos, as the character width may have changed
                                  	end
                                  	editor:SetTargetRange(pos, after) -- Target the after character
                                  	editor:ReplaceTarget(editor:textrange(before, pos)) -- Replace the after char with the before character
                                  	
                                  	editor:SetTargetRange(before, pos) -- Target the first character
                                  	editor:ReplaceTarget(str) -- Replace the first character with the saved last character
                                  end
                                  
                                  
                                  -- Rotate the characters backwards between the given positions
                                  local function rotateCharactersBackwards(start, endd)
                                  	-- This is tricky to make work with multi-byte characters, which include CR LF newlines.
                                  	-- We can't simply reverse direction relative to the forward rotation function above.
                                  	-- We still need to work backwards.
                                  	if start == endd then return end
                                  	local afterStart = editor:PositionAfter(start)
                                  	if afterStart == endd then return end
                                  	local posStr = editor:textrange(start, afterStart) -- Get the first character
                                  	local pos = endd
                                  	local before = editor:PositionBefore(endd) -- Allow for multi-byte characters
                                  	while before > start do -- While we haven't reached the start
                                  		local beforeStr = editor:textrange(before, pos) -- Save the before character
                                  		editor:SetTargetRange(before, pos) -- Target the before character.
                                  		editor:ReplaceTarget(posStr) -- Replace the before character with the saved present character
                                  		posStr = beforeStr -- Replace the saved present character with the saved before character
                                  		-- Move backwards
                                  		pos = before
                                  		before = editor:PositionBefore(pos)
                                  	end
                                  	editor:SetTargetRange(before, pos) -- Target the first character
                                  	editor:ReplaceTarget(posStr) -- Replace the first character with the saved present character
                                  end
                                  
                                  
                                  -- Reverse the characters between the given positions
                                  local function reverseCharacters(start, endd)
                                  	if start == endd then return end
                                  	local afterStart = editor:PositionAfter(start) -- Allow for multi-byte characters
                                  	if afterStart == endd then return end
                                  	local beforeEnd = editor:PositionBefore(endd)
                                  	while afterStart <= beforeEnd do -- While we haven't reached the middle
                                  		local str = editor:textrange(beforeEnd, endd) -- Save the character from the last half
                                  		editor:SetTargetRange(beforeEnd, endd) -- Target the character in the last half. Doesn't work with different character widths if you go the other way first.
                                  		editor:ReplaceTarget(editor:textrange(start, afterStart)) -- Replace the char in the last half with its opposite in the first half
                                  		editor:SetTargetRange(start, afterStart) -- Target the character in the first half
                                  		editor:ReplaceTarget(str) -- Replace the char in the first half with the saved char from the last half
                                  		-- Move inward
                                  		start = editor:PositionAfter(start) -- Can't just assign afterStart, as the character width may have changed
                                  		endd = editor:PositionBefore(endd)
                                  		afterStart = editor:PositionAfter(start)
                                  		beforeEnd = editor:PositionBefore(endd)
                                  	end
                                  end
                                  
                                  
                                  
                                  1 Reply Last reply Reply Quote 3
                                  • dkeenan7D
                                    dkeenan7
                                    last edited by

                                    -- 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)
                                    
                                    
                                    1 Reply Last reply Reply Quote 3
                                    • First post
                                      Last post
                                    The Community of users of the Notepad++ text editor.
                                    Powered by NodeBB | Contributors