% Open templates and do some replacements
%
% Copyright © 2004 Günter Milde (milde users.sf.net)
% Released under the terms of the GNU General Public License (ver. 2 or later)
%
% 1.0              Based on template (without s) modes
%                  from Audun Ytterdal and Dino Leonardo Sangoi
% 1.1 		   compatibility with Mikael Hultgren's template.sl
% 		   added by Paul Boekholt
% 1.1.1 2004-02-23 extension to insert_template()
% 1.1.2 2004-10-11 fix: changed order of find_template and insert_template
% 1.1.3 2004-11-18 fix: find_template should call insert_template_file
% 1.1.4 2005-03-01 fix: insert_template_file_hook did insert an empty line
%                  find_template now asks for both, new name and template name
% 1.2   2005-04-21 new functions get_iso_date(), insert_date()
% 		   fix: <DATE> replacement lagged 1 month behind real date
% 		   (as localtime().tm_mon is in the range 0...11)
% 1.2.1 2005-06-03 renamed insert_date to insert_iso_date
% 1.3   2005-06-10 code cleanup and reorganizing
%                  Templates_Path is now a comma separated list of directories
% 		   Added tm documentation
% 2008-08-20 08-20 add `delim' arg to search_path_for_file
% 		   as the default changes in 0.99.19
%
% USAGE
%
% * Copy templates.sl to your Library-Path
% 
% * Place your templates into Templates_Path
%
% * For a Templates menu entry, add something like this to your ~/.jedrc
% 
%   autoload("find_template", "templates");
%   define templates_load_popup_hook (menubar)
%   {
%       menu_insert_item ("Save &Buffers", "Global.&File",
%                         "&New from Template", "find_template");
%   }
%   append_to_hook ("load_popup_hooks", &templates_load_popup_hook);
%
% Alternatively, add something like this to your modehooks:
%
%   autoload("insert_template", "templates");
%   define slang_mode_hook ()
%   {
%     insert_template();
%   }
%

provide("templates");

% Requirements
% ============
% none

% Variables
% =========

% backwards compatibility (versions < 1.3 had Templates_Dir)
#ifexists Templates_Dir
custom_variable("Templates_Path", "Templates_Dir");
#endif

%!%+
%\variable{Templates_Path}
%\synopsis{Comma-separated list of template directories}
%\description
%  List of directories where template files are stored.
%\notes  
%  This is a comma separated list similar to Color_Scheme_Path.
%  KDE usrers might want to set
%   variable Templates_Path = "~/.jed/templates,~/Desktop/Templates";
%\seealso{insert_template_file, insert_template, find_template}
%!%-
custom_variable("Templates_Path", 
   path_concat(Jed_Home_Directory, "templates"));

% special strings that get replaced with autogenerated values
private variable replacements = Assoc_Type[String_Type];

% Functions
% =========

% Return a string with current date in YYYY-MM-DD format
define get_iso_date()
{
   variable time_struct = localtime(_time);
   return sprintf("%d-%02d-%02d", time_struct.tm_year+1900,
		  time_struct.tm_mon+1, time_struct.tm_mday);
}

public define insert_iso_date()
{
   insert(get_iso_date());
}

static define update_replacements()
{
   replacements["<REALNAME>"] = extract_element(get_realname(), 0, ',');
   replacements["<USERNAME>"] = get_username();
   replacements["<EMAIL>"]    = get_emailaddress();
   replacements["<DATE>"]     = get_iso_date();
   replacements["<FILENAME>"] = path_basename(buffer_filename);
   replacements["<FILENAME_SANS>"] =
     path_sans_extname(replacements["<FILENAME>"]);

   % for compatibility with Mikael Hultgren's template.sl
   replacements["@author@"] = replacements["<REALNAME>"];
   replacements["@mail@"] = replacements["<EMAIL>"];
   replacements["@filename@"] = replacements["<FILENAME>"];
   replacements["@filename_sans@"] = replacements["<FILENAME_SANS>"];
}

define templates_add_replacement(key, repl)
{
   replacements[key] = repl;
}

%!%+
%\function{expand_template_file}
%\synopsis{Expand a template filename}
%\usage{String expand_template_file(String name)}
%\description
% Search for FILE in \var{Templates_Path} and return
% expanded pathname if found or the Null string otherwise.
%\seealso{insert_template_file, find_template, insert_template}
%!%-
define expand_template_file(name)
{
   name = search_path_for_file(Templates_Path, name, ',');
   if (name == NULL)
     return "";
   return name;
}

% return comma separated list of all template files matching \var{glob}
define list_templates()
{
   variable dir, dirlist, list = String_Type[0];
   foreach dir (strtok(Templates_Path, ","))
     {
	dirlist = listdir(expand_filename(dir));
	if (dirlist != NULL)
	  list = [list, dirlist];
     }
   return list;
}


%!%+
%\function{insert_template_file}
%\synopsis{Insert a template file and replace tags}
%\usage{Integer insert_template_file(String name)}
%\description
%  Insert a template file into the buffer. The template file is
%  searched on the \var{Templates_Path}.
%  The following tags are expanded:
%#v+
%    <REALNAME>      = extract_element(get_realname(), 0, ',');
%    <USERNAME>      = get_username();
%    <EMAIL>         = get_emailaddress();
%    <DATE>          = get_iso_date();
%    <FILENAME>      = path_basename(buffer_filename);
%    <FILENAME_SANS> = path_sans_extname(<FILENAME>)
%  for compatibility with Mikael Hultgren's template.sl
%    @author@        = <REALNAME>
%    @mail@          = <EMAIL>
%    @filename@      = <FILENAME>
%    @filename_sans@ = <FILENAME_SANS>
%#v-
%  The cursor (point) is placed at the tag <CURSOR> or @point@.
%  
%  The return value is identical to insert file: 
%  the number of lines inserted or -1 (if no file was found)
%\seealso{find_template, insert_template, insert_file, templates_add_replacement}
%!%-
public define insert_template_file(name)
{
   variable result;
   % insert the file
   push_mark();
   result = insert_file(expand_template_file(name));
   narrow();

   % replace tags
   bob;
   update_replacements();
   foreach(replacements) using ("keys", "values")
     replace();

   % place cursor/point
   if (fsearch("<CURSOR>"))
     deln(strlen("<CURSOR>"));
   else if (fsearch("@point@"))
     deln(strlen("@point@"));
   else
     eob();
   widen();
   return result;
}

%!%+
%\function{insert_template}
%\synopsis{Insert matching template into an empty buffer}
%\usage{Void insert_template()}
%\description
% Hook for inserting a matching template into an empty buffer.
% 
% A match is the first file on \var{Templates_Path} with name
%    modename + ".tpl"  (downcased first part of modename)
%    "template" + extension
%    "template" + extension + ".tpl"
%    
% The following filenames all match the buffername "cua.sl"
%#v+
%    slang.tpl, template.sl, template.sl.tpl
%#v-
%\example
% To insert a template for every new file,
%#v+
%   add_to_hook("_jed_find_file_after_hooks", "insert_template");
%#v-
% If you only need a template in html_mode say, add it to html_mode_hook.
%\notes
% This provides compatibility with Mikael Hultgren's template.sl.  Put
% template.sl.tpl, template.html.tpl etc. in the \var{Templates_Path}.  
%\seealso{insert_template, find_template}
%!%-
public define insert_template()
{
   !if(bobp() and eobp()) % Return if the buffer is not empty
     return;
   variable template_file, ext = path_extname(buffer_filename());
   variable modename = strlow(extract_element(get_mode_name(), 0, ' '));

   % get the template file name
   % it looks like modename.tpl, template.cc.tpl, or template.cc
   template_file = expand_template_file(modename + ".tpl");
   if (template_file == "" and strlen(ext))
     template_file = expand_template_file("template" + ext);
   if (template_file == "" and strlen(ext))
     template_file = expand_template_file("template" + ext + ".tpl");
   % insert template file, if found
   if (template_file != "")
     () = insert_template_file(template_file);
   else
     message("no matching template found");
}

%!%+
%\function{find_template}
%\synopsis{Open a new buffer and insert a template}
%\usage{find_template([template_name])}
%\description
%   Opens a new buffer and asks for a template file name to insert (if not
%   given as optional argument)
%\notes
%   For automatic insertion of a template into an empty buffer
%   a la Hultgren see \sfun{insert_template}
%\seealso{Templates_Path, insert_template, insert_template_file}
%!%-
public define find_template()
{
   variable newbuf = "*newfile*", template;

   if (bufferp(newbuf))
     newbuf += "+";
   template = read_with_completion(strjoin(list_templates(), ","),
      "Find template:", "", "", 's');

   sw2buf(newbuf);
   () = insert_template_file(template);
   % set the mode (works if the template file has name "template."+ext)
   call_function("mode_hook", file_type(template));
   set_buffer_modified_flag(0);
}

