unit sqlmain;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, ecSyntMemo, ecSyntAnal, StdCtrls, DB, ExtCtrls, dbtables,
  ComCtrls, ecSyntTree, ecPopupCtrl, Buttons, Grids, DBGrids,
  ecExtHighlight, ecSpell, ImgList, ecKeyMap;

type
  TForm6 = class(TForm)
    ComboBox1: TComboBox;
    Label1: TLabel;
    SyntAnalyzer1: TSyntAnalyzer;
    Bevel1: TBevel;
    AutoCompletePopup1: TAutoCompletePopup;
    AutoCompletePopup2: TAutoCompletePopup;
    TableHighlighter: TecSpellChecker;
    AliasHighloghter: TecSpellChecker;
    Panel1: TPanel;
    SyntaxMemo1: TSyntaxMemo;
    Splitter1: TSplitter;
    SyntaxTreeView1: TSyntaxTreeView;
    Splitter2: TSplitter;
    DBGrid1: TDBGrid;
    Query1: TQuery;
    DataSource1: TDataSource;
    BitBtn1: TBitBtn;
    ImageList1: TImageList;
    SyntKeyMapping1: TSyntKeyMapping;
    procedure FormCreate(Sender: TObject);
    procedure AutoCompletePopup1GetAutoCompleteList(Sender: TObject;
      Pos: TPoint; List, Display: TStrings);
    procedure AutoCompletePopup2GetAutoCompleteList(Sender: TObject;
      Pos: TPoint; List, Display: TStrings);
    procedure SyntaxMemo1TextChanged(Sender: TObject; Pos, Count,
      LineChange: Integer);
    procedure TableHighlighterCheckWord(Sender: TObject;
      const AWord: String; APos: integer; var Valid: Boolean);
    procedure AliasHighloghterCheckWord(Sender: TObject;
      const AWord: String; APos: integer; var Valid: Boolean);
    procedure BitBtn1Click(Sender: TObject);
    procedure SyntaxMemo1FinishAnalysis(Sender: TObject);
  private
    procedure GetTables(List: TStrings);
    procedure GetAliases(List: TStrings; Tables: TStrings = nil);
    procedure GetFields(TableName: string; List, Display: TStrings);
    function IsIdentifier(APos: integer): Boolean;
  public
    { Public declarations }
  end;

var
  Form6: TForm6;

implementation

uses ecStrUtils;

{$R *.dfm}

// Extracting databases
procedure TForm6.FormCreate(Sender: TObject);
begin
  Session.GetAliasNames(ComboBox1.Items);
  ComboBox1.ItemIndex := ComboBox1.Items.IndexOf('DBDEMOS');
end;

// Extracting tables
procedure TForm6.GetTables(List: TStrings);
var Alias: string;
begin
  if ComboBox1.ItemIndex = -1 then Exit;
  Alias := ComboBox1.Items[ComboBox1.ItemIndex];
  Session.GetTableNames(Alias, '', False, False, List);
end;

// Extracting aliases using parsing results
procedure TForm6.GetAliases(List, Tables: TStrings);
var i: integer;
    r: TTagBlockCondition;
begin
  with SyntaxMemo1.SyntObj do
   begin
     r := TTagBlockCondition(Owner.BlockRules.ItemByName('alias'));
     if r <> nil then
     for i := 0 to RangeCount - 1 do
      if Ranges[i].Rule = r then  // lloking for all text ranges of 'alias' rule
       begin
         List.Add(TagStr[Ranges[i].StartIdx + 1]);
         if Tables <> nil then
           Tables.Add(TagStr[Ranges[i].StartIdx]);
       end;
   end;
end;

// Extracting fields
procedure TForm6.GetFields(TableName: string; List, Display: TStrings);
var Table: TTable;
    i: integer;

   function FieldToImageFmt(F: TField): string;
   begin
     Result := '';
     if F <> nil then
      case F.DataType of
         ftString, ftWideString:  Result := '\i7\';
         ftSmallint, ftInteger, ftAutoInc,
         ftWord, ftLargeint:      Result := '\i4\';
         ftBoolean:               Result := '\i3\';
         ftCurrency, ftBCD:       Result := '\i6\';
         ftFloat:                 Result := '\i5\';
         ftDate, ftTime, ftDateTime,
         ftBytes, ftVarBytes, ftBlob, ftMemo, ftFmtMemo,
         ftGraphic:               Result := '\i8\';
      end;
   end;
begin
  if ComboBox1.ItemIndex = -1 then Exit;
  Table := TTable.Create(Self);
  try
    Table.DatabaseName := ComboBox1.Items[ComboBox1.ItemIndex];
    Table.TableName := TableName;
    Table.GetFieldNames(List);
    Table.Active := True;
    if Display <> nil then
     for i := 0 to List.Count - 1 do
      Display.Add(FieldToImageFmt(Table.FieldByName(List[i])) + List[i]);
  finally
    Table.Free;
  end;
end;

// Fill tables name auto-completion list
procedure TForm6.AutoCompletePopup1GetAutoCompleteList(Sender: TObject;
  Pos: TPoint; List, Display: TStrings);
var range: TTextRange;
    p, idx: integer;
    r: TTagBlockCondition;
  function IsInRule(Range: TTextRange; Rule: TTagBlockCondition): Boolean;
  begin
    Result := (Range <> nil) and
              ((Range.Rule = Rule) or
               IsInRule(Range.Parent, Rule));
  end;
begin
  List.Clear;
  Display.Clear;
  p := SyntaxMemo1.CaretPosToStrPos(Pos);
  with SyntaxMemo1.SyntObj do
   begin
    r := TTagBlockCondition(Owner.BlockRules.ItemByName('from'));
    idx := PriorTokenAt(p);
    if idx < 1 then Exit;
    Range := NearestRangeAtPos(p);
    if IsInRule(Range, r) then
      begin
        if Tags[idx].EndPos > p then Dec(idx);
        if Tags[idx].TokenType = 2 then // Identifier
          Exit;
        GetTables(List);
      end;
   end;
end;

// Fill fields name auto-completion list
procedure TForm6.AutoCompletePopup2GetAutoCompleteList(Sender: TObject;
  Pos: TPoint; List, Display: TStrings);
var range: TTextRange;
    p, idx: integer;
    Al, Tables: TStringList;
    S: string;
begin
  List.Clear;
  Display.Clear;
  p := SyntaxMemo1.CaretPosToStrPos(Pos);
  if p < 3 then Exit;
  if SyntaxMemo1.Lines.Chars[p] = '.' then
   begin
     idx := SyntaxMemo1.SyntObj.TokenAtPos(p - 2); // get index of table/alias name token
     if idx <> -1 then
      begin
        S := SyntaxMemo1.SyntObj.TagStr[idx]; // table/alias name
        Al := TStringList.Create;
        Tables := TStringList.Create;
        try
          GetAliases(Al, Tables);
          idx := Al.IndexOf(S);
          if idx = -1 then
           begin  // it is not alias
            Tables.Clear;
            GetTables(Tables);
            idx := Tables.IndexOf(S);
           end;
          if idx <> -1 then
           begin
            GetFields(Tables[idx], List, Display); // get fields list
            if List.Count > 0 then
              begin
               List.Insert(0, '*');
               Display.Insert(0, '<ALL>');
              end;
           end;
        finally
          Al.Free;
          Tables.Free;
        end;
      end;
   end;
end;

// Run table name auto-completion after first letter inputing
procedure TForm6.SyntaxMemo1TextChanged(Sender: TObject; Pos, Count,
  LineChange: Integer);
begin
  with SyntaxMemo1 do
   if (Count = 1) and IsWordChar(Lines.Chars[Pos + 1]) and
      ((Pos = 0) or IsSpaceChar(Lines.Chars[Pos])) then
     begin
       AutoCompletePopup1.Execute;
     end;
end;

// Check type of token at given position
function TForm6.IsIdentifier(APos: integer): Boolean;
var idx: integer;
begin
  idx := SyntaxMemo1.SyntObj.TokenAtPos(APos);
  Result := (idx <> -1) and (SyntaxMemo1.SyntObj.Tags[idx].TokenType = 2);
end;

// Using ecSpellChecker to highlight valid tables
procedure TForm6.TableHighlighterCheckWord(Sender: TObject;
  const AWord: String; APos: integer; var Valid: Boolean);
var sl: TStringList;
begin
  if not IsIdentifier(APos) then
   begin
    Valid := True;
    Exit;
   end;
  sl := TStringList.Create;
  GetTables(sl);
  Valid := sl.IndexOf(AWord) = -1;
  sl.Free;
end;

// Using ecSpellChecker to highlight aliases
procedure TForm6.AliasHighloghterCheckWord(Sender: TObject;
  const AWord: String; APos: integer; var Valid: Boolean);
var sl: TStringList;
begin
  if not IsIdentifier(APos) then
   begin
    Valid := True;
    Exit;
   end;
  sl := TStringList.Create;
  GetAliases(sl);
  Valid := sl.IndexOf(AWord) = -1;
  sl.Free;
end;

procedure TForm6.BitBtn1Click(Sender: TObject);
begin
  // We use Text, because in UNICODE mode Lines is of
  //  type derived from TWideStrings
  Query1.Active := False;
  if ComboBox1.ItemIndex = -1 then Exit;
  Query1.DatabaseName := ComboBox1.Items[ComboBox1.ItemIndex];
  Query1.SQL.Text := SyntaxMemo1.Lines.Text;
  Query1.Active := True;
end;

procedure TForm6.SyntaxMemo1FinishAnalysis(Sender: TObject);
begin
  // Clear previous results
  TableHighlighter.Clear;
  AliasHighloghter.Clear;
  // Analyze (not background)
  TableHighlighter.Analyze(False);
  AliasHighloghter.Analyze(False);
end;

end.
