home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / p2demo21.exe / PEL / TAGS.PEL < prev    next >
Text File  |  1995-03-13  |  32KB  |  1,129 lines

  1. # $Header:   P:\source\wmacros\tags.pev   1.41   13 Mar 1995 16:37:52   WALKER  $
  2.  
  3. ##############################################################################
  4. #
  5. #       Compuware Corporation
  6. #         31440 Northwestern Highway
  7. #           Farmington Hills, Michigan 48334-2564
  8. #
  9. #   This source code listing contains information that is
  10. #   proprietary to Compuware Corporation and may not be copied
  11. #   duplicated, translated, transmitted, stored, retrieved
  12. #   or in any manner or by any method conveyed or disclosed
  13. #   to a third party or parties without express written
  14. #   permission from Compuware Corporation.
  15. #
  16. #  
  17. ##############################################################################
  18.  
  19. #### $Workfile:   tags.pel  $: tags support
  20.  
  21. ## overview:
  22. #
  23. # A tags file is a data file which lists identifiers (function names, 
  24. # variables, etc.) along with information on where each was defined within 
  25. # the source code.  With a tags file in place, one can use the functions 
  26. # "tags" and "tags_auto" contained herein to "bring in" the appropriate 
  27. # source file and position the cursor at the definition.
  28. #
  29. # How does one build a tags file?  In addition to the two tags file building 
  30. # functions included with the editor (peltags_make and ctags_make), several commercial 
  31. # tags file building utilities exist which quickly build tags files for many 
  32. # languages.  The tags file format used here is compatible with Moderne 
  33. # Software's PC-TAGS(tm) progam.
  34.  
  35.  
  36. local tags_search_flags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH + SEARCH_FORWARD
  37. ## public entry points:
  38. #
  39. # tags        - prompt for a function or variable name and move to its
  40. #               definition in the source code.  Requires a tags data
  41. #               file.
  42. #
  43. # tags_auto   - use the function or variable name under the cursor and
  44. #               move to its definition in the source code.
  45. #
  46. #  (NOTE:   The following two "peltags" functions find global functions
  47. #     and variables even without a tags data file.  The tags file
  48. #     is still need to find local functions and variables.)
  49. #
  50. # peltags     - like tags, above, but is optimized for pel language source
  51. #               code.
  52. #
  53. # peltags_auto- like tags_auto, above, but is optimized for pel language
  54. #               source code.
  55. #
  56. # peltags_make- builds a "peltags.tag" data file for all functions and
  57. #               initialized global variables in the current cpe.ae file.
  58. #
  59. #
  60. ## global variables
  61.  
  62. ## tags_path
  63. #
  64. # The following may contain a ";" separated list of tags data file names.
  65. # The "TAGS" environment variable, of the same format, may be used to
  66. # override the tags_path variable.
  67. #
  68. global   tags_path
  69.  
  70. global   IDD_CTAGS_MAKE          = 1620
  71. global   IDL_TAGS_DIRS           = 1621      
  72. global   IDE_TAGS_ADD_DIRS       = 1622
  73. global   IDB_TAGS_DELETE         = 1623
  74. global   IDB_TAGS_ADD            = 1635
  75. global   IDR_OUTLOC_SOURCE_DIRS  = 1624
  76. global   IDR_OUTLOC_SPECIFIED    = 1625
  77. global   IDE_TAGS_OUTPUT_DIR     = 1626
  78. global   IDE_TAGS_EXT_LIST       = 1627
  79. global   IDB_TAGS_MAKE           = 1628
  80.  
  81. local time_buf
  82. global function init_timestamp( timefile )
  83. {
  84.    time_buf = create_buffer(  timefile, 
  85.                               timefile, 
  86.                               BUFFER_SYSTEM + BUFFER_NO_UNDO )
  87. }
  88.  
  89. global function cleanup_timestamp()
  90. {
  91.    local old_cur_buf = current_buffer
  92.  
  93.    if ( time_buf )
  94.    {
  95.       current_buffer = time_buf
  96.       write_buffer()
  97.       delete_buffer( time_buf ); time_buf = 0
  98.    }
  99.  
  100.    current_buffer = old_cur_buf
  101. }
  102.  
  103. global function get_timestamp( fname )
  104. {
  105.    local old_cur_buf = current_buffer
  106.    local line
  107.    local timestamp
  108.  
  109.    if ( time_buf )
  110.    {
  111.       current_buffer = time_buf
  112.    
  113.       if ( search("^" quote_regex(fname), SEARCH_FORWARD + SEARCH_REGEX + SEARCH_WRAPS) )
  114.       {
  115.          line = read_buffer()
  116.          timestamp = suffix( line, length(line) - cindex(line, " ") )
  117.       }
  118.    }
  119.  
  120.    current_buffer = old_cur_buf
  121.  
  122.    return timestamp+0
  123. }
  124.  
  125. global function update_timestamp( fname )
  126. {
  127.    local old_cur_buf = current_buffer
  128.  
  129.    if ( time_buf )
  130.    {
  131.       current_buffer = time_buf
  132.  
  133.       # normalize the file name
  134.       fname = buildpath( fname )
  135.  
  136.       if ( search("^" quote_regex(fname), SEARCH_FORWARD + SEARCH_REGEX + SEARCH_WRAPS) )
  137.       {
  138.          delete_line()
  139.          goto_bol()
  140.       }
  141.       else
  142.       {
  143.          goto_buffer_bottom()
  144.  
  145.          if ( current_column != 1 )
  146.             insert_newline()
  147.       }
  148.       
  149.       insert_string( fname " " filetime(fname) )
  150.       insert_newline()
  151.    }
  152.  
  153.    current_buffer = old_cur_buf
  154. }
  155.  
  156. global function generate_timestamps( files, timefile )
  157. {
  158.    local full_spec
  159.    local current_file
  160.  
  161.    init_timestamp( timefile )
  162.  
  163.    current_file = findfirst_path( files )
  164.  
  165.    if ( current_file )
  166.    {
  167.       do
  168.       {
  169.          full_spec = buildpath( current_find_path() current_file )
  170.          update_timestamp( full_spec, filetime(full_spec) )
  171.    
  172.       } while ( (current_file = findnext_path()) );
  173.    }
  174.  
  175.    cleanup_timestamp()
  176. }
  177.  
  178. ## tags_auto()
  179. #
  180. # Treats the text under the cursor as a function or variable name, then
  181. # attempts to locate the symbol's definition in source code.  If found,
  182. # the cursor is moved to the beginning of the symbol's definition.
  183. #
  184. # Returns TRUE if successful, otherwise FALSE
  185. #
  186. global function tags_auto()
  187. {
  188.    return execute_auto("tags")
  189. }
  190.  
  191. ## tags()
  192. #
  193. # Prompts for a symbol to look up in the list of tags files.  If found,
  194. # the cursor is moved to the beginning of the function's or variable's
  195. # definition in the source code.  "use_symbol" can be use to search for 
  196. # the symbol if passed in as specified, w/out us doing any modifications
  197. #
  198. # Returns TRUE if successful, otherwise FALSE
  199. #
  200. global function tags( symbol, use_symbol )
  201. {
  202.    local current_symbol = ""
  203.  
  204.    # prompt for command if not specified as an argument
  205.    if ( !symbol )
  206.    {
  207.       current_symbol = symbol_under_cursor()
  208.       symbol = prompt_history( "CTAGSLOCATE", "Search for symbol: ", current_symbol, 1, 1, "ctagslocate" )
  209.  
  210.       if ( !symbol )
  211.          return FALSE
  212.    }
  213.  
  214.    # perform tags operation
  215.    if (tolower(path_ext(buffer_filename)) == ".pel")
  216.       return peltags( symbol )
  217.    else
  218.       return tags_find( symbol, use_symbol )
  219. }
  220.  
  221. ## tags_find()
  222. #
  223. # Scan tags data files for a specified symbol.  Each tags file contains a
  224. # list of function and variable names, paired with information about where
  225. # the symbol is defined in source code.  If an entry is found in one of the
  226. # tags files, a buffer is created for the appropriate file, and the cursor
  227. # is positioned on the first line of the definition.
  228. #
  229. # See tags() for a definition of use_symbol
  230. #
  231. # The rules for locating tags files are as follow:
  232. #  1.  Try each name listed in the TAGS environment variable;
  233. #  2.  If no TAGS var exists, use the tags_path PEL global variable
  234. #  3.  Try each file in the current directory with a ".tag" extension
  235. #
  236. local function tags_find( symbol, use_symbol )
  237. {
  238.    local tempTagsPath
  239.    local tagsFile
  240.    local wildcard
  241.    local searched
  242.    local paths[]
  243.    local i
  244.  
  245.    if ( !symbol )
  246.       return FALSE
  247.  
  248.    # search all tags files in TAGS env variable
  249.  
  250.    if ( "TAGS" in ENV )
  251.       tempTagsPath = ENV[ "TAGS" ]
  252.    else if ( tags_path )
  253.    {
  254.       # if no TAGS environment variable exists, try tags_path var
  255.       tempTagsPath = tags_path
  256.    }
  257.    else
  258.    {
  259.       # if neither of the above exist, use any *.tag found in the
  260.       # current directory or the directory where the current file
  261.       # resides
  262.       tempTagsPath = "*.tag;" path_path( buffer_filename ) "*.tag"
  263.    }
  264.  
  265.    split( tempTagsPath, paths, ";" )
  266.  
  267.    for ( i in paths )
  268.    {
  269.       wildcard = buildpath( paths[ i ] )
  270.  
  271.       # if the element is a directory, append *.tag.
  272.       if ( and(filemode( wildcard ), _SUBDIR) )
  273.          wildcard = wildcard "\\*.tag"
  274.  
  275.       if (( tagsFile = findfirst( wildcard )))
  276.       {
  277.          tagsFile = tolower( path_path( wildcard ) tagsFile )
  278.          if ( tags_lookup( tagsFile, symbol, use_symbol ))
  279.             return TRUE
  280.  
  281.          # expand a filespec containng "?" or "*"
  282.          if ( wildcard ~ /\?\*/ )
  283.          {
  284.             while (( tagsFile = findnext()))
  285.             {
  286.                tagsFile = tolower( path_path( wildcard ) tagsFile )
  287.                if ( tags_lookup( tagsFile, symbol, use_symbol ))
  288.                   return TRUE
  289.             }
  290.          }
  291.          searched = TRUE
  292.       }
  293.    }
  294.  
  295.    # can't find string in tags file
  296.    if ( searched )
  297.       warning( "Unknown symbol \"%s\"", symbol )
  298.    else
  299.       warning( "No tags file found" )
  300.  
  301.    return FALSE
  302. }
  303.  
  304. # Find all tags that begin with symbol and 
  305. # prompt the user to choose between them.
  306. local function prompt_tag_list( symbol, use_symbol )
  307. {
  308.    # save search flags
  309.    local pattern           = search_pattern
  310.    local old_search_flags  = search_flags
  311.  
  312.    local anIndex
  313.    local mysymbol = use_symbol ? symbol : "^" symbol " "
  314.    local tempArray
  315.    local tag[]
  316.    local count
  317.    local i
  318.  
  319.    tag.symbol = symbol
  320.  
  321.    # locate all occurrences of mysymbol in the file
  322.    search_flags = SEARCH_FORWARD + SEARCH_REGEX
  323.    tempArray = find_all( mysymbol, FALSE ) 
  324.    count = tempArray[0]
  325.    delete( tempArray[0] )
  326.  
  327.    if ( count == 1 )
  328.    {
  329.       for ( i in tempArray )
  330.          tag.line = i
  331.    }
  332.    else if ( count > 1 )
  333.    {
  334.       tag.symbol = prompt_selection( "Which line?", 150, tempArray )
  335.  
  336.       if ( tag.symbol )
  337.          tag.line = find_in_array( tempArray, tag.symbol )
  338.    }
  339.  
  340.    delete_history_item("SEARCH", mysymbol )
  341.  
  342.    # restore search pattern and flags
  343.    search_pattern = pattern
  344.    search_flags   = old_search_flags
  345.  
  346.    return tag
  347. }
  348.  
  349. ## tags_lookup()
  350. #
  351. # Given the name of a tags data file, and the name of a symbol to look up,
  352. # determine whether the symbol is defined in the tags file.  If so, create
  353. # a buffer for the appropriate source file, and position the cursor on
  354. # the first line of the definition.
  355. #
  356. # The format of a line in the tags file is as follows:
  357. #  <symbol><space><mode><source filename><space><^><pattern>
  358. # where:
  359. #  <mode> determines how to interpret the <pattern> field
  360. #     if mode==none  then pattern is an exact text match
  361. #     if mode=="|"   then pattern is a regular expression pattern
  362. #     if mode=="#"   then pattern is a line number
  363. #
  364. # Returns TRUE if the symbol was successfully matched, FALSE otherwise
  365. #
  366. local function tags_lookup( tagsFile, symbol, use_symbol )
  367. {
  368.    local priorBuffer = current_buffer
  369.    local definition
  370.    local mode
  371.    local filename
  372.    local line = 0
  373.    local theLine
  374.    local tag[]
  375.  
  376.    message( "searching %s...", tagsFile )
  377.  
  378.    if ( filemode(tagsFile) == -1 )
  379.    {
  380.       warning( "Tags file %s not found)", tagsFile )
  381.       return FALSE
  382.    }
  383.  
  384.    current_buffer = create_buffer( tagsFile, tagsFile, BUFFER_SYSTEM + 
  385.                                                        BUFFER_NO_UNDO )
  386.  
  387.    # look for symbol string in tags file
  388.    goto_buffer_top()
  389.  
  390.    tag = prompt_tag_list( symbol, use_symbol )
  391.  
  392.    if ( tag.line )
  393.    {
  394.       goto_line( tag.line )
  395.  
  396.       # skip past the symbol name and
  397.       # read the definition portion of the line
  398.       theLine = read_buffer()
  399.       current_column = match( theLine, " " ) + 1
  400.  
  401.       definition = read_buffer()
  402.  
  403.       # read <mode> field if present
  404.       if ( match( definition, /^[|#!]/ ))
  405.       {
  406.          mode = substr( definition, 1, RLENGTH )
  407.          definition = substr( definition, RLENGTH+1 )
  408.       }
  409.  
  410.       # read the <pattern> field
  411.       if ( !match( definition, / \^/ ))
  412.       {
  413.          if ( mode || !match( definition, /[ \t]\/.+\// ))
  414.          {
  415.             warning( "Tags file %s - format is invalid", tagsFile )
  416.             # return TRUE to end the tags search
  417.             return TRUE
  418.          }
  419.          # convert a unix style tags file to our format
  420.          mode = "|"
  421.          definition = substr( definition, 1, length(definition) - 1 )
  422.          match( definition, /[ \t]+\// )
  423.       }
  424.       filename = substr( definition, 1, RSTART-1)
  425.       definition = substr( definition, RSTART+RLENGTH )
  426.  
  427.       # remove the tags file buffer
  428.       if ( current_buffer != priorBuffer )
  429.       {
  430.          delete_buffer()
  431.          current_buffer = priorBuffer
  432.       }
  433.  
  434.       # place the cursor at the definition in the source file
  435.       if ( !tags_locate(filename, mode, definition) && buffer_filename == filename )
  436.          search( tag.symbol, SEARCH_FORWARD )
  437.  
  438.       return TRUE    # end the tags search
  439.    }
  440.  
  441.    # remove the tags file buffer
  442.    if ( current_buffer != priorBuffer )
  443.    {
  444.       delete_buffer()
  445.       current_buffer = priorBuffer
  446.    }
  447.    return FALSE
  448. }
  449.  
  450.  
  451. ## tags_locate()
  452. #
  453. # Create a buffer for the specified source file, search for the specified
  454. # line, and position the cursor on the first line of the definition.
  455. #
  456. global function tags_locate( filename, mode, definition )
  457. {
  458.    local success
  459.  
  460.    # switch to the new buffer
  461.    if ( filemode( filename ) == -1 || !create_buf_and_win( filename ))
  462.    {
  463.       warning( "Unable to find file %s", filename )
  464.       return FALSE
  465.    }
  466.    goto_buffer_top()
  467.    if ( !mode )
  468.    {
  469.       # exact match (whole line)
  470.       success = bol_search( definition "\r" )
  471.    }
  472.    else if ( mode ~ /\|/ )
  473.    {
  474.       # context sensitive (regex) search
  475.       success = !!search( definition, tags_search_flags )
  476.    }
  477.    else if ( mode ~ /#/ )
  478.    {
  479.       # definition is a line number
  480.       goto_line( 0+definition )
  481.       success = TRUE
  482.    }       
  483.  
  484.    center_cursor()
  485.    display_filename()
  486.  
  487.    return success
  488. }
  489.  
  490. ## bol_search()
  491. #
  492. # Search for an exact match of specified text beginning in column 1
  493. #
  494. local function bol_search( text )
  495. {
  496.    save_position()
  497.  
  498.    # search without regex, until we succeed in matching text in col 1
  499.    while ( search( text, SEARCH_FORWARD ))
  500.    {
  501.       if ( current_column == 1 )
  502.          return TRUE
  503.  
  504.       current_column = 1
  505.       current_line++
  506.    }
  507.  
  508.    # text not found
  509.    restore_position(1)
  510.    return FALSE
  511. }
  512.  
  513. ## execute_auto()
  514. #
  515. # This function executes the function specified by fun and passes it the
  516. # selected text or the symbol_under_cursor as the parameter.
  517. #
  518. global function execute_auto(fun, symbol_regex)
  519. {
  520.    local len
  521.    local symbol
  522.  
  523.    if (selection_type())
  524.    {
  525.       len = distance_between_marks(selection_mark_top(), selection_mark_bottom())
  526.       if (len)
  527.       {
  528.          save_position()
  529.          end_selection()
  530.          goto_mark(selection_mark_top())
  531.          symbol = read_buffer(len)
  532.          restore_position(1)
  533.       }
  534.    }
  535.  
  536.    if ( !symbol )
  537.       symbol = symbol_under_cursor(symbol_regex)
  538.  
  539.    if ( symbol )
  540.    {
  541.       if (fun)
  542.          return execute_function(function_id(fun, symbol))
  543.       else
  544.          return symbol
  545.    }
  546.    else
  547.    {
  548.       warning( "cursor not on valid symbol" )
  549.       return FALSE
  550.    }
  551. }
  552.  
  553. ############################### pel tags support ############################
  554.  
  555. ## local variables
  556.  
  557. local   pelTagsFile
  558.  
  559. ## peltags()
  560. #
  561. # Variant of the tags() function which looks first in the current .AE file's
  562. # symbol table for the specified symbol.  Only if that fails does the search
  563. # proceed to the tags data files.
  564. #
  565. global function peltags( symbol )
  566. {
  567.    local tmp_array
  568.    local filename
  569.    local definition
  570.    local response
  571.  
  572.    # prompt for command if not specified as an argument
  573.    if ( !symbol )
  574.    {
  575.       symbol = prompt_history( "CTAGSLOCATE", "Search for symbol:", "", 1, 1, "peltags_dialog" )
  576.       if ( !symbol )
  577.          return FALSE
  578.    }
  579.  
  580.    # Look in the current .AE file's symbol table for a PEL local or
  581.    # global function.
  582.    #
  583.    message( "Looking for:%s", symbol )
  584.    tmp_array = symbol_match( symbol, LOCAL_FUN + GLOBAL_FUN )
  585.    if ( tmp_array[1] )
  586.    {
  587.       # if the symbol matched more than one, or if it was only the
  588.       # beginning of one, display choices box.
  589.       if (symbol != tmp_array[1] || tmp_array[2])
  590.       {
  591.       response = prompt_selection("Choose symbol", 25, tmp_array, 0, 1)
  592.       if (!response)
  593.          return FALSE
  594.       else
  595.          symbol = response
  596.       }
  597.  
  598.       if ( match( symbol , /:/ ))
  599.       {
  600.          definition = "^local[ \\t]*function[ \\t]+" \
  601.                       substr( symbol, RSTART+1 ) "\\("
  602.       }
  603.       else
  604.       {
  605.          definition = "^(global)?[ \\t]*function[ \\t]+" symbol "\\("
  606.       }
  607.       filename = source_file_name( function_id( symbol ))
  608.       if ( filename )
  609.       {
  610.          if ( !tags_locate(filename, "|", definition) && buffer_filename == filename )
  611.             search( symbol, SEARCH_FORWARD )
  612.  
  613.          return TRUE
  614.       }
  615.    }
  616.  
  617.    # otherwise pass it on to tags
  618.    #
  619.    if ( !pelTagsFile )
  620.    {
  621.       # get name of tags file
  622.       initPelTagsFile()
  623.    }
  624.    return tags_find( symbol )
  625. }
  626.  
  627. ## peltags_auto()
  628. #
  629. # Variant of the tags_auto() function which looks first in the current .AE
  630. # file's symbol table for the symbol under the cursor.  Only if that fails
  631. # does the search proceed to the tags data files.
  632. #
  633. global function peltags_auto()
  634. {
  635.    return execute_auto("peltags")
  636. }
  637.  
  638. ## peltags_make()
  639. #
  640. # build the PEL tags database
  641. #
  642. global function peltags_make() 
  643. {
  644.    local priorBuffer = current_buffer
  645.    local tagsBuffer
  646.    local function_list
  647.    local file_list
  648.    local tmp_array
  649.    local lastUpdate
  650.    local symbol
  651.    local srch
  652.    local fn
  653.    local i
  654.    local j
  655.    local step
  656.    local count
  657.  
  658.    local old_sbflags = status_bar_flags
  659.  
  660.    # get name of tags file
  661.    if ( !pelTagsFile ) 
  662.       initPelTagsFile()
  663.  
  664.    # this will take a while...
  665.    lastUpdate = ctime( filetime( pelTagsFile ) )
  666.    if ( lastUpdate ) 
  667.       lastUpdate = "Tags file was last updated " lastUpdate
  668.    else 
  669.       lastUpdate = "This will take a while"
  670.  
  671.    i = confirm( lastUpdate " - really update? [yn] ", "yYnN" )
  672.    if ( tolower(i) != "y" ) 
  673.       return
  674.  
  675.    status_bar_flags = or( status_bar_flags, STB_MESSAGES )
  676.  
  677.    message( "Building %s - Please wait...", pelTagsFile )
  678.  
  679.    # create a system buffer for the tags info
  680.    # (no file name avoids reading an obsolete file)
  681.    current_buffer = tagsBuffer = create_buffer( pelTagsFile,  "",
  682.                                                 BUFFER_SYSTEM + BUFFER_NO_UNDO )
  683.  
  684.    process_background( DISABLE_FOREGROUND )
  685.  
  686.    ##
  687.    ## create a sorted list of all global functions
  688.    ##
  689.  
  690.    tmp_array = symbol_match( "", GLOBAL_FUN )
  691.    count = 0
  692.    for ( i in tmp_array )
  693.    {
  694.       count++
  695.       function_list[ tmp_array[ i ] ] = 0
  696.    }
  697.  
  698.    delete tmp_array
  699.  
  700.    # insert the name, source filename, and regex pattern into the buffer
  701.    message( "global functions..." )
  702.    j = 0
  703.    step = count / 100
  704.    step += (count % 100) ? 1 : 0
  705.    for ( i in function_list )
  706.    {
  707.       fn = source_file_name( function_id( i ))
  708.       file_list[ fn ] = 1
  709.  
  710.       insert_string( i " |" fn " ^^(global)?[ \\t]*function[ \\t]+" i "\\(" )
  711.       insert_newline()
  712.  
  713.       if ( (j++ % step) == 0 )
  714.          message( "%s( %d%% done )", "global functions", j / step )
  715.    }
  716.    delete function_list
  717.  
  718.    ##
  719.    ## append a sorted list of all local functions
  720.    ##
  721.  
  722.    message( "local functions..." )
  723.    tmp_array = symbol_match( "", LOCAL_FUN )
  724.    count = 0
  725.    for ( i in tmp_array )
  726.    {
  727.       count++
  728.       symbol = tmp_array[ i ]
  729.       match( symbol, ":" )
  730.       function_list[ substr( symbol, RSTART+1 ) ] =   \
  731.             source_file_name( function_id( symbol ))
  732.    }
  733.    delete tmp_array
  734.  
  735.    # insert the name, source filename, and regex pattern into the buffer
  736.    j = 0
  737.    step = count / 100
  738.    step += (count % 100) ? 1 : 0
  739.    for ( i in function_list )
  740.    {
  741.       insert_string( i " |" function_list[ i ]  \
  742.                      " ^^local[ \\t]*function[ \\t]+" i "\\(" )
  743.       insert_newline()
  744.  
  745.       if ( (j++ % step) == 0 )
  746.          message( "%s( %d%% done )", "local functions", j / step ) 
  747.    }
  748.    delete function_list
  749.  
  750.    ##
  751.    ## append a list of all global variables
  752.    ##
  753.  
  754.    message( "global variables..." )
  755.  
  756.    j = 0
  757.    for ( fn in file_list ) 
  758.    {
  759.       # open the buffer as a system buffer
  760.       if ( filemode( fn ) == -1 ) 
  761.          continue
  762.  
  763.       message( "global variables in %s...", fn )
  764.  
  765.       current_buffer = create_buffer( "", fn, BUFFER_SYSTEM + BUFFER_NO_UNDO )
  766.  
  767.       # search for global variable declarations
  768.       goto_buffer_top()
  769.       while ( search( "global", SEARCH_FORWARD ) )
  770.       {
  771.          # read the entire line
  772.          current_column = 0
  773.          symbol = srch = read_buffer()
  774.          next_line()
  775.  
  776.          # trim comments
  777.          if ( match( symbol, /[ \t]*#/ )) 
  778.             symbol = substr( symbol, 1, RSTART-1 )
  779.  
  780.          # trim the "="
  781.          if ( match( symbol, /[ \t]*=/ )) 
  782.             symbol = substr( symbol, 1, RSTART-1 )
  783.          else 
  784.             continue
  785.  
  786.          # trim the "global" keyword
  787.          if ( match( symbol, /^[ \t]*global[ \t]+/ ) ) 
  788.             symbol = substr( symbol, RSTART+RLENGTH )
  789.          else 
  790.             continue
  791.  
  792.          # add the symbol to the list
  793.          function_list[ symbol ] = srch
  794.       }
  795.       delete_buffer()
  796.  
  797.       # insert the name and source filename in the buffer
  798.       current_buffer = tagsBuffer
  799.       for ( i in function_list ) 
  800.       {
  801.          insert_string( i " " fn " ^" function_list[ i ] )
  802.          insert_newline()
  803.       }
  804.       delete function_list
  805.    }
  806.  
  807.    # save the new tags file
  808.    current_buffer = tagsBuffer
  809.    write_buffer( pelTagsFile )
  810.    if ( current_buffer != priorBuffer ) 
  811.    {
  812.       delete_buffer()
  813.       current_buffer = priorBuffer
  814.    }
  815.    message( "" )
  816.    status_bar_flags = old_sbflags 
  817.  
  818.    process_background( END_BACKGROUND )
  819.  
  820.    play( "L4MS<ccc" )   # signal that we are done
  821. }
  822.  
  823. ## initialize the pelTagsFile variable with the full name of the tags file
  824. #
  825. local function initPelTagsFile()
  826. {
  827.    local thisFilename = source_file_name( function_id( "tags" ))
  828.  
  829.    # use "peltags.tag" prefixed with the full path
  830.    if ( thisFilename )
  831.       pelTagsFile = path_path( thisFilename ) "peltags.tag"
  832.    else
  833.       pelTagsFile = buildpath( "peltags.tag" )
  834.  
  835.    # create the tags_path variable if necessary
  836.    if ( !tags_path )
  837.       tags_path = pelTagsFile
  838. }
  839.  
  840. function tags_settings( settings_index, settings_data )
  841. {
  842.    local new_array;
  843.  
  844.    settings_data[ settings_index++ ] = sprintf( "tags_path=%s\n", tags_path )
  845.    
  846.    new_array.array = settings_data
  847.    new_array.index = settings_index
  848.    return new_array
  849. }
  850.  
  851. local function update_tags_make_config( tags_paths, output_dir )
  852. {
  853.    local i
  854.    local index
  855.    local path
  856.    local paths_list
  857.    local settings_data[]
  858.  
  859.    for ( index in tags_paths )
  860.       if ( paths_list )
  861.          paths_list = paths_list ";" tags_paths[ index ]
  862.       else
  863.          paths_list = tags_paths[ index ]
  864.    
  865.    settings_data[ ++i ] = paths_list "\n"
  866.  
  867.    if ( output_dir )
  868.       settings_data[ ++i ] = output_dir "\n"
  869.  
  870.    update_config_section( "$TAGS_MAKE$", settings_data )
  871. }
  872.  
  873. local start_dir
  874. global function ctags_make_dialog_callback()
  875. {
  876.    local ret = DRC_CONTINUE
  877.    local index
  878.    local count
  879.    local path
  880.    local str
  881.    local tagsDirs[]
  882.    local tagsOutput
  883.    local config_data
  884.    local tag_file_specified
  885.  
  886.    if ( callback_msg == DM_INIT )
  887.    {
  888.       start_dir = getcwd()
  889.  
  890.       add_dialog_item( callback_dialog_handle, IDE_TAGS_ADD_DIRS, DCTRL_EDIT_KEY )
  891.  
  892.       config_data = read_config_section( "$TAGS_MAKE$", 0, TRUE )
  893.  
  894.       str = config_data[ 1 ]
  895.       tagsOutput = config_data[ 2 ]
  896.  
  897.       if ( split(str, tagsDirs, ";") )
  898.       {
  899.          for ( index in tagsDirs )
  900.          {
  901.             path = tagsDirs[ index ]
  902.    
  903.             if ( query_dialog_item( callback_dialog_handle, 
  904.                                     IDL_TAGS_DIRS, 
  905.                                     DAC_FIND, 
  906.                                     path) == -1 )
  907.             {
  908.                set_dialog_item(  callback_dialog_handle, 
  909.                                  IDL_TAGS_DIRS, 
  910.                                  DAC_ADD_ITEM, 
  911.                                  path )
  912.             }
  913.          }
  914.       }
  915.  
  916.       # add a blank entry so the user can insert at the end of the list
  917.       set_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_ADD_ITEM, " " )
  918.  
  919.    
  920.       set_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_SELECT_INDEX, 0 )
  921.  
  922.       if ( tagsOutput )
  923.       {
  924.          set_dialog_item(  callback_dialog_handle, 
  925.                            IDR_OUTLOC_SOURCE_DIRS, 
  926.                            DAC_UNCHECK )
  927.    
  928.          set_dialog_item(  callback_dialog_handle, 
  929.                            IDR_OUTLOC_SPECIFIED, 
  930.                            DAC_CHECK )
  931.  
  932.          set_dialog_item( callback_dialog_handle, IDE_TAGS_OUTPUT_DIR, DAC_ENABLE )
  933.          set_dialog_item( callback_dialog_handle, IDE_TAGS_OUTPUT_DIR, DAC_TEXT, tagsOutput )
  934.       }
  935.       else
  936.       {
  937.          set_dialog_item(  callback_dialog_handle, 
  938.                            IDR_OUTLOC_SOURCE_DIRS, 
  939.                            DAC_CHECK )
  940.    
  941.          set_dialog_item(  callback_dialog_handle, 
  942.                            IDR_OUTLOC_SPECIFIED, 
  943.                            DAC_UNCHECK )
  944.  
  945.          set_dialog_item( callback_dialog_handle, IDE_TAGS_OUTPUT_DIR, DAC_DISABLE )
  946.       }
  947.  
  948.       str = get_ext_list( "c" )
  949.       gsub( "\\.", "*.", str )
  950.  
  951.       set_dialog_item( callback_dialog_handle, IDE_TAGS_EXT_LIST, DAC_TEXT, str )
  952.  
  953.       set_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_SETFOCUS )
  954.    }
  955.    else if ( callback_msg == DM_HELPREQUESTED )
  956.    {
  957.       display_help("ctagsmake")
  958.       ret = DRC_MSG_PROCESSED
  959.    }
  960.    else if ( callback_msg == DM_CLICK || callback_msg == DM_DOUBLE_CLICK )
  961.    {
  962.       ret = DRC_MSG_PROCESSED
  963.  
  964.       if ( callback_index == IDB_TAGS_DELETE )
  965.       {
  966.          index = query_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_SELECT_INDEX )
  967.  
  968.          if ( index >= 0 )
  969.          {
  970.             set_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_DELETE_INDEX, index )
  971.  
  972.             count = query_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_COUNT_ITEMS )
  973.  
  974.             if ( index <= count - 1 )
  975.                set_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_SELECT_INDEX, index )
  976.             else
  977.                set_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_SELECT_INDEX, count - 1 )
  978.          }
  979.       }
  980.       else if ( callback_index == IDB_TAGS_ADD )
  981.       {
  982.          ctags_add_path(callback_dialog_handle)
  983.       }
  984.       else if ( callback_index == IDR_OUTLOC_SOURCE_DIRS )
  985.       {
  986.          set_dialog_item(  callback_dialog_handle, IDR_OUTLOC_SPECIFIED, DAC_UNCHECK )
  987.  
  988.          set_dialog_item(  callback_dialog_handle, IDE_TAGS_OUTPUT_DIR, DAC_DISABLE )
  989.       }
  990.       else if ( callback_index == IDR_OUTLOC_SPECIFIED )
  991.       {
  992.          set_dialog_item( callback_dialog_handle, IDR_OUTLOC_SOURCE_DIRS, DAC_UNCHECK )
  993.  
  994.          set_dialog_item( callback_dialog_handle, IDE_TAGS_OUTPUT_DIR, DAC_ENABLE )
  995.       }
  996.       else if ( callback_index == IDB_TAGS_MAKE )
  997.       {
  998.          count = query_dialog_item( callback_dialog_handle, IDL_TAGS_DIRS, DAC_COUNT_ITEMS )
  999.  
  1000.          for ( index = 0; index < count; index++ )
  1001.          {
  1002.             str = query_dialog_item(   callback_dialog_handle, 
  1003.                                        IDL_TAGS_DIRS, 
  1004.                                        DAC_GET_INDEX_ITEM, index )
  1005.  
  1006.             str = ltrim( trim(str) )
  1007.  
  1008.             if ( str )
  1009.                tagsDirs[ index ] = str
  1010.          }
  1011.  
  1012.          set_dialog_item( callback_dialog_handle, IDB_TAGS_MAKE, DAC_DISABLE )
  1013.  
  1014.          if ( !query_dialog_item(callback_dialog_handle, IDR_OUTLOC_SOURCE_DIRS, DAC_CHECK) )
  1015.          {
  1016.             str = query_dialog_item( callback_dialog_handle, IDE_TAGS_OUTPUT_DIR, DAC_TEXT )
  1017.             tag_file_specified = buildpath( str )
  1018.  
  1019.             if ( filemode(tag_file_specified) == _SUBDIR )
  1020.                tag_file_specified = tag_file_specified "/ctags.tag"
  1021.          }
  1022.  
  1023.          set_dialog_window( callback_dialog_handle, DWC_HIDE )
  1024.          str = query_dialog_item( callback_dialog_handle, IDE_TAGS_EXT_LIST, DAC_TEXT )
  1025.          for ( index in tagsDirs )
  1026.          {
  1027.             if ( chdir( tagsDirs[index] ) )
  1028.             {
  1029.                if ( tag_file_specified )
  1030.                {
  1031.                   if ( !ctags_make(str, tag_file_specified) )
  1032.                      break    # break out of loop if user interrupted tags
  1033.                }
  1034.                else if ( !ctags_make(str, buildpath(tagsDirs[index] "/ctags.tag")) )
  1035.                   break    # break out of loop if user interrupted tags
  1036.             }
  1037.             else
  1038.                warning( "path " tagsDirs[index] " does not exist" )
  1039.          }
  1040.          play( "L4MS<ccc" )   # signal that we are done
  1041.  
  1042.          update_tags_make_config( tagsDirs, tag_file_specified )
  1043.  
  1044.          chdir( start_dir )
  1045.  
  1046.          ret = DRC_CONTINUE
  1047.       }
  1048.       else
  1049.          ret = DRC_CONTINUE
  1050.    }
  1051.    else if ( callback_msg == DM_INVALID_PCHAR && callback_index == IDE_TAGS_ADD_DIRS )
  1052.    {
  1053.       if ( callback_data == KEYCODE_ENTER || callback_data == KEYCODE_KEYPAD_ENTER )
  1054.       {
  1055.          ret = DRC_MSG_PROCESSED
  1056.          ctags_add_path(callback_dialog_handle)
  1057.       }
  1058.       else
  1059.          ret = DRC_CONTINUE
  1060.    }
  1061.    else if ( callback_msg == DM_CANCEL )
  1062.    {
  1063.       if ( query_dialog_item(callback_dialog_handle, IDB_TAGS_MAKE, DAC_DISABLE) )
  1064.       {
  1065.          process_background( STOP_BACKGROUND )
  1066.          chdir( start_dir )
  1067.       }
  1068.    }
  1069.  
  1070.    return ret
  1071. }
  1072.  
  1073. local function ctags_add_path(handle)
  1074. {
  1075.    local str, path
  1076.    local index
  1077.    local ret
  1078.    local oldPE = pause_on_error
  1079.    
  1080.    # if not enabled, warning message goes on the status bar of parent window
  1081.    pause_on_error = 1   
  1082.   
  1083.    str = query_dialog_item( handle, IDE_TAGS_ADD_DIRS, DAC_TEXT )
  1084.    path = buildpath( ltrim(trim(str)) )
  1085.  
  1086.    if ( path && filemode(path) != -1 )
  1087.    {
  1088.       index = query_dialog_item( handle, IDL_TAGS_DIRS, DAC_FIND, path )
  1089.       if ( index == -1 )
  1090.       {
  1091.          # get the index of the currently selected item
  1092.          index = query_dialog_item( handle, IDL_TAGS_DIRS, DAC_SELECT_INDEX )
  1093.  
  1094.          if ( index == -1 )
  1095.             index = 0
  1096.  
  1097.          # insert the new path before the currently selected item
  1098.          set_dialog_item( handle, IDL_TAGS_DIRS, DAC_ADD_INDEX, path, index )
  1099.  
  1100.          # reset index to the currently selected item
  1101.          index = query_dialog_item( handle, IDL_TAGS_DIRS, DAC_FIND, path )
  1102.       }
  1103.  
  1104.       if ( index != -1 )
  1105.          set_dialog_item( handle, IDL_TAGS_DIRS, DAC_SELECT_INDEX, index )
  1106.    }
  1107.    else
  1108.       warning( "path %s is not valid", path )
  1109.  
  1110.    pause_on_error = oldPE
  1111.    set_dialog_item( handle, IDE_TAGS_ADD_DIRS, DAC_SETFOCUS )
  1112.    set_dialog_item( handle, IDE_TAGS_ADD_DIRS, DAC_SELECTED_RANGE, 
  1113.                      0, length(str) )
  1114. }
  1115.  
  1116.  
  1117. global function ctags_make_dialog()
  1118. {  
  1119.    local dlgHand = create_dialog( function_id("ctags_make_dialog_callback"), 0, IDD_CTAGS_MAKE )
  1120.  
  1121.    set_dialog_item( dlgHand, IDE_TAGS_ADD_DIRS,   DAC_SET_TEXT_LEN, 512 )
  1122.    set_dialog_item( dlgHand, IDE_TAGS_OUTPUT_DIR, DAC_SET_TEXT_LEN, 512 )
  1123.    set_dialog_item( dlgHand, IDE_TAGS_EXT_LIST,   DAC_SET_TEXT_LEN, 512 )
  1124.  
  1125.    begin_dialog( dlgHand )
  1126.    
  1127.    delete_dialog(dlgHand)
  1128. }
  1129.