1/*
2 * This macro provides a simplistic, extensible mechanism for providing
3 * context-sensitive help for language instructions/functions.
4 * Simply assign a key to this macro; DEFINE F1 macro syntax F1
5 *
6 * When the macro is invoked, the current COLORING is queried. If there is
7 * a parsing engine enabled for the file, (see QUERY COLORING), then a
8 * check is made for the matching "syntax" file for the parser in MACROPATH.
9 * If found, then the word (non-blank) at the cursor position is extracted and
10 * mathed against all keywords present in the "syntax" file, and a popup menu
11 * is displayed with all matches from the file.  Use the up and down arrow keys
12 * to select the item, and it will be inserted (after adjustment; see rexx.syntax
13 * for details) into the filearea where the matched text exists.
14 * To ignore the insertion, press 'q'. To see extended help (if it exists) on the
15 * highlighted match, press the key specified in the DEFINE statement (the first
16 * argument to this macro).
17 */
18!global.!path.unix = ':'
19!global.!dir.unix = '/'
20!global.!path.windows = ';'
21!global.!dir.windows = '\'
22!global.!delim = x2c( 'FF' )
23
24!globalv = '!global. fieldword. coloring. cursor. curline.'
25
26Parse Arg key
27If key = '' Then Call Abort 'No escape key provided'
28
29!global.!keyword_values = 'UPPER LOWER INITCAP LITERAL'
30'Extract /fieldword/coloring/cursor/curline'
31If coloring.1 = 'ON' Then
32   Do
33      !global.!func = Translate( fieldword.2 )
34      !global.!lenfunc = Length( !global.!func )
35      !global.!idx = 0
36      !global.!match.0 = 0
37      Call ProcessFile coloring.3'.syntax', 1
38      initial_item = 1
39      If !global.!match.0 \= 0 Then
40         Do Forever
41            cmd = 'POPUP BELOW KEYS' key 'INITIAL' initial_item !global.!delim
42            Do i = 1 To !global.!match.0
43               cmd = cmd || !global.!match.i || !global.!delim
44            End
45            cmd
46            If popup.1 \= '' Then
47               Do
48                  initial_item = popup.2
49                  Parse Var popup.1 keyword (!global.!argstart) args (!global.!argend) . (!global.!ignoreafter) .
50                  idx = popup.2
51                  If popup.4 = 1 Then
52                     Do
53                        /*
54                         * Our alternate key ended the popup, see if we have details
55                         */
56                        If !global.!details.idx \= '' Then
57                           Do
58                              !global.!details.idx = Changestr( !global.!delim, Substr( !global.!details.idx, 2 ), d2c(10) )
59                              cmd = 'DIALOG' || !global.!delim || !global.!details.idx || !global.!delim 'TITLE /Details for' keyword'/ OK'
60                              cmd
61                           End
62                     End
63                  Else
64                     Do
65                        args = Strip( args )
66                        Do i = 1 To Length( !global.!optional )
67                           args = Changestr( Substr( !global.!optional, i, 1 ), args, '' )
68                        End
69                        /*
70                         * We have the text to insert, we now have to work out where
71                         * the text started in the current line and overlay the new
72                         * text at that location.
73                         * If we have a later version of THE with FIELDWORD.3 then its easy...
74                         */
75                        If fieldword.0 = 3 Then
76                           Do
77                              res = InsertText( !global.!keyword.idx, curline.3, cursor.4, fieldword.3, !global.!func, args )
78                           End
79                        Else
80                           Do
81                              /*
82                               * We have to start at the cursor location and search backwards until we
83                               * find the location in the line where the keyword starts
84                               */
85                              Do i = cursor.4 To 1 By -1
86                                 If Translate( curline.3 ) = Translate( Overlay( !global.!func, curline.3, i ) ) Then
87                                    Do
88                                       res = InsertText( !global.!keyword.idx, curline.3, cursor.4, i, !global.!func, args )
89                                       Leave
90                                    End
91                              End
92                           End
93                        'r' res
94                        Leave
95                     End
96               End
97            Else Leave
98         End
99      Else
100         Do
101            Call Abort 'No matches for "'!global.!func'".'
102            Exit 0
103         End
104   End
105Else
106   Do
107      Call Abort 'COLORING is not on for this file'
108   End
109Exit 0
110
111ProcessFile: Procedure Expose (!globalv)
112Parse Arg fname, initial
113-- some defaults
114!global.!keyword_format.initial = 'NONE'
115sfile = GetSyntaxFile( fname )
116If sfile \= '' Then sf = Stream( sfile, 'C', 'QUERY EXISTS' )
117If sf = '' Then
118   Do
119--      If initial Then Call Abort 'Syntax file:' fname 'does not exist in MACROPATH.'
120--      Else Call Abort 'Included file:' fname 'does not exist.'
121      If initial Then Call Warning 'Syntax file:' fname 'does not exist in MACROPATH.'
122      Else Call Warning 'Included file:' fname 'does not exist.'
123      Return
124   End
125Call Stream sf, 'C', 'OPEN READ'
126Do While Lines( sf ) > 0
127   line = Linein( sf )
128   Select
129      When Left( line, 1 ) = '*' Then Iterate
130      When Left( line, 1 ) = ':' Then
131         Do
132            Parse Var line ':' directive value .
133            If initial = 0 & directive \= 'KEYWORD' Then Call Abort 'Invalid directive:' directive 'invalid in included files.'
134            Select
135               When directive = 'OPTIONAL' Then !global.!optional = value
136               When directive = 'IGNOREAFTER' Then !global.!ignoreafter = value
137               When directive = 'ARGSTART' Then !global.!argstart = value
138               When directive = 'ARGEND' Then !global.!argend = value
139               When directive = 'INCLUDE' Then
140                  Do
141                     Call ProcessFile value, 0
142                  End
143               When directive = 'KEYWORD' Then
144                  Do
145                     If Wordpos( value, !global.!keyword_values ) = 0 Then Call Abort 'Invalid value of "'value'" for directive :'directive'. Valid values:' !global.!keyword_values
146                     !global.!keyword_format.initial = value
147                  End
148               Otherwise
149                  Do
150                     Call Abort 'Unknown directive :'directive'.'
151                  End
152            End
153            If value = '' Then Call Abort 'Must supply a value for directive :'directive'.'
154         End
155      When Left( line, 1 ) = '>' & new_keyword = 1 Then
156         Do
157            idx = !global.!idx
158            !global.!details.idx = !global.!details.idx || !global.!delim || Substr( line, 2 )
159         End
160      When Left( Translate( line ), !global.!lenfunc ) = !global.!func Then
161         Do
162            !global.!idx = !global.!idx + 1
163            idx = !global.!idx
164            Parse Var line keyword (!global.!argstart) .
165            Select
166               When !global.!keyword_format.initial = 'UPPER' Then
167                  Do
168                     !global.!keyword.idx = Translate( keyword )
169                  End
170               When !global.!keyword_format.initial = 'INITCAP' Then
171                  Do
172                     fc = Translate( Left( keyword, 1 ) )
173                     !global.!keyword.idx = Overlay( fc, keyword , 1, 1 )
174                  End
175               Otherwise
176                  Do
177                     !global.!keyword.idx = keyword
178                  End
179            End
180            !global.!match.idx = line
181            !global.!match.0 = !global.!idx
182            !global.!details.idx = ''
183            new_keyword = 1
184         End
185      Otherwise
186         Do
187            new_keyword = 0
188         End
189   End
190End
191Call Stream sf, 'C', 'CLOSE'
192Return
193
194GetSyntaxFile: Procedure Expose (!globalv)
195Parse Arg sf
196'extract /macropath'
197os = GetOS()
198mp = macropath.1
199Do While mp \= ''
200   Parse Var mp dir (!global.!path.os) mp
201   If Right( dir, 1 ) \= !global.!dir.os Then dir = dir||!global.!dir.os
202   fn = dir || sf
203   If Stream( fn, 'C', 'QUERY EXISTS' ) \= '' Then Return fn
204End
205Return ''
206
207InsertText: Procedure Expose (!globalv)
208Parse Arg keyword, line, curpos, fieldstart, func, args
209If coloring.3 = 'rexx' Then
210   Do
211      Parse Var line 1 call =(fieldstart) .
212      If Translate( Strip( call ) ) = 'CALL' Then args = ' 'args
213      Else args = !global.!argstart || args || !global.!argend
214   End
215Else args = !global.!argstart || args || !global.!argend
216res = Delstr( curline.3, fieldstart, Length( func ) )
217res = Insert( keyword || args, res, fieldstart-1 )
218Return res
219
220GetOS: Procedure
221Parse Upper Source os .
222Select
223   When os = 'WINDOWSNT' Then os = 'WINDOWS'
224   When os = 'WIN32' Then os = 'WINDOWS'
225   When os = 'WIN64' Then os = 'WINDOWS'
226   When os = 'OS/2' Then os = 'WINDOWS'
227   When os = 'LINUX' Then os = 'UNIX'
228   When os = 'QNX' Then os = 'UNIX'
229   Otherwise Nop
230End
231Return os
232
233Abort:
234Parse Arg msg
235'EMSG' msg
236Exit 0
237
238Warning:
239Parse Arg msg
240'EMSG' msg
241Return
242