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/ctags.sl
% tags.sl	-*- SLang -*-
%
% read a tags file produced by ctags/etags programs
%
% Public Functions:
%   ctags_forward
%      Go forward to the previous tag
%   ctags_backward
%      Go back to the previous position
%   ctags_find
%      Lookup a tag in the tags file and jump to the indicated position
%   ctags_popup_tag
%      Like ctags_find, except popup in a separate window
%
% Public Variables:
%
%   Tags_file:  The name of the tags file to use.   The default is
%   "tags".
%
% Buffer Local Variables:
%   Tags_File: The name of the tag file to use
%   Word_Chars: The characters that make up a word
custom_variable ("Tags_File", "tags");

private variable Position_Stack = NULL;
private variable Position_Stack_Ptr = NULL;

private define create_position ()
{
   variable s = struct
     {
	file, line, mark, next, prev
     };
   s.mark = create_user_mark ();
   s.file = buffer_filename ();
   s.line = what_line ();
   return s;
}

private define save_position (s)
{
   if (Position_Stack_Ptr != NULL)
     {
	Position_Stack_Ptr.next = s;
	s.prev = Position_Stack_Ptr;
     }
   else
     Position_Stack = s;

   Position_Stack_Ptr = s;
}

private define goto_position ()
{
   variable s = Position_Stack_Ptr;
   variable buf;
   EXIT_BLOCK
     {
	sw2buf (buf);
     }

   try
     {
	buf = user_mark_buffer (s.mark);
	if ((buf != whatbuf ()) && (s.file == "") && bufferp(buf))
	  sw2buf (buf);
	goto_user_mark (s.mark);
	return;
     }
   catch AnyError;

   () = read_file (s.file);
   goto_line (s.line);
   s.mark = create_user_mark ();
   buf = whatbuf ();
}

private define back_position ()
{
   if (Position_Stack_Ptr == NULL)
     verror ("Can't go back more");
   variable s = Position_Stack_Ptr.prev;
   if (s == NULL)
     verror ("Can't go back more");
   Position_Stack_Ptr = s;
   goto_position ();
}

private define forw_position ()
{
   if (Position_Stack_Ptr == NULL)
     verror ("Can't go forward");
   variable s = Position_Stack_Ptr.next;
   if (s == NULL)
     verror ("Can't go forward");
   Position_Stack_Ptr = s;
   goto_position ();
}

% ctags format:
%  function-name\tfilename\t/^function-prototype/
%  typedef-name\tfilename\tline-number
% Note: an extended format file will look like:
%  function-name\tfilename\t/^function-prototype$/;"TAB...
private define _ctags_find (tag, sinfo)
{
   variable n, file, proto;

   n = re_fsearch (strcat ("\\c^", tag, "\t+\\([^\t]+\\)\t+"));
   if (n == 0)
     {
	n = re_fsearch (strcat ("^", tag, "\t+\\([^\t]+\\)\t+"));
	if (n == 0)
	  return NULL;
     }
   file = regexp_nth_match (1);

   variable dir;
   (,dir,,) = getbuf_info ();
   sinfo.file = dircat (dir, file);

   n--;
   go_right (n);
   if (looking_at ("/^"))
     {
	go_right (2);
	push_mark ();
	ifnot (ffind ("/;\"\t"))
	  {
	     eol (); bskip_chars ("\\\\$/");
	  }
	if (blooking_at ("$"))
	  go_left (1);

#ifexists strreplace
	proto = str_replace_all (bufsubstr (), "\\/", "/");
#else
	% Versions of slang prior to 1.4.1 do not have strreplace and
	% str_replace_all will fail unless something is done to trick it.
	proto = str_replace_all (bufsubstr (), "\\/", "\001\002\x7f");
	proto = str_replace_all (proto, "\001\002\x7f", "/");
#endif
	sinfo.line = proto;
     }
   else
     {
	push_mark ();
	eol ();
	sinfo.line = atoi (bufsubstr ());
     }
   return sinfo;
}

% etags format:
%  ^L
%  filename,some-number
%  [function-type] function-name ^?line-name,some-number
private define etags_find (tag, sinfo)
{
   variable file, line;

   variable re = "\x7F" + tag + "\x01" + `\(\d+\)`;
   ifnot (re_fsearch (re))
     return NULL;
   line = integer (regexp_nth_match (1));
   eol();
   push_spot ();

   () = bol_bsearch (char (014));	% previous ^L
   go_down_1 ();
   push_mark (); skip_chars ("^,\n");
   file = bufsubstr ();

   pop_spot ();
   sinfo.line = line;
   sinfo.file = file;
   return sinfo;
}

private define tag_search_forward (tag)
{
   variable m = create_user_mark ();
   variable len = strlen (tag);
   variable syntax_table = what_syntax_table ();
   variable is_case_insensitive
     = ((syntax_table == NULL) ? 1
	: get_syntax_flags (syntax_table) & 0x1);

   variable cs = CASE_SEARCH;
   CASE_SEARCH = not (is_case_insensitive);

   EXIT_BLOCK
     {
	CASE_SEARCH = cs;
     }
   variable wordchars = "\\w_";
   while (fsearch (tag))
     {
	bskip_chars (wordchars);
	push_mark ();
	skip_chars (wordchars);
	variable t = bufsubstr();
	if ((t == tag)
	    || (is_case_insensitive && (strup(t) == strup(tag))))
	  {
	     bskip_chars (wordchars);
	     return 1;
	  }
     }
   goto_user_mark (m);
   return 0;
}

private define goto_tag (s, tag)
{
   () = read_file (s.file);
   variable line = s.line;
   if (String_Type == typeof (line))
     {
	bob ();
	() = bol_fsearch (line);
     }
   else goto_line (line);

   bol ();

   ifnot (tag_search_forward (tag))
     {
	message ("Your tags file needs to be updated");
	return;
     }
}

private define tags_find (find_method, tag)
{
   variable s = Struct_Type[0];
   forever
     {
	variable s1 = struct
	  {
	     file, line
	  };
	s1 = (@find_method) (tag, s1);
	if (s1 == NULL)
	  break;
	s = [s, s1];
     }

   if (length (s) == 0)
     verror ("Unable to find %s.  Perhaps your tags file needs updated.", tag);

   return s;
}

private define locate_tags_file (tags_file)
{
   variable dir;

   if (path_is_absolute (tags_file))
     {
	if (1 == file_status (tags_file))
	  return tags_file;

	return NULL;
     }

   (,dir,,) = getbuf_info ();

   forever
     {
	variable file = dircat (dir, tags_file);
	if (1 == file_status (file))
	  return file;

	% This may need modified for non-Unix systems...
#ifdef UNIX
	dir = expand_filename (dircat (dir, "../"));
	if (dir == "/")
	  break;
#elifdef IBMPC_SYSTEM
	dir = expand_filename (dircat (dir, "..\\"));
	if ((dir == "/") or (dir == "\\"))
	  break;
	if (strlen (dir) == 3)
	  {
	     if (dir[1] == ':')
	       break;
	  }
#elifdef VMS
	% Does this work?
	dir = expand_filename (dircat (dir, "[-]"));
#endif
     }

   return NULL;
}

private define find_tags_file ()
{
   variable file, dir, dir1;
   variable tbuf = " *tags*";

   file = get_blocal_var("Tags_File", Tags_File);
   file = locate_tags_file (file);
   if (file == NULL)
     error ("Unable to find a tags file");

   (dir1,) = parse_filename (file);

   if (bufferp (tbuf))
     {
	(,dir,,) = getbuf_info (tbuf);
	if (dir == dir1)
	  return;
     }

   setbuf (tbuf);

   erase_buffer ();
   if (insert_file (file) < 0)
     error ("File tags not found!");

   variable flags;

   (file,,tbuf,flags) = getbuf_info ();
   setbuf_info (file,dir1,tbuf,flags);
}

private define get_word_at_point (word_chars)
{
   push_spot ();
   skip_white ();
   bskip_chars (word_chars);
   push_mark ();
   skip_chars (word_chars);
   variable tag = bufsubstr ();
   pop_spot ();
   return tag;
}

private define get_tag_at_point ()
{
#ifexists _slang_utf8_ok
   variable word_chars = "\\w_";
#else
   variable word_chars = "0-9A-Za-z_";
#endif
#ifdef VMS
   word_chars = strcat (word_chars, "$");
#endif
   word_chars = get_blocal_var ("Word_Chars", word_chars);
   return read_mini ("Find tag:", get_word_at_point (word_chars), "");
}

define ctags_find ()
{
   variable tag;
   if (_NARGS == 1)
     tag = ();
   else
     tag = get_tag_at_point ();

   tag = strtrim (tag);
   ifnot (strlen (tag))
     return;

   variable cbuf = whatbuf ();
   variable cpos = create_position ();

   find_tags_file ();

   bob ();
   variable find_method = &_ctags_find;

   if (looking_at_char (014))	% if first char is ^L (etags)
     find_method = &etags_find;

   variable s = tags_find (find_method, tag);
   goto_tag (s[0], tag);

   save_position (cpos);
   variable tag_pos = create_position ();
   save_position (tag_pos);
   sw2buf (whatbuf ());
}

define ctags_forward ()
{
   forw_position ();
}
define ctags_backward ()
{
   back_position ();
}

% This function pops up a window containing a specified position but
% leaves the point in the current window/buffer.
private define popup_window_containing_buffer_position (m1)
{
   variable m0 = create_user_mark ();
   variable m0buf = user_mark_buffer (m0);
   variable m1buf = user_mark_buffer (m1);
   variable cwindow = window_info ('t');

   if (m0buf == m1buf)
     {
	% We may need to split this window if it is the only one
	% containing m0buf
	variable nwin = 0;
	loop (nwindows ())
	  {
	     if (m0buf == whatbuf ())
	       nwin++;
	     otherwindow ();
	  }
	variable total_windows = nwindows ();
	if (MINIBUFFER_ACTIVE)
	  total_windows--;

	if (nwin == 1)
	  {
	     if (total_windows == 1)
	       splitwindow ();
	     else loop (nwindows ())
	       {
		  if ((whatbuf () != m0buf)
		      and (whatbuf() != " <mini>"))
		    {
		       sw2buf (m0buf);
		       break;
		    }
		  otherwindow ();
	       }
	  }
     }
   pop2buf (m1buf);
   goto_user_mark (m1);

   % Now go back to the original window.
   % If there are several windows that contain m0buf, find the one we
   % started from.
   loop (nwindows ())
     {
	otherwindow ();
	if ((window_info ('t') == cwindow)
	    and (m0 == create_user_mark ()))
	  return;
     }
   % We could not find the window _and_ mark, so just look for the mark
   loop (nwindows ())
     {
	if (m0 == create_user_mark ())
	  return;
	otherwindow ();
     }
   % Should not get here
}

define ctags_popup_tag ()
{
   variable m0 = create_user_mark ();
   ctags_find ();
   variable m1 = create_user_mark ();
   if (m0 == m1)
     return;

   % Restore the previous position
   sw2buf (user_mark_buffer (m0));
   goto_user_mark (m0);

   % Popup a window containing the new position.
   popup_window_containing_buffer_position (m1);
}

% Compatibility
define find_tag ()
{
   ctags_popup_tag ();
}