home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / p2demo21.exe / PEL / ELECTRIC.PEL < prev    next >
Text File  |  1995-04-19  |  36KB  |  1,261 lines

  1. # $Header:   P:\source\wmacros\electric.pev   1.101   19 Apr 1995 12:59:56   PFHDWM0  $
  2. # $Tabs:4 7$
  3.  
  4. ##############################################################################
  5. #
  6. #       Compuware Corporation
  7. #         31440 Northwestern Highway
  8. #           Farmington Hills, Michigan 48334-2564
  9. #
  10. #   This source code listing contains information that is
  11. #   proprietary to Compuware Corporation and may not be copied
  12. #   duplicated, translated, transmitted, stored, retrieved
  13. #   or in any manner or by any method conveyed or disclosed
  14. #   to a third party or parties without express written
  15. #   permission from Compuware Corporation.
  16. #
  17. #  
  18. ##############################################################################
  19.  
  20. #### $Workfile:   electric.pel  $: helper routines for entering C code
  21. #
  22. #       There are three principal facilities in this package:
  23. #       1. Insertion of code templates
  24. #               (see "function initialized_template()" in this module
  25. #                on how to add new templates)
  26. #       2. Bracket matching (several flavors)
  27. #
  28.  
  29. #
  30. #       Clipper Support and general improvements made to this module
  31. #       have be provided by P. Polakoff III, 3P Software, Inc.
  32. #
  33.    
  34. ### "electric" templates program-entry features.
  35. #
  36. #       The template entry facility works differently than some other
  37. #       products' in that templates are inserted only upon demand, and
  38. #       not automatically upon recognition of an abbreviation.
  39. #
  40. #       Templates are auto-indented independently of the autoindent mode.
  41. #
  42. #       Template insertion is activated manually by a keypress.  The key
  43. #       that is assigned by default to do the template expansion is the
  44. #       ESC key.  Any other key may be used, but to not lose this key's 
  45. #       original behavior, it should be handled in the expand_template_key()
  46. #       function.  This function is prepared to work for the SPACE, TAB,
  47. #       or ESC key.
  48. #
  49. #       The mode may be turned on and off by calls to ec() or 
  50. #       toggle_electric(). It additionally is enabled or disabled depending 
  51. #       on whether the extension on the current buffer's name matches one of 
  52. #       the patterns in the expand template list.
  53. #
  54. #       Multiple-character abbreviation sequences are allowed.
  55. #
  56.  
  57. global  language_template       # the map of abbreviations onto expanded strings
  58.  
  59. global  D_DIALECT = "clipper87" # dBASE dialect (used by errorfix.pel also)
  60. global  ELEC_LANG = "text"      # current electric language
  61.  
  62. global  electric_mode = 0       # 0 = disabled, 1 = enabled
  63.                                 # initialized_template() was called.
  64. global K_R = 1                   # set the K&R brace matching
  65. #local   newEditFileId
  66. local   _es = "`"
  67.  
  68. global electric_symbol 
  69.  
  70. # keep track of former assignments to ec keys
  71. local expand_key_set
  72. local next_key_set
  73. local default_keys[]
  74. local DEFAULT_EXPAND_TEMPLATE_KEY   = "<Space>"
  75. local DEFAULT_NEXT_FIELD_KEY        = "<Ctrl-Enter>"
  76.  
  77. global syntax_loaded[];     #  Array keeping track of what syntax has been loaded
  78. #
  79. # Addition of new templates:
  80. #  --------------------------
  81. # Add a new element to the file_extensions[] array and initialize it
  82. # with the associated extensions as demonstrated below.  Then add
  83. # the new electric array as defined in initialize_template() below.
  84. #
  85.  
  86. # Possible file extents for each language.  Each is a regular expression
  87. # to match the buffer's filename extension.
  88. #
  89. # "electric_extensions" takes on one of these values initialized in
  90. # initialize_extensions().
  91. #
  92.  
  93. global function load_factory_default_templates(reset)
  94. {
  95.    templates["awk"]       = "setup_awk_template";
  96. #     templates["basic"]     = "setup_null_template basic"; Temporarily discontinued;
  97.    templates["c"]         = "setup_C_template";
  98.    templates["clipper50"] = "dBaseDialect";
  99.    templates["clipper87"] = "dBaseDialect";
  100.    templates["cobol"]     = "setup_cobol_template";
  101. #     templates["cpp"]       = "setup_C_template";
  102.    templates["dbase"]     = "dBaseDialect";
  103.    templates["force"]     = "dBaseDialect";
  104.    templates["pascal"]    = "setup_pascal_template";
  105.    templates["rexx"]      = "setup_rexx_template";
  106.    templates["text"]      = "setup_null_template text";
  107.    templates["[None]"]      = "setup_null_template text";
  108. }
  109.  
  110. ## ec()
  111. #
  112. #  enable/display template generation ( 0 = disable, 1 = enable );
  113. #
  114. #function ec( mode )
  115. #{
  116. #   if (argcount())
  117. #      toggle_electric( mode );
  118. #   else
  119. #      toggle_electric()
  120. #}
  121.  
  122. global function set_expand_template_key( key )
  123. {
  124.    local strkey
  125.    local newkey = "expand_template_key"
  126.     
  127.    if (expand_key_set && (argcount() == 0) )
  128.       return
  129.       
  130.    if (key == "")
  131.       key = DEFAULT_EXPAND_TEMPLATE_KEY
  132.       
  133.    # don't create a circular loop by assigning the same
  134.    # function to both variables
  135.    strkey = keymap_binding( key )
  136.    if ( strkey != newkey ) 
  137.    {
  138.       default_keys[ key ] = strkey
  139.       assign_key( key, newkey )
  140.    }
  141.  
  142.    expand_key_set = 1
  143. }
  144.  
  145. global function remove_expand_template_key( key )
  146. {
  147.    if ( default_keys[ key ] )
  148.    {
  149.       assign_key( key, default_keys[ key ] )
  150.       delete default_keys[ key ]
  151.    }
  152.    else
  153.    {
  154.       warning( "remove_expand_template_key:  key \"%s\" not assigned", key )
  155.    }
  156. }
  157.  
  158. global function set_next_field_key( key )
  159. {
  160.    local strkey
  161.    local newkey = "next_field_key"
  162.  
  163.    if (next_key_set && (argcount() == 0) )
  164.       return
  165.       
  166.    if (key == "")
  167.       key = DEFAULT_NEXT_FIELD_KEY
  168.    
  169.    # don't create a circular loop by assigning the same
  170.    # function to both variables
  171.    strkey = keymap_binding( key )
  172.    if ( strkey != newkey )
  173.    {
  174.       default_keys[ key ] = strkey
  175.       assign_key( key,  newkey )
  176.    }
  177.  
  178.    next_key_set = 1
  179. }
  180.  
  181. global function remove_next_field_key( key )
  182. {
  183.    if ( default_keys[ key ] )
  184.    {
  185.       assign_key( key, default_keys[ key ] )
  186.       delete default_keys[ key ]
  187.    }
  188.    else
  189.    {
  190.       warning( "remove_next_field_key:  key \"%s\" not assigned", key )
  191.    }
  192. }
  193.  
  194. ## toggle_electric()
  195. #
  196. #  enable/display template generation ( 0 = disable, 1 = enable );
  197. #
  198. function toggle_electric( on )
  199. {
  200.    local verbose = 0
  201.  
  202.    if( argcount() < 1 )
  203.    {
  204.       on = !electric_mode
  205.       verbose = 1
  206.    }
  207.  
  208.    electric_mode = on
  209.    set_expand_template(path_ext(buffer_filename), on);
  210.    
  211.    if (electric_mode)
  212.    {
  213.       initialize_template();
  214. #      set_expand_template_key( DEFAULT_EXPAND_TEMPLATE_KEY )
  215. #      set_next_field_key( DEFAULT_NEXT_FIELD_KEY )
  216.       set_expand_template_key( )
  217.       set_next_field_key( )
  218.    }
  219.    else
  220.    {
  221. #      if (newEditFileId)
  222. #      {
  223. #         #delete_event( EVENT.NEW_EDIT_FILE, newEditFileId );
  224. #         newEditFileId = 0;
  225. #      }
  226.  
  227.       # reinitialize the electric vars
  228.       #  electric_extensions = initialized_file_ext = ""
  229.  
  230. #      remove_expand_template_key( DEFAULT_EXPAND_TEMPLATE_KEY )
  231. #      remove_next_field_key( DEFAULT_NEXT_FIELD_KEY )
  232.    }
  233.    
  234.    if( verbose )
  235.       message( "Template expansion is %s.", electric_mode ? "on" : "off")
  236. }
  237.  
  238. ## initialize_template()
  239. #
  240. #  Initialize the correct template for the language being used.
  241. #  The language is determined by the extension of the current buffer's
  242. #  name.  If the template has already been initialized, it is ignored.
  243. #
  244. #  Addition of new templates:
  245. #  --------------------------
  246. #  To add a new template, create a new element in the file_extensions
  247. #  array defined in initialize_extensions() above.
  248. #
  249. #  Next, duplicate one of the if-else statements below and change the
  250. #  language_template[] settings appropriately.  Also, be sure the comparison
  251. #  with "_extensions" (your new variable name) is updated as well.
  252. #
  253. local function initialize_template()
  254. {
  255.    local OB, EB, EB2
  256.    local temp = Template(path_ext(buffer_filename));
  257.    local fn, id;
  258.    local def_name      = "_default"
  259.  
  260.    if(temp == ELEC_LANG)
  261.       return;
  262.  
  263.    if (temp)
  264.    {
  265.       fn = "__" temp;
  266.       id = function_id( fn );
  267.     }
  268.  
  269.    if (!id)
  270.       id = function_id( def_name );
  271.  
  272. #     cvdebug("calling >" function_name(id) "<");
  273.    if ( id )
  274.       execute_function( id );
  275.  
  276.    # setup brace and block style
  277.  
  278.    if ( K_R )
  279.    {
  280.       OB  = electric_symbol.open_brace = "{\n\t"
  281.       EB  = electric_symbol.end_brace  = "\n\b} "
  282.       EB2 = electric_symbol.end_brace2 = "\n\b\b} "
  283.    }
  284.    else
  285.    {
  286.       OB  = electric_symbol.open_brace = "\t{\n"
  287.       EB  = electric_symbol.end_brace  = "\n} "
  288.       EB2 = electric_symbol.end_brace2 = "\n\b} "
  289.    }
  290.  
  291.    electric_symbol.new_block = OB "`statement-1`" EB "\n"
  292.    electric_symbol.block     = OB "`statement-1`" EB 
  293.  
  294.    #  this condition is here to remove the warning during a compile
  295.    if(0)
  296.       setup_null_template();
  297.  
  298.    #  This creates an infinite loop on startup.  If anyone can remember why it
  299.    #  is here, please fix and comment it; (JAM:12-29-94);
  300.    #  new_edit_file();
  301.  
  302.    if( (temp == "clipper87") || (temp == "clipper50") ||
  303.        (temp == "force")     || (temp == "dbase") )
  304.        
  305.       optional_function(templates[temp], temp);
  306.    else
  307.       optional_function(templates[temp]);
  308. }
  309.  
  310. local function setup_null_template(template)
  311. {
  312.    ELEC_LANG = template;
  313.    language_template[ "null" ] = "\0";
  314. }
  315.  
  316. ## template_add()
  317. #
  318. #  Provides ability to add templates on the fly
  319. #  It is also possible to change a current template with this function
  320. #
  321. function template_add( tstr, expstr )
  322. {
  323.    if (argcount() < 1)
  324.       tstr=prompt("Template characters: ","")
  325.    if (tstr && argcount() < 2)
  326.       expstr=prompt("Template expansion: ", language_template[ tstr ] )
  327.    if(tstr)
  328.    {
  329.       gsub("\\\\\\\\","\\",expstr)
  330.       language_template[tstr]=expstr
  331.       message("Template \""tstr"\" created.")
  332.    }
  333. }
  334.  
  335. ## expand_template_key()
  336. #
  337. #  Expand a template or do default behavior of key
  338. #
  339. function expand_template_key()
  340. {
  341.    local key
  342.    local key_action
  343.  
  344.    if ( !expand_template( 1 ) )
  345.    {
  346.       key = default_keys[ int_to_key( current_key ) ]
  347.  
  348.       if ( key )
  349.       {
  350.          key_action = function_id( key )
  351.    
  352.          if (  key_action                                         && 
  353.                key_action != function_id("expand_template_key")   &&
  354.                function_caller() == "Keyboard" ) 
  355.             execute_function( key_action )
  356.       }
  357.    }
  358.  
  359. }
  360.  
  361. ## expand_template()
  362. #
  363. #  Expand a template if one can be located at or near the cursor.
  364. #
  365. function expand_template( noError )
  366. {
  367.    local keyLen, key, i
  368.    local oldPE = pause_on_error
  369.    local status = 0
  370.    local ext = path_ext(buffer_filename)
  371.    local item, catname, match_case
  372.    
  373.    # turn pause_on_error off so message just displays on status_bar
  374.    # and no user input is involved
  375.    #
  376.    pause_on_error =  0 
  377.  
  378.    initialize_template()
  379. #     status = ( electric_mode && buffer_name ~ electric_extensions )
  380.    
  381.    status = ( electric_mode && Template(ext))
  382.    
  383.    if ( !noError )
  384.    {
  385.       if ( !electric_mode )
  386.          warning("Template expansion mode is not on!")
  387.       else if(!Template(path_ext(buffer_filename)))
  388.          warning("Invalid extension for template expansions!")
  389.    }
  390.  
  391.    if ( status )
  392.    {
  393.       # reset status to zero since this is the value we will return
  394.       # and it'll be changed later if need be
  395.       status = 0
  396.       
  397.       item = get_style_item();
  398.       catname = get_category_name(ext, item)
  399.       match_case = get_case_sensitive(path_ext(buffer_filename))
  400.  
  401.       key = left_symbol_under_cursor( SYMBOL_REGEX2 )
  402.  
  403.       # if key is not a comment or literal type then don't allow expansion when
  404.       # inside a comment or literal
  405.       if( match(item, quote_regex(key), !match_case) || (catname != "Comments" && catname != "Literals") )
  406.       {
  407.          if( key in language_template )
  408.          {
  409.             # since the key was found, set status to 1
  410.             #
  411.             status = 1
  412.          
  413.             keyLen = length( key )
  414.             
  415.             # starting at current cursor position, read the buffer for a string the
  416.             # size of the key.  If match, delete, else move to left and try again 
  417.             # so that it can be deleted before the template is inserted
  418.             #
  419.             for (i=0; i<=keyLen; i++) 
  420.             {
  421.                if (read_buffer( keyLen ) == key )
  422.                {
  423.                   delete_chars( keyLen )
  424.                   break
  425.                }
  426.                current_column--
  427.             }
  428.             insert_template( language_template[ key ])
  429.  
  430.             # make sure we are in the first field of the template
  431.             if ( match(language_template[key], _es) )
  432.                next_field_key()
  433.          }
  434.          else if ( !noError )
  435.             warning("No template for \"%s\".", key );
  436.       }
  437.       #  else
  438.       #     cvdebug("we are in a comment dude!");
  439.    }
  440.    
  441.    pause_on_error = oldPE 
  442.    return status
  443. }
  444.  
  445. ## insert_template()
  446. #
  447. #  insert a particular template
  448. #
  449. #       The following characters are treated specially:
  450. #
  451. #       1. "@" => place the cursor here after the template has been inserted
  452. #       2. "\n" => insert a newline and auto-indent.
  453. #       3. "\r" => insert a newline
  454. #       4. "\t" => indent an extra level (not actually treated specially)
  455. #       5. "\b" => unindent one level
  456. #       
  457. #       eg.: "{\n\t\n\b}" follows K&R
  458.  
  459. local function insert_template( t )
  460. {
  461.    local i, c
  462.    local col = current_column
  463.    local row = current_line
  464.    local rs = and(buffer_flags,BUFFER_REAL_SPACE_ONLY);
  465.  
  466.    buffer_flags = and( buffer_flags, not(BUFFER_REAL_SPACE_ONLY) );
  467.    
  468.    while( t && ( i = match( t, "@|\\n|\\b|\\r" )))
  469.    {
  470.       if( i > 1 )
  471.          insert_string( substr( t, 1, i - 1 ))
  472.       c = substr( t, i, 1 )
  473.       t = substr( t, i + 1 )
  474.       
  475.       if( c == "\n" )
  476.          insert_auto_indent()
  477.       else if( c == "\r" )
  478.          insert_newline()
  479.       else if( c == "\b" )
  480.          backspace();           
  481.       else if( c == "@" )
  482.       {
  483.          col = current_column
  484.          row = current_line
  485.       }
  486.    }
  487.    
  488.    insert_string( t )
  489.    
  490.    place_bookmark(buffer_filename":electric_end", 1, 1)
  491.  
  492.    buffer_flags = or( buffer_flags, rs );
  493.    
  494.    goto_pos( row, col )
  495. }
  496.  
  497. function insert_all_templates()
  498. {
  499.    local t
  500.    
  501.    initialize_template()
  502.    
  503.    for( t in language_template )
  504.    {
  505.       insert_string( t "\n" )
  506.       begin_selection( LINE_SELECTION )
  507.       insert_template( language_template[ t ])
  508.       insert_string( "@" )
  509.       goto_buffer_bottom()
  510.       insert_string( "\n\n" )
  511.       indent_tabs()
  512.       goto_mark( selection_mark_top() )
  513.       remove_selection()
  514.       prev_char()
  515.       delete_chars( 1 )
  516.       goto_buffer_bottom()
  517.       goto_bol()
  518.    }            
  519. }
  520.  
  521. local eventDeleted = TRUE
  522. local _nextFieldKey
  523. function next_field_key()
  524. {
  525.    local sf =  SEARCH_FORWARD+SEARCH_HIGHLIGHT+SEARCH_REGEX+SEARCH_WRAPS+SEARCH_ADVANCE
  526.    local pattern = _es ".*" _es
  527.    local old_search_pattern = search_pattern
  528.    local temp = message_level
  529.    local status = 0;
  530.    local key_action
  531.    local key
  532.  
  533.    if ( electric_mode )
  534.    {
  535.       # this function is usually only called by a keypress so we'll save
  536.       # it so it can be used when it comes time to delete a field
  537.       _nextFieldKey = current_key
  538.       
  539.       # This is to make sure the event is only attached once
  540.       if ( eventDeleted )
  541.       {
  542.          eventDeleted = FALSE;
  543.          attach_event_handler(EVENT.KEYPRESS, function_id("delete_field") )
  544.       }
  545.       
  546.       # change message level to prevent warning message if string isn't found
  547.       #
  548.       message_level = 3
  549.       status = sf_search(pattern, sf);
  550.       message_level = temp
  551.  
  552.       # remove our pattern from the history list and restore old search
  553.       # pattern
  554.       #
  555.       delete_history_item( "SEARCH", pattern )
  556.       search_pattern = old_search_pattern
  557.       if (!status && bookmark_defined(buffer_filename":electric_end") )
  558.       {
  559.          goto_bookmark(buffer_filename":electric_end")
  560.          delete_bookmark(buffer_filename":electric_end");
  561.          status = 1
  562.       }
  563.    }  
  564.  
  565.    if ( !status )
  566.    {
  567.       eventDeleted = TRUE;
  568.       delete_event(EVENT.KEYPRESS, function_id("delete_field") )
  569.       
  570.       # this is for default behavior if there is no field to go to 
  571.       #
  572.       key = default_keys[ int_to_key( current_key ) ]
  573.  
  574.       if ( key )
  575.          {
  576.          key_action = function_id( key )
  577.    
  578.          if (  key_action                                         && 
  579.                key_action != function_id("expand_template_key")   &&
  580.                function_caller() == "Keyboard" )
  581.             execute_function( key_action )
  582.          }
  583.    }
  584.  
  585.    return status;
  586. }
  587.  
  588. function delete_field()
  589. {
  590.    # only delete the field and event if we press a key other than a 
  591.    # directional key or the field key
  592.    #
  593.    if ( isDirectionalKey() )
  594.       eventDeleted = FALSE;
  595.    else if ( current_key != _nextFieldKey )
  596.    {
  597.       delete_event(EVENT.KEYPRESS, function_id("delete_field") )
  598.       if( selection_type() )
  599.          delete_chars();
  600.       eventDeleted = TRUE;
  601.       update_current_view()
  602.    }
  603. }
  604.  
  605. local function isDirectionalKey()
  606. {
  607.    local key = int_to_key(current_key)
  608.    if (key == "<Down>" || key == "<Up>" || key == "<Left>" || 
  609.        key == "<Right>" || key == "<PageDown>" || key == "<PageUp>" || 
  610.        key == "<Home>" || key == "<End>" )
  611.       return TRUE;
  612.    else
  613.       return FALSE;
  614. }
  615.  
  616. ### support for matching constructs
  617. #
  618.  
  619. global default_matching_pairs = "{ } [ ] ( ) /* */ %if %endif #if #endif #ifdef #endif"  # string of matching pairs
  620. global matching_pairs = default_matching_pairs
  621. local  matching_left  = "(\\{)|(\\[)|(\\()|(/\\*)|(%if)|(#if)|(#ifdef)"
  622.  
  623. local  FORWARD                   = TRUE
  624. local  BACKWARD                  = FALSE
  625.  
  626. local  last_matching_direction   = FORWARD
  627. local  last_matching_open        = ""
  628. local  last_matching_close       = ""
  629.  
  630. ## record the particular matching constructs
  631. #
  632. #  argument is a whitespace-separated sequence of opening, closing pairs
  633. #  a null string causes the defaults to be selected
  634.  
  635. local function init_matching_pairs( pairs )
  636. {
  637.    local larray[]
  638.    local odd = TRUE
  639.    local i
  640.    local ext
  641.  
  642.    if ( pairs )
  643.       matching_pairs = pairs
  644.    else
  645.    {
  646.       ext = path_ext(buffer_name)
  647.  
  648.       if ( ext in extensions )
  649.          matching_pairs = MatchingPairs(ext);
  650.       else
  651.          matching_pairs = ""
  652.    }
  653.  
  654.    matching_pairs = compress(ltrim(trim(matching_pairs)))
  655.    matching_left = ""
  656.  
  657.    if (matching_pairs == "")
  658.       return
  659.  
  660.    # create a regex in matching_left that will find the left side of pair
  661.    split( matching_pairs, larray, " " )
  662.    for ( i in larray )
  663.    {
  664.       if ( odd )
  665.       {
  666. #         gsub( "\\(", "\\(", larray[ i ] )
  667. #         matching_left = matching_left "(" larray[ i ] ")" "|"
  668.          matching_left = matching_left " " larray[ i ]
  669.          odd = FALSE
  670.       }
  671.       else
  672.          odd = TRUE
  673.    }
  674.  
  675.    # remove the last trailing "|"
  676. #   matching_left = substr( matching_left, 1, length(matching_left) - 1 )
  677.  
  678. #   gsub( "\\*", "\\*", matching_left )
  679. #   gsub( "\\[", "\\[", matching_left )
  680. #   gsub( "\\{", "\\{", matching_left )
  681.    matching_left = quote_regex(ltrim(matching_left))
  682.    matching_left = trans(matching_left, " ", "|")
  683. }
  684.  
  685.  
  686. ## form an inclusive selection around a matching pair
  687. #
  688. #  If a selection has already been made, merely swap selection marks.
  689. #  It is an error if no half of a matching pair can be found.
  690.  
  691. function mark_matching()
  692. {
  693.    local   m1, m2
  694.    local   status = FALSE
  695.    local   len_close
  696.    
  697.    if ( selection_type() )
  698.    {
  699.       # The selection is already present.  Goto the opposite mark.
  700.       m1 = selection_mark_top()
  701.       m2 = selection_mark_bottom()
  702.       if ( m1 && m2 )
  703.       {
  704.          if (mark_line( m1 )   == current_line &&
  705.              mark_column( m2 ) == current_column)
  706.          {
  707.             goto_mark( m2 )
  708.          }
  709.          else
  710.             goto_mark( m1 )
  711.       }
  712.       else
  713.          beep()
  714.    }
  715.    else
  716.    {
  717.       save_position()
  718.       if ( goto_matching() )
  719.       {
  720.          len_close = length( last_matching_close )
  721.  
  722.          # go to the last character of the matching construct
  723.          if ( last_matching_direction == FORWARD )
  724.             next_char( len_close - 1 )
  725.  
  726.          # A match has been found.  Select all text
  727.          # between and including the matching pair.
  728.          begin_selection( INCLUSIVE_SELECTION )
  729.          restore_position( TRUE )
  730.  
  731.          # go to the last character of the matching construct
  732.          if ( last_matching_direction == BACKWARD )
  733.             {
  734.             save_position()
  735.             next_char( len_close - 1 )
  736.             end_selection()
  737.             restore_position( TRUE )
  738.             }
  739.          else
  740.             end_selection()
  741.  
  742.          status = TRUE
  743.       }
  744.       else
  745.          restore_position( TRUE )
  746.    }
  747.    return status
  748. }
  749.  
  750.  
  751. ## form an inclusive selection around a NESTED matching pair
  752. #
  753. function mark_matching_next()
  754. {
  755.    local item
  756.    local catname
  757.    local ext = path_ext(buffer_filename)
  758.    local found_open
  759.    
  760.    init_matching_pairs( )
  761.  
  762.    if ( !matching_pairs )
  763.    {
  764.       warning( "No matching pairs defined for this extension" )
  765.       return
  766.    }
  767.  
  768.    if ( selection_type() != NO_SELECTION )
  769.    {
  770.       goto_mark( selection_mark_top() )
  771.       remove_selection()
  772.       update_current_view()
  773.       next_char()
  774.    }
  775.    
  776.    save_position()
  777.    while ( (found_open = search(matching_left, SEARCH_FWD_REGEX_MAX_IG + SEARCH_ADVANCE )) != 0 )
  778.    {
  779.       item = get_style_item();
  780.       catname = get_category_name(ext, item)
  781.       if( catname != "Comments" && catname != "Literals")
  782.          break;
  783.    }
  784.       
  785.    if (found_open)
  786.    {
  787.       restore_position(0);
  788.       mark_matching();
  789.    }
  790.    else
  791.    {
  792.       message("No opening pairs found.");
  793.       restore_position(1);
  794.    }
  795. }
  796.  
  797.  
  798. local function on_item(item, s)
  799. {
  800.    local ret = FALSE
  801.    local match_case = get_case_sensitive(path_ext(buffer_filename))
  802.    local symbol_offset
  803.    local start_offset = current_line_offset + 1 # make start_offset 1 based
  804.    local cur_line
  805.    local match_start, match_len
  806.    
  807.    save_position()
  808.    current_column = 0
  809.    cur_line = read_buffer()
  810.    restore_position(1)
  811.    
  812.    item = trans( quote_regex(compress(item)), " ", "|" )
  813.    
  814.    s = symbol_under_cursor( "[^ \t]+" )
  815.    symbol_offset = start_offset + 1 - index(cur_line, s)
  816.    
  817.    if ( match(s, item, !match_case) || match(s, item, !match_case))
  818.    {
  819.       match_start = RSTART
  820.       match_len = RLENGTH
  821.       if (symbol_offset >= RSTART && symbol_offset < RSTART+RLENGTH)
  822.       {
  823.          #warning("On Comment!!!!")
  824.          ret = TRUE
  825.       }
  826.    }
  827.       
  828.    return ret
  829. }
  830.  
  831. ## goto the matching construct corresponding to the one under the cursor
  832. #
  833. #  The cursor may be on a matching construct and be valid.
  834. #  The cursor is restored to its original position if no match is found.
  835. #  The optional parameter terminator is a pattern that if found terminates
  836. #  the command with an error status.  This is useful, for example, in C when
  837. #  you are on a ')', you can stop if you encounter a '{'.
  838.  
  839. function goto_matching( terminator, noErr )
  840. {
  841.    local forward           # odd => forward; even => backward
  842.    local base
  843.    local len, i, p
  844.    local matching_pair     # the array identifying matching pairs
  845.    local matching_count    # number of elements in matching_pair
  846.    local ext = path_ext(buffer_filename)
  847.    local item
  848.    local catname
  849.    local match_case = get_case_sensitive(ext)
  850.    local success = FALSE
  851.    local unbalanced = FALSE
  852.    local oldPE = pause_on_error;
  853.    local found1 = 0
  854.    local found2 = 0
  855.    local cur_symbol = ""
  856.    
  857.    # turn pause_on_error off so message just displays on status_bar
  858.    # and no user input is involved
  859.    #
  860.    pause_on_error =  0 
  861.    
  862.    last_matching_open   = ""
  863.    last_matching_close  = ""
  864.  
  865.    item = get_style_item()
  866.    catname = get_category_name(ext, item)
  867.    if( catname != "Comments" && catname != "Literals" || on_item(item))
  868.    {
  869.       init_matching_pairs()
  870.    
  871.       if (matching_pairs)
  872.       {
  873.          matching_count = split( matching_pairs, matching_pair )
  874.             
  875.          for ( i = 1; i <= matching_count; i++ )
  876.          {
  877.             p = matching_pair[i]
  878.             len = length( p )
  879.             if (!match_case)
  880.                p = tolower(p)
  881.       
  882.             cur_symbol = readn1(1)
  883.             if (!match_case)
  884.                cur_symbol = tolower(cur_symbol)
  885.                   
  886.             found1 = ( p == cur_symbol )
  887.             if ( !found1 && len > 1)
  888.             {
  889.                cur_symbol = symbol_under_cursor( "[^ \t]+" )
  890.                found2 = match(cur_symbol, quote_regex(p), !match_case)
  891.             }
  892.             
  893.             if ( found1 || found2 ) 
  894.                break
  895.          }  # end for(matching_pairs)
  896.       
  897.          if ( found1 || found2 ) 
  898.          {
  899.             forward              = and( i, 1 )
  900.             base                 = and( i + 1, 0xFFFFFFFE )
  901.       
  902.             last_matching_direction = forward
  903.             last_matching_open      = matching_pair[ base - 1 ]
  904.             last_matching_close     = matching_pair[ base ]
  905.       
  906.             if ( found1 )
  907.                success = scan_for_matching( forward, last_matching_open, last_matching_close, terminator )
  908.             else if ( found2 )
  909.             {
  910.                next_char()
  911.                prev_word( 1, "^|[ \t]" )
  912.                if ( in_whitespace() ) 
  913.                   right()
  914.                      
  915.                success = scan_for_matching( forward, last_matching_open, last_matching_close, terminator )
  916.             }
  917.       
  918.             if ( !noErr && !success )
  919.                warning( "unbalanced nesting pair: %s %s", last_matching_open, last_matching_close )
  920.          }  # end if (on matching construct)
  921.          else if ( !noErr )
  922.             warning( "Cursor not positioned on matching construct." )
  923.       }
  924.       else if ( !noErr )
  925.          warning( "No matching pairs defined for this extension" )
  926.    }
  927.    else if ( !noErr )
  928.       warning( "In comment or literal" )
  929.    
  930.    pause_on_error =  oldPE 
  931.    return success
  932. }
  933.  
  934. ## move the cursor to a matching construct, 
  935. #
  936. #  given a direction, and the opening and closing constructs.
  937. #  The cursor is restored to its original position if no match is found.
  938. local function scan_for_matching( forward, open, close, term )
  939. {
  940.    local item
  941.    local catname
  942.    local ext = path_ext(buffer_filename)
  943.    local match_case = get_case_sensitive(ext)
  944.    local olen = length( open )
  945.    local clen = length( close )
  946.    local tlen = length( term )
  947.    local pattern = quote_regex( open ) "|" quote_regex( close )
  948.    local flags = SEARCH_REGEX                      \
  949.                    + SEARCH_MAXIMAL_MATCH          \
  950.                    + SEARCH_ADVANCE                \
  951.                    + SEARCH_FORWARD * forward
  952.    local level = forward * 2 - 1   # forward => +1; backward => -1
  953.    local s
  954.    local block_items = merge_arrays(Block(ext), Line(ext))
  955.    local pattern_is_block
  956.    local i
  957.    
  958.    if ( (!match(open close, "[" quote_regex(word_delimiter) "]")) )
  959.    {
  960.       # Add the begin and end-of-word regex chars to the pattern iff neither
  961.       # string contains any word delimiters.  The search flag
  962.       # SEARCH_WHOLE_WORD didn't work.  This fixes a bug where a matching pair of
  963.       # 'if end' would stop when it found a word containing either one, like 'append'.
  964.       pattern = "<" quote_regex( open ) ">|<" quote_regex( close ) ">"
  965.    }
  966.       
  967.    for (i in block_items)
  968.       if (i == open " " close)
  969.       {
  970.          catname = get_category_name(ext, i)
  971.          if(catname == "Comments" || catname == "Literals")
  972.          {
  973.             pattern_is_block = TRUE
  974.             break
  975.          }
  976.       }  
  977.    
  978.    if (term)
  979.       pattern = pattern "|" quote_regex( term )
  980.       
  981.    if (!match_case)
  982.    {
  983.       open = tolower(open)
  984.       close = tolower(close)
  985.    }
  986.    else
  987.       flags = or(flags, SEARCH_CASE)
  988.    
  989.    save_position()
  990.    
  991.    while ( level != 0 )
  992.    {
  993.       if ( search(pattern, flags) )
  994.       {
  995.          item = get_style_item();
  996.          catname = get_category_name(ext, item)
  997.          if(catname != "Comments" && catname != "Literals" || pattern_is_block)
  998.          {
  999.             if (term)
  1000.             {
  1001.                s = readn1( tlen )
  1002.                if ( s == term )
  1003.                {
  1004.                   restore_position( TRUE )   # restore saved pos
  1005.                   return FALSE
  1006.                }
  1007.             }
  1008.             s = readn1( olen ) 
  1009.             if (!match_case)
  1010.                s = tolower(s)
  1011.                
  1012.             if ( s == open )         # == was opening symbol
  1013.                level += 1
  1014.             if ( olen != clen )
  1015.             {
  1016.                s = readn1( clen )
  1017.                if (!match_case)
  1018.                   s = tolower(s)
  1019.             }
  1020.             if ( s == close )        # == was closing symbol
  1021.                level -= 1
  1022.          }
  1023.       }
  1024.       else
  1025.       {
  1026.          restore_position( TRUE )   # restore saved pos
  1027.          return FALSE
  1028.       }
  1029.    }  # end while(level != 0)
  1030.    restore_position( FALSE )   # discard saved pos
  1031.    return TRUE
  1032. }
  1033.  
  1034. ##
  1035. # matched_is_comment(block_items, matched)
  1036. # This function returns TRUE if the string in matched is part of one of the
  1037. # block styles of category "Comment" or "Literals".  Parameter block_items
  1038. # is an array containing all of the items that are line or block type for
  1039. # the current file extension.  Since matched can be part of a multiword
  1040. # style item, we test for either a preceding or following space.
  1041. local function matched_is_comment(block_items, s)
  1042. {
  1043.    local i
  1044.    local ext = path_ext(buffer_filename)
  1045.    local match_case = get_case_sensitive(ext)
  1046.    local catname
  1047.    local ret = FALSE
  1048.    
  1049.    for (i in block_items)
  1050.    {
  1051.       if ( (s == i) || match(s " ", i, !match_case) || match(" " s, i, !match_case))
  1052.       {
  1053.          catname = get_category_name(ext, i)
  1054.          if(catname == "Comments" || catname == "Literals")
  1055.          {
  1056.             ret = TRUE
  1057.             break
  1058.          }
  1059.       }
  1060.    }
  1061.    
  1062.    return ret
  1063. }
  1064.  
  1065.  
  1066. ##
  1067. # braces -- report on unmatched opening and closing braces
  1068. #
  1069. function braces()
  1070. {
  1071.    local level = 0
  1072.    local max_level = 0
  1073.    local matched
  1074.    local flags = SEARCH_FWD_REGEX_MAX_IG
  1075.    local pattern
  1076.    local opening
  1077.    local catname, item
  1078.    local ext = path_ext(buffer_filename)
  1079.    local match_case = get_case_sensitive(ext)
  1080.    local block_items = merge_arrays(Block(ext), Line(ext))
  1081.    local level_string
  1082.    
  1083.    level_string[0] = ""
  1084.    level_string[1] = "."
  1085.    level_string[2] = ".."
  1086.    level_string[3] = "..."
  1087.    level_string[4] = "...."
  1088.    level_string[5] = "....."
  1089.    level_string[6] = "......"
  1090.    level_string[7] = "......."
  1091.    level_string[8] = "........"
  1092.    level_string[9] = "........."
  1093.    
  1094.    save_position()
  1095.    goto_buffer_top()
  1096.    
  1097.    init_matching_pairs( )
  1098.  
  1099.    if ( !matching_pairs )
  1100.    {
  1101.       warning( "No matching pairs defined for this extension" )
  1102.       return
  1103.    }
  1104.  
  1105.    opening = matching_left
  1106.    if (!match_case)
  1107.       opening = tolower(opening)
  1108.    else
  1109.       flags = or(flags, SEARCH_CASE)
  1110.  
  1111.    # call quote_regex to make sure any special characters are preceded
  1112.    # with a backslash.
  1113.    pattern = quote_regex( matching_pairs )
  1114.  
  1115.    # since 'matching_pairs' is space delimited, replace all spaces with
  1116.    # the regex 'or' operator.
  1117.    pattern = trans( pattern, " ", "|" )
  1118.  
  1119.    while( search( pattern, flags ))
  1120.    {
  1121.       # use global search_string_length to get found string
  1122.       matched = read_buffer( search_string_length )
  1123.       
  1124.       item = get_style_item();
  1125.       catname = get_category_name(ext, item)
  1126.       if ( catname != "Comments" && catname != "Literals" ||
  1127.             matched_is_comment(block_items, matched) )
  1128.       {
  1129.          if (!match_case)
  1130.             matched = tolower(matched)
  1131.             
  1132.          if( matched ~ opening )
  1133.          {
  1134.             # if the characters found are part of the opening string
  1135.             save_position()
  1136.             level++
  1137.          }
  1138.          else
  1139.          {
  1140.             if( level-- )
  1141.                restore_position( 0 )
  1142.             else
  1143.             {
  1144.                save_position()
  1145.                break
  1146.             }
  1147.          }
  1148.          message( "nesting level == %d %s", level , 
  1149.                   level < 10 ? level_string[level] : "..........")
  1150.          if (level > max_level)
  1151.             max_level = level
  1152.       }
  1153.  
  1154.       next_char()
  1155.    }
  1156.  
  1157.    restore_position( 1 )
  1158.    if( level > 0 )
  1159.    {
  1160.       while( level-- )
  1161.       {
  1162.          restore_position( 0 )
  1163.       }
  1164.       warning( "unmatched opening" )
  1165.    }
  1166.    else if( level < 0 )
  1167.    {
  1168.       restore_position( 0 )
  1169.       warning( "unmatched closing" )
  1170.    }
  1171.    else
  1172.       message( "braces match, max nesting = %d", max_level )
  1173. }
  1174.  
  1175.  
  1176. # This function is commented out since it is not used.  It is a useful function
  1177. # which is why it hasn't been deleted.
  1178.  
  1179. # read n characters before the cursor
  1180. #local function readn( n )
  1181. #{
  1182. #   return ( n <= current_line_offset ) ? read_buffer( -n ) : ""
  1183. #}
  1184.  
  1185. # read n characters following and including the cursor
  1186.  
  1187. local function readn1( n )
  1188. {
  1189.    return ( n + current_line_offset <= current_line_length )       \
  1190.            ? read_buffer( n )                                      \
  1191.            : ""
  1192. }
  1193.  
  1194. ###
  1195. #
  1196. # new_edit_file()
  1197. #
  1198. # Whenever a new edit file is created, the extension is stripped from
  1199. # the file name and a function with that extension (preceded by "__")
  1200. # is invoked. If the function does not exist, "_default()" is called.
  1201. #
  1202. global function new_edit_file()
  1203. {
  1204.    local value;
  1205.    local ext           = path_ext( buffer_filename );
  1206.  
  1207.    local num
  1208.    local margins[], pp;
  1209.    local type, old_level = message_level;
  1210.    local mask = or(BUFFER1MASK, BUFFER3MASK);
  1211.  
  1212.    if(!and(buffer_flags, BUFFER_SYSTEM))
  1213.    {
  1214.       message_level = 1;
  1215.       type = Type(ext);
  1216.       type = type ? type : DEFAULT;
  1217.  
  1218.       if(!(DEFAULT in languages))
  1219.          add_type(DEFAULT);
  1220.       
  1221.       buffer_tabs      = Tabs(type);
  1222.       wp_left_margin   = LeftMargin(type);
  1223.       wp_right_margin  = RightMargin(type);
  1224.       buffer_flags     = set_flag_bits(buffer_flags, mask, BufferFlags(type));
  1225.       auto_indent_mode = AutoIndent(type);
  1226.       set_word_mode(get_word_mode(type)+0);
  1227.       #  This is commented out until electric_mode is turned into a buffer variable;
  1228.       #     toggle_electric(get_expand_template(type)+0);
  1229.       
  1230.       if(!(type in syntax_loaded))
  1231.       {
  1232.          if(!("__styles" in syntax_loaded))
  1233.          {
  1234.             optional_function("load_user_styles");
  1235.             syntax_loaded["__styles"] = TRUE;
  1236.          }
  1237.  
  1238.          optional_function("load_user_" type "_syntax");
  1239.          syntax_loaded[type] = TRUE;
  1240.       }
  1241.       
  1242.       if(languages_id != type)
  1243.          languages_id = type;
  1244.       
  1245.       syntax_highlight = Syntax(type);
  1246.       case_insensitive = !get_case_sensitive(type);
  1247.       style_delimiter  = StyleDelimiters(type);
  1248.       escape_character = EscapeCharacter(type);
  1249.       
  1250.       message_level = old_level;
  1251.    }
  1252. }
  1253.  
  1254. # this function could be used to mark a field by returning the field and the
  1255. # field character on both sides of the field.  It is commented out to prevent
  1256. # the warning message since it is not used
  1257. #local function __es( value )
  1258. #{
  1259. #   return _es value _es
  1260. #}
  1261.