unit
OldSort;
{ Sort selected lines of text in Delphi's source code editor.
Copyright © 1998 Tempest Software, Inc.
}
interface
// Sort the selection in the current file.
procedure SortSelection;
implementation
uses Classes, SysUtils, ExptIntf, EditIntf, ToolIntf;
resourcestring
sCloseViews = 'Close all views but one';
type
// Keep track of buffer positions for each line of text.
// An edit writer's positions are relative to the original file.
// When writing the sorted text, it is important to keep track
// of the original position for each line.
TPos = class
private
fLeft, fRight: LongInt;
public
constructor Create(Left, Right: LongInt);
property Left: LongInt read fLeft;
property Right: LongInt read fRight;
end;
TPosList = class(TList)
public
procedure AddPos(Left, Right: LongInt);
destructor Destroy; override;
end;
{ TPos }
constructor TPos.Create(Left, Right: Integer);
begin
inherited Create;
fLeft := Left;
fRight := Right;
end;
{ TPosList }
procedure TPosList.AddPos(Left, Right: LongInt);
begin
inherited Add(TPos.Create(Left, Right));
end;
destructor TPosList.Destroy;
var
I: Integer;
Obj: TObject;
begin
for I := 0 to Count-1 do
begin
Obj := Self[I];
Self[I] := nil;
Obj.Free;
end;
inherited;
end;
// Sort the columnar selection by extracting the selected text
// in each line, sorting the text, and replacing the text in
// each line with the sorted text.
procedure SortColumns(Editor: TIEditorInterface; View: TIEditView);
var
TopLeft: TEditPos;
BottomRight: TEditPos;
Left, Right: TEditPos;
LeftChar, RightChar: TCharPos;
LeftPos, RightPos: LongInt;
Reader: TIEditReader;
Writer: TIEditWriter;
Text: string;
Strings: TStringList;
PosList: TPosList;
Line: LongInt;
I: Integer;
begin
// Get the corners of the selected region.
TopLeft := TEditPos(Editor.BlockStart);
BottomRight := TEditPos(Editor.BlockAfter);
Strings := TStringList.Create;
try
PosList := TPosList.Create;
try
// Get the selected text.
Reader := Editor.CreateReader;
try
Left.Col := TopLeft.Col;
Right.Col := BottomRight.Col;
for Line := TopLeft.Line to BottomRight.Line do
begin
// Convert the edit positions on the current line to
// buffer positions. Delphi requires the intermediate
// step of character positions.
Left.Line := Line;
Right.Line := Line;
View.ConvertPos(True, Left, LeftChar);
View.ConvertPos(True, Right, RightChar);
// include the character at RightChar
Inc(RightChar.CharIndex);
LeftPos := View.CharPosToPos(LeftChar);
RightPos := View.CharPosToPos(RightChar);
SetLength(Text, RightPos - LeftPos);
Reader.GetText(LeftPos, PChar(Text), Length(Text));
// If the text includes the end of the line characters,
// strip the CR and LF.
while (Length(Text) > 0) and (Text[Length(Text)] in [#13, #10]) do
begin
Delete(Text, Length(Text), 1);
Dec(RightPos);
end;
Strings.Add(Text);
PosList.AddPos(LeftPos, RightPos);
end;
finally
Reader.Free;
end;
// Now sort the text.
Strings.Sort;
// And write the sorted text, line by line.
Writer := Editor.CreateUndoableWriter;
try
I := 0;
for Line := TopLeft.Line to BottomRight.Line do
begin
with TPos(PosList[I]) do
begin
Writer.CopyTo(Left);
Writer.DeleteTo(Right);
end;
Writer.Insert(PChar(Strings[I]));
Inc(I);
end;
finally
Writer.Free;
end;
finally
PosList.Free;
end;
finally
Strings.Free;
end;
// Set the cursor to the start of the sorted text.
View.CursorPos := TopLeft;
// Make sure the top of the sorted text is visible.
// Scroll the edit window if ncessary.
if (TopLeft.Line < View.TopPos.Line) or
(BottomRight.Line >= View.TopPos.Line + View.ViewSize.CY)
then
View.TopPos := TopLeft;
// Select the newly inserted, sorted text.
Editor.BlockVisible := False;
Editor.BlockType := btColumn;
Editor.BlockStart := TCharPos(TopLeft);
Editor.BlockAfter := TCharPos(BottomRight);
Editor.BlockVisible := True;
end;
// Sort the selection as lines.
procedure SortLines(Editor: TIEditorInterface; View: TIEditView);
var
BlockStart: TCharPos;
BlockAfter: TCharPos;
StartPos, EndPos: LongInt;
Reader: TIEditReader;
Writer: TIEditWriter;
TopPos, CursorPos: TEditPos;
Text: string;
Strings: TStringList;
begin
// Get the limits of the selected text.
BlockStart := Editor.BlockStart;
BlockAfter := Editor.BlockAfter;
// Sort entire lines, so modify the positions accordingly.
BlockStart.CharIndex := 0; // start of line
if BlockAfter.CharIndex > 0 then
begin
// Select the entire line by setting the After position
// to the start of the next line.
BlockAfter.CharIndex := 0;
Inc(BlockAfter.Line);
end;
// Convert the character positions to buffer positions.
StartPos := View.CharPosToPos(BlockStart);
EndPos := View.CharPosToPos(BlockAfter);
// Get the selected text.
Reader := Editor.CreateReader;
try
SetLength(Text, EndPos - StartPos - 1);
Reader.GetText(StartPos, PChar(Text), Length(Text));
finally
Reader.Free;
end;
// Sort the text. Use a TStringList because it's easy.
Strings := TStringList.Create;
try
Strings.Text := Text;
Strings.Sort;
Text := Strings.Text;
finally
Strings.Free;
end;
// Replace the selection with the sorted text.
Writer := Editor.CreateUndoableWriter;
try
Writer.CopyTo(StartPos);
Writer.DeleteTo(EndPos);
Writer.Insert(PChar(Text));
finally
Writer.Free;
end;
// Set the cursor to the start of the sorted text.
View.ConvertPos(False, CursorPos, BlockStart);
View.CursorPos := CursorPos;
// Make sure the top of the sorted text is visible.
// Scroll the edit window if ncessary.
if (BlockStart.Line < View.TopPos.Line) or
(BlockAfter.Line >= View.TopPos.Line + View.ViewSize.CY) then
begin
View.ConvertPos(False, TopPos, BlockStart);
View.TopPos := TopPos;
end;
// Select the newly inserted, sorted text.
Editor.BlockVisible := False;
Editor.BlockType := btNonInclusive;
Editor.BlockStart := BlockStart;
Editor.BlockAfter := BlockAfter;
Editor.BlockVisible := True;
end;
// Sort the selection in the current file.
procedure SortSelection;
var
Module: TIModuleInterface;
Editor: TIEditorInterface;
View: TIEditView;
begin
// Get the module interface for the current file.
with ToolServices do
Module := GetModuleInterface(GetCurrentFile);
// If no file is open, Module is nil.
if Module = nil then
Exit;
try
// Get the interface to the source editor.
Editor := Module.GetEditorInterface;
// If the file is not a source file, Editor is nil.
if Editor = nil then
Exit;
try
// The expert cannot tell which view is active, so force
// the user to have only one view at a time.
if Editor.GetViewCount > 1 then
raise Exception.Create(sCloseViews);
View := Editor.GetView(0);
try
// Columnar selections work entirely differently.
if Editor.BlockType = btColumn then
SortColumns(Editor, View)
else
SortLines(Editor, View);
finally
View.Free;
end;
finally
Editor.Free;
end;
// Bring the focus back to the source editor window.
Module.ShowSource;
finally
Module.Free;
end;
end;
end.