Fix previous gdb-mi fix...
[bpt/emacs.git] / lisp / progmodes / idlwave.el
CommitLineData
52a244eb 1;; idlwave.el --- IDL editing mode for GNU Emacs
d7a0267c 2
73b0cd50 3;; Copyright (C) 1999-2011 Free Software Foundation, Inc.
f32b3b91 4
52a244eb 5;; Authors: J.D. Smith <jdsmith@as.arizona.edu>
65363a4e 6;; Carsten Dominik <dominik@science.uva.nl>
52a244eb 7;; Chris Chase <chase@att.com>
5e72c6b2 8;; Maintainer: J.D. Smith <jdsmith@as.arizona.edu>
bd78fa1d 9;; Version: 6.1.22
f32b3b91
CD
10;; Keywords: languages
11
e8af40ee 12;; This file is part of GNU Emacs.
f32b3b91 13
b1fc2b50 14;; GNU Emacs is free software: you can redistribute it and/or modify
f32b3b91 15;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
f32b3b91
CD
18
19;; GNU Emacs is distributed in the hope that it will be useful,
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
23
24;; You should have received a copy of the GNU General Public License
b1fc2b50 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
f32b3b91
CD
26
27;;; Commentary:
28
f66f03de
S
29;; IDLWAVE enables feature-rich development and interaction with IDL,
30;; the Interactive Data Language. It provides a compelling,
31;; full-featured alternative to the IDLDE development environment
32;; bundled with IDL.
3938cb82 33
52a244eb
S
34;; In the remotely distant past, based on pascal.el, though bears
35;; little resemblance to it now.
f32b3b91
CD
36;;
37;; Incorporates many ideas, such as abbrevs, action routines, and
38;; continuation line indenting, from wave.el.
39;; wave.el original written by Lubos Pochman, Precision Visuals, Boulder.
40;;
41;; See the mode description ("C-h m" in idlwave-mode or "C-h f idlwave-mode")
42;; for features, key bindings, and info.
43;; Also, Info format documentation is available with `M-x idlwave-info'
44;;
5e72c6b2
S
45;; New versions of IDLWAVE, documentation, and more information
46;; available from:
47;; http://idlwave.org
f32b3b91
CD
48;;
49;; INSTALLATION
50;; ============
51;;
52;; Follow the instructions in the INSTALL file of the distribution.
53;; In short, put this file on your load path and add the following
54;; lines to your .emacs file:
55;;
56;; (autoload 'idlwave-mode "idlwave" "IDLWAVE Mode" t)
8c7b4ec8 57;; (autoload 'idlwave-shell "idlw-shell" "IDLWAVE Shell" t)
f32b3b91
CD
58;; (setq auto-mode-alist (cons '("\\.pro\\'" . idlwave-mode) auto-mode-alist))
59;;
60;;
61;; SOURCE
62;; ======
63;;
76959b77 64;; The newest version of this file is available from the maintainer's
52a244eb 65;; Webpage:
f32b3b91 66;;
5e72c6b2 67;; http://idlwave.org
f32b3b91
CD
68;;
69;; DOCUMENTATION
70;; =============
71;;
52a244eb
S
72;; IDLWAVE is documented online in info format. A printable version
73;; of the documentation is available from the maintainers webpage (see
74;; SOURCE).
775591f7 75;;
4b1aaa8b 76;;
f32b3b91
CD
77;; ACKNOWLEDGMENTS
78;; ===============
79;;
80;; Thanks to the following people for their contributions and comments:
81;;
52a244eb
S
82;; Ulrik Dickow <dickow_at_nbi.dk>
83;; Eric E. Dors <edors_at_lanl.gov>
84;; Stein Vidar H. Haugan <s.v.h.haugan_at_astro.uio.no>
85;; David Huenemoerder <dph_at_space.mit.edu>
86;; Kevin Ivory <Kevin.Ivory_at_linmpi.mpg.de>
87;; Dick Jackson <dick_at_d-jackson.com>
88;; Xuyong Liu <liu_at_stsci.edu>
89;; Simon Marshall <Simon.Marshall_at_esrin.esa.it>
90;; Laurent Mugnier <mugnier_at_onera.fr>
91;; Lubos Pochman <lubos_at_rsinc.com>
92;; Bob Portmann <portmann_at_al.noaa.gov>
93;; Patrick M. Ryan <pat_at_jaameri.gsfc.nasa.gov>
94;; Marty Ryba <ryba_at_ll.mit.edu>
95;; Paul Sorenson <aardvark62_at_msn.com>
96;; Phil Sterne <sterne_at_dublin.llnl.gov>
97;; Phil Williams <williams_at_irc.chmcc.org>
f32b3b91
CD
98;;
99;; CUSTOMIZATION:
100;; =============
101;;
52a244eb
S
102;; IDLWAVE has extensive customize support; to learn about the
103;; variables which control the mode's behavior, use `M-x
104;; idlwave-customize'.
f32b3b91
CD
105;;
106;; You can set your own preferred values with Customize, or with Lisp
107;; code in .emacs. For an example of what to put into .emacs, check
52a244eb
S
108;; the TexInfo documentation or see a complete .emacs available at the
109;; website.
f32b3b91
CD
110;;
111;; KNOWN PROBLEMS:
112;; ==============
113;;
76959b77
S
114;; IDLWAVE support for the IDL-derived PV-WAVE CL language of Visual
115;; Numerics, Inc. is growing less and less complete as the two
116;; languages grow increasingly apart. The mode probably shouldn't
3938cb82 117;; even have "WAVE" in its title, but it's catchy, and was required
52a244eb 118;; to avoid conflict with the CORBA idl.el mode. Caveat WAVEor.
76959b77 119;;
f32b3b91
CD
120;; Moving the point backwards in conjunction with abbrev expansion
121;; does not work as I would like it, but this is a problem with
122;; emacs abbrev expansion done by the self-insert-command. It ends
123;; up inserting the character that expanded the abbrev after moving
124;; point backward, e.g., "\cl" expanded with a space becomes
125;; "LONG( )" with point before the close paren. This is solved by
4b1aaa8b 126;; using a temporary function in `post-command-hook' - not pretty,
595ab50b 127;; but it works.
f32b3b91
CD
128;;
129;; Tabs and spaces are treated equally as whitespace when filling a
130;; comment paragraph. To accomplish this, tabs are permanently
131;; replaced by spaces in the text surrounding the paragraph, which
132;; may be an undesirable side-effect. Replacing tabs with spaces is
133;; limited to comments only and occurs only when a comment
134;; paragraph is filled via `idlwave-fill-paragraph'.
135;;
52a244eb
S
136;; Muti-statement lines (using "&") on block begin and end lines can
137;; ruin the formatting. For example, multiple end statements on a
138;; line: endif & endif. Using "&" outside of block begin/end lines
139;; should be okay.
f32b3b91 140;;
76959b77
S
141;; Determining the expression at point for printing and other
142;; examination commands is somewhat rough: currently only fairly
143;; simple entities are found. You can always drag-select or examine
52a244eb 144;; a pre-selected region.
f32b3b91 145;;
f32b3b91
CD
146;; When forcing completion of method keywords, the initial
147;; query for a method has multiple entries for some methods. Would
595ab50b 148;; be too difficult to fix this hardly used case.
f32b3b91
CD
149;;
150\f
151;;; Code:
152
52a244eb 153
f32b3b91 154(eval-when-compile (require 'cl))
52a244eb
S
155(require 'idlw-help)
156
157;; For XEmacs
158(unless (fboundp 'line-beginning-position)
159 (defalias 'line-beginning-position 'point-at-bol))
160(unless (fboundp 'line-end-position)
161 (defalias 'line-end-position 'point-at-eol))
162(unless (fboundp 'char-valid-p)
163 (defalias 'char-valid-p 'characterp))
f66f03de
S
164(unless (fboundp 'match-string-no-properties)
165 (defalias 'match-string-no-properties 'match-string))
f32b3b91 166
3938cb82
S
167(if (not (fboundp 'cancel-timer))
168 (condition-case nil
169 (require 'timer)
170 (error nil)))
171
73e72da4
DN
172(declare-function idlwave-shell-get-path-info "idlw-shell")
173(declare-function idlwave-shell-temp-file "idlw-shell")
174(declare-function idlwave-shell-is-running "idlw-shell")
175(declare-function widget-value "wid-edit" (widget))
176(declare-function comint-dynamic-complete-filename "comint" ())
73e72da4 177
f32b3b91 178(defgroup idlwave nil
31b58798 179 "Major mode for editing IDL .pro files."
f32b3b91 180 :tag "IDLWAVE"
4b1aaa8b 181 :link '(url-link :tag "Home Page"
5e72c6b2 182 "http://idlwave.org")
595ab50b
CD
183 :link '(emacs-commentary-link :tag "Commentary in idlw-shell.el"
184 "idlw-shell.el")
f32b3b91
CD
185 :link '(emacs-commentary-link :tag "Commentary in idlwave.el" "idlwave.el")
186 :link '(custom-manual "(idlwave)Top")
187 :prefix "idlwave"
188 :group 'languages)
189
52a244eb 190
f32b3b91
CD
191;;; Variables for indentation behavior ---------------------------------------
192
193(defgroup idlwave-code-formatting nil
194 "Indentation and formatting options for IDLWAVE mode."
195 :group 'idlwave)
196
f66f03de 197(defcustom idlwave-main-block-indent 2
f32b3b91
CD
198 "*Extra indentation for the main block of code.
199That is the block between the FUNCTION/PRO statement and the END
200statement for that program unit."
201 :group 'idlwave-code-formatting
202 :type 'integer)
203
f66f03de 204(defcustom idlwave-block-indent 3
f32b3b91
CD
205 "*Extra indentation applied to block lines.
206If you change this, you probably also want to change `idlwave-end-offset'."
207 :group 'idlwave-code-formatting
208 :type 'integer)
209
f66f03de 210(defcustom idlwave-end-offset -3
f32b3b91
CD
211 "*Extra indentation applied to block END lines.
212A value equal to negative `idlwave-block-indent' will make END lines
213line up with the block BEGIN lines."
214 :group 'idlwave-code-formatting
215 :type 'integer)
216
f66f03de 217(defcustom idlwave-continuation-indent 3
f32b3b91
CD
218 "*Extra indentation applied to continuation lines.
219This extra offset applies to the first of a set of continuation lines.
5e72c6b2
S
220The following lines receive the same indentation as the first."
221 :group 'idlwave-code-formatting
222 :type 'integer)
223
f66f03de 224(defcustom idlwave-max-extra-continuation-indent 40
5e72c6b2
S
225 "*Maximum additional indentation for special continuation indent.
226Several special indentations are tried to help line up continuation
227lines in routine calls or definitions, other statements with
134b6671 228parentheses, or assignment statements. This variable specifies a
5e72c6b2
S
229maximum amount by which this special indentation can exceed the
230standard continuation indentation, otherwise defaulting to a fixed
231offset. Set to 0 to effectively disable all special continuation
232indentation, or to a large number (like 100) to enable it in all
52a244eb 233cases. See also `idlwave-indent-to-open-paren', which can override
5e72c6b2 234this variable."
f32b3b91
CD
235 :group 'idlwave-code-formatting
236 :type 'integer)
237
5e72c6b2 238(defcustom idlwave-indent-to-open-paren t
5a0c3f56
JB
239 "*Non-nil means, indent continuation lines to innermost open parenthesis.
240This indentation occurs even if otherwise disallowed by
5e72c6b2
S
241`idlwave-max-extra-continuation-indent'. Matching parens and the
242interleaving args are lined up. Example:
243
244 x = function_a(function_b(function_c( a, b, [1,2,3, $
245 4,5,6 $
246 ], $
247 c, d $
248 )))
249
250When this variable is nil, paren alignment may still occur, based on
5a0c3f56
JB
251the value of `idlwave-max-extra-continuation-indent', which, if zero,
252would yield:
5e72c6b2
S
253
254 x = function_a(function_b(function_c( a, b, [1,2,3, $
255 4,5,6 $
256 ], $
257 c, d $
258 )))"
5a0c3f56 259 :group 'idlwave-code-formatting
5e72c6b2
S
260 :type 'boolean)
261
52a244eb
S
262(defcustom idlwave-indent-parens-nested nil
263 "*Non-nil means, indent continuation lines with parens by nesting
264lines at consecutively deeper levels."
265 :group 'idlwave-code-formatting
266 :type 'boolean)
267
268
f32b3b91
CD
269(defcustom idlwave-hanging-indent t
270 "*If set non-nil then comment paragraphs are indented under the
271hanging indent given by `idlwave-hang-indent-regexp' match in the first line
272of the paragraph."
273 :group 'idlwave-code-formatting
274 :type 'boolean)
275
276(defcustom idlwave-hang-indent-regexp "- "
277 "*Regular expression matching the position of the hanging indent
5a0c3f56 278in the first line of a comment paragraph. The size of the indent
f32b3b91
CD
279extends to the end of the match for the regular expression."
280 :group 'idlwave-code-formatting
281 :type 'regexp)
282
283(defcustom idlwave-use-last-hang-indent nil
284 "*If non-nil then use last match on line for `idlwave-indent-regexp'."
285 :group 'idlwave-code-formatting
286 :type 'boolean)
287
288(defcustom idlwave-fill-comment-line-only t
289 "*If non-nil then auto fill will only operate on comment lines."
290 :group 'idlwave-code-formatting
291 :type 'boolean)
292
293(defcustom idlwave-auto-fill-split-string t
294 "*If non-nil then auto fill will split strings with the IDL `+' operator.
4b1aaa8b
PE
295When the line end falls within a string, string concatenation with the
296'+' operator will be used to distribute a long string over lines.
f32b3b91
CD
297If nil and a string is split then a terminal beep and warning are issued.
298
299This variable is ignored when `idlwave-fill-comment-line-only' is
300non-nil, since in this case code is not auto-filled."
301 :group 'idlwave-code-formatting
302 :type 'boolean)
303
304(defcustom idlwave-split-line-string t
305 "*If non-nil then `idlwave-split-line' will split strings with `+'.
306When the splitting point of a line falls inside a string, split the string
307using the `+' string concatenation operator. If nil and a string is
308split then a terminal beep and warning are issued."
309 :group 'idlwave-code-formatting
310 :type 'boolean)
311
312(defcustom idlwave-no-change-comment ";;;"
313 "*The indentation of a comment that starts with this regular
5a0c3f56 314expression will not be changed. Note that the indentation of a comment
f32b3b91
CD
315at the beginning of a line is never changed."
316 :group 'idlwave-code-formatting
317 :type 'string)
318
319(defcustom idlwave-begin-line-comment nil
320 "*A comment anchored at the beginning of line.
321A comment matching this regular expression will not have its
322indentation changed. If nil the default is \"^;\", i.e., any line
323beginning with a \";\". Expressions for comments at the beginning of
324the line should begin with \"^\"."
325 :group 'idlwave-code-formatting
326 :type '(choice (const :tag "Any line beginning with `;'" nil)
327 'regexp))
328
329(defcustom idlwave-code-comment ";;[^;]"
330 "*A comment that starts with this regular expression on a line by
331itself is indented as if it is a part of IDL code. As a result if
332the comment is not preceded by whitespace it is unchanged."
333 :group 'idlwave-code-formatting
334 :type 'regexp)
335
336;; Comments not matching any of the above will be indented as a
337;; right-margin comment, i.e., to a minimum of `comment-column'.
338
f32b3b91
CD
339;;; Routine Info and Completion ---------------------------------------
340
15e42531
CD
341(defgroup idlwave-routine-info nil
342 "Routine Info options for IDLWAVE mode."
f32b3b91
CD
343 :group 'idlwave)
344
52a244eb
S
345(defcustom idlwave-use-library-catalogs t
346 "*Non-nil means search the IDL path for library catalog files.
347
348These files, named .idlwave_catalog, document routine information for
349individual directories and libraries of IDL .pro files. Many popular
5a0c3f56
JB
350libraries come with catalog files by default, so leaving this on is
351usually a good idea."
52a244eb
S
352 :group 'idlwave-routine-info
353 :type 'boolean)
5e72c6b2
S
354
355(defcustom idlwave-init-rinfo-when-idle-after 10
5a0c3f56
JB
356 "*Seconds of idle time before routine info is automatically initialized.
357Initializing the routine info can take a long time, in particular if a
358large number of library catalogs are involved. When Emacs is idle for
359more than the number of seconds specified by this variable, it starts
360the initialization. The process is split into five steps, in order to
361keep work interruption as short as possible. If one of the steps
362finishes, and no user input has arrived in the mean time, initialization
363proceeds immediately to the next step. A good value for this variable
364is about 1/3 of the time initialization take in your setup. So if you
365have a fast machine and no problems with a slow network connection,
366don't hesitate to set this to 2 seconds. A value of 0 means, don't
367initialize automatically, but instead wait until routine information is
368needed, and initialize then."
5e72c6b2
S
369 :group 'idlwave-routine-info
370 :type 'number)
371
f32b3b91 372(defcustom idlwave-scan-all-buffers-for-routine-info t
15e42531
CD
373 "*Non-nil means, scan buffers for IDL programs when updating info.
374The scanning is done by the command `idlwave-update-routine-info'.
375The following values are allowed:
376
377nil Don't scan any buffers.
5a0c3f56 378t Scan all `idlwave-mode' buffers in the current editing session.
15e42531
CD
379current Scan only the current buffer, but no other buffers."
380 :group 'idlwave-routine-info
381 :type '(choice
382 (const :tag "No buffer" nil)
383 (const :tag "All buffers" t)
384 (const :tag "Current buffer only" 'current)))
f32b3b91
CD
385
386(defcustom idlwave-query-shell-for-routine-info t
387 "*Non-nil means query the shell for info about compiled routines.
388Querying the shell is useful to get information about compiled modules,
389and it is turned on by default. However, when you have a complete library
390scan, this is not necessary."
15e42531 391 :group 'idlwave-routine-info
f32b3b91
CD
392 :type 'boolean)
393
15e42531
CD
394(defcustom idlwave-auto-routine-info-updates
395 '(find-file save-buffer kill-buffer compile-buffer)
396 "*Controls under what circumstances routine info is updated automatically.
397Possible values:
398nil Never
399t All available
5a0c3f56 400\(...) A list of circumstances. Allowed members are:
15e42531
CD
401 find-file Add info for new IDLWAVE buffers.
402 save-buffer Update buffer info when buffer is saved
403 kill-buffer Remove buffer info when buffer gets killed
404 compile-buffer Update shell info after `idlwave-shell-save-and...'"
405 :group 'idlwave-routine-info
406 :type '(choice
407 (const :tag "Never" nil)
408 (const :tag "As often as possible" t)
409 (set :tag "Checklist" :greedy t
410 (const :tag "When visiting a file" find-file)
411 (const :tag "When saving a buffer" save-buffer)
412 (const :tag "After a buffer was killed" kill-buffer)
413 (const :tag "After a buffer was compiled successfully, update shell info" compile-buffer))))
4b1aaa8b 414
15e42531
CD
415(defcustom idlwave-rinfo-max-source-lines 5
416 "*Maximum number of source files displayed in the Routine Info window.
417When an integer, it is the maximum number of source files displayed.
5a0c3f56 418A value of t means to show all source files."
15e42531
CD
419 :group 'idlwave-routine-info
420 :type 'integer)
421
f32b3b91 422(defcustom idlwave-library-path nil
8c43762b 423 "Library path for Windows and MacOS (OS9). Not needed under UNIX.
f66f03de
S
424When selecting the directories to scan for IDL user catalog routine
425info, IDLWAVE can, under UNIX, query the shell for the exact search
426path \(the value of !PATH). However, under Windows and MacOS
8c43762b 427\(pre-OSX), the IDLWAVE shell does not work. In this case, this
f66f03de
S
428variable can be set to specify the paths where IDLWAVE can find PRO
429files. The shell will only be asked for a list of paths when this
430variable is nil. The value is a list of directories. A directory
97610156 431preceded by a `+' will be searched recursively. If you set this
f66f03de
S
432variable on a UNIX system, the shell will not be queried. See also
433`idlwave-system-directory'."
15e42531 434 :group 'idlwave-routine-info
f32b3b91
CD
435 :type '(repeat (directory)))
436
15e42531 437(defcustom idlwave-system-directory ""
52a244eb
S
438 "The IDL system directory for Windows and MacOS. Not needed under
439UNIX. Set this to the value of the `!DIR' system variable in IDL.
440IDLWAVE uses this to find out which of the library routines belong to
441the official system library. All files inside the `lib' subdirectory
442are considered system library files - so don't install private stuff
443in this directory. On UNIX systems, IDLWAVE queries the shell for the
444value of `!DIR'. See also `idlwave-library-path'."
15e42531
CD
445 :group 'idlwave-routine-info
446 :type 'directory)
447
f66f03de 448;; Configuration files
4b1aaa8b 449(defcustom idlwave-config-directory
52a244eb
S
450 (convert-standard-filename "~/.idlwave")
451 "*Directory for configuration files and user-library catalog."
15e42531 452 :group 'idlwave-routine-info
f32b3b91
CD
453 :type 'file)
454
52a244eb 455(defvar idlwave-user-catalog-file "idlusercat.el")
f66f03de 456(defvar idlwave-xml-system-rinfo-converted-file "idl_xml_rinfo.el")
52a244eb
S
457(defvar idlwave-path-file "idlpath.el")
458
459(defvar idlwave-libinfo-file nil
460 "*Obsolete variable, no longer used.")
461
15e42531
CD
462(defcustom idlwave-special-lib-alist nil
463 "Alist of regular expressions matching special library directories.
464When listing routine source locations, IDLWAVE gives a short hint where
4b1aaa8b 465the file defining the routine is located. By default it lists `SystemLib'
15e42531
CD
466for routines in the system library `!DIR/lib' and `Library' for anything
467else. This variable can define additional types. The car of each entry
468is a regular expression matching the file name (they normally will match
469on the path). The cdr is the string to be used as identifier. Max 10
470chars are allowed."
471 :group 'idlwave-routine-info
472 :type '(repeat
473 (cons regexp string)))
474
52a244eb 475(defcustom idlwave-auto-write-paths t
4b1aaa8b 476 "Write out path (!PATH) and system directory (!DIR) info automatically.
52a244eb
S
477Path info is needed to locate library catalog files. If non-nil,
478whenever the path-list changes as a result of shell-query, etc., it is
479written to file. Otherwise, the menu option \"Write Paths\" can be
480used to force a write."
481 :group 'idlwave-routine-info
05a1abfc 482 :type 'boolean)
775591f7 483
15e42531
CD
484(defgroup idlwave-completion nil
485 "Completion options for IDLWAVE mode."
486 :prefix "idlwave"
487 :group 'idlwave)
488
f32b3b91
CD
489(eval-and-compile
490 (defconst idlwave-tmp
491 '(choice :tag "by applying the function"
492 (const upcase)
493 (const downcase)
494 (const capitalize)
495 (const preserve)
496 (symbol :tag "Other"))))
497
f32b3b91
CD
498(defcustom idlwave-completion-case '((routine . upcase)
499 (keyword . upcase)
500 (class . preserve)
501 (method . preserve))
502 "Association list setting the case of completed words.
503
504This variable determines the case (UPPER/lower/Capitalized...) of
505words inserted into the buffer by completion. The preferred case can
506be specified separately for routine names, keywords, classes and
4b1aaa8b 507methods.
f32b3b91
CD
508This alist should therefore have entries for `routine' (normal
509functions and procedures, i.e. non-methods), `keyword', `class', and
510`method'. Plausible values are
511
512upcase upcase whole word, like `BOX_CURSOR'
513downcase downcase whole word, like `read_ppm'
514capitalize capitalize each part, like `Widget_Control'
515preserve preserve case as is, like `IDLgrView'
516
517The value can also be any Emacs Lisp function which transforms the
518case of characters in a string.
519
520A value of `preserve' means that the case of the completed word is
521identical to the way it was written in the definition statement of the
522routine. This was implemented to allow for mixed-case completion, in
523particular of object classes and methods.
524If a completable word is defined in multiple locations, the meaning of
525`preserve' is not unique since the different definitions might be
526cased differently. Therefore IDLWAVE always takes the case of the
527*first* definition it encounters during routine info collection and
528uses the case derived from it consistently.
529
530Note that a lowercase-only string in the buffer will always be completed in
531lower case (but see the variable `idlwave-completion-force-default-case').
532
533After changing this variable, you need to either restart Emacs or press
534`C-u C-c C-i' to update the internal lists."
15e42531 535 :group 'idlwave-completion
f32b3b91
CD
536 :type `(repeat
537 (cons (symbol :tag "Derive completion case for")
538 ,idlwave-tmp)))
539
540(defcustom idlwave-completion-force-default-case nil
541 "*Non-nil means, completion will always honor `idlwave-completion-case'.
542When nil, only the completion of a mixed case or upper case string
543will honor the default settings in `idlwave-completion-case', while
544the completion of lower case strings will be completed entirely in
545lower case."
15e42531 546 :group 'idlwave-completion
f32b3b91
CD
547 :type 'boolean)
548
549(defcustom idlwave-complete-empty-string-as-lower-case nil
550 "*Non-nil means, the empty string is considered downcase for completion.
551The case of what is already in the buffer determines the case of completions.
552When this variable is non-nil, the empty string is considered to be downcase.
553Completing on the empty string then offers downcase versions of the possible
554completions."
15e42531 555 :group 'idlwave-completion
f32b3b91
CD
556 :type 'boolean)
557
558(defvar idlwave-default-completion-case-is-down nil
559 "Obsolete variable. See `idlwave-complete-empty-string-as-lower-case' and
560`idlwave-completion-case'.")
561
562(defcustom idlwave-buffer-case-takes-precedence nil
563 "*Non-nil means, the case of tokens in buffers dominates over system stuff.
564To make this possible, we need to re-case everything each time we update
565the routine info from the buffers. This is slow.
566The default is to consider the case given in the system and library files
567first which makes updating much faster."
15e42531
CD
568 :group 'idlwave-completion
569 :type 'boolean)
570
571(defcustom idlwave-highlight-help-links-in-completion t
572 "*Non-nil means, highlight completions for which system help is available.
573Help can then be accessed with mouse-3.
574This option is only effective when the online help system is installed."
575 :group 'idlwave-completion
f32b3b91
CD
576 :type 'boolean)
577
05a1abfc
CD
578(defcustom idlwave-support-inheritance t
579 "Non-nil means, treat inheritance with completion, online help etc.
cef6cafe 580When nil, IDLWAVE only knows about the native methods and tags of a class,
05a1abfc
CD
581not about inherited ones."
582 :group 'idlwave-routine-info
583 :type 'boolean)
584
5e72c6b2
S
585(defcustom idlwave-keyword-class-inheritance '("^[gs]etproperty$" "^init$")
586 "List of regular expressions for class-driven keyword inheritance.
587Keyword inheritance is often tied to class inheritance by \"chaining\"
588up the class tree. While it cannot be assumed that the presence of an
589_EXTRA or _REF_EXTRA symbol guarantees such chaining will occur, for
590certain methods this assumption is almost always true. The methods
591for which to assume this can be set here."
592 :group 'idlwave-routine-info
593 :type '(repeat (regexp :tag "Match method:")))
4b1aaa8b 594
5e72c6b2 595
f32b3b91
CD
596(defcustom idlwave-completion-show-classes 1
597 "*Number of classes to show when completing object methods and keywords.
598When completing methods or keywords for an object with unknown class,
2e8b9c7d 599the *Completions* buffer will show the valid classes for each completion
f32b3b91
CD
600like this:
601
602MyMethod <Class1,Class2,Class3>
603
604The value of this variable may be nil to inhibit display, or an integer to
605indicate the maximum number of classes to display.
606
607On XEmacs, a full list of classes will also be placed into a `help-echo'
608property on the competion items, so that the list of classes for the current
609item is displayed in the echo area. If the value of this variable is a
610negative integer, the `help-echo' property will be suppressed."
15e42531 611 :group 'idlwave-completion
f32b3b91
CD
612 :type '(choice (const :tag "Don't show" nil)
613 (integer :tag "Number of classes shown" 1)))
614
615(defcustom idlwave-completion-fontify-classes t
616 "*Non-nil means, fontify the classes in completions buffer.
617This makes it easier to distinguish the completion items from the extra
618class info listed. See `idlwave-completion-show-classes'."
15e42531 619 :group 'idlwave-completion
f32b3b91
CD
620 :type 'boolean)
621
622(defcustom idlwave-query-class '((method-default . nil)
623 (keyword-default . nil))
624 "Association list governing specification of object classes for completion.
625
5e72c6b2
S
626When IDLWAVE tries to complete object-oriented methods, it usually
627cannot determine the class of a given object from context. In order
628to provide the user with a correct list of methods or keywords, it
76959b77
S
629needs to determine the appropriate class. IDLWAVE has two ways of
630doing this (well, three ways if you count the shell... see
631`idlwave-shell-query-for-class'):
632
6331. Combine the items of all available classes which contain this
634 method for the purpose of completion. So when completing a method,
635 all methods of all known classes are available, and when completing
636 a keyword, all keywords allowed for this method in any class are
637 shown. This behavior is very much like normal completion and is
638 therefore the default. It works much better than one might think -
639 only for the INIT, GETPROPERTY and SETPROPERTY the keyword lists
640 become uncomfortably long. See also
5e72c6b2 641 `idlwave-completion-show-classes'.
f32b3b91
CD
642
6432. The second possibility is to ask the user on each occasion. To
644 make this less interruptive, IDLWAVE can store the class as a text
645 property on the object operator `->'. For a given object in the
646 source code, class selection will then be needed only once
647 - for example to complete the method. Keywords to the method can
648 then be completed directly, because the class is already known.
649 You will have to turn on the storage of the selected class
650 explicitly with the variable `idlwave-store-inquired-class'.
651
5e72c6b2
S
652This variable allows you to configure IDLWAVE's method and
653method-keyword completion behavior. Its value is an alist, which
654should contain at least two elements: (method-default . VALUE) and
facebc7b 655\(keyword-default . VALUE), where VALUE is either t or nil. These
5e72c6b2
S
656specify if the class should be found during method and keyword
657completion, respectively.
f32b3b91 658
4b1aaa8b 659The alist may have additional entries specifying exceptions from the
f32b3b91
CD
660keyword completion rule for specific methods, like INIT or
661GETPROPERTY. In order to turn on class specification for the INIT
662method, add an entry (\"INIT\" . t). The method name must be ALL-CAPS."
15e42531 663 :group 'idlwave-completion
f32b3b91
CD
664 :type '(list
665 (cons (const method-default)
666 (boolean :tag "Determine class when completing METHODS "))
667 (cons (const keyword-default)
668 (boolean :tag "Determine class when completing KEYWORDS "))
669 (repeat
670 :tag "Exceptions to defaults"
671 :inline t
672 (cons (string :tag "MODULE" :value "")
673 (boolean :tag "Determine class for this method")))))
674
f66f03de 675(defcustom idlwave-store-inquired-class t
f32b3b91
CD
676 "*Non-nil means, store class of a method call as text property on `->'.
677IDLWAVE sometimes has to ask the user for the class associated with a
678particular object method call. This happens during the commands
679`idlwave-routine-info' and `idlwave-complete', depending upon the
680value of the variable `idlwave-query-class'.
681
682When you specify a class, this information can be stored as a text
4b1aaa8b 683property on the `->' arrow in the source code, so that during the same
f32b3b91
CD
684editing session, IDLWAVE will not have to ask again. When this
685variable is non-nil, IDLWAVE will store and reuse the class information.
686The class stored can be checked and removed with `\\[idlwave-routine-info]'
687on the arrow.
688
689The default of this variable is nil, since the result of commands then
690is more predictable. However, if you know what you are doing, it can
691be nice to turn this on.
692
693An arrow which knows the class will be highlighted with
694`idlwave-class-arrow-face'. The command \\[idlwave-routine-info]
695displays (with prefix arg: deletes) the class stored on the arrow
696at point."
15e42531 697 :group 'idlwave-completion
f32b3b91
CD
698 :type 'boolean)
699
700(defcustom idlwave-class-arrow-face 'bold
701 "*Face to highlight object operator arrows `->' which carry a class property.
702When IDLWAVE stores a class name as text property on an object arrow
facebc7b 703\(see variable `idlwave-store-inquired-class', it highlights the arrow
f32b3b91 704with this font in order to remind the user that this arrow is special."
15e42531 705 :group 'idlwave-completion
f32b3b91
CD
706 :type 'symbol)
707
708(defcustom idlwave-resize-routine-help-window t
709 "*Non-nil means, resize the Routine-info *Help* window to fit the content."
15e42531 710 :group 'idlwave-completion
f32b3b91
CD
711 :type 'boolean)
712
713(defcustom idlwave-keyword-completion-adds-equal t
714 "*Non-nil means, completion automatically adds `=' after completed keywords."
15e42531 715 :group 'idlwave-completion
f32b3b91
CD
716 :type 'boolean)
717
718(defcustom idlwave-function-completion-adds-paren t
719 "*Non-nil means, completion automatically adds `(' after completed function.
0ff9b955 720nil means, don't add anything.
f32b3b91
CD
721A value of `2' means, also add the closing parenthesis and position cursor
722between the two."
15e42531 723 :group 'idlwave-completion
f32b3b91
CD
724 :type '(choice (const :tag "Nothing" nil)
725 (const :tag "(" t)
726 (const :tag "()" 2)))
727
728(defcustom idlwave-completion-restore-window-configuration t
729 "*Non-nil means, try to restore the window configuration after completion.
730When completion is not unique, Emacs displays a list of completions.
731This messes up your window configuration. With this variable set, IDLWAVE
732restores the old configuration after successful completion."
15e42531 733 :group 'idlwave-completion
f32b3b91
CD
734 :type 'boolean)
735
736;;; Variables for abbrev and action behavior -----------------------------
737
738(defgroup idlwave-abbrev-and-indent-action nil
739 "IDLWAVE performs actions when expanding abbreviations or indenting lines.
740The variables in this group govern this."
741 :group 'idlwave)
742
743(defcustom idlwave-do-actions nil
744 "*Non-nil means performs actions when indenting.
745The actions that can be performed are listed in `idlwave-indent-action-table'."
746 :group 'idlwave-abbrev-and-indent-action
747 :type 'boolean)
748
749(defcustom idlwave-abbrev-start-char "\\"
750 "*A single character string used to start abbreviations in abbrev mode.
751Possible characters to chose from: ~`\%
752or even '?'. '.' is not a good choice because it can make structure
753field names act like abbrevs in certain circumstances.
754
755Changes to this in `idlwave-mode-hook' will have no effect. Instead a user
756must set it directly using `setq' in the .emacs file before idlwave.el
757is loaded."
758 :group 'idlwave-abbrev-and-indent-action
759 :type 'string)
760
761(defcustom idlwave-surround-by-blank nil
762 "*Non-nil means, enable `idlwave-surround'.
595ab50b 763If non-nil, `=',`<',`>',`&',`,', `->' are surrounded with spaces by
f32b3b91
CD
764`idlwave-surround'.
765See help for `idlwave-indent-action-table' for symbols using `idlwave-surround'.
766
767Also see the default key bindings for keys using `idlwave-surround'.
768Keys are bound and made into actions calling `idlwave-surround' with
769`idlwave-action-and-binding'.
770See help for `idlwave-action-and-binding' for examples.
771
772Also see help for `idlwave-surround'."
773 :group 'idlwave-abbrev-and-indent-action
774 :type 'boolean)
775
776(defcustom idlwave-pad-keyword t
52a244eb
S
777 "*Non-nil means pad '=' in keywords (routine calls or defs) like assignment.
778Whenever `idlwave-surround' is non-nil then this affects how '=' is
779padded for keywords and for variables. If t, pad the same as for
780assignments. If nil then spaces are removed. With any other value,
781spaces are left unchanged."
f32b3b91 782 :group 'idlwave-abbrev-and-indent-action
15e42531
CD
783 :type '(choice
784 (const :tag "Pad like assignments" t)
785 (const :tag "Remove space near `='" nil)
786 (const :tag "Keep space near `='" 'keep)))
f32b3b91
CD
787
788(defcustom idlwave-show-block t
789 "*Non-nil means point blinks to block beginning for `idlwave-show-begin'."
790 :group 'idlwave-abbrev-and-indent-action
791 :type 'boolean)
792
793(defcustom idlwave-expand-generic-end nil
794 "*Non-nil means expand generic END to ENDIF/ENDELSE/ENDWHILE etc."
795 :group 'idlwave-abbrev-and-indent-action
796 :type 'boolean)
797
15e42531
CD
798(defcustom idlwave-reindent-end t
799 "*Non-nil means re-indent line after END was typed."
800 :group 'idlwave-abbrev-and-indent-action
801 :type 'boolean)
802
f32b3b91
CD
803(defcustom idlwave-abbrev-move t
804 "*Non-nil means the abbrev hook can move point.
5a0c3f56 805Set to nil by `idlwave-expand-region-abbrevs'. To see the abbrev
f32b3b91 806definitions, use the command `list-abbrevs', for abbrevs that move
5a0c3f56 807point. Moving point is useful, for example, to place point between
f32b3b91
CD
808parentheses of expanded functions.
809
810See `idlwave-check-abbrev'."
811 :group 'idlwave-abbrev-and-indent-action
812 :type 'boolean)
813
814(defcustom idlwave-abbrev-change-case nil
815 "*Non-nil means all abbrevs will be forced to either upper or lower case.
816If the value t, all expanded abbrevs will be upper case.
817If the value is 'down then abbrevs will be forced to lower case.
818If nil, the case will not change.
819If `idlwave-reserved-word-upcase' is non-nil, reserved words will always be
820upper case, regardless of this variable."
821 :group 'idlwave-abbrev-and-indent-action
822 :type 'boolean)
823
824(defcustom idlwave-reserved-word-upcase nil
825 "*Non-nil means, reserved words will be made upper case via abbrev expansion.
826If nil case of reserved words is controlled by `idlwave-abbrev-change-case'.
827Has effect only if in abbrev-mode."
828 :group 'idlwave-abbrev-and-indent-action
829 :type 'boolean)
830
831;;; Action/Expand Tables.
832;;
833;; The average user may have difficulty modifying this directly. It
834;; can be modified/set in idlwave-mode-hook, but it is easier to use
835;; idlwave-action-and-binding. See help for idlwave-action-and-binding for
836;; examples of how to add an action.
837;;
838;; The action table is used by `idlwave-indent-line' whereas both the
839;; action and expand tables are used by `idlwave-indent-and-action'. In
840;; general, the expand table is only used when a line is explicitly
841;; indented. Whereas, in addition to being used when the expand table
842;; is used, the action table is used when a line is indirectly
843;; indented via line splitting, auto-filling or a new line creation.
844;;
845;; Example actions:
846;;
847;; Capitalize system vars
848;; (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)
849;;
850;; Capitalize procedure name
851;; (idlwave-action-and-binding "\\<\\(pro\\|function\\)\\>[ \t]*\\<"
852;; '(capitalize-word 1) t)
853;;
854;; Capitalize common block name
855;; (idlwave-action-and-binding "\\<common\\>[ \t]+\\<"
856;; '(capitalize-word 1) t)
857;; Capitalize label
858;; (idlwave-action-and-binding (concat "^[ \t]*" idlwave-label)
859;; '(capitalize-word -1) t)
860
861(defvar idlwave-indent-action-table nil
862 "*Associated array containing action lists of search string (car),
5a0c3f56 863and function as a cdr. This table is used by `idlwave-indent-line'.
f32b3b91
CD
864See documentation for `idlwave-do-action' for a complete description of
865the action lists.
866
867Additions to the table are made with `idlwave-action-and-binding' when a
868binding is not requested.
869See help on `idlwave-action-and-binding' for examples.")
870
871(defvar idlwave-indent-expand-table nil
872 "*Associated array containing action lists of search string (car),
5a0c3f56
JB
873and function as a cdr. The table is used by the
874`idlwave-indent-and-action' function. See documentation for
f32b3b91
CD
875`idlwave-do-action' for a complete description of the action lists.
876
877Additions to the table are made with `idlwave-action-and-binding' when a
878binding is requested.
879See help on `idlwave-action-and-binding' for examples.")
880
881;;; Documentation header and history keyword ---------------------------------
882
883(defgroup idlwave-documentation nil
884 "Options for documenting IDLWAVE files."
885 :group 'idlwave)
886
887;; FIXME: make defcustom?
888(defvar idlwave-file-header
889 (list nil
890 ";+
891; NAME:
892;
893;
894;
895; PURPOSE:
896;
897;
898;
899; CATEGORY:
900;
901;
902;
903; CALLING SEQUENCE:
904;
905;
906;
907; INPUTS:
908;
909;
910;
911; OPTIONAL INPUTS:
912;
913;
914;
915; KEYWORD PARAMETERS:
916;
917;
918;
919; OUTPUTS:
920;
921;
922;
923; OPTIONAL OUTPUTS:
924;
925;
926;
927; COMMON BLOCKS:
928;
929;
930;
931; SIDE EFFECTS:
932;
933;
934;
935; RESTRICTIONS:
936;
937;
938;
939; PROCEDURE:
940;
941;
942;
943; EXAMPLE:
944;
945;
946;
947; MODIFICATION HISTORY:
948;
949;-
950")
951 "*A list (PATHNAME STRING) specifying the doc-header template to use for
5a0c3f56
JB
952summarizing a file. If PATHNAME is non-nil then this file will be included.
953Otherwise STRING is used. If nil, the file summary will be omitted.
f32b3b91
CD
954For example you might set PATHNAME to the path for the
955lib_template.pro file included in the IDL distribution.")
956
f66f03de 957(defcustom idlwave-header-to-beginning-of-file t
5e72c6b2
S
958 "*Non-nil means, the documentation header will always be at start of file.
959When nil, the header is positioned between the PRO/FUNCTION line of
960the current routine and the code, allowing several routine headers in
961a file."
962 :group 'idlwave-documentation
963 :type 'boolean)
964
f32b3b91
CD
965(defcustom idlwave-timestamp-hook 'idlwave-default-insert-timestamp
966 "*The hook function used to update the timestamp of a function."
967 :group 'idlwave-documentation
968 :type 'function)
969
970(defcustom idlwave-doc-modifications-keyword "HISTORY"
971 "*The modifications keyword to use with the log documentation commands.
972A ':' is added to the keyword end.
973Inserted by doc-header and used to position logs by doc-modification.
974If nil it will not be inserted."
975 :group 'idlwave-documentation
976 :type 'string)
977
978(defcustom idlwave-doclib-start "^;+\\+"
979 "*Regexp matching the start of a document library header."
980 :group 'idlwave-documentation
981 :type 'regexp)
982
983(defcustom idlwave-doclib-end "^;+-"
984 "*Regexp matching the end of a document library header."
985 :group 'idlwave-documentation
986 :type 'regexp)
987
988;;; External Programs -------------------------------------------------------
989
990(defgroup idlwave-external-programs nil
05a1abfc 991 "Path locations of external commands used by IDLWAVE."
f32b3b91
CD
992 :group 'idlwave)
993
f32b3b91 994(defcustom idlwave-shell-explicit-file-name "idl"
5e72c6b2 995 "*If non-nil, this is the command to run IDL.
f32b3b91 996Should be an absolute file path or path relative to the current environment
5e72c6b2 997execution search path. If you want to specify command line switches
5a0c3f56 998for the IDL program, use `idlwave-shell-command-line-options'.
5e72c6b2
S
999
1000I know the name of this variable is badly chosen, but I cannot change
5a0c3f56 1001it without compromising backwards-compatibility."
f32b3b91
CD
1002 :group 'idlwave-external-programs
1003 :type 'string)
1004
f32b3b91 1005(defcustom idlwave-shell-command-line-options nil
5e72c6b2
S
1006 "*A list of command line options for calling the IDL program.
1007Since IDL is executed directly without going through a shell like /bin/sh,
1008this should be a list of strings like '(\"-rt=file\" \"-nw\") with a separate
1009string for each argument. But you may also give a single string which
1010contains the options whitespace-separated. Emacs will be kind enough to
1011split it for you."
1012 :type '(choice
1013 string
1014 (repeat (string :value "")))
f32b3b91
CD
1015 :group 'idlwave-external-programs)
1016
1017(defcustom idlwave-help-application "idlhelp"
f66f03de
S
1018 "*The external application providing reference help for programming.
1019Obsolete, if the IDL Assistant is being used for help."
f32b3b91
CD
1020 :group 'idlwave-external-programs
1021 :type 'string)
1022
05a1abfc
CD
1023;;; Some Shell variables which must be defined here.-----------------------
1024
1025(defcustom idlwave-shell-debug-modifiers '()
1026 "List of modifiers to be used for the debugging commands.
1027Will be used to bind debugging commands in the shell buffer and in all
1028source buffers. These are additional convenience bindings, the debugging
1029commands are always available with the `C-c C-d' prefix.
1030If you set this to '(control shift), this means setting a breakpoint will
1031be on `C-S-b', compiling a source file on `C-S-c' etc. Possible modifiers
1032are `control', `meta', `super', `hyper', `alt', and `shift'."
1033 :group 'idlwave-shell-general-setup
1034 :type '(set :tag "Specify modifiers"
1035 (const control)
1036 (const meta)
1037 (const super)
1038 (const hyper)
1039 (const alt)
1040 (const shift)))
1041
1042(defcustom idlwave-shell-automatic-start nil
5a0c3f56 1043 "*If non-nil attempt invoke `idlwave-shell' if not already running.
05a1abfc
CD
1044This is checked when an attempt to send a command to an
1045IDL process is made."
1046 :group 'idlwave-shell-general-setup
1047 :type 'boolean)
1048
f32b3b91
CD
1049;;; Miscellaneous variables -------------------------------------------------
1050
1051(defgroup idlwave-misc nil
1052 "Miscellaneous options for IDLWAVE mode."
8ec3bce0 1053 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
f32b3b91
CD
1054 :group 'idlwave)
1055
1056(defcustom idlwave-startup-message t
1057 "*Non-nil displays a startup message when `idlwave-mode' is first called."
1058 :group 'idlwave-misc
1059 :type 'boolean)
1060
4b1aaa8b 1061(defcustom idlwave-default-font-lock-items
facebc7b 1062 '(pros-and-functions batch-files idlwave-idl-keywords label goto
f32b3b91
CD
1063 common-blocks class-arrows)
1064 "Items which should be fontified on the default fontification level 2.
1065IDLWAVE defines 3 levels of fontification. Level 1 is very little, level 3
1066is everything and level 2 is specified by this list.
5a0c3f56
JB
1067This variable must be set before IDLWAVE gets loaded.
1068It is a list of symbols; the following symbols are allowed:
f32b3b91
CD
1069
1070pros-and-functions Procedure and Function definitions
1071batch-files Batch Files
facebc7b 1072idlwave-idl-keywords IDL Keywords
f32b3b91
CD
1073label Statement Labels
1074goto Goto Statements
1075common-blocks Common Blocks
1076keyword-parameters Keyword Parameters in routine definitions and calls
1077system-variables System Variables
1078fixme FIXME: Warning in comments (on XEmacs only v. 21.0 and up)
1079class-arrows Object Arrows with class property"
1080 :group 'idlwave-misc
1081 :type '(set
1082 :inline t :greedy t
1083 (const :tag "Procedure and Function definitions" pros-and-functions)
facebc7b
S
1084 (const :tag "Batch Files" batch-files)
1085 (const :tag "IDL Keywords (reserved words)" idlwave-idl-keywords)
1086 (const :tag "Statement Labels" label)
1087 (const :tag "Goto Statements" goto)
1088 (const :tag "Tags in Structure Definition" structtag)
1089 (const :tag "Structure Name" structname)
1090 (const :tag "Common Blocks" common-blocks)
1091 (const :tag "Keyword Parameters" keyword-parameters)
1092 (const :tag "System Variables" system-variables)
1093 (const :tag "FIXME: Warning" fixme)
f32b3b91
CD
1094 (const :tag "Object Arrows with class property " class-arrows)))
1095
1096(defcustom idlwave-mode-hook nil
1097 "Normal hook. Executed when a buffer is put into `idlwave-mode'."
1098 :group 'idlwave-misc
1099 :type 'hook)
1100
1101(defcustom idlwave-load-hook nil
1102 "Normal hook. Executed when idlwave.el is loaded."
1103 :group 'idlwave-misc
1104 :type 'hook)
1105
15e42531
CD
1106(defvar idlwave-experimental nil
1107 "Non-nil means turn on a few experimental features.
1108This variable is only for the maintainer, to test difficult stuff,
1109while still distributing stable releases.
1110As a user, you should not set this to t.")
1111
f32b3b91
CD
1112;;;
1113;;; End customization variables section
1114;;;
1115
1116;;; Non customization variables
1117
1118;;; font-lock mode - Additions by Phil Williams, Ulrik Dickow and
52a244eb 1119;;; Simon Marshall <simon_at_gnu.ai.mit.edu>
f32b3b91
CD
1120;;; and Carsten Dominik...
1121
76959b77 1122;; The following are the reserved words in IDL. Maybe we should
4b1aaa8b 1123;; highlight some more stuff as well?
76959b77
S
1124;; Procedure declarations. Fontify keyword plus procedure name.
1125(defvar idlwave-idl-keywords
4b1aaa8b 1126 ;; To update this regexp, update the list of keywords and
76959b77 1127 ;; evaluate the form.
4b1aaa8b 1128 ;; (insert
76959b77 1129 ;; (prin1-to-string
4b1aaa8b 1130 ;; (concat
76959b77 1131 ;; "\\<\\("
4b1aaa8b 1132 ;; (regexp-opt
52a244eb 1133 ;; '("||" "&&" "and" "or" "xor" "not"
4b1aaa8b 1134 ;; "eq" "ge" "gt" "le" "lt" "ne"
76959b77 1135 ;; "for" "do" "endfor"
4b1aaa8b 1136 ;; "if" "then" "endif" "else" "endelse"
76959b77
S
1137 ;; "case" "of" "endcase"
1138 ;; "switch" "break" "continue" "endswitch"
1139 ;; "begin" "end"
1140 ;; "repeat" "until" "endrep"
4b1aaa8b 1141 ;; "while" "endwhile"
76959b77
S
1142 ;; "goto" "return"
1143 ;; "inherits" "mod"
1144 ;; "compile_opt" "forward_function"
1145 ;; "on_error" "on_ioerror")) ; on_error is not officially reserved
1146 ;; "\\)\\>")))
52a244eb
S
1147 "\\<\\(&&\\|and\\|b\\(egin\\|reak\\)\\|c\\(ase\\|o\\(mpile_opt\\|ntinue\\)\\)\\|do\\|e\\(lse\\|nd\\(case\\|else\\|for\\|if\\|rep\\|switch\\|while\\)?\\|q\\)\\|for\\(ward_function\\)?\\|g\\(oto\\|[et]\\)\\|i\\(f\\|nherits\\)\\|l[et]\\|mod\\|n\\(e\\|ot\\)\\|o\\(n_\\(error\\|ioerror\\)\\|[fr]\\)\\|re\\(peat\\|turn\\)\\|switch\\|then\\|until\\|while\\|xor\\|||\\)\\>")
1148
76959b77 1149
facebc7b 1150(let* (;; Procedure declarations. Fontify keyword plus procedure name.
f32b3b91
CD
1151 ;; Function declarations. Fontify keyword plus function name.
1152 (pros-and-functions
1153 '("\\<\\(function\\|pro\\)\\>[ \t]+\\(\\sw+\\(::\\sw+\\)?\\)"
1154 (1 font-lock-keyword-face)
1155 (2 font-lock-function-name-face nil t)))
1156
1157 ;; Common blocks
1158 (common-blocks
1159 '("\\<\\(common\\)\\>[ \t]*\\(\\sw+\\)?[ \t]*,?"
1160 (1 font-lock-keyword-face) ; "common"
1161 (2 font-lock-reference-face nil t) ; block name
f66f03de 1162 ("[ \t]*\\(\\sw+\\)[ ,]*"
f32b3b91 1163 ;; Start with point after block name and comma
4b1aaa8b 1164 (goto-char (match-end 0)) ; needed for XEmacs, could be nil
f32b3b91
CD
1165 nil
1166 (1 font-lock-variable-name-face) ; variable names
1167 )))
1168
1169 ;; Batch files
1170 (batch-files
1171 '("^[ \t]*\\(@[^ \t\n]+\\)" (1 font-lock-string-face)))
1172
1173 ;; FIXME warning.
1174 (fixme
1175 '("\\<FIXME:" (0 font-lock-warning-face t)))
1176
1177 ;; Labels
1178 (label
1179 '("^[ \t]*\\([a-zA-Z]\\sw*:\\)" (1 font-lock-reference-face)))
1180
1181 ;; The goto statement and its label
1182 (goto
1183 '("\\(goto\\)[ \t]*,[ \t]*\\([a-zA-Z]\\sw*\\)"
1184 (1 font-lock-keyword-face)
1185 (2 font-lock-reference-face)))
1186
52a244eb
S
1187 ;; Tags in structure definitions. Note that this definition
1188 ;; actually collides with labels, so we have to use the same
1189 ;; face. It also matches named subscript ranges,
1190 ;; e.g. vec{bottom:top]. No good way around this.
05a1abfc
CD
1191 (structtag
1192 '("\\<\\([a-zA-Z][a-zA-Z0-9_]*:\\)[^:]" (1 font-lock-reference-face)))
1193
1194 ;; Structure names
1195 (structname
1196 '("\\({\\|\\<inherits\\s-\\)\\s-*\\([a-zA-Z][a-zA-Z0-9_]*\\)[},\t \n]"
1197 (2 font-lock-function-name-face)))
1198
52a244eb 1199 ;; Keyword parameters, like /xlog or ,xrange=[]
97610156 1200 ;; This is anchored to the comma preceding the keyword.
595ab50b
CD
1201 ;; Treats continuation lines, works only during whole buffer
1202 ;; fontification. Slow, use it only in fancy fontification.
f32b3b91 1203 (keyword-parameters
0dc2be2f
S
1204 '("\\(,\\|[a-zA-Z0-9_](\\)[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\([ \t]*\\(;.*\\)?\n\\)*[ \t]*\\)?\\(/[a-zA-Z_]\\sw*\\|[a-zA-Z_]\\sw*[ \t]*=\\)"
1205 (6 font-lock-reference-face)))
f32b3b91 1206
595ab50b 1207 ;; System variables start with a bang.
f32b3b91 1208 (system-variables
15e42531 1209 '("\\(![a-zA-Z_0-9]+\\(\\.\\sw+\\)?\\)"
f32b3b91
CD
1210 (1 font-lock-variable-name-face)))
1211
1212 ;; Special and unusual operators (not used because too noisy)
8d222148
SM
1213 ;; (special-operators
1214 ;; '("[<>#]" (0 font-lock-keyword-face)))
f32b3b91
CD
1215
1216 ;; All operators (not used because too noisy)
8d222148
SM
1217 ;; (all-operators
1218 ;; '("[-*^#+<>/]" (0 font-lock-keyword-face)))
4b1aaa8b 1219
f32b3b91
CD
1220 ;; Arrows with text property `idlwave-class'
1221 (class-arrows
facebc7b
S
1222 '(idlwave-match-class-arrows (0 idlwave-class-arrow-face))))
1223
1224 (defconst idlwave-font-lock-keywords-1
1225 (list pros-and-functions batch-files)
1226 "Subdued level highlighting for IDLWAVE mode.")
f32b3b91 1227
facebc7b
S
1228 (defconst idlwave-font-lock-keywords-2
1229 (mapcar 'symbol-value idlwave-default-font-lock-items)
1230 "Medium level highlighting for IDLWAVE mode.")
f32b3b91 1231
facebc7b 1232 (defconst idlwave-font-lock-keywords-3
f32b3b91
CD
1233 (list pros-and-functions
1234 batch-files
76959b77 1235 idlwave-idl-keywords
f32b3b91 1236 label goto
05a1abfc
CD
1237 structtag
1238 structname
f32b3b91
CD
1239 common-blocks
1240 keyword-parameters
1241 system-variables
facebc7b
S
1242 class-arrows)
1243 "Gaudy level highlighting for IDLWAVE mode."))
f32b3b91
CD
1244
1245(defun idlwave-match-class-arrows (limit)
1246 ;; Match an object arrow with class property
1247 (and idlwave-store-inquired-class
1248 (re-search-forward "->" limit 'limit)
1249 (get-text-property (match-beginning 0) 'idlwave-class)))
1250
1251(defvar idlwave-font-lock-keywords idlwave-font-lock-keywords-2
1252 "Default expressions to highlight in IDLWAVE mode.")
1253
1254(defvar idlwave-font-lock-defaults
1255 '((idlwave-font-lock-keywords
4b1aaa8b 1256 idlwave-font-lock-keywords-1
f32b3b91
CD
1257 idlwave-font-lock-keywords-2
1258 idlwave-font-lock-keywords-3)
4b1aaa8b
PE
1259 nil t
1260 ((?$ . "w") (?_ . "w") (?. . "w") (?| . "w") (?& . "w"))
f32b3b91
CD
1261 beginning-of-line))
1262
4b1aaa8b 1263(put 'idlwave-mode 'font-lock-defaults
f32b3b91
CD
1264 idlwave-font-lock-defaults) ; XEmacs
1265
1266(defconst idlwave-comment-line-start-skip "^[ \t]*;"
1267 "Regexp to match the start of a full-line comment.
1268That is the _beginning_ of a line containing a comment delimiter `;' preceded
1269only by whitespace.")
1270
4b1aaa8b 1271(defconst idlwave-begin-block-reg
05a1abfc 1272 "\\<\\(pro\\|function\\|begin\\|case\\|switch\\)\\>"
5a0c3f56
JB
1273 "Regular expression to find the beginning of a block.
1274The case does not matter. The search skips matches in comments.")
f32b3b91 1275
52a244eb 1276(defconst idlwave-begin-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\`"
5a0c3f56
JB
1277 "Regular expression to find the beginning of a unit.
1278The case does not matter.")
f32b3b91 1279
52a244eb 1280(defconst idlwave-end-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\'"
f32b3b91 1281 "Regular expression to find the line that indicates the end of unit.
5a0c3f56
JB
1282This line is the end of buffer or the start of another unit.
1283The case does not matter. The search skips matches in comments.")
f32b3b91
CD
1284
1285(defconst idlwave-continue-line-reg "\\<\\$"
1286 "Regular expression to match a continued line.")
1287
1288(defconst idlwave-end-block-reg
05a1abfc 1289 "\\<end\\(\\|case\\|switch\\|else\\|for\\|if\\|rep\\|while\\)\\>"
5a0c3f56
JB
1290 "Regular expression to find the end of a block.
1291The case does not matter. The search skips matches in comments.")
f32b3b91
CD
1292
1293(defconst idlwave-block-matches
1294 '(("pro" . "end")
1295 ("function" . "end")
1296 ("case" . "endcase")
1297 ("else" . "endelse")
1298 ("for" . "endfor")
1299 ("then" . "endif")
1300 ("repeat" . "endrep")
05a1abfc 1301 ("switch" . "endswitch")
f32b3b91
CD
1302 ("while" . "endwhile"))
1303 "Matches between statements and the corresponding END variant.
1304The cars are the reserved words starting a block. If the block really
1305begins with BEGIN, the cars are the reserved words before the begin
1306which can be used to identify the block type.
1307This is used to check for the correct END type, to close blocks and
1308to expand generic end statements to their detailed form.")
1309
1310(defconst idlwave-block-match-regexp
1311 "\\<\\(else\\|for\\|then\\|repeat\\|while\\)\\>"
1312"Regular expression matching reserved words which can stand before
1313blocks starting with a BEGIN statement. The matches must have associations
5a0c3f56 1314`idlwave-block-matches'.")
f32b3b91 1315
52a244eb 1316(defconst idlwave-identifier "[a-zA-Z_][a-zA-Z0-9$_]*"
f32b3b91
CD
1317 "Regular expression matching an IDL identifier.")
1318
1319(defconst idlwave-sysvar (concat "!" idlwave-identifier)
1320 "Regular expression matching IDL system variables.")
1321
1322(defconst idlwave-variable (concat idlwave-identifier "\\|" idlwave-sysvar)
1323 "Regular expression matching IDL variable names.")
1324
1325(defconst idlwave-label (concat idlwave-identifier ":")
1326 "Regular expression matching IDL labels.")
1327
52a244eb
S
1328(defconst idlwave-method-call (concat idlwave-identifier "\\s *->"
1329 "\\(\\s *" idlwave-identifier "::\\)?"
1330))
1331
f32b3b91
CD
1332(defconst idlwave-statement-match
1333 (list
aa87aafc 1334 ;; "endif else" is the only possible "end" that can be
f32b3b91
CD
1335 ;; followed by a statement on the same line.
1336 '(endelse . ("end\\(\\|if\\)\\s +else" "end\\(\\|if\\)\\s +else"))
1337 ;; all other "end"s can not be followed by a statement.
1338 (cons 'end (list idlwave-end-block-reg nil))
1339 '(if . ("if\\>" "then"))
1340 '(for . ("for\\>" "do"))
1341 '(begin . ("begin\\>" nil))
1342 '(pdef . ("pro\\>\\|function\\>" nil))
1343 '(while . ("while\\>" "do"))
1344 '(repeat . ("repeat\\>" "repeat"))
1345 '(goto . ("goto\\>" nil))
1346 '(case . ("case\\>" nil))
05a1abfc 1347 '(switch . ("switch\\>" nil))
4b1aaa8b 1348 (cons 'call (list (concat "\\(" idlwave-variable "\\) *= *"
52a244eb
S
1349 "\\(" idlwave-method-call "\\s *\\)?"
1350 idlwave-identifier
1351 "\\s *(") nil))
4b1aaa8b 1352 (cons 'call (list (concat
52a244eb 1353 "\\(" idlwave-method-call "\\s *\\)?"
4b1aaa8b 1354 idlwave-identifier
52a244eb 1355 "\\( *\\($\\|\\$\\)\\|\\s *,\\)") nil))
4b1aaa8b 1356 (cons 'assign (list (concat
52a244eb 1357 "\\(" idlwave-variable "\\) *=") nil)))
4b1aaa8b 1358
f32b3b91 1359 "Associated list of statement matching regular expressions.
5a0c3f56
JB
1360Each regular expression matches the start of an IDL statement.
1361The first element of each association is a symbol giving the statement
f32b3b91
CD
1362type. The associated value is a list. The first element of this list
1363is a regular expression matching the start of an IDL statement for
1364identifying the statement type. The second element of this list is a
1365regular expression for finding a substatement for the type. The
1366substatement starts after the end of the found match modulo
1367whitespace. If it is nil then the statement has no substatement. The
1368list order matters since matching an assignment statement exactly is
1369not possible without parsing. Thus assignment statement become just
5a0c3f56 1370the leftover unidentified statements containing an equal sign.")
f32b3b91 1371
f44379e7 1372;; FIXME: This var seems to only ever be set, but never actually used!
f32b3b91
CD
1373(defvar idlwave-fill-function 'auto-fill-function
1374 "IDL mode auto fill function.")
1375
1376(defvar idlwave-comment-indent-function 'comment-indent-function
1377 "IDL mode comment indent function.")
1378
1379;; Note that this is documented in the v18 manuals as being a string
1380;; of length one rather than a single character.
1381;; The code in this file accepts either format for compatibility.
4b1aaa8b 1382(defvar idlwave-comment-indent-char ?\
f32b3b91
CD
1383 "Character to be inserted for IDL comment indentation.
1384Normally a space.")
1385
1386(defconst idlwave-continuation-char ?$
1387 "Character which is inserted as a last character on previous line by
1388 \\[idlwave-split-line] to begin a continuation line. Normally $.")
1389
e08734e2 1390(defconst idlwave-mode-version "6.1_em22")
f32b3b91
CD
1391
1392(defmacro idlwave-keyword-abbrev (&rest args)
1393 "Creates a function for abbrev hooks to call `idlwave-check-abbrev' with args."
8a946354 1394 `(quote (lambda ()
5e72c6b2 1395 ,(append '(idlwave-check-abbrev) args))))
f32b3b91
CD
1396
1397;; If I take the time I can replace idlwave-keyword-abbrev with
1398;; idlwave-code-abbrev and remove the quoted abbrev check from
1399;; idlwave-check-abbrev. Then, e.g, (idlwave-keyword-abbrev 0 t) becomes
1400;; (idlwave-code-abbrev idlwave-check-abbrev 0 t). In fact I should change
1401;; the name of idlwave-check-abbrev to something like idlwave-modify-abbrev.
1402
1403(defmacro idlwave-code-abbrev (&rest args)
1404 "Creates a function for abbrev hooks that ensures abbrevs are not quoted.
1405Specifically, if the abbrev is in a comment or string it is unexpanded.
1406Otherwise ARGS forms a list that is evaluated."
8d222148
SM
1407 ;; FIXME: it would probably be better to rely on the new :enable-function
1408 ;; to enforce the "don't expand in comments or strings".
1409 `(lambda ()
1410 ,(prin1-to-string args) ;; Puts the code in the doc string
1411 (if (idlwave-quoted)
1412 (progn (unexpand-abbrev) nil)
1413 ,(append args))))
1414
1415(autoload 'idlwave-shell "idlw-shell"
1416 "Run an inferior IDL, with I/O through buffer `(idlwave-shell-buffer)'." t)
1417(autoload 'idlwave-shell-send-command "idlw-shell")
1418(autoload 'idlwave-shell-recenter-shell-window "idlw-shell"
1419 "Run `idlwave-shell' and switch back to current window" t)
1420(autoload 'idlwave-shell-save-and-run "idlw-shell"
1421 "Save and run buffer under the shell." t)
1422(autoload 'idlwave-shell-break-here "idlw-shell"
1423 "Set breakpoint in current line." t)
1424(autoload 'idlwave-shell-run-region "idlw-shell"
1425 "Compile and run the region." t)
f32b3b91 1426
8d222148
SM
1427(fset 'idlwave-debug-map (make-sparse-keymap))
1428
1429(defvar idlwave-mode-map
1430 (let ((map (make-sparse-keymap)))
1431 (define-key map "\C-c " 'idlwave-hard-tab)
1432 (define-key map [(control tab)] 'idlwave-hard-tab)
1433 ;;(define-key map "\C-c\C- " 'idlwave-hard-tab)
1434 (define-key map "'" 'idlwave-show-matching-quote)
1435 (define-key map "\"" 'idlwave-show-matching-quote)
1436 (define-key map "\C-g" 'idlwave-keyboard-quit)
1437 (define-key map "\C-c;" 'idlwave-toggle-comment-region)
1438 (define-key map "\C-\M-a" 'idlwave-beginning-of-subprogram)
1439 (define-key map "\C-\M-e" 'idlwave-end-of-subprogram)
1440 (define-key map "\C-c{" 'idlwave-beginning-of-block)
1441 (define-key map "\C-c}" 'idlwave-end-of-block)
1442 (define-key map "\C-c]" 'idlwave-close-block)
1443 (define-key map [(meta control h)] 'idlwave-mark-subprogram)
1444 (define-key map "\M-\C-n" 'idlwave-forward-block)
1445 (define-key map "\M-\C-p" 'idlwave-backward-block)
1446 (define-key map "\M-\C-d" 'idlwave-down-block)
1447 (define-key map "\M-\C-u" 'idlwave-backward-up-block)
1448 (define-key map "\M-\r" 'idlwave-split-line)
1449 (define-key map "\M-\C-q" 'idlwave-indent-subprogram)
1450 (define-key map "\C-c\C-p" 'idlwave-previous-statement)
1451 (define-key map "\C-c\C-n" 'idlwave-next-statement)
1452 ;; (define-key map "\r" 'idlwave-newline)
1453 ;; (define-key map "\t" 'idlwave-indent-line)
1454 (define-key map [(shift iso-lefttab)] 'idlwave-indent-statement)
1455 (define-key map "\C-c\C-a" 'idlwave-auto-fill-mode)
1456 (define-key map "\M-q" 'idlwave-fill-paragraph)
1457 (define-key map "\M-s" 'idlwave-edit-in-idlde)
1458 (define-key map "\C-c\C-h" 'idlwave-doc-header)
1459 (define-key map "\C-c\C-m" 'idlwave-doc-modification)
1460 (define-key map "\C-c\C-c" 'idlwave-case)
1461 (define-key map "\C-c\C-d" 'idlwave-debug-map)
1462 (when (and (listp idlwave-shell-debug-modifiers)
1463 (not (equal idlwave-shell-debug-modifiers '())))
1464 ;; Bind the debug commands also with the special modifiers.
1465 (let ((shift (memq 'shift idlwave-shell-debug-modifiers))
1466 (mods-noshift
1467 (delq 'shift (copy-sequence idlwave-shell-debug-modifiers))))
1468 (define-key map
1469 (vector (append mods-noshift (list (if shift ?C ?c))))
1470 'idlwave-shell-save-and-run)
1471 (define-key map
1472 (vector (append mods-noshift (list (if shift ?B ?b))))
1473 'idlwave-shell-break-here)
1474 (define-key map
1475 (vector (append mods-noshift (list (if shift ?E ?e))))
1476 'idlwave-shell-run-region)))
1477 (define-key map "\C-c\C-d\C-c" 'idlwave-shell-save-and-run)
1478 (define-key map "\C-c\C-d\C-b" 'idlwave-shell-break-here)
1479 (define-key map "\C-c\C-d\C-e" 'idlwave-shell-run-region)
1480 (define-key map "\C-c\C-f" 'idlwave-for)
1481 ;; (define-key map "\C-c\C-f" 'idlwave-function)
1482 ;; (define-key map "\C-c\C-p" 'idlwave-procedure)
1483 (define-key map "\C-c\C-r" 'idlwave-repeat)
1484 (define-key map "\C-c\C-w" 'idlwave-while)
1485 (define-key map "\C-c\C-k" 'idlwave-kill-autoloaded-buffers)
1486 (define-key map "\C-c\C-s" 'idlwave-shell)
1487 (define-key map "\C-c\C-l" 'idlwave-shell-recenter-shell-window)
1488 (define-key map "\C-c\C-b" 'idlwave-list-buffer-load-path-shadows)
1489 (define-key map "\C-c\C-v" 'idlwave-find-module)
1490 (define-key map "\C-c\C-t" 'idlwave-find-module-this-file)
1491 (define-key map "\C-c?" 'idlwave-routine-info)
1492 (define-key map "\M-?" 'idlwave-context-help)
1493 (define-key map [(control meta ?\?)]
1494 'idlwave-help-assistant-help-with-topic)
1495 ;; Pickup both forms of Esc/Meta binding
1496 (define-key map [(meta tab)] 'idlwave-complete)
1497 (define-key map [?\e?\t] 'idlwave-complete)
1498 (define-key map "\M-\C-i" 'idlwave-complete)
1499 (define-key map "\C-c\C-i" 'idlwave-update-routine-info)
1500 (define-key map "\C-c=" 'idlwave-resolve)
1501 (define-key map
1502 (if (featurep 'xemacs) [(shift button3)] [(shift mouse-3)])
1503 'idlwave-mouse-context-help)
1504 map)
f32b3b91
CD
1505 "Keymap used in IDL mode.")
1506
8d222148
SM
1507(defvar idlwave-mode-syntax-table
1508 (let ((st (make-syntax-table)))
1509 (modify-syntax-entry ?+ "." st)
1510 (modify-syntax-entry ?- "." st)
1511 (modify-syntax-entry ?* "." st)
1512 (modify-syntax-entry ?/ "." st)
1513 (modify-syntax-entry ?^ "." st)
1514 (modify-syntax-entry ?# "." st)
1515 (modify-syntax-entry ?= "." st)
1516 (modify-syntax-entry ?% "." st)
1517 (modify-syntax-entry ?< "." st)
1518 (modify-syntax-entry ?> "." st)
1519 (modify-syntax-entry ?\' "\"" st)
1520 (modify-syntax-entry ?\" "\"" st)
1521 (modify-syntax-entry ?\\ "." st)
1522 (modify-syntax-entry ?_ "_" st)
1523 (modify-syntax-entry ?{ "(}" st)
1524 (modify-syntax-entry ?} "){" st)
1525 (modify-syntax-entry ?$ "_" st)
1526 (modify-syntax-entry ?. "." st)
1527 (modify-syntax-entry ?\; "<" st)
1528 (modify-syntax-entry ?\n ">" st)
1529 (modify-syntax-entry ?\f ">" st)
1530 st)
f32b3b91
CD
1531 "Syntax table in use in `idlwave-mode' buffers.")
1532
f32b3b91 1533(defvar idlwave-find-symbol-syntax-table
8d222148
SM
1534 (let ((st (copy-syntax-table idlwave-mode-syntax-table)))
1535 (modify-syntax-entry ?$ "w" st)
1536 (modify-syntax-entry ?_ "w" st)
1537 (modify-syntax-entry ?! "w" st)
1538 (modify-syntax-entry ?. "w" st)
1539 st)
f32b3b91
CD
1540 "Syntax table that treats symbol characters as word characters.")
1541
76959b77
S
1542(defmacro idlwave-with-special-syntax (&rest body)
1543 "Execute BODY with a different syntax table."
05a1abfc
CD
1544 `(let ((saved-syntax (syntax-table)))
1545 (unwind-protect
1546 (progn
1547 (set-syntax-table idlwave-find-symbol-syntax-table)
1548 ,@body)
1549 (set-syntax-table saved-syntax))))
1550
76959b77
S
1551;(defmacro idlwave-with-special-syntax1 (&rest body)
1552; "Execute BODY with a different syntax table."
1553; `(let ((saved-syntax (syntax-table)))
1554; (unwind-protect
1555; (progn
1556; (set-syntax-table idlwave-find-symbol-syntax-table)
1557; ,@body)
1558; (set-syntax-table saved-syntax))))
1559
f32b3b91
CD
1560(defun idlwave-action-and-binding (key cmd &optional select)
1561 "KEY and CMD are made into a key binding and an indent action.
1562KEY is a string - same as for the `define-key' function. CMD is a
1563function of no arguments or a list to be evaluated. CMD is bound to
1564KEY in `idlwave-mode-map' by defining an anonymous function calling
1565`self-insert-command' followed by CMD. If KEY contains more than one
1566character a binding will only be set if SELECT is 'both.
1567
5e72c6b2 1568\(KEY . CMD\) is also placed in the `idlwave-indent-expand-table',
f32b3b91
CD
1569replacing any previous value for KEY. If a binding is not set then it
1570will instead be placed in `idlwave-indent-action-table'.
1571
1572If the optional argument SELECT is nil then an action and binding are
1573created. If SELECT is 'noaction, then a binding is always set and no
1574action is created. If SELECT is 'both then an action and binding
1575will both be created even if KEY contains more than one character.
1576Otherwise, if SELECT is non-nil then only an action is created.
1577
1578Some examples:
1579No spaces before and 1 after a comma
1580 (idlwave-action-and-binding \",\" '(idlwave-surround 0 1))
1581A minimum of 1 space before and after `=' (see `idlwave-expand-equal').
1582 (idlwave-action-and-binding \"=\" '(idlwave-expand-equal -1 -1))
1583Capitalize system variables - action only
1584 (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)"
1585 (if (not (equal select 'noaction))
1586 ;; Add action
1587 (let* ((table (if select 'idlwave-indent-action-table
1588 'idlwave-indent-expand-table))
3938cb82
S
1589 (table-key (regexp-quote key))
1590 (cell (assoc table-key (eval table))))
f32b3b91
CD
1591 (if cell
1592 ;; Replace action command
1593 (setcdr cell cmd)
1594 ;; New action
3938cb82 1595 (set table (append (eval table) (list (cons table-key cmd)))))))
f32b3b91
CD
1596 ;; Make key binding for action
1597 (if (or (and (null select) (= (length key) 1))
1598 (equal select 'noaction)
1599 (equal select 'both))
1600 (define-key idlwave-mode-map key
8d222148
SM
1601 `(lambda ()
1602 (interactive)
1603 (self-insert-command 1)
4111f0c7 1604 ,(if (listp cmd) cmd (list cmd))))))
f32b3b91
CD
1605
1606;; Set action and key bindings.
1607;; See description of the function `idlwave-action-and-binding'.
1608;; Automatically add spaces for the following characters
f66f03de
S
1609
1610;; Actions for & are complicated by &&
1611(idlwave-action-and-binding "&" 'idlwave-custom-ampersand-surround)
1612
1613;; Automatically add spaces to equal sign if not keyword. This needs
1614;; to go ahead of > and <, so >= and <= will be treated correctly
f32b3b91
CD
1615(idlwave-action-and-binding "=" '(idlwave-expand-equal -1 -1))
1616
4b1aaa8b 1617;; Actions for > and < are complicated by >=, <=, and ->...
f66f03de
S
1618(idlwave-action-and-binding "<" '(idlwave-custom-ltgtr-surround nil))
1619(idlwave-action-and-binding ">" '(idlwave-custom-ltgtr-surround 'gtr))
1620
1621(idlwave-action-and-binding "," '(idlwave-surround 0 -1 1))
1622
1623
f32b3b91
CD
1624;;;
1625;;; Abbrev Section
1626;;;
1627;;; When expanding abbrevs and the abbrev hook moves backward, an extra
1628;;; space is inserted (this is the space typed by the user to expanded
1629;;; the abbrev).
1630;;;
5e72c6b2 1631(defvar idlwave-mode-abbrev-table nil
5a0c3f56 1632 "Abbreviation table used for IDLWAVE mode.")
5e72c6b2
S
1633(define-abbrev-table 'idlwave-mode-abbrev-table ())
1634
1635(defun idlwave-define-abbrev (name expansion hook &optional noprefix table)
1636 "Define-abbrev with backward compatibility.
1637
1638If NOPREFIX is non-nil, don't prepend prefix character. Installs into
5a0c3f56 1639`idlwave-mode-abbrev-table' unless TABLE is non-nil."
5e72c6b2
S
1640 (let ((abbrevs-changed nil) ;; mask the current value to avoid save
1641 (args (list (or table idlwave-mode-abbrev-table)
1642 (if noprefix name (concat idlwave-abbrev-start-char name))
1643 expansion
1644 hook)))
1645 (condition-case nil
1646 (apply 'define-abbrev (append args '(0 t)))
1647 (error (apply 'define-abbrev args)))))
f32b3b91
CD
1648
1649(condition-case nil
4b1aaa8b 1650 (modify-syntax-entry (string-to-char idlwave-abbrev-start-char)
f32b3b91
CD
1651 "w" idlwave-mode-syntax-table)
1652 (error nil))
1653
5e72c6b2
S
1654;;
1655;; Templates
1656;;
1657(idlwave-define-abbrev "c" "" (idlwave-code-abbrev idlwave-case))
1658(idlwave-define-abbrev "sw" "" (idlwave-code-abbrev idlwave-switch))
1659(idlwave-define-abbrev "f" "" (idlwave-code-abbrev idlwave-for))
1660(idlwave-define-abbrev "fu" "" (idlwave-code-abbrev idlwave-function))
1661(idlwave-define-abbrev "pr" "" (idlwave-code-abbrev idlwave-procedure))
1662(idlwave-define-abbrev "r" "" (idlwave-code-abbrev idlwave-repeat))
1663(idlwave-define-abbrev "w" "" (idlwave-code-abbrev idlwave-while))
1664(idlwave-define-abbrev "i" "" (idlwave-code-abbrev idlwave-if))
1665(idlwave-define-abbrev "elif" "" (idlwave-code-abbrev idlwave-elif))
1666;;
1667;; Keywords, system functions, conversion routines
1668;;
1669(idlwave-define-abbrev "ap" "arg_present()" (idlwave-keyword-abbrev 1))
1670(idlwave-define-abbrev "b" "begin" (idlwave-keyword-abbrev 0 t))
1671(idlwave-define-abbrev "co" "common" (idlwave-keyword-abbrev 0 t))
1672(idlwave-define-abbrev "cb" "byte()" (idlwave-keyword-abbrev 1))
1673(idlwave-define-abbrev "cx" "fix()" (idlwave-keyword-abbrev 1))
1674(idlwave-define-abbrev "cl" "long()" (idlwave-keyword-abbrev 1))
1675(idlwave-define-abbrev "cf" "float()" (idlwave-keyword-abbrev 1))
1676(idlwave-define-abbrev "cs" "string()" (idlwave-keyword-abbrev 1))
1677(idlwave-define-abbrev "cc" "complex()" (idlwave-keyword-abbrev 1))
1678(idlwave-define-abbrev "cd" "double()" (idlwave-keyword-abbrev 1))
1679(idlwave-define-abbrev "e" "else" (idlwave-keyword-abbrev 0 t))
1680(idlwave-define-abbrev "ec" "endcase" 'idlwave-show-begin)
1681(idlwave-define-abbrev "es" "endswitch" 'idlwave-show-begin)
1682(idlwave-define-abbrev "ee" "endelse" 'idlwave-show-begin)
1683(idlwave-define-abbrev "ef" "endfor" 'idlwave-show-begin)
1684(idlwave-define-abbrev "ei" "endif else if" 'idlwave-show-begin)
1685(idlwave-define-abbrev "el" "endif else" 'idlwave-show-begin)
1686(idlwave-define-abbrev "en" "endif" 'idlwave-show-begin)
1687(idlwave-define-abbrev "er" "endrep" 'idlwave-show-begin)
1688(idlwave-define-abbrev "ew" "endwhile" 'idlwave-show-begin)
1689(idlwave-define-abbrev "g" "goto," (idlwave-keyword-abbrev 0 t))
1690(idlwave-define-abbrev "h" "help," (idlwave-keyword-abbrev 0))
1691(idlwave-define-abbrev "k" "keyword_set()" (idlwave-keyword-abbrev 1))
1692(idlwave-define-abbrev "n" "n_elements()" (idlwave-keyword-abbrev 1))
1693(idlwave-define-abbrev "on" "on_error," (idlwave-keyword-abbrev 0))
1694(idlwave-define-abbrev "oi" "on_ioerror," (idlwave-keyword-abbrev 0 1))
1695(idlwave-define-abbrev "ow" "openw," (idlwave-keyword-abbrev 0))
1696(idlwave-define-abbrev "or" "openr," (idlwave-keyword-abbrev 0))
1697(idlwave-define-abbrev "ou" "openu," (idlwave-keyword-abbrev 0))
1698(idlwave-define-abbrev "p" "print," (idlwave-keyword-abbrev 0))
1699(idlwave-define-abbrev "pt" "plot," (idlwave-keyword-abbrev 0))
1700(idlwave-define-abbrev "re" "read," (idlwave-keyword-abbrev 0))
1701(idlwave-define-abbrev "rf" "readf," (idlwave-keyword-abbrev 0))
1702(idlwave-define-abbrev "ru" "readu," (idlwave-keyword-abbrev 0))
1703(idlwave-define-abbrev "rt" "return" (idlwave-keyword-abbrev 0))
1704(idlwave-define-abbrev "sc" "strcompress()" (idlwave-keyword-abbrev 1))
1705(idlwave-define-abbrev "sn" "strlen()" (idlwave-keyword-abbrev 1))
1706(idlwave-define-abbrev "sl" "strlowcase()" (idlwave-keyword-abbrev 1))
1707(idlwave-define-abbrev "su" "strupcase()" (idlwave-keyword-abbrev 1))
1708(idlwave-define-abbrev "sm" "strmid()" (idlwave-keyword-abbrev 1))
1709(idlwave-define-abbrev "sp" "strpos()" (idlwave-keyword-abbrev 1))
1710(idlwave-define-abbrev "st" "strput()" (idlwave-keyword-abbrev 1))
1711(idlwave-define-abbrev "sr" "strtrim()" (idlwave-keyword-abbrev 1))
1712(idlwave-define-abbrev "t" "then" (idlwave-keyword-abbrev 0 t))
1713(idlwave-define-abbrev "u" "until" (idlwave-keyword-abbrev 0 t))
1714(idlwave-define-abbrev "wu" "writeu," (idlwave-keyword-abbrev 0))
1715(idlwave-define-abbrev "iap" "if arg_present() then" (idlwave-keyword-abbrev 6))
1716(idlwave-define-abbrev "ik" "if keyword_set() then" (idlwave-keyword-abbrev 6))
1717(idlwave-define-abbrev "ine" "if n_elements() eq 0 then" (idlwave-keyword-abbrev 11))
1718(idlwave-define-abbrev "inn" "if n_elements() ne 0 then" (idlwave-keyword-abbrev 11))
1719(idlwave-define-abbrev "np" "n_params()" (idlwave-keyword-abbrev 0))
1720(idlwave-define-abbrev "s" "size()" (idlwave-keyword-abbrev 1))
1721(idlwave-define-abbrev "wi" "widget_info()" (idlwave-keyword-abbrev 1))
1722(idlwave-define-abbrev "wc" "widget_control," (idlwave-keyword-abbrev 0))
3938cb82
S
1723(idlwave-define-abbrev "pv" "ptr_valid()" (idlwave-keyword-abbrev 1))
1724(idlwave-define-abbrev "ipv" "if ptr_valid() then" (idlwave-keyword-abbrev 6))
ff689efd 1725
5e72c6b2
S
1726;; This section is reserved words only. (From IDL user manual)
1727;;
1728(idlwave-define-abbrev "and" "and" (idlwave-keyword-abbrev 0 t) t)
1729(idlwave-define-abbrev "begin" "begin" (idlwave-keyword-abbrev 0 t) t)
1730(idlwave-define-abbrev "break" "break" (idlwave-keyword-abbrev 0 t) t)
1731(idlwave-define-abbrev "case" "case" (idlwave-keyword-abbrev 0 t) t)
1732(idlwave-define-abbrev "common" "common" (idlwave-keyword-abbrev 0 t) t)
1733(idlwave-define-abbrev "continue" "continue" (idlwave-keyword-abbrev 0 t) t)
1734(idlwave-define-abbrev "do" "do" (idlwave-keyword-abbrev 0 t) t)
1735(idlwave-define-abbrev "else" "else" (idlwave-keyword-abbrev 0 t) t)
1736(idlwave-define-abbrev "end" "end" 'idlwave-show-begin-check t)
1737(idlwave-define-abbrev "endcase" "endcase" 'idlwave-show-begin-check t)
1738(idlwave-define-abbrev "endelse" "endelse" 'idlwave-show-begin-check t)
1739(idlwave-define-abbrev "endfor" "endfor" 'idlwave-show-begin-check t)
1740(idlwave-define-abbrev "endif" "endif" 'idlwave-show-begin-check t)
1741(idlwave-define-abbrev "endrep" "endrep" 'idlwave-show-begin-check t)
1742(idlwave-define-abbrev "endswitch" "endswitch" 'idlwave-show-begin-check t)
1743(idlwave-define-abbrev "endwhi" "endwhi" 'idlwave-show-begin-check t)
1744(idlwave-define-abbrev "endwhile" "endwhile" 'idlwave-show-begin-check t)
1745(idlwave-define-abbrev "eq" "eq" (idlwave-keyword-abbrev 0 t) t)
1746(idlwave-define-abbrev "for" "for" (idlwave-keyword-abbrev 0 t) t)
1747(idlwave-define-abbrev "function" "function" (idlwave-keyword-abbrev 0 t) t)
1748(idlwave-define-abbrev "ge" "ge" (idlwave-keyword-abbrev 0 t) t)
1749(idlwave-define-abbrev "goto" "goto" (idlwave-keyword-abbrev 0 t) t)
1750(idlwave-define-abbrev "gt" "gt" (idlwave-keyword-abbrev 0 t) t)
1751(idlwave-define-abbrev "if" "if" (idlwave-keyword-abbrev 0 t) t)
1752(idlwave-define-abbrev "le" "le" (idlwave-keyword-abbrev 0 t) t)
1753(idlwave-define-abbrev "lt" "lt" (idlwave-keyword-abbrev 0 t) t)
1754(idlwave-define-abbrev "mod" "mod" (idlwave-keyword-abbrev 0 t) t)
1755(idlwave-define-abbrev "ne" "ne" (idlwave-keyword-abbrev 0 t) t)
1756(idlwave-define-abbrev "not" "not" (idlwave-keyword-abbrev 0 t) t)
1757(idlwave-define-abbrev "of" "of" (idlwave-keyword-abbrev 0 t) t)
1758(idlwave-define-abbrev "on_ioerror" "on_ioerror" (idlwave-keyword-abbrev 0 t) t)
1759(idlwave-define-abbrev "or" "or" (idlwave-keyword-abbrev 0 t) t)
1760(idlwave-define-abbrev "pro" "pro" (idlwave-keyword-abbrev 0 t) t)
1761(idlwave-define-abbrev "repeat" "repeat" (idlwave-keyword-abbrev 0 t) t)
1762(idlwave-define-abbrev "switch" "switch" (idlwave-keyword-abbrev 0 t) t)
1763(idlwave-define-abbrev "then" "then" (idlwave-keyword-abbrev 0 t) t)
1764(idlwave-define-abbrev "until" "until" (idlwave-keyword-abbrev 0 t) t)
1765(idlwave-define-abbrev "while" "while" (idlwave-keyword-abbrev 0 t) t)
1766(idlwave-define-abbrev "xor" "xor" (idlwave-keyword-abbrev 0 t) t)
f32b3b91
CD
1767
1768(defvar imenu-create-index-function)
1769(defvar extract-index-name-function)
1770(defvar prev-index-position-function)
1771(defvar imenu-extract-index-name-function)
1772(defvar imenu-prev-index-position-function)
5e72c6b2 1773;; defined later - so just make the compiler hush
4b1aaa8b 1774(defvar idlwave-mode-menu)
f32b3b91
CD
1775(defvar idlwave-mode-debug-menu)
1776
1777;;;###autoload
175069ef 1778(define-derived-mode idlwave-mode prog-mode "IDLWAVE"
e08734e2 1779 "Major mode for editing IDL source files (version 6.1_em22).
f32b3b91
CD
1780
1781The main features of this mode are
1782
17831. Indentation and Formatting
1784 --------------------------
1785 Like other Emacs programming modes, C-j inserts a newline and indents.
1786 TAB is used for explicit indentation of the current line.
1787
5e72c6b2
S
1788 To start a continuation line, use \\[idlwave-split-line]. This
1789 function can also be used in the middle of a line to split the line
1790 at that point. When used inside a long constant string, the string
1791 is split at that point with the `+' concatenation operator.
f32b3b91
CD
1792
1793 Comments are indented as follows:
1794
1795 `;;;' Indentation remains unchanged.
1796 `;;' Indent like the surrounding code
1797 `;' Indent to a minimum column.
1798
1799 The indentation of comments starting in column 0 is never changed.
1800
5e72c6b2
S
1801 Use \\[idlwave-fill-paragraph] to refill a paragraph inside a
1802 comment. The indentation of the second line of the paragraph
1803 relative to the first will be retained. Use
1804 \\[idlwave-auto-fill-mode] to toggle auto-fill mode for these
1805 comments. When the variable `idlwave-fill-comment-line-only' is
52a244eb 1806 nil, code can also be auto-filled and auto-indented.
f32b3b91
CD
1807
1808 To convert pre-existing IDL code to your formatting style, mark the
5e72c6b2
S
1809 entire buffer with \\[mark-whole-buffer] and execute
1810 \\[idlwave-expand-region-abbrevs]. Then mark the entire buffer
1811 again followed by \\[indent-region] (`indent-region').
f32b3b91
CD
1812
18132. Routine Info
1814 ------------
5e72c6b2
S
1815 IDLWAVE displays information about the calling sequence and the
1816 accepted keyword parameters of a procedure or function with
1817 \\[idlwave-routine-info]. \\[idlwave-find-module] jumps to the
1818 source file of a module. These commands know about system
1819 routines, all routines in idlwave-mode buffers and (when the
1820 idlwave-shell is active) about all modules currently compiled under
52a244eb
S
1821 this shell. It also makes use of pre-compiled or custom-scanned
1822 user and library catalogs many popular libraries ship with by
1823 default. Use \\[idlwave-update-routine-info] to update this
15e42531
CD
1824 information, which is also used for completion (see item 4).
1825
18263. Online IDL Help
1827 ---------------
f66f03de 1828
15e42531 1829 \\[idlwave-context-help] displays the IDL documentation relevant
f66f03de
S
1830 for the system variable, keyword, or routines at point. A single
1831 key stroke gets you directly to the right place in the docs. See
52a244eb 1832 the manual to configure where and how the HTML help is displayed.
f32b3b91 1833
15e42531 18344. Completion
f32b3b91 1835 ----------
15e42531 1836 \\[idlwave-complete] completes the names of procedures, functions
52a244eb
S
1837 class names, keyword parameters, system variables and tags, class
1838 tags, structure tags, filenames and much more. It is context
1839 sensitive and figures out what is expected at point. Lower case
1840 strings are completed in lower case, other strings in mixed or
1841 upper case.
f32b3b91 1842
15e42531 18435. Code Templates and Abbreviations
f32b3b91
CD
1844 --------------------------------
1845 Many Abbreviations are predefined to expand to code fragments and templates.
5a0c3f56 1846 The abbreviations start generally with a `\\`. Some examples:
f32b3b91
CD
1847
1848 \\pr PROCEDURE template
1849 \\fu FUNCTION template
1850 \\c CASE statement template
05a1abfc 1851 \\sw SWITCH statement template
f32b3b91
CD
1852 \\f FOR loop template
1853 \\r REPEAT Loop template
1854 \\w WHILE loop template
1855 \\i IF statement template
1856 \\elif IF-ELSE statement template
1857 \\b BEGIN
4b1aaa8b 1858
52a244eb
S
1859 For a full list, use \\[idlwave-list-abbrevs]. Some templates also
1860 have direct keybindings - see the list of keybindings below.
775591f7 1861
52a244eb
S
1862 \\[idlwave-doc-header] inserts a documentation header at the
1863 beginning of the current program unit (pro, function or main).
1864 Change log entries can be added to the current program unit with
1865 \\[idlwave-doc-modification].
f32b3b91 1866
15e42531 18676. Automatic Case Conversion
f32b3b91
CD
1868 -------------------------
1869 The case of reserved words and some abbrevs is controlled by
1870 `idlwave-reserved-word-upcase' and `idlwave-abbrev-change-case'.
1871
15e42531 18727. Automatic END completion
f32b3b91
CD
1873 ------------------------
1874 If the variable `idlwave-expand-generic-end' is non-nil, each END typed
1875 will be converted to the specific version, like ENDIF, ENDFOR, etc.
1876
15e42531 18778. Hooks
f32b3b91
CD
1878 -----
1879 Loading idlwave.el runs `idlwave-load-hook'.
1880 Turning on `idlwave-mode' runs `idlwave-mode-hook'.
1881
15e42531 18829. Documentation and Customization
f32b3b91 1883 -------------------------------
5e72c6b2
S
1884 Info documentation for this package is available. Use
1885 \\[idlwave-info] to display (complain to your sysadmin if that does
1886 not work). For Postscript, PDF, and HTML versions of the
855b42a2 1887 documentation, check IDLWAVE's homepage at URL `http://idlwave.org'.
f32b3b91
CD
1888 IDLWAVE has customize support - see the group `idlwave'.
1889
15e42531 189010.Keybindings
f32b3b91
CD
1891 -----------
1892 Here is a list of all keybindings of this mode.
1893 If some of the key bindings below show with ??, use \\[describe-key]
1894 followed by the key sequence to see what the key sequence does.
1895
1896\\{idlwave-mode-map}"
175069ef 1897 :abbrev-table idlwave-mode-abbrev-table
f32b3b91
CD
1898 (if idlwave-startup-message
1899 (message "Emacs IDLWAVE mode version %s." idlwave-mode-version))
1900 (setq idlwave-startup-message nil)
4b1aaa8b 1901
f32b3b91 1902 (set (make-local-variable 'indent-line-function) 'idlwave-indent-and-action)
4b1aaa8b 1903
175069ef
SM
1904 (set (make-local-variable idlwave-comment-indent-function)
1905 #'idlwave-comment-hook)
4b1aaa8b 1906
f32b3b91
CD
1907 (set (make-local-variable 'comment-start-skip) ";+[ \t]*")
1908 (set (make-local-variable 'comment-start) ";")
0dc2be2f 1909 (set (make-local-variable 'comment-add) 1) ; ";;" for new and regions
f66f03de 1910 (set (make-local-variable 'require-final-newline) t)
f32b3b91
CD
1911 (set (make-local-variable 'abbrev-all-caps) t)
1912 (set (make-local-variable 'indent-tabs-mode) nil)
1913 (set (make-local-variable 'completion-ignore-case) t)
4b1aaa8b 1914
f32b3b91
CD
1915 (when (featurep 'easymenu)
1916 (easy-menu-add idlwave-mode-menu idlwave-mode-map)
1917 (easy-menu-add idlwave-mode-debug-menu idlwave-mode-map))
1918
f32b3b91 1919 (setq abbrev-mode t)
4b1aaa8b 1920
f32b3b91
CD
1921 (set (make-local-variable idlwave-fill-function) 'idlwave-auto-fill)
1922 (setq comment-end "")
1923 (set (make-local-variable 'comment-multi-line) nil)
4b1aaa8b 1924 (set (make-local-variable 'paragraph-separate)
5e72c6b2 1925 "[ \t\f]*$\\|[ \t]*;+[ \t]*$\\|;+[+=-_*]+$")
f32b3b91
CD
1926 (set (make-local-variable 'paragraph-start) "[ \t\f]\\|[ \t]*;+[ \t]")
1927 (set (make-local-variable 'paragraph-ignore-fill-prefix) nil)
76959b77 1928 (set (make-local-variable 'parse-sexp-ignore-comments) t)
775591f7 1929
e08734e2 1930 ;; ChangeLog
8c43762b 1931 (set (make-local-variable 'add-log-current-defun-function)
e08734e2
S
1932 'idlwave-current-routine-fullname)
1933
f32b3b91
CD
1934 ;; Set tag table list to use IDLTAGS as file name.
1935 (if (boundp 'tag-table-alist)
1936 (add-to-list 'tag-table-alist '("\\.pro$" . "IDLTAGS")))
4b1aaa8b 1937
e08734e2 1938 ;; Font-lock additions
52a244eb 1939 ;; Following line is for Emacs - XEmacs uses the corresponding property
f32b3b91
CD
1940 ;; on the `idlwave-mode' symbol.
1941 (set (make-local-variable 'font-lock-defaults) idlwave-font-lock-defaults)
0dc2be2f
S
1942 (set (make-local-variable 'font-lock-mark-block-function)
1943 'idlwave-mark-subprogram)
1944 (set (make-local-variable 'font-lock-fontify-region-function)
1945 'idlwave-font-lock-fontify-region)
f32b3b91
CD
1946
1947 ;; Imenu setup
1948 (set (make-local-variable 'imenu-create-index-function)
1949 'imenu-default-create-index-function)
1950 (set (make-local-variable 'imenu-extract-index-name-function)
1951 'idlwave-unit-name)
1952 (set (make-local-variable 'imenu-prev-index-position-function)
1953 'idlwave-prev-index-position)
1954
0dc2be2f
S
1955 ;; HideShow setup
1956 (add-to-list 'hs-special-modes-alist
1957 (list 'idlwave-mode
1958 idlwave-begin-block-reg
1959 idlwave-end-block-reg
1960 ";"
1961 'idlwave-forward-block nil))
4b1aaa8b 1962
f32b3b91 1963 ;; Make a local post-command-hook and add our hook to it
f66f03de
S
1964 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1965 ;; (make-local-hook 'post-command-hook)
15e42531
CD
1966 (add-hook 'post-command-hook 'idlwave-command-hook nil 'local)
1967
1968 ;; Make local hooks for buffer updates
f66f03de
S
1969 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1970 ;; (make-local-hook 'kill-buffer-hook)
15e42531 1971 (add-hook 'kill-buffer-hook 'idlwave-kill-buffer-update nil 'local)
f66f03de 1972 ;; (make-local-hook 'after-save-hook)
e08734e2 1973 (add-hook 'after-save-hook 'idlwave-save-buffer-update nil 'local)
15e42531
CD
1974 (add-hook 'after-save-hook 'idlwave-revoke-license-to-kill nil 'local)
1975
52a244eb
S
1976 ;; Setup directories and file, if necessary
1977 (idlwave-setup)
1978
15e42531
CD
1979 ;; Update the routine info with info about current buffer?
1980 (idlwave-new-buffer-update)
f32b3b91 1981
f66f03de 1982 ;; Check help location
175069ef 1983 (idlwave-help-check-locations))
f32b3b91 1984
52a244eb
S
1985(defvar idlwave-setup-done nil)
1986(defun idlwave-setup ()
1987 (unless idlwave-setup-done
1988 (if (not (file-directory-p idlwave-config-directory))
1989 (make-directory idlwave-config-directory))
4b1aaa8b
PE
1990 (setq
1991 idlwave-user-catalog-file (expand-file-name
1992 idlwave-user-catalog-file
f66f03de 1993 idlwave-config-directory)
4b1aaa8b
PE
1994 idlwave-xml-system-rinfo-converted-file
1995 (expand-file-name
f66f03de
S
1996 idlwave-xml-system-rinfo-converted-file
1997 idlwave-config-directory)
4b1aaa8b
PE
1998 idlwave-path-file (expand-file-name
1999 idlwave-path-file
f66f03de 2000 idlwave-config-directory))
52a244eb
S
2001 (idlwave-read-paths) ; we may need these early
2002 (setq idlwave-setup-done t)))
2003
0dc2be2f
S
2004(defun idlwave-font-lock-fontify-region (beg end &optional verbose)
2005 "Fontify continuation lines correctly."
2006 (let (pos)
2007 (save-excursion
2008 (goto-char beg)
2009 (forward-line -1)
2010 (when (setq pos (idlwave-is-continuation-line))
2011 (goto-char pos)
2012 (idlwave-beginning-of-statement)
2013 (setq beg (point)))))
2014 (font-lock-default-fontify-region beg end verbose))
2015
f32b3b91 2016;;
52a244eb 2017;; Code Formatting ----------------------------------------------------
4b1aaa8b 2018;;
f32b3b91 2019
f32b3b91 2020(defun idlwave-hard-tab ()
5a0c3f56 2021 "Insert TAB in buffer in current position."
f32b3b91
CD
2022 (interactive)
2023 (insert "\t"))
2024
2025;;; This stuff is experimental
2026
2027(defvar idlwave-command-hook nil
2028 "If non-nil, a list that can be evaluated using `eval'.
2029It is evaluated in the lisp function `idlwave-command-hook' which is
2030placed in `post-command-hook'.")
2031
2032(defun idlwave-command-hook ()
2033 "Command run after every command.
2034Evaluates a non-nil value of the *variable* `idlwave-command-hook' and
2035sets the variable to zero afterwards."
2036 (and idlwave-command-hook
2037 (listp idlwave-command-hook)
2038 (condition-case nil
2039 (eval idlwave-command-hook)
2040 (error nil)))
2041 (setq idlwave-command-hook nil))
2042
2043;;; End experiment
2044
2045;; It would be better to use expand.el for better abbrev handling and
2046;; versatility.
2047
2048(defun idlwave-check-abbrev (arg &optional reserved)
5a0c3f56 2049 "Reverse abbrev expansion if in comment or string.
f32b3b91
CD
2050Argument ARG is the number of characters to move point
2051backward if `idlwave-abbrev-move' is non-nil.
2052If optional argument RESERVED is non-nil then the expansion
2053consists of reserved words, which will be capitalized if
2054`idlwave-reserved-word-upcase' is non-nil.
2055Otherwise, the abbrev will be capitalized if `idlwave-abbrev-change-case'
2056is non-nil, unless its value is \`down in which case the abbrev will be
2057made into all lowercase.
2058Returns non-nil if abbrev is left expanded."
2059 (if (idlwave-quoted)
2060 (progn (unexpand-abbrev)
2061 nil)
2062 (if (and reserved idlwave-reserved-word-upcase)
2063 (upcase-region last-abbrev-location (point))
2064 (cond
2065 ((equal idlwave-abbrev-change-case 'down)
2066 (downcase-region last-abbrev-location (point)))
2067 (idlwave-abbrev-change-case
2068 (upcase-region last-abbrev-location (point)))))
2069 (if (and idlwave-abbrev-move (> arg 0))
2070 (if (boundp 'post-command-hook)
2071 (setq idlwave-command-hook (list 'backward-char (1+ arg)))
2072 (backward-char arg)))
2073 t))
2074
2075(defun idlwave-in-comment ()
5a0c3f56 2076 "Return t if point is inside a comment, nil otherwise."
f32b3b91
CD
2077 (save-excursion
2078 (let ((here (point)))
2079 (and (idlwave-goto-comment) (> here (point))))))
2080
2081(defun idlwave-goto-comment ()
2082 "Move to start of comment delimiter on current line.
2083Moves to end of line if there is no comment delimiter.
2084Ignores comment delimiters in strings.
2085Returns point if comment found and nil otherwise."
9b026d9f 2086 (let ((eos (point-at-eol))
f32b3b91
CD
2087 (data (match-data))
2088 found)
2089 ;; Look for first comment delimiter not in a string
2090 (beginning-of-line)
2091 (setq found (search-forward comment-start eos 'lim))
2092 (while (and found (idlwave-in-quote))
2093 (setq found (search-forward comment-start eos 'lim)))
2094 (store-match-data data)
2095 (and found (not (idlwave-in-quote))
2096 (progn
2097 (backward-char 1)
2098 (point)))))
2099
5e72c6b2 2100(defun idlwave-region-active-p ()
a00e54f7
RS
2101 "Should we operate on an active region?"
2102 (if (fboundp 'use-region-p)
2103 (use-region-p)
2104 (region-active-p)))
5e72c6b2 2105
f32b3b91
CD
2106(defun idlwave-show-matching-quote ()
2107 "Insert quote and show matching quote if this is end of a string."
2108 (interactive)
2109 (let ((bq (idlwave-in-quote))
1ba983e8 2110 (inq last-command-event))
f32b3b91
CD
2111 (if (and bq (not (idlwave-in-comment)))
2112 (let ((delim (char-after bq)))
2113 (insert inq)
2114 (if (eq inq delim)
2115 (save-excursion
2116 (goto-char bq)
2117 (sit-for 1))))
2118 ;; Not the end of a string
2119 (insert inq))))
2120
2121(defun idlwave-show-begin-check ()
2122 "Ensure that the previous word was a token before `idlwave-show-begin'.
2123An END token must be preceded by whitespace."
5e72c6b2
S
2124 (if (not (idlwave-quoted))
2125 (if
2126 (save-excursion
2127 (backward-word 1)
2128 (backward-char 1)
2129 (looking-at "[ \t\n\f]"))
2130 (idlwave-show-begin))))
f32b3b91
CD
2131
2132(defun idlwave-show-begin ()
5a0c3f56
JB
2133 "Find the start of current block and blinks to it for a second.
2134Also checks if the correct END statement has been used."
f32b3b91 2135 ;; All end statements are reserved words
76959b77 2136 ;; Re-indent end line
52a244eb
S
2137 ;;(insert-char ?\ 1) ;; So indent, etc. work well
2138 ;;(backward-char 1)
76959b77
S
2139 (let* ((pos (point-marker))
2140 (last-abbrev-marker (copy-marker last-abbrev-location))
e180ab9f 2141 (eol-pos (point-at-eol))
76959b77
S
2142 begin-pos end-pos end end1 )
2143 (if idlwave-reindent-end (idlwave-indent-line))
52a244eb 2144 (setq last-abbrev-location (marker-position last-abbrev-marker))
f32b3b91
CD
2145 (when (and (idlwave-check-abbrev 0 t)
2146 idlwave-show-block)
2147 (save-excursion
2148 ;; Move inside current block
76959b77 2149 (goto-char last-abbrev-marker)
f32b3b91 2150 (idlwave-block-jump-out -1 'nomark)
76959b77
S
2151 (setq begin-pos (point))
2152 (idlwave-block-jump-out 1 'nomark)
2153 (setq end-pos (point))
2154 (if (> end-pos eol-pos)
2155 (setq end-pos pos))
2156 (goto-char end-pos)
4b1aaa8b 2157 (setq end (buffer-substring
76959b77
S
2158 (progn
2159 (skip-chars-backward "a-zA-Z")
2160 (point))
2161 end-pos))
2162 (goto-char begin-pos)
f32b3b91
CD
2163 (when (setq end1 (cdr (idlwave-block-master)))
2164 (cond
5e72c6b2 2165 ((null end1)) ; no-operation
f32b3b91
CD
2166 ((string= (downcase end) (downcase end1))
2167 (sit-for 1))
2168 ((string= (downcase end) "end")
2169 ;; A generic end
2170 (if idlwave-expand-generic-end
2171 (save-excursion
2172 (goto-char pos)
2173 (backward-char 3)
2174 (insert (if (string= end "END") (upcase end1) end1))
2175 (delete-char 3)))
2176 (sit-for 1))
2177 (t
2178 (beep)
4b1aaa8b 2179 (message "Warning: Shouldn't this be \"%s\" instead of \"%s\"?"
f32b3b91 2180 end1 end)
52a244eb
S
2181 (sit-for 1))))))))
2182 ;;(delete-char 1))
f32b3b91
CD
2183
2184(defun idlwave-block-master ()
2185 (let ((case-fold-search t))
2186 (save-excursion
2187 (cond
05a1abfc 2188 ((looking-at "pro\\|case\\|switch\\|function\\>")
f32b3b91
CD
2189 (assoc (downcase (match-string 0)) idlwave-block-matches))
2190 ((looking-at "begin\\>")
4b1aaa8b
PE
2191 (let ((limit (save-excursion
2192 (idlwave-beginning-of-statement)
f32b3b91
CD
2193 (point))))
2194 (cond
52a244eb
S
2195 ((re-search-backward ":[ \t]*\\=" limit t)
2196 ;; seems to be a case thing
2197 '("begin" . "end"))
f32b3b91
CD
2198 ((re-search-backward idlwave-block-match-regexp limit t)
2199 (assoc (downcase (match-string 1))
2200 idlwave-block-matches))
f32b3b91 2201 (t
52a244eb 2202 ;; Just a normal block
f32b3b91
CD
2203 '("begin" . "end")))))
2204 (t nil)))))
2205
2206(defun idlwave-close-block ()
2207 "Terminate the current block with the correct END statement."
2208 (interactive)
f32b3b91
CD
2209 ;; Start new line if we are not in a new line
2210 (unless (save-excursion
2211 (skip-chars-backward " \t")
2212 (bolp))
2213 (let ((idlwave-show-block nil))
2214 (newline-and-indent)))
5e72c6b2
S
2215 (let ((last-abbrev-location (point))) ; for upcasing
2216 (insert "end")
2217 (idlwave-show-begin)))
2218
f66f03de 2219(defun idlwave-custom-ampersand-surround (&optional is-action)
5a0c3f56 2220 "Surround &, leaving room for && (which surround as well)."
f66f03de
S
2221 (let* ((prev-char (char-after (- (point) 2)))
2222 (next-char (char-after (point)))
2223 (amp-left (eq prev-char ?&))
2224 (amp-right (eq next-char ?&))
2225 (len (if amp-left 2 1)))
2226 (unless amp-right ;no need to do it twice, amp-left will catch it.
2227 (idlwave-surround -1 (if (or is-action amp-left) -1) len))))
2228
2229(defun idlwave-custom-ltgtr-surround (gtr &optional is-action)
2230 "Surround > and < by blanks, leaving room for >= and <=, and considering ->."
2231 (let* ((prev-char (char-after (- (point) 2)))
2232 (next-char (char-after (point)))
2233 (method-invoke (and gtr (eq prev-char ?-)))
2234 (len (if method-invoke 2 1)))
2235 (unless (eq next-char ?=)
2236 ;; Key binding: pad only on left, to save for possible >=/<=
2237 (idlwave-surround -1 (if (or is-action method-invoke) -1) len))))
2238
2239(defun idlwave-surround (&optional before after length is-action)
595ab50b
CD
2240 "Surround the LENGTH characters before point with blanks.
2241LENGTH defaults to 1.
f32b3b91 2242Optional arguments BEFORE and AFTER affect the behavior before and
595ab50b
CD
2243after the characters (see also description of `idlwave-make-space'):
2244
2245nil do nothing
22460 force no spaces
2247integer > 0 force exactly n spaces
2248integer < 0 at least |n| spaces
f32b3b91
CD
2249
2250The function does nothing if any of the following conditions is true:
2251- `idlwave-surround-by-blank' is nil
f66f03de 2252- the character before point is inside a string or comment"
5e72c6b2 2253 (when (and idlwave-surround-by-blank (not (idlwave-quoted)))
f66f03de
S
2254 (let ((length (or length 1))) ; establish a default for LENGTH
2255 (backward-char length)
2256 (save-restriction
2257 (let ((here (point)))
2258 (skip-chars-backward " \t")
2259 (if (bolp)
2260 ;; avoid clobbering indent
2261 (progn
2262 (move-to-column (idlwave-calculate-indent))
2263 (if (<= (point) here)
2264 (narrow-to-region (point) here))
2265 (goto-char here)))
2266 (idlwave-make-space before))
2267 (skip-chars-forward " \t"))
2268 (forward-char length)
2269 (idlwave-make-space after)
2270 ;; Check to see if the line should auto wrap
2271 (if (and (equal (char-after (1- (point))) ?\ )
2272 (> (current-column) fill-column))
2273 (funcall auto-fill-function)))))
f32b3b91
CD
2274
2275(defun idlwave-make-space (n)
2276 "Make space at point.
2277The space affected is all the spaces and tabs around point.
2278If n is non-nil then point is left abs(n) spaces from the beginning of
2279the contiguous space.
2280The amount of space at point is determined by N.
2281If the value of N is:
2282nil - do nothing.
595ab50b
CD
2283> 0 - exactly N spaces.
2284< 0 - a minimum of -N spaces, i.e., do not change if there are
2285 already -N spaces.
22860 - no spaces (i.e. remove any existing space)."
f32b3b91
CD
2287 (if (integerp n)
2288 (let
2289 ((start-col (progn (skip-chars-backward " \t") (current-column)))
2290 (left (point))
2291 (end-col (progn (skip-chars-forward " \t") (current-column))))
2292 (delete-horizontal-space)
2293 (cond
2294 ((> n 0)
2295 (idlwave-indent-to (+ start-col n))
2296 (goto-char (+ left n)))
2297 ((< n 0)
2298 (idlwave-indent-to end-col (- n))
2299 (goto-char (- left n)))
2300 ;; n = 0, done
2301 ))))
2302
2303(defun idlwave-newline ()
5a0c3f56 2304 "Insert a newline and indent the current and previous line."
f32b3b91
CD
2305 (interactive)
2306 ;;
2307 ;; Handle unterminated single and double quotes
2308 ;; If not in a comment and in a string then insertion of a newline
2309 ;; will mean unbalanced quotes.
2310 ;;
2311 (if (and (not (idlwave-in-comment)) (idlwave-in-quote))
2312 (progn (beep)
2313 (message "Warning: unbalanced quotes?")))
2314 (newline)
2315 ;;
2316 ;; The current line is being split, the cursor should be at the
2317 ;; beginning of the new line skipping the leading indentation.
2318 ;;
2319 ;; The reason we insert the new line before indenting is that the
2320 ;; indenting could be confused by keywords (e.g. END) on the line
2321 ;; after the split point. This prevents us from just using
2322 ;; `indent-for-tab-command' followed by `newline-and-indent'.
2323 ;;
2324 (beginning-of-line 0)
2325 (idlwave-indent-line)
2326 (forward-line)
2327 (idlwave-indent-line))
2328
2329;;
2330;; Use global variable 'comment-column' to set parallel comment
2331;;
2332;; Modeled on lisp.el
2333;; Emacs Lisp and IDL (Wave CL) have identical comment syntax
2334(defun idlwave-comment-hook ()
2335 "Compute indent for the beginning of the IDL comment delimiter."
2336 (if (or (looking-at idlwave-no-change-comment)
8d222148 2337 (looking-at (or idlwave-begin-line-comment "^;")))
f32b3b91
CD
2338 (current-column)
2339 (if (looking-at idlwave-code-comment)
2340 (if (save-excursion (skip-chars-backward " \t") (bolp))
2341 ;; On line by itself, indent as code
2342 (let ((tem (idlwave-calculate-indent)))
2343 (if (listp tem) (car tem) tem))
2344 ;; after code - do not change
2345 (current-column))
2346 (skip-chars-backward " \t")
2347 (max (if (bolp) 0 (1+ (current-column)))
2348 comment-column))))
2349
2350(defun idlwave-split-line ()
2351 "Continue line by breaking line at point and indent the lines.
5a0c3f56 2352For a code line insert continuation marker. If the line is a line comment
f32b3b91
CD
2353then the new line will contain a comment with the same indentation.
2354Splits strings with the IDL operator `+' if `idlwave-split-line-string' is
2355non-nil."
2356 (interactive)
15e42531
CD
2357 ;; Expand abbreviation, just like normal RET would.
2358 (and abbrev-mode (expand-abbrev))
f32b3b91
CD
2359 (let (beg)
2360 (if (not (idlwave-in-comment))
2361 ;; For code line add continuation.
2362 ;; Check if splitting a string.
2363 (progn
2364 (if (setq beg (idlwave-in-quote))
2365 (if idlwave-split-line-string
2366 ;; Split the string.
2367 (progn (insert (setq beg (char-after beg)) " + "
2368 idlwave-continuation-char beg)
5e72c6b2
S
2369 (backward-char 1)
2370 (newline-and-indent)
2371 (forward-char 1))
f32b3b91
CD
2372 ;; Do not split the string.
2373 (beep)
2374 (message "Warning: continuation inside string!!")
2375 (insert " " idlwave-continuation-char))
2376 ;; Not splitting a string.
15e42531
CD
2377 (if (not (member (char-before) '(?\ ?\t)))
2378 (insert " "))
5e72c6b2
S
2379 (insert idlwave-continuation-char)
2380 (newline-and-indent)))
f32b3b91
CD
2381 (indent-new-comment-line))
2382 ;; Indent previous line
2383 (setq beg (- (point-max) (point)))
2384 (forward-line -1)
2385 (idlwave-indent-line)
2386 (goto-char (- (point-max) beg))
2387 ;; Reindent new line
2388 (idlwave-indent-line)))
2389
cca13260 2390(defun idlwave-beginning-of-subprogram (&optional nomark)
5a0c3f56 2391 "Move point to the beginning of the current program unit.
cca13260 2392If NOMARK is non-nil, do not push mark."
f32b3b91 2393 (interactive)
cca13260 2394 (idlwave-find-key idlwave-begin-unit-reg -1 nomark))
f32b3b91 2395
cca13260 2396(defun idlwave-end-of-subprogram (&optional nomark)
5a0c3f56 2397 "Move point to the start of the next program unit.
cca13260 2398If NOMARK is non-nil, do not push mark."
f32b3b91
CD
2399 (interactive)
2400 (idlwave-end-of-statement)
cca13260 2401 (idlwave-find-key idlwave-end-unit-reg 1 nomark))
f32b3b91
CD
2402
2403(defun idlwave-mark-statement ()
2404 "Mark current IDL statement."
2405 (interactive)
2406 (idlwave-end-of-statement)
2407 (let ((end (point)))
2408 (idlwave-beginning-of-statement)
0dc2be2f 2409 (push-mark end nil t)))
f32b3b91
CD
2410
2411(defun idlwave-mark-block ()
2412 "Mark containing block."
2413 (interactive)
2414 (idlwave-end-of-statement)
2415 (idlwave-backward-up-block -1)
2416 (idlwave-end-of-statement)
2417 (let ((end (point)))
2418 (idlwave-backward-block)
2419 (idlwave-beginning-of-statement)
0dc2be2f 2420 (push-mark end nil t)))
f32b3b91
CD
2421
2422
2423(defun idlwave-mark-subprogram ()
2424 "Put mark at beginning of program, point at end.
2425The marks are pushed."
2426 (interactive)
2427 (idlwave-end-of-statement)
2428 (idlwave-beginning-of-subprogram)
2429 (let ((beg (point)))
2430 (idlwave-forward-block)
0dc2be2f 2431 (push-mark beg nil t))
f32b3b91
CD
2432 (exchange-point-and-mark))
2433
2434(defun idlwave-backward-up-block (&optional arg)
2435 "Move to beginning of enclosing block if prefix ARG >= 0.
2436If prefix ARG < 0 then move forward to enclosing block end."
2437 (interactive "p")
2438 (idlwave-block-jump-out (- arg) 'nomark))
2439
2440(defun idlwave-beginning-of-block ()
2441 "Go to the beginning of the current block."
2442 (interactive)
2443 (idlwave-block-jump-out -1 'nomark)
2444 (forward-word 1))
2445
2446(defun idlwave-end-of-block ()
2447 "Go to the beginning of the current block."
2448 (interactive)
2449 (idlwave-block-jump-out 1 'nomark)
2450 (backward-word 1))
2451
0dc2be2f 2452(defun idlwave-forward-block (&optional arg)
f32b3b91
CD
2453 "Move across next nested block."
2454 (interactive)
0dc2be2f
S
2455 (let ((arg (or arg 1)))
2456 (if (idlwave-down-block arg)
2457 (idlwave-block-jump-out arg 'nomark))))
f32b3b91
CD
2458
2459(defun idlwave-backward-block ()
2460 "Move backward across previous nested block."
2461 (interactive)
2462 (if (idlwave-down-block -1)
2463 (idlwave-block-jump-out -1 'nomark)))
2464
2465(defun idlwave-down-block (&optional arg)
2466 "Go down a block.
2467With ARG: ARG >= 0 go forwards, ARG < 0 go backwards.
2468Returns non-nil if successfull."
2469 (interactive "p")
2470 (let (status)
2471 (if (< arg 0)
2472 ;; Backward
2473 (let ((eos (save-excursion
2474 (idlwave-block-jump-out -1 'nomark)
2475 (point))))
4b1aaa8b 2476 (if (setq status (idlwave-find-key
f32b3b91
CD
2477 idlwave-end-block-reg -1 'nomark eos))
2478 (idlwave-beginning-of-statement)
2479 (message "No nested block before beginning of containing block.")))
2480 ;; Forward
2481 (let ((eos (save-excursion
2482 (idlwave-block-jump-out 1 'nomark)
2483 (point))))
4b1aaa8b 2484 (if (setq status (idlwave-find-key
f32b3b91
CD
2485 idlwave-begin-block-reg 1 'nomark eos))
2486 (idlwave-end-of-statement)
2487 (message "No nested block before end of containing block."))))
2488 status))
2489
2490(defun idlwave-mark-doclib ()
2491 "Put point at beginning of doc library header, mark at end.
2492The marks are pushed."
2493 (interactive)
2494 (let (beg
2495 (here (point)))
2496 (goto-char (point-max))
2497 (if (re-search-backward idlwave-doclib-start nil t)
4b1aaa8b 2498 (progn
f32b3b91
CD
2499 (setq beg (progn (beginning-of-line) (point)))
2500 (if (re-search-forward idlwave-doclib-end nil t)
2501 (progn
2502 (forward-line 1)
0dc2be2f 2503 (push-mark beg nil t)
f32b3b91
CD
2504 (message "Could not find end of doc library header.")))
2505 (message "Could not find doc library header start.")
2506 (goto-char here)))))
2507
e08734e2
S
2508(defun idlwave-current-routine-fullname ()
2509 (let ((name (idlwave-current-routine)))
2510 (idlwave-make-full-name (nth 2 name) (car name))))
2511
15e42531
CD
2512(defun idlwave-current-routine ()
2513 "Return (NAME TYPE CLASS) of current routine."
2514 (idlwave-routines)
2515 (save-excursion
cca13260 2516 (idlwave-beginning-of-subprogram 'nomark)
15e42531
CD
2517 (if (looking-at "[ \t]*\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)")
2518 (let* ((type (if (string= (downcase (match-string 1)) "pro")
2519 'pro 'function))
2520 (class (idlwave-sintern-class (match-string 3)))
2521 (name (idlwave-sintern-routine-or-method (match-string 4) class)))
2522 (list name type class)))))
2523
f32b3b91
CD
2524(defvar idlwave-shell-prompt-pattern)
2525(defun idlwave-beginning-of-statement ()
2526 "Move to beginning of the current statement.
2527Skips back past statement continuations.
2528Point is placed at the beginning of the line whether or not this is an
2529actual statement."
2530 (interactive)
2531 (cond
175069ef 2532 ((derived-mode-p 'idlwave-shell-mode)
f32b3b91
CD
2533 (if (re-search-backward idlwave-shell-prompt-pattern nil t)
2534 (goto-char (match-end 0))))
4b1aaa8b 2535 (t
f32b3b91
CD
2536 (if (save-excursion (forward-line -1) (idlwave-is-continuation-line))
2537 (idlwave-previous-statement)
2538 (beginning-of-line)))))
2539
2540(defun idlwave-previous-statement ()
5a0c3f56 2541 "Move point to beginning of the previous statement.
f32b3b91
CD
2542Returns t if the current line before moving is the beginning of
2543the first non-comment statement in the file, and nil otherwise."
2544 (interactive)
2545 (let (first-statement)
2546 (if (not (= (forward-line -1) 0))
2547 ;; first line in file
2548 t
2549 ;; skip blank lines, label lines, include lines and line comments
2550 (while (and
2551 ;; The current statement is the first statement until we
2552 ;; reach another statement.
2553 (setq first-statement
2554 (or
2555 (looking-at idlwave-comment-line-start-skip)
2556 (looking-at "[ \t]*$")
2557 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2558 (looking-at "^@")))
2559 (= (forward-line -1) 0)))
2560 ;; skip continuation lines
2561 (while (and
2562 (save-excursion
2563 (forward-line -1)
2564 (idlwave-is-continuation-line))
2565 (= (forward-line -1) 0)))
2566 first-statement)))
2567
f32b3b91 2568(defun idlwave-end-of-statement ()
5a0c3f56 2569 "Move point to the end of the current IDL statement.
05a1abfc
CD
2570If not in a statement just moves to end of line. Returns position."
2571 (interactive)
2572 (while (and (idlwave-is-continuation-line)
2573 (= (forward-line 1) 0))
2574 (while (and (idlwave-is-comment-or-empty-line)
2575 (= (forward-line 1) 0))))
2576 (end-of-line)
2577 (point))
2578
2579(defun idlwave-end-of-statement0 ()
5a0c3f56
JB
2580 "Move point to the end of the current IDL statement.
2581If not in a statement just moves to end of line. Returns position."
f32b3b91
CD
2582 (interactive)
2583 (while (and (idlwave-is-continuation-line)
2584 (= (forward-line 1) 0)))
2585 (end-of-line)
2586 (point))
2587
2588(defun idlwave-next-statement ()
5a0c3f56
JB
2589 "Move point to beginning of the next IDL statement.
2590Returns t if that statement is the last non-comment IDL statement
2591in the file, and nil otherwise."
f32b3b91
CD
2592 (interactive)
2593 (let (last-statement)
2594 (idlwave-end-of-statement)
2595 ;; skip blank lines, label lines, include lines and line comments
2596 (while (and (= (forward-line 1) 0)
2597 ;; The current statement is the last statement until
2598 ;; we reach a new statement.
2599 (setq last-statement
2600 (or
2601 (looking-at idlwave-comment-line-start-skip)
2602 (looking-at "[ \t]*$")
2603 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2604 (looking-at "^@")))))
2605 last-statement))
2606
76959b77
S
2607(defun idlwave-skip-multi-commands (&optional lim)
2608 "Skip past multiple commands on a line (with `&')."
2609 (let ((save-point (point)))
2610 (when (re-search-forward ".*&" lim t)
2611 (goto-char (match-end 0))
4b1aaa8b 2612 (if (idlwave-quoted)
6b75c9af
S
2613 (goto-char save-point)
2614 (if (eq (char-after (- (point) 2)) ?&) (goto-char save-point))))
76959b77
S
2615 (point)))
2616
15e42531 2617(defun idlwave-skip-label-or-case ()
f32b3b91
CD
2618 "Skip label or case statement element.
2619Returns position after label.
2620If there is no label point is not moved and nil is returned."
15e42531
CD
2621 ;; Case expressions and labels are terminated by a colon.
2622 ;; So we find the first colon in the line and make sure
2623 ;; - no `?' is before it (might be a ? b : c)
2624 ;; - it is not in a comment
2625 ;; - not in a string constant
2626 ;; - not in parenthesis (like a[0:3])
5e72c6b2 2627 ;; - not followed by another ":" in explicit class, ala a->b::c
15e42531 2628 ;; As many in this mode, this function is heuristic and not an exact
4b1aaa8b 2629 ;; parser.
5e72c6b2
S
2630 (let* ((start (point))
2631 (eos (save-excursion (idlwave-end-of-statement) (point)))
2632 (end (idlwave-find-key ":" 1 'nomark eos)))
f32b3b91 2633 (if (and end
15e42531 2634 (= (nth 0 (parse-partial-sexp start end)) 0)
5e72c6b2
S
2635 (not (string-match "\\?" (buffer-substring start end)))
2636 (not (string-match "^::" (buffer-substring end eos))))
f32b3b91
CD
2637 (progn
2638 (forward-char)
2639 (point))
2640 (goto-char start)
2641 nil)))
2642
2643(defun idlwave-start-of-substatement (&optional pre)
2644 "Move to start of next IDL substatement after point.
2645Uses the type of the current IDL statement to determine if the next
2646statement is on a new line or is a subpart of the current statement.
2647Returns point at start of substatement modulo whitespace.
2648If optional argument is non-nil move to beginning of current
15e42531 2649substatement."
f32b3b91
CD
2650 (let ((orig (point))
2651 (eos (idlwave-end-of-statement))
2652 (ifnest 0)
2653 st nst last)
2654 (idlwave-beginning-of-statement)
15e42531 2655 (idlwave-skip-label-or-case)
52a244eb
S
2656 (if (< (point) orig)
2657 (idlwave-skip-multi-commands orig))
f32b3b91
CD
2658 (setq last (point))
2659 ;; Continue looking for substatements until we are past orig
2660 (while (and (<= (point) orig) (not (eobp)))
2661 (setq last (point))
2662 (setq nst (nth 1 (cdr (setq st (car (idlwave-statement-type))))))
2663 (if (equal (car st) 'if) (setq ifnest (1+ ifnest)))
2664 (cond ((and nst
2665 (idlwave-find-key nst 1 'nomark eos))
2666 (goto-char (match-end 0)))
2667 ((and (> ifnest 0) (idlwave-find-key "\\<else\\>" 1 'nomark eos))
2668 (setq ifnest (1- ifnest))
2669 (goto-char (match-end 0)))
2670 (t (setq ifnest 0)
2671 (idlwave-next-statement))))
2672 (if pre (goto-char last))
15e42531
CD
2673 ;; If a continuation line starts here, move to next line
2674 (if (looking-at "[ \t]*\\$\\([ \t]*\\(;\\|$\\)\\)")
2675 (beginning-of-line 2))
f32b3b91
CD
2676 (point)))
2677
2678(defun idlwave-statement-type ()
2679 "Return the type of the current IDL statement.
2680Uses `idlwave-statement-match' to return a cons of (type . point) with
5a0c3f56 2681point the ending position where the type was determined. Type is the
f32b3b91 2682association from `idlwave-statement-match', i.e. the cons cell from the
5a0c3f56 2683list not just the type symbol. Returns nil if not an identifiable
f32b3b91
CD
2684statement."
2685 (save-excursion
2686 ;; Skip whitespace within a statement which is spaces, tabs, continuations
76959b77
S
2687 ;; and possibly comments
2688 (while (looking-at "[ \t]*\\$")
f32b3b91
CD
2689 (forward-line 1))
2690 (skip-chars-forward " \t")
2691 (let ((st idlwave-statement-match)
2692 (case-fold-search t))
2693 (while (and (not (looking-at (nth 0 (cdr (car st)))))
2694 (setq st (cdr st))))
2695 (if st
2696 (append st (match-end 0))))))
2697
f66f03de 2698(defun idlwave-expand-equal (&optional before after is-action)
5a0c3f56
JB
2699 "Pad '=' with spaces.
2700Two cases: Assignment statement, and keyword assignment.
2701Which case is determined using `idlwave-start-of-substatement' and
2702`idlwave-statement-type'. The equal sign will be surrounded by BEFORE
2703and AFTER blanks. If `idlwave-pad-keyword' is t then keyword assignment
2704is treated just like assignment statements. When nil, spaces are
2705removed for keyword assignment. Any other value keeps the current space
2706around the `='. Limits in for loops are treated as keyword assignment.
52a244eb
S
2707
2708Starting with IDL 6.0, a number of op= assignments are available.
2709Since ambiguities of the form:
2710
2711r and= b
2712rand= b
2713
2714can occur, alphanumeric operator assignment will never be pre-padded,
2715only post-padded. You must use a space before these to disambiguate
2716\(not just for padding, but for proper parsing by IDL too!). Other
2717operators, such as ##=, ^=, etc., will be pre-padded.
2718
f66f03de
S
2719IS-ACTION is ignored.
2720
52a244eb 2721See `idlwave-surround'."
f32b3b91 2722 (if idlwave-surround-by-blank
4b1aaa8b 2723 (let
52a244eb 2724 ((non-an-ops "\\(##\\|\\*\\|\\+\\|-\\|/\\|<\\|>\\|\\^\\)\\=")
4b1aaa8b 2725 (an-ops
52a244eb
S
2726 "\\s-\\(AND\\|EQ\\|GE\\|GT\\|LE\\|LT\\|MOD\\|NE\\|OR\\|XOR\\)\\=")
2727 (len 1))
4b1aaa8b
PE
2728
2729 (save-excursion
52a244eb
S
2730 (let ((case-fold-search t))
2731 (backward-char)
4b1aaa8b 2732 (if (or
52a244eb
S
2733 (re-search-backward non-an-ops nil t)
2734 ;; Why doesn't ##? work for both?
4b1aaa8b 2735 (re-search-backward "\\(#\\)\\=" nil t))
52a244eb
S
2736 (setq len (1+ (length (match-string 1))))
2737 (when (re-search-backward an-ops nil t)
3938cb82 2738 ;(setq begin nil) ; won't modify begin
52a244eb 2739 (setq len (1+ (length (match-string 1))))))))
4b1aaa8b
PE
2740
2741 (if (eq t idlwave-pad-keyword)
52a244eb 2742 ;; Everything gets padded equally
f66f03de 2743 (idlwave-surround before after len)
52a244eb
S
2744 ;; Treating keywords/for variables specially...
2745 (let ((st (save-excursion ; To catch "for" variables
2746 (idlwave-start-of-substatement t)
2747 (idlwave-statement-type)))
2748 (what (save-excursion ; To catch keywords
2749 (skip-chars-backward "= \t")
2750 (nth 2 (idlwave-where)))))
2751 (cond ((or (memq what '(function-keyword procedure-keyword))
4b1aaa8b
PE
2752 (memq (caar st) '(for pdef)))
2753 (cond
52a244eb
S
2754 ((null idlwave-pad-keyword)
2755 (idlwave-surround 0 0)
2756 ) ; remove space
2757 (t))) ; leave any spaces alone
f66f03de 2758 (t (idlwave-surround before after len))))))))
4b1aaa8b 2759
f32b3b91 2760
5e72c6b2
S
2761(defun idlwave-indent-and-action (&optional arg)
2762 "Call `idlwave-indent-line' and do expand actions.
2763With prefix ARG non-nil, indent the entire sub-statement."
2764 (interactive "p")
05a1abfc 2765 (save-excursion
4b1aaa8b
PE
2766 (if (and idlwave-expand-generic-end
2767 (re-search-backward "\\<\\(end\\)\\s-*\\="
05a1abfc
CD
2768 (max 0 (- (point) 10)) t)
2769 (looking-at "\\(end\\)\\([ \n\t]\\|\\'\\)"))
2770 (progn (goto-char (match-end 1))
5e72c6b2
S
2771 ;;Expand the END abbreviation, just as RET or Space would have.
2772 (if abbrev-mode (expand-abbrev)
2773 (idlwave-show-begin)))))
52a244eb
S
2774 (when (and (not arg) current-prefix-arg)
2775 (setq arg current-prefix-arg)
2776 (setq current-prefix-arg nil))
4b1aaa8b 2777 (if arg
5e72c6b2
S
2778 (idlwave-indent-statement)
2779 (idlwave-indent-line t)))
f32b3b91
CD
2780
2781(defun idlwave-indent-line (&optional expand)
5a0c3f56 2782 "Indent current IDL line as code or as a comment.
f32b3b91
CD
2783The actions in `idlwave-indent-action-table' are performed.
2784If the optional argument EXPAND is non-nil then the actions in
2785`idlwave-indent-expand-table' are performed."
2786 (interactive)
2787 ;; Move point out of left margin.
2788 (if (save-excursion
2789 (skip-chars-backward " \t")
2790 (bolp))
2791 (skip-chars-forward " \t"))
2792 (let ((mloc (point-marker)))
2793 (save-excursion
2794 (beginning-of-line)
2795 (if (looking-at idlwave-comment-line-start-skip)
2796 ;; Indentation for a line comment
2797 (progn
2798 (skip-chars-forward " \t")
2799 (idlwave-indent-left-margin (idlwave-comment-hook)))
2800 ;;
2801 ;; Code Line
2802 ;;
2803 ;; Before indenting, run action routines.
2804 ;;
2805 (if (and expand idlwave-do-actions)
8ffcfb27 2806 (mapc 'idlwave-do-action idlwave-indent-expand-table))
f32b3b91
CD
2807 ;;
2808 (if idlwave-do-actions
8ffcfb27 2809 (mapc 'idlwave-do-action idlwave-indent-action-table))
f32b3b91
CD
2810 ;;
2811 ;; No longer expand abbrevs on the line. The user can do this
2812 ;; manually using expand-region-abbrevs.
2813 ;;
2814 ;; Indent for code line
2815 ;;
2816 (beginning-of-line)
2817 (if (or
2818 ;; a label line
2819 (looking-at (concat "^" idlwave-label "[ \t]*$"))
2820 ;; a batch command
2821 (looking-at "^[ \t]*@"))
2822 ;; leave flush left
2823 nil
2824 ;; indent the line
2825 (idlwave-indent-left-margin (idlwave-calculate-indent)))
2826 ;; Adjust parallel comment
76959b77
S
2827 (end-of-line)
2828 (if (idlwave-in-comment)
2829 ;; Emacs 21 is too smart with fill-column on comment indent
2830 (let ((fill-column (if (fboundp 'comment-indent-new-line)
2831 (1- (frame-width))
2832 fill-column)))
2833 (indent-for-comment)))))
f32b3b91
CD
2834 (goto-char mloc)
2835 ;; Get rid of marker
76959b77 2836 (set-marker mloc nil)))
f32b3b91
CD
2837
2838(defun idlwave-do-action (action)
5a0c3f56
JB
2839 "Perform an action repeatedly on a line.
2840ACTION is a list (REG . FUNC). REG is a regular expression. FUNC is
2841either a function name to be called with `funcall' or a list to be
2842evaluated with `eval'. The action performed by FUNC should leave
2843point after the match for REG - otherwise an infinite loop may be
2844entered. FUNC is always passed a final argument of 'is-action, so it
2845can discriminate between being run as an action, or a key binding."
f32b3b91
CD
2846 (let ((action-key (car action))
2847 (action-routine (cdr action)))
2848 (beginning-of-line)
2849 (while (idlwave-look-at action-key)
2850 (if (listp action-routine)
f66f03de
S
2851 (eval (append action-routine '('is-action)))
2852 (funcall action-routine 'is-action)))))
f32b3b91
CD
2853
2854(defun idlwave-indent-to (col &optional min)
2855 "Indent from point with spaces until column COL.
2856Inserts space before markers at point."
2857 (if (not min) (setq min 0))
2858 (insert-before-markers
15e42531 2859 (make-string (max min (- col (current-column))) ?\ )))
f32b3b91
CD
2860
2861(defun idlwave-indent-left-margin (col)
2862 "Indent the current line to column COL.
2863Indents such that first non-whitespace character is at column COL
2864Inserts spaces before markers at point."
2865 (save-excursion
2866 (beginning-of-line)
2867 (delete-horizontal-space)
2868 (idlwave-indent-to col)))
2869
2870(defun idlwave-indent-subprogram ()
5a0c3f56 2871 "Indent program unit which contains point."
f32b3b91
CD
2872 (interactive)
2873 (save-excursion
2874 (idlwave-end-of-statement)
2875 (idlwave-beginning-of-subprogram)
2876 (let ((beg (point)))
2877 (idlwave-forward-block)
2878 (message "Indenting subprogram...")
2879 (indent-region beg (point) nil))
2880 (message "Indenting subprogram...done.")))
2881
5e72c6b2
S
2882(defun idlwave-indent-statement ()
2883 "Indent current statement, including all continuation lines."
2884 (interactive)
2885 (save-excursion
2886 (idlwave-beginning-of-statement)
2887 (let ((beg (point)))
2888 (idlwave-end-of-statement)
2889 (indent-region beg (point) nil))))
2890
f32b3b91
CD
2891(defun idlwave-calculate-indent ()
2892 "Return appropriate indentation for current line as IDL code."
2893 (save-excursion
2894 (beginning-of-line)
2895 (cond
2896 ;; Check for beginning of unit - main (beginning of buffer), pro, or
2897 ;; function
2898 ((idlwave-look-at idlwave-begin-unit-reg)
2899 0)
2900 ;; Check for continuation line
2901 ((save-excursion
2902 (and (= (forward-line -1) 0)
2903 (idlwave-is-continuation-line)))
2904 (idlwave-calculate-cont-indent))
2905 ;; calculate indent based on previous and current statements
52a244eb
S
2906 (t (let* (beg-prev-pos
2907 (the-indent
2908 ;; calculate indent based on previous statement
2909 (save-excursion
2910 (cond
2911 ;; Beginning of file
4b1aaa8b 2912 ((prog1
52a244eb
S
2913 (idlwave-previous-statement)
2914 (setq beg-prev-pos (point)))
2915 0)
2916 ;; Main block
2917 ((idlwave-look-at idlwave-begin-unit-reg t)
2918 (+ (idlwave-current-statement-indent)
2919 idlwave-main-block-indent))
2920 ;; Begin block
2921 ((idlwave-look-at idlwave-begin-block-reg t)
4b1aaa8b 2922 (+ (idlwave-min-current-statement-indent)
52a244eb
S
2923 idlwave-block-indent))
2924 ;; End Block
2925 ((idlwave-look-at idlwave-end-block-reg t)
2926 (progn
2927 ;; Match to the *beginning* of the block opener
2928 (goto-char beg-prev-pos)
2929 (idlwave-block-jump-out -1 'nomark) ; go to begin block
2930 (idlwave-min-current-statement-indent)))
2931 ;; idlwave-end-offset
2932 ;; idlwave-block-indent))
4b1aaa8b 2933
52a244eb
S
2934 ;; Default to current indent
2935 ((idlwave-current-statement-indent))))))
f32b3b91
CD
2936 ;; adjust the indentation based on the current statement
2937 (cond
2938 ;; End block
5e72c6b2
S
2939 ((idlwave-look-at idlwave-end-block-reg)
2940 (+ the-indent idlwave-end-offset))
f32b3b91
CD
2941 (the-indent)))))))
2942
2943;;
52a244eb 2944;; Parentheses indent
f32b3b91
CD
2945;;
2946
5e72c6b2
S
2947(defun idlwave-calculate-paren-indent (beg-reg end-reg close-exp)
2948 "Calculate the continuation indent inside a paren group.
4b1aaa8b 2949Returns a cons-cell with (open . indent), where open is the
5a0c3f56 2950location of the open paren."
5e72c6b2
S
2951 (let ((open (nth 1 (parse-partial-sexp beg-reg end-reg))))
2952 ;; Found an innermost open paren.
2953 (when open
2954 (goto-char open)
2955 ;; Line up with next word unless this is a closing paren.
2956 (cons open
2957 (cond
52a244eb
S
2958 ;; Plain Kernighan-style nested indent
2959 (idlwave-indent-parens-nested
2960 (+ idlwave-continuation-indent (idlwave-current-indent)))
2961
5e72c6b2
S
2962 ;; This is a closed paren - line up under open paren.
2963 (close-exp
2964 (current-column))
52a244eb
S
2965
2966 ;; Empty (or just comment) follows -- revert to basic indent
5e72c6b2
S
2967 ((progn
2968 ;; Skip paren
2969 (forward-char 1)
2970 (looking-at "[ \t$]*\\(;.*\\)?$"))
52a244eb
S
2971 nil)
2972
2973 ;; Line up with first word after any blank space
5e72c6b2
S
2974 ((progn
2975 (skip-chars-forward " \t")
2976 (current-column))))))))
2977
f32b3b91 2978(defun idlwave-calculate-cont-indent ()
5a0c3f56
JB
2979 "Calculates the IDL continuation indent column from the previous statement.
2980Note that here previous statement usually means the beginning of the
2981current statement if this statement is a continuation of the previous
2982line. Various special types of continuations, including assignments,
2983routine definitions, and parenthetical groupings, are treated separately."
f32b3b91 2984 (save-excursion
52a244eb 2985 (let* ((case-fold-search t)
f32b3b91 2986 (end-reg (progn (beginning-of-line) (point)))
52a244eb
S
2987 (beg-last-statement (save-excursion (idlwave-previous-statement)
2988 (point)))
4b1aaa8b 2989 (beg-reg (progn (idlwave-start-of-substatement 'pre)
52a244eb
S
2990 (if (eq (line-beginning-position) end-reg)
2991 (goto-char beg-last-statement)
2992 (point))))
2993 (basic-indent (+ (idlwave-min-current-statement-indent end-reg)
2994 idlwave-continuation-indent))
2995 fancy-nonparen-indent fancy-paren-indent)
4b1aaa8b 2996 (cond
52a244eb
S
2997 ;; Align then with its matching if, etc.
2998 ((let ((matchers '(("\\<if\\>" . "[ \t]*then")
2999 ("\\<\\(if\\|end\\(if\\)?\\)\\>" . "[ \t]*else")
3000 ("\\<\\(for\\|while\\)\\>" . "[ \t]*do")
4b1aaa8b 3001 ("\\<\\(repeat\\|end\\(rep\\)?\\)\\>" .
52a244eb
S
3002 "[ \t]*until")
3003 ("\\<case\\>" . "[ \t]*of")))
3004 match cont-re)
3005 (goto-char end-reg)
4b1aaa8b 3006 (and
52a244eb
S
3007 (setq cont-re
3008 (catch 'exit
3009 (while (setq match (car matchers))
3010 (if (looking-at (cdr match))
3011 (throw 'exit (car match)))
3012 (setq matchers (cdr matchers)))))
3013 (idlwave-find-key cont-re -1 'nomark beg-last-statement)))
3014 (if (looking-at "end") ;; that one's special
4b1aaa8b 3015 (- (idlwave-current-indent)
52a244eb
S
3016 (+ idlwave-block-indent idlwave-end-offset))
3017 (idlwave-current-indent)))
3018
3019 ;; Indent in from the previous line for continuing statements
3020 ((let ((matchers '("\\<then\\>"
3021 "\\<do\\>"
3022 "\\<repeat\\>"
3023 "\\<else\\>"))
3024 match)
3025 (catch 'exit
3026 (goto-char end-reg)
3027 (if (/= (forward-line -1) 0)
3028 (throw 'exit nil))
3029 (while (setq match (car matchers))
3030 (if (looking-at (concat ".*" match "[ \t]*\\$[ \t]*"
3031 "\\(;.*\\)?$"))
3032 (throw 'exit t))
3033 (setq matchers (cdr matchers)))))
3034 (+ idlwave-continuation-indent (idlwave-current-indent)))
3035
3036 ;; Parenthetical indent, either traditional or Kernighan style
3037 ((setq fancy-paren-indent
3038 (let* ((end-reg end-reg)
3039 (close-exp (progn
3040 (goto-char end-reg)
4b1aaa8b 3041 (skip-chars-forward " \t")
52a244eb
S
3042 (looking-at "\\s)")))
3043 indent-cons)
3044 (catch 'loop
3045 (while (setq indent-cons (idlwave-calculate-paren-indent
3046 beg-reg end-reg close-exp))
3047 ;; First permitted containing paren
3048 (if (or
3049 idlwave-indent-to-open-paren
3050 idlwave-indent-parens-nested
3051 (null (cdr indent-cons))
3052 (< (- (cdr indent-cons) basic-indent)
3053 idlwave-max-extra-continuation-indent))
3054 (throw 'loop (cdr indent-cons)))
3055 (setq end-reg (car indent-cons))))))
5e72c6b2
S
3056 fancy-paren-indent)
3057
52a244eb
S
3058 ;; A continued assignment, or procedure call/definition
3059 ((and
3060 (> idlwave-max-extra-continuation-indent 0)
3061 (setq fancy-nonparen-indent
3062 (progn
3063 (goto-char beg-reg)
3064 (while (idlwave-look-at "&")) ; skip continued statements
3065 (cond
3066 ;; A continued Procedure call or definition
3067 ((progn
3068 (idlwave-look-at "^[ \t]*\\(pro\\|function\\)") ;skip over
3069 (looking-at "[ \t]*\\([a-zA-Z0-9.$_]+[ \t]*->[ \t]*\\)?[a-zA-Z][:a-zA-Z0-9$_]*[ \t]*\\(,\\)[ \t]*"))
3070 (goto-char (match-end 0))
3071 ;; Comment only, or blank line with "$"? Basic indent.
3072 (if (save-match-data (looking-at "[ \t$]*\\(;.*\\)?$"))
3073 nil
3074 (current-column)))
4b1aaa8b 3075
52a244eb
S
3076 ;; Continued assignment (with =):
3077 ((catch 'assign ;
3078 (while (looking-at "[^=\n\r]*\\(=\\)[ \t]*")
3079 (goto-char (match-end 0))
4b1aaa8b 3080 (if (null (idlwave-what-function beg-reg))
52a244eb
S
3081 (throw 'assign t))))
3082 (unless (or
3083 (idlwave-in-quote)
3084 (looking-at "[ \t$]*\\(;.*\\)?$") ; use basic
3085 (save-excursion
3086 (goto-char beg-last-statement)
3087 (eq (caar (idlwave-statement-type)) 'for)))
3088 (current-column))))))
3089 (< (- fancy-nonparen-indent basic-indent)
3090 idlwave-max-extra-continuation-indent))
3091 (if fancy-paren-indent ;calculated but disallowed paren indent
3092 (+ fancy-nonparen-indent idlwave-continuation-indent)
3093 fancy-nonparen-indent))
3094
3095 ;; Basic indent, by default
3096 (t basic-indent)))))
3097
3098
f32b3b91 3099
15e42531
CD
3100(defun idlwave-find-key (key-re &optional dir nomark limit)
3101 "Move to next match of the regular expression KEY-RE.
3102Matches inside comments or string constants will be ignored.
3103If DIR is negative, the search will be backwards.
3104At a successful match, the mark is pushed unless NOMARK is non-nil.
3105Searches are limited to LIMIT.
3106Searches are case-insensitive and use a special syntax table which
3107treats `$' and `_' as word characters.
3108Return value is the beginning of the match or (in case of failure) nil."
3109 (setq dir (or dir 0))
3110 (let ((case-fold-search t)
3111 (search-func (if (> dir 0) 're-search-forward 're-search-backward))
3112 found)
3113 (idlwave-with-special-syntax
3114 (save-excursion
3115 (catch 'exit
3116 (while (funcall search-func key-re limit t)
3117 (if (not (idlwave-quoted))
52a244eb
S
3118 (throw 'exit (setq found (match-beginning 0)))
3119 (if (or (and (> dir 0) (eobp))
3120 (and (< dir 0) (bobp)))
3121 (throw 'exit nil)))))))
15e42531
CD
3122 (if found
3123 (progn
3124 (if (not nomark) (push-mark))
3125 (goto-char found)
3126 found)
3127 nil)))
3128
f32b3b91
CD
3129(defun idlwave-block-jump-out (&optional dir nomark)
3130 "When optional argument DIR is non-negative, move forward to end of
3131current block using the `idlwave-begin-block-reg' and `idlwave-end-block-reg'
5a0c3f56
JB
3132regular expressions. When DIR is negative, move backwards to block beginning.
3133Recursively calls itself to skip over nested blocks. DIR defaults to
3134forward. Calls `push-mark' unless the optional argument NOMARK is
3135non-nil. Movement is limited by the start of program units because of
f32b3b91
CD
3136possibility of unbalanced blocks."
3137 (interactive "P")
3138 (or dir (setq dir 0))
3139 (let* ((here (point))
3140 (case-fold-search t)
3141 (limit (if (>= dir 0) (point-max) (point-min)))
4b1aaa8b 3142 (block-limit (if (>= dir 0)
f32b3b91
CD
3143 idlwave-begin-block-reg
3144 idlwave-end-block-reg))
3145 found
3146 (block-reg (concat idlwave-begin-block-reg "\\|"
3147 idlwave-end-block-reg))
3148 (unit-limit (or (save-excursion
3149 (if (< dir 0)
3150 (idlwave-find-key
3151 idlwave-begin-unit-reg dir t limit)
3152 (end-of-line)
4b1aaa8b 3153 (idlwave-find-key
f32b3b91
CD
3154 idlwave-end-unit-reg dir t limit)))
3155 limit)))
3156 (if (>= dir 0) (end-of-line)) ;Make sure we are in current block
3157 (if (setq found (idlwave-find-key block-reg dir t unit-limit))
3158 (while (and found (looking-at block-limit))
3159 (if (>= dir 0) (forward-word 1))
3160 (idlwave-block-jump-out dir t)
3161 (setq found (idlwave-find-key block-reg dir t unit-limit))))
3162 (if (not nomark) (push-mark here))
3163 (if (not found) (goto-char unit-limit)
3164 (if (>= dir 0) (forward-word 1)))))
3165
52a244eb
S
3166(defun idlwave-min-current-statement-indent (&optional end-reg)
3167 "The minimum indent in the current statement."
3168 (idlwave-beginning-of-statement)
3169 (if (not (idlwave-is-continuation-line))
3170 (idlwave-current-indent)
3171 (let ((min (idlwave-current-indent)) comm-or-empty)
3172 (while (and (= (forward-line 1) 0)
3173 (or (setq comm-or-empty (idlwave-is-comment-or-empty-line))
3174 (idlwave-is-continuation-line))
3175 (or (null end-reg) (< (point) end-reg)))
3176 (unless comm-or-empty (setq min (min min (idlwave-current-indent)))))
3177 (if (or comm-or-empty (and end-reg (>= (point) end-reg)))
4b1aaa8b 3178 min
52a244eb
S
3179 (min min (idlwave-current-indent))))))
3180
3181(defun idlwave-current-statement-indent (&optional last-line)
f32b3b91
CD
3182 "Return indentation of the current statement.
3183If in a statement, moves to beginning of statement before finding indent."
52a244eb
S
3184 (if last-line
3185 (idlwave-end-of-statement)
3186 (idlwave-beginning-of-statement))
f32b3b91
CD
3187 (idlwave-current-indent))
3188
3189(defun idlwave-current-indent ()
3190 "Return the column of the indentation of the current line.
5a0c3f56 3191Skips any whitespace. Returns 0 if the end-of-line follows the whitespace."
f32b3b91
CD
3192 (save-excursion
3193 (beginning-of-line)
3194 (skip-chars-forward " \t")
3195 ;; if we are at the end of blank line return 0
3196 (cond ((eolp) 0)
3197 ((current-column)))))
3198
3199(defun idlwave-is-continuation-line ()
5a0c3f56 3200 "Test if current line is continuation line.
5e72c6b2
S
3201Blank or comment-only lines following regular continuation lines (with
3202`$') count as continuations too."
0dc2be2f
S
3203 (let (p)
3204 (save-excursion
4b1aaa8b 3205 (or
0dc2be2f
S
3206 (idlwave-look-at "\\<\\$")
3207 (catch 'loop
4b1aaa8b 3208 (while (and (looking-at "^[ \t]*\\(;.*\\)?$")
0dc2be2f
S
3209 (eq (forward-line -1) 0))
3210 (if (setq p (idlwave-look-at "\\<\\$")) (throw 'loop p))))))))
f32b3b91
CD
3211
3212(defun idlwave-is-comment-line ()
5a0c3f56 3213 "Test if the current line is a comment line."
f32b3b91
CD
3214 (save-excursion
3215 (beginning-of-line 1)
3216 (looking-at "[ \t]*;")))
3217
05a1abfc 3218(defun idlwave-is-comment-or-empty-line ()
5a0c3f56 3219 "Test if the current line is a comment line."
05a1abfc
CD
3220 (save-excursion
3221 (beginning-of-line 1)
3222 (looking-at "[ \t]*[;\n]")))
3223
f32b3b91 3224(defun idlwave-look-at (regexp &optional cont beg)
5a0c3f56 3225 "Search current line from current point for REGEXP.
15e42531
CD
3226If optional argument CONT is non-nil, searches to the end of
3227the current statement.
3228If optional arg BEG is non-nil, search starts from the beginning of the
3229current statement.
3230Ignores matches that end in a comment or inside a string expression.
3231Returns point if successful, nil otherwise.
3232This function produces unexpected results if REGEXP contains quotes or
5a0c3f56 3233a comment delimiter. The search is case insensitive.
15e42531 3234If successful leaves point after the match, otherwise, does not move point."
f32b3b91 3235 (let ((here (point))
f32b3b91 3236 (case-fold-search t)
15e42531
CD
3237 (eos (save-excursion
3238 (if cont (idlwave-end-of-statement) (end-of-line))
3239 (point)))
f32b3b91 3240 found)
15e42531
CD
3241 (idlwave-with-special-syntax
3242 (if beg (idlwave-beginning-of-statement))
3243 (while (and (setq found (re-search-forward regexp eos t))
3244 (idlwave-quoted))))
f32b3b91
CD
3245 (if (not found) (goto-char here))
3246 found))
3247
3248(defun idlwave-fill-paragraph (&optional nohang)
5a0c3f56 3249 "Fill paragraphs in comments.
f32b3b91
CD
3250A paragraph is made up of all contiguous lines having the same comment
3251leader (the leading whitespace before the comment delimiter and the
3252comment delimiter). In addition, paragraphs are separated by blank
5a0c3f56 3253line comments. The indentation is given by the hanging indent of the
f32b3b91 3254first line, otherwise by the minimum indentation of the lines after
5a0c3f56
JB
3255the first line. The indentation of the first line does not change.
3256Does not effect code lines. Does not fill comments on the same line
f32b3b91 3257with code. The hanging indent is given by the end of the first match
5a0c3f56
JB
3258matching `idlwave-hang-indent-regexp' on the paragraph's first line.
3259If the optional argument NOHANG is non-nil then the hanging indent is
f32b3b91
CD
3260ignored."
3261 (interactive "P")
3262 ;; check if this is a line comment
3263 (if (save-excursion
3264 (beginning-of-line)
3265 (skip-chars-forward " \t")
3266 (looking-at comment-start))
3267 (let
3268 ((indent 999)
3269 pre here diff fill-prefix-reg bcl first-indent
3270 hang start end)
3271 ;; Change tabs to spaces in the surrounding paragraph.
3272 ;; The surrounding paragraph will be the largest containing block of
3273 ;; contiguous line comments. Thus, we may be changing tabs in
3274 ;; a much larger area than is needed, but this is the easiest
3275 ;; brute force way to do it.
3276 ;;
3277 ;; This has the undesirable side effect of replacing the tabs
3278 ;; permanently without the user's request or knowledge.
3279 (save-excursion
3280 (backward-paragraph)
3281 (setq start (point)))
3282 (save-excursion
3283 (forward-paragraph)
3284 (setq end (point)))
3285 (untabify start end)
3286 ;;
3287 (setq here (point))
3288 (beginning-of-line)
3289 (setq bcl (point))
e180ab9f
GM
3290 (re-search-forward (concat "^[ \t]*" comment-start "+")
3291 (point-at-eol) t)
f32b3b91
CD
3292 ;; Get the comment leader on the line and its length
3293 (setq pre (current-column))
3294 ;; the comment leader is the indentation plus exactly the
3295 ;; number of consecutive ";".
3296 (setq fill-prefix-reg
3297 (concat
3298 (setq fill-prefix
9b026d9f 3299 (regexp-quote (buffer-substring (point-at-bol) (point))))
f32b3b91 3300 "[^;]"))
4b1aaa8b 3301
f32b3b91
CD
3302 ;; Mark the beginning and end of the paragraph
3303 (goto-char bcl)
3304 (while (and (looking-at fill-prefix-reg)
3305 (not (looking-at paragraph-separate))
3306 (not (bobp)))
3307 (forward-line -1))
3308 ;; Move to first line of paragraph
3309 (if (/= (point) bcl)
3310 (forward-line 1))
3311 (setq start (point))
3312 (goto-char bcl)
3313 (while (and (looking-at fill-prefix-reg)
3314 (not (looking-at paragraph-separate))
3315 (not (eobp)))
3316 (forward-line 1))
3317 (beginning-of-line)
3318 (if (or (not (looking-at fill-prefix-reg))
3319 (looking-at paragraph-separate))
3320 (forward-line -1))
3321 (end-of-line)
3322 ;; if at end of buffer add a newline (need this because
3323 ;; fill-region needs END to be at the beginning of line after
3324 ;; the paragraph or it will add a line).
3325 (if (eobp)
3326 (progn (insert ?\n) (backward-char 1)))
3327 ;; Set END to the beginning of line after the paragraph
3328 ;; END is calculated as distance from end of buffer
3329 (setq end (- (point-max) (point) 1))
3330 ;;
3331 ;; Calculate the indentation for the paragraph.
3332 ;;
3333 ;; In the following while statements, after one iteration
3334 ;; point will be at the beginning of a line in which case
3335 ;; the while will not be executed for the
3336 ;; the first paragraph line and thus will not affect the
3337 ;; indentation.
3338 ;;
3339 ;; First check to see if indentation is based on hanging indent.
3340 (if (and (not nohang) idlwave-hanging-indent
3341 (setq hang
3342 (save-excursion
3343 (goto-char start)
3344 (idlwave-calc-hanging-indent))))
3345 ;; Adjust lines of paragraph by inserting spaces so that
3346 ;; each line's indent is at least as great as the hanging
3347 ;; indent. This is needed for fill-paragraph to work with
3348 ;; a fill-prefix.
3349 (progn
3350 (setq indent hang)
3351 (beginning-of-line)
3352 (while (> (point) start)
e180ab9f 3353 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91
CD
3354 (if (> (setq diff (- indent (current-column))) 0)
3355 (progn
3356 (if (>= here (point))
3357 ;; adjust the original location for the
3358 ;; inserted text.
3359 (setq here (+ here diff)))
15e42531 3360 (insert (make-string diff ?\ ))))
f32b3b91
CD
3361 (forward-line -1))
3362 )
4b1aaa8b 3363
f32b3b91
CD
3364 ;; No hang. Instead find minimum indentation of paragraph
3365 ;; after first line.
3366 ;; For the following while statement, since START is at the
aa87aafc 3367 ;; beginning of line and END is at the end of line
f32b3b91
CD
3368 ;; point is greater than START at least once (which would
3369 ;; be the case for a single line paragraph).
3370 (while (> (point) start)
3371 (beginning-of-line)
3372 (setq indent
3373 (min indent
3374 (progn
e180ab9f 3375 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91 3376 (current-column))))
e180ab9f 3377 (forward-line -1)))
f32b3b91
CD
3378 (setq fill-prefix (concat fill-prefix
3379 (make-string (- indent pre)
15e42531 3380 ?\ )))
f32b3b91
CD
3381 ;; first-line indent
3382 (setq first-indent
3383 (max
3384 (progn
e180ab9f 3385 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91
CD
3386 (current-column))
3387 indent))
4b1aaa8b 3388
f32b3b91
CD
3389 ;; try to keep point at its original place
3390 (goto-char here)
3391
3392 ;; In place of the more modern fill-region-as-paragraph, a hack
3393 ;; to keep whitespace untouched on the first line within the
3394 ;; indent length and to preserve any indent on the first line
3395 ;; (first indent).
3396 (save-excursion
3397 (setq diff
3398 (buffer-substring start (+ start first-indent -1)))
15e42531 3399 (subst-char-in-region start (+ start first-indent -1) ?\ ?~ nil)
f32b3b91
CD
3400 (fill-region-as-paragraph
3401 start
3402 (- (point-max) end)
3403 (current-justification)
3404 nil)
3405 (delete-region start (+ start first-indent -1))
3406 (goto-char start)
3407 (insert diff))
3408 ;; When we want the point at the beginning of the comment
3409 ;; body fill-region will put it at the beginning of the line.
3410 (if (bolp) (skip-chars-forward (concat " \t" comment-start)))
3411 (setq fill-prefix nil))))
3412
3413(defun idlwave-calc-hanging-indent ()
5a0c3f56
JB
3414 "Calculate the position of the hanging indent for the comment paragraph.
3415The hanging indent position is given by the first match with the
3416`idlwave-hang-indent-regexp'. If `idlwave-use-last-hang-indent' is
3417non-nil then use last occurrence matching `idlwave-hang-indent-regexp'
3418on the line.
f32b3b91
CD
3419If not found returns nil."
3420 (if idlwave-use-last-hang-indent
3421 (save-excursion
3422 (end-of-line)
e180ab9f 3423 (if (re-search-backward idlwave-hang-indent-regexp (point-at-bol) t)
f32b3b91
CD
3424 (+ (current-column) (length idlwave-hang-indent-regexp))))
3425 (save-excursion
3426 (beginning-of-line)
e180ab9f 3427 (if (re-search-forward idlwave-hang-indent-regexp (point-at-eol) t)
f32b3b91
CD
3428 (current-column)))))
3429
3430(defun idlwave-auto-fill ()
4b1aaa8b 3431 "Called to break lines in auto fill mode.
52a244eb
S
3432Only fills non-comment lines if `idlwave-fill-comment-line-only' is
3433non-nil. Places a continuation character at the end of the line if
3434not in a comment. Splits strings with IDL concatenation operator `+'
3435if `idlwave-auto-fill-split-string' is non-nil."
f32b3b91
CD
3436 (if (<= (current-column) fill-column)
3437 nil ; do not to fill
3438 (if (or (not idlwave-fill-comment-line-only)
3439 (save-excursion
3440 ;; Check for comment line
3441 (beginning-of-line)
3442 (looking-at idlwave-comment-line-start-skip)))
3443 (let (beg)
3444 (idlwave-indent-line)
3445 ;; Prevent actions do-auto-fill which calls indent-line-function.
3446 (let (idlwave-do-actions
d6aac72d 3447 (paragraph-separate ".")
52a244eb
S
3448 (fill-nobreak-predicate
3449 (if (and (idlwave-in-quote)
3450 idlwave-auto-fill-split-string)
3451 (lambda () ;; We'll need 5 spaces for " ' + $"
3452 (<= (- fill-column (current-column)) 5)
3453 ))))
f32b3b91
CD
3454 (do-auto-fill))
3455 (save-excursion
3456 (end-of-line 0)
3457 ;; Indent the split line
a86bd650 3458 (idlwave-indent-line))
f32b3b91
CD
3459 (if (save-excursion
3460 (beginning-of-line)
3461 (looking-at idlwave-comment-line-start-skip))
3462 ;; A continued line comment
3463 ;; We treat continued line comments as part of a comment
3464 ;; paragraph. So we check for a hanging indent.
3465 (if idlwave-hanging-indent
3466 (let ((here (- (point-max) (point)))
3467 (indent
3468 (save-excursion
3469 (forward-line -1)
3470 (idlwave-calc-hanging-indent))))
e180ab9f
GM
3471 (when indent
3472 ;; Remove whitespace between comment delimiter and
3473 ;; text, insert spaces for appropriate indentation.
3474 (beginning-of-line)
3475 (re-search-forward comment-start-skip (point-at-eol) t)
3476 (delete-horizontal-space)
3477 (idlwave-indent-to indent)
3478 (goto-char (- (point-max) here)))))
f32b3b91
CD
3479 ;; Split code or comment?
3480 (if (save-excursion
3481 (end-of-line 0)
3482 (idlwave-in-comment))
52a244eb 3483 ;; Splitting a non-full-line comment.
f32b3b91
CD
3484 ;; Insert the comment delimiter from split line
3485 (progn
3486 (save-excursion
3487 (beginning-of-line)
3488 (skip-chars-forward " \t")
3489 ;; Insert blank to keep off beginning of line
3490 (insert " "
3491 (save-excursion
3492 (forward-line -1)
3493 (buffer-substring (idlwave-goto-comment)
3494 (progn
3495 (skip-chars-forward "; ")
3496 (point))))))
3497 (idlwave-indent-line))
3498 ;; Split code line - add continuation character
3499 (save-excursion
3500 (end-of-line 0)
3501 ;; Check to see if we split a string
3502 (if (and (setq beg (idlwave-in-quote))
3503 idlwave-auto-fill-split-string)
3504 ;; Split the string and concatenate.
3505 ;; The first extra space is for the space
3506 ;; the line was split. That space was removed.
3507 (insert " " (char-after beg) " +"))
3508 (insert " $"))
3509 (if beg
3510 (if idlwave-auto-fill-split-string
3511 ;; Make the second part of continued string
3512 (save-excursion
3513 (beginning-of-line)
3514 (skip-chars-forward " \t")
3515 (insert (char-after beg)))
3516 ;; Warning
3517 (beep)
3518 (message "Warning: continuation inside a string.")))
3519 ;; Although do-auto-fill (via indent-new-comment-line) calls
3520 ;; idlwave-indent-line for the new line, re-indent again
3521 ;; because of the addition of the continuation character.
3522 (idlwave-indent-line))
3523 )))))
3524
3525(defun idlwave-auto-fill-mode (arg)
3526 "Toggle auto-fill mode for IDL mode.
3527With arg, turn auto-fill mode on if arg is positive.
3528In auto-fill mode, inserting a space at a column beyond `fill-column'
3529automatically breaks the line at a previous space."
3530 (interactive "P")
3531 (prog1 (set idlwave-fill-function
3532 (if (if (null arg)
3533 (not (symbol-value idlwave-fill-function))
3534 (> (prefix-numeric-value arg) 0))
3535 'idlwave-auto-fill
3536 nil))
3537 ;; update mode-line
3538 (set-buffer-modified-p (buffer-modified-p))))
3539
52a244eb
S
3540;(defun idlwave-fill-routine-call ()
3541; "Fill a routine definition or statement, indenting appropriately."
3542; (let ((where (idlwave-where)))))
3543
3544
5a0c3f56 3545(defun idlwave-doc-header (&optional nomark)
f32b3b91 3546 "Insert a documentation header at the beginning of the unit.
5a0c3f56
JB
3547Inserts the value of the variable `idlwave-file-header'. Sets mark
3548before moving to do insertion unless the optional prefix argument
3549NOMARK is non-nil."
f32b3b91
CD
3550 (interactive "P")
3551 (or nomark (push-mark))
3552 ;; make sure we catch the current line if it begins the unit
5e72c6b2
S
3553 (if idlwave-header-to-beginning-of-file
3554 (goto-char (point-min))
3555 (end-of-line)
3556 (idlwave-beginning-of-subprogram)
3557 (beginning-of-line)
3558 ;; skip function or procedure line
3559 (if (idlwave-look-at "\\<\\(pro\\|function\\)\\>")
3560 (progn
3561 (idlwave-end-of-statement)
3562 (if (> (forward-line 1) 0) (insert "\n")))))
3563 (let ((pos (point)))
3564 (if idlwave-file-header
3565 (cond ((car idlwave-file-header)
a527b753 3566 (insert-file-contents (car idlwave-file-header)))
5e72c6b2
S
3567 ((stringp (car (cdr idlwave-file-header)))
3568 (insert (car (cdr idlwave-file-header))))))
3569 (goto-char pos)))
f32b3b91
CD
3570
3571(defun idlwave-default-insert-timestamp ()
5a0c3f56 3572 "Default timestamp insertion function."
f32b3b91
CD
3573 (insert (current-time-string))
3574 (insert ", " (user-full-name))
5e72c6b2 3575 (if (boundp 'user-mail-address)
4b1aaa8b 3576 (insert " <" user-mail-address ">")
5e72c6b2 3577 (insert " <" (user-login-name) "@" (system-name) ">"))
f32b3b91
CD
3578 ;; Remove extra spaces from line
3579 (idlwave-fill-paragraph)
3580 ;; Insert a blank line comment to separate from the date entry -
3581 ;; will keep the entry from flowing onto date line if re-filled.
5e72c6b2 3582 (insert "\n;\n;\t\t"))
f32b3b91
CD
3583
3584(defun idlwave-doc-modification ()
3585 "Insert a brief modification log at the beginning of the current program.
3586Looks for an occurrence of the value of user variable
5a0c3f56
JB
3587`idlwave-doc-modifications-keyword' if non-nil. Inserts time and user
3588name and places the point for the user to add a log. Before moving, saves
f32b3b91
CD
3589location on mark ring so that the user can return to previous point."
3590 (interactive)
3591 (push-mark)
05a1abfc
CD
3592 (let* (beg end)
3593 (if (and (or (re-search-backward idlwave-doclib-start nil t)
3594 (progn
3595 (goto-char (point-min))
3596 (re-search-forward idlwave-doclib-start nil t)))
3597 (setq beg (match-beginning 0))
3598 (re-search-forward idlwave-doclib-end nil t)
3599 (setq end (match-end 0)))
3600 (progn
3601 (goto-char beg)
4b1aaa8b 3602 (if (re-search-forward
05a1abfc
CD
3603 (concat idlwave-doc-modifications-keyword ":")
3604 end t)
3605 (end-of-line)
3606 (goto-char end)
3607 (end-of-line -1)
3608 (insert "\n" comment-start "\n")
3609 (insert comment-start " " idlwave-doc-modifications-keyword ":"))
3610 (insert "\n;\n;\t")
3611 (run-hooks 'idlwave-timestamp-hook))
3612 (error "No valid DOCLIB header"))))
f32b3b91 3613
e08734e2 3614
8d222148
SM
3615;; CJC 3/16/93
3616;; Interface to expand-region-abbrevs which did not work when the
3617;; abbrev hook associated with an abbrev moves point backwards
3618;; after abbrev expansion, e.g., as with the abbrev '.n'.
3619;; The original would enter an infinite loop in attempting to expand
3620;; .n (it would continually expand and unexpand the abbrev without expanding
3621;; because the point would keep going back to the beginning of the
3622;; abbrev instead of to the end of the abbrev). We now keep the
3623;; abbrev hook from moving backwards.
f32b3b91
CD
3624;;;
3625(defun idlwave-expand-region-abbrevs (start end)
3626 "Expand each abbrev occurrence in the region.
3627Calling from a program, arguments are START END."
3628 (interactive "r")
3629 (save-excursion
3630 (goto-char (min start end))
3631 (let ((idlwave-show-block nil) ;Do not blink
3632 (idlwave-abbrev-move nil)) ;Do not move
3633 (expand-region-abbrevs start end 'noquery))))
3634
3635(defun idlwave-quoted ()
5a0c3f56
JB
3636 "Return t if point is in a comment or quoted string.
3637Returns nil otherwise."
f32b3b91
CD
3638 (or (idlwave-in-comment) (idlwave-in-quote)))
3639
3640(defun idlwave-in-quote ()
5a0c3f56 3641 "Return location of the opening quote
f32b3b91
CD
3642if point is in a IDL string constant, nil otherwise.
3643Ignores comment delimiters on the current line.
3644Properly handles nested quotation marks and octal
3645constants - a double quote followed by an octal digit."
8d222148
SM
3646;; Treat an octal inside an apostrophe to be a normal string. Treat a
3647;; double quote followed by an octal digit to be an octal constant
3648;; rather than a string. Therefore, there is no terminating double
3649;; quote.
f32b3b91
CD
3650 (save-excursion
3651 ;; Because single and double quotes can quote each other we must
3652 ;; search for the string start from the beginning of line.
3653 (let* ((start (point))
9b026d9f 3654 (eol (point-at-eol))
f32b3b91
CD
3655 (bq (progn (beginning-of-line) (point)))
3656 (endq (point))
3657 (data (match-data))
3658 delim
3659 found)
3660 (while (< endq start)
3661 ;; Find string start
3662 ;; Don't find an octal constant beginning with a double quote
52a244eb 3663 (if (re-search-forward "[\"']" eol 'lim)
f32b3b91
CD
3664 ;; Find the string end.
3665 ;; In IDL, two consecutive delimiters after the start of a
3666 ;; string act as an
3667 ;; escape for the delimiter in the string.
3668 ;; Two consecutive delimiters alone (i.e., not after the
aa87aafc 3669 ;; start of a string) is the null string.
f32b3b91
CD
3670 (progn
3671 ;; Move to position after quote
3672 (goto-char (1+ (match-beginning 0)))
3673 (setq bq (1- (point)))
3674 ;; Get the string delimiter
3675 (setq delim (char-to-string (preceding-char)))
3676 ;; Check for null string
3677 (if (looking-at delim)
3678 (progn (setq endq (point)) (forward-char 1))
3679 ;; Look for next unpaired delimiter
3680 (setq found (search-forward delim eol 'lim))
3681 (while (looking-at delim)
3682 (forward-char 1)
3683 (setq found (search-forward delim eol 'lim)))
8d222148 3684 (setq endq (if found (1- (point)) (point)))
f32b3b91
CD
3685 ))
3686 (progn (setq bq (point)) (setq endq (point)))))
3687 (store-match-data data)
3688 ;; return string beginning position or nil
3689 (if (> start bq) bq))))
3690
76959b77 3691(defun idlwave-is-pointer-dereference (&optional limit)
5a0c3f56 3692 "Determine if the character after point is a pointer dereference *."
8d222148
SM
3693 (and
3694 (eq (char-after) ?\*)
3695 (not (idlwave-in-quote))
3696 (save-excursion
3697 (forward-char)
3698 (re-search-backward (concat "\\(" idlwave-idl-keywords
3699 "\\|[[(*+-/=,^><]\\)\\s-*\\*") limit t))))
76959b77
S
3700
3701
f32b3b91
CD
3702;; Statement templates
3703
3704;; Replace these with a general template function, something like
3705;; expand.el (I think there was also something with a name similar to
3706;; dmacro.el)
3707
3708(defun idlwave-template (s1 s2 &optional prompt noindent)
3709 "Build a template with optional prompt expression.
3710
3711Opens a line if point is not followed by a newline modulo intervening
3712whitespace. S1 and S2 are strings. S1 is inserted at point followed
595ab50b 3713by S2. Point is inserted between S1 and S2. The case of S1 and S2 is
5a0c3f56
JB
3714adjusted according to `idlwave-abbrev-change-case'. If optional
3715argument PROMPT is a string then it is displayed as a message in the
f32b3b91
CD
3716minibuffer. The PROMPT serves as a reminder to the user of an
3717expression to enter.
3718
3719The lines containing S1 and S2 are reindented using `indent-region'
3720unless the optional second argument NOINDENT is non-nil."
175069ef 3721 (if (derived-mode-p 'idlwave-shell-mode)
05a1abfc 3722 ;; This is a gross hack to avoit template abbrev expansion
15e42531
CD
3723 ;; in the shell. FIXME: This is a dirty hack.
3724 (if (and (eq this-command 'self-insert-command)
3725 (equal last-abbrev-location (point)))
3726 (insert last-abbrev-text)
3727 (error "No templates in idlwave-shell"))
3728 (cond ((eq idlwave-abbrev-change-case 'down)
3729 (setq s1 (downcase s1) s2 (downcase s2)))
3730 (idlwave-abbrev-change-case
3731 (setq s1 (upcase s1) s2 (upcase s2))))
e180ab9f 3732 (let ((beg (point-at-bol))
15e42531
CD
3733 end)
3734 (if (not (looking-at "\\s-*\n"))
3735 (open-line 1))
3736 (insert s1)
3737 (save-excursion
3738 (insert s2)
3739 (setq end (point)))
3740 (if (not noindent)
3741 (indent-region beg end nil))
3742 (if (stringp prompt)
274f1353 3743 (message "%s" prompt)))))
4b1aaa8b 3744
595ab50b
CD
3745(defun idlwave-rw-case (string)
3746 "Make STRING have the case required by `idlwave-reserved-word-upcase'."
3747 (if idlwave-reserved-word-upcase
3748 (upcase string)
3749 string))
3750
f32b3b91
CD
3751(defun idlwave-elif ()
3752 "Build skeleton IDL if-else block."
3753 (interactive)
595ab50b
CD
3754 (idlwave-template
3755 (idlwave-rw-case "if")
3756 (idlwave-rw-case " then begin\n\nendif else begin\n\nendelse")
3757 "Condition expression"))
f32b3b91
CD
3758
3759(defun idlwave-case ()
3760 "Build skeleton IDL case statement."
3761 (interactive)
4b1aaa8b 3762 (idlwave-template
595ab50b
CD
3763 (idlwave-rw-case "case")
3764 (idlwave-rw-case " of\n\nendcase")
3765 "Selector expression"))
f32b3b91 3766
05a1abfc
CD
3767(defun idlwave-switch ()
3768 "Build skeleton IDL switch statement."
3769 (interactive)
4b1aaa8b 3770 (idlwave-template
05a1abfc
CD
3771 (idlwave-rw-case "switch")
3772 (idlwave-rw-case " of\n\nendswitch")
3773 "Selector expression"))
3774
f32b3b91 3775(defun idlwave-for ()
5a0c3f56 3776 "Build skeleton IDL loop statement."
f32b3b91 3777 (interactive)
4b1aaa8b 3778 (idlwave-template
595ab50b
CD
3779 (idlwave-rw-case "for")
3780 (idlwave-rw-case " do begin\n\nendfor")
3781 "Loop expression"))
f32b3b91
CD
3782
3783(defun idlwave-if ()
5a0c3f56 3784 "Build skeleton IDL if statement."
f32b3b91 3785 (interactive)
595ab50b
CD
3786 (idlwave-template
3787 (idlwave-rw-case "if")
3788 (idlwave-rw-case " then begin\n\nendif")
3789 "Scalar logical expression"))
f32b3b91
CD
3790
3791(defun idlwave-procedure ()
3792 (interactive)
4b1aaa8b 3793 (idlwave-template
595ab50b
CD
3794 (idlwave-rw-case "pro")
3795 (idlwave-rw-case "\n\nreturn\nend")
3796 "Procedure name"))
f32b3b91
CD
3797
3798(defun idlwave-function ()
3799 (interactive)
4b1aaa8b 3800 (idlwave-template
595ab50b
CD
3801 (idlwave-rw-case "function")
3802 (idlwave-rw-case "\n\nreturn\nend")
3803 "Function name"))
f32b3b91
CD
3804
3805(defun idlwave-repeat ()
3806 (interactive)
595ab50b
CD
3807 (idlwave-template
3808 (idlwave-rw-case "repeat begin\n\nendrep until")
3809 (idlwave-rw-case "")
3810 "Exit condition"))
f32b3b91
CD
3811
3812(defun idlwave-while ()
3813 (interactive)
4b1aaa8b 3814 (idlwave-template
595ab50b
CD
3815 (idlwave-rw-case "while")
3816 (idlwave-rw-case " do begin\n\nendwhile")
3817 "Entry condition"))
f32b3b91
CD
3818
3819(defun idlwave-split-string (string &optional pattern)
3820 "Return a list of substrings of STRING which are separated by PATTERN.
3821If PATTERN is omitted, it defaults to \"[ \\f\\t\\n\\r\\v]+\"."
3822 (or pattern
3823 (setq pattern "[ \f\t\n\r\v]+"))
3824 (let (parts (start 0))
3825 (while (string-match pattern string start)
3826 (setq parts (cons (substring string start (match-beginning 0)) parts)
3827 start (match-end 0)))
3828 (nreverse (cons (substring string start) parts))))
3829
3830(defun idlwave-replace-string (string replace_string replace_with)
3831 (let* ((start 0)
3832 (last (length string))
3833 (ret_string "")
3834 end)
3835 (while (setq end (string-match replace_string string start))
3836 (setq ret_string
3837 (concat ret_string (substring string start end) replace_with))
3838 (setq start (match-end 0)))
3839 (setq ret_string (concat ret_string (substring string start last)))))
3840
3841(defun idlwave-get-buffer-visiting (file)
3842 ;; Return the buffer currently visiting FILE
3843 (cond
3844 ((boundp 'find-file-compare-truenames) ; XEmacs
3845 (let ((find-file-compare-truenames t))
3846 (get-file-buffer file)))
3847 ((fboundp 'find-buffer-visiting) ; Emacs
3848 (find-buffer-visiting file))
3849 (t (error "This should not happen (idlwave-get-buffer-visiting)"))))
3850
15e42531 3851(defvar idlwave-outlawed-buffers nil
5a0c3f56 3852 "List of buffers pulled up by IDLWAVE for special reasons.
15e42531
CD
3853Buffers in this list may be killed by `idlwave-kill-autoloaded-buffers'.")
3854
3855(defun idlwave-find-file-noselect (file &optional why)
f32b3b91
CD
3856 ;; Return a buffer visiting file.
3857 (or (idlwave-get-buffer-visiting file)
15e42531
CD
3858 (let ((buf (find-file-noselect file)))
3859 (if why (add-to-list 'idlwave-outlawed-buffers (cons buf why)))
3860 buf)))
3861
3862(defun idlwave-kill-autoloaded-buffers ()
52a244eb 3863 "Kill buffers created automatically by IDLWAVE.
15e42531
CD
3864Function prompts for a letter to identify the buffers to kill.
3865Possible letters are:
3866
3867f Buffers created by the command \\[idlwave-find-module] or mouse
3868 clicks in the routine info window.
3869s Buffers created by the IDLWAVE Shell to display where execution
3870 stopped or an error was found.
3871a Both of the above.
3872
5a0c3f56 3873Buffers containing unsaved changes require confirmation before they are killed."
15e42531
CD
3874 (interactive)
3875 (if (null idlwave-outlawed-buffers)
3876 (error "No IDLWAVE-created buffers available")
3877 (princ (format "Kill IDLWAVE-created buffers: [f]ind source(%d), [s]hell display(%d), [a]ll ? "
3878 (idlwave-count-outlawed-buffers 'find)
3879 (idlwave-count-outlawed-buffers 'shell)))
3880 (let ((c (read-char)))
3881 (cond
3882 ((member c '(?f ?\C-f))
3883 (idlwave-do-kill-autoloaded-buffers 'find))
3884 ((member c '(?s ?\C-s))
3885 (idlwave-do-kill-autoloaded-buffers 'shell))
3886 ((member c '(?a ?\C-a))
3887 (idlwave-do-kill-autoloaded-buffers t))
3888 (t (error "Abort"))))))
3889
3890(defun idlwave-count-outlawed-buffers (tag)
3891 "How many outlawed buffers have tag TAG?"
3892 (length (delq nil
4b1aaa8b
PE
3893 (mapcar
3894 (lambda (x) (eq (cdr x) tag))
15e42531
CD
3895 idlwave-outlawed-buffers))))
3896
3897(defun idlwave-do-kill-autoloaded-buffers (&rest reasons)
3898 "Kill all buffers pulled up by IDLWAVE matching REASONS."
3899 (let* ((list (copy-sequence idlwave-outlawed-buffers))
3900 (cnt 0)
3901 entry)
3902 (while (setq entry (pop list))
3903 (if (buffer-live-p (car entry))
3904 (and (or (memq t reasons)
3905 (memq (cdr entry) reasons))
3906 (kill-buffer (car entry))
3907 (incf cnt)
4b1aaa8b 3908 (setq idlwave-outlawed-buffers
15e42531 3909 (delq entry idlwave-outlawed-buffers)))
4b1aaa8b 3910 (setq idlwave-outlawed-buffers
15e42531
CD
3911 (delq entry idlwave-outlawed-buffers))))
3912 (message "%d buffer%s killed" cnt (if (= cnt 1) "" "s"))))
3913
3914(defun idlwave-revoke-license-to-kill ()
3915 "Remove BUFFER from the buffers which may be killed.
3916Killing would be done by `idlwave-do-kill-autoloaded-buffers'.
3917Intended for `after-save-hook'."
3918 (let* ((buf (current-buffer))
3919 (entry (assq buf idlwave-outlawed-buffers)))
3920 ;; Revoke license
3921 (if entry
4b1aaa8b 3922 (setq idlwave-outlawed-buffers
15e42531
CD
3923 (delq entry idlwave-outlawed-buffers)))
3924 ;; Remove this function from the hook.
3925 (remove-hook 'after-save-hook 'idlwave-revoke-license-to-kill 'local)))
3926
3927(defvar idlwave-path-alist)
3928(defun idlwave-locate-lib-file (file)
f32b3b91 3929 ;; Find FILE on the scanned lib path and return a buffer visiting it
15e42531 3930 (let* ((dirs idlwave-path-alist)
f32b3b91
CD
3931 dir efile)
3932 (catch 'exit
15e42531 3933 (while (setq dir (car (pop dirs)))
f32b3b91
CD
3934 (if (file-regular-p
3935 (setq efile (expand-file-name file dir)))
15e42531 3936 (throw 'exit efile))))))
52a244eb 3937
15e42531
CD
3938(defun idlwave-expand-lib-file-name (file)
3939 ;; Find FILE on the scanned lib path and return a buffer visiting it
52a244eb 3940 ;; This is for, e.g., finding source with no user catalog
4b1aaa8b 3941 (cond
15e42531 3942 ((null file) nil)
15e42531
CD
3943 ((file-name-absolute-p file) file)
3944 (t (idlwave-locate-lib-file file))))
f32b3b91
CD
3945
3946(defun idlwave-make-tags ()
5a0c3f56
JB
3947 "Create the IDL tags file IDLTAGS in the current directory from
3948the list of directories specified in the minibuffer. Directories may be
3949for example: . /usr/local/rsi/idl/lib. All the subdirectories of the
f32b3b91 3950specified top directories are searched if the directory name is prefixed
5a0c3f56 3951by @. Specify @ directories with care, it may take a long, long time if
f32b3b91
CD
3952you specify /."
3953 (interactive)
3954 (let (directory directories cmd append status numdirs dir getsubdirs
3955 buffer save_buffer files numfiles item errbuf)
4b1aaa8b 3956
f32b3b91
CD
3957 ;;
3958 ;; Read list of directories
3959 (setq directory (read-string "Tag Directories: " "."))
3960 (setq directories (idlwave-split-string directory "[ \t]+"))
3961 ;;
3962 ;; Set etags command, vars
3963 (setq cmd "etags --output=IDLTAGS --language=none --regex='/[
3964\\t]*[pP][Rr][Oo][ \\t]+\\([^ \\t,]+\\)/' --regex='/[
3965\\t]*[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn][ \\t]+\\([^ \\t,]+\\)/' ")
3966 (setq append " ")
3967 (setq status 0)
3968 ;;
3969 ;; For each directory
3970 (setq numdirs 0)
3971 (setq dir (nth numdirs directories))
3972 (while (and dir)
3973 ;;
3974 ;; Find the subdirectories
3975 (if (string-match "^[@]\\(.+\\)$" dir)
3976 (setq getsubdirs t) (setq getsubdirs nil))
3977 (if (and getsubdirs) (setq dir (substring dir 1 (length dir))))
3978 (setq dir (expand-file-name dir))
3979 (if (file-directory-p dir)
3980 (progn
3981 (if (and getsubdirs)
3982 (progn
3983 (setq buffer (get-buffer-create "*idltags*"))
3984 (call-process "sh" nil buffer nil "-c"
3985 (concat "find " dir " -type d -print"))
3986 (setq save_buffer (current-buffer))
3987 (set-buffer buffer)
3988 (setq files (idlwave-split-string
3989 (idlwave-replace-string
3990 (buffer-substring 1 (point-max))
3991 "\n" "/*.pro ")
3992 "[ \t]+"))
3993 (set-buffer save_buffer)
3994 (kill-buffer buffer))
3995 (setq files (list (concat dir "/*.pro"))))
3996 ;;
3997 ;; For each subdirectory
3998 (setq numfiles 0)
3999 (setq item (nth numfiles files))
4000 (while (and item)
4001 ;;
4002 ;; Call etags
4003 (if (not (string-match "^[ \\t]*$" item))
4004 (progn
29a4e67d 4005 (message "%s" (concat "Tagging " item "..."))
f32b3b91 4006 (setq errbuf (get-buffer-create "*idltags-error*"))
52a244eb 4007 (setq status (+ status
4b1aaa8b 4008 (if (eq 0 (call-process
52a244eb
S
4009 "sh" nil errbuf nil "-c"
4010 (concat cmd append item)))
4011 0
4012 1)))
f32b3b91
CD
4013 ;;
4014 ;; Append additional tags
4015 (setq append " --append ")
4016 (setq numfiles (1+ numfiles))
4017 (setq item (nth numfiles files)))
4018 (progn
4019 (setq numfiles (1+ numfiles))
4020 (setq item (nth numfiles files))
4021 )))
4b1aaa8b 4022
f32b3b91
CD
4023 (setq numdirs (1+ numdirs))
4024 (setq dir (nth numdirs directories)))
4025 (progn
4026 (setq numdirs (1+ numdirs))
4027 (setq dir (nth numdirs directories)))))
4b1aaa8b 4028
f32b3b91
CD
4029 (setq errbuf (get-buffer-create "*idltags-error*"))
4030 (if (= status 0)
4031 (kill-buffer errbuf))
4032 (message "")
4033 ))
4034
4035(defun idlwave-toggle-comment-region (beg end &optional n)
4036 "Comment the lines in the region if the first non-blank line is
5a0c3f56 4037commented, and conversely, uncomment region. If optional prefix arg
f32b3b91
CD
4038N is non-nil, then for N positive, add N comment delimiters or for N
4039negative, remove N comment delimiters.
4040Uses `comment-region' which does not place comment delimiters on
4041blank lines."
4042 (interactive "r\nP")
4043 (if n
4044 (comment-region beg end (prefix-numeric-value n))
4045 (save-excursion
4046 (goto-char beg)
4047 (beginning-of-line)
4048 ;; skip blank lines
4049 (skip-chars-forward " \t\n")
4050 (if (looking-at (concat "[ \t]*\\(" comment-start "+\\)"))
52a244eb
S
4051 (if (fboundp 'uncomment-region)
4052 (uncomment-region beg end)
4053 (comment-region beg end
4054 (- (length (buffer-substring
4055 (match-beginning 1)
4056 (match-end 1))))))
4057 (comment-region beg end)))))
f32b3b91
CD
4058
4059
4060;; ----------------------------------------------------------------------------
4061;; ----------------------------------------------------------------------------
4062;; ----------------------------------------------------------------------------
4063;; ----------------------------------------------------------------------------
4064;;
4065;; Completion and Routine Info
4066;;
4067
4068;; String "intern" functions
4069
4070;; For the completion and routine info function, we want to normalize
4071;; the case of procedure names etc. We do this by "interning" these
4072;; string is a hand-crafted way. Hashes are used to map the downcase
52a244eb
S
4073;; version of the strings to the cased versions. Most *-sint-*
4074;; variables consist of *two* hashes, a buffer+shell, followed by a
4075;; system hash. The former is re-scanned, and the latter takes case
4076;; precedence.
4077;;
4078;; Since these cased versions are really lisp objects, we can use `eq'
4079;; to search, which is a large performance boost. All new strings
4080;; need to be "sinterned". We do this as early as possible after
4081;; getting these strings from completion or buffer substrings. So
4082;; most of the code can simply assume to deal with "sinterned"
4083;; strings. The only exception is that the functions which scan whole
4084;; buffers for routine information do not intern the grabbed strings.
4085;; This is only done afterwards. Therefore in these functions it is
4086;; *not* safe to assume the strings can be compared with `eq' and be
4087;; fed into the routine assq functions.
f32b3b91
CD
4088
4089;; Here we define the hashing functions.
4090
4091;; The variables which hold the hashes.
4092(defvar idlwave-sint-routines '(nil))
4093(defvar idlwave-sint-keywords '(nil))
4094(defvar idlwave-sint-methods '(nil))
4095(defvar idlwave-sint-classes '(nil))
52a244eb
S
4096(defvar idlwave-sint-dirs '(nil))
4097(defvar idlwave-sint-libnames '(nil))
f32b3b91
CD
4098
4099(defun idlwave-reset-sintern (&optional what)
4100 "Reset all sintern hashes."
4101 ;; Make sure the hash functions are accessible.
8d222148
SM
4102 (unless (and (fboundp 'gethash)
4103 (fboundp 'puthash))
4104 (require 'cl)
4105 (or (fboundp 'puthash)
4106 (defalias 'puthash 'cl-puthash)))
f32b3b91
CD
4107 (let ((entries '((idlwave-sint-routines 1000 10)
4108 (idlwave-sint-keywords 1000 10)
4109 (idlwave-sint-methods 100 10)
4110 (idlwave-sint-classes 10 10))))
4111
4112 ;; Make sure these are lists
4113 (loop for entry in entries
4114 for var = (car entry)
4115 do (if (not (consp (symbol-value var))) (set var (list nil))))
4116
f66f03de 4117 ;; Reset the system & library hash
f32b3b91
CD
4118 (when (or (eq what t) (eq what 'syslib)
4119 (null (cdr idlwave-sint-routines)))
f32b3b91
CD
4120 (loop for entry in entries
4121 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4122 do (setcdr (symbol-value var)
f32b3b91 4123 (make-hash-table ':size size ':test 'equal)))
52a244eb
S
4124 (setq idlwave-sint-dirs nil
4125 idlwave-sint-libnames nil))
f32b3b91 4126
f66f03de 4127 ;; Reset the buffer & shell hash
f32b3b91
CD
4128 (when (or (eq what t) (eq what 'bufsh)
4129 (null (car idlwave-sint-routines)))
f32b3b91
CD
4130 (loop for entry in entries
4131 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4132 do (setcar (symbol-value var)
f32b3b91
CD
4133 (make-hash-table ':size size ':test 'equal))))))
4134
4135(defun idlwave-sintern-routine-or-method (name &optional class set)
4136 (if class
4137 (idlwave-sintern-method name set)
4138 (idlwave-sintern-routine name set)))
4139
4140(defun idlwave-sintern (stype &rest args)
4141 (apply (intern (concat "idlwave-sintern-" (symbol-name stype))) args))
4142
4143;;(defmacro idlwave-sintern (type var)
4144;; `(cond ((not (stringp name)) name)
4145;; ((gethash (downcase name) (cdr ,var)))
4146;; ((gethash (downcase name) (car ,var)))
4147;; (set (idlwave-sintern-set name ,type ,var set))
4148;; (name)))
4149
4150(defun idlwave-sintern-routine (name &optional set)
4151 (cond ((not (stringp name)) name)
4152 ((gethash (downcase name) (cdr idlwave-sint-routines)))
4153 ((gethash (downcase name) (car idlwave-sint-routines)))
4154 (set (idlwave-sintern-set name 'routine idlwave-sint-routines set))
4155 (name)))
4156(defun idlwave-sintern-keyword (name &optional set)
4157 (cond ((not (stringp name)) name)
4158 ((gethash (downcase name) (cdr idlwave-sint-keywords)))
4159 ((gethash (downcase name) (car idlwave-sint-keywords)))
4160 (set (idlwave-sintern-set name 'keyword idlwave-sint-keywords set))
4161 (name)))
4162(defun idlwave-sintern-method (name &optional set)
4163 (cond ((not (stringp name)) name)
4164 ((gethash (downcase name) (cdr idlwave-sint-methods)))
4165 ((gethash (downcase name) (car idlwave-sint-methods)))
4166 (set (idlwave-sintern-set name 'method idlwave-sint-methods set))
4167 (name)))
4168(defun idlwave-sintern-class (name &optional set)
4169 (cond ((not (stringp name)) name)
4170 ((gethash (downcase name) (cdr idlwave-sint-classes)))
4171 ((gethash (downcase name) (car idlwave-sint-classes)))
4172 (set (idlwave-sintern-set name 'class idlwave-sint-classes set))
4173 (name)))
4174
52a244eb
S
4175(defun idlwave-sintern-dir (dir &optional set)
4176 (car (or (member dir idlwave-sint-dirs)
4177 (setq idlwave-sint-dirs (cons dir idlwave-sint-dirs)))))
4178(defun idlwave-sintern-libname (name &optional set)
4179 (car (or (member name idlwave-sint-libnames)
4180 (setq idlwave-sint-libnames (cons name idlwave-sint-libnames)))))
f32b3b91
CD
4181
4182(defun idlwave-sintern-set (name type tables set)
4183 (let* ((func (or (cdr (assq type idlwave-completion-case))
4184 'identity))
4185 (iname (funcall (if (eq func 'preserve) 'identity func) name))
4186 (table (if (eq set 'sys) (cdr tables) (car tables))))
4187 (puthash (downcase name) iname table)
4188 iname))
4189
52a244eb
S
4190(defun idlwave-sintern-keyword-list (kwd-list &optional set)
4191 "Sintern a set of keywords (file (key . link) (key2 . link2) ...)"
8ffcfb27
GM
4192 (mapc (lambda(x)
4193 (setcar x (idlwave-sintern-keyword (car x) set)))
4194 (cdr kwd-list))
52a244eb
S
4195 kwd-list)
4196
4197(defun idlwave-sintern-rinfo-list (list &optional set default-dir)
5a0c3f56
JB
4198 "Sintern all strings in the rinfo LIST.
4199With optional parameter SET: also set new patterns. Probably this
4200will always have to be t. If DEFAULT-DIR is passed, it is used as
4201the base of the directory."
52a244eb 4202 (let (entry name type class kwds res source call new)
f32b3b91
CD
4203 (while list
4204 (setq entry (car list)
4205 list (cdr list)
4206 name (car entry)
4207 type (nth 1 entry)
4208 class (nth 2 entry)
4209 source (nth 3 entry)
4210 call (nth 4 entry)
52a244eb
S
4211 kwds (nthcdr 5 entry))
4212
4213 ;; The class and name
f32b3b91
CD
4214 (if class
4215 (progn
4216 (if (symbolp class) (setq class (symbol-name class)))
4217 (setq class (idlwave-sintern-class class set))
4218 (setq name (idlwave-sintern-method name set)))
4219 (setq name (idlwave-sintern-routine name set)))
4b1aaa8b 4220
52a244eb
S
4221 ;; The source
4222 (let ((source-type (car source))
4223 (source-file (nth 1 source))
4b1aaa8b 4224 (source-dir (if default-dir
52a244eb
S
4225 (file-name-as-directory default-dir)
4226 (nth 2 source)))
4227 (source-lib (nth 3 source)))
4228 (if (stringp source-dir)
4229 (setq source-dir (idlwave-sintern-dir source-dir set)))
4230 (if (stringp source-lib)
4231 (setq source-lib (idlwave-sintern-libname source-lib set)))
4232 (setq source (list source-type source-file source-dir source-lib)))
4b1aaa8b 4233
52a244eb
S
4234 ;; The keywords
4235 (setq kwds (mapcar (lambda (x)
4236 (idlwave-sintern-keyword-list x set))
4237 kwds))
4238
4239 ;; Build a canonicalized list
4240 (setq new (nconc (list name type class source call) kwds)
4241 res (cons new res)))
f32b3b91
CD
4242 (nreverse res)))
4243
05a1abfc
CD
4244;; Creating new sintern tables
4245
4246(defun idlwave-new-sintern-type (tag)
4247 "Define a variable and a function to sintern the new type TAG.
4248This defines the function `idlwave-sintern-TAG' and the variable
4249`idlwave-sint-TAGs'."
4250 (let* ((name (symbol-name tag))
4251 (names (concat name "s"))
4252 (var (intern (concat "idlwave-sint-" names)))
4253 (func (intern (concat "idlwave-sintern-" name))))
4254 (set var nil) ; initial value of the association list
4255 (fset func ; set the function
4256 `(lambda (name &optional set)
4257 (cond ((not (stringp name)) name)
4258 ((cdr (assoc (downcase name) ,var)))
4259 (set
4260 (setq ,var (cons (cons (downcase name) name) ,var))
4261 name)
4262 (name))))))
4263
4264(defun idlwave-reset-sintern-type (tag)
4265 "Reset the sintern variable associated with TAG."
4266 (set (intern (concat "idlwave-sint-" (symbol-name tag) "s")) nil))
4267
f32b3b91
CD
4268;;---------------------------------------------------------------------------
4269
4270
4271;; The variables which hold the information
15e42531 4272(defvar idlwave-system-routines nil
f32b3b91
CD
4273 "Holds the routine-info obtained by scanning buffers.")
4274(defvar idlwave-buffer-routines nil
4275 "Holds the routine-info obtained by scanning buffers.")
4276(defvar idlwave-compiled-routines nil
15e42531
CD
4277 "Holds the routine-info obtained by asking the shell.")
4278(defvar idlwave-unresolved-routines nil
4279 "Holds the unresolved routine-info obtained by asking the shell.")
52a244eb
S
4280(defvar idlwave-user-catalog-routines nil
4281 "Holds the procedure routine-info from the user scan.")
4282(defvar idlwave-library-catalog-routines nil
3938cb82
S
4283 "Holds the procedure routine-info from the .idlwave_catalog library files.")
4284(defvar idlwave-library-catalog-libname nil
4285 "Name of library catalog loaded from .idlwave_catalog files.")
15e42531 4286(defvar idlwave-path-alist nil
52a244eb
S
4287 "Alist with !PATH directories and zero or more flags if the dir has
4288been scanned in a user catalog ('user) or discovered in a library
4289catalog \('lib).")
15e42531
CD
4290(defvar idlwave-true-path-alist nil
4291 "Like `idlwave-path-alist', but with true filenames.")
f32b3b91 4292(defvar idlwave-routines nil
b9e4fbd3 4293 "Holds the combined procedure/function/method routine-info.")
f32b3b91
CD
4294(defvar idlwave-class-alist nil
4295 "Holds the class names known to IDLWAVE.")
4296(defvar idlwave-class-history nil
4297 "The history of classes selected with the minibuffer.")
4298(defvar idlwave-force-class-query nil)
4299(defvar idlwave-before-completion-wconf nil
4300 "The window configuration just before the completion buffer was displayed.")
15e42531
CD
4301(defvar idlwave-last-system-routine-info-cons-cell nil
4302 "The last cons cell in the system routine info.")
f32b3b91
CD
4303
4304;;
4305;; The code to get routine info from different sources.
4306
15e42531 4307(defvar idlwave-system-routines)
5e72c6b2
S
4308(defvar idlwave-catalog-process nil
4309 "The background process currently updating the catalog.")
4310
f32b3b91
CD
4311(defun idlwave-routines ()
4312 "Provide a list of IDL routines.
5a0c3f56
JB
4313This routine loads the builtin routines on the first call.
4314Later it only returns the value of the variable."
5e72c6b2
S
4315 (if (and idlwave-catalog-process
4316 (processp idlwave-catalog-process))
4317 (progn
4318 (cond
4319 ((equal (process-status idlwave-catalog-process) 'exit)
4320 (message "updating........")
4321 (setq idlwave-catalog-process nil)
4322 (idlwave-update-routine-info '(4)))
4323 ((equal (process-status idlwave-catalog-process) 'run)
4324 ;; Keep it running...
4325 )
4326 (t
4327 ;; Something is wrong, get rid of the process
4328 (message "Problem with catalog process") (beep)
4329 (condition-case nil
4330 (kill-process idlwave-catalog-process)
4331 (error nil))
4332 (setq idlwave-catalog-process nil)))))
f32b3b91
CD
4333 (or idlwave-routines
4334 (progn
4335 (idlwave-update-routine-info)
4336 ;; return the current value
4337 idlwave-routines)))
4338
05a1abfc
CD
4339(defvar idlwave-update-rinfo-hook nil
4340 "List of functions which should run after a global rinfo update.
4341Does not run after automatic updates of buffer or the shell.")
4342
5e72c6b2 4343(defun idlwave-rescan-catalog-directories ()
5a0c3f56 4344 "Rescan the previously selected directories. For batch processing."
5e72c6b2
S
4345 (idlwave-update-routine-info '(16)))
4346
4347(defun idlwave-rescan-asynchronously ()
8a6a28ac 4348 "Dispatch another Emacs instance to update the idlwave catalog.
5e72c6b2
S
4349After the process finishes normally, the first access to routine info
4350will re-read the catalog."
4351 (interactive)
4352 (if (processp idlwave-catalog-process)
4353 (if (eq (process-status idlwave-catalog-process) 'run)
4354 (if (yes-or-no-p "A catalog-updating process is running. Kill it? ")
4355 (progn
4356 (condition-case nil
4357 (kill-process idlwave-catalog-process)
4358 (error nil))
4359 (error "Process killed, no new process started"))
4360 (error "Quit"))
4361 (condition-case nil
4362 (kill-process idlwave-catalog-process)
4363 (error nil))))
52a244eb
S
4364 (if (or (not idlwave-user-catalog-file)
4365 (not (stringp idlwave-user-catalog-file))
4366 (not (file-regular-p idlwave-user-catalog-file)))
5e72c6b2 4367 (error "No catalog has been produced yet"))
4b1aaa8b 4368 (let* ((emacs (concat invocation-directory invocation-name))
5e72c6b2
S
4369 (args (list "-batch"
4370 "-l" (expand-file-name "~/.emacs")
4371 "-l" "idlwave"
4372 "-f" "idlwave-rescan-catalog-directories"))
4b1aaa8b 4373 (process (apply 'start-process "idlcat"
5e72c6b2
S
4374 nil emacs args)))
4375 (setq idlwave-catalog-process process)
4b1aaa8b 4376 (set-process-sentinel
5e72c6b2
S
4377 process
4378 (lambda (pro why)
4379 (when (string-match "finished" why)
4380 (setq idlwave-routines nil
4381 idlwave-system-routines nil
4382 idlwave-catalog-process nil)
4383 (or (idlwave-start-load-rinfo-timer)
4384 (idlwave-update-routine-info '(4))))))
4385 (message "Background job started to update catalog file")))
4386
4387
52a244eb
S
4388;; Format for all routine info user catalog, library catalogs, etc.:
4389;;
4390;; ("ROUTINE" type class
4391;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4392;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4393;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de 4394;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
52a244eb
S
4395;;
4396;; DIR will be supplied dynamically while loading library catalogs,
4397;; and is sinterned to save space, as is LIBNAME. PRO_FILE can be a
4398;; complete filepath, in which case DIR is unnecessary. HELPFILE can
4399;; be nil, as can LINK1, etc., if no HTML help is available.
4400
4401
5e72c6b2 4402(defvar idlwave-load-rinfo-idle-timer)
3938cb82
S
4403(defvar idlwave-shell-path-query)
4404
52a244eb 4405(defun idlwave-update-routine-info (&optional arg no-concatenate)
f32b3b91
CD
4406 "Update the internal routine-info lists.
4407These lists are used by `idlwave-routine-info' (\\[idlwave-routine-info])
4408and by `idlwave-complete' (\\[idlwave-complete]) to provide information
4409about individual routines.
4410
4411The information can come from 4 sources:
44121. IDL programs in the current editing session
44132. Compiled modules in an IDL shell running as Emacs subprocess
44143. A list which covers the IDL system routines.
44154. A list which covers the prescanned library files.
4416
4417Scans all IDLWAVE-mode buffers of the current editing session (see
4418`idlwave-scan-all-buffers-for-routine-info').
4419When an IDL shell is running, this command also queries the IDL program
4420for currently compiled routines.
4421
4422With prefix ARG, also reload the system and library lists.
52a244eb
S
4423With two prefix ARG's, also rescans the chosen user catalog tree.
4424With three prefix args, dispatch asynchronous process to do the update.
4425
4426If NO-CONCATENATE is non-nil, don't pre-concatenate the routine info
4427lists, but instead wait for the shell query to complete and
4428asynchronously finish updating routine info. This is set
4429automatically when called interactively. When you need routine
4430information updated immediately, leave NO-CONCATENATE nil."
751adbde 4431 (interactive "P\np")
5e72c6b2
S
4432 ;; Stop any idle processing
4433 (if (or (and (fboundp 'itimerp)
4434 (itimerp idlwave-load-rinfo-idle-timer))
4435 (and (fboundp 'timerp)
4436 (timerp idlwave-load-rinfo-idle-timer)))
4437 (cancel-timer idlwave-load-rinfo-idle-timer))
4438 (cond
4439 ((equal arg '(64))
4440 ;; Start a background process which updates the catalog.
4441 (idlwave-rescan-asynchronously))
4442 ((equal arg '(16))
52a244eb
S
4443 ;; Update the user catalog now, and wait for them.
4444 (idlwave-create-user-catalog-file t))
5e72c6b2
S
4445 (t
4446 (let* ((load (or arg
4447 idlwave-buffer-case-takes-precedence
4448 (null idlwave-routines)))
4449 ;; The override-idle means, even if the idle timer has done some
4450 ;; preparing work, load and renormalize everything anyway.
4451 (override-idle (or arg idlwave-buffer-case-takes-precedence)))
4b1aaa8b 4452
f32b3b91 4453 (setq idlwave-buffer-routines nil
15e42531
CD
4454 idlwave-compiled-routines nil
4455 idlwave-unresolved-routines nil)
f32b3b91 4456 ;; Reset the appropriate hashes
5e72c6b2
S
4457 (if (get 'idlwave-reset-sintern 'done-by-idle)
4458 ;; reset was already done in idle time, so skip this step now once
4459 (put 'idlwave-reset-sintern 'done-by-idle nil)
4460 (idlwave-reset-sintern (cond (load t)
4461 ((null idlwave-system-routines) t)
4462 (t 'bufsh))))
4b1aaa8b 4463
f32b3b91
CD
4464 (if idlwave-buffer-case-takes-precedence
4465 ;; We can safely scan the buffer stuff first
4466 (progn
4467 (idlwave-update-buffer-routine-info)
f66f03de 4468 (and load (idlwave-load-all-rinfo override-idle)))
f32b3b91 4469 ;; We first do the system info, and then the buffers
f66f03de 4470 (and load (idlwave-load-all-rinfo override-idle))
f32b3b91
CD
4471 (idlwave-update-buffer-routine-info))
4472
4473 ;; Let's see if there is a shell
4474 (let* ((shell-is-running (and (fboundp 'idlwave-shell-is-running)
4475 (idlwave-shell-is-running)))
4476 (ask-shell (and shell-is-running
4477 idlwave-query-shell-for-routine-info)))
4b1aaa8b 4478
52a244eb 4479 ;; Load the library catalogs again, first re-scanning the path
4b1aaa8b 4480 (when arg
52a244eb
S
4481 (if shell-is-running
4482 (idlwave-shell-send-command idlwave-shell-path-query
4483 '(progn
4484 (idlwave-shell-get-path-info)
4485 (idlwave-scan-library-catalogs))
4486 'hide)
4487 (idlwave-scan-library-catalogs)))
775591f7 4488
f32b3b91 4489 (if (or (not ask-shell)
52a244eb 4490 (not no-concatenate))
f32b3b91
CD
4491 ;; 1. If we are not going to ask the shell, we need to do the
4492 ;; concatenation now.
52a244eb
S
4493 ;; 2. When this function is called non-interactively, it
4494 ;; means that someone needs routine info *now*. The
4495 ;; shell update causes the concatenation to be
4496 ;; *delayed*, so not in time for the current command.
4497 ;; Therefore, we do a concatenation now, even though
4498 ;; the shell might do it again.
4499 (idlwave-concatenate-rinfo-lists nil 'run-hooks))
4b1aaa8b 4500
f32b3b91 4501 (when ask-shell
52a244eb 4502 ;; Ask the shell about the routines it knows of.
f32b3b91 4503 (message "Querying the shell")
5e72c6b2
S
4504 (idlwave-shell-update-routine-info nil t)))))))
4505
52a244eb
S
4506
4507(defvar idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4508(defvar idlwave-load-rinfo-idle-timer nil)
4509(defun idlwave-start-load-rinfo-timer ()
4510 (if (or (and (fboundp 'itimerp)
4511 (itimerp idlwave-load-rinfo-idle-timer))
4512 (and (fboundp 'timerp)
4513 (timerp idlwave-load-rinfo-idle-timer)))
4514 (cancel-timer idlwave-load-rinfo-idle-timer))
52a244eb 4515 (setq idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4516 (setq idlwave-load-rinfo-idle-timer nil)
4517 (if (and idlwave-init-rinfo-when-idle-after
4518 (numberp idlwave-init-rinfo-when-idle-after)
4519 (not (equal 0 idlwave-init-rinfo-when-idle-after))
4520 (not idlwave-routines))
4521 (condition-case nil
4522 (progn
4523 (setq idlwave-load-rinfo-idle-timer
4524 (run-with-idle-timer
4525 idlwave-init-rinfo-when-idle-after
4526 nil 'idlwave-load-rinfo-next-step)))
4527 (error nil))))
4528
3938cb82
S
4529(defvar idlwave-library-routines nil "Obsolete variable.")
4530
f66f03de
S
4531;;------ XML Help routine info system
4532(defun idlwave-load-system-routine-info ()
4533 ;; Load the system routine info from the cached routine info file,
4534 ;; which, if necessary, will be re-created from the XML file on
4535 ;; disk. As a last fallback, load the (likely outdated) idlw-rinfo
4536 ;; file distributed with older IDLWAVE versions (<6.0)
4b1aaa8b 4537 (unless (and (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4538 'noerror 'nomessage)
4539 (idlwave-xml-system-routine-info-up-to-date))
4540 ;; See if we can create it from XML source
4541 (condition-case nil
4542 (idlwave-convert-xml-system-routine-info)
4b1aaa8b
PE
4543 (error
4544 (unless (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4545 'noerror 'nomessage)
4546 (if idlwave-system-routines
4b1aaa8b 4547 (message
f66f03de 4548 "Failed to load converted routine info, using old conversion.")
4b1aaa8b 4549 (message
f66f03de
S
4550 "Failed to convert XML routine info, falling back on idlw-rinfo.")
4551 (if (not (load "idlw-rinfo" 'noerror 'nomessage))
4b1aaa8b 4552 (message
f66f03de
S
4553 "Could not locate any system routine information."))))))))
4554
4555(defun idlwave-xml-system-routine-info-up-to-date()
4b1aaa8b 4556 (let* ((dir (file-name-as-directory
f66f03de
S
4557 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4558 (catalog-file (expand-file-name "idl_catalog.xml" dir)))
4559 (file-newer-than-file-p ;converted file is newer than catalog
4560 idlwave-xml-system-rinfo-converted-file
4561 catalog-file)))
4562
4563(defvar idlwave-system-class-info nil) ; Gathered from idlw-rinfo
4564(defvar idlwave-system-variables-alist nil
4565 "Alist of system variables and the associated structure tags.
4566Gets set in cached XML rinfo, or `idlw-rinfo.el'.")
4567(defvar idlwave-executive-commands-alist nil
4568 "Alist of system variables and their help files.")
4569(defvar idlwave-help-special-topic-words nil)
4570
4b1aaa8b 4571
f66f03de 4572(defun idlwave-shorten-syntax (syntax name &optional class)
5a89f0a7 4573 ;; From a list of syntax statements, shorten with %s and group with "or"
f66f03de 4574 (let ((case-fold-search t))
4b1aaa8b 4575 (mapconcat
f66f03de
S
4576 (lambda (x)
4577 (while (string-match name x)
4578 (setq x (replace-match "%s" t t x)))
4b1aaa8b 4579 (if class
f66f03de
S
4580 (while (string-match class x)
4581 (setq x (replace-match "%s" t t x))))
4582 x)
4583 (nreverse syntax)
4584 " or ")))
4585
4586(defun idlwave-xml-create-class-method-lists (xml-entry)
4587 ;; Create a class list entry from the xml parsed list., returning a
4588 ;; cons of form (class-entry method-entries).
4589 (let* ((nameblock (nth 1 xml-entry))
4590 (class (cdr (assq 'name nameblock)))
4591 (link (cdr (assq 'link nameblock)))
4592 (params (cddr xml-entry))
4593 (case-fold-search t)
4594 class-entry
4595 method methods-entry extra-kwds
4596 props get-props set-props init-props inherits
4597 pelem ptype)
4598 (while params
4599 (setq pelem (car params))
4600 (when (listp pelem)
4601 (setq ptype (car pelem)
4602 props (car (cdr pelem)))
4603 (cond
4604 ((eq ptype 'SUPERCLASS)
58c8f915
S
4605 (let ((pname (cdr (assq 'name props)))
4606 (plink (cdr (assq 'link props))))
4607 (unless (and (string= pname "None")
4608 (string= plink "None"))
4609 (push pname inherits))))
f66f03de
S
4610
4611 ((eq ptype 'PROPERTY)
4612 (let ((pname (cdr (assq 'name props)))
4613 (plink (cdr (assq 'link props)))
4614 (get (string= (cdr (assq 'get props)) "Yes"))
4615 (set (string= (cdr (assq 'set props)) "Yes"))
4616 (init (string= (cdr (assq 'init props)) "Yes")))
4617 (if get (push (list pname plink) get-props))
4618 (if set (push (list pname plink) set-props))
4619 (if init (push (list pname plink) init-props))))
4620
4621 ((eq ptype 'METHOD)
4622 (setq method (cdr (assq 'name props)))
4623 (setq extra-kwds ;;Assume all property keywords are gathered already
4624 (cond
4625 ((string-match (concat class "::Init") method)
4626 (put 'init-props 'matched t)
4627 init-props)
4628 ((string-match (concat class "::GetProperty") method)
4629 (put 'get-props 'matched t)
4630 get-props)
4631 ((string-match (concat class "::SetProperty") method)
4632 (put 'set-props 'matched t)
4633 set-props)
4634 (t nil)))
4b1aaa8b
PE
4635 (setq methods-entry
4636 (nconc (idlwave-xml-create-rinfo-list pelem class extra-kwds)
f66f03de
S
4637 methods-entry)))
4638 (t)))
4639 (setq params (cdr params)))
8d222148
SM
4640 ;;(unless (get 'init-props 'matched)
4641 ;; (message "Failed to match Init in class %s" class))
4642 ;;(unless (get 'get-props 'matched)
4643 ;; (message "Failed to match GetProperty in class %s" class))
4644 ;;(unless (get 'set-props 'matched)
4645 ;; (message "Failed to match SetProperty in class %s" class))
4b1aaa8b
PE
4646 (setq class-entry
4647 (if inherits
f66f03de
S
4648 (list class (append '(inherits) inherits) (list 'link link))
4649 (list class (list 'link link))))
4650 (cons class-entry methods-entry)))
4b1aaa8b 4651
f66f03de
S
4652(defun idlwave-xml-create-rinfo-list (xml-entry &optional class extra-kws)
4653 ;; Create correctly structured list elements from ROUTINE or METHOD
4654 ;; XML list structures. Return a list of list elements, with more
4655 ;; than one sub-list possible if a routine can serve as both
4656 ;; procedure and function (e.g. call_method).
4657 (let* ((nameblock (nth 1 xml-entry))
4658 (name (cdr (assq 'name nameblock)))
4659 (link (cdr (assq 'link nameblock)))
4660 (params (cddr xml-entry))
4661 (syntax-vec (make-vector 3 nil)) ; procedure, function, exec command
4662 (case-fold-search t)
8d222148 4663 syntax kwd klink pref-list kwds pelem ptype props result type)
f66f03de
S
4664 (if class ;; strip out class name from class method name string
4665 (if (string-match (concat class "::") name)
4666 (setq name (substring name (match-end 0)))))
4667 (while params
4668 (setq pelem (car params))
4669 (when (listp pelem)
4670 (setq ptype (car pelem)
4671 props (car (cdr pelem)))
4672 (cond
4673 ((eq ptype 'SYNTAX)
4674 (setq syntax (cdr (assq 'name props)))
4675 (if (string-match "-&gt;" syntax)
4676 (setq syntax (replace-match "->" t nil syntax)))
4677 (setq type (cdr (assq 'type props)))
4678 (push syntax
4679 (aref syntax-vec (cond
4680 ((string-match "^pro" type) 0)
4681 ((string-match "^fun" type) 1)
4682 ((string-match "^exec" type) 2)))))
4683 ((eq ptype 'KEYWORD)
4684 (setq kwd (cdr (assq 'name props))
4685 klink (cdr (assq 'link props)))
4686 (if (string-match "^\\[XY\\(Z?\\)\\]" kwd)
4b1aaa8b
PE
4687 (progn
4688 (setq pref-list
f66f03de
S
4689 (if (match-string 1 kwd) '("X" "Y" "Z") '("X" "Y"))
4690 kwd (substring kwd (match-end 0)))
4691 (loop for x in pref-list do
4692 (push (list (concat x kwd) klink) kwds)))
4693 (push (list kwd klink) kwds)))
4694
4695 (t))); Do nothing for the others
4696 (setq params (cdr params)))
4b1aaa8b 4697
f66f03de 4698 ;; Debug
8d222148
SM
4699 ;; (if (and (null (aref syntax-vec 0))
4700 ;; (null (aref syntax-vec 1))
4701 ;; (null (aref syntax-vec 2)))
4702 ;; (with-current-buffer (get-buffer-create "IDL_XML_catalog_complaints")
4703 ;; (if class
4704 ;; (insert (format "Missing SYNTAX entry for %s::%s\n" class name))
4705 ;; (insert (message "Missing SYNTAX entry for %s\n" name)))))
f66f03de
S
4706
4707 ;; Executive commands are treated specially
4708 (if (aref syntax-vec 2)
4709 (cons (substring name 1) link)
4710 (if extra-kws (setq kwds (nconc kwds extra-kws)))
4711 (setq kwds (idlwave-rinfo-group-keywords kwds link))
4712 (loop for idx from 0 to 1 do
4713 (if (aref syntax-vec idx)
4b1aaa8b 4714 (push (append (list name (if (eq idx 0) 'pro 'fun)
f66f03de 4715 class '(system)
4b1aaa8b 4716 (idlwave-shorten-syntax
f66f03de
S
4717 (aref syntax-vec idx) name class))
4718 kwds) result)))
4719 result)))
4720
4721
4722(defun idlwave-rinfo-group-keywords (kwds master-link)
4b1aaa8b 4723 ;; Group keywords by link file, as a list with elements
f66f03de
S
4724 ;; (linkfile ( ("KWD1" . link1) ("KWD2" . link2))
4725 (let (kwd link anchor linkfiles block master-elt)
4726 (while kwds
4727 (setq kwd (car kwds)
4728 link (idlwave-split-link-target (nth 1 kwd))
4729 anchor (cdr link)
4730 link (car link)
4731 kwd (car kwd))
4732 (if (setq block (assoc link linkfiles))
4733 (push (cons kwd anchor) (cdr block))
4734 (push (list link (cons kwd anchor)) linkfiles))
4735 (setq kwds (cdr kwds)))
4736 ;; Ensure the master link is there
4737 (if (setq master-elt (assoc master-link linkfiles))
4738 (if (eq (car linkfiles) master-elt)
4739 linkfiles
4740 (cons master-elt (delq master-elt linkfiles)))
4741 (push (list master-link) linkfiles))))
4b1aaa8b 4742
f66f03de
S
4743(defun idlwave-convert-xml-clean-statement-aliases (aliases)
4744 ;; Clean up the syntax of routines which are actually aliases by
4745 ;; removing the "OR" from the statements
4746 (let (syntax entry)
4747 (loop for x in aliases do
4748 (setq entry (assoc x idlwave-system-routines))
4749 (when entry
4750 (while (string-match " +or +" (setq syntax (nth 4 entry)))
4751 (setf (nth 4 entry) (replace-match ", " t t syntax)))))))
4752
4753(defun idlwave-convert-xml-clean-routine-aliases (aliases)
4754 ;; Duplicate and trim original routine aliases from rinfo list
4b1aaa8b 4755 ;; This if for, e.g. OPENR/OPENW/OPENU
f66f03de
S
4756 (let (alias remove-list new parts all-parts)
4757 (loop for x in aliases do
4758 (when (setq parts (split-string (cdr x) "/"))
4759 (setq new (assoc (cdr x) all-parts))
4760 (unless new
4761 (setq new (cons (cdr x) parts))
4762 (push new all-parts))
4763 (setcdr new (delete (car x) (cdr new)))))
4b1aaa8b 4764
f66f03de
S
4765 ;; Add any missing aliases (separate by slashes)
4766 (loop for x in all-parts do
4767 (if (cdr x)
4768 (push (cons (nth 1 x) (car x)) aliases)))
4769
4770 (loop for x in aliases do
4771 (when (setq alias (assoc (cdr x) idlwave-system-routines))
4772 (unless (memq alias remove-list) (push alias remove-list))
4773 (setq alias (copy-sequence alias))
4774 (setcar alias (car x))
4775 (push alias idlwave-system-routines)))
4776 (loop for x in remove-list do
4777 (delq x idlwave-system-routines))))
4778
4779(defun idlwave-convert-xml-clean-sysvar-aliases (aliases)
4780 ;; Duplicate and trim original routine aliases from rinfo list
4781 ;; This if for, e.g. !X, !Y, !Z.
8d222148 4782 (let (alias remove-list)
f66f03de
S
4783 (loop for x in aliases do
4784 (when (setq alias (assoc (cdr x) idlwave-system-variables-alist))
4785 (unless (memq alias remove-list) (push alias remove-list))
4786 (setq alias (copy-sequence alias))
4787 (setcar alias (car x))
4788 (push alias idlwave-system-variables-alist)))
4789 (loop for x in remove-list do
4790 (delq x idlwave-system-variables-alist))))
4791
4792
4793(defun idlwave-xml-create-sysvar-alist (xml-entry)
4794 ;; Create a sysvar list entry from the xml parsed list.
4795 (let* ((nameblock (nth 1 xml-entry))
a86bd650 4796 (name (cdr (assq 'name nameblock)))
b9e4fbd3 4797 (sysvar (substring name (progn (string-match "^ *!" name)
a86bd650 4798 (match-end 0))))
f66f03de
S
4799 (link (cdr (assq 'link nameblock)))
4800 (params (cddr xml-entry))
4801 (case-fold-search t)
8d222148 4802 pelem ptype props tags)
f66f03de
S
4803 (while params
4804 (setq pelem (car params))
4805 (when (listp pelem)
4806 (setq ptype (car pelem)
4807 props (car (cdr pelem)))
4808 (cond
4809 ((eq ptype 'FIELD)
4b1aaa8b 4810 (push (cons (cdr (assq 'name props))
f66f03de
S
4811 (cdr
4812 (idlwave-split-link-target (cdr (assq 'link props)))))
4813 tags))))
4814 (setq params (cdr params)))
4815 (delq nil
4816 (list sysvar (if tags (cons 'tags tags)) (list 'link link)))))
4817
4818
4819(defvar idlwave-xml-routine-info-file nil)
4820
4821(defun idlwave-save-routine-info ()
4822 (if idlwave-xml-routine-info-file
4823 (with-temp-file idlwave-xml-system-rinfo-converted-file
4b1aaa8b 4824 (insert
f66f03de 4825 (concat ";; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
4b1aaa8b
PE
4826;; IDLWAVE Routine Information File (IDLWAVE version " idlwave-mode-version ")
4827;; Automatically generated from source file:
f66f03de
S
4828;; " idlwave-xml-routine-info-file "
4829;; on " (current-time-string) "
4830;; Do not edit."))
4831 (insert (format "\n(setq idlwave-xml-routine-info-file \n \"%s\")"
4832 idlwave-xml-routine-info-file))
4833 (insert "\n(setq idlwave-system-routines\n '")
4834 (prin1 idlwave-system-routines (current-buffer))
4835 (insert ")")
4836 (insert "\n(setq idlwave-system-variables-alist\n '")
4837 (prin1 idlwave-system-variables-alist (current-buffer))
4838 (insert ")")
4839 (insert "\n(setq idlwave-system-class-info\n '")
4840 (prin1 idlwave-system-class-info (current-buffer))
4841 (insert ")")
4842 (insert "\n(setq idlwave-executive-commands-alist\n '")
4843 (prin1 idlwave-executive-commands-alist (current-buffer))
4844 (insert ")")
4845 (insert "\n(setq idlwave-help-special-topic-words\n '")
4846 (prin1 idlwave-help-special-topic-words (current-buffer))
4847 (insert ")"))))
4848
4849(defun idlwave-convert-xml-system-routine-info ()
4850 "Convert XML supplied IDL routine info into internal form.
4851Cache to disk for quick recovery."
4852 (interactive)
4b1aaa8b 4853 (let* ((dir (file-name-as-directory
f66f03de
S
4854 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4855 (catalog-file (expand-file-name "idl_catalog.xml" dir))
4856 (elem-cnt 0)
4b1aaa8b 4857 props rinfo msg-cnt elem type nelem class-result alias
8d222148 4858 routines routine-aliases statement-aliases sysvar-aliases)
f66f03de
S
4859 (if (not (file-exists-p catalog-file))
4860 (error "No such XML routine info file: %s" catalog-file)
4861 (if (not (file-readable-p catalog-file))
4862 (error "Cannot read XML routine info file: %s" catalog-file)))
4b1aaa8b 4863 (message "Reading XML routine info...")
e08734e2 4864 (setq rinfo (xml-parse-file catalog-file))
f66f03de
S
4865 (message "Reading XML routine info...done")
4866 (setq rinfo (assq 'CATALOG rinfo))
4867 (unless rinfo (error "Failed to parse XML routine info"))
4868 ;;(setq rinfo (car rinfo)) ; Skip the catalog stuff.
4b1aaa8b 4869
8d222148 4870 (setq rinfo (cddr rinfo))
f66f03de
S
4871
4872 (setq nelem (length rinfo)
4873 msg-cnt (/ nelem 20))
4b1aaa8b 4874
f66f03de
S
4875 (setq idlwave-xml-routine-info-file nil)
4876 (message "Converting XML routine info...")
4877 (setq idlwave-system-routines nil
4878 idlwave-system-variables-alist nil
4879 idlwave-system-class-info nil
4880 idlwave-executive-commands-alist nil
4881 idlwave-help-special-topic-words nil)
4882
4883 (while rinfo
4884 (setq elem (car rinfo)
4885 rinfo (cdr rinfo))
4886 (incf elem-cnt)
4887 (when (listp elem)
4888 (setq type (car elem)
4889 props (car (cdr elem)))
4890 (if (= (mod elem-cnt msg-cnt) 0)
4b1aaa8b 4891 (message "Converting XML routine info...%2d%%"
f66f03de 4892 (/ (* elem-cnt 100) nelem)))
4b1aaa8b 4893 (cond
f66f03de
S
4894 ((eq type 'ROUTINE)
4895 (if (setq alias (assq 'alias_to props))
4b1aaa8b 4896 (push (cons (cdr (assq 'name props)) (cdr alias))
f66f03de
S
4897 routine-aliases)
4898 (setq routines (idlwave-xml-create-rinfo-list elem))
4899 (if (listp (cdr routines))
4900 (setq idlwave-system-routines
4901 (nconc idlwave-system-routines routines))
4902 ;; a cons cell is an executive commands
4903 (push routines idlwave-executive-commands-alist))))
4b1aaa8b 4904
f66f03de
S
4905 ((eq type 'CLASS)
4906 (setq class-result (idlwave-xml-create-class-method-lists elem))
4907 (push (car class-result) idlwave-system-class-info)
4908 (setq idlwave-system-routines
4909 (nconc idlwave-system-routines (cdr class-result))))
4910
4911 ((eq type 'STATEMENT)
4912 (push (cons (cdr (assq 'name props))
4913 (cdr (assq 'link props)))
4914 idlwave-help-special-topic-words)
4915 ;; Save the links to those which are statement aliases (not routines)
4916 (if (setq alias (assq 'alias_to props))
4917 (unless (member (cdr alias) statement-aliases)
4918 (push (cdr alias) statement-aliases))))
4919
4920 ((eq type 'SYSVAR)
4921 (if (setq alias (cdr (assq 'alias_to props)))
4b1aaa8b 4922 (push (cons (substring (cdr (assq 'name props)) 1)
f66f03de
S
4923 (substring alias 1))
4924 sysvar-aliases)
4b1aaa8b 4925 (push (idlwave-xml-create-sysvar-alist elem)
f66f03de
S
4926 idlwave-system-variables-alist)))
4927 (t))))
4928 (idlwave-convert-xml-clean-routine-aliases routine-aliases)
4929 (idlwave-convert-xml-clean-statement-aliases statement-aliases)
4930 (idlwave-convert-xml-clean-sysvar-aliases sysvar-aliases)
4931
4932 (setq idlwave-xml-routine-info-file catalog-file)
4933 (idlwave-save-routine-info)
4934 (message "Converting XML routine info...done")))
4b1aaa8b
PE
4935
4936
f66f03de
S
4937;; ("ROUTINE" type class
4938;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4939;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4940;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de
S
4941;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
4942
4943
5e72c6b2
S
4944(defun idlwave-load-rinfo-next-step ()
4945 (let ((inhibit-quit t)
4946 (arr idlwave-load-rinfo-steps-done))
f66f03de 4947 (if (catch 'exit
5e72c6b2 4948 (when (not (aref arr 0))
f66f03de
S
4949 (message "Loading system routine info in idle time...")
4950 (idlwave-load-system-routine-info)
4951 ;;(load "idlw-rinfo" 'noerror 'nomessage)
4952 (message "Loading system routine info in idle time...done")
5e72c6b2
S
4953 (aset arr 0 t)
4954 (throw 'exit t))
4b1aaa8b 4955
5e72c6b2
S
4956 (when (not (aref arr 1))
4957 (message "Normalizing idlwave-system-routines in idle time...")
4958 (idlwave-reset-sintern t)
4959 (put 'idlwave-reset-sintern 'done-by-idle t)
4960 (setq idlwave-system-routines
4961 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
4962 (message "Normalizing idlwave-system-routines in idle time...done")
4963 (aset arr 1 t)
4964 (throw 'exit t))
f66f03de 4965
5e72c6b2 4966 (when (not (aref arr 2))
52a244eb
S
4967 (when (and (stringp idlwave-user-catalog-file)
4968 (file-regular-p idlwave-user-catalog-file))
4969 (message "Loading user catalog in idle time...")
5e72c6b2 4970 (condition-case nil
52a244eb
S
4971 (load-file idlwave-user-catalog-file)
4972 (error (throw 'exit nil)))
4973 ;; Check for the old style catalog and warn
4974 (if (and
4975 (boundp 'idlwave-library-routines)
4976 idlwave-library-routines)
775591f7 4977 (progn
52a244eb
S
4978 (setq idlwave-library-routines nil)
4979 (ding)
4b1aaa8b 4980 (message "Outdated user catalog: %s... recreate"
52a244eb 4981 idlwave-user-catalog-file))
f66f03de
S
4982 (message "Loading user catalog in idle time...done")))
4983 (aset arr 2 t)
4984 (throw 'exit t))
4985
5e72c6b2 4986 (when (not (aref arr 3))
52a244eb
S
4987 (when idlwave-user-catalog-routines
4988 (message "Normalizing user catalog routines in idle time...")
4b1aaa8b 4989 (setq idlwave-user-catalog-routines
52a244eb
S
4990 (idlwave-sintern-rinfo-list
4991 idlwave-user-catalog-routines 'sys))
4b1aaa8b 4992 (message
52a244eb 4993 "Normalizing user catalog routines in idle time...done"))
5e72c6b2
S
4994 (aset arr 3 t)
4995 (throw 'exit t))
f66f03de 4996
5e72c6b2 4997 (when (not (aref arr 4))
4b1aaa8b 4998 (idlwave-scan-library-catalogs
52a244eb
S
4999 "Loading and normalizing library catalogs in idle time...")
5000 (aset arr 4 t)
5001 (throw 'exit t))
5002 (when (not (aref arr 5))
5e72c6b2
S
5003 (message "Finishing initialization in idle time...")
5004 (idlwave-routines)
5005 (message "Finishing initialization in idle time...done")
4b1aaa8b 5006 (aset arr 5 t)
5e72c6b2 5007 (throw 'exit nil)))
52a244eb
S
5008 ;; restart the timer
5009 (if (sit-for 1)
5010 (idlwave-load-rinfo-next-step)
5011 (setq idlwave-load-rinfo-idle-timer
5012 (run-with-idle-timer
5013 idlwave-init-rinfo-when-idle-after
5014 nil 'idlwave-load-rinfo-next-step))))))
5e72c6b2 5015
8d222148
SM
5016(defvar idlwave-after-load-rinfo-hook nil)
5017
f66f03de
S
5018(defun idlwave-load-all-rinfo (&optional force)
5019 ;; Load and case-treat the system, user catalog, and library routine
5020 ;; info files.
5021
5022 ;; System
5e72c6b2 5023 (when (or force (not (aref idlwave-load-rinfo-steps-done 0)))
f66f03de
S
5024 ;;(load "idlw-rinfo" 'noerror 'nomessage))
5025 (idlwave-load-system-routine-info))
5e72c6b2
S
5026 (when (or force (not (aref idlwave-load-rinfo-steps-done 1)))
5027 (message "Normalizing idlwave-system-routines...")
5028 (setq idlwave-system-routines
5029 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
5030 (message "Normalizing idlwave-system-routines...done"))
f66f03de
S
5031 (when idlwave-system-routines
5032 (setq idlwave-routines (copy-sequence idlwave-system-routines))
5033 (setq idlwave-last-system-routine-info-cons-cell
5034 (nthcdr (1- (length idlwave-routines)) idlwave-routines)))
5035
5036 ;; User catalog
52a244eb
S
5037 (when (and (stringp idlwave-user-catalog-file)
5038 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5039 (condition-case nil
52a244eb
S
5040 (when (or force (not (aref idlwave-load-rinfo-steps-done 2)))
5041 (load-file idlwave-user-catalog-file))
5042 (error nil))
4b1aaa8b 5043 (when (and
f66f03de
S
5044 (boundp 'idlwave-library-routines)
5045 idlwave-library-routines)
52a244eb 5046 (setq idlwave-library-routines nil)
4b1aaa8b 5047 (error "Outdated user catalog: %s... recreate"
f66f03de 5048 idlwave-user-catalog-file))
52a244eb
S
5049 (setq idlwave-true-path-alist nil)
5050 (when (or force (not (aref idlwave-load-rinfo-steps-done 3)))
5051 (message "Normalizing user catalog routines...")
4b1aaa8b
PE
5052 (setq idlwave-user-catalog-routines
5053 (idlwave-sintern-rinfo-list
52a244eb
S
5054 idlwave-user-catalog-routines 'sys))
5055 (message "Normalizing user catalog routines...done")))
f66f03de
S
5056
5057 ;; Library catalog
52a244eb
S
5058 (when (or force (not (aref idlwave-load-rinfo-steps-done 4)))
5059 (idlwave-scan-library-catalogs
5060 "Loading and normalizing library catalogs..."))
05a1abfc
CD
5061 (run-hooks 'idlwave-after-load-rinfo-hook))
5062
f32b3b91
CD
5063
5064(defun idlwave-update-buffer-routine-info ()
5065 (let (res)
4b1aaa8b 5066 (cond
15e42531
CD
5067 ((eq idlwave-scan-all-buffers-for-routine-info t)
5068 ;; Scan all buffers, current buffer last
5069 (message "Scanning all buffers...")
4b1aaa8b 5070 (setq res (idlwave-get-routine-info-from-buffers
15e42531
CD
5071 (reverse (buffer-list)))))
5072 ((null idlwave-scan-all-buffers-for-routine-info)
5073 ;; Don't scan any buffers
5074 (setq res nil))
5075 (t
f32b3b91 5076 ;; Just scan this buffer
175069ef 5077 (if (derived-mode-p 'idlwave-mode)
f32b3b91
CD
5078 (progn
5079 (message "Scanning current buffer...")
5080 (setq res (idlwave-get-routine-info-from-buffers
15e42531 5081 (list (current-buffer))))))))
f32b3b91 5082 ;; Put the result into the correct variable
4b1aaa8b 5083 (setq idlwave-buffer-routines
52a244eb 5084 (idlwave-sintern-rinfo-list res 'set))))
f32b3b91 5085
05a1abfc 5086(defun idlwave-concatenate-rinfo-lists (&optional quiet run-hook)
f32b3b91 5087 "Put the different sources for routine information together."
4b1aaa8b 5088 ;; The sequence here is important because earlier definitions shadow
f32b3b91 5089 ;; later ones. We assume that if things in the buffers are newer
52a244eb 5090 ;; then in the shell of the system, they are meant to be different.
15e42531
CD
5091 (setcdr idlwave-last-system-routine-info-cons-cell
5092 (append idlwave-buffer-routines
5093 idlwave-compiled-routines
52a244eb
S
5094 idlwave-library-catalog-routines
5095 idlwave-user-catalog-routines))
f32b3b91 5096 (setq idlwave-class-alist nil)
15e42531 5097
f32b3b91 5098 ;; Give a message with information about the number of routines we have.
15e42531 5099 (unless quiet
4b1aaa8b 5100 (message
52a244eb 5101 "Routines Found: buffer(%d) compiled(%d) library(%d) user(%d) system(%d)"
15e42531
CD
5102 (length idlwave-buffer-routines)
5103 (length idlwave-compiled-routines)
52a244eb
S
5104 (length idlwave-library-catalog-routines)
5105 (length idlwave-user-catalog-routines)
05a1abfc
CD
5106 (length idlwave-system-routines)))
5107 (if run-hook
5108 (run-hooks 'idlwave-update-rinfo-hook)))
15e42531
CD
5109
5110(defun idlwave-class-alist ()
5111 "Return the class alist - make it if necessary."
5112 (or idlwave-class-alist
5113 (let (class)
5114 (loop for x in idlwave-routines do
5115 (when (and (setq class (nth 2 x))
5116 (not (assq class idlwave-class-alist)))
5117 (push (list class) idlwave-class-alist)))
4b1aaa8b 5118 idlwave-class-alist)))
15e42531
CD
5119
5120;; Three functions for the hooks
5121(defun idlwave-save-buffer-update ()
5122 (idlwave-update-current-buffer-info 'save-buffer))
5123(defun idlwave-kill-buffer-update ()
5124 (idlwave-update-current-buffer-info 'kill-buffer))
5125(defun idlwave-new-buffer-update ()
5126 (idlwave-update-current-buffer-info 'find-file))
5127
5128(defun idlwave-update-current-buffer-info (why)
5a0c3f56
JB
5129 "Update `idlwave-routines' for current buffer.
5130Can run from `after-save-hook'."
175069ef 5131 (when (and (derived-mode-p 'idlwave-mode)
15e42531
CD
5132 (or (eq t idlwave-auto-routine-info-updates)
5133 (memq why idlwave-auto-routine-info-updates))
5134 idlwave-scan-all-buffers-for-routine-info
5135 idlwave-routines)
5136 (condition-case nil
5137 (let (routines)
5138 (idlwave-replace-buffer-routine-info
5139 (buffer-file-name)
5140 (if (eq why 'kill-buffer)
5141 nil
5142 (setq routines
5143 (idlwave-sintern-rinfo-list
5144 (idlwave-get-routine-info-from-buffers
5145 (list (current-buffer))) 'set))))
5146 (idlwave-concatenate-rinfo-lists 'quiet)
5147 routines)
5148 (error nil))))
5149
5150(defun idlwave-replace-buffer-routine-info (file new)
5151 "Cut the part from FILE out of `idlwave-buffer-routines' and add NEW."
4b1aaa8b 5152 (let ((list idlwave-buffer-routines)
15e42531
CD
5153 found)
5154 (while list
5155 ;; The following test uses eq to make sure it works correctly
5156 ;; when two buffers visit the same file. Then the file names
5157 ;; will be equal, but not eq.
52a244eb 5158 (if (eq (idlwave-routine-source-file (nth 3 (car list))) file)
15e42531
CD
5159 (progn
5160 (setcar list nil)
5161 (setq found t))
5162 (if found
4b1aaa8b 5163 ;; End of that section reached. Jump.
15e42531
CD
5164 (setq list nil)))
5165 (setq list (cdr list)))
5166 (setq idlwave-buffer-routines
5167 (append new (delq nil idlwave-buffer-routines)))))
f32b3b91
CD
5168
5169;;----- Scanning buffers -------------------
5170
5171(defun idlwave-get-routine-info-from-buffers (buffers)
5172 "Call `idlwave-get-buffer-routine-info' on idlwave-mode buffers in BUFFERS."
5173 (let (buf routine-lists res)
5174 (save-excursion
5175 (while (setq buf (pop buffers))
5176 (set-buffer buf)
175069ef 5177 (if (and (derived-mode-p 'idlwave-mode)
05a1abfc 5178 buffer-file-name)
f32b3b91
CD
5179 ;; yes, this buffer has the right mode.
5180 (progn (setq res (condition-case nil
5181 (idlwave-get-buffer-routine-info)
5182 (error nil)))
5183 (push res routine-lists)))))
5184 ;; Concatenate the individual lists and return the result
5185 (apply 'nconc routine-lists)))
5186
5187(defun idlwave-get-buffer-routine-info ()
5188 "Scan the current buffer for routine info. Return (PRO-LIST FUNC-LIST)."
5189 (let* ((case-fold-search t)
5190 routine-list string entry)
5191 (save-excursion
5192 (save-restriction
5193 (widen)
5194 (goto-char (point-min))
4b1aaa8b 5195 (while (re-search-forward
15e42531 5196 "^[ \t]*\\(pro\\|function\\)[ \t]" nil t)
76959b77 5197 (setq string (buffer-substring-no-properties
f32b3b91 5198 (match-beginning 0)
4b1aaa8b 5199 (progn
f32b3b91
CD
5200 (idlwave-end-of-statement)
5201 (point))))
5202 (setq entry (idlwave-parse-definition string))
5203 (push entry routine-list))))
5204 routine-list))
5205
15e42531 5206(defvar idlwave-scanning-lib-dir)
8d222148 5207(defvar idlwave-scanning-lib)
f32b3b91
CD
5208(defun idlwave-parse-definition (string)
5209 "Parse a module definition."
5210 (let ((case-fold-search t)
5211 start name args type keywords class)
5212 ;; Remove comments
5213 (while (string-match ";.*" string)
5214 (setq string (replace-match "" t t string)))
5215 ;; Remove the continuation line stuff
5216 (while (string-match "\\([^a-zA-Z0-9$_]\\)\\$[ \t]*\n" string)
5217 (setq string (replace-match "\\1 " t nil string)))
05a1abfc
CD
5218 (while (string-match "\n" string)
5219 (setq string (replace-match " " t nil string)))
f32b3b91
CD
5220 ;; Match the name and type.
5221 (when (string-match
5222 "\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)" string)
5223 (setq start (match-end 0))
5224 (setq type (downcase (match-string 1 string)))
5225 (if (match-beginning 3)
5226 (setq class (match-string 3 string)))
5227 (setq name (match-string 4 string)))
5228 ;; Match normal args and keyword args
5229 (while (string-match
15e42531 5230 ",\\s-*\\([a-zA-Z][a-zA-Z0-9$_]*\\|\\(_ref\\)?_extra\\)\\s-*\\(=\\)?"
f32b3b91
CD
5231 string start)
5232 (setq start (match-end 0))
15e42531 5233 (if (match-beginning 3)
f32b3b91
CD
5234 (push (match-string 1 string) keywords)
5235 (push (match-string 1 string) args)))
5236 ;; Normalize and sort.
5237 (setq args (nreverse args))
4b1aaa8b 5238 (setq keywords (sort keywords (lambda (a b)
f32b3b91
CD
5239 (string< (downcase a) (downcase b)))))
5240 ;; Make and return the entry
5241 ;; We don't know which argument are optional, so this information
5242 ;; will not be contained in the calling sequence.
5243 (list name
5244 (if (equal type "pro") 'pro 'fun)
5245 class
5246 (cond ((not (boundp 'idlwave-scanning-lib))
52a244eb 5247 (list 'buffer (buffer-file-name)))
4b1aaa8b 5248; ((string= (downcase
15e42531
CD
5249; (file-name-sans-extension
5250; (file-name-nondirectory (buffer-file-name))))
5251; (downcase name))
5252; (list 'lib))
5253; (t (cons 'lib (file-name-nondirectory (buffer-file-name))))
52a244eb
S
5254 (t (list 'user (file-name-nondirectory (buffer-file-name))
5255 idlwave-scanning-lib-dir "UserLib")))
4b1aaa8b 5256 (concat
f32b3b91
CD
5257 (if (string= type "function") "Result = " "")
5258 (if class "Obj ->[%s::]" "")
5259 "%s"
5260 (if args
5261 (concat
5262 (if (string= type "function") "(" ", ")
5263 (mapconcat 'identity args ", ")
5264 (if (string= type "function") ")" ""))))
5265 (if keywords
52a244eb 5266 (cons nil (mapcar 'list keywords)) ;No help file
f32b3b91
CD
5267 nil))))
5268
f32b3b91 5269
52a244eb 5270;;----- Scanning the user catalog -------------------
15e42531
CD
5271
5272(defun idlwave-sys-dir ()
5273 "Return the syslib directory, or a dummy that never matches."
3938cb82
S
5274 (cond
5275 ((and idlwave-system-directory
5276 (not (string= idlwave-system-directory "")))
5277 idlwave-system-directory)
5278 ((getenv "IDL_DIR"))
5279 (t "@@@@@@@@")))
5280
52a244eb 5281
52a244eb 5282(defun idlwave-create-user-catalog-file (&optional arg)
f32b3b91 5283 "Scan all files on selected dirs of IDL search path for routine information.
52a244eb
S
5284
5285A widget checklist will allow you to choose the directories. Write
5286the result as a file `idlwave-user-catalog-file'. When this file
5a0c3f56
JB
5287exists, it will be automatically loaded to give routine information
5288about library routines. With ARG, just rescan the same directories
5289as last time - so no widget will pop up."
f32b3b91
CD
5290 (interactive "P")
5291 ;; Make sure the file is loaded if it exists.
52a244eb
S
5292 (if (and (stringp idlwave-user-catalog-file)
5293 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5294 (condition-case nil
52a244eb 5295 (load-file idlwave-user-catalog-file)
f32b3b91
CD
5296 (error nil)))
5297 ;; Make sure the file name makes sense
52a244eb
S
5298 (unless (and (stringp idlwave-user-catalog-file)
5299 (> (length idlwave-user-catalog-file) 0)
f32b3b91 5300 (file-accessible-directory-p
52a244eb 5301 (file-name-directory idlwave-user-catalog-file))
4b1aaa8b 5302 (not (string= "" (file-name-nondirectory
52a244eb
S
5303 idlwave-user-catalog-file))))
5304 (error "`idlwave-user-catalog-file' does not point to a file in an accessible directory"))
4b1aaa8b 5305
f32b3b91 5306 (cond
f32b3b91 5307 ;; Rescan the known directories
52a244eb
S
5308 ((and arg idlwave-path-alist
5309 (consp (car idlwave-path-alist)))
5310 (idlwave-scan-user-lib-files idlwave-path-alist))
5311
5312 ;; Expand the directories from library-path and run the widget
f32b3b91 5313 (idlwave-library-path
52a244eb 5314 (idlwave-display-user-catalog-widget
4b1aaa8b 5315 (if idlwave-true-path-alist
52a244eb
S
5316 ;; Propagate any flags on the existing path-alist
5317 (mapcar (lambda (x)
5318 (let ((path-entry (assoc (file-truename x)
5319 idlwave-true-path-alist)))
5320 (if path-entry
4b1aaa8b 5321 (cons x (cdr path-entry))
52a244eb
S
5322 (list x))))
5323 (idlwave-expand-path idlwave-library-path))
5324 (mapcar 'list (idlwave-expand-path idlwave-library-path)))))
5325
5326 ;; Ask the shell for the path and then run the widget
f32b3b91 5327 (t
f32b3b91 5328 (message "Asking the shell for IDL path...")
15e42531
CD
5329 (require 'idlw-shell)
5330 (idlwave-shell-send-command idlwave-shell-path-query
52a244eb 5331 '(idlwave-user-catalog-command-hook nil)
15e42531 5332 'hide))))
f32b3b91 5333
52a244eb
S
5334
5335;; Parse shell path information and select among it.
5336(defun idlwave-user-catalog-command-hook (&optional arg)
5337 ;; Command hook used by `idlwave-create-user-catalog-file'.
f32b3b91
CD
5338 (if arg
5339 ;; Scan immediately
52a244eb
S
5340 (idlwave-scan-user-lib-files idlwave-path-alist)
5341 ;; Set the path and display the widget
5342 (idlwave-shell-get-path-info 'no-write) ; set to something path-alist
5343 (idlwave-scan-library-catalogs "Locating library catalogs..." 'no-load)
5344 (idlwave-display-user-catalog-widget idlwave-path-alist)))
5345
4b1aaa8b 5346(defconst idlwave-user-catalog-widget-help-string
52a244eb
S
5347 "This is the front-end to the creation of the IDLWAVE user catalog.
5348Please select the directories on IDL's search path from which you
5349would like to extract routine information, to be stored in the file:
f32b3b91
CD
5350
5351 %s
5352
52a244eb
S
5353If this is not the correct file, first set variable
5354`idlwave-user-catalog-file', and call this command again.
15e42531 5355
52a244eb
S
5356N.B. Many libraries include pre-scanned catalog files
5357\(\".idlwave_catalog\"). These are marked with \"[LIB]\", and need
5358not be scanned. You can scan your own libraries off-line using the
5359perl script `idlwave_catalog'.
15e42531 5360
f32b3b91
CD
5361After selecting the directories, choose [Scan & Save] to scan the library
5362directories and save the routine info.
5363\n")
5364
5365(defvar idlwave-widget)
5366(defvar widget-keymap)
52a244eb 5367(defun idlwave-display-user-catalog-widget (dirs-list)
f32b3b91
CD
5368 "Create the widget to select IDL search path directories for scanning."
5369 (interactive)
5370 (require 'widget)
5371 (require 'wid-edit)
52a244eb 5372 (unless dirs-list
f32b3b91
CD
5373 (error "Don't know IDL's search path"))
5374
f32b3b91
CD
5375 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
5376 (switch-to-buffer (get-buffer-create "*IDLWAVE Widget*"))
5377 (kill-all-local-variables)
5378 (make-local-variable 'idlwave-widget)
52a244eb
S
5379 (widget-insert (format idlwave-user-catalog-widget-help-string
5380 idlwave-user-catalog-file))
4b1aaa8b 5381
f32b3b91 5382 (widget-create 'push-button
52a244eb 5383 :notify 'idlwave-widget-scan-user-lib-files
f32b3b91
CD
5384 "Scan & Save")
5385 (widget-insert " ")
5386 (widget-create 'push-button
52a244eb 5387 :notify 'idlwave-delete-user-catalog-file
f32b3b91
CD
5388 "Delete File")
5389 (widget-insert " ")
5390 (widget-create 'push-button
4b1aaa8b 5391 :notify
8d222148
SM
5392 (lambda (&rest ignore)
5393 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5394 (dolist (x path-list)
5395 (unless (memq 'lib (cdr x))
5396 (idlwave-path-alist-add-flag x 'user)))
5397 (idlwave-display-user-catalog-widget path-list)))
52a244eb 5398 "Select All Non-Lib")
f32b3b91
CD
5399 (widget-insert " ")
5400 (widget-create 'push-button
4b1aaa8b 5401 :notify
8d222148
SM
5402 (lambda (&rest ignore)
5403 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5404 (dolist (x path-list)
5405 (idlwave-path-alist-remove-flag x 'user))
5406 (idlwave-display-user-catalog-widget path-list)))
f32b3b91 5407 "Deselect All")
52a244eb
S
5408 (widget-insert " ")
5409 (widget-create 'push-button
5410 :notify (lambda (&rest ignore)
5411 (kill-buffer (current-buffer)))
5412 "Quit")
f32b3b91
CD
5413 (widget-insert "\n\n")
5414
52a244eb 5415 (widget-insert "Select Directories: \n")
4b1aaa8b 5416
f32b3b91
CD
5417 (setq idlwave-widget
5418 (apply 'widget-create
5419 'checklist
4b1aaa8b
PE
5420 :value (delq nil (mapcar (lambda (x)
5421 (if (memq 'user (cdr x))
52a244eb
S
5422 (car x)))
5423 dirs-list))
f32b3b91
CD
5424 :greedy t
5425 :tag "List of directories"
4b1aaa8b
PE
5426 (mapcar (lambda (x)
5427 (list 'item
52a244eb
S
5428 (if (memq 'lib (cdr x))
5429 (concat "[LIB] " (car x) )
5430 (car x)))) dirs-list)))
5431 (widget-put idlwave-widget :path-dirs dirs-list)
f32b3b91
CD
5432 (widget-insert "\n")
5433 (use-local-map widget-keymap)
5434 (widget-setup)
5435 (goto-char (point-min))
5436 (delete-other-windows))
4b1aaa8b 5437
52a244eb 5438(defun idlwave-delete-user-catalog-file (&rest ignore)
f32b3b91 5439 (if (yes-or-no-p
52a244eb 5440 (format "Delete file %s " idlwave-user-catalog-file))
f32b3b91 5441 (progn
52a244eb
S
5442 (delete-file idlwave-user-catalog-file)
5443 (message "%s has been deleted" idlwave-user-catalog-file))))
f32b3b91 5444
52a244eb
S
5445(defun idlwave-widget-scan-user-lib-files (&rest ignore)
5446 ;; Call `idlwave-scan-user-lib-files' with data taken from the widget.
f32b3b91 5447 (let* ((widget idlwave-widget)
15e42531 5448 (selected-dirs (widget-value widget))
52a244eb
S
5449 (path-alist (widget-get widget :path-dirs))
5450 (this-path-alist path-alist)
5451 dir-entry)
5452 (while (setq dir-entry (pop this-path-alist))
4b1aaa8b 5453 (if (member
52a244eb
S
5454 (if (memq 'lib (cdr dir-entry))
5455 (concat "[LIB] " (car dir-entry))
5456 (car dir-entry))
5457 selected-dirs)
5458 (idlwave-path-alist-add-flag dir-entry 'user)
5459 (idlwave-path-alist-remove-flag dir-entry 'user)))
5460 (idlwave-scan-user-lib-files path-alist)))
f32b3b91
CD
5461
5462(defvar font-lock-mode)
52a244eb
S
5463(defun idlwave-scan-user-lib-files (path-alist)
5464 ;; Scan the PRO files in PATH-ALIST and store the info in the user catalog
f32b3b91 5465 (let* ((idlwave-scanning-lib t)
15e42531 5466 (idlwave-scanning-lib-dir "")
f32b3b91 5467 (idlwave-completion-case nil)
15e42531 5468 dirs-alist dir files file)
52a244eb
S
5469 (setq idlwave-user-catalog-routines nil
5470 idlwave-path-alist path-alist ; for library-path instead
5471 idlwave-true-path-alist nil)
5472 (if idlwave-auto-write-paths (idlwave-write-paths))
9a529312 5473 (with-current-buffer (get-buffer-create "*idlwave-scan.pro*")
f32b3b91 5474 (idlwave-mode)
15e42531
CD
5475 (setq dirs-alist (reverse path-alist))
5476 (while (setq dir (pop dirs-alist))
52a244eb 5477 (when (memq 'user (cdr dir)) ; Has it marked for scan?
15e42531 5478 (setq dir (car dir))
52a244eb 5479 (setq idlwave-scanning-lib-dir dir)
15e42531
CD
5480 (when (file-directory-p dir)
5481 (setq files (directory-files dir 'full "\\.[pP][rR][oO]\\'"))
5482 (while (setq file (pop files))
5483 (when (file-regular-p file)
5484 (if (not (file-readable-p file))
5485 (message "Skipping %s (no read permission)" file)
5486 (message "Scanning %s..." file)
5487 (erase-buffer)
5488 (insert-file-contents file 'visit)
52a244eb 5489 (setq idlwave-user-catalog-routines
15e42531
CD
5490 (append (idlwave-get-routine-info-from-buffers
5491 (list (current-buffer)))
52a244eb
S
5492 idlwave-user-catalog-routines)))))))))
5493 (message "Creating user catalog file...")
f32b3b91
CD
5494 (kill-buffer "*idlwave-scan.pro*")
5495 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
15e42531
CD
5496 (let ((font-lock-maximum-size 0)
5497 (auto-mode-alist nil))
52a244eb 5498 (find-file idlwave-user-catalog-file))
f32b3b91
CD
5499 (if (and (boundp 'font-lock-mode)
5500 font-lock-mode)
5501 (font-lock-mode 0))
5502 (erase-buffer)
52a244eb 5503 (insert ";; IDLWAVE user catalog file\n")
f32b3b91
CD
5504 (insert (format ";; Created %s\n\n" (current-time-string)))
5505
f32b3b91 5506 ;; Define the routine info list
52a244eb 5507 (insert "\n(setq idlwave-user-catalog-routines\n '(")
5e72c6b2 5508 (let ((standard-output (current-buffer)))
8ffcfb27
GM
5509 (mapc (lambda (x)
5510 (insert "\n ")
5511 (prin1 x)
5512 (goto-char (point-max)))
5513 idlwave-user-catalog-routines))
f32b3b91 5514 (insert (format "))\n\n;;; %s ends here\n"
52a244eb 5515 (file-name-nondirectory idlwave-user-catalog-file)))
f32b3b91
CD
5516 (goto-char (point-min))
5517 ;; Save the buffer
5518 (save-buffer 0)
5519 (kill-buffer (current-buffer)))
52a244eb 5520 (message "Creating user catalog file...done")
f32b3b91 5521 (message "Info for %d routines saved in %s"
52a244eb
S
5522 (length idlwave-user-catalog-routines)
5523 idlwave-user-catalog-file)
f32b3b91
CD
5524 (sit-for 2)
5525 (idlwave-update-routine-info t))
5526
52a244eb
S
5527(defun idlwave-read-paths ()
5528 (if (and (stringp idlwave-path-file)
5529 (file-regular-p idlwave-path-file))
5530 (condition-case nil
5531 (load idlwave-path-file t t t)
5532 (error nil))))
5533
5534(defun idlwave-write-paths ()
5535 (interactive)
5536 (when (and idlwave-path-alist idlwave-system-directory)
5537 (let ((font-lock-maximum-size 0)
5538 (auto-mode-alist nil))
5539 (find-file idlwave-path-file))
5540 (if (and (boundp 'font-lock-mode)
5541 font-lock-mode)
5542 (font-lock-mode 0))
5543 (erase-buffer)
5544 (insert ";; IDLWAVE paths\n")
5545 (insert (format ";; Created %s\n\n" (current-time-string)))
5546 ;; Define the variable which knows the value of "!DIR"
5547 (insert (format "\n(setq idlwave-system-directory \"%s\")\n"
5548 idlwave-system-directory))
4b1aaa8b 5549
52a244eb
S
5550 ;; Define the variable which contains a list of all scanned directories
5551 (insert "\n(setq idlwave-path-alist\n '(")
5552 (let ((standard-output (current-buffer)))
8ffcfb27
GM
5553 (mapc (lambda (x)
5554 (insert "\n ")
5555 (prin1 x)
5556 (goto-char (point-max)))
5557 idlwave-path-alist))
52a244eb
S
5558 (insert "))\n")
5559 (save-buffer 0)
5560 (kill-buffer (current-buffer))))
5561
5562
f32b3b91
CD
5563(defun idlwave-expand-path (path &optional default-dir)
5564 ;; Expand parts of path starting with '+' recursively into directory list.
5565 ;; Relative recursive path elements are expanded relative to DEFAULT-DIR.
5566 (message "Expanding path...")
5567 (let (path1 dir recursive)
5568 (while (setq dir (pop path))
5569 (if (setq recursive (string= (substring dir 0 1) "+"))
5570 (setq dir (substring dir 1)))
5571 (if (and recursive
5572 (not (file-name-absolute-p dir)))
5573 (setq dir (expand-file-name dir default-dir)))
5574 (if recursive
5575 ;; Expand recursively
5576 (setq path1 (append (idlwave-recursive-directory-list dir) path1))
5577 ;; Keep unchanged
5578 (push dir path1)))
5579 (message "Expanding path...done")
5580 (nreverse path1)))
5581
5582(defun idlwave-recursive-directory-list (dir)
5583 ;; Return a list of all directories below DIR, including DIR itself
5584 (let ((path (list dir)) path1 file files)
5585 (while (setq dir (pop path))
5586 (when (file-directory-p dir)
5587 (setq files (nreverse (directory-files dir t "[^.]")))
5588 (while (setq file (pop files))
4b1aaa8b 5589 (if (file-directory-p file)
f32b3b91
CD
5590 (push (file-name-as-directory file) path)))
5591 (push dir path1)))
5592 path1))
5593
52a244eb
S
5594
5595;;----- Scanning the library catalogs ------------------
5596
3938cb82
S
5597
5598
5599
52a244eb 5600(defun idlwave-scan-library-catalogs (&optional message-base no-load)
4b1aaa8b 5601 "Scan for library catalog files (.idlwave_catalog) and ingest.
52a244eb
S
5602
5603All directories on `idlwave-path-alist' (or `idlwave-library-path'
5604instead, if present) are searched. Print MESSAGE-BASE along with the
5605libraries being loaded, if passed, and skip loading/normalizing if
5606NO-LOAD is non-nil. The variable `idlwave-use-library-catalogs' can
5607be set to nil to disable library catalog scanning."
5608 (when idlwave-use-library-catalogs
4b1aaa8b 5609 (let ((dirs
52a244eb
S
5610 (if idlwave-library-path
5611 (idlwave-expand-path idlwave-library-path)
5612 (mapcar 'car idlwave-path-alist)))
5613 (old-libname "")
8d222148 5614 dir-entry dir catalog all-routines)
52a244eb
S
5615 (if message-base (message message-base))
5616 (while (setq dir (pop dirs))
5617 (catch 'continue
4b1aaa8b 5618 (when (file-readable-p
52a244eb
S
5619 (setq catalog (expand-file-name ".idlwave_catalog" dir)))
5620 (unless no-load
5621 (setq idlwave-library-catalog-routines nil)
5622 ;; Load the catalog file
5623 (condition-case nil
5624 (load catalog t t t)
5625 (error (throw 'continue t)))
4b1aaa8b
PE
5626 (when (and
5627 message-base
5628 (not (string= idlwave-library-catalog-libname
52a244eb 5629 old-libname)))
4b1aaa8b 5630 (message "%s" (concat message-base
f66f03de 5631 idlwave-library-catalog-libname))
52a244eb
S
5632 (setq old-libname idlwave-library-catalog-libname))
5633 (when idlwave-library-catalog-routines
5634 (setq all-routines
4b1aaa8b 5635 (append
52a244eb
S
5636 (idlwave-sintern-rinfo-list
5637 idlwave-library-catalog-routines 'sys dir)
5638 all-routines))))
4b1aaa8b 5639
52a244eb
S
5640 ;; Add a 'lib flag if on path-alist
5641 (when (and idlwave-path-alist
5642 (setq dir-entry (assoc dir idlwave-path-alist)))
5643 (idlwave-path-alist-add-flag dir-entry 'lib)))))
5644 (unless no-load (setq idlwave-library-catalog-routines all-routines))
5645 (if message-base (message (concat message-base "done"))))))
5646
5647;;----- Communicating with the Shell -------------------
f32b3b91
CD
5648
5649;; First, here is the idl program which can be used to query IDL for
4b1aaa8b 5650;; defined routines.
f32b3b91
CD
5651(defconst idlwave-routine-info.pro
5652 "
05a1abfc 5653;; START OF IDLWAVE SUPPORT ROUTINES
f66f03de
S
5654pro idlwave_print_safe,item,limit
5655 catch,err
5656 if err ne 0 then begin
5657 print,'Could not print item.'
5658 return
5659 endif
5660 if n_elements(item) gt limit then $
5661 print,item[0:limit-1],'<... truncated at ',strtrim(limit,2),' elements>' $
5662 else print,item
5663end
5664
15e42531 5665pro idlwave_print_info_entry,name,func=func,separator=sep
f32b3b91 5666 ;; See if it's an object method
15e42531 5667 if name eq '' then return
4b1aaa8b 5668 func = keyword_set(func)
f32b3b91
CD
5669 methsep = strpos(name,'::')
5670 meth = methsep ne -1
4b1aaa8b 5671
f32b3b91
CD
5672 ;; Get routine info
5673 pars = routine_info(name,/parameters,functions=func)
5674 source = routine_info(name,/source,functions=func)
5675 nargs = pars.num_args
5676 nkw = pars.num_kw_args
5677 if nargs gt 0 then args = pars.args
5678 if nkw gt 0 then kwargs = pars.kw_args
4b1aaa8b 5679
f32b3b91 5680 ;; Trim the class, and make the name
4b1aaa8b 5681 if meth then begin
f32b3b91
CD
5682 class = strmid(name,0,methsep)
5683 name = strmid(name,methsep+2,strlen(name)-1)
4b1aaa8b 5684 if nargs gt 0 then begin
f32b3b91
CD
5685 ;; remove the self argument
5686 wh = where(args ne 'SELF',nargs)
52a244eb 5687 if nargs gt 0 then args = args[wh]
f32b3b91
CD
5688 endif
5689 endif else begin
5690 ;; No class, just a normal routine.
5691 class = \"\"
5692 endelse
4b1aaa8b 5693
f32b3b91
CD
5694 ;; Calling sequence
5695 cs = \"\"
5696 if func then cs = 'Result = '
5697 if meth then cs = cs + 'Obj -> [' + '%s' + '::]'
5698 cs = cs + '%s'
5699 if func then cs = cs + '(' else if nargs gt 0 then cs = cs + ', '
5700 if nargs gt 0 then begin
5701 for j=0,nargs-1 do begin
52a244eb 5702 cs = cs + args[j]
f32b3b91
CD
5703 if j lt nargs-1 then cs = cs + ', '
5704 endfor
5705 end
5706 if func then cs = cs + ')'
5707 ;; Keyword arguments
5708 kwstring = ''
5709 if nkw gt 0 then begin
5710 for j=0,nkw-1 do begin
52a244eb 5711 kwstring = kwstring + ' ' + kwargs[j]
f32b3b91
CD
5712 endfor
5713 endif
4b1aaa8b 5714
52a244eb 5715 ret=(['IDLWAVE-PRO','IDLWAVE-FUN'])[func]
4b1aaa8b 5716
52a244eb 5717 print,ret + ': ' + name + sep + class + sep + source[0].path $
f32b3b91
CD
5718 + sep + cs + sep + kwstring
5719end
5720
f66f03de 5721pro idlwave_routine_info,file
52a244eb 5722 on_error,1
f32b3b91
CD
5723 sep = '<@>'
5724 print,'>>>BEGIN OF IDLWAVE ROUTINE INFO (\"' + sep + '\" IS THE SEPARATOR)'
5725 all = routine_info()
f66f03de
S
5726 fileQ=n_elements(file) ne 0
5727 if fileQ then file=strtrim(file,2)
4b1aaa8b
PE
5728 for i=0L,n_elements(all)-1L do begin
5729 if fileQ then begin
f66f03de
S
5730 if (routine_info(all[i],/SOURCE)).path eq file then $
5731 idlwave_print_info_entry,all[i],separator=sep
5732 endif else idlwave_print_info_entry,all[i],separator=sep
4b1aaa8b 5733 endfor
f32b3b91 5734 all = routine_info(/functions)
4b1aaa8b
PE
5735 for i=0L,n_elements(all)-1L do begin
5736 if fileQ then begin
f66f03de
S
5737 if (routine_info(all[i],/FUNCTIONS,/SOURCE)).path eq file then $
5738 idlwave_print_info_entry,all[i],separator=sep,/FUNC
5739 endif else idlwave_print_info_entry,all[i],separator=sep,/FUNC
4b1aaa8b 5740 endfor
f32b3b91
CD
5741 print,'>>>END OF IDLWAVE ROUTINE INFO'
5742end
05a1abfc
CD
5743
5744pro idlwave_get_sysvars
52a244eb 5745 on_error,1
05a1abfc
CD
5746 catch,error_status
5747 if error_status ne 0 then begin
5748 print, 'Cannot get info about system variables'
5749 endif else begin
5750 help,/brief,output=s,/system_variables ; ? unsafe use of OUTPUT=
5751 s = strtrim(strjoin(s,' ',/single),2) ; make one line
5752 v = strsplit(s,' +',/regex,/extract) ; get variables
f66f03de 5753 for i=0L,n_elements(v)-1 do begin
05a1abfc
CD
5754 t = [''] ; get tag list
5755 a=execute('if n_tags('+v[i]+') gt 0 then t=tag_names('+v[i]+')')
5756 print, 'IDLWAVE-SYSVAR: '+v[i]+' '+strjoin(t,' ',/single)
5757 endfor
5758 endelse
5759end
5760
5761pro idlwave_get_class_tags, class
5762 res = execute('tags=tag_names({'+class+'})')
5e72c6b2 5763 if res then print,'IDLWAVE-CLASS-TAGS: '+class+' '+strjoin(tags,' ',/single)
05a1abfc
CD
5764end
5765;; END OF IDLWAVE SUPPORT ROUTINES
4b1aaa8b 5766"
5a0c3f56 5767 "The IDL programs to get info from the shell.")
f32b3b91 5768
15e42531 5769(defvar idlwave-idlwave_routine_info-compiled nil
5a0c3f56 5770 "Remember if the routine info procedure is already compiled.")
f32b3b91
CD
5771
5772(defvar idlwave-shell-temp-pro-file)
15e42531 5773(defvar idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5774
5775(defun idlwave-shell-compile-helper-routines (&optional wait)
15e42531 5776 (unless (and idlwave-idlwave_routine_info-compiled
5e72c6b2 5777 (file-readable-p (idlwave-shell-temp-file 'rinfo)))
9a529312
SM
5778 (with-current-buffer (idlwave-find-file-noselect
5779 (idlwave-shell-temp-file 'pro))
15e42531
CD
5780 (erase-buffer)
5781 (insert idlwave-routine-info.pro)
5782 (save-buffer 0))
4b1aaa8b 5783 (idlwave-shell-send-command
f66f03de 5784 (concat ".run \"" idlwave-shell-temp-pro-file "\"")
52a244eb 5785 nil 'hide wait)
15e42531 5786 (idlwave-shell-send-command
4b1aaa8b 5787 (format "save,'idlwave_print_safe','idlwave_routine_info','idlwave_print_info_entry','idlwave_get_class_tags','idlwave_get_sysvars',FILE='%s',/ROUTINES"
5e72c6b2 5788 (idlwave-shell-temp-file 'rinfo))
f66f03de
S
5789 nil 'hide)
5790 (setq idlwave-idlwave_routine_info-compiled t))
15e42531 5791
f66f03de
S
5792 ;; Restore if necessary. Must use execute to hide lame routine_info
5793 ;; errors on undefinded routine
15e42531 5794 (idlwave-shell-send-command
f66f03de
S
5795 (format "if execute(\"_v=routine_info('idlwave_routine_info',/SOURCE)\") eq 0 then restore,'%s' else if _v.path eq '' then restore,'%s'"
5796 idlwave-shell-temp-rinfo-save-file
15e42531 5797 idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5798 nil 'hide))
5799
5800
5801(defun idlwave-shell-update-routine-info (&optional quiet run-hooks wait file)
5802 "Query the shell for routine_info of compiled modules and update the lists."
5803 ;; Save and compile the procedure. The compiled procedure is then
5804 ;; saved into an IDL SAVE file, to allow for fast RESTORE. We may
5805 ;; need to test for and possibly RESTORE the procedure each time we
5806 ;; use it, since the user may have killed or redefined it. In
5807 ;; particular, .RESET_SESSION will kill all user procedures. If
5808 ;; FILE is set, only update routine info for routines in that file.
5809
5810 (idlwave-shell-compile-helper-routines wait)
5811 ; execute the routine_info procedure, and analyze the output
5812 (idlwave-shell-send-command
5813 (format "idlwave_routine_info%s" (if file (concat ",'" file "'") ""))
15e42531
CD
5814 `(progn
5815 (idlwave-shell-routine-info-filter)
05a1abfc 5816 (idlwave-concatenate-rinfo-lists ,quiet ,run-hooks))
52a244eb 5817 'hide wait))
f32b3b91
CD
5818
5819;; ---------------------------------------------------------------------------
5820;;
5821;; Completion and displaying routine calling sequences
5822
15e42531 5823(defvar idlwave-completion-help-info nil)
52a244eb 5824(defvar idlwave-completion-help-links nil)
15e42531 5825(defvar idlwave-current-obj_new-class nil)
05a1abfc 5826(defvar idlwave-complete-special nil)
8d222148
SM
5827(defvar method-selector)
5828(defvar class-selector)
5829(defvar type-selector)
5830(defvar super-classes)
15e42531 5831
f32b3b91
CD
5832(defun idlwave-complete (&optional arg module class)
5833 "Complete a function, procedure or keyword name at point.
2e8b9c7d 5834This function is smart and figures out what can be completed
f32b3b91
CD
5835at this point.
5836- At the beginning of a statement it completes procedure names.
5837- In the middle of a statement it completes function names.
5a0c3f56 5838- After a `(' or `,' in the argument list of a function or procedure,
f32b3b91
CD
5839 it completes a keyword of the relevant function or procedure.
5840- In the first arg of `OBJ_NEW', it completes a class name.
5841
5a0c3f56
JB
5842When several completions are possible, a list will be displayed in
5843the *Completions* buffer. If this list is too long to fit into the
5e72c6b2
S
5844window, scrolling can be achieved by repeatedly pressing
5845\\[idlwave-complete].
f32b3b91
CD
5846
5847The function also knows about object methods. When it needs a class
5848name, the action depends upon `idlwave-query-class', which see. You
5e72c6b2
S
5849can force IDLWAVE to ask you for a class name with a
5850\\[universal-argument] prefix argument to this command.
f32b3b91
CD
5851
5852See also the variables `idlwave-keyword-completion-adds-equal' and
5853`idlwave-function-completion-adds-paren'.
5854
5855The optional ARG can be used to specify the completion type in order
5856to override IDLWAVE's idea of what should be completed at point.
5857Possible values are:
5858
58590 <=> query for the completion type
58601 <=> 'procedure
58612 <=> 'procedure-keyword
58623 <=> 'function
58634 <=> 'function-keyword
58645 <=> 'procedure-method
58656 <=> 'procedure-method-keyword
58667 <=> 'function-method
58678 <=> 'function-method-keyword
58689 <=> 'class
5869
5e72c6b2
S
5870As a special case, the universal argument C-u forces completion of
5871function names in places where the default would be a keyword.
5872
52a244eb
S
5873Two prefix argument, C-u C-u, prompts for a regexp by which to limit
5874completion.
5875
f32b3b91
CD
5876For Lisp programmers only:
5877When we force a keyword, optional argument MODULE can contain the module name.
5878When we force a method or a method keyword, CLASS can specify the class."
5879 (interactive "P")
5880 (idlwave-routines)
5881 (let* ((where-list
5882 (if (and arg
52a244eb 5883 (or (and (integerp arg) (not (equal arg '(16))))
f32b3b91
CD
5884 (symbolp arg)))
5885 (idlwave-make-force-complete-where-list arg module class)
5886 (idlwave-where)))
5887 (what (nth 2 where-list))
52a244eb
S
5888 (idlwave-force-class-query (equal arg '(4)))
5889 (completion-regexp-list
5890 (if (equal arg '(16))
5891 (list (read-string (concat "Completion Regexp: "))))))
4b1aaa8b 5892
f32b3b91
CD
5893 (if (and module (string-match "::" module))
5894 (setq class (substring module 0 (match-beginning 0))
5895 module (substring module (match-end 0))))
5896
5897 (cond
5898
5899 ((and (null arg)
5900 (eq (car-safe last-command) 'idlwave-display-completion-list)
595ab50b 5901 (get-buffer-window "*Completions*"))
f32b3b91
CD
5902 (setq this-command last-command)
5903 (idlwave-scroll-completions))
5904
52a244eb 5905 ;; Complete a filename in quotes
05a1abfc
CD
5906 ((and (idlwave-in-quote)
5907 (not (eq what 'class)))
5908 (idlwave-complete-filename))
5909
52a244eb
S
5910 ;; Check for any special completion functions
5911 ((and idlwave-complete-special
5912 (idlwave-call-special idlwave-complete-special)))
4b1aaa8b 5913
f32b3b91
CD
5914 ((null what)
5915 (error "Nothing to complete here"))
5916
52a244eb 5917 ;; Complete a class
f32b3b91 5918 ((eq what 'class)
15e42531 5919 (setq idlwave-completion-help-info '(class))
f32b3b91
CD
5920 (idlwave-complete-class))
5921
5922 ((eq what 'procedure)
5923 ;; Complete a procedure name
5e72c6b2
S
5924 (let* ((cw-list (nth 3 where-list))
5925 (class-selector (idlwave-determine-class cw-list 'pro))
5926 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5927 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5928 (isa (concat "procedure" (if class-selector "-method" "")))
5929 (type-selector 'pro))
4b1aaa8b 5930 (setq idlwave-completion-help-info
05a1abfc 5931 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5932 (idlwave-complete-in-buffer
5933 'procedure (if class-selector 'method 'routine)
5934 (idlwave-routines) 'idlwave-selector
5935 (format "Select a %s name%s"
5936 isa
5937 (if class-selector
4b1aaa8b
PE
5938 (format " (class is %s)"
5939 (if (eq class-selector t)
76959b77 5940 "unknown" class-selector))
f32b3b91
CD
5941 ""))
5942 isa
52a244eb 5943 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91
CD
5944
5945 ((eq what 'function)
5946 ;; Complete a function name
5e72c6b2
S
5947 (let* ((cw-list (nth 3 where-list))
5948 (class-selector (idlwave-determine-class cw-list 'fun))
5949 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5950 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5951 (isa (concat "function" (if class-selector "-method" "")))
5952 (type-selector 'fun))
4b1aaa8b 5953 (setq idlwave-completion-help-info
05a1abfc 5954 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5955 (idlwave-complete-in-buffer
5956 'function (if class-selector 'method 'routine)
5957 (idlwave-routines) 'idlwave-selector
5958 (format "Select a %s name%s"
5959 isa
5960 (if class-selector
4b1aaa8b 5961 (format " (class is %s)"
76959b77
S
5962 (if (eq class-selector t)
5963 "unknown" class-selector))
f32b3b91
CD
5964 ""))
5965 isa
52a244eb 5966 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91 5967
52a244eb 5968 ((and (memq what '(procedure-keyword function-keyword)) ; Special Case
5e72c6b2
S
5969 (equal arg '(4)))
5970 (idlwave-complete 3))
5971
f32b3b91
CD
5972 ((eq what 'procedure-keyword)
5973 ;; Complete a procedure keyword
5974 (let* ((where (nth 3 where-list))
5975 (name (car where))
5976 (method-selector name)
5977 (type-selector 'pro)
5978 (class (idlwave-determine-class where 'pro))
5979 (class-selector class)
05a1abfc 5980 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 5981 (isa (format "procedure%s-keyword" (if class "-method" "")))
15e42531 5982 (entry (idlwave-best-rinfo-assq
f32b3b91 5983 name 'pro class (idlwave-routines)))
3938cb82 5984 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 5985 (list (idlwave-entry-keywords entry 'do-link)))
f32b3b91
CD
5986 (unless (or entry (eq class t))
5987 (error "Nothing known about procedure %s"
5988 (idlwave-make-full-name class name)))
4b1aaa8b 5989 (setq list (idlwave-fix-keywords name 'pro class list
3938cb82 5990 super-classes system))
b6a97790 5991 (unless list (error "No keywords available for procedure %s"
3938cb82 5992 (idlwave-make-full-name class name)))
4b1aaa8b 5993 (setq idlwave-completion-help-info
52a244eb 5994 (list 'keyword name type-selector class-selector entry super-classes))
f32b3b91
CD
5995 (idlwave-complete-in-buffer
5996 'keyword 'keyword list nil
5997 (format "Select keyword for procedure %s%s"
5998 (idlwave-make-full-name class name)
15e42531 5999 (if (or (member '("_EXTRA") list)
4b1aaa8b 6000 (member '("_REF_EXTRA") list))
15e42531 6001 " (note _EXTRA)" ""))
f32b3b91
CD
6002 isa
6003 'idlwave-attach-keyword-classes)))
6004
6005 ((eq what 'function-keyword)
6006 ;; Complete a function keyword
6007 (let* ((where (nth 3 where-list))
6008 (name (car where))
6009 (method-selector name)
6010 (type-selector 'fun)
6011 (class (idlwave-determine-class where 'fun))
6012 (class-selector class)
05a1abfc 6013 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 6014 (isa (format "function%s-keyword" (if class "-method" "")))
15e42531 6015 (entry (idlwave-best-rinfo-assq
f32b3b91 6016 name 'fun class (idlwave-routines)))
3938cb82 6017 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 6018 (list (idlwave-entry-keywords entry 'do-link))
15e42531 6019 msg-name)
f32b3b91
CD
6020 (unless (or entry (eq class t))
6021 (error "Nothing known about function %s"
6022 (idlwave-make-full-name class name)))
4b1aaa8b 6023 (setq list (idlwave-fix-keywords name 'fun class list
3938cb82 6024 super-classes system))
15e42531
CD
6025 ;; OBJ_NEW: Messages mention the proper Init method
6026 (setq msg-name (if (and (null class)
6027 (string= (upcase name) "OBJ_NEW"))
6028 (concat idlwave-current-obj_new-class
6029 "::Init (via OBJ_NEW)")
6030 (idlwave-make-full-name class name)))
b6a97790 6031 (unless list (error "No keywords available for function %s"
3938cb82 6032 msg-name))
4b1aaa8b 6033 (setq idlwave-completion-help-info
05a1abfc 6034 (list 'keyword name type-selector class-selector nil super-classes))
f32b3b91
CD
6035 (idlwave-complete-in-buffer
6036 'keyword 'keyword list nil
15e42531
CD
6037 (format "Select keyword for function %s%s" msg-name
6038 (if (or (member '("_EXTRA") list)
4b1aaa8b 6039 (member '("_REF_EXTRA") list))
15e42531 6040 " (note _EXTRA)" ""))
f32b3b91
CD
6041 isa
6042 'idlwave-attach-keyword-classes)))
15e42531 6043
f32b3b91
CD
6044 (t (error "This should not happen (idlwave-complete)")))))
6045
05a1abfc
CD
6046(defvar idlwave-complete-special nil
6047 "List of special completion functions.
52a244eb
S
6048These functions are called for each completion. Each function must
6049check if its own special completion context is present. If yes, it
6050should use `idlwave-complete-in-buffer' to do some completion and
6051return t. If such a function returns t, *no further* attempts to
6052complete other contexts will be done. If the function returns nil,
6053other completions will be tried.")
76959b77
S
6054
6055(defun idlwave-call-special (functions &rest args)
6056 (let ((funcs functions)
6057 fun ret)
05a1abfc 6058 (catch 'exit
76959b77
S
6059 (while (setq fun (pop funcs))
6060 (if (setq ret (apply fun args))
6061 (throw 'exit ret)))
05a1abfc
CD
6062 nil)))
6063
f32b3b91
CD
6064(defun idlwave-make-force-complete-where-list (what &optional module class)
6065 ;; Return an artificial WHERE specification to force the completion
6066 ;; routine to complete a specific item independent of context.
6067 ;; WHAT is the prefix arg of `idlwave-complete', see there for details.
6068 ;; MODULE and CLASS can be used to specify the routine name and class.
6069 ;; The class name will also be found in MODULE if that is like "class::mod".
6070 (let* ((what-list '(("procedure") ("procedure-keyword")
6071 ("function") ("function-keyword")
6072 ("procedure-method") ("procedure-method-keyword")
6073 ("function-method") ("function-method-keyword")
6074 ("class")))
6075 (module (idlwave-sintern-routine-or-method module class))
6076 (class (idlwave-sintern-class class))
4b1aaa8b 6077 (what (cond
f32b3b91
CD
6078 ((equal what 0)
6079 (setq what
4b1aaa8b 6080 (intern (completing-read
f32b3b91
CD
6081 "Complete what? " what-list nil t))))
6082 ((integerp what)
6083 (setq what (intern (car (nth (1- what) what-list)))))
6084 ((and what
6085 (symbolp what)
6086 (assoc (symbol-name what) what-list))
6087 what)
eac9c0ef 6088 (t (error "Invalid WHAT"))))
f32b3b91
CD
6089 (nil-list '(nil nil nil nil))
6090 (class-list (list nil nil (or class t) nil)))
6091
6092 (cond
6093
6094 ((eq what 'procedure)
6095 (list nil-list nil-list 'procedure nil-list nil))
6096
6097 ((eq what 'procedure-keyword)
6098 (let* ((class-selector nil)
05a1abfc 6099 (super-classes nil)
f32b3b91
CD
6100 (type-selector 'pro)
6101 (pro (or module
4b1aaa8b 6102 (idlwave-completing-read
f32b3b91
CD
6103 "Procedure: " (idlwave-routines) 'idlwave-selector))))
6104 (setq pro (idlwave-sintern-routine pro))
6105 (list nil-list nil-list 'procedure-keyword
6106 (list pro nil nil nil) nil)))
6107
6108 ((eq what 'function)
6109 (list nil-list nil-list 'function nil-list nil))
6110
6111 ((eq what 'function-keyword)
6112 (let* ((class-selector nil)
05a1abfc 6113 (super-classes nil)
f32b3b91
CD
6114 (type-selector 'fun)
6115 (func (or module
4b1aaa8b 6116 (idlwave-completing-read
f32b3b91
CD
6117 "Function: " (idlwave-routines) 'idlwave-selector))))
6118 (setq func (idlwave-sintern-routine func))
6119 (list nil-list nil-list 'function-keyword
6120 (list func nil nil nil) nil)))
6121
6122 ((eq what 'procedure-method)
6123 (list nil-list nil-list 'procedure class-list nil))
6124
6125 ((eq what 'procedure-method-keyword)
6126 (let* ((class (idlwave-determine-class class-list 'pro))
6127 (class-selector class)
05a1abfc 6128 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6129 (type-selector 'pro)
6130 (pro (or module
6131 (idlwave-completing-read
6132 (format "Procedure in %s class: " class-selector)
6133 (idlwave-routines) 'idlwave-selector))))
6134 (setq pro (idlwave-sintern-method pro))
6135 (list nil-list nil-list 'procedure-keyword
6136 (list pro nil class nil) nil)))
6137
6138 ((eq what 'function-method)
6139 (list nil-list nil-list 'function class-list nil))
6140
6141 ((eq what 'function-method-keyword)
6142 (let* ((class (idlwave-determine-class class-list 'fun))
6143 (class-selector class)
05a1abfc 6144 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6145 (type-selector 'fun)
6146 (func (or module
6147 (idlwave-completing-read
6148 (format "Function in %s class: " class-selector)
6149 (idlwave-routines) 'idlwave-selector))))
6150 (setq func (idlwave-sintern-method func))
6151 (list nil-list nil-list 'function-keyword
6152 (list func nil class nil) nil)))
6153
6154 ((eq what 'class)
6155 (list nil-list nil-list 'class nil-list nil))
4b1aaa8b 6156
eac9c0ef 6157 (t (error "Invalid value for WHAT")))))
f32b3b91
CD
6158
6159(defun idlwave-completing-read (&rest args)
6160 ;; Completing read, case insensitive
6161 (let ((old-value (default-value 'completion-ignore-case)))
6162 (unwind-protect
6163 (progn
6164 (setq-default completion-ignore-case t)
6165 (apply 'completing-read args))
6166 (setq-default completion-ignore-case old-value))))
6167
05a1abfc
CD
6168(defvar idlwave-shell-default-directory)
6169(defun idlwave-complete-filename ()
6170 "Use the comint stuff to complete a file name."
6171 (require 'comint)
6172 (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-")
6173 (comint-completion-addsuffix nil)
6174 (default-directory
6175 (if (and (boundp 'idlwave-shell-default-directory)
6176 (stringp idlwave-shell-default-directory)
6177 (file-directory-p idlwave-shell-default-directory))
6178 idlwave-shell-default-directory
4b1aaa8b 6179 default-directory)))
05a1abfc
CD
6180 (comint-dynamic-complete-filename)))
6181
f32b3b91
CD
6182(defun idlwave-make-full-name (class name)
6183 ;; Make a fully qualified module name including the class name
6184 (concat (if class (format "%s::" class) "") name))
6185
15e42531
CD
6186(defun idlwave-rinfo-assoc (name type class list)
6187 "Like `idlwave-rinfo-assq', but sintern strings first."
4b1aaa8b 6188 (idlwave-rinfo-assq
15e42531
CD
6189 (idlwave-sintern-routine-or-method name class)
6190 type (idlwave-sintern-class class) list))
6191
f32b3b91
CD
6192(defun idlwave-rinfo-assq (name type class list)
6193 ;; Works like assq, but also checks type and class
6194 (catch 'exit
6195 (let (match)
6196 (while (setq match (assq name list))
6197 (and (or (eq type t)
6198 (eq (nth 1 match) type))
6199 (eq (nth 2 match) class)
6200 (throw 'exit match))
6201 (setq list (cdr (memq match list)))))))
6202
05a1abfc 6203(defun idlwave-rinfo-assq-any-class (name type class list)
52a244eb 6204 ;; Return the first matching method on the inheritance list
05a1abfc
CD
6205 (let* ((classes (cons class (idlwave-all-class-inherits class)))
6206 class rtn)
6207 (while classes
6208 (if (setq rtn (idlwave-rinfo-assq name type (pop classes) list))
6209 (setq classes nil)))
6210 rtn))
6211
4b1aaa8b 6212(defun idlwave-best-rinfo-assq (name type class list &optional with-file
52a244eb
S
6213 keep-system)
6214 "Like `idlwave-rinfo-assq', but get all twins and sort, then return first.
6215If WITH-FILE is passed, find the best rinfo entry with a file
6216included. If KEEP-SYSTEM is set, don't prune system for compiled
6217syslib files."
15e42531 6218 (let ((twins (idlwave-routine-twins
05a1abfc 6219 (idlwave-rinfo-assq-any-class name type class list)
15e42531
CD
6220 list))
6221 syslibp)
6222 (when (> (length twins) 1)
6223 (setq twins (sort twins 'idlwave-routine-entry-compare-twins))
52a244eb
S
6224 (if (and (null keep-system)
6225 (eq 'system (car (nth 3 (car twins))))
15e42531
CD
6226 (setq syslibp (idlwave-any-syslib (cdr twins)))
6227 (not (equal 1 syslibp)))
52a244eb
S
6228 ;; Its a compiled syslib, so we need to remove the system entry
6229 (setq twins (cdr twins)))
6230 (if with-file
6231 (setq twins (delq nil
6232 (mapcar (lambda (x)
6233 (if (nth 1 (nth 3 x)) x))
6234 twins)))))
15e42531
CD
6235 (car twins)))
6236
4b1aaa8b 6237(defun idlwave-best-rinfo-assoc (name type class list &optional with-file
52a244eb 6238 keep-system)
15e42531
CD
6239 "Like `idlwave-best-rinfo-assq', but sintern strings first."
6240 (idlwave-best-rinfo-assq
6241 (idlwave-sintern-routine-or-method name class)
52a244eb 6242 type (idlwave-sintern-class class) list with-file keep-system))
15e42531
CD
6243
6244(defun idlwave-any-syslib (entries)
6245 "Does the entry list ENTRIES contain a syslib entry?
6246If yes, return the index (>=1)."
6247 (let (file (cnt 0))
6248 (catch 'exit
6249 (while entries
6250 (incf cnt)
52a244eb
S
6251 (setq file (idlwave-routine-source-file (nth 3 (car entries))))
6252 (if (and file (idlwave-syslib-p file))
15e42531
CD
6253 (throw 'exit cnt)
6254 (setq entries (cdr entries))))
6255 nil)))
6256
f32b3b91
CD
6257(defun idlwave-all-assq (key list)
6258 "Return a list of all associations of Key in LIST."
6259 (let (rtn elt)
6260 (while (setq elt (assq key list))
6261 (push elt rtn)
6262 (setq list (cdr (memq elt list))))
6263 (nreverse rtn)))
6264
6265(defun idlwave-all-method-classes (method &optional type)
5a0c3f56
JB
6266 "Return all classes which have a method METHOD.
6267TYPE is 'fun or 'pro.
f32b3b91
CD
6268When TYPE is not specified, both procedures and functions will be considered."
6269 (if (null method)
15e42531 6270 (mapcar 'car (idlwave-class-alist))
f32b3b91 6271 (let (rtn)
8ffcfb27
GM
6272 (mapc (lambda (x)
6273 (and (nth 2 x)
6274 (or (not type)
6275 (eq type (nth 1 x)))
6276 (push (nth 2 x) rtn)))
6277 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6278 (idlwave-uniquify rtn))))
6279
6280(defun idlwave-all-method-keyword-classes (method keyword &optional type)
6281 "Return all classes which have a method METHOD with keyword KEYWORD.
6282TYPE is 'fun or 'pro.
6283When TYPE is not specified, both procedures and functions will be considered."
6284 (if (or (null method)
6285 (null keyword))
6286 nil
6287 (let (rtn)
8ffcfb27
GM
6288 (mapc (lambda (x)
6289 (and (nth 2 x) ; non-nil class
6290 (or (not type) ; correct or unspecified type
6291 (eq type (nth 1 x)))
6292 (assoc keyword (idlwave-entry-keywords x))
6293 (push (nth 2 x) rtn)))
6294 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6295 (idlwave-uniquify rtn))))
6296
05a1abfc
CD
6297(defun idlwave-members-only (list club)
6298 "Return list of all elements in LIST which are also in CLUB."
6299 (let (rtn)
6300 (while list
6301 (if (member (car list) club)
6302 (setq rtn (cons (car list) rtn)))
6303 (setq list (cdr list)))
6304 (nreverse rtn)))
6305
6306(defun idlwave-nonmembers-only (list club)
6307 "Return list of all elements in LIST which are not in CLUB."
6308 (let (rtn)
6309 (while list
6310 (if (member (car list) club)
6311 nil
6312 (setq rtn (cons (car list) rtn)))
6313 (setq list (cdr list)))
6314 (nreverse rtn)))
6315
5e72c6b2
S
6316(defun idlwave-explicit-class-listed (info)
6317 "Return whether or not the class is listed explicitly, ala a->b::c.
5a0c3f56 6318INFO is as returned by `idlwave-what-function' or `-procedure'."
5e72c6b2
S
6319 (let ((apos (nth 3 info)))
6320 (if apos
6321 (save-excursion (goto-char apos)
6322 (looking-at "->[a-zA-Z][a-zA-Z0-9$_]*::")))))
6323
76959b77
S
6324(defvar idlwave-determine-class-special nil
6325 "List of special functions for determining class.
5a0c3f56 6326Must accept two arguments: `apos' and `info'.")
76959b77 6327
f32b3b91 6328(defun idlwave-determine-class (info type)
4b1aaa8b 6329 ;; Determine the class of a routine call.
76959b77
S
6330 ;; INFO is the `cw-list' structure as returned by idlwave-where.
6331 ;; The second element in this structure is the class. When nil, we
6332 ;; return nil. When t, try to get the class from text properties at
6333 ;; the arrow. When the object is "self", we use the class of the
6334 ;; current routine. otherwise prompt the user for a class name.
6335 ;; Also stores the selected class as a text property at the arrow.
f32b3b91
CD
6336 ;; TYPE is 'fun or 'pro.
6337 (let* ((class (nth 2 info))
6338 (apos (nth 3 info))
6339 (nassoc (assoc (if (stringp (car info))
6340 (upcase (car info))
6341 (car info))
6342 idlwave-query-class))
6343 (dassoc (assq (if (car info) 'keyword-default 'method-default)
6344 idlwave-query-class))
6345 (query (cond (nassoc (cdr nassoc))
6346 (dassoc (cdr dassoc))
6347 (t t)))
6348 (arrow (and apos (string= (buffer-substring apos (+ 2 apos)) "->")))
4b1aaa8b 6349 (is-self
15e42531
CD
6350 (and arrow
6351 (save-excursion (goto-char apos)
6352 (forward-word -1)
6353 (let ((case-fold-search t))
6354 (looking-at "self\\>")))))
f32b3b91 6355 (force-query idlwave-force-class-query)
76959b77 6356 store special-class class-alist)
f32b3b91
CD
6357 (cond
6358 ((null class) nil)
6359 ((eq t class)
6360 ;; There is an object which would like to know its class
6361 (if (and arrow (get-text-property apos 'idlwave-class)
6362 idlwave-store-inquired-class
6363 (not force-query))
6364 (setq class (get-text-property apos 'idlwave-class)
6365 class (idlwave-sintern-class class)))
76959b77
S
6366 (if (and (eq t class) is-self)
6367 (setq class (or (nth 2 (idlwave-current-routine)) class)))
6368
6369 ;; Before prompting, try any special class determination routines
4b1aaa8b 6370 (when (and (eq t class)
76959b77
S
6371 idlwave-determine-class-special
6372 (not force-query))
4b1aaa8b 6373 (setq special-class
76959b77 6374 (idlwave-call-special idlwave-determine-class-special apos))
4b1aaa8b 6375 (if special-class
76959b77
S
6376 (setq class (idlwave-sintern-class special-class)
6377 store idlwave-store-inquired-class)))
4b1aaa8b 6378
76959b77 6379 ;; Prompt for a class, if we need to
f32b3b91
CD
6380 (when (and (eq class t)
6381 (or force-query query))
4b1aaa8b 6382 (setq class-alist
f32b3b91
CD
6383 (mapcar 'list (idlwave-all-method-classes (car info) type)))
6384 (setq class
6385 (idlwave-sintern-class
6386 (cond
6387 ((and (= (length class-alist) 0) (not force-query))
6388 (error "No classes available with method %s" (car info)))
6389 ((and (= (length class-alist) 1) (not force-query))
6390 (car (car class-alist)))
4b1aaa8b 6391 (t
f32b3b91 6392 (setq store idlwave-store-inquired-class)
4b1aaa8b 6393 (idlwave-completing-read
f32b3b91
CD
6394 (format "Class%s: " (if (stringp (car info))
6395 (format " for %s method %s"
6396 type (car info))
6397 ""))
6398 class-alist nil nil nil 'idlwave-class-history))))))
76959b77
S
6399
6400 ;; Store it, if requested
f32b3b91
CD
6401 (when (and class (not (eq t class)))
6402 ;; We have a real class here
6403 (when (and store arrow)
76959b77 6404 (condition-case ()
4b1aaa8b
PE
6405 (add-text-properties
6406 apos (+ apos 2)
6407 `(idlwave-class ,class face ,idlwave-class-arrow-face
76959b77
S
6408 rear-nonsticky t))
6409 (error nil)))
f32b3b91
CD
6410 (setf (nth 2 info) class))
6411 ;; Return the class
6412 class)
6413 ;; Default as fallback
6414 (t class))))
6415
f32b3b91
CD
6416(defun idlwave-selector (a)
6417 (and (eq (nth 1 a) type-selector)
6418 (or (and (nth 2 a) (eq class-selector t))
05a1abfc 6419 (eq (nth 2 a) class-selector)
52a244eb
S
6420 (memq (nth 2 a) super-classes))))
6421
6422(defun idlwave-add-file-link-selector (a)
6423 ;; Record a file link, if any, for the tested names during selection.
6424 (let ((sel (idlwave-selector a)) file)
6425 (if (and sel (setq file (idlwave-entry-has-help a)))
6426 (push (cons (car a) file) idlwave-completion-help-links))
6427 sel))
6428
f32b3b91
CD
6429
6430(defun idlwave-where ()
4b1aaa8b 6431 "Find out where we are.
f32b3b91 6432The return value is a list with the following stuff:
5e72c6b2 6433\(PRO-LIST FUNC-LIST COMPLETE-WHAT CW-LIST LAST-CHAR)
f32b3b91
CD
6434
6435PRO-LIST (PRO POINT CLASS ARROW)
6436FUNC-LIST (FUNC POINT CLASS ARROW)
6437COMPLETE-WHAT a symbol indicating what kind of completion makes sense here
4b1aaa8b 6438CW-LIST (PRO-OR-FUNC POINT CLASS ARROW) Like PRO-LIST, for what can
5e72c6b2 6439 be completed here.
f32b3b91
CD
6440LAST-CHAR last relevant character before point (non-white non-comment,
6441 not part of current identifier or leading slash).
6442
6443In the lists, we have these meanings:
6444PRO: Procedure name
6445FUNC: Function name
6446POINT: Where is this
6447CLASS: What class has the routine (nil=no, t=is method, but class unknown)
5e72c6b2 6448ARROW: Location of the arrow"
f32b3b91 6449 (idlwave-routines)
4b1aaa8b 6450 (let* (;(bos (save-excursion (idlwave-beginning-of-statement) (point)))
15e42531 6451 (bos (save-excursion (idlwave-start-of-substatement 'pre) (point)))
f32b3b91
CD
6452 (func-entry (idlwave-what-function bos))
6453 (func (car func-entry))
6454 (func-class (nth 1 func-entry))
6455 (func-arrow (nth 2 func-entry))
6456 (func-point (or (nth 3 func-entry) 0))
6457 (func-level (or (nth 4 func-entry) 0))
6458 (pro-entry (idlwave-what-procedure bos))
6459 (pro (car pro-entry))
6460 (pro-class (nth 1 pro-entry))
6461 (pro-arrow (nth 2 pro-entry))
6462 (pro-point (or (nth 3 pro-entry) 0))
6463 (last-char (idlwave-last-valid-char))
6464 (case-fold-search t)
52a244eb 6465 (match-string (buffer-substring bos (point)))
f32b3b91
CD
6466 cw cw-mod cw-arrow cw-class cw-point)
6467 (if (< func-point pro-point) (setq func nil))
6468 (cond
15e42531 6469 ((string-match "\\`[ \t]*\\(pro\\|function\\)[ \t]+[a-zA-Z0-9_]*\\'"
52a244eb 6470 match-string)
15e42531 6471 (setq cw 'class))
4b1aaa8b
PE
6472 ((string-match
6473 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'"
52a244eb
S
6474 (if (> pro-point 0)
6475 (buffer-substring pro-point (point))
6476 match-string))
f32b3b91
CD
6477 (setq cw 'procedure cw-class pro-class cw-point pro-point
6478 cw-arrow pro-arrow))
6479 ((string-match "\\`[ \t]*\\(pro\\|function\\)\\>"
52a244eb 6480 match-string)
f32b3b91 6481 nil)
05a1abfc 6482 ((string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6483 match-string)
4b1aaa8b 6484 (setq cw 'class))
05a1abfc 6485 ((string-match "\\<inherits\\s-+\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6486 match-string)
4b1aaa8b
PE
6487 (setq cw 'class))
6488 ((and func
f32b3b91
CD
6489 (> func-point pro-point)
6490 (= func-level 1)
6491 (memq last-char '(?\( ?,)))
6492 (setq cw 'function-keyword cw-mod func cw-point func-point
6493 cw-class func-class cw-arrow func-arrow))
6494 ((and pro (eq last-char ?,))
6495 (setq cw 'procedure-keyword cw-mod pro cw-point pro-point
6496 cw-class pro-class cw-arrow pro-arrow))
6497; ((member last-char '(?\' ?\) ?\] ?!))
6498; ;; after these chars, a function makes no sense
6499; ;; FIXME: I am sure there can be more in this list
6500; ;; FIXME: Do we want to do this at all?
6501; nil)
6502 ;; Everywhere else we try a function.
6503 (t
6504 (setq cw 'function)
6505 (save-excursion
52a244eb 6506 (if (re-search-backward "->[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\s-*\\)?\\(\\([$a-zA-Z0-9_]+\\)::\\)?[$a-zA-Z0-9_]*\\=" bos t)
76959b77 6507 (setq cw-arrow (copy-marker (match-beginning 0))
52a244eb
S
6508 cw-class (if (match-end 4)
6509 (idlwave-sintern-class (match-string 4))
5e72c6b2 6510 t))))))
f32b3b91
CD
6511 (list (list pro pro-point pro-class pro-arrow)
6512 (list func func-point func-class func-arrow)
6513 cw
6514 (list cw-mod cw-point cw-class cw-arrow)
6515 last-char)))
6516
6517(defun idlwave-this-word (&optional class)
6518 ;; Grab the word around point. CLASS is for the `skip-chars=...' functions
52a244eb 6519 (setq class (or class "a-zA-Z0-9$_."))
f32b3b91 6520 (save-excursion
52a244eb 6521 (buffer-substring
f32b3b91
CD
6522 (progn (skip-chars-backward class) (point))
6523 (progn (skip-chars-forward class) (point)))))
6524
f32b3b91
CD
6525(defun idlwave-what-function (&optional bound)
6526 ;; Find out if point is within the argument list of a function.
76959b77
S
6527 ;; The return value is ("function-name" class arrow-start (point) level).
6528 ;; Level is 1 on the top level parentheses, higher further down.
f32b3b91
CD
6529
6530 ;; If the optional BOUND is an integer, bound backwards directed
6531 ;; searches to this point.
6532
6533 (catch 'exit
4b1aaa8b 6534 (let (pos
f32b3b91 6535 func-point
f32b3b91
CD
6536 (cnt 0)
6537 func arrow-start class)
15e42531
CD
6538 (idlwave-with-special-syntax
6539 (save-restriction
6540 (save-excursion
6541 (narrow-to-region (max 1 (or bound 0)) (point-max))
6542 ;; move back out of the current parenthesis
6543 (while (condition-case nil
6544 (progn (up-list -1) t)
6545 (error nil))
6546 (setq pos (point))
6547 (incf cnt)
6548 (when (and (= (following-char) ?\()
4b1aaa8b 6549 (re-search-backward
15e42531
CD
6550 "\\(::\\|\\<\\)\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\="
6551 bound t))
6552 (setq func (match-string 2)
6553 func-point (goto-char (match-beginning 2))
6554 pos func-point)
4b1aaa8b 6555 (if (re-search-backward
15e42531 6556 "->[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\=" bound t)
76959b77 6557 (setq arrow-start (copy-marker (match-beginning 0))
15e42531 6558 class (or (match-string 2) t)))
4b1aaa8b
PE
6559 (throw
6560 'exit
15e42531
CD
6561 (list
6562 (idlwave-sintern-routine-or-method func class)
6563 (idlwave-sintern-class class)
6564 arrow-start func-point cnt)))
6565 (goto-char pos))
6566 (throw 'exit nil)))))))
f32b3b91
CD
6567
6568(defun idlwave-what-procedure (&optional bound)
6569 ;; Find out if point is within the argument list of a procedure.
6570 ;; The return value is ("procedure-name" class arrow-pos (point)).
6571
6572 ;; If the optional BOUND is an integer, bound backwards directed
6573 ;; searches to this point.
6574 (let ((pos (point)) pro-point
6575 pro class arrow-start string)
4b1aaa8b 6576 (save-excursion
05a1abfc 6577 ;;(idlwave-beginning-of-statement)
15e42531 6578 (idlwave-start-of-substatement 'pre)
f32b3b91 6579 (setq string (buffer-substring (point) pos))
4b1aaa8b 6580 (if (string-match
76959b77
S
6581 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\(,\\|\\'\\)" string)
6582 (setq pro (match-string 1 string)
6583 pro-point (+ (point) (match-beginning 1)))
f32b3b91
CD
6584 (if (and (idlwave-skip-object)
6585 (setq string (buffer-substring (point) pos))
4b1aaa8b
PE
6586 (string-match
6587 "\\`[ \t]*\\(->\\)[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\([a-zA-Z][a-zA-Z0-9$_]*\\)?[ \t]*\\(,\\|\\(\\$\\s *\\(;.*\\)?\\)?$\\)"
52a244eb 6588 string))
f32b3b91
CD
6589 (setq pro (if (match-beginning 4)
6590 (match-string 4 string))
6591 pro-point (if (match-beginning 4)
6592 (+ (point) (match-beginning 4))
6593 pos)
76959b77 6594 arrow-start (copy-marker (+ (point) (match-beginning 1)))
f32b3b91
CD
6595 class (or (match-string 3 string) t)))))
6596 (list (idlwave-sintern-routine-or-method pro class)
6597 (idlwave-sintern-class class)
6598 arrow-start
6599 pro-point)))
6600
6601(defun idlwave-skip-object ()
6602 ;; If there is an object at point, move over it and return t.
6603 (let ((pos (point)))
6604 (if (catch 'exit
6605 (save-excursion
6606 (skip-chars-forward " ") ; white space
6607 (skip-chars-forward "*") ; de-reference
6608 (cond
6609 ((looking-at idlwave-identifier)
6610 (goto-char (match-end 0)))
6611 ((eq (following-char) ?\()
6612 nil)
6613 (t (throw 'exit nil)))
6614 (catch 'endwhile
6615 (while t
6616 (cond ((eq (following-char) ?.)
6617 (forward-char 1)
6618 (if (not (looking-at idlwave-identifier))
6619 (throw 'exit nil))
6620 (goto-char (match-end 0)))
6621 ((memq (following-char) '(?\( ?\[))
6622 (condition-case nil
6623 (forward-list 1)
6624 (error (throw 'exit nil))))
6625 (t (throw 'endwhile t)))))
6626 (if (looking-at "[ \t]*->")
6627 (throw 'exit (setq pos (match-beginning 0)))
6628 (throw 'exit nil))))
6629 (goto-char pos)
6630 nil)))
4b1aaa8b 6631
f32b3b91
CD
6632(defun idlwave-last-valid-char ()
6633 "Return the last character before point which is not white or a comment
6634and also not part of the current identifier. Since we do this in
6635order to identify places where keywords are, we consider the initial
6636`/' of a keyword as part of the identifier.
6637This function is not general, can only be used for completion stuff."
6638 (catch 'exit
6639 (save-excursion
6640 ;; skip the current identifier
6641 (skip-chars-backward "a-zA-Z0-9_$")
6642 ;; also skip a leading slash which might be belong to the keyword
6643 (if (eq (preceding-char) ?/)
6644 (backward-char 1))
6645 ;; FIXME: does not check if this is a valid identifier
6646 (while t
6647 (skip-chars-backward " \t")
6648 (cond
6649 ((memq (preceding-char) '(?\; ?\$)) (throw 'exit nil))
6650 ((eq (preceding-char) ?\n)
6651 (beginning-of-line 0)
3938cb82 6652 (if (looking-at "\\([^\n]*\\)\\$[ \t]*\\(;[^\n]*\\)?\n")
f32b3b91
CD
6653 ;; continuation line
6654 (goto-char (match-end 1))
6655 (throw 'exit nil)))
6656 (t (throw 'exit (preceding-char))))))))
6657
6658(defvar idlwave-complete-after-success-form nil
6659 "A form to evaluate after successful completion.")
6660(defvar idlwave-complete-after-success-form-force nil
6661 "A form to evaluate after completion selection in *Completions* buffer.")
6662(defconst idlwave-completion-mark (make-marker)
6663 "A mark pointing to the beginning of the completion string.")
8d222148 6664(defvar completion-highlight-first-word-only) ;XEmacs.
f32b3b91
CD
6665
6666(defun idlwave-complete-in-buffer (type stype list selector prompt isa
52a244eb
S
6667 &optional prepare-display-function
6668 special-selector)
f32b3b91 6669 "Perform TYPE completion of word before point against LIST.
76959b77 6670SELECTOR is the PREDICATE argument for the completion function. Show
52a244eb 6671PROMPT in echo area. TYPE is one of the intern types, e.g. 'function,
5a0c3f56 6672'procedure, 'class-tag, 'keyword, 'sysvar, etc. SPECIAL-SELECTOR is
52a244eb
S
6673used only once, for `all-completions', and can be used to, e.g.,
6674accumulate information on matching completions."
f32b3b91
CD
6675 (let* ((completion-ignore-case t)
6676 beg (end (point)) slash part spart completion all-completions
6677 dpart dcompletion)
6678
6679 (unless list
6680 (error (concat prompt ": No completions available")))
6681
6682 ;; What is already in the buffer?
6683 (save-excursion
6684 (skip-chars-backward "a-zA-Z0-9_$")
6685 (setq slash (eq (preceding-char) ?/)
6686 beg (point)
6687 idlwave-complete-after-success-form
6688 (list 'idlwave-after-successful-completion
6689 (list 'quote type) slash beg)
6690 idlwave-complete-after-success-form-force
6691 (list 'idlwave-after-successful-completion
6692 (list 'quote type) slash (list 'quote 'force))))
6693
6694 ;; Try a completion
6695 (setq part (buffer-substring beg end)
6696 dpart (downcase part)
6697 spart (idlwave-sintern stype part)
6698 completion (try-completion part list selector)
52a244eb
S
6699 dcompletion (if (stringp completion) (downcase completion))
6700 idlwave-completion-help-links nil)
f32b3b91
CD
6701 (cond
6702 ((null completion)
6703 ;; nothing available.
76959b77 6704 (error (concat prompt ": no completion for \"%s\"") part))
f32b3b91
CD
6705 ((and (not (equal dpart dcompletion))
6706 (not (eq t completion)))
6707 ;; We can add something
6708 (delete-region beg end)
8d222148
SM
6709 (insert (if (and (string= part dpart)
6710 (or (not (string= part ""))
6711 idlwave-complete-empty-string-as-lower-case)
6712 (not idlwave-completion-force-default-case))
6713 dcompletion
6714 completion))
f32b3b91
CD
6715 (if (eq t (try-completion completion list selector))
6716 ;; Now this is a unique match
6717 (idlwave-after-successful-completion type slash beg))
6718 t)
6719 ((or (eq completion t)
52a244eb 6720 (and (= 1 (length (setq all-completions
f32b3b91 6721 (idlwave-uniquify
4b1aaa8b
PE
6722 (all-completions part list
6723 (or special-selector
52a244eb
S
6724 selector))))))
6725 (equal dpart dcompletion)))
f32b3b91
CD
6726 ;; This is already complete
6727 (idlwave-after-successful-completion type slash beg)
6728 (message "%s is already the complete %s" part isa)
6729 nil)
4b1aaa8b 6730 (t
f32b3b91
CD
6731 ;; We cannot add something - offer a list.
6732 (message "Making completion list...")
4b1aaa8b 6733
52a244eb 6734 (unless idlwave-completion-help-links ; already set somewhere?
9001c33f
GM
6735 (mapc (lambda (x) ; Pass link prop through to highlight-linked
6736 (let ((link (get-text-property 0 'link (car x))))
6737 (if link
6738 (push (cons (car x) link)
6739 idlwave-completion-help-links))))
6740 list))
f32b3b91 6741 (let* ((list all-completions)
05a1abfc 6742 ;; "complete" means, this is already a valid completion
f32b3b91 6743 (complete (memq spart all-completions))
52a244eb 6744 (completion-highlight-first-word-only t)) ; XEmacs
8d222148
SM
6745 ;; (completion-fixup-function ; Emacs
6746 ;; (lambda () (and (eq (preceding-char) ?>)
6747 ;; (re-search-backward " <" beg t)))))
4b1aaa8b 6748
f32b3b91
CD
6749 (setq list (sort list (lambda (a b)
6750 (string< (downcase a) (downcase b)))))
6751 (if prepare-display-function
6752 (setq list (funcall prepare-display-function list)))
6753 (if (and (string= part dpart)
6754 (or (not (string= part ""))
6755 idlwave-complete-empty-string-as-lower-case)
6756 (not idlwave-completion-force-default-case))
6757 (setq list (mapcar (lambda (x)
4b1aaa8b 6758 (if (listp x)
f32b3b91
CD
6759 (setcar x (downcase (car x)))
6760 (setq x (downcase x)))
6761 x)
6762 list)))
6763 (idlwave-display-completion-list list prompt beg complete))
6764 t))))
6765
6766(defun idlwave-complete-class ()
6767 "Complete a class at point."
6768 (interactive)
6769 ;; Call `idlwave-routines' to make sure the class list will be available
6770 (idlwave-routines)
15e42531
CD
6771 ;; Check for the special case of completing empty string after pro/function
6772 (if (let ((case-fold-search t))
6773 (save-excursion
6774 (and
6775 (re-search-backward "\\<\\(pro\\|function\\)[ \t]+\\="
6776 (- (point) 15) t)
6777 (goto-char (point-min))
4b1aaa8b 6778 (re-search-forward
15e42531
CD
6779 "^[ \t]*\\(pro\\|function\\)[ \t]+\\([a-zA-Z0-9_]+::\\)" nil t))))
6780 ;; Yank the full class specification
6781 (insert (match-string 2))
52a244eb 6782 ;; Do the completion, using list gathered from `idlwave-routines'
4b1aaa8b
PE
6783 (idlwave-complete-in-buffer
6784 'class 'class (idlwave-class-alist) nil
52a244eb 6785 "Select a class" "class"
8d222148
SM
6786 (lambda (list) ;; Push it to help-links if system help available
6787 (mapcar (lambda (x)
6788 (let* ((entry (idlwave-class-info x))
6789 (link (nth 1 (assq 'link entry))))
6790 (if link (push (cons x link)
6791 idlwave-completion-help-links))
6792 x))
6793 list)))))
f32b3b91 6794
76959b77 6795(defun idlwave-attach-classes (list type show-classes)
05a1abfc 6796 ;; Attach the proper class list to a LIST of completion items.
76959b77
S
6797 ;; TYPE, when 'kwd, shows classes for method keywords, when
6798 ;; 'class-tag, for class tags, and otherwise for methods.
f32b3b91 6799 ;; SHOW-CLASSES is the value of `idlwave-completion-show-classes'.
76959b77
S
6800 (if (or (null show-classes) ; don't want to see classes
6801 (null class-selector) ; not a method call
4b1aaa8b 6802 (and
76959b77
S
6803 (stringp class-selector) ; the class is already known
6804 (not super-classes))) ; no possibilities for inheritance
6805 ;; In these cases, we do not have to do anything
6806 list
05a1abfc
CD
6807 (let* ((do-prop (and (>= show-classes 0)
6808 (>= emacs-major-version 21)))
f32b3b91 6809 (do-buf (not (= show-classes 0)))
76959b77 6810 ;; (do-dots (featurep 'xemacs))
05a1abfc 6811 (do-dots t)
76959b77 6812 (inherit (if (and (not (eq type 'class-tag)) super-classes)
05a1abfc 6813 (cons class-selector super-classes)))
f32b3b91
CD
6814 (max (abs show-classes))
6815 (lmax (if do-dots (apply 'max (mapcar 'length list))))
6816 classes nclasses class-info space)
4b1aaa8b 6817 (mapcar
f32b3b91
CD
6818 (lambda (x)
6819 ;; get the classes
76959b77
S
6820 (if (eq type 'class-tag)
6821 ;; Just one class for tags
6822 (setq classes
4b1aaa8b 6823 (list
76959b77 6824 (idlwave-class-or-superclass-with-tag class-selector x)))
52a244eb 6825 ;; Multiple classes for method or method-keyword
76959b77
S
6826 (setq classes
6827 (if (eq type 'kwd)
6828 (idlwave-all-method-keyword-classes
6829 method-selector x type-selector)
6830 (idlwave-all-method-classes x type-selector)))
6831 (if inherit
4b1aaa8b 6832 (setq classes
76959b77
S
6833 (delq nil
6834 (mapcar (lambda (x) (if (memq x inherit) x nil))
6835 classes)))))
f32b3b91
CD
6836 (setq nclasses (length classes))
6837 ;; Make the separator between item and class-info
6838 (if do-dots
6839 (setq space (concat " " (make-string (- lmax (length x)) ?.)))
6840 (setq space " "))
6841 (if do-buf
6842 ;; We do want info in the buffer
6843 (if (<= nclasses max)
6844 (setq class-info (concat
6845 space
6846 "<" (mapconcat 'identity classes ",") ">"))
6847 (setq class-info (format "%s<%d classes>" space nclasses)))
6848 (setq class-info nil))
6849 (when do-prop
6850 ;; We do want properties
6851 (setq x (copy-sequence x))
6852 (put-text-property 0 (length x)
52a244eb
S
6853 'help-echo (mapconcat 'identity classes " ")
6854 x))
f32b3b91
CD
6855 (if class-info
6856 (list x class-info)
6857 x))
6858 list))))
6859
6860(defun idlwave-attach-method-classes (list)
6861 ;; Call idlwave-attach-classes with method parameters
76959b77 6862 (idlwave-attach-classes list 'method idlwave-completion-show-classes))
f32b3b91
CD
6863(defun idlwave-attach-keyword-classes (list)
6864 ;; Call idlwave-attach-classes with keyword parameters
76959b77
S
6865 (idlwave-attach-classes list 'kwd idlwave-completion-show-classes))
6866(defun idlwave-attach-class-tag-classes (list)
6867 ;; Call idlwave-attach-classes with class structure tags
6868 (idlwave-attach-classes list 'class-tag idlwave-completion-show-classes))
4b1aaa8b 6869
f32b3b91
CD
6870
6871;;----------------------------------------------------------------------
6872;;----------------------------------------------------------------------
6873;;----------------------------------------------------------------------
6874;;----------------------------------------------------------------------
6875;;----------------------------------------------------------------------
0b03a950
GM
6876(when (featurep 'xemacs)
6877 (defvar rtn)
6878 (defun idlwave-pset (item)
6879 (set 'rtn item)))
5e72c6b2
S
6880
6881(defun idlwave-popup-select (ev list title &optional sort)
6882 "Select an item in LIST with a popup menu.
6883TITLE is the title to put atop the popup. If SORT is non-nil,
5a0c3f56 6884sort the list before displaying."
5e72c6b2 6885 (let ((maxpopup idlwave-max-popup-menu-items)
8d222148 6886 rtn menu)
5e72c6b2
S
6887 (cond ((null list))
6888 ((= 1 (length list))
6889 (setq rtn (car list)))
6890 ((featurep 'xemacs)
4b1aaa8b 6891 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6892 (string< (upcase a) (upcase b))))))
6893 (setq menu
6894 (append (list title)
6895 (mapcar (lambda (x) (vector x (list 'idlwave-pset
6896 x)))
6897 list)))
6898 (setq menu (idlwave-split-menu-xemacs menu maxpopup))
8d222148
SM
6899 (let ((resp (get-popup-menu-response menu)))
6900 (funcall (event-function resp) (event-object resp))))
5e72c6b2 6901 (t
4b1aaa8b 6902 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6903 (string< (upcase a) (upcase b))))))
6904 (setq menu (cons title
6905 (list
6906 (append (list "")
6907 (mapcar (lambda(x) (cons x x)) list)))))
6908 (setq menu (idlwave-split-menu-emacs menu maxpopup))
6909 (setq rtn (x-popup-menu ev menu))))
6910 rtn))
6911
6912(defun idlwave-split-menu-xemacs (menu N)
6913 "Split the MENU into submenus of maximum length N."
6914 (if (<= (length menu) (1+ N))
6915 ;; No splitting needed
6916 menu
6917 (let* ((title (car menu))
6918 (entries (cdr menu))
6919 (menu (list title))
6920 (cnt 0)
6921 (nextmenu nil))
6922 (while entries
6923 (while (and entries (< cnt N))
6924 (setq cnt (1+ cnt)
6925 nextmenu (cons (car entries) nextmenu)
6926 entries (cdr entries)))
6927 (setq nextmenu (nreverse nextmenu))
6928 (setq nextmenu (cons (format "%s...%s"
6929 (aref (car nextmenu) 0)
6930 (aref (nth (1- cnt) nextmenu) 0))
6931 nextmenu))
6932 (setq menu (cons nextmenu menu)
6933 nextmenu nil
6934 cnt 0))
6935 (nreverse menu))))
6936
6937(defun idlwave-split-menu-emacs (menu N)
6938 "Split the MENU into submenus of maximum length N."
6939 (if (<= (length (nth 1 menu)) (1+ N))
6940 ;; No splitting needed
6941 menu
6942 (let* ((title (car menu))
6943 (entries (cdr (nth 1 menu)))
6944 (menu nil)
6945 (cnt 0)
6946 (nextmenu nil))
6947 (while entries
6948 (while (and entries (< cnt N))
6949 (setq cnt (1+ cnt)
6950 nextmenu (cons (car entries) nextmenu)
6951 entries (cdr entries)))
6952 (setq nextmenu (nreverse nextmenu))
6953 (prin1 nextmenu)
6954 (setq nextmenu (cons (format "%s...%s"
6955 (car (car nextmenu))
6956 (car (nth (1- cnt) nextmenu)))
6957 nextmenu))
6958 (setq menu (cons nextmenu menu)
6959 nextmenu nil
6960 cnt 0))
6961 (setq menu (nreverse menu))
6962 (setq menu (cons title menu))
6963 menu)))
f32b3b91 6964
15e42531
CD
6965(defvar idlwave-completion-setup-hook nil)
6966
f32b3b91
CD
6967(defun idlwave-scroll-completions (&optional message)
6968 "Scroll the completion window on this frame."
6969 (let ((cwin (get-buffer-window "*Completions*" 'visible))
6970 (win (selected-window)))
6971 (unwind-protect
6972 (progn
6973 (select-window cwin)
6974 (condition-case nil
6975 (scroll-up)
6976 (error (if (and (listp last-command)
6977 (nth 2 last-command))
6978 (progn
6979 (select-window win)
6980 (eval idlwave-complete-after-success-form))
6981 (set-window-start cwin (point-min)))))
274f1353 6982 (and message (message "%s" message)))
f32b3b91
CD
6983 (select-window win))))
6984
6985(defun idlwave-display-completion-list (list &optional message beg complete)
6986 "Display the completions in LIST in the completions buffer and echo MESSAGE."
6987 (unless (and (get-buffer-window "*Completions*")
6988 (idlwave-local-value 'idlwave-completion-p "*Completions*"))
6989 (move-marker idlwave-completion-mark beg)
6990 (setq idlwave-before-completion-wconf (current-window-configuration)))
6991
6992 (if (featurep 'xemacs)
4b1aaa8b 6993 (idlwave-display-completion-list-xemacs
15e42531 6994 list)
f32b3b91
CD
6995 (idlwave-display-completion-list-emacs list))
6996
6997 ;; Store a special value in `this-command'. When `idlwave-complete'
6998 ;; finds this in `last-command', it will scroll the *Completions* buffer.
6999 (setq this-command (list 'idlwave-display-completion-list message complete))
7000
7001 ;; Mark the completions buffer as created by cib
7002 (idlwave-set-local 'idlwave-completion-p t "*Completions*")
7003
7004 ;; Fontify the classes
7005 (if (and idlwave-completion-fontify-classes
7006 (consp (car list)))
7007 (idlwave-completion-fontify-classes))
7008
15e42531
CD
7009 ;; Run the hook
7010 (run-hooks 'idlwave-completion-setup-hook)
7011
f32b3b91 7012 ;; Display the message
274f1353 7013 (message "%s" (or message "Making completion list...done")))
f32b3b91
CD
7014
7015(defun idlwave-choose (function &rest args)
7016 "Call FUNCTION as a completion chooser and pass ARGS to it."
7017 (let ((completion-ignore-case t)) ; install correct value
7018 (apply function args))
175069ef 7019 (if (and (derived-mode-p 'idlwave-shell-mode)
15e42531
CD
7020 (boundp 'font-lock-mode)
7021 (not font-lock-mode))
52a244eb 7022 ;; For the shell, remove the fontification of the word before point
15e42531
CD
7023 (let ((beg (save-excursion
7024 (skip-chars-backward "a-zA-Z0-9_")
7025 (point))))
7026 (remove-text-properties beg (point) '(face nil))))
f32b3b91
CD
7027 (eval idlwave-complete-after-success-form-force))
7028
76959b77
S
7029(defun idlwave-keyboard-quit ()
7030 (interactive)
7031 (unwind-protect
7032 (if (eq (car-safe last-command) 'idlwave-display-completion-list)
7033 (idlwave-restore-wconf-after-completion))
7034 (keyboard-quit)))
7035
f32b3b91
CD
7036(defun idlwave-restore-wconf-after-completion ()
7037 "Restore the old (before completion) window configuration."
7038 (and idlwave-completion-restore-window-configuration
7039 idlwave-before-completion-wconf
7040 (set-window-configuration idlwave-before-completion-wconf)))
7041
52a244eb
S
7042(defun idlwave-one-key-select (sym prompt delay)
7043 "Make the user select an element from the alist in the variable SYM.
7044The keys of the alist are expected to be strings. The function returns the
7045car of the selected association.
d9271f41 7046To do this, PROMPT is displayed and the user must hit a letter key to
52a244eb
S
7047select an entry. If the user does not reply within DELAY seconds, a help
7048window with the options is displayed automatically.
7049The key which is associated with each option is generated automatically.
7050First, the strings are checked for preselected keys, like in \"[P]rint\".
7051If these don't exist, a letter in the string is automatically selected."
7052 (let* ((alist (symbol-value sym))
7053 (temp-buffer-show-hook (if (fboundp 'fit-window-to-buffer)
7054 '(fit-window-to-buffer)))
7055 keys-alist char)
7056 ;; First check the cache
7057 (if (and (eq (symbol-value sym) (get sym :one-key-alist-last)))
7058 (setq keys-alist (get sym :one-key-alist-cache))
7059 ;; Need to make new list
7060 (setq keys-alist (idlwave-make-one-key-alist alist))
7061 (put sym :one-key-alist-cache keys-alist)
7062 (put sym :one-key-alist-last alist))
7063 ;; Display prompt and wait for quick reply
7064 (message "%s[%s]" prompt
7065 (mapconcat (lambda(x) (char-to-string (car x)))
7066 keys-alist ""))
7067 (if (sit-for delay)
7068 ;; No quick reply: Show help
7069 (save-window-excursion
7070 (with-output-to-temp-buffer "*Completions*"
7071 (mapcar (lambda(x)
7072 (princ (nth 1 x))
7073 (princ "\n"))
4b1aaa8b 7074 keys-alist))
52a244eb
S
7075 (setq char (read-char)))
7076 (setq char (read-char)))
7077 (message nil)
7078 ;; Return the selected result
7079 (nth 2 (assoc char keys-alist))))
7080
7081;; Used for, e.g., electric debug super-examine.
7082(defun idlwave-make-one-key-alist (alist)
7083 "Make an alist for single key selection."
7084 (let ((l alist) keys-alist name start char help
7085 (cnt 0)
7086 (case-fold-search nil))
7087 (while l
7088 (setq name (car (car l))
7089 l (cdr l))
7090 (catch 'exit
7091 ;; First check if the configuration predetermined a key
7092 (if (string-match "\\[\\(.\\)\\]" name)
7093 (progn
7094 (setq char (string-to-char (downcase (match-string 1 name)))
7095 help (format "%c: %s" char name)
7096 keys-alist (cons (list char help name) keys-alist))
7097 (throw 'exit t)))
7098 ;; Then check for capital letters
7099 (setq start 0)
7100 (while (string-match "[A-Z]" name start)
7101 (setq start (match-end 0)
7102 char (string-to-char (downcase (match-string 0 name))))
7103 (if (not (assoc char keys-alist))
7104 (progn
7105 (setq help (format "%c: %s" char
7106 (replace-match
7107 (concat "[" (match-string 0 name) "]")
7108 t t name))
7109 keys-alist (cons (list char help name) keys-alist))
7110 (throw 'exit t))))
7111 ;; Now check for lowercase letters
7112 (setq start 0)
7113 (while (string-match "[a-z]" name start)
7114 (setq start (match-end 0)
7115 char (string-to-char (match-string 0 name)))
7116 (if (not (assoc char keys-alist))
7117 (progn
7118 (setq help (format "%c: %s" char
7119 (replace-match
7120 (concat "[" (match-string 0 name) "]")
7121 t t name))
7122 keys-alist (cons (list char help name) keys-alist))
7123 (throw 'exit t))))
7124 ;; Bummer, nothing found! Use a stupid number
7125 (setq char (string-to-char (int-to-string (setq cnt (1+ cnt))))
7126 help (format "%c: %s" char name)
7127 keys-alist (cons (list char help name) keys-alist))))
7128 (nreverse keys-alist)))
7129
f32b3b91
CD
7130(defun idlwave-set-local (var value &optional buffer)
7131 "Set the buffer-local value of VAR in BUFFER to VALUE."
9a529312 7132 (with-current-buffer (or buffer (current-buffer))
f32b3b91
CD
7133 (set (make-local-variable var) value)))
7134
7135(defun idlwave-local-value (var &optional buffer)
7136 "Return the value of VAR in BUFFER, but only if VAR is local to BUFFER."
9a529312 7137 (with-current-buffer (or buffer (current-buffer))
f32b3b91
CD
7138 (and (local-variable-p var (current-buffer))
7139 (symbol-value var))))
7140
15e42531
CD
7141;; In XEmacs, we can use :activate-callback directly to advice the
7142;; choose functions. We use the private keymap only for the online
7143;; help feature.
f32b3b91 7144
15e42531 7145(defvar idlwave-completion-map nil
5a0c3f56 7146 "Keymap for `completion-list-mode' with `idlwave-complete'.")
15e42531
CD
7147
7148(defun idlwave-display-completion-list-xemacs (list &rest cl-args)
f32b3b91 7149 (with-output-to-temp-buffer "*Completions*"
15e42531
CD
7150 (apply 'display-completion-list list
7151 ':activate-callback 'idlwave-default-choose-completion
7152 cl-args))
9a529312 7153 (with-current-buffer "*Completions*"
15e42531
CD
7154 (use-local-map
7155 (or idlwave-completion-map
7156 (setq idlwave-completion-map
7157 (idlwave-make-modified-completion-map-xemacs
7158 (current-local-map)))))))
f32b3b91
CD
7159
7160(defun idlwave-default-choose-completion (&rest args)
7161 "Execute `default-choose-completion' and then restore the win-conf."
7162 (apply 'idlwave-choose 'default-choose-completion args))
7163
15e42531
CD
7164(defun idlwave-make-modified-completion-map-xemacs (old-map)
7165 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7166 (let ((new-map (copy-keymap old-map)))
7167 (define-key new-map [button3up] 'idlwave-mouse-completion-help)
7168 (define-key new-map [button3] (lambda ()
7169 (interactive)
7170 (setq this-command last-command)))
7171 new-map))
f32b3b91 7172
76959b77 7173;; In Emacs we also replace keybindings in the completion
15e42531 7174;; map in order to install our wrappers.
f32b3b91
CD
7175
7176(defun idlwave-display-completion-list-emacs (list)
7177 "Display completion list and install the choose wrappers."
7178 (with-output-to-temp-buffer "*Completions*"
7179 (display-completion-list list))
9a529312 7180 (with-current-buffer "*Completions*"
f32b3b91
CD
7181 (use-local-map
7182 (or idlwave-completion-map
7183 (setq idlwave-completion-map
15e42531
CD
7184 (idlwave-make-modified-completion-map-emacs
7185 (current-local-map)))))))
7186
7187(defun idlwave-make-modified-completion-map-emacs (old-map)
f32b3b91
CD
7188 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7189 (let ((new-map (copy-keymap old-map)))
4b1aaa8b 7190 (substitute-key-definition
f32b3b91
CD
7191 'choose-completion 'idlwave-choose-completion new-map)
7192 (substitute-key-definition
7193 'mouse-choose-completion 'idlwave-mouse-choose-completion new-map)
15e42531 7194 (define-key new-map [mouse-3] 'idlwave-mouse-completion-help)
f32b3b91
CD
7195 new-map))
7196
7197(defun idlwave-choose-completion (&rest args)
7198 "Choose the completion that point is in or next to."
7199 (interactive)
7200 (apply 'idlwave-choose 'choose-completion args))
7201
7202(defun idlwave-mouse-choose-completion (&rest args)
7203 "Click on an alternative in the `*Completions*' buffer to choose it."
7204 (interactive "e")
7205 (apply 'idlwave-choose 'mouse-choose-completion args))
7206
7207;;----------------------------------------------------------------------
7208;;----------------------------------------------------------------------
7209
05a1abfc 7210;;; ------------------------------------------------------------------------
52a244eb 7211;;; Stucture parsing code, and code to manage class info
05a1abfc
CD
7212
7213;;
7214;; - Go again over the documentation how to write a completion
7215;; plugin. It is in self.el, but currently still very bad.
4b1aaa8b
PE
7216;; This could be in a separate file in the distribution, or
7217;; in an appendix for the manual.
52a244eb
S
7218
7219(defvar idlwave-struct-skip
7220 "[ \t]*\\(\\$.*\n\\(^[ \t]*\\(\\$[ \t]*\\)?\\(;.*\\)?\n\\)*\\)?[ \t]*"
5a0c3f56 7221 "Regexp for skipping continued blank or comment-only lines in structures.")
52a244eb
S
7222
7223(defvar idlwave-struct-tag-regexp
7224 (concat "[{,]" ;leading comma/brace
7225 idlwave-struct-skip ; 4 groups
7226 "\\([a-zA-Z][a-zA-Z0-9_]*\\)" ;the tag itself, group 5
7227 "[ \t]*:") ; the final colon
7228 "Regexp for structure tags.")
05a1abfc
CD
7229
7230(defun idlwave-struct-tags ()
7231 "Return a list of all tags in the structure defined at point.
7232Point is expected just before the opening `{' of the struct definition."
7233 (save-excursion
7234 (let* ((borders (idlwave-struct-borders))
7235 (beg (car borders))
7236 (end (cdr borders))
7237 tags)
7238 (goto-char beg)
52a244eb
S
7239 (save-restriction
7240 (narrow-to-region beg end)
7241 (while (re-search-forward idlwave-struct-tag-regexp end t)
7242 ;; Check if we are still on the top level of the structure.
7243 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7244 (= (point) beg))
7245 (push (match-string-no-properties 5) tags))
7246 (goto-char (match-end 0))))
7247 (nreverse tags))))
05a1abfc 7248
76959b77
S
7249(defun idlwave-find-struct-tag (tag)
7250 "Find a given TAG in the structure defined at point."
7251 (let* ((borders (idlwave-struct-borders))
76959b77
S
7252 (end (cdr borders))
7253 (case-fold-search t))
4b1aaa8b 7254 (re-search-forward (concat "\\(^[ \t]*\\|[,{][ \t]*\\)" tag "[ \t]*:")
76959b77
S
7255 end t)))
7256
05a1abfc
CD
7257(defun idlwave-struct-inherits ()
7258 "Return a list of all `inherits' names in the struct at point.
7259Point is expected just before the opening `{' of the struct definition."
7260 (save-excursion
7261 (let* ((borders (idlwave-struct-borders))
7262 (beg (car borders))
7263 (end (cdr borders))
7264 (case-fold-search t)
7265 names)
7266 (goto-char beg)
52a244eb
S
7267 (save-restriction
7268 (narrow-to-region beg end)
4b1aaa8b 7269 (while (re-search-forward
52a244eb
S
7270 (concat "[{,]" ;leading comma/brace
7271 idlwave-struct-skip ; 4 groups
7272 "inherits" ; The INHERITS tag
7273 idlwave-struct-skip ; 4 more
7274 "\\([a-zA-Z][a-zA-Z0-9_]*\\)") ; The super-group, #9
7275 end t)
7276 ;; Check if we are still on the top level of the structure.
7277 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7278 (= (point) beg))
7279 (push (match-string-no-properties 9) names))
7280 (goto-char (match-end 0))))
05a1abfc
CD
7281 (nreverse names))))
7282
5e72c6b2 7283(defun idlwave-in-structure ()
52a244eb 7284 "Return t if point is inside an IDL structure definition."
5e72c6b2
S
7285 (let ((beg (point)))
7286 (save-excursion
7287 (if (not (or (idlwave-in-comment) (idlwave-in-quote)))
7288 (if (idlwave-find-structure-definition nil nil 'back)
7289 (let ((borders (idlwave-struct-borders)))
7290 (or (= (car borders) (cdr borders)) ;; struct not yet closed...
7291 (and (> beg (car borders)) (< beg (cdr borders))))))))))
05a1abfc
CD
7292
7293(defun idlwave-struct-borders ()
7294 "Return the borders of the {...} after point as a cons cell."
7295 (let (beg)
7296 (save-excursion
7297 (skip-chars-forward "^{")
7298 (setq beg (point))
7299 (condition-case nil (forward-list 1)
7300 (error (goto-char beg)))
7301 (cons beg (point)))))
7302
7303(defun idlwave-find-structure-definition (&optional var name bound)
5a0c3f56
JB
7304 "Search forward for a structure definition.
7305If VAR is non-nil, search for a structure assigned to variable VAR.
7306If NAME is non-nil, search for a named structure NAME, if a string,
7307or a generic named structure otherwise. If BOUND is an integer, limit
7308the search. If BOUND is the symbol `all', we search first back and
7309then forward through the entire file. If BOUND is the symbol `back'
7310we search only backward."
76959b77 7311 (let* ((ws "[ \t]*\\(\\$.*\n[ \t]*\\)*")
05a1abfc
CD
7312 (case-fold-search t)
7313 (lim (if (integerp bound) bound nil))
7314 (re (concat
7315 (if var
7316 (concat "\\<" (regexp-quote (downcase var)) "\\>" ws)
7317 "\\(\\)")
7318 "=" ws "\\({\\)"
4b1aaa8b 7319 (if name
52a244eb 7320 (if (stringp name)
4b1aaa8b 7321 (concat ws "\\(\\<" (downcase name) "\\)[^a-zA-Z0-9_$]")
52a244eb
S
7322 ;; Just a generic name
7323 (concat ws "\\<\\([a-zA-Z_0-9$]+\\)" ws ","))
7324 ""))))
5e72c6b2 7325 (if (or (and (or (eq bound 'all) (eq bound 'back))
05a1abfc 7326 (re-search-backward re nil t))
5e72c6b2 7327 (and (not (eq bound 'back)) (re-search-forward re lim t)))
52a244eb
S
7328 (progn
7329 (goto-char (match-beginning 3))
7330 (match-string-no-properties 5)))))
7331
4b1aaa8b 7332(defvar idlwave-class-info nil)
52a244eb 7333(defvar idlwave-class-reset nil) ; to reset buffer-local classes
05a1abfc 7334
05a1abfc 7335(add-hook 'idlwave-update-rinfo-hook
52a244eb 7336 (lambda () (setq idlwave-class-reset t)))
05a1abfc
CD
7337(add-hook 'idlwave-after-load-rinfo-hook
7338 (lambda () (setq idlwave-class-info nil)))
7339
7340(defun idlwave-class-info (class)
7341 (let (list entry)
52a244eb
S
7342 (if idlwave-class-info
7343 (if idlwave-class-reset
4b1aaa8b 7344 (setq
52a244eb
S
7345 idlwave-class-reset nil
7346 idlwave-class-info ; Remove any visited in a buffer
4b1aaa8b
PE
7347 (delq nil (mapcar
7348 (lambda (x)
7349 (let ((filebuf
7350 (idlwave-class-file-or-buffer
52a244eb
S
7351 (or (cdr (assq 'found-in x)) (car x)))))
7352 (if (cdr filebuf)
7353 nil
7354 x)))
7355 idlwave-class-info))))
7356 ;; Info is nil, put in the system stuff to start.
05a1abfc
CD
7357 (setq idlwave-class-info idlwave-system-class-info)
7358 (setq list idlwave-class-info)
7359 (while (setq entry (pop list))
7360 (idlwave-sintern-class-info entry)))
7361 (setq class (idlwave-sintern-class class))
52a244eb
S
7362 (or (assq class idlwave-class-info)
7363 (progn (idlwave-scan-class-info class)
7364 (assq class idlwave-class-info)))))
05a1abfc
CD
7365
7366(defun idlwave-sintern-class-info (entry)
7367 "Sintern the class names in a class-info entry."
8d222148 7368 (let ((inherits (assq 'inherits entry)))
05a1abfc
CD
7369 (setcar entry (idlwave-sintern-class (car entry) 'set))
7370 (if inherits
7371 (setcdr inherits (mapcar (lambda (x) (idlwave-sintern-class x 'set))
7372 (cdr inherits))))))
7373
52a244eb 7374(defun idlwave-find-class-definition (class &optional all-hook alt-class)
5a0c3f56 7375 "Find class structure definition(s).
52a244eb
S
7376If ALL-HOOK is set, find all named structure definitions in a given
7377class__define routine, on which ALL-HOOK will be run. If ALT-CLASS is
7378set, look for the name__define pro, and inside of it, for the ALT-CLASS
5a0c3f56 7379class/struct definition."
8d222148 7380 (let ((case-fold-search t) end-lim name)
52a244eb
S
7381 (when (re-search-forward
7382 (concat "^[ \t]*pro[ \t]+" (downcase class) "__define" "\\>") nil t)
7383 (if all-hook
7384 (progn
7385 ;; For everything there
7386 (setq end-lim (save-excursion (idlwave-end-of-subprogram) (point)))
4b1aaa8b 7387 (while (setq name
52a244eb
S
7388 (idlwave-find-structure-definition nil t end-lim))
7389 (funcall all-hook name)))
7390 (idlwave-find-structure-definition nil (or alt-class class))))))
76959b77 7391
52a244eb
S
7392
7393(defun idlwave-class-file-or-buffer (class)
5a0c3f56 7394 "Find buffer visiting CLASS definition."
05a1abfc 7395 (let* ((pro (concat (downcase class) "__define"))
52a244eb
S
7396 (file (idlwave-routine-source-file
7397 (nth 3 (idlwave-rinfo-assoc pro 'pro nil
7398 (idlwave-routines))))))
7399 (cons file (if file (idlwave-get-buffer-visiting file)))))
7400
7401
7402(defun idlwave-scan-class-info (class)
5a0c3f56 7403 "Scan all class and named structure info in the class__define pro."
52a244eb
S
7404 (let* ((idlwave-auto-routine-info-updates nil)
7405 (filebuf (idlwave-class-file-or-buffer class))
7406 (file (car filebuf))
7407 (buf (cdr filebuf))
7408 (class (idlwave-sintern-class class)))
7409 (if (or
7410 (not file)
7411 (and ;; neither a regular file nor a visited buffer
7412 (not buf)
7413 (not (file-regular-p file))))
7414 nil ; Cannot find the file/buffer to get any info
05a1abfc 7415 (save-excursion
52a244eb
S
7416 (if buf (set-buffer buf)
7417 ;; Read the file in temporarily
05a1abfc
CD
7418 (set-buffer (get-buffer-create " *IDLWAVE-tmp*"))
7419 (erase-buffer)
175069ef 7420 (unless (derived-mode-p 'idlwave-mode)
05a1abfc
CD
7421 (idlwave-mode))
7422 (insert-file-contents file))
7423 (save-excursion
7424 (goto-char 1)
4b1aaa8b 7425 (idlwave-find-class-definition class
52a244eb
S
7426 ;; Scan all of the structures found there
7427 (lambda (name)
7428 (let* ((this-class (idlwave-sintern-class name))
4b1aaa8b 7429 (entry
52a244eb
S
7430 (list this-class
7431 (cons 'tags (idlwave-struct-tags))
7432 (cons 'inherits (idlwave-struct-inherits)))))
7433 (if (not (eq this-class class))
7434 (setq entry (nconc entry (list (cons 'found-in class)))))
7435 (idlwave-sintern-class-info entry)
7436 (push entry idlwave-class-info)))))))))
7437
7438(defun idlwave-class-found-in (class)
5a0c3f56 7439 "Return the FOUND-IN property of the CLASS."
52a244eb 7440 (cdr (assq 'found-in (idlwave-class-info class))))
05a1abfc
CD
7441(defun idlwave-class-tags (class)
7442 "Return the native tags in CLASS."
7443 (cdr (assq 'tags (idlwave-class-info class))))
7444(defun idlwave-class-inherits (class)
7445 "Return the direct superclasses of CLASS."
7446 (cdr (assq 'inherits (idlwave-class-info class))))
7447
52a244eb 7448
05a1abfc
CD
7449(defun idlwave-all-class-tags (class)
7450 "Return a list of native and inherited tags in CLASS."
76959b77
S
7451 (condition-case err
7452 (apply 'append (mapcar 'idlwave-class-tags
7453 (cons class (idlwave-all-class-inherits class))))
4b1aaa8b 7454 (error
76959b77
S
7455 (idlwave-class-tag-reset)
7456 (error "%s" (error-message-string err)))))
7457
05a1abfc
CD
7458
7459(defun idlwave-all-class-inherits (class)
7460 "Return a list of all superclasses of CLASS (recursively expanded).
5e72c6b2 7461The list is cached in `idlwave-class-info' for faster access."
05a1abfc
CD
7462 (cond
7463 ((not idlwave-support-inheritance) nil)
7464 ((eq class nil) nil)
7465 ((eq class t) nil)
7466 (t
7467 (let ((info (idlwave-class-info class))
7468 entry)
7469 (if (setq entry (assq 'all-inherits info))
7470 (cdr entry)
76959b77
S
7471 ;; Save the depth of inheritance scan to check for circular references
7472 (let ((inherits (mapcar (lambda (x) (cons x 0))
7473 (idlwave-class-inherits class)))
05a1abfc
CD
7474 rtn all-inherits cl)
7475 (while inherits
7476 (setq cl (pop inherits)
76959b77
S
7477 rtn (cons (car cl) rtn)
7478 inherits (append (mapcar (lambda (x)
7479 (cons x (1+ (cdr cl))))
7480 (idlwave-class-inherits (car cl)))
7481 inherits))
7482 (if (> (cdr cl) 999)
7483 (error
7484 "Class scan: inheritance depth exceeded. Circular inheritance?")
7485 ))
05a1abfc
CD
7486 (setq all-inherits (nreverse rtn))
7487 (nconc info (list (cons 'all-inherits all-inherits)))
7488 all-inherits))))))
7489
52a244eb 7490(defun idlwave-entry-keywords (entry &optional record-link)
4b1aaa8b 7491 "Return the flat entry keywords alist from routine-info entry.
52a244eb
S
7492If RECORD-LINK is non-nil, the keyword text is copied and a text
7493property indicating the link is added."
7494 (let (kwds)
8ffcfb27 7495 (mapc
4b1aaa8b 7496 (lambda (key-list)
52a244eb
S
7497 (let ((file (car key-list)))
7498 (mapcar (lambda (key-cons)
7499 (let ((key (car key-cons))
7500 (link (cdr key-cons)))
7501 (when (and record-link file)
7502 (setq key (copy-sequence key))
4b1aaa8b 7503 (put-text-property
52a244eb 7504 0 (length key)
4b1aaa8b
PE
7505 'link
7506 (concat
7507 file
7508 (if link
52a244eb
S
7509 (concat idlwave-html-link-sep
7510 (number-to-string link))))
7511 key))
7512 (push (list key) kwds)))
7513 (cdr key-list))))
7514 (nthcdr 5 entry))
7515 (nreverse kwds)))
7516
7517(defun idlwave-entry-find-keyword (entry keyword)
5a0c3f56 7518 "Find keyword KEYWORD in entry ENTRY, and return (with link) if set."
52a244eb
S
7519 (catch 'exit
7520 (mapc
4b1aaa8b 7521 (lambda (key-list)
52a244eb
S
7522 (let ((file (car key-list))
7523 (kwd (assoc keyword (cdr key-list))))
7524 (when kwd
4b1aaa8b 7525 (setq kwd (cons (car kwd)
52a244eb 7526 (if (and file (cdr kwd))
4b1aaa8b 7527 (concat file
52a244eb
S
7528 idlwave-html-link-sep
7529 (number-to-string (cdr kwd)))
7530 (cdr kwd))))
7531 (throw 'exit kwd))))
7532 (nthcdr 5 entry))))
05a1abfc
CD
7533
7534;;==========================================================================
7535;;
7536;; Completing class structure tags. This is a completion plugin.
7537;; The necessary taglist is constructed dynamically
7538
7539(defvar idlwave-current-tags-class nil)
7540(defvar idlwave-current-class-tags nil)
7541(defvar idlwave-current-native-class-tags nil)
76959b77 7542(defvar idlwave-sint-class-tags nil)
1a717047 7543(declare-function idlwave-sintern-class-tag "idlwave" t t)
76959b77 7544(idlwave-new-sintern-type 'class-tag)
05a1abfc 7545(add-to-list 'idlwave-complete-special 'idlwave-complete-class-structure-tag)
76959b77 7546(add-hook 'idlwave-update-rinfo-hook 'idlwave-class-tag-reset)
05a1abfc
CD
7547
7548(defun idlwave-complete-class-structure-tag ()
7549 "Complete a structure tag on a `self' argument in an object method."
7550 (interactive)
7551 (let ((pos (point))
7552 (case-fold-search t))
7553 (if (save-excursion
7554 ;; Check if the context is right
52a244eb 7555 (skip-chars-backward "a-zA-Z0-9._$")
05a1abfc
CD
7556 (and (< (point) (- pos 4))
7557 (looking-at "self\\.")))
76959b77
S
7558 (let* ((class-selector (nth 2 (idlwave-current-routine)))
7559 (super-classes (idlwave-all-class-inherits class-selector)))
05a1abfc 7560 ;; Check if we are in a class routine
76959b77 7561 (unless class-selector
e8af40ee 7562 (error "Not in a method procedure or function"))
05a1abfc 7563 ;; Check if we need to update the "current" class
76959b77
S
7564 (if (not (equal class-selector idlwave-current-tags-class))
7565 (idlwave-prepare-class-tag-completion class-selector))
4b1aaa8b 7566 (setq idlwave-completion-help-info
76959b77 7567 (list 'idlwave-complete-class-structure-tag-help
4b1aaa8b 7568 (idlwave-sintern-routine
76959b77
S
7569 (concat class-selector "__define"))
7570 nil))
8d222148 7571 ;; FIXME: idlwave-cpl-bold doesn't seem used anywhere.
05a1abfc
CD
7572 (let ((idlwave-cpl-bold idlwave-current-native-class-tags))
7573 (idlwave-complete-in-buffer
4b1aaa8b 7574 'class-tag 'class-tag
05a1abfc 7575 idlwave-current-class-tags nil
76959b77
S
7576 (format "Select a tag of class %s" class-selector)
7577 "class tag"
7578 'idlwave-attach-class-tag-classes))
05a1abfc
CD
7579 t) ; return t to skip other completions
7580 nil)))
7581
76959b77 7582(defun idlwave-class-tag-reset ()
05a1abfc
CD
7583 (setq idlwave-current-tags-class nil))
7584
7585(defun idlwave-prepare-class-tag-completion (class)
7586 "Find and parse the necessary class definitions for class structure tags."
76959b77 7587 (setq idlwave-sint-class-tags nil)
05a1abfc
CD
7588 (setq idlwave-current-tags-class class)
7589 (setq idlwave-current-class-tags
7590 (mapcar (lambda (x)
76959b77 7591 (list (idlwave-sintern-class-tag x 'set)))
05a1abfc
CD
7592 (idlwave-all-class-tags class)))
7593 (setq idlwave-current-native-class-tags
7594 (mapcar 'downcase (idlwave-class-tags class))))
7595
7596;===========================================================================
7597;;
7598;; Completing system variables and their structure fields
52a244eb 7599;; This is also a plugin.
05a1abfc
CD
7600
7601(defvar idlwave-sint-sysvars nil)
7602(defvar idlwave-sint-sysvartags nil)
1a717047
GM
7603(declare-function idlwave-sintern-sysvar "idlwave" t t)
7604(declare-function idlwave-sintern-sysvartag "idlwave" t t)
05a1abfc
CD
7605(idlwave-new-sintern-type 'sysvar)
7606(idlwave-new-sintern-type 'sysvartag)
7607(add-to-list 'idlwave-complete-special 'idlwave-complete-sysvar-or-tag)
7608(add-hook 'idlwave-update-rinfo-hook 'idlwave-sysvars-reset)
05a1abfc
CD
7609(add-hook 'idlwave-after-load-rinfo-hook 'idlwave-sintern-sysvar-alist)
7610
05a1abfc
CD
7611
7612(defun idlwave-complete-sysvar-or-tag ()
7613 "Complete a system variable."
7614 (interactive)
7615 (let ((pos (point))
7616 (case-fold-search t))
7617 (cond ((save-excursion
7618 ;; Check if the context is right for system variable
7619 (skip-chars-backward "[a-zA-Z0-9_$]")
7620 (equal (char-before) ?!))
7621 (setq idlwave-completion-help-info '(idlwave-complete-sysvar-help))
4b1aaa8b 7622 (idlwave-complete-in-buffer 'sysvar 'sysvar
05a1abfc
CD
7623 idlwave-system-variables-alist nil
7624 "Select a system variable"
7625 "system variable")
7626 t) ; return t to skip other completions
7627 ((save-excursion
7628 ;; Check if the context is right for sysvar tag
52a244eb 7629 (skip-chars-backward "a-zA-Z0-9_$.")
05a1abfc
CD
7630 (and (equal (char-before) ?!)
7631 (looking-at "\\([a-zA-Z][a-zA-Z0-9_$]*\\)\\.")
7632 (<= (match-end 0) pos)))
7633 ;; Complete a system variable tag
7634 (let* ((var (idlwave-sintern-sysvar (match-string 1)))
7635 (entry (assq var idlwave-system-variables-alist))
52a244eb
S
7636 (tags (cdr (assq 'tags entry))))
7637 (or entry (error "!%s is not a known system variable" var))
05a1abfc
CD
7638 (or tags (error "System variable !%s is not a structure" var))
7639 (setq idlwave-completion-help-info
52a244eb 7640 (list 'idlwave-complete-sysvar-tag-help var))
4b1aaa8b 7641 (idlwave-complete-in-buffer 'sysvartag 'sysvartag
05a1abfc
CD
7642 tags nil
7643 "Select a system variable tag"
7644 "system variable tag")
7645 t)) ; return t to skip other completions
7646 (t nil))))
7647
e7c4fb1e 7648(defvar idlw-help-link) ;dynamic variables set by help callback
05a1abfc 7649(defun idlwave-complete-sysvar-help (mode word)
52a244eb
S
7650 (let ((word (or (nth 1 idlwave-completion-help-info) word))
7651 (entry (assoc word idlwave-system-variables-alist)))
7652 (cond
7653 ((eq mode 'test)
7654 (and (stringp word) entry (nth 1 (assq 'link entry))))
7655 ((eq mode 'set)
e7c4fb1e
GM
7656 ;; Setting dynamic!!!
7657 (if entry (setq idlw-help-link (nth 1 (assq 'link entry)))))
52a244eb
S
7658 (t (error "This should not happen")))))
7659
7660(defun idlwave-complete-sysvar-tag-help (mode word)
7661 (let* ((var (nth 1 idlwave-completion-help-info))
7662 (entry (assoc var idlwave-system-variables-alist))
7663 (tags (cdr (assq 'tags entry)))
7664 (main (nth 1 (assq 'link entry)))
8d222148 7665 target)
52a244eb
S
7666 (cond
7667 ((eq mode 'test) ; we can at least link the main
7668 (and (stringp word) entry main))
7669 ((eq mode 'set)
4b1aaa8b 7670 (if entry
e7c4fb1e 7671 (setq idlw-help-link
e08734e2 7672 (if (setq target (cdr (assoc-string word tags t)))
e7c4fb1e
GM
7673 (idlwave-substitute-link-target main target)
7674 main)))) ;; setting dynamic!!!
52a244eb
S
7675 (t (error "This should not happen")))))
7676
f66f03de 7677(defun idlwave-split-link-target (link)
5a0c3f56 7678 "Split a given LINK into link file and anchor."
f66f03de
S
7679 (if (string-match idlwave-html-link-sep link)
7680 (cons (substring link 0 (match-beginning 0))
7681 (string-to-number (substring link (match-end 0))))))
7682
52a244eb 7683(defun idlwave-substitute-link-target (link target)
5a0c3f56 7684 "Substitute the TARGET anchor for the given LINK."
52a244eb
S
7685 (let (main-base)
7686 (setq main-base (if (string-match "#" link)
7687 (substring link 0 (match-beginning 0))
7688 link))
7689 (if target
7690 (concat main-base idlwave-html-link-sep (number-to-string target))
7691 link)))
76959b77
S
7692
7693;; Fake help in the source buffer for class structure tags.
e7c4fb1e
GM
7694;; IDLW-HELP-LINK AND IDLW-HELP-NAME ARE GLOBAL-VARIABLES HERE.
7695;; (from idlwave-do-mouse-completion-help)
7696(defvar idlw-help-name)
7697(defvar idlw-help-link)
76959b77
S
7698(defvar idlwave-help-do-class-struct-tag nil)
7699(defun idlwave-complete-class-structure-tag-help (mode word)
7700 (cond
7701 ((eq mode 'test) ; nothing gets fontified for class tags
7702 nil)
7703 ((eq mode 'set)
52a244eb 7704 (let (class-with found-in)
4b1aaa8b
PE
7705 (when (setq class-with
7706 (idlwave-class-or-superclass-with-tag
76959b77
S
7707 idlwave-current-tags-class
7708 word))
4b1aaa8b 7709 (if (assq (idlwave-sintern-class class-with)
76959b77 7710 idlwave-system-class-info)
ff689efd 7711 (error "No help available for system class tags"))
52a244eb 7712 (if (setq found-in (idlwave-class-found-in class-with))
e7c4fb1e
GM
7713 (setq idlw-help-name (cons (concat found-in "__define") class-with))
7714 (setq idlw-help-name (concat class-with "__define")))))
7715 (setq idlw-help-link word
76959b77
S
7716 idlwave-help-do-class-struct-tag t))
7717 (t (error "This should not happen"))))
7718
7719(defun idlwave-class-or-superclass-with-tag (class tag)
7720 "Find and return the CLASS or one of its superclass with the
7721associated TAG, if any."
e08734e2 7722 (let ((sclasses (cons class (idlwave-all-class-inherits class)))
76959b77
S
7723 cl)
7724 (catch 'exit
7725 (while sclasses
7726 (setq cl (pop sclasses))
7727 (let ((tags (idlwave-class-tags cl)))
7728 (while tags
7729 (if (eq t (compare-strings tag 0 nil (car tags) 0 nil t))
4b1aaa8b 7730 (throw 'exit cl))
76959b77
S
7731 (setq tags (cdr tags))))))))
7732
05a1abfc
CD
7733
7734(defun idlwave-sysvars-reset ()
7735 (if (and (fboundp 'idlwave-shell-is-running)
52a244eb
S
7736 (idlwave-shell-is-running)
7737 idlwave-idlwave_routine_info-compiled)
05a1abfc
CD
7738 (idlwave-shell-send-command "idlwave_get_sysvars"
7739 'idlwave-process-sysvars 'hide)))
7740
7741(defun idlwave-process-sysvars ()
7742 (idlwave-shell-filter-sysvars)
7743 (setq idlwave-sint-sysvars nil
7744 idlwave-sint-sysvartags nil)
7745 (idlwave-sintern-sysvar-alist))
7746
05a1abfc 7747(defun idlwave-sintern-sysvar-alist ()
52a244eb 7748 (let ((list idlwave-system-variables-alist) entry tags)
05a1abfc
CD
7749 (while (setq entry (pop list))
7750 (setcar entry (idlwave-sintern-sysvar (car entry) 'set))
52a244eb
S
7751 (setq tags (assq 'tags entry))
7752 (if tags
4b1aaa8b
PE
7753 (setcdr tags
7754 (mapcar (lambda (x)
52a244eb
S
7755 (cons (idlwave-sintern-sysvartag (car x) 'set)
7756 (cdr x)))
7757 (cdr tags)))))))
05a1abfc
CD
7758
7759(defvar idlwave-shell-command-output)
7760(defun idlwave-shell-filter-sysvars ()
52a244eb 7761 "Get any new system variables and tags."
05a1abfc
CD
7762 (let ((text idlwave-shell-command-output)
7763 (start 0)
7764 (old idlwave-system-variables-alist)
52a244eb 7765 var tags type name class link old-entry)
05a1abfc
CD
7766 (setq idlwave-system-variables-alist nil)
7767 (while (string-match "^IDLWAVE-SYSVAR: !\\([a-zA-Z0-9_$]+\\)\\( \\(.*\\)\\)?"
7768 text start)
7769 (setq start (match-end 0)
7770 var (match-string 1 text)
4b1aaa8b 7771 tags (if (match-end 3)
52a244eb
S
7772 (idlwave-split-string (match-string 3 text))))
7773 ;; Maintain old links, if present
7774 (setq old-entry (assq (idlwave-sintern-sysvar var) old))
7775 (setq link (assq 'link old-entry))
05a1abfc 7776 (setq idlwave-system-variables-alist
4b1aaa8b
PE
7777 (cons (list var
7778 (cons
7779 'tags
7780 (mapcar (lambda (x)
7781 (cons x
7782 (cdr (assq
7783 (idlwave-sintern-sysvartag x)
52a244eb
S
7784 (cdr (assq 'tags old-entry))))))
7785 tags)) link)
05a1abfc
CD
7786 idlwave-system-variables-alist)))
7787 ;; Keep the old value if query was not successful
7788 (setq idlwave-system-variables-alist
7789 (or idlwave-system-variables-alist old))))
7790
f32b3b91
CD
7791(defun idlwave-completion-fontify-classes ()
7792 "Goto the *Completions* buffer and fontify the class info."
7793 (when (featurep 'font-lock)
9a529312 7794 (with-current-buffer "*Completions*"
f32b3b91
CD
7795 (save-excursion
7796 (goto-char (point-min))
76959b77
S
7797 (let ((buffer-read-only nil))
7798 (while (re-search-forward "\\.*<[^>]+>" nil t)
7799 (put-text-property (match-beginning 0) (match-end 0)
7800 'face 'font-lock-string-face)))))))
f32b3b91
CD
7801
7802(defun idlwave-uniquify (list)
52a244eb 7803 (let ((ht (make-hash-table :size (length list) :test 'equal)))
4b1aaa8b 7804 (delq nil
52a244eb 7805 (mapcar (lambda (x)
4b1aaa8b 7806 (unless (gethash x ht)
52a244eb
S
7807 (puthash x t ht)
7808 x))
7809 list))))
f32b3b91
CD
7810
7811(defun idlwave-after-successful-completion (type slash &optional verify)
7812 "Add `=' or `(' after successful completion of keyword and function.
7813Restore the pre-completion window configuration if possible."
7814 (cond
7815 ((eq type 'procedure)
7816 nil)
7817 ((eq type 'function)
7818 (cond
7819 ((equal idlwave-function-completion-adds-paren nil) nil)
7820 ((or (equal idlwave-function-completion-adds-paren t)
7821 (equal idlwave-function-completion-adds-paren 1))
7822 (insert "("))
7823 ((equal idlwave-function-completion-adds-paren 2)
7824 (insert "()")
7825 (backward-char 1))
7826 (t nil)))
7827 ((eq type 'keyword)
7828 (if (and idlwave-keyword-completion-adds-equal
7829 (not slash))
7830 (progn (insert "=") t)
7831 nil)))
7832
7833 ;; Restore the pre-completion window configuration if this is safe.
4b1aaa8b
PE
7834
7835 (if (or (eq verify 'force) ; force
7836 (and
f32b3b91 7837 (get-buffer-window "*Completions*") ; visible
4b1aaa8b 7838 (idlwave-local-value 'idlwave-completion-p
f32b3b91
CD
7839 "*Completions*") ; cib-buffer
7840 (eq (marker-buffer idlwave-completion-mark)
7841 (current-buffer)) ; buffer OK
7842 (equal (marker-position idlwave-completion-mark)
7843 verify))) ; pos OK
7844 (idlwave-restore-wconf-after-completion))
7845 (move-marker idlwave-completion-mark nil)
7846 (setq idlwave-before-completion-wconf nil))
7847
15e42531
CD
7848(defun idlwave-mouse-context-help (ev &optional arg)
7849 "Call `idlwave-context-help' on the clicked location."
7850 (interactive "eP")
7851 (mouse-set-point ev)
7852 (idlwave-context-help arg))
7853
7854(defvar idlwave-last-context-help-pos nil)
7855(defun idlwave-context-help (&optional arg)
7856 "Display IDL Online Help on context.
76959b77
S
7857If point is on a keyword, help for that keyword will be shown. If
7858point is on a routine name or in the argument list of a routine, help
7859for that routine will be displayed. Works for system routines and
7860keywords, it pulls up text help. For other routies and keywords,
7861visits the source file, finding help in the header (if
7862`idlwave-help-source-try-header' is non-nil) or the routine definition
7863itself."
f32b3b91 7864 (interactive "P")
15e42531
CD
7865 (idlwave-do-context-help arg))
7866
7867(defun idlwave-mouse-completion-help (ev)
7868 "Display online help about the completion at point."
7869 (interactive "eP")
52a244eb
S
7870 ;; Restore last-command for next command, to make
7871 ;; scrolling/cancelling of completions work.
15e42531
CD
7872 (setq this-command last-command)
7873 (idlwave-do-mouse-completion-help ev))
15e42531 7874
f32b3b91 7875(defun idlwave-routine-info (&optional arg external)
5a0c3f56
JB
7876 "Display a routines calling sequence and list of keywords.
7877When point is on the name a function or procedure, or in the argument
7878list of a function or procedure, this command displays a help buffer with
52a244eb 7879the information. When called with prefix arg, enforce class query.
f32b3b91
CD
7880
7881When point is on an object operator `->', display the class stored in
5a0c3f56
JB
7882this arrow, if any (see `idlwave-store-inquired-class'). With a prefix
7883arg, the class property is cleared out."
f32b3b91
CD
7884
7885 (interactive "P")
7886 (idlwave-routines)
7887 (if (string-match "->" (buffer-substring
7888 (max (point-min) (1- (point)))
7889 (min (+ 2 (point)) (point-max))))
7890 ;; Cursor is on an arrow
7891 (if (get-text-property (point) 'idlwave-class)
7892 ;; arrow has class property
7893 (if arg
7894 ;; Remove property
7895 (save-excursion
7896 (backward-char 1)
7897 (when (looking-at ".?\\(->\\)")
7898 (remove-text-properties (match-beginning 1) (match-end 1)
7899 '(idlwave-class nil face nil))
7900 (message "Class property removed from arrow")))
7901 ;; Echo class property
7902 (message "Arrow has text property identifying object to be class %s"
7903 (get-text-property (point) 'idlwave-class)))
7904 ;; No property found
7905 (message "Arrow has no class text property"))
7906
7907 ;; Not on an arrow...
7908 (let* ((idlwave-query-class nil)
7909 (idlwave-force-class-query (equal arg '(4)))
7910 (module (idlwave-what-module)))
15e42531 7911 (if (car module)
05a1abfc
CD
7912 (apply 'idlwave-display-calling-sequence
7913 (idlwave-fix-module-if-obj_new module))
e8af40ee 7914 (error "Don't know which calling sequence to show")))))
f32b3b91
CD
7915
7916(defun idlwave-resolve (&optional arg)
52a244eb 7917 "Call RESOLVE_ROUTINE on the module name at point.
f32b3b91
CD
7918Like `idlwave-routine-info', this looks for a routine call at point.
7919After confirmation in the minibuffer, it will use the shell to issue
7920a RESOLVE call for this routine, to attempt to make it defined and its
7921routine info available for IDLWAVE. If the routine is a method call,
7922both `class__method' and `class__define' will be tried.
7923With ARG, enforce query for the class of object methods."
7924 (interactive "P")
7925 (let* ((idlwave-query-class nil)
7926 (idlwave-force-class-query (equal arg '(4)))
7927 (module (idlwave-what-module))
7928 (name (idlwave-make-full-name (nth 2 module) (car module)))
7929 (type (if (eq (nth 1 module) 'pro) "pro" "function"))
7930 (resolve (read-string "Resolve: " (format "%s %s" type name)))
7931 (kwd "")
7932 class)
7933 (if (string-match "\\(pro\\|function\\)[ \t]+\\(\\(.*\\)::\\)?\\(.*\\)"
7934 resolve)
7935 (setq type (match-string 1 resolve)
4b1aaa8b 7936 class (if (match-beginning 2)
f32b3b91
CD
7937 (match-string 3 resolve)
7938 nil)
7939 name (match-string 4 resolve)))
7940 (if (string= (downcase type) "function")
7941 (setq kwd ",/is_function"))
7942
7943 (cond
7944 ((null class)
4b1aaa8b 7945 (idlwave-shell-send-command
f32b3b91
CD
7946 (format "resolve_routine,'%s'%s" (downcase name) kwd)
7947 'idlwave-update-routine-info
7948 nil t))
7949 (t
4b1aaa8b 7950 (idlwave-shell-send-command
f32b3b91 7951 (format "resolve_routine,'%s__define'%s" (downcase class) kwd)
4b1aaa8b
PE
7952 (list 'idlwave-shell-send-command
7953 (format "resolve_routine,'%s__%s'%s"
f32b3b91
CD
7954 (downcase class) (downcase name) kwd)
7955 '(idlwave-update-routine-info)
7956 nil t))))))
7957
3938cb82
S
7958(defun idlwave-find-module-this-file ()
7959 (interactive)
7960 (idlwave-find-module '(4)))
7961
f32b3b91
CD
7962(defun idlwave-find-module (&optional arg)
7963 "Find the source code of an IDL module.
5a0c3f56
JB
7964Works for modules for which IDLWAVE has routine info available.
7965The function offers as default the module name `idlwave-routine-info'
52a244eb
S
7966would use. With ARG limit to this buffer. With two prefix ARG's
7967force class query for object methods."
f32b3b91
CD
7968 (interactive "P")
7969 (let* ((idlwave-query-class nil)
52a244eb
S
7970 (idlwave-force-class-query (equal arg '(16)))
7971 (this-buffer (equal arg '(4)))
05a1abfc 7972 (module (idlwave-fix-module-if-obj_new (idlwave-what-module)))
52a244eb 7973 (default (if module
4b1aaa8b 7974 (concat (idlwave-make-full-name
52a244eb
S
7975 (nth 2 module) (car module))
7976 (if (eq (nth 1 module) 'pro) "<p>" "<f>"))
7977 "none"))
4b1aaa8b 7978 (list
52a244eb
S
7979 (idlwave-uniquify
7980 (delq nil
4b1aaa8b 7981 (mapcar (lambda (x)
52a244eb
S
7982 (if (eq 'system (car-safe (nth 3 x)))
7983 ;; Take out system routines with no source.
7984 nil
7985 (list
4b1aaa8b 7986 (concat (idlwave-make-full-name
52a244eb
S
7987 (nth 2 x) (car x))
7988 (if (eq (nth 1 x) 'pro) "<p>" "<f>")))))
7989 (if this-buffer
7990 (idlwave-save-buffer-update)
7991 (idlwave-routines))))))
f32b3b91 7992 (name (idlwave-completing-read
52a244eb
S
7993 (if (or (not this-buffer)
7994 (assoc default list))
7995 (format "Module (Default %s): " default)
7996 (format "Module in this file: "))
f32b3b91
CD
7997 list))
7998 type class)
7999 (if (string-match "\\`\\s-*\\'" name)
8000 ;; Nothing, use the default.
8001 (setq name default))
8002 (if (string-match "<[fp]>" name)
8003 (setq type (substring name -2 -1)
8004 name (substring name 0 -3)))
8005 (if (string-match "\\(.*\\)::\\(.*\\)" name)
8006 (setq class (match-string 1 name)
8007 name (match-string 2 name)))
8008 (setq name (idlwave-sintern-routine-or-method name class)
8009 class (idlwave-sintern-class class)
8010 type (cond ((equal type "f") 'fun)
8011 ((equal type "p") 'pro)
8012 (t t)))
52a244eb 8013 (idlwave-do-find-module name type class nil this-buffer)))
f32b3b91 8014
4b1aaa8b 8015(defun idlwave-do-find-module (name type class
52a244eb 8016 &optional force-source this-buffer)
f32b3b91 8017 (let ((name1 (idlwave-make-full-name class name))
4b1aaa8b 8018 source buf1 entry
f32b3b91 8019 (buf (current-buffer))
05a1abfc 8020 (pos (point))
52a244eb
S
8021 file name2)
8022 (setq entry (idlwave-best-rinfo-assq name type class (idlwave-routines)
8023 'WITH-FILE)
05a1abfc
CD
8024 source (or force-source (nth 3 entry))
8025 name2 (if (nth 2 entry)
8026 (idlwave-make-full-name (nth 2 entry) name)
775591f7 8027 name1))
4b1aaa8b 8028 (if source
52a244eb
S
8029 (setq file (idlwave-routine-source-file source)))
8030 (unless file ; Try to find it on the path.
4b1aaa8b
PE
8031 (setq file
8032 (idlwave-expand-lib-file-name
52a244eb
S
8033 (if class
8034 (format "%s__define.pro" (downcase class))
8035 (format "%s.pro" (downcase name))))))
f32b3b91
CD
8036 (cond
8037 ((or (null name) (equal name ""))
8038 (error "Abort"))
f32b3b91 8039 ((eq (car source) 'system)
4b1aaa8b 8040 (error "Source code for system routine %s is not available"
05a1abfc 8041 name2))
52a244eb 8042 ((or (not file) (not (file-regular-p file)))
e8af40ee 8043 (error "Source code for routine %s is not available"
05a1abfc 8044 name2))
52a244eb
S
8045 (t
8046 (when (not this-buffer)
4b1aaa8b 8047 (setq buf1
52a244eb
S
8048 (idlwave-find-file-noselect file 'find))
8049 (pop-to-buffer buf1 t))
15e42531 8050 (goto-char (point-max))
f32b3b91 8051 (let ((case-fold-search t))
15e42531 8052 (if (re-search-backward
f32b3b91 8053 (concat "^[ \t]*\\<"
52a244eb
S
8054 (cond ((eq type 'fun) "function")
8055 ((eq type 'pro) "pro")
f32b3b91 8056 (t "\\(pro\\|function\\)"))
4b1aaa8b 8057 "\\>[ \t]+"
05a1abfc 8058 (regexp-quote (downcase name2))
f32b3b91
CD
8059 "[^a-zA-Z0-9_$]")
8060 nil t)
8061 (goto-char (match-beginning 0))
8062 (pop-to-buffer buf)
8063 (goto-char pos)
05a1abfc 8064 (error "Could not find routine %s" name2)))))))
f32b3b91
CD
8065
8066(defun idlwave-what-module ()
8067 "Return a default module for stuff near point.
8068Used by `idlwave-routine-info' and `idlwave-find-module'."
8069 (idlwave-routines)
15e42531
CD
8070 (if (let ((case-fold-search t))
8071 (save-excursion
8072 (idlwave-beginning-of-statement)
8073 (looking-at "[ \t]*\\(pro\\|function\\)[ \t]+\\(\\([a-zA-Z0-9_$]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)\\([, \t\n]\\|$\\)")))
8074 ;; This is a function or procedure definition statement
8075 ;; We return the defined routine as module.
8076 (list
52a244eb
S
8077 (idlwave-sintern-routine-or-method (match-string-no-properties 4)
8078 (match-string-no-properties 2))
15e42531
CD
8079 (if (equal (downcase (match-string 1)) "pro") 'pro 'fun)
8080 (idlwave-sintern-class (match-string 3)))
8081
52a244eb 8082 ;; Not a definition statement - analyze precise position.
15e42531
CD
8083 (let* ((where (idlwave-where))
8084 (cw (nth 2 where))
8085 (pro (car (nth 0 where)))
8086 (func (car (nth 1 where)))
8087 (this-word (idlwave-this-word "a-zA-Z0-9$_"))
8088 (next-char (save-excursion (skip-chars-forward "a-zA-Z0-9$_")
8089 (following-char)))
8090 )
8091 (cond
8092 ((and (eq cw 'procedure)
8093 (not (equal this-word "")))
4b1aaa8b 8094 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8095 this-word (nth 2 (nth 3 where))))
8096 (list this-word 'pro
4b1aaa8b 8097 (idlwave-determine-class
15e42531
CD
8098 (cons this-word (cdr (nth 3 where)))
8099 'pro)))
4b1aaa8b 8100 ((and (eq cw 'function)
15e42531
CD
8101 (not (equal this-word ""))
8102 (or (eq next-char ?\() ; exclude arrays, vars.
8103 (looking-at "[a-zA-Z0-9_]*[ \t]*(")))
4b1aaa8b 8104 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8105 this-word (nth 2 (nth 3 where))))
8106 (list this-word 'fun
8107 (idlwave-determine-class
8108 (cons this-word (cdr (nth 3 where)))
8109 'fun)))
8110 ((and (memq cw '(function-keyword procedure-keyword))
8111 (not (equal this-word ""))
8112 (eq next-char ?\()) ; A function!
8113 (setq this-word (idlwave-sintern-routine this-word))
8114 (list this-word 'fun nil))
8115 (func
8116 (list func 'fun (idlwave-determine-class (nth 1 where) 'fun)))
8117 (pro
8118 (list pro 'pro (idlwave-determine-class (nth 0 where) 'pro)))
8119 (t nil)))))
f32b3b91 8120
05a1abfc 8121(defun idlwave-what-module-find-class ()
5a0c3f56 8122 "Call `idlwave-what-module' and find the inherited class if necessary."
05a1abfc 8123 (let* ((module (idlwave-what-module))
8d222148 8124 (class (nth 2 module)))
05a1abfc
CD
8125 (if (and (= (length module) 3)
8126 (stringp class))
8127 (list (car module)
8128 (nth 1 module)
8129 (apply 'idlwave-find-inherited-class module))
8130 module)))
8131
8132(defun idlwave-find-inherited-class (name type class)
8133 "Find the class which defines TYPE NAME and is CLASS or inherited by CLASS."
8134 (let ((entry (idlwave-best-rinfo-assoc name type class (idlwave-routines))))
8135 (if entry
8136 (nth 2 entry)
8137 class)))
8138
8139(defun idlwave-fix-module-if-obj_new (module)
4b1aaa8b 8140 "Check if MODULE points to obj_new.
52a244eb
S
8141If yes, and if the cursor is in the keyword region, change to the
8142appropriate Init method."
05a1abfc
CD
8143 (let* ((name (car module))
8144 (pos (point))
8145 (case-fold-search t)
8146 string)
8147 (if (and (stringp name)
8148 (equal (downcase name) "obj_new")
8149 (save-excursion
8150 (idlwave-beginning-of-statement)
8151 (setq string (buffer-substring (point) pos))
8152 (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8153 string)))
8154 (let ((name "Init")
8155 (class (match-string 1 string)))
8156 (setq module (list (idlwave-sintern-method "Init")
8157 'fun
8158 (idlwave-sintern-class class)))))
8159 module))
8160
4b1aaa8b 8161(defun idlwave-fix-keywords (name type class keywords
3938cb82 8162 &optional super-classes system)
52a244eb
S
8163 "Update a list of keywords.
8164Translate OBJ_NEW, adding all super-class keywords, or all keywords
5a0c3f56 8165from all classes if CLASS equals t. If SYSTEM is non-nil, don't
3938cb82 8166demand _EXTRA in the keyword list."
5e72c6b2 8167 (let ((case-fold-search t))
f32b3b91
CD
8168
8169 ;; If this is the OBJ_NEW function, try to figure out the class and use
8170 ;; the keywords from the corresponding INIT method.
5e72c6b2 8171 (if (and (equal (upcase name) "OBJ_NEW")
175069ef 8172 (derived-mode-p 'idlwave-mode 'idlwave-shell-mode))
f32b3b91
CD
8173 (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point)))
8174 (string (buffer-substring bos (point)))
8175 (case-fold-search t)
8176 class)
8177 (and (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8178 string)
8179 (setq class (idlwave-sintern-class (match-string 1 string)))
15e42531 8180 (setq idlwave-current-obj_new-class class)
4b1aaa8b
PE
8181 (setq keywords
8182 (append keywords
52a244eb
S
8183 (idlwave-entry-keywords
8184 (idlwave-rinfo-assq
8185 (idlwave-sintern-method "INIT")
8186 'fun
8187 class
8188 (idlwave-routines)) 'do-link))))))
4b1aaa8b 8189
f32b3b91
CD
8190 ;; If the class is `t', combine all keywords of all methods NAME
8191 (when (eq class t)
52a244eb
S
8192 (mapc (lambda (entry)
8193 (and
8194 (nth 2 entry) ; non-nil class
8195 (eq (nth 1 entry) type) ; correct type
4b1aaa8b
PE
8196 (setq keywords
8197 (append keywords
52a244eb
S
8198 (idlwave-entry-keywords entry 'do-link)))))
8199 (idlwave-all-assq name (idlwave-routines)))
5e72c6b2 8200 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8201
5e72c6b2 8202 ;; If we have inheritance, add all keywords from superclasses, if
52a244eb 8203 ;; the user indicated that method in `idlwave-keyword-class-inheritance'
4b1aaa8b 8204 (when (and
52a244eb 8205 super-classes
5e72c6b2
S
8206 idlwave-keyword-class-inheritance
8207 (stringp class)
4b1aaa8b 8208 (or
3938cb82
S
8209 system
8210 (assq (idlwave-sintern-keyword "_extra") keywords)
8211 (assq (idlwave-sintern-keyword "_ref_extra") keywords))
5e72c6b2
S
8212 ;; Check if one of the keyword-class regexps matches the name
8213 (let ((regexps idlwave-keyword-class-inheritance) re)
8214 (catch 'exit
8215 (while (setq re (pop regexps))
8216 (if (string-match re name) (throw 'exit t))))))
52a244eb
S
8217
8218 (loop for entry in (idlwave-routines) do
8219 (and (nth 2 entry) ; non-nil class
8220 (memq (nth 2 entry) super-classes) ; an inherited class
8221 (eq (nth 1 entry) type) ; correct type
8222 (eq (car entry) name) ; correct name
8ffcfb27
GM
8223 (mapc (lambda (k) (add-to-list 'keywords k))
8224 (idlwave-entry-keywords entry 'do-link))))
f32b3b91 8225 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8226
f32b3b91
CD
8227 ;; Return the final list
8228 keywords))
8229
15e42531 8230(defun idlwave-expand-keyword (keyword module)
2e8b9c7d 8231 "Expand KEYWORD to one of the valid keyword parameters of MODULE.
15e42531
CD
8232KEYWORD may be an exact match or an abbreviation of a keyword.
8233If the match is exact, KEYWORD itself is returned, even if there may be other
8234keywords of which KEYWORD is an abbreviation. This is necessary because some
8235system routines have keywords which are prefixes of other keywords.
8236If KEYWORD is an abbreviation of several keywords, a list of all possible
8237completions is returned.
8238If the abbreviation was unique, the correct keyword is returned.
8239If it cannot be a keyword, the function return nil.
8240If we do not know about MODULE, just return KEYWORD literally."
8241 (let* ((name (car module))
8242 (type (nth 1 module))
8243 (class (nth 2 module))
8244 (kwd (idlwave-sintern-keyword keyword))
8245 (entry (idlwave-best-rinfo-assoc name type class (idlwave-routines)))
52a244eb 8246 (kwd-alist (idlwave-entry-keywords entry))
15e42531
CD
8247 (extra (or (assq (idlwave-sintern-keyword "_EXTRA") kwd-alist)
8248 (assq (idlwave-sintern-keyword "_REF_EXTRA") kwd-alist)))
8249 (completion-ignore-case t)
8250 candidates)
4b1aaa8b 8251 (cond ((assq kwd kwd-alist)
15e42531
CD
8252 kwd)
8253 ((setq candidates (all-completions kwd kwd-alist))
8254 (if (= (length candidates) 1)
8255 (car candidates)
8256 candidates))
8257 ((and entry extra)
4b1aaa8b 8258 ;; Inheritance may cause this keyword to be correct
15e42531
CD
8259 keyword)
8260 (entry
8261 ;; We do know the function, which does not have the keyword.
8262 nil)
8263 (t
8264 ;; We do not know the function, so this just might be a correct
8265 ;; keyword - return it as it is.
8266 keyword))))
8267
b016851c
SM
8268(defvar idlwave-rinfo-mouse-map
8269 (let ((map (make-sparse-keymap)))
8270 (define-key map
8271 (if (featurep 'xemacs) [button2] [mouse-2])
8272 'idlwave-mouse-active-rinfo)
8273 (define-key map
8274 (if (featurep 'xemacs) [(shift button2)] [(shift mouse-2)])
8275 'idlwave-mouse-active-rinfo-shift)
8276 (define-key map
8277 (if (featurep 'xemacs) [button3] [mouse-3])
8278 'idlwave-mouse-active-rinfo-right)
8279 (define-key map " " 'idlwave-active-rinfo-space)
8280 (define-key map "q" 'idlwave-quit-help)
8281 map))
8282
8283(defvar idlwave-rinfo-map
8284 (let ((map (make-sparse-keymap)))
8285 (define-key map "q" 'idlwave-quit-help)
8286 map))
8287
15e42531
CD
8288(defvar idlwave-popup-source nil)
8289(defvar idlwave-rinfo-marker (make-marker))
8290
8291(defun idlwave-quit-help ()
8292 (interactive)
8293 (let ((ri-window (get-buffer-window "*Help*"))
8294 (olh-window (get-buffer-window "*IDLWAVE Help*")))
8295 (when (and olh-window
8296 (fboundp 'idlwave-help-quit))
8297 (select-window olh-window)
8298 (idlwave-help-quit))
8299 (when (window-live-p ri-window)
8300 (delete-window ri-window))))
f32b3b91 8301
05a1abfc
CD
8302(defun idlwave-display-calling-sequence (name type class
8303 &optional initial-class)
f32b3b91 8304 ;; Display the calling sequence of module NAME, type TYPE in class CLASS.
05a1abfc
CD
8305 (let* ((initial-class (or initial-class class))
8306 (entry (or (idlwave-best-rinfo-assq name type class
15e42531 8307 (idlwave-routines))
4b1aaa8b 8308 (idlwave-rinfo-assq name type class
15e42531 8309 idlwave-unresolved-routines)))
f32b3b91
CD
8310 (name (or (car entry) name))
8311 (class (or (nth 2 entry) class))
05a1abfc 8312 (superclasses (idlwave-all-class-inherits initial-class))
15e42531
CD
8313 (twins (idlwave-routine-twins entry))
8314 (dtwins (idlwave-study-twins twins))
8315 (all dtwins)
52a244eb 8316 (system (eq (car (nth 3 entry)) 'system))
f32b3b91 8317 (calling-seq (nth 4 entry))
52a244eb
S
8318 (keywords (idlwave-entry-keywords entry 'do-link))
8319 (html-file (car (nth 5 entry)))
15e42531 8320 (help-echo-kwd
52a244eb 8321 "Button2: Insert KEYWORD (SHIFT=`/KEYWORD') | Button3: Online Help ")
15e42531 8322 (help-echo-use
52a244eb 8323 "Button2/3: Online Help")
15e42531 8324 (help-echo-src
52a244eb 8325 "Button2: Jump to source and back | Button3: Source in Help window.")
05a1abfc
CD
8326 (help-echo-class
8327 "Button2: Display info about same method in superclass")
f32b3b91 8328 (col 0)
52a244eb 8329 (data (list name type class (current-buffer) nil initial-class))
f32b3b91 8330 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
f66f03de 8331 (face 'idlwave-help-link)
15e42531 8332 beg props win cnt total)
4b1aaa8b 8333 ;; Fix keywords, but don't add chained super-classes, since these
52a244eb 8334 ;; are shown separately for that super-class
f32b3b91
CD
8335 (setq keywords (idlwave-fix-keywords name type class keywords))
8336 (cond
8337 ((null entry)
05a1abfc
CD
8338 (error "No %s %s known %s" type name
8339 (if initial-class (concat "in class " initial-class) "")))
f32b3b91 8340 ((or (null name) (equal name ""))
e8af40ee 8341 (error "No function or procedure call at point"))
f32b3b91 8342 ((null calling-seq)
52a244eb 8343 (error "Calling sequence of %s %s not available" type name))
f32b3b91 8344 (t
9a529312
SM
8345 (move-marker idlwave-rinfo-marker (point))
8346 (with-current-buffer (get-buffer-create "*Help*")
15e42531 8347 (use-local-map idlwave-rinfo-map)
f32b3b91
CD
8348 (setq buffer-read-only nil)
8349 (erase-buffer)
8350 (set (make-local-variable 'idlwave-popup-source) nil)
15e42531
CD
8351 (set (make-local-variable 'idlwave-current-obj_new-class)
8352 idlwave-current-obj_new-class)
05a1abfc
CD
8353 (when superclasses
8354 (setq props (list 'mouse-face 'highlight
8355 km-prop idlwave-rinfo-mouse-map
8356 'help-echo help-echo-class
8357 'data (cons 'class data)))
8358 (let ((classes (cons initial-class superclasses)) c)
8359 (insert "Classes: ")
8360 (while (setq c (pop classes))
8361 (insert " ")
8362 (setq beg (point))
8363 (insert c)
8364 (if (equal (downcase c) (downcase class))
8365 (add-text-properties beg (point) (list 'face 'bold))
52a244eb 8366 ;; If Method exists in a different class link it
05a1abfc
CD
8367 (if (idlwave-rinfo-assq name type c (idlwave-routines))
8368 (add-text-properties beg (point) props))))
8369 (insert "\n")))
52a244eb
S
8370 (setq props (list 'mouse-face 'highlight
8371 km-prop idlwave-rinfo-mouse-map
8372 'help-echo help-echo-use
8373 'data (cons 'usage data)))
4b1aaa8b 8374 (if html-file (setq props (append (list 'face face 'link html-file)
52a244eb 8375 props)))
f32b3b91
CD
8376 (insert "Usage: ")
8377 (setq beg (point))
8378 (insert (if class
52a244eb
S
8379 (format calling-seq class name class name class name)
8380 (format calling-seq name name name name))
f32b3b91
CD
8381 "\n")
8382 (add-text-properties beg (point) props)
4b1aaa8b 8383
f32b3b91
CD
8384 (insert "Keywords:")
8385 (if (null keywords)
8386 (insert " No keywords accepted.")
8387 (setq col 9)
8ffcfb27 8388 (mapc
f32b3b91 8389 (lambda (x)
4b1aaa8b 8390 (if (>= (+ col 1 (length (car x)))
f32b3b91
CD
8391 (window-width))
8392 (progn
8393 (insert "\n ")
8394 (setq col 9)))
8395 (insert " ")
8396 (setq beg (point)
52a244eb 8397 ;; Relevant keywords already have link property attached
f32b3b91 8398 props (list 'mouse-face 'highlight
15e42531 8399 km-prop idlwave-rinfo-mouse-map
f32b3b91 8400 'data (cons 'keyword data)
15e42531 8401 'help-echo help-echo-kwd
f32b3b91 8402 'keyword (car x)))
52a244eb 8403 (if system (setq props (append (list 'face face) props)))
f32b3b91
CD
8404 (insert (car x))
8405 (add-text-properties beg (point) props)
8406 (setq col (+ col 1 (length (car x)))))
8407 keywords))
4b1aaa8b 8408
15e42531 8409 (setq cnt 1 total (length all))
52a244eb 8410 ;; Here entry is (key file (list of type-conses))
15e42531
CD
8411 (while (setq entry (pop all))
8412 (setq props (list 'mouse-face 'highlight
8413 km-prop idlwave-rinfo-mouse-map
8414 'help-echo help-echo-src
52a244eb
S
8415 'source (list (car (car (nth 2 entry))) ;type
8416 (nth 1 entry)
8417 nil
8418 (cdr (car (nth 2 entry))))
15e42531
CD
8419 'data (cons 'source data)))
8420 (idlwave-insert-source-location
4b1aaa8b 8421 (format "\n%-8s %s"
15e42531
CD
8422 (if (equal cnt 1)
8423 (if (> total 1) "Sources:" "Source:")
8424 "")
8425 (if (> total 1) "- " ""))
8426 entry props)
8427 (incf cnt)
8428 (when (and all (> cnt idlwave-rinfo-max-source-lines))
8429 ;; No more source lines, please
4b1aaa8b 8430 (insert (format
15e42531
CD
8431 "\n Source information truncated to %d entries."
8432 idlwave-rinfo-max-source-lines))
8433 (setq all nil)))
10c8e594 8434 (goto-char (point-min))
f32b3b91
CD
8435 (setq buffer-read-only t))
8436 (display-buffer "*Help*")
8437 (if (and (setq win (get-buffer-window "*Help*"))
8438 idlwave-resize-routine-help-window)
8439 (progn
8440 (let ((ww (selected-window)))
8441 (unwind-protect
8442 (progn
8443 (select-window win)
4b1aaa8b 8444 (enlarge-window (- (/ (frame-height) 2)
f32b3b91
CD
8445 (window-height)))
8446 (shrink-window-if-larger-than-buffer))
8447 (select-window ww)))))))))
8448
15e42531
CD
8449(defun idlwave-insert-source-location (prefix entry &optional file-props)
8450 "Insert a source location into the routine info buffer.
5a0c3f56
JB
8451Start line with PREFIX. If a file name is inserted, add FILE-PROPS
8452to it."
15e42531
CD
8453 (let* ((key (car entry))
8454 (file (nth 1 entry))
8455 (types (nth 2 entry))
52a244eb
S
8456 (shell-flag (assq 'compiled types))
8457 (buffer-flag (assq 'buffer types))
8458 (user-flag (assq 'user types))
8459 (lib-flag (assq 'lib types))
8460 (ndupl (or (and buffer-flag (idlwave-count-memq 'buffer types))
8461 (and user-flag (idlwave-count-memq 'user types))
8462 (and lib-flag (idlwave-count-memq 'lib types))
15e42531
CD
8463 1))
8464 (doflags t)
8465 beg special)
8466
8467 (insert prefix)
8468
8469 (cond
8470 ((eq key 'system)
8471 (setq doflags nil)
52a244eb
S
8472 (insert "System "))
8473
15e42531
CD
8474 ((eq key 'builtin)
8475 (setq doflags nil)
52a244eb
S
8476 (insert "Builtin "))
8477
15e42531 8478 ((and (not file) shell-flag)
52a244eb
S
8479 (insert "Unresolved"))
8480
4b1aaa8b 8481 ((null file)
52a244eb 8482 (insert "ERROR"))
4b1aaa8b 8483
15e42531
CD
8484 ((idlwave-syslib-p file)
8485 (if (string-match "obsolete" (file-name-directory file))
52a244eb
S
8486 (insert "Obsolete ")
8487 (insert "SystemLib ")))
8488
8489 ;; New special syntax: taken directly from routine-info for
8490 ;; library catalog routines
8491 ((setq special (or (cdr lib-flag) (cdr user-flag)))
8492 (insert (format "%-10s" special)))
8493
8494 ;; Old special syntax: a matching regexp
8495 ((setq special (idlwave-special-lib-test file))
8496 (insert (format "%-10s" special)))
4b1aaa8b 8497
52a244eb 8498 ;; Catch-all with file
15e42531 8499 ((idlwave-lib-p file) (insert "Library "))
52a244eb
S
8500
8501 ;; Sanity catch all
15e42531
CD
8502 (t (insert "Other ")))
8503
8504 (when doflags
8505 (insert (concat
8506 " ["
52a244eb
S
8507 (if lib-flag "L" "-")
8508 (if user-flag "C" "-")
15e42531
CD
8509 (if shell-flag "S" "-")
8510 (if buffer-flag "B" "-")
8511 "] ")))
4b1aaa8b 8512 (when (> ndupl 1)
15e42531
CD
8513 (setq beg (point))
8514 (insert (format "(%dx) " ndupl))
8515 (add-text-properties beg (point) (list 'face 'bold)))
8516 (when (and file (not (equal file "")))
8517 (setq beg (point))
8518 (insert (apply 'abbreviate-file-name
8519 (if (featurep 'xemacs) (list file t) (list file))))
8520 (if file-props
8521 (add-text-properties beg (point) file-props)))))
8522
8523(defun idlwave-special-lib-test (file)
8524 "Check the path of FILE against the regexps which define special libs.
8525Return the name of the special lib if there is a match."
8526 (let ((alist idlwave-special-lib-alist)
8527 entry rtn)
8528 (cond
8529 ((stringp file)
8530 (while (setq entry (pop alist))
8531 (if (string-match (car entry) file)
8532 (setq rtn (cdr entry)
8533 alist nil)))
8534 rtn)
8535 (t nil))))
4b1aaa8b 8536
f32b3b91
CD
8537(defun idlwave-mouse-active-rinfo-right (ev)
8538 (interactive "e")
8539 (idlwave-mouse-active-rinfo ev 'right))
8540
15e42531 8541(defun idlwave-mouse-active-rinfo-shift (ev)
f32b3b91 8542 (interactive "e")
15e42531
CD
8543 (idlwave-mouse-active-rinfo ev nil 'shift))
8544
8545(defun idlwave-active-rinfo-space ()
8546 (interactive)
8547 (idlwave-mouse-active-rinfo nil 'right))
8548
8549(defun idlwave-mouse-active-rinfo (ev &optional right shift)
5a0c3f56 8550 "Do the mouse actions in the routine info buffer.
15e42531
CD
8551Optional args RIGHT and SHIFT indicate, if mouse-3 was used, and if SHIFT
8552was pressed."
8553 (interactive "e")
8554 (if ev (mouse-set-point ev))
4b1aaa8b 8555 (let (data id name type class buf bufwin source link keyword
3938cb82 8556 word initial-class)
f32b3b91 8557 (setq data (get-text-property (point) 'data)
15e42531 8558 source (get-text-property (point) 'source)
f32b3b91 8559 keyword (get-text-property (point) 'keyword)
52a244eb 8560 link (get-text-property (point) 'link)
f32b3b91 8561 id (car data)
15e42531 8562 name (nth 1 data) type (nth 2 data) class (nth 3 data)
f32b3b91 8563 buf (nth 4 data)
05a1abfc
CD
8564 initial-class (nth 6 data)
8565 word (idlwave-this-word)
f32b3b91 8566 bufwin (get-buffer-window buf t))
52a244eb
S
8567
8568 (cond ((eq id 'class) ; Switch class being displayed
05a1abfc 8569 (if (window-live-p bufwin) (select-window bufwin))
4b1aaa8b 8570 (idlwave-display-calling-sequence
05a1abfc 8571 (idlwave-sintern-method name)
4b1aaa8b 8572 type (idlwave-sintern-class word)
05a1abfc 8573 initial-class))
52a244eb
S
8574 ((eq id 'usage) ; Online help on this routine
8575 (idlwave-online-help link name type class))
8576 ((eq id 'source) ; Source in help or buffer
8577 (if right ; In help
15e42531
CD
8578 (let ((idlwave-extra-help-function 'idlwave-help-with-source)
8579 (idlwave-help-source-try-header nil)
52a244eb 8580 ;; Fake idlwave-routines so help will find the right entry
15e42531 8581 (idlwave-routines
52a244eb 8582 (list (list name type class source ""))))
15e42531 8583 (idlwave-help-get-special-help name type class nil))
52a244eb 8584 ;; Otherwise just pop to the source
f32b3b91
CD
8585 (setq idlwave-popup-source (not idlwave-popup-source))
8586 (if idlwave-popup-source
8587 (condition-case err
15e42531 8588 (idlwave-do-find-module name type class source)
f32b3b91
CD
8589 (error
8590 (setq idlwave-popup-source nil)
8591 (if (window-live-p bufwin) (select-window bufwin))
8592 (error (nth 1 err))))
8593 (if bufwin
8594 (select-window bufwin)
15e42531
CD
8595 (pop-to-buffer buf))
8596 (goto-char (marker-position idlwave-rinfo-marker)))))
f32b3b91
CD
8597 ((eq id 'keyword)
8598 (if right
52a244eb 8599 (idlwave-online-help link name type class keyword)
15e42531
CD
8600 (idlwave-rinfo-insert-keyword keyword buf shift))))))
8601
8602(defun idlwave-rinfo-insert-keyword (keyword buffer &optional shift)
8603 "Insert KEYWORD in BUFFER. Make sure buffer is displayed in a window."
8604 (let ((bwin (get-buffer-window buffer)))
8605 (if idlwave-complete-empty-string-as-lower-case
8606 (setq keyword (downcase keyword)))
8607 (if bwin
8608 (select-window bwin)
8609 (pop-to-buffer buffer)
8610 (setq bwin (get-buffer-window buffer)))
8611 (if (eq (preceding-char) ?/)
8612 (insert keyword)
4b1aaa8b 8613 (unless (save-excursion
15e42531 8614 (re-search-backward
4b1aaa8b 8615 "[(,][ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\)?[ \t]*\\="
15e42531
CD
8616 (min (- (point) 100) (point-min)) t))
8617 (insert ", "))
8618 (if shift (insert "/"))
8619 (insert keyword)
8620 (if (and (not shift)
8621 idlwave-keyword-completion-adds-equal)
8622 (insert "=")))))
8623
8624(defun idlwave-list-buffer-load-path-shadows (&optional arg)
8625 "List the load path shadows of all routines defined in current buffer."
8626 (interactive "P")
8627 (idlwave-routines)
175069ef 8628 (if (derived-mode-p 'idlwave-mode)
15e42531
CD
8629 (idlwave-list-load-path-shadows
8630 nil (idlwave-update-current-buffer-info 'save-buffer)
8631 "in current buffer")
8632 (error "Current buffer is not in idlwave-mode")))
8633
8634(defun idlwave-list-shell-load-path-shadows (&optional arg)
8635 "List the load path shadows of all routines compiled under the shell.
8636This is very useful for checking an IDL application. Just compile the
8637application, do RESOLVE_ALL, and `C-c C-i' to compile all referenced
8638routines and update IDLWAVE internal info. Then check for shadowing
8639with this command."
8640 (interactive "P")
8641 (cond
8642 ((or (not (fboundp 'idlwave-shell-is-running))
8643 (not (idlwave-shell-is-running)))
8644 (error "Shell is not running"))
8645 ((null idlwave-compiled-routines)
e8af40ee 8646 (error "No compiled routines. Maybe you need to update with `C-c C-i'"))
15e42531
CD
8647 (t
8648 (idlwave-list-load-path-shadows nil idlwave-compiled-routines
8649 "in the shell"))))
8650
8651(defun idlwave-list-all-load-path-shadows (&optional arg)
8652 "List the load path shadows of all routines known to IDLWAVE."
8653 (interactive "P")
8654 (idlwave-list-load-path-shadows nil nil "globally"))
8655
8d222148
SM
8656(defvar idlwave-sort-prefer-buffer-info t
8657 "Internal variable used to influence `idlwave-routine-twin-compare'.")
8658
15e42531
CD
8659(defun idlwave-list-load-path-shadows (arg &optional special-routines loc)
8660 "List the routines which are defined multiple times.
8661Search the information IDLWAVE has about IDL routines for multiple
8662definitions.
8663When SPECIAL-ROUTINES in non-nil, only look for shadows of these routines.
8664
8665When IDL hits a routine call which is not defined, it will search on
5a0c3f56
JB
8666the load path in order to find a definition. The output of this command
8667can be used to detect possible name clashes during this process."
15e42531 8668 (idlwave-routines) ; Make sure everything is loaded.
52a244eb 8669 (unless (or idlwave-user-catalog-routines idlwave-library-catalog-routines)
4b1aaa8b 8670 (or (y-or-n-p
52a244eb 8671 "You don't have any user or library catalogs. Continue anyway? ")
15e42531
CD
8672 (error "Abort")))
8673 (let* ((routines (append idlwave-system-routines
8674 idlwave-compiled-routines
52a244eb
S
8675 idlwave-library-catalog-routines
8676 idlwave-user-catalog-routines
15e42531
CD
8677 idlwave-buffer-routines
8678 nil))
8679 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
8680 (keymap (make-sparse-keymap))
8681 (props (list 'mouse-face 'highlight
8682 km-prop keymap
4b1aaa8b 8683 'help-echo "Mouse2: Find source"))
15e42531 8684 (nroutines (length (or special-routines routines)))
f66f03de 8685 (step (/ nroutines 100))
15e42531 8686 (n 0)
15e42531
CD
8687 (cnt 0)
8688 (idlwave-sort-prefer-buffer-info nil)
8689 routine twins dtwins twin done props1 lroutines)
8690
8691 (if special-routines
8692 ;; Just looking for shadows of a few special routines
8693 (setq lroutines routines
8694 routines special-routines))
8695
8696 (message "Sorting routines...")
8697 (setq routines (sort routines
8698 (lambda (a b)
8699 (string< (downcase (idlwave-make-full-name
8700 (nth 2 a) (car a)))
8701 (downcase (idlwave-make-full-name
8702 (nth 2 b) (car b)))))))
8703 (message "Sorting routines...done")
8704
8705 (define-key keymap (if (featurep 'xemacs) [(button2)] [(mouse-2)])
4b1aaa8b 8706 (lambda (ev)
15e42531
CD
8707 (interactive "e")
8708 (mouse-set-point ev)
8709 (apply 'idlwave-do-find-module
8710 (get-text-property (point) 'find-args))))
8711 (define-key keymap [(return)]
4b1aaa8b 8712 (lambda ()
15e42531
CD
8713 (interactive)
8714 (apply 'idlwave-do-find-module
8715 (get-text-property (point) 'find-args))))
8716 (message "Compiling list...( 0%%)")
9a529312 8717 (with-current-buffer (get-buffer-create "*Shadows*")
15e42531
CD
8718 (setq buffer-read-only nil)
8719 (erase-buffer)
8720 (while (setq routine (pop routines))
f66f03de
S
8721 (if (= (mod (setq n (1+ n)) step) 0)
8722 (message "Compiling list...(%2d%%)" (/ (* n 100) nroutines)))
8723
15e42531
CD
8724 ;; Get a list of all twins
8725 (setq twins (idlwave-routine-twins routine (or lroutines routines)))
8726 (if (memq routine done)
8727 (setq dtwins nil)
8728 (setq dtwins (idlwave-study-twins twins)))
5e72c6b2 8729 ;; Mark all twins as dealt with
15e42531
CD
8730 (setq done (append twins done))
8731 (when (or (> (length dtwins) 1)
52a244eb
S
8732 (> (idlwave-count-memq 'lib (nth 2 (car dtwins))) 1)
8733 (> (idlwave-count-memq 'user (nth 2 (car dtwins))) 1)
8734 (> (idlwave-count-memq 'buffer (nth 2 (car dtwins))) 1))
15e42531
CD
8735 (incf cnt)
8736 (insert (format "\n%s%s"
4b1aaa8b 8737 (idlwave-make-full-name (nth 2 routine)
52a244eb 8738 (car routine))
15e42531
CD
8739 (if (eq (nth 1 routine) 'fun) "()" "")))
8740 (while (setq twin (pop dtwins))
8741 (setq props1 (append (list 'find-args
4b1aaa8b
PE
8742 (list (nth 0 routine)
8743 (nth 1 routine)
52a244eb 8744 (nth 2 routine)))
15e42531
CD
8745 props))
8746 (idlwave-insert-source-location "\n - " twin props1))))
8747 (goto-char (point-min))
8748 (setq buffer-read-only t))
8749 (setq loc (or loc ""))
8750 (if (> cnt 0)
8751 (progn
8752 (display-buffer (get-buffer "*Shadows*"))
8753 (message "%d case%s of shadowing found %s"
8754 cnt (if (= cnt 1) "" "s") loc))
8755 (message "No shadowing conflicts found %s" loc))))
8756
8757(defun idlwave-print-source (routine)
8758 (let* ((source (nth 3 routine))
8759 (stype (car source))
52a244eb
S
8760 (sfile (idlwave-routine-source-file source)))
8761 (if (idlwave-syslib-p sfile) (setq stype 'syslib))
15e42531
CD
8762 (if (and (eq stype 'compiled)
8763 (or (not (stringp sfile))
8764 (not (string-match "\\S-" sfile))))
8765 (setq stype 'unresolved))
4b1aaa8b 8766 (princ (format " %-10s %s\n"
15e42531
CD
8767 stype
8768 (if sfile sfile "No source code available")))))
8769
8770(defun idlwave-routine-twins (entry &optional list)
8771 "Return all twin entries of ENTRY in LIST.
8772LIST defaults to `idlwave-routines'.
8773Twin entries are those which have the same name, type, and class.
8774ENTRY will also be returned, as the first item of this list."
8775 (let* ((name (car entry))
8776 (type (nth 1 entry))
8777 (class (nth 2 entry))
8778 (candidates (idlwave-all-assq name (or list (idlwave-routines))))
8779 twins candidate)
8780 (while (setq candidate (pop candidates))
8781 (if (and (not (eq candidate entry))
8782 (eq type (nth 1 candidate))
8783 (eq class (nth 2 candidate)))
8784 (push candidate twins)))
4b1aaa8b 8785 (if (setq candidate (idlwave-rinfo-assq name type class
15e42531
CD
8786 idlwave-unresolved-routines))
8787 (push candidate twins))
8788 (cons entry (nreverse twins))))
8789
8790(defun idlwave-study-twins (entries)
4b1aaa8b 8791 "Return dangerous twins of first entry in ENTRIES.
52a244eb
S
8792Dangerous twins are routines with same name, but in different files on
8793the load path. If a file is in the system library and has an entry in
8794the `idlwave-system-routines' list, we omit the latter as
8795non-dangerous because many IDL routines are implemented as library
8796routines, and may have been scanned."
15e42531 8797 (let* ((entry (car entries))
e7c4fb1e 8798 (idlwave-twin-name (car entry)) ;
15e42531 8799 (type (nth 1 entry)) ; Must be bound for
e2a9c0bc 8800 (idlwave-twin-class (nth 2 entry)) ; idlwave-routine-twin-compare
15e42531 8801 (cnt 0)
52a244eb 8802 source type type-cons file alist syslibp key)
15e42531
CD
8803 (while (setq entry (pop entries))
8804 (incf cnt)
8805 (setq source (nth 3 entry)
8806 type (car source)
52a244eb
S
8807 type-cons (cons type (nth 3 source))
8808 file (idlwave-routine-source-file source))
8809
15e42531
CD
8810 ;; Make KEY to index entry properly
8811 (setq key (cond ((eq type 'system) type)
8812 (file (file-truename file))
8813 (t 'unresolved)))
52a244eb
S
8814
8815 ;; Check for an entry in the system library
4b1aaa8b 8816 (if (and file
15e42531
CD
8817 (not syslibp)
8818 (idlwave-syslib-p file))
15e42531 8819 (setq syslibp t))
4b1aaa8b 8820
52a244eb
S
8821 ;; If there's more than one matching entry for the same file, just
8822 ;; append the type-cons to the type list.
15e42531 8823 (if (setq entry (assoc key alist))
52a244eb
S
8824 (push type-cons (nth 2 entry))
8825 (push (list key file (list type-cons)) alist)))
4b1aaa8b 8826
15e42531 8827 (setq alist (nreverse alist))
4b1aaa8b 8828
15e42531 8829 (when syslibp
52a244eb
S
8830 ;; File is in system *library* - remove any 'system entry
8831 (setq alist (delq (assq 'system alist) alist)))
4b1aaa8b 8832
52a244eb
S
8833 ;; If 'system remains and we've scanned the syslib, it's a builtin
8834 ;; (rather than a !DIR/lib/.pro file bundled as source).
15e42531
CD
8835 (when (and (idlwave-syslib-scanned-p)
8836 (setq entry (assoc 'system alist)))
8837 (setcar entry 'builtin))
8838 (sort alist 'idlwave-routine-twin-compare)))
8839
8d222148
SM
8840;; FIXME: Dynamically scoped vars need to use the `idlwave-' prefix.
8841;; (defvar type)
15e42531
CD
8842(defmacro idlwave-xor (a b)
8843 `(and (or ,a ,b)
8844 (not (and ,a ,b))))
8845
8846(defun idlwave-routine-entry-compare (a b)
5a0c3f56
JB
8847 "Compare two routine info entries for sorting.
8848This is the general case. It first compares class, names, and type.
8849If it turns out that A and B are twins (same name, class, and type),
8850calls another routine which compares twins on the basis of their file
8851names and path locations."
15e42531
CD
8852 (let ((name (car a)) (type (nth 1 a)) (class (nth 2 a)))
8853 (cond
8854 ((not (equal (idlwave-downcase-safe class)
8855 (idlwave-downcase-safe (nth 2 b))))
8856 ;; Class decides
8857 (cond ((null (nth 2 b)) nil)
8858 ((null class) t)
8859 (t (string< (downcase class) (downcase (nth 2 b))))))
8860 ((not (equal (downcase name) (downcase (car b))))
8861 ;; Name decides
8862 (string< (downcase name) (downcase (car b))))
8863 ((not (eq type (nth 1 b)))
8864 ;; Type decides
8865 (< (if (eq type 'fun) 1 0) (if (eq (nth 1 b) 'fun) 1 0)))
4b1aaa8b 8866 (t
15e42531
CD
8867 ;; A and B are twins - so the decision is more complicated.
8868 ;; Call twin-compare with the proper arguments.
8869 (idlwave-routine-entry-compare-twins a b)))))
8870
8871(defun idlwave-routine-entry-compare-twins (a b)
5a0c3f56
JB
8872 "Compare two routine entries, under the assumption that they are twins.
8873This basically calls `idlwave-routine-twin-compare' with the correct args."
e7c4fb1e 8874 (let* ((idlwave-twin-name (car a))
e2a9c0bc
GM
8875 (type (nth 1 a))
8876 (idlwave-twin-class (nth 2 a)) ; used in idlwave-routine-twin-compare
52a244eb
S
8877 (asrc (nth 3 a))
8878 (atype (car asrc))
8879 (bsrc (nth 3 b))
8880 (btype (car bsrc))
8881 (afile (idlwave-routine-source-file asrc))
8882 (bfile (idlwave-routine-source-file bsrc)))
15e42531
CD
8883 (idlwave-routine-twin-compare
8884 (if (stringp afile)
8885 (list (file-truename afile) afile (list atype))
8886 (list atype afile (list atype)))
8887 (if (stringp bfile)
8888 (list (file-truename bfile) bfile (list btype))
e2a9c0bc 8889 (list btype bfile (list btype))))))
15e42531 8890
627e0a14 8891;; Bound in idlwave-study-twins,idlwave-routine-entry-compare-twins.
e2a9c0bc 8892(defvar idlwave-twin-class)
e7c4fb1e 8893(defvar idlwave-twin-name)
627e0a14 8894
15e42531
CD
8895(defun idlwave-routine-twin-compare (a b)
8896 "Compare two routine twin entries for sorting.
8897In here, A and B are not normal routine info entries, but special
8898lists (KEY FILENAME (TYPES...)).
e2a9c0bc 8899This expects NAME TYPE IDLWAVE-TWIN-CLASS to be bound to the right values."
15e42531
CD
8900 (let* (;; Dis-assemble entries
8901 (akey (car a)) (bkey (car b))
8902 (afile (nth 1 a)) (bfile (nth 1 b))
8903 (atypes (nth 2 a)) (btypes (nth 2 b))
8904 ;; System routines?
8905 (asysp (memq akey '(builtin system)))
8906 (bsysp (memq bkey '(builtin system)))
8907 ;; Compiled routines?
8908 (acompp (memq 'compiled atypes))
8909 (bcompp (memq 'compiled btypes))
8910 ;; Unresolved?
8911 (aunresp (or (eq akey 'unresolved)
8912 (and acompp (not afile))))
8913 (bunresp (or (eq bkey 'unresolved)
8914 (and bcompp (not bfile))))
8915 ;; Buffer info available?
8916 (abufp (memq 'buffer atypes))
8917 (bbufp (memq 'buffer btypes))
8918 ;; On search path?
8919 (tpath-alist (idlwave-true-path-alist))
52a244eb
S
8920 (apathp (and (stringp akey)
8921 (assoc (file-name-directory akey) tpath-alist)))
4b1aaa8b 8922 (bpathp (and (stringp bkey)
52a244eb 8923 (assoc (file-name-directory bkey) tpath-alist)))
15e42531
CD
8924 ;; How early on search path? High number means early since we
8925 ;; measure the tail of the path list
8926 (anpath (length (memq apathp tpath-alist)))
8927 (bnpath (length (memq bpathp tpath-alist)))
8928 ;; Look at file names
8929 (aname (if (stringp afile) (downcase (file-name-nondirectory afile)) ""))
8930 (bname (if (stringp bfile) (downcase (file-name-nondirectory bfile)) ""))
e2a9c0bc
GM
8931 (fname-re (if idlwave-twin-class
8932 (format "\\`%s__\\(%s\\|define\\)\\.pro\\'"
8933 (regexp-quote (downcase idlwave-twin-class))
e7c4fb1e
GM
8934 (regexp-quote (downcase idlwave-twin-name)))
8935 (format "\\`%s\\.pro" (regexp-quote (downcase idlwave-twin-name)))))
15e42531
CD
8936 ;; Is file name derived from the routine name?
8937 ;; Method file or class definition file?
8938 (anamep (string-match fname-re aname))
e2a9c0bc
GM
8939 (adefp (and idlwave-twin-class anamep
8940 (string= "define" (match-string 1 aname))))
15e42531 8941 (bnamep (string-match fname-re bname))
e2a9c0bc
GM
8942 (bdefp (and idlwave-twin-class bnamep
8943 (string= "define" (match-string 1 bname)))))
15e42531
CD
8944
8945 ;; Now: follow JD's ideas about sorting. Looks really simple now,
8946 ;; doesn't it? The difficult stuff is hidden above...
8947 (cond
8948 ((idlwave-xor asysp bsysp) asysp) ; System entries first
8949 ((idlwave-xor aunresp bunresp) bunresp) ; Unresolved last
8950 ((and idlwave-sort-prefer-buffer-info
8951 (idlwave-xor abufp bbufp)) abufp) ; Buffers before non-buffers
8952 ((idlwave-xor acompp bcompp) acompp) ; Compiled entries
8953 ((idlwave-xor apathp bpathp) apathp) ; Library before non-library
8954 ((idlwave-xor anamep bnamep) anamep) ; Correct file names first
e2a9c0bc 8955 ((and idlwave-twin-class anamep bnamep ; both file names match ->
15e42531
CD
8956 (idlwave-xor adefp bdefp)) bdefp) ; __define after __method
8957 ((> anpath bnpath) t) ; Who is first on path?
8958 (t nil)))) ; Default
8959
52a244eb 8960(defun idlwave-routine-source-file (source)
4b1aaa8b 8961 (if (nth 2 source)
52a244eb
S
8962 (expand-file-name (nth 1 source) (nth 2 source))
8963 (nth 1 source)))
8964
15e42531
CD
8965(defun idlwave-downcase-safe (string)
8966 "Donwcase if string, else return unchanged."
8967 (if (stringp string)
8968 (downcase string)
8969 string))
8970
8971(defun idlwave-count-eq (elt list)
8972 "How often is ELT in LIST?"
8973 (length (delq nil (mapcar (lambda (x) (eq x elt)) list))))
8974
52a244eb
S
8975(defun idlwave-count-memq (elt alist)
8976 "How often is ELT a key in ALIST?"
8977 (length (delq nil (mapcar (lambda (x) (eq (car x) elt)) alist))))
8978
15e42531 8979(defun idlwave-syslib-p (file)
52a244eb 8980 "Non-nil if FILE is in the system library."
15e42531
CD
8981 (let* ((true-syslib (file-name-as-directory
8982 (file-truename
8983 (expand-file-name "lib" (idlwave-sys-dir)))))
8984 (true-file (file-truename file)))
8985 (string-match (concat "^" (regexp-quote true-syslib)) true-file)))
8986
8987(defun idlwave-lib-p (file)
5a0c3f56 8988 "Non-nil if FILE is in the library."
15e42531
CD
8989 (let ((true-dir (file-name-directory (file-truename file))))
8990 (assoc true-dir (idlwave-true-path-alist))))
8991
52a244eb
S
8992(defun idlwave-path-alist-add-flag (list-entry flag)
8993 "Add a flag to the path list entry, if not set."
8994 (let ((flags (cdr list-entry)))
8995 (add-to-list 'flags flag)
8996 (setcdr list-entry flags)))
8997
8998(defun idlwave-path-alist-remove-flag (list-entry flag)
8999 "Remove a flag to the path list entry, if set."
9000 (let ((flags (delq flag (cdr list-entry))))
9001 (setcdr list-entry flags)))
9002
15e42531
CD
9003(defun idlwave-true-path-alist ()
9004 "Return `idlwave-path-alist' alist with true-names.
52a244eb 9005Info is cached, but relies on the functions setting `idlwave-path-alist'
15e42531
CD
9006to reset the variable `idlwave-true-path-alist' to nil."
9007 (or idlwave-true-path-alist
9008 (setq idlwave-true-path-alist
9009 (mapcar (lambda(x) (cons
9010 (file-name-as-directory
9011 (file-truename
9012 (directory-file-name
9013 (car x))))
9014 (cdr x)))
9015 idlwave-path-alist))))
9016
9017(defun idlwave-syslib-scanned-p ()
9018 "Non-nil if the system lib file !DIR/lib has been scanned."
9019 (let* ((true-syslib (file-name-as-directory
9020 (file-truename
9021 (expand-file-name "lib" (idlwave-sys-dir))))))
9022 (cdr (assoc true-syslib (idlwave-true-path-alist)))))
9023
9024;; ----------------------------------------------------------------------------
9025;;
9026;; Online Help display
9027
f32b3b91
CD
9028
9029;; ----------------------------------------------------------------------------
9030;;
9031;; Additions for use with imenu.el and func-menu.el
9032;; (pop-up a list of IDL units in the current file).
9033;;
9034
9035(defun idlwave-prev-index-position ()
9036 "Search for the previous procedure or function.
9037Return nil if not found. For use with imenu.el."
9038 (save-match-data
9039 (cond
9040 ((idlwave-find-key "\\<\\(pro\\|function\\)\\>" -1 'nomark))
9041 ;; ((idlwave-find-key idlwave-begin-unit-reg 1 'nomark)
9042 (t nil))))
9043
9044(defun idlwave-unit-name ()
9045 "Return the unit name.
9046Assumes that point is at the beginning of the unit as found by
9047`idlwave-prev-index-position'."
9048 (forward-sexp 2)
9049 (forward-sexp -1)
9050 (let ((begin (point)))
4b1aaa8b 9051 (re-search-forward
52a244eb 9052 "[a-zA-Z_][a-zA-Z0-9$_]+\\(::[a-zA-Z_][a-zA-Z0-9$_]+\\)?")
f32b3b91
CD
9053 (if (fboundp 'buffer-substring-no-properties)
9054 (buffer-substring-no-properties begin (point))
9055 (buffer-substring begin (point)))))
9056
facebc7b
S
9057(defalias 'idlwave-function-menu
9058 (condition-case nil
f32b3b91
CD
9059 (progn
9060 (require 'func-menu)
facebc7b
S
9061 'function-menu)
9062 (error (condition-case nil
9063 (progn
9064 (require 'imenu)
9065 'imenu)
9066 (error nil)))))
f32b3b91 9067
52a244eb 9068;; Here we hack func-menu.el in order to support this new mode.
f32b3b91
CD
9069;; The latest versions of func-menu.el already have this stuff in, so
9070;; we hack only if it is not already there.
9071(when (fboundp 'eval-after-load)
9072 (eval-after-load "func-menu"
9073 '(progn
9074 (or (assq 'idlwave-mode fume-function-name-regexp-alist)
9075 (not (boundp 'fume-function-name-regexp-idl)) ; avoid problems
9076 (setq fume-function-name-regexp-alist
9077 (cons '(idlwave-mode . fume-function-name-regexp-idl)
9078 fume-function-name-regexp-alist)))
9079 (or (assq 'idlwave-mode fume-find-function-name-method-alist)
9080 (not (fboundp 'fume-find-next-idl-function-name)) ; avoid problems
9081 (setq fume-find-function-name-method-alist
9082 (cons '(idlwave-mode . fume-find-next-idl-function-name)
9083 fume-find-function-name-method-alist))))))
9084
9085(defun idlwave-edit-in-idlde ()
9086 "Edit the current file in IDL Development environment."
9087 (interactive)
9088 (start-process "idldeclient" nil
9089 idlwave-shell-explicit-file-name "-c" "-e"
f66f03de 9090 (buffer-file-name)))
4b1aaa8b 9091
f66f03de 9092(defvar idlwave-help-use-assistant)
f32b3b91
CD
9093(defun idlwave-launch-idlhelp ()
9094 "Start the IDLhelp application."
9095 (interactive)
f66f03de
S
9096 (if idlwave-help-use-assistant
9097 (idlwave-help-assistant-raise)
9098 (start-process "idlhelp" nil idlwave-help-application)))
4b1aaa8b 9099
f32b3b91
CD
9100;; Menus - using easymenu.el
9101(defvar idlwave-mode-menu-def
9102 `("IDLWAVE"
9103 ["PRO/FUNC menu" idlwave-function-menu t]
9104 ("Motion"
9105 ["Subprogram Start" idlwave-beginning-of-subprogram t]
9106 ["Subprogram End" idlwave-end-of-subprogram t]
9107 ["Block Start" idlwave-beginning-of-block t]
9108 ["Block End" idlwave-end-of-block t]
9109 ["Up Block" idlwave-backward-up-block t]
9110 ["Down Block" idlwave-down-block t]
9111 ["Skip Block Backward" idlwave-backward-block t]
9112 ["Skip Block Forward" idlwave-forward-block t])
9113 ("Mark"
9114 ["Subprogram" idlwave-mark-subprogram t]
9115 ["Block" idlwave-mark-block t]
9116 ["Header" idlwave-mark-doclib t])
9117 ("Format"
4b1aaa8b 9118 ["Indent Entire Statement" idlwave-indent-statement
f66f03de 9119 :active t :keys "C-u \\[indent-for-tab-command]" ]
f32b3b91 9120 ["Indent Subprogram" idlwave-indent-subprogram t]
f66f03de 9121 ["(Un)Comment Region" idlwave-toggle-comment-region t]
f32b3b91
CD
9122 ["Continue/Split line" idlwave-split-line t]
9123 "--"
9124 ["Toggle Auto Fill" idlwave-auto-fill-mode :style toggle
9125 :selected (symbol-value idlwave-fill-function)])
9126 ("Templates"
9127 ["Procedure" idlwave-procedure t]
9128 ["Function" idlwave-function t]
9129 ["Doc Header" idlwave-doc-header t]
9130 ["Log" idlwave-doc-modification t]
9131 "--"
9132 ["Case" idlwave-case t]
9133 ["For" idlwave-for t]
9134 ["Repeat" idlwave-repeat t]
9135 ["While" idlwave-while t]
9136 "--"
9137 ["Close Block" idlwave-close-block t])
15e42531 9138 ("Completion"
f32b3b91 9139 ["Complete" idlwave-complete t]
f66f03de 9140 ("Complete Specific"
f32b3b91
CD
9141 ["1 Procedure Name" (idlwave-complete 'procedure) t]
9142 ["2 Procedure Keyword" (idlwave-complete 'procedure-keyword) t]
9143 "--"
9144 ["3 Function Name" (idlwave-complete 'function) t]
9145 ["4 Function Keyword" (idlwave-complete 'function-keyword) t]
9146 "--"
9147 ["5 Procedure Method Name" (idlwave-complete 'procedure-method) t]
9148 ["6 Procedure Method Keyword" (idlwave-complete 'procedure-method-keyword) t]
9149 "--"
9150 ["7 Function Method Name" (idlwave-complete 'function-method) t]
9151 ["8 Function Method Keyword" (idlwave-complete 'function-method-keyword) t]
9152 "--"
15e42531
CD
9153 ["9 Class Name" idlwave-complete-class t]))
9154 ("Routine Info"
f32b3b91 9155 ["Show Routine Info" idlwave-routine-info t]
52a244eb 9156 ["Online Context Help" idlwave-context-help t]
f32b3b91
CD
9157 "--"
9158 ["Find Routine Source" idlwave-find-module t]
15e42531 9159 ["Resolve Routine" idlwave-resolve (featurep 'idlw-shell)]
f32b3b91
CD
9160 "--"
9161 ["Update Routine Info" idlwave-update-routine-info t]
f66f03de 9162 ["Rescan XML Help Catalog" idlwave-convert-xml-system-routine-info t]
f32b3b91 9163 "--"
52a244eb
S
9164 "IDL User Catalog"
9165 ["Select Catalog Directories" (idlwave-create-user-catalog-file nil) t]
15e42531 9166 ["Scan Directories" (idlwave-update-routine-info '(16))
5e72c6b2
S
9167 (and idlwave-path-alist (not idlwave-catalog-process))]
9168 ["Scan Directories &" (idlwave-update-routine-info '(64))
9169 (and idlwave-path-alist (not idlwave-catalog-process))]
15e42531
CD
9170 "--"
9171 "Routine Shadows"
9172 ["Check Current Buffer" idlwave-list-buffer-load-path-shadows t]
9173 ["Check Compiled Routines" idlwave-list-shell-load-path-shadows t]
9174 ["Check Everything" idlwave-list-all-load-path-shadows t])
9175 ("Misc"
9176 ["Kill auto-created buffers" idlwave-kill-autoloaded-buffers t]
9177 "--"
9178 ["Insert TAB character" idlwave-hard-tab t])
f32b3b91
CD
9179 "--"
9180 ("External"
f32b3b91
CD
9181 ["Start IDL shell" idlwave-shell t]
9182 ["Edit file in IDLDE" idlwave-edit-in-idlde t]
9183 ["Launch IDL Help" idlwave-launch-idlhelp t])
9184 "--"
9185 ("Customize"
9186 ["Browse IDLWAVE Group" idlwave-customize t]
9187 "--"
4b1aaa8b 9188 ["Build Full Customize Menu" idlwave-create-customize-menu
f32b3b91
CD
9189 (fboundp 'customize-menu-create)])
9190 ("Documentation"
9191 ["Describe Mode" describe-mode t]
9192 ["Abbreviation List" idlwave-list-abbrevs t]
9193 "--"
9194 ["Commentary in idlwave.el" idlwave-show-commentary t]
595ab50b 9195 ["Commentary in idlw-shell.el" idlwave-shell-show-commentary t]
f32b3b91
CD
9196 "--"
9197 ["Info" idlwave-info t]
9198 "--"
8c43762b 9199 ["Help with Topic" idlwave-help-assistant-help-with-topic
e08734e2 9200 idlwave-help-use-assistant]
f32b3b91
CD
9201 ["Launch IDL Help" idlwave-launch-idlhelp t])))
9202
9203(defvar idlwave-mode-debug-menu-def
9204 '("Debug"
9205 ["Start IDL shell" idlwave-shell t]
9206 ["Save and .RUN buffer" idlwave-shell-save-and-run
4b1aaa8b 9207 (and (boundp 'idlwave-shell-automatic-start)
f32b3b91
CD
9208 idlwave-shell-automatic-start)]))
9209
9210(if (or (featurep 'easymenu) (load "easymenu" t))
9211 (progn
4b1aaa8b
PE
9212 (easy-menu-define idlwave-mode-menu idlwave-mode-map
9213 "IDL and WAVE CL editing menu"
f32b3b91 9214 idlwave-mode-menu-def)
4b1aaa8b
PE
9215 (easy-menu-define idlwave-mode-debug-menu idlwave-mode-map
9216 "IDL and WAVE CL editing menu"
f32b3b91
CD
9217 idlwave-mode-debug-menu-def)))
9218
9219(defun idlwave-customize ()
5a0c3f56 9220 "Call the customize function with `idlwave' as argument."
f32b3b91 9221 (interactive)
4b1aaa8b 9222 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9223 ;; as well.
22d5821d
CD
9224 (or (featurep 'idlw-shell)
9225 (load "idlw-shell" t))
f32b3b91
CD
9226 (customize-browse 'idlwave))
9227
9228(defun idlwave-create-customize-menu ()
9229 "Create a full customization menu for IDLWAVE, insert it into the menu."
9230 (interactive)
9231 (if (fboundp 'customize-menu-create)
9232 (progn
4b1aaa8b 9233 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9234 ;; as well.
22d5821d
CD
9235 (or (featurep 'idlw-shell)
9236 (load "idlw-shell" t))
4b1aaa8b 9237 (easy-menu-change
f32b3b91
CD
9238 '("IDLWAVE") "Customize"
9239 `(["Browse IDLWAVE group" idlwave-customize t]
9240 "--"
9241 ,(customize-menu-create 'idlwave)
9242 ["Set" Custom-set t]
9243 ["Save" Custom-save t]
9244 ["Reset to Current" Custom-reset-current t]
9245 ["Reset to Saved" Custom-reset-saved t]
9246 ["Reset to Standard Settings" Custom-reset-standard t]))
9247 (message "\"IDLWAVE\"-menu now contains full customization menu"))
9248 (error "Cannot expand menu (outdated version of cus-edit.el)")))
9249
9250(defun idlwave-show-commentary ()
9251 "Use the finder to view the file documentation from `idlwave.el'."
9252 (interactive)
f32b3b91
CD
9253 (finder-commentary "idlwave.el"))
9254
9255(defun idlwave-shell-show-commentary ()
595ab50b 9256 "Use the finder to view the file documentation from `idlw-shell.el'."
f32b3b91 9257 (interactive)
595ab50b 9258 (finder-commentary "idlw-shell.el"))
f32b3b91
CD
9259
9260(defun idlwave-info ()
9261 "Read documentation for IDLWAVE in the info system."
9262 (interactive)
d6a277d0 9263 (info "idlwave"))
f32b3b91
CD
9264
9265(defun idlwave-list-abbrevs (arg)
9266 "Show the code abbreviations define in IDLWAVE mode.
9267This lists all abbrevs where the replacement text differs from the input text.
9268These are the ones the users want to learn to speed up their writing.
9269
9270The function does *not* list abbrevs which replace a word with itself
9271to call a hook. These hooks are used to change the case of words or
9272to blink the matching `begin', and the user does not need to know them.
9273
9274With arg, list all abbrevs with the corresponding hook.
9275
9276This function was written since `list-abbrevs' looks terrible for IDLWAVE mode."
9277
9278 (interactive "P")
9279 (let ((table (symbol-value 'idlwave-mode-abbrev-table))
9280 abbrevs
9281 str rpl func fmt (len-str 0) (len-rpl 0))
4b1aaa8b 9282 (mapatoms
f32b3b91
CD
9283 (lambda (sym)
9284 (if (symbol-value sym)
9285 (progn
9286 (setq str (symbol-name sym)
9287 rpl (symbol-value sym)
9288 func (symbol-function sym))
9289 (if arg
9290 (setq func (prin1-to-string func))
9291 (if (and (listp func) (stringp (nth 2 func)))
9292 (setq rpl (concat "EVAL: " (nth 2 func))
9293 func "")
9294 (setq func "")))
9295 (if (or arg (not (string= rpl str)))
9296 (progn
9297 (setq len-str (max len-str (length str)))
9298 (setq len-rpl (max len-rpl (length rpl)))
9299 (setq abbrevs (cons (list str rpl func) abbrevs)))))))
9300 table)
9301 ;; sort the list
9302 (setq abbrevs (sort abbrevs (lambda (a b) (string< (car a) (car b)))))
9303 ;; Make the format
9304 (setq fmt (format "%%-%ds %%-%ds %%s\n" len-str len-rpl))
9305 (with-output-to-temp-buffer "*Help*"
9306 (if arg
9307 (progn
4b1aaa8b 9308 (princ "Abbreviations and Actions in IDLWAVE-Mode\n")
f32b3b91
CD
9309 (princ "=========================================\n\n")
9310 (princ (format fmt "KEY" "REPLACE" "HOOK"))
9311 (princ (format fmt "---" "-------" "----")))
9312 (princ "Code Abbreviations and Templates in IDLWAVE-Mode\n")
9313 (princ "================================================\n\n")
9314 (princ (format fmt "KEY" "ACTION" ""))
9315 (princ (format fmt "---" "------" "")))
9316 (mapcar
9317 (lambda (list)
9318 (setq str (car list)
9319 rpl (nth 1 list)
9320 func (nth 2 list))
9321 (princ (format fmt str rpl func)))
9322 abbrevs)))
9323 ;; Make sure each abbreviation uses only one display line
9a529312 9324 (with-current-buffer "*Help*"
f32b3b91
CD
9325 (setq truncate-lines t)))
9326
5e72c6b2
S
9327;; Add .pro files to speedbar for support, if it's loaded
9328(eval-after-load "speedbar" '(speedbar-add-supported-extension ".pro"))
9329
5e72c6b2
S
9330;; Set an idle timer to load the routine info.
9331;; Will only work on systems which support this.
9332(or idlwave-routines (idlwave-start-load-rinfo-timer))
9333
15e42531 9334;; Run the hook
f32b3b91
CD
9335(run-hooks 'idlwave-load-hook)
9336
9337(provide 'idlwave)
9338
9339;;; idlwave.el ends here