1/*
2 * This is a THE macro to use the output from ctags; especially Exuberent
3 * Ctags.
4 * To use this macro, define a function key to execute the macro without
5 * any arguments; eg "define F1 macro tags.the"
6 * Then when you want to view the definition of a valid language construct
7 * move the cursor to anywhere on the word and press F1. If the word has
8 * been found in source files by ctags, the file containing the definition
9 * is edited and the cursor moved to the definition line.
10 *
11 * Before this macro can work you need to have run ctags; either external
12 * to THE or by using the 'tags' argument to tags.the. This macro relies
13 * on being able to read the output of the ctags program; a file called
14 * 'tags'. You must specify where this file is as another argument following
15 * 'tags'. eg assume that all source files are in /home/mark/THE, then you
16 * should run: "tags tags /home/mark/THE"
17 *
18 * If you have run ctags outside of THE, you still need to tell tags.the
19 * where the 'tags' file is. This is done by: "tags init /home/mark/THE"
20 *
21 * If you have made significant changes to the source you probably need to
22 * rerun ctags occasionally to bring the 'tags' file into sync with the
23 * source. You can do this while inside THE, by running "tags refresh"
24 *
25 * As mentioned before, tags.the works best with recent versions of
26 * Exuberent Ctags, because it has a recursive flag.  If you are using a
27 * version of ctags without the recursive "-R" flag, or you want to use
28 * any other ctags flags when you build your 'tags' file, then the 'tags'
29 * argument to tags.the can be passed any ctags flags. eg:
30 * "tags tags /home/mark/THE * -h junk"
31 */
32Parse Arg command path options
33Select
34   When command = 'refresh' Then Call refresh path options
35   When command = 'init' Then Call init 'init' path options
36   When command = 'tags' Then Call init 'tags' path options
37   When command = 'prev' Then Call prev
38   When command = 'next' Then Call next
39   When command = 'goto' Then Call goto
40   When command = '' Then Call doit path options
41   Otherwise Call Abort 0,'Invalid option supplied; must be one of "refresh", "init" or empty'
42End
43Return 0
44
45doit: Procedure
46lf = d2c(10)
47'editv get' path
48If path = '' Then Call Abort 0,'You must run tags.the with the "init" or "tags" command first'
49If Stream( path'tags', 'C', 'QUERY EXISTS' ) = '' Then Call Abort 0,'There is no "tags" file in' path
50'extract /curline/fieldword/regexp/cursor/lscreen/'
51fieldword.1 = Strip( fieldword.1)
52tab = d2c(9)
53fn = path'tags'
54save_regexp = regexp.1
55'regexp egrep'
56found.0 = 0
57Call Stream fn, 'C', 'OPEN READ'
58Do While Lines(fn) > 0
59   line = Linein(fn)
60   If Left( line, 2 ) = '!_' Then Iterate
61   Parse Var line keyword (tab) file (tab) location ';' .
62   If keyword = fieldword.1 Then
63      Do
64         idx = found.0
65         idx = idx+1
66         found.idx = file','location
67         found.0 = idx
68      End
69   Else
70      Do
71         If found.0 \= 0 Then Leave
72      End
73End
74Call Stream fn, 'C', 'CLOSE'
75
76Select
77   When found.0 = 0 Then line = ''
78   When found.0 = 1 Then line = found.1
79   Otherwise
80      Do
81         args = ''
82         Do i = 1 To found.0
83            args = args||Strip( Changestr('$/', Changestr( '/^',found.i,'' ), '' ) )lf
84         End
85         If cursor.1 < lscreen.5 % 2 Then 'popup below' lf || args
86         Else 'popup above' lf || args
87         If rc \= 0 | popup.2 = 0 Then line = ''
88         Else
89            Do
90               idx = popup.2
91               line = found.idx
92            End
93      End
94End
95If found.0 \= 0 & line \= '' Then
96   Do
97      Parse Var line file ',' location
98      Call show
99      /*
100       * Now save the current location in a stem
101       */
102      'editv get TAGS_LAST'
103      If Datatype( tags_last ) = 'NUM' Then next_tag = tags_last+1
104      Else next_tag = 1
105      'editv get TAGS.0'
106      If Datatype( tags.0 ) = 'NUM' Then max_tags = tags.0
107      Else max_tags = 0
108      tags.next_tag = fieldword.1 file location
109      'editv setl TAGS.'next_tag tags.next_tag
110      'editv setl TAGS_LAST' next_tag
111      'editv setl TAGS.0' next_tag
112      Do i = next_tag+1 To max_tags
113         'editv set TAGS.'i
114      End
115   End
116'regexp' save_regexp
117Return 0
118
119prev: Procedure
120'editv get PATH'
121If path = '' Then Call Abort 0,'You must run tags.the with the "init" or "tags" command first'
122'editv get TAGS_LAST'
123If Datatype( tags_last ) \= 'NUM' Then Call Abort 0,'You are at the beginning'
124prev = tags_last - 1
125If prev = 0 Then Call Abort 0,'You are at the beginning'
126
127'extract /regexp'
128save_regexp = regexp.1
129'regexp egrep'
130
131'editv get TAGS.'prev
132Parse Var tags.prev . file location
133Call show
134/*
135 * Now save the current location in a list
136 */
137'editv setl TAGS_LAST' prev
138'regexp' save_regexp
139Return 0
140
141next: Procedure
142'editv get PATH'
143If path = '' Then Call Abort 0,'You must run tags.the with the "init" or "tags" command first'
144'editv get TAGS_LAST'
145If Datatype( tags_last ) \= 'NUM' Then Call Abort 0,'You are at the end'
146'editv get TAGS.0'
147If Datatype( tags.0 ) = 'NUM' Then max_tags = tags.0
148Else max_tags = 0
149next = tags_last + 1
150If next > max_tags Then Call Abort 0,'You are at the end'
151
152'extract /regexp'
153save_regexp = regexp.1
154'regexp egrep'
155
156'editv get TAGS.'next
157Parse Var tags.next . file location
158Call show
159/*
160 * Now save the current location in a list
161 */
162'editv setl TAGS_LAST' next
163'regexp' save_regexp
164Return 0
165
166goto: Procedure
167lf = d2c(10)
168'editv get PATH'
169If path = '' Then Call Abort 0,'You must run tags.the with the "init" or "tags" command first'
170'editv get TAGS_LAST'
171If Datatype( tags_last ) \= 'NUM' Then Call Abort 0,'Nothing to show'
172'editv get TAGS.0'
173If Datatype( tags.0 ) \= 'NUM' Then Call Abort 0,'Nothing to show'
174args = ''
175Do i = 1 To tags.0
176   'editv get TAGS.'i
177   Parse Var tags.i key file atag
178   args = args||key'('file')' Strip( Changestr('$/', Changestr( '/^',atag,'' ), '' ) )lf
179End
180'popup centre initial' tags_last lf || args
181If rc \= 0 | popup.2 = 0 Then Nop
182Else
183   Do
184      idx = popup.2
185      'extract /regexp'
186      save_regexp = regexp.1
187      'regexp egrep'
188      'editv get TAGS.'idx
189      Parse Var tags.idx . file location
190      Call show
191      /*
192       * Now save the current location in a list
193       */
194      'editv setl TAGS_LAST' idx
195      'regexp' save_regexp
196   End
197Return
198
199Escape: Procedure
200Parse Arg str
201str = Changestr( '*', str, '\*' )
202str = Changestr( '[', str, '\[' )
203str = Changestr( ']', str, '\]' )
204str = Changestr( '(', str, '\(' )
205str = Changestr( ')', str, '\)' )
206Return str
207
208Init: Procedure
209Parse Arg type path options
210'extract/version'
211If version.3 = 'UNIX' | version.3 = 'X11' | version.3 = 'QNX' Then ossep = '/'
212Else ossep = '\'
213If path = '' Then Call Abort 0,'Must supply a path for "'type'" command'
214here = Stream( path, 'C', 'QUERY EXISTS' )
215If here = '' Then Call Abort 0,path 'does not exist'
216If Right( here, 1 ) \= ossep Then path = here||ossep
217Else path = here
218'editv set path' path
219If type = 'tags' Then Call Runctags options
220Return 0
221
222Refresh: Procedure
223Parse Arg args
224If args \= '' Then Call Abort 0,'No parameters should be supplied with "refresh" command'
225Call Runctags
226Return 0
227
228Runctags: Procedure
229Parse Arg options
230'editv get' path
231If path = '' Then Call Abort 0,'You must run tags.the with the "init" command first'
232here = Directory()
233If Directory( path ) = '' Then Call Abort 1, path, 'does not exist'
234If options = '' Then Address System 'ctags -R *' With Output Stem junk. Error Stem err.
235Else Address System 'ctags' options With Output Stem junk. Error Stem err.
236If rc \= 0 Then
237   Do i = 1 To err.0
238      'emsg' err.i
239   End
240Call Directory here
241Return 0
242
243show:
244'the' path||file
245'top'
246If Datatype( location ) = 'NUM' Then
247   Do
248      ':'location
249      '-1'
250      'l r /^.*$/'
251   End
252Else 'locate r' Escape(location)
253If incommand() Then 'cursor home'
254Return
255
256Abort: Procedure
257Parse Arg reset,msg
258'emsg' msg
259If reset Then 'editv set path'
260Exit 0
261