diff --git a/icons/other/sort_az.png b/icons/other/sort_az.png
new file mode 100644
index 00000000..8d2a45d8
Binary files /dev/null and b/icons/other/sort_az.png differ
diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi
index 5ba5adab..840565e7 100644
--- a/lazproj/coedit.lpi
+++ b/lazproj/coedit.lpi
@@ -13,7 +13,7 @@
-
+
@@ -100,6 +100,7 @@
+
diff --git a/src/ce_editor.lfm b/src/ce_editor.lfm
index 3b502f15..75dddc6d 100644
--- a/src/ce_editor.lfm
+++ b/src/ce_editor.lfm
@@ -125,6 +125,13 @@ inherited CEEditorWidget: TCEEditorWidget
object MenuItem7: TMenuItem
Caption = '-'
end
+ object mnuedSortLines: TMenuItem
+ Caption = 'Sort lines'
+ OnClick = mnuedSortLinesClick
+ end
+ object MenuItem1: TMenuItem
+ Caption = '-'
+ end
object mnuedComm: TMenuItem
Caption = 'Comment selected lines'
OnClick = mnuedCommClick
diff --git a/src/ce_editor.pas b/src/ce_editor.pas
index 5606b385..fa0978cd 100644
--- a/src/ce_editor.pas
+++ b/src/ce_editor.pas
@@ -52,9 +52,11 @@ type
{ TCEEditorWidget }
TCEEditorWidget = class(TCEWidget, ICEDocumentObserver, ICEMultiDocHandler, ICEProjectObserver)
+ MenuItem1: TMenuItem;
MenuItem10: TMenuItem;
MenuItem11: TMenuItem;
MenuItem12: TMenuItem;
+ mnuedSortLines: TMenuItem;
mnuedNextCarea: TMenuItem;
mnuedPrevCarea: TMenuItem;
mnuedLowcase: TMenuItem;
@@ -86,6 +88,7 @@ type
procedure mnuedNextCareaClick(Sender: TObject);
procedure mnuedPrevCareaClick(Sender: TObject);
procedure mnuedLowcaseClick(Sender: TObject);
+ procedure mnuedSortLinesClick(Sender: TObject);
procedure mnuedUpcaseClick(Sender: TObject);
procedure MenuItem5Click(Sender: TObject);
procedure MenuItem6Click(Sender: TObject);
@@ -314,6 +317,7 @@ begin
AssignPng(mnuedLowcase.Bitmap, 'CASE');
AssignPng(mnuedNextCarea.Bitmap, 'GO_NEXT');
AssignPng(mnuedPrevCarea.Bitmap, 'GO_PREVIOUS');
+ AssignPng(mnuedSortLines.Bitmap, 'SORT_AZ');
//
EntitiesConnector.addObserver(self);
EntitiesConnector.addSingleService(self);
@@ -841,6 +845,12 @@ begin
fDoc.CommandProcessor(ecLowerCaseWordOrSel, #0, nil);
end;
+procedure TCEEditorWidget.mnuedSortLinesClick(Sender: TObject);
+begin
+ if fDoc.isNotNil then
+ fDoc.CommandProcessor(ecSortLines, #0, nil);
+end;
+
procedure TCEEditorWidget.mnuedNextCareaClick(Sender: TObject);
begin
if fDoc.isNotNil then
diff --git a/src/ce_synmemo.pas b/src/ce_synmemo.pas
index 462f2494..e7b6d009 100644
--- a/src/ce_synmemo.pas
+++ b/src/ce_synmemo.pas
@@ -9,7 +9,7 @@ uses
SynEdit, SynPluginSyncroEdit, SynCompletion, SynEditKeyCmds, LazSynEditText,
SynHighlighterLFM, SynEditHighlighter, SynEditMouseCmds, SynEditFoldedView,
SynEditMarks, SynEditTypes, SynHighlighterJScript, SynBeautifier, dialogs,
- fpjson, jsonparser, LazUTF8,
+ fpjson, jsonparser, LazUTF8, LazUTF8Classes, Buttons, StdCtrls,
ce_common, ce_writableComponent, ce_d2syn, ce_txtsyn, ce_dialogs,
ce_sharedres, ce_dlang, ce_stringrange;
@@ -120,6 +120,8 @@ type
procedure next;
end;
+ TSortDialog = class;
+
TCESynMemo = class(TSynEdit)
private
fFilename: string;
@@ -165,6 +167,7 @@ type
fAlwaysAdvancedFeatures: boolean;
fIsProjectDescription: boolean;
fAutoClosedPairs: TAutoClosePairs;
+ fSortDialog: TSortDialog;
procedure decCallTipsLvl;
procedure setMatchOpts(value: TIdentifierMatchOptions);
function getMouseBytePosition: Integer;
@@ -196,6 +199,7 @@ type
procedure gotoToChangedArea(next: boolean);
procedure autoClosePair(value: TAutoClosedPair);
procedure setSelectionOrWordCase(upper: boolean);
+ procedure sortSelectedLines(descending, caseSensitive: boolean);
protected
procedure DoEnter; override;
procedure DoExit; override;
@@ -234,8 +238,8 @@ type
procedure ShowPhobosDoc;
procedure nextChangedArea;
procedure previousChangedArea;
- procedure copy;
function implementMain: THasMain;
+ procedure sortLines;
//
function breakPointsCount: integer;
function breakPointLine(index: integer): integer;
@@ -268,6 +272,20 @@ type
property autoClosedPairs: TAutoClosePairs read fAutoClosedPairs write fAutoClosedPairs;
end;
+ TSortDialog = class(TForm)
+ private
+ class var fDescending: boolean;
+ class var fCaseSensitive: boolean;
+ fEditor: TCESynMemo;
+ fCanUndo: boolean;
+ procedure btnApplyClick(sender: TObject);
+ procedure btnUndoClick(sender: TObject);
+ procedure chkCaseSensClick(sender: TObject);
+ procedure chkDescClick(sender: TObject);
+ public
+ constructor construct(editor: TCESynMemo);
+ end;
+
procedure SetDefaultCoeditKeystrokes(ed: TSynEdit);
function CustomStringToCommand(const Ident: string; var Int: Longint): Boolean;
@@ -292,6 +310,7 @@ const
ecNextChangedArea = ecUserFirst + 16;
ecUpperCaseWordOrSel = ecUserFirst + 17;
ecLowerCaseWordOrSel = ecUserFirst + 18;
+ ecSortLines = ecUserFirst + 19;
var
D2Syn: TSynD2Syn; // used as model to set the options when no editor exists.
@@ -311,6 +330,95 @@ begin
result := inherited CalcHintRect(MaxWidth, AHint, AData);
end;
+{$REGION TSortDialog -----------------------------------------------------------}
+constructor TSortDialog.construct(editor: TCESynMemo);
+var
+ pnl: TPanel;
+begin
+ inherited Create(nil);
+ fEditor := editor;
+
+ width := 150;
+ Height:= 95;
+ FormStyle:= fsStayOnTop;
+ BorderStyle:= bsToolWindow;
+ Position:= poScreenCenter;
+ ShowHint:=true;
+
+ with TCheckBox.Create(self) do
+ begin
+ parent := self;
+ BorderSpacing.Around:=2;
+ OnClick:=@chkCaseSensClick;
+ Caption:='case sensitive';
+ checked := fCaseSensitive;
+ align := alTop;
+ end;
+
+ with TCheckBox.Create(self) do
+ begin
+ parent := self;
+ BorderSpacing.Around:=2;
+ OnClick:=@chkDescClick;
+ Caption:='descending';
+ Checked:= fDescending;
+ align := alTop;
+ end;
+
+ pnl := TPanel.Create(self);
+ pnl.Parent := self;
+ pnl.Align:=alBottom;
+ pnl.Caption:='';
+ pnl.Height:= 32;
+ pnl.BevelOuter:=bvLowered;
+
+ with TSpeedButton.Create(self) do
+ begin
+ parent := pnl;
+ BorderSpacing.Around:=2;
+ OnClick:=@btnUndoClick;
+ align := alRight;
+ width := 28;
+ Hint := 'undo changes';
+ AssignPng(Glyph, 'ARROW_UNDO');
+ end;
+
+ with TSpeedButton.Create(self) do
+ begin
+ parent := pnl;
+ BorderSpacing.Around:=2;
+ OnClick:=@btnApplyClick;
+ align := alRight;
+ width := 28;
+ Hint := 'apply sorting';
+ AssignPng(Glyph, 'ACCEPT');
+ end;
+end;
+
+procedure TSortDialog.btnApplyClick(sender: TObject);
+begin
+ fEditor.sortSelectedLines(fDescending, fCaseSensitive);
+ fCanUndo:= true;
+end;
+
+procedure TSortDialog.btnUndoClick(sender: TObject);
+begin
+ if fCanUndo then
+ fEditor.undo;
+ fCanUndo:= false;
+end;
+
+procedure TSortDialog.chkCaseSensClick(sender: TObject);
+begin
+ fCaseSensitive := TCheckBox(sender).checked;
+end;
+
+procedure TSortDialog.chkDescClick(sender: TObject);
+begin
+ fDescending := TCheckBox(sender).checked;
+end;
+{$ENDREGION}
+
{$REGION TCESynMemoCache -------------------------------------------------------}
constructor TCESynMemoCache.create(aComponent: TComponent);
begin
@@ -622,6 +730,7 @@ begin
fCallTipStrings.Free;
fLexToks.Clear;
fLexToks.Free;
+ fSortDialog.Free;
//
if fTempFileName.fileExists then
sysutils.DeleteFile(fTempFileName);
@@ -792,6 +901,7 @@ begin
AddKey(ecNextChangedArea, VK_DOWN, [ssAlt], 0, []);
addKey(ecLowerCaseWordOrSel, 0, [], 0, []);
addKey(ecUpperCaseWordOrSel, 0, [], 0, []);
+ addKey(ecSortLines, 0, [], 0, []);
end;
end;
@@ -816,6 +926,7 @@ begin
'ecPreviousChangedArea':begin Int := ecPreviousChangedArea; exit(true); end;
'ecUpperCaseWordOrSel': begin Int := ecUpperCaseWordOrSel; exit(true); end;
'ecLowerCaseWordOrSel': begin Int := ecLowerCaseWordOrSel; exit(true); end;
+ 'ecSortLines': begin Int := ecSortLines; exit(true); end;
else exit(false);
end;
end;
@@ -841,6 +952,7 @@ begin
ecPreviousChangedArea:begin Ident := 'ecPreviousChangedArea'; exit(true); end;
ecUpperCaseWordOrSel: begin Ident := 'ecUpperCaseWordOrSel'; exit(true); end;
ecLowerCaseWordOrSel: begin Ident := 'ecLowerCaseWordOrSel'; exit(true); end;
+ ecSortLines: begin Ident := 'ecSortLines'; exit(true); end;
else exit(false);
end;
end;
@@ -898,6 +1010,8 @@ begin
setSelectionOrWordCase(true);
ecLowerCaseWordOrSel:
setSelectionOrWordCase(false);
+ ecSortLines:
+ sortLines;
end;
if fOverrideColMode and not SelAvail then
begin
@@ -1283,14 +1397,6 @@ begin
OpenURL(pth);
end;
-procedure TCESynMemo.copy;
-begin
- {$IFDEF WINDOWS}
- {$ELSE}
- // workaround https://github.com/BBasile/Coedit/issues/39
- {$ENDIF}
-end;
-
procedure TCESynMemo.nextChangedArea;
begin
gotoToChangedArea(true);
@@ -1453,6 +1559,59 @@ begin
EndUndoBlock;
end;
end;
+
+procedure TCESynMemo.sortSelectedLines(descending, caseSensitive: boolean);
+var
+ i,j: integer;
+ lne: string;
+ lst: TStringListUTF8;
+ pt0: TPoint;
+begin
+ if BlockEnd.Y - BlockBegin.Y < 1 then
+ exit;
+ lst := TStringListUTF8.Create;
+ try
+ BeginUndoBlock;
+ for i:= BlockBegin.Y-1 to BlockEnd.Y-1 do
+ lst.Add(lines[i]);
+ pt0 := BlockBegin;
+ pt0.X:=1;
+ ExecuteCommand(ecGotoXY, #0, @pt0);
+ lst.CaseSensitive:=caseSensitive;
+ if not caseSensitive then
+ lst.Sorted:=true;
+ case descending of
+ false: for i:= 0 to lst.Count-1 do
+ begin
+ ExecuteCommand(ecDeleteLine, #0, nil);
+ ExecuteCommand(ecInsertLine, #0, nil);
+ lne := lst[i];
+ for j := 1 to lne.length do
+ ExecuteCommand(ecChar, lne[j], nil);
+ ExecuteCommand(ecDown, #0, nil);
+ end;
+ true: for i:= lst.Count-1 downto 0 do
+ begin
+ ExecuteCommand(ecDeleteLine, #0, nil);
+ ExecuteCommand(ecInsertLine, #0, nil);
+ lne := lst[i];
+ for j := 1 to lne.length do
+ ExecuteCommand(ecChar, lne[j], nil);
+ ExecuteCommand(ecDown, #0, nil);
+ end;
+ end;
+ EndUndoBlock;
+ finally
+ lst.Free;
+ end;
+end;
+
+procedure TCESynMemo.sortLines;
+begin
+ if not assigned(fSortDialog) then
+ fSortDialog := TSortDialog.construct(self);
+ fSortDialog.Show;
+end;
{$ENDREGION}
{$REGION DDoc & CallTip --------------------------------------------------------}