Skip to content

Commit 8be06f4

Browse files
committed
True multi-caret editing support ✅
1 parent 02abe0c commit 8be06f4

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

Components/ScintEdit.pas

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ TScintRangeList = class(TList<TScintRange>)
6565
TScintRectangle = record
6666
Left, Top, Right, Bottom: Integer;
6767
end;
68+
TScintSelectionMode = (ssmStream, ssmRectangular, ssmLines, ssmThinRectangular);
6869
TScintStyleNumber = 0..StyleNumbers-1;
6970
TScintVirtualSpaceOption = (svsRectangularSelection, svsUserAccessible,
7071
svsNoWrapLineStart);
@@ -79,6 +80,8 @@ TScintRangeToFormat = record
7980
TScintEditStrings = class;
8081
TScintCustomStyler = class;
8182

83+
EScintEditError = class(Exception);
84+
8285
TScintEdit = class(TWinControl)
8386
private
8487
FAcceptDroppedFiles: Boolean;
@@ -135,6 +138,7 @@ TScintEdit = class(TWinControl)
135138
function GetSelectionAnchorPosition(Selection: Integer): Integer;
136139
function GetSelectionCaretPosition(Selection: Integer): Integer;
137140
function GetSelectionCount: Integer;
141+
function GetSelectionMode: TScintSelectionMode;
138142
function GetSelText: String;
139143
function GetTopLine: Integer;
140144
function GetZoom: Integer;
@@ -157,6 +161,7 @@ TScintEdit = class(TWinControl)
157161
procedure SetSelection(const Value: TScintRange);
158162
procedure SetSelectionAnchorPosition(Selection: Integer; const AnchorPos: Integer);
159163
procedure SetSelectionCaretPosition(Selection: Integer; const CaretPos: Integer);
164+
procedure SetSelectionMode(const Value: TScintSelectionMode);
160165
procedure SetSelText(const Value: String);
161166
procedure SetStyler(const Value: TScintCustomStyler);
162167
procedure SetTabWidth(const Value: Integer);
@@ -185,7 +190,8 @@ TScintEdit = class(TWinControl)
185190
procedure CheckPosRange(const StartPos, EndPos: Integer);
186191
procedure CreateParams(var Params: TCreateParams); override;
187192
procedure CreateWnd; override;
188-
class procedure Error(const S: String);
193+
class function GetErrorException(const S: String): EScintEditError;
194+
class procedure Error(const S: String); overload;
189195
class procedure ErrorFmt(const S: String; const Args: array of const);
190196
function GetMainSelection: Integer;
191197
function GetTarget: TScintRange;
@@ -316,6 +322,7 @@ TScintEdit = class(TWinControl)
316322
property SelectionAnchorPosition[Selection: Integer]: Integer read GetSelectionAnchorPosition write SetSelectionAnchorPosition;
317323
property SelectionCaretPosition[Selection: Integer]: Integer read GetSelectionCaretPosition write SetSelectionCaretPosition;
318324
property SelectionCount: Integer read GetSelectionCount;
325+
property SelectionMode: TScintSelectionMode read GetSelectionMode write SetSelectionMode;
319326
property SelText: String read GetSelText write SetSelText;
320327
property Styler: TScintCustomStyler read FStyler write SetStyler;
321328
property TopLine: Integer read GetTopLine write SetTopLine;
@@ -462,8 +469,6 @@ TScintPixmap = class
462469
property Pixmap: Pointer read GetPixmap;
463470
end;
464471

465-
EScintEditError = class(Exception);
466-
467472
function ScintRawStringIsBlank(const S: TScintRawString): Boolean;
468473

469474
implementation
@@ -743,9 +748,14 @@ procedure TScintEdit.EndUndoAction;
743748
Call(SCI_ENDUNDOACTION, 0, 0);
744749
end;
745750

751+
class function TScintEdit.GetErrorException(const S: String): EScintEditError;
752+
begin
753+
Result := EScintEditError.Create('TScintEdit error: ' + S);
754+
end;
755+
746756
class procedure TScintEdit.Error(const S: String);
747757
begin
748-
raise EScintEditError.Create('TScintEdit error: ' + S);
758+
raise GetErrorException(S);
749759
end;
750760

751761
class procedure TScintEdit.ErrorFmt(const S: String; const Args: array of const);
@@ -856,8 +866,9 @@ function TScintEdit.GetLineEndings: TScintLineEndings;
856866
case Call(SCI_GETEOLMODE, 0, 0) of
857867
SC_EOL_CR: Result := sleCR;
858868
SC_EOL_LF: Result := sleLF;
869+
SC_EOL_CRLF: Result := sleCRLF;
859870
else
860-
Result := sleCRLF;
871+
raise GetErrorException('Unexpected SCI_GETEOLMODE result');
861872
end;
862873
end;
863874

@@ -1077,6 +1088,18 @@ function TScintEdit.GetSelectionCount: Integer;
10771088
Result := Call(SCI_GETSELECTIONS, 0, 0);
10781089
end;
10791090

1091+
function TScintEdit.GetSelectionMode: TScintSelectionMode;
1092+
begin
1093+
case Call(SCI_GETSELECTIONMODE, 0, 0) of
1094+
SC_SEL_STREAM: Result := ssmStream;
1095+
SC_SEL_RECTANGLE: Result := ssmRectangular;
1096+
SC_SEL_LINES: Result := ssmLines;
1097+
SC_SEL_THIN: Result := ssmThinRectangular;
1098+
else
1099+
raise GetErrorException('Unexpected SCI_GETSELECTIONMODE result');
1100+
end;
1101+
end;
1102+
10801103
function TScintEdit.GetSelText: String;
10811104
begin
10821105
Result := ConvertRawStringToString(GetRawSelText);
@@ -1536,6 +1559,21 @@ procedure TScintEdit.SetSelectionCaretPosition(Selection: Integer;
15361559
Call(SCI_SETSELECTIONNCARET, Selection, CaretPos);
15371560
end;
15381561

1562+
procedure TScintEdit.SetSelectionMode(const Value: TScintSelectionMode);
1563+
begin
1564+
var Mode: Integer;
1565+
if Value = ssmStream then
1566+
Mode := SC_SEL_STREAM
1567+
else if Value = ssmRectangular then
1568+
Mode := SC_SEL_RECTANGLE
1569+
else if Value = ssmLines then
1570+
Mode := SC_SEL_LINES
1571+
else
1572+
Mode := SC_SEL_THIN;
1573+
{ Note this uses *CHANGE* and not *SET* }
1574+
Call(SCI_CHANGESELECTIONMODE, Mode, 0);
1575+
end;
1576+
15391577
procedure TScintEdit.SetSelText(const Value: String);
15401578
begin
15411579
SetRawSelText(ConvertStringToRawString(Value));

Projects/Src/CompForm.pas

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2807,6 +2807,18 @@ procedure TCompileForm.HDocClick(Sender: TObject);
28072807
procedure TCompileForm.MemoKeyDown(Sender: TObject; var Key: Word;
28082808
Shift: TShiftState);
28092809
begin
2810+
if Key in [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_HOME, VK_END] then begin
2811+
var Memo := Sender as TScintEdit;
2812+
if Memo.SelectionMode in [ssmRectangular, ssmThinRectangular] then begin
2813+
{ Allow left/right/etc. navigation with rectangular selection, see
2814+
https://sourceforge.net/p/scintilla/feature-requests/1275/ and
2815+
https://sourceforge.net/p/scintilla/bugs/2412/#cb37
2816+
Notepad++ calls this "Enable Column Selection to Multi-editing" which
2817+
is on by default and in VSCode and VS it's also on by default. }
2818+
Memo.SelectionMode := ssmStream;
2819+
end;
2820+
end;
2821+
28102822
if Key = VK_F1 then begin
28112823
var HelpFile := GetHelpFile;
28122824
if Assigned(HtmlHelp) then begin

whatsnew.htm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
<li>Added shortcut to add the next occurrence of the current word or selected text as an additional selection (Alt+Shift+.).</li>
3939
<li>Added shortcut to select all occurrences of the current word or selected text (Alt+Shift+;).</li>
4040
<li>Added shortcuts to add a word or line as an additional selection (Ctrl+Double Click and Ctrl+Triple Click).</li>
41-
<li>Multiple selection now works over horizontal movement and selection commands.</li>
42-
<li>Multiple selection now works over line up and down movement and selection commands.</li>
41+
<li>Multiple selection now works over Left, Right, Up, Down, Home and End navigation and selection commands.</li>
4342
<li>Multiple selection now works over word and line deletion commands, and line end insertion.</li>
43+
<li>Left, Right, etc. navigation with rectangular selection is now allowed which completes "true" multi-caret editing support.</li>
4444
<li>When autocompleting with multiple selections present, the autocompleted text now goes into each selection.</li>
4545
</ul>
4646
<p>Other changes:</p>

0 commit comments

Comments
 (0)