% 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: % % Tag_file: The name of the tags file to use. The default is % "tags". custom_variable ("Tag_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); } #ifeval (_slang_version < 20000) variable error_happened = 0; ERROR_BLOCK { _clear_error (); error_happened = 1; } buf = user_mark_buffer (s.mark); if (error_happened == 0) goto_user_mark (s.mark); if (error_happened == 0) return; #else try { buf = user_mark_buffer (s.mark); goto_user_mark (s.mark); return; } catch AnyError; #endif () = 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; !if ((n = re_fsearch (strcat ("\\c^", tag, "\t+\\([^\t]+\\)\t+"))), n) 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 (); !if (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 = integer (bufsubstr ()); } return sinfo; } % etags format: % ^L % filename,some-number % [function-type] function-name ^?line-name,some-number private define etags_find (tag) { variable file, line, tmptag, msg = "Tag file needs updated?"; % we do the re_fsearch in order of preference: user->function->array tmptag = strcat ("[: ]", tag); !if (re_fsearch (strcat (tmptag, "[\t ]+\x7F\\(\\d+\\),"))) !if (re_fsearch (strcat (tmptag, "[\t \\(]+\x7F\\(\\d+\\),"))) !if (re_fsearch (strcat (tmptag, "[\t \\[]+\x7F\\(\\d+\\),"))) error (msg); line = integer (regexp_nth_match (1)); () = bol_bsearch (char (014)); % previous ^L go_down_1 (); push_mark (); skip_chars ("^,\n"); file = bufsubstr (); !if (read_file (file)) error ("File not found."); goto_line (line); } private define goto_tag (s) { () = read_file (s.file); variable line = s.line; if (String_Type == typeof (line)) { bob (); () = bol_fsearch (line); } else goto_line (line); } 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; (,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 = locate_tags_file (Tag_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 () { #ifeval (_slang_version >= 20000) variable word_chars = "\\w_"; #else variable word_chars = "0-9A-Za-z_"; #endif #ifdef VMS word_chars = strcat (word_chars, "$"); #endif 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); !if (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]); 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() != " ")) { 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 (); }