MODULE ctags IDENT "V01-000" ! ! TPU program to use ctags file ! ! Author: Martin Vorlaender ! ! Use like ! EDIT/TPU/COMMAND=CTAGS.TPU file.c ! search for tag by positioning the cursor on it ! and pressing GOLD-T (default setting; see below) ! ! The Exuberant Ctags project can be found at ! http://ctags.sourceforge.net/ ! ! This is my first TPU program, so bear with me... !----- Configurable options -----! ! Key defined to execute find-tag CONSTANT ctags_key := KEY_NAME( "T", SHIFT_KEY ); ! requires SET GOLD PF1 ! ! another option (that doesn't need the GOLD key) would be e.g. CTRL_F_KEY ! ctags file name (default: tags. file in current directory) CONSTANT ctags_filename := "sys$disk:[]tags."; ! video attribute for recognized tag CONSTANT attribute := BOLD; ! valid characters in tags (i.e. identifiers) CONSTANT alphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "$_"; !----- End Configuration section -----! CONSTANT TAB := ASCII(9); VARIABLE ctags_buffer, ! buffer that holds the tags file ctags_window; ! window for display of the ctags_buffer ! Procedures for use with EVE$BUILD ! to make this a permanent part of EVE ! (cf. "EVE Reference", Appendix D) ! (also see at end of file) PROCEDURE ctags_module_ident RETURN "v01-000"; ENDPROCEDURE; ! PROCEDURE ctags_module_init DEFINE_KEY( "find_current_tag", ctags_key, "Find CTAGS" ); ENDPROCEDURE; PROCEDURE find_tag( mytag ) ! Load a tags file & find mytag in it ON_ERROR CASE ERROR [TPU$_OPENIN] : MESSAGE( "Tag file " + ctags_filename + " not found" ); [OTHERWISE] : MESSAGE( ERROR_TEXT ); ENDCASE; ABORT; ENDON_ERROR; ! Avoid reload ctags_buffer := GET_INFO( BUFFER, "FIND_BUFFER", ctags_filename ); IF ctags_buffer = 0 THEN ctags_buffer := CREATE_BUFFER( ctags_filename, ctags_filename ); SET( MODIFIABLE, ctags_buffer, OFF ); SET( NO_WRITE, ctags_buffer ); MESSAGE( "ctags file loaded" ); ENDIF; POSITION( BEGINNING_OF( ctags_buffer ) ); RETURN( SEARCH_QUIETLY( LINE_BEGIN + mytag, FORWARD, EXACT ) ); ENDPROCEDURE; ! find_tag PROCEDURE split_on_tab( str1; max_parts ) ! split a string on TAB characters (optionally into max_parts at most) ! returns an array of substrings LOCAL i, p, result; IF GET_INFO( max_parts, "TYPE" ) = INTEGER THEN p := max_parts; ELSE p := 3; ENDIF; result := CREATE_ARRAY( p, 0 ); i := 0; LOOP p := INDEX( str1, TAB ); EXITIF (p = 0) OR (i+1 = max_parts); result{i} := SUBSTR( str1, 1, p-1 ); i := i+1; str1 := SUBSTR( str1, p+1 ); ENDLOOP; result{i} := str1; RETURN( result ); ENDPROCEDURE; ! split_on_tab PROCEDURE mark_word ! Find a word at the current editing point ! Mark it & return it as a string ! (modeled after eve$current_word, eve$end_of_word, eve$start_of_word) LOCAL start_of_word, end_of_word, saved_mark, word_range; POSITION( TEXT ); ! snap cursor saved_mark := MARK( FREE_CURSOR ); IF saved_mark = END_OF( CURRENT_BUFFER ) THEN RETURN( 0 ); ENDIF; IF CURRENT_CHARACTER = "" THEN MOVE_HORIZONTAL( 1 ); word_range := CREATE_RANGE( saved_mark, saved_mark, attribute ); RETURN( STR( word_range ) ); ENDIF; ! If not on a word, goto next word IF INDEX( alphabet, CURRENT_CHARACTER ) = 0 THEN POSITION( SEARCH_QUIETLY( ANY( alphabet ) | LINE_END, FORWARD ) ); ENDIF; ! Goto end of word POSITION( SEARCH_QUIETLY( NOTANY( alphabet ) | LINE_END, FORWARD ) ); MOVE_HORIZONTAL( -1 ); end_of_word := MARK( FREE_CURSOR ); ! Goto start of word POSITION( SEARCH_QUIETLY( NOTANY( alphabet ) | LINE_BEGIN, REVERSE ) ); IF CURRENT_OFFSET <> 0 THEN MOVE_HORIZONTAL( 1 ); ENDIF; start_of_word := MARK( FREE_CURSOR ); word_range := CREATE_RANGE( start_of_word, end_of_word, attribute ); RETURN( STR( word_range ) ); ENDPROCEDURE; ! mark_word PROCEDURE get_locator( loc_str ) ! get a locator from a string ! emulate the simple regex "/^...$/" case ! return an integer or a pattern LOCAL l; IF INDEX( "0123456789", SUBSTR( loc_str, 1, 1 ) ) <> 0 THEN ! EX command is a line number ! (well...okay... at least the first character...) RETURN( INT( loc_str ) ); ELSE ! EX command is a regex l := LENGTH( loc_str ); IF (SUBSTR( loc_str, 1, 2 ) = "/^") AND (SUBSTR( loc_str, l-1, 2 ) = "$/") THEN RETURN( LINE_BEGIN + SUBSTR( loc_str, 3, l-4 ) + LINE_END ); ELSE RETURN( TPU$K_UNSPECIFIED ); ENDIF; ENDIF; ENDPROCEDURE; ! get_locator PROCEDURE show_def_file( filename, locator ) ! Load a file, map to a window, position at locator LOCAL this_buffer, ! buffer containg tag definition file org_top, ! top row of original window org_len, ! # of rows in original window win_top, ! top row of tag window win_len, ! # of rows in tag window loc_cmd; ! return from get_locator ON_ERROR CASE ERROR [TPU$_OPENIN] : MESSAGE( "Error opening " + filename ); [OTHERWISE] : MESSAGE( ERROR_TEXT ); ENDCASE; ABORT; ENDON_ERROR; this_buffer := GET_INFO( BUFFER, "FIND_BUFFER", filename ); IF this_buffer = 0 THEN this_buffer := CREATE_BUFFER( filename, filename ); SET( MODIFIABLE, this_buffer, OFF ); SET( NO_WRITE, this_buffer ); ENDIF; ! Open window for file buffer IF GET_INFO( ctags_window, "TYPE" ) <> WINDOW THEN org_top := GET_INFO( CURRENT_WINDOW, "TOP", VISIBLE_TEXT ); org_len := GET_INFO( CURRENT_WINDOW, "LENGTH", VISIBLE_TEXT ); IF org_len < 9 THEN MESSAGE( "Not enough room to split window"); ABORT; ENDIF; win_top := org_top; win_len := org_len / 3; ! at least 3 (2 + status line) ctags_window := CREATE_WINDOW( win_top, win_len, ON ); ! Fool EVE window management (or really myself?) eve$x_number_of_windows := eve$x_number_of_windows + 1; eve$$x_windows{eve$x_number_of_windows} := ctags_window; ENDIF; MAP( ctags_window, this_buffer ); POSITION( BEGINNING_OF( this_buffer ) ); ! Execute command to jump to tag line loc_cmd := get_locator( locator ); CASE GET_INFO( loc_cmd, "TYPE" ) [INTEGER] : POSITION( loc_cmd ); [PATTERN] : POSITION( SEARCH_QUIETLY( loc_cmd, FORWARD, EXACT ) ); [OTHERWISE] : MESSAGE( "Unsupported locator" ); ABORT; ENDCASE; ENDPROCEDURE; ! show_def_file PROCEDURE locate_tag( mytag ) ! find a tag by name LOCAL tag_line, ! (string) line containing tag complete_tag, ! complete tag name filename, ! file containing tag ex_cmd, ! EX command locator, ! used to locate tag: line number or regexp line, ! (array) temp variable ext_opt, ! (array) extended options; key:value pairs tagrange, ! temp variable pos; ! temp variable ON_ERROR MESSAGE( ERROR_TEXT ); ABORT; ENDON_ERROR; IF LENGTH( mytag ) < 1 THEN RETURN( 0 ); ENDIF; tagrange := find_tag( mytag ); IF tagrange = 0 ! not found THEN MESSAGE( mytag + " not found in tags file" ); RETURN( 0 ); ENDIF; ! Dissect line POSITION( tagrange ); tag_line := CURRENT_LINE; line := split_on_tab( tag_line, 3 ); complete_tag := line{0}; filename := line{1}; ex_cmd := line{2}; pos := INDEX( ex_cmd, ';"'+TAB ); IF pos = 0 THEN locator := ex_cmd; ext_opt := CREATE_ARRAY; ELSE locator := SUBSTR( ex_cmd, 1, pos-1 ); ext_opt := split_on_tab( SUBSTR( ex_cmd, pos+1 ) ); ENDIF; show_def_file( filename, locator ); ENDPROCEDURE; ! locate_tag PROCEDURE find_current_tag ! find the tag under the cursor LOCAL mytag, saved_mark, ! save current cursor position saved_win; ! save current window ON_ERROR POSITION( saved_win ); POSITION( saved_mark ); ENDON_ERROR; saved_win := CURRENT_WINDOW; saved_mark := MARK( FREE_CURSOR ); mytag := mark_word; locate_tag( mytag ); ! Return to cursor position POSITION( saved_win ); POSITION( saved_mark ); ENDPROCEDURE; ! find_current_tag ENDMODULE; ! ctags ! comment this out if using with EVE$BUILD DEFINE_KEY( "find_current_tag", ctags_key, "Find CTAGS" );