(* *****************************************************************
** File    : Tags.pas
** Created : 28.07.2001
** Author  : M. Leute / A. Raible
**
** Macro for FoldMaster
**    Needs FoldMaster version 1.61 or later
**
**    Used for taging with gnu- ctags
**    Stores tags in a TempStore
**    Is able to load and to write tag data
**    search for a tag using line_number
**       if not found use search_string
**       if not found ask to rescan the file
**    Projektsupport
**       only callable from project window
**       Generate database from all files in project
**       Update database from all changed files in project
**    Update tags of the current file.
**
********************************************************************
** Version     Date      Change
** 1.50        28.07.01  Creation
** 1.60        02.07.02  manual search added. Mapped to SHIFT-F11
**                       generating groups if transfering
**                       to messagport
**                       including keydefs.inc
** 1.62        13.08.03  Jump back function added (F12)
**
** **************************************************************-**)

PROGRAM Tags;

const
   TAGFILEFILTER = 'TAG files(*.tags)|*.tags|';
   ETAGS         = 'ETAGS';
   ETAGS_VAR_NAME= 'ETAGS_LOADFILE';
   EOPTS         = '-x -d -T ';

   MaxPath       = 10;

   {$I keydefs.inc}

{ -------------------------------------------------------- }
{ variables }
{ -------------------------------------------------------- }
(*{{{  global variables*)
var
   TagsInstPath : string;
   etags_store  : integer;
   file_loaded  : string;

   { remember last search results in order to determine
     if user wants an incremental search }
   last_searchstr : string;
   last_lineno    : integer;
   last_filename  : string;
   last_index     : integer;

   { remember last positions
     if user wants to jump back }
   last_edit_line  : array[0..MaxPath] of integer;
   last_edit_col   : array[0..MaxPath] of integer;
   last_edit_file  : array[0..MaxPath] of string;
   last_edit_ptr   : integer;


(*}}}*)

{ -------------------------------------------------------- }
{ funstions and procedures }
{ -------------------------------------------------------- }
(*{{{  procedure KillLeadingBlanks(var s : string);*)
{ KillLeadingBlanks
  Removes Leading Blanks from a string
}
procedure KillLeadingBlanks(var s : string);
begin
   while (copy(s,1,1) = ' ') do begin
      Delete(s,1,1);
      If (Length(s) = 0) then exit;
   end;
end;
(*}}}*)
(*{{{  procedure KillEndingBlanks(var s : string);*)
{ KillEndingBlanks
  Removes Blanks at the end of a string
}
procedure KillEndingBlanks(var s : string);
var len : integer;
begin
   while (1) do begin
      len := Length(s);
      if (len = 0) then exit;
      if (copy(s,len,1) = ' ') then Delete(s,len,1)
      else exit;
   end;
end;
(*}}}*)

(*{{{  etags_tx_tempstore*)
{ etags_tx_tempstore
  Transfer filter for etags
  writting output to a temporary storage which
  is used for tagging
}
procedure etags_tx_tempstore(s : string);
begin
   if (pos('Second entry ignored',s) = 1) then exit;
   if (pos('Duplicate entry in file',s) = 1) then exit;
   TempStoreAddLine(etags_store,s);
end;
(*}}}*)
(*{{{  procedure define_tool_etags;*)
{ define_tool_etags
 defines tool etags.
}
procedure define_tool_etags;
begin
   (* ------------------------------------------------- *)
   (* Definition of GNU-Indent-Int
      as hidden tool only to be called from macro with
      ToolRunParameter *)
   ToolDefine       (ETAGS, TagsInstPath+'\etags.exe',
                     '$DRIVEDIR($EDNAME)',  { working directory }
                     '');                   { no more options }
   ToolDefineType   (ETAGS, '', '', '', 0); { hidden tool }
   ToolDefineMode   (ETAGS, 'TAGS/etags_tx_tempstore', 3);
   { ToolDefineConnect(ETAGS, '', 0);         }
   { ToolDefineOptions(ETAGS, 0, 0);          }

end;
(*}}}*)

// Some utility functions
(*{{{  procedure replace_slashes(var s : string);*)
{ replace_slashes
  Replaces all '\' within a String with simple '/'
  ETAGS includes filenames in its output with '/'
  instead of '\'. So these slashes have to be replaced
  prior to search for it in TempStore.
}
procedure replace_slashes(var s : string);
var i : integer;
begin
   i := 1;
   repeat
      i := posx('\',s,i);
      if (i > 0) then begin
         Delete(s,i,1); Insert('/',s,i);
      end;
   until (i <= 0);
end;
(*}}}*)
(*{{{  procedure remove_file_tags(fn : string);*)
{ remove_file_tags
  remove all tags from this file from the etags_store
}
procedure remove_file_tags(fn : string);
var ind,i : integer;
    s     : string;
begin
   replace_slashes(fn);
   { tags will be replaced in etags_store }
   if (TempStoreGetCount(etags_store) > 0) then begin
      ind := 0; i:= 0; { start search at 0 }
      { remove all tags in TempStore from the current file }
      repeat
         s := TempStoreGetLineStr(etags_store,fn,ind,1,0); { not case sensitive }
         if (s <> '') then begin
            { remove found entry }
            TempStoreDeleteAt(etags_store, ind);
            inc(i);
         end;
      until (s='');
      if (i > 0) then Writeln(i:4, ' Tags removed');
   end;
end;
(*}}}*)
(*{{{  procedure run_etags(fn : string);*)
{ run_etags
  Execute the tool etags with its parameters.
  A TimeTag is added to the storage.
}
procedure run_etags(fn : string);
var s : string;
    f : file;
    t : integer;
    ok : boolean;
begin
   StrUpper(fn);
   s := FileNameGetExt(fn);
   replace_slashes(fn);
   ok := false;
   if (s = '.C')   then ok := true;
   if (s = '.CPP') then ok := true;
   if (s = '.H')   then ok := true;
   if (s = '.RH')  then ok := true;
   if (s = '.HPP') then ok := true;
   if (NOT ok) then exit;
   assign(f, fn);  GetFTime(f,t);
   s := ''; swrite(s,'#*#', fn, '#~#', t);
   writeln('processing: ', fn);
   TempStoreAddLine(etags_store,s);
   ToolRunParameter(ETAGS, EOPTS+fn);
end;
(*}}}*)
(*{{{  function  get_time_tag(fn : string) : integer;*)
{ get_time_tag
  retrieve time tag from TimeTag- entry from
  etags store.
}
function get_time_tag(fn : string) : integer;
var s : string;
    sf: string;
    p,c : integer;
begin
   get_time_tag := 0;
   replace_slashes(fn); StrUpper(fn);
   sf := ''; swrite(sf,'*#', fn, '#~#');
   p  := 0;
   s  := TempStoreGetLineStr(etags_store,sf,p,1,0); // no case sensitive search
   if (s <> '') then begin
      // Entry found
      p := pos('#~#', s);
      if (p > 0) then begin
         Delete(s, 1, p+2); val(s, p, c);
         get_time_tag := p;
      end;
   end;
end;
(*}}}*)

// File Maintainance
(*{{{  procedure load_file(fn : string);*)
{ load_file
  load the given file to the TempStore identified by store
}
procedure load_file(fn : string);
var f  : text;
    s  : string;
    count : integer;
begin
   TempStoreClear(etags_store);
   count := 0;
   assign(f, fn); reset(f);
   while (NOT Eof(f)) do begin
      readln(f,s);
      TempStoreAddLine(etags_store, s);
      Inc(Count);
   end;
   close(f);
   MessageAddMessage('Tags', '', 'Tags loaded from file '+ fn, 0,0);
end;
(*}}}*)
(*{{{  procedure save_file(fn : string);*)
{ save_file
  save the data base to the specified file
}
procedure save_file(fn : string);
var f      : text;
    s      : string;
    i,max  : integer;
begin
   MessageAddMessage('Tags', '', 'Saving tags to file '+ fn, 0,0);
   assign(f, fn);
   rewrite(f);
   max := TempStoreGetCount(etags_store);
   for i:= 0 to max-1 do begin
      s := TempStoreGetLineAt(etags_store, i);
      writeln(f,s);
   end;
   close(f);
end;
(*}}}*)
(*{{{  procedure load_tags;*)
{ load tags
  loads tags from a file to the tempstore
  for use with search_tag
}
procedure load_tags;
var fn : string;
begin
   { read filename }
   fn := 'TAGS.*';
   if (FileNameInput ('LOAD TAGS : Tags file ',TAGFILEFILTER, fn)) then begin
      if (FileExist(fn)) then begin
         load_file(fn);
      end else begin
         if (BoxWarning('LOAD TAGS', 'File not found')) then begin
         end;
      end;
   end;
end;
(*}}}*)
(*{{{  procedure save_tags;*)
{ save tags
  save tags from the tempstore to a file
  for use with search_tag
  Later on this file can be loaded to the load store
}
procedure save_tags;
var fn : string;
    s  : string;
begin
   { read filename }
   fn := 'TAGS.*';
   if (FileNameInput ('SAVE TAGS : Tags file ',TAGFILEFILTER, fn)) then begin
      save_file(fn);
      s := GetVarValue(ETAGS_VAR_NAME);
      if (BoxQuestion('SEARCH_TAG','Variable not available! Shall it be added to the project?') = 1) then begin
         ProjectShow;
         StartOfText;
         InsertVariable(ETAGS_VAR_NAME,fn);
      end;
   end;
end;
(*}}}*)

// Generating and Updating Tags
(*{{{  procedure prj_make_tags;*)
{ prj_make_tags
  re- generate the tag file from the whole project
  save to file if a filename is given
}
procedure prj_make_tags;
var s : string;
    i : integer;
    fn: string;
begin
   ProjectShow;
   if (NOT IsProject) then exit;
   FileSaveAll;
   i := 0; TempStoreClear(etags_store);
   repeat
      fn := IterateProjectModules(false, i);
      StrUpper(fn);
      if (fn <> '') then begin
         // Datei bearbeiten
         run_etags(fn);
      end;
      inc(i);
   until (fn = '');
   writeln('Tags added : ', TempStoreGetCount(etags_store));

   // write data base to a file if a name is given
   // otherwise ask user for the name
   ProjectShow;
   s := GetVarValue(ETAGS_VAR_NAME);
   if (s = '') then save_tags
   else save_file(s);
end;
(*}}}*)
(*{{{  procedure prj_update_tags;*)
{ prj_update_tags
}
procedure prj_update_tags;
var s : string;
    i : integer;
    fn: string;
    f : file;
    t : integer;
    processed : integer;
begin
   ProjectShow;
   if (NOT IsProject) then exit;
   { Get Time of the current tags in etags_store }
   i := 0;  processed := 0;
   repeat
      fn := IterateProjectModules(false, i);
      StrUpper(fn);
      if ((fn <> '') AND (FileExist(fn)))then begin
         assign(f, fn);  GetFTime(f,t);
         if (t > get_time_tag(fn)) then begin
            remove_file_tags(fn);      { remove all tags }
            run_etags(fn);
            inc(processed);
         end;
      end;
      inc(i);
   until (fn = '');
   if (processed > 0) then begin
      // only if at least one file was processed
      writeln('Tags added : ', TempStoreGetCount(etags_store));
      // write data base to a file if a name is given
      // otherwise ask user for the name
      ProjectShow;
      s := GetVarValue(ETAGS_VAR_NAME);
      if (s = '') then save_tags
      else save_file(s);
   end;
end;
(*}}}*)
(*{{{  procedure update_file_tags;*)
{ update_tags
  remove all tags in TempStore from the current file
  run ETAGS with the current file
}
procedure update_file_tags;
var fn     : string; { Name of the current file }
    s      : string;
begin
   if (IsProject) then Exit;
   FileSave;
   fn  := GetModuleName; { get name of current module }
   if (NOT IsModuleInProject(fn)) then Exit;
   remove_file_tags(fn); { remove all tags related to this file }
   run_etags(fn);        { run ETAGS with the current file }
   // write data base back to a file if a name is given
   // otherwise ask for the name
   s := GetVarValue(ETAGS_VAR_NAME);
   if (s = '') then save_tags
   else save_file(s);
end;
(*}}}*)

// Search for a label
(*{{{  procedure scan_string(s : string; var lno : integer; var fn : string);*)
{ scan_string
  process a string from the storage, which is the same as
  the contents of the Tag file, and extract line number, search string
  and filename.
}
procedure scan_string(s : string; var lno : integer;
                      var fn : string; var search : string);
var p    : integer;
    code : integer;
begin
   p := pos(' ',s); lno := -1;
   if (p > 0) then begin
      Delete(s,1,p); KillLeadingBlanks(s);
      { get line number }
      val(s, lno, code);
      if (code > 0) then begin
         { at this pos filename starts }
         Delete(s,1,code); KillLeadingBlanks(s);
         p := pos(' ', s);
         if (p > 0) then begin
            fn := copy(s, 1, p);
            // at the end the search string is found
            p := p+1; // skip blank
            search := copy(s,p,255); // copy rest of line
            KillLeadingBlanks(search);
            KillEndingBlanks(search);
         end else begin
            fn := copy(s, code, 255);
         end;
         KillEndingBlanks(fn);
      end;
   end;
end;
(*}}}*)
(*{{{  process_output*)
{ process_output
}
procedure process_output(s : string; l : string; idx : integer);
var
    lno    : integer;
    fn     : string;
    search : string;
begin
   { process output }
   scan_string(s, lno, fn, search);
   if (lno > 0) then begin
      FileOpen(fn);
      if (IsModuleOpen(fn)) then begin
         Lock;
         GotoLineNum(lno,0);
         // Test if the line contains the search string
         s := GetLine;
         if (pos(search, s) > 0) then begin
            last_searchstr := l;
            last_lineno    := lno;
            last_filename  := fn;
            last_index     := idx;
         end else begin
            // search string not found in current line:
            // search in whole document
            SearchText(search, 'g');
            if (TextFound) then begin
               lno            := GetLineNum;
               last_lineno    := lno; // Line may be changed
               last_searchstr := l;
               last_filename  := fn;
               last_index     := idx;
               GotoLineNum(lno,0);  // Set Cursor to pos 1 and hilight line
            end else begin
               // Tag not found in file
               // Update of the Tagfile is necessary
               if (BoxQuestion ('SearchTag','File is out of sync. Do an update of the tags?') = 1) then begin
                  update_file_tags;
               end;
            end;
         end;
         UnLock;
      end;
      exit;
   end;
end;
(*}}}*)


(*{{{  procedure search_tag;*)
procedure search_tag;
var s,l    : string;
    fn     : string;
    search : string;
    p      : integer;
    lno    : integer;
    idx    : integer;
    current_file : string;
    current_lno  : integer;
    incremental  : boolean;
    found        : boolean;
begin
   found        := false;
   current_lno  := GetLineNum;
   current_file := GetModuleName;
   replace_slashes(current_file);
   strupper(current_file);

   // save current position
   if last_edit_ptr <= MaxPath then
     begin
     last_edit_line[last_edit_ptr] := GetLineNum;
     last_edit_col[last_edit_ptr]  := GetX;
     last_edit_file[last_edit_ptr] := GetModuleName;
     inc(last_edit_ptr);
     end;

   (*{{{  Test if available data base is correct*)
   // Test if available data base is correct
   s := GetVarValue(ETAGS_VAR_NAME);
   if (file_loaded <> s) OR (TempStoreGetCount(etags_store) = 0) then begin
      // name of loaded data_bases is different to the one
      // specified in the project file
      if ((s <> '') AND (FileExist(s))) then begin
         file_loaded := s;  // remember the name of the loaded file
         load_file(s);
      end;
   end;
   (*}}}*)
   (*{{{  warning if no tags are available*)
   if (TempStoreGetCount(etags_store) = 0) then begin
      // no tags are loaded
      if (BoxWarning('SEARCH_TAG','No Tags available!') = 0) then begin end;
      Halt;
   end;
   (*}}}*)

   l := GetLineSelection;
   (*{{{  test if incremental search is wanted *)
   { test if incremental search is wanted }
   { this is if at start of line
          in the same file
          and in the same line }
   lno := GetLineNum;
   incremental := false;
   if ((GetLinePos = 1) AND (lno = last_lineno) AND (last_filename = current_file)) then begin
      incremental := true;
      l := last_searchstr;
   end;
   (*}}}*)

   { first search in the current data base }
   (*{{{  if (TempStoreGetCount(etags_store) > 0) then begin*)
   if (TempStoreGetCount(etags_store) > 0) then begin
      if (incremental) then idx:= last_index + 1
      else idx := 0;
      repeat
         s := TempStoreGetLineStr(etags_store,l,idx,1,1); { Groß u. Kleinschreibung unterscheiden }
         if (s <> '') then begin
            process_output(s, l, idx);
            (*{{{  process output *)
            { process output }
            scan_string(s, lno, fn, search);
            if (lno > 0) then begin
               FileOpen(fn);
               if (IsModuleOpen(fn)) then begin
                  Lock;
                  GotoLineNum(lno,0);
                  // Test if the line contains the search string
                  s := GetLine;
                  if (pos(search, s) > 0) then begin
                     last_searchstr := l;
                     last_lineno    := lno;
                     last_filename  := fn;
                     last_index     := idx;
                  end else begin
                     // search string not found in current line:
                     // search in whole document
                     SearchText(search, 'g');
                     if (TextFound) then begin
                        lno            := GetLineNum;
                        last_lineno    := lno; // Line may be changed
                        last_searchstr := l;
                        last_filename  := fn;
                        last_index     := idx;
                        GotoLineNum(lno,0);  // Set Cursor to pos 1 and hilight line
                     end else begin
                        // Tag not found in file
                        // Update of the Tagfile is necessary
                        if (BoxQuestion ('SearchTag','File is out of sync. Do an update of the tags?') = 1) then begin
                           update_file_tags;
                        end;
                     end;
                  end;
                  UnLock;
               end;
               exit;
            end;
            (*}}}*)
         end;
      until (s = '') OR (lno <= 0);
   end;
   (*}}}*)

   { if not found in etags_store this point is reached. }
   if last_edit_ptr > 0 then dec(last_edit_ptr);
   Beep;
end;
(*}}}*)
(*{{{  manual_search*)
{ manual_search
}
procedure manual_search;
var s,l    : string;
    fn     : string;
    search : string;
    p      : integer;
    lno    : integer;
    idx    : integer;
    current_file : string;
    current_lno  : integer;
    incremental  : boolean;
    found        : boolean;
begin
   found        := false;
   current_lno  := GetLineNum;
   current_file := GetModuleName;
   replace_slashes(current_file);
   strupper(current_file);

   (*{{{  Test if available data base is correct*)
   // Test if available data base is correct
   s := GetVarValue(ETAGS_VAR_NAME);
   if (file_loaded <> s) OR (TempStoreGetCount(etags_store) = 0) then begin
      // name of loaded data_bases is different to the one
      // specified in the project file
      if ((s <> '') AND (FileExist(s))) then begin
         file_loaded := s;  // remember the name of the loaded file
         load_file(s);
      end;
   end;
   (*}}}*)
   (*{{{  warning if no tags are available*)
   if (TempStoreGetCount(etags_store) = 0) then begin
      // no tags are loaded
      if (BoxWarning('SEARCH_TAG','No Tags available!') = 0) then begin end;
      Halt;
   end;
   (*}}}*)

   // Prompt for label
   TempStoreClear(0);
   if InputBox('Enter label', l) = 1 then begin
      // first search the etags_store for occurence of the lable
      (*{{{  if (TempStoreGetCount(etags_store) > 0) then begin*)
      if (TempStoreGetCount(etags_store) > 0) then begin
         idx := 0;
         repeat
            s := TempStoreGetLineStr(etags_store,l,idx,0,0); { Groß u. Kleinschreibung unterscheiden }
            if (s <> '') AND (pos(l, s) = 1) then begin
               // Reset search
               lno := -1;
               // Add the index
               str(idx:5, fn); fn := strfill(fn, 8);
               insert(fn, s, 1);
               TempstoreAddLine(0, s);
            end;
            inc(idx);
         until (s = '');
      end;
      (*}}}*)

      // Promp the user
      s := TempStoreSelect(0, 'TAGS', 'Select label');
      if s <> '' then begin
         Val(s, idx, p);
         s := TempStoreGetLineAt(etags_store,idx); { Groß u. Kleinschreibung unterscheiden }
         process_output(s, l, idx);
         writeln(s, ' selected');
      end;
   end;

end;
(*}}}*)

// List tags in Console or in Messagewindow
(*{{{  procedure list_tags;*)
{ list_tags
  lists all tags in both temp stores
}
procedure list_tags;
var i : integer;
    s : string;
    max : integer;
begin
   (*{{{  ETAGS_STORE*)
   i := 0; max := TempStoreGetCount(etags_store);
   for i:= 0 to max do begin
      s := TempStoreGetLineAt(etags_store,i);
      writeln(s);
   end;
   (*}}}*)
end;
(*}}}*)
(*{{{  procedure list_time_tags;*)
{ list_tags
  lists all time tags in temp stores
}
procedure list_time_tags;
var i : integer;
    s, fn : string;
begin
   i := 0;
   if (TempStoreGetCount(etags_store) > 0) then begin
      repeat
         s := TempStoreGetLineStr(etags_store,'#*#',i,1,0); { not case sensitive }
         if (s <> '') then begin
            writeln(s); inc(i);
         end;
      until (s = '');
   end;
end;
(*}}}*)
(*{{{  procedure gen_messages;*)
{ gen_messages
  generates messages from contents of tags store
  generic strings are used
}
procedure gen_messages;
var i,max : integer;
    p     : integer;
    s     : string;
begin
   max := TempStoreGetCount(etags_store);
   {MessageSetPort('TAGS');}
   for i:= 0 to max-1 do begin
      s := TempStoreGetLineAt(etags_store, i);
      // Add a group if a file marker is found
      p := pos('#~#',s);
      if (p <> 0) then begin
         // Leave Group and add a new group
         Delete(s, p, 255);
         MessageAddGroup('TAGS', copy(s, 4, 255));
      end else begin
         MessageAddGenMessage('TAGS','%t %l %f %*', s);
      end;
   end;
end;
(*}}}*)

// Setting Filename to load
(*{{{  procedure add_file_to_load;*)
{ add_file_to_load
  set the global variable  ETAGS_LOADFILE
  The next time this makro is executed this file will be loaded
  to the etags store.
}
procedure add_file_to_load;
var fn : string;
    s  : string;
begin
   { read filename }
   s  := GetProjectName;
   fn := FileNameGetName(s) + '.TAGS';
   ChDir(FileNameGetDriveDir(s));
   if (FileNameInput ('Select File To Load',TAGFILEFILTER, fn)) then begin
      if (FileExist(fn)) then begin
         ProjectShow;
         StartOfText;
         InsertVariable(ETAGS_VAR_NAME,fn);
      end else begin
         if (BoxWarning('ADD FILENAME', 'File not found')) then begin
         end;
      end;
   end;
end;
(*}}}*)

// Jump to previous editor
(*{{{  procedure jump_back;*)
procedure jump_back;

begin
   if last_edit_ptr > 0 then
     begin
     dec(last_edit_ptr);
     FileOpen(last_edit_file[last_edit_ptr]);
     GotoXY(last_edit_col[last_edit_ptr], last_edit_line[last_edit_ptr]);
     end
   else
     Beep;
end;
(*}}}*)

{ -------------------------------------------------------- }
{ Installation }
{ -------------------------------------------------------- }
// Functions called while start-up
// used to initialise the menues and key
(*{{{  procedure set_inst_path;*)
{ Unterprogramm
}
procedure set_inst_path;
var s  : string;
    p1 : integer;
begin
   (*{{{  Check for installation directory of TAGS / ETAGS*)
   { Search for installation directory of RCS }
   TagsInstPath := GetVarValue('TAGSINSTPATH');
   if (TagsInstPath = '') then begin
      { Search for directory }
      Beep; s := 'ETAGS.EXE';
      if (FileNameInput ('Tags Install Directory ','Executable files(*.exe)|*.exe|', s)) then begin
         { Last Character in Dir is '/' or '\' this character
           needs to be deleted especially if added to the environmental
           variable PATH }
         TagsInstPath := FileNameGetDriveDir(s);
         p1 := Length(TagsInstPath);
         if (copy(TagsInstPath, p1, 1) = '/') OR (copy(TagsInstPath, p1, 1) = '\') then begin
            Delete(TagsInstPath, p1, 1);
         end;
         SetGlobalVar('TAGSINSTPATH', TagsInstPath);
         writeln('TagsInstPath set to ', TagsInstPath);
      end;
   end;
   (*}}}*)
end;
(*}}}*)
(*{{{  procedure set_key_map_and_menu;*)
{ set_key_map
  set key function
  F11      search for tags
  ALT F11  update tags for current file
}
procedure set_key_map_and_menu;
begin
   KeyMapSetMacro('TAGS/search_tag',       VK_F11, 0);
   KeyMapSetMacro('TAGS/jump_back',        VK_F12, 0);
   KeyMapSetMacro('TAGS/manual_search',    VK_F11, SHIFT);
   KeyMapSetMacro('TAGS/update_file_tags', VK_F11, ALT);
   KeyMapSetMacro('TAGS/prj_update_tags',  VK_F11, CTRL);

   MacroMenuAdd  ('search_tag',       'Search tag F11');
   MacroMenuAdd  ('jump_back',        'Jump back F12');
   MacroMenuAdd  ('manual_search',    'Manual search SHIFT+F11');
   MacroMenuAdd  ('');
   MacroMenuAdd  ('update_file_tags', 'Update file tags    ALT +F11');
   MacroMenuAdd  ('prj_update_tags',  'Update project tags CTRL+F11');
   MacroMenuAdd  ('prj_make_tags',    'Generate all');
   MacroMenuAdd  ('');
   MacroMenuAdd  ('list_tags',        'List TAGS in macro console');
   MacroMenuAdd  ('list_time_tags',   'List all tagged files');
   MacroMenuAdd  ('gen_messages',     'transfer to message port');
   MacroMenuAdd  ('');
   MacroMenuAdd  ('load_tags',        'Load TAGS from file');
   MacroMenuAdd  ('save_tags',        'Save TAGS to file');
   MacroMenuAdd  ('add_file_to_load', 'Add tag-file variable to project');

end;
(*}}}*)

{ -------------------------------------------------------- }
{ Main }
{ -------------------------------------------------------- }
BEGIN
   MessageAddPort('Tags');
   { reserve TempStore to be used as data base }
   etags_store := TempStoreAllocate;
   TempStoreClear(etags_store);

   last_edit_ptr := 0;

   { Ask for and set InstPath }
   set_inst_path;

   { define tools for internal use }
   if (NOT ToolAvail(ETAGS))  then define_tool_etags;

   { define special keys and menues }
   set_key_map_and_menu;
   MessageAddMessage('Tags', '', 'Tags initialized', 0,0);
END.

