source: trunk/src/netlabs/macros/assist.e@ 4348

Last change on this file since 4348 was 4348, checked in by Andreas Schnellbacher, 4 years ago
  • Minor changes.
  • Property svn:keywords set to Date Revision Author HeadURL Id
File size: 129.1 KB
Line 
1/****************************** Module Header *******************************
2*
3* Module Name: assist.e
4*
5* Copyright (c) Netlabs EPM Distribution Project 2002
6*
7* $Id: assist.e 4348 2020-12-17 09:43:13Z aschn $
8*
9* ===========================================================================
10*
11* This file is part of the Netlabs EPM Distribution package and is free
12* software. You can redistribute it and/or modify it under the terms of the
13* GNU General Public License as published by the Free Software
14* Foundation, in version 2 as it comes in the "COPYING" file of the
15* Netlabs EPM Distribution. This library is distributed in the hope that it
16* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18* General Public License for more details.
19*
20****************************************************************************/
21
22/*****************************************************************************/
23/* Assist interface for E3 Ralph Yozzo, Larry Margolis */
24/* */
25/* This macro is intended for use with programming language */
26/* which have tokens which must be balanced to compile correctly. */
27/* We shall call these tokens "balanceable tokens" or BalTok for */
28/* short. */
29/* */
30/* The functions provided include moving from an opening token */
31/* (e.g., (, {, [ ) to a closing token (e.g., ), }, ] ) and vice versa. */
32/* */
33/* KEYS: */
34/* Ctrl-[, Ctrl-], Ctrl-8 -- move to corresponding BalTok) */
35/* */
36/* CONSTANTS: */
37/* gold -BalTok tokens are defined in the const gold and additional */
38/* tokens may be added. */
39/* */
40/* Example: */
41/* if ((c=getch())=='c' */
42/* &&(d=complicatedisntit(e))){ */
43/* lookforbracket(); */
44/* } */
45/* In the above program segment if one places the cursor on an opening */
46/* parenthesis and presses Ctrl-[ the cursor will move to the corresponding */
47/* closing parenthesis if one exists. Pressing Ctrl-[ again will reverse */
48/* the process. */
49/* */
50/* Modified by Larry Margolis to use the GREP option of Locate to search */
51/* for either the opening or closing token, rather than checking a line at */
52/* a time. I also changed the key from Ctrl-A to Ctrl-[ or -], which are */
53/* newly allowed as definable keys, and deleted the matching of /* and */. */
54/* (The GREP search is much faster than the way Ralph did it, but doesn't */
55/* let you match either of 2 strings.) Finally, the user's previous search */
56/* arguments are saved and restored, so Ctrl-F (repeatfind) will not be */
57/* affected by this routine. */
58/* */
59/* Updated by LAM to use EGREP to also handle #if, #endif, etc. and COMPILE */
60/* IF, COMPILE ENDIF, etc. */
61/* */
62/* 1995/02/21 Updated by LAM to also handle SCRIPT list tags and /* */. */
63/* 1995/02/22 Updated by LAM to also handle SGML tags. */
64/* */
65/* 1999/03/15 Updated by Petr Mikulik, http://www.sci.muni.cz/~mikulik/, to */
66/* also handle TeX tokens: \if... \else \fi; \begin... \end...; */
67/* \if... \else \fi; \begin{ \end{; \left \right */
68/* This patch is part of the "pmCSTeX for EPM" package, see */
69/* http://www.sci.muni.cz/~mikulik/os2/pmCSTeX.html */
70/*****************************************************************************/
71
72; Included TeX extensions for the pAssist procedure by Petr Mikulik from his
73; PMCSTeX package.
74
75; 2006 changes: JBS
76; A major rework of the code:
77; o Bugs were fixed.
78; o More tokens are balanced, including the start and end points of
79; multi-line comments.
80; o More modes supported (PASCAL, FORTRAN77, JAVA, WARPIN).
81; o Added initial stages of support for ADA, CSS, PERL, PHP.
82; o Code was added to ensure tokens found within comments or literals were
83; not matched.
84; o Better variable names and code documentation.
85; o Contains procs that return if a position is within a multi-line commemt,
86; single-line comment or literal.
87; o Added a multi-line comment (MLC) array to speed up searching and for
88; making MLC matching for modes with nested MLCs reliable.
89
90; After that: aschn
91; o Commented out debug messages (minor speed improvement).
92; o Replaced searching in the MLC array (big speed improvement).
93; o Renamed procs and vars.
94; o Added more procs for use from outside.
95; o Added optional line and col parameters for querying another than
96; current position.
97; o Use the comment and literal procs now by tags and syntax expansion.
98
99; 2020 changes: aschn
100; o Building the multi-line comment (MLC) array takes much time for longer
101; files. For MatchFindOnMove (matching brackets while moving the cursor),
102; a simple method is used instead to keep cursor movement fluently. For
103; modes that support nested MLCs (E and REXX), the MLC array has to be
104; built to determine the within-comment state.
105; o That simple method just checks if the previous found MLC string is a
106; start or end string. From that, the in-MLC state is determined.
107; o If a valid MLC array exists, that one is always used with precedence.
108; o The MLC array method is also used for other cases than MatchFindOnMove.
109; By the way: For larger files and e.g. TagScan, the array method is
110; multiple (something like ten) times faster.
111
112; Ideas:
113; o Use attributes for marking MLCs and scan only that region that has
114; changed.
115; o Use attributes for tags and scan only that region that has changed.
116
117compile if not defined( SMALL) -- If compiled separately
118 EA_comment 'Linkable bracket-matching routines.'
119
120define
121 INCLUDING_FILE = 'ASSIST.E'
122
123 include 'stdconst.e'
124const
125 tryinclude 'mycnf.e'
126const
127 compile if not defined( NLS_LANGUAGE)
128 NLS_LANGUAGE = 'ENGLISH'
129 compile endif
130 include NLS_LANGUAGE'.e'
131
132defmain
133 'Assist'
134compile endif -- not defined(SMALL)
135
136; ---------------------------------------------------------------------------
137const
138 -- NOTE: The logic below relies on GOLD being defined with the left
139 -- "brackets" in the odd positions and the right "brackets" in
140 -- the even positions.
141 GOLD = '(){}[]<>' -- Parens, braces, brackets & angle brackets
142
143 ASSIST_RC_MLC_MATCHED = -1
144 ASSIST_RC_NO_ERROR = 0
145 ASSIST_RC_IN_ONELINE_COMMENT = 1
146 ASSIST_RC_IN_MULTILINE_COMMENT = 2
147 ASSIST_RC_IN_LITERAL = 3
148 ASSIST_RC_NOT_ON_A_TOKEN = 4
149 ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN = 5
150 ASSIST_RC_BAD_FORTRAN77_CURSOR = 6
151 ASSIST_RC_MODE_NOT_SUPPORTED = 7
152 ASSIST_RC_TOO_MANY_LINES = 8
153 ASSIST_RC_TOO_MANY_LOOPS = 9
154
155 EGREP_METACHARACTERS = '\[]()?+*^$.|' -- JBSQ: This may need to changed if the grep code
156 -- ever supports {, } and possibly other egrep metacharacters
157
158 -- JBSQ: The following constants 'hard-code' the versions of NMAKE and
159 -- FORTRAN77 for which pAssist works. Perhaps there is a better way
160 -- that would allow this to be selected by the user?
161compile if not defined( USE_NMAKE32)
162 USE_NMAKE32 = 1 -- 0 means do not accept NMAKE32-specific directives
163compile endif
164compile if not defined( USE_FORTRAN90_SLC)
165 USE_FORTRAN90_SLC = 1 -- 0 means disregard FORTRAN90 SLC: '!'
166compile endif
167
168compile if not defined( ASSIST_DEFAULT_MAX_LINES)
169 ASSIST_DEFAULT_MAX_LINES = 2000
170compile endif
171compile if not defined( ASSIST_DEFAULT_MAX_LOOPS)
172 ASSIST_DEFAULT_MAX_LOOPS = 500
173compile endif
174
175; ---------------------------------------------------------------------------
176; Syntax: Assist [quiet] [[<max_lines>] <max_loops>]
177defc Assist
178 universal vepm_pointer
179
180 parse arg args
181 wp = wordpos( 'QUIET', upcase( args))
182 fQuiet = (wp > 0)
183 if wp then
184 args = delword( args, wp, 1)
185 endif
186 if fQuiet then
187 VerboseLevel = 0 -- 0 = quiet, no 'Invalid:' etc. messages (0 also disables pointer change)
188 else
189 VerboseLevel = 1 -- 1 = normal
190 endif
191 parse value args with MaxLines MaxLoops
192 MaxLines = strip( MaxLines)
193 MaxLoops = strip( MaxLoops)
194
195 if VerboseLevel <> 0 then
196 mouse_setpointer WAIT_POINTER
197 endif
198 fHighlight = 1
199 fUseArray = 1
200 call pAssist( VerboseLevel, fHighlight, MaxLines, MaxLoops, fUseArray)
201 if VerboseLevel <> 0 then
202 mouse_setpointer vepm_pointer
203 endif
204
205; ---------------------------------------------------------------------------
206; id = found word under cursor (or beneath the cursor in some cases)
207; fIntermediate = set to 1 if id is an intermediate conditional token
208; (e.g. 'else', but not 'if' or 'endif')
209; fForward = a flag to indicate which direction to search
210; 1 = forward, 0 = backward
211; SearchStr = string for locate command, without seps and options,
212; egrep will be used
213; clist = a space delimited list of substrings to match on. Usually this
214; is a single substring. An example of multiple substrings in
215; clist would be that an "end" in REXX mode can match up with
216; either a "do" or a "select" statement. If fForward = 1 then
217; these substrings should identify the starting token(s) of the
218; bracketed code. If fForward = 0 then these substrings should
219; identify the ending token(s).
220; coffset = offset from cursor pos after the search to substring to match
221; The cursor pos, specified with \c in the regex, marks also
222; the begin of the highlighted string.
223; clen = the length of the substrings in clist
224; hcol = the start pos of the highlighted string for intermediate
225; tokens (e.g. else, elseif). The first search is suppressed
226; for them, so that no search data exists for them. If not
227; specified, the start is determined from the id at the
228; cursor.
229; hlen = the length of the highlighted string for intermediate
230; tokens (e.g. else, elseif). The first search is suppressed
231; for them, so that no search data exists for them. If not
232; specified, the length is determined from the id at the
233; cursor.
234; nestlevel = counter for nesting of matched tokens. On nestlevel = 0,
235; the matched token was found.
236; startcol = the pos of id start in current line
237; endcol = the pos of id end (last char of id)
238; TexEnvStr = a string to submit the searched TEX environment, surrounded
239; by braces and preceded by '\begin' or \'end'.
240; fEExcludeCompile = a flag to indicate if an E condition is not preceded by
241; 'compile'.
242; 1 = 'compile' excluded, 0 = not excluded or not a condition.
243; 'compile if|...' uses 0, 'if|...' uses 1.
244defproc pAssist
245 universal lastassistpos
246
247 call pSave_Pos( savepos) -- Save the cursor location
248 getsearch search_command -- Save user's search command.
249
250 StartLineNum = .line
251 VerboseLevel = arg( 1) -- 0 = quiet (default), 1 = normal
252 fHighlight = arg( 2) -- 1 = highlighting of matching expression and scrolling
253 -- 0 = no highlighting (default)
254 MaxLines = arg( 3)
255 MaxLoops = arg( 4)
256 fUseArray = arg( 5)
257 if VerboseLevel < 1 then
258 VerboseLevel = 0
259 endif
260 if fHighlight <> 1 then
261 fHighlight = 0
262 endif
263 if MaxLines = '' then
264 MaxLines = ASSIST_DEFAULT_MAX_LINES
265 endif
266 if MaxLoops = '' then
267 MaxLoops = ASSIST_DEFAULT_MAX_LOOPS
268 endif
269 if not wordpos( fUseArray, '0 1') then
270 fUseArray = 1 -- default value
271 endif
272 -- The non-array version can not be used for retrieving pos. data or for
273 -- finding the matching MLC string. It should only be used for
274 -- MatchFindOnMove. For repeated use and long texts, the array version
275 -- is much faster. It causes a short delay to scan the entire file and
276 -- leads to non-fluently cursor move.
277 call SetAvar( 'assist.fUseArray', fUseArray)
278 SearchLimitData = StartLineNum MaxLines MaxLoops -- Use one arg for AssistSearch to save args
279
280 case = 'e' -- respect case is default
281 coffset = 0 -- cursor offset for clist keyword(s) after the search
282 clen = 1
283 hcol = 0
284 hlen = 0
285 fIntermediate = 0
286 fForward = 1
287 nestlevel = 1
288 TexEnvStr = ''
289 fEExcludeCompile = 0
290
291 id = '' -- token under cursor
292 startcol = 1 -- start column of id/token
293 endcol = 1 -- end column of id/token
294 SearchStr = ''
295
296 -- Init search position data for highlighting
297 HighlightLine = .line
298 HighlightCol = .col
299 HighlightLen = 1
300
301 --#dprintf( "pAssist", "------------------------------------------------------------")
302 --#dprintf( "pAssist", "Initial cursor: ".line",".col)
303 CurMode = NepmdGetMode()
304 lastassistpos = '0 0 0'
305
306 -- These are MLCs in E and just be better used as variables to allow
307 -- using them as MLCs in E:
308 SlashStar = '/'||'*'
309 StarSlash = '*'||'/'
310
311 do once = 1 to 1
312 assist_rc = InsideComment2( CurMode, CommentData)
313 -- InsideComment2 -> LocateMLC may have changed fUseArray for modes
314 -- that support nested MLCs
315 fUseArray = GetAvar( 'assist.fUseArray')
316
317 --#dprintf( "pAssist", "comment return:" assist_rc CommentData)
318 if assist_rc = ASSIST_RC_IN_MULTILINE_COMMENT then
319 parse value CommentData with CommentStartLine CommentStartCol CommentStartLen CommentEndLine CommentEndCol CommentEndLen
320
321 if fUseArray then
322 if .line = CommentStartLine & .col - CommentStartCol < CommentStartLen then -- if cursor on start
323 .line = CommentEndLine -- move to the end
324 .col = CommentEndCol
325 HighlightLine = CommentStartLine
326 HighlightCol = CommentStartCol
327 HighlightLen = CommentStartLen
328 elseif .line = CommentEndLine & .col >= CommentEndCol then -- if cursor on end
329 .line = CommentStartLine -- move to the start
330 .col = CommentStartCol
331 HighlightLine = CommentEndLine
332 HighlightCol = CommentEndCol
333 HighlightLen = CommentEndLen
334 endif
335 endif
336
337 assist_rc = ASSIST_RC_MLC_MATCHED
338 leave
339 endif
340
341 if InsideLiteral( CurMode) then
342 assist_rc = ASSIST_RC_IN_LITERAL
343 leave
344 endif
345
346 getline LineStr
347 CurChar = substr( LineStr, .col, 1)
348/*
349 -- Make Assist work if the cursor is just after a keyword
350 -- If c = space, then try it 1 col left
351 if (CurChar == ' ' | CurChar == \t) & .col > 1 then
352 left
353 CurChar = substr( LineStr, .col, 1)
354 endif
355*/
356
357 -- Determine SearchStr and vars for brackets
358 BracketChar = pos( CurChar, GOLD) -- '(){}[]<>'
359 -- Braces, '{}', can be matched with BEGIN and END in RC files.
360 -- So they need to be handled separately.
361 if BracketChar then
362 if pos( CurChar, '{}') & CurMode = 'RC' then
363 BracketChar = 0
364 endif
365 endif
366 -- If BracketChar, then set SearchStr to the corresponding char out of GOLD
367 if BracketChar then
368 leftbracket = substr( GOLD, (BracketChar + 1) % 2 * 2 - 1, 1)
369 rightbracket = substr( GOLD, (BracketChar + 1) % 2 * 2, 1)
370 -- Escape brackets with '\'
371 SearchStr = '[\'leftbracket'\'rightbracket']'
372 clist = CurChar
373 fForward = BracketChar // 2
374 leave
375 endif
376
377 -- Build the separator list for find_token
378 if CurMode = 'FORTRAN77' then
379 seps = "+-*/=().,':$"
380 else
381 seps = ' ~`!.%^&*()-+=][{}|\;?<>,''"'\t
382 endif
383
384 if CurMode = 'TEX' then
385 -- Move cursor right if it is on \backslash
386 if substr( LineStr, .col, 1) = '\' then
387 right
388 endif
389 endif
390
391 -- Get the word under cursor and return startcol and endcol
392 -- Stop at separators = arg( 3)
393 -- Stop at double char separators = arg( 4)
394 -- Checking for SlashStar and StarSlash is not required here, because of
395 -- the MLC search above.
396 if not Find_Token( startcol, endcol, seps, 'NONE:') then
397 assist_rc = ASSIST_RC_NOT_ON_A_TOKEN
398 leave
399 endif
400
401 --#dprintf( "pAssist", "Initial token start,end: "startcol","endcol)
402 if startcol > 1 then
403 prevchar = substr( LineStr, startcol - 1, 1)
404 if prevchar = '<' then
405 if CurMode = 'HTML' | CurMode = 'WARPIN' /* | CurMode = 'XML' */ then
406 -- Add '<' to found word if it is on the left side of it
407 -- this assumes ALL balanceable tokens for HTML and WARPIN
408 -- start with '<'
409 startcol = startcol - 1
410 endif
411 elseif prevchar = '\' then
412 if CurMode = 'TEX' then
413 --//PM TeX macros are preceded by \backslash which is also separator
414 -- Add '\' to found word if it is on the left side of it
415 startcol = startcol - 1
416 endif
417 endif
418 elseif startcol = 1 then -- if cursor is on the first column of a MAKE directive
419 if leftstr( LineStr, 1) = '!' then -- "shift" the id to the following token
420 if CurMode = 'MAKE' then
421 newstart = verify( LineStr, ' '\t, 'N', 2) -- skip to next non-whitespace
422 if newstart then
423 newend = verify( LineStr' ', ' ', 'M', newstart)
424 if newend then
425 startcol = newstart
426 endcol = newend - 1
427 endif
428 endif
429 endif
430 endif
431 endif
432
433 -- id = found word
434 id = substr( LineStr, startcol, (endcol - startcol) + 1)
435 --#dprintf( "pAssist", "Token after preprocessing : '"id"' Startcol: "startcol" Endcol: "endcol)
436
437 -- Set/re-init search position data for highlighting
438 HighlightLine = .line
439 HighlightCol = startcol
440 HighlightLen = endcol - startcol + 1
441
442 if 0 then
443 -- just a placeholder so the following ELSEIFs can be freely reordered
444
445 -- Mode(s): E -------------------------------------------------------------------------------
446 elseif CurMode = 'E' then
447
448 --dprintf( "pAssist", "E Token after : '"id"'")
449 case = 'c' -- Case insensitive for all E tokens
450 id = lowcase( id)
451 LineStr = lowcase( LineStr)
452 if 0 then
453 -- another placeholder
454
455 ---- E compiler directives: compile if, compile else, compile elseif, compile endif
456 elseif id = 'compile' then
457 temp = substr( LineStr, pos( id, LineStr))
458 --#dprintf( "pAssist", "Initial temp: "temp)
459 do while (pos( SlashStar, temp) > 0) & (pos( StarSlash, temp) > 0)
460 temp = substr( temp, pos( StarSlash, temp))
461 --#dprintf( "pAssist", "Intermediate temp: "temp)
462 enddo
463 --#dprintf( "pAssist", "Final temp: "temp)
464 if (words( temp) = 0) then
465 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
466 else
467 lcword2 = word( temp, 2)
468 --#dprintf( "pAssist", "lcword2: "lcword2)
469 if wordpos( lcword2, 'if endif else elseif') then
470 pcompile = pos( 'compile', LineStr)
471 pcondition = pos( lcword2, LineStr, pcompile + 8)
472 coffset = pcondition - pcompile
473 hcol = pcompile
474 hlen = pcondition + length( lcword2) - pcompile
475 -- compile if/... code (when cursor is on compile
476 SearchStr = '^[ \t]*\ccompile[ \t]*(/\*.*\*/)*[ \t]*(end)?if([; \t]|(--)|(/\*)|$)'
477 clist = leftstr( lcword2, 1)
478 fForward = (clist <> 'e')
479 if fForward then -- move to beginning
480 .col = startcol
481 else -- move to end, so first Locate will hit this instance.
482 endline
483 endif
484 fIntermediate = (substr( lcword2, 2, 1) = 'l')
485 else
486 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
487 endif
488 endif
489
490 ---- E conditions: (compile) if, (compile) else, (compile) elseif, (compile) endif
491 elseif wordpos( id, 'if endif else elseif') then
492 pcompile = pos( 'compile', LineStr)
493 if pcompile & pcompile <= startcol then
494 -- compile if|endif|else|elseif
495 parse value LineStr with part1 'compile' part2 (id) part3
496 --#dprintf( 'pAssist', 'E if parse: "'part1'"compile"'part2'"'id'"'part3'"')
497 part1 = strip( part1)
498 part2 = strip( part2)
499 if (part1 = '' | ((length( part1) >= 4) & (leftstr( part1, 2) = SlashStar) & (rightstr( part1, 2) = StarSlash))) &
500 (part2 = '' | ((length( part2) >= 4) & (leftstr( part2, 2) = SlashStar) & (rightstr( part2, 2) = StarSlash))) then
501 pcompile = pos( 'compile', LineStr)
502 pcondition = pos( id, LineStr, pcompile + 8)
503 coffset = pcondition - pcompile
504 hcol = pcompile
505 hlen = pcondition + length( id) - pcompile
506 -- compile if/... code (when cursor is on if/...
507 --SearchStr = '^[ \t]*compile[ \t]*(/\*.*\*/)*[ \t]*\c(end)?if([; \t]|(--)|(/\*)|$)'
508 SearchStr = '^[ \t]*\ccompile[ \t]*(/\*.*\*/)*[ \t]*(end)?if([; \t]|(--)|(/\*)|$)'
509 clist = leftstr( id, 1)
510 fForward = (clist <> 'e')
511 if fForward then -- move to beginning
512 .col = length( part1) + 1
513 else -- move to end, so first Locate will hit this instance.
514 endline
515 endif
516 fIntermediate = (substr( id, 2, 1) = 'l')
517 else
518 -- /* compile */ if/... code
519 SearchStr = '(^|[ \t]|(\*/))\c(end)?if([; \t]|(--)|(/\*)|$)'
520 clist = leftstr( id, 1)
521 fForward = (clist <> 'e')
522 if fForward then -- move to beginning
523 .col = startcol
524 else -- move to end, so first Locate will hit this instance.
525 .col = endcol
526 endif
527 fIntermediate = (substr( id, 2, 1) = 'l')
528 endif
529 else
530 -- if|endif (without compile)
531 -- The found line is checked by a 2nd search for a preceding 'compile',
532 -- otherwise the re would have two many parentheses.
533 fEExcludeCompile = 1
534 --SearchStr = '((\*/:o)|(^:o)|(^:o(~compile:w)))\c(end)?if([; \t]|(--)|(/\*)|$)'
535 --SearchStr = '((\*/:o)|(;:o)|(^:o)|(^:o(~compile:w)))\c(end)?if([; \t]|(--)|(/\*)|$)'
536 SearchStr = '(^|[ \t]|(\*/))\c(end)?if([; \t]|(--)|(/\*)|$)'
537 clist = leftstr( id, 1)
538 fForward = (clist <> 'e')
539 if fForward then -- move to beginning
540 .col = startcol
541 else -- move to end, so first Locate will hit this instance.
542 .col = endcol
543 endif
544 fIntermediate = (substr( id, 2, 1) = 'l')
545 endif
546
547 ---- E loop keywords 1: loop, endloop
548 elseif wordpos( id, 'loop endloop') then
549 SearchStr = '(^|[ \t]|(\*/))\c(end)?loop([; \t]|(--)|(/\*)|$)'
550 clist = leftstr( id, 1)
551 fForward = (clist <> 'e')
552 if fForward then -- move to beginning
553 .col = startcol
554 else -- move to end, so first Locate will hit this instance.
555 .col = endcol
556 endif
557
558 ---- E loop keywords 2: for, endfor
559 elseif wordpos( id, 'for endfor') then
560 SearchStr = '(^|[ \t]|(\*/))\c(end)?for([; \t]|(--)|(/\*)|$)'
561 clist = leftstr( id, 1)
562 fForward = (clist <> 'e')
563 if fForward then -- move to beginning
564 .col = startcol
565 else -- move to end, so first Locate will hit this instance.
566 .col = endcol
567 endif
568
569 ---- E loop keywords 3: do, while, end, enddo, endwhile
570 elseif wordpos( id, 'do while end enddo endwhile') then
571 if (id = 'end') then
572 SearchStr = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)'
573 elseif (id = 'enddo') then
574 SearchStr = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)'
575 elseif (id = 'endwhile') then
576 SearchStr = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)|(/\*)|$)'
577 else -- check for do and/or while
578 whilepos = wordpos( 'while', LineStr)
579 dopos = wordpos( 'do', LineStr)
580 if (not dopos) | (whilepos & (whilepos < dopos)) then -- while or while ... do?
581 SearchStr = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)|(/\*)|$)'
582 startcol = whilepos
583 endcol = whilepos + 4
584 else -- do or do ... while
585 SearchStr = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)'
586 startcol = dopos
587 endcol = dopos + 1
588 endif
589compile if 0
590 if whilepos & dopos then -- if both
591 if whilepos < dopos then -- while ... do ?
592 SearchStr = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)|(/\*)|$)'
593 startcol = whilepos
594 endcol = whilepos + 4
595 else -- do ... while
596 SearchStr = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)'
597 startcol = dopos
598 endcol = dopos + 1
599 endif
600 else
601 if dopos then -- do
602 SearchStr = '(^|[ \t]|(\*/))\c(do|end|enddo)([; \t]|(--)|(/\*)|$)'
603 else -- while
604 SearchStr = '(^|[ \t]|(\*/))\c(end)?while([; \t]|(--)||(/\*)$)'
605 endif
606 endif
607compile endif
608 --#dprintf( "pAssist", "Whilepos: "whilepos "Dopos: "dopos "SearchStr: "SearchStr)
609 endif
610 clist = leftstr( id, 1)
611 --#dprintf( "pAssist", "clist: "clist)
612 fForward = (clist <> 'e')
613 if fForward then -- move to beginning
614 .col = startcol
615 else -- move to end, so first Locate will hit this instance.
616 .col = endcol
617 endif
618
619 ---- E loop keywords 4: leave, iterate
620 elseif wordpos( id, 'leave iterate') then
621 SearchStr = '(^|[ \t]|(\*/))\c(do|end|enddo|loop|endloop|for|endfor|endwhile)([; \t]|(--)|(/\*)|$)'
622 fForward = 0
623 clist = 'e'
624 clen = 1
625 .col = endcol
626 fIntermediate = 1
627
628 else -- not a known balanceable E token
629 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
630 endif
631
632 -- Mode(s): C, JAVA JAVASCRIPT and RC -------------------------------------------------------
633 elseif wordpos( CurMode, 'C JAVA RC JAVASCRIPT') > 0 then
634
635 -- C allows for spaces between '#' and keyword, JAVA, RC and JAVASCRIPT don't.
636 if CurMode = 'C' then
637 if wordpos( id, 'if ifdef ifndef endif else elif') > 0 then -- Check for "# if", etc.
638 .col = startcol
639 if NextCodeChar( Mode, .line, .col, '-R') = '#' then
640 id = '#'id
641 startcol = .col
642 endif
643 call pRestore_Pos( savepos)
644 endif
645 endif
646
647 if 0 then
648 --placeholder
649
650 ---- Directive(s): #if #ifdef #ifndef #endif #else #elif
651 elseif wordpos( id, '#if #ifdef #ifndef #endif #else #elif') then
652 if CurMode <> 'JAVA' & CurMode <> 'JAVASCRIPT' then
653 SearchStr = '\#[ \t]*\c((if((n?def)?))|endif)([ \t]|$)'
654 if CurMode = 'C' then
655 SearchStr = '^[ \t]*\c'SearchStr
656 elseif startcol = 1 then -- RC directives must start in column one
657 SearchStr = '^'SearchStr
658 else
659 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
660 endif
661 clist = substr( id, 2, 1)
662 fForward = (clist = 'i')
663 if fForward then -- move to beginning
664 .col = startcol
665 else -- move to end, so first Locate will hit this instance.
666 .col = endcol
667 endif
668 fIntermediate = (substr( id, 3, 1) = 'l')
669 else
670 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
671 endif
672
673 ---- Keyword(s): do, try
674 elseif wordpos( id, 'do try') then
675 -- this code might be expanded to <anytoken> { .... }
676 if CurMode <> 'RC' then
677 setsearch 'xcom l /[{;]/xe+F' -- does following a '{' precede a ';'?
678 clist = '{ ;'
679 case = 'e' -- 'e' = case-sensitive
680 coffset = 0
681 clen = 1
682 nestlevel = -1 -- -1 = stop on first 'hit'
683 assist_rc = AssistSearch( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData)
684 if assist_rc then
685 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
686 else
687 --#dprintf( "pAssist", "token {} '{;' SearchStr found: "substr( textline( .line), .col, 1))
688 if substr( textline( .line), .col, 1) = '{' then -- found the braces
689 SearchStr = '[{}]'
690 fForward = 1
691 clist = '{'
692 else
693 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- token without braces
694 endif
695 endif
696 else
697 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
698 endif
699
700 ---- Keyword(s): while
701 elseif id = 'while' then
702 if CurMode <> 'RC' then
703 setsearch 'xcom l /[()]/xe+F' -- find the end of the conditional
704 clist = '{'
705 case = 'e' -- 'e' = case-sensitive
706 coffset = 0
707 clen = 1
708 nestlevel = 0
709 assist_rc = AssistSearch( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData)
710 if assist_rc then -- no conditional??
711 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
712 else
713 nextchar = NextCodeChar( CurMode)
714 if not nextchar then
715 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
716 else
717 --#dprintf( "pAssist", "while {; SearchStr found: "nextchar)
718 if nextchar = '{' then -- must be while loop (i.e. NOT do/while) with braces
719 SearchStr = '[{}]'
720 fForward = 1
721 clist = '{'
722 elseif nextchar = ';' then -- cursor is on the 'while' of a do/while loop
723 -- SearchStr = '\{|\}|((^|[ \t])\cdo([ \t]|$))'
724 SearchStr = '[{}]|((^|[ \t])\cdo([ \t]|$))'
725 fForward = 0
726 clist = '}'
727 nestlevel = 2
728 else -- cursor is on a one-statement while loop
729 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- no braces follow, so JBSQ: unbalanceable?
730 endif
731 endif
732 endif
733 endif
734/*
735 JBSQ: The following code should work for break/continue statements within
736 nested do and for loops which have braces. Problems arise if there are
737 DOs or FORs without braces between the break/continue and the enclosing
738 loop. Also problems arise if the enclosing loop is a while loop.
739
740 The solution, if it is worth it, is to code the entire search here so that these different
741 variations of loops can be handled correctly.
742
743 ---- Keyword(s): break continue
744 elseif wordpos( id, 'break continue' then
745 if CurMode <> 'RC' then
746 SearchStr = '([{}])|((^|[ \t])\c(do|for)([ \t]|$))'
747 fForward = 0
748 clist = '}'
749 fIntermediate = 1
750 else
751 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
752 endif
753*/
754
755 elseif CurMode = 'RC' then
756 -- All remaining RC tokens here
757 case = 'c' -- RC files are case insensitive, except for direcives (#if, #define, etc.)
758 id = lowcase( id)
759 if wordpos( id, '{ } begin end') then
760 SearchStr = '[{}]|((^|[ \t])\c(begin|end)([ \t;]|$))'
761 fForward = (wordpos( id, '{ begin') > 0)
762 if fForward then -- move to beginning begin
763 .col = startcol
764 clist = '{ b'
765 else -- move to end, so first Locate will hit this instance.
766 .col = endcol
767 clist = '} e'
768 endif
769 else
770 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
771 endif
772
773 else
774 -- All remaining C/JAVA/JAVASCRIPT tokens here
775 idlist = 'case default default:'
776 if CurMode <> 'C' then
777 idlist = idlist 'finally'
778 endif
779 if wordpos( id, idlist) then
780 SearchStr = '[{}]'
781 fForward = 0
782 clist = '}'
783 fIntermediate = 1
784 else
785 -- This code seems to handle matching the beginning and end of
786 -- a) functions when the cursor is on a token preceding the
787 -- parameter list
788 -- b) 'if' when the 'if' has following braces
789 -- c) 'for' when the 'for' has following braces
790 -- d) 'switch' statements
791 -- e) anything of the structure: token ( ) { ... }
792 -- e.g. catch ( ) { ... }
793
794 -- JBSQ: Check for NextCodeChar( CurMode) = '(' first?
795 -- (This would force the cursor to actually be on the function
796 -- name. The current code allows the cursor on any nonblank,
797 -- noncomment, nonliteral character preceding the parameter
798 -- list.)
799 setsearch 'xcom l /[()]/xe+F' -- find the ending ')' or ;
800 clist = '{'
801 case = 'e' -- 'e' = case-sensitive
802 coffset = 0
803 clen = 1
804 nestlevel = 0
805 assist_rc = AssistSearch( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData)
806 --#dprintf( 'pAssist', 'last chance c,... srch_rc line col' assist_rc .line .col)
807 if not assist_rc & substr( textline( .line), .col, 1) = ')' then -- no conditional??
808 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
809 nextchar = NextCodeChar( CurMode)
810 if nextchar then
811 --#dprintf( "pAssist", "Generic token () {; SearchStr found: "nextchar)
812 if nextchar = '{' then -- structure is: token ( ) { ... }
813 SearchStr = '[{}]'
814 fForward = 1
815 clist = '{'
816 assist_rc = ASSIST_RC_NO_ERROR
817 endif
818 endif
819 else
820 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
821 endif
822 endif
823 endif
824
825 -- Mode(s): REXX ----------------------------------------------------------------------------
826 elseif CurMode = 'REXX' then
827
828 ---- REXX conditions: if, else
829 case = 'c' -- Case insensitive for all REXX tokens
830 id = lowcase(id)
831 if 0 then
832 -- another placeholder
833
834 elseif wordpos( id, 'do end select when otherwise') then
835 SearchStr = '(^|[ \t])\c(do|end|select)([; \t]|$)'
836 fForward = (wordpos(id, 'do select') > 0)
837 if fForward then -- move to beginning
838 clist = 'do se'
839 .col = startcol
840 else -- move to end, so first Locate will hit this instance.
841 clist = 'en'
842 .col = endcol
843 endif
844 clen = 2
845 fIntermediate = ((id = 'when') | (id = 'otherwise'))
846
847 elseif id = 'else' then
848 .col = startcol
849 pchar = NextCodeChar( CurMode, .line, .col, '-R')
850 --#dprintf( "pAssist", 'Previous char:' pchar)
851 if lowcase( pchar) = 'd' then
852 if .col > 2 then
853 .col = .col - 2
854 pword = lowcase( substr( textline( .line), .col, 3))
855 if .col > 1 & substr( textline( .line), .col - 1, 1) <> ' ' then
856 --
857 else
858 --#dprintf( "pAssist", 'Previous word:' pword)
859 if pword = 'end' then
860 display -1
861 call pAssist( VerboseLevel, fHighlight)
862 display 1
863 endcol = .col
864 endif
865 endif
866 endif
867 endif
868 SearchStr = '(^|[ \t])\cif([ \t]|$)'
869 clist = 'i'
870 fForward = 0
871 fIntermediate = 1
872 .col = endcol
873 nestlevel = -1
874
875compile if 0
876 elseif wordpos( id, 'if else') then
877 -- JBSQ: How is this supposed to work? IFs don't always have ELSEs
878 SearchStr = '(^|[ \t])(if|else)([ \t]|$)'
879 clist = leftstr( id, 1)
880 fForward = (clist <> 'e')
881 if fForward then -- move to beginning
882 .col = startcol
883 else -- move to end, so first Locate will hit this instance.
884 .col = endcol
885 endif
886compile endif
887
888 else -- not a known balanceable REXX token
889 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
890 endif
891
892 -- Mode(s): IPF -----------------------------------------------------------------------------
893 elseif CurMode = 'IPF' then
894
895 -- IPF tags start with ':' and end with '.'
896 -- If id = '.', then go 1 col left
897 if id = '.' & .col > 1 then
898 left
899 if Find_Token( startcol, endcol) then
900 id = substr( LineStr, startcol, (endcol - startcol) + 1)
901 endif
902 endif
903
904 if pos( ':', id) > 1 then -- <text>:tag, separate the :tag
905 id = substr( id, pos( ':', id))
906 endif
907
908 if length( LineStr) > endcol then
909 if (pos( substr( LineStr, endcol + 1, 1), '. ')) then
910 IPFBeginTags = ':artlink :caution :cgraphic :ctrldef :fn :hide'
911 IPFBeginTags = IPFBegintags ':hp1 :hp2 :hp3 :hp4 :hp5 :hp6 :hp7 :hp8 :hp9'
912 IPFBeginTags = IPFBegintags ':lines :link :nt :userdoc :warning :xmp'
913 IPFEndTags = ':eartlink :ecaution :ecgraphic :ectrldef :efn :ehide'
914 IPFEndTags = IPFEndTags ':ehp1 :ehp2 :ehp3 :ehp4 :ehp5 :ehp6 :ehp7 :ehp8 :ehp9'
915 IPFEndTags = IPFEndTags ':elines :elink :ent :euserdoc :ewarning :exmp'
916 IPFTags = IPFBeginTags IPFEndTags
917 --#dprintf( "pAssist", "IPF ID wordpos: "wordpos( id, IPFTags))
918 --#dprintf( "pAssist", "IPF Tags: "IPFTags)
919 coffset = 1 -- omit the leading ':' in words of clist
920
921 if wordpos( id, IPFTags) then
922 clist = substr( id, 2, 1) -- Character to check to see if it's an end tag
923 fForward = (clist <> 'e') -- fForward = 1 if searching forward; 0 if backwards
924 if fForward then -- move to beginning
925 .col = startcol
926 id = substr( id, 2)
927 else -- move to end, so first Locate will hit this instance.
928 .col = endcol + 1
929 id = substr( id, 3)
930 endif
931 SearchStr = '\:e?'id'(\.| )'
932
933 elseif wordpos( id, ':ol :ul :sl :eol :eul :esl :li :lp') then
934 fIntermediate = (id = ':li' | id = ':lp')
935 fForward = (wordpos( id, ':ol :ul :sl') > 0) -- fForward = 1 if searching forward; 0 if backwards
936 if fForward then -- move to beginning
937 .col = startcol
938 id = substr( id, 2)
939 clist = 'ol ul sl'
940 clen = 2
941 else -- move to end, so first Locate will hit this instance.
942 .col = endcol + 1
943 id = substr( id, 3)
944 clist = 'eol eul esl'
945 clen = 3
946 endif
947 SearchStr = '\:e?(o|u|s)l(\.| )'
948
949 elseif wordpos( id, ':table :etable :row :c') then
950 fIntermediate = (id = ':row' | id = ':c')
951 fForward = (id = ':table') -- fForward = 1 if searching forward; 0 if backwards
952 if fForward then -- move to beginning
953 .col = startcol
954 clist = 'table'
955 clen = 5
956 id = substr( id, 2)
957 else -- move to end, so first Locate will hit this instance.
958 .col = endcol + 1
959 clist = 'etable'
960 clen = 6
961 id = substr( id, 3)
962 endif
963 SearchStr = '\:e?table(\.| )'
964
965 elseif wordpos( id, ':parml :eparml :pt :pd') then
966 fIntermediate = (id = ':pt' | id = ':pd')
967 fForward = (id = ':parml') -- fForward = 1 if searching forward; 0 if backwards
968 if fForward then -- move to beginning
969 .col = startcol
970 id = substr( id, 2)
971 clist = 'parml'
972 clen = 5
973 else -- move to end, so first Locate will hit this instance.
974 .col = endcol + 1
975 id = substr( id, 3)
976 clist = 'eparml'
977 clen = 6
978 endif
979 SearchStr = '\:e?parml(\.| )'
980
981 elseif wordpos( id, ':dl :dthd :ddhd :dt :dd :edl') then
982 fIntermediate = (wordpos( id, ':dthd :ddhd :dt :dd') > 0)
983 fForward = (id = ':dl') -- fForward = 1 if searching forward; 0 if backwards
984 if fForward then -- move to beginning
985 .col = startcol
986 id = substr( id, 2)
987 clist = 'dl'
988 clen = 2
989 else -- move to end, so first Locate will hit this instance.
990 .col = endcol + 1
991 id = substr( id, 3)
992 clist = 'edl'
993 clen = 3
994 endif
995 SearchStr = '\:e?dl(\.| )'
996
997 elseif wordpos( id, ':fig :efig :figcap') then
998 fIntermediate = (id = ':figcap')
999 fForward = (id = ':fig') -- fForward = 1 if searching forward; 0 if backwards
1000 if fForward then -- move to beginning
1001 .col = startcol
1002 id = substr( id, 2)
1003 else -- move to end, so first Locate will hit this instance.
1004 .col = endcol + 1
1005 id = substr( id, 3)
1006 endif
1007 SearchStr = '\:e?fig(\.| )'
1008
1009 else -- not a known balanceable IPF token
1010 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1011 endif
1012
1013 else -- not a known balanceable IPF token
1014 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1015 endif
1016 else -- not a known balanceable IPF token
1017 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1018 endif
1019
1020 -- Mode(s): HTML, WARPIN --------------------------------------------------------------------
1021 elseif (CurMode = 'HTML' | CurMode = 'WARPIN') then
1022
1023 case = 'c'
1024 ---- HTML tags: <...,</...>
1025 if leftstr( id, 1) = '<' then
1026 clist = substr( id, 2, 1) -- Character to check to see if it's the same or the other
1027 fForward = (clist <> '/') -- fForward = 1 if searching forward; 0 if backwards
1028 if fForward then -- move to beginning
1029 id = substr( id, 2) -- Strip off the '<'
1030 clist = id
1031 .col = startcol
1032 else -- move to end, so first Locate will hit this instance.
1033 id = substr( id, 3) -- Strip off the '</'
1034 clist = '/'id
1035 .col = endcol + 1 -- +1 for the '>' after the tag
1036 endif
1037 SearchStr = '<\c/?'id'(>| )' -- Use \c to not put cursor on angle bracket.
1038 clen = length( clist)
1039 else -- not a known balanceable HTML token
1040 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1041 endif
1042
1043 -- Mode(s): MAKE ----------------------------------------------------------------------------
1044 elseif CurMode = 'MAKE' then
1045
1046 case = 'c' -- Case insensitive for all MAKE tokens
1047 id = lowcase( id)
1048 -- Currently ALL balanceable MAKE tokens must be on a line with
1049 -- '!' in column 1 and if there are any characters between the '!'
1050 -- and the token they must be whitespace.
1051 if leftstr( LineStr, 1) = '!' then
1052 if startcol > 2 then -- Are there characters between '!' and token?
1053 if verify( substr( LineStr, 2, startcol - 2), ' '\t) then
1054 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1055 id = '' -- Disable further processing
1056 endif
1057 endif
1058 if 0 then
1059 -- placeholder
1060
1061compile if USE_NMAKE32
1062 elseif wordpos( id, 'if ifdef ifndef endif else elif elseif') then
1063compile else
1064 elseif wordpos( id, 'if ifdef ifndef endif else') then
1065compile endif
1066 SearchStr = '^![ \t]*\c(if((n?def)?)|endif)([ \t]|$)'
1067 clist = leftstr( id, 1)
1068 fForward = (clist = 'i')
1069 if fForward then -- move to beginning
1070 .col = 1
1071 else -- move to end, so first Locate will hit this instance.
1072 .col = endcol
1073 endif
1074 fIntermediate = (substr( id, 2, 1) = 'l')
1075
1076compile if USE_NMAKE32
1077 elseif wordpos( id, 'foreach endfor') then -- NMAKE32
1078 SearchStr = '^![ \t]*\c(foreach|endfor)([ \t]|$)'
1079 clist = leftstr( id, 1)
1080 fForward = (clist = 'f')
1081 if fForward then -- move to beginning
1082 .col = 1
1083 else -- move to end, so first Locate will hit this instance.
1084 .col = endcol
1085 endif
1086 fIntermediate = (wordpos( id, 'else elif elseif') > 0)
1087compile endif
1088
1089 else
1090 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1091 endif
1092 else
1093 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1094 endif
1095
1096 -- Mode(s): Pascal --------------------------------------------------------------------------
1097 elseif CurMode = 'PASCAL' then
1098
1099 case = 'c' -- case insensitive for PASCAL
1100 id = lowcase( id)
1101 PascalStartBlockTokens = 'begin record try asm case unit object class' -- interface?
1102
1103 if wordpos( id, PascalStartBlockTokens 'end except finally') then
1104 SearchStr = '(^|[^a-zA_Z0-9_])\c('translate( PascalStartBlockTokens, '|', ' ')'|end)([;. \t]|$)'
1105 fForward = (wordpos( id, PascalStartBlockTokens) > 0)
1106 clen = 3
1107 if fForward then
1108 .col = startcol
1109 clist = ''
1110 do i = 1 to words( PascalStartBlockTokens)
1111 clist = clist leftstr( word( PascalStartBlockTokens, i), 3)
1112 enddo
1113 else
1114 .col = endcol
1115 clist = 'end'
1116 endif
1117 fIntermediate = (id = 'except' | id = 'finally')
1118
1119 elseif wordpos( id, 'repeat until') then
1120 SearchStr = '(^|[ \t])\c(repeat|until)([ \t]|$)'
1121 fForward = (id = 'repeat')
1122 if fForward then
1123 .col = startcol
1124 clist = 'r'
1125 else
1126 .col = endcol
1127 clist = 'u'
1128 endif
1129
1130 elseif wordpos( id, 'while for') then
1131 -- Check for begin before ';' (i.e a block loop instead of a
1132 -- single statement loop)
1133 setsearch 'xcom l /[ \t]\cdo([ \t]|$)/x'case'c+F' -- find the end of following 'do'
1134 clist = 'd'
1135 coffset = 0
1136 clen = 1
1137 nestlevel = -1 -- -1 = stop on first 'hit'
1138 assist_rc = AssistSearch( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData)
1139 do once2 = 1 to 1
1140 if assist_rc then -- no 'do'??
1141 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1142 leave
1143 endif
1144 .col = .col + 2
1145 nextchar = NextCodeChar( CurMode)
1146 if not nextchar then
1147 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1148 leave
1149 endif
1150 do once3 = 1 to 1
1151 --#dprintf( "pAssist", "while/for 'do' SearchStr found: "nextchar)
1152 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- assume bad
1153 if lowcase( nextchar) <> 'b' then
1154 leave
1155 endif
1156 tmpline = textline( .line)
1157 tmpline_len = length( tmpline)
1158 --#dprintf( "pAssist", "col: ".col "len: "tmpline_len "line: "tmpline)
1159 if tmpline_len < .col + 4 then
1160 leave
1161 endif
1162 if lowcase( substr( tmpline, .col, 5)) <> 'begin' then
1163 leave
1164 endif
1165 if (tmpline_len > .col + 4) then
1166 nextchar = substr( tmpline, .col + 5, 1)
1167 --#dprintf( "pAssist", "charafter: '"nextchar"'")
1168 if nextchar = ' ' | nextchar = \9 then
1169 assist_rc = ASSIST_RC_NO_ERROR
1170 endif
1171 else
1172 assist_rc = ASSIST_RC_NO_ERROR
1173 endif
1174 if not assist_rc then
1175 SearchStr = '(^|[ \t])\cbegin|end([;. \t]|$)'
1176 fForward = 1
1177 clist = 'b'
1178 endif
1179 enddo
1180 enddo
1181 else
1182 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1183 endif
1184
1185 -- Mode(s): TEX -----------------------------------------------------------------------------
1186 elseif CurMode = 'TEX' then
1187
1188 -- NOTE: There is some special code for \begin and \end which depends
1189 -- on 'clist' being set to 'be' or 'en'. If it becomes necessary
1190 -- to use these values for 'clist' for other TEX tokens, then the
1191 -- special code for \begin and \end will break.
1192 coffset = 1 -- omit the leading '\' in words of clist
1193
1194 -- //PM additions: balanceable tokens for (La)TeX
1195
1196 ---- TeX conditions: \if, \else, \fi
1197 if substr( id, 1, 3) = '\if' | wordpos( id, '\else \fi') then --// \if.. \else \fi
1198 SearchStr = '\\(if|fi)'
1199 clist = substr( id, 2, 1)
1200 fForward = (clist = 'i') -- fForward=1: forward, fForward=0 backward search
1201 if fForward then -- move cursor so that the first Locate will hit this instance
1202 .col = startcol -- \if: move to beginning
1203 else
1204 .col = endcol -- \else,\fi: move to end
1205 endif
1206 fIntermediate = (id = '\else')
1207
1208 ---- TeX environment: \begin..., \end...
1209 elseif id = '\begin' | id = '\end' then --// \begin.. \end..
1210 SearchStr = '\\(begin|end)[ \t]*'
1211 ---- LaTeX environment: \begin{...}, \end{...}
1212 .col = endcol
1213 if NextCodeChar( CurMode) == '{' then
1214 --#dprintf( 'pAssist', 'TEX: found } at '.line .col)
1215 temp = substr( textline( .line), .col)
1216 p = pos( '}', temp)
1217 if p > 0 then
1218 TexEnvStr = leftstr( temp, p)
1219 endif
1220 endif
1221 call pRestore_Pos( savepos)
1222 clist = substr( id, 2, 2)
1223 clen = 2
1224 fForward = (clist = 'be')
1225 if fForward then
1226 .col = startcol
1227 else
1228 .col = endcol
1229 endif
1230
1231 elseif id = '\bgroup' | id = '\egroup' then
1232 SearchStr = '\\(bgroup|egroup)'
1233 clist = substr( id, 2, 1)
1234 fForward = (clist = 'b')
1235 if fForward then
1236 .col = startcol
1237 else
1238 .col = endcol
1239 endif
1240
1241 elseif id = '\begingroup' | id = '\endgroup' then
1242 SearchStr = '\\(begingroup|endgroup)'
1243 clist = substr( id, 2, 1)
1244 fForward = (clist = 'b')
1245 if fForward then
1246 .col = startcol
1247 else
1248 .col = endcol
1249 endif
1250
1251 elseif id = '\makeatletter' | id = '\makeatother' then
1252 SearchStr = '\\makeat(letter|other)'
1253 clist = substr( id, 8, 1)
1254 fForward = (clist = 'l')
1255 if fForward then
1256 .col = startcol
1257 else
1258 .col = endcol
1259 endif
1260 coffset = 7
1261
1262 elseif id = '\[' | id = '\]' then
1263 SearchStr = '\\(\[|\])'
1264 clist = substr( id, 2, 1)
1265 fForward = (clist = '[')
1266 if fForward then
1267 .col = startcol
1268 else
1269 .col = endcol
1270 endif
1271
1272 elseif id = '\(' | id = '\)' then
1273 SearchStr = '\\(\(|\))'
1274 clist = substr( id, 2, 1)
1275 fForward = (clist = '(')
1276 if fForward then
1277 .col = startcol
1278 else
1279 .col = endcol
1280 endif
1281
1282 ---- TeX math
1283 elseif wordpos( id, '\left \right') then
1284 SearchStr = '\\(left|right)'
1285 clist = substr( id, 2, 1)
1286 fForward = (clist = 'l')
1287 if fForward then
1288 .col = startcol
1289 else
1290 .col = endcol
1291 endif
1292
1293 ---- TeX environment: \begin{env}, \end{env} when cursor is on {env}
1294 else
1295 fEnvFound = 0
1296 if NextCodeChar( CurMode, .line, endcol) == '}' & NextCodeChar( CurMode, .line, startcol, '-R') == '{' then
1297 -- Find \begin or \end before {env}
1298 call NextCodeChar( CurMode, '', '', '-R')
1299 rcx = Find_Token( bestartcol, beendcol, seps, 'NONE:')
1300 if rcx then
1301 beLineStr = ( textline( .line))
1302 if bestartcol > 1 then
1303 beprevchar = substr( beLineStr, bestartcol - 1, 1)
1304 if beprevchar = '\' then
1305 bestartcol = bestartcol - 1
1306 SearchStr = '\\(begin|end)[ \t]*'
1307 TexEnvStr = '{'id'}'
1308 clist = substr( beLineStr, bestartcol + 1, 2)
1309 clen = 2
1310 fForward = (clist = 'be')
1311 if fForward then
1312 .col = bestartcol - 1
1313 else
1314 .col = beendcol
1315 endif
1316 fEnvFound = 1
1317 endif
1318 endif
1319 endif
1320 endif
1321
1322 if not fEnvFound then -- not a known balanceable 'TEX' token
1323 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1324 endif
1325 endif
1326
1327 -- Mode(s): FORTRAN77 -----------------------------------------------------------------------
1328 elseif CurMode = 'FORTRAN77' then
1329
1330compile if 1
1331 -- This code disregards the "token" and allows the cursor anywhere
1332 -- within columns 7 ... 72
1333 if .col > 6 & .col < 73 then
1334 statement = Fortran77ExtractText( .line)
1335 do i = .line + 1 to .last
1336 temp = textline( i)
1337 if pos( substr( temp, 6, 1), ' 0') then
1338 leave
1339 elseif not pos( leftstr( temp, 1), 'C*') then
1340 statement = statement''Fortran77ExtractText( i)
1341 endif
1342 enddo
1343 statement = Fortran77RemoveSpaces( statement)
1344
1345 if leftstr( statement, 2) = 'DO' then
1346 equalpos = pos( '=', statement, 4)
1347 commapos = pos( ',', statement, 6)
1348 if equalpos & commapos then -- DO loop of some kind?
1349 fForward = 1
1350 clist = 'D'
1351 .col = pos( 'D', LineStr, 7)
1352 p = verify( statement, '0123456789', 'N', 3)
1353 if p > 3 then -- DO <labelnum>,... loop
1354 label = substr( statement, 3, p - 3)
1355 --#dprintf( 'pAssist', 'label as string: 'label)
1356 label = label + 0
1357 --#dprintf( 'pAssist', 'label as number: 'label)
1358 temp = ''
1359 do i = length( label) to 4
1360 temp = temp'[ 0]?'
1361 enddo
1362 SearchStr = '^('temp''label'[ 0]?[ ]*\c[^0])'
1363 --#dprintf( "pAssist", "DO <label> search: "SearchStr)
1364 'xcom l /'SearchStr'/x'
1365 if rc = 0 then
1366 assist_rc = -1
1367 else
1368 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1369 endif
1370 --#dprintf( 'pAssist', 'F77 search rc: 'rc)
1371 else
1372 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1373 endif
1374 else
1375 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1376 endif
1377
1378 else
1379 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1380 endif
1381 else
1382 assist_rc = ASSIST_RC_BAD_FORTRAN77_CURSOR -- Cursor before col 7 or after col 72
1383 endif
1384
1385compile else
1386
1387 -- This code disregards the "token" and allows the cursor anywhere
1388 -- within columns 7 ... 72
1389 if .col > 6 & .col < 73 then
1390 statement = Fortran77ExtractText( .line)
1391 do i = .line + 1 to .last
1392 temp = textline( i)
1393 if pos( substr( temp, 6, 1), ' 0') then
1394 leave
1395 elseif not pos( leftstr( temp, 1), 'C*') then
1396 statement = statement''Fortran77ExtractText( i)
1397 endif
1398 enddo
1399 statement = Fortran77RemoveSpaces( statement)
1400
1401 if leftstr( statement, 2) = 'DO' then
1402 equalpos = pos( '=', statement, 4)
1403 commapos = pos( ',', statement, 6)
1404 if equalpos & commapos then -- DO loop of some kind?
1405 fForward = 1
1406 clist = 'D'
1407 .col = pos( 'D', LineStr, 7)
1408 p = verify( statement, '0123456789', 'N', 3)
1409 if p > 3 then -- DO <labelnum>,... loop
1410 label = substr( statement, 3, p - 3)
1411 --#dprintf( 'pAssist', 'label as string: 'label)
1412 label = label + 0
1413 --#dprintf( 'pAssist', 'label as number: 'label)
1414 temp = ''
1415 do i = length( label) to 4
1416 temp = temp'[ 0]?'
1417 enddo
1418 SearchStr = '^('temp''label'[ 0]?[ ]*\c[^0])'
1419 --#dprintf( "pAssist", "DO <label> search: "SearchStr)
1420 'xcom l /'SearchStr'/x'
1421 if rc = 0 then
1422 assist_rc = -1
1423 else
1424 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1425 endif
1426 --#dprintf( 'pAssist', 'F77 search rc: 'rc)
1427 -- DO/ENDDO is not FORTRAN77 but the code (esp. the regex
1428 -- search string) for future use
1429 else -- DO var = xx,limit loop
1430 SearchStr = '^[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].[ ]*\c((D[ ]*O[ ]*[A-Z][A-Z0-9]*[ ]*=.+,)|(E[ ]*N[ ]*D[ ]*D[ ]*O))'
1431 endif
1432 else
1433 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1434 endif
1435
1436 -- o FORTRAN77 support (beyond the generic ()[]{}-matching and
1437 -- do <label> ...) is disabled.
1438 -- o The code below tries to match "Block" IFs (i.e.
1439 -- IF(<condiiton>)THEN ... ENDIF)
1440 -- o But FORTRAN allows statements to be contiued. This means
1441 -- that for "IF (confition) THEN" the IF and the THEN might
1442 -- be on different lines.
1443 -- o For long conditions, the use of continuations can make
1444 -- stylistic sense. For example:
1445 -- IF( CARDS(I)(2:2) .EQ. 'J'
1446 -- 1 .OR. CARDS(I)(2:2) .EQ. 'Q'
1447 -- 2 .OR. CARDS(I)(2:2) .EQ. 'K' )THEN
1448 -- o Unless grep searches start supporting searches across
1449 -- multiple lines, the only way to implement matching with
1450 -- this kind of code is
1451 -- 1) Create a temporary file
1452 -- 2) Rewrite the original file in to the temp file, "merging"
1453 -- the continuation lines in the process
1454 -- 3) Somehow keep track of which columns of the merged lines
1455 -- came from which lines and columns of the original file
1456 -- 4) Perform a search on the temp file
1457 -- 5) Use the data from #3 to map the location of the "found"
1458 -- string back into the original file
1459 -- 6) Discard the temp file
1460 -- o At this time the effort need to make this work was deemed
1461 -- not worth the "value" of matching
1462
1463 -- "Block" IFs in FORTRAN.
1464 elseif leftstr( statement, 3) = 'IF(' then
1465 if pos( ')THEN', statement) then -- Assume ')THEN' is NOT in a literal?
1466 SearchStr = '^[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].[ ]*\c((I[ ]*F[ ]*(.*)[ ]*T[ ]*H[ ]*E[ ]*N)|(E[ ]*N[ ]*D[ ]*I[ ]*F))'
1467 fForward = 1
1468 clist = 'I'
1469 .col = pos( 'I', LineStr, 7)
1470 else
1471 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- IF without THEN is unbalanceable
1472 endif
1473
1474 elseif leftstr( statement, 7) = 'ELSEIF(' then
1475 if pos( ')THEN', statement) then -- Assume ')THEN' is NOT in a literal?
1476 SearchStr = '^[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].[ ]*\c((I[ ]*F[ ]*(.*)[ ]*T[ ]*H[ ]*E[ ]*N)|(E[ ]*N[ ]*D[ ]*I[ ]*F))'
1477 fForward = 0
1478 fIntermediate = 1
1479 clist = 'E'
1480 .col = pos( 'E', LineStr, 7)
1481 else
1482 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN -- ELSEIF without THEN is unbalanceable
1483 endif
1484
1485 elseif statement = 'ENDIF' then
1486 SearchStr = '^[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9][ 0]+\c((I[ ]*F[ ]*\(.*\)T[ ]*H[ ]*E[ ]*N)|(E[ ]*N[ ]*D[ ]*I[ ]*F))'
1487 fForward = 0
1488 clist = 'E'
1489 .col = pos( 'E', LineStr, 7)
1490
1491 -- FORTRAN77 does not require END of program/subroutine/function
1492 -- statements, nor is a PROGRAM statement required
1493 elseif not pos( '=', statement) & statement <> '' then -- remaining matchable tokens require this
1494 function_pos = pos( 'FUNCTION', statement)
1495 punc_pos = verify( statement, "+-*/=().,':$!", 'M')
1496 function_statement = (function_pos > 0 & (punc_pos = 0 | function_pos < punc_pos))
1497 if leftstr( statement, 7) = 'PROGRAM' |
1498 leftstr( statement, 10) = 'SUBROUTINE' |
1499 leftstr( statement, 9) = 'BLOCKDATA' |
1500 function_statement |
1501 strip( statement) = 'END' then
1502 SearchStr = '^[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].[ ]*((('
1503 SearchStr = SearchStr'(\cP[ ]*R[ ]*O[ ]*G[ ]*R[ ]*A[ ]*M)|'
1504 SearchStr = SearchStr'(\cS[ ]*U[ ]*B[ ]*R[ ]*O[ ]*U[ ]*T[ ]*I[ ]*N[ ]*E)|'
1505 SearchStr = SearchStr'(\cB[ ]*L[ ]*O[ ]*C[ ]*K[ ]*D[ ]*A[ ]*T[ ]*A)|'
1506 SearchStr = SearchStr'([A-Z][A-Z0-9]*[ ]*\cF[ ]*U[ ]*N[ ]*C[ ]*T[ ]*I[ ]*O[ ]*N))'
1507 SearchStr = SearchStr'[ ]*(~[=])*)|(\cE[ ]*N[ ]*D[ ]*$))'
1508 fForward = (strip( statement) <> 'END')
1509 if function_statement then
1510 .col = pos( 'FUNCTION', LineStr, 7)
1511 else
1512 .col = verify( LineStr, ' ', 'N', 7)
1513 endif
1514 if fForward then
1515 clist = 'P S B F'
1516 else
1517 clist = 'E'
1518 endif
1519 else
1520 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1521 endif
1522
1523 else
1524 assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1525 endif
1526 else
1527 assist_rc = ASSIST_RC_BAD_FORTRAN77_CURSOR -- Cursor before col 7 or after col 72
1528 endif
1529compile endif
1530
1531 -- End of mode defs -------------------------------------------------------------------------
1532
1533 -- For all modes: SearchStr may be a bracket
1534 elseif SearchStr = '' then -- not a known balanceable token or a mode that is not yet supported
1535 assist_rc = ASSIST_RC_MODE_NOT_SUPPORTED
1536 endif
1537
1538 enddo -- do once = 1 to 1
1539
1540 -- Search logic:
1541 -- o The first search finds the string at the cursor.
1542 -- o From the second search on, it is searched for the matching string
1543 -- of the previous search.
1544 -- o Note that the extended grep option is used. The var SearchStr
1545 -- contains the matching start and end strings with the egrep syntax.
1546 -- o The string at the cursor is highlighted.
1547 -- o For an 'else' or 'elseif' condition string at the cursor: the
1548 -- start string of the condition is highlighted.
1549 -- o The cursor is placed at the matching string of the highlighted one.
1550 if not assist_rc then
1551 if fForward then
1552 direction = '+F'
1553 else
1554 direction = '-R'
1555 endif
1556 SearchCall = 'xcom l '\1''SearchStr\1'x'case''direction
1557 --#dprintf( "pAssist", "id clist fForward fIntermediate:" id "'"clist"'" fForward fIntermediate)
1558 --#dprintf( "pAssist", 'search: 'SearchCall)
1559
1560 if fIntermediate then -- if cursor is on 'else' or 'elseif' etc.
1561 -- Search begin of condition
1562 setsearch SearchCall
1563
1564 HighlightLine = .line
1565 if hcol > 0 then
1566 HighlightCol = hcol
1567 endif
1568 if hlen > 0 then
1569 HighlightLen = hlen
1570 endif
1571 else
1572 -- This is the first search
1573 SearchCall
1574
1575 -- Workaround for missing col at highlight_match call:
1576 -- Compose the CircleIt data already here and submit it to
1577 -- Highlight_Match.
1578 HighlightLine = .line
1579 HighlightCol = getpminfo( EPMINFO_SEARCHPOS)
1580 HighlightLen = getpminfo( EPMINFO_LSLENGTH)
1581 if HighlightLen then
1582 -- If the SearchStr includes spaces or MLCs as separator, then
1583 -- HighlightCol or HighlightLen are too wide.
1584 -- This tries to split at least the leading chars. Therefore
1585 -- the cursor pos in the regex must be set to the start of
1586 -- the token:
1587 if .col > HighlightCol then
1588 HighlightLen = Max( 0, HighlightLen - (.col - HighlightCol)) -- must come before changing HighlightCol
1589 HighlightCol = .col
1590 endif
1591 -- (The cursor offset (coffset) has to be adjusted, if the
1592 -- token includes leading e.g. '\' or ':' chars while the
1593 -- search strings in clist don't.)
1594 -- Other spaces are stripped with the fStripBlanks option of
1595 -- Highlight_Match.
1596 endif
1597 --dprintf( 'pAssist: SearchCall 1 = 'SearchCall', rc = 'rc', line col len = 'HighlightLine HighlightCol HighlightLen', prevline prevcol = 'HighlightLine HighlightCol)
1598 endif
1599
1600 -- This is the search loop from the second search on. It also
1601 -- o checks if the cursor is within a comment or a literal,
1602 -- o alters nestlevel, the counter for matching expressions,
1603 -- o checks for too many processed lines and
1604 -- o does additional search for the modes E and TEX.
1605 assist_rc = AssistSearch( CurMode, clist, case, coffset, clen, nestlevel, SearchLimitData, TexEnvStr, fEExcludeCompile)
1606 endif -- if OK to search
1607
1608 if assist_rc > 0 then
1609 call pRestore_Pos( savepos)
1610 if VerboseLevel <> 0 then
1611 if assist_rc = ASSIST_RC_IN_ONELINE_COMMENT then
1612 sayerror "Invalid: One-line comment starts in column "CommentData
1613 elseif assist_rc = ASSIST_RC_IN_MULTILINE_COMMENT then
1614 sayerror "Invalid: Multi-line comment starting at "CommentStartLine","CommentStartCol" and ending at "CommentEndLIne","CommentEndCol
1615 elseif assist_rc = ASSIST_RC_IN_LITERAL then
1616 sayerror "Invalid: Cursor located within a literal."
1617 elseif assist_rc = ASSIST_RC_NOT_ON_A_TOKEN then
1618 sayerror "Invalid: Cursor is not located on a token."
1619 elseif assist_rc = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN then
1620 sayerror UNBALANCED_TOKEN__MSG
1621 elseif assist_rc = ASSIST_RC_MODE_NOT_SUPPORTED then
1622 sayerror "Token balancing not yet supported for mode: "CurMode
1623 elseif assist_rc = ASSIST_RC_TOO_MANY_LINES then
1624 sayerror "Assist: More than "MaxLines" lines reached. Further processing might get slow."
1625 elseif assist_rc = ASSIST_RC_TOO_MANY_LOOPS then
1626 sayerror "Assist: More than "MaxLoops" times searched. Further processing might get slow."
1627 else
1628 sayerror "Unknown rc: "assist_rc
1629 endif
1630 endif
1631 else
1632 if VerboseLevel <> 0 then
1633 sayerror 1
1634 endif
1635
1636 call pSave_Pos( lpos)
1637 -- Save the found position as universal
1638 lastassistpos = HighlightLine HighlightCol HighlightLen
1639 if fHighlight then
1640 if pos( '-', direction) > 0 then
1641 ScrollToTopBot = 'T'
1642 else
1643 ScrollToTopBot = 'B'
1644 endif
1645 call ScrollAfterLocate( savepos, lpos, ScrollToTopBot, '-4 -4')
1646
1647 -- At this time, getpminfo( EPMINFO_SEARCHPOS) has the value 0, not
1648 -- the col after the last search. Additionally, the second search
1649 -- was executed, so the position data of the first search has
1650 -- changed. Therefore submit the data from after the starting seach
1651 -- to CircleIt as arg:
1652 fStripBlanks = 1
1653 call Highlight_Match( HighlightLine HighlightCol HighlightLen, fStripBlanks)
1654 endif
1655 endif
1656
1657 -- Invalidation of the MLC array was moved to NextCmdAltersText().
1658 -- The MLC array is now re-used until a command or a key is executed that
1659 -- calls NextCmdAltersText().
1660
1661 -- Restores user's command so Ctrl-F works.
1662 setsearch search_command
1663
1664 return assist_rc
1665
1666; ---------------------------------------------------------------------------
1667; Performs the actual search for the matching token (which is NOT located
1668; within a comment or literal).
1669defproc AssistSearch( Mode, clist, case, coffset, clen, nestlevel, SearchLimitData)
1670 parse value SearchLimitData with StartLineNum MaxLines MaxLoops
1671 TexEnvStr = arg( 8)
1672 fEExcludeCompile = arg( 9)
1673 if fEExcludeCompile = '' then
1674 fEExcludeCompile = 0
1675 endif
1676 retval = 0
1677 getsearch ThisSearch
1678 fForward = (rightstr( ThisSearch, 2) = '+F')
1679 LoopCount = 1 -- The first search is done by xcom l
1680
1681 -- This is the search loop from the second search on.
1682 do forever
1683 --#dprintf( "pAssist", "- - - - -")
1684 --#dprintf( "pAssist", "before search pos: ".line",".col "nestlevel = "nestlevel)
1685 display -8 -- suppress writing to MsgBox
1686 repeatfind
1687 lrc = rc
1688 display 8
1689
1690 if lrc then
1691 retval = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1692 --#dprintf( 'pAssist', 'AssistSearch: rc = 'lrc)
1693 leave
1694 endif
1695
1696 --#dprintf( "pAssist", "Match at ".line",".col)
1697 if InsideComment( Mode) then
1698 --#dprintf( 'pAssist', 'InsideComment')
1699 iterate
1700 endif
1701 if InsideLiteral( Mode) then
1702 --#dprintf( 'pAssist', 'InsideLiteral')
1703 iterate
1704 endif
1705
1706 -- Mode E: Omit conditions with a preceding 'compile', except for
1707 -- conditions that include 'compile' in the search string
1708 if Mode = 'E' & fEExcludeCompile then
1709 -- if|else|elseif|endif (without compile)
1710 LineStr = textline( .line)
1711 leftline = lowcase( leftstr( LineStr, .col - 1))
1712 pcompile = pos( 'compile:w', leftline, 1, 'x')
1713 if pcompile & pcompile <= .col then
1714 iterate
1715 endif
1716 endif
1717
1718 --#dprintf( "pAssist", "line# ".line" col: ".col" text: "textline( .line))
1719 cword = substr( textline( .line), .col + coffset, clen)
1720 if case = 'c' then
1721 cword = lowcase( cword)
1722 endif
1723 --#dprintf( "pAssist", "CList: "clist "Cword: "cword "Coffset: "coffset "CLen: "clen)
1724 if wordpos( cword, clist) then
1725 nestlevel = nestlevel + 1
1726 else
1727 nestlevel = nestlevel - 1
1728 endif
1729
1730 --#dprintf( "pAssist", 'after nestlevel = 'nestlevel)
1731 --retval = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN * (rc = sayerror('String not found'))
1732 if nestlevel = 0 then
1733 leave
1734 endif
1735
1736 -- Check number of searched lines
1737 if fForward then
1738 if .line > StartLineNum + MaxLines then
1739 retval = ASSIST_RC_TOO_MANY_LINES
1740 leave
1741 endif
1742 else
1743 if .line < StartLineNum - MaxLines then
1744 retval = ASSIST_RC_TOO_MANY_LINES
1745 leave
1746 endif
1747 endif
1748
1749 -- Check number of searched loops
1750 LoopCount = LoopCount + 1
1751 if LoopCount >= MaxLoops then
1752 retval = ASSIST_RC_TOO_MANY_LOOPS
1753 leave
1754 endif
1755 enddo
1756
1757 if retval = 0 & Mode == 'TEX' & (clist = 'be' | clist = 'en') then
1758 call psave_pos( texsavepos)
1759 if clist = 'be' then
1760 .col = .col + 3
1761 else
1762 .col = .col + 5
1763 endif
1764 nextchar = NextCodeChar( Mode)
1765 --#dprintf( "pAssist", "TEX env: "TexEnvStr" NextChar: "nextchar)
1766 if TexEnvStr <> '' then
1767 if nextchar == '{' then
1768 --#dprintf( "pAssist", "TEX env found { at ".line .col)
1769 if substr( textline( .line), .col, length( TexEnvStr)) == TexEnvStr then
1770 --#dprintf( "pAssist", "TEX env matched")
1771 else
1772 retval = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1773 endif
1774 else
1775 retval = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1776 endif
1777 else
1778 if nextchar = '{' then
1779 retval = ASSIST_RC_NOT_ON_A_BALANCEABLE_TOKEN
1780 endif
1781 endif
1782 call prestore_pos( texsavepos)
1783 endif
1784 return retval
1785
1786; ---------------------------------------------------------------------------
1787; In FORTRAN the text of interest to pAssist is located only in columns
1788; 7 ... 72. (An exception is when FORTRAN90-style SLCs are supported.)
1789; This routine extracts and returns the text of interest to the pAssist
1790; routine.
1791defproc Fortran77ExtractText( linenum)
1792 text = substr( textline( linenum), 7, 66) -- columns 7 ... 72
1793compile if 0 /* USE_FORTRAN90_SLC = 1 */
1794 if pos( "!", text) then
1795 call psave_pos( savepos3)
1796 .line = linenum
1797 .col = 7
1798 setsearch 'xcom l /!/xe+F'
1799 Mode = 'FORTRAN90'
1800 clist = '!'
1801 case = 'e' -- 'e' = case-sensitive
1802 coffset = 0
1803 clen = 1
1804 nestlevel = -1 -- -1 = stop on first 'hit'
1805 fortran90_rc = AssistSearch( Mode, clist, case, coffset, clen, nestlevel)
1806 if not rc then
1807 if .line = linenum & .col > 6 then
1808 if .col = 7 then
1809 text = ''
1810 else
1811 text = substr( linenum, 7, .col - 6)
1812 endif
1813 endif
1814 endif
1815 call prestore_pos( savepos3)
1816 endif
1817compile endif
1818 return text
1819
1820; ---------------------------------------------------------------------------
1821; In FORTRAN, spaces, which are not located within a literal, are irrelevant.
1822; This routine removes these insignificant spaces, if any.
1823defproc Fortran77RemoveSpaces( text)
1824 do once = 1 to 1
1825 if not pos( ' ', text) then
1826 leave
1827 endif
1828
1829 getfileid fid
1830
1831 'xcom e .temp_fortran'
1832 insertline text
1833 p = 0
1834 .col = 1
1835 .line = 1
1836 do forever
1837 p = pos( ' ', textline( 1), p + 1)
1838 if p then
1839 .col = p
1840 if not InsideLiteral( 'FORTRAN77') then
1841 delete_char
1842 p = 0
1843 .col = 1
1844 endif
1845 else
1846 leave
1847 endif
1848 enddo
1849 getline text
1850 .modify = 0
1851 'xcom quit'
1852
1853 activatefile fid
1854
1855 enddo
1856 return text
1857
1858; ---------------------------------------------------------------------------
1859; Determines if the cursor is within a comment or within a literal.
1860; Return values:
1861; 0: the cursor is NOT within a comment NOR within a literal.
1862; other: the cursor IS within a comment or within a literal.
1863defproc InsideCommentLiteral( Mode)
1864 CurLine = arg( 2)
1865 if CurLine = '' then
1866 CurLine = .line
1867 endif
1868 CurCol = arg( 3)
1869 if CurCol = '' then
1870 CurCol = .col
1871 endif
1872 do once = 1 to 1
1873 rc = InsideComment( Mode, CurLine, CurCol)
1874 if rc <> 0 then
1875 leave
1876 endif
1877 rc = InsideLiteral( Mode, CurLine, CurCol)
1878 enddo
1879 return rc
1880
1881; ---------------------------------------------------------------------------
1882; A front-end call to InsideLiteral2. This is used when the location of the
1883; literal, returned by InsideLiteral2, is not needed.
1884defproc InsideLiteral( Mode)
1885 CurLine = arg( 2)
1886 if CurLine = '' then
1887 CurLine = .line
1888 endif
1889 CurCol = arg( 3)
1890 if CurCol = '' then
1891 CurCol = .col
1892 endif
1893 return InsideLiteral2( Mode, dummy, CurLine, CurCol)
1894
1895; ---------------------------------------------------------------------------
1896; Determines if the cursor is within a literal. Unlike InsideCommentLiteral,
1897; this routine does not first determine if the cursor is within a comment.
1898; This is called by InsideCommentLiteral and can be
1899; called directly if the test for the presence of a comment is unneeded.
1900; Return value:
1901; 0: cursor is NOT within a literal
1902; and LitData is meaningless.
1903; 1: cursor IS within a literal
1904; and LitData is set to 2 blank-separated words:
1905; o the start col and end col of the literal.
1906defproc InsideLiteral2( Mode, var LitData)
1907 CurLine = arg( 3)
1908 if CurLine = '' then
1909 CurLine = .line
1910 endif
1911 CurCol = arg( 4)
1912 if CurCol = '' then
1913 CurCol = .col
1914 endif
1915 --#dprintf( "lit2", "Entry: "CurLine CurCol)
1916 retval = 0
1917 call psave_pos( savepos)
1918 getsearch search_command -- Save caller's search command.
1919 do once = 1 to 1
1920 LineStr = textline( CurLine)
1921 fexit = 0
1922 FoundStartLitCol = 0
1923 FoundEndLitCol = 0
1924 LitData = FoundStartLitCol FoundEndLitCol
1925 parse value GetLitChars( Mode) with StartLitChars EndLitChars EscapeChars
1926 if StartLitChars = '' then
1927 leave
1928 endif
1929 if LineStr = '' then
1930 -- Start and end not on the same line: This can't be a literal
1931 leave
1932 endif
1933
1934 EndLitCol = 0
1935 do forever
1936 StartCol = EndLitCol + 1
1937 next = verify( LineStr, StartLitChars, 'M', StartCol) -- find first start-of-literal
1938 --#dprintf( "lit2", "next CurCol LineStr: "next CurCol LineStr)
1939 if not next then -- if none, exit
1940 leave
1941 elseif next > CurCol then -- if past cursor position, exit
1942 leave
1943 endif
1944 StartLitCol = next
1945 FoundStartLitCol = StartLitCol
1946 startq = substr( LineStr, StartLitCol, 1) -- extract start-of-literal char
1947 qpos = pos( startq, StartLitChars) -- determine which start-of-literal char
1948 escapechar = substr( EscapeChars, qpos, 1) -- select matching escape char
1949 endq = substr( EndLitChars, qpos, 1) -- select matching end-of-literal char
1950 EndLitCol = StartLitCol
1951
1952 do forever
1953 EndLitCol = verify( LineStr, endq''escapechar, 'M', EndLitCol + 1) -- find next end-of-literal or escape char
1954 --#dprintf( "lit2", "startq StartLitCol endq EndLitCol escapechar: "startq StartLitCol endq EndLitCol escapechar)
1955 if EndLitCol >= CurCol then -- JBSQ: Don't care if literal is properly closed?
1956 retval = 1
1957 leave
1958 elseif not EndLitCol then -- No end "quote"??
1959 --#dprintf( "lit2", "Unmatched start-of-literal character: "startq "at "CurLine","StartLitCol)
1960 --retval = 1 -- JBSQ: Return true on unmatched "quote"?
1961 -- Better don't count the rest of the line as literal and leave retval = 0.
1962 fexit = 1
1963 leave
1964 elseif endq = escapechar then -- escape "quote"s case 1: doubled "quotes"
1965 if length( LineStr) > EndLitCol then
1966 if substr( LineStr, EndLitCol + 1, 1) = endq then -- doubled-"quote" escape sequence?
1967 EndLitCol = EndLitCol + 1 -- "jump" past doubled "quote"
1968 --#dprintf( "lit2", "Doubled-quote")
1969 iterate
1970 else -- not escaped and EndLitCol < CurCol
1971 leave -- literal starts and ends before cursor col
1972 endif
1973 else -- end-of-literal at end-of-line
1974 retval = 1
1975 leave
1976 endif
1977 elseif substr( LineStr, EndLitCol, 1) = escapechar then -- escaped char
1978 -- JBSQ: Don't care which char and assume not at end of line?
1979 EndLitCol = EndLitCol + 1
1980 --#dprintf( "lit2", "Escaped char")
1981 iterate
1982 else -- EndLitCol > 0 and EndLitCol < CurCol, i.e. literal starts and ends before CurCol
1983 leave
1984 endif
1985 enddo
1986
1987 if retval | fexit then
1988 leave
1989 endif
1990 enddo
1991
1992 FoundEndLitCol = EndLitCol
1993 LitData = FoundStartLitCol FoundEndLitCol
1994 enddo -- once
1995 setsearch search_command -- Restores user's command so Ctrl-F works.
1996 call prestore_pos( savepos)
1997 --#dprintf( "lit2", "Exit: "retval .line .col)
1998 return retval
1999
2000; ---------------------------------------------------------------------------
2001; A routine which, given a mode, returns the start-of-literal, end-of-literal
2002; and escape characters for that mode.
2003; The return value is three "words":
2004; o Each character of the first "word" is a start-of-literal character.
2005; o The corresponding character in the second "word" is the corresponding
2006; end-of-literal character.
2007; o The third "word" is a list of the escape characters, if any, which
2008; allow a literal to include a start-of-literal, end-of-literal or itself
2009; in a literal.
2010defproc GetLitChars( Mode)
2011 SingleQuote = "'"
2012 DoubleQuote = '"'
2013 StartLitChars = DoubleQuote''SingleQuote
2014 EndLitChars = StartLitChars
2015 EscapeChars = '\\'
2016 if 0 then
2017 -- placeholder
2018 elseif wordpos( Mode, 'REXX E') then
2019 EscapeChars = StartLitChars
2020 elseif wordpos( Mode, 'MAKE RC WARPIN') then
2021 StartLitChars = DoubleQuote
2022 EndLitChars = StartLitChars
2023 if Mode = 'RC' then
2024 EscapeChars = '\\'
2025 else
2026 EscapeChars = \0\0 -- JBSQ: No escape chars for MAKE?
2027 endif
2028 elseif Mode = 'PERL' then
2029 StartLitChars = StartLitChars'`'
2030 EndLitChars = StartLitChars
2031 EscapeChars = '\\\'
2032; elseif Mode = 'ADA' then -- JBSQ: ADA strings and chars are default: (" " and ' ')?
2033; StartLitChars = DoubleQuote'%'
2034; EndLitChars = StartLitChars
2035; EscapeChars = \0\0 -- JBSQ: Escape Chars?
2036 elseif wordpos( Mode, 'DEF PASCAL FORTRAN77') > 0 then
2037 StartLitChars = SingleQuote
2038 EndLitChars = SingleQuote
2039 if Mode = 'DEF' then
2040 EscapeChars = \0
2041 else
2042 EscapeChars = SingleQuote
2043 endif
2044 -- other modes here
2045 endif
2046 return StartLitChars EndLitChars EscapeChars
2047
2048; ---------------------------------------------------------------------------
2049; A front-end call to InsideComment2. This is used when the location of the
2050; comment, returned by InsideComment2, is not needed.
2051defproc InsideComment( Mode)
2052 CurLine = arg( 2)
2053 if CurLine = '' then
2054 CurLine = .line
2055 endif
2056 CurCol = arg( 3)
2057 if CurCol = '' then
2058 CurCol = .col
2059 endif
2060 return InsideComment2( Mode, dummy, CurLine, CurCol)
2061
2062; ---------------------------------------------------------------------------
2063; Determines if the cursor is located within a comment, multi-line or
2064; single-line. The input is the mode and a "var" variable
2065; (named "CommentData") in which the location of the comment is returned
2066; to the caller.
2067; Return value:
2068; 0: cursor is NOT within a comment
2069; and CommentData is meaningless
2070; 1: cursor IS within a one-line comment
2071; and CommentData is set to the column of the start of one-line
2072; comment
2073; 2: cursor IS within a multi-line comment
2074; and CommentData is set to 6 blank-separated words:
2075; o The line, col and length of the starting MLC token and
2076; o the line, col and length of the ending MLC token.
2077defproc InsideComment2( Mode, var CommentData)
2078 CurLine = arg( 3)
2079 if CurLine = '' then
2080 CurLine = .line
2081 endif
2082 CurCol = arg( 4)
2083 if CurCol = '' then
2084 CurCol = .col
2085 endif
2086 display -2
2087 --#dprintf( "comm", "Entry: "CurLine","CurCol)
2088 call psave_pos( savepos)
2089 getsearch search_command -- Save caller's search command.
2090 retval = 0
2091 CommentData = ""
2092 LineStr = textline( CurLine)
2093 if QueryModeKey( Mode, 'CaseSensitive', '1') then
2094 MLCCase = 'e' -- case-sensitive (exact)
2095 else
2096 MLCCase = 'c' -- caseless
2097 endif
2098
2099 MLCData = LocateMLC( Mode, CurLine, CurCol)
2100 parse value MLCData with BestMLCStartLine BestMLCStartCol BestMLCStartLen BestMLCEndLine BestMLCEndCol BestMLCEndLen
2101 if BestMLCStartLine > 0 then
2102 retval = 2
2103 endif
2104 --#dprintf( "comm", "Retval on exit of outer MLC loop: "retval", cursor: ".line",".col)
2105
2106 if retval = 0 then
2107 SLCPosition = InsideSLC( Mode, CurLine, CurCol)
2108 CommentData = SLCPosition
2109 retval = (SLCPosition > 0)
2110 else
2111 CommentData = BestMLCStartLine BestMLCStartCol BestMLCStartLen BestMLCEndLine BestMLCEndCol BestMLCEndLen
2112 endif
2113
2114 setsearch search_command -- Restores user's command so Ctrl-F works.
2115 call prestore_pos( savepos)
2116 display 2
2117 --#dprintf( "comm", "MLC rc CommentData: "retval CommentData)
2118
2119 --dprintf( 'InsideComment2: retval = 'retval)
2120 return retval
2121
2122; ---------------------------------------------------------------------------
2123; A front-end call to LocateMLC. This is used when the location of the
2124; comment, returned by LocateMLC, is not needed.
2125defproc InsideMLC( Mode)
2126 CurLine = arg( 2)
2127 if CurLine = '' then
2128 CurLine = .line
2129 endif
2130 CurCol = arg( 3)
2131 if CurCol = '' then
2132 CurCol = .col
2133 endif
2134 retval = 0
2135 MLCData = LocateMLC( Mode, CurLine, CurCol)
2136 parse value MLCData with BestMLCStartLine BestMLCStartCol BestMLCStartLen BestMLCEndLine BestMLCEndCol BestMLCEndLen
2137 if BestMLCStartLine > 0 then
2138 retval = 1
2139 endif
2140
2141 --dprintf( retval' = InsideMLC( 'Mode', 'CurLine', 'CurCol'), Start 'MLCStart' = ('MLCStartLine','MLCStartCol'), End = 'MLCEnd' ('MLCEndLine','MLCEndCol')')
2142 return retval
2143
2144; ---------------------------------------------------------------------------
2145; Determines if provided location (line, col) is within a multi-line comment
2146; (MLC) for the provided mode. The return value is six space-separated
2147; numbers representing:
2148; - Start line of MLC
2149; - Start col of MLC
2150; - Length of the Start MLC token
2151; - End line of MLC
2152; - End col of MLC
2153; - Length of the End MLC token
2154; All zeroes indicate that the given line, col is NOT within an MLC.
2155; Binary search.
2156defproc LocateMLC( Mode, CurLine, CurCol)
2157 universal curkey
2158 universal prevkey
2159
2160 --dprintf( 'LocateMLC( 'Mode', 'CurLine', 'CurCol')')
2161 getfileid fid
2162 BestMLCStartLine = 0
2163 BestMLCStartCol = 0
2164 BestMLCStartLen = 0
2165 BestMLCEndLine = 0
2166 BestMLCEndCol = 0
2167 BestMLCEndLen = 0
2168 retval = 0
2169 -- i = counter for MLC pairs
2170 -- j = counter for MLC list items
2171
2172 -- Must query ModeMLCCount and set the other MLC vars here to handle
2173 -- modes with nested MLCs
2174 ModeMLCCount = GetAVar( 'assist.mode.'Mode'.MLC.0')
2175 if ModeMLCCount = '' then
2176 call GetMLCChars( Mode, MLCStartChars, MLCEndChars, MLCNestList, MLCEndPrioList)
2177 ModeMLCCount = words( MLCStartChars)
2178 call SetAVar( 'assist.mode.'Mode'.MLC.0', ModeMLCCount)
2179 do i = 1 to ModeMLCCount
2180 call SetAVar( 'assist.mode.'Mode'.MLCStart.'i, word( MLCStartChars, i))
2181 call SetAVar( 'assist.mode.'Mode'.MLCEnd.'i, word( MLCEndChars, i))
2182 call SetAVar( 'assist.mode.'Mode'.MLCNest.'i, word( MLCNestList, i))
2183 call SetAVar( 'assist.mode.'Mode'.MLCEndPrio.'i, word( MLCEndPrioList, i))
2184 enddo
2185 endif
2186
2187 --#dprintf( "array", "Extracted Starts: "MLCStartChars "Ends: "MLCEndChars "Nests: "MLCNestList "Count: "ModeMLCCount)
2188 fArrayValid = (GetAVar( fid'.assist.ArrayValid') = 1)
2189 --#dprintf( "array", "fArrayValid: '"fArrayValid"'")
2190 PrevModify = GetAVar( fid'.assist.Modify')
2191 fModifyChanged = (fid.modify <> PrevModify)
2192 parse value curkey with CurKeyString \1 CurCmd
2193 parse value prevkey with PrevKeyString \1 PrevCmd
2194 fSameCmd = (CurCmd = PrevCmd)
2195 -- For non-repeated keys rebuild the array when .modify has changed
2196 if not fSameCmd & fModifyChanged then
2197 fArrayValid = 0
2198 endif
2199
2200 fUseArray = GetAvar( 'assist.fUseArray')
2201 -- Modes with nested MLCs must use the array method
2202 fModeSupportsNestedMLCs = 0
2203 do i = 1 to ModeMLCCount
2204 MLCNest = GetAVar( 'assist.mode.'Mode'.MLCNest.'i)
2205 if MLCNest then
2206 fModeSupportsNestedMLCs = 1
2207 leave
2208 endif
2209 enddo
2210 if not fUseArray & fModeSupportsNestedMLCs then
2211 fUseArray = 1
2212 endif
2213 -- If a valid array exists, always prefer this method, because it's faster
2214 if fArrayValid then
2215 fUseArray = 1
2216 endif
2217
2218 if fUseArray then
2219 if not fArrayValid then
2220 -- Build MLC array
2221 --dprintf( 'BuildMLCArray( 'Mode'), '.filename', 'DateTime())
2222 call BuildMLCArray( Mode)
2223 --#dprintf( "array", "LocateMLC Mode: "Mode CurLine","CurCol "ModeMLCCount: "ModeMLCCount)
2224 endif
2225
2226 do i = 1 to ModeMLCCount
2227 MLCListcount = GetAVar( fid'.assist.'i'.List.0')
2228 --#dprintf( "array", "Listcount #"i": "MLCListCount)
2229 --dprintf( 'MLCListCount = 'MLCListCount', i = 'i', ModeMLCCount = 'ModeMLCCount)
2230 if MLCListCount = '' then
2231 MLCListCount = 0
2232 endif
2233
2234 -- Start values
2235 lval = 1
2236 rval = MLCListcount
2237 do forever
2238 if rval < lval then
2239 leave
2240 endif
2241 -- Next list item value
2242 j = (lval + rval) % 2
2243
2244 -- Query MLC array var value for j.
2245 MLCEntry = GetAVar( fid'.assist.'i'.List.'j)
2246 parse value MLCEntry with MLCStartLine MLCStartCol MLCStartLen MLCEndLine MLCEndCol MLCEndLen MLCNestStartLine MLCNestStartCol .
2247
2248 -- Check if MLC j ended before the current pos.
2249 fEndBefore = 0
2250 if MLCEndLine < CurLine then
2251 fEndBefore = 1
2252 elseif MLCEndLine = CurLine then
2253 if not (MLCEndCol + MLCEndLen > CurCol) then
2254 fEndBefore = 1
2255 endif
2256 endif
2257 if fEndBefore then
2258 -- Increase lval
2259 lval = j + 1
2260 iterate
2261 endif
2262
2263 -- Check if MLC j started after the current pos.
2264 fStartAfter = 0
2265 if MLCStartLine > CurLine then
2266 fStartAfter = 1
2267 elseif MLCStartLine = CurLine then
2268 if not (MLCStartCol <= CurCol) then
2269 fStartAfter = 1
2270 endif
2271 endif
2272 if fStartAfter then
2273 -- Decrease rval
2274 rval = j - 1
2275 iterate
2276 endif
2277
2278 -- Best match found
2279 parse value MLCEntry with BestMLCStartLine BestMLCStartCol BestMLCStartLen BestMLCEndLine BestMLCEndCol BestMLCEndLen .
2280 --dprintf( 'Best: 'MLCEntry)
2281 --#dprintf( "array", " Best, so far" MLCEntry)
2282 leave -- LISTCOMM: with current buildMLC logic, first should be "best"
2283 enddo
2284 enddo
2285
2286 else
2287 -- Most of the following lines are also used in BuildMLC
2288 if QueryModeKey( Mode, 'CaseSensitive', '1') then
2289 MLCCase = 'e' -- case-sensitive (exact)
2290 else
2291 MLCCase = 'c' -- caseless
2292 endif
2293 ModeMLCCount = GetAVar( 'assist.mode.'Mode'.MLC.0')
2294
2295 do i = 1 to ModeMLCCount
2296 MLCStart = GetAVar( 'assist.mode.'Mode'.MLCStart.'i)
2297 MLCEnd = GetAVar( 'assist.mode.'Mode'.MLCEnd.'i)
2298 MLCNest = GetAVar( 'assist.mode.'Mode'.MLCNest.'i)
2299 MLCEndPrio = GetAVar( 'assist.mode.'Mode'.MLCEndPrio.'i)
2300
2301 MLCStartSearch = EscapeSearchChars( MLCStart)
2302 MLCEndSearch = EscapeSearchChars( MLCEnd)
2303 MLCStartLen = length( MLCStart)
2304 MLCEndLen = length( MLCEnd)
2305
2306 SearchCmdStart = 'xcom l '\1''MLCStartSearch''\1'x'MLCCase
2307 SearchCmdEnd = 'xcom l '\1''MLCEndSearch''\1'x'MLCCase
2308 SearchCmdBoth = 'xcom l '\1'('MLCStartSearch'|'MLCEndSearch')'\1'x'MLCCase
2309
2310 Direction = '-R' -- Backward
2311 setsearch SearchCmdBoth''Direction
2312 .line = CurLine
2313 .col = CurCol
2314
2315 MLCStartLine = 0
2316 MLCStartCol = 0
2317 MLCEndLine = 0
2318 MLCEndCol = 0
2319 fSearchFirst = 1
2320 do forever
2321 if fSearchFirst then
2322 -- Get string at the cursor
2323 startcol = .col
2324 rc = 1
2325 LineStr = textline( .line)
2326 do l = 1 to MLCStartLen
2327 if .col = 1 then
2328 leave
2329 endif
2330 .col = startcol - (l - 1)
2331 ThisMLC = substr( LineStr, .col, MLCStartLen)
2332 if ThisMLC = MLCStart then
2333 rc = 0
2334 leave
2335 endif
2336 end
2337
2338 if rc = 1 then
2339 .col = startcol
2340 repeatfind
2341 endif
2342 fSearchFirst = 0
2343 else
2344 repeatfind
2345 endif
2346
2347 if rc then
2348 retval = 0
2349 leave
2350 endif
2351 ThisMLC = substr( textline( .line), .col, MLCStartLen)
2352 if ThisMLC = MLCStart then
2353 if InsideSLC( Mode) then
2354 iterate
2355 endif
2356 if InsideLiteral( Mode) then
2357 iterate
2358 endif
2359 -- MLCStart after MLCEnd => within MLC
2360 MLCStartLine = .line
2361 MLCStartCol = .col
2362 retval = 1
2363 leave
2364 else
2365 if MLCEndPrio = 0 then -- 1 = default value
2366 -- 0 = Started MLCs have higher priority than SLCs and strings.
2367 -- That means that for recognizing a MLC end, it can follow a
2368 -- SLC string or a string opener. In between two corresponding
2369 -- MLC strings all other chars are ignored.
2370 if InsideSLC( Mode) then
2371 iterate
2372 endif
2373 if InsideLiteral( Mode) then
2374 iterate
2375 endif
2376 endif
2377
2378 -- MLCEnd after MLCStart => not within MLC
2379 MLCEndLine = .line
2380 MLCEndCol = .col
2381 retval = 0
2382 leave
2383 endif
2384 enddo
2385 if retval = 1 then
2386 leave
2387 endif
2388 enddo
2389
2390 if retval = 1 then
2391 -- Make InsideComment2 return 2 and InsideMLC return 1
2392 BestMLCStartLine = 1
2393 endif
2394
2395 endif
2396
2397 --dprintf( 'LocateMLC: return = 'BestMLCStartLine BestMLCStartCol BestMLCStartLen BestMLCEndLine BestMLCEndCol BestMLCEndLen)
2398 return BestMLCStartLine BestMLCStartCol BestMLCStartLen BestMLCEndLine BestMLCEndCol BestMLCEndLen
2399
2400; ---------------------------------------------------------------------------
2401; Builds an array containing the start and end points of all multi-line
2402; comments in the current file.
2403defproc BuildMLCArray( Mode)
2404 getfileid fid
2405 if QueryModeKey( Mode, 'CaseSensitive', '1') then
2406 MLCCase = 'e' -- case-sensitive (exact)
2407 else
2408 MLCCase = 'c' -- caseless
2409 endif
2410 ModeMLCCount = GetAVar( 'assist.mode.'Mode'.MLC.0')
2411
2412 --#dprintf( "array", "Build mode: "Mode "MLCCount: "ModeMLCCount)
2413 do once = 1 to 1
2414 call pSave_Pos( savepos)
2415 do i = 1 to ModeMLCCount
2416 MLCStart = GetAVar( 'assist.mode.'Mode'.MLCStart.'i)
2417 MLCEnd = GetAVar( 'assist.mode.'Mode'.MLCEnd.'i)
2418 MLCNest = GetAVar( 'assist.mode.'Mode'.MLCNest.'i)
2419 MLCEndPrio = GetAVar( 'assist.mode.'Mode'.MLCEndPrio.'i)
2420
2421 --#dprintf( "array", "MLC data: "MLCStart MLCEnd MLCNest)
2422 MLCStartSearch = EscapeSearchChars( MLCStart)
2423 MLCEndSearch = EscapeSearchChars( MLCEnd)
2424 --#dprintf( "array", ""MLCStart "search = "MLCStartSearch)
2425 --#dprintf( "array", ""MLCEnd "search = "MLCEndSearch)
2426 MLCStartLen = length( MLCStart)
2427 MLCEndLen = length( MLCEnd)
2428 .line = 1
2429 .col = 1
2430 array_index = 0 -- LISTCOMM: assumes a separate list for each MLC string-pair is built
2431 NestedStartsList = ''
2432 AdvLen = 0
2433
2434 do forever
2435
2436 -- Use setsearch and repeatfind to move the cursor after a previous search
2437 -- Bugs of 'xcom l', 'xcom c' and repeatfind:
2438 -- 'xcom l' and 'xcom c' don't move the cursor, repeatfind does.
2439 -- ... (see locate.e)
2440 -- repeatfind at the start pos. won't start searching at that pos.,
2441 -- but xcom l does.
2442 --setsearch 'xcom l '\1''MLCStartSearch''\1'x'MLCCase'+F'
2443 --repeatfind
2444 SearchCmdStart = 'xcom l '\1''MLCStartSearch''\1'x'MLCCase'+F'
2445 SearchCmdEnd = 'xcom l '\1''MLCEndSearch''\1'x'MLCCase'+F'
2446 SearchCmdBoth = 'xcom l '\1'('MLCStartSearch'|'MLCEndSearch')'\1'x'MLCCase'+F'
2447
2448 -- Search the MLC start
2449 do l = 1 to AdvLen
2450 right
2451 enddo
2452 -- Search the MLC end
2453 SearchCmdStart
2454 if rc then
2455 leave
2456 endif
2457 AdvLen = MLCStartLen
2458
2459 -- Check if position is within a SLC or a literal
2460 if InsideSLC( Mode) then
2461 iterate
2462 endif
2463 if InsideLiteral( Mode) then
2464 iterate
2465 endif
2466
2467 MLCStartLine = .line
2468 MLCStartCol = .col
2469 --#dprintf( "comm", MLCStart "found at" MLCStartLine MLCStartCol)
2470 NestedStartsList = MLCStartLine MLCStartCol NestedStartsList
2471
2472 -- Search the MLC end - for modes with nesting, search both
2473 do while (NestedStartsList <> '')
2474 --#dprintf( "comm", "MLCEnd, presearch loc: ".line",".col)
2475 do l = 1 to AdvLen
2476 right
2477 enddo
2478 -- Search the matching MLC end
2479 if MLCNest then
2480 SearchCmdBoth
2481 else
2482 SearchCmdEnd
2483 endif
2484 if rc then
2485 leave
2486 endif
2487
2488 -- For a found MLC end
2489 if substr( textline( .line), .col, MLCEndLen) = MLCEnd then
2490 AdvLen = MLCEndLen
2491 -- Check if position is within a SLC or a literal
2492 if MLCEndPrio = 0 then -- 1 = default value
2493 -- 0 = Started MLCs have higher priority than SLCs and strings.
2494 -- That means that for recognizing a MLC end, it can follow a
2495 -- SLC string or a string opener. In between two corresponding
2496 -- MLC strings all other chars are ignored.
2497
2498 --#dprintf( "comm", "MLCEnd, postsearch loc: ".line",".col)
2499 SLCPosition = InsideSLC( Mode, .line, .col)
2500 if SLCPosition then
2501 iterate
2502 endif
2503
2504 if InsideLiteral( Mode) then
2505 iterate
2506 endif
2507 --#dprintf( "comm", "line# ".line" col: ".col" text: "textline( .line))
2508 endif
2509
2510 -- Remove matching MLCStartLine MLCStartCol from NestedStartsList
2511 parse value NestedStartsList with MLCStartLine MLCStartCol NestedStartsList
2512 array_value = MLCStartLine MLCStartCol MLCStartLen .line .col MLCEndLen NestedStartsList
2513
2514 -- Save only unnested MLCs: Nested MLCs can be ignored.
2515 -- They would slow down LocateMLC and complicate it as well:
2516 if NestedStartsList = '' then
2517 array_index = array_index + 1
2518 call SetAvar( fid'.assist.'i'.List.'array_index, array_value)
2519 else
2520 --dprintf( 'MLC --: 'array_value' (nested, not saved)')
2521 endif
2522
2523 -- For a found MLC start (if nesting is allowed)
2524 else
2525 -- this code should only be reached if MLCNest = 1 and MLCStart was matched
2526 AdvLen = MLCStartLen
2527 NestedStartsList = .line .col NestedStartsList
2528
2529 endif
2530 --#dprintf( "comm", 'after Nestlist: 'NestedStartsList)
2531 enddo
2532
2533 if NestedStartsList <> '' then
2534 --#dprintf( "comm", 'Unmatched MLCs: 'NestedStartsList)
2535 /*
2536 sayerror 'Unmatched MLCs: 'NestedStartsList
2537 */
2538 leave
2539 endif
2540
2541 -- Save the number of found MLC entries for this MLC string-pair
2542 call SetAvar( fid'.assist.'i'.List.0', array_index)
2543
2544 enddo -- End of loop for each unnested comment
2545
2546 enddo -- End of loop for each MLC string-pair
2547
2548 call SetAVar( fid'.assist.Modify', fid.modify)
2549 call SetAvar( fid'.assist.ArrayValid', 1)
2550 call pRestore_Pos( savepos)
2551 enddo -- once
2552 return
2553
2554; ---------------------------------------------------------------------------
2555; Returns the tokens which start and end multi-line comments
2556; for the given mode. Also returned is whether the MLC can be nested
2557; within another MLC. These values are returned through three "var"
2558; parameters. Each "word" of each these parameters represents the
2559; start token, the end token and a flag indicating if theat MLC can
2560; be nested within another.
2561defproc GetMLCChars( Mode, var MLCStartChars, var MLCEndChars, var MLCNestList, var MLCEndPrioList)
2562 MLCStartChars = QueryModeKey( Mode, 'MultiLineCommentStart')
2563 if MLCStartChars <> '' then
2564 MLCEndChars = QueryModeKey( Mode, 'MultiLineCommentEnd')
2565 MLCNestList = QueryModeKey( Mode, 'MultiLineCommentNested', '0')
2566 MLCEndPrioList = QueryModeKey( Mode, 'MultiLineCommentEndPrio', '1')
2567 endif
2568 return
2569
2570; ---------------------------------------------------------------------------
2571; This routine takes a "search-for-this" search string and it inserts escape
2572; characters in front of any extended grep metacharacters.
2573; For example, if the search string is "(abc)" (i.e. find "(abc)"), this
2574; routine returns "\(abc\)" because "(" and ")" are extended grep
2575; metacharacters and so they must be "escaped" with "\".
2576defproc EscapeSearchChars( SearchStr)
2577 p = -1
2578 do forever
2579 if SearchStr == '' then
2580 leave
2581 endif
2582 p = verify( SearchStr, EGREP_METACHARACTERS, 'M', p + 2)
2583 if not p then
2584 leave
2585 else
2586 SearchStr = leftstr( SearchStr, p - 1)'\'substr( SearchStr, p)
2587 endif
2588 enddo
2589 return SearchStr
2590
2591; ---------------------------------------------------------------------------
2592; Determines if the cursor is located within a single-line comment.
2593; Returns the column of the start of the comment. A value of 0 is returned,
2594; if no one-line comment is found.
2595defproc InsideSLC( Mode)
2596 CurLine = arg( 2)
2597 if CurLine = '' then
2598 CurLine = .line
2599 endif
2600 CurCol = arg( 3)
2601 if CurCol = '' then
2602 CurCol = .col
2603 endif
2604
2605 --#dprintf( "1line", "Entry: "CurLine CurCol "mode = "Mode)
2606 LineStr = textline( CurLine)
2607 retval = 0
2608
2609 SLCCount = GetAVar( 'assist.mode.'Mode'.SLC.0')
2610 if SLCCount = '' then
2611 call GetSLCChars( Mode, SLCCharList, SLCPosList, SLCNeedList)
2612 SLCCount = words( SLCCharList)
2613 --#dprintf( "1line", "SLCCount: "SLCCount)
2614 call SetAVar( 'assist.mode.'Mode'.SLC.0', SLCCount)
2615 do i = 1 to SLCCount
2616 call SetAVar( 'assist.mode.'Mode'.SLC.'i, word( SLCCharList, i))
2617 call SetAVar( 'assist.mode.'Mode'.SLCPos.'i, word( SLCPosList, i))
2618 call SetAVar( 'assist.mode.'Mode'.SLCNeed.'i, word( SLCNeedList, i))
2619 enddo
2620 endif
2621
2622 do SLCIndex = 1 to SLCCount
2623 SLC = GetAVar( 'assist.mode.'Mode'.SLC.'SLCIndex)
2624 SLCPos = GetAVar( 'assist.mode.'Mode'.SLCPos.'SLCIndex)
2625 SLCNeed = GetAVar( 'assist.mode.'Mode'.SLCNeed.'SLCIndex)
2626 --#dprintf( "1line", "SLC/Pos/Need: "SLC"/"SLCPos"/"SLCNeed)
2627
2628 if SLCNeed = 1 then
2629 SLC = SLC' '
2630 endif
2631
2632/*
2633 -- Disabled, because column restrictions are only valid for Fortran < 90.
2634 -- Therefore the '!' comment char of Fortran90 can't be affected.
2635 -- Fortran77 doesn't need a special treatment here, because a '*' or 'C'
2636 -- comment has to start in column 1.
2637
2638 -- Extension for Fortran90's SLC "!". SLCPos for this char is -6.
2639 -- SLCPos = -6 means: any column except column 6
2640 if SLCPos < 0 then
2641 if CurCol = -SLCPos + 1 then -- needs + 1 to handle '!' in column 6
2642 return 0
2643 else
2644 SLCPos = '0' -- SLC is in an acceptable column so treat it as any other '0'-type
2645 endif
2646 endif
2647*/
2648
2649 -- SLCPos = 0 means: any column is allowed
2650 if SLCPos = '0' then
2651 savecol = CurCol
2652 len = length( SLC)
2653 p = 1 - len
2654 do forever
2655 p = pos( SLC, LineStr, p + len)
2656 if (p = 0) then -- if not found
2657 leave
2658 elseif (p >= savecol) then -- if found on or after cursor column
2659 leave
2660 elseif (retval > 0 & p >= retval) then -- if later than a previously found comment
2661 leave
2662 endif
2663 CurCol = p
2664 if not InsideLiteral( Mode) then
2665 leave
2666 endif
2667 enddo
2668 CurCol = savecol
2669 if (p & p <= CurCol) then -- if found before cursor location
2670 if (retval = 0 | (retval > 0 & p < retval)) then
2671 retval = p
2672 --#dprintf( "1line", "Found "SLC" comment")
2673 endif
2674 endif
2675
2676 -- SLCPos = F means: first non-blank char in LineStr
2677 elseif SLCPos = 'F' then
2678 --#dprintf( "1line", "PosF "leftstr( word( LineStr, 1), length( SLC)))
2679 if leftstr( word( LineStr, 1), length( SLC)) = SLC then -- if line starts with a comment
2680 --#dprintf( "1line", "Found '"SLC"' comm")
2681 p = pos( SLC, LineStr)
2682 if (retval = 0) | (p < retval) then
2683 retval = p
2684 endif
2685 endif
2686
2687 -- SLCPos = 1 means: only column 1 is allowed
2688 elseif SLCPos = '1' then
2689 --#dprintf( "1line", "Pos1 "leftstr( LineStr, length( SLC)))
2690 if leftstr( LineStr, length( SLC)) = SLC then -- if line starts with a comment in column 1
2691 --#dprintf( "1line", "Found '"SLC"' comm")
2692 retval = 1
2693 leave
2694 endif
2695 endif
2696
2697 enddo
2698 --#dprintf( "1line", "Exit: "retval CurLine CurCol)
2699 return retval
2700
2701; ---------------------------------------------------------------------------
2702; Returns the single-line comment data for the given mode.
2703; For each possible SLC the following is returned:
2704; o The token which initiates the SLC
2705; (SLCCharlist)
2706; o A flag indicating any positional requirements
2707; (SLCPosList)
2708; 0: SLC can start anywhere on a line
2709; 1: SLC MUST start in column 1
2710; F: SLC must be the first non-blank on the line
2711; <negative_number> : SLC must NOT start in this column
2712; (-6 means SLC must NOT start in column 6)
2713; o A flag indicating if the token must be followed by a blank
2714; (SLCNeedList)
2715; 0: No (i.e. ANY character may follow the start token
2716; 1: A blank must follow the token
2717defproc GetSLCChars( Mode, var SLCCharList, var SLCPosList, var SLCNeedList)
2718 SLCCharList = QueryModeKey( Mode, 'LineComment', '')
2719 if SLCCharList <> '' then
2720 SLCNeedList = QueryModeKey( Mode, 'LineCommentNeedSpace', '0')
2721 SLCPosList = QueryModeKey( Mode, 'LineCommentPos', '0')
2722 endif
2723 return
2724
2725; ---------------------------------------------------------------------------
2726; Makes the multi-line comment array rebuild on next pAssist call.
2727defproc InvalidateMlcArray()
2728 getfileid fid
2729 call SetAvar( fid'.assist.ArrayValid', 0)
2730
2731 return
2732
2733; ---------------------------------------------------------------------------
2734; Repositions the cursor on the next non-blank, non-comment and non-literal
2735; character.
2736; Syntax: NextCodeChar( Mode [, Line [, Col [, Direction]]])
2737; The default values for Line and Col are the current cursor position.
2738; Direction = '+F' | '-R'. The default direction is '+F'.
2739defproc NextCodeChar( Mode)
2740 Mode = upcase( Mode)
2741 CurLine = arg( 2)
2742 if CurLine = '' then
2743 CurLine = .line
2744 endif
2745 CurCol = arg( 3)
2746 if CurCol = '' then
2747 CurCol = .col
2748 endif
2749 Direction = upcase( arg( 4))
2750 if Direction = '' | not wordpos( Direction, '+F -R') then
2751 Direction = '+F'
2752 endif
2753 FoundChar = ''
2754 pSave_Pos( SavedPos)
2755 .line = CurLine
2756 .col = CurCol
2757 getsearch savesearch
2758 setsearch 'xcom l /[^ \t]+/x'Direction -- find the next non-blank
2759 do forever
2760 display -11 -- Enable only errors to the messageline
2761 repeatfind
2762 display 11
2763 if rc then
2764 pRestore_Pos( SavedPos)
2765 leave
2766 endif
2767
2768 literal_rc = InsideLiteral2( Mode, LitData)
2769 if literal_rc then
2770 -- Move to start or end of literal
2771 parse value LitData with LitStartCol LitEndCol
2772 if LitStartCol & LitEndCol then
2773 if pos( 'R', Direction) then
2774 .col = LitStartCol
2775 else
2776 .col = LitEndCol
2777 endif
2778 endif
2779 -- Search again
2780 iterate
2781 endif
2782
2783 comment_rc = InsideComment2( Mode, CommentData)
2784 -- CommentData is returned and used to position the cursor before
2785 -- the next search
2786 --dprintf( "pAssist", "next pos: ".line",".col "Char: '"substr( textline( .line), .col, 1)'"' "comment_rc: "comment_rc)
2787 if comment_rc = 1 then
2788 -- rc = 1: Inside SLC
2789 if Direction = '+F' then
2790 endline
2791 else
2792 .col = word( CommentData, 1)
2793 endif
2794 -- Search again
2795 iterate
2796 elseif comment_rc = 2 then
2797 -- rc = 2: Inside MLC
2798 parse value CommentData with MLCStartLine MLCStartCol . MLCEndLine MLCEndCol MLCEndLen
2799 --dprintf( "pAssist", "next comment data: "MLCEndLine MLCEndCol MLCEndLen)
2800 if Direction = '+F' then
2801 .line = MLCEndLine
2802 .col = MLCEndCol + MLCEndLen - 1
2803 else
2804 .line = MLCStartLine
2805 .col = MLCStartCol
2806 endif
2807 -- Search again
2808 iterate
2809 endif
2810
2811 -- rc = 0: Found
2812 FoundChar = substr( textline( .line), .col, 1)
2813 leave
2814 enddo
2815 setsearch savesearch
2816 return FoundChar
2817
2818; ---------------------------------------------------------------------------
2819; Returns the search string for a mode-specific identifier for egrep search.
2820; Syntax: GetIdentifierMask( Mode [, Option])
2821; Option = 'F' | 'N' | 'B' (first, next, both). The default option is Both.
2822defproc GetIdentifierMask( Mode)
2823 Mode = upcase( Mode)
2824 Option = upcase( strip( leftstr( arg( 2), 1)))
2825 UsedMode = Mode
2826
2827 if Mode = 'C' then
2828 FirstMask = '[a-zA-Z_\$]'
2829 NextMask = '[a-zA-Z0-9_\$]*'
2830 elseif Mode = 'REXX' then
2831 FirstMask = '[a-zA-Z!\?_]'
2832 NextMask = '[a-zA-Z0-9!\?_]*'
2833 elseif Mode = 'E' then
2834 FirstMask = '[A-Z_]'
2835 NextMask = '[A-Z0-9_]*'
2836 elseif Mode = 'TEX' then
2837 FirstMask = '\\'
2838 NextMask = '[a-zA-Z]+'
2839 elseif Mode = 'IPF' then
2840 FirstMask = '\:'
2841 NextMask = '[a-zA-Z0-9_]+'
2842 elseif Mode = 'HTEXT' then
2843 FirstMask = '^\.'
2844 NextMask = '[a-zA-Z0-9_]+'
2845 elseif wordpos( Mode, 'HTML, WARPIN, XML') then
2846 FirstMask = '\<'
2847 NextMask = '/?[a-zA-Z0-9_]+\>?'
2848 elseif Mode = 'PASCAL' then
2849 FirstMask = '[a-zA-Z_\$]'
2850 NextMask = '[a-zA-Z0-9_\$\.]*'
2851 elseif Mode = 'CMD' then
2852 FirstMask = '[A-Z_]'
2853 NextMask = '[A-Z0-9_]*'
2854 elseif Mode = 'PERL' then
2855 FirstMask = '[a-zA-Z_\$]'
2856 NextMask = "[a-zA-Z0-9_\$\']*" -- contains '
2857 elseif wordpos( Mode, 'ASSEMBLER JAVASCRIPT PYTHON PHP') then
2858 UsedMode = 'C'
2859 FirstMask = '[a-zA-Z_\$]'
2860 NextMask = '[a-zA-Z0-9_\$]*'
2861 else -- use C
2862 UsedMode = 'C'
2863 FirstMask = '[a-zA-Z_\$]'
2864 NextMask = '[a-zA-Z0-9_\$]*'
2865 endif
2866
2867 if Option = '' | Option = 'B' then
2868 Mask = FirstMask''NextMask
2869 if UsedMode = 'C' then
2870 Mask = ':c'
2871 elseif UsedMode = 'REXX' then
2872 Mask = ':r'
2873 endif
2874 elseif Option = 'F' then
2875 Mask = FirstMask
2876 else
2877 -- Option = 'N'
2878 Mask = NextMask
2879 endif
2880 return Mask
2881
2882; ---------------------------------------------------------------------------
2883; Finds the mode-specific identifier at the cursor.
2884; Syntax: FoundIdStr = CurIdentifier( Mode [, fIncludeLitCom])
2885; Returns pIdStart pIdEnd FoundId or empty.
2886; If 1 is specified for fIncludeLitCom, then literals and comments are
2887; handled like code. The default behavior is to ignore them.
2888defproc CurIdentifier( Mode)
2889 FoundIdStr = ''
2890 rc = 0
2891 fIncludeLitCom = (arg( 2) = 1)
2892
2893 do once = 1 to 1
2894 Mode = upcase( Mode)
2895
2896 if not fIncludeLitCom then
2897 literal_rc = InsideLiteral2( Mode, LitData)
2898 if literal_rc then
2899 parse value LitData with LitStartCol LitEndCol
2900 if LitStartCol & LitEndCol then
2901 -- Inside a literal
2902 rc = -2
2903 leave
2904 endif
2905 endif
2906
2907 comment_rc = InsideComment2( Mode, CommentData)
2908 if comment_rc = 1 then
2909 -- Inside a SLC
2910 rc = -3
2911 leave
2912 elseif comment_rc = 2 then
2913 -- Inside a MLC
2914 rc = -4
2915 leave
2916 endif
2917 endif
2918
2919 fCaseSensitive = QueryModeKey( Mode, 'CaseSensitive')
2920 FirstMask = GetIdentifierMask( Mode, 'F')
2921 NextMask = GetIdentifierMask( Mode, 'N')
2922 IdMask = FirstMask''NextMask
2923 RevIdMask = NextMask''FirstMask
2924 if fCaseSensitive then
2925 CaseIdMask = IdMask
2926 RevCaseIdMask = RevIdMask
2927 else
2928 CaseIdMask = upcase( IdMask)
2929 RevCaseIdMask = upcase( RevIdMask)
2930 endif
2931
2932 getline LineStr
2933 if fCaseSensitive then
2934 CaseLineStr = LineStr
2935 else
2936 CaseLineStr = upcase( LineStr)
2937 endif
2938 RevCaseLineStr = reverse( CaseLineStr)
2939 LineLen = length( LineStr)
2940
2941 -- Find pIdEnd
2942 pStart = .col
2943 next = pos( CaseIdMask, CaseLineStr, pStart, 'xm')
2944 parse value next with . pIdEnd .
2945
2946 if not IsNum( pIdEnd) then
2947 -- Not on an id
2948 rc = -1
2949 leave
2950 endif
2951
2952 -- Find pIdStart
2953 pRevStart = LineLen - pIdEnd + 1
2954 next = pos( RevCaseIdMask, RevCaseLineStr, pRevStart, 'xm')
2955 parse value next with pRevIdStart pRevIdEnd .
2956 pIdStart = LineLen - pRevIdEnd + 1
2957
2958 if pStart < pIdStart | pStart > pIdEnd then
2959 -- Not on an id
2960 rc = -1
2961 leave
2962 endif
2963
2964 FoundId = substr( LineStr, pIdStart, pIdEnd - pIdStart + 1)
2965 FoundIdStr = pIdStart pIdEnd FoundId
2966 enddo
2967 return FoundIdStr
2968
2969; ---------------------------------------------------------------------------
2970; Repositions the cursor on the next non-comment and non-literal identifier.
2971; Syntax: FoundIdStr = NextIdentifier( Mode [, Line [, Col [, Direction]]])
2972; Returns pIdStart pIdEnd FoundId or empty.
2973; The default values for Line and Col are the current cursor position.
2974; Direction = '+F' | '-R'. The default direction is '+F'.
2975defproc NextIdentifier( Mode)
2976 Mode = upcase( Mode)
2977 CurLine = arg( 2)
2978 if CurLine = '' then
2979 CurLine = .line
2980 endif
2981 CurCol = arg( 3)
2982 if CurCol = '' then
2983 CurCol = .col
2984 endif
2985 Direction = upcase( arg( 4))
2986 if Direction = '' | not wordpos( Direction, '+F -R') then
2987 Direction = '+F'
2988 endif
2989
2990 pSave_Pos( SavedPos)
2991 .line = CurLine
2992 .col = CurCol
2993
2994 -- Find Id at cursor and go to end or start
2995 next = CurIdentifier( Mode)
2996 if next <> '' then
2997 parse value next with pIdStart pIdEnd FoundId
2998 if pIdStart > 0 & pIdEnd >= pIdStart then
2999 if Direction = '+F' then
3000 .col = pIdEnd
3001 else
3002 .col = pIdStart
3003 endif
3004 endif
3005 else
3006 endif
3007
3008 FirstMask = GetIdentifierMask( Mode, 'F')
3009 NextMask = GetIdentifierMask( Mode, 'N')
3010 IdMask = FirstMask''NextMask
3011 RevIdMask = NextMask''FirstMask
3012 FoundIdStr = ''
3013 getsearch SavedSearch
3014 -- Foreward (left to right): The cursor is on pStart
3015 -- Backward (right to left): The cursor is on pEnd
3016 setsearch 'xcom l '\1''IdMask''\1'ex'Direction
3017
3018 do forever
3019 display -11 -- Enable only errors to the messageline
3020 repeatfind
3021 display 11
3022 if rc then
3023 pRestore_Pos( SavedPos)
3024 leave
3025 endif
3026
3027 literal_rc = InsideLiteral2( Mode, LitData)
3028 if literal_rc then
3029 -- Move to start or end of literal
3030 parse value LitData with LitStartCol LitEndCol
3031 if LitStartCol & LitEndCol then
3032 if pos( 'R', Direction) then
3033 .col = LitStartCol
3034 else
3035 .col = LitEndCol
3036 endif
3037 endif
3038 -- Search again
3039 iterate
3040 endif
3041
3042 comment_rc = InsideComment2( Mode, CommentData)
3043 if comment_rc = 1 then
3044 -- Inside a SLC
3045 if Direction = '+F' then
3046 endline
3047 else
3048 .col = word( CommentData, 1)
3049 endif
3050 -- Search again
3051 iterate
3052 elseif comment_rc = 2 then
3053 -- Inside a MLC
3054 parse value CommentData with MLCStartLine MLCStartCol . MLCEndLine MLCEndCol MLCEndLen
3055 if Direction = '+F' then
3056 -- Does CommentData always return the correct MLC pair?
3057 -- Go after this comment pos if after current
3058 tmpline = MLCEndLine
3059 tmpcol = MLCEndCol + MLCEndLen - 1
3060 if tmpline > .line | (tmpline = .line & tmpcol > .col) then
3061 .line = tmpline
3062 .col = tmpcol
3063 endif
3064 else
3065 -- Go before this comment pos if before current
3066 tmpline = MLCStartLine
3067 tmpcol = MLCStartCol
3068 if tmpline < .line | (tmpline = .line & tmpcol < .col) then
3069 .line = tmpline
3070 .col = tmpcol
3071 endif
3072 endif
3073 -- Search again
3074 iterate
3075 endif
3076
3077 -- Found
3078 getline LineStr
3079 RevLineStr = reverse( LineStr)
3080 LineLen = length( LineStr)
3081 if Direction = '+F' then
3082 -- Foreward (left to right): The cursor is on pStart
3083 pStart = .col
3084 next = pos( IdMask, LineStr, pStart, 'xm')
3085 parse value next with pIdStart pIdEnd .
3086 else
3087 -- Backward (right to left): The cursor is on pEnd
3088 pEnd = .col
3089 pRevStart = LineLen - pEnd + 1
3090 next = pos( RevIdMask, RevLineStr, pRevStart, 'xm')
3091 parse value next with pRevIdStart pRevIdEnd .
3092 pIdStart = LineLen - pRevIdEnd + 1
3093 pIdEnd = LineLen - pRevIdStart + 1
3094 .col = Max( 1, pIdStart)
3095 endif
3096 FoundId = substr( LineStr, pIdStart, pIdEnd - pIdStart + 1)
3097 FoundIdStr = pIdStart pIdEnd FoundId
3098 leave
3099 enddo
3100
3101 setsearch SavedSearch
3102 return FoundIdStr
3103
3104; ---------------------------------------------------------------------------
3105; Dynamically set the array variable 'debuglist', which is used by
3106; the dprintf proc.
3107/*
3108defc t8
3109-- AddAVar( 'debuglist', str)
3110 SetAVar( 'debuglist', arg( 1))
3111
3112defc t10
3113 list = GetAVar( 'debuglist')
3114 'SayHint debuglist: 'list
3115
3116defc InsideComment, IC
3117 CurMode = GetMode()
3118 parse arg CurLine CurCol .
3119 if CurLine = '' then
3120 CurLine = .line
3121 endif
3122 if CurCol = '' then
3123 CurCol = .col
3124 endif
3125 pSave_Pos( SavedPos)
3126 if InsideComment( CurMode, CurLine, CurCol) then
3127 'SayHint Inside a comment'
3128 elseif InsideLiteral( CurMode, CurLine, CurCol) then
3129 'SayHint Inside a literal'
3130 else
3131 'SayHint Not inside a comment or literal'
3132 endif
3133 pRestore_Pos( SavedPos)
3134
3135defc InsideLiteral, IL
3136 CurMode = GetMode()
3137 parse arg CurLine CurCol .
3138 if CurLine = '' then
3139 CurLine = .line
3140 endif
3141 if CurCol = '' then
3142 CurCol = .col
3143 endif
3144 pSave_Pos( SavedPos)
3145 if InsideLiteral( CurMode, CurLine, CurCol) then
3146 'SayHint Inside a literal'
3147 else
3148 'SayHint Not inside a literal'
3149 endif
3150 pRestore_Pos( SavedPos)
3151
3152defc InsideSingleLineComment, ISLC
3153 CurMode = GetMode()
3154 parse arg CurLine CurCol .
3155 if CurLine = '' then
3156 CurLine = .line
3157 endif
3158 if CurCol = '' then
3159 CurCol = .col
3160 endif
3161 pSave_Pos( SavedPos)
3162 if InsideSLC( CurMode, CurLine, CurCol) then
3163 'SayHint Inside a single-line comment'
3164 else
3165 'SayHint Not inside a single-line comment'
3166 endif
3167 pRestore_Pos( SavedPos)
3168
3169defc InsideMultiLineComment, IMLC
3170 CurMode = GetMode()
3171 parse arg CurLine CurCol .
3172 if CurLine = '' then
3173 CurLine = .line
3174 endif
3175 if CurCol = '' then
3176 CurCol = .col
3177 endif
3178 pSave_Pos( SavedPos)
3179 if InsideMLC( CurMode, CurLine, CurCol) then
3180 'SayHint Inside a multi-line comment'
3181 else
3182 'SayHint Not inside a multi-line comment'
3183 endif
3184 pRestore_Pos( SavedPos)
3185
3186defc PrevCodeChar, PCC
3187 CurMode = GetMode()
3188 parse arg CurLine CurCol .
3189 if CurLine = '' then
3190 CurLine = .line
3191 endif
3192 if CurCol = '' then
3193 CurCol = .col
3194 endif
3195 Direction = '-R'
3196 FoundChar = NextCodeChar( CurMode, CurLine, CurCol, Direction)
3197 dprintf( 'PrevCodeChar: FoundChar = 'FoundChar)
3198
3199defc NextCodeChar, NCC
3200 CurMode = GetMode()
3201 parse arg CurLine CurCol .
3202 if CurLine = '' then
3203 CurLine = .line
3204 endif
3205 if CurCol = '' then
3206 CurCol = .col
3207 endif
3208 Direction = '+F'
3209 FoundChar = NextCodeChar( CurMode, CurLine, CurCol, Direction)
3210 dprintf( 'NextCodeChar: FoundChar = 'FoundChar)
3211
3212defc CurIdentifier, CID
3213 CurMode = GetMode()
3214 dprintf( 'CurIdentifier: FoundIdStr = 'CurIdentifier( CurMode))
3215
3216defc PrevIdentifier, PID
3217 CurMode = GetMode()
3218 parse arg CurLine CurCol .
3219 if CurLine = '' then
3220 CurLine = .line
3221 endif
3222 if CurCol = '' then
3223 CurCol = .col
3224 endif
3225 Direction = '-R'
3226 FoundIdStr = NextIdentifier( CurMode, CurLine, CurCol, Direction)
3227 dprintf( 'PrevIdentifier: FoundIdStr = 'FoundIdStr)
3228
3229defc NextIdentifier, NID
3230 CurMode = GetMode()
3231 parse arg CurLine CurCol .
3232 if CurLine = '' then
3233 CurLine = .line
3234 endif
3235 if CurCol = '' then
3236 CurCol = .col
3237 endif
3238 Direction = '+F'
3239 FoundIdStr = NextIdentifier( CurMode, CurLine, CurCol, Direction)
3240 dprintf( 'NextIdentifier: FoundIdStr = 'FoundIdStr)
3241*/
3242
Note: See TracBrowser for help on using the repository browser.