The Delphi Open Tools API
by Ray Lischner

Listing One
unit Sort;
{ Sort selected lines of text in Delphi's source code editor.
  Copyright (c) 1997 Tempest Software, Inc.
}
interface
uses Windows, SysUtils, Classes, Graphics, Controls, Dialogs, Forms,
  ExptIntf, ToolIntf, EditIntf, Etk;
type
  TSortExpert = class(TEtkModule)
    EditSort: TEtkMenuItem;
    procedure EditSortClick(Sender: TObject);
  end;
var
  SortExpert: TSortExpert;
procedure Register;
implementation
{$R *.DFM}
// Sort the selection in the current file.
procedure SortSelection;
var
  Module: TIModuleInterface;
  Editor: TIEditorInterface;
  View: TIEditView;
  Reader: TIEditReader;
  Writer: TIEditWriter;
  BlockStart: TCharPos;
  BlockAfter: TCharPos;
  StartPos, EndPos: LongInt;
  TopPos, CursorPos: TEditPos;
  Text: string;
  Strings: TStringList;
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;
    // 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('Close all views but one');
    try
      // 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;
      View := Editor.GetView(0);
      Assert(View <> nil);
      try
        // Convert the character positions to buffer positions.
        StartPos := View.CharPosToPos(BlockStart);
        EndPos   := View.CharPosToPos(BlockAfter);
        // Get the selected text.
        Reader := Editor.CreateReader;
        try
          Assert(Reader <> nil);
          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;
      finally
        View.Free;
      end;
        // Select the newly inserted, sorted text.
        Editor.BlockVisible := False;
        Editor.BlockType    := btNonInclusive;
        Editor.BlockStart   := BlockStart;
        Editor.BlockAfter   := BlockAfter;
        Editor.BlockVisible := True;
    finally
      Editor.Free;
    end;
    // Bring the focus back to the source editor window.
    Module.ShowSource;
  finally
    Module.Free;
  end;
end;
// OnClick handler for the Edit|Sort menu item.
procedure TSortExpert.EditSortClick(Sender: TObject);
begin
  Screen.Cursor := crHourglass;
  try
    SortSelection;
  finally
    Screen.Cursor := crDefault;
  end;
end;
// You can also install this unit in a package.
// The Register procedure creates and register the expert.
procedure Register;
begin
  TSortExpert.CreateAndRegister(SortExpert);
end;
end.

3


