/****************************** Module Header ******************************* * * Module Name: assist.e * * Copyright (c) Netlabs EPM Distribution Project 2002 * * $Id$ * * =========================================================================== * * This file is part of the Netlabs EPM Distribution package and is free * software. You can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * Netlabs EPM Distribution. This library is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * ****************************************************************************/ /*****************************************************************************/ /* Assist interface for E3 Ralph Yozzo, Larry Margolis */ /* */ /* This macro is intended for use with programming language */ /* which have tokens which must be balanced to compile correctly. */ /* We shall call these tokens "balanceable tokens" or BalTok for */ /* short. */ /* */ /* The functions provided include moving from an opening token */ /* (e.g., (, {, [ ) to a closing token (e.g., ), }, ] ) and vice versa. */ /* */ /* KEYS: */ /* Ctrl-[, Ctrl-], Ctrl-8 -- move to corresponding BalTok) */ /* */ /* CONSTANTS: */ /* gold -BalTok tokens are defined in the const gold and additional */ /* tokens may be added. */ /* */ /* Example: */ /* if ((c=getch())=='c' */ /* &&(d=complicatedisntit(e))){ */ /* lookforbracket(); */ /* } */ /* In the above program segment if one places the cursor on an opening */ /* parenthesis and presses Ctrl-[ the cursor will move to the corresponding */ /* closing parenthesis if one exists. Pressing Ctrl-[ again will reverse */ /* the process. */ /* */ /* Modified by Larry Margolis to use the GREP option of Locate to search */ /* for either the opening or closing token, rather than checking a line at */ /* a time. I also changed the key from Ctrl-A to Ctrl-[ or -], which are */ /* newly allowed as definable keys, and deleted the matching of /* and */. */ /* (The GREP search is much faster than the way Ralph did it, but doesn't */ /* let you match either of 2 strings.) Finally, the user's previous search */ /* arguments are saved and restored, so Ctrl-F (repeatfind) will not be */ /* affected by this routine. */ /* */ /* Updated by LAM to use EGREP to also handle #if, #endif, etc. and COMPILE */ /* IF, COMPILE ENDIF, etc. */ /* */ /* 1995/02/21 Updated by LAM to also handle SCRIPT list tags and /* */. */ /* 1995/02/22 Updated by LAM to also handle SGML tags. */ /* */ /* 1999/03/15 Updated by Petr Mikulik, http://www.sci.muni.cz/~mikulik/, to */ /* also handle TeX tokens: \if... \else \fi; \begin... \end...; */ /* \if... \else \fi; \begin{ \end{; \left \right */ /* This patch is part of the "pmCSTeX for EPM" package, see */ /* http://www.sci.muni.cz/~mikulik/os2/pmCSTeX.html */ /*****************************************************************************/ ; Included TeX extensions for the passist procedure by Petr Mikulik from his ; PMCSTeX package. ; Added c_8 for german keyboards. ; ; 2006 changes: JBS ; ; A major rework of the code ; Bugs were fixed ; More tokens are balanced, including the start and end points of multi-line comments ; More modes supported (PASCAL, FORTRAN77, JAVA, WARPIN) ; Added initial stages of support for ADA, CSS, PERL, PHP ; Code was added to ensure tokens found within comments or literals were not matched ; Better variable names and code documentation ; compile if not defined(SMALL) -- If SMALL not defined, then being separately compiled. define INCLUDING_FILE = 'ASSIST.E' compile if not defined(EPM) -- Can be included in the base or separately linked. include 'stdconst.e' compile endif const compile if not defined(NLS_LANGUAGE) NLS_LANGUAGE = 'ENGLISH' compile endif include NLS_LANGUAGE'.e' EA_comment 'Linkable bracket-matching routines.' defmain call passist() compile endif -- not defined(SMALL) const -- NOTE: The logic below relies on GOLD being defined with the left "brackets" -- in the odd positions and the right "brackets" in the even positions. GOLD = '(){}[]<>' -- Parens, braces, brackets & angle brackets ASSIST_RC_MLC_MATCHED = -1 ASSIST_RC_NO_ERROR = 0 ASSIST_RC_IN_ONELINE_COMMENT = 1 ASSIST_RC_IN_MULTILINE_COMMENT = 2 ASSIST_RC_IN_LITERAL = 3 ASSIST_RC_NOT_ON_A_TOKEN = 4 ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN = 5 ASSIST_RC_BAD_FORTRAN77_CURSOR = 6 ASSIST_RC_MODE_NOT_SUPPORTED = 7 ASSIST_RC_TOO_MANY_LINES = 8 ASSIST_RC_TOO_MANY_LOOPS = 9 EGREP_METACHARACTERS = '\[]()?+*^$.|' -- JBSQ: This may need to changed if the grep code -- ever supports {, } and possibly other egrep metacharacters -- JBSQ: The following constants 'hard-code' the versions of NMAKE and FORTRAN77 for which -- passist works. Perhaps there is a better way that would allow this to be -- selected by the user? compile if not defined( USE_NMAKE32) USE_NMAKE32 = 1 -- 0 means do not accept NMAKE32-specific directives compile endif compile if not defined( USE_FORTRAN90_SLC) USE_FORTRAN90_SLC = 1 -- 0 means disregard FORTRAN90 SLC: '!' compile endif compile if not defined( ASSIST_DEFAULT_MAX_LINES) ASSIST_DEFAULT_MAX_LINES = 2000 compile endif compile if not defined( ASSIST_DEFAULT_MAX_LOOPS) ASSIST_DEFAULT_MAX_LOOPS = 500 compile endif ; --------------------------------------------------------------------------- ; Syntax: assist [quiet] [[] ] defc assist, passist universal vEPM_POINTER parse arg args wp = wordpos( 'QUIET', upcase( args)) fQuiet = (wp > 0) if wp then args = delword( args, wp, 1) endif if fQuiet then VerboseLevel = 0 -- 0 = quiet, no 'Invalid:' etc. messages (0 also disables pointer change) else VerboseLevel = 1 -- 1 = normal endif parse value args with MaxLines MaxLoops MaxLines = strip( MaxLines) MaxLoops = strip( MaxLoops) if VerboseLevel <> 0 then mouse_setpointer WAIT_POINTER endif fHighlight = 1 call passist( VerboseLevel, fHighlight, MaxLines, MaxLoops) if VerboseLevel <> 0 then mouse_setpointer vEPM_POINTER endif ; --------------------------------------------------------------------------- ; id = found word under cursor (or beneath the cursor in some cases) ; fIntermediate = set to 1 if id is an intermediate conditional token ; (e.g. 'else', but not 'if' or 'endif') ; fForward = a flag to indicate which direction to search ; 1 = forward, 0 = backward ; search = string for locate command, without seps and options, ; egrep will be used ; clist = a space delimited list of substrings to match on. Usually this ; is a single substring. An example of multiple substrings in ; clist would be that an "end" in REXX mode can match up with ; either a "do" or a "select" statement. If fForward = 1 then ; these substrings should identify the starting token(s) of the ; bracketed code. If fForward = 0 then these substrings should ; identify the ending token(s). ; coffset = offset from cursor pos to substring to match ; clen = the length of the substrings in clist ; nestlevel = counter for nesting of matched tokens. On nestlevel = 0, ; the matched token was found. ; --------------------------------------------------------------------------- defproc passist call psave_pos( savepos) -- Save the cursor location getsearch search_command -- Save user's search command. StartLineNum = .line VerboseLevel = arg(1) -- 0 = quiet, 1 = normal fHighlight = arg(2) -- 1 = highlighting of matching expression and scrolling MaxLines = arg(3) MaxLoops = arg(4) if VerboseLevel <> 0 then VerboseLevel = 1 endif if fHighlight <> 0 then fHighlight = 1 endif if MaxLines = '' then MaxLines = ASSIST_DEFAULT_MAX_LINES endif if MaxLoops = '' then MaxLoops = ASSIST_DEFAULT_MAX_LOOPS endif SearchLimitData = StartLineNum MaxLines MaxLoops -- Use one arg for assist_search to save args case = 'e' -- respect case is default coffset = 0 clen = 1 fIntermediate = 0 fForward = 1 nestlevel = 1 tex_env = '' ECompileFlag = 0 id = '' -- token under cursor startcol = 1 -- start column of id/token endcol = 1 -- end column of id/token -- Init search position data for highlighting HighlightLine = .line HighlightCol = .col HighlightLen = 1 --#dprintf( "passist", "------------------------------------------------------------") --#dprintf( "passist", "Initial cursor: ".line",".col) CurMode = NepmdGetMode() do once = 1 to 1 assist_rc = inside_comment2( CurMode, comment_data) --#dprintf( "passist", "comment return:" assist_rc comment_data) if assist_rc = ASSIST_RC_IN_MULTILINE_COMMENT then parse value comment_data with CommentStartLine CommentStartCol CommentStartLen CommentEndLine CommentEndCol CommentEndLen if .line = CommentStartLine and .col - CommentStartCol < CommentStartLen then -- if cursor on start .line = CommentEndLine -- move to the end .col = CommentEndCol HighlightLine = CommentStartLine HighlightCol = CommentStartCol HighlightLen = CommentStartLen elseif .line = CommentEndLine and .col >= CommentEndCol then -- if cursor on end .line = CommentStartLine -- move to the start .col = CommentStartCol HighlightLine = CommentEndLine HighlightCol = CommentEndCol HighlightLen = CommentEndLen endif assist_rc = ASSIST_RC_MLC_MATCHED leave endif if inside_literal( CurMode) then assist_rc = ASSIST_RC_IN_LITERAL leave endif -- get c = char at cursor c = substr( textline( .line), .col, 1) -- JBSQ: Why this code?? It moves the cursor. -- if c = space, then try it 1 col left if c == ' ' & .col > 1 then left c = substr( textline( .line), .col, 1) endif if pos( c, '{}') and CurMode = 'RC' then -- Braces, '{}', can be matched with k = 0 -- BEGIN and END in RC files. So they else -- need to be handled separately. k = pos( c, GOLD) -- '(){}[]<>' endif if k then -- if c = bracket defined in GOLD, then set search to the corresponding char out of GOLD leftbracket = substr( GOLD, (k + 1)%2*2 - 1, 1) rightbracket = substr( GOLD, (k + 1)%2*2, 1) -- escape brackets with '\' search = '[\'leftbracket'\'rightbracket']' clist = c fForward = k // 2 leave endif getline line -- build the separator list for find_token if CurMode = 'FORTRAN77' then seps = "+-*/=().,':$" else seps = ' ~`!.%^&*()-+=][{}|\;?<>,''"'\t endif -- JBSQ: Add '.' to default token_separators & remove ':' for GML markup? if CurMode = 'TEX' then if substr( line, .col, 1) = '\' then right endif -- ...move cursor right if it is on \backslash endif -- get the word under cursor and return startcol and endcol -- stop at separators = arg(3) -- stop at double char separators = arg(4) if not find_token( startcol, endcol, seps, '/* */') then -- JBSQ: Should /* */ be used for ALL modes? assist_rc = ASSIST_RC_NOT_ON_A_TOKEN leave endif --#dprintf( "passist", "Initial token start,end: "startcol","endcol) if startcol > 1 then prevchar = substr( line, startcol - 1, 1) if prevchar = '<' then if CurMode = 'HTML' or CurMode = 'WARPIN' /* or CurMode = 'XML' */ then -- add '<' to found word if it is on the left side of it -- this assumes ALL balanceable tokens for HTML and WARPIN start with '<' startcol = startcol - 1 endif elseif prevchar = '\' then if CurMode = 'TEX' then --//PM TeX macros are preceded by \backslash which is also separator -- add '\' to found word if it is on the left side of it startcol = startcol - 1 endif endif elseif startcol = 1 then -- if cursor is on the first column of a MAKE directive if leftstr( line, 1) = '!' then -- "shift" the id to the following token if CurMode = 'MAKE' then newstart = verify( line, ' '\t, 'N', 2) -- skip to next non-whitespace if newstart then newend = verify( line' ', ' ', 'M', newstart) if newend then startcol = newstart endcol = newend - 1 endif endif endif endif endif -- id = found word id = substr( line, startcol, (endcol - startcol) + 1) --> IPF tags start with ':' and end with '.' -- JBSQ: Since this is for IPF only, should it ne moved to the IPF portion of the code? -- if id = '.', then go 1 col left and search again if id = '.' & .col > 1 then --sayerror "id = '.'" left if find_token( startcol, endcol) then id = substr( line, startcol, (endcol - startcol) + 1) endif --test endif --#dprintf( "passist", "Token after preprocessing : '"id"' Startcol: "startcol" Endcol: "endcol) -- Set/re-init search position data for highlighting HighlightLine = .line HighlightCol = startcol HighlightLen = endcol - startcol + 1 if 0 then -- just a placeholder so the following ELSEIFs can be freely reordered -- Mode(s): E ------------------------------------------------------------------------------- elseif CurMode = 'E' then if startcol > 1 then if substr( line, startcol - 1, 2) = '*/' then -- allow */ just before token startcol = startcol + 1 id = substr( line, startcol, (endcol - startcol + 1)) /* test */endif/* test */ -- Apparently E allow comments as "word" delimiters endif if endcol < length( line) then temp = substr( line, endcol, 2) if temp = '/*' then -- allow /* after token id = substr( line, startcol, (endcol - startcol)) endcol = endcol - 1 endif--test endif --dprintf( "passist", "E Token after : '"id"'") case = 'c' -- Case insensitive for all E tokens id = lowcase( id) line = lowcase( line) if 0 then -- another placeholder ---- E compiler directives: compile if, compile else, compile elseif, compile endif elseif id = 'compile' then ECompileFlag = 1 temp = substr( line, pos( id, line)) --#dprintf( "passist", "Initial temp: "temp) do while (pos( '/*', temp) > 0) and (pos( '*/', temp) > 0) temp = substr( temp, pos( '*/', temp)) --#dprintf( "passist", "Intermediate temp: "temp) enddo --#dprintf( "passist", "Final temp: "temp) if (words( temp) = 0) then assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN else lcword2 = word( temp, 2) --#dprintf( "passist", "lcword2: "lcword2) if wordpos( lcword2, 'if endif else elseif') then -- compile if/... code (when cursor is on compile search = '^[ \t]*compile[ \t]*(/\*.*\*/)*[ \t]*\c(end)?if([; \t]|(--)|(/\*)|$)' clist = leftstr( lcword2, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. end_line endif fIntermediate = (substr( lcword2, 2, 1) = 'l') else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif endif ---- E conditions: (compile) if, (compile) else, (compile) elseif, (compile) endif elseif wordpos( id, 'if endif else elseif') then pcompile = pos( 'compile', line) if pcompile & pcompile <= startcol then -- compile if|endif|else|elseif parse value line with part1 'compile' part2 (id) part3 --#dprintf( 'passist', 'E if parse: "'part1'"compile"'part2'"'id'"'part3'"') part1 = strip( part1) part2 = strip( part2) if (part1 = '' or ((length( part1) >= 4) and (leftstr( part1, 2) = '/*') and (rightstr( part1, 2) = '*/'))) and (part2 = '' or ((length( part2) >= 4) and (leftstr( part2, 2) = '/*') and (rightstr( part2, 2) = '*/'))) then ECompileFlag = 1 -- compile if/... code (when cursor is on if/... search = '^[ \t]*compile[ \t]*(/\*.*\*/)*[ \t]*\c(end)?if([; \t]|(--)|(/\*)|$)' clist = leftstr( id, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = length( part1) + 1 else -- move to end, so first Locate will hit this instance. end_line endif fIntermediate = (substr( lcword2, 2, 1) = 'l') else -- /* compile */ if/... code search = '(^|[ \t]|(\*/))\c(end)?if([; \t]|(--)|(/\*)|$)' clist = leftstr( id, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif fIntermediate = (substr( id, 2, 1) = 'l') endif else -- if|endif (without compile) -- The found line is checked by a 2nd search for a preceding 'compile', -- otherwise the re would have two many parentheses. --search = '((\*/:o)|(^:o)|(^:o(~compile:w)))\c(end)?if([; \t]|(--)|(/\*)|$)' --search = '((\*/:o)|(;:o)|(^:o)|(^:o(~compile:w)))\c(end)?if([; \t]|(--)|(/\*)|$)' search = '(^|[ \t]|(\*/))\c(end)?if([; \t]|(--)|(/\*)|$)' clist = leftstr( id, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif fIntermediate = (substr( id, 2, 1) = 'l') endif ---- E loop keywords 1: loop, endloop elseif wordpos( id, 'loop endloop') then search = '(^|[ \t]|(\*/))\c(end)?loop([; \t]|(--)|(/\*)|$)' clist = leftstr( id, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif ---- E loop keywords 2: for, endfor elseif wordpos( id, 'for endfor') then search = '(^|[ \t]|(\*/))\c(end)?for([; \t]|(--)|(/\*)|$)' clist = leftstr( id, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif ---- E loop keywords 3: do, while, end, enddo, endwhile elseif wordpos( id, 'do while end enddo endwhile') then if (id = 'end') then search = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)' elseif (id = 'enddo') then search = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)' elseif (id = 'endwhile') then search = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)|(/\*)|$)' else -- check for do and/or while whilepos = wordpos( 'while', line) dopos = wordpos( 'do', line) if (not dopos) or (whilepos and (whilepos < dopos)) then -- while or while ... do? search = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)|(/\*)|$)' startcol = whilepos endcol = whilepos + 4 else -- do or do ... while search = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)' startcol = dopos endcol = dopos + 1 endif compile if 0 if whilepos and dopos then -- if both if whilepos < dopos then -- while ... do ? search = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)|(/\*)|$)' startcol = whilepos endcol = whilepos + 4 else -- do ... while search = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)' startcol = dopos endcol = dopos + 1 endif else if dopos then -- do search = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)' else -- while search = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)||(/\*)$)' endif endif compile endif --#dprintf( "passist", "Whilepos: "whilepos "Dopos: "dopos "Search: "search) endif clist = leftstr( id, 1) --#dprintf( "passist", "clist: "clist) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif ---- E loop keywords 4: leave, iterate elseif wordpos( id, 'leave iterate') then search = '(^|[ \t]|(\*/))\c(do|end|enddo|loop|endloop|for|endfor|endwhile)([; \t]|(--)|(/\*)|$)' fForward = 0 clist = 'e' clen = 1 .col = endcol fIntermediate = 1 else -- not a known balanceable E token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): C, JAVA JAVASCRIPT and RC ------------------------------------------------------- elseif wordpos( Curmode, 'C JAVA RC JAVASCRIPT') > 0 then -- JBSQ: Was this "if" left out on purpose? -- C allows for spaces between '#' and keyword, JAVA, RC and JAVASCRIPT don't. if CurMode = 'C' then if wordpos( id, 'if ifdef ifndef endif else elif') > 0 then -- Check for "# if", etc. .col = startcol if next_nonblank_noncomment_nonliteral( mode, '-R') = '#' then id = '#'id startcol = .col endif call prestore_pos( savepos) endif endif if 0 then --placeholder ---- Directive(s): #if #ifdef #ifndef #endif #else #elif elseif wordpos(id, '#if #ifdef #ifndef #endif #else #elif') then if CurMode <> 'JAVA' and CurMode <> 'JAVASCRIPT' then search = '\#[ \t]*\c((if((n?def)?))|endif)([ \t]|$)' if CurMode = 'C' then search = '^[ \t]*\c'search elseif startcol = 1 then -- RC directives must start in column one search = '^'search else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif clist = substr( id, 2, 1) fForward = (clist = 'i') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif fIntermediate = (substr( id, 3, 1) = 'l') else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif ---- Keyword(s): do, try elseif wordpos( id, 'do try') then -- this code might be expanded to { .... } if CurMode <> 'RC' then setsearch 'xcom l /[{;]/xe+F' -- does following a '{' precede a ';'? clist = '{ ;' case = 'e' -- 'e' = case-sensitive coffset = 0 clen = 1 nestlevel = -1 -- -1 = stop on first 'hit' assist_rc = assist_search( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData) if assist_rc then assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN else --#dprintf( "passist", "token {} '{;' search found: "substr( textline( .line), .col, 1)) if substr( textline( .line), .col, 1) = '{' then -- found the braces search = '[{}]' fForward = 1 clist = '{' else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- token without braces endif endif else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif ---- Keyword(s): while elseif id = 'while' then if CurMode <> 'RC' then setsearch 'xcom l /[()]/xe+F' -- find the end of the conditional clist = '{' case = 'e' -- 'e' = case-sensitive coffset = 0 clen = 1 nestlevel = 0 assist_rc = assist_search( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData) if assist_rc then -- no conditional?? assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN else nextchar = next_nonblank_noncomment_nonliteral( CurMode) if not nextchar then assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN else --#dprintf( "passist", "while {; search found: "nextchar) if nextchar = '{' then -- must be while loop (i.e. NOT do/while) with braces search = '[{}]' fForward = 1 clist = '{' elseif nextchar = ';' then -- cursor is on the 'while' of a do/while loop -- search = '\{|\}|((^|[ \t])\cdo([ \t]|$))' search = '[{}]|((^|[ \t])\cdo([ \t]|$))' fForward = 0 clist = '}' nestlevel = 2 else -- cursor is on a one-statement while loop assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- no braces follow, so JBSQ: unbalanceable? endif endif endif endif /* JBSQ: The following code should work for break/continue statements within nested do and for loops which have braces. Problems arise if there are DOs or FORs without braces between the break/continue and the enclosing loop. Also problems arise if the enclosing loop is a while loop. The solution, if it is worth it, is to code the entire search here so that these different variations of loops can be handled correctly. ---- Keyword(s): break continue elseif wordpos( id, 'break continue' then if CurMode <> 'RC' then search = '([{}])|((^|[ \t])\c(do|for)([ \t]|$))' fForward = 0 clist = '}' fIntermediate = 1 else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif */ elseif CurMode = 'RC' then -- All remaining RC tokens here case = 'c' -- RC files are case insensitive, except for direcives (#if, #define, etc.) id = lowcase( id) if wordpos( id, '{ } begin end') then search = '[{}]|((^|[ \t])\c(begin|end)([ \t;]|$))' fForward = (wordpos( id, '{ begin') > 0) if fForward then -- move to beginning begin .col = startcol clist = '{ b' else -- move to end, so first Locate will hit this instance. .col = endcol clist = '} e' endif else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif else -- All remaining C/JAVA/JAVASCRIPT tokens here idlist = 'case default default:' if CurMode <> 'C' then idlist = idlist 'finally' endif if wordpos( id, idlist) then search = '[{}]' fForward = 0 clist = '}' fIntermediate = 1 else -- This code seems to handle matching the beginning and end of -- a) functions when the cursor is on a token preceding the parameter list -- b) 'if' when the 'if' has following braces -- c) 'for' when the 'for' has following braces -- d) 'switch' statements -- e) anything of the structure: token ( ) { ... } -- e.g. catch ( ) { ... } -- JBSQ: Check for next_nonblank_noncomemnt_nonliteral(CurMode) = '(' first? -- (This would force the cursor to actually be on the function name. -- The current code allows the cursor on any nonblank, noncomment, -- nonliteral character preceding the parameter list.) setsearch 'xcom l /[()]/xe+F' -- find the ending ')' or ; clist = '{' case = 'e' -- 'e' = case-sensitive coffset = 0 clen = 1 nestlevel = 0 assist_rc = assist_search( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData) --#dprintf( 'passist', 'last chance c,... srch_rc line col' assist_rc .line .col) if not assist_rc and substr( textline( .line), .col, 1) = ')' then -- no conditional?? assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN nextchar = next_nonblank_noncomment_nonliteral( CurMode) if nextchar then --#dprintf( "passist", "Generic token () {; search found: "nextchar) if nextchar = '{' then -- structure is: token ( ) { ... } search = '[{}]' fForward = 1 clist = '{' assist_rc = ASSIST_RC_NO_ERROR endif endif else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif endif endif -- Mode(s): REXX ---------------------------------------------------------------------------- elseif CurMode = 'REXX' then ---- REXX conditions: if, else case = 'c' -- Case insensitive for all REXX tokens id = lowcase(id) if 0 then -- another placeholder elseif wordpos( id, 'do end select when otherwise') then search = '(^|[ \t])\c(do|end|select)([; \t]|$)' fForward = (wordpos(id, 'do select') > 0) if fForward then -- move to beginning clist = 'do se' .col = startcol else -- move to end, so first Locate will hit this instance. clist = 'en' .col = endcol endif clen = 2 fIntermediate = ((id = 'when') or (id = 'otherwise')) elseif id = 'else' then .col = startcol pchar = next_nonblank_noncomment_nonliteral( CurMode, '-R') --#dprintf( "passist", 'Previous char:' pchar) if lowcase( pchar) = 'd' then if .col > 2 then .col = .col - 2 pword = lowcase( substr( textline( .line), .col, 3)) if .col > 1 and substr( textline( .line), .col - 1, 1) <> ' ' then -- else --#dprintf( "passist", 'Previous word:' pword) if pword = 'end' then display -1 call passist() display 1 endcol = .col endif endif endif endif search = '(^|[ \t])\cif([ \t]|$)' clist = 'i' fForward = 0 fIntermediate = 1 .col = endcol nestlevel = -1 compile if 0 elseif wordpos( id, 'if else') then -- JBSQ: How is this supposed to work? IFs don't always have ELSEs search = '(^|[ \t])(if|else)([ \t]|$)' clist = leftstr( id, 1) fForward = (clist <> 'e') if fForward then -- move to beginning .col = startcol else -- move to end, so first Locate will hit this instance. .col = endcol endif compile endif else -- not a known balanceable REXX token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): IPF ----------------------------------------------------------------------------- elseif CurMode = 'IPF' then if pos( ':', id) > 1 then -- :tag, separate the :tag id = substr( id, pos( ':', id)) endif if length( line) > endcol then if (pos( substr( line, endcol + 1, 1), '. ')) then IPFBeginTags = ':artlink :caution :cgraphic :ctrldef :fn :hide' IPFBeginTags = IPFBegintags ':hp1 :hp2 :hp3 :hp4 :hp5 :hp6 :hp7 :hp8 :hp9' IPFBeginTags = IPFBegintags ':lines :link :nt :userdoc :warning :xmp' IPFEndTags = ':eartlink :ecaution :ecgraphic :ectrldef :efn :ehide' IPFEndTags = IPFEndTags ':ehp1 :ehp2 :ehp3 :ehp4 :ehp5 :ehp6 :ehp7 :ehp8 :ehp9' IPFEndTags = IPFEndTags ':elines :elink :ent :euserdoc :ewarning :exmp' IPFTags = IPFBeginTags IPFEndTags --#dprintf( "passist", "IPF ID wordpos: "wordpos( id, IPFTags)) --#dprintf( "passist", "IPF Tags: "IPFTags) if wordpos( id, IPFTags) then clist = substr( id, 2, 1) -- Character to check to see if it's an end tag fForward = (clist <> 'e') -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning .col = startcol id = substr( id, 2) else -- move to end, so first Locate will hit this instance. .col = endcol + 1 id = substr( id, 3) endif search = '\:\ce?'id'(\.| )' elseif wordpos( id, ':ol :ul :sl :eol :eul :esl :li :lp') then fIntermediate = (id = ':li' or id = ':lp') fForward = (wordpos( id, ':ol :ul :sl') > 0) -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning .col = startcol id = substr( id, 2) clist = 'ol ul sl' clen = 2 else -- move to end, so first Locate will hit this instance. .col = endcol + 1 id = substr( id, 3) clist = 'eol eul esl' clen = 3 endif search = '\:\ce?(o|u|s)l(\.| )' elseif wordpos( id, ':table :etable :row :c') then fIntermediate = (id = ':row' or id = ':c') fForward = (id = ':table') -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning .col = startcol clist = 'table' clen = 5 id = substr( id, 2) else -- move to end, so first Locate will hit this instance. .col = endcol + 1 clist = 'etable' clen = 6 id = substr( id, 3) endif search = '\:\ce?table(\.| )' elseif wordpos( id, ':parml :eparml :pt :pd') then fIntermediate = (id = ':pt' or id = ':pd') fForward = (id = ':parml') -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning .col = startcol id = substr( id, 2) clist = 'parml' clen = 5 else -- move to end, so first Locate will hit this instance. .col = endcol + 1 id = substr( id, 3) clist = 'eparml' clen = 6 endif search = '\:\ce?parml(\.| )' elseif wordpos( id, ':dl :dthd :ddhd :dt :dd :edl') then fIntermediate = (wordpos( id, ':dthd :ddhd :dt :dd') > 0) fForward = (id = ':dl') -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning .col = startcol id = substr( id, 2) clist = 'dl' clen = 2 else -- move to end, so first Locate will hit this instance. .col = endcol + 1 id = substr( id, 3) clist = 'edl' clen = 3 endif search = '\:\ce?dl(\.| )' elseif wordpos( id, ':fig :efig :figcap') then fIntermediate = (id = ':figcap') fForward = (id = ':fig') -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning .col = startcol id = substr( id, 2) else -- move to end, so first Locate will hit this instance. .col = endcol + 1 id = substr( id, 3) endif search = '\:\ce?fig(\.| )' else -- not a known balanceable IPF token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif else -- not a known balanceable IPF token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif else -- not a known balanceable IPF token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): HTML, WARPIN -------------------------------------------------------------------- elseif (CurMode = 'HTML' or CurMode = 'WARPIN') then case = 'c' -- Case insensitive for all HTML tokens (JBSQ: WARPIN, too?) ---- HTML tags: <..., if leftstr( id, 1) = '<' then clist = substr( id, 2, 1) -- Character to check to see if it's the same or the other fForward = (clist <> '/') -- fForward = 1 if searching forward; 0 if backwards if fForward then -- move to beginning id = substr( id, 2) -- Strip off the '<' clist = id .col = startcol else -- move to end, so first Locate will hit this instance. id = substr( id, 3) -- Strip off the '' after the tag endif search = '<\c/?'id'(>| )' -- Use \c to not put cursor on angle bracket. clen = length( clist) else -- not a known balanceable HTML token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): MAKE ---------------------------------------------------------------------------- elseif CurMode = 'MAKE' then case = 'c' -- Case insensitive for all MAKE tokens id = lowcase( id) -- Currently ALL balanceable MAKE tokens must be on a line with '!' in column 1 -- and if there are any characters between the '!' and the token they must -- be whitespace. if leftstr( line, 1) = '!' then if startcol > 2 then -- Are there characters between '!' and token? if verify( substr( line, 2, startcol - 2), ' '\t) then assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN id = '' -- Disable further processing endif endif if 0 then -- placeholder compile if USE_NMAKE32 elseif wordpos( id, 'if ifdef ifndef endif else elif elseif') then compile else elseif wordpos( id, 'if ifdef ifndef endif else') then compile endif search = '^![ \t]*\c(if((n?def)?)|endif)([ \t]|$)' clist = leftstr( id, 1) fForward = (clist = 'i') if fForward then -- move to beginning .col = 1 else -- move to end, so first Locate will hit this instance. .col = endcol endif fIntermediate = (substr( id, 2, 1) = 'l') compile if USE_NMAKE32 elseif wordpos( id, 'foreach endfor') then -- NMAKE32 search = '^![ \t]*\c(foreach|endfor)([ \t]|$)' clist = leftstr( id, 1) fForward = (clist = 'f') if fForward then -- move to beginning .col = 1 else -- move to end, so first Locate will hit this instance. .col = endcol endif fIntermediate = (wordpos( id, 'else elif elseif') > 0) compile endif else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): Pascal -------------------------------------------------------------------------- elseif Curmode = 'PASCAL' then case = 'c' -- case insensitive for PASCAL id = lowcase( id) --PascalStartBlockTokens = 'begin record try asm object class interface' PascalStartBlockTokens = 'begin record try asm case' -- object?, class, interface? if wordpos( id, PascalStartBlockTokens 'end except finally') then search = '(^|[^a-zA_Z0-9_])\c('translate( PascalStartBlockTokens, '|', ' ')'|end)([;. \t]|$)' fForward = (wordpos( id, PascalStartBlockTokens) > 0) clen = 3 if fForward then .col = startcol clist = '' do i = 1 to words( PascalStartBlockTokens) clist = clist leftstr( word( PascalStartBlockTokens, i), 3) enddo else .col = endcol clist = 'end' endif fIntermediate = (id = 'except' or id = 'finally') elseif wordpos( id, 'repeat until') then search = '(^|[ \t])\c(repeat|until)([ \t]|$)' fForward = (id = 'repeat') if fForward then .col = startcol clist = 'r' else .col = endcol clist = 'u' endif elseif wordpos( id, 'while for') then -- check for begin before ';' (i.e a block loop instead of a single statement loop) setsearch 'xcom l /[ \t]\cdo([ \t]|$)/x'case'c+F' -- find the end of following 'do' clist = 'd' coffset = 0 clen = 1 nestlevel = -1 -- -1 = stop on first 'hit' assist_rc = assist_search( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData) do once2 = 1 to 1 if assist_rc then -- no 'do'?? assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN leave endif .col = .col + 2 nextchar = next_nonblank_noncomment_nonliteral( CurMode) if not nextchar then assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN leave endif do once3 = 1 to 1 --#dprintf( "passist", "while/for 'do' search found: "nextchar) assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- assume bad if lowcase( nextchar) <> 'b' then leave endif tmpline = textline( .line) tmpline_len = length( tmpline) --#dprintf( "passist", "col: ".col "len: "tmpline_len "line: "tmpline) if tmpline_len < .col + 4 then leave endif if lowcase( substr( tmpline, .col, 5)) <> 'begin' then leave endif if (tmpline_len > .col + 4) then nextchar = substr( tmpline, .col + 5, 1) --#dprintf( "passist", "charafter: '"nextchar"'") if nextchar = ' ' or nextchar = \9 then assist_rc = ASSIST_RC_NO_ERROR endif else assist_rc = ASSIST_RC_NO_ERROR endif if not assist_rc then search = '(^|[ \t])\cbegin|end([;. \t]|$)' fForward = 1 clist = 'b' endif enddo enddo else assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): TEX ----------------------------------------------------------------------------- elseif Curmode = 'TEX' then -- begin addition for TeX -- NOTE: There is some special code for \begin and \end which depends on 'clist' -- being set to 'be' or 'en'. If it becomes necessary to use these values -- for 'clist' for other TEX tokens, then the special code for \begin and -- end will break. coffset = 1 -- most TEX tokens use this offset -- //PM additions: balanceable tokens for (La)TeX ---- TeX conditions: \if, \else, \fi if substr( id, 1, 3) = '\if' or wordpos( id, '\else \fi') then --// \if.. \else \fi search = '\\(if|fi)' clist = substr( id, 2, 1) fForward = (clist = 'i') -- fForward=1: forward, fForward=0 backward search if fForward then -- move cursor so that the first Locate will hit this instance .col = startcol -- \if: move to beginning else .col = endcol -- \else,\fi: move to end endif fIntermediate = (id = '\else') ---- TeX environment: \begin..., \end... elseif id = '\begin' or id = '\end' then --// \begin.. \end.. search = '\\(begin|end)[ \t]*' ---- LaTeX environment: \begin{...}, \end{...} .col = endcol if next_nonblank_noncomment_nonliteral( CurMode) == '{' then --#dprintf( 'passist', 'TEX: found } at '.line .col) temp = substr( textline( .line), .col) p = pos( '}', temp) if p > 0 then tex_env = leftstr( temp, p) endif endif call prestore_pos( savepos) clist = substr( id, 2, 2) clen = 2 fForward = (clist = 'be') if fForward then .col = startcol else .col = endcol endif elseif id = '\bgroup' or id = '\egroup' then search = '\\(bgroup|egroup)' clist = substr( id, 2, 1) fForward = (clist = 'b') if fForward then .col = startcol else .col = endcol endif elseif id = '\begingroup' or id = '\endgroup' then search = '\\(begingroup|endgroup)' clist = substr( id, 2, 1) fForward = (clist = 'b') if fForward then .col = startcol else .col = endcol endif elseif id = '\makeatletter' or id = '\makeatother' then search = '\\makeat(letter|other)' clist = substr( id, 8, 1) fForward = (clist = 'l') if fForward then .col = startcol else .col = endcol endif coffset = 7 elseif id = '\[' or id = '\]' then search = '\\(\[|\])' clist = substr( id, 2, 1) fForward = (clist = '[') if fForward then .col = startcol else .col = endcol endif elseif id = '\(' or id = '\)' then search = '\\(\(|\))' clist = substr( id, 2, 1) fForward = (clist = '(') if fForward then .col = startcol else .col = endcol endif ---- TeX math elseif wordpos( id, '\left \right') then search = '\\(left|right)' clist = substr( id, 2, 1) fForward = (clist = 'l') if fForward then .col = startcol else .col = endcol endif else -- not a known balanceable 'TEX' token assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN endif -- Mode(s): FORTRAN77 ----------------------------------------------------------------------- elseif (CurMode = 'FORTRAN77') then -- This code disregards the "token" and allows the cursor anywhere within -- columns 7 ... 72 if .col > 6 and .col < 73 then statement = fortran77_extract_text( .line) do i = .line + 1 to .last temp = textline( i) if pos( substr( temp, 6, 1), ' 0') then leave elseif not pos( leftstr( temp, 1), 'C*') then statement = statement''fortran77_extract_text( i) endif enddo statement = fortran77_remove_spaces( statement) if leftstr( statement, 2) = 'DO' then equalpos = pos( '=', statement, 4) commapos = pos( ',', statement, 6) if equalpos and commapos then -- DO loop of some kind? fForward = 1 clist = 'D' .col = pos( 'D', line, 7) p = verify( statement, '0123456789', 'N', 3) if p > 3 then -- DO ,... loop label = substr( statement, 3, p - 3) --#dprintf( 'passist', 'label as string: 'label) label = label + 0 --#dprintf( 'passist', 'label as number: 'label) temp = '' do i = length( label) to 4 temp = temp'[ 0]?' enddo search = '^('temp''label'[ 0]?[ ]*\c[^0])' --#dprintf( "passist", "DO