(* *****************************************************************
** File    : AutoFold_C_CPP.PAS
** Created : 24.09.01
** Author  :
**
** Macro for FoldMaster
**    automatic folding of C or CPP files and headers
**
********************************************************************
** Version     Date      Change
** 1.52        24.09.01  Creation
** **************************************************************-**)

PROGRAM AutoFold_C_CPP;

// Here you can add constants and variables.
// Variables defined in global scope will hold their contents
// until the macro file is unloaded. Functions may use global
// variables to store some information.
(*{{{  const*)
// const
   // global constants
(*}}}*)
(*{{{  var*)
var
   s                 : string;
   last              : string;
(*}}}*)

// ------------------------------------------------------------------
// FUNCTIONS AND PROCEDURES
// ------------------------------------------------------------------
(*{{{  skip_white*)
{ skip_white
  Ueberspringt WhiteSpace
}
procedure skip_white;
begin
   while (s = '') AND (NOT IsEndOfText) do begin
      SyntaxRight; s := GetSyntaxItem;
   end;
end;
(*}}}*)
(*{{{  next*)
{ next
}
procedure next;
begin
   last := s;
   SyntaxRight; s := GetSyntaxItem;
   // writeln(s);
end;
(*}}}*)

(*{{{  function is_func : boolean;*)
// is func prueft ob aktueller Bezeichner zu einer
// Funktionsdeklaration gehoert ode nicht
// liefert true, wenn dies der Fall ist
function is_func : boolean;
var n1, x1 : integer;
begin
   // aktuelle Position merken
   n1 := GetLineNum; x1 := GetX;
   repeat
      if      (s = ';') then begin is_func := false; break; end
      else if (s = '{') then begin is_func := false; break; end
      else if (s = '(') then begin
         is_func := true;  exit;
      end;
      next;
   until (IsEndOfText);
   // wieder zurueckstellen
   GotoXY(1, n1); s := GetSyntaxItem;
end;
(*}}}*)
(*{{{  function last_char(str:string) : char;*)
function last_char(str:string) : char;
var len : integer;
begin
   len := length(str);
   last_char := copy(str, len, 1);
end;
(*}}}*)
(*{{{  scan_includes*)
{ scan_includes
}
procedure scan_includes;
var n1, n2, x2 : integer;
begin
   n1 := GetLineNum;  // Zeilennummer mit "type"
   n2 := n1;
   repeat
      s  := GetLine; n2 := GetLineNum;
      if (pos('#include', s) = 0) then break;
      LineDown;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      ReplaceLine ('INCLUDES'); EndOfLine;
end;
(*}}}*)
(*{{{  scan_defines*)
{ scan_defines
  durch Leerzeile abgeschlossen
}
procedure scan_defines;
var n1, n2, x2 : integer;
begin
   n1 := GetLineNum;  // Zeilennummer mit "type"
   n2 := n1;
   repeat
      s  := GetLineNC(0); n2 := GetLineNum;
      if (s = '') then break
      else if (last_char(s) <> '\') then break;
      LineDown;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      EndOfLine;
end;
(*}}}*)
(*{{{  scan_preprocessor*)
{ scan_preprocessor
  wird aufgerufen, nachdem ein # gefunden wurde
  prueft ob ein "include" oder ein "define" kommt
  Jenachdem wird scan_include oder scan_define aufgerufen
  andere Anweisungen werden uebersprungen
}
procedure scan_preprocessor;
begin
   // s steht auf #
   // Leerzeichen ueberspringen
   repeat
      next; Skip_white;
   until (s <> '') or (IsEndOfText);

   // Was kommt dann
   if (s = 'include') then begin
      scan_includes;
      s := GetSyntaxItem;
   end else if (s = 'define') then begin
      scan_defines;
      s := GetSyntaxItem;
   end else begin
      next;
   end;
end;
(*}}}*)
(*{{{  scan_block*)
{ scan_block
  ueberspringt einen Block der mit geschweiften Klammern
  eingefasst wird. Verschachtelte Bloecke werden durch
  rekursive Aufrufe uebersprungen.
}
procedure scan_block;
begin
   repeat
      next;
      if (s = '{') then scan_block;
   until (s = '}') OR (IsEndOfText);
   next;
end;
(*}}}*)
(*{{{  scan_parameterlist*)
{ scan_parameterlist
  beginnt mit runder Klammer und endet mit runder Klammer
  Enthaltene Klammern werden durch rekursiven Aufruf bearbeitet
}
procedure scan_parameterlist;
begin
   repeat
      next;
      if (s = '(') then scan_parameterlist;
   until (s = ')') OR (IsEndOfText);
   next;
end;
(*}}}*)

(*{{{  scan_typedef*)
{ scan_typedef
  typedefs beginnen mit dem Schluesselwort typedef
  es folgen weitere Bezeichner oder ein Block bis zu
  einem abschliessenden ;
}
procedure scan_typedef;
var n1, n2, x2 : integer;
begin
   n1 := GetLineNum;  // Zeilennummer mit "type"
   n2 := n1;
   repeat
      n2 := GetLineNum;
      if      (s = ';') then break
      else if (s = '{') then scan_block
      else next;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      EndOfLine;
end;
(*}}}*)
(*{{{  scan_class*)
{ scan_typedef
  typedefs beginnen mit dem Schluesselwort typedef
  es folgen weitere Bezeichner oder ein Block bis zu
  einem abschliessenden ;
}
procedure scan_class;
var n1, n2, x2 : integer;
begin
   n1 := GetLineNum;  // Zeilennummer mit "type"
   n2 := n1;
   repeat
      n2 := GetLineNum;
      if (s = ';') then break
      else if (s = '{') then scan_block
      else next;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      EndOfLine;
end;
(*}}}*)

(*{{{  scan_struct*)
{ scan_typedef
  typedefs beginnen mit dem Schluesselwort typedef
  es folgen weitere Bezeichner oder ein Block bis zu
  einem abschliessenden ;
}
procedure scan_struct;
var n1, n2, x2 : integer;
begin
   if (is_func) then exit;
   n1 := GetLineNum;  // Zeilennummer mit "struct"
   n2 := n1;
   repeat
      n2 := GetLineNum;
      if   (s = ';')    then break
      else if (s = '{') then scan_block
      else next;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      EndOfLine;
end;
(*}}}*)
(*{{{  scan_union*)
{ scan_typedef
  typedefs beginnen mit dem Schluesselwort typedef
  es folgen weitere Bezeichner oder ein Block bis zu
  einem abschliessenden ;
}
procedure scan_union;
var n1, n2, x2 : integer;
begin
   if (is_func) then exit;
   n1 := GetLineNum;  // Zeilennummer mit "type"
   n2 := n1;
   repeat
      n2 := GetLineNum;
      if      (s = ';') then break
      else if (s = '{') then scan_block
      else next;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      EndOfLine;
end;
(*}}}*)
(*{{{  scan_enum*)
{ scan_typedef
  typedefs beginnen mit dem Schluesselwort typedef
  es folgen weitere Bezeichner oder ein Block bis zu
  einem abschliessenden ;
}
procedure scan_enum;
var n1, n2, x2 : integer;
begin
   if (is_func) then exit;
   n1 := GetLineNum;  // Zeilennummer mit "type"
   n2 := n1;
   repeat
      n2 := GetLineNum;
      if      (s = ';') then break
      else if (s = '{') then scan_block
      else next;
   until (IsEndOfText);
   GotoXY(1, n1); SelectExtend(1);
   GotoXY(1, n2); EndOfLine;
   FoldMake;      SelectExtend(0);
   FoldHide;      EndOfLine;
end;
(*}}}*)

(*{{{  scan_function*)
{ scan_function
  Funktionen erkennt man an der Parameterliste
  Folgt nach der Parameterliste ein ; handelt es sich um eine
  Forward- Deklaration.
  Parameterlisten koennen ebenfalls runde Klammern enthalten
}
procedure scan_function;
var n1, n2, x2 : integer;
    name : string;
begin
   n1   := GetLineNum;  // Zeilennummer mit "type"
   n2   := n1;
   name := last;
   // s sollte auf ( stehen
   scan_parameterlist;

   // Alle tokens bis zu { oder ; ueberspringen
   while (s <> '{') AND (s <> ';') AND (NOT IsEndOfText) do next;
   if (s = '{') then begin
      // es faengt ein Definitionsblock an
      scan_block;    n2 := GetLineNum;
      GotoXY(1, n1); SelectExtend(1);
      GotoXY(1, n2); EndOfLine;
      FoldMake;      SelectExtend(0);
      FoldHide;      EndOfLine;
      // ReplaceLine(name);
   end;
end;
(*}}}*)

(*{{{  auto_fold_cpp*)
{ auto_fold_cpp
  Auf erster Ebene werden nur
  Funktionen INCLUDES DEFINES gefaltet.
  Funktionen:
     Funktionen sind gekennzeichnet durch die Parameterliste,
     die in runden Klammern eingefasst ist.
     Der Bezeichner unmittelbar davor ist der Funktionsname.
     Folgt unmittelbar nach der schliessenden Klammer ein ;
     kennzeichnet dies eine Forward- Deklaration.
     Bei jedem Bezeichner ist zu pruefen ob er zu einer Funktions-
     Deklaration gehoert. Dies ist dann der Fall, wenn vor { oder ;
     eine offene runde Klammer folgt.
}
procedure auto_fold_cpp;
begin
   MacroConsoleClear; Lock;
   TabsToSpace;  // TABS mag ich nicht
   StartOfText; s := GetSyntaxItem;
   repeat
      if (s = '')  then skip_white
      else if (pos('#', s) <> 0) then scan_preprocessor
      else if (s = 'typedef')    then scan_typedef
      else if (s = 'class')      then scan_class
      else if (s = 'struct')     then scan_struct
      else if (s = 'union')      then scan_union
      else if (s = 'enum')       then scan_enum
      else if (s = '(')          then scan_function
      else begin
         next;
      end;
   until (IsEndOfText);
   UnLock;
end;
(*}}}*)

(*{{{  procedure project_auto_fold_cpp;*)
{ project_auto_fold_pascal
  Das Projekt Modul fuer Modul durchgehen und die AutoFoldFunktion
  auf jede Datei anwenden.
}
procedure project_auto_fold_cpp;
var fn : string;
    i  : integer;
    ext: string;
begin
   ProjectShow; StartOfText;
   i := 0;
   repeat
      { kompletten Dateinamen mit Pfad ermitteln }
      fn := IterateProjectModules(false, i);
      if (fn <> '') then begin
         ext := FileNameGetExt(fn);

         if (ext = '.CPP')
         or (ext = '.C')
         or (ext = '.H')
         then begin
            writeln ('Working on ', fn);
            FileOpen(fn);
            if (IsModuleOpen(fn)) then begin
               s := '';
               auto_fold_cpp;
            end;
         end;
      end;
      Inc(i);
   until fn = '';
end;
(*}}}*)
(*{{{  procedure setup_menue;*)
{ setup_menue
  setup macro menu entries
}
procedure setup_menue;
begin
   MacroMenuAdd('project_auto_fold_cpp', 'Fold each module in Project');
   MacroMenuAdd('auto_fold_cpp', 'Auto fold current module');
end;
(*}}}*)

// ------------------------------------------------------------------
// MAIN BLOCK
// ------------------------------------------------------------------
BEGIN
   // if the macro file itself is executed this block will be
   // executed. Here you can call other procedures and
   // functions as necessary.
   setup_menue;
END.

