Delphi and C++ Builder IDE Syntax-Highlighting Extensions Editor

By: John Kaster

Abstract: This simple utility uses the TRegistry component, a TListBox, and a TAction to provide a (hopefully) convenient interface for adding file extensions to the IDE editor's syntax highlighter

Delphi and C++ Builder IDE Syntax-Highlighting Extensions Editor

by John Kaster

The Delphi and C++ Builder IDEs for Windows use the Windows registry for a wide variety of settings that can be configured by the user. Most of these options are configurable in dialogs inside the IDE. The file extensions to associate with the editor's syntax highlighting is not one of the dialogs available inside the IDE.

Just the Highlights

This simple utility uses the TRegistry component, a TListBox, and a TAction to provide a (hopefully) convenient interface for adding file extensions to the IDE editor's syntax highlighter.

What's your extension?

For the work I do with SQL, I currently use two different file extensions. I use SQL for straight SQL commands like INSERT, UPDATE, and DELETE. I use DDL as the file extension for Data Definition Language, for statements like ALTER TABLE and CREATE PROCEDURE.

This helps me keep my projects organized, particularly when I need to transfer a project from one machine to another. The additional file extension helps me to create a self-documenting file of sorts, because the extensions are different. The problem is, the Delphi and C++ Builder IDEs don't recognize DDL as one of the valid extensions for its SQL-based syntax highlighting, which I greatly prefer to use if it's available.

If you customize your editor options by using Tools | Editor Options in the IDE, a new registry key called Editor\Options will be written the appropriate registry section for that IDE. For example, in Delphi 5, it's:

\Software\Borland\Delphi\5.0\Editor\Options
and in C++ Builder 5, it's:
\Software\Borland\C++Builder\5.0\Editor\Options
When this registry key is written, there are several values listed in the registry that can be used to configure the IDE's syntax highlighting support. These values are:
ValueC++ Builder ValuesDelphi Values
C Syntax Extensionscpp;c;cc;hpp;h;hh;cxx;hxxcpp;c;hpp;h;hh;cxx;hxx
IDL Syntax Extensionsidlidl
SQL Syntax Extensionssqlsql
Syntax Extensionspas;dpr;dpk;inc;dfmpas;dpr;dpk;inc;dfm

HighEdit

Rather than edit the registry manually, I wanted something that made modifying the extensions a little safer and more convenient. HighEdit is a simple Delphi utility that reads in the registry values listed above, and allows you to edit the extension values for each one with a (hopefully) convenient interface.

Reading the registry values

Since there were multiple values to configure for the syntax extensions, I wanted a convenient format for preserving the changes. Descendants of the TStrings class all have support for Name/Value pairs, where the Name/Value pair looks like this:

Name=Value
The TStrings.Names property allows you to retrieve the Name portion of the string by number. The TStrings.Values property supports retrieving the Value portion of the string by passing in the Name to it.

The first thing I need to do is open the appropriate registry key to read the current values for syntax highlighting. I dropped a RadioGroup called rgOptionson the form, and set its Items property to:

Delphi
C++Builder

Then I created a function called OptionsKey to return the appropriate registry value, depending on whether the Delphi or C++ Builder radio button was checked. Here's the relevant code:


const
  SEditorOptionsKey = '\Software\Borland\%s\5.0\Editor\Options';

...

function TFormSynHigh.OptionsKey: string;
begin
  Result := Format( SEditorOptionsKey, [ EditorOption ] );
end;

function TFormSynHigh.EditorOption: string;
begin
  Result := rgOptions.Items[ rgOptions.ItemIndex ];
end;

For displaying the display and easy selection of the various values in the registry, I decided to use a ListBox, called lbExtensions which has an Items property that is a TStrings descendant, to store and display the registry values the utlity loads and saves. Here's the code:


procedure TFormSynHigh.LoadExtensions;
var
  Exts : TStringList;
  Reg : TRegistry;
  i : integer;

  function Ext( Value : string; Sep : string = ',' ) : string;
  const
    SExt = 'Syntax Extensions';
  var
    Spc : string;
  begin
    if Value = '' then
      Spc := ''
    else
      Spc := ' ';
    Result := '"' + Value + Spc + SExt + '"' + Sep;
  end;

begin
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if not Reg.OpenKey(OptionsKey,False) then
      IDENotInstalled;
    Exts := TStringList.Create;
    try
      Exts.CommaText := Ext('C') + Ext('IDL') + Ext( 'SQL' ) + Ext( '', '' );
      lbExtensions.Items.Clear;
      for i := 0 to Exts.Count - 1 do
        lbExtensions.Items.Add( Exts[ i ] + '='
          + Reg.ReadString( Exts[ i ] ) );
      lbExtensions.ItemIndex := 0;
      lbExtensionsClick(nil);
    finally
      Exts.Free;
    end;
  finally
    if Assigned( Reg ) then
      Reg.CloseKey;
    Reg.Free;
  end; { Finally }
end;

Notice the embedded function Ext, which is a shorthand function for creating the appropriate Registry entries. It simply saves some typing, some space in the executable, and abstracts the naming logic for the registry keys somewhat. The space savings would be more significant if there were more registry keys to set.

If the registry key is not found (the call to Reg.OpenKey fails, then an exception is raised in the application, stating that the editor properties have not been customized. The following code provides this service:


resourcestring
  SDelphiNotInstalled = 'The %s 5 editor properties have not been customized';

...

procedure TFormSynHigh.IDENotInstalled;
begin
  Raise Exception.CreateFmt( SDelphiNotInstalled, [ EditorOption ] );
end;

Editing the extensions

After the values are loaded into the ListBox, a TLabel called lblExtension is used to indicate the name of the label currently being edited, and a TEdit called eExt is used for editing the extensions. The label and edit controls get their values set in the ListBox.OnClick event, shown here:


procedure TFormSynHigh.lbExtensionsClick(Sender: TObject);
begin
  lblExtension.Caption := lbExtensions.Items.Names[ lbExtensions.ItemIndex ];
  eExt.Text := lbExtensions.Items.Values[ lblExtension.Caption ];
end;

Whatever changes the user makes to the extension value is immediately reflected in the selected ListBox item by the Edit.OnChange event, as shown here:


procedure TFormSynHigh.eExtChange(Sender: TObject);
begin
  lbExtensions.Items[ lbExtensions.ItemIndex ] := lblExtension.Caption + '='
    + eExt.Text;
end;

Saving the changes

Clicking the Save button will write the changes to the registry. This button is only enabled when the values have been loaded into the ListBox from the registry. The TAction.OnUpdate event called actSaveUpdateEvent is what enables or disables the Save button.

The SaveExtensions procedure writes the values from the ListBox to the registry, then clears the ListBox, which will disable the Save button until the values are loaded again.


procedure TFormSynHigh.SaveExtensions;
var
  Reg : TRegistry;
  i : integer;
  ExtName : string;

begin
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if not Reg.OpenKey(OptionsKey,False) then
      IDENotInstalled;
    for i := 0 to lbExtensions.Items.Count - 1 do
    begin
      ExtName := lbExtensions.Items.Names[ i ];
      Reg.WriteString( ExtName, lbExtensions.Items.Values[ ExtName ] );
    end;
    lblExtension.Caption := 'Extension Type';
    eExt.Text := '';
    lbExtensions.Items.Clear;
  finally
    if Assigned( Reg ) then
      Reg.CloseKey;
    Reg.Free;
  end; { Finally }
end;

Download and use it

You can download the complete Delphi source code for this project this from CodeCentral. If you make improvements to it, let me know by attaching a comment tho this article or to the CodeCentral submission, and I'll implement the improvements in a future version.


Server Response from: ETNASC03