Browse Source

Added shortcut to add cursors to line ends (Shift+Alt+I).

Martijn Laan 1 week ago
parent
commit
048a1a8df9
4 changed files with 91 additions and 1 deletions
  1. 3 0
      ISHelp/isetup.xml
  2. 2 1
      Projects/Src/IDE.IDEScintEdit.pas
  3. 85 0
      Projects/Src/IDE.MainForm.pas
  4. 1 0
      whatsnew.htm

+ 3 - 0
ISHelp/isetup.xml

@@ -3288,6 +3288,9 @@ Filename: "{win}\MYPROG.INI"; Section: "InstallSettings"; Key: "InstallPath"; St
 <tr>
   <td>Add additional cursor or selection down</td><td>Ctrl+Alt+Down</td>
 </tr>
+<tr>
+  <td>Add cursors to line ends</td><td>Shift+Alt+I</td>
+</tr>
 <tr>
   <td>&nbsp;</td>
 </tr>

+ 2 - 1
Projects/Src/IDE.IDEScintEdit.pas

@@ -66,7 +66,7 @@ type
   TIDEScintComplexCommand = (ccNone, ccSelectNextOccurrence,
     ccSelectAllOccurrences, ccSelectAllFindMatches, ccSimplifySelection,
     ccUnfoldLine, ccFoldLine, ccToggleLinesComment, ccAddCursorUp,
-    ccAddCursorDown, ccBraceMatch);
+    ccAddCursorDown, ccBraceMatch, ccAddCursorsToLineEnds);
 
   TIDEScintEdit = class(TScintEdit)
   private
@@ -379,6 +379,7 @@ begin
   { Use freed Ctrl+/ }
   AddComplexCommand(ShortCut(VK_OEM_2, [ssCtrl]), ccToggleLinesComment); { Also see GetComplexCommand for ReadOnly check }
   AddComplexCommand(ShortCut(VK_OEM_5, [ssShift, ssCtrl]), ccBraceMatch);
+  AddComplexCommand(ShortCut(KeyToKeyCode('I'), [ssShift, ssAlt]), ccAddCursorsToLineEnds);
 end;
 
 procedure TIDEScintEdit.SetUseFolding(const Value: Boolean);

+ 85 - 0
Projects/Src/IDE.MainForm.pas

@@ -1304,6 +1304,89 @@ procedure TMainForm.MemoKeyDown(Sender: TObject; var Key: Word;
     end;
   end;
 
+  procedure AddCursorsToLineEnds(const AMemo: TIDEScintEdit);
+  begin
+    { Does not try to keep the main selection. Otherwise behaves the same as
+      observed in Visual Studio Code, see comments. }
+
+    var Selections: TScintCaretAndAnchorList := nil;
+    var VirtualSpaces: TScintCaretAndAnchorList := nil;
+    try
+      Selections := TScintCaretAndAnchorList.Create;
+      VirtualSpaces := TScintCaretAndAnchorList.Create;
+      AMemo.GetSelections(Selections, VirtualSpaces);
+
+      { First remove all empty selections }
+      for var I := Selections.Count-1 downto 0 do begin
+        var Selection := Selections[I];
+        var VirtualSpace := VirtualSpaces[I];
+        if (Selection.CaretPos + VirtualSpace.CaretPos) =
+           (Selection.AnchorPos + VirtualSpace.AnchorPos) then begin
+          Selections.Delete(I);
+          VirtualSpaces.Delete(I);
+        end;
+      end;
+
+      { If all selections were empty do nothing }
+      if Selections.Count = 0 then
+        Exit;
+
+      { Handle non empty selections }
+      for var I := Selections.Count-1 downto 0 do begin
+        var Selection := Selections[I];
+        var Line1 := AMemo.GetLineFromPosition(Selection.CaretPos);
+        var Line2 := AMemo.GetLineFromPosition(Selection.AnchorPos);
+        var SelSingleLine := Line1 = Line2;
+        if SelSingleLine then begin
+          { Single line selections are updated into empty selection at end of selection }
+          var VirtualSpace := VirtualSpaces[I];
+          if Selection.CaretPos + VirtualSpace.CaretPos > Selection.AnchorPos + VirtualSpace.AnchorPos then begin
+            Selection.AnchorPos := Selection.CaretPos;
+            VirtualSpace.AnchorPos := VirtualSpace.CaretPos;
+          end else begin
+            Selection.CaretPos := Selection.AnchorPos;
+            VirtualSpace.CaretPos := VirtualSpace.AnchorPos;
+          end;
+          Selections[I] := Selection;
+          VirtualSpaces[I] := VirtualSpace;
+        end else begin
+          { Multiline selections are replaced by empty selections at each end of line }
+          if Line1 > Line2 then begin
+            var TmpLine := Line1;
+            Line1 := Line2;
+            Line2 := TmpLine;
+          end;
+          { Ignore last line if the selection doesn't really select anything on that line }
+          if Selection.Range.EndPos = AMemo.GetPositionFromLine(Line2) then
+            Dec(Line2);
+          for var Line := Line1 to Line2 do begin
+            Selection.CaretPos := AMemo.GetLineEndPosition(Line);
+            Selection.AnchorPos := Selection.CaretPos;
+            Selections.Add(Selection);
+            VirtualSpaces.Add(TScintCaretAndAnchor.Create(0, 0));
+          end;
+          Selections.Delete(I);
+          VirtualSpaces.Delete(I);
+        end;
+      end;
+
+      { Send updated selections to memo }
+      for var I := 0 to Selections.Count-1 do begin
+        var Selection := Selections[I];
+        var VirtualSpace := VirtualSpaces[I];
+        if I = 0 then
+          AMemo.SetSingleSelection(Selection.CaretPos, Selection.AnchorPos)
+        else
+          AMemo.AddSelection(Selection.CaretPos, Selection.AnchorPos);
+        AMemo.SelectionCaretVirtualSpace[I] := VirtualSpaces[I].CaretPos;
+        AMemo.SelectionAnchorVirtualSpace[I] := VirtualSpaces[I].AnchorPos;
+      end;
+    finally
+      VirtualSpaces.Free;
+      Selections.Free;
+    end;
+  end;
+
 begin
   if (Key in [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_HOME, VK_END]) then begin
     var Memo := Sender as TIDEScintEdit;
@@ -1376,6 +1459,8 @@ begin
             AddCursor(FActiveMemo, ComplexCommand = ccAddCursorUp);
           ccBraceMatch:
             EBraceMatchClick(Self);
+          ccAddCursorsToLineEnds:
+            AddCursorsToLineEnds(FActiveMemo);
           else
             raise Exception.Create('Unknown ComplexCommand');
         end;

+ 1 - 0
whatsnew.htm

@@ -38,6 +38,7 @@ For conditions of distribution and use, see <a href="files/is/license.txt">LICEN
   <li>Added new <i>Select All Occurrences</i> menu item to the <i>Edit</i> menu to select all occurrences of the current word or selected text (Shift+Alt+;).</li>
   <li>Added new <i>Select All Find Matches</i> menu item to the <i>Edit</i> menu to select all matches of the last find text (Alt+Enter).<br />Additionally, the <i>Find</i> (Ctrl+F) and <i>Replace</i> (Ctrl+H) dialogs now both support being closed by Alt+Enter to directly select all matches.</li>
   <li>Added shortcuts to add a cursor or selection up or down (Ctrl+Alt+Up and Ctrl+Alt+Down). For multi-line selections this extends the selection up or down and never shrinks.</li>
+  <li>Added shortcut to add cursors to line ends (Shift+Alt+I). Behaves the same as in Visual Studio Code, so for example does nothing if all selections are empty.</li>
   <li>Added shortcuts to add a word or line as an additional selection (Ctrl+Double Click and Ctrl+Triple Click).</li>
   <li>Added shortcut to remove a selection by clicking it (Ctrl+Click).</li>
   <li>Multiple selection now works over Left, Right, Up, Down, Home and End navigation and selection commands.</li>