HEX
Server: Apache
System: Linux pdx1-shared-a1-38 6.6.104-grsec-jammy+ #3 SMP Tue Sep 16 00:28:11 UTC 2025 x86_64
User: mmickelson (3396398)
PHP: 8.1.31
Disabled: NONE
Upload Files
File: //usr/share/jed/lib/recentx.sl
% This file is like recent.sl, except that it logs files according to 
% filename extension.  It draws upon the ideas in recent.sl, as well
% as upon those of the version at
% <http://jedmodes.sourceforge.net/mode/recent>.
%
% To make use of this file, put the following in your .jedrc file:
% 
%    require ("recentx");
%    Recentx_Cache_Filename = ".jedrecent";
%    Recentx_Max_Files = 15;     % The number of files per extension
%    Recentx_Cache_Exclude_Patterns = {"^/tmp/", "\.tmp$"R};
%----------------------------------------------------------------------

#ifdef IBMPC_SYSTEM
custom_variable ("Recentx_Cache_Filename", "_jedrcnt");
#else
custom_variable ("Recentx_Cache_Filename", ".jedrecent");
#endif
%!%+
%\variable{Recentx_Cache_Filename}
%\synopsis{The name of the file used for the recent file cache}
%\usage{Recentx_Cache_Filename = ".jedrecent";}
%\description
% This value of this variable specifies the name of the cache file
% used for recently accessed files.  If the filename is given
% as a relative path, it will be taken as relative to value of the
% Jed_Home_Directory variable, which typically coincides with the
% users's HOME directory.
%\notes
% This variable is defined in \file{recentx.sl}.
%\seealso{Recentx_Cache_Exclude_Patterns, Recentx_Max_Files, Recentx_Use_Cache}
%!%-

custom_variable ("Recentx_Use_Cache", 1);
%!%+
%\variable{Recentx_Use_Cache}
%\synopsis{Turn on/off caching of recent filenames}
%\usage{Recentx_Use_Cache=1;}
%\description
% If non-zero, the recent-filename-cache will be enabled.  Otherwise,
% caching will be turned off.
%\notes
% This variable is defined in \file{recentx.sl}. 
%\seealso{Recentx_Cache_Exclude_Patterns, Recentx_Max_Files, Recentx_Cache_Filename}
%!%-

custom_variable ("Recentx_Max_Files", 15);
%!%+
%\variable{Recentx_Max_Files}
%\synopsis{Set the maximum number of recent-files to cache per extension}
%\usage{Recentx_Max_Files=15;}
%\description
% The value of this variable specifies the maximum number of number of
% files per extension to store in the recent-files-cache.  The maximum number
% of filenames in the cache will be the product of the number of
% extensions and the value of this variable.
%\notes
% This variable is defined in \file{recentx.sl}.
%\seealso{Recentx_Cache_Exclude_Patterns, Recentx_Cache_Filename, Recentx_Use_Cache}
%!%-

custom_variable ("Recentx_Cache_Exclude_Patterns", {});
%!%+
%\variable{Recentx_Cache_Exclude_Patterns}
%\synopsis{List of patterns used to exclude filenames from the recent-files-cache}
%\usage{Recentx_Cache_Exclude_Patterns = \{...\};}
%\description
%  The value of this variable is a list of regular expressions such that
%  if a pathname matches any of the patterns, the file will be
%  excluded from the recent-files-cache.
%\example
%#v+
%   variable Recentx_Cache_Exclude_Patterns = {"^/tmp/", "\\.tmp$"};
%#v-
%  This example excludes any file in the /tmp directory, or any file
%  name with the extension ".tmp".
%\notes
% This variable is defined in \file{recentx.sl}.
%\seealso{Recentx_Cache_Ext_Exclude_Patterns, Recentx_Max_Files, Recentx_Cache_Filename, Recentx_Use_Cache}
%!%-

custom_variable ("Recentx_Cache_Ext_Exclude_Patterns", {});
%!%+
%\variable{Recentx_Cache_Ext_Exclude_Patterns}
%\synopsis{List of patterns used to exclude filename extensions from the recent-files-cache}
%\usage{Recentx_Cache_Ext_Exclude_Patterns = \{...\};}
%\description
%  The value of this variable is a list of regular expressions such that
%  if a filename extension matches any of the patterns, the file will be
%  excluded from the recent-files-cache.
%\example
%#v+
%   variable Recentx_Cache_Ext_Exclude_Patterns = {"~$", "^tmp$", "^[0-9]+"};
%#v-
%  This example excludes any file whose extension ends in \exmp{~}, or
%  is \exmp{"tmp"}, or consists entirely of digits.
%\notes
% This variable is defined in \file{recentx.sl}.
%\seealso{Recentx_Cache_Exclude_Patterns, Recentx_Cache_Filename, Recentx_Use_Cache}
%!%-

private variable Last_Sync_Time = 0;
private define new_recent_files_database ()
{
   return Assoc_Type[Assoc_Type];
}
private variable Recentx_Cache = new_recent_files_database ();

private define get_recent_file_list_name ()
{
   variable file = Recentx_Cache_Filename;

   if (path_is_absolute (file))
     return file;

   variable dir = Jed_Home_Directory;
#ifdef IBMPC_SYSTEM
   if (dir == "")
     {
	dir = getenv ("TEMP");
	if (dir == NULL)
	  dir = "";
     }
#endif
   return dircat (dir, file);
}


private define add_file_to_database (name, time)
{
   variable ext = path_extname (name), pat;
   ext = strtrim_beg (ext, ".");
   if (strlen (ext)) foreach pat (Recentx_Cache_Ext_Exclude_Patterns)
     {
	try
	  {
	     if (string_match (ext, pat, 1))
	       return;
	  }
	catch AnyError:
	  vmessage ("Exclude pattern may be bad: %S", pat);
     }

   foreach pat (Recentx_Cache_Exclude_Patterns)
     {
	try
	  {
	     if (string_match (name, pat, 1))
	       return;
	  }
	catch AnyError:
	  vmessage ("Exclude pattern may be bad: %S", pat);
     }

   if (ext == "") ext = "(none)";

   ifnot (assoc_key_exists (Recentx_Cache, ext))
     Recentx_Cache[ext] = Assoc_Type[Long_Type];

   variable s = Recentx_Cache[ext];
   s[name] = time;
}

private define update_last_sync_time (file)
{
   variable st = stat_file (file);
   if (st != NULL)
     Last_Sync_Time = st.st_mtime;
   else
     Last_Sync_Time = _time ();
}

private define read_recent_file_list (file)
{
   Recentx_Cache = new_recent_files_database ();
   variable fp = fopen (file, "r");
   if (fp == NULL)
     return;

   variable line;
   variable i = 0;
   foreach line (fp)
     {
	line = strtrim_end (line, "\n");
	ifnot (strlen(line))
	  continue;
	variable fields = strchop (line, '|', 0);

	if (length (fields) == 1)
	  {
	     % old format --- filename only
	     add_file_to_database (fields[0], i);
	     i++;
	  }
	else
	  {
	     add_file_to_database (fields[1], atol(fields[0]));
	  }
     }
   () = fclose (fp);
   update_last_sync_time (file);
}

private define sort_files_by_time (files, times)
{
   variable i = array_sort (times);
   array_reverse (i);
   if (length(i) > Recentx_Max_Files)
     i = i[[0:Recentx_Max_Files-1]];
   return (files[i], times[i]);
}

private define save_recent_file_list ()
{
   variable file = get_recent_file_list_name ();
   variable fp = fopen (file, "wb");
   if (fp == NULL)
     return;

   foreach (assoc_get_values (Recentx_Cache))
     {
	variable item = ();
	variable files, times;
	(files, times) = sort_files_by_time (assoc_get_keys(item), assoc_get_values(item));
	() = array_map (Int_Type, &fprintf, fp, "%ld|%s\n", times, files);
     }
   () = fclose (fp);
   () = chmod (file, 0600);
   update_last_sync_time (file);
}

private define load_recent_file_list ()
{
   variable file = get_recent_file_list_name ();
   variable st = stat_file (file);
   if ((st != NULL) && (st.st_mtime > Last_Sync_Time))
     Recentx_Cache = NULL;

   if (Recentx_Cache == NULL)
     read_recent_file_list (file);
}

private define get_most_recent_files ()
{
   variable times, files, num = 0, item;
   foreach item (assoc_get_values (Recentx_Cache))
     num += length(item);

   times = Long_Type[num];
   files = String_Type[num];

   variable i = 0, ii;
   foreach item (assoc_get_values (Recentx_Cache))
     {
	num = length(item);
	ii = i + [0:num-1];
	times[ii] = assoc_get_values (item);
	files[ii] = assoc_get_keys (item);
	i += num;
     }
   (files, ) = sort_files_by_time (files, times);
   return files;
}

private define get_most_recent_files_by_ext (ext)
{
   variable item = Recentx_Cache[ext];
   variable files;
   (files, ) = sort_files_by_time (assoc_get_keys(item), assoc_get_values(item));
   return files;
}

private define menu_select_file_callback (file)
{
   () = find_file (file);
}

% Build the menu of recent files.
private define display_recent_files_menu (popup, files)
{
   variable i = '1'; % use 1-9 first, then a-z, then A-Z, then give up and restart
   foreach (files)
     {
	variable file = ();
	menu_append_item (popup, sprintf ("&%c %s", i, file), 
			  &menu_select_file_callback, file);
	% check - what should we use?
	switch (i)
	  { case '9': i = 'a' - 1; }
	  { case 'z': i = 'A' - 1; }
	  { case 'Z': i = '1' - 1; }
	i++;
     }
}

private variable Recent_Files_Menu_Name = "&Recent Files";
private variable Recent_Files_Ext_Menu_Name = "Recent Files by Ex&t";

private define recent_files_menu_callback (popup)
{
   load_recent_file_list ();
   variable files = get_most_recent_files ();
   display_recent_files_menu (popup, files);
}

private define recent_files_specific_ext_menu_callback (popup)
{
   variable ext = strchop (popup, '.', 0)[-1];
   variable files = get_most_recent_files_by_ext (ext);
   display_recent_files_menu (popup, files);
}

private define recent_files_ext_menu_callback (popup)
{
   load_recent_file_list ();
   variable exts = assoc_get_keys (Recentx_Cache);
   exts = exts [array_sort (exts)];

   variable menu = "Global.&File." + Recent_Files_Ext_Menu_Name;
   foreach (exts)
     {
	variable ext = ();
	menu_append_popup (menu, ext);
	menu_set_select_popup_callback (strcat (menu, ".", ext),
					&recent_files_specific_ext_menu_callback);
     }
}


% This function is called by _jed_switch_active_buffer_hooks
private define switch_active_buffer_hook (buf)
{
   ifnot (Recentx_Use_Cache)
     return;

   variable file, dir, n;
   variable blist;

   % find out the file name with full path
   (file,dir,buf,) = getbuf_info ();
   ifnot (strlen (file))
     return;

   file = dircat (dir, file);
   load_recent_file_list ();
   add_file_to_database (file, _time ());

   save_recent_file_list ();
}

append_to_hook ("_jed_switch_active_buffer_hooks", &switch_active_buffer_hook);

private define add_recent_files_popup_hooks (menubar)
{
   ifnot (Recentx_Use_Cache)
     return;

   variable menu = "Global.&File";

   menu_append_separator (menu);
   variable dmenu = Recent_Files_Menu_Name;
   menu_append_popup (menu, dmenu);
   menu_set_select_popup_callback (strcat (menu, ".", dmenu),
				   &recent_files_menu_callback);
   dmenu = Recent_Files_Ext_Menu_Name;
   menu_append_popup (menu, dmenu);
   menu_set_select_popup_callback (strcat (menu, ".", dmenu),
				   &recent_files_ext_menu_callback);
}
append_to_hook ("load_popup_hooks", &add_recent_files_popup_hooks);

% End of file recent.sl