declare smobs in alloc.c
[bpt/emacs.git] / lisp / progmodes / idlwave.el
CommitLineData
52a244eb 1;; idlwave.el --- IDL editing mode for GNU Emacs
d7a0267c 2
ba318903 3;; Copyright (C) 1999-2014 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 29;; IDLWAVE enables feature-rich development and interaction with IDL,
940e5099 30;; the Interactive Data Language. It provides a compelling,
f66f03de
S
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
865fe16f 54;; lines to your init file:
f32b3b91
CD
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
fb7ada5f 198 "Extra indentation for the main block of code.
f32b3b91
CD
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
fb7ada5f 205 "Extra indentation applied to block lines.
f32b3b91
CD
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
fb7ada5f 211 "Extra indentation applied to block END lines.
f32b3b91
CD
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
fb7ada5f 218 "Extra indentation applied to continuation lines.
f32b3b91 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
fb7ada5f 225 "Maximum additional indentation for special continuation indent.
5e72c6b2
S
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
fb7ada5f 239 "Non-nil means, indent continuation lines to innermost open parenthesis.
5a0c3f56 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 262(defcustom idlwave-indent-parens-nested nil
fb7ada5f 263 "Non-nil means, indent continuation lines with parens by nesting
52a244eb
S
264lines at consecutively deeper levels."
265 :group 'idlwave-code-formatting
266 :type 'boolean)
267
268
f32b3b91 269(defcustom idlwave-hanging-indent t
fb7ada5f 270 "If set non-nil then comment paragraphs are indented under the
f32b3b91
CD
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 "- "
fb7ada5f 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
fb7ada5f 284 "If non-nil then use last match on line for `idlwave-indent-regexp'."
f32b3b91
CD
285 :group 'idlwave-code-formatting
286 :type 'boolean)
287
288(defcustom idlwave-fill-comment-line-only t
fb7ada5f 289 "If non-nil then auto fill will only operate on comment lines."
f32b3b91
CD
290 :group 'idlwave-code-formatting
291 :type 'boolean)
292
293(defcustom idlwave-auto-fill-split-string t
fb7ada5f 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
fb7ada5f 305 "If non-nil then `idlwave-split-line' will split strings with `+'.
f32b3b91
CD
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 ";;;"
fb7ada5f 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
fb7ada5f 320 "A comment anchored at the beginning of line.
f32b3b91
CD
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 ";;[^;]"
fb7ada5f 330 "A comment that starts with this regular expression on a line by
f32b3b91
CD
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 345(defcustom idlwave-use-library-catalogs t
fb7ada5f 346 "Non-nil means search the IDL path for library catalog files.
52a244eb
S
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
fb7ada5f 356 "Seconds of idle time before routine info is automatically initialized.
5a0c3f56
JB
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
fb7ada5f 373 "Non-nil means, scan buffers for IDL programs when updating info.
15e42531
CD
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)
9c61f806 384 (const :tag "Current buffer only" current)))
f32b3b91
CD
385
386(defcustom idlwave-query-shell-for-routine-info t
fb7ada5f 387 "Non-nil means query the shell for info about compiled routines.
f32b3b91
CD
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)
fb7ada5f 396 "Controls under what circumstances routine info is updated automatically.
15e42531
CD
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 415(defcustom idlwave-rinfo-max-source-lines 5
fb7ada5f 416 "Maximum number of source files displayed in the Routine Info window.
15e42531 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
940e5099 450 (locate-user-emacs-file "idlwave" ".idlwave")
fb7ada5f 451 "Directory for configuration files and user-library catalog."
ece4bae5 452 :version "24.4" ; added locate-user-emacs-file
15e42531 453 :group 'idlwave-routine-info
f32b3b91
CD
454 :type 'file)
455
52a244eb 456(defvar idlwave-user-catalog-file "idlusercat.el")
f66f03de 457(defvar idlwave-xml-system-rinfo-converted-file "idl_xml_rinfo.el")
52a244eb
S
458(defvar idlwave-path-file "idlpath.el")
459
15e42531
CD
460(defcustom idlwave-special-lib-alist nil
461 "Alist of regular expressions matching special library directories.
462When listing routine source locations, IDLWAVE gives a short hint where
4b1aaa8b 463the file defining the routine is located. By default it lists `SystemLib'
15e42531
CD
464for routines in the system library `!DIR/lib' and `Library' for anything
465else. This variable can define additional types. The car of each entry
466is a regular expression matching the file name (they normally will match
467on the path). The cdr is the string to be used as identifier. Max 10
468chars are allowed."
469 :group 'idlwave-routine-info
470 :type '(repeat
471 (cons regexp string)))
472
52a244eb 473(defcustom idlwave-auto-write-paths t
4b1aaa8b 474 "Write out path (!PATH) and system directory (!DIR) info automatically.
52a244eb
S
475Path info is needed to locate library catalog files. If non-nil,
476whenever the path-list changes as a result of shell-query, etc., it is
477written to file. Otherwise, the menu option \"Write Paths\" can be
478used to force a write."
479 :group 'idlwave-routine-info
05a1abfc 480 :type 'boolean)
775591f7 481
15e42531
CD
482(defgroup idlwave-completion nil
483 "Completion options for IDLWAVE mode."
484 :prefix "idlwave"
485 :group 'idlwave)
486
f32b3b91
CD
487(eval-and-compile
488 (defconst idlwave-tmp
489 '(choice :tag "by applying the function"
490 (const upcase)
491 (const downcase)
492 (const capitalize)
493 (const preserve)
494 (symbol :tag "Other"))))
495
f32b3b91
CD
496(defcustom idlwave-completion-case '((routine . upcase)
497 (keyword . upcase)
498 (class . preserve)
499 (method . preserve))
500 "Association list setting the case of completed words.
501
502This variable determines the case (UPPER/lower/Capitalized...) of
503words inserted into the buffer by completion. The preferred case can
504be specified separately for routine names, keywords, classes and
4b1aaa8b 505methods.
f32b3b91
CD
506This alist should therefore have entries for `routine' (normal
507functions and procedures, i.e. non-methods), `keyword', `class', and
508`method'. Plausible values are
509
510upcase upcase whole word, like `BOX_CURSOR'
511downcase downcase whole word, like `read_ppm'
512capitalize capitalize each part, like `Widget_Control'
513preserve preserve case as is, like `IDLgrView'
514
515The value can also be any Emacs Lisp function which transforms the
516case of characters in a string.
517
518A value of `preserve' means that the case of the completed word is
519identical to the way it was written in the definition statement of the
520routine. This was implemented to allow for mixed-case completion, in
521particular of object classes and methods.
522If a completable word is defined in multiple locations, the meaning of
523`preserve' is not unique since the different definitions might be
524cased differently. Therefore IDLWAVE always takes the case of the
525*first* definition it encounters during routine info collection and
526uses the case derived from it consistently.
527
528Note that a lowercase-only string in the buffer will always be completed in
529lower case (but see the variable `idlwave-completion-force-default-case').
530
531After changing this variable, you need to either restart Emacs or press
532`C-u C-c C-i' to update the internal lists."
15e42531 533 :group 'idlwave-completion
f32b3b91
CD
534 :type `(repeat
535 (cons (symbol :tag "Derive completion case for")
536 ,idlwave-tmp)))
537
538(defcustom idlwave-completion-force-default-case nil
fb7ada5f 539 "Non-nil means, completion will always honor `idlwave-completion-case'.
f32b3b91
CD
540When nil, only the completion of a mixed case or upper case string
541will honor the default settings in `idlwave-completion-case', while
542the completion of lower case strings will be completed entirely in
543lower case."
15e42531 544 :group 'idlwave-completion
f32b3b91
CD
545 :type 'boolean)
546
547(defcustom idlwave-complete-empty-string-as-lower-case nil
fb7ada5f 548 "Non-nil means, the empty string is considered downcase for completion.
f32b3b91
CD
549The case of what is already in the buffer determines the case of completions.
550When this variable is non-nil, the empty string is considered to be downcase.
551Completing on the empty string then offers downcase versions of the possible
552completions."
15e42531 553 :group 'idlwave-completion
f32b3b91
CD
554 :type 'boolean)
555
f32b3b91 556(defcustom idlwave-buffer-case-takes-precedence nil
fb7ada5f 557 "Non-nil means, the case of tokens in buffers dominates over system stuff.
f32b3b91
CD
558To make this possible, we need to re-case everything each time we update
559the routine info from the buffers. This is slow.
560The default is to consider the case given in the system and library files
561first which makes updating much faster."
15e42531
CD
562 :group 'idlwave-completion
563 :type 'boolean)
564
565(defcustom idlwave-highlight-help-links-in-completion t
fb7ada5f 566 "Non-nil means, highlight completions for which system help is available.
15e42531
CD
567Help can then be accessed with mouse-3.
568This option is only effective when the online help system is installed."
569 :group 'idlwave-completion
f32b3b91
CD
570 :type 'boolean)
571
05a1abfc
CD
572(defcustom idlwave-support-inheritance t
573 "Non-nil means, treat inheritance with completion, online help etc.
cef6cafe 574When nil, IDLWAVE only knows about the native methods and tags of a class,
05a1abfc
CD
575not about inherited ones."
576 :group 'idlwave-routine-info
577 :type 'boolean)
578
5e72c6b2
S
579(defcustom idlwave-keyword-class-inheritance '("^[gs]etproperty$" "^init$")
580 "List of regular expressions for class-driven keyword inheritance.
581Keyword inheritance is often tied to class inheritance by \"chaining\"
582up the class tree. While it cannot be assumed that the presence of an
583_EXTRA or _REF_EXTRA symbol guarantees such chaining will occur, for
584certain methods this assumption is almost always true. The methods
585for which to assume this can be set here."
586 :group 'idlwave-routine-info
587 :type '(repeat (regexp :tag "Match method:")))
4b1aaa8b 588
5e72c6b2 589
f32b3b91 590(defcustom idlwave-completion-show-classes 1
fb7ada5f 591 "Number of classes to show when completing object methods and keywords.
f32b3b91 592When completing methods or keywords for an object with unknown class,
2e8b9c7d 593the *Completions* buffer will show the valid classes for each completion
f32b3b91
CD
594like this:
595
596MyMethod <Class1,Class2,Class3>
597
598The value of this variable may be nil to inhibit display, or an integer to
599indicate the maximum number of classes to display.
600
601On XEmacs, a full list of classes will also be placed into a `help-echo'
da6062e6 602property on the completion items, so that the list of classes for the current
f32b3b91
CD
603item is displayed in the echo area. If the value of this variable is a
604negative integer, the `help-echo' property will be suppressed."
15e42531 605 :group 'idlwave-completion
f32b3b91
CD
606 :type '(choice (const :tag "Don't show" nil)
607 (integer :tag "Number of classes shown" 1)))
608
609(defcustom idlwave-completion-fontify-classes t
fb7ada5f 610 "Non-nil means, fontify the classes in completions buffer.
f32b3b91
CD
611This makes it easier to distinguish the completion items from the extra
612class info listed. See `idlwave-completion-show-classes'."
15e42531 613 :group 'idlwave-completion
f32b3b91
CD
614 :type 'boolean)
615
616(defcustom idlwave-query-class '((method-default . nil)
617 (keyword-default . nil))
618 "Association list governing specification of object classes for completion.
619
5e72c6b2
S
620When IDLWAVE tries to complete object-oriented methods, it usually
621cannot determine the class of a given object from context. In order
622to provide the user with a correct list of methods or keywords, it
76959b77
S
623needs to determine the appropriate class. IDLWAVE has two ways of
624doing this (well, three ways if you count the shell... see
625`idlwave-shell-query-for-class'):
626
6271. Combine the items of all available classes which contain this
628 method for the purpose of completion. So when completing a method,
629 all methods of all known classes are available, and when completing
630 a keyword, all keywords allowed for this method in any class are
631 shown. This behavior is very much like normal completion and is
632 therefore the default. It works much better than one might think -
633 only for the INIT, GETPROPERTY and SETPROPERTY the keyword lists
634 become uncomfortably long. See also
5e72c6b2 635 `idlwave-completion-show-classes'.
f32b3b91
CD
636
6372. The second possibility is to ask the user on each occasion. To
638 make this less interruptive, IDLWAVE can store the class as a text
639 property on the object operator `->'. For a given object in the
640 source code, class selection will then be needed only once
641 - for example to complete the method. Keywords to the method can
642 then be completed directly, because the class is already known.
643 You will have to turn on the storage of the selected class
644 explicitly with the variable `idlwave-store-inquired-class'.
645
5e72c6b2
S
646This variable allows you to configure IDLWAVE's method and
647method-keyword completion behavior. Its value is an alist, which
648should contain at least two elements: (method-default . VALUE) and
facebc7b 649\(keyword-default . VALUE), where VALUE is either t or nil. These
5e72c6b2
S
650specify if the class should be found during method and keyword
651completion, respectively.
f32b3b91 652
4b1aaa8b 653The alist may have additional entries specifying exceptions from the
f32b3b91
CD
654keyword completion rule for specific methods, like INIT or
655GETPROPERTY. In order to turn on class specification for the INIT
656method, add an entry (\"INIT\" . t). The method name must be ALL-CAPS."
15e42531 657 :group 'idlwave-completion
f32b3b91
CD
658 :type '(list
659 (cons (const method-default)
660 (boolean :tag "Determine class when completing METHODS "))
661 (cons (const keyword-default)
662 (boolean :tag "Determine class when completing KEYWORDS "))
663 (repeat
664 :tag "Exceptions to defaults"
665 :inline t
666 (cons (string :tag "MODULE" :value "")
667 (boolean :tag "Determine class for this method")))))
668
f66f03de 669(defcustom idlwave-store-inquired-class t
fb7ada5f 670 "Non-nil means, store class of a method call as text property on `->'.
f32b3b91
CD
671IDLWAVE sometimes has to ask the user for the class associated with a
672particular object method call. This happens during the commands
673`idlwave-routine-info' and `idlwave-complete', depending upon the
674value of the variable `idlwave-query-class'.
675
676When you specify a class, this information can be stored as a text
4b1aaa8b 677property on the `->' arrow in the source code, so that during the same
f32b3b91
CD
678editing session, IDLWAVE will not have to ask again. When this
679variable is non-nil, IDLWAVE will store and reuse the class information.
680The class stored can be checked and removed with `\\[idlwave-routine-info]'
681on the arrow.
682
683The default of this variable is nil, since the result of commands then
684is more predictable. However, if you know what you are doing, it can
685be nice to turn this on.
686
687An arrow which knows the class will be highlighted with
688`idlwave-class-arrow-face'. The command \\[idlwave-routine-info]
689displays (with prefix arg: deletes) the class stored on the arrow
690at point."
15e42531 691 :group 'idlwave-completion
f32b3b91
CD
692 :type 'boolean)
693
694(defcustom idlwave-class-arrow-face 'bold
fb7ada5f 695 "Face to highlight object operator arrows `->' which carry a class property.
f32b3b91 696When IDLWAVE stores a class name as text property on an object arrow
facebc7b 697\(see variable `idlwave-store-inquired-class', it highlights the arrow
f32b3b91 698with this font in order to remind the user that this arrow is special."
15e42531 699 :group 'idlwave-completion
f32b3b91
CD
700 :type 'symbol)
701
702(defcustom idlwave-resize-routine-help-window t
fb7ada5f 703 "Non-nil means, resize the Routine-info *Help* window to fit the content."
15e42531 704 :group 'idlwave-completion
f32b3b91
CD
705 :type 'boolean)
706
707(defcustom idlwave-keyword-completion-adds-equal t
fb7ada5f 708 "Non-nil means, completion automatically adds `=' after completed keywords."
15e42531 709 :group 'idlwave-completion
f32b3b91
CD
710 :type 'boolean)
711
712(defcustom idlwave-function-completion-adds-paren t
fb7ada5f 713 "Non-nil means, completion automatically adds `(' after completed function.
0ff9b955 714nil means, don't add anything.
f32b3b91
CD
715A value of `2' means, also add the closing parenthesis and position cursor
716between the two."
15e42531 717 :group 'idlwave-completion
f32b3b91
CD
718 :type '(choice (const :tag "Nothing" nil)
719 (const :tag "(" t)
720 (const :tag "()" 2)))
721
722(defcustom idlwave-completion-restore-window-configuration t
fb7ada5f 723 "Non-nil means, try to restore the window configuration after completion.
f32b3b91
CD
724When completion is not unique, Emacs displays a list of completions.
725This messes up your window configuration. With this variable set, IDLWAVE
726restores the old configuration after successful completion."
15e42531 727 :group 'idlwave-completion
f32b3b91
CD
728 :type 'boolean)
729
730;;; Variables for abbrev and action behavior -----------------------------
731
732(defgroup idlwave-abbrev-and-indent-action nil
733 "IDLWAVE performs actions when expanding abbreviations or indenting lines.
734The variables in this group govern this."
735 :group 'idlwave)
736
737(defcustom idlwave-do-actions nil
fb7ada5f 738 "Non-nil means performs actions when indenting.
f32b3b91
CD
739The actions that can be performed are listed in `idlwave-indent-action-table'."
740 :group 'idlwave-abbrev-and-indent-action
741 :type 'boolean)
742
743(defcustom idlwave-abbrev-start-char "\\"
fb7ada5f 744 "A single character string used to start abbreviations in abbrev mode.
f32b3b91
CD
745Possible characters to chose from: ~`\%
746or even '?'. '.' is not a good choice because it can make structure
747field names act like abbrevs in certain circumstances.
748
749Changes to this in `idlwave-mode-hook' will have no effect. Instead a user
865fe16f 750must set it directly using `setq' in the init file before idlwave.el
f32b3b91
CD
751is loaded."
752 :group 'idlwave-abbrev-and-indent-action
753 :type 'string)
754
755(defcustom idlwave-surround-by-blank nil
fb7ada5f 756 "Non-nil means, enable `idlwave-surround'.
595ab50b 757If non-nil, `=',`<',`>',`&',`,', `->' are surrounded with spaces by
f32b3b91
CD
758`idlwave-surround'.
759See help for `idlwave-indent-action-table' for symbols using `idlwave-surround'.
760
761Also see the default key bindings for keys using `idlwave-surround'.
762Keys are bound and made into actions calling `idlwave-surround' with
763`idlwave-action-and-binding'.
764See help for `idlwave-action-and-binding' for examples.
765
766Also see help for `idlwave-surround'."
767 :group 'idlwave-abbrev-and-indent-action
768 :type 'boolean)
769
770(defcustom idlwave-pad-keyword t
fb7ada5f 771 "Non-nil means pad '=' in keywords (routine calls or defs) like assignment.
52a244eb
S
772Whenever `idlwave-surround' is non-nil then this affects how '=' is
773padded for keywords and for variables. If t, pad the same as for
774assignments. If nil then spaces are removed. With any other value,
775spaces are left unchanged."
f32b3b91 776 :group 'idlwave-abbrev-and-indent-action
15e42531
CD
777 :type '(choice
778 (const :tag "Pad like assignments" t)
779 (const :tag "Remove space near `='" nil)
9c61f806 780 (other :tag "Keep space near `='" keep)))
f32b3b91
CD
781
782(defcustom idlwave-show-block t
fb7ada5f 783 "Non-nil means point blinks to block beginning for `idlwave-show-begin'."
f32b3b91
CD
784 :group 'idlwave-abbrev-and-indent-action
785 :type 'boolean)
786
787(defcustom idlwave-expand-generic-end nil
fb7ada5f 788 "Non-nil means expand generic END to ENDIF/ENDELSE/ENDWHILE etc."
f32b3b91
CD
789 :group 'idlwave-abbrev-and-indent-action
790 :type 'boolean)
791
15e42531 792(defcustom idlwave-reindent-end t
fb7ada5f 793 "Non-nil means re-indent line after END was typed."
15e42531
CD
794 :group 'idlwave-abbrev-and-indent-action
795 :type 'boolean)
796
f32b3b91 797(defcustom idlwave-abbrev-move t
fb7ada5f 798 "Non-nil means the abbrev hook can move point.
5a0c3f56 799Set to nil by `idlwave-expand-region-abbrevs'. To see the abbrev
f32b3b91 800definitions, use the command `list-abbrevs', for abbrevs that move
5a0c3f56 801point. Moving point is useful, for example, to place point between
f32b3b91
CD
802parentheses of expanded functions.
803
804See `idlwave-check-abbrev'."
805 :group 'idlwave-abbrev-and-indent-action
806 :type 'boolean)
807
808(defcustom idlwave-abbrev-change-case nil
fb7ada5f 809 "Non-nil means all abbrevs will be forced to either upper or lower case.
f32b3b91
CD
810If the value t, all expanded abbrevs will be upper case.
811If the value is 'down then abbrevs will be forced to lower case.
812If nil, the case will not change.
813If `idlwave-reserved-word-upcase' is non-nil, reserved words will always be
814upper case, regardless of this variable."
815 :group 'idlwave-abbrev-and-indent-action
816 :type 'boolean)
817
818(defcustom idlwave-reserved-word-upcase nil
fb7ada5f 819 "Non-nil means, reserved words will be made upper case via abbrev expansion.
f32b3b91
CD
820If nil case of reserved words is controlled by `idlwave-abbrev-change-case'.
821Has effect only if in abbrev-mode."
822 :group 'idlwave-abbrev-and-indent-action
823 :type 'boolean)
824
825;;; Action/Expand Tables.
826;;
827;; The average user may have difficulty modifying this directly. It
828;; can be modified/set in idlwave-mode-hook, but it is easier to use
829;; idlwave-action-and-binding. See help for idlwave-action-and-binding for
830;; examples of how to add an action.
831;;
832;; The action table is used by `idlwave-indent-line' whereas both the
833;; action and expand tables are used by `idlwave-indent-and-action'. In
834;; general, the expand table is only used when a line is explicitly
835;; indented. Whereas, in addition to being used when the expand table
836;; is used, the action table is used when a line is indirectly
837;; indented via line splitting, auto-filling or a new line creation.
838;;
839;; Example actions:
840;;
841;; Capitalize system vars
842;; (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)
843;;
844;; Capitalize procedure name
845;; (idlwave-action-and-binding "\\<\\(pro\\|function\\)\\>[ \t]*\\<"
846;; '(capitalize-word 1) t)
847;;
848;; Capitalize common block name
849;; (idlwave-action-and-binding "\\<common\\>[ \t]+\\<"
850;; '(capitalize-word 1) t)
851;; Capitalize label
852;; (idlwave-action-and-binding (concat "^[ \t]*" idlwave-label)
853;; '(capitalize-word -1) t)
854
855(defvar idlwave-indent-action-table nil
fb7ada5f 856 "Associated array containing action lists of search string (car),
5a0c3f56 857and function as a cdr. This table is used by `idlwave-indent-line'.
f32b3b91
CD
858See documentation for `idlwave-do-action' for a complete description of
859the action lists.
860
861Additions to the table are made with `idlwave-action-and-binding' when a
862binding is not requested.
863See help on `idlwave-action-and-binding' for examples.")
864
865(defvar idlwave-indent-expand-table nil
fb7ada5f 866 "Associated array containing action lists of search string (car),
5a0c3f56
JB
867and function as a cdr. The table is used by the
868`idlwave-indent-and-action' function. See documentation for
f32b3b91
CD
869`idlwave-do-action' for a complete description of the action lists.
870
871Additions to the table are made with `idlwave-action-and-binding' when a
872binding is requested.
873See help on `idlwave-action-and-binding' for examples.")
874
875;;; Documentation header and history keyword ---------------------------------
876
877(defgroup idlwave-documentation nil
878 "Options for documenting IDLWAVE files."
879 :group 'idlwave)
880
881;; FIXME: make defcustom?
882(defvar idlwave-file-header
883 (list nil
884 ";+
885; NAME:
886;
887;
888;
889; PURPOSE:
890;
891;
892;
893; CATEGORY:
894;
895;
896;
897; CALLING SEQUENCE:
898;
899;
900;
901; INPUTS:
902;
903;
904;
905; OPTIONAL INPUTS:
906;
907;
908;
909; KEYWORD PARAMETERS:
910;
911;
912;
913; OUTPUTS:
914;
915;
916;
917; OPTIONAL OUTPUTS:
918;
919;
920;
921; COMMON BLOCKS:
922;
923;
924;
925; SIDE EFFECTS:
926;
927;
928;
929; RESTRICTIONS:
930;
931;
932;
933; PROCEDURE:
934;
935;
936;
937; EXAMPLE:
938;
939;
940;
941; MODIFICATION HISTORY:
942;
943;-
944")
fb7ada5f 945 "A list (PATHNAME STRING) specifying the doc-header template to use for
5a0c3f56
JB
946summarizing a file. If PATHNAME is non-nil then this file will be included.
947Otherwise STRING is used. If nil, the file summary will be omitted.
f32b3b91
CD
948For example you might set PATHNAME to the path for the
949lib_template.pro file included in the IDL distribution.")
950
f66f03de 951(defcustom idlwave-header-to-beginning-of-file t
fb7ada5f 952 "Non-nil means, the documentation header will always be at start of file.
5e72c6b2
S
953When nil, the header is positioned between the PRO/FUNCTION line of
954the current routine and the code, allowing several routine headers in
955a file."
956 :group 'idlwave-documentation
957 :type 'boolean)
958
f32b3b91 959(defcustom idlwave-timestamp-hook 'idlwave-default-insert-timestamp
fb7ada5f 960 "The hook function used to update the timestamp of a function."
f32b3b91
CD
961 :group 'idlwave-documentation
962 :type 'function)
963
964(defcustom idlwave-doc-modifications-keyword "HISTORY"
fb7ada5f 965 "The modifications keyword to use with the log documentation commands.
f32b3b91
CD
966A ':' is added to the keyword end.
967Inserted by doc-header and used to position logs by doc-modification.
968If nil it will not be inserted."
969 :group 'idlwave-documentation
970 :type 'string)
971
972(defcustom idlwave-doclib-start "^;+\\+"
fb7ada5f 973 "Regexp matching the start of a document library header."
f32b3b91
CD
974 :group 'idlwave-documentation
975 :type 'regexp)
976
977(defcustom idlwave-doclib-end "^;+-"
fb7ada5f 978 "Regexp matching the end of a document library header."
f32b3b91
CD
979 :group 'idlwave-documentation
980 :type 'regexp)
981
982;;; External Programs -------------------------------------------------------
983
984(defgroup idlwave-external-programs nil
05a1abfc 985 "Path locations of external commands used by IDLWAVE."
f32b3b91
CD
986 :group 'idlwave)
987
f32b3b91 988(defcustom idlwave-shell-explicit-file-name "idl"
fb7ada5f 989 "If non-nil, this is the command to run IDL.
f32b3b91 990Should be an absolute file path or path relative to the current environment
5e72c6b2 991execution search path. If you want to specify command line switches
5a0c3f56 992for the IDL program, use `idlwave-shell-command-line-options'.
5e72c6b2
S
993
994I know the name of this variable is badly chosen, but I cannot change
5a0c3f56 995it without compromising backwards-compatibility."
f32b3b91
CD
996 :group 'idlwave-external-programs
997 :type 'string)
998
f32b3b91 999(defcustom idlwave-shell-command-line-options nil
fb7ada5f 1000 "A list of command line options for calling the IDL program.
5e72c6b2
S
1001Since IDL is executed directly without going through a shell like /bin/sh,
1002this should be a list of strings like '(\"-rt=file\" \"-nw\") with a separate
1003string for each argument. But you may also give a single string which
1004contains the options whitespace-separated. Emacs will be kind enough to
1005split it for you."
1006 :type '(choice
1007 string
1008 (repeat (string :value "")))
f32b3b91
CD
1009 :group 'idlwave-external-programs)
1010
1011(defcustom idlwave-help-application "idlhelp"
fb7ada5f 1012 "The external application providing reference help for programming.
f66f03de 1013Obsolete, if the IDL Assistant is being used for help."
f32b3b91
CD
1014 :group 'idlwave-external-programs
1015 :type 'string)
1016
05a1abfc
CD
1017;;; Some Shell variables which must be defined here.-----------------------
1018
1019(defcustom idlwave-shell-debug-modifiers '()
1020 "List of modifiers to be used for the debugging commands.
1021Will be used to bind debugging commands in the shell buffer and in all
1022source buffers. These are additional convenience bindings, the debugging
1023commands are always available with the `C-c C-d' prefix.
1024If you set this to '(control shift), this means setting a breakpoint will
1025be on `C-S-b', compiling a source file on `C-S-c' etc. Possible modifiers
1026are `control', `meta', `super', `hyper', `alt', and `shift'."
1027 :group 'idlwave-shell-general-setup
1028 :type '(set :tag "Specify modifiers"
1029 (const control)
1030 (const meta)
1031 (const super)
1032 (const hyper)
1033 (const alt)
1034 (const shift)))
1035
1036(defcustom idlwave-shell-automatic-start nil
fb7ada5f 1037 "If non-nil attempt invoke `idlwave-shell' if not already running.
05a1abfc
CD
1038This is checked when an attempt to send a command to an
1039IDL process is made."
1040 :group 'idlwave-shell-general-setup
1041 :type 'boolean)
1042
f32b3b91
CD
1043;;; Miscellaneous variables -------------------------------------------------
1044
1045(defgroup idlwave-misc nil
1046 "Miscellaneous options for IDLWAVE mode."
8ec3bce0 1047 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
f32b3b91
CD
1048 :group 'idlwave)
1049
1050(defcustom idlwave-startup-message t
fb7ada5f 1051 "Non-nil displays a startup message when `idlwave-mode' is first called."
f32b3b91
CD
1052 :group 'idlwave-misc
1053 :type 'boolean)
1054
4b1aaa8b 1055(defcustom idlwave-default-font-lock-items
facebc7b 1056 '(pros-and-functions batch-files idlwave-idl-keywords label goto
f32b3b91
CD
1057 common-blocks class-arrows)
1058 "Items which should be fontified on the default fontification level 2.
1059IDLWAVE defines 3 levels of fontification. Level 1 is very little, level 3
1060is everything and level 2 is specified by this list.
5a0c3f56
JB
1061This variable must be set before IDLWAVE gets loaded.
1062It is a list of symbols; the following symbols are allowed:
f32b3b91
CD
1063
1064pros-and-functions Procedure and Function definitions
1065batch-files Batch Files
facebc7b 1066idlwave-idl-keywords IDL Keywords
f32b3b91
CD
1067label Statement Labels
1068goto Goto Statements
1069common-blocks Common Blocks
1070keyword-parameters Keyword Parameters in routine definitions and calls
1071system-variables System Variables
1072fixme FIXME: Warning in comments (on XEmacs only v. 21.0 and up)
1073class-arrows Object Arrows with class property"
1074 :group 'idlwave-misc
1075 :type '(set
1076 :inline t :greedy t
1077 (const :tag "Procedure and Function definitions" pros-and-functions)
facebc7b
S
1078 (const :tag "Batch Files" batch-files)
1079 (const :tag "IDL Keywords (reserved words)" idlwave-idl-keywords)
1080 (const :tag "Statement Labels" label)
1081 (const :tag "Goto Statements" goto)
1082 (const :tag "Tags in Structure Definition" structtag)
1083 (const :tag "Structure Name" structname)
1084 (const :tag "Common Blocks" common-blocks)
1085 (const :tag "Keyword Parameters" keyword-parameters)
1086 (const :tag "System Variables" system-variables)
1087 (const :tag "FIXME: Warning" fixme)
f32b3b91
CD
1088 (const :tag "Object Arrows with class property " class-arrows)))
1089
1090(defcustom idlwave-mode-hook nil
1091 "Normal hook. Executed when a buffer is put into `idlwave-mode'."
1092 :group 'idlwave-misc
1093 :type 'hook)
1094
1095(defcustom idlwave-load-hook nil
1096 "Normal hook. Executed when idlwave.el is loaded."
1097 :group 'idlwave-misc
1098 :type 'hook)
1099
15e42531
CD
1100(defvar idlwave-experimental nil
1101 "Non-nil means turn on a few experimental features.
1102This variable is only for the maintainer, to test difficult stuff,
1103while still distributing stable releases.
1104As a user, you should not set this to t.")
1105
f32b3b91
CD
1106;;;
1107;;; End customization variables section
1108;;;
1109
1110;;; Non customization variables
1111
1112;;; font-lock mode - Additions by Phil Williams, Ulrik Dickow and
52a244eb 1113;;; Simon Marshall <simon_at_gnu.ai.mit.edu>
f32b3b91
CD
1114;;; and Carsten Dominik...
1115
76959b77 1116;; The following are the reserved words in IDL. Maybe we should
4b1aaa8b 1117;; highlight some more stuff as well?
76959b77
S
1118;; Procedure declarations. Fontify keyword plus procedure name.
1119(defvar idlwave-idl-keywords
4b1aaa8b 1120 ;; To update this regexp, update the list of keywords and
76959b77 1121 ;; evaluate the form.
4b1aaa8b 1122 ;; (insert
76959b77 1123 ;; (prin1-to-string
4b1aaa8b 1124 ;; (concat
76959b77 1125 ;; "\\<\\("
4b1aaa8b 1126 ;; (regexp-opt
52a244eb 1127 ;; '("||" "&&" "and" "or" "xor" "not"
4b1aaa8b 1128 ;; "eq" "ge" "gt" "le" "lt" "ne"
76959b77 1129 ;; "for" "do" "endfor"
4b1aaa8b 1130 ;; "if" "then" "endif" "else" "endelse"
76959b77
S
1131 ;; "case" "of" "endcase"
1132 ;; "switch" "break" "continue" "endswitch"
1133 ;; "begin" "end"
1134 ;; "repeat" "until" "endrep"
4b1aaa8b 1135 ;; "while" "endwhile"
76959b77
S
1136 ;; "goto" "return"
1137 ;; "inherits" "mod"
1138 ;; "compile_opt" "forward_function"
1139 ;; "on_error" "on_ioerror")) ; on_error is not officially reserved
1140 ;; "\\)\\>")))
52a244eb
S
1141 "\\<\\(&&\\|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\\|||\\)\\>")
1142
76959b77 1143
facebc7b 1144(let* (;; Procedure declarations. Fontify keyword plus procedure name.
f32b3b91
CD
1145 ;; Function declarations. Fontify keyword plus function name.
1146 (pros-and-functions
1147 '("\\<\\(function\\|pro\\)\\>[ \t]+\\(\\sw+\\(::\\sw+\\)?\\)"
1148 (1 font-lock-keyword-face)
1149 (2 font-lock-function-name-face nil t)))
1150
1151 ;; Common blocks
1152 (common-blocks
1153 '("\\<\\(common\\)\\>[ \t]*\\(\\sw+\\)?[ \t]*,?"
1154 (1 font-lock-keyword-face) ; "common"
6c27f0f8 1155 (2 font-lock-constant-face nil t) ; block name
f66f03de 1156 ("[ \t]*\\(\\sw+\\)[ ,]*"
f32b3b91 1157 ;; Start with point after block name and comma
4b1aaa8b 1158 (goto-char (match-end 0)) ; needed for XEmacs, could be nil
f32b3b91
CD
1159 nil
1160 (1 font-lock-variable-name-face) ; variable names
1161 )))
1162
1163 ;; Batch files
1164 (batch-files
1165 '("^[ \t]*\\(@[^ \t\n]+\\)" (1 font-lock-string-face)))
1166
1167 ;; FIXME warning.
1168 (fixme
1169 '("\\<FIXME:" (0 font-lock-warning-face t)))
1170
1171 ;; Labels
1172 (label
6c27f0f8 1173 '("^[ \t]*\\([a-zA-Z]\\sw*:\\)" (1 font-lock-constant-face)))
f32b3b91
CD
1174
1175 ;; The goto statement and its label
1176 (goto
1177 '("\\(goto\\)[ \t]*,[ \t]*\\([a-zA-Z]\\sw*\\)"
1178 (1 font-lock-keyword-face)
6c27f0f8 1179 (2 font-lock-constant-face)))
f32b3b91 1180
52a244eb
S
1181 ;; Tags in structure definitions. Note that this definition
1182 ;; actually collides with labels, so we have to use the same
1183 ;; face. It also matches named subscript ranges,
1184 ;; e.g. vec{bottom:top]. No good way around this.
05a1abfc 1185 (structtag
6c27f0f8 1186 '("\\<\\([a-zA-Z][a-zA-Z0-9_]*:\\)[^:]" (1 font-lock-constant-face)))
05a1abfc
CD
1187
1188 ;; Structure names
1189 (structname
1190 '("\\({\\|\\<inherits\\s-\\)\\s-*\\([a-zA-Z][a-zA-Z0-9_]*\\)[},\t \n]"
1191 (2 font-lock-function-name-face)))
1192
52a244eb 1193 ;; Keyword parameters, like /xlog or ,xrange=[]
97610156 1194 ;; This is anchored to the comma preceding the keyword.
595ab50b
CD
1195 ;; Treats continuation lines, works only during whole buffer
1196 ;; fontification. Slow, use it only in fancy fontification.
f32b3b91 1197 (keyword-parameters
0dc2be2f 1198 '("\\(,\\|[a-zA-Z0-9_](\\)[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\([ \t]*\\(;.*\\)?\n\\)*[ \t]*\\)?\\(/[a-zA-Z_]\\sw*\\|[a-zA-Z_]\\sw*[ \t]*=\\)"
6c27f0f8 1199 (6 font-lock-constant-face)))
f32b3b91 1200
595ab50b 1201 ;; System variables start with a bang.
f32b3b91 1202 (system-variables
15e42531 1203 '("\\(![a-zA-Z_0-9]+\\(\\.\\sw+\\)?\\)"
f32b3b91
CD
1204 (1 font-lock-variable-name-face)))
1205
1206 ;; Special and unusual operators (not used because too noisy)
8d222148
SM
1207 ;; (special-operators
1208 ;; '("[<>#]" (0 font-lock-keyword-face)))
f32b3b91
CD
1209
1210 ;; All operators (not used because too noisy)
8d222148
SM
1211 ;; (all-operators
1212 ;; '("[-*^#+<>/]" (0 font-lock-keyword-face)))
4b1aaa8b 1213
f32b3b91
CD
1214 ;; Arrows with text property `idlwave-class'
1215 (class-arrows
facebc7b
S
1216 '(idlwave-match-class-arrows (0 idlwave-class-arrow-face))))
1217
1218 (defconst idlwave-font-lock-keywords-1
1219 (list pros-and-functions batch-files)
1220 "Subdued level highlighting for IDLWAVE mode.")
f32b3b91 1221
facebc7b
S
1222 (defconst idlwave-font-lock-keywords-2
1223 (mapcar 'symbol-value idlwave-default-font-lock-items)
1224 "Medium level highlighting for IDLWAVE mode.")
f32b3b91 1225
facebc7b 1226 (defconst idlwave-font-lock-keywords-3
f32b3b91
CD
1227 (list pros-and-functions
1228 batch-files
76959b77 1229 idlwave-idl-keywords
f32b3b91 1230 label goto
05a1abfc
CD
1231 structtag
1232 structname
f32b3b91
CD
1233 common-blocks
1234 keyword-parameters
1235 system-variables
facebc7b
S
1236 class-arrows)
1237 "Gaudy level highlighting for IDLWAVE mode."))
f32b3b91
CD
1238
1239(defun idlwave-match-class-arrows (limit)
1240 ;; Match an object arrow with class property
1241 (and idlwave-store-inquired-class
1242 (re-search-forward "->" limit 'limit)
1243 (get-text-property (match-beginning 0) 'idlwave-class)))
1244
1245(defvar idlwave-font-lock-keywords idlwave-font-lock-keywords-2
1246 "Default expressions to highlight in IDLWAVE mode.")
1247
1248(defvar idlwave-font-lock-defaults
1249 '((idlwave-font-lock-keywords
4b1aaa8b 1250 idlwave-font-lock-keywords-1
f32b3b91
CD
1251 idlwave-font-lock-keywords-2
1252 idlwave-font-lock-keywords-3)
4b1aaa8b
PE
1253 nil t
1254 ((?$ . "w") (?_ . "w") (?. . "w") (?| . "w") (?& . "w"))
f32b3b91
CD
1255 beginning-of-line))
1256
4b1aaa8b 1257(put 'idlwave-mode 'font-lock-defaults
f32b3b91
CD
1258 idlwave-font-lock-defaults) ; XEmacs
1259
1260(defconst idlwave-comment-line-start-skip "^[ \t]*;"
1261 "Regexp to match the start of a full-line comment.
1262That is the _beginning_ of a line containing a comment delimiter `;' preceded
1263only by whitespace.")
1264
4b1aaa8b 1265(defconst idlwave-begin-block-reg
05a1abfc 1266 "\\<\\(pro\\|function\\|begin\\|case\\|switch\\)\\>"
5a0c3f56
JB
1267 "Regular expression to find the beginning of a block.
1268The case does not matter. The search skips matches in comments.")
f32b3b91 1269
52a244eb 1270(defconst idlwave-begin-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\`"
5a0c3f56
JB
1271 "Regular expression to find the beginning of a unit.
1272The case does not matter.")
f32b3b91 1273
52a244eb 1274(defconst idlwave-end-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\'"
f32b3b91 1275 "Regular expression to find the line that indicates the end of unit.
5a0c3f56
JB
1276This line is the end of buffer or the start of another unit.
1277The case does not matter. The search skips matches in comments.")
f32b3b91
CD
1278
1279(defconst idlwave-continue-line-reg "\\<\\$"
1280 "Regular expression to match a continued line.")
1281
1282(defconst idlwave-end-block-reg
05a1abfc 1283 "\\<end\\(\\|case\\|switch\\|else\\|for\\|if\\|rep\\|while\\)\\>"
5a0c3f56
JB
1284 "Regular expression to find the end of a block.
1285The case does not matter. The search skips matches in comments.")
f32b3b91
CD
1286
1287(defconst idlwave-block-matches
1288 '(("pro" . "end")
1289 ("function" . "end")
1290 ("case" . "endcase")
1291 ("else" . "endelse")
1292 ("for" . "endfor")
1293 ("then" . "endif")
1294 ("repeat" . "endrep")
05a1abfc 1295 ("switch" . "endswitch")
f32b3b91
CD
1296 ("while" . "endwhile"))
1297 "Matches between statements and the corresponding END variant.
1298The cars are the reserved words starting a block. If the block really
1299begins with BEGIN, the cars are the reserved words before the begin
1300which can be used to identify the block type.
1301This is used to check for the correct END type, to close blocks and
1302to expand generic end statements to their detailed form.")
1303
1304(defconst idlwave-block-match-regexp
1305 "\\<\\(else\\|for\\|then\\|repeat\\|while\\)\\>"
1306"Regular expression matching reserved words which can stand before
1307blocks starting with a BEGIN statement. The matches must have associations
5a0c3f56 1308`idlwave-block-matches'.")
f32b3b91 1309
52a244eb 1310(defconst idlwave-identifier "[a-zA-Z_][a-zA-Z0-9$_]*"
f32b3b91
CD
1311 "Regular expression matching an IDL identifier.")
1312
1313(defconst idlwave-sysvar (concat "!" idlwave-identifier)
1314 "Regular expression matching IDL system variables.")
1315
1316(defconst idlwave-variable (concat idlwave-identifier "\\|" idlwave-sysvar)
1317 "Regular expression matching IDL variable names.")
1318
1319(defconst idlwave-label (concat idlwave-identifier ":")
1320 "Regular expression matching IDL labels.")
1321
52a244eb
S
1322(defconst idlwave-method-call (concat idlwave-identifier "\\s *->"
1323 "\\(\\s *" idlwave-identifier "::\\)?"
1324))
1325
f32b3b91
CD
1326(defconst idlwave-statement-match
1327 (list
aa87aafc 1328 ;; "endif else" is the only possible "end" that can be
f32b3b91
CD
1329 ;; followed by a statement on the same line.
1330 '(endelse . ("end\\(\\|if\\)\\s +else" "end\\(\\|if\\)\\s +else"))
1331 ;; all other "end"s can not be followed by a statement.
1332 (cons 'end (list idlwave-end-block-reg nil))
1333 '(if . ("if\\>" "then"))
1334 '(for . ("for\\>" "do"))
1335 '(begin . ("begin\\>" nil))
1336 '(pdef . ("pro\\>\\|function\\>" nil))
1337 '(while . ("while\\>" "do"))
1338 '(repeat . ("repeat\\>" "repeat"))
1339 '(goto . ("goto\\>" nil))
1340 '(case . ("case\\>" nil))
05a1abfc 1341 '(switch . ("switch\\>" nil))
4b1aaa8b 1342 (cons 'call (list (concat "\\(" idlwave-variable "\\) *= *"
52a244eb
S
1343 "\\(" idlwave-method-call "\\s *\\)?"
1344 idlwave-identifier
1345 "\\s *(") nil))
4b1aaa8b 1346 (cons 'call (list (concat
52a244eb 1347 "\\(" idlwave-method-call "\\s *\\)?"
4b1aaa8b 1348 idlwave-identifier
52a244eb 1349 "\\( *\\($\\|\\$\\)\\|\\s *,\\)") nil))
4b1aaa8b 1350 (cons 'assign (list (concat
52a244eb 1351 "\\(" idlwave-variable "\\) *=") nil)))
4b1aaa8b 1352
f32b3b91 1353 "Associated list of statement matching regular expressions.
5a0c3f56
JB
1354Each regular expression matches the start of an IDL statement.
1355The first element of each association is a symbol giving the statement
f32b3b91
CD
1356type. The associated value is a list. The first element of this list
1357is a regular expression matching the start of an IDL statement for
1358identifying the statement type. The second element of this list is a
1359regular expression for finding a substatement for the type. The
1360substatement starts after the end of the found match modulo
1361whitespace. If it is nil then the statement has no substatement. The
1362list order matters since matching an assignment statement exactly is
1363not possible without parsing. Thus assignment statement become just
5a0c3f56 1364the leftover unidentified statements containing an equal sign.")
f32b3b91 1365
f44379e7 1366;; FIXME: This var seems to only ever be set, but never actually used!
f32b3b91
CD
1367(defvar idlwave-fill-function 'auto-fill-function
1368 "IDL mode auto fill function.")
1369
1370(defvar idlwave-comment-indent-function 'comment-indent-function
1371 "IDL mode comment indent function.")
1372
1373;; Note that this is documented in the v18 manuals as being a string
1374;; of length one rather than a single character.
1375;; The code in this file accepts either format for compatibility.
4b1aaa8b 1376(defvar idlwave-comment-indent-char ?\
f32b3b91
CD
1377 "Character to be inserted for IDL comment indentation.
1378Normally a space.")
1379
1380(defconst idlwave-continuation-char ?$
1381 "Character which is inserted as a last character on previous line by
1382 \\[idlwave-split-line] to begin a continuation line. Normally $.")
1383
e08734e2 1384(defconst idlwave-mode-version "6.1_em22")
f32b3b91
CD
1385
1386(defmacro idlwave-keyword-abbrev (&rest args)
1387 "Creates a function for abbrev hooks to call `idlwave-check-abbrev' with args."
8a946354 1388 `(quote (lambda ()
5e72c6b2 1389 ,(append '(idlwave-check-abbrev) args))))
f32b3b91
CD
1390
1391;; If I take the time I can replace idlwave-keyword-abbrev with
1392;; idlwave-code-abbrev and remove the quoted abbrev check from
1393;; idlwave-check-abbrev. Then, e.g, (idlwave-keyword-abbrev 0 t) becomes
1394;; (idlwave-code-abbrev idlwave-check-abbrev 0 t). In fact I should change
1395;; the name of idlwave-check-abbrev to something like idlwave-modify-abbrev.
1396
1397(defmacro idlwave-code-abbrev (&rest args)
1398 "Creates a function for abbrev hooks that ensures abbrevs are not quoted.
1399Specifically, if the abbrev is in a comment or string it is unexpanded.
1400Otherwise ARGS forms a list that is evaluated."
8d222148
SM
1401 ;; FIXME: it would probably be better to rely on the new :enable-function
1402 ;; to enforce the "don't expand in comments or strings".
1403 `(lambda ()
1404 ,(prin1-to-string args) ;; Puts the code in the doc string
1405 (if (idlwave-quoted)
1406 (progn (unexpand-abbrev) nil)
1407 ,(append args))))
1408
1409(autoload 'idlwave-shell "idlw-shell"
1410 "Run an inferior IDL, with I/O through buffer `(idlwave-shell-buffer)'." t)
1411(autoload 'idlwave-shell-send-command "idlw-shell")
1412(autoload 'idlwave-shell-recenter-shell-window "idlw-shell"
1413 "Run `idlwave-shell' and switch back to current window" t)
1414(autoload 'idlwave-shell-save-and-run "idlw-shell"
1415 "Save and run buffer under the shell." t)
1416(autoload 'idlwave-shell-break-here "idlw-shell"
1417 "Set breakpoint in current line." t)
1418(autoload 'idlwave-shell-run-region "idlw-shell"
1419 "Compile and run the region." t)
f32b3b91 1420
8d222148
SM
1421(fset 'idlwave-debug-map (make-sparse-keymap))
1422
1423(defvar idlwave-mode-map
1424 (let ((map (make-sparse-keymap)))
1425 (define-key map "\C-c " 'idlwave-hard-tab)
1426 (define-key map [(control tab)] 'idlwave-hard-tab)
1427 ;;(define-key map "\C-c\C- " 'idlwave-hard-tab)
1428 (define-key map "'" 'idlwave-show-matching-quote)
1429 (define-key map "\"" 'idlwave-show-matching-quote)
1430 (define-key map "\C-g" 'idlwave-keyboard-quit)
1431 (define-key map "\C-c;" 'idlwave-toggle-comment-region)
1432 (define-key map "\C-\M-a" 'idlwave-beginning-of-subprogram)
1433 (define-key map "\C-\M-e" 'idlwave-end-of-subprogram)
1434 (define-key map "\C-c{" 'idlwave-beginning-of-block)
1435 (define-key map "\C-c}" 'idlwave-end-of-block)
1436 (define-key map "\C-c]" 'idlwave-close-block)
1437 (define-key map [(meta control h)] 'idlwave-mark-subprogram)
1438 (define-key map "\M-\C-n" 'idlwave-forward-block)
1439 (define-key map "\M-\C-p" 'idlwave-backward-block)
1440 (define-key map "\M-\C-d" 'idlwave-down-block)
1441 (define-key map "\M-\C-u" 'idlwave-backward-up-block)
1442 (define-key map "\M-\r" 'idlwave-split-line)
1443 (define-key map "\M-\C-q" 'idlwave-indent-subprogram)
1444 (define-key map "\C-c\C-p" 'idlwave-previous-statement)
1445 (define-key map "\C-c\C-n" 'idlwave-next-statement)
1446 ;; (define-key map "\r" 'idlwave-newline)
1447 ;; (define-key map "\t" 'idlwave-indent-line)
1448 (define-key map [(shift iso-lefttab)] 'idlwave-indent-statement)
1449 (define-key map "\C-c\C-a" 'idlwave-auto-fill-mode)
1450 (define-key map "\M-q" 'idlwave-fill-paragraph)
1451 (define-key map "\M-s" 'idlwave-edit-in-idlde)
1452 (define-key map "\C-c\C-h" 'idlwave-doc-header)
1453 (define-key map "\C-c\C-m" 'idlwave-doc-modification)
1454 (define-key map "\C-c\C-c" 'idlwave-case)
1455 (define-key map "\C-c\C-d" 'idlwave-debug-map)
1456 (when (and (listp idlwave-shell-debug-modifiers)
1457 (not (equal idlwave-shell-debug-modifiers '())))
1458 ;; Bind the debug commands also with the special modifiers.
1459 (let ((shift (memq 'shift idlwave-shell-debug-modifiers))
1460 (mods-noshift
1461 (delq 'shift (copy-sequence idlwave-shell-debug-modifiers))))
1462 (define-key map
1463 (vector (append mods-noshift (list (if shift ?C ?c))))
1464 'idlwave-shell-save-and-run)
1465 (define-key map
1466 (vector (append mods-noshift (list (if shift ?B ?b))))
1467 'idlwave-shell-break-here)
1468 (define-key map
1469 (vector (append mods-noshift (list (if shift ?E ?e))))
1470 'idlwave-shell-run-region)))
1471 (define-key map "\C-c\C-d\C-c" 'idlwave-shell-save-and-run)
1472 (define-key map "\C-c\C-d\C-b" 'idlwave-shell-break-here)
1473 (define-key map "\C-c\C-d\C-e" 'idlwave-shell-run-region)
1474 (define-key map "\C-c\C-f" 'idlwave-for)
1475 ;; (define-key map "\C-c\C-f" 'idlwave-function)
1476 ;; (define-key map "\C-c\C-p" 'idlwave-procedure)
1477 (define-key map "\C-c\C-r" 'idlwave-repeat)
1478 (define-key map "\C-c\C-w" 'idlwave-while)
1479 (define-key map "\C-c\C-k" 'idlwave-kill-autoloaded-buffers)
1480 (define-key map "\C-c\C-s" 'idlwave-shell)
1481 (define-key map "\C-c\C-l" 'idlwave-shell-recenter-shell-window)
1482 (define-key map "\C-c\C-b" 'idlwave-list-buffer-load-path-shadows)
1483 (define-key map "\C-c\C-v" 'idlwave-find-module)
1484 (define-key map "\C-c\C-t" 'idlwave-find-module-this-file)
1485 (define-key map "\C-c?" 'idlwave-routine-info)
1486 (define-key map "\M-?" 'idlwave-context-help)
1487 (define-key map [(control meta ?\?)]
1488 'idlwave-help-assistant-help-with-topic)
1489 ;; Pickup both forms of Esc/Meta binding
1490 (define-key map [(meta tab)] 'idlwave-complete)
1491 (define-key map [?\e?\t] 'idlwave-complete)
1492 (define-key map "\M-\C-i" 'idlwave-complete)
1493 (define-key map "\C-c\C-i" 'idlwave-update-routine-info)
1494 (define-key map "\C-c=" 'idlwave-resolve)
1495 (define-key map
1496 (if (featurep 'xemacs) [(shift button3)] [(shift mouse-3)])
1497 'idlwave-mouse-context-help)
1498 map)
f32b3b91
CD
1499 "Keymap used in IDL mode.")
1500
8d222148
SM
1501(defvar idlwave-mode-syntax-table
1502 (let ((st (make-syntax-table)))
1503 (modify-syntax-entry ?+ "." st)
1504 (modify-syntax-entry ?- "." st)
1505 (modify-syntax-entry ?* "." st)
1506 (modify-syntax-entry ?/ "." st)
1507 (modify-syntax-entry ?^ "." st)
1508 (modify-syntax-entry ?# "." st)
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 ?\n ">" st)
1523 (modify-syntax-entry ?\f ">" st)
1524 st)
f32b3b91
CD
1525 "Syntax table in use in `idlwave-mode' buffers.")
1526
f32b3b91 1527(defvar idlwave-find-symbol-syntax-table
8d222148
SM
1528 (let ((st (copy-syntax-table idlwave-mode-syntax-table)))
1529 (modify-syntax-entry ?$ "w" st)
1530 (modify-syntax-entry ?_ "w" st)
1531 (modify-syntax-entry ?! "w" st)
1532 (modify-syntax-entry ?. "w" st)
1533 st)
f32b3b91
CD
1534 "Syntax table that treats symbol characters as word characters.")
1535
76959b77
S
1536(defmacro idlwave-with-special-syntax (&rest body)
1537 "Execute BODY with a different syntax table."
05a1abfc
CD
1538 `(let ((saved-syntax (syntax-table)))
1539 (unwind-protect
1540 (progn
1541 (set-syntax-table idlwave-find-symbol-syntax-table)
1542 ,@body)
1543 (set-syntax-table saved-syntax))))
1544
76959b77
S
1545;(defmacro idlwave-with-special-syntax1 (&rest body)
1546; "Execute BODY with a different syntax table."
1547; `(let ((saved-syntax (syntax-table)))
1548; (unwind-protect
1549; (progn
1550; (set-syntax-table idlwave-find-symbol-syntax-table)
1551; ,@body)
1552; (set-syntax-table saved-syntax))))
1553
f32b3b91
CD
1554(defun idlwave-action-and-binding (key cmd &optional select)
1555 "KEY and CMD are made into a key binding and an indent action.
1556KEY is a string - same as for the `define-key' function. CMD is a
1557function of no arguments or a list to be evaluated. CMD is bound to
1558KEY in `idlwave-mode-map' by defining an anonymous function calling
1559`self-insert-command' followed by CMD. If KEY contains more than one
1560character a binding will only be set if SELECT is 'both.
1561
5e72c6b2 1562\(KEY . CMD\) is also placed in the `idlwave-indent-expand-table',
f32b3b91
CD
1563replacing any previous value for KEY. If a binding is not set then it
1564will instead be placed in `idlwave-indent-action-table'.
1565
1566If the optional argument SELECT is nil then an action and binding are
1567created. If SELECT is 'noaction, then a binding is always set and no
1568action is created. If SELECT is 'both then an action and binding
1569will both be created even if KEY contains more than one character.
1570Otherwise, if SELECT is non-nil then only an action is created.
1571
1572Some examples:
1573No spaces before and 1 after a comma
1574 (idlwave-action-and-binding \",\" '(idlwave-surround 0 1))
1575A minimum of 1 space before and after `=' (see `idlwave-expand-equal').
1576 (idlwave-action-and-binding \"=\" '(idlwave-expand-equal -1 -1))
1577Capitalize system variables - action only
1578 (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)"
1579 (if (not (equal select 'noaction))
1580 ;; Add action
1581 (let* ((table (if select 'idlwave-indent-action-table
1582 'idlwave-indent-expand-table))
3938cb82
S
1583 (table-key (regexp-quote key))
1584 (cell (assoc table-key (eval table))))
f32b3b91
CD
1585 (if cell
1586 ;; Replace action command
1587 (setcdr cell cmd)
1588 ;; New action
3938cb82 1589 (set table (append (eval table) (list (cons table-key cmd)))))))
f32b3b91
CD
1590 ;; Make key binding for action
1591 (if (or (and (null select) (= (length key) 1))
1592 (equal select 'noaction)
1593 (equal select 'both))
1594 (define-key idlwave-mode-map key
8d222148
SM
1595 `(lambda ()
1596 (interactive)
1597 (self-insert-command 1)
4111f0c7 1598 ,(if (listp cmd) cmd (list cmd))))))
f32b3b91
CD
1599
1600;; Set action and key bindings.
1601;; See description of the function `idlwave-action-and-binding'.
1602;; Automatically add spaces for the following characters
f66f03de
S
1603
1604;; Actions for & are complicated by &&
1605(idlwave-action-and-binding "&" 'idlwave-custom-ampersand-surround)
1606
1607;; Automatically add spaces to equal sign if not keyword. This needs
1608;; to go ahead of > and <, so >= and <= will be treated correctly
f32b3b91
CD
1609(idlwave-action-and-binding "=" '(idlwave-expand-equal -1 -1))
1610
4b1aaa8b 1611;; Actions for > and < are complicated by >=, <=, and ->...
f66f03de
S
1612(idlwave-action-and-binding "<" '(idlwave-custom-ltgtr-surround nil))
1613(idlwave-action-and-binding ">" '(idlwave-custom-ltgtr-surround 'gtr))
1614
1615(idlwave-action-and-binding "," '(idlwave-surround 0 -1 1))
1616
1617
f32b3b91
CD
1618;;;
1619;;; Abbrev Section
1620;;;
1621;;; When expanding abbrevs and the abbrev hook moves backward, an extra
1622;;; space is inserted (this is the space typed by the user to expanded
1623;;; the abbrev).
1624;;;
5e72c6b2 1625(defvar idlwave-mode-abbrev-table nil
5a0c3f56 1626 "Abbreviation table used for IDLWAVE mode.")
5e72c6b2
S
1627(define-abbrev-table 'idlwave-mode-abbrev-table ())
1628
1629(defun idlwave-define-abbrev (name expansion hook &optional noprefix table)
1630 "Define-abbrev with backward compatibility.
1631
1632If NOPREFIX is non-nil, don't prepend prefix character. Installs into
5a0c3f56 1633`idlwave-mode-abbrev-table' unless TABLE is non-nil."
5e72c6b2
S
1634 (let ((abbrevs-changed nil) ;; mask the current value to avoid save
1635 (args (list (or table idlwave-mode-abbrev-table)
1636 (if noprefix name (concat idlwave-abbrev-start-char name))
1637 expansion
1638 hook)))
1639 (condition-case nil
1640 (apply 'define-abbrev (append args '(0 t)))
1641 (error (apply 'define-abbrev args)))))
f32b3b91
CD
1642
1643(condition-case nil
4b1aaa8b 1644 (modify-syntax-entry (string-to-char idlwave-abbrev-start-char)
f32b3b91
CD
1645 "w" idlwave-mode-syntax-table)
1646 (error nil))
1647
5e72c6b2
S
1648;;
1649;; Templates
1650;;
1651(idlwave-define-abbrev "c" "" (idlwave-code-abbrev idlwave-case))
1652(idlwave-define-abbrev "sw" "" (idlwave-code-abbrev idlwave-switch))
1653(idlwave-define-abbrev "f" "" (idlwave-code-abbrev idlwave-for))
1654(idlwave-define-abbrev "fu" "" (idlwave-code-abbrev idlwave-function))
1655(idlwave-define-abbrev "pr" "" (idlwave-code-abbrev idlwave-procedure))
1656(idlwave-define-abbrev "r" "" (idlwave-code-abbrev idlwave-repeat))
1657(idlwave-define-abbrev "w" "" (idlwave-code-abbrev idlwave-while))
1658(idlwave-define-abbrev "i" "" (idlwave-code-abbrev idlwave-if))
1659(idlwave-define-abbrev "elif" "" (idlwave-code-abbrev idlwave-elif))
1660;;
1661;; Keywords, system functions, conversion routines
1662;;
1663(idlwave-define-abbrev "ap" "arg_present()" (idlwave-keyword-abbrev 1))
1664(idlwave-define-abbrev "b" "begin" (idlwave-keyword-abbrev 0 t))
1665(idlwave-define-abbrev "co" "common" (idlwave-keyword-abbrev 0 t))
1666(idlwave-define-abbrev "cb" "byte()" (idlwave-keyword-abbrev 1))
1667(idlwave-define-abbrev "cx" "fix()" (idlwave-keyword-abbrev 1))
1668(idlwave-define-abbrev "cl" "long()" (idlwave-keyword-abbrev 1))
1669(idlwave-define-abbrev "cf" "float()" (idlwave-keyword-abbrev 1))
1670(idlwave-define-abbrev "cs" "string()" (idlwave-keyword-abbrev 1))
1671(idlwave-define-abbrev "cc" "complex()" (idlwave-keyword-abbrev 1))
1672(idlwave-define-abbrev "cd" "double()" (idlwave-keyword-abbrev 1))
1673(idlwave-define-abbrev "e" "else" (idlwave-keyword-abbrev 0 t))
1674(idlwave-define-abbrev "ec" "endcase" 'idlwave-show-begin)
1675(idlwave-define-abbrev "es" "endswitch" 'idlwave-show-begin)
1676(idlwave-define-abbrev "ee" "endelse" 'idlwave-show-begin)
1677(idlwave-define-abbrev "ef" "endfor" 'idlwave-show-begin)
1678(idlwave-define-abbrev "ei" "endif else if" 'idlwave-show-begin)
1679(idlwave-define-abbrev "el" "endif else" 'idlwave-show-begin)
1680(idlwave-define-abbrev "en" "endif" 'idlwave-show-begin)
1681(idlwave-define-abbrev "er" "endrep" 'idlwave-show-begin)
1682(idlwave-define-abbrev "ew" "endwhile" 'idlwave-show-begin)
1683(idlwave-define-abbrev "g" "goto," (idlwave-keyword-abbrev 0 t))
1684(idlwave-define-abbrev "h" "help," (idlwave-keyword-abbrev 0))
1685(idlwave-define-abbrev "k" "keyword_set()" (idlwave-keyword-abbrev 1))
1686(idlwave-define-abbrev "n" "n_elements()" (idlwave-keyword-abbrev 1))
1687(idlwave-define-abbrev "on" "on_error," (idlwave-keyword-abbrev 0))
1688(idlwave-define-abbrev "oi" "on_ioerror," (idlwave-keyword-abbrev 0 1))
1689(idlwave-define-abbrev "ow" "openw," (idlwave-keyword-abbrev 0))
1690(idlwave-define-abbrev "or" "openr," (idlwave-keyword-abbrev 0))
1691(idlwave-define-abbrev "ou" "openu," (idlwave-keyword-abbrev 0))
1692(idlwave-define-abbrev "p" "print," (idlwave-keyword-abbrev 0))
1693(idlwave-define-abbrev "pt" "plot," (idlwave-keyword-abbrev 0))
1694(idlwave-define-abbrev "re" "read," (idlwave-keyword-abbrev 0))
1695(idlwave-define-abbrev "rf" "readf," (idlwave-keyword-abbrev 0))
1696(idlwave-define-abbrev "ru" "readu," (idlwave-keyword-abbrev 0))
1697(idlwave-define-abbrev "rt" "return" (idlwave-keyword-abbrev 0))
1698(idlwave-define-abbrev "sc" "strcompress()" (idlwave-keyword-abbrev 1))
1699(idlwave-define-abbrev "sn" "strlen()" (idlwave-keyword-abbrev 1))
1700(idlwave-define-abbrev "sl" "strlowcase()" (idlwave-keyword-abbrev 1))
1701(idlwave-define-abbrev "su" "strupcase()" (idlwave-keyword-abbrev 1))
1702(idlwave-define-abbrev "sm" "strmid()" (idlwave-keyword-abbrev 1))
1703(idlwave-define-abbrev "sp" "strpos()" (idlwave-keyword-abbrev 1))
1704(idlwave-define-abbrev "st" "strput()" (idlwave-keyword-abbrev 1))
1705(idlwave-define-abbrev "sr" "strtrim()" (idlwave-keyword-abbrev 1))
1706(idlwave-define-abbrev "t" "then" (idlwave-keyword-abbrev 0 t))
1707(idlwave-define-abbrev "u" "until" (idlwave-keyword-abbrev 0 t))
1708(idlwave-define-abbrev "wu" "writeu," (idlwave-keyword-abbrev 0))
1709(idlwave-define-abbrev "iap" "if arg_present() then" (idlwave-keyword-abbrev 6))
1710(idlwave-define-abbrev "ik" "if keyword_set() then" (idlwave-keyword-abbrev 6))
1711(idlwave-define-abbrev "ine" "if n_elements() eq 0 then" (idlwave-keyword-abbrev 11))
1712(idlwave-define-abbrev "inn" "if n_elements() ne 0 then" (idlwave-keyword-abbrev 11))
1713(idlwave-define-abbrev "np" "n_params()" (idlwave-keyword-abbrev 0))
1714(idlwave-define-abbrev "s" "size()" (idlwave-keyword-abbrev 1))
1715(idlwave-define-abbrev "wi" "widget_info()" (idlwave-keyword-abbrev 1))
1716(idlwave-define-abbrev "wc" "widget_control," (idlwave-keyword-abbrev 0))
3938cb82
S
1717(idlwave-define-abbrev "pv" "ptr_valid()" (idlwave-keyword-abbrev 1))
1718(idlwave-define-abbrev "ipv" "if ptr_valid() then" (idlwave-keyword-abbrev 6))
ff689efd 1719
5e72c6b2
S
1720;; This section is reserved words only. (From IDL user manual)
1721;;
1722(idlwave-define-abbrev "and" "and" (idlwave-keyword-abbrev 0 t) t)
1723(idlwave-define-abbrev "begin" "begin" (idlwave-keyword-abbrev 0 t) t)
1724(idlwave-define-abbrev "break" "break" (idlwave-keyword-abbrev 0 t) t)
1725(idlwave-define-abbrev "case" "case" (idlwave-keyword-abbrev 0 t) t)
1726(idlwave-define-abbrev "common" "common" (idlwave-keyword-abbrev 0 t) t)
1727(idlwave-define-abbrev "continue" "continue" (idlwave-keyword-abbrev 0 t) t)
1728(idlwave-define-abbrev "do" "do" (idlwave-keyword-abbrev 0 t) t)
1729(idlwave-define-abbrev "else" "else" (idlwave-keyword-abbrev 0 t) t)
1730(idlwave-define-abbrev "end" "end" 'idlwave-show-begin-check t)
1731(idlwave-define-abbrev "endcase" "endcase" 'idlwave-show-begin-check t)
1732(idlwave-define-abbrev "endelse" "endelse" 'idlwave-show-begin-check t)
1733(idlwave-define-abbrev "endfor" "endfor" 'idlwave-show-begin-check t)
1734(idlwave-define-abbrev "endif" "endif" 'idlwave-show-begin-check t)
1735(idlwave-define-abbrev "endrep" "endrep" 'idlwave-show-begin-check t)
1736(idlwave-define-abbrev "endswitch" "endswitch" 'idlwave-show-begin-check t)
1737(idlwave-define-abbrev "endwhi" "endwhi" 'idlwave-show-begin-check t)
1738(idlwave-define-abbrev "endwhile" "endwhile" 'idlwave-show-begin-check t)
1739(idlwave-define-abbrev "eq" "eq" (idlwave-keyword-abbrev 0 t) t)
1740(idlwave-define-abbrev "for" "for" (idlwave-keyword-abbrev 0 t) t)
1741(idlwave-define-abbrev "function" "function" (idlwave-keyword-abbrev 0 t) t)
1742(idlwave-define-abbrev "ge" "ge" (idlwave-keyword-abbrev 0 t) t)
1743(idlwave-define-abbrev "goto" "goto" (idlwave-keyword-abbrev 0 t) t)
1744(idlwave-define-abbrev "gt" "gt" (idlwave-keyword-abbrev 0 t) t)
1745(idlwave-define-abbrev "if" "if" (idlwave-keyword-abbrev 0 t) t)
1746(idlwave-define-abbrev "le" "le" (idlwave-keyword-abbrev 0 t) t)
1747(idlwave-define-abbrev "lt" "lt" (idlwave-keyword-abbrev 0 t) t)
1748(idlwave-define-abbrev "mod" "mod" (idlwave-keyword-abbrev 0 t) t)
1749(idlwave-define-abbrev "ne" "ne" (idlwave-keyword-abbrev 0 t) t)
1750(idlwave-define-abbrev "not" "not" (idlwave-keyword-abbrev 0 t) t)
1751(idlwave-define-abbrev "of" "of" (idlwave-keyword-abbrev 0 t) t)
1752(idlwave-define-abbrev "on_ioerror" "on_ioerror" (idlwave-keyword-abbrev 0 t) t)
1753(idlwave-define-abbrev "or" "or" (idlwave-keyword-abbrev 0 t) t)
1754(idlwave-define-abbrev "pro" "pro" (idlwave-keyword-abbrev 0 t) t)
1755(idlwave-define-abbrev "repeat" "repeat" (idlwave-keyword-abbrev 0 t) t)
1756(idlwave-define-abbrev "switch" "switch" (idlwave-keyword-abbrev 0 t) t)
1757(idlwave-define-abbrev "then" "then" (idlwave-keyword-abbrev 0 t) t)
1758(idlwave-define-abbrev "until" "until" (idlwave-keyword-abbrev 0 t) t)
1759(idlwave-define-abbrev "while" "while" (idlwave-keyword-abbrev 0 t) t)
1760(idlwave-define-abbrev "xor" "xor" (idlwave-keyword-abbrev 0 t) t)
f32b3b91
CD
1761
1762(defvar imenu-create-index-function)
1763(defvar extract-index-name-function)
1764(defvar prev-index-position-function)
1765(defvar imenu-extract-index-name-function)
1766(defvar imenu-prev-index-position-function)
5e72c6b2 1767;; defined later - so just make the compiler hush
4b1aaa8b 1768(defvar idlwave-mode-menu)
f32b3b91
CD
1769(defvar idlwave-mode-debug-menu)
1770
1771;;;###autoload
175069ef 1772(define-derived-mode idlwave-mode prog-mode "IDLWAVE"
e08734e2 1773 "Major mode for editing IDL source files (version 6.1_em22).
f32b3b91
CD
1774
1775The main features of this mode are
1776
17771. Indentation and Formatting
1778 --------------------------
1779 Like other Emacs programming modes, C-j inserts a newline and indents.
1780 TAB is used for explicit indentation of the current line.
1781
5e72c6b2
S
1782 To start a continuation line, use \\[idlwave-split-line]. This
1783 function can also be used in the middle of a line to split the line
1784 at that point. When used inside a long constant string, the string
1785 is split at that point with the `+' concatenation operator.
f32b3b91
CD
1786
1787 Comments are indented as follows:
1788
1789 `;;;' Indentation remains unchanged.
1790 `;;' Indent like the surrounding code
1791 `;' Indent to a minimum column.
1792
1793 The indentation of comments starting in column 0 is never changed.
1794
5e72c6b2
S
1795 Use \\[idlwave-fill-paragraph] to refill a paragraph inside a
1796 comment. The indentation of the second line of the paragraph
1797 relative to the first will be retained. Use
1798 \\[idlwave-auto-fill-mode] to toggle auto-fill mode for these
1799 comments. When the variable `idlwave-fill-comment-line-only' is
52a244eb 1800 nil, code can also be auto-filled and auto-indented.
f32b3b91
CD
1801
1802 To convert pre-existing IDL code to your formatting style, mark the
5e72c6b2
S
1803 entire buffer with \\[mark-whole-buffer] and execute
1804 \\[idlwave-expand-region-abbrevs]. Then mark the entire buffer
1805 again followed by \\[indent-region] (`indent-region').
f32b3b91
CD
1806
18072. Routine Info
1808 ------------
5e72c6b2
S
1809 IDLWAVE displays information about the calling sequence and the
1810 accepted keyword parameters of a procedure or function with
1811 \\[idlwave-routine-info]. \\[idlwave-find-module] jumps to the
1812 source file of a module. These commands know about system
1813 routines, all routines in idlwave-mode buffers and (when the
1814 idlwave-shell is active) about all modules currently compiled under
52a244eb
S
1815 this shell. It also makes use of pre-compiled or custom-scanned
1816 user and library catalogs many popular libraries ship with by
1817 default. Use \\[idlwave-update-routine-info] to update this
15e42531
CD
1818 information, which is also used for completion (see item 4).
1819
18203. Online IDL Help
1821 ---------------
f66f03de 1822
15e42531 1823 \\[idlwave-context-help] displays the IDL documentation relevant
f66f03de
S
1824 for the system variable, keyword, or routines at point. A single
1825 key stroke gets you directly to the right place in the docs. See
52a244eb 1826 the manual to configure where and how the HTML help is displayed.
f32b3b91 1827
15e42531 18284. Completion
f32b3b91 1829 ----------
15e42531 1830 \\[idlwave-complete] completes the names of procedures, functions
52a244eb
S
1831 class names, keyword parameters, system variables and tags, class
1832 tags, structure tags, filenames and much more. It is context
1833 sensitive and figures out what is expected at point. Lower case
1834 strings are completed in lower case, other strings in mixed or
1835 upper case.
f32b3b91 1836
15e42531 18375. Code Templates and Abbreviations
f32b3b91
CD
1838 --------------------------------
1839 Many Abbreviations are predefined to expand to code fragments and templates.
5a0c3f56 1840 The abbreviations start generally with a `\\`. Some examples:
f32b3b91
CD
1841
1842 \\pr PROCEDURE template
1843 \\fu FUNCTION template
1844 \\c CASE statement template
05a1abfc 1845 \\sw SWITCH statement template
f32b3b91
CD
1846 \\f FOR loop template
1847 \\r REPEAT Loop template
1848 \\w WHILE loop template
1849 \\i IF statement template
1850 \\elif IF-ELSE statement template
1851 \\b BEGIN
4b1aaa8b 1852
52a244eb
S
1853 For a full list, use \\[idlwave-list-abbrevs]. Some templates also
1854 have direct keybindings - see the list of keybindings below.
775591f7 1855
52a244eb
S
1856 \\[idlwave-doc-header] inserts a documentation header at the
1857 beginning of the current program unit (pro, function or main).
1858 Change log entries can be added to the current program unit with
1859 \\[idlwave-doc-modification].
f32b3b91 1860
15e42531 18616. Automatic Case Conversion
f32b3b91
CD
1862 -------------------------
1863 The case of reserved words and some abbrevs is controlled by
1864 `idlwave-reserved-word-upcase' and `idlwave-abbrev-change-case'.
1865
15e42531 18667. Automatic END completion
f32b3b91
CD
1867 ------------------------
1868 If the variable `idlwave-expand-generic-end' is non-nil, each END typed
1869 will be converted to the specific version, like ENDIF, ENDFOR, etc.
1870
15e42531 18718. Hooks
f32b3b91
CD
1872 -----
1873 Loading idlwave.el runs `idlwave-load-hook'.
1874 Turning on `idlwave-mode' runs `idlwave-mode-hook'.
1875
15e42531 18769. Documentation and Customization
f32b3b91 1877 -------------------------------
5e72c6b2
S
1878 Info documentation for this package is available. Use
1879 \\[idlwave-info] to display (complain to your sysadmin if that does
1880 not work). For Postscript, PDF, and HTML versions of the
855b42a2 1881 documentation, check IDLWAVE's homepage at URL `http://idlwave.org'.
f32b3b91
CD
1882 IDLWAVE has customize support - see the group `idlwave'.
1883
15e42531 188410.Keybindings
f32b3b91
CD
1885 -----------
1886 Here is a list of all keybindings of this mode.
1887 If some of the key bindings below show with ??, use \\[describe-key]
1888 followed by the key sequence to see what the key sequence does.
1889
1890\\{idlwave-mode-map}"
175069ef 1891 :abbrev-table idlwave-mode-abbrev-table
f32b3b91
CD
1892 (if idlwave-startup-message
1893 (message "Emacs IDLWAVE mode version %s." idlwave-mode-version))
1894 (setq idlwave-startup-message nil)
4b1aaa8b 1895
f32b3b91 1896 (set (make-local-variable 'indent-line-function) 'idlwave-indent-and-action)
4b1aaa8b 1897
175069ef
SM
1898 (set (make-local-variable idlwave-comment-indent-function)
1899 #'idlwave-comment-hook)
4b1aaa8b 1900
f32b3b91
CD
1901 (set (make-local-variable 'comment-start-skip) ";+[ \t]*")
1902 (set (make-local-variable 'comment-start) ";")
0dc2be2f 1903 (set (make-local-variable 'comment-add) 1) ; ";;" for new and regions
f32b3b91
CD
1904 (set (make-local-variable 'abbrev-all-caps) t)
1905 (set (make-local-variable 'indent-tabs-mode) nil)
1906 (set (make-local-variable 'completion-ignore-case) t)
4b1aaa8b 1907
f32b3b91
CD
1908 (when (featurep 'easymenu)
1909 (easy-menu-add idlwave-mode-menu idlwave-mode-map)
1910 (easy-menu-add idlwave-mode-debug-menu idlwave-mode-map))
1911
f32b3b91 1912 (setq abbrev-mode t)
4b1aaa8b 1913
f32b3b91
CD
1914 (set (make-local-variable idlwave-fill-function) 'idlwave-auto-fill)
1915 (setq comment-end "")
1916 (set (make-local-variable 'comment-multi-line) nil)
4b1aaa8b 1917 (set (make-local-variable 'paragraph-separate)
5e72c6b2 1918 "[ \t\f]*$\\|[ \t]*;+[ \t]*$\\|;+[+=-_*]+$")
f32b3b91
CD
1919 (set (make-local-variable 'paragraph-start) "[ \t\f]\\|[ \t]*;+[ \t]")
1920 (set (make-local-variable 'paragraph-ignore-fill-prefix) nil)
76959b77 1921 (set (make-local-variable 'parse-sexp-ignore-comments) t)
775591f7 1922
e08734e2 1923 ;; ChangeLog
8c43762b 1924 (set (make-local-variable 'add-log-current-defun-function)
e08734e2
S
1925 'idlwave-current-routine-fullname)
1926
f32b3b91
CD
1927 ;; Set tag table list to use IDLTAGS as file name.
1928 (if (boundp 'tag-table-alist)
1929 (add-to-list 'tag-table-alist '("\\.pro$" . "IDLTAGS")))
4b1aaa8b 1930
e08734e2 1931 ;; Font-lock additions
52a244eb 1932 ;; Following line is for Emacs - XEmacs uses the corresponding property
f32b3b91
CD
1933 ;; on the `idlwave-mode' symbol.
1934 (set (make-local-variable 'font-lock-defaults) idlwave-font-lock-defaults)
0dc2be2f
S
1935 (set (make-local-variable 'font-lock-mark-block-function)
1936 'idlwave-mark-subprogram)
1937 (set (make-local-variable 'font-lock-fontify-region-function)
1938 'idlwave-font-lock-fontify-region)
f32b3b91
CD
1939
1940 ;; Imenu setup
1941 (set (make-local-variable 'imenu-create-index-function)
1942 'imenu-default-create-index-function)
1943 (set (make-local-variable 'imenu-extract-index-name-function)
1944 'idlwave-unit-name)
1945 (set (make-local-variable 'imenu-prev-index-position-function)
1946 'idlwave-prev-index-position)
1947
0dc2be2f
S
1948 ;; HideShow setup
1949 (add-to-list 'hs-special-modes-alist
1950 (list 'idlwave-mode
1951 idlwave-begin-block-reg
1952 idlwave-end-block-reg
1953 ";"
1954 'idlwave-forward-block nil))
4b1aaa8b 1955
f32b3b91 1956 ;; Make a local post-command-hook and add our hook to it
f66f03de
S
1957 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1958 ;; (make-local-hook 'post-command-hook)
15e42531
CD
1959 (add-hook 'post-command-hook 'idlwave-command-hook nil 'local)
1960
1961 ;; Make local hooks for buffer updates
f66f03de
S
1962 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1963 ;; (make-local-hook 'kill-buffer-hook)
15e42531 1964 (add-hook 'kill-buffer-hook 'idlwave-kill-buffer-update nil 'local)
f66f03de 1965 ;; (make-local-hook 'after-save-hook)
e08734e2 1966 (add-hook 'after-save-hook 'idlwave-save-buffer-update nil 'local)
15e42531
CD
1967 (add-hook 'after-save-hook 'idlwave-revoke-license-to-kill nil 'local)
1968
52a244eb
S
1969 ;; Setup directories and file, if necessary
1970 (idlwave-setup)
1971
15e42531
CD
1972 ;; Update the routine info with info about current buffer?
1973 (idlwave-new-buffer-update)
f32b3b91 1974
f66f03de 1975 ;; Check help location
175069ef 1976 (idlwave-help-check-locations))
f32b3b91 1977
52a244eb
S
1978(defvar idlwave-setup-done nil)
1979(defun idlwave-setup ()
1980 (unless idlwave-setup-done
1981 (if (not (file-directory-p idlwave-config-directory))
1982 (make-directory idlwave-config-directory))
4b1aaa8b
PE
1983 (setq
1984 idlwave-user-catalog-file (expand-file-name
1985 idlwave-user-catalog-file
f66f03de 1986 idlwave-config-directory)
4b1aaa8b
PE
1987 idlwave-xml-system-rinfo-converted-file
1988 (expand-file-name
f66f03de
S
1989 idlwave-xml-system-rinfo-converted-file
1990 idlwave-config-directory)
4b1aaa8b
PE
1991 idlwave-path-file (expand-file-name
1992 idlwave-path-file
f66f03de 1993 idlwave-config-directory))
52a244eb
S
1994 (idlwave-read-paths) ; we may need these early
1995 (setq idlwave-setup-done t)))
1996
0dc2be2f
S
1997(defun idlwave-font-lock-fontify-region (beg end &optional verbose)
1998 "Fontify continuation lines correctly."
1999 (let (pos)
2000 (save-excursion
2001 (goto-char beg)
2002 (forward-line -1)
2003 (when (setq pos (idlwave-is-continuation-line))
2004 (goto-char pos)
2005 (idlwave-beginning-of-statement)
2006 (setq beg (point)))))
2007 (font-lock-default-fontify-region beg end verbose))
2008
f32b3b91 2009;;
52a244eb 2010;; Code Formatting ----------------------------------------------------
4b1aaa8b 2011;;
f32b3b91 2012
f32b3b91 2013(defun idlwave-hard-tab ()
5a0c3f56 2014 "Insert TAB in buffer in current position."
f32b3b91
CD
2015 (interactive)
2016 (insert "\t"))
2017
2018;;; This stuff is experimental
2019
2020(defvar idlwave-command-hook nil
2021 "If non-nil, a list that can be evaluated using `eval'.
2022It is evaluated in the lisp function `idlwave-command-hook' which is
2023placed in `post-command-hook'.")
2024
2025(defun idlwave-command-hook ()
2026 "Command run after every command.
2027Evaluates a non-nil value of the *variable* `idlwave-command-hook' and
2028sets the variable to zero afterwards."
2029 (and idlwave-command-hook
2030 (listp idlwave-command-hook)
2031 (condition-case nil
2032 (eval idlwave-command-hook)
2033 (error nil)))
2034 (setq idlwave-command-hook nil))
2035
2036;;; End experiment
2037
2038;; It would be better to use expand.el for better abbrev handling and
2039;; versatility.
2040
2041(defun idlwave-check-abbrev (arg &optional reserved)
5a0c3f56 2042 "Reverse abbrev expansion if in comment or string.
f32b3b91
CD
2043Argument ARG is the number of characters to move point
2044backward if `idlwave-abbrev-move' is non-nil.
2045If optional argument RESERVED is non-nil then the expansion
2046consists of reserved words, which will be capitalized if
2047`idlwave-reserved-word-upcase' is non-nil.
2048Otherwise, the abbrev will be capitalized if `idlwave-abbrev-change-case'
2049is non-nil, unless its value is \`down in which case the abbrev will be
2050made into all lowercase.
2051Returns non-nil if abbrev is left expanded."
2052 (if (idlwave-quoted)
2053 (progn (unexpand-abbrev)
2054 nil)
2055 (if (and reserved idlwave-reserved-word-upcase)
2056 (upcase-region last-abbrev-location (point))
2057 (cond
2058 ((equal idlwave-abbrev-change-case 'down)
2059 (downcase-region last-abbrev-location (point)))
2060 (idlwave-abbrev-change-case
2061 (upcase-region last-abbrev-location (point)))))
2062 (if (and idlwave-abbrev-move (> arg 0))
2063 (if (boundp 'post-command-hook)
2064 (setq idlwave-command-hook (list 'backward-char (1+ arg)))
2065 (backward-char arg)))
2066 t))
2067
2068(defun idlwave-in-comment ()
5a0c3f56 2069 "Return t if point is inside a comment, nil otherwise."
f32b3b91
CD
2070 (save-excursion
2071 (let ((here (point)))
2072 (and (idlwave-goto-comment) (> here (point))))))
2073
2074(defun idlwave-goto-comment ()
2075 "Move to start of comment delimiter on current line.
2076Moves to end of line if there is no comment delimiter.
2077Ignores comment delimiters in strings.
2078Returns point if comment found and nil otherwise."
9b026d9f 2079 (let ((eos (point-at-eol))
f32b3b91
CD
2080 (data (match-data))
2081 found)
2082 ;; Look for first comment delimiter not in a string
2083 (beginning-of-line)
2084 (setq found (search-forward comment-start eos 'lim))
2085 (while (and found (idlwave-in-quote))
2086 (setq found (search-forward comment-start eos 'lim)))
2087 (store-match-data data)
2088 (and found (not (idlwave-in-quote))
2089 (progn
2090 (backward-char 1)
2091 (point)))))
2092
5e72c6b2 2093(defun idlwave-region-active-p ()
a00e54f7
RS
2094 "Should we operate on an active region?"
2095 (if (fboundp 'use-region-p)
2096 (use-region-p)
2097 (region-active-p)))
5e72c6b2 2098
f32b3b91
CD
2099(defun idlwave-show-matching-quote ()
2100 "Insert quote and show matching quote if this is end of a string."
2101 (interactive)
2102 (let ((bq (idlwave-in-quote))
1ba983e8 2103 (inq last-command-event))
f32b3b91
CD
2104 (if (and bq (not (idlwave-in-comment)))
2105 (let ((delim (char-after bq)))
2106 (insert inq)
2107 (if (eq inq delim)
2108 (save-excursion
2109 (goto-char bq)
2110 (sit-for 1))))
2111 ;; Not the end of a string
2112 (insert inq))))
2113
2114(defun idlwave-show-begin-check ()
2115 "Ensure that the previous word was a token before `idlwave-show-begin'.
2116An END token must be preceded by whitespace."
5e72c6b2
S
2117 (if (not (idlwave-quoted))
2118 (if
2119 (save-excursion
2120 (backward-word 1)
2121 (backward-char 1)
2122 (looking-at "[ \t\n\f]"))
2123 (idlwave-show-begin))))
f32b3b91
CD
2124
2125(defun idlwave-show-begin ()
5a0c3f56
JB
2126 "Find the start of current block and blinks to it for a second.
2127Also checks if the correct END statement has been used."
f32b3b91 2128 ;; All end statements are reserved words
76959b77 2129 ;; Re-indent end line
52a244eb
S
2130 ;;(insert-char ?\ 1) ;; So indent, etc. work well
2131 ;;(backward-char 1)
76959b77
S
2132 (let* ((pos (point-marker))
2133 (last-abbrev-marker (copy-marker last-abbrev-location))
e180ab9f 2134 (eol-pos (point-at-eol))
76959b77
S
2135 begin-pos end-pos end end1 )
2136 (if idlwave-reindent-end (idlwave-indent-line))
52a244eb 2137 (setq last-abbrev-location (marker-position last-abbrev-marker))
f32b3b91
CD
2138 (when (and (idlwave-check-abbrev 0 t)
2139 idlwave-show-block)
2140 (save-excursion
2141 ;; Move inside current block
76959b77 2142 (goto-char last-abbrev-marker)
f32b3b91 2143 (idlwave-block-jump-out -1 'nomark)
76959b77
S
2144 (setq begin-pos (point))
2145 (idlwave-block-jump-out 1 'nomark)
2146 (setq end-pos (point))
2147 (if (> end-pos eol-pos)
2148 (setq end-pos pos))
2149 (goto-char end-pos)
4b1aaa8b 2150 (setq end (buffer-substring
76959b77
S
2151 (progn
2152 (skip-chars-backward "a-zA-Z")
2153 (point))
2154 end-pos))
2155 (goto-char begin-pos)
f32b3b91
CD
2156 (when (setq end1 (cdr (idlwave-block-master)))
2157 (cond
5e72c6b2 2158 ((null end1)) ; no-operation
f32b3b91
CD
2159 ((string= (downcase end) (downcase end1))
2160 (sit-for 1))
2161 ((string= (downcase end) "end")
2162 ;; A generic end
2163 (if idlwave-expand-generic-end
2164 (save-excursion
2165 (goto-char pos)
2166 (backward-char 3)
2167 (insert (if (string= end "END") (upcase end1) end1))
2168 (delete-char 3)))
2169 (sit-for 1))
2170 (t
2171 (beep)
4b1aaa8b 2172 (message "Warning: Shouldn't this be \"%s\" instead of \"%s\"?"
f32b3b91 2173 end1 end)
52a244eb
S
2174 (sit-for 1))))))))
2175 ;;(delete-char 1))
f32b3b91
CD
2176
2177(defun idlwave-block-master ()
2178 (let ((case-fold-search t))
2179 (save-excursion
2180 (cond
05a1abfc 2181 ((looking-at "pro\\|case\\|switch\\|function\\>")
f32b3b91
CD
2182 (assoc (downcase (match-string 0)) idlwave-block-matches))
2183 ((looking-at "begin\\>")
4b1aaa8b
PE
2184 (let ((limit (save-excursion
2185 (idlwave-beginning-of-statement)
f32b3b91
CD
2186 (point))))
2187 (cond
52a244eb
S
2188 ((re-search-backward ":[ \t]*\\=" limit t)
2189 ;; seems to be a case thing
2190 '("begin" . "end"))
f32b3b91
CD
2191 ((re-search-backward idlwave-block-match-regexp limit t)
2192 (assoc (downcase (match-string 1))
2193 idlwave-block-matches))
f32b3b91 2194 (t
52a244eb 2195 ;; Just a normal block
f32b3b91
CD
2196 '("begin" . "end")))))
2197 (t nil)))))
2198
2199(defun idlwave-close-block ()
2200 "Terminate the current block with the correct END statement."
2201 (interactive)
f32b3b91
CD
2202 ;; Start new line if we are not in a new line
2203 (unless (save-excursion
2204 (skip-chars-backward " \t")
2205 (bolp))
2206 (let ((idlwave-show-block nil))
2207 (newline-and-indent)))
5e72c6b2
S
2208 (let ((last-abbrev-location (point))) ; for upcasing
2209 (insert "end")
2210 (idlwave-show-begin)))
2211
f66f03de 2212(defun idlwave-custom-ampersand-surround (&optional is-action)
5a0c3f56 2213 "Surround &, leaving room for && (which surround as well)."
f66f03de
S
2214 (let* ((prev-char (char-after (- (point) 2)))
2215 (next-char (char-after (point)))
2216 (amp-left (eq prev-char ?&))
2217 (amp-right (eq next-char ?&))
2218 (len (if amp-left 2 1)))
2219 (unless amp-right ;no need to do it twice, amp-left will catch it.
2220 (idlwave-surround -1 (if (or is-action amp-left) -1) len))))
2221
2222(defun idlwave-custom-ltgtr-surround (gtr &optional is-action)
2223 "Surround > and < by blanks, leaving room for >= and <=, and considering ->."
2224 (let* ((prev-char (char-after (- (point) 2)))
2225 (next-char (char-after (point)))
2226 (method-invoke (and gtr (eq prev-char ?-)))
2227 (len (if method-invoke 2 1)))
2228 (unless (eq next-char ?=)
2229 ;; Key binding: pad only on left, to save for possible >=/<=
2230 (idlwave-surround -1 (if (or is-action method-invoke) -1) len))))
2231
2232(defun idlwave-surround (&optional before after length is-action)
595ab50b
CD
2233 "Surround the LENGTH characters before point with blanks.
2234LENGTH defaults to 1.
f32b3b91 2235Optional arguments BEFORE and AFTER affect the behavior before and
595ab50b
CD
2236after the characters (see also description of `idlwave-make-space'):
2237
2238nil do nothing
22390 force no spaces
2240integer > 0 force exactly n spaces
2241integer < 0 at least |n| spaces
f32b3b91
CD
2242
2243The function does nothing if any of the following conditions is true:
2244- `idlwave-surround-by-blank' is nil
f66f03de 2245- the character before point is inside a string or comment"
5e72c6b2 2246 (when (and idlwave-surround-by-blank (not (idlwave-quoted)))
f66f03de
S
2247 (let ((length (or length 1))) ; establish a default for LENGTH
2248 (backward-char length)
2249 (save-restriction
2250 (let ((here (point)))
2251 (skip-chars-backward " \t")
2252 (if (bolp)
2253 ;; avoid clobbering indent
2254 (progn
2255 (move-to-column (idlwave-calculate-indent))
2256 (if (<= (point) here)
2257 (narrow-to-region (point) here))
2258 (goto-char here)))
2259 (idlwave-make-space before))
2260 (skip-chars-forward " \t"))
2261 (forward-char length)
2262 (idlwave-make-space after)
2263 ;; Check to see if the line should auto wrap
2264 (if (and (equal (char-after (1- (point))) ?\ )
2265 (> (current-column) fill-column))
2266 (funcall auto-fill-function)))))
f32b3b91
CD
2267
2268(defun idlwave-make-space (n)
2269 "Make space at point.
2270The space affected is all the spaces and tabs around point.
2271If n is non-nil then point is left abs(n) spaces from the beginning of
2272the contiguous space.
2273The amount of space at point is determined by N.
2274If the value of N is:
2275nil - do nothing.
595ab50b
CD
2276> 0 - exactly N spaces.
2277< 0 - a minimum of -N spaces, i.e., do not change if there are
2278 already -N spaces.
22790 - no spaces (i.e. remove any existing space)."
f32b3b91
CD
2280 (if (integerp n)
2281 (let
2282 ((start-col (progn (skip-chars-backward " \t") (current-column)))
2283 (left (point))
2284 (end-col (progn (skip-chars-forward " \t") (current-column))))
2285 (delete-horizontal-space)
2286 (cond
2287 ((> n 0)
2288 (idlwave-indent-to (+ start-col n))
2289 (goto-char (+ left n)))
2290 ((< n 0)
2291 (idlwave-indent-to end-col (- n))
2292 (goto-char (- left n)))
2293 ;; n = 0, done
2294 ))))
2295
2296(defun idlwave-newline ()
5a0c3f56 2297 "Insert a newline and indent the current and previous line."
f32b3b91
CD
2298 (interactive)
2299 ;;
2300 ;; Handle unterminated single and double quotes
2301 ;; If not in a comment and in a string then insertion of a newline
2302 ;; will mean unbalanced quotes.
2303 ;;
2304 (if (and (not (idlwave-in-comment)) (idlwave-in-quote))
2305 (progn (beep)
2306 (message "Warning: unbalanced quotes?")))
2307 (newline)
2308 ;;
2309 ;; The current line is being split, the cursor should be at the
2310 ;; beginning of the new line skipping the leading indentation.
2311 ;;
2312 ;; The reason we insert the new line before indenting is that the
2313 ;; indenting could be confused by keywords (e.g. END) on the line
2314 ;; after the split point. This prevents us from just using
2315 ;; `indent-for-tab-command' followed by `newline-and-indent'.
2316 ;;
2317 (beginning-of-line 0)
2318 (idlwave-indent-line)
2319 (forward-line)
2320 (idlwave-indent-line))
2321
2322;;
2323;; Use global variable 'comment-column' to set parallel comment
2324;;
2325;; Modeled on lisp.el
2326;; Emacs Lisp and IDL (Wave CL) have identical comment syntax
2327(defun idlwave-comment-hook ()
2328 "Compute indent for the beginning of the IDL comment delimiter."
2329 (if (or (looking-at idlwave-no-change-comment)
8d222148 2330 (looking-at (or idlwave-begin-line-comment "^;")))
f32b3b91
CD
2331 (current-column)
2332 (if (looking-at idlwave-code-comment)
2333 (if (save-excursion (skip-chars-backward " \t") (bolp))
2334 ;; On line by itself, indent as code
2335 (let ((tem (idlwave-calculate-indent)))
2336 (if (listp tem) (car tem) tem))
2337 ;; after code - do not change
2338 (current-column))
2339 (skip-chars-backward " \t")
2340 (max (if (bolp) 0 (1+ (current-column)))
2341 comment-column))))
2342
2343(defun idlwave-split-line ()
2344 "Continue line by breaking line at point and indent the lines.
5a0c3f56 2345For a code line insert continuation marker. If the line is a line comment
f32b3b91
CD
2346then the new line will contain a comment with the same indentation.
2347Splits strings with the IDL operator `+' if `idlwave-split-line-string' is
2348non-nil."
2349 (interactive)
15e42531
CD
2350 ;; Expand abbreviation, just like normal RET would.
2351 (and abbrev-mode (expand-abbrev))
f32b3b91
CD
2352 (let (beg)
2353 (if (not (idlwave-in-comment))
2354 ;; For code line add continuation.
2355 ;; Check if splitting a string.
2356 (progn
2357 (if (setq beg (idlwave-in-quote))
2358 (if idlwave-split-line-string
2359 ;; Split the string.
2360 (progn (insert (setq beg (char-after beg)) " + "
2361 idlwave-continuation-char beg)
5e72c6b2
S
2362 (backward-char 1)
2363 (newline-and-indent)
2364 (forward-char 1))
f32b3b91
CD
2365 ;; Do not split the string.
2366 (beep)
2367 (message "Warning: continuation inside string!!")
2368 (insert " " idlwave-continuation-char))
2369 ;; Not splitting a string.
15e42531
CD
2370 (if (not (member (char-before) '(?\ ?\t)))
2371 (insert " "))
5e72c6b2
S
2372 (insert idlwave-continuation-char)
2373 (newline-and-indent)))
f32b3b91
CD
2374 (indent-new-comment-line))
2375 ;; Indent previous line
2376 (setq beg (- (point-max) (point)))
2377 (forward-line -1)
2378 (idlwave-indent-line)
2379 (goto-char (- (point-max) beg))
2380 ;; Reindent new line
2381 (idlwave-indent-line)))
2382
cca13260 2383(defun idlwave-beginning-of-subprogram (&optional nomark)
5a0c3f56 2384 "Move point to the beginning of the current program unit.
cca13260 2385If NOMARK is non-nil, do not push mark."
f32b3b91 2386 (interactive)
cca13260 2387 (idlwave-find-key idlwave-begin-unit-reg -1 nomark))
f32b3b91 2388
cca13260 2389(defun idlwave-end-of-subprogram (&optional nomark)
5a0c3f56 2390 "Move point to the start of the next program unit.
cca13260 2391If NOMARK is non-nil, do not push mark."
f32b3b91
CD
2392 (interactive)
2393 (idlwave-end-of-statement)
cca13260 2394 (idlwave-find-key idlwave-end-unit-reg 1 nomark))
f32b3b91
CD
2395
2396(defun idlwave-mark-statement ()
2397 "Mark current IDL statement."
2398 (interactive)
2399 (idlwave-end-of-statement)
2400 (let ((end (point)))
2401 (idlwave-beginning-of-statement)
0dc2be2f 2402 (push-mark end nil t)))
f32b3b91
CD
2403
2404(defun idlwave-mark-block ()
2405 "Mark containing block."
2406 (interactive)
2407 (idlwave-end-of-statement)
2408 (idlwave-backward-up-block -1)
2409 (idlwave-end-of-statement)
2410 (let ((end (point)))
2411 (idlwave-backward-block)
2412 (idlwave-beginning-of-statement)
0dc2be2f 2413 (push-mark end nil t)))
f32b3b91
CD
2414
2415
2416(defun idlwave-mark-subprogram ()
2417 "Put mark at beginning of program, point at end.
2418The marks are pushed."
2419 (interactive)
2420 (idlwave-end-of-statement)
2421 (idlwave-beginning-of-subprogram)
2422 (let ((beg (point)))
2423 (idlwave-forward-block)
0dc2be2f 2424 (push-mark beg nil t))
f32b3b91
CD
2425 (exchange-point-and-mark))
2426
2427(defun idlwave-backward-up-block (&optional arg)
2428 "Move to beginning of enclosing block if prefix ARG >= 0.
2429If prefix ARG < 0 then move forward to enclosing block end."
2430 (interactive "p")
2431 (idlwave-block-jump-out (- arg) 'nomark))
2432
2433(defun idlwave-beginning-of-block ()
2434 "Go to the beginning of the current block."
2435 (interactive)
2436 (idlwave-block-jump-out -1 'nomark)
2437 (forward-word 1))
2438
2439(defun idlwave-end-of-block ()
2440 "Go to the beginning of the current block."
2441 (interactive)
2442 (idlwave-block-jump-out 1 'nomark)
2443 (backward-word 1))
2444
0dc2be2f 2445(defun idlwave-forward-block (&optional arg)
f32b3b91
CD
2446 "Move across next nested block."
2447 (interactive)
0dc2be2f
S
2448 (let ((arg (or arg 1)))
2449 (if (idlwave-down-block arg)
2450 (idlwave-block-jump-out arg 'nomark))))
f32b3b91
CD
2451
2452(defun idlwave-backward-block ()
2453 "Move backward across previous nested block."
2454 (interactive)
2455 (if (idlwave-down-block -1)
2456 (idlwave-block-jump-out -1 'nomark)))
2457
2458(defun idlwave-down-block (&optional arg)
2459 "Go down a block.
2460With ARG: ARG >= 0 go forwards, ARG < 0 go backwards.
bbd240ce 2461Returns non-nil if successful."
f32b3b91
CD
2462 (interactive "p")
2463 (let (status)
2464 (if (< arg 0)
2465 ;; Backward
2466 (let ((eos (save-excursion
2467 (idlwave-block-jump-out -1 'nomark)
2468 (point))))
4b1aaa8b 2469 (if (setq status (idlwave-find-key
f32b3b91
CD
2470 idlwave-end-block-reg -1 'nomark eos))
2471 (idlwave-beginning-of-statement)
2472 (message "No nested block before beginning of containing block.")))
2473 ;; Forward
2474 (let ((eos (save-excursion
2475 (idlwave-block-jump-out 1 'nomark)
2476 (point))))
4b1aaa8b 2477 (if (setq status (idlwave-find-key
f32b3b91
CD
2478 idlwave-begin-block-reg 1 'nomark eos))
2479 (idlwave-end-of-statement)
2480 (message "No nested block before end of containing block."))))
2481 status))
2482
2483(defun idlwave-mark-doclib ()
2484 "Put point at beginning of doc library header, mark at end.
2485The marks are pushed."
2486 (interactive)
2487 (let (beg
2488 (here (point)))
2489 (goto-char (point-max))
2490 (if (re-search-backward idlwave-doclib-start nil t)
4b1aaa8b 2491 (progn
f32b3b91
CD
2492 (setq beg (progn (beginning-of-line) (point)))
2493 (if (re-search-forward idlwave-doclib-end nil t)
2494 (progn
2495 (forward-line 1)
0dc2be2f 2496 (push-mark beg nil t)
f32b3b91
CD
2497 (message "Could not find end of doc library header.")))
2498 (message "Could not find doc library header start.")
2499 (goto-char here)))))
2500
e08734e2
S
2501(defun idlwave-current-routine-fullname ()
2502 (let ((name (idlwave-current-routine)))
2503 (idlwave-make-full-name (nth 2 name) (car name))))
2504
15e42531
CD
2505(defun idlwave-current-routine ()
2506 "Return (NAME TYPE CLASS) of current routine."
2507 (idlwave-routines)
2508 (save-excursion
cca13260 2509 (idlwave-beginning-of-subprogram 'nomark)
15e42531
CD
2510 (if (looking-at "[ \t]*\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)")
2511 (let* ((type (if (string= (downcase (match-string 1)) "pro")
2512 'pro 'function))
2513 (class (idlwave-sintern-class (match-string 3)))
2514 (name (idlwave-sintern-routine-or-method (match-string 4) class)))
2515 (list name type class)))))
2516
f32b3b91
CD
2517(defvar idlwave-shell-prompt-pattern)
2518(defun idlwave-beginning-of-statement ()
2519 "Move to beginning of the current statement.
2520Skips back past statement continuations.
2521Point is placed at the beginning of the line whether or not this is an
2522actual statement."
2523 (interactive)
2524 (cond
175069ef 2525 ((derived-mode-p 'idlwave-shell-mode)
f32b3b91
CD
2526 (if (re-search-backward idlwave-shell-prompt-pattern nil t)
2527 (goto-char (match-end 0))))
4b1aaa8b 2528 (t
f32b3b91
CD
2529 (if (save-excursion (forward-line -1) (idlwave-is-continuation-line))
2530 (idlwave-previous-statement)
2531 (beginning-of-line)))))
2532
2533(defun idlwave-previous-statement ()
5a0c3f56 2534 "Move point to beginning of the previous statement.
f32b3b91
CD
2535Returns t if the current line before moving is the beginning of
2536the first non-comment statement in the file, and nil otherwise."
2537 (interactive)
2538 (let (first-statement)
2539 (if (not (= (forward-line -1) 0))
2540 ;; first line in file
2541 t
2542 ;; skip blank lines, label lines, include lines and line comments
2543 (while (and
2544 ;; The current statement is the first statement until we
2545 ;; reach another statement.
2546 (setq first-statement
2547 (or
2548 (looking-at idlwave-comment-line-start-skip)
2549 (looking-at "[ \t]*$")
2550 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2551 (looking-at "^@")))
2552 (= (forward-line -1) 0)))
2553 ;; skip continuation lines
2554 (while (and
2555 (save-excursion
2556 (forward-line -1)
2557 (idlwave-is-continuation-line))
2558 (= (forward-line -1) 0)))
2559 first-statement)))
2560
f32b3b91 2561(defun idlwave-end-of-statement ()
5a0c3f56 2562 "Move point to the end of the current IDL statement.
05a1abfc
CD
2563If not in a statement just moves to end of line. Returns position."
2564 (interactive)
2565 (while (and (idlwave-is-continuation-line)
2566 (= (forward-line 1) 0))
2567 (while (and (idlwave-is-comment-or-empty-line)
2568 (= (forward-line 1) 0))))
2569 (end-of-line)
2570 (point))
2571
2572(defun idlwave-end-of-statement0 ()
5a0c3f56
JB
2573 "Move point to the end of the current IDL statement.
2574If not in a statement just moves to end of line. Returns position."
f32b3b91
CD
2575 (interactive)
2576 (while (and (idlwave-is-continuation-line)
2577 (= (forward-line 1) 0)))
2578 (end-of-line)
2579 (point))
2580
2581(defun idlwave-next-statement ()
5a0c3f56
JB
2582 "Move point to beginning of the next IDL statement.
2583Returns t if that statement is the last non-comment IDL statement
2584in the file, and nil otherwise."
f32b3b91
CD
2585 (interactive)
2586 (let (last-statement)
2587 (idlwave-end-of-statement)
2588 ;; skip blank lines, label lines, include lines and line comments
2589 (while (and (= (forward-line 1) 0)
2590 ;; The current statement is the last statement until
2591 ;; we reach a new statement.
2592 (setq last-statement
2593 (or
2594 (looking-at idlwave-comment-line-start-skip)
2595 (looking-at "[ \t]*$")
2596 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2597 (looking-at "^@")))))
2598 last-statement))
2599
76959b77
S
2600(defun idlwave-skip-multi-commands (&optional lim)
2601 "Skip past multiple commands on a line (with `&')."
2602 (let ((save-point (point)))
2603 (when (re-search-forward ".*&" lim t)
2604 (goto-char (match-end 0))
4b1aaa8b 2605 (if (idlwave-quoted)
6b75c9af
S
2606 (goto-char save-point)
2607 (if (eq (char-after (- (point) 2)) ?&) (goto-char save-point))))
76959b77
S
2608 (point)))
2609
15e42531 2610(defun idlwave-skip-label-or-case ()
f32b3b91
CD
2611 "Skip label or case statement element.
2612Returns position after label.
2613If there is no label point is not moved and nil is returned."
15e42531
CD
2614 ;; Case expressions and labels are terminated by a colon.
2615 ;; So we find the first colon in the line and make sure
2616 ;; - no `?' is before it (might be a ? b : c)
2617 ;; - it is not in a comment
2618 ;; - not in a string constant
2619 ;; - not in parenthesis (like a[0:3])
5e72c6b2 2620 ;; - not followed by another ":" in explicit class, ala a->b::c
15e42531 2621 ;; As many in this mode, this function is heuristic and not an exact
4b1aaa8b 2622 ;; parser.
5e72c6b2
S
2623 (let* ((start (point))
2624 (eos (save-excursion (idlwave-end-of-statement) (point)))
2625 (end (idlwave-find-key ":" 1 'nomark eos)))
f32b3b91 2626 (if (and end
15e42531 2627 (= (nth 0 (parse-partial-sexp start end)) 0)
5e72c6b2
S
2628 (not (string-match "\\?" (buffer-substring start end)))
2629 (not (string-match "^::" (buffer-substring end eos))))
f32b3b91
CD
2630 (progn
2631 (forward-char)
2632 (point))
2633 (goto-char start)
2634 nil)))
2635
2636(defun idlwave-start-of-substatement (&optional pre)
2637 "Move to start of next IDL substatement after point.
2638Uses the type of the current IDL statement to determine if the next
2639statement is on a new line or is a subpart of the current statement.
2640Returns point at start of substatement modulo whitespace.
2641If optional argument is non-nil move to beginning of current
15e42531 2642substatement."
f32b3b91
CD
2643 (let ((orig (point))
2644 (eos (idlwave-end-of-statement))
2645 (ifnest 0)
2646 st nst last)
2647 (idlwave-beginning-of-statement)
15e42531 2648 (idlwave-skip-label-or-case)
52a244eb
S
2649 (if (< (point) orig)
2650 (idlwave-skip-multi-commands orig))
f32b3b91
CD
2651 (setq last (point))
2652 ;; Continue looking for substatements until we are past orig
2653 (while (and (<= (point) orig) (not (eobp)))
2654 (setq last (point))
2655 (setq nst (nth 1 (cdr (setq st (car (idlwave-statement-type))))))
2656 (if (equal (car st) 'if) (setq ifnest (1+ ifnest)))
2657 (cond ((and nst
2658 (idlwave-find-key nst 1 'nomark eos))
2659 (goto-char (match-end 0)))
2660 ((and (> ifnest 0) (idlwave-find-key "\\<else\\>" 1 'nomark eos))
2661 (setq ifnest (1- ifnest))
2662 (goto-char (match-end 0)))
2663 (t (setq ifnest 0)
2664 (idlwave-next-statement))))
2665 (if pre (goto-char last))
15e42531
CD
2666 ;; If a continuation line starts here, move to next line
2667 (if (looking-at "[ \t]*\\$\\([ \t]*\\(;\\|$\\)\\)")
2668 (beginning-of-line 2))
f32b3b91
CD
2669 (point)))
2670
2671(defun idlwave-statement-type ()
2672 "Return the type of the current IDL statement.
2673Uses `idlwave-statement-match' to return a cons of (type . point) with
5a0c3f56 2674point the ending position where the type was determined. Type is the
f32b3b91 2675association from `idlwave-statement-match', i.e. the cons cell from the
5a0c3f56 2676list not just the type symbol. Returns nil if not an identifiable
f32b3b91
CD
2677statement."
2678 (save-excursion
2679 ;; Skip whitespace within a statement which is spaces, tabs, continuations
76959b77
S
2680 ;; and possibly comments
2681 (while (looking-at "[ \t]*\\$")
f32b3b91
CD
2682 (forward-line 1))
2683 (skip-chars-forward " \t")
2684 (let ((st idlwave-statement-match)
2685 (case-fold-search t))
2686 (while (and (not (looking-at (nth 0 (cdr (car st)))))
2687 (setq st (cdr st))))
2688 (if st
2689 (append st (match-end 0))))))
2690
f66f03de 2691(defun idlwave-expand-equal (&optional before after is-action)
5a0c3f56
JB
2692 "Pad '=' with spaces.
2693Two cases: Assignment statement, and keyword assignment.
2694Which case is determined using `idlwave-start-of-substatement' and
2695`idlwave-statement-type'. The equal sign will be surrounded by BEFORE
2696and AFTER blanks. If `idlwave-pad-keyword' is t then keyword assignment
2697is treated just like assignment statements. When nil, spaces are
2698removed for keyword assignment. Any other value keeps the current space
2699around the `='. Limits in for loops are treated as keyword assignment.
52a244eb
S
2700
2701Starting with IDL 6.0, a number of op= assignments are available.
2702Since ambiguities of the form:
2703
2704r and= b
2705rand= b
2706
2707can occur, alphanumeric operator assignment will never be pre-padded,
2708only post-padded. You must use a space before these to disambiguate
2709\(not just for padding, but for proper parsing by IDL too!). Other
2710operators, such as ##=, ^=, etc., will be pre-padded.
2711
f66f03de
S
2712IS-ACTION is ignored.
2713
52a244eb 2714See `idlwave-surround'."
f32b3b91 2715 (if idlwave-surround-by-blank
4b1aaa8b 2716 (let
52a244eb 2717 ((non-an-ops "\\(##\\|\\*\\|\\+\\|-\\|/\\|<\\|>\\|\\^\\)\\=")
4b1aaa8b 2718 (an-ops
52a244eb
S
2719 "\\s-\\(AND\\|EQ\\|GE\\|GT\\|LE\\|LT\\|MOD\\|NE\\|OR\\|XOR\\)\\=")
2720 (len 1))
4b1aaa8b
PE
2721
2722 (save-excursion
52a244eb
S
2723 (let ((case-fold-search t))
2724 (backward-char)
4b1aaa8b 2725 (if (or
52a244eb
S
2726 (re-search-backward non-an-ops nil t)
2727 ;; Why doesn't ##? work for both?
4b1aaa8b 2728 (re-search-backward "\\(#\\)\\=" nil t))
52a244eb
S
2729 (setq len (1+ (length (match-string 1))))
2730 (when (re-search-backward an-ops nil t)
3938cb82 2731 ;(setq begin nil) ; won't modify begin
52a244eb 2732 (setq len (1+ (length (match-string 1))))))))
4b1aaa8b
PE
2733
2734 (if (eq t idlwave-pad-keyword)
52a244eb 2735 ;; Everything gets padded equally
f66f03de 2736 (idlwave-surround before after len)
52a244eb
S
2737 ;; Treating keywords/for variables specially...
2738 (let ((st (save-excursion ; To catch "for" variables
2739 (idlwave-start-of-substatement t)
2740 (idlwave-statement-type)))
2741 (what (save-excursion ; To catch keywords
2742 (skip-chars-backward "= \t")
2743 (nth 2 (idlwave-where)))))
2744 (cond ((or (memq what '(function-keyword procedure-keyword))
4b1aaa8b
PE
2745 (memq (caar st) '(for pdef)))
2746 (cond
52a244eb
S
2747 ((null idlwave-pad-keyword)
2748 (idlwave-surround 0 0)
2749 ) ; remove space
2750 (t))) ; leave any spaces alone
f66f03de 2751 (t (idlwave-surround before after len))))))))
4b1aaa8b 2752
f32b3b91 2753
5e72c6b2
S
2754(defun idlwave-indent-and-action (&optional arg)
2755 "Call `idlwave-indent-line' and do expand actions.
2756With prefix ARG non-nil, indent the entire sub-statement."
2757 (interactive "p")
05a1abfc 2758 (save-excursion
4b1aaa8b
PE
2759 (if (and idlwave-expand-generic-end
2760 (re-search-backward "\\<\\(end\\)\\s-*\\="
05a1abfc
CD
2761 (max 0 (- (point) 10)) t)
2762 (looking-at "\\(end\\)\\([ \n\t]\\|\\'\\)"))
2763 (progn (goto-char (match-end 1))
5e72c6b2
S
2764 ;;Expand the END abbreviation, just as RET or Space would have.
2765 (if abbrev-mode (expand-abbrev)
2766 (idlwave-show-begin)))))
52a244eb
S
2767 (when (and (not arg) current-prefix-arg)
2768 (setq arg current-prefix-arg)
2769 (setq current-prefix-arg nil))
4b1aaa8b 2770 (if arg
5e72c6b2
S
2771 (idlwave-indent-statement)
2772 (idlwave-indent-line t)))
f32b3b91
CD
2773
2774(defun idlwave-indent-line (&optional expand)
5a0c3f56 2775 "Indent current IDL line as code or as a comment.
f32b3b91
CD
2776The actions in `idlwave-indent-action-table' are performed.
2777If the optional argument EXPAND is non-nil then the actions in
2778`idlwave-indent-expand-table' are performed."
2779 (interactive)
2780 ;; Move point out of left margin.
2781 (if (save-excursion
2782 (skip-chars-backward " \t")
2783 (bolp))
2784 (skip-chars-forward " \t"))
2785 (let ((mloc (point-marker)))
2786 (save-excursion
2787 (beginning-of-line)
2788 (if (looking-at idlwave-comment-line-start-skip)
2789 ;; Indentation for a line comment
2790 (progn
2791 (skip-chars-forward " \t")
2792 (idlwave-indent-left-margin (idlwave-comment-hook)))
2793 ;;
2794 ;; Code Line
2795 ;;
2796 ;; Before indenting, run action routines.
2797 ;;
2798 (if (and expand idlwave-do-actions)
8ffcfb27 2799 (mapc 'idlwave-do-action idlwave-indent-expand-table))
f32b3b91
CD
2800 ;;
2801 (if idlwave-do-actions
8ffcfb27 2802 (mapc 'idlwave-do-action idlwave-indent-action-table))
f32b3b91
CD
2803 ;;
2804 ;; No longer expand abbrevs on the line. The user can do this
2805 ;; manually using expand-region-abbrevs.
2806 ;;
2807 ;; Indent for code line
2808 ;;
2809 (beginning-of-line)
2810 (if (or
2811 ;; a label line
2812 (looking-at (concat "^" idlwave-label "[ \t]*$"))
2813 ;; a batch command
2814 (looking-at "^[ \t]*@"))
2815 ;; leave flush left
2816 nil
2817 ;; indent the line
2818 (idlwave-indent-left-margin (idlwave-calculate-indent)))
2819 ;; Adjust parallel comment
76959b77
S
2820 (end-of-line)
2821 (if (idlwave-in-comment)
2822 ;; Emacs 21 is too smart with fill-column on comment indent
2823 (let ((fill-column (if (fboundp 'comment-indent-new-line)
2824 (1- (frame-width))
2825 fill-column)))
2826 (indent-for-comment)))))
f32b3b91
CD
2827 (goto-char mloc)
2828 ;; Get rid of marker
76959b77 2829 (set-marker mloc nil)))
f32b3b91
CD
2830
2831(defun idlwave-do-action (action)
5a0c3f56
JB
2832 "Perform an action repeatedly on a line.
2833ACTION is a list (REG . FUNC). REG is a regular expression. FUNC is
2834either a function name to be called with `funcall' or a list to be
2835evaluated with `eval'. The action performed by FUNC should leave
2836point after the match for REG - otherwise an infinite loop may be
2837entered. FUNC is always passed a final argument of 'is-action, so it
2838can discriminate between being run as an action, or a key binding."
f32b3b91
CD
2839 (let ((action-key (car action))
2840 (action-routine (cdr action)))
2841 (beginning-of-line)
2842 (while (idlwave-look-at action-key)
2843 (if (listp action-routine)
f66f03de
S
2844 (eval (append action-routine '('is-action)))
2845 (funcall action-routine 'is-action)))))
f32b3b91
CD
2846
2847(defun idlwave-indent-to (col &optional min)
2848 "Indent from point with spaces until column COL.
2849Inserts space before markers at point."
2850 (if (not min) (setq min 0))
2851 (insert-before-markers
15e42531 2852 (make-string (max min (- col (current-column))) ?\ )))
f32b3b91
CD
2853
2854(defun idlwave-indent-left-margin (col)
2855 "Indent the current line to column COL.
2856Indents such that first non-whitespace character is at column COL
2857Inserts spaces before markers at point."
2858 (save-excursion
2859 (beginning-of-line)
2860 (delete-horizontal-space)
2861 (idlwave-indent-to col)))
2862
2863(defun idlwave-indent-subprogram ()
5a0c3f56 2864 "Indent program unit which contains point."
f32b3b91
CD
2865 (interactive)
2866 (save-excursion
2867 (idlwave-end-of-statement)
2868 (idlwave-beginning-of-subprogram)
2869 (let ((beg (point)))
2870 (idlwave-forward-block)
2871 (message "Indenting subprogram...")
2872 (indent-region beg (point) nil))
2873 (message "Indenting subprogram...done.")))
2874
5e72c6b2
S
2875(defun idlwave-indent-statement ()
2876 "Indent current statement, including all continuation lines."
2877 (interactive)
2878 (save-excursion
2879 (idlwave-beginning-of-statement)
2880 (let ((beg (point)))
2881 (idlwave-end-of-statement)
2882 (indent-region beg (point) nil))))
2883
f32b3b91
CD
2884(defun idlwave-calculate-indent ()
2885 "Return appropriate indentation for current line as IDL code."
2886 (save-excursion
2887 (beginning-of-line)
2888 (cond
2889 ;; Check for beginning of unit - main (beginning of buffer), pro, or
2890 ;; function
2891 ((idlwave-look-at idlwave-begin-unit-reg)
2892 0)
2893 ;; Check for continuation line
2894 ((save-excursion
2895 (and (= (forward-line -1) 0)
2896 (idlwave-is-continuation-line)))
2897 (idlwave-calculate-cont-indent))
2898 ;; calculate indent based on previous and current statements
52a244eb
S
2899 (t (let* (beg-prev-pos
2900 (the-indent
2901 ;; calculate indent based on previous statement
2902 (save-excursion
2903 (cond
2904 ;; Beginning of file
4b1aaa8b 2905 ((prog1
52a244eb
S
2906 (idlwave-previous-statement)
2907 (setq beg-prev-pos (point)))
2908 0)
2909 ;; Main block
2910 ((idlwave-look-at idlwave-begin-unit-reg t)
2911 (+ (idlwave-current-statement-indent)
2912 idlwave-main-block-indent))
2913 ;; Begin block
2914 ((idlwave-look-at idlwave-begin-block-reg t)
4b1aaa8b 2915 (+ (idlwave-min-current-statement-indent)
52a244eb
S
2916 idlwave-block-indent))
2917 ;; End Block
2918 ((idlwave-look-at idlwave-end-block-reg t)
2919 (progn
2920 ;; Match to the *beginning* of the block opener
2921 (goto-char beg-prev-pos)
2922 (idlwave-block-jump-out -1 'nomark) ; go to begin block
2923 (idlwave-min-current-statement-indent)))
2924 ;; idlwave-end-offset
2925 ;; idlwave-block-indent))
4b1aaa8b 2926
52a244eb
S
2927 ;; Default to current indent
2928 ((idlwave-current-statement-indent))))))
f32b3b91
CD
2929 ;; adjust the indentation based on the current statement
2930 (cond
2931 ;; End block
5e72c6b2
S
2932 ((idlwave-look-at idlwave-end-block-reg)
2933 (+ the-indent idlwave-end-offset))
f32b3b91
CD
2934 (the-indent)))))))
2935
2936;;
52a244eb 2937;; Parentheses indent
f32b3b91
CD
2938;;
2939
5e72c6b2
S
2940(defun idlwave-calculate-paren-indent (beg-reg end-reg close-exp)
2941 "Calculate the continuation indent inside a paren group.
4b1aaa8b 2942Returns a cons-cell with (open . indent), where open is the
5a0c3f56 2943location of the open paren."
5e72c6b2
S
2944 (let ((open (nth 1 (parse-partial-sexp beg-reg end-reg))))
2945 ;; Found an innermost open paren.
2946 (when open
2947 (goto-char open)
2948 ;; Line up with next word unless this is a closing paren.
2949 (cons open
2950 (cond
52a244eb
S
2951 ;; Plain Kernighan-style nested indent
2952 (idlwave-indent-parens-nested
2953 (+ idlwave-continuation-indent (idlwave-current-indent)))
2954
5e72c6b2
S
2955 ;; This is a closed paren - line up under open paren.
2956 (close-exp
2957 (current-column))
52a244eb
S
2958
2959 ;; Empty (or just comment) follows -- revert to basic indent
5e72c6b2
S
2960 ((progn
2961 ;; Skip paren
2962 (forward-char 1)
2963 (looking-at "[ \t$]*\\(;.*\\)?$"))
52a244eb
S
2964 nil)
2965
2966 ;; Line up with first word after any blank space
5e72c6b2
S
2967 ((progn
2968 (skip-chars-forward " \t")
2969 (current-column))))))))
2970
f32b3b91 2971(defun idlwave-calculate-cont-indent ()
5a0c3f56
JB
2972 "Calculates the IDL continuation indent column from the previous statement.
2973Note that here previous statement usually means the beginning of the
2974current statement if this statement is a continuation of the previous
2975line. Various special types of continuations, including assignments,
2976routine definitions, and parenthetical groupings, are treated separately."
f32b3b91 2977 (save-excursion
52a244eb 2978 (let* ((case-fold-search t)
f32b3b91 2979 (end-reg (progn (beginning-of-line) (point)))
52a244eb
S
2980 (beg-last-statement (save-excursion (idlwave-previous-statement)
2981 (point)))
4b1aaa8b 2982 (beg-reg (progn (idlwave-start-of-substatement 'pre)
52a244eb
S
2983 (if (eq (line-beginning-position) end-reg)
2984 (goto-char beg-last-statement)
2985 (point))))
2986 (basic-indent (+ (idlwave-min-current-statement-indent end-reg)
2987 idlwave-continuation-indent))
2988 fancy-nonparen-indent fancy-paren-indent)
4b1aaa8b 2989 (cond
52a244eb
S
2990 ;; Align then with its matching if, etc.
2991 ((let ((matchers '(("\\<if\\>" . "[ \t]*then")
2992 ("\\<\\(if\\|end\\(if\\)?\\)\\>" . "[ \t]*else")
2993 ("\\<\\(for\\|while\\)\\>" . "[ \t]*do")
4b1aaa8b 2994 ("\\<\\(repeat\\|end\\(rep\\)?\\)\\>" .
52a244eb
S
2995 "[ \t]*until")
2996 ("\\<case\\>" . "[ \t]*of")))
2997 match cont-re)
2998 (goto-char end-reg)
4b1aaa8b 2999 (and
52a244eb
S
3000 (setq cont-re
3001 (catch 'exit
3002 (while (setq match (car matchers))
3003 (if (looking-at (cdr match))
3004 (throw 'exit (car match)))
3005 (setq matchers (cdr matchers)))))
3006 (idlwave-find-key cont-re -1 'nomark beg-last-statement)))
3007 (if (looking-at "end") ;; that one's special
4b1aaa8b 3008 (- (idlwave-current-indent)
52a244eb
S
3009 (+ idlwave-block-indent idlwave-end-offset))
3010 (idlwave-current-indent)))
3011
3012 ;; Indent in from the previous line for continuing statements
3013 ((let ((matchers '("\\<then\\>"
3014 "\\<do\\>"
3015 "\\<repeat\\>"
3016 "\\<else\\>"))
3017 match)
3018 (catch 'exit
3019 (goto-char end-reg)
3020 (if (/= (forward-line -1) 0)
3021 (throw 'exit nil))
3022 (while (setq match (car matchers))
3023 (if (looking-at (concat ".*" match "[ \t]*\\$[ \t]*"
3024 "\\(;.*\\)?$"))
3025 (throw 'exit t))
3026 (setq matchers (cdr matchers)))))
3027 (+ idlwave-continuation-indent (idlwave-current-indent)))
3028
3029 ;; Parenthetical indent, either traditional or Kernighan style
3030 ((setq fancy-paren-indent
3031 (let* ((end-reg end-reg)
3032 (close-exp (progn
3033 (goto-char end-reg)
4b1aaa8b 3034 (skip-chars-forward " \t")
52a244eb
S
3035 (looking-at "\\s)")))
3036 indent-cons)
3037 (catch 'loop
3038 (while (setq indent-cons (idlwave-calculate-paren-indent
3039 beg-reg end-reg close-exp))
3040 ;; First permitted containing paren
3041 (if (or
3042 idlwave-indent-to-open-paren
3043 idlwave-indent-parens-nested
3044 (null (cdr indent-cons))
3045 (< (- (cdr indent-cons) basic-indent)
3046 idlwave-max-extra-continuation-indent))
3047 (throw 'loop (cdr indent-cons)))
3048 (setq end-reg (car indent-cons))))))
5e72c6b2
S
3049 fancy-paren-indent)
3050
52a244eb
S
3051 ;; A continued assignment, or procedure call/definition
3052 ((and
3053 (> idlwave-max-extra-continuation-indent 0)
3054 (setq fancy-nonparen-indent
3055 (progn
3056 (goto-char beg-reg)
3057 (while (idlwave-look-at "&")) ; skip continued statements
3058 (cond
3059 ;; A continued Procedure call or definition
3060 ((progn
3061 (idlwave-look-at "^[ \t]*\\(pro\\|function\\)") ;skip over
3062 (looking-at "[ \t]*\\([a-zA-Z0-9.$_]+[ \t]*->[ \t]*\\)?[a-zA-Z][:a-zA-Z0-9$_]*[ \t]*\\(,\\)[ \t]*"))
3063 (goto-char (match-end 0))
3064 ;; Comment only, or blank line with "$"? Basic indent.
3065 (if (save-match-data (looking-at "[ \t$]*\\(;.*\\)?$"))
3066 nil
3067 (current-column)))
4b1aaa8b 3068
52a244eb
S
3069 ;; Continued assignment (with =):
3070 ((catch 'assign ;
3071 (while (looking-at "[^=\n\r]*\\(=\\)[ \t]*")
3072 (goto-char (match-end 0))
4b1aaa8b 3073 (if (null (idlwave-what-function beg-reg))
52a244eb
S
3074 (throw 'assign t))))
3075 (unless (or
3076 (idlwave-in-quote)
3077 (looking-at "[ \t$]*\\(;.*\\)?$") ; use basic
3078 (save-excursion
3079 (goto-char beg-last-statement)
3080 (eq (caar (idlwave-statement-type)) 'for)))
3081 (current-column))))))
3082 (< (- fancy-nonparen-indent basic-indent)
3083 idlwave-max-extra-continuation-indent))
3084 (if fancy-paren-indent ;calculated but disallowed paren indent
3085 (+ fancy-nonparen-indent idlwave-continuation-indent)
3086 fancy-nonparen-indent))
3087
3088 ;; Basic indent, by default
3089 (t basic-indent)))))
3090
3091
f32b3b91 3092
15e42531
CD
3093(defun idlwave-find-key (key-re &optional dir nomark limit)
3094 "Move to next match of the regular expression KEY-RE.
3095Matches inside comments or string constants will be ignored.
3096If DIR is negative, the search will be backwards.
3097At a successful match, the mark is pushed unless NOMARK is non-nil.
3098Searches are limited to LIMIT.
3099Searches are case-insensitive and use a special syntax table which
3100treats `$' and `_' as word characters.
3101Return value is the beginning of the match or (in case of failure) nil."
3102 (setq dir (or dir 0))
3103 (let ((case-fold-search t)
3104 (search-func (if (> dir 0) 're-search-forward 're-search-backward))
3105 found)
3106 (idlwave-with-special-syntax
3107 (save-excursion
3108 (catch 'exit
3109 (while (funcall search-func key-re limit t)
3110 (if (not (idlwave-quoted))
52a244eb
S
3111 (throw 'exit (setq found (match-beginning 0)))
3112 (if (or (and (> dir 0) (eobp))
3113 (and (< dir 0) (bobp)))
3114 (throw 'exit nil)))))))
15e42531
CD
3115 (if found
3116 (progn
3117 (if (not nomark) (push-mark))
3118 (goto-char found)
3119 found)
3120 nil)))
3121
f32b3b91
CD
3122(defun idlwave-block-jump-out (&optional dir nomark)
3123 "When optional argument DIR is non-negative, move forward to end of
3124current block using the `idlwave-begin-block-reg' and `idlwave-end-block-reg'
5a0c3f56
JB
3125regular expressions. When DIR is negative, move backwards to block beginning.
3126Recursively calls itself to skip over nested blocks. DIR defaults to
3127forward. Calls `push-mark' unless the optional argument NOMARK is
3128non-nil. Movement is limited by the start of program units because of
f32b3b91
CD
3129possibility of unbalanced blocks."
3130 (interactive "P")
3131 (or dir (setq dir 0))
3132 (let* ((here (point))
3133 (case-fold-search t)
3134 (limit (if (>= dir 0) (point-max) (point-min)))
4b1aaa8b 3135 (block-limit (if (>= dir 0)
f32b3b91
CD
3136 idlwave-begin-block-reg
3137 idlwave-end-block-reg))
3138 found
3139 (block-reg (concat idlwave-begin-block-reg "\\|"
3140 idlwave-end-block-reg))
3141 (unit-limit (or (save-excursion
3142 (if (< dir 0)
3143 (idlwave-find-key
3144 idlwave-begin-unit-reg dir t limit)
3145 (end-of-line)
4b1aaa8b 3146 (idlwave-find-key
f32b3b91
CD
3147 idlwave-end-unit-reg dir t limit)))
3148 limit)))
3149 (if (>= dir 0) (end-of-line)) ;Make sure we are in current block
3150 (if (setq found (idlwave-find-key block-reg dir t unit-limit))
3151 (while (and found (looking-at block-limit))
3152 (if (>= dir 0) (forward-word 1))
3153 (idlwave-block-jump-out dir t)
3154 (setq found (idlwave-find-key block-reg dir t unit-limit))))
3155 (if (not nomark) (push-mark here))
3156 (if (not found) (goto-char unit-limit)
3157 (if (>= dir 0) (forward-word 1)))))
3158
52a244eb
S
3159(defun idlwave-min-current-statement-indent (&optional end-reg)
3160 "The minimum indent in the current statement."
3161 (idlwave-beginning-of-statement)
3162 (if (not (idlwave-is-continuation-line))
3163 (idlwave-current-indent)
3164 (let ((min (idlwave-current-indent)) comm-or-empty)
3165 (while (and (= (forward-line 1) 0)
3166 (or (setq comm-or-empty (idlwave-is-comment-or-empty-line))
3167 (idlwave-is-continuation-line))
3168 (or (null end-reg) (< (point) end-reg)))
3169 (unless comm-or-empty (setq min (min min (idlwave-current-indent)))))
3170 (if (or comm-or-empty (and end-reg (>= (point) end-reg)))
4b1aaa8b 3171 min
52a244eb
S
3172 (min min (idlwave-current-indent))))))
3173
3174(defun idlwave-current-statement-indent (&optional last-line)
f32b3b91
CD
3175 "Return indentation of the current statement.
3176If in a statement, moves to beginning of statement before finding indent."
52a244eb
S
3177 (if last-line
3178 (idlwave-end-of-statement)
3179 (idlwave-beginning-of-statement))
f32b3b91
CD
3180 (idlwave-current-indent))
3181
3182(defun idlwave-current-indent ()
3183 "Return the column of the indentation of the current line.
5a0c3f56 3184Skips any whitespace. Returns 0 if the end-of-line follows the whitespace."
f32b3b91
CD
3185 (save-excursion
3186 (beginning-of-line)
3187 (skip-chars-forward " \t")
3188 ;; if we are at the end of blank line return 0
3189 (cond ((eolp) 0)
3190 ((current-column)))))
3191
3192(defun idlwave-is-continuation-line ()
5a0c3f56 3193 "Test if current line is continuation line.
5e72c6b2
S
3194Blank or comment-only lines following regular continuation lines (with
3195`$') count as continuations too."
0dc2be2f
S
3196 (let (p)
3197 (save-excursion
4b1aaa8b 3198 (or
0dc2be2f
S
3199 (idlwave-look-at "\\<\\$")
3200 (catch 'loop
4b1aaa8b 3201 (while (and (looking-at "^[ \t]*\\(;.*\\)?$")
0dc2be2f
S
3202 (eq (forward-line -1) 0))
3203 (if (setq p (idlwave-look-at "\\<\\$")) (throw 'loop p))))))))
f32b3b91
CD
3204
3205(defun idlwave-is-comment-line ()
5a0c3f56 3206 "Test if the current line is a comment line."
f32b3b91
CD
3207 (save-excursion
3208 (beginning-of-line 1)
3209 (looking-at "[ \t]*;")))
3210
05a1abfc 3211(defun idlwave-is-comment-or-empty-line ()
5a0c3f56 3212 "Test if the current line is a comment line."
05a1abfc
CD
3213 (save-excursion
3214 (beginning-of-line 1)
3215 (looking-at "[ \t]*[;\n]")))
3216
f32b3b91 3217(defun idlwave-look-at (regexp &optional cont beg)
5a0c3f56 3218 "Search current line from current point for REGEXP.
15e42531
CD
3219If optional argument CONT is non-nil, searches to the end of
3220the current statement.
3221If optional arg BEG is non-nil, search starts from the beginning of the
3222current statement.
3223Ignores matches that end in a comment or inside a string expression.
3224Returns point if successful, nil otherwise.
3225This function produces unexpected results if REGEXP contains quotes or
5a0c3f56 3226a comment delimiter. The search is case insensitive.
15e42531 3227If successful leaves point after the match, otherwise, does not move point."
f32b3b91 3228 (let ((here (point))
f32b3b91 3229 (case-fold-search t)
15e42531
CD
3230 (eos (save-excursion
3231 (if cont (idlwave-end-of-statement) (end-of-line))
3232 (point)))
f32b3b91 3233 found)
15e42531
CD
3234 (idlwave-with-special-syntax
3235 (if beg (idlwave-beginning-of-statement))
3236 (while (and (setq found (re-search-forward regexp eos t))
3237 (idlwave-quoted))))
f32b3b91
CD
3238 (if (not found) (goto-char here))
3239 found))
3240
3241(defun idlwave-fill-paragraph (&optional nohang)
5a0c3f56 3242 "Fill paragraphs in comments.
f32b3b91
CD
3243A paragraph is made up of all contiguous lines having the same comment
3244leader (the leading whitespace before the comment delimiter and the
3245comment delimiter). In addition, paragraphs are separated by blank
5a0c3f56 3246line comments. The indentation is given by the hanging indent of the
f32b3b91 3247first line, otherwise by the minimum indentation of the lines after
5a0c3f56
JB
3248the first line. The indentation of the first line does not change.
3249Does not effect code lines. Does not fill comments on the same line
f32b3b91 3250with code. The hanging indent is given by the end of the first match
5a0c3f56
JB
3251matching `idlwave-hang-indent-regexp' on the paragraph's first line.
3252If the optional argument NOHANG is non-nil then the hanging indent is
f32b3b91
CD
3253ignored."
3254 (interactive "P")
3255 ;; check if this is a line comment
3256 (if (save-excursion
3257 (beginning-of-line)
3258 (skip-chars-forward " \t")
3259 (looking-at comment-start))
3260 (let
3261 ((indent 999)
3262 pre here diff fill-prefix-reg bcl first-indent
3263 hang start end)
3264 ;; Change tabs to spaces in the surrounding paragraph.
3265 ;; The surrounding paragraph will be the largest containing block of
3266 ;; contiguous line comments. Thus, we may be changing tabs in
3267 ;; a much larger area than is needed, but this is the easiest
3268 ;; brute force way to do it.
3269 ;;
3270 ;; This has the undesirable side effect of replacing the tabs
3271 ;; permanently without the user's request or knowledge.
3272 (save-excursion
3273 (backward-paragraph)
3274 (setq start (point)))
3275 (save-excursion
3276 (forward-paragraph)
3277 (setq end (point)))
3278 (untabify start end)
3279 ;;
3280 (setq here (point))
3281 (beginning-of-line)
3282 (setq bcl (point))
e180ab9f
GM
3283 (re-search-forward (concat "^[ \t]*" comment-start "+")
3284 (point-at-eol) t)
f32b3b91
CD
3285 ;; Get the comment leader on the line and its length
3286 (setq pre (current-column))
3287 ;; the comment leader is the indentation plus exactly the
3288 ;; number of consecutive ";".
3289 (setq fill-prefix-reg
3290 (concat
3291 (setq fill-prefix
9b026d9f 3292 (regexp-quote (buffer-substring (point-at-bol) (point))))
f32b3b91 3293 "[^;]"))
4b1aaa8b 3294
f32b3b91
CD
3295 ;; Mark the beginning and end of the paragraph
3296 (goto-char bcl)
3297 (while (and (looking-at fill-prefix-reg)
3298 (not (looking-at paragraph-separate))
3299 (not (bobp)))
3300 (forward-line -1))
3301 ;; Move to first line of paragraph
3302 (if (/= (point) bcl)
3303 (forward-line 1))
3304 (setq start (point))
3305 (goto-char bcl)
3306 (while (and (looking-at fill-prefix-reg)
3307 (not (looking-at paragraph-separate))
3308 (not (eobp)))
3309 (forward-line 1))
3310 (beginning-of-line)
3311 (if (or (not (looking-at fill-prefix-reg))
3312 (looking-at paragraph-separate))
3313 (forward-line -1))
3314 (end-of-line)
3315 ;; if at end of buffer add a newline (need this because
3316 ;; fill-region needs END to be at the beginning of line after
3317 ;; the paragraph or it will add a line).
3318 (if (eobp)
3319 (progn (insert ?\n) (backward-char 1)))
3320 ;; Set END to the beginning of line after the paragraph
3321 ;; END is calculated as distance from end of buffer
3322 (setq end (- (point-max) (point) 1))
3323 ;;
3324 ;; Calculate the indentation for the paragraph.
3325 ;;
3326 ;; In the following while statements, after one iteration
3327 ;; point will be at the beginning of a line in which case
3328 ;; the while will not be executed for the
3329 ;; the first paragraph line and thus will not affect the
3330 ;; indentation.
3331 ;;
3332 ;; First check to see if indentation is based on hanging indent.
3333 (if (and (not nohang) idlwave-hanging-indent
3334 (setq hang
3335 (save-excursion
3336 (goto-char start)
3337 (idlwave-calc-hanging-indent))))
3338 ;; Adjust lines of paragraph by inserting spaces so that
3339 ;; each line's indent is at least as great as the hanging
3340 ;; indent. This is needed for fill-paragraph to work with
3341 ;; a fill-prefix.
3342 (progn
3343 (setq indent hang)
3344 (beginning-of-line)
3345 (while (> (point) start)
e180ab9f 3346 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91
CD
3347 (if (> (setq diff (- indent (current-column))) 0)
3348 (progn
3349 (if (>= here (point))
3350 ;; adjust the original location for the
3351 ;; inserted text.
3352 (setq here (+ here diff)))
15e42531 3353 (insert (make-string diff ?\ ))))
f32b3b91
CD
3354 (forward-line -1))
3355 )
4b1aaa8b 3356
f32b3b91
CD
3357 ;; No hang. Instead find minimum indentation of paragraph
3358 ;; after first line.
3359 ;; For the following while statement, since START is at the
aa87aafc 3360 ;; beginning of line and END is at the end of line
f32b3b91
CD
3361 ;; point is greater than START at least once (which would
3362 ;; be the case for a single line paragraph).
3363 (while (> (point) start)
3364 (beginning-of-line)
3365 (setq indent
3366 (min indent
3367 (progn
e180ab9f 3368 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91 3369 (current-column))))
e180ab9f 3370 (forward-line -1)))
f32b3b91
CD
3371 (setq fill-prefix (concat fill-prefix
3372 (make-string (- indent pre)
15e42531 3373 ?\ )))
f32b3b91
CD
3374 ;; first-line indent
3375 (setq first-indent
3376 (max
3377 (progn
e180ab9f 3378 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91
CD
3379 (current-column))
3380 indent))
4b1aaa8b 3381
f32b3b91
CD
3382 ;; try to keep point at its original place
3383 (goto-char here)
3384
3385 ;; In place of the more modern fill-region-as-paragraph, a hack
3386 ;; to keep whitespace untouched on the first line within the
3387 ;; indent length and to preserve any indent on the first line
3388 ;; (first indent).
3389 (save-excursion
3390 (setq diff
3391 (buffer-substring start (+ start first-indent -1)))
15e42531 3392 (subst-char-in-region start (+ start first-indent -1) ?\ ?~ nil)
f32b3b91
CD
3393 (fill-region-as-paragraph
3394 start
3395 (- (point-max) end)
3396 (current-justification)
3397 nil)
3398 (delete-region start (+ start first-indent -1))
3399 (goto-char start)
3400 (insert diff))
3401 ;; When we want the point at the beginning of the comment
3402 ;; body fill-region will put it at the beginning of the line.
3403 (if (bolp) (skip-chars-forward (concat " \t" comment-start)))
3404 (setq fill-prefix nil))))
3405
3406(defun idlwave-calc-hanging-indent ()
5a0c3f56
JB
3407 "Calculate the position of the hanging indent for the comment paragraph.
3408The hanging indent position is given by the first match with the
3409`idlwave-hang-indent-regexp'. If `idlwave-use-last-hang-indent' is
3410non-nil then use last occurrence matching `idlwave-hang-indent-regexp'
3411on the line.
f32b3b91
CD
3412If not found returns nil."
3413 (if idlwave-use-last-hang-indent
3414 (save-excursion
3415 (end-of-line)
e180ab9f 3416 (if (re-search-backward idlwave-hang-indent-regexp (point-at-bol) t)
f32b3b91
CD
3417 (+ (current-column) (length idlwave-hang-indent-regexp))))
3418 (save-excursion
3419 (beginning-of-line)
e180ab9f 3420 (if (re-search-forward idlwave-hang-indent-regexp (point-at-eol) t)
f32b3b91
CD
3421 (current-column)))))
3422
3423(defun idlwave-auto-fill ()
4b1aaa8b 3424 "Called to break lines in auto fill mode.
52a244eb
S
3425Only fills non-comment lines if `idlwave-fill-comment-line-only' is
3426non-nil. Places a continuation character at the end of the line if
3427not in a comment. Splits strings with IDL concatenation operator `+'
3428if `idlwave-auto-fill-split-string' is non-nil."
f32b3b91
CD
3429 (if (<= (current-column) fill-column)
3430 nil ; do not to fill
3431 (if (or (not idlwave-fill-comment-line-only)
3432 (save-excursion
3433 ;; Check for comment line
3434 (beginning-of-line)
3435 (looking-at idlwave-comment-line-start-skip)))
3436 (let (beg)
3437 (idlwave-indent-line)
3438 ;; Prevent actions do-auto-fill which calls indent-line-function.
3439 (let (idlwave-do-actions
d6aac72d 3440 (paragraph-separate ".")
52a244eb
S
3441 (fill-nobreak-predicate
3442 (if (and (idlwave-in-quote)
3443 idlwave-auto-fill-split-string)
3444 (lambda () ;; We'll need 5 spaces for " ' + $"
3445 (<= (- fill-column (current-column)) 5)
3446 ))))
f32b3b91
CD
3447 (do-auto-fill))
3448 (save-excursion
3449 (end-of-line 0)
3450 ;; Indent the split line
a86bd650 3451 (idlwave-indent-line))
f32b3b91
CD
3452 (if (save-excursion
3453 (beginning-of-line)
3454 (looking-at idlwave-comment-line-start-skip))
3455 ;; A continued line comment
3456 ;; We treat continued line comments as part of a comment
3457 ;; paragraph. So we check for a hanging indent.
3458 (if idlwave-hanging-indent
3459 (let ((here (- (point-max) (point)))
3460 (indent
3461 (save-excursion
3462 (forward-line -1)
3463 (idlwave-calc-hanging-indent))))
e180ab9f
GM
3464 (when indent
3465 ;; Remove whitespace between comment delimiter and
3466 ;; text, insert spaces for appropriate indentation.
3467 (beginning-of-line)
3468 (re-search-forward comment-start-skip (point-at-eol) t)
3469 (delete-horizontal-space)
3470 (idlwave-indent-to indent)
3471 (goto-char (- (point-max) here)))))
f32b3b91
CD
3472 ;; Split code or comment?
3473 (if (save-excursion
3474 (end-of-line 0)
3475 (idlwave-in-comment))
52a244eb 3476 ;; Splitting a non-full-line comment.
f32b3b91
CD
3477 ;; Insert the comment delimiter from split line
3478 (progn
3479 (save-excursion
3480 (beginning-of-line)
3481 (skip-chars-forward " \t")
3482 ;; Insert blank to keep off beginning of line
3483 (insert " "
3484 (save-excursion
3485 (forward-line -1)
3486 (buffer-substring (idlwave-goto-comment)
3487 (progn
3488 (skip-chars-forward "; ")
3489 (point))))))
3490 (idlwave-indent-line))
3491 ;; Split code line - add continuation character
3492 (save-excursion
3493 (end-of-line 0)
3494 ;; Check to see if we split a string
3495 (if (and (setq beg (idlwave-in-quote))
3496 idlwave-auto-fill-split-string)
3497 ;; Split the string and concatenate.
3498 ;; The first extra space is for the space
3499 ;; the line was split. That space was removed.
3500 (insert " " (char-after beg) " +"))
3501 (insert " $"))
3502 (if beg
3503 (if idlwave-auto-fill-split-string
3504 ;; Make the second part of continued string
3505 (save-excursion
3506 (beginning-of-line)
3507 (skip-chars-forward " \t")
3508 (insert (char-after beg)))
3509 ;; Warning
3510 (beep)
3511 (message "Warning: continuation inside a string.")))
3512 ;; Although do-auto-fill (via indent-new-comment-line) calls
3513 ;; idlwave-indent-line for the new line, re-indent again
3514 ;; because of the addition of the continuation character.
3515 (idlwave-indent-line))
3516 )))))
3517
3518(defun idlwave-auto-fill-mode (arg)
3519 "Toggle auto-fill mode for IDL mode.
3520With arg, turn auto-fill mode on if arg is positive.
3521In auto-fill mode, inserting a space at a column beyond `fill-column'
3522automatically breaks the line at a previous space."
3523 (interactive "P")
3524 (prog1 (set idlwave-fill-function
3525 (if (if (null arg)
3526 (not (symbol-value idlwave-fill-function))
3527 (> (prefix-numeric-value arg) 0))
3528 'idlwave-auto-fill
3529 nil))
3530 ;; update mode-line
3531 (set-buffer-modified-p (buffer-modified-p))))
3532
52a244eb
S
3533;(defun idlwave-fill-routine-call ()
3534; "Fill a routine definition or statement, indenting appropriately."
3535; (let ((where (idlwave-where)))))
3536
3537
5a0c3f56 3538(defun idlwave-doc-header (&optional nomark)
f32b3b91 3539 "Insert a documentation header at the beginning of the unit.
5a0c3f56
JB
3540Inserts the value of the variable `idlwave-file-header'. Sets mark
3541before moving to do insertion unless the optional prefix argument
3542NOMARK is non-nil."
f32b3b91
CD
3543 (interactive "P")
3544 (or nomark (push-mark))
3545 ;; make sure we catch the current line if it begins the unit
5e72c6b2
S
3546 (if idlwave-header-to-beginning-of-file
3547 (goto-char (point-min))
3548 (end-of-line)
3549 (idlwave-beginning-of-subprogram)
3550 (beginning-of-line)
3551 ;; skip function or procedure line
3552 (if (idlwave-look-at "\\<\\(pro\\|function\\)\\>")
3553 (progn
3554 (idlwave-end-of-statement)
3555 (if (> (forward-line 1) 0) (insert "\n")))))
3556 (let ((pos (point)))
3557 (if idlwave-file-header
3558 (cond ((car idlwave-file-header)
a527b753 3559 (insert-file-contents (car idlwave-file-header)))
5e72c6b2
S
3560 ((stringp (car (cdr idlwave-file-header)))
3561 (insert (car (cdr idlwave-file-header))))))
3562 (goto-char pos)))
f32b3b91
CD
3563
3564(defun idlwave-default-insert-timestamp ()
5a0c3f56 3565 "Default timestamp insertion function."
f32b3b91
CD
3566 (insert (current-time-string))
3567 (insert ", " (user-full-name))
5e72c6b2 3568 (if (boundp 'user-mail-address)
4b1aaa8b 3569 (insert " <" user-mail-address ">")
5e72c6b2 3570 (insert " <" (user-login-name) "@" (system-name) ">"))
f32b3b91
CD
3571 ;; Remove extra spaces from line
3572 (idlwave-fill-paragraph)
3573 ;; Insert a blank line comment to separate from the date entry -
3574 ;; will keep the entry from flowing onto date line if re-filled.
5e72c6b2 3575 (insert "\n;\n;\t\t"))
f32b3b91
CD
3576
3577(defun idlwave-doc-modification ()
3578 "Insert a brief modification log at the beginning of the current program.
3579Looks for an occurrence of the value of user variable
5a0c3f56
JB
3580`idlwave-doc-modifications-keyword' if non-nil. Inserts time and user
3581name and places the point for the user to add a log. Before moving, saves
f32b3b91
CD
3582location on mark ring so that the user can return to previous point."
3583 (interactive)
3584 (push-mark)
05a1abfc
CD
3585 (let* (beg end)
3586 (if (and (or (re-search-backward idlwave-doclib-start nil t)
3587 (progn
3588 (goto-char (point-min))
3589 (re-search-forward idlwave-doclib-start nil t)))
3590 (setq beg (match-beginning 0))
3591 (re-search-forward idlwave-doclib-end nil t)
3592 (setq end (match-end 0)))
3593 (progn
3594 (goto-char beg)
4b1aaa8b 3595 (if (re-search-forward
05a1abfc
CD
3596 (concat idlwave-doc-modifications-keyword ":")
3597 end t)
3598 (end-of-line)
3599 (goto-char end)
3600 (end-of-line -1)
3601 (insert "\n" comment-start "\n")
3602 (insert comment-start " " idlwave-doc-modifications-keyword ":"))
3603 (insert "\n;\n;\t")
3604 (run-hooks 'idlwave-timestamp-hook))
3605 (error "No valid DOCLIB header"))))
f32b3b91 3606
e08734e2 3607
8d222148
SM
3608;; CJC 3/16/93
3609;; Interface to expand-region-abbrevs which did not work when the
3610;; abbrev hook associated with an abbrev moves point backwards
3611;; after abbrev expansion, e.g., as with the abbrev '.n'.
3612;; The original would enter an infinite loop in attempting to expand
3613;; .n (it would continually expand and unexpand the abbrev without expanding
3614;; because the point would keep going back to the beginning of the
3615;; abbrev instead of to the end of the abbrev). We now keep the
3616;; abbrev hook from moving backwards.
f32b3b91
CD
3617;;;
3618(defun idlwave-expand-region-abbrevs (start end)
3619 "Expand each abbrev occurrence in the region.
3620Calling from a program, arguments are START END."
3621 (interactive "r")
3622 (save-excursion
3623 (goto-char (min start end))
3624 (let ((idlwave-show-block nil) ;Do not blink
3625 (idlwave-abbrev-move nil)) ;Do not move
3626 (expand-region-abbrevs start end 'noquery))))
3627
3628(defun idlwave-quoted ()
5a0c3f56
JB
3629 "Return t if point is in a comment or quoted string.
3630Returns nil otherwise."
f32b3b91
CD
3631 (or (idlwave-in-comment) (idlwave-in-quote)))
3632
3633(defun idlwave-in-quote ()
5a0c3f56 3634 "Return location of the opening quote
f32b3b91
CD
3635if point is in a IDL string constant, nil otherwise.
3636Ignores comment delimiters on the current line.
3637Properly handles nested quotation marks and octal
3638constants - a double quote followed by an octal digit."
8d222148
SM
3639;; Treat an octal inside an apostrophe to be a normal string. Treat a
3640;; double quote followed by an octal digit to be an octal constant
3641;; rather than a string. Therefore, there is no terminating double
3642;; quote.
f32b3b91
CD
3643 (save-excursion
3644 ;; Because single and double quotes can quote each other we must
3645 ;; search for the string start from the beginning of line.
3646 (let* ((start (point))
9b026d9f 3647 (eol (point-at-eol))
f32b3b91
CD
3648 (bq (progn (beginning-of-line) (point)))
3649 (endq (point))
3650 (data (match-data))
3651 delim
3652 found)
3653 (while (< endq start)
3654 ;; Find string start
3655 ;; Don't find an octal constant beginning with a double quote
52a244eb 3656 (if (re-search-forward "[\"']" eol 'lim)
f32b3b91
CD
3657 ;; Find the string end.
3658 ;; In IDL, two consecutive delimiters after the start of a
3659 ;; string act as an
3660 ;; escape for the delimiter in the string.
3661 ;; Two consecutive delimiters alone (i.e., not after the
aa87aafc 3662 ;; start of a string) is the null string.
f32b3b91
CD
3663 (progn
3664 ;; Move to position after quote
3665 (goto-char (1+ (match-beginning 0)))
3666 (setq bq (1- (point)))
3667 ;; Get the string delimiter
3668 (setq delim (char-to-string (preceding-char)))
3669 ;; Check for null string
3670 (if (looking-at delim)
3671 (progn (setq endq (point)) (forward-char 1))
3672 ;; Look for next unpaired delimiter
3673 (setq found (search-forward delim eol 'lim))
3674 (while (looking-at delim)
3675 (forward-char 1)
3676 (setq found (search-forward delim eol 'lim)))
8d222148 3677 (setq endq (if found (1- (point)) (point)))
f32b3b91
CD
3678 ))
3679 (progn (setq bq (point)) (setq endq (point)))))
3680 (store-match-data data)
3681 ;; return string beginning position or nil
3682 (if (> start bq) bq))))
3683
76959b77 3684(defun idlwave-is-pointer-dereference (&optional limit)
5a0c3f56 3685 "Determine if the character after point is a pointer dereference *."
8d222148
SM
3686 (and
3687 (eq (char-after) ?\*)
3688 (not (idlwave-in-quote))
3689 (save-excursion
3690 (forward-char)
3691 (re-search-backward (concat "\\(" idlwave-idl-keywords
3692 "\\|[[(*+-/=,^><]\\)\\s-*\\*") limit t))))
76959b77
S
3693
3694
f32b3b91
CD
3695;; Statement templates
3696
3697;; Replace these with a general template function, something like
3698;; expand.el (I think there was also something with a name similar to
3699;; dmacro.el)
3700
3701(defun idlwave-template (s1 s2 &optional prompt noindent)
3702 "Build a template with optional prompt expression.
3703
3704Opens a line if point is not followed by a newline modulo intervening
3705whitespace. S1 and S2 are strings. S1 is inserted at point followed
595ab50b 3706by S2. Point is inserted between S1 and S2. The case of S1 and S2 is
5a0c3f56
JB
3707adjusted according to `idlwave-abbrev-change-case'. If optional
3708argument PROMPT is a string then it is displayed as a message in the
f32b3b91
CD
3709minibuffer. The PROMPT serves as a reminder to the user of an
3710expression to enter.
3711
3712The lines containing S1 and S2 are reindented using `indent-region'
3713unless the optional second argument NOINDENT is non-nil."
175069ef 3714 (if (derived-mode-p 'idlwave-shell-mode)
05a1abfc 3715 ;; This is a gross hack to avoit template abbrev expansion
15e42531
CD
3716 ;; in the shell. FIXME: This is a dirty hack.
3717 (if (and (eq this-command 'self-insert-command)
3718 (equal last-abbrev-location (point)))
3719 (insert last-abbrev-text)
3720 (error "No templates in idlwave-shell"))
3721 (cond ((eq idlwave-abbrev-change-case 'down)
3722 (setq s1 (downcase s1) s2 (downcase s2)))
3723 (idlwave-abbrev-change-case
3724 (setq s1 (upcase s1) s2 (upcase s2))))
e180ab9f 3725 (let ((beg (point-at-bol))
15e42531
CD
3726 end)
3727 (if (not (looking-at "\\s-*\n"))
3728 (open-line 1))
3729 (insert s1)
3730 (save-excursion
3731 (insert s2)
3732 (setq end (point)))
3733 (if (not noindent)
3734 (indent-region beg end nil))
3735 (if (stringp prompt)
274f1353 3736 (message "%s" prompt)))))
4b1aaa8b 3737
595ab50b
CD
3738(defun idlwave-rw-case (string)
3739 "Make STRING have the case required by `idlwave-reserved-word-upcase'."
3740 (if idlwave-reserved-word-upcase
3741 (upcase string)
3742 string))
3743
f32b3b91
CD
3744(defun idlwave-elif ()
3745 "Build skeleton IDL if-else block."
3746 (interactive)
595ab50b
CD
3747 (idlwave-template
3748 (idlwave-rw-case "if")
3749 (idlwave-rw-case " then begin\n\nendif else begin\n\nendelse")
3750 "Condition expression"))
f32b3b91
CD
3751
3752(defun idlwave-case ()
3753 "Build skeleton IDL case statement."
3754 (interactive)
4b1aaa8b 3755 (idlwave-template
595ab50b
CD
3756 (idlwave-rw-case "case")
3757 (idlwave-rw-case " of\n\nendcase")
3758 "Selector expression"))
f32b3b91 3759
05a1abfc
CD
3760(defun idlwave-switch ()
3761 "Build skeleton IDL switch statement."
3762 (interactive)
4b1aaa8b 3763 (idlwave-template
05a1abfc
CD
3764 (idlwave-rw-case "switch")
3765 (idlwave-rw-case " of\n\nendswitch")
3766 "Selector expression"))
3767
f32b3b91 3768(defun idlwave-for ()
5a0c3f56 3769 "Build skeleton IDL loop statement."
f32b3b91 3770 (interactive)
4b1aaa8b 3771 (idlwave-template
595ab50b
CD
3772 (idlwave-rw-case "for")
3773 (idlwave-rw-case " do begin\n\nendfor")
3774 "Loop expression"))
f32b3b91
CD
3775
3776(defun idlwave-if ()
5a0c3f56 3777 "Build skeleton IDL if statement."
f32b3b91 3778 (interactive)
595ab50b
CD
3779 (idlwave-template
3780 (idlwave-rw-case "if")
3781 (idlwave-rw-case " then begin\n\nendif")
3782 "Scalar logical expression"))
f32b3b91
CD
3783
3784(defun idlwave-procedure ()
3785 (interactive)
4b1aaa8b 3786 (idlwave-template
595ab50b
CD
3787 (idlwave-rw-case "pro")
3788 (idlwave-rw-case "\n\nreturn\nend")
3789 "Procedure name"))
f32b3b91
CD
3790
3791(defun idlwave-function ()
3792 (interactive)
4b1aaa8b 3793 (idlwave-template
595ab50b
CD
3794 (idlwave-rw-case "function")
3795 (idlwave-rw-case "\n\nreturn\nend")
3796 "Function name"))
f32b3b91
CD
3797
3798(defun idlwave-repeat ()
3799 (interactive)
595ab50b
CD
3800 (idlwave-template
3801 (idlwave-rw-case "repeat begin\n\nendrep until")
3802 (idlwave-rw-case "")
3803 "Exit condition"))
f32b3b91
CD
3804
3805(defun idlwave-while ()
3806 (interactive)
4b1aaa8b 3807 (idlwave-template
595ab50b
CD
3808 (idlwave-rw-case "while")
3809 (idlwave-rw-case " do begin\n\nendwhile")
3810 "Entry condition"))
f32b3b91
CD
3811
3812(defun idlwave-split-string (string &optional pattern)
3813 "Return a list of substrings of STRING which are separated by PATTERN.
3814If PATTERN is omitted, it defaults to \"[ \\f\\t\\n\\r\\v]+\"."
3815 (or pattern
3816 (setq pattern "[ \f\t\n\r\v]+"))
3817 (let (parts (start 0))
3818 (while (string-match pattern string start)
3819 (setq parts (cons (substring string start (match-beginning 0)) parts)
3820 start (match-end 0)))
3821 (nreverse (cons (substring string start) parts))))
3822
3823(defun idlwave-replace-string (string replace_string replace_with)
3824 (let* ((start 0)
3825 (last (length string))
3826 (ret_string "")
3827 end)
3828 (while (setq end (string-match replace_string string start))
3829 (setq ret_string
3830 (concat ret_string (substring string start end) replace_with))
3831 (setq start (match-end 0)))
3832 (setq ret_string (concat ret_string (substring string start last)))))
3833
3834(defun idlwave-get-buffer-visiting (file)
3835 ;; Return the buffer currently visiting FILE
3836 (cond
3837 ((boundp 'find-file-compare-truenames) ; XEmacs
3838 (let ((find-file-compare-truenames t))
3839 (get-file-buffer file)))
3840 ((fboundp 'find-buffer-visiting) ; Emacs
3841 (find-buffer-visiting file))
3842 (t (error "This should not happen (idlwave-get-buffer-visiting)"))))
3843
15e42531 3844(defvar idlwave-outlawed-buffers nil
5a0c3f56 3845 "List of buffers pulled up by IDLWAVE for special reasons.
15e42531
CD
3846Buffers in this list may be killed by `idlwave-kill-autoloaded-buffers'.")
3847
3848(defun idlwave-find-file-noselect (file &optional why)
f32b3b91
CD
3849 ;; Return a buffer visiting file.
3850 (or (idlwave-get-buffer-visiting file)
15e42531
CD
3851 (let ((buf (find-file-noselect file)))
3852 (if why (add-to-list 'idlwave-outlawed-buffers (cons buf why)))
3853 buf)))
3854
3855(defun idlwave-kill-autoloaded-buffers ()
52a244eb 3856 "Kill buffers created automatically by IDLWAVE.
15e42531
CD
3857Function prompts for a letter to identify the buffers to kill.
3858Possible letters are:
3859
3860f Buffers created by the command \\[idlwave-find-module] or mouse
3861 clicks in the routine info window.
3862s Buffers created by the IDLWAVE Shell to display where execution
3863 stopped or an error was found.
3864a Both of the above.
3865
5a0c3f56 3866Buffers containing unsaved changes require confirmation before they are killed."
15e42531
CD
3867 (interactive)
3868 (if (null idlwave-outlawed-buffers)
3869 (error "No IDLWAVE-created buffers available")
3870 (princ (format "Kill IDLWAVE-created buffers: [f]ind source(%d), [s]hell display(%d), [a]ll ? "
3871 (idlwave-count-outlawed-buffers 'find)
3872 (idlwave-count-outlawed-buffers 'shell)))
3873 (let ((c (read-char)))
3874 (cond
3875 ((member c '(?f ?\C-f))
3876 (idlwave-do-kill-autoloaded-buffers 'find))
3877 ((member c '(?s ?\C-s))
3878 (idlwave-do-kill-autoloaded-buffers 'shell))
3879 ((member c '(?a ?\C-a))
3880 (idlwave-do-kill-autoloaded-buffers t))
3881 (t (error "Abort"))))))
3882
3883(defun idlwave-count-outlawed-buffers (tag)
3884 "How many outlawed buffers have tag TAG?"
3885 (length (delq nil
4b1aaa8b
PE
3886 (mapcar
3887 (lambda (x) (eq (cdr x) tag))
15e42531
CD
3888 idlwave-outlawed-buffers))))
3889
3890(defun idlwave-do-kill-autoloaded-buffers (&rest reasons)
3891 "Kill all buffers pulled up by IDLWAVE matching REASONS."
3892 (let* ((list (copy-sequence idlwave-outlawed-buffers))
3893 (cnt 0)
3894 entry)
3895 (while (setq entry (pop list))
3896 (if (buffer-live-p (car entry))
3897 (and (or (memq t reasons)
3898 (memq (cdr entry) reasons))
3899 (kill-buffer (car entry))
3900 (incf cnt)
4b1aaa8b 3901 (setq idlwave-outlawed-buffers
15e42531 3902 (delq entry idlwave-outlawed-buffers)))
4b1aaa8b 3903 (setq idlwave-outlawed-buffers
15e42531
CD
3904 (delq entry idlwave-outlawed-buffers))))
3905 (message "%d buffer%s killed" cnt (if (= cnt 1) "" "s"))))
3906
3907(defun idlwave-revoke-license-to-kill ()
3908 "Remove BUFFER from the buffers which may be killed.
3909Killing would be done by `idlwave-do-kill-autoloaded-buffers'.
3910Intended for `after-save-hook'."
3911 (let* ((buf (current-buffer))
3912 (entry (assq buf idlwave-outlawed-buffers)))
3913 ;; Revoke license
3914 (if entry
4b1aaa8b 3915 (setq idlwave-outlawed-buffers
15e42531
CD
3916 (delq entry idlwave-outlawed-buffers)))
3917 ;; Remove this function from the hook.
3918 (remove-hook 'after-save-hook 'idlwave-revoke-license-to-kill 'local)))
3919
3920(defvar idlwave-path-alist)
3921(defun idlwave-locate-lib-file (file)
f32b3b91 3922 ;; Find FILE on the scanned lib path and return a buffer visiting it
15e42531 3923 (let* ((dirs idlwave-path-alist)
f32b3b91
CD
3924 dir efile)
3925 (catch 'exit
15e42531 3926 (while (setq dir (car (pop dirs)))
f32b3b91
CD
3927 (if (file-regular-p
3928 (setq efile (expand-file-name file dir)))
15e42531 3929 (throw 'exit efile))))))
52a244eb 3930
15e42531
CD
3931(defun idlwave-expand-lib-file-name (file)
3932 ;; Find FILE on the scanned lib path and return a buffer visiting it
52a244eb 3933 ;; This is for, e.g., finding source with no user catalog
4b1aaa8b 3934 (cond
15e42531 3935 ((null file) nil)
15e42531
CD
3936 ((file-name-absolute-p file) file)
3937 (t (idlwave-locate-lib-file file))))
f32b3b91
CD
3938
3939(defun idlwave-make-tags ()
5a0c3f56
JB
3940 "Create the IDL tags file IDLTAGS in the current directory from
3941the list of directories specified in the minibuffer. Directories may be
3942for example: . /usr/local/rsi/idl/lib. All the subdirectories of the
f32b3b91 3943specified top directories are searched if the directory name is prefixed
5a0c3f56 3944by @. Specify @ directories with care, it may take a long, long time if
f32b3b91
CD
3945you specify /."
3946 (interactive)
3947 (let (directory directories cmd append status numdirs dir getsubdirs
3948 buffer save_buffer files numfiles item errbuf)
4b1aaa8b 3949
f32b3b91
CD
3950 ;;
3951 ;; Read list of directories
3952 (setq directory (read-string "Tag Directories: " "."))
3953 (setq directories (idlwave-split-string directory "[ \t]+"))
3954 ;;
3955 ;; Set etags command, vars
3956 (setq cmd "etags --output=IDLTAGS --language=none --regex='/[
3957\\t]*[pP][Rr][Oo][ \\t]+\\([^ \\t,]+\\)/' --regex='/[
3958\\t]*[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn][ \\t]+\\([^ \\t,]+\\)/' ")
3959 (setq append " ")
3960 (setq status 0)
3961 ;;
3962 ;; For each directory
3963 (setq numdirs 0)
3964 (setq dir (nth numdirs directories))
3965 (while (and dir)
3966 ;;
3967 ;; Find the subdirectories
3968 (if (string-match "^[@]\\(.+\\)$" dir)
3969 (setq getsubdirs t) (setq getsubdirs nil))
3970 (if (and getsubdirs) (setq dir (substring dir 1 (length dir))))
3971 (setq dir (expand-file-name dir))
3972 (if (file-directory-p dir)
3973 (progn
3974 (if (and getsubdirs)
3975 (progn
3976 (setq buffer (get-buffer-create "*idltags*"))
3977 (call-process "sh" nil buffer nil "-c"
3978 (concat "find " dir " -type d -print"))
3979 (setq save_buffer (current-buffer))
3980 (set-buffer buffer)
3981 (setq files (idlwave-split-string
3982 (idlwave-replace-string
3983 (buffer-substring 1 (point-max))
3984 "\n" "/*.pro ")
3985 "[ \t]+"))
3986 (set-buffer save_buffer)
3987 (kill-buffer buffer))
3988 (setq files (list (concat dir "/*.pro"))))
3989 ;;
3990 ;; For each subdirectory
3991 (setq numfiles 0)
3992 (setq item (nth numfiles files))
3993 (while (and item)
3994 ;;
3995 ;; Call etags
3996 (if (not (string-match "^[ \\t]*$" item))
3997 (progn
29a4e67d 3998 (message "%s" (concat "Tagging " item "..."))
f32b3b91 3999 (setq errbuf (get-buffer-create "*idltags-error*"))
52a244eb 4000 (setq status (+ status
4b1aaa8b 4001 (if (eq 0 (call-process
52a244eb
S
4002 "sh" nil errbuf nil "-c"
4003 (concat cmd append item)))
4004 0
4005 1)))
f32b3b91
CD
4006 ;;
4007 ;; Append additional tags
4008 (setq append " --append ")
4009 (setq numfiles (1+ numfiles))
4010 (setq item (nth numfiles files)))
4011 (progn
4012 (setq numfiles (1+ numfiles))
4013 (setq item (nth numfiles files))
4014 )))
4b1aaa8b 4015
f32b3b91
CD
4016 (setq numdirs (1+ numdirs))
4017 (setq dir (nth numdirs directories)))
4018 (progn
4019 (setq numdirs (1+ numdirs))
4020 (setq dir (nth numdirs directories)))))
4b1aaa8b 4021
f32b3b91
CD
4022 (setq errbuf (get-buffer-create "*idltags-error*"))
4023 (if (= status 0)
4024 (kill-buffer errbuf))
4025 (message "")
4026 ))
4027
4028(defun idlwave-toggle-comment-region (beg end &optional n)
4029 "Comment the lines in the region if the first non-blank line is
5a0c3f56 4030commented, and conversely, uncomment region. If optional prefix arg
f32b3b91
CD
4031N is non-nil, then for N positive, add N comment delimiters or for N
4032negative, remove N comment delimiters.
4033Uses `comment-region' which does not place comment delimiters on
4034blank lines."
4035 (interactive "r\nP")
4036 (if n
4037 (comment-region beg end (prefix-numeric-value n))
4038 (save-excursion
4039 (goto-char beg)
4040 (beginning-of-line)
4041 ;; skip blank lines
4042 (skip-chars-forward " \t\n")
4043 (if (looking-at (concat "[ \t]*\\(" comment-start "+\\)"))
52a244eb
S
4044 (if (fboundp 'uncomment-region)
4045 (uncomment-region beg end)
4046 (comment-region beg end
4047 (- (length (buffer-substring
4048 (match-beginning 1)
4049 (match-end 1))))))
4050 (comment-region beg end)))))
f32b3b91
CD
4051
4052
4053;; ----------------------------------------------------------------------------
4054;; ----------------------------------------------------------------------------
4055;; ----------------------------------------------------------------------------
4056;; ----------------------------------------------------------------------------
4057;;
4058;; Completion and Routine Info
4059;;
4060
4061;; String "intern" functions
4062
4063;; For the completion and routine info function, we want to normalize
4064;; the case of procedure names etc. We do this by "interning" these
4065;; string is a hand-crafted way. Hashes are used to map the downcase
52a244eb
S
4066;; version of the strings to the cased versions. Most *-sint-*
4067;; variables consist of *two* hashes, a buffer+shell, followed by a
4068;; system hash. The former is re-scanned, and the latter takes case
4069;; precedence.
4070;;
4071;; Since these cased versions are really lisp objects, we can use `eq'
4072;; to search, which is a large performance boost. All new strings
4073;; need to be "sinterned". We do this as early as possible after
4074;; getting these strings from completion or buffer substrings. So
4075;; most of the code can simply assume to deal with "sinterned"
4076;; strings. The only exception is that the functions which scan whole
4077;; buffers for routine information do not intern the grabbed strings.
4078;; This is only done afterwards. Therefore in these functions it is
4079;; *not* safe to assume the strings can be compared with `eq' and be
4080;; fed into the routine assq functions.
f32b3b91
CD
4081
4082;; Here we define the hashing functions.
4083
4084;; The variables which hold the hashes.
4085(defvar idlwave-sint-routines '(nil))
4086(defvar idlwave-sint-keywords '(nil))
4087(defvar idlwave-sint-methods '(nil))
4088(defvar idlwave-sint-classes '(nil))
52a244eb
S
4089(defvar idlwave-sint-dirs '(nil))
4090(defvar idlwave-sint-libnames '(nil))
f32b3b91
CD
4091
4092(defun idlwave-reset-sintern (&optional what)
4093 "Reset all sintern hashes."
4094 ;; Make sure the hash functions are accessible.
8d222148
SM
4095 (unless (and (fboundp 'gethash)
4096 (fboundp 'puthash))
4097 (require 'cl)
4098 (or (fboundp 'puthash)
4099 (defalias 'puthash 'cl-puthash)))
f32b3b91
CD
4100 (let ((entries '((idlwave-sint-routines 1000 10)
4101 (idlwave-sint-keywords 1000 10)
4102 (idlwave-sint-methods 100 10)
4103 (idlwave-sint-classes 10 10))))
4104
4105 ;; Make sure these are lists
4106 (loop for entry in entries
4107 for var = (car entry)
4108 do (if (not (consp (symbol-value var))) (set var (list nil))))
4109
f66f03de 4110 ;; Reset the system & library hash
f32b3b91
CD
4111 (when (or (eq what t) (eq what 'syslib)
4112 (null (cdr idlwave-sint-routines)))
f32b3b91
CD
4113 (loop for entry in entries
4114 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4115 do (setcdr (symbol-value var)
f32b3b91 4116 (make-hash-table ':size size ':test 'equal)))
52a244eb
S
4117 (setq idlwave-sint-dirs nil
4118 idlwave-sint-libnames nil))
f32b3b91 4119
f66f03de 4120 ;; Reset the buffer & shell hash
f32b3b91
CD
4121 (when (or (eq what t) (eq what 'bufsh)
4122 (null (car idlwave-sint-routines)))
f32b3b91
CD
4123 (loop for entry in entries
4124 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4125 do (setcar (symbol-value var)
f32b3b91
CD
4126 (make-hash-table ':size size ':test 'equal))))))
4127
4128(defun idlwave-sintern-routine-or-method (name &optional class set)
4129 (if class
4130 (idlwave-sintern-method name set)
4131 (idlwave-sintern-routine name set)))
4132
4133(defun idlwave-sintern (stype &rest args)
4134 (apply (intern (concat "idlwave-sintern-" (symbol-name stype))) args))
4135
4136;;(defmacro idlwave-sintern (type var)
4137;; `(cond ((not (stringp name)) name)
4138;; ((gethash (downcase name) (cdr ,var)))
4139;; ((gethash (downcase name) (car ,var)))
4140;; (set (idlwave-sintern-set name ,type ,var set))
4141;; (name)))
4142
4143(defun idlwave-sintern-routine (name &optional set)
4144 (cond ((not (stringp name)) name)
4145 ((gethash (downcase name) (cdr idlwave-sint-routines)))
4146 ((gethash (downcase name) (car idlwave-sint-routines)))
4147 (set (idlwave-sintern-set name 'routine idlwave-sint-routines set))
4148 (name)))
4149(defun idlwave-sintern-keyword (name &optional set)
4150 (cond ((not (stringp name)) name)
4151 ((gethash (downcase name) (cdr idlwave-sint-keywords)))
4152 ((gethash (downcase name) (car idlwave-sint-keywords)))
4153 (set (idlwave-sintern-set name 'keyword idlwave-sint-keywords set))
4154 (name)))
4155(defun idlwave-sintern-method (name &optional set)
4156 (cond ((not (stringp name)) name)
4157 ((gethash (downcase name) (cdr idlwave-sint-methods)))
4158 ((gethash (downcase name) (car idlwave-sint-methods)))
4159 (set (idlwave-sintern-set name 'method idlwave-sint-methods set))
4160 (name)))
4161(defun idlwave-sintern-class (name &optional set)
4162 (cond ((not (stringp name)) name)
4163 ((gethash (downcase name) (cdr idlwave-sint-classes)))
4164 ((gethash (downcase name) (car idlwave-sint-classes)))
4165 (set (idlwave-sintern-set name 'class idlwave-sint-classes set))
4166 (name)))
4167
52a244eb
S
4168(defun idlwave-sintern-dir (dir &optional set)
4169 (car (or (member dir idlwave-sint-dirs)
4170 (setq idlwave-sint-dirs (cons dir idlwave-sint-dirs)))))
4171(defun idlwave-sintern-libname (name &optional set)
4172 (car (or (member name idlwave-sint-libnames)
4173 (setq idlwave-sint-libnames (cons name idlwave-sint-libnames)))))
f32b3b91
CD
4174
4175(defun idlwave-sintern-set (name type tables set)
4176 (let* ((func (or (cdr (assq type idlwave-completion-case))
4177 'identity))
4178 (iname (funcall (if (eq func 'preserve) 'identity func) name))
4179 (table (if (eq set 'sys) (cdr tables) (car tables))))
4180 (puthash (downcase name) iname table)
4181 iname))
4182
52a244eb
S
4183(defun idlwave-sintern-keyword-list (kwd-list &optional set)
4184 "Sintern a set of keywords (file (key . link) (key2 . link2) ...)"
8ffcfb27
GM
4185 (mapc (lambda(x)
4186 (setcar x (idlwave-sintern-keyword (car x) set)))
4187 (cdr kwd-list))
52a244eb
S
4188 kwd-list)
4189
4190(defun idlwave-sintern-rinfo-list (list &optional set default-dir)
5a0c3f56
JB
4191 "Sintern all strings in the rinfo LIST.
4192With optional parameter SET: also set new patterns. Probably this
4193will always have to be t. If DEFAULT-DIR is passed, it is used as
4194the base of the directory."
52a244eb 4195 (let (entry name type class kwds res source call new)
f32b3b91
CD
4196 (while list
4197 (setq entry (car list)
4198 list (cdr list)
4199 name (car entry)
4200 type (nth 1 entry)
4201 class (nth 2 entry)
4202 source (nth 3 entry)
4203 call (nth 4 entry)
52a244eb
S
4204 kwds (nthcdr 5 entry))
4205
4206 ;; The class and name
f32b3b91
CD
4207 (if class
4208 (progn
4209 (if (symbolp class) (setq class (symbol-name class)))
4210 (setq class (idlwave-sintern-class class set))
4211 (setq name (idlwave-sintern-method name set)))
4212 (setq name (idlwave-sintern-routine name set)))
4b1aaa8b 4213
52a244eb
S
4214 ;; The source
4215 (let ((source-type (car source))
4216 (source-file (nth 1 source))
4b1aaa8b 4217 (source-dir (if default-dir
52a244eb
S
4218 (file-name-as-directory default-dir)
4219 (nth 2 source)))
4220 (source-lib (nth 3 source)))
4221 (if (stringp source-dir)
4222 (setq source-dir (idlwave-sintern-dir source-dir set)))
4223 (if (stringp source-lib)
4224 (setq source-lib (idlwave-sintern-libname source-lib set)))
4225 (setq source (list source-type source-file source-dir source-lib)))
4b1aaa8b 4226
52a244eb
S
4227 ;; The keywords
4228 (setq kwds (mapcar (lambda (x)
4229 (idlwave-sintern-keyword-list x set))
4230 kwds))
4231
4232 ;; Build a canonicalized list
4233 (setq new (nconc (list name type class source call) kwds)
4234 res (cons new res)))
f32b3b91
CD
4235 (nreverse res)))
4236
05a1abfc
CD
4237;; Creating new sintern tables
4238
4239(defun idlwave-new-sintern-type (tag)
4240 "Define a variable and a function to sintern the new type TAG.
4241This defines the function `idlwave-sintern-TAG' and the variable
4242`idlwave-sint-TAGs'."
4243 (let* ((name (symbol-name tag))
4244 (names (concat name "s"))
4245 (var (intern (concat "idlwave-sint-" names)))
4246 (func (intern (concat "idlwave-sintern-" name))))
4247 (set var nil) ; initial value of the association list
4248 (fset func ; set the function
4249 `(lambda (name &optional set)
4250 (cond ((not (stringp name)) name)
4251 ((cdr (assoc (downcase name) ,var)))
4252 (set
4253 (setq ,var (cons (cons (downcase name) name) ,var))
4254 name)
4255 (name))))))
4256
4257(defun idlwave-reset-sintern-type (tag)
4258 "Reset the sintern variable associated with TAG."
4259 (set (intern (concat "idlwave-sint-" (symbol-name tag) "s")) nil))
4260
f32b3b91
CD
4261;;---------------------------------------------------------------------------
4262
4263
4264;; The variables which hold the information
15e42531 4265(defvar idlwave-system-routines nil
f32b3b91
CD
4266 "Holds the routine-info obtained by scanning buffers.")
4267(defvar idlwave-buffer-routines nil
4268 "Holds the routine-info obtained by scanning buffers.")
4269(defvar idlwave-compiled-routines nil
15e42531
CD
4270 "Holds the routine-info obtained by asking the shell.")
4271(defvar idlwave-unresolved-routines nil
4272 "Holds the unresolved routine-info obtained by asking the shell.")
52a244eb
S
4273(defvar idlwave-user-catalog-routines nil
4274 "Holds the procedure routine-info from the user scan.")
4275(defvar idlwave-library-catalog-routines nil
3938cb82
S
4276 "Holds the procedure routine-info from the .idlwave_catalog library files.")
4277(defvar idlwave-library-catalog-libname nil
4278 "Name of library catalog loaded from .idlwave_catalog files.")
15e42531 4279(defvar idlwave-path-alist nil
52a244eb
S
4280 "Alist with !PATH directories and zero or more flags if the dir has
4281been scanned in a user catalog ('user) or discovered in a library
4282catalog \('lib).")
15e42531
CD
4283(defvar idlwave-true-path-alist nil
4284 "Like `idlwave-path-alist', but with true filenames.")
f32b3b91 4285(defvar idlwave-routines nil
b9e4fbd3 4286 "Holds the combined procedure/function/method routine-info.")
f32b3b91
CD
4287(defvar idlwave-class-alist nil
4288 "Holds the class names known to IDLWAVE.")
4289(defvar idlwave-class-history nil
4290 "The history of classes selected with the minibuffer.")
4291(defvar idlwave-force-class-query nil)
4292(defvar idlwave-before-completion-wconf nil
4293 "The window configuration just before the completion buffer was displayed.")
15e42531
CD
4294(defvar idlwave-last-system-routine-info-cons-cell nil
4295 "The last cons cell in the system routine info.")
f32b3b91
CD
4296
4297;;
4298;; The code to get routine info from different sources.
4299
15e42531 4300(defvar idlwave-system-routines)
5e72c6b2
S
4301(defvar idlwave-catalog-process nil
4302 "The background process currently updating the catalog.")
4303
f32b3b91
CD
4304(defun idlwave-routines ()
4305 "Provide a list of IDL routines.
5a0c3f56
JB
4306This routine loads the builtin routines on the first call.
4307Later it only returns the value of the variable."
5e72c6b2
S
4308 (if (and idlwave-catalog-process
4309 (processp idlwave-catalog-process))
4310 (progn
4311 (cond
4312 ((equal (process-status idlwave-catalog-process) 'exit)
4313 (message "updating........")
4314 (setq idlwave-catalog-process nil)
4315 (idlwave-update-routine-info '(4)))
4316 ((equal (process-status idlwave-catalog-process) 'run)
4317 ;; Keep it running...
4318 )
4319 (t
4320 ;; Something is wrong, get rid of the process
4321 (message "Problem with catalog process") (beep)
4322 (condition-case nil
4323 (kill-process idlwave-catalog-process)
4324 (error nil))
4325 (setq idlwave-catalog-process nil)))))
f32b3b91
CD
4326 (or idlwave-routines
4327 (progn
4328 (idlwave-update-routine-info)
4329 ;; return the current value
4330 idlwave-routines)))
4331
05a1abfc
CD
4332(defvar idlwave-update-rinfo-hook nil
4333 "List of functions which should run after a global rinfo update.
4334Does not run after automatic updates of buffer or the shell.")
4335
5e72c6b2 4336(defun idlwave-rescan-catalog-directories ()
5a0c3f56 4337 "Rescan the previously selected directories. For batch processing."
5e72c6b2
S
4338 (idlwave-update-routine-info '(16)))
4339
4340(defun idlwave-rescan-asynchronously ()
8a6a28ac 4341 "Dispatch another Emacs instance to update the idlwave catalog.
5e72c6b2
S
4342After the process finishes normally, the first access to routine info
4343will re-read the catalog."
4344 (interactive)
4345 (if (processp idlwave-catalog-process)
4346 (if (eq (process-status idlwave-catalog-process) 'run)
4347 (if (yes-or-no-p "A catalog-updating process is running. Kill it? ")
4348 (progn
4349 (condition-case nil
4350 (kill-process idlwave-catalog-process)
4351 (error nil))
4352 (error "Process killed, no new process started"))
4353 (error "Quit"))
4354 (condition-case nil
4355 (kill-process idlwave-catalog-process)
4356 (error nil))))
52a244eb
S
4357 (if (or (not idlwave-user-catalog-file)
4358 (not (stringp idlwave-user-catalog-file))
4359 (not (file-regular-p idlwave-user-catalog-file)))
5e72c6b2 4360 (error "No catalog has been produced yet"))
4b1aaa8b 4361 (let* ((emacs (concat invocation-directory invocation-name))
5e72c6b2
S
4362 (args (list "-batch"
4363 "-l" (expand-file-name "~/.emacs")
4364 "-l" "idlwave"
4365 "-f" "idlwave-rescan-catalog-directories"))
4b1aaa8b 4366 (process (apply 'start-process "idlcat"
5e72c6b2
S
4367 nil emacs args)))
4368 (setq idlwave-catalog-process process)
4b1aaa8b 4369 (set-process-sentinel
5e72c6b2
S
4370 process
4371 (lambda (pro why)
4372 (when (string-match "finished" why)
4373 (setq idlwave-routines nil
4374 idlwave-system-routines nil
4375 idlwave-catalog-process nil)
4376 (or (idlwave-start-load-rinfo-timer)
4377 (idlwave-update-routine-info '(4))))))
4378 (message "Background job started to update catalog file")))
4379
4380
52a244eb
S
4381;; Format for all routine info user catalog, library catalogs, etc.:
4382;;
4383;; ("ROUTINE" type class
4384;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4385;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4386;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de 4387;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
52a244eb
S
4388;;
4389;; DIR will be supplied dynamically while loading library catalogs,
4390;; and is sinterned to save space, as is LIBNAME. PRO_FILE can be a
4391;; complete filepath, in which case DIR is unnecessary. HELPFILE can
4392;; be nil, as can LINK1, etc., if no HTML help is available.
4393
4394
5e72c6b2 4395(defvar idlwave-load-rinfo-idle-timer)
3938cb82
S
4396(defvar idlwave-shell-path-query)
4397
52a244eb 4398(defun idlwave-update-routine-info (&optional arg no-concatenate)
f32b3b91
CD
4399 "Update the internal routine-info lists.
4400These lists are used by `idlwave-routine-info' (\\[idlwave-routine-info])
4401and by `idlwave-complete' (\\[idlwave-complete]) to provide information
4402about individual routines.
4403
4404The information can come from 4 sources:
44051. IDL programs in the current editing session
44062. Compiled modules in an IDL shell running as Emacs subprocess
44073. A list which covers the IDL system routines.
44084. A list which covers the prescanned library files.
4409
4410Scans all IDLWAVE-mode buffers of the current editing session (see
4411`idlwave-scan-all-buffers-for-routine-info').
4412When an IDL shell is running, this command also queries the IDL program
4413for currently compiled routines.
4414
4415With prefix ARG, also reload the system and library lists.
52a244eb
S
4416With two prefix ARG's, also rescans the chosen user catalog tree.
4417With three prefix args, dispatch asynchronous process to do the update.
4418
4419If NO-CONCATENATE is non-nil, don't pre-concatenate the routine info
4420lists, but instead wait for the shell query to complete and
4421asynchronously finish updating routine info. This is set
4422automatically when called interactively. When you need routine
4423information updated immediately, leave NO-CONCATENATE nil."
751adbde 4424 (interactive "P\np")
5e72c6b2
S
4425 ;; Stop any idle processing
4426 (if (or (and (fboundp 'itimerp)
4427 (itimerp idlwave-load-rinfo-idle-timer))
4428 (and (fboundp 'timerp)
4429 (timerp idlwave-load-rinfo-idle-timer)))
4430 (cancel-timer idlwave-load-rinfo-idle-timer))
4431 (cond
4432 ((equal arg '(64))
4433 ;; Start a background process which updates the catalog.
4434 (idlwave-rescan-asynchronously))
4435 ((equal arg '(16))
52a244eb
S
4436 ;; Update the user catalog now, and wait for them.
4437 (idlwave-create-user-catalog-file t))
5e72c6b2
S
4438 (t
4439 (let* ((load (or arg
4440 idlwave-buffer-case-takes-precedence
4441 (null idlwave-routines)))
4442 ;; The override-idle means, even if the idle timer has done some
4443 ;; preparing work, load and renormalize everything anyway.
4444 (override-idle (or arg idlwave-buffer-case-takes-precedence)))
4b1aaa8b 4445
f32b3b91 4446 (setq idlwave-buffer-routines nil
15e42531
CD
4447 idlwave-compiled-routines nil
4448 idlwave-unresolved-routines nil)
f32b3b91 4449 ;; Reset the appropriate hashes
5e72c6b2
S
4450 (if (get 'idlwave-reset-sintern 'done-by-idle)
4451 ;; reset was already done in idle time, so skip this step now once
4452 (put 'idlwave-reset-sintern 'done-by-idle nil)
4453 (idlwave-reset-sintern (cond (load t)
4454 ((null idlwave-system-routines) t)
4455 (t 'bufsh))))
4b1aaa8b 4456
f32b3b91
CD
4457 (if idlwave-buffer-case-takes-precedence
4458 ;; We can safely scan the buffer stuff first
4459 (progn
4460 (idlwave-update-buffer-routine-info)
f66f03de 4461 (and load (idlwave-load-all-rinfo override-idle)))
f32b3b91 4462 ;; We first do the system info, and then the buffers
f66f03de 4463 (and load (idlwave-load-all-rinfo override-idle))
f32b3b91
CD
4464 (idlwave-update-buffer-routine-info))
4465
4466 ;; Let's see if there is a shell
4467 (let* ((shell-is-running (and (fboundp 'idlwave-shell-is-running)
4468 (idlwave-shell-is-running)))
4469 (ask-shell (and shell-is-running
4470 idlwave-query-shell-for-routine-info)))
4b1aaa8b 4471
52a244eb 4472 ;; Load the library catalogs again, first re-scanning the path
4b1aaa8b 4473 (when arg
52a244eb
S
4474 (if shell-is-running
4475 (idlwave-shell-send-command idlwave-shell-path-query
4476 '(progn
4477 (idlwave-shell-get-path-info)
4478 (idlwave-scan-library-catalogs))
4479 'hide)
4480 (idlwave-scan-library-catalogs)))
775591f7 4481
f32b3b91 4482 (if (or (not ask-shell)
52a244eb 4483 (not no-concatenate))
f32b3b91
CD
4484 ;; 1. If we are not going to ask the shell, we need to do the
4485 ;; concatenation now.
52a244eb
S
4486 ;; 2. When this function is called non-interactively, it
4487 ;; means that someone needs routine info *now*. The
4488 ;; shell update causes the concatenation to be
4489 ;; *delayed*, so not in time for the current command.
4490 ;; Therefore, we do a concatenation now, even though
4491 ;; the shell might do it again.
4492 (idlwave-concatenate-rinfo-lists nil 'run-hooks))
4b1aaa8b 4493
f32b3b91 4494 (when ask-shell
52a244eb 4495 ;; Ask the shell about the routines it knows of.
f32b3b91 4496 (message "Querying the shell")
5e72c6b2
S
4497 (idlwave-shell-update-routine-info nil t)))))))
4498
52a244eb
S
4499
4500(defvar idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4501(defvar idlwave-load-rinfo-idle-timer nil)
4502(defun idlwave-start-load-rinfo-timer ()
4503 (if (or (and (fboundp 'itimerp)
4504 (itimerp idlwave-load-rinfo-idle-timer))
4505 (and (fboundp 'timerp)
4506 (timerp idlwave-load-rinfo-idle-timer)))
4507 (cancel-timer idlwave-load-rinfo-idle-timer))
52a244eb 4508 (setq idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4509 (setq idlwave-load-rinfo-idle-timer nil)
4510 (if (and idlwave-init-rinfo-when-idle-after
4511 (numberp idlwave-init-rinfo-when-idle-after)
4512 (not (equal 0 idlwave-init-rinfo-when-idle-after))
4513 (not idlwave-routines))
4514 (condition-case nil
4515 (progn
4516 (setq idlwave-load-rinfo-idle-timer
4517 (run-with-idle-timer
4518 idlwave-init-rinfo-when-idle-after
4519 nil 'idlwave-load-rinfo-next-step)))
4520 (error nil))))
4521
f66f03de
S
4522;;------ XML Help routine info system
4523(defun idlwave-load-system-routine-info ()
4524 ;; Load the system routine info from the cached routine info file,
4525 ;; which, if necessary, will be re-created from the XML file on
4526 ;; disk. As a last fallback, load the (likely outdated) idlw-rinfo
4527 ;; file distributed with older IDLWAVE versions (<6.0)
4b1aaa8b 4528 (unless (and (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4529 'noerror 'nomessage)
4530 (idlwave-xml-system-routine-info-up-to-date))
4531 ;; See if we can create it from XML source
4532 (condition-case nil
4533 (idlwave-convert-xml-system-routine-info)
4b1aaa8b
PE
4534 (error
4535 (unless (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4536 'noerror 'nomessage)
4537 (if idlwave-system-routines
4b1aaa8b 4538 (message
f66f03de 4539 "Failed to load converted routine info, using old conversion.")
4b1aaa8b 4540 (message
f66f03de
S
4541 "Failed to convert XML routine info, falling back on idlw-rinfo.")
4542 (if (not (load "idlw-rinfo" 'noerror 'nomessage))
4b1aaa8b 4543 (message
f66f03de
S
4544 "Could not locate any system routine information."))))))))
4545
4546(defun idlwave-xml-system-routine-info-up-to-date()
4b1aaa8b 4547 (let* ((dir (file-name-as-directory
f66f03de
S
4548 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4549 (catalog-file (expand-file-name "idl_catalog.xml" dir)))
4550 (file-newer-than-file-p ;converted file is newer than catalog
4551 idlwave-xml-system-rinfo-converted-file
4552 catalog-file)))
4553
4554(defvar idlwave-system-class-info nil) ; Gathered from idlw-rinfo
4555(defvar idlwave-system-variables-alist nil
4556 "Alist of system variables and the associated structure tags.
4557Gets set in cached XML rinfo, or `idlw-rinfo.el'.")
4558(defvar idlwave-executive-commands-alist nil
4559 "Alist of system variables and their help files.")
4560(defvar idlwave-help-special-topic-words nil)
4561
4b1aaa8b 4562
f66f03de 4563(defun idlwave-shorten-syntax (syntax name &optional class)
5a89f0a7 4564 ;; From a list of syntax statements, shorten with %s and group with "or"
f66f03de 4565 (let ((case-fold-search t))
4b1aaa8b 4566 (mapconcat
f66f03de
S
4567 (lambda (x)
4568 (while (string-match name x)
4569 (setq x (replace-match "%s" t t x)))
4b1aaa8b 4570 (if class
f66f03de
S
4571 (while (string-match class x)
4572 (setq x (replace-match "%s" t t x))))
4573 x)
4574 (nreverse syntax)
4575 " or ")))
4576
4577(defun idlwave-xml-create-class-method-lists (xml-entry)
4578 ;; Create a class list entry from the xml parsed list., returning a
4579 ;; cons of form (class-entry method-entries).
4580 (let* ((nameblock (nth 1 xml-entry))
4581 (class (cdr (assq 'name nameblock)))
4582 (link (cdr (assq 'link nameblock)))
4583 (params (cddr xml-entry))
4584 (case-fold-search t)
4585 class-entry
4586 method methods-entry extra-kwds
4587 props get-props set-props init-props inherits
4588 pelem ptype)
4589 (while params
4590 (setq pelem (car params))
4591 (when (listp pelem)
4592 (setq ptype (car pelem)
4593 props (car (cdr pelem)))
4594 (cond
4595 ((eq ptype 'SUPERCLASS)
58c8f915
S
4596 (let ((pname (cdr (assq 'name props)))
4597 (plink (cdr (assq 'link props))))
4598 (unless (and (string= pname "None")
4599 (string= plink "None"))
4600 (push pname inherits))))
f66f03de
S
4601
4602 ((eq ptype 'PROPERTY)
4603 (let ((pname (cdr (assq 'name props)))
4604 (plink (cdr (assq 'link props)))
4605 (get (string= (cdr (assq 'get props)) "Yes"))
4606 (set (string= (cdr (assq 'set props)) "Yes"))
4607 (init (string= (cdr (assq 'init props)) "Yes")))
4608 (if get (push (list pname plink) get-props))
4609 (if set (push (list pname plink) set-props))
4610 (if init (push (list pname plink) init-props))))
4611
4612 ((eq ptype 'METHOD)
4613 (setq method (cdr (assq 'name props)))
4614 (setq extra-kwds ;;Assume all property keywords are gathered already
4615 (cond
4616 ((string-match (concat class "::Init") method)
4617 (put 'init-props 'matched t)
4618 init-props)
4619 ((string-match (concat class "::GetProperty") method)
4620 (put 'get-props 'matched t)
4621 get-props)
4622 ((string-match (concat class "::SetProperty") method)
4623 (put 'set-props 'matched t)
4624 set-props)
4625 (t nil)))
4b1aaa8b
PE
4626 (setq methods-entry
4627 (nconc (idlwave-xml-create-rinfo-list pelem class extra-kwds)
f66f03de
S
4628 methods-entry)))
4629 (t)))
4630 (setq params (cdr params)))
8d222148
SM
4631 ;;(unless (get 'init-props 'matched)
4632 ;; (message "Failed to match Init in class %s" class))
4633 ;;(unless (get 'get-props 'matched)
4634 ;; (message "Failed to match GetProperty in class %s" class))
4635 ;;(unless (get 'set-props 'matched)
4636 ;; (message "Failed to match SetProperty in class %s" class))
4b1aaa8b
PE
4637 (setq class-entry
4638 (if inherits
f66f03de
S
4639 (list class (append '(inherits) inherits) (list 'link link))
4640 (list class (list 'link link))))
4641 (cons class-entry methods-entry)))
4b1aaa8b 4642
f66f03de
S
4643(defun idlwave-xml-create-rinfo-list (xml-entry &optional class extra-kws)
4644 ;; Create correctly structured list elements from ROUTINE or METHOD
4645 ;; XML list structures. Return a list of list elements, with more
4646 ;; than one sub-list possible if a routine can serve as both
4647 ;; procedure and function (e.g. call_method).
4648 (let* ((nameblock (nth 1 xml-entry))
4649 (name (cdr (assq 'name nameblock)))
4650 (link (cdr (assq 'link nameblock)))
4651 (params (cddr xml-entry))
4652 (syntax-vec (make-vector 3 nil)) ; procedure, function, exec command
4653 (case-fold-search t)
8d222148 4654 syntax kwd klink pref-list kwds pelem ptype props result type)
f66f03de
S
4655 (if class ;; strip out class name from class method name string
4656 (if (string-match (concat class "::") name)
4657 (setq name (substring name (match-end 0)))))
4658 (while params
4659 (setq pelem (car params))
4660 (when (listp pelem)
4661 (setq ptype (car pelem)
4662 props (car (cdr pelem)))
4663 (cond
4664 ((eq ptype 'SYNTAX)
4665 (setq syntax (cdr (assq 'name props)))
4666 (if (string-match "-&gt;" syntax)
4667 (setq syntax (replace-match "->" t nil syntax)))
4668 (setq type (cdr (assq 'type props)))
4669 (push syntax
4670 (aref syntax-vec (cond
4671 ((string-match "^pro" type) 0)
4672 ((string-match "^fun" type) 1)
4673 ((string-match "^exec" type) 2)))))
4674 ((eq ptype 'KEYWORD)
4675 (setq kwd (cdr (assq 'name props))
4676 klink (cdr (assq 'link props)))
4677 (if (string-match "^\\[XY\\(Z?\\)\\]" kwd)
4b1aaa8b
PE
4678 (progn
4679 (setq pref-list
f66f03de
S
4680 (if (match-string 1 kwd) '("X" "Y" "Z") '("X" "Y"))
4681 kwd (substring kwd (match-end 0)))
4682 (loop for x in pref-list do
4683 (push (list (concat x kwd) klink) kwds)))
4684 (push (list kwd klink) kwds)))
4685
4686 (t))); Do nothing for the others
4687 (setq params (cdr params)))
4b1aaa8b 4688
f66f03de 4689 ;; Debug
8d222148
SM
4690 ;; (if (and (null (aref syntax-vec 0))
4691 ;; (null (aref syntax-vec 1))
4692 ;; (null (aref syntax-vec 2)))
4693 ;; (with-current-buffer (get-buffer-create "IDL_XML_catalog_complaints")
4694 ;; (if class
4695 ;; (insert (format "Missing SYNTAX entry for %s::%s\n" class name))
4696 ;; (insert (message "Missing SYNTAX entry for %s\n" name)))))
f66f03de
S
4697
4698 ;; Executive commands are treated specially
4699 (if (aref syntax-vec 2)
4700 (cons (substring name 1) link)
4701 (if extra-kws (setq kwds (nconc kwds extra-kws)))
4702 (setq kwds (idlwave-rinfo-group-keywords kwds link))
4703 (loop for idx from 0 to 1 do
4704 (if (aref syntax-vec idx)
4b1aaa8b 4705 (push (append (list name (if (eq idx 0) 'pro 'fun)
f66f03de 4706 class '(system)
4b1aaa8b 4707 (idlwave-shorten-syntax
f66f03de
S
4708 (aref syntax-vec idx) name class))
4709 kwds) result)))
4710 result)))
4711
4712
4713(defun idlwave-rinfo-group-keywords (kwds master-link)
4b1aaa8b 4714 ;; Group keywords by link file, as a list with elements
f66f03de
S
4715 ;; (linkfile ( ("KWD1" . link1) ("KWD2" . link2))
4716 (let (kwd link anchor linkfiles block master-elt)
4717 (while kwds
4718 (setq kwd (car kwds)
4719 link (idlwave-split-link-target (nth 1 kwd))
4720 anchor (cdr link)
4721 link (car link)
4722 kwd (car kwd))
4723 (if (setq block (assoc link linkfiles))
4724 (push (cons kwd anchor) (cdr block))
4725 (push (list link (cons kwd anchor)) linkfiles))
4726 (setq kwds (cdr kwds)))
4727 ;; Ensure the master link is there
4728 (if (setq master-elt (assoc master-link linkfiles))
4729 (if (eq (car linkfiles) master-elt)
4730 linkfiles
4731 (cons master-elt (delq master-elt linkfiles)))
4732 (push (list master-link) linkfiles))))
4b1aaa8b 4733
f66f03de
S
4734(defun idlwave-convert-xml-clean-statement-aliases (aliases)
4735 ;; Clean up the syntax of routines which are actually aliases by
4736 ;; removing the "OR" from the statements
4737 (let (syntax entry)
4738 (loop for x in aliases do
4739 (setq entry (assoc x idlwave-system-routines))
4740 (when entry
4741 (while (string-match " +or +" (setq syntax (nth 4 entry)))
4742 (setf (nth 4 entry) (replace-match ", " t t syntax)))))))
4743
4744(defun idlwave-convert-xml-clean-routine-aliases (aliases)
4745 ;; Duplicate and trim original routine aliases from rinfo list
4b1aaa8b 4746 ;; This if for, e.g. OPENR/OPENW/OPENU
f66f03de
S
4747 (let (alias remove-list new parts all-parts)
4748 (loop for x in aliases do
4749 (when (setq parts (split-string (cdr x) "/"))
4750 (setq new (assoc (cdr x) all-parts))
4751 (unless new
4752 (setq new (cons (cdr x) parts))
4753 (push new all-parts))
4754 (setcdr new (delete (car x) (cdr new)))))
4b1aaa8b 4755
f66f03de
S
4756 ;; Add any missing aliases (separate by slashes)
4757 (loop for x in all-parts do
4758 (if (cdr x)
4759 (push (cons (nth 1 x) (car x)) aliases)))
4760
4761 (loop for x in aliases do
4762 (when (setq alias (assoc (cdr x) idlwave-system-routines))
4763 (unless (memq alias remove-list) (push alias remove-list))
4764 (setq alias (copy-sequence alias))
4765 (setcar alias (car x))
4766 (push alias idlwave-system-routines)))
4767 (loop for x in remove-list do
4768 (delq x idlwave-system-routines))))
4769
4770(defun idlwave-convert-xml-clean-sysvar-aliases (aliases)
4771 ;; Duplicate and trim original routine aliases from rinfo list
4772 ;; This if for, e.g. !X, !Y, !Z.
8d222148 4773 (let (alias remove-list)
f66f03de
S
4774 (loop for x in aliases do
4775 (when (setq alias (assoc (cdr x) idlwave-system-variables-alist))
4776 (unless (memq alias remove-list) (push alias remove-list))
4777 (setq alias (copy-sequence alias))
4778 (setcar alias (car x))
4779 (push alias idlwave-system-variables-alist)))
4780 (loop for x in remove-list do
4781 (delq x idlwave-system-variables-alist))))
4782
4783
4784(defun idlwave-xml-create-sysvar-alist (xml-entry)
4785 ;; Create a sysvar list entry from the xml parsed list.
4786 (let* ((nameblock (nth 1 xml-entry))
a86bd650 4787 (name (cdr (assq 'name nameblock)))
b9e4fbd3 4788 (sysvar (substring name (progn (string-match "^ *!" name)
a86bd650 4789 (match-end 0))))
f66f03de
S
4790 (link (cdr (assq 'link nameblock)))
4791 (params (cddr xml-entry))
4792 (case-fold-search t)
8d222148 4793 pelem ptype props tags)
f66f03de
S
4794 (while params
4795 (setq pelem (car params))
4796 (when (listp pelem)
4797 (setq ptype (car pelem)
4798 props (car (cdr pelem)))
4799 (cond
4800 ((eq ptype 'FIELD)
4b1aaa8b 4801 (push (cons (cdr (assq 'name props))
f66f03de
S
4802 (cdr
4803 (idlwave-split-link-target (cdr (assq 'link props)))))
4804 tags))))
4805 (setq params (cdr params)))
4806 (delq nil
4807 (list sysvar (if tags (cons 'tags tags)) (list 'link link)))))
4808
4809
4810(defvar idlwave-xml-routine-info-file nil)
4811
4812(defun idlwave-save-routine-info ()
4813 (if idlwave-xml-routine-info-file
4814 (with-temp-file idlwave-xml-system-rinfo-converted-file
4b1aaa8b 4815 (insert
f66f03de 4816 (concat ";; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
4b1aaa8b
PE
4817;; IDLWAVE Routine Information File (IDLWAVE version " idlwave-mode-version ")
4818;; Automatically generated from source file:
f66f03de
S
4819;; " idlwave-xml-routine-info-file "
4820;; on " (current-time-string) "
4821;; Do not edit."))
4822 (insert (format "\n(setq idlwave-xml-routine-info-file \n \"%s\")"
4823 idlwave-xml-routine-info-file))
4824 (insert "\n(setq idlwave-system-routines\n '")
4825 (prin1 idlwave-system-routines (current-buffer))
4826 (insert ")")
4827 (insert "\n(setq idlwave-system-variables-alist\n '")
4828 (prin1 idlwave-system-variables-alist (current-buffer))
4829 (insert ")")
4830 (insert "\n(setq idlwave-system-class-info\n '")
4831 (prin1 idlwave-system-class-info (current-buffer))
4832 (insert ")")
4833 (insert "\n(setq idlwave-executive-commands-alist\n '")
4834 (prin1 idlwave-executive-commands-alist (current-buffer))
4835 (insert ")")
4836 (insert "\n(setq idlwave-help-special-topic-words\n '")
4837 (prin1 idlwave-help-special-topic-words (current-buffer))
4838 (insert ")"))))
4839
4840(defun idlwave-convert-xml-system-routine-info ()
4841 "Convert XML supplied IDL routine info into internal form.
4842Cache to disk for quick recovery."
4843 (interactive)
4b1aaa8b 4844 (let* ((dir (file-name-as-directory
f66f03de
S
4845 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4846 (catalog-file (expand-file-name "idl_catalog.xml" dir))
4847 (elem-cnt 0)
4b1aaa8b 4848 props rinfo msg-cnt elem type nelem class-result alias
8d222148 4849 routines routine-aliases statement-aliases sysvar-aliases)
f66f03de
S
4850 (if (not (file-exists-p catalog-file))
4851 (error "No such XML routine info file: %s" catalog-file)
4852 (if (not (file-readable-p catalog-file))
4853 (error "Cannot read XML routine info file: %s" catalog-file)))
4b1aaa8b 4854 (message "Reading XML routine info...")
e08734e2 4855 (setq rinfo (xml-parse-file catalog-file))
f66f03de
S
4856 (message "Reading XML routine info...done")
4857 (setq rinfo (assq 'CATALOG rinfo))
4858 (unless rinfo (error "Failed to parse XML routine info"))
4859 ;;(setq rinfo (car rinfo)) ; Skip the catalog stuff.
4b1aaa8b 4860
8d222148 4861 (setq rinfo (cddr rinfo))
f66f03de
S
4862
4863 (setq nelem (length rinfo)
4864 msg-cnt (/ nelem 20))
4b1aaa8b 4865
f66f03de
S
4866 (setq idlwave-xml-routine-info-file nil)
4867 (message "Converting XML routine info...")
4868 (setq idlwave-system-routines nil
4869 idlwave-system-variables-alist nil
4870 idlwave-system-class-info nil
4871 idlwave-executive-commands-alist nil
4872 idlwave-help-special-topic-words nil)
4873
4874 (while rinfo
4875 (setq elem (car rinfo)
4876 rinfo (cdr rinfo))
4877 (incf elem-cnt)
4878 (when (listp elem)
4879 (setq type (car elem)
4880 props (car (cdr elem)))
4881 (if (= (mod elem-cnt msg-cnt) 0)
4b1aaa8b 4882 (message "Converting XML routine info...%2d%%"
f66f03de 4883 (/ (* elem-cnt 100) nelem)))
4b1aaa8b 4884 (cond
f66f03de
S
4885 ((eq type 'ROUTINE)
4886 (if (setq alias (assq 'alias_to props))
4b1aaa8b 4887 (push (cons (cdr (assq 'name props)) (cdr alias))
f66f03de
S
4888 routine-aliases)
4889 (setq routines (idlwave-xml-create-rinfo-list elem))
4890 (if (listp (cdr routines))
4891 (setq idlwave-system-routines
4892 (nconc idlwave-system-routines routines))
4893 ;; a cons cell is an executive commands
4894 (push routines idlwave-executive-commands-alist))))
4b1aaa8b 4895
f66f03de
S
4896 ((eq type 'CLASS)
4897 (setq class-result (idlwave-xml-create-class-method-lists elem))
4898 (push (car class-result) idlwave-system-class-info)
4899 (setq idlwave-system-routines
4900 (nconc idlwave-system-routines (cdr class-result))))
4901
4902 ((eq type 'STATEMENT)
4903 (push (cons (cdr (assq 'name props))
4904 (cdr (assq 'link props)))
4905 idlwave-help-special-topic-words)
4906 ;; Save the links to those which are statement aliases (not routines)
4907 (if (setq alias (assq 'alias_to props))
4908 (unless (member (cdr alias) statement-aliases)
4909 (push (cdr alias) statement-aliases))))
4910
4911 ((eq type 'SYSVAR)
4912 (if (setq alias (cdr (assq 'alias_to props)))
4b1aaa8b 4913 (push (cons (substring (cdr (assq 'name props)) 1)
f66f03de
S
4914 (substring alias 1))
4915 sysvar-aliases)
4b1aaa8b 4916 (push (idlwave-xml-create-sysvar-alist elem)
f66f03de
S
4917 idlwave-system-variables-alist)))
4918 (t))))
4919 (idlwave-convert-xml-clean-routine-aliases routine-aliases)
4920 (idlwave-convert-xml-clean-statement-aliases statement-aliases)
4921 (idlwave-convert-xml-clean-sysvar-aliases sysvar-aliases)
4922
4923 (setq idlwave-xml-routine-info-file catalog-file)
4924 (idlwave-save-routine-info)
4925 (message "Converting XML routine info...done")))
4b1aaa8b
PE
4926
4927
f66f03de
S
4928;; ("ROUTINE" type class
4929;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4930;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4931;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de
S
4932;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
4933
4934
5e72c6b2
S
4935(defun idlwave-load-rinfo-next-step ()
4936 (let ((inhibit-quit t)
4937 (arr idlwave-load-rinfo-steps-done))
f66f03de 4938 (if (catch 'exit
5e72c6b2 4939 (when (not (aref arr 0))
f66f03de
S
4940 (message "Loading system routine info in idle time...")
4941 (idlwave-load-system-routine-info)
4942 ;;(load "idlw-rinfo" 'noerror 'nomessage)
4943 (message "Loading system routine info in idle time...done")
5e72c6b2
S
4944 (aset arr 0 t)
4945 (throw 'exit t))
4b1aaa8b 4946
5e72c6b2
S
4947 (when (not (aref arr 1))
4948 (message "Normalizing idlwave-system-routines in idle time...")
4949 (idlwave-reset-sintern t)
4950 (put 'idlwave-reset-sintern 'done-by-idle t)
4951 (setq idlwave-system-routines
4952 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
4953 (message "Normalizing idlwave-system-routines in idle time...done")
4954 (aset arr 1 t)
4955 (throw 'exit t))
f66f03de 4956
5e72c6b2 4957 (when (not (aref arr 2))
52a244eb
S
4958 (when (and (stringp idlwave-user-catalog-file)
4959 (file-regular-p idlwave-user-catalog-file))
4960 (message "Loading user catalog in idle time...")
5e72c6b2 4961 (condition-case nil
52a244eb
S
4962 (load-file idlwave-user-catalog-file)
4963 (error (throw 'exit nil)))
4964 ;; Check for the old style catalog and warn
4965 (if (and
4966 (boundp 'idlwave-library-routines)
4967 idlwave-library-routines)
775591f7 4968 (progn
52a244eb
S
4969 (setq idlwave-library-routines nil)
4970 (ding)
4b1aaa8b 4971 (message "Outdated user catalog: %s... recreate"
52a244eb 4972 idlwave-user-catalog-file))
f66f03de
S
4973 (message "Loading user catalog in idle time...done")))
4974 (aset arr 2 t)
4975 (throw 'exit t))
4976
5e72c6b2 4977 (when (not (aref arr 3))
52a244eb
S
4978 (when idlwave-user-catalog-routines
4979 (message "Normalizing user catalog routines in idle time...")
4b1aaa8b 4980 (setq idlwave-user-catalog-routines
52a244eb
S
4981 (idlwave-sintern-rinfo-list
4982 idlwave-user-catalog-routines 'sys))
4b1aaa8b 4983 (message
52a244eb 4984 "Normalizing user catalog routines in idle time...done"))
5e72c6b2
S
4985 (aset arr 3 t)
4986 (throw 'exit t))
f66f03de 4987
5e72c6b2 4988 (when (not (aref arr 4))
4b1aaa8b 4989 (idlwave-scan-library-catalogs
52a244eb
S
4990 "Loading and normalizing library catalogs in idle time...")
4991 (aset arr 4 t)
4992 (throw 'exit t))
4993 (when (not (aref arr 5))
5e72c6b2
S
4994 (message "Finishing initialization in idle time...")
4995 (idlwave-routines)
4996 (message "Finishing initialization in idle time...done")
4b1aaa8b 4997 (aset arr 5 t)
5e72c6b2 4998 (throw 'exit nil)))
52a244eb
S
4999 ;; restart the timer
5000 (if (sit-for 1)
5001 (idlwave-load-rinfo-next-step)
5002 (setq idlwave-load-rinfo-idle-timer
5003 (run-with-idle-timer
5004 idlwave-init-rinfo-when-idle-after
5005 nil 'idlwave-load-rinfo-next-step))))))
5e72c6b2 5006
8d222148
SM
5007(defvar idlwave-after-load-rinfo-hook nil)
5008
f66f03de
S
5009(defun idlwave-load-all-rinfo (&optional force)
5010 ;; Load and case-treat the system, user catalog, and library routine
5011 ;; info files.
5012
5013 ;; System
5e72c6b2 5014 (when (or force (not (aref idlwave-load-rinfo-steps-done 0)))
f66f03de
S
5015 ;;(load "idlw-rinfo" 'noerror 'nomessage))
5016 (idlwave-load-system-routine-info))
5e72c6b2
S
5017 (when (or force (not (aref idlwave-load-rinfo-steps-done 1)))
5018 (message "Normalizing idlwave-system-routines...")
5019 (setq idlwave-system-routines
5020 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
5021 (message "Normalizing idlwave-system-routines...done"))
f66f03de
S
5022 (when idlwave-system-routines
5023 (setq idlwave-routines (copy-sequence idlwave-system-routines))
5024 (setq idlwave-last-system-routine-info-cons-cell
5025 (nthcdr (1- (length idlwave-routines)) idlwave-routines)))
5026
5027 ;; User catalog
52a244eb
S
5028 (when (and (stringp idlwave-user-catalog-file)
5029 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5030 (condition-case nil
52a244eb
S
5031 (when (or force (not (aref idlwave-load-rinfo-steps-done 2)))
5032 (load-file idlwave-user-catalog-file))
5033 (error nil))
4b1aaa8b 5034 (when (and
f66f03de
S
5035 (boundp 'idlwave-library-routines)
5036 idlwave-library-routines)
52a244eb 5037 (setq idlwave-library-routines nil)
4b1aaa8b 5038 (error "Outdated user catalog: %s... recreate"
f66f03de 5039 idlwave-user-catalog-file))
52a244eb
S
5040 (setq idlwave-true-path-alist nil)
5041 (when (or force (not (aref idlwave-load-rinfo-steps-done 3)))
5042 (message "Normalizing user catalog routines...")
4b1aaa8b
PE
5043 (setq idlwave-user-catalog-routines
5044 (idlwave-sintern-rinfo-list
52a244eb
S
5045 idlwave-user-catalog-routines 'sys))
5046 (message "Normalizing user catalog routines...done")))
f66f03de
S
5047
5048 ;; Library catalog
52a244eb
S
5049 (when (or force (not (aref idlwave-load-rinfo-steps-done 4)))
5050 (idlwave-scan-library-catalogs
5051 "Loading and normalizing library catalogs..."))
05a1abfc
CD
5052 (run-hooks 'idlwave-after-load-rinfo-hook))
5053
f32b3b91
CD
5054
5055(defun idlwave-update-buffer-routine-info ()
5056 (let (res)
4b1aaa8b 5057 (cond
15e42531
CD
5058 ((eq idlwave-scan-all-buffers-for-routine-info t)
5059 ;; Scan all buffers, current buffer last
5060 (message "Scanning all buffers...")
4b1aaa8b 5061 (setq res (idlwave-get-routine-info-from-buffers
15e42531
CD
5062 (reverse (buffer-list)))))
5063 ((null idlwave-scan-all-buffers-for-routine-info)
5064 ;; Don't scan any buffers
5065 (setq res nil))
5066 (t
f32b3b91 5067 ;; Just scan this buffer
175069ef 5068 (if (derived-mode-p 'idlwave-mode)
f32b3b91
CD
5069 (progn
5070 (message "Scanning current buffer...")
5071 (setq res (idlwave-get-routine-info-from-buffers
15e42531 5072 (list (current-buffer))))))))
f32b3b91 5073 ;; Put the result into the correct variable
4b1aaa8b 5074 (setq idlwave-buffer-routines
52a244eb 5075 (idlwave-sintern-rinfo-list res 'set))))
f32b3b91 5076
05a1abfc 5077(defun idlwave-concatenate-rinfo-lists (&optional quiet run-hook)
f32b3b91 5078 "Put the different sources for routine information together."
4b1aaa8b 5079 ;; The sequence here is important because earlier definitions shadow
f32b3b91 5080 ;; later ones. We assume that if things in the buffers are newer
52a244eb 5081 ;; then in the shell of the system, they are meant to be different.
03983bdc
GM
5082 (let ((temp (append idlwave-buffer-routines
5083 idlwave-compiled-routines
5084 idlwave-library-catalog-routines
5085 idlwave-user-catalog-routines)))
5086 ;; Not actually used for anything?
5087 (if idlwave-last-system-routine-info-cons-cell
5088 (setcdr idlwave-last-system-routine-info-cons-cell temp)
5089 (setq idlwave-last-system-routine-info-cons-cell (cons temp nil))))
f32b3b91 5090 (setq idlwave-class-alist nil)
15e42531 5091
f32b3b91 5092 ;; Give a message with information about the number of routines we have.
15e42531 5093 (unless quiet
4b1aaa8b 5094 (message
52a244eb 5095 "Routines Found: buffer(%d) compiled(%d) library(%d) user(%d) system(%d)"
15e42531
CD
5096 (length idlwave-buffer-routines)
5097 (length idlwave-compiled-routines)
52a244eb
S
5098 (length idlwave-library-catalog-routines)
5099 (length idlwave-user-catalog-routines)
05a1abfc
CD
5100 (length idlwave-system-routines)))
5101 (if run-hook
5102 (run-hooks 'idlwave-update-rinfo-hook)))
15e42531
CD
5103
5104(defun idlwave-class-alist ()
5105 "Return the class alist - make it if necessary."
5106 (or idlwave-class-alist
5107 (let (class)
5108 (loop for x in idlwave-routines do
5109 (when (and (setq class (nth 2 x))
5110 (not (assq class idlwave-class-alist)))
5111 (push (list class) idlwave-class-alist)))
4b1aaa8b 5112 idlwave-class-alist)))
15e42531
CD
5113
5114;; Three functions for the hooks
5115(defun idlwave-save-buffer-update ()
5116 (idlwave-update-current-buffer-info 'save-buffer))
5117(defun idlwave-kill-buffer-update ()
5118 (idlwave-update-current-buffer-info 'kill-buffer))
5119(defun idlwave-new-buffer-update ()
5120 (idlwave-update-current-buffer-info 'find-file))
5121
5122(defun idlwave-update-current-buffer-info (why)
5a0c3f56
JB
5123 "Update `idlwave-routines' for current buffer.
5124Can run from `after-save-hook'."
175069ef 5125 (when (and (derived-mode-p 'idlwave-mode)
15e42531
CD
5126 (or (eq t idlwave-auto-routine-info-updates)
5127 (memq why idlwave-auto-routine-info-updates))
5128 idlwave-scan-all-buffers-for-routine-info
5129 idlwave-routines)
5130 (condition-case nil
5131 (let (routines)
5132 (idlwave-replace-buffer-routine-info
5133 (buffer-file-name)
5134 (if (eq why 'kill-buffer)
5135 nil
5136 (setq routines
5137 (idlwave-sintern-rinfo-list
5138 (idlwave-get-routine-info-from-buffers
5139 (list (current-buffer))) 'set))))
5140 (idlwave-concatenate-rinfo-lists 'quiet)
5141 routines)
5142 (error nil))))
5143
5144(defun idlwave-replace-buffer-routine-info (file new)
5145 "Cut the part from FILE out of `idlwave-buffer-routines' and add NEW."
4b1aaa8b 5146 (let ((list idlwave-buffer-routines)
15e42531
CD
5147 found)
5148 (while list
5149 ;; The following test uses eq to make sure it works correctly
5150 ;; when two buffers visit the same file. Then the file names
5151 ;; will be equal, but not eq.
52a244eb 5152 (if (eq (idlwave-routine-source-file (nth 3 (car list))) file)
15e42531
CD
5153 (progn
5154 (setcar list nil)
5155 (setq found t))
5156 (if found
4b1aaa8b 5157 ;; End of that section reached. Jump.
15e42531
CD
5158 (setq list nil)))
5159 (setq list (cdr list)))
5160 (setq idlwave-buffer-routines
5161 (append new (delq nil idlwave-buffer-routines)))))
f32b3b91
CD
5162
5163;;----- Scanning buffers -------------------
5164
5165(defun idlwave-get-routine-info-from-buffers (buffers)
5166 "Call `idlwave-get-buffer-routine-info' on idlwave-mode buffers in BUFFERS."
5167 (let (buf routine-lists res)
5168 (save-excursion
5169 (while (setq buf (pop buffers))
5170 (set-buffer buf)
175069ef 5171 (if (and (derived-mode-p 'idlwave-mode)
05a1abfc 5172 buffer-file-name)
f32b3b91
CD
5173 ;; yes, this buffer has the right mode.
5174 (progn (setq res (condition-case nil
5175 (idlwave-get-buffer-routine-info)
5176 (error nil)))
5177 (push res routine-lists)))))
5178 ;; Concatenate the individual lists and return the result
5179 (apply 'nconc routine-lists)))
5180
5181(defun idlwave-get-buffer-routine-info ()
5182 "Scan the current buffer for routine info. Return (PRO-LIST FUNC-LIST)."
5183 (let* ((case-fold-search t)
5184 routine-list string entry)
5185 (save-excursion
5186 (save-restriction
5187 (widen)
5188 (goto-char (point-min))
4b1aaa8b 5189 (while (re-search-forward
15e42531 5190 "^[ \t]*\\(pro\\|function\\)[ \t]" nil t)
76959b77 5191 (setq string (buffer-substring-no-properties
f32b3b91 5192 (match-beginning 0)
4b1aaa8b 5193 (progn
f32b3b91
CD
5194 (idlwave-end-of-statement)
5195 (point))))
5196 (setq entry (idlwave-parse-definition string))
5197 (push entry routine-list))))
5198 routine-list))
5199
15e42531 5200(defvar idlwave-scanning-lib-dir)
8d222148 5201(defvar idlwave-scanning-lib)
f32b3b91
CD
5202(defun idlwave-parse-definition (string)
5203 "Parse a module definition."
5204 (let ((case-fold-search t)
5205 start name args type keywords class)
5206 ;; Remove comments
5207 (while (string-match ";.*" string)
5208 (setq string (replace-match "" t t string)))
5209 ;; Remove the continuation line stuff
5210 (while (string-match "\\([^a-zA-Z0-9$_]\\)\\$[ \t]*\n" string)
5211 (setq string (replace-match "\\1 " t nil string)))
05a1abfc
CD
5212 (while (string-match "\n" string)
5213 (setq string (replace-match " " t nil string)))
f32b3b91
CD
5214 ;; Match the name and type.
5215 (when (string-match
5216 "\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)" string)
5217 (setq start (match-end 0))
5218 (setq type (downcase (match-string 1 string)))
5219 (if (match-beginning 3)
5220 (setq class (match-string 3 string)))
5221 (setq name (match-string 4 string)))
5222 ;; Match normal args and keyword args
5223 (while (string-match
15e42531 5224 ",\\s-*\\([a-zA-Z][a-zA-Z0-9$_]*\\|\\(_ref\\)?_extra\\)\\s-*\\(=\\)?"
f32b3b91
CD
5225 string start)
5226 (setq start (match-end 0))
15e42531 5227 (if (match-beginning 3)
f32b3b91
CD
5228 (push (match-string 1 string) keywords)
5229 (push (match-string 1 string) args)))
5230 ;; Normalize and sort.
5231 (setq args (nreverse args))
4b1aaa8b 5232 (setq keywords (sort keywords (lambda (a b)
f32b3b91
CD
5233 (string< (downcase a) (downcase b)))))
5234 ;; Make and return the entry
5235 ;; We don't know which argument are optional, so this information
5236 ;; will not be contained in the calling sequence.
5237 (list name
5238 (if (equal type "pro") 'pro 'fun)
5239 class
5240 (cond ((not (boundp 'idlwave-scanning-lib))
52a244eb 5241 (list 'buffer (buffer-file-name)))
d2c32364 5242; ((string= (downcase (file-name-base))
15e42531
CD
5243; (downcase name))
5244; (list 'lib))
5245; (t (cons 'lib (file-name-nondirectory (buffer-file-name))))
52a244eb
S
5246 (t (list 'user (file-name-nondirectory (buffer-file-name))
5247 idlwave-scanning-lib-dir "UserLib")))
4b1aaa8b 5248 (concat
f32b3b91
CD
5249 (if (string= type "function") "Result = " "")
5250 (if class "Obj ->[%s::]" "")
5251 "%s"
5252 (if args
5253 (concat
5254 (if (string= type "function") "(" ", ")
5255 (mapconcat 'identity args ", ")
5256 (if (string= type "function") ")" ""))))
5257 (if keywords
52a244eb 5258 (cons nil (mapcar 'list keywords)) ;No help file
f32b3b91
CD
5259 nil))))
5260
f32b3b91 5261
52a244eb 5262;;----- Scanning the user catalog -------------------
15e42531
CD
5263
5264(defun idlwave-sys-dir ()
5265 "Return the syslib directory, or a dummy that never matches."
3938cb82
S
5266 (cond
5267 ((and idlwave-system-directory
5268 (not (string= idlwave-system-directory "")))
5269 idlwave-system-directory)
5270 ((getenv "IDL_DIR"))
5271 (t "@@@@@@@@")))
5272
52a244eb 5273
52a244eb 5274(defun idlwave-create-user-catalog-file (&optional arg)
f32b3b91 5275 "Scan all files on selected dirs of IDL search path for routine information.
52a244eb
S
5276
5277A widget checklist will allow you to choose the directories. Write
5278the result as a file `idlwave-user-catalog-file'. When this file
5a0c3f56
JB
5279exists, it will be automatically loaded to give routine information
5280about library routines. With ARG, just rescan the same directories
5281as last time - so no widget will pop up."
f32b3b91
CD
5282 (interactive "P")
5283 ;; Make sure the file is loaded if it exists.
52a244eb
S
5284 (if (and (stringp idlwave-user-catalog-file)
5285 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5286 (condition-case nil
52a244eb 5287 (load-file idlwave-user-catalog-file)
f32b3b91
CD
5288 (error nil)))
5289 ;; Make sure the file name makes sense
52a244eb
S
5290 (unless (and (stringp idlwave-user-catalog-file)
5291 (> (length idlwave-user-catalog-file) 0)
f32b3b91 5292 (file-accessible-directory-p
52a244eb 5293 (file-name-directory idlwave-user-catalog-file))
4b1aaa8b 5294 (not (string= "" (file-name-nondirectory
52a244eb
S
5295 idlwave-user-catalog-file))))
5296 (error "`idlwave-user-catalog-file' does not point to a file in an accessible directory"))
4b1aaa8b 5297
f32b3b91 5298 (cond
f32b3b91 5299 ;; Rescan the known directories
52a244eb
S
5300 ((and arg idlwave-path-alist
5301 (consp (car idlwave-path-alist)))
5302 (idlwave-scan-user-lib-files idlwave-path-alist))
5303
5304 ;; Expand the directories from library-path and run the widget
f32b3b91 5305 (idlwave-library-path
52a244eb 5306 (idlwave-display-user-catalog-widget
4b1aaa8b 5307 (if idlwave-true-path-alist
52a244eb
S
5308 ;; Propagate any flags on the existing path-alist
5309 (mapcar (lambda (x)
5310 (let ((path-entry (assoc (file-truename x)
5311 idlwave-true-path-alist)))
5312 (if path-entry
4b1aaa8b 5313 (cons x (cdr path-entry))
52a244eb
S
5314 (list x))))
5315 (idlwave-expand-path idlwave-library-path))
5316 (mapcar 'list (idlwave-expand-path idlwave-library-path)))))
5317
5318 ;; Ask the shell for the path and then run the widget
f32b3b91 5319 (t
f32b3b91 5320 (message "Asking the shell for IDL path...")
15e42531
CD
5321 (require 'idlw-shell)
5322 (idlwave-shell-send-command idlwave-shell-path-query
52a244eb 5323 '(idlwave-user-catalog-command-hook nil)
15e42531 5324 'hide))))
f32b3b91 5325
52a244eb
S
5326
5327;; Parse shell path information and select among it.
5328(defun idlwave-user-catalog-command-hook (&optional arg)
5329 ;; Command hook used by `idlwave-create-user-catalog-file'.
f32b3b91
CD
5330 (if arg
5331 ;; Scan immediately
52a244eb
S
5332 (idlwave-scan-user-lib-files idlwave-path-alist)
5333 ;; Set the path and display the widget
5334 (idlwave-shell-get-path-info 'no-write) ; set to something path-alist
5335 (idlwave-scan-library-catalogs "Locating library catalogs..." 'no-load)
5336 (idlwave-display-user-catalog-widget idlwave-path-alist)))
5337
4b1aaa8b 5338(defconst idlwave-user-catalog-widget-help-string
52a244eb
S
5339 "This is the front-end to the creation of the IDLWAVE user catalog.
5340Please select the directories on IDL's search path from which you
5341would like to extract routine information, to be stored in the file:
f32b3b91
CD
5342
5343 %s
5344
52a244eb
S
5345If this is not the correct file, first set variable
5346`idlwave-user-catalog-file', and call this command again.
15e42531 5347
52a244eb
S
5348N.B. Many libraries include pre-scanned catalog files
5349\(\".idlwave_catalog\"). These are marked with \"[LIB]\", and need
5350not be scanned. You can scan your own libraries off-line using the
5351perl script `idlwave_catalog'.
15e42531 5352
f32b3b91
CD
5353After selecting the directories, choose [Scan & Save] to scan the library
5354directories and save the routine info.
5355\n")
5356
5357(defvar idlwave-widget)
5358(defvar widget-keymap)
52a244eb 5359(defun idlwave-display-user-catalog-widget (dirs-list)
f32b3b91
CD
5360 "Create the widget to select IDL search path directories for scanning."
5361 (interactive)
5362 (require 'widget)
5363 (require 'wid-edit)
52a244eb 5364 (unless dirs-list
f32b3b91
CD
5365 (error "Don't know IDL's search path"))
5366
f32b3b91
CD
5367 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
5368 (switch-to-buffer (get-buffer-create "*IDLWAVE Widget*"))
5369 (kill-all-local-variables)
5370 (make-local-variable 'idlwave-widget)
52a244eb
S
5371 (widget-insert (format idlwave-user-catalog-widget-help-string
5372 idlwave-user-catalog-file))
4b1aaa8b 5373
f32b3b91 5374 (widget-create 'push-button
52a244eb 5375 :notify 'idlwave-widget-scan-user-lib-files
f32b3b91
CD
5376 "Scan & Save")
5377 (widget-insert " ")
5378 (widget-create 'push-button
52a244eb 5379 :notify 'idlwave-delete-user-catalog-file
f32b3b91
CD
5380 "Delete File")
5381 (widget-insert " ")
5382 (widget-create 'push-button
4b1aaa8b 5383 :notify
8d222148
SM
5384 (lambda (&rest ignore)
5385 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5386 (dolist (x path-list)
5387 (unless (memq 'lib (cdr x))
5388 (idlwave-path-alist-add-flag x 'user)))
5389 (idlwave-display-user-catalog-widget path-list)))
52a244eb 5390 "Select All Non-Lib")
f32b3b91
CD
5391 (widget-insert " ")
5392 (widget-create 'push-button
4b1aaa8b 5393 :notify
8d222148
SM
5394 (lambda (&rest ignore)
5395 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5396 (dolist (x path-list)
5397 (idlwave-path-alist-remove-flag x 'user))
5398 (idlwave-display-user-catalog-widget path-list)))
f32b3b91 5399 "Deselect All")
52a244eb
S
5400 (widget-insert " ")
5401 (widget-create 'push-button
5402 :notify (lambda (&rest ignore)
5403 (kill-buffer (current-buffer)))
5404 "Quit")
f32b3b91
CD
5405 (widget-insert "\n\n")
5406
52a244eb 5407 (widget-insert "Select Directories: \n")
4b1aaa8b 5408
f32b3b91
CD
5409 (setq idlwave-widget
5410 (apply 'widget-create
5411 'checklist
4b1aaa8b
PE
5412 :value (delq nil (mapcar (lambda (x)
5413 (if (memq 'user (cdr x))
52a244eb
S
5414 (car x)))
5415 dirs-list))
f32b3b91
CD
5416 :greedy t
5417 :tag "List of directories"
4b1aaa8b
PE
5418 (mapcar (lambda (x)
5419 (list 'item
52a244eb
S
5420 (if (memq 'lib (cdr x))
5421 (concat "[LIB] " (car x) )
5422 (car x)))) dirs-list)))
5423 (widget-put idlwave-widget :path-dirs dirs-list)
f32b3b91
CD
5424 (widget-insert "\n")
5425 (use-local-map widget-keymap)
5426 (widget-setup)
5427 (goto-char (point-min))
5428 (delete-other-windows))
4b1aaa8b 5429
52a244eb 5430(defun idlwave-delete-user-catalog-file (&rest ignore)
f32b3b91 5431 (if (yes-or-no-p
52a244eb 5432 (format "Delete file %s " idlwave-user-catalog-file))
f32b3b91 5433 (progn
52a244eb
S
5434 (delete-file idlwave-user-catalog-file)
5435 (message "%s has been deleted" idlwave-user-catalog-file))))
f32b3b91 5436
52a244eb
S
5437(defun idlwave-widget-scan-user-lib-files (&rest ignore)
5438 ;; Call `idlwave-scan-user-lib-files' with data taken from the widget.
f32b3b91 5439 (let* ((widget idlwave-widget)
15e42531 5440 (selected-dirs (widget-value widget))
52a244eb
S
5441 (path-alist (widget-get widget :path-dirs))
5442 (this-path-alist path-alist)
5443 dir-entry)
5444 (while (setq dir-entry (pop this-path-alist))
4b1aaa8b 5445 (if (member
52a244eb
S
5446 (if (memq 'lib (cdr dir-entry))
5447 (concat "[LIB] " (car dir-entry))
5448 (car dir-entry))
5449 selected-dirs)
5450 (idlwave-path-alist-add-flag dir-entry 'user)
5451 (idlwave-path-alist-remove-flag dir-entry 'user)))
5452 (idlwave-scan-user-lib-files path-alist)))
f32b3b91
CD
5453
5454(defvar font-lock-mode)
52a244eb
S
5455(defun idlwave-scan-user-lib-files (path-alist)
5456 ;; Scan the PRO files in PATH-ALIST and store the info in the user catalog
f32b3b91 5457 (let* ((idlwave-scanning-lib t)
15e42531 5458 (idlwave-scanning-lib-dir "")
f32b3b91 5459 (idlwave-completion-case nil)
15e42531 5460 dirs-alist dir files file)
52a244eb
S
5461 (setq idlwave-user-catalog-routines nil
5462 idlwave-path-alist path-alist ; for library-path instead
5463 idlwave-true-path-alist nil)
5464 (if idlwave-auto-write-paths (idlwave-write-paths))
9a529312 5465 (with-current-buffer (get-buffer-create "*idlwave-scan.pro*")
f32b3b91 5466 (idlwave-mode)
15e42531
CD
5467 (setq dirs-alist (reverse path-alist))
5468 (while (setq dir (pop dirs-alist))
52a244eb 5469 (when (memq 'user (cdr dir)) ; Has it marked for scan?
15e42531 5470 (setq dir (car dir))
52a244eb 5471 (setq idlwave-scanning-lib-dir dir)
15e42531
CD
5472 (when (file-directory-p dir)
5473 (setq files (directory-files dir 'full "\\.[pP][rR][oO]\\'"))
5474 (while (setq file (pop files))
5475 (when (file-regular-p file)
5476 (if (not (file-readable-p file))
5477 (message "Skipping %s (no read permission)" file)
5478 (message "Scanning %s..." file)
5479 (erase-buffer)
5480 (insert-file-contents file 'visit)
52a244eb 5481 (setq idlwave-user-catalog-routines
15e42531
CD
5482 (append (idlwave-get-routine-info-from-buffers
5483 (list (current-buffer)))
52a244eb
S
5484 idlwave-user-catalog-routines)))))))))
5485 (message "Creating user catalog file...")
f32b3b91
CD
5486 (kill-buffer "*idlwave-scan.pro*")
5487 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
bc74a74a
GM
5488 (with-temp-buffer
5489 (insert ";; IDLWAVE user catalog file\n")
5490 (insert (format ";; Created %s\n\n" (current-time-string)))
5491
5492 ;; Define the routine info list
5493 (insert "\n(setq idlwave-user-catalog-routines\n '(")
5494 (let ((standard-output (current-buffer)))
5495 (mapc (lambda (x)
5496 (insert "\n ")
5497 (prin1 x)
5498 (goto-char (point-max)))
5499 idlwave-user-catalog-routines))
5500 (insert (format "))\n\n;;; %s ends here\n"
5501 (file-name-nondirectory idlwave-user-catalog-file)))
5502 (write-region nil nil idlwave-user-catalog-file)))
52a244eb 5503 (message "Creating user catalog file...done")
f32b3b91 5504 (message "Info for %d routines saved in %s"
52a244eb
S
5505 (length idlwave-user-catalog-routines)
5506 idlwave-user-catalog-file)
f32b3b91
CD
5507 (sit-for 2)
5508 (idlwave-update-routine-info t))
5509
52a244eb
S
5510(defun idlwave-read-paths ()
5511 (if (and (stringp idlwave-path-file)
5512 (file-regular-p idlwave-path-file))
5513 (condition-case nil
5514 (load idlwave-path-file t t t)
5515 (error nil))))
5516
5517(defun idlwave-write-paths ()
5518 (interactive)
5519 (when (and idlwave-path-alist idlwave-system-directory)
bc74a74a
GM
5520 (with-temp-buffer
5521 (insert ";; IDLWAVE paths\n")
5522 (insert (format ";; Created %s\n\n" (current-time-string)))
52a244eb 5523 ;; Define the variable which knows the value of "!DIR"
bc74a74a
GM
5524 (insert (format "\n(setq idlwave-system-directory \"%s\")\n"
5525 idlwave-system-directory))
5526
5527 ;; Define the variable which contains a list of all scanned directories
5528 (insert "\n(setq idlwave-path-alist\n '(")
5529 (let ((standard-output (current-buffer)))
5530 (mapc (lambda (x)
5531 (insert "\n ")
5532 (prin1 x)
5533 (goto-char (point-max)))
5534 idlwave-path-alist))
5535 (insert "))\n")
5536 (write-region nil nil idlwave-path-file))))
52a244eb 5537
f32b3b91
CD
5538(defun idlwave-expand-path (path &optional default-dir)
5539 ;; Expand parts of path starting with '+' recursively into directory list.
5540 ;; Relative recursive path elements are expanded relative to DEFAULT-DIR.
5541 (message "Expanding path...")
5542 (let (path1 dir recursive)
5543 (while (setq dir (pop path))
5544 (if (setq recursive (string= (substring dir 0 1) "+"))
5545 (setq dir (substring dir 1)))
5546 (if (and recursive
5547 (not (file-name-absolute-p dir)))
5548 (setq dir (expand-file-name dir default-dir)))
5549 (if recursive
5550 ;; Expand recursively
5551 (setq path1 (append (idlwave-recursive-directory-list dir) path1))
5552 ;; Keep unchanged
5553 (push dir path1)))
5554 (message "Expanding path...done")
5555 (nreverse path1)))
5556
5557(defun idlwave-recursive-directory-list (dir)
5558 ;; Return a list of all directories below DIR, including DIR itself
5559 (let ((path (list dir)) path1 file files)
5560 (while (setq dir (pop path))
5561 (when (file-directory-p dir)
5562 (setq files (nreverse (directory-files dir t "[^.]")))
5563 (while (setq file (pop files))
4b1aaa8b 5564 (if (file-directory-p file)
f32b3b91
CD
5565 (push (file-name-as-directory file) path)))
5566 (push dir path1)))
5567 path1))
5568
52a244eb
S
5569
5570;;----- Scanning the library catalogs ------------------
5571
3938cb82
S
5572
5573
5574
52a244eb 5575(defun idlwave-scan-library-catalogs (&optional message-base no-load)
4b1aaa8b 5576 "Scan for library catalog files (.idlwave_catalog) and ingest.
52a244eb
S
5577
5578All directories on `idlwave-path-alist' (or `idlwave-library-path'
5579instead, if present) are searched. Print MESSAGE-BASE along with the
5580libraries being loaded, if passed, and skip loading/normalizing if
5581NO-LOAD is non-nil. The variable `idlwave-use-library-catalogs' can
5582be set to nil to disable library catalog scanning."
5583 (when idlwave-use-library-catalogs
4b1aaa8b 5584 (let ((dirs
52a244eb
S
5585 (if idlwave-library-path
5586 (idlwave-expand-path idlwave-library-path)
5587 (mapcar 'car idlwave-path-alist)))
5588 (old-libname "")
8d222148 5589 dir-entry dir catalog all-routines)
52a244eb
S
5590 (if message-base (message message-base))
5591 (while (setq dir (pop dirs))
5592 (catch 'continue
4b1aaa8b 5593 (when (file-readable-p
52a244eb
S
5594 (setq catalog (expand-file-name ".idlwave_catalog" dir)))
5595 (unless no-load
5596 (setq idlwave-library-catalog-routines nil)
5597 ;; Load the catalog file
5598 (condition-case nil
5599 (load catalog t t t)
5600 (error (throw 'continue t)))
4b1aaa8b
PE
5601 (when (and
5602 message-base
5603 (not (string= idlwave-library-catalog-libname
52a244eb 5604 old-libname)))
4b1aaa8b 5605 (message "%s" (concat message-base
f66f03de 5606 idlwave-library-catalog-libname))
52a244eb
S
5607 (setq old-libname idlwave-library-catalog-libname))
5608 (when idlwave-library-catalog-routines
5609 (setq all-routines
4b1aaa8b 5610 (append
52a244eb
S
5611 (idlwave-sintern-rinfo-list
5612 idlwave-library-catalog-routines 'sys dir)
5613 all-routines))))
4b1aaa8b 5614
52a244eb
S
5615 ;; Add a 'lib flag if on path-alist
5616 (when (and idlwave-path-alist
5617 (setq dir-entry (assoc dir idlwave-path-alist)))
5618 (idlwave-path-alist-add-flag dir-entry 'lib)))))
5619 (unless no-load (setq idlwave-library-catalog-routines all-routines))
5620 (if message-base (message (concat message-base "done"))))))
5621
5622;;----- Communicating with the Shell -------------------
f32b3b91
CD
5623
5624;; First, here is the idl program which can be used to query IDL for
4b1aaa8b 5625;; defined routines.
f32b3b91
CD
5626(defconst idlwave-routine-info.pro
5627 "
05a1abfc 5628;; START OF IDLWAVE SUPPORT ROUTINES
f66f03de
S
5629pro idlwave_print_safe,item,limit
5630 catch,err
5631 if err ne 0 then begin
5632 print,'Could not print item.'
5633 return
5634 endif
5635 if n_elements(item) gt limit then $
5636 print,item[0:limit-1],'<... truncated at ',strtrim(limit,2),' elements>' $
5637 else print,item
5638end
5639
15e42531 5640pro idlwave_print_info_entry,name,func=func,separator=sep
f32b3b91 5641 ;; See if it's an object method
15e42531 5642 if name eq '' then return
4b1aaa8b 5643 func = keyword_set(func)
f32b3b91
CD
5644 methsep = strpos(name,'::')
5645 meth = methsep ne -1
4b1aaa8b 5646
f32b3b91
CD
5647 ;; Get routine info
5648 pars = routine_info(name,/parameters,functions=func)
5649 source = routine_info(name,/source,functions=func)
5650 nargs = pars.num_args
5651 nkw = pars.num_kw_args
5652 if nargs gt 0 then args = pars.args
5653 if nkw gt 0 then kwargs = pars.kw_args
4b1aaa8b 5654
f32b3b91 5655 ;; Trim the class, and make the name
4b1aaa8b 5656 if meth then begin
f32b3b91
CD
5657 class = strmid(name,0,methsep)
5658 name = strmid(name,methsep+2,strlen(name)-1)
4b1aaa8b 5659 if nargs gt 0 then begin
f32b3b91
CD
5660 ;; remove the self argument
5661 wh = where(args ne 'SELF',nargs)
52a244eb 5662 if nargs gt 0 then args = args[wh]
f32b3b91
CD
5663 endif
5664 endif else begin
5665 ;; No class, just a normal routine.
5666 class = \"\"
5667 endelse
4b1aaa8b 5668
f32b3b91
CD
5669 ;; Calling sequence
5670 cs = \"\"
5671 if func then cs = 'Result = '
5672 if meth then cs = cs + 'Obj -> [' + '%s' + '::]'
5673 cs = cs + '%s'
5674 if func then cs = cs + '(' else if nargs gt 0 then cs = cs + ', '
5675 if nargs gt 0 then begin
5676 for j=0,nargs-1 do begin
52a244eb 5677 cs = cs + args[j]
f32b3b91
CD
5678 if j lt nargs-1 then cs = cs + ', '
5679 endfor
5680 end
5681 if func then cs = cs + ')'
5682 ;; Keyword arguments
5683 kwstring = ''
5684 if nkw gt 0 then begin
5685 for j=0,nkw-1 do begin
52a244eb 5686 kwstring = kwstring + ' ' + kwargs[j]
f32b3b91
CD
5687 endfor
5688 endif
4b1aaa8b 5689
52a244eb 5690 ret=(['IDLWAVE-PRO','IDLWAVE-FUN'])[func]
4b1aaa8b 5691
52a244eb 5692 print,ret + ': ' + name + sep + class + sep + source[0].path $
f32b3b91
CD
5693 + sep + cs + sep + kwstring
5694end
5695
f66f03de 5696pro idlwave_routine_info,file
52a244eb 5697 on_error,1
f32b3b91
CD
5698 sep = '<@>'
5699 print,'>>>BEGIN OF IDLWAVE ROUTINE INFO (\"' + sep + '\" IS THE SEPARATOR)'
5700 all = routine_info()
f66f03de
S
5701 fileQ=n_elements(file) ne 0
5702 if fileQ then file=strtrim(file,2)
4b1aaa8b
PE
5703 for i=0L,n_elements(all)-1L do begin
5704 if fileQ then begin
f66f03de
S
5705 if (routine_info(all[i],/SOURCE)).path eq file then $
5706 idlwave_print_info_entry,all[i],separator=sep
5707 endif else idlwave_print_info_entry,all[i],separator=sep
4b1aaa8b 5708 endfor
f32b3b91 5709 all = routine_info(/functions)
4b1aaa8b
PE
5710 for i=0L,n_elements(all)-1L do begin
5711 if fileQ then begin
f66f03de
S
5712 if (routine_info(all[i],/FUNCTIONS,/SOURCE)).path eq file then $
5713 idlwave_print_info_entry,all[i],separator=sep,/FUNC
5714 endif else idlwave_print_info_entry,all[i],separator=sep,/FUNC
4b1aaa8b 5715 endfor
f32b3b91
CD
5716 print,'>>>END OF IDLWAVE ROUTINE INFO'
5717end
05a1abfc
CD
5718
5719pro idlwave_get_sysvars
52a244eb 5720 on_error,1
05a1abfc
CD
5721 catch,error_status
5722 if error_status ne 0 then begin
5723 print, 'Cannot get info about system variables'
5724 endif else begin
5725 help,/brief,output=s,/system_variables ; ? unsafe use of OUTPUT=
5726 s = strtrim(strjoin(s,' ',/single),2) ; make one line
5727 v = strsplit(s,' +',/regex,/extract) ; get variables
f66f03de 5728 for i=0L,n_elements(v)-1 do begin
05a1abfc
CD
5729 t = [''] ; get tag list
5730 a=execute('if n_tags('+v[i]+') gt 0 then t=tag_names('+v[i]+')')
5731 print, 'IDLWAVE-SYSVAR: '+v[i]+' '+strjoin(t,' ',/single)
5732 endfor
5733 endelse
5734end
5735
5736pro idlwave_get_class_tags, class
5737 res = execute('tags=tag_names({'+class+'})')
5e72c6b2 5738 if res then print,'IDLWAVE-CLASS-TAGS: '+class+' '+strjoin(tags,' ',/single)
05a1abfc
CD
5739end
5740;; END OF IDLWAVE SUPPORT ROUTINES
4b1aaa8b 5741"
5a0c3f56 5742 "The IDL programs to get info from the shell.")
f32b3b91 5743
15e42531 5744(defvar idlwave-idlwave_routine_info-compiled nil
5a0c3f56 5745 "Remember if the routine info procedure is already compiled.")
f32b3b91
CD
5746
5747(defvar idlwave-shell-temp-pro-file)
15e42531 5748(defvar idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5749
5750(defun idlwave-shell-compile-helper-routines (&optional wait)
15e42531 5751 (unless (and idlwave-idlwave_routine_info-compiled
5e72c6b2 5752 (file-readable-p (idlwave-shell-temp-file 'rinfo)))
9a529312
SM
5753 (with-current-buffer (idlwave-find-file-noselect
5754 (idlwave-shell-temp-file 'pro))
15e42531
CD
5755 (erase-buffer)
5756 (insert idlwave-routine-info.pro)
5757 (save-buffer 0))
4b1aaa8b 5758 (idlwave-shell-send-command
f66f03de 5759 (concat ".run \"" idlwave-shell-temp-pro-file "\"")
52a244eb 5760 nil 'hide wait)
15e42531 5761 (idlwave-shell-send-command
4b1aaa8b 5762 (format "save,'idlwave_print_safe','idlwave_routine_info','idlwave_print_info_entry','idlwave_get_class_tags','idlwave_get_sysvars',FILE='%s',/ROUTINES"
5e72c6b2 5763 (idlwave-shell-temp-file 'rinfo))
f66f03de
S
5764 nil 'hide)
5765 (setq idlwave-idlwave_routine_info-compiled t))
15e42531 5766
f66f03de 5767 ;; Restore if necessary. Must use execute to hide lame routine_info
cd1181db 5768 ;; errors on undefined routine
15e42531 5769 (idlwave-shell-send-command
f66f03de
S
5770 (format "if execute(\"_v=routine_info('idlwave_routine_info',/SOURCE)\") eq 0 then restore,'%s' else if _v.path eq '' then restore,'%s'"
5771 idlwave-shell-temp-rinfo-save-file
15e42531 5772 idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5773 nil 'hide))
5774
5775
5776(defun idlwave-shell-update-routine-info (&optional quiet run-hooks wait file)
5777 "Query the shell for routine_info of compiled modules and update the lists."
5778 ;; Save and compile the procedure. The compiled procedure is then
5779 ;; saved into an IDL SAVE file, to allow for fast RESTORE. We may
5780 ;; need to test for and possibly RESTORE the procedure each time we
5781 ;; use it, since the user may have killed or redefined it. In
5782 ;; particular, .RESET_SESSION will kill all user procedures. If
5783 ;; FILE is set, only update routine info for routines in that file.
5784
5785 (idlwave-shell-compile-helper-routines wait)
5786 ; execute the routine_info procedure, and analyze the output
5787 (idlwave-shell-send-command
5788 (format "idlwave_routine_info%s" (if file (concat ",'" file "'") ""))
15e42531
CD
5789 `(progn
5790 (idlwave-shell-routine-info-filter)
05a1abfc 5791 (idlwave-concatenate-rinfo-lists ,quiet ,run-hooks))
52a244eb 5792 'hide wait))
f32b3b91
CD
5793
5794;; ---------------------------------------------------------------------------
5795;;
5796;; Completion and displaying routine calling sequences
5797
15e42531 5798(defvar idlwave-completion-help-info nil)
52a244eb 5799(defvar idlwave-completion-help-links nil)
15e42531 5800(defvar idlwave-current-obj_new-class nil)
05a1abfc 5801(defvar idlwave-complete-special nil)
8d222148
SM
5802(defvar method-selector)
5803(defvar class-selector)
5804(defvar type-selector)
5805(defvar super-classes)
15e42531 5806
f32b3b91
CD
5807(defun idlwave-complete (&optional arg module class)
5808 "Complete a function, procedure or keyword name at point.
2e8b9c7d 5809This function is smart and figures out what can be completed
f32b3b91
CD
5810at this point.
5811- At the beginning of a statement it completes procedure names.
5812- In the middle of a statement it completes function names.
5a0c3f56 5813- After a `(' or `,' in the argument list of a function or procedure,
f32b3b91
CD
5814 it completes a keyword of the relevant function or procedure.
5815- In the first arg of `OBJ_NEW', it completes a class name.
5816
5a0c3f56
JB
5817When several completions are possible, a list will be displayed in
5818the *Completions* buffer. If this list is too long to fit into the
5e72c6b2
S
5819window, scrolling can be achieved by repeatedly pressing
5820\\[idlwave-complete].
f32b3b91
CD
5821
5822The function also knows about object methods. When it needs a class
5823name, the action depends upon `idlwave-query-class', which see. You
5e72c6b2
S
5824can force IDLWAVE to ask you for a class name with a
5825\\[universal-argument] prefix argument to this command.
f32b3b91
CD
5826
5827See also the variables `idlwave-keyword-completion-adds-equal' and
5828`idlwave-function-completion-adds-paren'.
5829
5830The optional ARG can be used to specify the completion type in order
5831to override IDLWAVE's idea of what should be completed at point.
5832Possible values are:
5833
58340 <=> query for the completion type
58351 <=> 'procedure
58362 <=> 'procedure-keyword
58373 <=> 'function
58384 <=> 'function-keyword
58395 <=> 'procedure-method
58406 <=> 'procedure-method-keyword
58417 <=> 'function-method
58428 <=> 'function-method-keyword
58439 <=> 'class
5844
5e72c6b2
S
5845As a special case, the universal argument C-u forces completion of
5846function names in places where the default would be a keyword.
5847
52a244eb
S
5848Two prefix argument, C-u C-u, prompts for a regexp by which to limit
5849completion.
5850
f32b3b91
CD
5851For Lisp programmers only:
5852When we force a keyword, optional argument MODULE can contain the module name.
5853When we force a method or a method keyword, CLASS can specify the class."
5854 (interactive "P")
5855 (idlwave-routines)
5856 (let* ((where-list
5857 (if (and arg
52a244eb 5858 (or (and (integerp arg) (not (equal arg '(16))))
f32b3b91
CD
5859 (symbolp arg)))
5860 (idlwave-make-force-complete-where-list arg module class)
5861 (idlwave-where)))
5862 (what (nth 2 where-list))
52a244eb
S
5863 (idlwave-force-class-query (equal arg '(4)))
5864 (completion-regexp-list
5865 (if (equal arg '(16))
5866 (list (read-string (concat "Completion Regexp: "))))))
4b1aaa8b 5867
f32b3b91
CD
5868 (if (and module (string-match "::" module))
5869 (setq class (substring module 0 (match-beginning 0))
5870 module (substring module (match-end 0))))
5871
5872 (cond
5873
5874 ((and (null arg)
5875 (eq (car-safe last-command) 'idlwave-display-completion-list)
595ab50b 5876 (get-buffer-window "*Completions*"))
f32b3b91
CD
5877 (setq this-command last-command)
5878 (idlwave-scroll-completions))
5879
52a244eb 5880 ;; Complete a filename in quotes
05a1abfc
CD
5881 ((and (idlwave-in-quote)
5882 (not (eq what 'class)))
5883 (idlwave-complete-filename))
5884
52a244eb
S
5885 ;; Check for any special completion functions
5886 ((and idlwave-complete-special
5887 (idlwave-call-special idlwave-complete-special)))
4b1aaa8b 5888
f32b3b91
CD
5889 ((null what)
5890 (error "Nothing to complete here"))
5891
52a244eb 5892 ;; Complete a class
f32b3b91 5893 ((eq what 'class)
15e42531 5894 (setq idlwave-completion-help-info '(class))
f32b3b91
CD
5895 (idlwave-complete-class))
5896
5897 ((eq what 'procedure)
5898 ;; Complete a procedure name
5e72c6b2
S
5899 (let* ((cw-list (nth 3 where-list))
5900 (class-selector (idlwave-determine-class cw-list 'pro))
5901 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5902 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5903 (isa (concat "procedure" (if class-selector "-method" "")))
5904 (type-selector 'pro))
4b1aaa8b 5905 (setq idlwave-completion-help-info
05a1abfc 5906 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5907 (idlwave-complete-in-buffer
5908 'procedure (if class-selector 'method 'routine)
5909 (idlwave-routines) 'idlwave-selector
5910 (format "Select a %s name%s"
5911 isa
5912 (if class-selector
4b1aaa8b
PE
5913 (format " (class is %s)"
5914 (if (eq class-selector t)
76959b77 5915 "unknown" class-selector))
f32b3b91
CD
5916 ""))
5917 isa
52a244eb 5918 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91
CD
5919
5920 ((eq what 'function)
5921 ;; Complete a function name
5e72c6b2
S
5922 (let* ((cw-list (nth 3 where-list))
5923 (class-selector (idlwave-determine-class cw-list 'fun))
5924 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5925 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5926 (isa (concat "function" (if class-selector "-method" "")))
5927 (type-selector 'fun))
4b1aaa8b 5928 (setq idlwave-completion-help-info
05a1abfc 5929 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5930 (idlwave-complete-in-buffer
5931 'function (if class-selector 'method 'routine)
5932 (idlwave-routines) 'idlwave-selector
5933 (format "Select a %s name%s"
5934 isa
5935 (if class-selector
4b1aaa8b 5936 (format " (class is %s)"
76959b77
S
5937 (if (eq class-selector t)
5938 "unknown" class-selector))
f32b3b91
CD
5939 ""))
5940 isa
52a244eb 5941 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91 5942
52a244eb 5943 ((and (memq what '(procedure-keyword function-keyword)) ; Special Case
5e72c6b2
S
5944 (equal arg '(4)))
5945 (idlwave-complete 3))
5946
f32b3b91
CD
5947 ((eq what 'procedure-keyword)
5948 ;; Complete a procedure keyword
5949 (let* ((where (nth 3 where-list))
5950 (name (car where))
5951 (method-selector name)
5952 (type-selector 'pro)
5953 (class (idlwave-determine-class where 'pro))
5954 (class-selector class)
05a1abfc 5955 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 5956 (isa (format "procedure%s-keyword" (if class "-method" "")))
15e42531 5957 (entry (idlwave-best-rinfo-assq
f32b3b91 5958 name 'pro class (idlwave-routines)))
3938cb82 5959 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 5960 (list (idlwave-entry-keywords entry 'do-link)))
f32b3b91
CD
5961 (unless (or entry (eq class t))
5962 (error "Nothing known about procedure %s"
5963 (idlwave-make-full-name class name)))
4b1aaa8b 5964 (setq list (idlwave-fix-keywords name 'pro class list
3938cb82 5965 super-classes system))
b6a97790 5966 (unless list (error "No keywords available for procedure %s"
3938cb82 5967 (idlwave-make-full-name class name)))
4b1aaa8b 5968 (setq idlwave-completion-help-info
52a244eb 5969 (list 'keyword name type-selector class-selector entry super-classes))
f32b3b91
CD
5970 (idlwave-complete-in-buffer
5971 'keyword 'keyword list nil
5972 (format "Select keyword for procedure %s%s"
5973 (idlwave-make-full-name class name)
15e42531 5974 (if (or (member '("_EXTRA") list)
4b1aaa8b 5975 (member '("_REF_EXTRA") list))
15e42531 5976 " (note _EXTRA)" ""))
f32b3b91
CD
5977 isa
5978 'idlwave-attach-keyword-classes)))
5979
5980 ((eq what 'function-keyword)
5981 ;; Complete a function keyword
5982 (let* ((where (nth 3 where-list))
5983 (name (car where))
5984 (method-selector name)
5985 (type-selector 'fun)
5986 (class (idlwave-determine-class where 'fun))
5987 (class-selector class)
05a1abfc 5988 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 5989 (isa (format "function%s-keyword" (if class "-method" "")))
15e42531 5990 (entry (idlwave-best-rinfo-assq
f32b3b91 5991 name 'fun class (idlwave-routines)))
3938cb82 5992 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 5993 (list (idlwave-entry-keywords entry 'do-link))
15e42531 5994 msg-name)
f32b3b91
CD
5995 (unless (or entry (eq class t))
5996 (error "Nothing known about function %s"
5997 (idlwave-make-full-name class name)))
4b1aaa8b 5998 (setq list (idlwave-fix-keywords name 'fun class list
3938cb82 5999 super-classes system))
15e42531
CD
6000 ;; OBJ_NEW: Messages mention the proper Init method
6001 (setq msg-name (if (and (null class)
6002 (string= (upcase name) "OBJ_NEW"))
6003 (concat idlwave-current-obj_new-class
6004 "::Init (via OBJ_NEW)")
6005 (idlwave-make-full-name class name)))
b6a97790 6006 (unless list (error "No keywords available for function %s"
3938cb82 6007 msg-name))
4b1aaa8b 6008 (setq idlwave-completion-help-info
05a1abfc 6009 (list 'keyword name type-selector class-selector nil super-classes))
f32b3b91
CD
6010 (idlwave-complete-in-buffer
6011 'keyword 'keyword list nil
15e42531
CD
6012 (format "Select keyword for function %s%s" msg-name
6013 (if (or (member '("_EXTRA") list)
4b1aaa8b 6014 (member '("_REF_EXTRA") list))
15e42531 6015 " (note _EXTRA)" ""))
f32b3b91
CD
6016 isa
6017 'idlwave-attach-keyword-classes)))
15e42531 6018
f32b3b91
CD
6019 (t (error "This should not happen (idlwave-complete)")))))
6020
05a1abfc
CD
6021(defvar idlwave-complete-special nil
6022 "List of special completion functions.
52a244eb
S
6023These functions are called for each completion. Each function must
6024check if its own special completion context is present. If yes, it
6025should use `idlwave-complete-in-buffer' to do some completion and
6026return t. If such a function returns t, *no further* attempts to
6027complete other contexts will be done. If the function returns nil,
6028other completions will be tried.")
76959b77
S
6029
6030(defun idlwave-call-special (functions &rest args)
6031 (let ((funcs functions)
6032 fun ret)
05a1abfc 6033 (catch 'exit
76959b77
S
6034 (while (setq fun (pop funcs))
6035 (if (setq ret (apply fun args))
6036 (throw 'exit ret)))
05a1abfc
CD
6037 nil)))
6038
f32b3b91
CD
6039(defun idlwave-make-force-complete-where-list (what &optional module class)
6040 ;; Return an artificial WHERE specification to force the completion
6041 ;; routine to complete a specific item independent of context.
6042 ;; WHAT is the prefix arg of `idlwave-complete', see there for details.
6043 ;; MODULE and CLASS can be used to specify the routine name and class.
6044 ;; The class name will also be found in MODULE if that is like "class::mod".
6045 (let* ((what-list '(("procedure") ("procedure-keyword")
6046 ("function") ("function-keyword")
6047 ("procedure-method") ("procedure-method-keyword")
6048 ("function-method") ("function-method-keyword")
6049 ("class")))
6050 (module (idlwave-sintern-routine-or-method module class))
6051 (class (idlwave-sintern-class class))
4b1aaa8b 6052 (what (cond
f32b3b91
CD
6053 ((equal what 0)
6054 (setq what
4b1aaa8b 6055 (intern (completing-read
f32b3b91
CD
6056 "Complete what? " what-list nil t))))
6057 ((integerp what)
6058 (setq what (intern (car (nth (1- what) what-list)))))
6059 ((and what
6060 (symbolp what)
6061 (assoc (symbol-name what) what-list))
6062 what)
eac9c0ef 6063 (t (error "Invalid WHAT"))))
f32b3b91
CD
6064 (nil-list '(nil nil nil nil))
6065 (class-list (list nil nil (or class t) nil)))
6066
6067 (cond
6068
6069 ((eq what 'procedure)
6070 (list nil-list nil-list 'procedure nil-list nil))
6071
6072 ((eq what 'procedure-keyword)
6073 (let* ((class-selector nil)
05a1abfc 6074 (super-classes nil)
f32b3b91
CD
6075 (type-selector 'pro)
6076 (pro (or module
4b1aaa8b 6077 (idlwave-completing-read
f32b3b91
CD
6078 "Procedure: " (idlwave-routines) 'idlwave-selector))))
6079 (setq pro (idlwave-sintern-routine pro))
6080 (list nil-list nil-list 'procedure-keyword
6081 (list pro nil nil nil) nil)))
6082
6083 ((eq what 'function)
6084 (list nil-list nil-list 'function nil-list nil))
6085
6086 ((eq what 'function-keyword)
6087 (let* ((class-selector nil)
05a1abfc 6088 (super-classes nil)
f32b3b91
CD
6089 (type-selector 'fun)
6090 (func (or module
4b1aaa8b 6091 (idlwave-completing-read
f32b3b91
CD
6092 "Function: " (idlwave-routines) 'idlwave-selector))))
6093 (setq func (idlwave-sintern-routine func))
6094 (list nil-list nil-list 'function-keyword
6095 (list func nil nil nil) nil)))
6096
6097 ((eq what 'procedure-method)
6098 (list nil-list nil-list 'procedure class-list nil))
6099
6100 ((eq what 'procedure-method-keyword)
6101 (let* ((class (idlwave-determine-class class-list 'pro))
6102 (class-selector class)
05a1abfc 6103 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6104 (type-selector 'pro)
6105 (pro (or module
6106 (idlwave-completing-read
6107 (format "Procedure in %s class: " class-selector)
6108 (idlwave-routines) 'idlwave-selector))))
6109 (setq pro (idlwave-sintern-method pro))
6110 (list nil-list nil-list 'procedure-keyword
6111 (list pro nil class nil) nil)))
6112
6113 ((eq what 'function-method)
6114 (list nil-list nil-list 'function class-list nil))
6115
6116 ((eq what 'function-method-keyword)
6117 (let* ((class (idlwave-determine-class class-list 'fun))
6118 (class-selector class)
05a1abfc 6119 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6120 (type-selector 'fun)
6121 (func (or module
6122 (idlwave-completing-read
6123 (format "Function in %s class: " class-selector)
6124 (idlwave-routines) 'idlwave-selector))))
6125 (setq func (idlwave-sintern-method func))
6126 (list nil-list nil-list 'function-keyword
6127 (list func nil class nil) nil)))
6128
6129 ((eq what 'class)
6130 (list nil-list nil-list 'class nil-list nil))
4b1aaa8b 6131
eac9c0ef 6132 (t (error "Invalid value for WHAT")))))
f32b3b91
CD
6133
6134(defun idlwave-completing-read (&rest args)
6135 ;; Completing read, case insensitive
6136 (let ((old-value (default-value 'completion-ignore-case)))
6137 (unwind-protect
6138 (progn
6139 (setq-default completion-ignore-case t)
6140 (apply 'completing-read args))
6141 (setq-default completion-ignore-case old-value))))
6142
05a1abfc
CD
6143(defvar idlwave-shell-default-directory)
6144(defun idlwave-complete-filename ()
6145 "Use the comint stuff to complete a file name."
6146 (require 'comint)
6147 (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-")
6148 (comint-completion-addsuffix nil)
6149 (default-directory
6150 (if (and (boundp 'idlwave-shell-default-directory)
6151 (stringp idlwave-shell-default-directory)
6152 (file-directory-p idlwave-shell-default-directory))
6153 idlwave-shell-default-directory
4b1aaa8b 6154 default-directory)))
05a1abfc
CD
6155 (comint-dynamic-complete-filename)))
6156
f32b3b91
CD
6157(defun idlwave-make-full-name (class name)
6158 ;; Make a fully qualified module name including the class name
6159 (concat (if class (format "%s::" class) "") name))
6160
15e42531
CD
6161(defun idlwave-rinfo-assoc (name type class list)
6162 "Like `idlwave-rinfo-assq', but sintern strings first."
4b1aaa8b 6163 (idlwave-rinfo-assq
15e42531
CD
6164 (idlwave-sintern-routine-or-method name class)
6165 type (idlwave-sintern-class class) list))
6166
f32b3b91
CD
6167(defun idlwave-rinfo-assq (name type class list)
6168 ;; Works like assq, but also checks type and class
6169 (catch 'exit
6170 (let (match)
6171 (while (setq match (assq name list))
6172 (and (or (eq type t)
6173 (eq (nth 1 match) type))
6174 (eq (nth 2 match) class)
6175 (throw 'exit match))
6176 (setq list (cdr (memq match list)))))))
6177
05a1abfc 6178(defun idlwave-rinfo-assq-any-class (name type class list)
52a244eb 6179 ;; Return the first matching method on the inheritance list
05a1abfc
CD
6180 (let* ((classes (cons class (idlwave-all-class-inherits class)))
6181 class rtn)
6182 (while classes
6183 (if (setq rtn (idlwave-rinfo-assq name type (pop classes) list))
6184 (setq classes nil)))
6185 rtn))
6186
4b1aaa8b 6187(defun idlwave-best-rinfo-assq (name type class list &optional with-file
52a244eb
S
6188 keep-system)
6189 "Like `idlwave-rinfo-assq', but get all twins and sort, then return first.
6190If WITH-FILE is passed, find the best rinfo entry with a file
6191included. If KEEP-SYSTEM is set, don't prune system for compiled
6192syslib files."
15e42531 6193 (let ((twins (idlwave-routine-twins
05a1abfc 6194 (idlwave-rinfo-assq-any-class name type class list)
15e42531
CD
6195 list))
6196 syslibp)
6197 (when (> (length twins) 1)
6198 (setq twins (sort twins 'idlwave-routine-entry-compare-twins))
52a244eb
S
6199 (if (and (null keep-system)
6200 (eq 'system (car (nth 3 (car twins))))
15e42531
CD
6201 (setq syslibp (idlwave-any-syslib (cdr twins)))
6202 (not (equal 1 syslibp)))
52a244eb
S
6203 ;; Its a compiled syslib, so we need to remove the system entry
6204 (setq twins (cdr twins)))
6205 (if with-file
6206 (setq twins (delq nil
6207 (mapcar (lambda (x)
6208 (if (nth 1 (nth 3 x)) x))
6209 twins)))))
15e42531
CD
6210 (car twins)))
6211
4b1aaa8b 6212(defun idlwave-best-rinfo-assoc (name type class list &optional with-file
52a244eb 6213 keep-system)
15e42531
CD
6214 "Like `idlwave-best-rinfo-assq', but sintern strings first."
6215 (idlwave-best-rinfo-assq
6216 (idlwave-sintern-routine-or-method name class)
52a244eb 6217 type (idlwave-sintern-class class) list with-file keep-system))
15e42531
CD
6218
6219(defun idlwave-any-syslib (entries)
6220 "Does the entry list ENTRIES contain a syslib entry?
6221If yes, return the index (>=1)."
6222 (let (file (cnt 0))
6223 (catch 'exit
6224 (while entries
6225 (incf cnt)
52a244eb
S
6226 (setq file (idlwave-routine-source-file (nth 3 (car entries))))
6227 (if (and file (idlwave-syslib-p file))
15e42531
CD
6228 (throw 'exit cnt)
6229 (setq entries (cdr entries))))
6230 nil)))
6231
f32b3b91
CD
6232(defun idlwave-all-assq (key list)
6233 "Return a list of all associations of Key in LIST."
6234 (let (rtn elt)
6235 (while (setq elt (assq key list))
6236 (push elt rtn)
6237 (setq list (cdr (memq elt list))))
6238 (nreverse rtn)))
6239
6240(defun idlwave-all-method-classes (method &optional type)
5a0c3f56
JB
6241 "Return all classes which have a method METHOD.
6242TYPE is 'fun or 'pro.
f32b3b91
CD
6243When TYPE is not specified, both procedures and functions will be considered."
6244 (if (null method)
15e42531 6245 (mapcar 'car (idlwave-class-alist))
f32b3b91 6246 (let (rtn)
8ffcfb27
GM
6247 (mapc (lambda (x)
6248 (and (nth 2 x)
6249 (or (not type)
6250 (eq type (nth 1 x)))
6251 (push (nth 2 x) rtn)))
6252 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6253 (idlwave-uniquify rtn))))
6254
6255(defun idlwave-all-method-keyword-classes (method keyword &optional type)
6256 "Return all classes which have a method METHOD with keyword KEYWORD.
6257TYPE is 'fun or 'pro.
6258When TYPE is not specified, both procedures and functions will be considered."
6259 (if (or (null method)
6260 (null keyword))
6261 nil
6262 (let (rtn)
8ffcfb27
GM
6263 (mapc (lambda (x)
6264 (and (nth 2 x) ; non-nil class
6265 (or (not type) ; correct or unspecified type
6266 (eq type (nth 1 x)))
6267 (assoc keyword (idlwave-entry-keywords x))
6268 (push (nth 2 x) rtn)))
6269 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6270 (idlwave-uniquify rtn))))
6271
05a1abfc
CD
6272(defun idlwave-members-only (list club)
6273 "Return list of all elements in LIST which are also in CLUB."
6274 (let (rtn)
6275 (while list
6276 (if (member (car list) club)
6277 (setq rtn (cons (car list) rtn)))
6278 (setq list (cdr list)))
6279 (nreverse rtn)))
6280
6281(defun idlwave-nonmembers-only (list club)
6282 "Return list of all elements in LIST which are not in CLUB."
6283 (let (rtn)
6284 (while list
6285 (if (member (car list) club)
6286 nil
6287 (setq rtn (cons (car list) rtn)))
6288 (setq list (cdr list)))
6289 (nreverse rtn)))
6290
5e72c6b2
S
6291(defun idlwave-explicit-class-listed (info)
6292 "Return whether or not the class is listed explicitly, ala a->b::c.
5a0c3f56 6293INFO is as returned by `idlwave-what-function' or `-procedure'."
5e72c6b2
S
6294 (let ((apos (nth 3 info)))
6295 (if apos
6296 (save-excursion (goto-char apos)
6297 (looking-at "->[a-zA-Z][a-zA-Z0-9$_]*::")))))
6298
76959b77
S
6299(defvar idlwave-determine-class-special nil
6300 "List of special functions for determining class.
5a0c3f56 6301Must accept two arguments: `apos' and `info'.")
76959b77 6302
f32b3b91 6303(defun idlwave-determine-class (info type)
4b1aaa8b 6304 ;; Determine the class of a routine call.
76959b77
S
6305 ;; INFO is the `cw-list' structure as returned by idlwave-where.
6306 ;; The second element in this structure is the class. When nil, we
6307 ;; return nil. When t, try to get the class from text properties at
6308 ;; the arrow. When the object is "self", we use the class of the
6309 ;; current routine. otherwise prompt the user for a class name.
6310 ;; Also stores the selected class as a text property at the arrow.
f32b3b91
CD
6311 ;; TYPE is 'fun or 'pro.
6312 (let* ((class (nth 2 info))
6313 (apos (nth 3 info))
6314 (nassoc (assoc (if (stringp (car info))
6315 (upcase (car info))
6316 (car info))
6317 idlwave-query-class))
6318 (dassoc (assq (if (car info) 'keyword-default 'method-default)
6319 idlwave-query-class))
6320 (query (cond (nassoc (cdr nassoc))
6321 (dassoc (cdr dassoc))
6322 (t t)))
6323 (arrow (and apos (string= (buffer-substring apos (+ 2 apos)) "->")))
4b1aaa8b 6324 (is-self
15e42531
CD
6325 (and arrow
6326 (save-excursion (goto-char apos)
6327 (forward-word -1)
6328 (let ((case-fold-search t))
6329 (looking-at "self\\>")))))
f32b3b91 6330 (force-query idlwave-force-class-query)
76959b77 6331 store special-class class-alist)
f32b3b91
CD
6332 (cond
6333 ((null class) nil)
6334 ((eq t class)
6335 ;; There is an object which would like to know its class
6336 (if (and arrow (get-text-property apos 'idlwave-class)
6337 idlwave-store-inquired-class
6338 (not force-query))
6339 (setq class (get-text-property apos 'idlwave-class)
6340 class (idlwave-sintern-class class)))
76959b77
S
6341 (if (and (eq t class) is-self)
6342 (setq class (or (nth 2 (idlwave-current-routine)) class)))
6343
6344 ;; Before prompting, try any special class determination routines
4b1aaa8b 6345 (when (and (eq t class)
76959b77
S
6346 idlwave-determine-class-special
6347 (not force-query))
4b1aaa8b 6348 (setq special-class
76959b77 6349 (idlwave-call-special idlwave-determine-class-special apos))
4b1aaa8b 6350 (if special-class
76959b77
S
6351 (setq class (idlwave-sintern-class special-class)
6352 store idlwave-store-inquired-class)))
4b1aaa8b 6353
76959b77 6354 ;; Prompt for a class, if we need to
f32b3b91
CD
6355 (when (and (eq class t)
6356 (or force-query query))
4b1aaa8b 6357 (setq class-alist
f32b3b91
CD
6358 (mapcar 'list (idlwave-all-method-classes (car info) type)))
6359 (setq class
6360 (idlwave-sintern-class
6361 (cond
6362 ((and (= (length class-alist) 0) (not force-query))
6363 (error "No classes available with method %s" (car info)))
6364 ((and (= (length class-alist) 1) (not force-query))
6365 (car (car class-alist)))
4b1aaa8b 6366 (t
f32b3b91 6367 (setq store idlwave-store-inquired-class)
4b1aaa8b 6368 (idlwave-completing-read
f32b3b91
CD
6369 (format "Class%s: " (if (stringp (car info))
6370 (format " for %s method %s"
6371 type (car info))
6372 ""))
6373 class-alist nil nil nil 'idlwave-class-history))))))
76959b77
S
6374
6375 ;; Store it, if requested
f32b3b91
CD
6376 (when (and class (not (eq t class)))
6377 ;; We have a real class here
6378 (when (and store arrow)
76959b77 6379 (condition-case ()
4b1aaa8b
PE
6380 (add-text-properties
6381 apos (+ apos 2)
6382 `(idlwave-class ,class face ,idlwave-class-arrow-face
76959b77
S
6383 rear-nonsticky t))
6384 (error nil)))
f32b3b91
CD
6385 (setf (nth 2 info) class))
6386 ;; Return the class
6387 class)
6388 ;; Default as fallback
6389 (t class))))
6390
f32b3b91
CD
6391(defun idlwave-selector (a)
6392 (and (eq (nth 1 a) type-selector)
6393 (or (and (nth 2 a) (eq class-selector t))
05a1abfc 6394 (eq (nth 2 a) class-selector)
52a244eb
S
6395 (memq (nth 2 a) super-classes))))
6396
6397(defun idlwave-add-file-link-selector (a)
6398 ;; Record a file link, if any, for the tested names during selection.
6399 (let ((sel (idlwave-selector a)) file)
6400 (if (and sel (setq file (idlwave-entry-has-help a)))
6401 (push (cons (car a) file) idlwave-completion-help-links))
6402 sel))
6403
f32b3b91
CD
6404
6405(defun idlwave-where ()
4b1aaa8b 6406 "Find out where we are.
f32b3b91 6407The return value is a list with the following stuff:
5e72c6b2 6408\(PRO-LIST FUNC-LIST COMPLETE-WHAT CW-LIST LAST-CHAR)
f32b3b91
CD
6409
6410PRO-LIST (PRO POINT CLASS ARROW)
6411FUNC-LIST (FUNC POINT CLASS ARROW)
6412COMPLETE-WHAT a symbol indicating what kind of completion makes sense here
4b1aaa8b 6413CW-LIST (PRO-OR-FUNC POINT CLASS ARROW) Like PRO-LIST, for what can
5e72c6b2 6414 be completed here.
f32b3b91
CD
6415LAST-CHAR last relevant character before point (non-white non-comment,
6416 not part of current identifier or leading slash).
6417
6418In the lists, we have these meanings:
6419PRO: Procedure name
6420FUNC: Function name
6421POINT: Where is this
6422CLASS: What class has the routine (nil=no, t=is method, but class unknown)
5e72c6b2 6423ARROW: Location of the arrow"
f32b3b91 6424 (idlwave-routines)
4b1aaa8b 6425 (let* (;(bos (save-excursion (idlwave-beginning-of-statement) (point)))
15e42531 6426 (bos (save-excursion (idlwave-start-of-substatement 'pre) (point)))
f32b3b91
CD
6427 (func-entry (idlwave-what-function bos))
6428 (func (car func-entry))
6429 (func-class (nth 1 func-entry))
6430 (func-arrow (nth 2 func-entry))
6431 (func-point (or (nth 3 func-entry) 0))
6432 (func-level (or (nth 4 func-entry) 0))
6433 (pro-entry (idlwave-what-procedure bos))
6434 (pro (car pro-entry))
6435 (pro-class (nth 1 pro-entry))
6436 (pro-arrow (nth 2 pro-entry))
6437 (pro-point (or (nth 3 pro-entry) 0))
6438 (last-char (idlwave-last-valid-char))
6439 (case-fold-search t)
52a244eb 6440 (match-string (buffer-substring bos (point)))
f32b3b91
CD
6441 cw cw-mod cw-arrow cw-class cw-point)
6442 (if (< func-point pro-point) (setq func nil))
6443 (cond
15e42531 6444 ((string-match "\\`[ \t]*\\(pro\\|function\\)[ \t]+[a-zA-Z0-9_]*\\'"
52a244eb 6445 match-string)
15e42531 6446 (setq cw 'class))
4b1aaa8b
PE
6447 ((string-match
6448 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'"
52a244eb
S
6449 (if (> pro-point 0)
6450 (buffer-substring pro-point (point))
6451 match-string))
f32b3b91
CD
6452 (setq cw 'procedure cw-class pro-class cw-point pro-point
6453 cw-arrow pro-arrow))
6454 ((string-match "\\`[ \t]*\\(pro\\|function\\)\\>"
52a244eb 6455 match-string)
f32b3b91 6456 nil)
05a1abfc 6457 ((string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6458 match-string)
4b1aaa8b 6459 (setq cw 'class))
05a1abfc 6460 ((string-match "\\<inherits\\s-+\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6461 match-string)
4b1aaa8b
PE
6462 (setq cw 'class))
6463 ((and func
f32b3b91
CD
6464 (> func-point pro-point)
6465 (= func-level 1)
6466 (memq last-char '(?\( ?,)))
6467 (setq cw 'function-keyword cw-mod func cw-point func-point
6468 cw-class func-class cw-arrow func-arrow))
6469 ((and pro (eq last-char ?,))
6470 (setq cw 'procedure-keyword cw-mod pro cw-point pro-point
6471 cw-class pro-class cw-arrow pro-arrow))
6472; ((member last-char '(?\' ?\) ?\] ?!))
6473; ;; after these chars, a function makes no sense
6474; ;; FIXME: I am sure there can be more in this list
6475; ;; FIXME: Do we want to do this at all?
6476; nil)
6477 ;; Everywhere else we try a function.
6478 (t
6479 (setq cw 'function)
6480 (save-excursion
52a244eb 6481 (if (re-search-backward "->[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\s-*\\)?\\(\\([$a-zA-Z0-9_]+\\)::\\)?[$a-zA-Z0-9_]*\\=" bos t)
76959b77 6482 (setq cw-arrow (copy-marker (match-beginning 0))
52a244eb
S
6483 cw-class (if (match-end 4)
6484 (idlwave-sintern-class (match-string 4))
5e72c6b2 6485 t))))))
f32b3b91
CD
6486 (list (list pro pro-point pro-class pro-arrow)
6487 (list func func-point func-class func-arrow)
6488 cw
6489 (list cw-mod cw-point cw-class cw-arrow)
6490 last-char)))
6491
6492(defun idlwave-this-word (&optional class)
6493 ;; Grab the word around point. CLASS is for the `skip-chars=...' functions
52a244eb 6494 (setq class (or class "a-zA-Z0-9$_."))
f32b3b91 6495 (save-excursion
52a244eb 6496 (buffer-substring
f32b3b91
CD
6497 (progn (skip-chars-backward class) (point))
6498 (progn (skip-chars-forward class) (point)))))
6499
f32b3b91
CD
6500(defun idlwave-what-function (&optional bound)
6501 ;; Find out if point is within the argument list of a function.
76959b77
S
6502 ;; The return value is ("function-name" class arrow-start (point) level).
6503 ;; Level is 1 on the top level parentheses, higher further down.
f32b3b91
CD
6504
6505 ;; If the optional BOUND is an integer, bound backwards directed
6506 ;; searches to this point.
6507
6508 (catch 'exit
4b1aaa8b 6509 (let (pos
f32b3b91 6510 func-point
f32b3b91
CD
6511 (cnt 0)
6512 func arrow-start class)
15e42531
CD
6513 (idlwave-with-special-syntax
6514 (save-restriction
6515 (save-excursion
6516 (narrow-to-region (max 1 (or bound 0)) (point-max))
6517 ;; move back out of the current parenthesis
6518 (while (condition-case nil
6519 (progn (up-list -1) t)
6520 (error nil))
6521 (setq pos (point))
6522 (incf cnt)
6523 (when (and (= (following-char) ?\()
4b1aaa8b 6524 (re-search-backward
15e42531
CD
6525 "\\(::\\|\\<\\)\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\="
6526 bound t))
6527 (setq func (match-string 2)
6528 func-point (goto-char (match-beginning 2))
6529 pos func-point)
4b1aaa8b 6530 (if (re-search-backward
15e42531 6531 "->[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\=" bound t)
76959b77 6532 (setq arrow-start (copy-marker (match-beginning 0))
15e42531 6533 class (or (match-string 2) t)))
4b1aaa8b
PE
6534 (throw
6535 'exit
15e42531
CD
6536 (list
6537 (idlwave-sintern-routine-or-method func class)
6538 (idlwave-sintern-class class)
6539 arrow-start func-point cnt)))
6540 (goto-char pos))
6541 (throw 'exit nil)))))))
f32b3b91
CD
6542
6543(defun idlwave-what-procedure (&optional bound)
6544 ;; Find out if point is within the argument list of a procedure.
6545 ;; The return value is ("procedure-name" class arrow-pos (point)).
6546
6547 ;; If the optional BOUND is an integer, bound backwards directed
6548 ;; searches to this point.
6549 (let ((pos (point)) pro-point
6550 pro class arrow-start string)
4b1aaa8b 6551 (save-excursion
05a1abfc 6552 ;;(idlwave-beginning-of-statement)
15e42531 6553 (idlwave-start-of-substatement 'pre)
f32b3b91 6554 (setq string (buffer-substring (point) pos))
4b1aaa8b 6555 (if (string-match
76959b77
S
6556 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\(,\\|\\'\\)" string)
6557 (setq pro (match-string 1 string)
6558 pro-point (+ (point) (match-beginning 1)))
f32b3b91
CD
6559 (if (and (idlwave-skip-object)
6560 (setq string (buffer-substring (point) pos))
4b1aaa8b
PE
6561 (string-match
6562 "\\`[ \t]*\\(->\\)[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\([a-zA-Z][a-zA-Z0-9$_]*\\)?[ \t]*\\(,\\|\\(\\$\\s *\\(;.*\\)?\\)?$\\)"
52a244eb 6563 string))
f32b3b91
CD
6564 (setq pro (if (match-beginning 4)
6565 (match-string 4 string))
6566 pro-point (if (match-beginning 4)
6567 (+ (point) (match-beginning 4))
6568 pos)
76959b77 6569 arrow-start (copy-marker (+ (point) (match-beginning 1)))
f32b3b91
CD
6570 class (or (match-string 3 string) t)))))
6571 (list (idlwave-sintern-routine-or-method pro class)
6572 (idlwave-sintern-class class)
6573 arrow-start
6574 pro-point)))
6575
6576(defun idlwave-skip-object ()
6577 ;; If there is an object at point, move over it and return t.
6578 (let ((pos (point)))
6579 (if (catch 'exit
6580 (save-excursion
6581 (skip-chars-forward " ") ; white space
6582 (skip-chars-forward "*") ; de-reference
6583 (cond
6584 ((looking-at idlwave-identifier)
6585 (goto-char (match-end 0)))
6586 ((eq (following-char) ?\()
6587 nil)
6588 (t (throw 'exit nil)))
6589 (catch 'endwhile
6590 (while t
6591 (cond ((eq (following-char) ?.)
6592 (forward-char 1)
6593 (if (not (looking-at idlwave-identifier))
6594 (throw 'exit nil))
6595 (goto-char (match-end 0)))
6596 ((memq (following-char) '(?\( ?\[))
6597 (condition-case nil
6598 (forward-list 1)
6599 (error (throw 'exit nil))))
6600 (t (throw 'endwhile t)))))
6601 (if (looking-at "[ \t]*->")
6602 (throw 'exit (setq pos (match-beginning 0)))
6603 (throw 'exit nil))))
6604 (goto-char pos)
6605 nil)))
4b1aaa8b 6606
f32b3b91
CD
6607(defun idlwave-last-valid-char ()
6608 "Return the last character before point which is not white or a comment
6609and also not part of the current identifier. Since we do this in
6610order to identify places where keywords are, we consider the initial
6611`/' of a keyword as part of the identifier.
6612This function is not general, can only be used for completion stuff."
6613 (catch 'exit
6614 (save-excursion
6615 ;; skip the current identifier
6616 (skip-chars-backward "a-zA-Z0-9_$")
6617 ;; also skip a leading slash which might be belong to the keyword
6618 (if (eq (preceding-char) ?/)
6619 (backward-char 1))
6620 ;; FIXME: does not check if this is a valid identifier
6621 (while t
6622 (skip-chars-backward " \t")
6623 (cond
6624 ((memq (preceding-char) '(?\; ?\$)) (throw 'exit nil))
6625 ((eq (preceding-char) ?\n)
6626 (beginning-of-line 0)
3938cb82 6627 (if (looking-at "\\([^\n]*\\)\\$[ \t]*\\(;[^\n]*\\)?\n")
f32b3b91
CD
6628 ;; continuation line
6629 (goto-char (match-end 1))
6630 (throw 'exit nil)))
6631 (t (throw 'exit (preceding-char))))))))
6632
6633(defvar idlwave-complete-after-success-form nil
6634 "A form to evaluate after successful completion.")
6635(defvar idlwave-complete-after-success-form-force nil
6636 "A form to evaluate after completion selection in *Completions* buffer.")
6637(defconst idlwave-completion-mark (make-marker)
6638 "A mark pointing to the beginning of the completion string.")
8d222148 6639(defvar completion-highlight-first-word-only) ;XEmacs.
f32b3b91
CD
6640
6641(defun idlwave-complete-in-buffer (type stype list selector prompt isa
52a244eb
S
6642 &optional prepare-display-function
6643 special-selector)
f32b3b91 6644 "Perform TYPE completion of word before point against LIST.
76959b77 6645SELECTOR is the PREDICATE argument for the completion function. Show
52a244eb 6646PROMPT in echo area. TYPE is one of the intern types, e.g. 'function,
5a0c3f56 6647'procedure, 'class-tag, 'keyword, 'sysvar, etc. SPECIAL-SELECTOR is
52a244eb
S
6648used only once, for `all-completions', and can be used to, e.g.,
6649accumulate information on matching completions."
f32b3b91
CD
6650 (let* ((completion-ignore-case t)
6651 beg (end (point)) slash part spart completion all-completions
6652 dpart dcompletion)
6653
6654 (unless list
6655 (error (concat prompt ": No completions available")))
6656
6657 ;; What is already in the buffer?
6658 (save-excursion
6659 (skip-chars-backward "a-zA-Z0-9_$")
6660 (setq slash (eq (preceding-char) ?/)
6661 beg (point)
6662 idlwave-complete-after-success-form
6663 (list 'idlwave-after-successful-completion
6664 (list 'quote type) slash beg)
6665 idlwave-complete-after-success-form-force
6666 (list 'idlwave-after-successful-completion
6667 (list 'quote type) slash (list 'quote 'force))))
6668
6669 ;; Try a completion
6670 (setq part (buffer-substring beg end)
6671 dpart (downcase part)
6672 spart (idlwave-sintern stype part)
6673 completion (try-completion part list selector)
52a244eb
S
6674 dcompletion (if (stringp completion) (downcase completion))
6675 idlwave-completion-help-links nil)
f32b3b91
CD
6676 (cond
6677 ((null completion)
6678 ;; nothing available.
76959b77 6679 (error (concat prompt ": no completion for \"%s\"") part))
f32b3b91
CD
6680 ((and (not (equal dpart dcompletion))
6681 (not (eq t completion)))
6682 ;; We can add something
6683 (delete-region beg end)
8d222148
SM
6684 (insert (if (and (string= part dpart)
6685 (or (not (string= part ""))
6686 idlwave-complete-empty-string-as-lower-case)
6687 (not idlwave-completion-force-default-case))
6688 dcompletion
6689 completion))
f32b3b91
CD
6690 (if (eq t (try-completion completion list selector))
6691 ;; Now this is a unique match
6692 (idlwave-after-successful-completion type slash beg))
6693 t)
6694 ((or (eq completion t)
52a244eb 6695 (and (= 1 (length (setq all-completions
f32b3b91 6696 (idlwave-uniquify
4b1aaa8b
PE
6697 (all-completions part list
6698 (or special-selector
52a244eb
S
6699 selector))))))
6700 (equal dpart dcompletion)))
f32b3b91
CD
6701 ;; This is already complete
6702 (idlwave-after-successful-completion type slash beg)
6703 (message "%s is already the complete %s" part isa)
6704 nil)
4b1aaa8b 6705 (t
f32b3b91
CD
6706 ;; We cannot add something - offer a list.
6707 (message "Making completion list...")
4b1aaa8b 6708
52a244eb 6709 (unless idlwave-completion-help-links ; already set somewhere?
9001c33f
GM
6710 (mapc (lambda (x) ; Pass link prop through to highlight-linked
6711 (let ((link (get-text-property 0 'link (car x))))
6712 (if link
6713 (push (cons (car x) link)
6714 idlwave-completion-help-links))))
6715 list))
f32b3b91 6716 (let* ((list all-completions)
05a1abfc 6717 ;; "complete" means, this is already a valid completion
f32b3b91 6718 (complete (memq spart all-completions))
52a244eb 6719 (completion-highlight-first-word-only t)) ; XEmacs
8d222148
SM
6720 ;; (completion-fixup-function ; Emacs
6721 ;; (lambda () (and (eq (preceding-char) ?>)
6722 ;; (re-search-backward " <" beg t)))))
4b1aaa8b 6723
f32b3b91
CD
6724 (setq list (sort list (lambda (a b)
6725 (string< (downcase a) (downcase b)))))
6726 (if prepare-display-function
6727 (setq list (funcall prepare-display-function list)))
6728 (if (and (string= part dpart)
6729 (or (not (string= part ""))
6730 idlwave-complete-empty-string-as-lower-case)
6731 (not idlwave-completion-force-default-case))
6732 (setq list (mapcar (lambda (x)
4b1aaa8b 6733 (if (listp x)
f32b3b91
CD
6734 (setcar x (downcase (car x)))
6735 (setq x (downcase x)))
6736 x)
6737 list)))
6738 (idlwave-display-completion-list list prompt beg complete))
6739 t))))
6740
6741(defun idlwave-complete-class ()
6742 "Complete a class at point."
6743 (interactive)
6744 ;; Call `idlwave-routines' to make sure the class list will be available
6745 (idlwave-routines)
15e42531
CD
6746 ;; Check for the special case of completing empty string after pro/function
6747 (if (let ((case-fold-search t))
6748 (save-excursion
6749 (and
6750 (re-search-backward "\\<\\(pro\\|function\\)[ \t]+\\="
6751 (- (point) 15) t)
6752 (goto-char (point-min))
4b1aaa8b 6753 (re-search-forward
15e42531
CD
6754 "^[ \t]*\\(pro\\|function\\)[ \t]+\\([a-zA-Z0-9_]+::\\)" nil t))))
6755 ;; Yank the full class specification
6756 (insert (match-string 2))
52a244eb 6757 ;; Do the completion, using list gathered from `idlwave-routines'
4b1aaa8b
PE
6758 (idlwave-complete-in-buffer
6759 'class 'class (idlwave-class-alist) nil
52a244eb 6760 "Select a class" "class"
8d222148
SM
6761 (lambda (list) ;; Push it to help-links if system help available
6762 (mapcar (lambda (x)
6763 (let* ((entry (idlwave-class-info x))
6764 (link (nth 1 (assq 'link entry))))
6765 (if link (push (cons x link)
6766 idlwave-completion-help-links))
6767 x))
6768 list)))))
f32b3b91 6769
76959b77 6770(defun idlwave-attach-classes (list type show-classes)
05a1abfc 6771 ;; Attach the proper class list to a LIST of completion items.
76959b77
S
6772 ;; TYPE, when 'kwd, shows classes for method keywords, when
6773 ;; 'class-tag, for class tags, and otherwise for methods.
f32b3b91 6774 ;; SHOW-CLASSES is the value of `idlwave-completion-show-classes'.
76959b77
S
6775 (if (or (null show-classes) ; don't want to see classes
6776 (null class-selector) ; not a method call
4b1aaa8b 6777 (and
76959b77
S
6778 (stringp class-selector) ; the class is already known
6779 (not super-classes))) ; no possibilities for inheritance
6780 ;; In these cases, we do not have to do anything
6781 list
05a1abfc
CD
6782 (let* ((do-prop (and (>= show-classes 0)
6783 (>= emacs-major-version 21)))
f32b3b91 6784 (do-buf (not (= show-classes 0)))
76959b77 6785 ;; (do-dots (featurep 'xemacs))
05a1abfc 6786 (do-dots t)
76959b77 6787 (inherit (if (and (not (eq type 'class-tag)) super-classes)
05a1abfc 6788 (cons class-selector super-classes)))
f32b3b91
CD
6789 (max (abs show-classes))
6790 (lmax (if do-dots (apply 'max (mapcar 'length list))))
6791 classes nclasses class-info space)
4b1aaa8b 6792 (mapcar
f32b3b91
CD
6793 (lambda (x)
6794 ;; get the classes
76959b77
S
6795 (if (eq type 'class-tag)
6796 ;; Just one class for tags
6797 (setq classes
4b1aaa8b 6798 (list
76959b77 6799 (idlwave-class-or-superclass-with-tag class-selector x)))
52a244eb 6800 ;; Multiple classes for method or method-keyword
76959b77
S
6801 (setq classes
6802 (if (eq type 'kwd)
6803 (idlwave-all-method-keyword-classes
6804 method-selector x type-selector)
6805 (idlwave-all-method-classes x type-selector)))
6806 (if inherit
4b1aaa8b 6807 (setq classes
76959b77
S
6808 (delq nil
6809 (mapcar (lambda (x) (if (memq x inherit) x nil))
6810 classes)))))
f32b3b91
CD
6811 (setq nclasses (length classes))
6812 ;; Make the separator between item and class-info
6813 (if do-dots
6814 (setq space (concat " " (make-string (- lmax (length x)) ?.)))
6815 (setq space " "))
6816 (if do-buf
6817 ;; We do want info in the buffer
6818 (if (<= nclasses max)
6819 (setq class-info (concat
6820 space
6821 "<" (mapconcat 'identity classes ",") ">"))
6822 (setq class-info (format "%s<%d classes>" space nclasses)))
6823 (setq class-info nil))
6824 (when do-prop
6825 ;; We do want properties
6826 (setq x (copy-sequence x))
6827 (put-text-property 0 (length x)
52a244eb
S
6828 'help-echo (mapconcat 'identity classes " ")
6829 x))
f32b3b91
CD
6830 (if class-info
6831 (list x class-info)
6832 x))
6833 list))))
6834
6835(defun idlwave-attach-method-classes (list)
6836 ;; Call idlwave-attach-classes with method parameters
76959b77 6837 (idlwave-attach-classes list 'method idlwave-completion-show-classes))
f32b3b91
CD
6838(defun idlwave-attach-keyword-classes (list)
6839 ;; Call idlwave-attach-classes with keyword parameters
76959b77
S
6840 (idlwave-attach-classes list 'kwd idlwave-completion-show-classes))
6841(defun idlwave-attach-class-tag-classes (list)
6842 ;; Call idlwave-attach-classes with class structure tags
6843 (idlwave-attach-classes list 'class-tag idlwave-completion-show-classes))
4b1aaa8b 6844
f32b3b91
CD
6845
6846;;----------------------------------------------------------------------
6847;;----------------------------------------------------------------------
6848;;----------------------------------------------------------------------
6849;;----------------------------------------------------------------------
6850;;----------------------------------------------------------------------
0b03a950
GM
6851(when (featurep 'xemacs)
6852 (defvar rtn)
6853 (defun idlwave-pset (item)
6854 (set 'rtn item)))
5e72c6b2
S
6855
6856(defun idlwave-popup-select (ev list title &optional sort)
6857 "Select an item in LIST with a popup menu.
6858TITLE is the title to put atop the popup. If SORT is non-nil,
5a0c3f56 6859sort the list before displaying."
5e72c6b2 6860 (let ((maxpopup idlwave-max-popup-menu-items)
8d222148 6861 rtn menu)
5e72c6b2
S
6862 (cond ((null list))
6863 ((= 1 (length list))
6864 (setq rtn (car list)))
6865 ((featurep 'xemacs)
4b1aaa8b 6866 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6867 (string< (upcase a) (upcase b))))))
6868 (setq menu
6869 (append (list title)
6870 (mapcar (lambda (x) (vector x (list 'idlwave-pset
6871 x)))
6872 list)))
6873 (setq menu (idlwave-split-menu-xemacs menu maxpopup))
8d222148
SM
6874 (let ((resp (get-popup-menu-response menu)))
6875 (funcall (event-function resp) (event-object resp))))
5e72c6b2 6876 (t
4b1aaa8b 6877 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6878 (string< (upcase a) (upcase b))))))
6879 (setq menu (cons title
6880 (list
6881 (append (list "")
6882 (mapcar (lambda(x) (cons x x)) list)))))
6883 (setq menu (idlwave-split-menu-emacs menu maxpopup))
6884 (setq rtn (x-popup-menu ev menu))))
6885 rtn))
6886
6887(defun idlwave-split-menu-xemacs (menu N)
6888 "Split the MENU into submenus of maximum length N."
6889 (if (<= (length menu) (1+ N))
6890 ;; No splitting needed
6891 menu
6892 (let* ((title (car menu))
6893 (entries (cdr menu))
6894 (menu (list title))
6895 (cnt 0)
6896 (nextmenu nil))
6897 (while entries
6898 (while (and entries (< cnt N))
6899 (setq cnt (1+ cnt)
6900 nextmenu (cons (car entries) nextmenu)
6901 entries (cdr entries)))
6902 (setq nextmenu (nreverse nextmenu))
6903 (setq nextmenu (cons (format "%s...%s"
6904 (aref (car nextmenu) 0)
6905 (aref (nth (1- cnt) nextmenu) 0))
6906 nextmenu))
6907 (setq menu (cons nextmenu menu)
6908 nextmenu nil
6909 cnt 0))
6910 (nreverse menu))))
6911
6912(defun idlwave-split-menu-emacs (menu N)
6913 "Split the MENU into submenus of maximum length N."
6914 (if (<= (length (nth 1 menu)) (1+ N))
6915 ;; No splitting needed
6916 menu
6917 (let* ((title (car menu))
6918 (entries (cdr (nth 1 menu)))
6919 (menu nil)
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 (prin1 nextmenu)
6929 (setq nextmenu (cons (format "%s...%s"
6930 (car (car nextmenu))
6931 (car (nth (1- cnt) nextmenu)))
6932 nextmenu))
6933 (setq menu (cons nextmenu menu)
6934 nextmenu nil
6935 cnt 0))
6936 (setq menu (nreverse menu))
6937 (setq menu (cons title menu))
6938 menu)))
f32b3b91 6939
15e42531
CD
6940(defvar idlwave-completion-setup-hook nil)
6941
f32b3b91
CD
6942(defun idlwave-scroll-completions (&optional message)
6943 "Scroll the completion window on this frame."
6944 (let ((cwin (get-buffer-window "*Completions*" 'visible))
6945 (win (selected-window)))
6946 (unwind-protect
6947 (progn
6948 (select-window cwin)
6949 (condition-case nil
6950 (scroll-up)
6951 (error (if (and (listp last-command)
6952 (nth 2 last-command))
6953 (progn
6954 (select-window win)
6955 (eval idlwave-complete-after-success-form))
6956 (set-window-start cwin (point-min)))))
274f1353 6957 (and message (message "%s" message)))
f32b3b91
CD
6958 (select-window win))))
6959
6960(defun idlwave-display-completion-list (list &optional message beg complete)
6961 "Display the completions in LIST in the completions buffer and echo MESSAGE."
6962 (unless (and (get-buffer-window "*Completions*")
6963 (idlwave-local-value 'idlwave-completion-p "*Completions*"))
6964 (move-marker idlwave-completion-mark beg)
6965 (setq idlwave-before-completion-wconf (current-window-configuration)))
6966
6967 (if (featurep 'xemacs)
4b1aaa8b 6968 (idlwave-display-completion-list-xemacs
15e42531 6969 list)
f32b3b91
CD
6970 (idlwave-display-completion-list-emacs list))
6971
6972 ;; Store a special value in `this-command'. When `idlwave-complete'
6973 ;; finds this in `last-command', it will scroll the *Completions* buffer.
6974 (setq this-command (list 'idlwave-display-completion-list message complete))
6975
6976 ;; Mark the completions buffer as created by cib
6977 (idlwave-set-local 'idlwave-completion-p t "*Completions*")
6978
6979 ;; Fontify the classes
6980 (if (and idlwave-completion-fontify-classes
6981 (consp (car list)))
6982 (idlwave-completion-fontify-classes))
6983
15e42531
CD
6984 ;; Run the hook
6985 (run-hooks 'idlwave-completion-setup-hook)
6986
f32b3b91 6987 ;; Display the message
274f1353 6988 (message "%s" (or message "Making completion list...done")))
f32b3b91
CD
6989
6990(defun idlwave-choose (function &rest args)
6991 "Call FUNCTION as a completion chooser and pass ARGS to it."
6992 (let ((completion-ignore-case t)) ; install correct value
6993 (apply function args))
175069ef 6994 (if (and (derived-mode-p 'idlwave-shell-mode)
15e42531
CD
6995 (boundp 'font-lock-mode)
6996 (not font-lock-mode))
52a244eb 6997 ;; For the shell, remove the fontification of the word before point
15e42531
CD
6998 (let ((beg (save-excursion
6999 (skip-chars-backward "a-zA-Z0-9_")
7000 (point))))
7001 (remove-text-properties beg (point) '(face nil))))
f32b3b91
CD
7002 (eval idlwave-complete-after-success-form-force))
7003
76959b77
S
7004(defun idlwave-keyboard-quit ()
7005 (interactive)
7006 (unwind-protect
7007 (if (eq (car-safe last-command) 'idlwave-display-completion-list)
7008 (idlwave-restore-wconf-after-completion))
7009 (keyboard-quit)))
7010
f32b3b91
CD
7011(defun idlwave-restore-wconf-after-completion ()
7012 "Restore the old (before completion) window configuration."
7013 (and idlwave-completion-restore-window-configuration
7014 idlwave-before-completion-wconf
7015 (set-window-configuration idlwave-before-completion-wconf)))
7016
52a244eb
S
7017(defun idlwave-one-key-select (sym prompt delay)
7018 "Make the user select an element from the alist in the variable SYM.
7019The keys of the alist are expected to be strings. The function returns the
7020car of the selected association.
d9271f41 7021To do this, PROMPT is displayed and the user must hit a letter key to
52a244eb
S
7022select an entry. If the user does not reply within DELAY seconds, a help
7023window with the options is displayed automatically.
7024The key which is associated with each option is generated automatically.
7025First, the strings are checked for preselected keys, like in \"[P]rint\".
7026If these don't exist, a letter in the string is automatically selected."
7027 (let* ((alist (symbol-value sym))
7028 (temp-buffer-show-hook (if (fboundp 'fit-window-to-buffer)
7029 '(fit-window-to-buffer)))
7030 keys-alist char)
7031 ;; First check the cache
7032 (if (and (eq (symbol-value sym) (get sym :one-key-alist-last)))
7033 (setq keys-alist (get sym :one-key-alist-cache))
7034 ;; Need to make new list
7035 (setq keys-alist (idlwave-make-one-key-alist alist))
7036 (put sym :one-key-alist-cache keys-alist)
7037 (put sym :one-key-alist-last alist))
7038 ;; Display prompt and wait for quick reply
7039 (message "%s[%s]" prompt
7040 (mapconcat (lambda(x) (char-to-string (car x)))
7041 keys-alist ""))
7042 (if (sit-for delay)
7043 ;; No quick reply: Show help
7044 (save-window-excursion
7045 (with-output-to-temp-buffer "*Completions*"
26b51db5
JB
7046 (dolist (x keys-alist)
7047 (princ (nth 1 x))
7048 (princ "\n")))
52a244eb
S
7049 (setq char (read-char)))
7050 (setq char (read-char)))
7051 (message nil)
7052 ;; Return the selected result
7053 (nth 2 (assoc char keys-alist))))
7054
7055;; Used for, e.g., electric debug super-examine.
7056(defun idlwave-make-one-key-alist (alist)
7057 "Make an alist for single key selection."
7058 (let ((l alist) keys-alist name start char help
7059 (cnt 0)
7060 (case-fold-search nil))
7061 (while l
7062 (setq name (car (car l))
7063 l (cdr l))
7064 (catch 'exit
7065 ;; First check if the configuration predetermined a key
7066 (if (string-match "\\[\\(.\\)\\]" name)
7067 (progn
7068 (setq char (string-to-char (downcase (match-string 1 name)))
7069 help (format "%c: %s" char name)
7070 keys-alist (cons (list char help name) keys-alist))
7071 (throw 'exit t)))
7072 ;; Then check for capital letters
7073 (setq start 0)
7074 (while (string-match "[A-Z]" name start)
7075 (setq start (match-end 0)
7076 char (string-to-char (downcase (match-string 0 name))))
7077 (if (not (assoc char keys-alist))
7078 (progn
7079 (setq help (format "%c: %s" char
7080 (replace-match
7081 (concat "[" (match-string 0 name) "]")
7082 t t name))
7083 keys-alist (cons (list char help name) keys-alist))
7084 (throw 'exit t))))
7085 ;; Now check for lowercase letters
7086 (setq start 0)
7087 (while (string-match "[a-z]" name start)
7088 (setq start (match-end 0)
7089 char (string-to-char (match-string 0 name)))
7090 (if (not (assoc char keys-alist))
7091 (progn
7092 (setq help (format "%c: %s" char
7093 (replace-match
7094 (concat "[" (match-string 0 name) "]")
7095 t t name))
7096 keys-alist (cons (list char help name) keys-alist))
7097 (throw 'exit t))))
7098 ;; Bummer, nothing found! Use a stupid number
7099 (setq char (string-to-char (int-to-string (setq cnt (1+ cnt))))
7100 help (format "%c: %s" char name)
7101 keys-alist (cons (list char help name) keys-alist))))
7102 (nreverse keys-alist)))
7103
f32b3b91
CD
7104(defun idlwave-set-local (var value &optional buffer)
7105 "Set the buffer-local value of VAR in BUFFER to VALUE."
9a529312 7106 (with-current-buffer (or buffer (current-buffer))
f32b3b91
CD
7107 (set (make-local-variable var) value)))
7108
7109(defun idlwave-local-value (var &optional buffer)
7110 "Return the value of VAR in BUFFER, but only if VAR is local to BUFFER."
9a529312 7111 (with-current-buffer (or buffer (current-buffer))
f32b3b91
CD
7112 (and (local-variable-p var (current-buffer))
7113 (symbol-value var))))
7114
15e42531
CD
7115;; In XEmacs, we can use :activate-callback directly to advice the
7116;; choose functions. We use the private keymap only for the online
7117;; help feature.
f32b3b91 7118
15e42531 7119(defvar idlwave-completion-map nil
5a0c3f56 7120 "Keymap for `completion-list-mode' with `idlwave-complete'.")
15e42531
CD
7121
7122(defun idlwave-display-completion-list-xemacs (list &rest cl-args)
f32b3b91 7123 (with-output-to-temp-buffer "*Completions*"
15e42531
CD
7124 (apply 'display-completion-list list
7125 ':activate-callback 'idlwave-default-choose-completion
7126 cl-args))
9a529312 7127 (with-current-buffer "*Completions*"
15e42531
CD
7128 (use-local-map
7129 (or idlwave-completion-map
7130 (setq idlwave-completion-map
7131 (idlwave-make-modified-completion-map-xemacs
7132 (current-local-map)))))))
f32b3b91
CD
7133
7134(defun idlwave-default-choose-completion (&rest args)
7135 "Execute `default-choose-completion' and then restore the win-conf."
7136 (apply 'idlwave-choose 'default-choose-completion args))
7137
15e42531
CD
7138(defun idlwave-make-modified-completion-map-xemacs (old-map)
7139 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7140 (let ((new-map (copy-keymap old-map)))
7141 (define-key new-map [button3up] 'idlwave-mouse-completion-help)
7142 (define-key new-map [button3] (lambda ()
7143 (interactive)
7144 (setq this-command last-command)))
7145 new-map))
f32b3b91 7146
76959b77 7147;; In Emacs we also replace keybindings in the completion
15e42531 7148;; map in order to install our wrappers.
f32b3b91
CD
7149
7150(defun idlwave-display-completion-list-emacs (list)
7151 "Display completion list and install the choose wrappers."
7152 (with-output-to-temp-buffer "*Completions*"
7153 (display-completion-list list))
9a529312 7154 (with-current-buffer "*Completions*"
f32b3b91
CD
7155 (use-local-map
7156 (or idlwave-completion-map
7157 (setq idlwave-completion-map
15e42531
CD
7158 (idlwave-make-modified-completion-map-emacs
7159 (current-local-map)))))))
7160
7161(defun idlwave-make-modified-completion-map-emacs (old-map)
f32b3b91
CD
7162 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7163 (let ((new-map (copy-keymap old-map)))
4b1aaa8b 7164 (substitute-key-definition
f32b3b91
CD
7165 'choose-completion 'idlwave-choose-completion new-map)
7166 (substitute-key-definition
7167 'mouse-choose-completion 'idlwave-mouse-choose-completion new-map)
15e42531 7168 (define-key new-map [mouse-3] 'idlwave-mouse-completion-help)
f32b3b91
CD
7169 new-map))
7170
7171(defun idlwave-choose-completion (&rest args)
7172 "Choose the completion that point is in or next to."
0e8a9331 7173 (interactive (list last-nonmenu-event))
f32b3b91
CD
7174 (apply 'idlwave-choose 'choose-completion args))
7175
7176(defun idlwave-mouse-choose-completion (&rest args)
7177 "Click on an alternative in the `*Completions*' buffer to choose it."
7178 (interactive "e")
7179 (apply 'idlwave-choose 'mouse-choose-completion args))
7180
7181;;----------------------------------------------------------------------
7182;;----------------------------------------------------------------------
7183
05a1abfc 7184;;; ------------------------------------------------------------------------
8350f087 7185;;; Structure parsing code, and code to manage class info
05a1abfc
CD
7186
7187;;
7188;; - Go again over the documentation how to write a completion
7189;; plugin. It is in self.el, but currently still very bad.
4b1aaa8b
PE
7190;; This could be in a separate file in the distribution, or
7191;; in an appendix for the manual.
52a244eb
S
7192
7193(defvar idlwave-struct-skip
7194 "[ \t]*\\(\\$.*\n\\(^[ \t]*\\(\\$[ \t]*\\)?\\(;.*\\)?\n\\)*\\)?[ \t]*"
5a0c3f56 7195 "Regexp for skipping continued blank or comment-only lines in structures.")
52a244eb
S
7196
7197(defvar idlwave-struct-tag-regexp
7198 (concat "[{,]" ;leading comma/brace
7199 idlwave-struct-skip ; 4 groups
7200 "\\([a-zA-Z][a-zA-Z0-9_]*\\)" ;the tag itself, group 5
7201 "[ \t]*:") ; the final colon
7202 "Regexp for structure tags.")
05a1abfc
CD
7203
7204(defun idlwave-struct-tags ()
7205 "Return a list of all tags in the structure defined at point.
7206Point is expected just before the opening `{' of the struct definition."
7207 (save-excursion
7208 (let* ((borders (idlwave-struct-borders))
7209 (beg (car borders))
7210 (end (cdr borders))
7211 tags)
7212 (goto-char beg)
52a244eb
S
7213 (save-restriction
7214 (narrow-to-region beg end)
7215 (while (re-search-forward idlwave-struct-tag-regexp end t)
7216 ;; Check if we are still on the top level of the structure.
7217 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7218 (= (point) beg))
7219 (push (match-string-no-properties 5) tags))
7220 (goto-char (match-end 0))))
7221 (nreverse tags))))
05a1abfc 7222
76959b77
S
7223(defun idlwave-find-struct-tag (tag)
7224 "Find a given TAG in the structure defined at point."
7225 (let* ((borders (idlwave-struct-borders))
76959b77
S
7226 (end (cdr borders))
7227 (case-fold-search t))
4b1aaa8b 7228 (re-search-forward (concat "\\(^[ \t]*\\|[,{][ \t]*\\)" tag "[ \t]*:")
76959b77
S
7229 end t)))
7230
05a1abfc
CD
7231(defun idlwave-struct-inherits ()
7232 "Return a list of all `inherits' names in the struct at point.
7233Point is expected just before the opening `{' of the struct definition."
7234 (save-excursion
7235 (let* ((borders (idlwave-struct-borders))
7236 (beg (car borders))
7237 (end (cdr borders))
7238 (case-fold-search t)
7239 names)
7240 (goto-char beg)
52a244eb
S
7241 (save-restriction
7242 (narrow-to-region beg end)
4b1aaa8b 7243 (while (re-search-forward
52a244eb
S
7244 (concat "[{,]" ;leading comma/brace
7245 idlwave-struct-skip ; 4 groups
7246 "inherits" ; The INHERITS tag
7247 idlwave-struct-skip ; 4 more
7248 "\\([a-zA-Z][a-zA-Z0-9_]*\\)") ; The super-group, #9
7249 end t)
7250 ;; Check if we are still on the top level of the structure.
7251 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7252 (= (point) beg))
7253 (push (match-string-no-properties 9) names))
7254 (goto-char (match-end 0))))
05a1abfc
CD
7255 (nreverse names))))
7256
5e72c6b2 7257(defun idlwave-in-structure ()
52a244eb 7258 "Return t if point is inside an IDL structure definition."
5e72c6b2
S
7259 (let ((beg (point)))
7260 (save-excursion
7261 (if (not (or (idlwave-in-comment) (idlwave-in-quote)))
7262 (if (idlwave-find-structure-definition nil nil 'back)
7263 (let ((borders (idlwave-struct-borders)))
7264 (or (= (car borders) (cdr borders)) ;; struct not yet closed...
7265 (and (> beg (car borders)) (< beg (cdr borders))))))))))
05a1abfc
CD
7266
7267(defun idlwave-struct-borders ()
7268 "Return the borders of the {...} after point as a cons cell."
7269 (let (beg)
7270 (save-excursion
7271 (skip-chars-forward "^{")
7272 (setq beg (point))
7273 (condition-case nil (forward-list 1)
7274 (error (goto-char beg)))
7275 (cons beg (point)))))
7276
7277(defun idlwave-find-structure-definition (&optional var name bound)
5a0c3f56
JB
7278 "Search forward for a structure definition.
7279If VAR is non-nil, search for a structure assigned to variable VAR.
7280If NAME is non-nil, search for a named structure NAME, if a string,
7281or a generic named structure otherwise. If BOUND is an integer, limit
7282the search. If BOUND is the symbol `all', we search first back and
7283then forward through the entire file. If BOUND is the symbol `back'
7284we search only backward."
76959b77 7285 (let* ((ws "[ \t]*\\(\\$.*\n[ \t]*\\)*")
05a1abfc
CD
7286 (case-fold-search t)
7287 (lim (if (integerp bound) bound nil))
7288 (re (concat
7289 (if var
7290 (concat "\\<" (regexp-quote (downcase var)) "\\>" ws)
7291 "\\(\\)")
7292 "=" ws "\\({\\)"
4b1aaa8b 7293 (if name
52a244eb 7294 (if (stringp name)
4b1aaa8b 7295 (concat ws "\\(\\<" (downcase name) "\\)[^a-zA-Z0-9_$]")
52a244eb
S
7296 ;; Just a generic name
7297 (concat ws "\\<\\([a-zA-Z_0-9$]+\\)" ws ","))
7298 ""))))
5e72c6b2 7299 (if (or (and (or (eq bound 'all) (eq bound 'back))
05a1abfc 7300 (re-search-backward re nil t))
5e72c6b2 7301 (and (not (eq bound 'back)) (re-search-forward re lim t)))
52a244eb
S
7302 (progn
7303 (goto-char (match-beginning 3))
7304 (match-string-no-properties 5)))))
7305
4b1aaa8b 7306(defvar idlwave-class-info nil)
52a244eb 7307(defvar idlwave-class-reset nil) ; to reset buffer-local classes
05a1abfc 7308
05a1abfc 7309(add-hook 'idlwave-update-rinfo-hook
52a244eb 7310 (lambda () (setq idlwave-class-reset t)))
05a1abfc
CD
7311(add-hook 'idlwave-after-load-rinfo-hook
7312 (lambda () (setq idlwave-class-info nil)))
7313
7314(defun idlwave-class-info (class)
7315 (let (list entry)
52a244eb
S
7316 (if idlwave-class-info
7317 (if idlwave-class-reset
4b1aaa8b 7318 (setq
52a244eb
S
7319 idlwave-class-reset nil
7320 idlwave-class-info ; Remove any visited in a buffer
4b1aaa8b
PE
7321 (delq nil (mapcar
7322 (lambda (x)
7323 (let ((filebuf
7324 (idlwave-class-file-or-buffer
52a244eb
S
7325 (or (cdr (assq 'found-in x)) (car x)))))
7326 (if (cdr filebuf)
7327 nil
7328 x)))
7329 idlwave-class-info))))
7330 ;; Info is nil, put in the system stuff to start.
05a1abfc
CD
7331 (setq idlwave-class-info idlwave-system-class-info)
7332 (setq list idlwave-class-info)
7333 (while (setq entry (pop list))
7334 (idlwave-sintern-class-info entry)))
7335 (setq class (idlwave-sintern-class class))
52a244eb
S
7336 (or (assq class idlwave-class-info)
7337 (progn (idlwave-scan-class-info class)
7338 (assq class idlwave-class-info)))))
05a1abfc
CD
7339
7340(defun idlwave-sintern-class-info (entry)
7341 "Sintern the class names in a class-info entry."
8d222148 7342 (let ((inherits (assq 'inherits entry)))
05a1abfc
CD
7343 (setcar entry (idlwave-sintern-class (car entry) 'set))
7344 (if inherits
7345 (setcdr inherits (mapcar (lambda (x) (idlwave-sintern-class x 'set))
7346 (cdr inherits))))))
7347
52a244eb 7348(defun idlwave-find-class-definition (class &optional all-hook alt-class)
5a0c3f56 7349 "Find class structure definition(s).
52a244eb
S
7350If ALL-HOOK is set, find all named structure definitions in a given
7351class__define routine, on which ALL-HOOK will be run. If ALT-CLASS is
7352set, look for the name__define pro, and inside of it, for the ALT-CLASS
5a0c3f56 7353class/struct definition."
8d222148 7354 (let ((case-fold-search t) end-lim name)
52a244eb
S
7355 (when (re-search-forward
7356 (concat "^[ \t]*pro[ \t]+" (downcase class) "__define" "\\>") nil t)
7357 (if all-hook
7358 (progn
7359 ;; For everything there
7360 (setq end-lim (save-excursion (idlwave-end-of-subprogram) (point)))
4b1aaa8b 7361 (while (setq name
52a244eb
S
7362 (idlwave-find-structure-definition nil t end-lim))
7363 (funcall all-hook name)))
7364 (idlwave-find-structure-definition nil (or alt-class class))))))
76959b77 7365
52a244eb
S
7366
7367(defun idlwave-class-file-or-buffer (class)
5a0c3f56 7368 "Find buffer visiting CLASS definition."
05a1abfc 7369 (let* ((pro (concat (downcase class) "__define"))
52a244eb
S
7370 (file (idlwave-routine-source-file
7371 (nth 3 (idlwave-rinfo-assoc pro 'pro nil
7372 (idlwave-routines))))))
7373 (cons file (if file (idlwave-get-buffer-visiting file)))))
7374
7375
7376(defun idlwave-scan-class-info (class)
5a0c3f56 7377 "Scan all class and named structure info in the class__define pro."
52a244eb
S
7378 (let* ((idlwave-auto-routine-info-updates nil)
7379 (filebuf (idlwave-class-file-or-buffer class))
7380 (file (car filebuf))
7381 (buf (cdr filebuf))
7382 (class (idlwave-sintern-class class)))
7383 (if (or
7384 (not file)
7385 (and ;; neither a regular file nor a visited buffer
7386 (not buf)
7387 (not (file-regular-p file))))
7388 nil ; Cannot find the file/buffer to get any info
05a1abfc 7389 (save-excursion
52a244eb
S
7390 (if buf (set-buffer buf)
7391 ;; Read the file in temporarily
05a1abfc
CD
7392 (set-buffer (get-buffer-create " *IDLWAVE-tmp*"))
7393 (erase-buffer)
175069ef 7394 (unless (derived-mode-p 'idlwave-mode)
05a1abfc
CD
7395 (idlwave-mode))
7396 (insert-file-contents file))
7397 (save-excursion
7398 (goto-char 1)
4b1aaa8b 7399 (idlwave-find-class-definition class
52a244eb
S
7400 ;; Scan all of the structures found there
7401 (lambda (name)
7402 (let* ((this-class (idlwave-sintern-class name))
4b1aaa8b 7403 (entry
52a244eb
S
7404 (list this-class
7405 (cons 'tags (idlwave-struct-tags))
7406 (cons 'inherits (idlwave-struct-inherits)))))
7407 (if (not (eq this-class class))
7408 (setq entry (nconc entry (list (cons 'found-in class)))))
7409 (idlwave-sintern-class-info entry)
7410 (push entry idlwave-class-info)))))))))
7411
7412(defun idlwave-class-found-in (class)
5a0c3f56 7413 "Return the FOUND-IN property of the CLASS."
52a244eb 7414 (cdr (assq 'found-in (idlwave-class-info class))))
05a1abfc
CD
7415(defun idlwave-class-tags (class)
7416 "Return the native tags in CLASS."
7417 (cdr (assq 'tags (idlwave-class-info class))))
7418(defun idlwave-class-inherits (class)
7419 "Return the direct superclasses of CLASS."
7420 (cdr (assq 'inherits (idlwave-class-info class))))
7421
52a244eb 7422
05a1abfc
CD
7423(defun idlwave-all-class-tags (class)
7424 "Return a list of native and inherited tags in CLASS."
76959b77
S
7425 (condition-case err
7426 (apply 'append (mapcar 'idlwave-class-tags
7427 (cons class (idlwave-all-class-inherits class))))
4b1aaa8b 7428 (error
76959b77
S
7429 (idlwave-class-tag-reset)
7430 (error "%s" (error-message-string err)))))
7431
05a1abfc
CD
7432
7433(defun idlwave-all-class-inherits (class)
7434 "Return a list of all superclasses of CLASS (recursively expanded).
5e72c6b2 7435The list is cached in `idlwave-class-info' for faster access."
05a1abfc
CD
7436 (cond
7437 ((not idlwave-support-inheritance) nil)
7438 ((eq class nil) nil)
7439 ((eq class t) nil)
7440 (t
7441 (let ((info (idlwave-class-info class))
7442 entry)
7443 (if (setq entry (assq 'all-inherits info))
7444 (cdr entry)
76959b77
S
7445 ;; Save the depth of inheritance scan to check for circular references
7446 (let ((inherits (mapcar (lambda (x) (cons x 0))
7447 (idlwave-class-inherits class)))
05a1abfc
CD
7448 rtn all-inherits cl)
7449 (while inherits
7450 (setq cl (pop inherits)
76959b77
S
7451 rtn (cons (car cl) rtn)
7452 inherits (append (mapcar (lambda (x)
7453 (cons x (1+ (cdr cl))))
7454 (idlwave-class-inherits (car cl)))
7455 inherits))
7456 (if (> (cdr cl) 999)
7457 (error
7458 "Class scan: inheritance depth exceeded. Circular inheritance?")
7459 ))
05a1abfc
CD
7460 (setq all-inherits (nreverse rtn))
7461 (nconc info (list (cons 'all-inherits all-inherits)))
7462 all-inherits))))))
7463
52a244eb 7464(defun idlwave-entry-keywords (entry &optional record-link)
4b1aaa8b 7465 "Return the flat entry keywords alist from routine-info entry.
52a244eb
S
7466If RECORD-LINK is non-nil, the keyword text is copied and a text
7467property indicating the link is added."
7468 (let (kwds)
8ffcfb27 7469 (mapc
4b1aaa8b 7470 (lambda (key-list)
52a244eb
S
7471 (let ((file (car key-list)))
7472 (mapcar (lambda (key-cons)
7473 (let ((key (car key-cons))
7474 (link (cdr key-cons)))
7475 (when (and record-link file)
7476 (setq key (copy-sequence key))
4b1aaa8b 7477 (put-text-property
52a244eb 7478 0 (length key)
4b1aaa8b
PE
7479 'link
7480 (concat
7481 file
7482 (if link
52a244eb
S
7483 (concat idlwave-html-link-sep
7484 (number-to-string link))))
7485 key))
7486 (push (list key) kwds)))
7487 (cdr key-list))))
7488 (nthcdr 5 entry))
7489 (nreverse kwds)))
7490
7491(defun idlwave-entry-find-keyword (entry keyword)
5a0c3f56 7492 "Find keyword KEYWORD in entry ENTRY, and return (with link) if set."
52a244eb
S
7493 (catch 'exit
7494 (mapc
4b1aaa8b 7495 (lambda (key-list)
52a244eb
S
7496 (let ((file (car key-list))
7497 (kwd (assoc keyword (cdr key-list))))
7498 (when kwd
4b1aaa8b 7499 (setq kwd (cons (car kwd)
52a244eb 7500 (if (and file (cdr kwd))
4b1aaa8b 7501 (concat file
52a244eb
S
7502 idlwave-html-link-sep
7503 (number-to-string (cdr kwd)))
7504 (cdr kwd))))
7505 (throw 'exit kwd))))
7506 (nthcdr 5 entry))))
05a1abfc
CD
7507
7508;;==========================================================================
7509;;
7510;; Completing class structure tags. This is a completion plugin.
7511;; The necessary taglist is constructed dynamically
7512
7513(defvar idlwave-current-tags-class nil)
7514(defvar idlwave-current-class-tags nil)
7515(defvar idlwave-current-native-class-tags nil)
76959b77 7516(defvar idlwave-sint-class-tags nil)
1a717047 7517(declare-function idlwave-sintern-class-tag "idlwave" t t)
76959b77 7518(idlwave-new-sintern-type 'class-tag)
05a1abfc 7519(add-to-list 'idlwave-complete-special 'idlwave-complete-class-structure-tag)
76959b77 7520(add-hook 'idlwave-update-rinfo-hook 'idlwave-class-tag-reset)
05a1abfc
CD
7521
7522(defun idlwave-complete-class-structure-tag ()
7523 "Complete a structure tag on a `self' argument in an object method."
7524 (interactive)
7525 (let ((pos (point))
7526 (case-fold-search t))
7527 (if (save-excursion
7528 ;; Check if the context is right
52a244eb 7529 (skip-chars-backward "a-zA-Z0-9._$")
05a1abfc
CD
7530 (and (< (point) (- pos 4))
7531 (looking-at "self\\.")))
76959b77
S
7532 (let* ((class-selector (nth 2 (idlwave-current-routine)))
7533 (super-classes (idlwave-all-class-inherits class-selector)))
05a1abfc 7534 ;; Check if we are in a class routine
76959b77 7535 (unless class-selector
e8af40ee 7536 (error "Not in a method procedure or function"))
05a1abfc 7537 ;; Check if we need to update the "current" class
76959b77
S
7538 (if (not (equal class-selector idlwave-current-tags-class))
7539 (idlwave-prepare-class-tag-completion class-selector))
4b1aaa8b 7540 (setq idlwave-completion-help-info
76959b77 7541 (list 'idlwave-complete-class-structure-tag-help
4b1aaa8b 7542 (idlwave-sintern-routine
76959b77
S
7543 (concat class-selector "__define"))
7544 nil))
8d222148 7545 ;; FIXME: idlwave-cpl-bold doesn't seem used anywhere.
05a1abfc
CD
7546 (let ((idlwave-cpl-bold idlwave-current-native-class-tags))
7547 (idlwave-complete-in-buffer
4b1aaa8b 7548 'class-tag 'class-tag
05a1abfc 7549 idlwave-current-class-tags nil
76959b77
S
7550 (format "Select a tag of class %s" class-selector)
7551 "class tag"
7552 'idlwave-attach-class-tag-classes))
05a1abfc
CD
7553 t) ; return t to skip other completions
7554 nil)))
7555
76959b77 7556(defun idlwave-class-tag-reset ()
05a1abfc
CD
7557 (setq idlwave-current-tags-class nil))
7558
7559(defun idlwave-prepare-class-tag-completion (class)
7560 "Find and parse the necessary class definitions for class structure tags."
76959b77 7561 (setq idlwave-sint-class-tags nil)
05a1abfc
CD
7562 (setq idlwave-current-tags-class class)
7563 (setq idlwave-current-class-tags
7564 (mapcar (lambda (x)
76959b77 7565 (list (idlwave-sintern-class-tag x 'set)))
05a1abfc
CD
7566 (idlwave-all-class-tags class)))
7567 (setq idlwave-current-native-class-tags
7568 (mapcar 'downcase (idlwave-class-tags class))))
7569
7570;===========================================================================
7571;;
7572;; Completing system variables and their structure fields
52a244eb 7573;; This is also a plugin.
05a1abfc
CD
7574
7575(defvar idlwave-sint-sysvars nil)
7576(defvar idlwave-sint-sysvartags nil)
1a717047
GM
7577(declare-function idlwave-sintern-sysvar "idlwave" t t)
7578(declare-function idlwave-sintern-sysvartag "idlwave" t t)
05a1abfc
CD
7579(idlwave-new-sintern-type 'sysvar)
7580(idlwave-new-sintern-type 'sysvartag)
7581(add-to-list 'idlwave-complete-special 'idlwave-complete-sysvar-or-tag)
7582(add-hook 'idlwave-update-rinfo-hook 'idlwave-sysvars-reset)
05a1abfc
CD
7583(add-hook 'idlwave-after-load-rinfo-hook 'idlwave-sintern-sysvar-alist)
7584
05a1abfc
CD
7585
7586(defun idlwave-complete-sysvar-or-tag ()
7587 "Complete a system variable."
7588 (interactive)
7589 (let ((pos (point))
7590 (case-fold-search t))
7591 (cond ((save-excursion
7592 ;; Check if the context is right for system variable
7593 (skip-chars-backward "[a-zA-Z0-9_$]")
7594 (equal (char-before) ?!))
7595 (setq idlwave-completion-help-info '(idlwave-complete-sysvar-help))
4b1aaa8b 7596 (idlwave-complete-in-buffer 'sysvar 'sysvar
05a1abfc
CD
7597 idlwave-system-variables-alist nil
7598 "Select a system variable"
7599 "system variable")
7600 t) ; return t to skip other completions
7601 ((save-excursion
7602 ;; Check if the context is right for sysvar tag
52a244eb 7603 (skip-chars-backward "a-zA-Z0-9_$.")
05a1abfc
CD
7604 (and (equal (char-before) ?!)
7605 (looking-at "\\([a-zA-Z][a-zA-Z0-9_$]*\\)\\.")
7606 (<= (match-end 0) pos)))
7607 ;; Complete a system variable tag
7608 (let* ((var (idlwave-sintern-sysvar (match-string 1)))
7609 (entry (assq var idlwave-system-variables-alist))
52a244eb
S
7610 (tags (cdr (assq 'tags entry))))
7611 (or entry (error "!%s is not a known system variable" var))
05a1abfc
CD
7612 (or tags (error "System variable !%s is not a structure" var))
7613 (setq idlwave-completion-help-info
52a244eb 7614 (list 'idlwave-complete-sysvar-tag-help var))
4b1aaa8b 7615 (idlwave-complete-in-buffer 'sysvartag 'sysvartag
05a1abfc
CD
7616 tags nil
7617 "Select a system variable tag"
7618 "system variable tag")
7619 t)) ; return t to skip other completions
7620 (t nil))))
7621
e7c4fb1e 7622(defvar idlw-help-link) ;dynamic variables set by help callback
05a1abfc 7623(defun idlwave-complete-sysvar-help (mode word)
52a244eb
S
7624 (let ((word (or (nth 1 idlwave-completion-help-info) word))
7625 (entry (assoc word idlwave-system-variables-alist)))
7626 (cond
7627 ((eq mode 'test)
7628 (and (stringp word) entry (nth 1 (assq 'link entry))))
7629 ((eq mode 'set)
e7c4fb1e
GM
7630 ;; Setting dynamic!!!
7631 (if entry (setq idlw-help-link (nth 1 (assq 'link entry)))))
52a244eb
S
7632 (t (error "This should not happen")))))
7633
7634(defun idlwave-complete-sysvar-tag-help (mode word)
7635 (let* ((var (nth 1 idlwave-completion-help-info))
7636 (entry (assoc var idlwave-system-variables-alist))
7637 (tags (cdr (assq 'tags entry)))
7638 (main (nth 1 (assq 'link entry)))
8d222148 7639 target)
52a244eb
S
7640 (cond
7641 ((eq mode 'test) ; we can at least link the main
7642 (and (stringp word) entry main))
7643 ((eq mode 'set)
4b1aaa8b 7644 (if entry
e7c4fb1e 7645 (setq idlw-help-link
e08734e2 7646 (if (setq target (cdr (assoc-string word tags t)))
e7c4fb1e
GM
7647 (idlwave-substitute-link-target main target)
7648 main)))) ;; setting dynamic!!!
52a244eb
S
7649 (t (error "This should not happen")))))
7650
f66f03de 7651(defun idlwave-split-link-target (link)
5a0c3f56 7652 "Split a given LINK into link file and anchor."
f66f03de
S
7653 (if (string-match idlwave-html-link-sep link)
7654 (cons (substring link 0 (match-beginning 0))
7655 (string-to-number (substring link (match-end 0))))))
7656
52a244eb 7657(defun idlwave-substitute-link-target (link target)
5a0c3f56 7658 "Substitute the TARGET anchor for the given LINK."
52a244eb
S
7659 (let (main-base)
7660 (setq main-base (if (string-match "#" link)
7661 (substring link 0 (match-beginning 0))
7662 link))
7663 (if target
7664 (concat main-base idlwave-html-link-sep (number-to-string target))
7665 link)))
76959b77
S
7666
7667;; Fake help in the source buffer for class structure tags.
e7c4fb1e
GM
7668;; IDLW-HELP-LINK AND IDLW-HELP-NAME ARE GLOBAL-VARIABLES HERE.
7669;; (from idlwave-do-mouse-completion-help)
7670(defvar idlw-help-name)
7671(defvar idlw-help-link)
76959b77
S
7672(defvar idlwave-help-do-class-struct-tag nil)
7673(defun idlwave-complete-class-structure-tag-help (mode word)
7674 (cond
7675 ((eq mode 'test) ; nothing gets fontified for class tags
7676 nil)
7677 ((eq mode 'set)
52a244eb 7678 (let (class-with found-in)
4b1aaa8b
PE
7679 (when (setq class-with
7680 (idlwave-class-or-superclass-with-tag
76959b77
S
7681 idlwave-current-tags-class
7682 word))
4b1aaa8b 7683 (if (assq (idlwave-sintern-class class-with)
76959b77 7684 idlwave-system-class-info)
ff689efd 7685 (error "No help available for system class tags"))
52a244eb 7686 (if (setq found-in (idlwave-class-found-in class-with))
e7c4fb1e
GM
7687 (setq idlw-help-name (cons (concat found-in "__define") class-with))
7688 (setq idlw-help-name (concat class-with "__define")))))
7689 (setq idlw-help-link word
76959b77
S
7690 idlwave-help-do-class-struct-tag t))
7691 (t (error "This should not happen"))))
7692
7693(defun idlwave-class-or-superclass-with-tag (class tag)
7694 "Find and return the CLASS or one of its superclass with the
7695associated TAG, if any."
e08734e2 7696 (let ((sclasses (cons class (idlwave-all-class-inherits class)))
76959b77
S
7697 cl)
7698 (catch 'exit
7699 (while sclasses
7700 (setq cl (pop sclasses))
7701 (let ((tags (idlwave-class-tags cl)))
7702 (while tags
7703 (if (eq t (compare-strings tag 0 nil (car tags) 0 nil t))
4b1aaa8b 7704 (throw 'exit cl))
76959b77
S
7705 (setq tags (cdr tags))))))))
7706
05a1abfc
CD
7707
7708(defun idlwave-sysvars-reset ()
7709 (if (and (fboundp 'idlwave-shell-is-running)
52a244eb
S
7710 (idlwave-shell-is-running)
7711 idlwave-idlwave_routine_info-compiled)
05a1abfc
CD
7712 (idlwave-shell-send-command "idlwave_get_sysvars"
7713 'idlwave-process-sysvars 'hide)))
7714
7715(defun idlwave-process-sysvars ()
7716 (idlwave-shell-filter-sysvars)
7717 (setq idlwave-sint-sysvars nil
7718 idlwave-sint-sysvartags nil)
7719 (idlwave-sintern-sysvar-alist))
7720
05a1abfc 7721(defun idlwave-sintern-sysvar-alist ()
52a244eb 7722 (let ((list idlwave-system-variables-alist) entry tags)
05a1abfc
CD
7723 (while (setq entry (pop list))
7724 (setcar entry (idlwave-sintern-sysvar (car entry) 'set))
52a244eb
S
7725 (setq tags (assq 'tags entry))
7726 (if tags
4b1aaa8b
PE
7727 (setcdr tags
7728 (mapcar (lambda (x)
52a244eb
S
7729 (cons (idlwave-sintern-sysvartag (car x) 'set)
7730 (cdr x)))
7731 (cdr tags)))))))
05a1abfc
CD
7732
7733(defvar idlwave-shell-command-output)
7734(defun idlwave-shell-filter-sysvars ()
52a244eb 7735 "Get any new system variables and tags."
05a1abfc
CD
7736 (let ((text idlwave-shell-command-output)
7737 (start 0)
7738 (old idlwave-system-variables-alist)
52a244eb 7739 var tags type name class link old-entry)
05a1abfc
CD
7740 (setq idlwave-system-variables-alist nil)
7741 (while (string-match "^IDLWAVE-SYSVAR: !\\([a-zA-Z0-9_$]+\\)\\( \\(.*\\)\\)?"
7742 text start)
7743 (setq start (match-end 0)
7744 var (match-string 1 text)
4b1aaa8b 7745 tags (if (match-end 3)
52a244eb
S
7746 (idlwave-split-string (match-string 3 text))))
7747 ;; Maintain old links, if present
7748 (setq old-entry (assq (idlwave-sintern-sysvar var) old))
7749 (setq link (assq 'link old-entry))
05a1abfc 7750 (setq idlwave-system-variables-alist
4b1aaa8b
PE
7751 (cons (list var
7752 (cons
7753 'tags
7754 (mapcar (lambda (x)
7755 (cons x
7756 (cdr (assq
7757 (idlwave-sintern-sysvartag x)
52a244eb
S
7758 (cdr (assq 'tags old-entry))))))
7759 tags)) link)
05a1abfc
CD
7760 idlwave-system-variables-alist)))
7761 ;; Keep the old value if query was not successful
7762 (setq idlwave-system-variables-alist
7763 (or idlwave-system-variables-alist old))))
7764
f32b3b91
CD
7765(defun idlwave-completion-fontify-classes ()
7766 "Goto the *Completions* buffer and fontify the class info."
7767 (when (featurep 'font-lock)
9a529312 7768 (with-current-buffer "*Completions*"
f32b3b91
CD
7769 (save-excursion
7770 (goto-char (point-min))
76959b77
S
7771 (let ((buffer-read-only nil))
7772 (while (re-search-forward "\\.*<[^>]+>" nil t)
7773 (put-text-property (match-beginning 0) (match-end 0)
7774 'face 'font-lock-string-face)))))))
f32b3b91
CD
7775
7776(defun idlwave-uniquify (list)
52a244eb 7777 (let ((ht (make-hash-table :size (length list) :test 'equal)))
4b1aaa8b 7778 (delq nil
52a244eb 7779 (mapcar (lambda (x)
4b1aaa8b 7780 (unless (gethash x ht)
52a244eb
S
7781 (puthash x t ht)
7782 x))
7783 list))))
f32b3b91
CD
7784
7785(defun idlwave-after-successful-completion (type slash &optional verify)
7786 "Add `=' or `(' after successful completion of keyword and function.
7787Restore the pre-completion window configuration if possible."
7788 (cond
7789 ((eq type 'procedure)
7790 nil)
7791 ((eq type 'function)
7792 (cond
7793 ((equal idlwave-function-completion-adds-paren nil) nil)
7794 ((or (equal idlwave-function-completion-adds-paren t)
7795 (equal idlwave-function-completion-adds-paren 1))
7796 (insert "("))
7797 ((equal idlwave-function-completion-adds-paren 2)
7798 (insert "()")
7799 (backward-char 1))
7800 (t nil)))
7801 ((eq type 'keyword)
7802 (if (and idlwave-keyword-completion-adds-equal
7803 (not slash))
7804 (progn (insert "=") t)
7805 nil)))
7806
7807 ;; Restore the pre-completion window configuration if this is safe.
4b1aaa8b
PE
7808
7809 (if (or (eq verify 'force) ; force
7810 (and
f32b3b91 7811 (get-buffer-window "*Completions*") ; visible
4b1aaa8b 7812 (idlwave-local-value 'idlwave-completion-p
f32b3b91
CD
7813 "*Completions*") ; cib-buffer
7814 (eq (marker-buffer idlwave-completion-mark)
7815 (current-buffer)) ; buffer OK
7816 (equal (marker-position idlwave-completion-mark)
7817 verify))) ; pos OK
7818 (idlwave-restore-wconf-after-completion))
7819 (move-marker idlwave-completion-mark nil)
7820 (setq idlwave-before-completion-wconf nil))
7821
15e42531
CD
7822(defun idlwave-mouse-context-help (ev &optional arg)
7823 "Call `idlwave-context-help' on the clicked location."
7824 (interactive "eP")
7825 (mouse-set-point ev)
7826 (idlwave-context-help arg))
7827
7828(defvar idlwave-last-context-help-pos nil)
7829(defun idlwave-context-help (&optional arg)
7830 "Display IDL Online Help on context.
76959b77
S
7831If point is on a keyword, help for that keyword will be shown. If
7832point is on a routine name or in the argument list of a routine, help
7833for that routine will be displayed. Works for system routines and
9858f6c3 7834keywords, it pulls up text help. For other routines and keywords,
76959b77
S
7835visits the source file, finding help in the header (if
7836`idlwave-help-source-try-header' is non-nil) or the routine definition
7837itself."
f32b3b91 7838 (interactive "P")
15e42531
CD
7839 (idlwave-do-context-help arg))
7840
7841(defun idlwave-mouse-completion-help (ev)
7842 "Display online help about the completion at point."
7843 (interactive "eP")
52a244eb 7844 ;; Restore last-command for next command, to make
c80e3b4a 7845 ;; scrolling/canceling of completions work.
15e42531
CD
7846 (setq this-command last-command)
7847 (idlwave-do-mouse-completion-help ev))
15e42531 7848
f32b3b91 7849(defun idlwave-routine-info (&optional arg external)
5a0c3f56
JB
7850 "Display a routines calling sequence and list of keywords.
7851When point is on the name a function or procedure, or in the argument
7852list of a function or procedure, this command displays a help buffer with
52a244eb 7853the information. When called with prefix arg, enforce class query.
f32b3b91
CD
7854
7855When point is on an object operator `->', display the class stored in
5a0c3f56
JB
7856this arrow, if any (see `idlwave-store-inquired-class'). With a prefix
7857arg, the class property is cleared out."
f32b3b91
CD
7858
7859 (interactive "P")
7860 (idlwave-routines)
7861 (if (string-match "->" (buffer-substring
7862 (max (point-min) (1- (point)))
7863 (min (+ 2 (point)) (point-max))))
7864 ;; Cursor is on an arrow
7865 (if (get-text-property (point) 'idlwave-class)
7866 ;; arrow has class property
7867 (if arg
7868 ;; Remove property
7869 (save-excursion
7870 (backward-char 1)
7871 (when (looking-at ".?\\(->\\)")
7872 (remove-text-properties (match-beginning 1) (match-end 1)
7873 '(idlwave-class nil face nil))
7874 (message "Class property removed from arrow")))
7875 ;; Echo class property
7876 (message "Arrow has text property identifying object to be class %s"
7877 (get-text-property (point) 'idlwave-class)))
7878 ;; No property found
7879 (message "Arrow has no class text property"))
7880
7881 ;; Not on an arrow...
7882 (let* ((idlwave-query-class nil)
7883 (idlwave-force-class-query (equal arg '(4)))
7884 (module (idlwave-what-module)))
15e42531 7885 (if (car module)
05a1abfc
CD
7886 (apply 'idlwave-display-calling-sequence
7887 (idlwave-fix-module-if-obj_new module))
e8af40ee 7888 (error "Don't know which calling sequence to show")))))
f32b3b91
CD
7889
7890(defun idlwave-resolve (&optional arg)
52a244eb 7891 "Call RESOLVE_ROUTINE on the module name at point.
f32b3b91
CD
7892Like `idlwave-routine-info', this looks for a routine call at point.
7893After confirmation in the minibuffer, it will use the shell to issue
7894a RESOLVE call for this routine, to attempt to make it defined and its
7895routine info available for IDLWAVE. If the routine is a method call,
7896both `class__method' and `class__define' will be tried.
7897With ARG, enforce query for the class of object methods."
7898 (interactive "P")
7899 (let* ((idlwave-query-class nil)
7900 (idlwave-force-class-query (equal arg '(4)))
7901 (module (idlwave-what-module))
7902 (name (idlwave-make-full-name (nth 2 module) (car module)))
7903 (type (if (eq (nth 1 module) 'pro) "pro" "function"))
7904 (resolve (read-string "Resolve: " (format "%s %s" type name)))
7905 (kwd "")
7906 class)
7907 (if (string-match "\\(pro\\|function\\)[ \t]+\\(\\(.*\\)::\\)?\\(.*\\)"
7908 resolve)
7909 (setq type (match-string 1 resolve)
4b1aaa8b 7910 class (if (match-beginning 2)
f32b3b91
CD
7911 (match-string 3 resolve)
7912 nil)
7913 name (match-string 4 resolve)))
7914 (if (string= (downcase type) "function")
7915 (setq kwd ",/is_function"))
7916
7917 (cond
7918 ((null class)
4b1aaa8b 7919 (idlwave-shell-send-command
f32b3b91
CD
7920 (format "resolve_routine,'%s'%s" (downcase name) kwd)
7921 'idlwave-update-routine-info
7922 nil t))
7923 (t
4b1aaa8b 7924 (idlwave-shell-send-command
f32b3b91 7925 (format "resolve_routine,'%s__define'%s" (downcase class) kwd)
4b1aaa8b
PE
7926 (list 'idlwave-shell-send-command
7927 (format "resolve_routine,'%s__%s'%s"
f32b3b91
CD
7928 (downcase class) (downcase name) kwd)
7929 '(idlwave-update-routine-info)
7930 nil t))))))
7931
3938cb82
S
7932(defun idlwave-find-module-this-file ()
7933 (interactive)
7934 (idlwave-find-module '(4)))
7935
f32b3b91
CD
7936(defun idlwave-find-module (&optional arg)
7937 "Find the source code of an IDL module.
5a0c3f56
JB
7938Works for modules for which IDLWAVE has routine info available.
7939The function offers as default the module name `idlwave-routine-info'
52a244eb
S
7940would use. With ARG limit to this buffer. With two prefix ARG's
7941force class query for object methods."
f32b3b91
CD
7942 (interactive "P")
7943 (let* ((idlwave-query-class nil)
52a244eb
S
7944 (idlwave-force-class-query (equal arg '(16)))
7945 (this-buffer (equal arg '(4)))
05a1abfc 7946 (module (idlwave-fix-module-if-obj_new (idlwave-what-module)))
52a244eb 7947 (default (if module
4b1aaa8b 7948 (concat (idlwave-make-full-name
52a244eb
S
7949 (nth 2 module) (car module))
7950 (if (eq (nth 1 module) 'pro) "<p>" "<f>"))
7951 "none"))
4b1aaa8b 7952 (list
52a244eb
S
7953 (idlwave-uniquify
7954 (delq nil
4b1aaa8b 7955 (mapcar (lambda (x)
52a244eb
S
7956 (if (eq 'system (car-safe (nth 3 x)))
7957 ;; Take out system routines with no source.
7958 nil
7959 (list
4b1aaa8b 7960 (concat (idlwave-make-full-name
52a244eb
S
7961 (nth 2 x) (car x))
7962 (if (eq (nth 1 x) 'pro) "<p>" "<f>")))))
7963 (if this-buffer
7964 (idlwave-save-buffer-update)
7965 (idlwave-routines))))))
f32b3b91 7966 (name (idlwave-completing-read
52a244eb
S
7967 (if (or (not this-buffer)
7968 (assoc default list))
7969 (format "Module (Default %s): " default)
7970 (format "Module in this file: "))
f32b3b91
CD
7971 list))
7972 type class)
7973 (if (string-match "\\`\\s-*\\'" name)
7974 ;; Nothing, use the default.
7975 (setq name default))
7976 (if (string-match "<[fp]>" name)
7977 (setq type (substring name -2 -1)
7978 name (substring name 0 -3)))
7979 (if (string-match "\\(.*\\)::\\(.*\\)" name)
7980 (setq class (match-string 1 name)
7981 name (match-string 2 name)))
7982 (setq name (idlwave-sintern-routine-or-method name class)
7983 class (idlwave-sintern-class class)
7984 type (cond ((equal type "f") 'fun)
7985 ((equal type "p") 'pro)
7986 (t t)))
52a244eb 7987 (idlwave-do-find-module name type class nil this-buffer)))
f32b3b91 7988
4b1aaa8b 7989(defun idlwave-do-find-module (name type class
52a244eb 7990 &optional force-source this-buffer)
f32b3b91 7991 (let ((name1 (idlwave-make-full-name class name))
4b1aaa8b 7992 source buf1 entry
f32b3b91 7993 (buf (current-buffer))
05a1abfc 7994 (pos (point))
52a244eb
S
7995 file name2)
7996 (setq entry (idlwave-best-rinfo-assq name type class (idlwave-routines)
7997 'WITH-FILE)
05a1abfc
CD
7998 source (or force-source (nth 3 entry))
7999 name2 (if (nth 2 entry)
8000 (idlwave-make-full-name (nth 2 entry) name)
775591f7 8001 name1))
4b1aaa8b 8002 (if source
52a244eb
S
8003 (setq file (idlwave-routine-source-file source)))
8004 (unless file ; Try to find it on the path.
4b1aaa8b
PE
8005 (setq file
8006 (idlwave-expand-lib-file-name
52a244eb
S
8007 (if class
8008 (format "%s__define.pro" (downcase class))
8009 (format "%s.pro" (downcase name))))))
f32b3b91
CD
8010 (cond
8011 ((or (null name) (equal name ""))
8012 (error "Abort"))
f32b3b91 8013 ((eq (car source) 'system)
4b1aaa8b 8014 (error "Source code for system routine %s is not available"
05a1abfc 8015 name2))
52a244eb 8016 ((or (not file) (not (file-regular-p file)))
e8af40ee 8017 (error "Source code for routine %s is not available"
05a1abfc 8018 name2))
52a244eb
S
8019 (t
8020 (when (not this-buffer)
4b1aaa8b 8021 (setq buf1
52a244eb
S
8022 (idlwave-find-file-noselect file 'find))
8023 (pop-to-buffer buf1 t))
15e42531 8024 (goto-char (point-max))
f32b3b91 8025 (let ((case-fold-search t))
15e42531 8026 (if (re-search-backward
f32b3b91 8027 (concat "^[ \t]*\\<"
52a244eb
S
8028 (cond ((eq type 'fun) "function")
8029 ((eq type 'pro) "pro")
f32b3b91 8030 (t "\\(pro\\|function\\)"))
4b1aaa8b 8031 "\\>[ \t]+"
05a1abfc 8032 (regexp-quote (downcase name2))
f32b3b91
CD
8033 "[^a-zA-Z0-9_$]")
8034 nil t)
8035 (goto-char (match-beginning 0))
8036 (pop-to-buffer buf)
8037 (goto-char pos)
05a1abfc 8038 (error "Could not find routine %s" name2)))))))
f32b3b91
CD
8039
8040(defun idlwave-what-module ()
8041 "Return a default module for stuff near point.
8042Used by `idlwave-routine-info' and `idlwave-find-module'."
8043 (idlwave-routines)
15e42531
CD
8044 (if (let ((case-fold-search t))
8045 (save-excursion
8046 (idlwave-beginning-of-statement)
8047 (looking-at "[ \t]*\\(pro\\|function\\)[ \t]+\\(\\([a-zA-Z0-9_$]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)\\([, \t\n]\\|$\\)")))
8048 ;; This is a function or procedure definition statement
8049 ;; We return the defined routine as module.
8050 (list
52a244eb
S
8051 (idlwave-sintern-routine-or-method (match-string-no-properties 4)
8052 (match-string-no-properties 2))
15e42531
CD
8053 (if (equal (downcase (match-string 1)) "pro") 'pro 'fun)
8054 (idlwave-sintern-class (match-string 3)))
8055
52a244eb 8056 ;; Not a definition statement - analyze precise position.
15e42531
CD
8057 (let* ((where (idlwave-where))
8058 (cw (nth 2 where))
8059 (pro (car (nth 0 where)))
8060 (func (car (nth 1 where)))
8061 (this-word (idlwave-this-word "a-zA-Z0-9$_"))
8062 (next-char (save-excursion (skip-chars-forward "a-zA-Z0-9$_")
8063 (following-char)))
8064 )
8065 (cond
8066 ((and (eq cw 'procedure)
8067 (not (equal this-word "")))
4b1aaa8b 8068 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8069 this-word (nth 2 (nth 3 where))))
8070 (list this-word 'pro
4b1aaa8b 8071 (idlwave-determine-class
15e42531
CD
8072 (cons this-word (cdr (nth 3 where)))
8073 'pro)))
4b1aaa8b 8074 ((and (eq cw 'function)
15e42531
CD
8075 (not (equal this-word ""))
8076 (or (eq next-char ?\() ; exclude arrays, vars.
8077 (looking-at "[a-zA-Z0-9_]*[ \t]*(")))
4b1aaa8b 8078 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8079 this-word (nth 2 (nth 3 where))))
8080 (list this-word 'fun
8081 (idlwave-determine-class
8082 (cons this-word (cdr (nth 3 where)))
8083 'fun)))
8084 ((and (memq cw '(function-keyword procedure-keyword))
8085 (not (equal this-word ""))
8086 (eq next-char ?\()) ; A function!
8087 (setq this-word (idlwave-sintern-routine this-word))
8088 (list this-word 'fun nil))
8089 (func
8090 (list func 'fun (idlwave-determine-class (nth 1 where) 'fun)))
8091 (pro
8092 (list pro 'pro (idlwave-determine-class (nth 0 where) 'pro)))
8093 (t nil)))))
f32b3b91 8094
05a1abfc 8095(defun idlwave-what-module-find-class ()
5a0c3f56 8096 "Call `idlwave-what-module' and find the inherited class if necessary."
05a1abfc 8097 (let* ((module (idlwave-what-module))
8d222148 8098 (class (nth 2 module)))
05a1abfc
CD
8099 (if (and (= (length module) 3)
8100 (stringp class))
8101 (list (car module)
8102 (nth 1 module)
8103 (apply 'idlwave-find-inherited-class module))
8104 module)))
8105
8106(defun idlwave-find-inherited-class (name type class)
8107 "Find the class which defines TYPE NAME and is CLASS or inherited by CLASS."
8108 (let ((entry (idlwave-best-rinfo-assoc name type class (idlwave-routines))))
8109 (if entry
8110 (nth 2 entry)
8111 class)))
8112
8113(defun idlwave-fix-module-if-obj_new (module)
4b1aaa8b 8114 "Check if MODULE points to obj_new.
52a244eb
S
8115If yes, and if the cursor is in the keyword region, change to the
8116appropriate Init method."
05a1abfc
CD
8117 (let* ((name (car module))
8118 (pos (point))
8119 (case-fold-search t)
8120 string)
8121 (if (and (stringp name)
8122 (equal (downcase name) "obj_new")
8123 (save-excursion
8124 (idlwave-beginning-of-statement)
8125 (setq string (buffer-substring (point) pos))
8126 (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8127 string)))
8128 (let ((name "Init")
8129 (class (match-string 1 string)))
8130 (setq module (list (idlwave-sintern-method "Init")
8131 'fun
8132 (idlwave-sintern-class class)))))
8133 module))
8134
4b1aaa8b 8135(defun idlwave-fix-keywords (name type class keywords
3938cb82 8136 &optional super-classes system)
52a244eb
S
8137 "Update a list of keywords.
8138Translate OBJ_NEW, adding all super-class keywords, or all keywords
5a0c3f56 8139from all classes if CLASS equals t. If SYSTEM is non-nil, don't
3938cb82 8140demand _EXTRA in the keyword list."
5e72c6b2 8141 (let ((case-fold-search t))
f32b3b91
CD
8142
8143 ;; If this is the OBJ_NEW function, try to figure out the class and use
8144 ;; the keywords from the corresponding INIT method.
5e72c6b2 8145 (if (and (equal (upcase name) "OBJ_NEW")
175069ef 8146 (derived-mode-p 'idlwave-mode 'idlwave-shell-mode))
f32b3b91
CD
8147 (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point)))
8148 (string (buffer-substring bos (point)))
8149 (case-fold-search t)
8150 class)
8151 (and (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8152 string)
8153 (setq class (idlwave-sintern-class (match-string 1 string)))
15e42531 8154 (setq idlwave-current-obj_new-class class)
4b1aaa8b
PE
8155 (setq keywords
8156 (append keywords
52a244eb
S
8157 (idlwave-entry-keywords
8158 (idlwave-rinfo-assq
8159 (idlwave-sintern-method "INIT")
8160 'fun
8161 class
8162 (idlwave-routines)) 'do-link))))))
4b1aaa8b 8163
f32b3b91
CD
8164 ;; If the class is `t', combine all keywords of all methods NAME
8165 (when (eq class t)
52a244eb
S
8166 (mapc (lambda (entry)
8167 (and
8168 (nth 2 entry) ; non-nil class
8169 (eq (nth 1 entry) type) ; correct type
4b1aaa8b
PE
8170 (setq keywords
8171 (append keywords
52a244eb
S
8172 (idlwave-entry-keywords entry 'do-link)))))
8173 (idlwave-all-assq name (idlwave-routines)))
5e72c6b2 8174 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8175
5e72c6b2 8176 ;; If we have inheritance, add all keywords from superclasses, if
52a244eb 8177 ;; the user indicated that method in `idlwave-keyword-class-inheritance'
4b1aaa8b 8178 (when (and
52a244eb 8179 super-classes
5e72c6b2
S
8180 idlwave-keyword-class-inheritance
8181 (stringp class)
4b1aaa8b 8182 (or
3938cb82
S
8183 system
8184 (assq (idlwave-sintern-keyword "_extra") keywords)
8185 (assq (idlwave-sintern-keyword "_ref_extra") keywords))
5e72c6b2
S
8186 ;; Check if one of the keyword-class regexps matches the name
8187 (let ((regexps idlwave-keyword-class-inheritance) re)
8188 (catch 'exit
8189 (while (setq re (pop regexps))
8190 (if (string-match re name) (throw 'exit t))))))
52a244eb
S
8191
8192 (loop for entry in (idlwave-routines) do
8193 (and (nth 2 entry) ; non-nil class
8194 (memq (nth 2 entry) super-classes) ; an inherited class
8195 (eq (nth 1 entry) type) ; correct type
8196 (eq (car entry) name) ; correct name
8ffcfb27
GM
8197 (mapc (lambda (k) (add-to-list 'keywords k))
8198 (idlwave-entry-keywords entry 'do-link))))
f32b3b91 8199 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8200
f32b3b91
CD
8201 ;; Return the final list
8202 keywords))
8203
15e42531 8204(defun idlwave-expand-keyword (keyword module)
2e8b9c7d 8205 "Expand KEYWORD to one of the valid keyword parameters of MODULE.
15e42531
CD
8206KEYWORD may be an exact match or an abbreviation of a keyword.
8207If the match is exact, KEYWORD itself is returned, even if there may be other
8208keywords of which KEYWORD is an abbreviation. This is necessary because some
8209system routines have keywords which are prefixes of other keywords.
8210If KEYWORD is an abbreviation of several keywords, a list of all possible
8211completions is returned.
8212If the abbreviation was unique, the correct keyword is returned.
8213If it cannot be a keyword, the function return nil.
8214If we do not know about MODULE, just return KEYWORD literally."
8215 (let* ((name (car module))
8216 (type (nth 1 module))
8217 (class (nth 2 module))
8218 (kwd (idlwave-sintern-keyword keyword))
8219 (entry (idlwave-best-rinfo-assoc name type class (idlwave-routines)))
52a244eb 8220 (kwd-alist (idlwave-entry-keywords entry))
15e42531
CD
8221 (extra (or (assq (idlwave-sintern-keyword "_EXTRA") kwd-alist)
8222 (assq (idlwave-sintern-keyword "_REF_EXTRA") kwd-alist)))
8223 (completion-ignore-case t)
8224 candidates)
4b1aaa8b 8225 (cond ((assq kwd kwd-alist)
15e42531
CD
8226 kwd)
8227 ((setq candidates (all-completions kwd kwd-alist))
8228 (if (= (length candidates) 1)
8229 (car candidates)
8230 candidates))
8231 ((and entry extra)
4b1aaa8b 8232 ;; Inheritance may cause this keyword to be correct
15e42531
CD
8233 keyword)
8234 (entry
8235 ;; We do know the function, which does not have the keyword.
8236 nil)
8237 (t
8238 ;; We do not know the function, so this just might be a correct
8239 ;; keyword - return it as it is.
8240 keyword))))
8241
b016851c
SM
8242(defvar idlwave-rinfo-mouse-map
8243 (let ((map (make-sparse-keymap)))
8244 (define-key map
8245 (if (featurep 'xemacs) [button2] [mouse-2])
8246 'idlwave-mouse-active-rinfo)
8247 (define-key map
8248 (if (featurep 'xemacs) [(shift button2)] [(shift mouse-2)])
8249 'idlwave-mouse-active-rinfo-shift)
8250 (define-key map
8251 (if (featurep 'xemacs) [button3] [mouse-3])
8252 'idlwave-mouse-active-rinfo-right)
8253 (define-key map " " 'idlwave-active-rinfo-space)
8254 (define-key map "q" 'idlwave-quit-help)
8255 map))
8256
8257(defvar idlwave-rinfo-map
8258 (let ((map (make-sparse-keymap)))
8259 (define-key map "q" 'idlwave-quit-help)
8260 map))
8261
15e42531
CD
8262(defvar idlwave-popup-source nil)
8263(defvar idlwave-rinfo-marker (make-marker))
8264
8265(defun idlwave-quit-help ()
8266 (interactive)
8267 (let ((ri-window (get-buffer-window "*Help*"))
8268 (olh-window (get-buffer-window "*IDLWAVE Help*")))
8269 (when (and olh-window
8270 (fboundp 'idlwave-help-quit))
8271 (select-window olh-window)
8272 (idlwave-help-quit))
8273 (when (window-live-p ri-window)
8274 (delete-window ri-window))))
f32b3b91 8275
05a1abfc
CD
8276(defun idlwave-display-calling-sequence (name type class
8277 &optional initial-class)
f32b3b91 8278 ;; Display the calling sequence of module NAME, type TYPE in class CLASS.
05a1abfc
CD
8279 (let* ((initial-class (or initial-class class))
8280 (entry (or (idlwave-best-rinfo-assq name type class
15e42531 8281 (idlwave-routines))
4b1aaa8b 8282 (idlwave-rinfo-assq name type class
15e42531 8283 idlwave-unresolved-routines)))
f32b3b91
CD
8284 (name (or (car entry) name))
8285 (class (or (nth 2 entry) class))
05a1abfc 8286 (superclasses (idlwave-all-class-inherits initial-class))
15e42531
CD
8287 (twins (idlwave-routine-twins entry))
8288 (dtwins (idlwave-study-twins twins))
8289 (all dtwins)
52a244eb 8290 (system (eq (car (nth 3 entry)) 'system))
f32b3b91 8291 (calling-seq (nth 4 entry))
52a244eb
S
8292 (keywords (idlwave-entry-keywords entry 'do-link))
8293 (html-file (car (nth 5 entry)))
15e42531 8294 (help-echo-kwd
52a244eb 8295 "Button2: Insert KEYWORD (SHIFT=`/KEYWORD') | Button3: Online Help ")
15e42531 8296 (help-echo-use
52a244eb 8297 "Button2/3: Online Help")
15e42531 8298 (help-echo-src
52a244eb 8299 "Button2: Jump to source and back | Button3: Source in Help window.")
05a1abfc
CD
8300 (help-echo-class
8301 "Button2: Display info about same method in superclass")
f32b3b91 8302 (col 0)
52a244eb 8303 (data (list name type class (current-buffer) nil initial-class))
f32b3b91 8304 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
f66f03de 8305 (face 'idlwave-help-link)
15e42531 8306 beg props win cnt total)
4b1aaa8b 8307 ;; Fix keywords, but don't add chained super-classes, since these
52a244eb 8308 ;; are shown separately for that super-class
f32b3b91
CD
8309 (setq keywords (idlwave-fix-keywords name type class keywords))
8310 (cond
8311 ((null entry)
05a1abfc
CD
8312 (error "No %s %s known %s" type name
8313 (if initial-class (concat "in class " initial-class) "")))
f32b3b91 8314 ((or (null name) (equal name ""))
e8af40ee 8315 (error "No function or procedure call at point"))
f32b3b91 8316 ((null calling-seq)
52a244eb 8317 (error "Calling sequence of %s %s not available" type name))
f32b3b91 8318 (t
9a529312
SM
8319 (move-marker idlwave-rinfo-marker (point))
8320 (with-current-buffer (get-buffer-create "*Help*")
15e42531 8321 (use-local-map idlwave-rinfo-map)
f32b3b91
CD
8322 (setq buffer-read-only nil)
8323 (erase-buffer)
8324 (set (make-local-variable 'idlwave-popup-source) nil)
15e42531
CD
8325 (set (make-local-variable 'idlwave-current-obj_new-class)
8326 idlwave-current-obj_new-class)
05a1abfc
CD
8327 (when superclasses
8328 (setq props (list 'mouse-face 'highlight
8329 km-prop idlwave-rinfo-mouse-map
8330 'help-echo help-echo-class
8331 'data (cons 'class data)))
8332 (let ((classes (cons initial-class superclasses)) c)
8333 (insert "Classes: ")
8334 (while (setq c (pop classes))
8335 (insert " ")
8336 (setq beg (point))
8337 (insert c)
8338 (if (equal (downcase c) (downcase class))
8339 (add-text-properties beg (point) (list 'face 'bold))
52a244eb 8340 ;; If Method exists in a different class link it
05a1abfc
CD
8341 (if (idlwave-rinfo-assq name type c (idlwave-routines))
8342 (add-text-properties beg (point) props))))
8343 (insert "\n")))
52a244eb
S
8344 (setq props (list 'mouse-face 'highlight
8345 km-prop idlwave-rinfo-mouse-map
8346 'help-echo help-echo-use
8347 'data (cons 'usage data)))
4b1aaa8b 8348 (if html-file (setq props (append (list 'face face 'link html-file)
52a244eb 8349 props)))
f32b3b91
CD
8350 (insert "Usage: ")
8351 (setq beg (point))
8352 (insert (if class
52a244eb
S
8353 (format calling-seq class name class name class name)
8354 (format calling-seq name name name name))
f32b3b91
CD
8355 "\n")
8356 (add-text-properties beg (point) props)
4b1aaa8b 8357
f32b3b91
CD
8358 (insert "Keywords:")
8359 (if (null keywords)
8360 (insert " No keywords accepted.")
8361 (setq col 9)
8ffcfb27 8362 (mapc
f32b3b91 8363 (lambda (x)
4b1aaa8b 8364 (if (>= (+ col 1 (length (car x)))
f32b3b91
CD
8365 (window-width))
8366 (progn
8367 (insert "\n ")
8368 (setq col 9)))
8369 (insert " ")
8370 (setq beg (point)
52a244eb 8371 ;; Relevant keywords already have link property attached
f32b3b91 8372 props (list 'mouse-face 'highlight
15e42531 8373 km-prop idlwave-rinfo-mouse-map
f32b3b91 8374 'data (cons 'keyword data)
15e42531 8375 'help-echo help-echo-kwd
f32b3b91 8376 'keyword (car x)))
52a244eb 8377 (if system (setq props (append (list 'face face) props)))
f32b3b91
CD
8378 (insert (car x))
8379 (add-text-properties beg (point) props)
8380 (setq col (+ col 1 (length (car x)))))
8381 keywords))
4b1aaa8b 8382
15e42531 8383 (setq cnt 1 total (length all))
52a244eb 8384 ;; Here entry is (key file (list of type-conses))
15e42531
CD
8385 (while (setq entry (pop all))
8386 (setq props (list 'mouse-face 'highlight
8387 km-prop idlwave-rinfo-mouse-map
8388 'help-echo help-echo-src
52a244eb
S
8389 'source (list (car (car (nth 2 entry))) ;type
8390 (nth 1 entry)
8391 nil
8392 (cdr (car (nth 2 entry))))
15e42531
CD
8393 'data (cons 'source data)))
8394 (idlwave-insert-source-location
4b1aaa8b 8395 (format "\n%-8s %s"
15e42531
CD
8396 (if (equal cnt 1)
8397 (if (> total 1) "Sources:" "Source:")
8398 "")
8399 (if (> total 1) "- " ""))
8400 entry props)
8401 (incf cnt)
8402 (when (and all (> cnt idlwave-rinfo-max-source-lines))
8403 ;; No more source lines, please
4b1aaa8b 8404 (insert (format
15e42531
CD
8405 "\n Source information truncated to %d entries."
8406 idlwave-rinfo-max-source-lines))
8407 (setq all nil)))
10c8e594 8408 (goto-char (point-min))
f32b3b91
CD
8409 (setq buffer-read-only t))
8410 (display-buffer "*Help*")
8411 (if (and (setq win (get-buffer-window "*Help*"))
8412 idlwave-resize-routine-help-window)
8413 (progn
8414 (let ((ww (selected-window)))
8415 (unwind-protect
8416 (progn
8417 (select-window win)
4b1aaa8b 8418 (enlarge-window (- (/ (frame-height) 2)
f32b3b91
CD
8419 (window-height)))
8420 (shrink-window-if-larger-than-buffer))
8421 (select-window ww)))))))))
8422
15e42531
CD
8423(defun idlwave-insert-source-location (prefix entry &optional file-props)
8424 "Insert a source location into the routine info buffer.
5a0c3f56
JB
8425Start line with PREFIX. If a file name is inserted, add FILE-PROPS
8426to it."
15e42531
CD
8427 (let* ((key (car entry))
8428 (file (nth 1 entry))
8429 (types (nth 2 entry))
52a244eb
S
8430 (shell-flag (assq 'compiled types))
8431 (buffer-flag (assq 'buffer types))
8432 (user-flag (assq 'user types))
8433 (lib-flag (assq 'lib types))
8434 (ndupl (or (and buffer-flag (idlwave-count-memq 'buffer types))
8435 (and user-flag (idlwave-count-memq 'user types))
8436 (and lib-flag (idlwave-count-memq 'lib types))
15e42531
CD
8437 1))
8438 (doflags t)
8439 beg special)
8440
8441 (insert prefix)
8442
8443 (cond
8444 ((eq key 'system)
8445 (setq doflags nil)
52a244eb
S
8446 (insert "System "))
8447
15e42531
CD
8448 ((eq key 'builtin)
8449 (setq doflags nil)
52a244eb
S
8450 (insert "Builtin "))
8451
15e42531 8452 ((and (not file) shell-flag)
52a244eb
S
8453 (insert "Unresolved"))
8454
4b1aaa8b 8455 ((null file)
52a244eb 8456 (insert "ERROR"))
4b1aaa8b 8457
15e42531
CD
8458 ((idlwave-syslib-p file)
8459 (if (string-match "obsolete" (file-name-directory file))
52a244eb
S
8460 (insert "Obsolete ")
8461 (insert "SystemLib ")))
8462
8463 ;; New special syntax: taken directly from routine-info for
8464 ;; library catalog routines
8465 ((setq special (or (cdr lib-flag) (cdr user-flag)))
8466 (insert (format "%-10s" special)))
8467
8468 ;; Old special syntax: a matching regexp
8469 ((setq special (idlwave-special-lib-test file))
8470 (insert (format "%-10s" special)))
4b1aaa8b 8471
52a244eb 8472 ;; Catch-all with file
15e42531 8473 ((idlwave-lib-p file) (insert "Library "))
52a244eb
S
8474
8475 ;; Sanity catch all
15e42531
CD
8476 (t (insert "Other ")))
8477
8478 (when doflags
8479 (insert (concat
8480 " ["
52a244eb
S
8481 (if lib-flag "L" "-")
8482 (if user-flag "C" "-")
15e42531
CD
8483 (if shell-flag "S" "-")
8484 (if buffer-flag "B" "-")
8485 "] ")))
4b1aaa8b 8486 (when (> ndupl 1)
15e42531
CD
8487 (setq beg (point))
8488 (insert (format "(%dx) " ndupl))
8489 (add-text-properties beg (point) (list 'face 'bold)))
8490 (when (and file (not (equal file "")))
8491 (setq beg (point))
8492 (insert (apply 'abbreviate-file-name
8493 (if (featurep 'xemacs) (list file t) (list file))))
8494 (if file-props
8495 (add-text-properties beg (point) file-props)))))
8496
8497(defun idlwave-special-lib-test (file)
8498 "Check the path of FILE against the regexps which define special libs.
8499Return the name of the special lib if there is a match."
8500 (let ((alist idlwave-special-lib-alist)
8501 entry rtn)
8502 (cond
8503 ((stringp file)
8504 (while (setq entry (pop alist))
8505 (if (string-match (car entry) file)
8506 (setq rtn (cdr entry)
8507 alist nil)))
8508 rtn)
8509 (t nil))))
4b1aaa8b 8510
f32b3b91
CD
8511(defun idlwave-mouse-active-rinfo-right (ev)
8512 (interactive "e")
8513 (idlwave-mouse-active-rinfo ev 'right))
8514
15e42531 8515(defun idlwave-mouse-active-rinfo-shift (ev)
f32b3b91 8516 (interactive "e")
15e42531
CD
8517 (idlwave-mouse-active-rinfo ev nil 'shift))
8518
8519(defun idlwave-active-rinfo-space ()
8520 (interactive)
8521 (idlwave-mouse-active-rinfo nil 'right))
8522
8523(defun idlwave-mouse-active-rinfo (ev &optional right shift)
5a0c3f56 8524 "Do the mouse actions in the routine info buffer.
15e42531
CD
8525Optional args RIGHT and SHIFT indicate, if mouse-3 was used, and if SHIFT
8526was pressed."
8527 (interactive "e")
8528 (if ev (mouse-set-point ev))
4b1aaa8b 8529 (let (data id name type class buf bufwin source link keyword
3938cb82 8530 word initial-class)
f32b3b91 8531 (setq data (get-text-property (point) 'data)
15e42531 8532 source (get-text-property (point) 'source)
f32b3b91 8533 keyword (get-text-property (point) 'keyword)
52a244eb 8534 link (get-text-property (point) 'link)
f32b3b91 8535 id (car data)
15e42531 8536 name (nth 1 data) type (nth 2 data) class (nth 3 data)
f32b3b91 8537 buf (nth 4 data)
05a1abfc
CD
8538 initial-class (nth 6 data)
8539 word (idlwave-this-word)
f32b3b91 8540 bufwin (get-buffer-window buf t))
52a244eb
S
8541
8542 (cond ((eq id 'class) ; Switch class being displayed
05a1abfc 8543 (if (window-live-p bufwin) (select-window bufwin))
4b1aaa8b 8544 (idlwave-display-calling-sequence
05a1abfc 8545 (idlwave-sintern-method name)
4b1aaa8b 8546 type (idlwave-sintern-class word)
05a1abfc 8547 initial-class))
52a244eb
S
8548 ((eq id 'usage) ; Online help on this routine
8549 (idlwave-online-help link name type class))
8550 ((eq id 'source) ; Source in help or buffer
8551 (if right ; In help
15e42531
CD
8552 (let ((idlwave-extra-help-function 'idlwave-help-with-source)
8553 (idlwave-help-source-try-header nil)
52a244eb 8554 ;; Fake idlwave-routines so help will find the right entry
15e42531 8555 (idlwave-routines
52a244eb 8556 (list (list name type class source ""))))
15e42531 8557 (idlwave-help-get-special-help name type class nil))
52a244eb 8558 ;; Otherwise just pop to the source
f32b3b91
CD
8559 (setq idlwave-popup-source (not idlwave-popup-source))
8560 (if idlwave-popup-source
8561 (condition-case err
15e42531 8562 (idlwave-do-find-module name type class source)
f32b3b91
CD
8563 (error
8564 (setq idlwave-popup-source nil)
8565 (if (window-live-p bufwin) (select-window bufwin))
8566 (error (nth 1 err))))
8567 (if bufwin
8568 (select-window bufwin)
15e42531
CD
8569 (pop-to-buffer buf))
8570 (goto-char (marker-position idlwave-rinfo-marker)))))
f32b3b91
CD
8571 ((eq id 'keyword)
8572 (if right
52a244eb 8573 (idlwave-online-help link name type class keyword)
15e42531
CD
8574 (idlwave-rinfo-insert-keyword keyword buf shift))))))
8575
8576(defun idlwave-rinfo-insert-keyword (keyword buffer &optional shift)
8577 "Insert KEYWORD in BUFFER. Make sure buffer is displayed in a window."
8578 (let ((bwin (get-buffer-window buffer)))
8579 (if idlwave-complete-empty-string-as-lower-case
8580 (setq keyword (downcase keyword)))
8581 (if bwin
8582 (select-window bwin)
8583 (pop-to-buffer buffer)
8584 (setq bwin (get-buffer-window buffer)))
8585 (if (eq (preceding-char) ?/)
8586 (insert keyword)
4b1aaa8b 8587 (unless (save-excursion
15e42531 8588 (re-search-backward
4b1aaa8b 8589 "[(,][ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\)?[ \t]*\\="
15e42531
CD
8590 (min (- (point) 100) (point-min)) t))
8591 (insert ", "))
8592 (if shift (insert "/"))
8593 (insert keyword)
8594 (if (and (not shift)
8595 idlwave-keyword-completion-adds-equal)
8596 (insert "=")))))
8597
8598(defun idlwave-list-buffer-load-path-shadows (&optional arg)
8599 "List the load path shadows of all routines defined in current buffer."
8600 (interactive "P")
8601 (idlwave-routines)
175069ef 8602 (if (derived-mode-p 'idlwave-mode)
15e42531
CD
8603 (idlwave-list-load-path-shadows
8604 nil (idlwave-update-current-buffer-info 'save-buffer)
8605 "in current buffer")
8606 (error "Current buffer is not in idlwave-mode")))
8607
8608(defun idlwave-list-shell-load-path-shadows (&optional arg)
8609 "List the load path shadows of all routines compiled under the shell.
8610This is very useful for checking an IDL application. Just compile the
8611application, do RESOLVE_ALL, and `C-c C-i' to compile all referenced
8612routines and update IDLWAVE internal info. Then check for shadowing
8613with this command."
8614 (interactive "P")
8615 (cond
8616 ((or (not (fboundp 'idlwave-shell-is-running))
8617 (not (idlwave-shell-is-running)))
8618 (error "Shell is not running"))
8619 ((null idlwave-compiled-routines)
e8af40ee 8620 (error "No compiled routines. Maybe you need to update with `C-c C-i'"))
15e42531
CD
8621 (t
8622 (idlwave-list-load-path-shadows nil idlwave-compiled-routines
8623 "in the shell"))))
8624
8625(defun idlwave-list-all-load-path-shadows (&optional arg)
8626 "List the load path shadows of all routines known to IDLWAVE."
8627 (interactive "P")
8628 (idlwave-list-load-path-shadows nil nil "globally"))
8629
8d222148
SM
8630(defvar idlwave-sort-prefer-buffer-info t
8631 "Internal variable used to influence `idlwave-routine-twin-compare'.")
8632
15e42531
CD
8633(defun idlwave-list-load-path-shadows (arg &optional special-routines loc)
8634 "List the routines which are defined multiple times.
8635Search the information IDLWAVE has about IDL routines for multiple
8636definitions.
8637When SPECIAL-ROUTINES in non-nil, only look for shadows of these routines.
8638
8639When IDL hits a routine call which is not defined, it will search on
5a0c3f56
JB
8640the load path in order to find a definition. The output of this command
8641can be used to detect possible name clashes during this process."
15e42531 8642 (idlwave-routines) ; Make sure everything is loaded.
52a244eb 8643 (unless (or idlwave-user-catalog-routines idlwave-library-catalog-routines)
4b1aaa8b 8644 (or (y-or-n-p
52a244eb 8645 "You don't have any user or library catalogs. Continue anyway? ")
15e42531
CD
8646 (error "Abort")))
8647 (let* ((routines (append idlwave-system-routines
8648 idlwave-compiled-routines
52a244eb
S
8649 idlwave-library-catalog-routines
8650 idlwave-user-catalog-routines
15e42531
CD
8651 idlwave-buffer-routines
8652 nil))
8653 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
8654 (keymap (make-sparse-keymap))
8655 (props (list 'mouse-face 'highlight
8656 km-prop keymap
4b1aaa8b 8657 'help-echo "Mouse2: Find source"))
15e42531 8658 (nroutines (length (or special-routines routines)))
f66f03de 8659 (step (/ nroutines 100))
15e42531 8660 (n 0)
15e42531
CD
8661 (cnt 0)
8662 (idlwave-sort-prefer-buffer-info nil)
8663 routine twins dtwins twin done props1 lroutines)
8664
8665 (if special-routines
8666 ;; Just looking for shadows of a few special routines
8667 (setq lroutines routines
8668 routines special-routines))
8669
8670 (message "Sorting routines...")
8671 (setq routines (sort routines
8672 (lambda (a b)
8673 (string< (downcase (idlwave-make-full-name
8674 (nth 2 a) (car a)))
8675 (downcase (idlwave-make-full-name
8676 (nth 2 b) (car b)))))))
8677 (message "Sorting routines...done")
8678
8679 (define-key keymap (if (featurep 'xemacs) [(button2)] [(mouse-2)])
4b1aaa8b 8680 (lambda (ev)
15e42531
CD
8681 (interactive "e")
8682 (mouse-set-point ev)
8683 (apply 'idlwave-do-find-module
8684 (get-text-property (point) 'find-args))))
8685 (define-key keymap [(return)]
4b1aaa8b 8686 (lambda ()
15e42531
CD
8687 (interactive)
8688 (apply 'idlwave-do-find-module
8689 (get-text-property (point) 'find-args))))
8690 (message "Compiling list...( 0%%)")
9a529312 8691 (with-current-buffer (get-buffer-create "*Shadows*")
15e42531
CD
8692 (setq buffer-read-only nil)
8693 (erase-buffer)
8694 (while (setq routine (pop routines))
f66f03de
S
8695 (if (= (mod (setq n (1+ n)) step) 0)
8696 (message "Compiling list...(%2d%%)" (/ (* n 100) nroutines)))
8697
15e42531
CD
8698 ;; Get a list of all twins
8699 (setq twins (idlwave-routine-twins routine (or lroutines routines)))
8700 (if (memq routine done)
8701 (setq dtwins nil)
8702 (setq dtwins (idlwave-study-twins twins)))
5e72c6b2 8703 ;; Mark all twins as dealt with
15e42531
CD
8704 (setq done (append twins done))
8705 (when (or (> (length dtwins) 1)
52a244eb
S
8706 (> (idlwave-count-memq 'lib (nth 2 (car dtwins))) 1)
8707 (> (idlwave-count-memq 'user (nth 2 (car dtwins))) 1)
8708 (> (idlwave-count-memq 'buffer (nth 2 (car dtwins))) 1))
15e42531
CD
8709 (incf cnt)
8710 (insert (format "\n%s%s"
4b1aaa8b 8711 (idlwave-make-full-name (nth 2 routine)
52a244eb 8712 (car routine))
15e42531
CD
8713 (if (eq (nth 1 routine) 'fun) "()" "")))
8714 (while (setq twin (pop dtwins))
8715 (setq props1 (append (list 'find-args
4b1aaa8b
PE
8716 (list (nth 0 routine)
8717 (nth 1 routine)
52a244eb 8718 (nth 2 routine)))
15e42531
CD
8719 props))
8720 (idlwave-insert-source-location "\n - " twin props1))))
8721 (goto-char (point-min))
8722 (setq buffer-read-only t))
8723 (setq loc (or loc ""))
8724 (if (> cnt 0)
8725 (progn
8726 (display-buffer (get-buffer "*Shadows*"))
8727 (message "%d case%s of shadowing found %s"
8728 cnt (if (= cnt 1) "" "s") loc))
8729 (message "No shadowing conflicts found %s" loc))))
8730
8731(defun idlwave-print-source (routine)
8732 (let* ((source (nth 3 routine))
8733 (stype (car source))
52a244eb
S
8734 (sfile (idlwave-routine-source-file source)))
8735 (if (idlwave-syslib-p sfile) (setq stype 'syslib))
15e42531
CD
8736 (if (and (eq stype 'compiled)
8737 (or (not (stringp sfile))
8738 (not (string-match "\\S-" sfile))))
8739 (setq stype 'unresolved))
4b1aaa8b 8740 (princ (format " %-10s %s\n"
15e42531
CD
8741 stype
8742 (if sfile sfile "No source code available")))))
8743
8744(defun idlwave-routine-twins (entry &optional list)
8745 "Return all twin entries of ENTRY in LIST.
8746LIST defaults to `idlwave-routines'.
8747Twin entries are those which have the same name, type, and class.
8748ENTRY will also be returned, as the first item of this list."
8749 (let* ((name (car entry))
8750 (type (nth 1 entry))
8751 (class (nth 2 entry))
8752 (candidates (idlwave-all-assq name (or list (idlwave-routines))))
8753 twins candidate)
8754 (while (setq candidate (pop candidates))
8755 (if (and (not (eq candidate entry))
8756 (eq type (nth 1 candidate))
8757 (eq class (nth 2 candidate)))
8758 (push candidate twins)))
4b1aaa8b 8759 (if (setq candidate (idlwave-rinfo-assq name type class
15e42531
CD
8760 idlwave-unresolved-routines))
8761 (push candidate twins))
8762 (cons entry (nreverse twins))))
8763
8764(defun idlwave-study-twins (entries)
4b1aaa8b 8765 "Return dangerous twins of first entry in ENTRIES.
52a244eb
S
8766Dangerous twins are routines with same name, but in different files on
8767the load path. If a file is in the system library and has an entry in
8768the `idlwave-system-routines' list, we omit the latter as
8769non-dangerous because many IDL routines are implemented as library
8770routines, and may have been scanned."
15e42531 8771 (let* ((entry (car entries))
e7c4fb1e 8772 (idlwave-twin-name (car entry)) ;
15e42531 8773 (type (nth 1 entry)) ; Must be bound for
e2a9c0bc 8774 (idlwave-twin-class (nth 2 entry)) ; idlwave-routine-twin-compare
15e42531 8775 (cnt 0)
52a244eb 8776 source type type-cons file alist syslibp key)
15e42531
CD
8777 (while (setq entry (pop entries))
8778 (incf cnt)
8779 (setq source (nth 3 entry)
8780 type (car source)
52a244eb
S
8781 type-cons (cons type (nth 3 source))
8782 file (idlwave-routine-source-file source))
8783
15e42531
CD
8784 ;; Make KEY to index entry properly
8785 (setq key (cond ((eq type 'system) type)
8786 (file (file-truename file))
8787 (t 'unresolved)))
52a244eb
S
8788
8789 ;; Check for an entry in the system library
4b1aaa8b 8790 (if (and file
15e42531
CD
8791 (not syslibp)
8792 (idlwave-syslib-p file))
15e42531 8793 (setq syslibp t))
4b1aaa8b 8794
52a244eb
S
8795 ;; If there's more than one matching entry for the same file, just
8796 ;; append the type-cons to the type list.
15e42531 8797 (if (setq entry (assoc key alist))
52a244eb
S
8798 (push type-cons (nth 2 entry))
8799 (push (list key file (list type-cons)) alist)))
4b1aaa8b 8800
15e42531 8801 (setq alist (nreverse alist))
4b1aaa8b 8802
15e42531 8803 (when syslibp
52a244eb
S
8804 ;; File is in system *library* - remove any 'system entry
8805 (setq alist (delq (assq 'system alist) alist)))
4b1aaa8b 8806
52a244eb
S
8807 ;; If 'system remains and we've scanned the syslib, it's a builtin
8808 ;; (rather than a !DIR/lib/.pro file bundled as source).
15e42531
CD
8809 (when (and (idlwave-syslib-scanned-p)
8810 (setq entry (assoc 'system alist)))
8811 (setcar entry 'builtin))
8812 (sort alist 'idlwave-routine-twin-compare)))
8813
8d222148
SM
8814;; FIXME: Dynamically scoped vars need to use the `idlwave-' prefix.
8815;; (defvar type)
15e42531
CD
8816(defmacro idlwave-xor (a b)
8817 `(and (or ,a ,b)
8818 (not (and ,a ,b))))
8819
8820(defun idlwave-routine-entry-compare (a b)
5a0c3f56
JB
8821 "Compare two routine info entries for sorting.
8822This is the general case. It first compares class, names, and type.
8823If it turns out that A and B are twins (same name, class, and type),
8824calls another routine which compares twins on the basis of their file
8825names and path locations."
15e42531
CD
8826 (let ((name (car a)) (type (nth 1 a)) (class (nth 2 a)))
8827 (cond
8828 ((not (equal (idlwave-downcase-safe class)
8829 (idlwave-downcase-safe (nth 2 b))))
8830 ;; Class decides
8831 (cond ((null (nth 2 b)) nil)
8832 ((null class) t)
8833 (t (string< (downcase class) (downcase (nth 2 b))))))
8834 ((not (equal (downcase name) (downcase (car b))))
8835 ;; Name decides
8836 (string< (downcase name) (downcase (car b))))
8837 ((not (eq type (nth 1 b)))
8838 ;; Type decides
8839 (< (if (eq type 'fun) 1 0) (if (eq (nth 1 b) 'fun) 1 0)))
4b1aaa8b 8840 (t
15e42531
CD
8841 ;; A and B are twins - so the decision is more complicated.
8842 ;; Call twin-compare with the proper arguments.
8843 (idlwave-routine-entry-compare-twins a b)))))
8844
8845(defun idlwave-routine-entry-compare-twins (a b)
5a0c3f56
JB
8846 "Compare two routine entries, under the assumption that they are twins.
8847This basically calls `idlwave-routine-twin-compare' with the correct args."
e7c4fb1e 8848 (let* ((idlwave-twin-name (car a))
e2a9c0bc
GM
8849 (type (nth 1 a))
8850 (idlwave-twin-class (nth 2 a)) ; used in idlwave-routine-twin-compare
52a244eb
S
8851 (asrc (nth 3 a))
8852 (atype (car asrc))
8853 (bsrc (nth 3 b))
8854 (btype (car bsrc))
8855 (afile (idlwave-routine-source-file asrc))
8856 (bfile (idlwave-routine-source-file bsrc)))
15e42531
CD
8857 (idlwave-routine-twin-compare
8858 (if (stringp afile)
8859 (list (file-truename afile) afile (list atype))
8860 (list atype afile (list atype)))
8861 (if (stringp bfile)
8862 (list (file-truename bfile) bfile (list btype))
e2a9c0bc 8863 (list btype bfile (list btype))))))
15e42531 8864
627e0a14 8865;; Bound in idlwave-study-twins,idlwave-routine-entry-compare-twins.
e2a9c0bc 8866(defvar idlwave-twin-class)
e7c4fb1e 8867(defvar idlwave-twin-name)
627e0a14 8868
15e42531
CD
8869(defun idlwave-routine-twin-compare (a b)
8870 "Compare two routine twin entries for sorting.
8871In here, A and B are not normal routine info entries, but special
8872lists (KEY FILENAME (TYPES...)).
e2a9c0bc 8873This expects NAME TYPE IDLWAVE-TWIN-CLASS to be bound to the right values."
15e42531
CD
8874 (let* (;; Dis-assemble entries
8875 (akey (car a)) (bkey (car b))
8876 (afile (nth 1 a)) (bfile (nth 1 b))
8877 (atypes (nth 2 a)) (btypes (nth 2 b))
8878 ;; System routines?
8879 (asysp (memq akey '(builtin system)))
8880 (bsysp (memq bkey '(builtin system)))
8881 ;; Compiled routines?
8882 (acompp (memq 'compiled atypes))
8883 (bcompp (memq 'compiled btypes))
8884 ;; Unresolved?
8885 (aunresp (or (eq akey 'unresolved)
8886 (and acompp (not afile))))
8887 (bunresp (or (eq bkey 'unresolved)
8888 (and bcompp (not bfile))))
8889 ;; Buffer info available?
8890 (abufp (memq 'buffer atypes))
8891 (bbufp (memq 'buffer btypes))
8892 ;; On search path?
8893 (tpath-alist (idlwave-true-path-alist))
52a244eb
S
8894 (apathp (and (stringp akey)
8895 (assoc (file-name-directory akey) tpath-alist)))
4b1aaa8b 8896 (bpathp (and (stringp bkey)
52a244eb 8897 (assoc (file-name-directory bkey) tpath-alist)))
15e42531
CD
8898 ;; How early on search path? High number means early since we
8899 ;; measure the tail of the path list
8900 (anpath (length (memq apathp tpath-alist)))
8901 (bnpath (length (memq bpathp tpath-alist)))
8902 ;; Look at file names
8903 (aname (if (stringp afile) (downcase (file-name-nondirectory afile)) ""))
8904 (bname (if (stringp bfile) (downcase (file-name-nondirectory bfile)) ""))
e2a9c0bc
GM
8905 (fname-re (if idlwave-twin-class
8906 (format "\\`%s__\\(%s\\|define\\)\\.pro\\'"
8907 (regexp-quote (downcase idlwave-twin-class))
e7c4fb1e
GM
8908 (regexp-quote (downcase idlwave-twin-name)))
8909 (format "\\`%s\\.pro" (regexp-quote (downcase idlwave-twin-name)))))
15e42531
CD
8910 ;; Is file name derived from the routine name?
8911 ;; Method file or class definition file?
8912 (anamep (string-match fname-re aname))
e2a9c0bc
GM
8913 (adefp (and idlwave-twin-class anamep
8914 (string= "define" (match-string 1 aname))))
15e42531 8915 (bnamep (string-match fname-re bname))
e2a9c0bc
GM
8916 (bdefp (and idlwave-twin-class bnamep
8917 (string= "define" (match-string 1 bname)))))
15e42531
CD
8918
8919 ;; Now: follow JD's ideas about sorting. Looks really simple now,
8920 ;; doesn't it? The difficult stuff is hidden above...
8921 (cond
8922 ((idlwave-xor asysp bsysp) asysp) ; System entries first
8923 ((idlwave-xor aunresp bunresp) bunresp) ; Unresolved last
8924 ((and idlwave-sort-prefer-buffer-info
8925 (idlwave-xor abufp bbufp)) abufp) ; Buffers before non-buffers
8926 ((idlwave-xor acompp bcompp) acompp) ; Compiled entries
8927 ((idlwave-xor apathp bpathp) apathp) ; Library before non-library
8928 ((idlwave-xor anamep bnamep) anamep) ; Correct file names first
e2a9c0bc 8929 ((and idlwave-twin-class anamep bnamep ; both file names match ->
15e42531
CD
8930 (idlwave-xor adefp bdefp)) bdefp) ; __define after __method
8931 ((> anpath bnpath) t) ; Who is first on path?
8932 (t nil)))) ; Default
8933
52a244eb 8934(defun idlwave-routine-source-file (source)
4b1aaa8b 8935 (if (nth 2 source)
52a244eb
S
8936 (expand-file-name (nth 1 source) (nth 2 source))
8937 (nth 1 source)))
8938
15e42531 8939(defun idlwave-downcase-safe (string)
dbdb7031 8940 "Downcase if string, else return unchanged."
15e42531
CD
8941 (if (stringp string)
8942 (downcase string)
8943 string))
8944
8945(defun idlwave-count-eq (elt list)
8946 "How often is ELT in LIST?"
8947 (length (delq nil (mapcar (lambda (x) (eq x elt)) list))))
8948
52a244eb
S
8949(defun idlwave-count-memq (elt alist)
8950 "How often is ELT a key in ALIST?"
8951 (length (delq nil (mapcar (lambda (x) (eq (car x) elt)) alist))))
8952
15e42531 8953(defun idlwave-syslib-p (file)
52a244eb 8954 "Non-nil if FILE is in the system library."
15e42531
CD
8955 (let* ((true-syslib (file-name-as-directory
8956 (file-truename
8957 (expand-file-name "lib" (idlwave-sys-dir)))))
8958 (true-file (file-truename file)))
8959 (string-match (concat "^" (regexp-quote true-syslib)) true-file)))
8960
8961(defun idlwave-lib-p (file)
5a0c3f56 8962 "Non-nil if FILE is in the library."
15e42531
CD
8963 (let ((true-dir (file-name-directory (file-truename file))))
8964 (assoc true-dir (idlwave-true-path-alist))))
8965
52a244eb
S
8966(defun idlwave-path-alist-add-flag (list-entry flag)
8967 "Add a flag to the path list entry, if not set."
8968 (let ((flags (cdr list-entry)))
8969 (add-to-list 'flags flag)
8970 (setcdr list-entry flags)))
8971
8972(defun idlwave-path-alist-remove-flag (list-entry flag)
8973 "Remove a flag to the path list entry, if set."
8974 (let ((flags (delq flag (cdr list-entry))))
8975 (setcdr list-entry flags)))
8976
15e42531
CD
8977(defun idlwave-true-path-alist ()
8978 "Return `idlwave-path-alist' alist with true-names.
52a244eb 8979Info is cached, but relies on the functions setting `idlwave-path-alist'
15e42531
CD
8980to reset the variable `idlwave-true-path-alist' to nil."
8981 (or idlwave-true-path-alist
8982 (setq idlwave-true-path-alist
8983 (mapcar (lambda(x) (cons
8984 (file-name-as-directory
8985 (file-truename
8986 (directory-file-name
8987 (car x))))
8988 (cdr x)))
8989 idlwave-path-alist))))
8990
8991(defun idlwave-syslib-scanned-p ()
8992 "Non-nil if the system lib file !DIR/lib has been scanned."
8993 (let* ((true-syslib (file-name-as-directory
8994 (file-truename
8995 (expand-file-name "lib" (idlwave-sys-dir))))))
8996 (cdr (assoc true-syslib (idlwave-true-path-alist)))))
8997
8998;; ----------------------------------------------------------------------------
8999;;
9000;; Online Help display
9001
f32b3b91
CD
9002
9003;; ----------------------------------------------------------------------------
9004;;
9005;; Additions for use with imenu.el and func-menu.el
9006;; (pop-up a list of IDL units in the current file).
9007;;
9008
9009(defun idlwave-prev-index-position ()
9010 "Search for the previous procedure or function.
9011Return nil if not found. For use with imenu.el."
9012 (save-match-data
9013 (cond
9014 ((idlwave-find-key "\\<\\(pro\\|function\\)\\>" -1 'nomark))
9015 ;; ((idlwave-find-key idlwave-begin-unit-reg 1 'nomark)
9016 (t nil))))
9017
9018(defun idlwave-unit-name ()
9019 "Return the unit name.
9020Assumes that point is at the beginning of the unit as found by
9021`idlwave-prev-index-position'."
9022 (forward-sexp 2)
9023 (forward-sexp -1)
9024 (let ((begin (point)))
4b1aaa8b 9025 (re-search-forward
52a244eb 9026 "[a-zA-Z_][a-zA-Z0-9$_]+\\(::[a-zA-Z_][a-zA-Z0-9$_]+\\)?")
f32b3b91
CD
9027 (if (fboundp 'buffer-substring-no-properties)
9028 (buffer-substring-no-properties begin (point))
9029 (buffer-substring begin (point)))))
9030
facebc7b
S
9031(defalias 'idlwave-function-menu
9032 (condition-case nil
f32b3b91
CD
9033 (progn
9034 (require 'func-menu)
facebc7b
S
9035 'function-menu)
9036 (error (condition-case nil
9037 (progn
9038 (require 'imenu)
9039 'imenu)
9040 (error nil)))))
f32b3b91 9041
52a244eb 9042;; Here we hack func-menu.el in order to support this new mode.
f32b3b91
CD
9043;; The latest versions of func-menu.el already have this stuff in, so
9044;; we hack only if it is not already there.
bdd779ec 9045(when (featurep 'xemacs)
f32b3b91
CD
9046 (eval-after-load "func-menu"
9047 '(progn
9048 (or (assq 'idlwave-mode fume-function-name-regexp-alist)
9049 (not (boundp 'fume-function-name-regexp-idl)) ; avoid problems
9050 (setq fume-function-name-regexp-alist
9051 (cons '(idlwave-mode . fume-function-name-regexp-idl)
9052 fume-function-name-regexp-alist)))
9053 (or (assq 'idlwave-mode fume-find-function-name-method-alist)
9054 (not (fboundp 'fume-find-next-idl-function-name)) ; avoid problems
9055 (setq fume-find-function-name-method-alist
9056 (cons '(idlwave-mode . fume-find-next-idl-function-name)
9057 fume-find-function-name-method-alist))))))
9058
9059(defun idlwave-edit-in-idlde ()
9060 "Edit the current file in IDL Development environment."
9061 (interactive)
9062 (start-process "idldeclient" nil
9063 idlwave-shell-explicit-file-name "-c" "-e"
f66f03de 9064 (buffer-file-name)))
4b1aaa8b 9065
f66f03de 9066(defvar idlwave-help-use-assistant)
f32b3b91
CD
9067(defun idlwave-launch-idlhelp ()
9068 "Start the IDLhelp application."
9069 (interactive)
f66f03de
S
9070 (if idlwave-help-use-assistant
9071 (idlwave-help-assistant-raise)
9072 (start-process "idlhelp" nil idlwave-help-application)))
4b1aaa8b 9073
f32b3b91
CD
9074;; Menus - using easymenu.el
9075(defvar idlwave-mode-menu-def
9076 `("IDLWAVE"
9077 ["PRO/FUNC menu" idlwave-function-menu t]
9078 ("Motion"
9079 ["Subprogram Start" idlwave-beginning-of-subprogram t]
9080 ["Subprogram End" idlwave-end-of-subprogram t]
9081 ["Block Start" idlwave-beginning-of-block t]
9082 ["Block End" idlwave-end-of-block t]
9083 ["Up Block" idlwave-backward-up-block t]
9084 ["Down Block" idlwave-down-block t]
9085 ["Skip Block Backward" idlwave-backward-block t]
9086 ["Skip Block Forward" idlwave-forward-block t])
9087 ("Mark"
9088 ["Subprogram" idlwave-mark-subprogram t]
9089 ["Block" idlwave-mark-block t]
9090 ["Header" idlwave-mark-doclib t])
9091 ("Format"
4b1aaa8b 9092 ["Indent Entire Statement" idlwave-indent-statement
f66f03de 9093 :active t :keys "C-u \\[indent-for-tab-command]" ]
f32b3b91 9094 ["Indent Subprogram" idlwave-indent-subprogram t]
f66f03de 9095 ["(Un)Comment Region" idlwave-toggle-comment-region t]
f32b3b91
CD
9096 ["Continue/Split line" idlwave-split-line t]
9097 "--"
9098 ["Toggle Auto Fill" idlwave-auto-fill-mode :style toggle
9099 :selected (symbol-value idlwave-fill-function)])
9100 ("Templates"
9101 ["Procedure" idlwave-procedure t]
9102 ["Function" idlwave-function t]
9103 ["Doc Header" idlwave-doc-header t]
9104 ["Log" idlwave-doc-modification t]
9105 "--"
9106 ["Case" idlwave-case t]
9107 ["For" idlwave-for t]
9108 ["Repeat" idlwave-repeat t]
9109 ["While" idlwave-while t]
9110 "--"
9111 ["Close Block" idlwave-close-block t])
15e42531 9112 ("Completion"
f32b3b91 9113 ["Complete" idlwave-complete t]
f66f03de 9114 ("Complete Specific"
f32b3b91
CD
9115 ["1 Procedure Name" (idlwave-complete 'procedure) t]
9116 ["2 Procedure Keyword" (idlwave-complete 'procedure-keyword) t]
9117 "--"
9118 ["3 Function Name" (idlwave-complete 'function) t]
9119 ["4 Function Keyword" (idlwave-complete 'function-keyword) t]
9120 "--"
9121 ["5 Procedure Method Name" (idlwave-complete 'procedure-method) t]
9122 ["6 Procedure Method Keyword" (idlwave-complete 'procedure-method-keyword) t]
9123 "--"
9124 ["7 Function Method Name" (idlwave-complete 'function-method) t]
9125 ["8 Function Method Keyword" (idlwave-complete 'function-method-keyword) t]
9126 "--"
15e42531
CD
9127 ["9 Class Name" idlwave-complete-class t]))
9128 ("Routine Info"
f32b3b91 9129 ["Show Routine Info" idlwave-routine-info t]
52a244eb 9130 ["Online Context Help" idlwave-context-help t]
f32b3b91
CD
9131 "--"
9132 ["Find Routine Source" idlwave-find-module t]
15e42531 9133 ["Resolve Routine" idlwave-resolve (featurep 'idlw-shell)]
f32b3b91
CD
9134 "--"
9135 ["Update Routine Info" idlwave-update-routine-info t]
f66f03de 9136 ["Rescan XML Help Catalog" idlwave-convert-xml-system-routine-info t]
f32b3b91 9137 "--"
52a244eb
S
9138 "IDL User Catalog"
9139 ["Select Catalog Directories" (idlwave-create-user-catalog-file nil) t]
15e42531 9140 ["Scan Directories" (idlwave-update-routine-info '(16))
5e72c6b2
S
9141 (and idlwave-path-alist (not idlwave-catalog-process))]
9142 ["Scan Directories &" (idlwave-update-routine-info '(64))
9143 (and idlwave-path-alist (not idlwave-catalog-process))]
15e42531
CD
9144 "--"
9145 "Routine Shadows"
9146 ["Check Current Buffer" idlwave-list-buffer-load-path-shadows t]
9147 ["Check Compiled Routines" idlwave-list-shell-load-path-shadows t]
9148 ["Check Everything" idlwave-list-all-load-path-shadows t])
9149 ("Misc"
9150 ["Kill auto-created buffers" idlwave-kill-autoloaded-buffers t]
9151 "--"
9152 ["Insert TAB character" idlwave-hard-tab t])
f32b3b91
CD
9153 "--"
9154 ("External"
f32b3b91
CD
9155 ["Start IDL shell" idlwave-shell t]
9156 ["Edit file in IDLDE" idlwave-edit-in-idlde t]
9157 ["Launch IDL Help" idlwave-launch-idlhelp t])
9158 "--"
9159 ("Customize"
9160 ["Browse IDLWAVE Group" idlwave-customize t]
9161 "--"
4b1aaa8b 9162 ["Build Full Customize Menu" idlwave-create-customize-menu
f32b3b91
CD
9163 (fboundp 'customize-menu-create)])
9164 ("Documentation"
9165 ["Describe Mode" describe-mode t]
9166 ["Abbreviation List" idlwave-list-abbrevs t]
9167 "--"
9168 ["Commentary in idlwave.el" idlwave-show-commentary t]
595ab50b 9169 ["Commentary in idlw-shell.el" idlwave-shell-show-commentary t]
f32b3b91
CD
9170 "--"
9171 ["Info" idlwave-info t]
9172 "--"
8c43762b 9173 ["Help with Topic" idlwave-help-assistant-help-with-topic
e08734e2 9174 idlwave-help-use-assistant]
f32b3b91
CD
9175 ["Launch IDL Help" idlwave-launch-idlhelp t])))
9176
9177(defvar idlwave-mode-debug-menu-def
9178 '("Debug"
9179 ["Start IDL shell" idlwave-shell t]
9180 ["Save and .RUN buffer" idlwave-shell-save-and-run
4b1aaa8b 9181 (and (boundp 'idlwave-shell-automatic-start)
f32b3b91
CD
9182 idlwave-shell-automatic-start)]))
9183
9184(if (or (featurep 'easymenu) (load "easymenu" t))
9185 (progn
4b1aaa8b
PE
9186 (easy-menu-define idlwave-mode-menu idlwave-mode-map
9187 "IDL and WAVE CL editing menu"
f32b3b91 9188 idlwave-mode-menu-def)
4b1aaa8b
PE
9189 (easy-menu-define idlwave-mode-debug-menu idlwave-mode-map
9190 "IDL and WAVE CL editing menu"
f32b3b91
CD
9191 idlwave-mode-debug-menu-def)))
9192
9193(defun idlwave-customize ()
5a0c3f56 9194 "Call the customize function with `idlwave' as argument."
f32b3b91 9195 (interactive)
4b1aaa8b 9196 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9197 ;; as well.
22d5821d
CD
9198 (or (featurep 'idlw-shell)
9199 (load "idlw-shell" t))
f32b3b91
CD
9200 (customize-browse 'idlwave))
9201
9202(defun idlwave-create-customize-menu ()
9203 "Create a full customization menu for IDLWAVE, insert it into the menu."
9204 (interactive)
9205 (if (fboundp 'customize-menu-create)
9206 (progn
4b1aaa8b 9207 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9208 ;; as well.
22d5821d
CD
9209 (or (featurep 'idlw-shell)
9210 (load "idlw-shell" t))
4b1aaa8b 9211 (easy-menu-change
f32b3b91
CD
9212 '("IDLWAVE") "Customize"
9213 `(["Browse IDLWAVE group" idlwave-customize t]
9214 "--"
9215 ,(customize-menu-create 'idlwave)
9216 ["Set" Custom-set t]
9217 ["Save" Custom-save t]
9218 ["Reset to Current" Custom-reset-current t]
9219 ["Reset to Saved" Custom-reset-saved t]
9220 ["Reset to Standard Settings" Custom-reset-standard t]))
9221 (message "\"IDLWAVE\"-menu now contains full customization menu"))
9222 (error "Cannot expand menu (outdated version of cus-edit.el)")))
9223
9224(defun idlwave-show-commentary ()
9225 "Use the finder to view the file documentation from `idlwave.el'."
9226 (interactive)
f32b3b91
CD
9227 (finder-commentary "idlwave.el"))
9228
9229(defun idlwave-shell-show-commentary ()
595ab50b 9230 "Use the finder to view the file documentation from `idlw-shell.el'."
f32b3b91 9231 (interactive)
595ab50b 9232 (finder-commentary "idlw-shell.el"))
f32b3b91
CD
9233
9234(defun idlwave-info ()
9235 "Read documentation for IDLWAVE in the info system."
9236 (interactive)
d6a277d0 9237 (info "idlwave"))
f32b3b91
CD
9238
9239(defun idlwave-list-abbrevs (arg)
9240 "Show the code abbreviations define in IDLWAVE mode.
9241This lists all abbrevs where the replacement text differs from the input text.
9242These are the ones the users want to learn to speed up their writing.
9243
9244The function does *not* list abbrevs which replace a word with itself
9245to call a hook. These hooks are used to change the case of words or
9246to blink the matching `begin', and the user does not need to know them.
9247
9248With arg, list all abbrevs with the corresponding hook.
9249
9250This function was written since `list-abbrevs' looks terrible for IDLWAVE mode."
9251
9252 (interactive "P")
9253 (let ((table (symbol-value 'idlwave-mode-abbrev-table))
9254 abbrevs
9255 str rpl func fmt (len-str 0) (len-rpl 0))
4b1aaa8b 9256 (mapatoms
f32b3b91
CD
9257 (lambda (sym)
9258 (if (symbol-value sym)
9259 (progn
9260 (setq str (symbol-name sym)
9261 rpl (symbol-value sym)
9262 func (symbol-function sym))
9263 (if arg
9264 (setq func (prin1-to-string func))
9265 (if (and (listp func) (stringp (nth 2 func)))
9266 (setq rpl (concat "EVAL: " (nth 2 func))
9267 func "")
9268 (setq func "")))
9269 (if (or arg (not (string= rpl str)))
9270 (progn
9271 (setq len-str (max len-str (length str)))
9272 (setq len-rpl (max len-rpl (length rpl)))
9273 (setq abbrevs (cons (list str rpl func) abbrevs)))))))
9274 table)
9275 ;; sort the list
9276 (setq abbrevs (sort abbrevs (lambda (a b) (string< (car a) (car b)))))
9277 ;; Make the format
9278 (setq fmt (format "%%-%ds %%-%ds %%s\n" len-str len-rpl))
9279 (with-output-to-temp-buffer "*Help*"
9280 (if arg
9281 (progn
4b1aaa8b 9282 (princ "Abbreviations and Actions in IDLWAVE-Mode\n")
f32b3b91
CD
9283 (princ "=========================================\n\n")
9284 (princ (format fmt "KEY" "REPLACE" "HOOK"))
9285 (princ (format fmt "---" "-------" "----")))
9286 (princ "Code Abbreviations and Templates in IDLWAVE-Mode\n")
9287 (princ "================================================\n\n")
9288 (princ (format fmt "KEY" "ACTION" ""))
9289 (princ (format fmt "---" "------" "")))
26b51db5
JB
9290 (dolist (list abbrevs)
9291 (setq str (car list)
9292 rpl (nth 1 list)
9293 func (nth 2 list))
9294 (princ (format fmt str rpl func)))))
f32b3b91 9295 ;; Make sure each abbreviation uses only one display line
9a529312 9296 (with-current-buffer "*Help*"
f32b3b91
CD
9297 (setq truncate-lines t)))
9298
bdd779ec
GM
9299(declare-function speedbar-add-supported-extension "speedbar" (extension))
9300
5e72c6b2
S
9301;; Add .pro files to speedbar for support, if it's loaded
9302(eval-after-load "speedbar" '(speedbar-add-supported-extension ".pro"))
9303
5e72c6b2
S
9304;; Set an idle timer to load the routine info.
9305;; Will only work on systems which support this.
9306(or idlwave-routines (idlwave-start-load-rinfo-timer))
9307
15e42531 9308;; Run the hook
f32b3b91
CD
9309(run-hooks 'idlwave-load-hook)
9310
9311(provide 'idlwave)
9312
9313;;; idlwave.el ends here