Replace end-of-line, save-excursion etc with point-at-eol, point-at-bol.
[bpt/emacs.git] / lisp / progmodes / idlwave.el
CommitLineData
52a244eb 1;; idlwave.el --- IDL editing mode for GNU Emacs
d7a0267c 2
1ba983e8 3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
114f9c96 4;; 2008, 2009, 2010 Free Software Foundation, Inc.
f32b3b91 5
52a244eb 6;; Authors: J.D. Smith <jdsmith@as.arizona.edu>
65363a4e 7;; Carsten Dominik <dominik@science.uva.nl>
52a244eb 8;; Chris Chase <chase@att.com>
5e72c6b2 9;; Maintainer: J.D. Smith <jdsmith@as.arizona.edu>
bd78fa1d 10;; Version: 6.1.22
f32b3b91
CD
11;; Keywords: languages
12
e8af40ee 13;; This file is part of GNU Emacs.
f32b3b91 14
b1fc2b50 15;; GNU Emacs is free software: you can redistribute it and/or modify
f32b3b91 16;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
17;; the Free Software Foundation, either version 3 of the License, or
18;; (at your option) any later version.
f32b3b91
CD
19
20;; GNU Emacs is distributed in the hope that it will be useful,
21;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23;; GNU General Public License for more details.
24
25;; You should have received a copy of the GNU General Public License
b1fc2b50 26;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
f32b3b91
CD
27
28;;; Commentary:
29
f66f03de
S
30;; IDLWAVE enables feature-rich development and interaction with IDL,
31;; the Interactive Data Language. It provides a compelling,
32;; full-featured alternative to the IDLDE development environment
33;; bundled with IDL.
3938cb82 34
52a244eb
S
35;; In the remotely distant past, based on pascal.el, though bears
36;; little resemblance to it now.
f32b3b91
CD
37;;
38;; Incorporates many ideas, such as abbrevs, action routines, and
39;; continuation line indenting, from wave.el.
40;; wave.el original written by Lubos Pochman, Precision Visuals, Boulder.
41;;
42;; See the mode description ("C-h m" in idlwave-mode or "C-h f idlwave-mode")
43;; for features, key bindings, and info.
44;; Also, Info format documentation is available with `M-x idlwave-info'
45;;
5e72c6b2
S
46;; New versions of IDLWAVE, documentation, and more information
47;; available from:
48;; http://idlwave.org
f32b3b91
CD
49;;
50;; INSTALLATION
51;; ============
52;;
53;; Follow the instructions in the INSTALL file of the distribution.
54;; In short, put this file on your load path and add the following
55;; lines to your .emacs file:
56;;
57;; (autoload 'idlwave-mode "idlwave" "IDLWAVE Mode" t)
8c7b4ec8 58;; (autoload 'idlwave-shell "idlw-shell" "IDLWAVE Shell" t)
f32b3b91
CD
59;; (setq auto-mode-alist (cons '("\\.pro\\'" . idlwave-mode) auto-mode-alist))
60;;
61;;
62;; SOURCE
63;; ======
64;;
76959b77 65;; The newest version of this file is available from the maintainer's
52a244eb 66;; Webpage:
f32b3b91 67;;
5e72c6b2 68;; http://idlwave.org
f32b3b91
CD
69;;
70;; DOCUMENTATION
71;; =============
72;;
52a244eb
S
73;; IDLWAVE is documented online in info format. A printable version
74;; of the documentation is available from the maintainers webpage (see
75;; SOURCE).
775591f7 76;;
4b1aaa8b 77;;
f32b3b91
CD
78;; ACKNOWLEDGMENTS
79;; ===============
80;;
81;; Thanks to the following people for their contributions and comments:
82;;
52a244eb
S
83;; Ulrik Dickow <dickow_at_nbi.dk>
84;; Eric E. Dors <edors_at_lanl.gov>
85;; Stein Vidar H. Haugan <s.v.h.haugan_at_astro.uio.no>
86;; David Huenemoerder <dph_at_space.mit.edu>
87;; Kevin Ivory <Kevin.Ivory_at_linmpi.mpg.de>
88;; Dick Jackson <dick_at_d-jackson.com>
89;; Xuyong Liu <liu_at_stsci.edu>
90;; Simon Marshall <Simon.Marshall_at_esrin.esa.it>
91;; Laurent Mugnier <mugnier_at_onera.fr>
92;; Lubos Pochman <lubos_at_rsinc.com>
93;; Bob Portmann <portmann_at_al.noaa.gov>
94;; Patrick M. Ryan <pat_at_jaameri.gsfc.nasa.gov>
95;; Marty Ryba <ryba_at_ll.mit.edu>
96;; Paul Sorenson <aardvark62_at_msn.com>
97;; Phil Sterne <sterne_at_dublin.llnl.gov>
98;; Phil Williams <williams_at_irc.chmcc.org>
f32b3b91
CD
99;;
100;; CUSTOMIZATION:
101;; =============
102;;
52a244eb
S
103;; IDLWAVE has extensive customize support; to learn about the
104;; variables which control the mode's behavior, use `M-x
105;; idlwave-customize'.
f32b3b91
CD
106;;
107;; You can set your own preferred values with Customize, or with Lisp
108;; code in .emacs. For an example of what to put into .emacs, check
52a244eb
S
109;; the TexInfo documentation or see a complete .emacs available at the
110;; website.
f32b3b91
CD
111;;
112;; KNOWN PROBLEMS:
113;; ==============
114;;
76959b77
S
115;; IDLWAVE support for the IDL-derived PV-WAVE CL language of Visual
116;; Numerics, Inc. is growing less and less complete as the two
117;; languages grow increasingly apart. The mode probably shouldn't
3938cb82 118;; even have "WAVE" in its title, but it's catchy, and was required
52a244eb 119;; to avoid conflict with the CORBA idl.el mode. Caveat WAVEor.
76959b77 120;;
f32b3b91
CD
121;; Moving the point backwards in conjunction with abbrev expansion
122;; does not work as I would like it, but this is a problem with
123;; emacs abbrev expansion done by the self-insert-command. It ends
124;; up inserting the character that expanded the abbrev after moving
125;; point backward, e.g., "\cl" expanded with a space becomes
126;; "LONG( )" with point before the close paren. This is solved by
4b1aaa8b 127;; using a temporary function in `post-command-hook' - not pretty,
595ab50b 128;; but it works.
f32b3b91
CD
129;;
130;; Tabs and spaces are treated equally as whitespace when filling a
131;; comment paragraph. To accomplish this, tabs are permanently
132;; replaced by spaces in the text surrounding the paragraph, which
133;; may be an undesirable side-effect. Replacing tabs with spaces is
134;; limited to comments only and occurs only when a comment
135;; paragraph is filled via `idlwave-fill-paragraph'.
136;;
52a244eb
S
137;; Muti-statement lines (using "&") on block begin and end lines can
138;; ruin the formatting. For example, multiple end statements on a
139;; line: endif & endif. Using "&" outside of block begin/end lines
140;; should be okay.
f32b3b91 141;;
76959b77
S
142;; Determining the expression at point for printing and other
143;; examination commands is somewhat rough: currently only fairly
144;; simple entities are found. You can always drag-select or examine
52a244eb 145;; a pre-selected region.
f32b3b91 146;;
f32b3b91
CD
147;; When forcing completion of method keywords, the initial
148;; query for a method has multiple entries for some methods. Would
595ab50b 149;; be too difficult to fix this hardly used case.
f32b3b91
CD
150;;
151\f
152;;; Code:
153
52a244eb 154
f32b3b91 155(eval-when-compile (require 'cl))
52a244eb
S
156(require 'idlw-help)
157
158;; For XEmacs
159(unless (fboundp 'line-beginning-position)
160 (defalias 'line-beginning-position 'point-at-bol))
161(unless (fboundp 'line-end-position)
162 (defalias 'line-end-position 'point-at-eol))
163(unless (fboundp 'char-valid-p)
164 (defalias 'char-valid-p 'characterp))
f66f03de
S
165(unless (fboundp 'match-string-no-properties)
166 (defalias 'match-string-no-properties 'match-string))
f32b3b91 167
3938cb82
S
168(if (not (fboundp 'cancel-timer))
169 (condition-case nil
170 (require 'timer)
171 (error nil)))
172
73e72da4
DN
173(declare-function idlwave-shell-get-path-info "idlw-shell")
174(declare-function idlwave-shell-temp-file "idlw-shell")
175(declare-function idlwave-shell-is-running "idlw-shell")
176(declare-function widget-value "wid-edit" (widget))
177(declare-function comint-dynamic-complete-filename "comint" ())
73e72da4 178
f32b3b91 179(defgroup idlwave nil
31b58798 180 "Major mode for editing IDL .pro files."
f32b3b91 181 :tag "IDLWAVE"
4b1aaa8b 182 :link '(url-link :tag "Home Page"
5e72c6b2 183 "http://idlwave.org")
595ab50b
CD
184 :link '(emacs-commentary-link :tag "Commentary in idlw-shell.el"
185 "idlw-shell.el")
f32b3b91
CD
186 :link '(emacs-commentary-link :tag "Commentary in idlwave.el" "idlwave.el")
187 :link '(custom-manual "(idlwave)Top")
188 :prefix "idlwave"
189 :group 'languages)
190
52a244eb 191
f32b3b91
CD
192;;; Variables for indentation behavior ---------------------------------------
193
194(defgroup idlwave-code-formatting nil
195 "Indentation and formatting options for IDLWAVE mode."
196 :group 'idlwave)
197
f66f03de 198(defcustom idlwave-main-block-indent 2
f32b3b91
CD
199 "*Extra indentation for the main block of code.
200That is the block between the FUNCTION/PRO statement and the END
201statement for that program unit."
202 :group 'idlwave-code-formatting
203 :type 'integer)
204
f66f03de 205(defcustom idlwave-block-indent 3
f32b3b91
CD
206 "*Extra indentation applied to block lines.
207If you change this, you probably also want to change `idlwave-end-offset'."
208 :group 'idlwave-code-formatting
209 :type 'integer)
210
f66f03de 211(defcustom idlwave-end-offset -3
f32b3b91
CD
212 "*Extra indentation applied to block END lines.
213A value equal to negative `idlwave-block-indent' will make END lines
214line up with the block BEGIN lines."
215 :group 'idlwave-code-formatting
216 :type 'integer)
217
f66f03de 218(defcustom idlwave-continuation-indent 3
f32b3b91
CD
219 "*Extra indentation applied to continuation lines.
220This extra offset applies to the first of a set of continuation lines.
5e72c6b2
S
221The following lines receive the same indentation as the first."
222 :group 'idlwave-code-formatting
223 :type 'integer)
224
f66f03de 225(defcustom idlwave-max-extra-continuation-indent 40
5e72c6b2
S
226 "*Maximum additional indentation for special continuation indent.
227Several special indentations are tried to help line up continuation
228lines in routine calls or definitions, other statements with
134b6671 229parentheses, or assignment statements. This variable specifies a
5e72c6b2
S
230maximum amount by which this special indentation can exceed the
231standard continuation indentation, otherwise defaulting to a fixed
232offset. Set to 0 to effectively disable all special continuation
233indentation, or to a large number (like 100) to enable it in all
52a244eb 234cases. See also `idlwave-indent-to-open-paren', which can override
5e72c6b2 235this variable."
f32b3b91
CD
236 :group 'idlwave-code-formatting
237 :type 'integer)
238
5e72c6b2 239(defcustom idlwave-indent-to-open-paren t
5a0c3f56
JB
240 "*Non-nil means, indent continuation lines to innermost open parenthesis.
241This indentation occurs even if otherwise disallowed by
5e72c6b2
S
242`idlwave-max-extra-continuation-indent'. Matching parens and the
243interleaving args are lined up. Example:
244
245 x = function_a(function_b(function_c( a, b, [1,2,3, $
246 4,5,6 $
247 ], $
248 c, d $
249 )))
250
251When this variable is nil, paren alignment may still occur, based on
5a0c3f56
JB
252the value of `idlwave-max-extra-continuation-indent', which, if zero,
253would yield:
5e72c6b2
S
254
255 x = function_a(function_b(function_c( a, b, [1,2,3, $
256 4,5,6 $
257 ], $
258 c, d $
259 )))"
5a0c3f56 260 :group 'idlwave-code-formatting
5e72c6b2
S
261 :type 'boolean)
262
52a244eb
S
263(defcustom idlwave-indent-parens-nested nil
264 "*Non-nil means, indent continuation lines with parens by nesting
265lines at consecutively deeper levels."
266 :group 'idlwave-code-formatting
267 :type 'boolean)
268
269
f32b3b91
CD
270(defcustom idlwave-hanging-indent t
271 "*If set non-nil then comment paragraphs are indented under the
272hanging indent given by `idlwave-hang-indent-regexp' match in the first line
273of the paragraph."
274 :group 'idlwave-code-formatting
275 :type 'boolean)
276
277(defcustom idlwave-hang-indent-regexp "- "
278 "*Regular expression matching the position of the hanging indent
5a0c3f56 279in the first line of a comment paragraph. The size of the indent
f32b3b91
CD
280extends to the end of the match for the regular expression."
281 :group 'idlwave-code-formatting
282 :type 'regexp)
283
284(defcustom idlwave-use-last-hang-indent nil
285 "*If non-nil then use last match on line for `idlwave-indent-regexp'."
286 :group 'idlwave-code-formatting
287 :type 'boolean)
288
289(defcustom idlwave-fill-comment-line-only t
290 "*If non-nil then auto fill will only operate on comment lines."
291 :group 'idlwave-code-formatting
292 :type 'boolean)
293
294(defcustom idlwave-auto-fill-split-string t
295 "*If non-nil then auto fill will split strings with the IDL `+' operator.
4b1aaa8b
PE
296When the line end falls within a string, string concatenation with the
297'+' operator will be used to distribute a long string over lines.
f32b3b91
CD
298If nil and a string is split then a terminal beep and warning are issued.
299
300This variable is ignored when `idlwave-fill-comment-line-only' is
301non-nil, since in this case code is not auto-filled."
302 :group 'idlwave-code-formatting
303 :type 'boolean)
304
305(defcustom idlwave-split-line-string t
306 "*If non-nil then `idlwave-split-line' will split strings with `+'.
307When the splitting point of a line falls inside a string, split the string
308using the `+' string concatenation operator. If nil and a string is
309split then a terminal beep and warning are issued."
310 :group 'idlwave-code-formatting
311 :type 'boolean)
312
313(defcustom idlwave-no-change-comment ";;;"
314 "*The indentation of a comment that starts with this regular
5a0c3f56 315expression will not be changed. Note that the indentation of a comment
f32b3b91
CD
316at the beginning of a line is never changed."
317 :group 'idlwave-code-formatting
318 :type 'string)
319
320(defcustom idlwave-begin-line-comment nil
321 "*A comment anchored at the beginning of line.
322A comment matching this regular expression will not have its
323indentation changed. If nil the default is \"^;\", i.e., any line
324beginning with a \";\". Expressions for comments at the beginning of
325the line should begin with \"^\"."
326 :group 'idlwave-code-formatting
327 :type '(choice (const :tag "Any line beginning with `;'" nil)
328 'regexp))
329
330(defcustom idlwave-code-comment ";;[^;]"
331 "*A comment that starts with this regular expression on a line by
332itself is indented as if it is a part of IDL code. As a result if
333the comment is not preceded by whitespace it is unchanged."
334 :group 'idlwave-code-formatting
335 :type 'regexp)
336
337;; Comments not matching any of the above will be indented as a
338;; right-margin comment, i.e., to a minimum of `comment-column'.
339
f32b3b91
CD
340;;; Routine Info and Completion ---------------------------------------
341
15e42531
CD
342(defgroup idlwave-routine-info nil
343 "Routine Info options for IDLWAVE mode."
f32b3b91
CD
344 :group 'idlwave)
345
52a244eb
S
346(defcustom idlwave-use-library-catalogs t
347 "*Non-nil means search the IDL path for library catalog files.
348
349These files, named .idlwave_catalog, document routine information for
350individual directories and libraries of IDL .pro files. Many popular
5a0c3f56
JB
351libraries come with catalog files by default, so leaving this on is
352usually a good idea."
52a244eb
S
353 :group 'idlwave-routine-info
354 :type 'boolean)
5e72c6b2
S
355
356(defcustom idlwave-init-rinfo-when-idle-after 10
5a0c3f56
JB
357 "*Seconds of idle time before routine info is automatically initialized.
358Initializing the routine info can take a long time, in particular if a
359large number of library catalogs are involved. When Emacs is idle for
360more than the number of seconds specified by this variable, it starts
361the initialization. The process is split into five steps, in order to
362keep work interruption as short as possible. If one of the steps
363finishes, and no user input has arrived in the mean time, initialization
364proceeds immediately to the next step. A good value for this variable
365is about 1/3 of the time initialization take in your setup. So if you
366have a fast machine and no problems with a slow network connection,
367don't hesitate to set this to 2 seconds. A value of 0 means, don't
368initialize automatically, but instead wait until routine information is
369needed, and initialize then."
5e72c6b2
S
370 :group 'idlwave-routine-info
371 :type 'number)
372
f32b3b91 373(defcustom idlwave-scan-all-buffers-for-routine-info t
15e42531
CD
374 "*Non-nil means, scan buffers for IDL programs when updating info.
375The scanning is done by the command `idlwave-update-routine-info'.
376The following values are allowed:
377
378nil Don't scan any buffers.
5a0c3f56 379t Scan all `idlwave-mode' buffers in the current editing session.
15e42531
CD
380current Scan only the current buffer, but no other buffers."
381 :group 'idlwave-routine-info
382 :type '(choice
383 (const :tag "No buffer" nil)
384 (const :tag "All buffers" t)
385 (const :tag "Current buffer only" 'current)))
f32b3b91
CD
386
387(defcustom idlwave-query-shell-for-routine-info t
388 "*Non-nil means query the shell for info about compiled routines.
389Querying the shell is useful to get information about compiled modules,
390and it is turned on by default. However, when you have a complete library
391scan, this is not necessary."
15e42531 392 :group 'idlwave-routine-info
f32b3b91
CD
393 :type 'boolean)
394
15e42531
CD
395(defcustom idlwave-auto-routine-info-updates
396 '(find-file save-buffer kill-buffer compile-buffer)
397 "*Controls under what circumstances routine info is updated automatically.
398Possible values:
399nil Never
400t All available
5a0c3f56 401\(...) A list of circumstances. Allowed members are:
15e42531
CD
402 find-file Add info for new IDLWAVE buffers.
403 save-buffer Update buffer info when buffer is saved
404 kill-buffer Remove buffer info when buffer gets killed
405 compile-buffer Update shell info after `idlwave-shell-save-and...'"
406 :group 'idlwave-routine-info
407 :type '(choice
408 (const :tag "Never" nil)
409 (const :tag "As often as possible" t)
410 (set :tag "Checklist" :greedy t
411 (const :tag "When visiting a file" find-file)
412 (const :tag "When saving a buffer" save-buffer)
413 (const :tag "After a buffer was killed" kill-buffer)
414 (const :tag "After a buffer was compiled successfully, update shell info" compile-buffer))))
4b1aaa8b 415
15e42531
CD
416(defcustom idlwave-rinfo-max-source-lines 5
417 "*Maximum number of source files displayed in the Routine Info window.
418When an integer, it is the maximum number of source files displayed.
5a0c3f56 419A value of t means to show all source files."
15e42531
CD
420 :group 'idlwave-routine-info
421 :type 'integer)
422
f32b3b91 423(defcustom idlwave-library-path nil
8c43762b 424 "Library path for Windows and MacOS (OS9). Not needed under UNIX.
f66f03de
S
425When selecting the directories to scan for IDL user catalog routine
426info, IDLWAVE can, under UNIX, query the shell for the exact search
427path \(the value of !PATH). However, under Windows and MacOS
8c43762b 428\(pre-OSX), the IDLWAVE shell does not work. In this case, this
f66f03de
S
429variable can be set to specify the paths where IDLWAVE can find PRO
430files. The shell will only be asked for a list of paths when this
431variable is nil. The value is a list of directories. A directory
432preceeded by a `+' will be searched recursively. If you set this
433variable on a UNIX system, the shell will not be queried. See also
434`idlwave-system-directory'."
15e42531 435 :group 'idlwave-routine-info
f32b3b91
CD
436 :type '(repeat (directory)))
437
15e42531 438(defcustom idlwave-system-directory ""
52a244eb
S
439 "The IDL system directory for Windows and MacOS. Not needed under
440UNIX. Set this to the value of the `!DIR' system variable in IDL.
441IDLWAVE uses this to find out which of the library routines belong to
442the official system library. All files inside the `lib' subdirectory
443are considered system library files - so don't install private stuff
444in this directory. On UNIX systems, IDLWAVE queries the shell for the
445value of `!DIR'. See also `idlwave-library-path'."
15e42531
CD
446 :group 'idlwave-routine-info
447 :type 'directory)
448
f66f03de 449;; Configuration files
4b1aaa8b 450(defcustom idlwave-config-directory
52a244eb
S
451 (convert-standard-filename "~/.idlwave")
452 "*Directory for configuration files and user-library catalog."
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
460(defvar idlwave-libinfo-file nil
461 "*Obsolete variable, no longer used.")
462
15e42531
CD
463(defcustom idlwave-special-lib-alist nil
464 "Alist of regular expressions matching special library directories.
465When listing routine source locations, IDLWAVE gives a short hint where
4b1aaa8b 466the file defining the routine is located. By default it lists `SystemLib'
15e42531
CD
467for routines in the system library `!DIR/lib' and `Library' for anything
468else. This variable can define additional types. The car of each entry
469is a regular expression matching the file name (they normally will match
470on the path). The cdr is the string to be used as identifier. Max 10
471chars are allowed."
472 :group 'idlwave-routine-info
473 :type '(repeat
474 (cons regexp string)))
475
52a244eb 476(defcustom idlwave-auto-write-paths t
4b1aaa8b 477 "Write out path (!PATH) and system directory (!DIR) info automatically.
52a244eb
S
478Path info is needed to locate library catalog files. If non-nil,
479whenever the path-list changes as a result of shell-query, etc., it is
480written to file. Otherwise, the menu option \"Write Paths\" can be
481used to force a write."
482 :group 'idlwave-routine-info
05a1abfc 483 :type 'boolean)
775591f7 484
15e42531
CD
485(defgroup idlwave-completion nil
486 "Completion options for IDLWAVE mode."
487 :prefix "idlwave"
488 :group 'idlwave)
489
f32b3b91
CD
490(eval-and-compile
491 (defconst idlwave-tmp
492 '(choice :tag "by applying the function"
493 (const upcase)
494 (const downcase)
495 (const capitalize)
496 (const preserve)
497 (symbol :tag "Other"))))
498
f32b3b91
CD
499(defcustom idlwave-completion-case '((routine . upcase)
500 (keyword . upcase)
501 (class . preserve)
502 (method . preserve))
503 "Association list setting the case of completed words.
504
505This variable determines the case (UPPER/lower/Capitalized...) of
506words inserted into the buffer by completion. The preferred case can
507be specified separately for routine names, keywords, classes and
4b1aaa8b 508methods.
f32b3b91
CD
509This alist should therefore have entries for `routine' (normal
510functions and procedures, i.e. non-methods), `keyword', `class', and
511`method'. Plausible values are
512
513upcase upcase whole word, like `BOX_CURSOR'
514downcase downcase whole word, like `read_ppm'
515capitalize capitalize each part, like `Widget_Control'
516preserve preserve case as is, like `IDLgrView'
517
518The value can also be any Emacs Lisp function which transforms the
519case of characters in a string.
520
521A value of `preserve' means that the case of the completed word is
522identical to the way it was written in the definition statement of the
523routine. This was implemented to allow for mixed-case completion, in
524particular of object classes and methods.
525If a completable word is defined in multiple locations, the meaning of
526`preserve' is not unique since the different definitions might be
527cased differently. Therefore IDLWAVE always takes the case of the
528*first* definition it encounters during routine info collection and
529uses the case derived from it consistently.
530
531Note that a lowercase-only string in the buffer will always be completed in
532lower case (but see the variable `idlwave-completion-force-default-case').
533
534After changing this variable, you need to either restart Emacs or press
535`C-u C-c C-i' to update the internal lists."
15e42531 536 :group 'idlwave-completion
f32b3b91
CD
537 :type `(repeat
538 (cons (symbol :tag "Derive completion case for")
539 ,idlwave-tmp)))
540
541(defcustom idlwave-completion-force-default-case nil
542 "*Non-nil means, completion will always honor `idlwave-completion-case'.
543When nil, only the completion of a mixed case or upper case string
544will honor the default settings in `idlwave-completion-case', while
545the completion of lower case strings will be completed entirely in
546lower case."
15e42531 547 :group 'idlwave-completion
f32b3b91
CD
548 :type 'boolean)
549
550(defcustom idlwave-complete-empty-string-as-lower-case nil
551 "*Non-nil means, the empty string is considered downcase for completion.
552The case of what is already in the buffer determines the case of completions.
553When this variable is non-nil, the empty string is considered to be downcase.
554Completing on the empty string then offers downcase versions of the possible
555completions."
15e42531 556 :group 'idlwave-completion
f32b3b91
CD
557 :type 'boolean)
558
559(defvar idlwave-default-completion-case-is-down nil
560 "Obsolete variable. See `idlwave-complete-empty-string-as-lower-case' and
561`idlwave-completion-case'.")
562
563(defcustom idlwave-buffer-case-takes-precedence nil
564 "*Non-nil means, the case of tokens in buffers dominates over system stuff.
565To make this possible, we need to re-case everything each time we update
566the routine info from the buffers. This is slow.
567The default is to consider the case given in the system and library files
568first which makes updating much faster."
15e42531
CD
569 :group 'idlwave-completion
570 :type 'boolean)
571
572(defcustom idlwave-highlight-help-links-in-completion t
573 "*Non-nil means, highlight completions for which system help is available.
574Help can then be accessed with mouse-3.
575This option is only effective when the online help system is installed."
576 :group 'idlwave-completion
f32b3b91
CD
577 :type 'boolean)
578
05a1abfc
CD
579(defcustom idlwave-support-inheritance t
580 "Non-nil means, treat inheritance with completion, online help etc.
cef6cafe 581When nil, IDLWAVE only knows about the native methods and tags of a class,
05a1abfc
CD
582not about inherited ones."
583 :group 'idlwave-routine-info
584 :type 'boolean)
585
5e72c6b2
S
586(defcustom idlwave-keyword-class-inheritance '("^[gs]etproperty$" "^init$")
587 "List of regular expressions for class-driven keyword inheritance.
588Keyword inheritance is often tied to class inheritance by \"chaining\"
589up the class tree. While it cannot be assumed that the presence of an
590_EXTRA or _REF_EXTRA symbol guarantees such chaining will occur, for
591certain methods this assumption is almost always true. The methods
592for which to assume this can be set here."
593 :group 'idlwave-routine-info
594 :type '(repeat (regexp :tag "Match method:")))
4b1aaa8b 595
5e72c6b2 596
f32b3b91
CD
597(defcustom idlwave-completion-show-classes 1
598 "*Number of classes to show when completing object methods and keywords.
599When completing methods or keywords for an object with unknown class,
2e8b9c7d 600the *Completions* buffer will show the valid classes for each completion
f32b3b91
CD
601like this:
602
603MyMethod <Class1,Class2,Class3>
604
605The value of this variable may be nil to inhibit display, or an integer to
606indicate the maximum number of classes to display.
607
608On XEmacs, a full list of classes will also be placed into a `help-echo'
609property on the competion items, so that the list of classes for the current
610item is displayed in the echo area. If the value of this variable is a
611negative integer, the `help-echo' property will be suppressed."
15e42531 612 :group 'idlwave-completion
f32b3b91
CD
613 :type '(choice (const :tag "Don't show" nil)
614 (integer :tag "Number of classes shown" 1)))
615
616(defcustom idlwave-completion-fontify-classes t
617 "*Non-nil means, fontify the classes in completions buffer.
618This makes it easier to distinguish the completion items from the extra
619class info listed. See `idlwave-completion-show-classes'."
15e42531 620 :group 'idlwave-completion
f32b3b91
CD
621 :type 'boolean)
622
623(defcustom idlwave-query-class '((method-default . nil)
624 (keyword-default . nil))
625 "Association list governing specification of object classes for completion.
626
5e72c6b2
S
627When IDLWAVE tries to complete object-oriented methods, it usually
628cannot determine the class of a given object from context. In order
629to provide the user with a correct list of methods or keywords, it
76959b77
S
630needs to determine the appropriate class. IDLWAVE has two ways of
631doing this (well, three ways if you count the shell... see
632`idlwave-shell-query-for-class'):
633
6341. Combine the items of all available classes which contain this
635 method for the purpose of completion. So when completing a method,
636 all methods of all known classes are available, and when completing
637 a keyword, all keywords allowed for this method in any class are
638 shown. This behavior is very much like normal completion and is
639 therefore the default. It works much better than one might think -
640 only for the INIT, GETPROPERTY and SETPROPERTY the keyword lists
641 become uncomfortably long. See also
5e72c6b2 642 `idlwave-completion-show-classes'.
f32b3b91
CD
643
6442. The second possibility is to ask the user on each occasion. To
645 make this less interruptive, IDLWAVE can store the class as a text
646 property on the object operator `->'. For a given object in the
647 source code, class selection will then be needed only once
648 - for example to complete the method. Keywords to the method can
649 then be completed directly, because the class is already known.
650 You will have to turn on the storage of the selected class
651 explicitly with the variable `idlwave-store-inquired-class'.
652
5e72c6b2
S
653This variable allows you to configure IDLWAVE's method and
654method-keyword completion behavior. Its value is an alist, which
655should contain at least two elements: (method-default . VALUE) and
facebc7b 656\(keyword-default . VALUE), where VALUE is either t or nil. These
5e72c6b2
S
657specify if the class should be found during method and keyword
658completion, respectively.
f32b3b91 659
4b1aaa8b 660The alist may have additional entries specifying exceptions from the
f32b3b91
CD
661keyword completion rule for specific methods, like INIT or
662GETPROPERTY. In order to turn on class specification for the INIT
663method, add an entry (\"INIT\" . t). The method name must be ALL-CAPS."
15e42531 664 :group 'idlwave-completion
f32b3b91
CD
665 :type '(list
666 (cons (const method-default)
667 (boolean :tag "Determine class when completing METHODS "))
668 (cons (const keyword-default)
669 (boolean :tag "Determine class when completing KEYWORDS "))
670 (repeat
671 :tag "Exceptions to defaults"
672 :inline t
673 (cons (string :tag "MODULE" :value "")
674 (boolean :tag "Determine class for this method")))))
675
f66f03de 676(defcustom idlwave-store-inquired-class t
f32b3b91
CD
677 "*Non-nil means, store class of a method call as text property on `->'.
678IDLWAVE sometimes has to ask the user for the class associated with a
679particular object method call. This happens during the commands
680`idlwave-routine-info' and `idlwave-complete', depending upon the
681value of the variable `idlwave-query-class'.
682
683When you specify a class, this information can be stored as a text
4b1aaa8b 684property on the `->' arrow in the source code, so that during the same
f32b3b91
CD
685editing session, IDLWAVE will not have to ask again. When this
686variable is non-nil, IDLWAVE will store and reuse the class information.
687The class stored can be checked and removed with `\\[idlwave-routine-info]'
688on the arrow.
689
690The default of this variable is nil, since the result of commands then
691is more predictable. However, if you know what you are doing, it can
692be nice to turn this on.
693
694An arrow which knows the class will be highlighted with
695`idlwave-class-arrow-face'. The command \\[idlwave-routine-info]
696displays (with prefix arg: deletes) the class stored on the arrow
697at point."
15e42531 698 :group 'idlwave-completion
f32b3b91
CD
699 :type 'boolean)
700
701(defcustom idlwave-class-arrow-face 'bold
702 "*Face to highlight object operator arrows `->' which carry a class property.
703When IDLWAVE stores a class name as text property on an object arrow
facebc7b 704\(see variable `idlwave-store-inquired-class', it highlights the arrow
f32b3b91 705with this font in order to remind the user that this arrow is special."
15e42531 706 :group 'idlwave-completion
f32b3b91
CD
707 :type 'symbol)
708
709(defcustom idlwave-resize-routine-help-window t
710 "*Non-nil means, resize the Routine-info *Help* window to fit the content."
15e42531 711 :group 'idlwave-completion
f32b3b91
CD
712 :type 'boolean)
713
714(defcustom idlwave-keyword-completion-adds-equal t
715 "*Non-nil means, completion automatically adds `=' after completed keywords."
15e42531 716 :group 'idlwave-completion
f32b3b91
CD
717 :type 'boolean)
718
719(defcustom idlwave-function-completion-adds-paren t
720 "*Non-nil means, completion automatically adds `(' after completed function.
0ff9b955 721nil means, don't add anything.
f32b3b91
CD
722A value of `2' means, also add the closing parenthesis and position cursor
723between the two."
15e42531 724 :group 'idlwave-completion
f32b3b91
CD
725 :type '(choice (const :tag "Nothing" nil)
726 (const :tag "(" t)
727 (const :tag "()" 2)))
728
729(defcustom idlwave-completion-restore-window-configuration t
730 "*Non-nil means, try to restore the window configuration after completion.
731When completion is not unique, Emacs displays a list of completions.
732This messes up your window configuration. With this variable set, IDLWAVE
733restores the old configuration after successful completion."
15e42531 734 :group 'idlwave-completion
f32b3b91
CD
735 :type 'boolean)
736
737;;; Variables for abbrev and action behavior -----------------------------
738
739(defgroup idlwave-abbrev-and-indent-action nil
740 "IDLWAVE performs actions when expanding abbreviations or indenting lines.
741The variables in this group govern this."
742 :group 'idlwave)
743
744(defcustom idlwave-do-actions nil
745 "*Non-nil means performs actions when indenting.
746The actions that can be performed are listed in `idlwave-indent-action-table'."
747 :group 'idlwave-abbrev-and-indent-action
748 :type 'boolean)
749
750(defcustom idlwave-abbrev-start-char "\\"
751 "*A single character string used to start abbreviations in abbrev mode.
752Possible characters to chose from: ~`\%
753or even '?'. '.' is not a good choice because it can make structure
754field names act like abbrevs in certain circumstances.
755
756Changes to this in `idlwave-mode-hook' will have no effect. Instead a user
757must set it directly using `setq' in the .emacs file before idlwave.el
758is loaded."
759 :group 'idlwave-abbrev-and-indent-action
760 :type 'string)
761
762(defcustom idlwave-surround-by-blank nil
763 "*Non-nil means, enable `idlwave-surround'.
595ab50b 764If non-nil, `=',`<',`>',`&',`,', `->' are surrounded with spaces by
f32b3b91
CD
765`idlwave-surround'.
766See help for `idlwave-indent-action-table' for symbols using `idlwave-surround'.
767
768Also see the default key bindings for keys using `idlwave-surround'.
769Keys are bound and made into actions calling `idlwave-surround' with
770`idlwave-action-and-binding'.
771See help for `idlwave-action-and-binding' for examples.
772
773Also see help for `idlwave-surround'."
774 :group 'idlwave-abbrev-and-indent-action
775 :type 'boolean)
776
777(defcustom idlwave-pad-keyword t
52a244eb
S
778 "*Non-nil means pad '=' in keywords (routine calls or defs) like assignment.
779Whenever `idlwave-surround' is non-nil then this affects how '=' is
780padded for keywords and for variables. If t, pad the same as for
781assignments. If nil then spaces are removed. With any other value,
782spaces are left unchanged."
f32b3b91 783 :group 'idlwave-abbrev-and-indent-action
15e42531
CD
784 :type '(choice
785 (const :tag "Pad like assignments" t)
786 (const :tag "Remove space near `='" nil)
787 (const :tag "Keep space near `='" 'keep)))
f32b3b91
CD
788
789(defcustom idlwave-show-block t
790 "*Non-nil means point blinks to block beginning for `idlwave-show-begin'."
791 :group 'idlwave-abbrev-and-indent-action
792 :type 'boolean)
793
794(defcustom idlwave-expand-generic-end nil
795 "*Non-nil means expand generic END to ENDIF/ENDELSE/ENDWHILE etc."
796 :group 'idlwave-abbrev-and-indent-action
797 :type 'boolean)
798
15e42531
CD
799(defcustom idlwave-reindent-end t
800 "*Non-nil means re-indent line after END was typed."
801 :group 'idlwave-abbrev-and-indent-action
802 :type 'boolean)
803
f32b3b91
CD
804(defcustom idlwave-abbrev-move t
805 "*Non-nil means the abbrev hook can move point.
5a0c3f56 806Set to nil by `idlwave-expand-region-abbrevs'. To see the abbrev
f32b3b91 807definitions, use the command `list-abbrevs', for abbrevs that move
5a0c3f56 808point. Moving point is useful, for example, to place point between
f32b3b91
CD
809parentheses of expanded functions.
810
811See `idlwave-check-abbrev'."
812 :group 'idlwave-abbrev-and-indent-action
813 :type 'boolean)
814
815(defcustom idlwave-abbrev-change-case nil
816 "*Non-nil means all abbrevs will be forced to either upper or lower case.
817If the value t, all expanded abbrevs will be upper case.
818If the value is 'down then abbrevs will be forced to lower case.
819If nil, the case will not change.
820If `idlwave-reserved-word-upcase' is non-nil, reserved words will always be
821upper case, regardless of this variable."
822 :group 'idlwave-abbrev-and-indent-action
823 :type 'boolean)
824
825(defcustom idlwave-reserved-word-upcase nil
826 "*Non-nil means, reserved words will be made upper case via abbrev expansion.
827If nil case of reserved words is controlled by `idlwave-abbrev-change-case'.
828Has effect only if in abbrev-mode."
829 :group 'idlwave-abbrev-and-indent-action
830 :type 'boolean)
831
832;;; Action/Expand Tables.
833;;
834;; The average user may have difficulty modifying this directly. It
835;; can be modified/set in idlwave-mode-hook, but it is easier to use
836;; idlwave-action-and-binding. See help for idlwave-action-and-binding for
837;; examples of how to add an action.
838;;
839;; The action table is used by `idlwave-indent-line' whereas both the
840;; action and expand tables are used by `idlwave-indent-and-action'. In
841;; general, the expand table is only used when a line is explicitly
842;; indented. Whereas, in addition to being used when the expand table
843;; is used, the action table is used when a line is indirectly
844;; indented via line splitting, auto-filling or a new line creation.
845;;
846;; Example actions:
847;;
848;; Capitalize system vars
849;; (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)
850;;
851;; Capitalize procedure name
852;; (idlwave-action-and-binding "\\<\\(pro\\|function\\)\\>[ \t]*\\<"
853;; '(capitalize-word 1) t)
854;;
855;; Capitalize common block name
856;; (idlwave-action-and-binding "\\<common\\>[ \t]+\\<"
857;; '(capitalize-word 1) t)
858;; Capitalize label
859;; (idlwave-action-and-binding (concat "^[ \t]*" idlwave-label)
860;; '(capitalize-word -1) t)
861
862(defvar idlwave-indent-action-table nil
863 "*Associated array containing action lists of search string (car),
5a0c3f56 864and function as a cdr. This table is used by `idlwave-indent-line'.
f32b3b91
CD
865See documentation for `idlwave-do-action' for a complete description of
866the action lists.
867
868Additions to the table are made with `idlwave-action-and-binding' when a
869binding is not requested.
870See help on `idlwave-action-and-binding' for examples.")
871
872(defvar idlwave-indent-expand-table nil
873 "*Associated array containing action lists of search string (car),
5a0c3f56
JB
874and function as a cdr. The table is used by the
875`idlwave-indent-and-action' function. See documentation for
f32b3b91
CD
876`idlwave-do-action' for a complete description of the action lists.
877
878Additions to the table are made with `idlwave-action-and-binding' when a
879binding is requested.
880See help on `idlwave-action-and-binding' for examples.")
881
882;;; Documentation header and history keyword ---------------------------------
883
884(defgroup idlwave-documentation nil
885 "Options for documenting IDLWAVE files."
886 :group 'idlwave)
887
888;; FIXME: make defcustom?
889(defvar idlwave-file-header
890 (list nil
891 ";+
892; NAME:
893;
894;
895;
896; PURPOSE:
897;
898;
899;
900; CATEGORY:
901;
902;
903;
904; CALLING SEQUENCE:
905;
906;
907;
908; INPUTS:
909;
910;
911;
912; OPTIONAL INPUTS:
913;
914;
915;
916; KEYWORD PARAMETERS:
917;
918;
919;
920; OUTPUTS:
921;
922;
923;
924; OPTIONAL OUTPUTS:
925;
926;
927;
928; COMMON BLOCKS:
929;
930;
931;
932; SIDE EFFECTS:
933;
934;
935;
936; RESTRICTIONS:
937;
938;
939;
940; PROCEDURE:
941;
942;
943;
944; EXAMPLE:
945;
946;
947;
948; MODIFICATION HISTORY:
949;
950;-
951")
952 "*A list (PATHNAME STRING) specifying the doc-header template to use for
5a0c3f56
JB
953summarizing a file. If PATHNAME is non-nil then this file will be included.
954Otherwise STRING is used. If nil, the file summary will be omitted.
f32b3b91
CD
955For example you might set PATHNAME to the path for the
956lib_template.pro file included in the IDL distribution.")
957
f66f03de 958(defcustom idlwave-header-to-beginning-of-file t
5e72c6b2
S
959 "*Non-nil means, the documentation header will always be at start of file.
960When nil, the header is positioned between the PRO/FUNCTION line of
961the current routine and the code, allowing several routine headers in
962a file."
963 :group 'idlwave-documentation
964 :type 'boolean)
965
f32b3b91
CD
966(defcustom idlwave-timestamp-hook 'idlwave-default-insert-timestamp
967 "*The hook function used to update the timestamp of a function."
968 :group 'idlwave-documentation
969 :type 'function)
970
971(defcustom idlwave-doc-modifications-keyword "HISTORY"
972 "*The modifications keyword to use with the log documentation commands.
973A ':' is added to the keyword end.
974Inserted by doc-header and used to position logs by doc-modification.
975If nil it will not be inserted."
976 :group 'idlwave-documentation
977 :type 'string)
978
979(defcustom idlwave-doclib-start "^;+\\+"
980 "*Regexp matching the start of a document library header."
981 :group 'idlwave-documentation
982 :type 'regexp)
983
984(defcustom idlwave-doclib-end "^;+-"
985 "*Regexp matching the end of a document library header."
986 :group 'idlwave-documentation
987 :type 'regexp)
988
989;;; External Programs -------------------------------------------------------
990
991(defgroup idlwave-external-programs nil
05a1abfc 992 "Path locations of external commands used by IDLWAVE."
f32b3b91
CD
993 :group 'idlwave)
994
f32b3b91 995(defcustom idlwave-shell-explicit-file-name "idl"
5e72c6b2 996 "*If non-nil, this is the command to run IDL.
f32b3b91 997Should be an absolute file path or path relative to the current environment
5e72c6b2 998execution search path. If you want to specify command line switches
5a0c3f56 999for the IDL program, use `idlwave-shell-command-line-options'.
5e72c6b2
S
1000
1001I know the name of this variable is badly chosen, but I cannot change
5a0c3f56 1002it without compromising backwards-compatibility."
f32b3b91
CD
1003 :group 'idlwave-external-programs
1004 :type 'string)
1005
f32b3b91 1006(defcustom idlwave-shell-command-line-options nil
5e72c6b2
S
1007 "*A list of command line options for calling the IDL program.
1008Since IDL is executed directly without going through a shell like /bin/sh,
1009this should be a list of strings like '(\"-rt=file\" \"-nw\") with a separate
1010string for each argument. But you may also give a single string which
1011contains the options whitespace-separated. Emacs will be kind enough to
1012split it for you."
1013 :type '(choice
1014 string
1015 (repeat (string :value "")))
f32b3b91
CD
1016 :group 'idlwave-external-programs)
1017
1018(defcustom idlwave-help-application "idlhelp"
f66f03de
S
1019 "*The external application providing reference help for programming.
1020Obsolete, if the IDL Assistant is being used for help."
f32b3b91
CD
1021 :group 'idlwave-external-programs
1022 :type 'string)
1023
05a1abfc
CD
1024;;; Some Shell variables which must be defined here.-----------------------
1025
1026(defcustom idlwave-shell-debug-modifiers '()
1027 "List of modifiers to be used for the debugging commands.
1028Will be used to bind debugging commands in the shell buffer and in all
1029source buffers. These are additional convenience bindings, the debugging
1030commands are always available with the `C-c C-d' prefix.
1031If you set this to '(control shift), this means setting a breakpoint will
1032be on `C-S-b', compiling a source file on `C-S-c' etc. Possible modifiers
1033are `control', `meta', `super', `hyper', `alt', and `shift'."
1034 :group 'idlwave-shell-general-setup
1035 :type '(set :tag "Specify modifiers"
1036 (const control)
1037 (const meta)
1038 (const super)
1039 (const hyper)
1040 (const alt)
1041 (const shift)))
1042
1043(defcustom idlwave-shell-automatic-start nil
5a0c3f56 1044 "*If non-nil attempt invoke `idlwave-shell' if not already running.
05a1abfc
CD
1045This is checked when an attempt to send a command to an
1046IDL process is made."
1047 :group 'idlwave-shell-general-setup
1048 :type 'boolean)
1049
f32b3b91
CD
1050;;; Miscellaneous variables -------------------------------------------------
1051
1052(defgroup idlwave-misc nil
1053 "Miscellaneous options for IDLWAVE mode."
8ec3bce0 1054 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
f32b3b91
CD
1055 :group 'idlwave)
1056
1057(defcustom idlwave-startup-message t
1058 "*Non-nil displays a startup message when `idlwave-mode' is first called."
1059 :group 'idlwave-misc
1060 :type 'boolean)
1061
4b1aaa8b 1062(defcustom idlwave-default-font-lock-items
facebc7b 1063 '(pros-and-functions batch-files idlwave-idl-keywords label goto
f32b3b91
CD
1064 common-blocks class-arrows)
1065 "Items which should be fontified on the default fontification level 2.
1066IDLWAVE defines 3 levels of fontification. Level 1 is very little, level 3
1067is everything and level 2 is specified by this list.
5a0c3f56
JB
1068This variable must be set before IDLWAVE gets loaded.
1069It is a list of symbols; the following symbols are allowed:
f32b3b91
CD
1070
1071pros-and-functions Procedure and Function definitions
1072batch-files Batch Files
facebc7b 1073idlwave-idl-keywords IDL Keywords
f32b3b91
CD
1074label Statement Labels
1075goto Goto Statements
1076common-blocks Common Blocks
1077keyword-parameters Keyword Parameters in routine definitions and calls
1078system-variables System Variables
1079fixme FIXME: Warning in comments (on XEmacs only v. 21.0 and up)
1080class-arrows Object Arrows with class property"
1081 :group 'idlwave-misc
1082 :type '(set
1083 :inline t :greedy t
1084 (const :tag "Procedure and Function definitions" pros-and-functions)
facebc7b
S
1085 (const :tag "Batch Files" batch-files)
1086 (const :tag "IDL Keywords (reserved words)" idlwave-idl-keywords)
1087 (const :tag "Statement Labels" label)
1088 (const :tag "Goto Statements" goto)
1089 (const :tag "Tags in Structure Definition" structtag)
1090 (const :tag "Structure Name" structname)
1091 (const :tag "Common Blocks" common-blocks)
1092 (const :tag "Keyword Parameters" keyword-parameters)
1093 (const :tag "System Variables" system-variables)
1094 (const :tag "FIXME: Warning" fixme)
f32b3b91
CD
1095 (const :tag "Object Arrows with class property " class-arrows)))
1096
1097(defcustom idlwave-mode-hook nil
1098 "Normal hook. Executed when a buffer is put into `idlwave-mode'."
1099 :group 'idlwave-misc
1100 :type 'hook)
1101
1102(defcustom idlwave-load-hook nil
1103 "Normal hook. Executed when idlwave.el is loaded."
1104 :group 'idlwave-misc
1105 :type 'hook)
1106
15e42531
CD
1107(defvar idlwave-experimental nil
1108 "Non-nil means turn on a few experimental features.
1109This variable is only for the maintainer, to test difficult stuff,
1110while still distributing stable releases.
1111As a user, you should not set this to t.")
1112
f32b3b91
CD
1113;;;
1114;;; End customization variables section
1115;;;
1116
1117;;; Non customization variables
1118
1119;;; font-lock mode - Additions by Phil Williams, Ulrik Dickow and
52a244eb 1120;;; Simon Marshall <simon_at_gnu.ai.mit.edu>
f32b3b91
CD
1121;;; and Carsten Dominik...
1122
76959b77 1123;; The following are the reserved words in IDL. Maybe we should
4b1aaa8b 1124;; highlight some more stuff as well?
76959b77
S
1125;; Procedure declarations. Fontify keyword plus procedure name.
1126(defvar idlwave-idl-keywords
4b1aaa8b 1127 ;; To update this regexp, update the list of keywords and
76959b77 1128 ;; evaluate the form.
4b1aaa8b 1129 ;; (insert
76959b77 1130 ;; (prin1-to-string
4b1aaa8b 1131 ;; (concat
76959b77 1132 ;; "\\<\\("
4b1aaa8b 1133 ;; (regexp-opt
52a244eb 1134 ;; '("||" "&&" "and" "or" "xor" "not"
4b1aaa8b 1135 ;; "eq" "ge" "gt" "le" "lt" "ne"
76959b77 1136 ;; "for" "do" "endfor"
4b1aaa8b 1137 ;; "if" "then" "endif" "else" "endelse"
76959b77
S
1138 ;; "case" "of" "endcase"
1139 ;; "switch" "break" "continue" "endswitch"
1140 ;; "begin" "end"
1141 ;; "repeat" "until" "endrep"
4b1aaa8b 1142 ;; "while" "endwhile"
76959b77
S
1143 ;; "goto" "return"
1144 ;; "inherits" "mod"
1145 ;; "compile_opt" "forward_function"
1146 ;; "on_error" "on_ioerror")) ; on_error is not officially reserved
1147 ;; "\\)\\>")))
52a244eb
S
1148 "\\<\\(&&\\|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\\|||\\)\\>")
1149
76959b77 1150
facebc7b 1151(let* (;; Procedure declarations. Fontify keyword plus procedure name.
f32b3b91
CD
1152 ;; Function declarations. Fontify keyword plus function name.
1153 (pros-and-functions
1154 '("\\<\\(function\\|pro\\)\\>[ \t]+\\(\\sw+\\(::\\sw+\\)?\\)"
1155 (1 font-lock-keyword-face)
1156 (2 font-lock-function-name-face nil t)))
1157
1158 ;; Common blocks
1159 (common-blocks
1160 '("\\<\\(common\\)\\>[ \t]*\\(\\sw+\\)?[ \t]*,?"
1161 (1 font-lock-keyword-face) ; "common"
1162 (2 font-lock-reference-face nil t) ; block name
f66f03de 1163 ("[ \t]*\\(\\sw+\\)[ ,]*"
f32b3b91 1164 ;; Start with point after block name and comma
4b1aaa8b 1165 (goto-char (match-end 0)) ; needed for XEmacs, could be nil
f32b3b91
CD
1166 nil
1167 (1 font-lock-variable-name-face) ; variable names
1168 )))
1169
1170 ;; Batch files
1171 (batch-files
1172 '("^[ \t]*\\(@[^ \t\n]+\\)" (1 font-lock-string-face)))
1173
1174 ;; FIXME warning.
1175 (fixme
1176 '("\\<FIXME:" (0 font-lock-warning-face t)))
1177
1178 ;; Labels
1179 (label
1180 '("^[ \t]*\\([a-zA-Z]\\sw*:\\)" (1 font-lock-reference-face)))
1181
1182 ;; The goto statement and its label
1183 (goto
1184 '("\\(goto\\)[ \t]*,[ \t]*\\([a-zA-Z]\\sw*\\)"
1185 (1 font-lock-keyword-face)
1186 (2 font-lock-reference-face)))
1187
52a244eb
S
1188 ;; Tags in structure definitions. Note that this definition
1189 ;; actually collides with labels, so we have to use the same
1190 ;; face. It also matches named subscript ranges,
1191 ;; e.g. vec{bottom:top]. No good way around this.
05a1abfc
CD
1192 (structtag
1193 '("\\<\\([a-zA-Z][a-zA-Z0-9_]*:\\)[^:]" (1 font-lock-reference-face)))
1194
1195 ;; Structure names
1196 (structname
1197 '("\\({\\|\\<inherits\\s-\\)\\s-*\\([a-zA-Z][a-zA-Z0-9_]*\\)[},\t \n]"
1198 (2 font-lock-function-name-face)))
1199
52a244eb 1200 ;; Keyword parameters, like /xlog or ,xrange=[]
f32b3b91 1201 ;; This is anchored to the comma preceeding the keyword.
595ab50b
CD
1202 ;; Treats continuation lines, works only during whole buffer
1203 ;; fontification. Slow, use it only in fancy fontification.
f32b3b91 1204 (keyword-parameters
0dc2be2f
S
1205 '("\\(,\\|[a-zA-Z0-9_](\\)[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\([ \t]*\\(;.*\\)?\n\\)*[ \t]*\\)?\\(/[a-zA-Z_]\\sw*\\|[a-zA-Z_]\\sw*[ \t]*=\\)"
1206 (6 font-lock-reference-face)))
f32b3b91 1207
595ab50b 1208 ;; System variables start with a bang.
f32b3b91 1209 (system-variables
15e42531 1210 '("\\(![a-zA-Z_0-9]+\\(\\.\\sw+\\)?\\)"
f32b3b91
CD
1211 (1 font-lock-variable-name-face)))
1212
1213 ;; Special and unusual operators (not used because too noisy)
8d222148
SM
1214 ;; (special-operators
1215 ;; '("[<>#]" (0 font-lock-keyword-face)))
f32b3b91
CD
1216
1217 ;; All operators (not used because too noisy)
8d222148
SM
1218 ;; (all-operators
1219 ;; '("[-*^#+<>/]" (0 font-lock-keyword-face)))
4b1aaa8b 1220
f32b3b91
CD
1221 ;; Arrows with text property `idlwave-class'
1222 (class-arrows
facebc7b
S
1223 '(idlwave-match-class-arrows (0 idlwave-class-arrow-face))))
1224
1225 (defconst idlwave-font-lock-keywords-1
1226 (list pros-and-functions batch-files)
1227 "Subdued level highlighting for IDLWAVE mode.")
f32b3b91 1228
facebc7b
S
1229 (defconst idlwave-font-lock-keywords-2
1230 (mapcar 'symbol-value idlwave-default-font-lock-items)
1231 "Medium level highlighting for IDLWAVE mode.")
f32b3b91 1232
facebc7b 1233 (defconst idlwave-font-lock-keywords-3
f32b3b91
CD
1234 (list pros-and-functions
1235 batch-files
76959b77 1236 idlwave-idl-keywords
f32b3b91 1237 label goto
05a1abfc
CD
1238 structtag
1239 structname
f32b3b91
CD
1240 common-blocks
1241 keyword-parameters
1242 system-variables
facebc7b
S
1243 class-arrows)
1244 "Gaudy level highlighting for IDLWAVE mode."))
f32b3b91
CD
1245
1246(defun idlwave-match-class-arrows (limit)
1247 ;; Match an object arrow with class property
1248 (and idlwave-store-inquired-class
1249 (re-search-forward "->" limit 'limit)
1250 (get-text-property (match-beginning 0) 'idlwave-class)))
1251
1252(defvar idlwave-font-lock-keywords idlwave-font-lock-keywords-2
1253 "Default expressions to highlight in IDLWAVE mode.")
1254
1255(defvar idlwave-font-lock-defaults
1256 '((idlwave-font-lock-keywords
4b1aaa8b 1257 idlwave-font-lock-keywords-1
f32b3b91
CD
1258 idlwave-font-lock-keywords-2
1259 idlwave-font-lock-keywords-3)
4b1aaa8b
PE
1260 nil t
1261 ((?$ . "w") (?_ . "w") (?. . "w") (?| . "w") (?& . "w"))
f32b3b91
CD
1262 beginning-of-line))
1263
4b1aaa8b 1264(put 'idlwave-mode 'font-lock-defaults
f32b3b91
CD
1265 idlwave-font-lock-defaults) ; XEmacs
1266
1267(defconst idlwave-comment-line-start-skip "^[ \t]*;"
1268 "Regexp to match the start of a full-line comment.
1269That is the _beginning_ of a line containing a comment delimiter `;' preceded
1270only by whitespace.")
1271
4b1aaa8b 1272(defconst idlwave-begin-block-reg
05a1abfc 1273 "\\<\\(pro\\|function\\|begin\\|case\\|switch\\)\\>"
5a0c3f56
JB
1274 "Regular expression to find the beginning of a block.
1275The case does not matter. The search skips matches in comments.")
f32b3b91 1276
52a244eb 1277(defconst idlwave-begin-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\`"
5a0c3f56
JB
1278 "Regular expression to find the beginning of a unit.
1279The case does not matter.")
f32b3b91 1280
52a244eb 1281(defconst idlwave-end-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\'"
f32b3b91 1282 "Regular expression to find the line that indicates the end of unit.
5a0c3f56
JB
1283This line is the end of buffer or the start of another unit.
1284The case does not matter. The search skips matches in comments.")
f32b3b91
CD
1285
1286(defconst idlwave-continue-line-reg "\\<\\$"
1287 "Regular expression to match a continued line.")
1288
1289(defconst idlwave-end-block-reg
05a1abfc 1290 "\\<end\\(\\|case\\|switch\\|else\\|for\\|if\\|rep\\|while\\)\\>"
5a0c3f56
JB
1291 "Regular expression to find the end of a block.
1292The case does not matter. The search skips matches in comments.")
f32b3b91
CD
1293
1294(defconst idlwave-block-matches
1295 '(("pro" . "end")
1296 ("function" . "end")
1297 ("case" . "endcase")
1298 ("else" . "endelse")
1299 ("for" . "endfor")
1300 ("then" . "endif")
1301 ("repeat" . "endrep")
05a1abfc 1302 ("switch" . "endswitch")
f32b3b91
CD
1303 ("while" . "endwhile"))
1304 "Matches between statements and the corresponding END variant.
1305The cars are the reserved words starting a block. If the block really
1306begins with BEGIN, the cars are the reserved words before the begin
1307which can be used to identify the block type.
1308This is used to check for the correct END type, to close blocks and
1309to expand generic end statements to their detailed form.")
1310
1311(defconst idlwave-block-match-regexp
1312 "\\<\\(else\\|for\\|then\\|repeat\\|while\\)\\>"
1313"Regular expression matching reserved words which can stand before
1314blocks starting with a BEGIN statement. The matches must have associations
5a0c3f56 1315`idlwave-block-matches'.")
f32b3b91 1316
52a244eb 1317(defconst idlwave-identifier "[a-zA-Z_][a-zA-Z0-9$_]*"
f32b3b91
CD
1318 "Regular expression matching an IDL identifier.")
1319
1320(defconst idlwave-sysvar (concat "!" idlwave-identifier)
1321 "Regular expression matching IDL system variables.")
1322
1323(defconst idlwave-variable (concat idlwave-identifier "\\|" idlwave-sysvar)
1324 "Regular expression matching IDL variable names.")
1325
1326(defconst idlwave-label (concat idlwave-identifier ":")
1327 "Regular expression matching IDL labels.")
1328
52a244eb
S
1329(defconst idlwave-method-call (concat idlwave-identifier "\\s *->"
1330 "\\(\\s *" idlwave-identifier "::\\)?"
1331))
1332
f32b3b91
CD
1333(defconst idlwave-statement-match
1334 (list
aa87aafc 1335 ;; "endif else" is the only possible "end" that can be
f32b3b91
CD
1336 ;; followed by a statement on the same line.
1337 '(endelse . ("end\\(\\|if\\)\\s +else" "end\\(\\|if\\)\\s +else"))
1338 ;; all other "end"s can not be followed by a statement.
1339 (cons 'end (list idlwave-end-block-reg nil))
1340 '(if . ("if\\>" "then"))
1341 '(for . ("for\\>" "do"))
1342 '(begin . ("begin\\>" nil))
1343 '(pdef . ("pro\\>\\|function\\>" nil))
1344 '(while . ("while\\>" "do"))
1345 '(repeat . ("repeat\\>" "repeat"))
1346 '(goto . ("goto\\>" nil))
1347 '(case . ("case\\>" nil))
05a1abfc 1348 '(switch . ("switch\\>" nil))
4b1aaa8b 1349 (cons 'call (list (concat "\\(" idlwave-variable "\\) *= *"
52a244eb
S
1350 "\\(" idlwave-method-call "\\s *\\)?"
1351 idlwave-identifier
1352 "\\s *(") nil))
4b1aaa8b 1353 (cons 'call (list (concat
52a244eb 1354 "\\(" idlwave-method-call "\\s *\\)?"
4b1aaa8b 1355 idlwave-identifier
52a244eb 1356 "\\( *\\($\\|\\$\\)\\|\\s *,\\)") nil))
4b1aaa8b 1357 (cons 'assign (list (concat
52a244eb 1358 "\\(" idlwave-variable "\\) *=") nil)))
4b1aaa8b 1359
f32b3b91 1360 "Associated list of statement matching regular expressions.
5a0c3f56
JB
1361Each regular expression matches the start of an IDL statement.
1362The first element of each association is a symbol giving the statement
f32b3b91
CD
1363type. The associated value is a list. The first element of this list
1364is a regular expression matching the start of an IDL statement for
1365identifying the statement type. The second element of this list is a
1366regular expression for finding a substatement for the type. The
1367substatement starts after the end of the found match modulo
1368whitespace. If it is nil then the statement has no substatement. The
1369list order matters since matching an assignment statement exactly is
1370not possible without parsing. Thus assignment statement become just
5a0c3f56 1371the leftover unidentified statements containing an equal sign.")
f32b3b91 1372
f44379e7 1373;; FIXME: This var seems to only ever be set, but never actually used!
f32b3b91
CD
1374(defvar idlwave-fill-function 'auto-fill-function
1375 "IDL mode auto fill function.")
1376
1377(defvar idlwave-comment-indent-function 'comment-indent-function
1378 "IDL mode comment indent function.")
1379
1380;; Note that this is documented in the v18 manuals as being a string
1381;; of length one rather than a single character.
1382;; The code in this file accepts either format for compatibility.
4b1aaa8b 1383(defvar idlwave-comment-indent-char ?\
f32b3b91
CD
1384 "Character to be inserted for IDL comment indentation.
1385Normally a space.")
1386
1387(defconst idlwave-continuation-char ?$
1388 "Character which is inserted as a last character on previous line by
1389 \\[idlwave-split-line] to begin a continuation line. Normally $.")
1390
e08734e2 1391(defconst idlwave-mode-version "6.1_em22")
f32b3b91
CD
1392
1393(defmacro idlwave-keyword-abbrev (&rest args)
1394 "Creates a function for abbrev hooks to call `idlwave-check-abbrev' with args."
8a946354 1395 `(quote (lambda ()
5e72c6b2 1396 ,(append '(idlwave-check-abbrev) args))))
f32b3b91
CD
1397
1398;; If I take the time I can replace idlwave-keyword-abbrev with
1399;; idlwave-code-abbrev and remove the quoted abbrev check from
1400;; idlwave-check-abbrev. Then, e.g, (idlwave-keyword-abbrev 0 t) becomes
1401;; (idlwave-code-abbrev idlwave-check-abbrev 0 t). In fact I should change
1402;; the name of idlwave-check-abbrev to something like idlwave-modify-abbrev.
1403
1404(defmacro idlwave-code-abbrev (&rest args)
1405 "Creates a function for abbrev hooks that ensures abbrevs are not quoted.
1406Specifically, if the abbrev is in a comment or string it is unexpanded.
1407Otherwise ARGS forms a list that is evaluated."
8d222148
SM
1408 ;; FIXME: it would probably be better to rely on the new :enable-function
1409 ;; to enforce the "don't expand in comments or strings".
1410 `(lambda ()
1411 ,(prin1-to-string args) ;; Puts the code in the doc string
1412 (if (idlwave-quoted)
1413 (progn (unexpand-abbrev) nil)
1414 ,(append args))))
1415
1416(autoload 'idlwave-shell "idlw-shell"
1417 "Run an inferior IDL, with I/O through buffer `(idlwave-shell-buffer)'." t)
1418(autoload 'idlwave-shell-send-command "idlw-shell")
1419(autoload 'idlwave-shell-recenter-shell-window "idlw-shell"
1420 "Run `idlwave-shell' and switch back to current window" t)
1421(autoload 'idlwave-shell-save-and-run "idlw-shell"
1422 "Save and run buffer under the shell." t)
1423(autoload 'idlwave-shell-break-here "idlw-shell"
1424 "Set breakpoint in current line." t)
1425(autoload 'idlwave-shell-run-region "idlw-shell"
1426 "Compile and run the region." t)
f32b3b91 1427
8d222148
SM
1428(fset 'idlwave-debug-map (make-sparse-keymap))
1429
1430(defvar idlwave-mode-map
1431 (let ((map (make-sparse-keymap)))
1432 (define-key map "\C-c " 'idlwave-hard-tab)
1433 (define-key map [(control tab)] 'idlwave-hard-tab)
1434 ;;(define-key map "\C-c\C- " 'idlwave-hard-tab)
1435 (define-key map "'" 'idlwave-show-matching-quote)
1436 (define-key map "\"" 'idlwave-show-matching-quote)
1437 (define-key map "\C-g" 'idlwave-keyboard-quit)
1438 (define-key map "\C-c;" 'idlwave-toggle-comment-region)
1439 (define-key map "\C-\M-a" 'idlwave-beginning-of-subprogram)
1440 (define-key map "\C-\M-e" 'idlwave-end-of-subprogram)
1441 (define-key map "\C-c{" 'idlwave-beginning-of-block)
1442 (define-key map "\C-c}" 'idlwave-end-of-block)
1443 (define-key map "\C-c]" 'idlwave-close-block)
1444 (define-key map [(meta control h)] 'idlwave-mark-subprogram)
1445 (define-key map "\M-\C-n" 'idlwave-forward-block)
1446 (define-key map "\M-\C-p" 'idlwave-backward-block)
1447 (define-key map "\M-\C-d" 'idlwave-down-block)
1448 (define-key map "\M-\C-u" 'idlwave-backward-up-block)
1449 (define-key map "\M-\r" 'idlwave-split-line)
1450 (define-key map "\M-\C-q" 'idlwave-indent-subprogram)
1451 (define-key map "\C-c\C-p" 'idlwave-previous-statement)
1452 (define-key map "\C-c\C-n" 'idlwave-next-statement)
1453 ;; (define-key map "\r" 'idlwave-newline)
1454 ;; (define-key map "\t" 'idlwave-indent-line)
1455 (define-key map [(shift iso-lefttab)] 'idlwave-indent-statement)
1456 (define-key map "\C-c\C-a" 'idlwave-auto-fill-mode)
1457 (define-key map "\M-q" 'idlwave-fill-paragraph)
1458 (define-key map "\M-s" 'idlwave-edit-in-idlde)
1459 (define-key map "\C-c\C-h" 'idlwave-doc-header)
1460 (define-key map "\C-c\C-m" 'idlwave-doc-modification)
1461 (define-key map "\C-c\C-c" 'idlwave-case)
1462 (define-key map "\C-c\C-d" 'idlwave-debug-map)
1463 (when (and (listp idlwave-shell-debug-modifiers)
1464 (not (equal idlwave-shell-debug-modifiers '())))
1465 ;; Bind the debug commands also with the special modifiers.
1466 (let ((shift (memq 'shift idlwave-shell-debug-modifiers))
1467 (mods-noshift
1468 (delq 'shift (copy-sequence idlwave-shell-debug-modifiers))))
1469 (define-key map
1470 (vector (append mods-noshift (list (if shift ?C ?c))))
1471 'idlwave-shell-save-and-run)
1472 (define-key map
1473 (vector (append mods-noshift (list (if shift ?B ?b))))
1474 'idlwave-shell-break-here)
1475 (define-key map
1476 (vector (append mods-noshift (list (if shift ?E ?e))))
1477 'idlwave-shell-run-region)))
1478 (define-key map "\C-c\C-d\C-c" 'idlwave-shell-save-and-run)
1479 (define-key map "\C-c\C-d\C-b" 'idlwave-shell-break-here)
1480 (define-key map "\C-c\C-d\C-e" 'idlwave-shell-run-region)
1481 (define-key map "\C-c\C-f" 'idlwave-for)
1482 ;; (define-key map "\C-c\C-f" 'idlwave-function)
1483 ;; (define-key map "\C-c\C-p" 'idlwave-procedure)
1484 (define-key map "\C-c\C-r" 'idlwave-repeat)
1485 (define-key map "\C-c\C-w" 'idlwave-while)
1486 (define-key map "\C-c\C-k" 'idlwave-kill-autoloaded-buffers)
1487 (define-key map "\C-c\C-s" 'idlwave-shell)
1488 (define-key map "\C-c\C-l" 'idlwave-shell-recenter-shell-window)
1489 (define-key map "\C-c\C-b" 'idlwave-list-buffer-load-path-shadows)
1490 (define-key map "\C-c\C-v" 'idlwave-find-module)
1491 (define-key map "\C-c\C-t" 'idlwave-find-module-this-file)
1492 (define-key map "\C-c?" 'idlwave-routine-info)
1493 (define-key map "\M-?" 'idlwave-context-help)
1494 (define-key map [(control meta ?\?)]
1495 'idlwave-help-assistant-help-with-topic)
1496 ;; Pickup both forms of Esc/Meta binding
1497 (define-key map [(meta tab)] 'idlwave-complete)
1498 (define-key map [?\e?\t] 'idlwave-complete)
1499 (define-key map "\M-\C-i" 'idlwave-complete)
1500 (define-key map "\C-c\C-i" 'idlwave-update-routine-info)
1501 (define-key map "\C-c=" 'idlwave-resolve)
1502 (define-key map
1503 (if (featurep 'xemacs) [(shift button3)] [(shift mouse-3)])
1504 'idlwave-mouse-context-help)
1505 map)
f32b3b91
CD
1506 "Keymap used in IDL mode.")
1507
8d222148
SM
1508(defvar idlwave-mode-syntax-table
1509 (let ((st (make-syntax-table)))
1510 (modify-syntax-entry ?+ "." st)
1511 (modify-syntax-entry ?- "." st)
1512 (modify-syntax-entry ?* "." st)
1513 (modify-syntax-entry ?/ "." st)
1514 (modify-syntax-entry ?^ "." st)
1515 (modify-syntax-entry ?# "." st)
1516 (modify-syntax-entry ?= "." st)
1517 (modify-syntax-entry ?% "." st)
1518 (modify-syntax-entry ?< "." st)
1519 (modify-syntax-entry ?> "." st)
1520 (modify-syntax-entry ?\' "\"" st)
1521 (modify-syntax-entry ?\" "\"" st)
1522 (modify-syntax-entry ?\\ "." st)
1523 (modify-syntax-entry ?_ "_" st)
1524 (modify-syntax-entry ?{ "(}" st)
1525 (modify-syntax-entry ?} "){" st)
1526 (modify-syntax-entry ?$ "_" st)
1527 (modify-syntax-entry ?. "." st)
1528 (modify-syntax-entry ?\; "<" st)
1529 (modify-syntax-entry ?\n ">" st)
1530 (modify-syntax-entry ?\f ">" st)
1531 st)
f32b3b91
CD
1532 "Syntax table in use in `idlwave-mode' buffers.")
1533
f32b3b91 1534(defvar idlwave-find-symbol-syntax-table
8d222148
SM
1535 (let ((st (copy-syntax-table idlwave-mode-syntax-table)))
1536 (modify-syntax-entry ?$ "w" st)
1537 (modify-syntax-entry ?_ "w" st)
1538 (modify-syntax-entry ?! "w" st)
1539 (modify-syntax-entry ?. "w" st)
1540 st)
f32b3b91
CD
1541 "Syntax table that treats symbol characters as word characters.")
1542
76959b77
S
1543(defmacro idlwave-with-special-syntax (&rest body)
1544 "Execute BODY with a different syntax table."
05a1abfc
CD
1545 `(let ((saved-syntax (syntax-table)))
1546 (unwind-protect
1547 (progn
1548 (set-syntax-table idlwave-find-symbol-syntax-table)
1549 ,@body)
1550 (set-syntax-table saved-syntax))))
1551
76959b77
S
1552;(defmacro idlwave-with-special-syntax1 (&rest body)
1553; "Execute BODY with a different syntax table."
1554; `(let ((saved-syntax (syntax-table)))
1555; (unwind-protect
1556; (progn
1557; (set-syntax-table idlwave-find-symbol-syntax-table)
1558; ,@body)
1559; (set-syntax-table saved-syntax))))
1560
f32b3b91
CD
1561(defun idlwave-action-and-binding (key cmd &optional select)
1562 "KEY and CMD are made into a key binding and an indent action.
1563KEY is a string - same as for the `define-key' function. CMD is a
1564function of no arguments or a list to be evaluated. CMD is bound to
1565KEY in `idlwave-mode-map' by defining an anonymous function calling
1566`self-insert-command' followed by CMD. If KEY contains more than one
1567character a binding will only be set if SELECT is 'both.
1568
5e72c6b2 1569\(KEY . CMD\) is also placed in the `idlwave-indent-expand-table',
f32b3b91
CD
1570replacing any previous value for KEY. If a binding is not set then it
1571will instead be placed in `idlwave-indent-action-table'.
1572
1573If the optional argument SELECT is nil then an action and binding are
1574created. If SELECT is 'noaction, then a binding is always set and no
1575action is created. If SELECT is 'both then an action and binding
1576will both be created even if KEY contains more than one character.
1577Otherwise, if SELECT is non-nil then only an action is created.
1578
1579Some examples:
1580No spaces before and 1 after a comma
1581 (idlwave-action-and-binding \",\" '(idlwave-surround 0 1))
1582A minimum of 1 space before and after `=' (see `idlwave-expand-equal').
1583 (idlwave-action-and-binding \"=\" '(idlwave-expand-equal -1 -1))
1584Capitalize system variables - action only
1585 (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)"
1586 (if (not (equal select 'noaction))
1587 ;; Add action
1588 (let* ((table (if select 'idlwave-indent-action-table
1589 'idlwave-indent-expand-table))
3938cb82
S
1590 (table-key (regexp-quote key))
1591 (cell (assoc table-key (eval table))))
f32b3b91
CD
1592 (if cell
1593 ;; Replace action command
1594 (setcdr cell cmd)
1595 ;; New action
3938cb82 1596 (set table (append (eval table) (list (cons table-key cmd)))))))
f32b3b91
CD
1597 ;; Make key binding for action
1598 (if (or (and (null select) (= (length key) 1))
1599 (equal select 'noaction)
1600 (equal select 'both))
1601 (define-key idlwave-mode-map key
8d222148
SM
1602 `(lambda ()
1603 (interactive)
1604 (self-insert-command 1)
4111f0c7 1605 ,(if (listp cmd) cmd (list cmd))))))
f32b3b91
CD
1606
1607;; Set action and key bindings.
1608;; See description of the function `idlwave-action-and-binding'.
1609;; Automatically add spaces for the following characters
f66f03de
S
1610
1611;; Actions for & are complicated by &&
1612(idlwave-action-and-binding "&" 'idlwave-custom-ampersand-surround)
1613
1614;; Automatically add spaces to equal sign if not keyword. This needs
1615;; to go ahead of > and <, so >= and <= will be treated correctly
f32b3b91
CD
1616(idlwave-action-and-binding "=" '(idlwave-expand-equal -1 -1))
1617
4b1aaa8b 1618;; Actions for > and < are complicated by >=, <=, and ->...
f66f03de
S
1619(idlwave-action-and-binding "<" '(idlwave-custom-ltgtr-surround nil))
1620(idlwave-action-and-binding ">" '(idlwave-custom-ltgtr-surround 'gtr))
1621
1622(idlwave-action-and-binding "," '(idlwave-surround 0 -1 1))
1623
1624
f32b3b91
CD
1625;;;
1626;;; Abbrev Section
1627;;;
1628;;; When expanding abbrevs and the abbrev hook moves backward, an extra
1629;;; space is inserted (this is the space typed by the user to expanded
1630;;; the abbrev).
1631;;;
5e72c6b2 1632(defvar idlwave-mode-abbrev-table nil
5a0c3f56 1633 "Abbreviation table used for IDLWAVE mode.")
5e72c6b2
S
1634(define-abbrev-table 'idlwave-mode-abbrev-table ())
1635
1636(defun idlwave-define-abbrev (name expansion hook &optional noprefix table)
1637 "Define-abbrev with backward compatibility.
1638
1639If NOPREFIX is non-nil, don't prepend prefix character. Installs into
5a0c3f56 1640`idlwave-mode-abbrev-table' unless TABLE is non-nil."
5e72c6b2
S
1641 (let ((abbrevs-changed nil) ;; mask the current value to avoid save
1642 (args (list (or table idlwave-mode-abbrev-table)
1643 (if noprefix name (concat idlwave-abbrev-start-char name))
1644 expansion
1645 hook)))
1646 (condition-case nil
1647 (apply 'define-abbrev (append args '(0 t)))
1648 (error (apply 'define-abbrev args)))))
f32b3b91
CD
1649
1650(condition-case nil
4b1aaa8b 1651 (modify-syntax-entry (string-to-char idlwave-abbrev-start-char)
f32b3b91
CD
1652 "w" idlwave-mode-syntax-table)
1653 (error nil))
1654
5e72c6b2
S
1655;;
1656;; Templates
1657;;
1658(idlwave-define-abbrev "c" "" (idlwave-code-abbrev idlwave-case))
1659(idlwave-define-abbrev "sw" "" (idlwave-code-abbrev idlwave-switch))
1660(idlwave-define-abbrev "f" "" (idlwave-code-abbrev idlwave-for))
1661(idlwave-define-abbrev "fu" "" (idlwave-code-abbrev idlwave-function))
1662(idlwave-define-abbrev "pr" "" (idlwave-code-abbrev idlwave-procedure))
1663(idlwave-define-abbrev "r" "" (idlwave-code-abbrev idlwave-repeat))
1664(idlwave-define-abbrev "w" "" (idlwave-code-abbrev idlwave-while))
1665(idlwave-define-abbrev "i" "" (idlwave-code-abbrev idlwave-if))
1666(idlwave-define-abbrev "elif" "" (idlwave-code-abbrev idlwave-elif))
1667;;
1668;; Keywords, system functions, conversion routines
1669;;
1670(idlwave-define-abbrev "ap" "arg_present()" (idlwave-keyword-abbrev 1))
1671(idlwave-define-abbrev "b" "begin" (idlwave-keyword-abbrev 0 t))
1672(idlwave-define-abbrev "co" "common" (idlwave-keyword-abbrev 0 t))
1673(idlwave-define-abbrev "cb" "byte()" (idlwave-keyword-abbrev 1))
1674(idlwave-define-abbrev "cx" "fix()" (idlwave-keyword-abbrev 1))
1675(idlwave-define-abbrev "cl" "long()" (idlwave-keyword-abbrev 1))
1676(idlwave-define-abbrev "cf" "float()" (idlwave-keyword-abbrev 1))
1677(idlwave-define-abbrev "cs" "string()" (idlwave-keyword-abbrev 1))
1678(idlwave-define-abbrev "cc" "complex()" (idlwave-keyword-abbrev 1))
1679(idlwave-define-abbrev "cd" "double()" (idlwave-keyword-abbrev 1))
1680(idlwave-define-abbrev "e" "else" (idlwave-keyword-abbrev 0 t))
1681(idlwave-define-abbrev "ec" "endcase" 'idlwave-show-begin)
1682(idlwave-define-abbrev "es" "endswitch" 'idlwave-show-begin)
1683(idlwave-define-abbrev "ee" "endelse" 'idlwave-show-begin)
1684(idlwave-define-abbrev "ef" "endfor" 'idlwave-show-begin)
1685(idlwave-define-abbrev "ei" "endif else if" 'idlwave-show-begin)
1686(idlwave-define-abbrev "el" "endif else" 'idlwave-show-begin)
1687(idlwave-define-abbrev "en" "endif" 'idlwave-show-begin)
1688(idlwave-define-abbrev "er" "endrep" 'idlwave-show-begin)
1689(idlwave-define-abbrev "ew" "endwhile" 'idlwave-show-begin)
1690(idlwave-define-abbrev "g" "goto," (idlwave-keyword-abbrev 0 t))
1691(idlwave-define-abbrev "h" "help," (idlwave-keyword-abbrev 0))
1692(idlwave-define-abbrev "k" "keyword_set()" (idlwave-keyword-abbrev 1))
1693(idlwave-define-abbrev "n" "n_elements()" (idlwave-keyword-abbrev 1))
1694(idlwave-define-abbrev "on" "on_error," (idlwave-keyword-abbrev 0))
1695(idlwave-define-abbrev "oi" "on_ioerror," (idlwave-keyword-abbrev 0 1))
1696(idlwave-define-abbrev "ow" "openw," (idlwave-keyword-abbrev 0))
1697(idlwave-define-abbrev "or" "openr," (idlwave-keyword-abbrev 0))
1698(idlwave-define-abbrev "ou" "openu," (idlwave-keyword-abbrev 0))
1699(idlwave-define-abbrev "p" "print," (idlwave-keyword-abbrev 0))
1700(idlwave-define-abbrev "pt" "plot," (idlwave-keyword-abbrev 0))
1701(idlwave-define-abbrev "re" "read," (idlwave-keyword-abbrev 0))
1702(idlwave-define-abbrev "rf" "readf," (idlwave-keyword-abbrev 0))
1703(idlwave-define-abbrev "ru" "readu," (idlwave-keyword-abbrev 0))
1704(idlwave-define-abbrev "rt" "return" (idlwave-keyword-abbrev 0))
1705(idlwave-define-abbrev "sc" "strcompress()" (idlwave-keyword-abbrev 1))
1706(idlwave-define-abbrev "sn" "strlen()" (idlwave-keyword-abbrev 1))
1707(idlwave-define-abbrev "sl" "strlowcase()" (idlwave-keyword-abbrev 1))
1708(idlwave-define-abbrev "su" "strupcase()" (idlwave-keyword-abbrev 1))
1709(idlwave-define-abbrev "sm" "strmid()" (idlwave-keyword-abbrev 1))
1710(idlwave-define-abbrev "sp" "strpos()" (idlwave-keyword-abbrev 1))
1711(idlwave-define-abbrev "st" "strput()" (idlwave-keyword-abbrev 1))
1712(idlwave-define-abbrev "sr" "strtrim()" (idlwave-keyword-abbrev 1))
1713(idlwave-define-abbrev "t" "then" (idlwave-keyword-abbrev 0 t))
1714(idlwave-define-abbrev "u" "until" (idlwave-keyword-abbrev 0 t))
1715(idlwave-define-abbrev "wu" "writeu," (idlwave-keyword-abbrev 0))
1716(idlwave-define-abbrev "iap" "if arg_present() then" (idlwave-keyword-abbrev 6))
1717(idlwave-define-abbrev "ik" "if keyword_set() then" (idlwave-keyword-abbrev 6))
1718(idlwave-define-abbrev "ine" "if n_elements() eq 0 then" (idlwave-keyword-abbrev 11))
1719(idlwave-define-abbrev "inn" "if n_elements() ne 0 then" (idlwave-keyword-abbrev 11))
1720(idlwave-define-abbrev "np" "n_params()" (idlwave-keyword-abbrev 0))
1721(idlwave-define-abbrev "s" "size()" (idlwave-keyword-abbrev 1))
1722(idlwave-define-abbrev "wi" "widget_info()" (idlwave-keyword-abbrev 1))
1723(idlwave-define-abbrev "wc" "widget_control," (idlwave-keyword-abbrev 0))
3938cb82
S
1724(idlwave-define-abbrev "pv" "ptr_valid()" (idlwave-keyword-abbrev 1))
1725(idlwave-define-abbrev "ipv" "if ptr_valid() then" (idlwave-keyword-abbrev 6))
ff689efd 1726
5e72c6b2
S
1727;; This section is reserved words only. (From IDL user manual)
1728;;
1729(idlwave-define-abbrev "and" "and" (idlwave-keyword-abbrev 0 t) t)
1730(idlwave-define-abbrev "begin" "begin" (idlwave-keyword-abbrev 0 t) t)
1731(idlwave-define-abbrev "break" "break" (idlwave-keyword-abbrev 0 t) t)
1732(idlwave-define-abbrev "case" "case" (idlwave-keyword-abbrev 0 t) t)
1733(idlwave-define-abbrev "common" "common" (idlwave-keyword-abbrev 0 t) t)
1734(idlwave-define-abbrev "continue" "continue" (idlwave-keyword-abbrev 0 t) t)
1735(idlwave-define-abbrev "do" "do" (idlwave-keyword-abbrev 0 t) t)
1736(idlwave-define-abbrev "else" "else" (idlwave-keyword-abbrev 0 t) t)
1737(idlwave-define-abbrev "end" "end" 'idlwave-show-begin-check t)
1738(idlwave-define-abbrev "endcase" "endcase" 'idlwave-show-begin-check t)
1739(idlwave-define-abbrev "endelse" "endelse" 'idlwave-show-begin-check t)
1740(idlwave-define-abbrev "endfor" "endfor" 'idlwave-show-begin-check t)
1741(idlwave-define-abbrev "endif" "endif" 'idlwave-show-begin-check t)
1742(idlwave-define-abbrev "endrep" "endrep" 'idlwave-show-begin-check t)
1743(idlwave-define-abbrev "endswitch" "endswitch" 'idlwave-show-begin-check t)
1744(idlwave-define-abbrev "endwhi" "endwhi" 'idlwave-show-begin-check t)
1745(idlwave-define-abbrev "endwhile" "endwhile" 'idlwave-show-begin-check t)
1746(idlwave-define-abbrev "eq" "eq" (idlwave-keyword-abbrev 0 t) t)
1747(idlwave-define-abbrev "for" "for" (idlwave-keyword-abbrev 0 t) t)
1748(idlwave-define-abbrev "function" "function" (idlwave-keyword-abbrev 0 t) t)
1749(idlwave-define-abbrev "ge" "ge" (idlwave-keyword-abbrev 0 t) t)
1750(idlwave-define-abbrev "goto" "goto" (idlwave-keyword-abbrev 0 t) t)
1751(idlwave-define-abbrev "gt" "gt" (idlwave-keyword-abbrev 0 t) t)
1752(idlwave-define-abbrev "if" "if" (idlwave-keyword-abbrev 0 t) t)
1753(idlwave-define-abbrev "le" "le" (idlwave-keyword-abbrev 0 t) t)
1754(idlwave-define-abbrev "lt" "lt" (idlwave-keyword-abbrev 0 t) t)
1755(idlwave-define-abbrev "mod" "mod" (idlwave-keyword-abbrev 0 t) t)
1756(idlwave-define-abbrev "ne" "ne" (idlwave-keyword-abbrev 0 t) t)
1757(idlwave-define-abbrev "not" "not" (idlwave-keyword-abbrev 0 t) t)
1758(idlwave-define-abbrev "of" "of" (idlwave-keyword-abbrev 0 t) t)
1759(idlwave-define-abbrev "on_ioerror" "on_ioerror" (idlwave-keyword-abbrev 0 t) t)
1760(idlwave-define-abbrev "or" "or" (idlwave-keyword-abbrev 0 t) t)
1761(idlwave-define-abbrev "pro" "pro" (idlwave-keyword-abbrev 0 t) t)
1762(idlwave-define-abbrev "repeat" "repeat" (idlwave-keyword-abbrev 0 t) t)
1763(idlwave-define-abbrev "switch" "switch" (idlwave-keyword-abbrev 0 t) t)
1764(idlwave-define-abbrev "then" "then" (idlwave-keyword-abbrev 0 t) t)
1765(idlwave-define-abbrev "until" "until" (idlwave-keyword-abbrev 0 t) t)
1766(idlwave-define-abbrev "while" "while" (idlwave-keyword-abbrev 0 t) t)
1767(idlwave-define-abbrev "xor" "xor" (idlwave-keyword-abbrev 0 t) t)
f32b3b91
CD
1768
1769(defvar imenu-create-index-function)
1770(defvar extract-index-name-function)
1771(defvar prev-index-position-function)
1772(defvar imenu-extract-index-name-function)
1773(defvar imenu-prev-index-position-function)
5e72c6b2 1774;; defined later - so just make the compiler hush
4b1aaa8b 1775(defvar idlwave-mode-menu)
f32b3b91
CD
1776(defvar idlwave-mode-debug-menu)
1777
1778;;;###autoload
1779(defun idlwave-mode ()
e08734e2 1780 "Major mode for editing IDL source files (version 6.1_em22).
f32b3b91
CD
1781
1782The main features of this mode are
1783
17841. Indentation and Formatting
1785 --------------------------
1786 Like other Emacs programming modes, C-j inserts a newline and indents.
1787 TAB is used for explicit indentation of the current line.
1788
5e72c6b2
S
1789 To start a continuation line, use \\[idlwave-split-line]. This
1790 function can also be used in the middle of a line to split the line
1791 at that point. When used inside a long constant string, the string
1792 is split at that point with the `+' concatenation operator.
f32b3b91
CD
1793
1794 Comments are indented as follows:
1795
1796 `;;;' Indentation remains unchanged.
1797 `;;' Indent like the surrounding code
1798 `;' Indent to a minimum column.
1799
1800 The indentation of comments starting in column 0 is never changed.
1801
5e72c6b2
S
1802 Use \\[idlwave-fill-paragraph] to refill a paragraph inside a
1803 comment. The indentation of the second line of the paragraph
1804 relative to the first will be retained. Use
1805 \\[idlwave-auto-fill-mode] to toggle auto-fill mode for these
1806 comments. When the variable `idlwave-fill-comment-line-only' is
52a244eb 1807 nil, code can also be auto-filled and auto-indented.
f32b3b91
CD
1808
1809 To convert pre-existing IDL code to your formatting style, mark the
5e72c6b2
S
1810 entire buffer with \\[mark-whole-buffer] and execute
1811 \\[idlwave-expand-region-abbrevs]. Then mark the entire buffer
1812 again followed by \\[indent-region] (`indent-region').
f32b3b91
CD
1813
18142. Routine Info
1815 ------------
5e72c6b2
S
1816 IDLWAVE displays information about the calling sequence and the
1817 accepted keyword parameters of a procedure or function with
1818 \\[idlwave-routine-info]. \\[idlwave-find-module] jumps to the
1819 source file of a module. These commands know about system
1820 routines, all routines in idlwave-mode buffers and (when the
1821 idlwave-shell is active) about all modules currently compiled under
52a244eb
S
1822 this shell. It also makes use of pre-compiled or custom-scanned
1823 user and library catalogs many popular libraries ship with by
1824 default. Use \\[idlwave-update-routine-info] to update this
15e42531
CD
1825 information, which is also used for completion (see item 4).
1826
18273. Online IDL Help
1828 ---------------
f66f03de 1829
15e42531 1830 \\[idlwave-context-help] displays the IDL documentation relevant
f66f03de
S
1831 for the system variable, keyword, or routines at point. A single
1832 key stroke gets you directly to the right place in the docs. See
52a244eb 1833 the manual to configure where and how the HTML help is displayed.
f32b3b91 1834
15e42531 18354. Completion
f32b3b91 1836 ----------
15e42531 1837 \\[idlwave-complete] completes the names of procedures, functions
52a244eb
S
1838 class names, keyword parameters, system variables and tags, class
1839 tags, structure tags, filenames and much more. It is context
1840 sensitive and figures out what is expected at point. Lower case
1841 strings are completed in lower case, other strings in mixed or
1842 upper case.
f32b3b91 1843
15e42531 18445. Code Templates and Abbreviations
f32b3b91
CD
1845 --------------------------------
1846 Many Abbreviations are predefined to expand to code fragments and templates.
5a0c3f56 1847 The abbreviations start generally with a `\\`. Some examples:
f32b3b91
CD
1848
1849 \\pr PROCEDURE template
1850 \\fu FUNCTION template
1851 \\c CASE statement template
05a1abfc 1852 \\sw SWITCH statement template
f32b3b91
CD
1853 \\f FOR loop template
1854 \\r REPEAT Loop template
1855 \\w WHILE loop template
1856 \\i IF statement template
1857 \\elif IF-ELSE statement template
1858 \\b BEGIN
4b1aaa8b 1859
52a244eb
S
1860 For a full list, use \\[idlwave-list-abbrevs]. Some templates also
1861 have direct keybindings - see the list of keybindings below.
775591f7 1862
52a244eb
S
1863 \\[idlwave-doc-header] inserts a documentation header at the
1864 beginning of the current program unit (pro, function or main).
1865 Change log entries can be added to the current program unit with
1866 \\[idlwave-doc-modification].
f32b3b91 1867
15e42531 18686. Automatic Case Conversion
f32b3b91
CD
1869 -------------------------
1870 The case of reserved words and some abbrevs is controlled by
1871 `idlwave-reserved-word-upcase' and `idlwave-abbrev-change-case'.
1872
15e42531 18737. Automatic END completion
f32b3b91
CD
1874 ------------------------
1875 If the variable `idlwave-expand-generic-end' is non-nil, each END typed
1876 will be converted to the specific version, like ENDIF, ENDFOR, etc.
1877
15e42531 18788. Hooks
f32b3b91
CD
1879 -----
1880 Loading idlwave.el runs `idlwave-load-hook'.
1881 Turning on `idlwave-mode' runs `idlwave-mode-hook'.
1882
15e42531 18839. Documentation and Customization
f32b3b91 1884 -------------------------------
5e72c6b2
S
1885 Info documentation for this package is available. Use
1886 \\[idlwave-info] to display (complain to your sysadmin if that does
1887 not work). For Postscript, PDF, and HTML versions of the
855b42a2 1888 documentation, check IDLWAVE's homepage at URL `http://idlwave.org'.
f32b3b91
CD
1889 IDLWAVE has customize support - see the group `idlwave'.
1890
15e42531 189110.Keybindings
f32b3b91
CD
1892 -----------
1893 Here is a list of all keybindings of this mode.
1894 If some of the key bindings below show with ??, use \\[describe-key]
1895 followed by the key sequence to see what the key sequence does.
1896
1897\\{idlwave-mode-map}"
1898
1899 (interactive)
1900 (kill-all-local-variables)
4b1aaa8b 1901
f32b3b91
CD
1902 (if idlwave-startup-message
1903 (message "Emacs IDLWAVE mode version %s." idlwave-mode-version))
1904 (setq idlwave-startup-message nil)
4b1aaa8b 1905
f32b3b91
CD
1906 (setq local-abbrev-table idlwave-mode-abbrev-table)
1907 (set-syntax-table idlwave-mode-syntax-table)
4b1aaa8b 1908
f32b3b91 1909 (set (make-local-variable 'indent-line-function) 'idlwave-indent-and-action)
4b1aaa8b 1910
f32b3b91
CD
1911 (make-local-variable idlwave-comment-indent-function)
1912 (set idlwave-comment-indent-function 'idlwave-comment-hook)
4b1aaa8b 1913
f32b3b91
CD
1914 (set (make-local-variable 'comment-start-skip) ";+[ \t]*")
1915 (set (make-local-variable 'comment-start) ";")
0dc2be2f 1916 (set (make-local-variable 'comment-add) 1) ; ";;" for new and regions
f66f03de 1917 (set (make-local-variable 'require-final-newline) t)
f32b3b91
CD
1918 (set (make-local-variable 'abbrev-all-caps) t)
1919 (set (make-local-variable 'indent-tabs-mode) nil)
1920 (set (make-local-variable 'completion-ignore-case) t)
4b1aaa8b 1921
f32b3b91
CD
1922 (use-local-map idlwave-mode-map)
1923
1924 (when (featurep 'easymenu)
1925 (easy-menu-add idlwave-mode-menu idlwave-mode-map)
1926 (easy-menu-add idlwave-mode-debug-menu idlwave-mode-map))
1927
1928 (setq mode-name "IDLWAVE")
1929 (setq major-mode 'idlwave-mode)
1930 (setq abbrev-mode t)
4b1aaa8b 1931
f32b3b91
CD
1932 (set (make-local-variable idlwave-fill-function) 'idlwave-auto-fill)
1933 (setq comment-end "")
1934 (set (make-local-variable 'comment-multi-line) nil)
4b1aaa8b 1935 (set (make-local-variable 'paragraph-separate)
5e72c6b2 1936 "[ \t\f]*$\\|[ \t]*;+[ \t]*$\\|;+[+=-_*]+$")
f32b3b91
CD
1937 (set (make-local-variable 'paragraph-start) "[ \t\f]\\|[ \t]*;+[ \t]")
1938 (set (make-local-variable 'paragraph-ignore-fill-prefix) nil)
76959b77 1939 (set (make-local-variable 'parse-sexp-ignore-comments) t)
775591f7 1940
e08734e2 1941 ;; ChangeLog
8c43762b 1942 (set (make-local-variable 'add-log-current-defun-function)
e08734e2
S
1943 'idlwave-current-routine-fullname)
1944
f32b3b91
CD
1945 ;; Set tag table list to use IDLTAGS as file name.
1946 (if (boundp 'tag-table-alist)
1947 (add-to-list 'tag-table-alist '("\\.pro$" . "IDLTAGS")))
4b1aaa8b 1948
e08734e2 1949 ;; Font-lock additions
52a244eb 1950 ;; Following line is for Emacs - XEmacs uses the corresponding property
f32b3b91
CD
1951 ;; on the `idlwave-mode' symbol.
1952 (set (make-local-variable 'font-lock-defaults) idlwave-font-lock-defaults)
0dc2be2f
S
1953 (set (make-local-variable 'font-lock-mark-block-function)
1954 'idlwave-mark-subprogram)
1955 (set (make-local-variable 'font-lock-fontify-region-function)
1956 'idlwave-font-lock-fontify-region)
f32b3b91
CD
1957
1958 ;; Imenu setup
1959 (set (make-local-variable 'imenu-create-index-function)
1960 'imenu-default-create-index-function)
1961 (set (make-local-variable 'imenu-extract-index-name-function)
1962 'idlwave-unit-name)
1963 (set (make-local-variable 'imenu-prev-index-position-function)
1964 'idlwave-prev-index-position)
1965
0dc2be2f
S
1966 ;; HideShow setup
1967 (add-to-list 'hs-special-modes-alist
1968 (list 'idlwave-mode
1969 idlwave-begin-block-reg
1970 idlwave-end-block-reg
1971 ";"
1972 'idlwave-forward-block nil))
4b1aaa8b 1973
f32b3b91 1974 ;; Make a local post-command-hook and add our hook to it
f66f03de
S
1975 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1976 ;; (make-local-hook 'post-command-hook)
15e42531
CD
1977 (add-hook 'post-command-hook 'idlwave-command-hook nil 'local)
1978
1979 ;; Make local hooks for buffer updates
f66f03de
S
1980 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1981 ;; (make-local-hook 'kill-buffer-hook)
15e42531 1982 (add-hook 'kill-buffer-hook 'idlwave-kill-buffer-update nil 'local)
f66f03de 1983 ;; (make-local-hook 'after-save-hook)
e08734e2 1984 (add-hook 'after-save-hook 'idlwave-save-buffer-update nil 'local)
15e42531
CD
1985 (add-hook 'after-save-hook 'idlwave-revoke-license-to-kill nil 'local)
1986
52a244eb
S
1987 ;; Setup directories and file, if necessary
1988 (idlwave-setup)
1989
15e42531
CD
1990 ;; Update the routine info with info about current buffer?
1991 (idlwave-new-buffer-update)
f32b3b91 1992
f66f03de
S
1993 ;; Check help location
1994 (idlwave-help-check-locations)
1995
f32b3b91 1996 ;; Run the mode hook
9a969196 1997 (run-mode-hooks 'idlwave-mode-hook))
f32b3b91 1998
52a244eb
S
1999(defvar idlwave-setup-done nil)
2000(defun idlwave-setup ()
2001 (unless idlwave-setup-done
2002 (if (not (file-directory-p idlwave-config-directory))
2003 (make-directory idlwave-config-directory))
4b1aaa8b
PE
2004 (setq
2005 idlwave-user-catalog-file (expand-file-name
2006 idlwave-user-catalog-file
f66f03de 2007 idlwave-config-directory)
4b1aaa8b
PE
2008 idlwave-xml-system-rinfo-converted-file
2009 (expand-file-name
f66f03de
S
2010 idlwave-xml-system-rinfo-converted-file
2011 idlwave-config-directory)
4b1aaa8b
PE
2012 idlwave-path-file (expand-file-name
2013 idlwave-path-file
f66f03de 2014 idlwave-config-directory))
52a244eb
S
2015 (idlwave-read-paths) ; we may need these early
2016 (setq idlwave-setup-done t)))
2017
0dc2be2f
S
2018(defun idlwave-font-lock-fontify-region (beg end &optional verbose)
2019 "Fontify continuation lines correctly."
2020 (let (pos)
2021 (save-excursion
2022 (goto-char beg)
2023 (forward-line -1)
2024 (when (setq pos (idlwave-is-continuation-line))
2025 (goto-char pos)
2026 (idlwave-beginning-of-statement)
2027 (setq beg (point)))))
2028 (font-lock-default-fontify-region beg end verbose))
2029
f32b3b91 2030;;
52a244eb 2031;; Code Formatting ----------------------------------------------------
4b1aaa8b 2032;;
f32b3b91 2033
f32b3b91 2034(defun idlwave-hard-tab ()
5a0c3f56 2035 "Insert TAB in buffer in current position."
f32b3b91
CD
2036 (interactive)
2037 (insert "\t"))
2038
2039;;; This stuff is experimental
2040
2041(defvar idlwave-command-hook nil
2042 "If non-nil, a list that can be evaluated using `eval'.
2043It is evaluated in the lisp function `idlwave-command-hook' which is
2044placed in `post-command-hook'.")
2045
2046(defun idlwave-command-hook ()
2047 "Command run after every command.
2048Evaluates a non-nil value of the *variable* `idlwave-command-hook' and
2049sets the variable to zero afterwards."
2050 (and idlwave-command-hook
2051 (listp idlwave-command-hook)
2052 (condition-case nil
2053 (eval idlwave-command-hook)
2054 (error nil)))
2055 (setq idlwave-command-hook nil))
2056
2057;;; End experiment
2058
2059;; It would be better to use expand.el for better abbrev handling and
2060;; versatility.
2061
2062(defun idlwave-check-abbrev (arg &optional reserved)
5a0c3f56 2063 "Reverse abbrev expansion if in comment or string.
f32b3b91
CD
2064Argument ARG is the number of characters to move point
2065backward if `idlwave-abbrev-move' is non-nil.
2066If optional argument RESERVED is non-nil then the expansion
2067consists of reserved words, which will be capitalized if
2068`idlwave-reserved-word-upcase' is non-nil.
2069Otherwise, the abbrev will be capitalized if `idlwave-abbrev-change-case'
2070is non-nil, unless its value is \`down in which case the abbrev will be
2071made into all lowercase.
2072Returns non-nil if abbrev is left expanded."
2073 (if (idlwave-quoted)
2074 (progn (unexpand-abbrev)
2075 nil)
2076 (if (and reserved idlwave-reserved-word-upcase)
2077 (upcase-region last-abbrev-location (point))
2078 (cond
2079 ((equal idlwave-abbrev-change-case 'down)
2080 (downcase-region last-abbrev-location (point)))
2081 (idlwave-abbrev-change-case
2082 (upcase-region last-abbrev-location (point)))))
2083 (if (and idlwave-abbrev-move (> arg 0))
2084 (if (boundp 'post-command-hook)
2085 (setq idlwave-command-hook (list 'backward-char (1+ arg)))
2086 (backward-char arg)))
2087 t))
2088
2089(defun idlwave-in-comment ()
5a0c3f56 2090 "Return t if point is inside a comment, nil otherwise."
f32b3b91
CD
2091 (save-excursion
2092 (let ((here (point)))
2093 (and (idlwave-goto-comment) (> here (point))))))
2094
2095(defun idlwave-goto-comment ()
2096 "Move to start of comment delimiter on current line.
2097Moves to end of line if there is no comment delimiter.
2098Ignores comment delimiters in strings.
2099Returns point if comment found and nil otherwise."
2100 (let ((eos (progn (end-of-line) (point)))
2101 (data (match-data))
2102 found)
2103 ;; Look for first comment delimiter not in a string
2104 (beginning-of-line)
2105 (setq found (search-forward comment-start eos 'lim))
2106 (while (and found (idlwave-in-quote))
2107 (setq found (search-forward comment-start eos 'lim)))
2108 (store-match-data data)
2109 (and found (not (idlwave-in-quote))
2110 (progn
2111 (backward-char 1)
2112 (point)))))
2113
5e72c6b2 2114(defun idlwave-region-active-p ()
a00e54f7
RS
2115 "Should we operate on an active region?"
2116 (if (fboundp 'use-region-p)
2117 (use-region-p)
2118 (region-active-p)))
5e72c6b2 2119
f32b3b91
CD
2120(defun idlwave-show-matching-quote ()
2121 "Insert quote and show matching quote if this is end of a string."
2122 (interactive)
2123 (let ((bq (idlwave-in-quote))
1ba983e8 2124 (inq last-command-event))
f32b3b91
CD
2125 (if (and bq (not (idlwave-in-comment)))
2126 (let ((delim (char-after bq)))
2127 (insert inq)
2128 (if (eq inq delim)
2129 (save-excursion
2130 (goto-char bq)
2131 (sit-for 1))))
2132 ;; Not the end of a string
2133 (insert inq))))
2134
2135(defun idlwave-show-begin-check ()
2136 "Ensure that the previous word was a token before `idlwave-show-begin'.
2137An END token must be preceded by whitespace."
5e72c6b2
S
2138 (if (not (idlwave-quoted))
2139 (if
2140 (save-excursion
2141 (backward-word 1)
2142 (backward-char 1)
2143 (looking-at "[ \t\n\f]"))
2144 (idlwave-show-begin))))
f32b3b91
CD
2145
2146(defun idlwave-show-begin ()
5a0c3f56
JB
2147 "Find the start of current block and blinks to it for a second.
2148Also checks if the correct END statement has been used."
f32b3b91 2149 ;; All end statements are reserved words
76959b77 2150 ;; Re-indent end line
52a244eb
S
2151 ;;(insert-char ?\ 1) ;; So indent, etc. work well
2152 ;;(backward-char 1)
76959b77
S
2153 (let* ((pos (point-marker))
2154 (last-abbrev-marker (copy-marker last-abbrev-location))
e180ab9f 2155 (eol-pos (point-at-eol))
76959b77
S
2156 begin-pos end-pos end end1 )
2157 (if idlwave-reindent-end (idlwave-indent-line))
52a244eb 2158 (setq last-abbrev-location (marker-position last-abbrev-marker))
f32b3b91
CD
2159 (when (and (idlwave-check-abbrev 0 t)
2160 idlwave-show-block)
2161 (save-excursion
2162 ;; Move inside current block
76959b77 2163 (goto-char last-abbrev-marker)
f32b3b91 2164 (idlwave-block-jump-out -1 'nomark)
76959b77
S
2165 (setq begin-pos (point))
2166 (idlwave-block-jump-out 1 'nomark)
2167 (setq end-pos (point))
2168 (if (> end-pos eol-pos)
2169 (setq end-pos pos))
2170 (goto-char end-pos)
4b1aaa8b 2171 (setq end (buffer-substring
76959b77
S
2172 (progn
2173 (skip-chars-backward "a-zA-Z")
2174 (point))
2175 end-pos))
2176 (goto-char begin-pos)
f32b3b91
CD
2177 (when (setq end1 (cdr (idlwave-block-master)))
2178 (cond
5e72c6b2 2179 ((null end1)) ; no-operation
f32b3b91
CD
2180 ((string= (downcase end) (downcase end1))
2181 (sit-for 1))
2182 ((string= (downcase end) "end")
2183 ;; A generic end
2184 (if idlwave-expand-generic-end
2185 (save-excursion
2186 (goto-char pos)
2187 (backward-char 3)
2188 (insert (if (string= end "END") (upcase end1) end1))
2189 (delete-char 3)))
2190 (sit-for 1))
2191 (t
2192 (beep)
4b1aaa8b 2193 (message "Warning: Shouldn't this be \"%s\" instead of \"%s\"?"
f32b3b91 2194 end1 end)
52a244eb
S
2195 (sit-for 1))))))))
2196 ;;(delete-char 1))
f32b3b91
CD
2197
2198(defun idlwave-block-master ()
2199 (let ((case-fold-search t))
2200 (save-excursion
2201 (cond
05a1abfc 2202 ((looking-at "pro\\|case\\|switch\\|function\\>")
f32b3b91
CD
2203 (assoc (downcase (match-string 0)) idlwave-block-matches))
2204 ((looking-at "begin\\>")
4b1aaa8b
PE
2205 (let ((limit (save-excursion
2206 (idlwave-beginning-of-statement)
f32b3b91
CD
2207 (point))))
2208 (cond
52a244eb
S
2209 ((re-search-backward ":[ \t]*\\=" limit t)
2210 ;; seems to be a case thing
2211 '("begin" . "end"))
f32b3b91
CD
2212 ((re-search-backward idlwave-block-match-regexp limit t)
2213 (assoc (downcase (match-string 1))
2214 idlwave-block-matches))
f32b3b91 2215 (t
52a244eb 2216 ;; Just a normal block
f32b3b91
CD
2217 '("begin" . "end")))))
2218 (t nil)))))
2219
2220(defun idlwave-close-block ()
2221 "Terminate the current block with the correct END statement."
2222 (interactive)
f32b3b91
CD
2223 ;; Start new line if we are not in a new line
2224 (unless (save-excursion
2225 (skip-chars-backward " \t")
2226 (bolp))
2227 (let ((idlwave-show-block nil))
2228 (newline-and-indent)))
5e72c6b2
S
2229 (let ((last-abbrev-location (point))) ; for upcasing
2230 (insert "end")
2231 (idlwave-show-begin)))
2232
f66f03de 2233(defun idlwave-custom-ampersand-surround (&optional is-action)
5a0c3f56 2234 "Surround &, leaving room for && (which surround as well)."
f66f03de
S
2235 (let* ((prev-char (char-after (- (point) 2)))
2236 (next-char (char-after (point)))
2237 (amp-left (eq prev-char ?&))
2238 (amp-right (eq next-char ?&))
2239 (len (if amp-left 2 1)))
2240 (unless amp-right ;no need to do it twice, amp-left will catch it.
2241 (idlwave-surround -1 (if (or is-action amp-left) -1) len))))
2242
2243(defun idlwave-custom-ltgtr-surround (gtr &optional is-action)
2244 "Surround > and < by blanks, leaving room for >= and <=, and considering ->."
2245 (let* ((prev-char (char-after (- (point) 2)))
2246 (next-char (char-after (point)))
2247 (method-invoke (and gtr (eq prev-char ?-)))
2248 (len (if method-invoke 2 1)))
2249 (unless (eq next-char ?=)
2250 ;; Key binding: pad only on left, to save for possible >=/<=
2251 (idlwave-surround -1 (if (or is-action method-invoke) -1) len))))
2252
2253(defun idlwave-surround (&optional before after length is-action)
595ab50b
CD
2254 "Surround the LENGTH characters before point with blanks.
2255LENGTH defaults to 1.
f32b3b91 2256Optional arguments BEFORE and AFTER affect the behavior before and
595ab50b
CD
2257after the characters (see also description of `idlwave-make-space'):
2258
2259nil do nothing
22600 force no spaces
2261integer > 0 force exactly n spaces
2262integer < 0 at least |n| spaces
f32b3b91
CD
2263
2264The function does nothing if any of the following conditions is true:
2265- `idlwave-surround-by-blank' is nil
f66f03de 2266- the character before point is inside a string or comment"
5e72c6b2 2267 (when (and idlwave-surround-by-blank (not (idlwave-quoted)))
f66f03de
S
2268 (let ((length (or length 1))) ; establish a default for LENGTH
2269 (backward-char length)
2270 (save-restriction
2271 (let ((here (point)))
2272 (skip-chars-backward " \t")
2273 (if (bolp)
2274 ;; avoid clobbering indent
2275 (progn
2276 (move-to-column (idlwave-calculate-indent))
2277 (if (<= (point) here)
2278 (narrow-to-region (point) here))
2279 (goto-char here)))
2280 (idlwave-make-space before))
2281 (skip-chars-forward " \t"))
2282 (forward-char length)
2283 (idlwave-make-space after)
2284 ;; Check to see if the line should auto wrap
2285 (if (and (equal (char-after (1- (point))) ?\ )
2286 (> (current-column) fill-column))
2287 (funcall auto-fill-function)))))
f32b3b91
CD
2288
2289(defun idlwave-make-space (n)
2290 "Make space at point.
2291The space affected is all the spaces and tabs around point.
2292If n is non-nil then point is left abs(n) spaces from the beginning of
2293the contiguous space.
2294The amount of space at point is determined by N.
2295If the value of N is:
2296nil - do nothing.
595ab50b
CD
2297> 0 - exactly N spaces.
2298< 0 - a minimum of -N spaces, i.e., do not change if there are
2299 already -N spaces.
23000 - no spaces (i.e. remove any existing space)."
f32b3b91
CD
2301 (if (integerp n)
2302 (let
2303 ((start-col (progn (skip-chars-backward " \t") (current-column)))
2304 (left (point))
2305 (end-col (progn (skip-chars-forward " \t") (current-column))))
2306 (delete-horizontal-space)
2307 (cond
2308 ((> n 0)
2309 (idlwave-indent-to (+ start-col n))
2310 (goto-char (+ left n)))
2311 ((< n 0)
2312 (idlwave-indent-to end-col (- n))
2313 (goto-char (- left n)))
2314 ;; n = 0, done
2315 ))))
2316
2317(defun idlwave-newline ()
5a0c3f56 2318 "Insert a newline and indent the current and previous line."
f32b3b91
CD
2319 (interactive)
2320 ;;
2321 ;; Handle unterminated single and double quotes
2322 ;; If not in a comment and in a string then insertion of a newline
2323 ;; will mean unbalanced quotes.
2324 ;;
2325 (if (and (not (idlwave-in-comment)) (idlwave-in-quote))
2326 (progn (beep)
2327 (message "Warning: unbalanced quotes?")))
2328 (newline)
2329 ;;
2330 ;; The current line is being split, the cursor should be at the
2331 ;; beginning of the new line skipping the leading indentation.
2332 ;;
2333 ;; The reason we insert the new line before indenting is that the
2334 ;; indenting could be confused by keywords (e.g. END) on the line
2335 ;; after the split point. This prevents us from just using
2336 ;; `indent-for-tab-command' followed by `newline-and-indent'.
2337 ;;
2338 (beginning-of-line 0)
2339 (idlwave-indent-line)
2340 (forward-line)
2341 (idlwave-indent-line))
2342
2343;;
2344;; Use global variable 'comment-column' to set parallel comment
2345;;
2346;; Modeled on lisp.el
2347;; Emacs Lisp and IDL (Wave CL) have identical comment syntax
2348(defun idlwave-comment-hook ()
2349 "Compute indent for the beginning of the IDL comment delimiter."
2350 (if (or (looking-at idlwave-no-change-comment)
8d222148 2351 (looking-at (or idlwave-begin-line-comment "^;")))
f32b3b91
CD
2352 (current-column)
2353 (if (looking-at idlwave-code-comment)
2354 (if (save-excursion (skip-chars-backward " \t") (bolp))
2355 ;; On line by itself, indent as code
2356 (let ((tem (idlwave-calculate-indent)))
2357 (if (listp tem) (car tem) tem))
2358 ;; after code - do not change
2359 (current-column))
2360 (skip-chars-backward " \t")
2361 (max (if (bolp) 0 (1+ (current-column)))
2362 comment-column))))
2363
2364(defun idlwave-split-line ()
2365 "Continue line by breaking line at point and indent the lines.
5a0c3f56 2366For a code line insert continuation marker. If the line is a line comment
f32b3b91
CD
2367then the new line will contain a comment with the same indentation.
2368Splits strings with the IDL operator `+' if `idlwave-split-line-string' is
2369non-nil."
2370 (interactive)
15e42531
CD
2371 ;; Expand abbreviation, just like normal RET would.
2372 (and abbrev-mode (expand-abbrev))
f32b3b91
CD
2373 (let (beg)
2374 (if (not (idlwave-in-comment))
2375 ;; For code line add continuation.
2376 ;; Check if splitting a string.
2377 (progn
2378 (if (setq beg (idlwave-in-quote))
2379 (if idlwave-split-line-string
2380 ;; Split the string.
2381 (progn (insert (setq beg (char-after beg)) " + "
2382 idlwave-continuation-char beg)
5e72c6b2
S
2383 (backward-char 1)
2384 (newline-and-indent)
2385 (forward-char 1))
f32b3b91
CD
2386 ;; Do not split the string.
2387 (beep)
2388 (message "Warning: continuation inside string!!")
2389 (insert " " idlwave-continuation-char))
2390 ;; Not splitting a string.
15e42531
CD
2391 (if (not (member (char-before) '(?\ ?\t)))
2392 (insert " "))
5e72c6b2
S
2393 (insert idlwave-continuation-char)
2394 (newline-and-indent)))
f32b3b91
CD
2395 (indent-new-comment-line))
2396 ;; Indent previous line
2397 (setq beg (- (point-max) (point)))
2398 (forward-line -1)
2399 (idlwave-indent-line)
2400 (goto-char (- (point-max) beg))
2401 ;; Reindent new line
2402 (idlwave-indent-line)))
2403
cca13260 2404(defun idlwave-beginning-of-subprogram (&optional nomark)
5a0c3f56 2405 "Move point to the beginning of the current program unit.
cca13260 2406If NOMARK is non-nil, do not push mark."
f32b3b91 2407 (interactive)
cca13260 2408 (idlwave-find-key idlwave-begin-unit-reg -1 nomark))
f32b3b91 2409
cca13260 2410(defun idlwave-end-of-subprogram (&optional nomark)
5a0c3f56 2411 "Move point to the start of the next program unit.
cca13260 2412If NOMARK is non-nil, do not push mark."
f32b3b91
CD
2413 (interactive)
2414 (idlwave-end-of-statement)
cca13260 2415 (idlwave-find-key idlwave-end-unit-reg 1 nomark))
f32b3b91
CD
2416
2417(defun idlwave-mark-statement ()
2418 "Mark current IDL statement."
2419 (interactive)
2420 (idlwave-end-of-statement)
2421 (let ((end (point)))
2422 (idlwave-beginning-of-statement)
0dc2be2f 2423 (push-mark end nil t)))
f32b3b91
CD
2424
2425(defun idlwave-mark-block ()
2426 "Mark containing block."
2427 (interactive)
2428 (idlwave-end-of-statement)
2429 (idlwave-backward-up-block -1)
2430 (idlwave-end-of-statement)
2431 (let ((end (point)))
2432 (idlwave-backward-block)
2433 (idlwave-beginning-of-statement)
0dc2be2f 2434 (push-mark end nil t)))
f32b3b91
CD
2435
2436
2437(defun idlwave-mark-subprogram ()
2438 "Put mark at beginning of program, point at end.
2439The marks are pushed."
2440 (interactive)
2441 (idlwave-end-of-statement)
2442 (idlwave-beginning-of-subprogram)
2443 (let ((beg (point)))
2444 (idlwave-forward-block)
0dc2be2f 2445 (push-mark beg nil t))
f32b3b91
CD
2446 (exchange-point-and-mark))
2447
2448(defun idlwave-backward-up-block (&optional arg)
2449 "Move to beginning of enclosing block if prefix ARG >= 0.
2450If prefix ARG < 0 then move forward to enclosing block end."
2451 (interactive "p")
2452 (idlwave-block-jump-out (- arg) 'nomark))
2453
2454(defun idlwave-beginning-of-block ()
2455 "Go to the beginning of the current block."
2456 (interactive)
2457 (idlwave-block-jump-out -1 'nomark)
2458 (forward-word 1))
2459
2460(defun idlwave-end-of-block ()
2461 "Go to the beginning of the current block."
2462 (interactive)
2463 (idlwave-block-jump-out 1 'nomark)
2464 (backward-word 1))
2465
0dc2be2f 2466(defun idlwave-forward-block (&optional arg)
f32b3b91
CD
2467 "Move across next nested block."
2468 (interactive)
0dc2be2f
S
2469 (let ((arg (or arg 1)))
2470 (if (idlwave-down-block arg)
2471 (idlwave-block-jump-out arg 'nomark))))
f32b3b91
CD
2472
2473(defun idlwave-backward-block ()
2474 "Move backward across previous nested block."
2475 (interactive)
2476 (if (idlwave-down-block -1)
2477 (idlwave-block-jump-out -1 'nomark)))
2478
2479(defun idlwave-down-block (&optional arg)
2480 "Go down a block.
2481With ARG: ARG >= 0 go forwards, ARG < 0 go backwards.
2482Returns non-nil if successfull."
2483 (interactive "p")
2484 (let (status)
2485 (if (< arg 0)
2486 ;; Backward
2487 (let ((eos (save-excursion
2488 (idlwave-block-jump-out -1 'nomark)
2489 (point))))
4b1aaa8b 2490 (if (setq status (idlwave-find-key
f32b3b91
CD
2491 idlwave-end-block-reg -1 'nomark eos))
2492 (idlwave-beginning-of-statement)
2493 (message "No nested block before beginning of containing block.")))
2494 ;; Forward
2495 (let ((eos (save-excursion
2496 (idlwave-block-jump-out 1 'nomark)
2497 (point))))
4b1aaa8b 2498 (if (setq status (idlwave-find-key
f32b3b91
CD
2499 idlwave-begin-block-reg 1 'nomark eos))
2500 (idlwave-end-of-statement)
2501 (message "No nested block before end of containing block."))))
2502 status))
2503
2504(defun idlwave-mark-doclib ()
2505 "Put point at beginning of doc library header, mark at end.
2506The marks are pushed."
2507 (interactive)
2508 (let (beg
2509 (here (point)))
2510 (goto-char (point-max))
2511 (if (re-search-backward idlwave-doclib-start nil t)
4b1aaa8b 2512 (progn
f32b3b91
CD
2513 (setq beg (progn (beginning-of-line) (point)))
2514 (if (re-search-forward idlwave-doclib-end nil t)
2515 (progn
2516 (forward-line 1)
0dc2be2f 2517 (push-mark beg nil t)
f32b3b91
CD
2518 (message "Could not find end of doc library header.")))
2519 (message "Could not find doc library header start.")
2520 (goto-char here)))))
2521
e08734e2
S
2522(defun idlwave-current-routine-fullname ()
2523 (let ((name (idlwave-current-routine)))
2524 (idlwave-make-full-name (nth 2 name) (car name))))
2525
15e42531
CD
2526(defun idlwave-current-routine ()
2527 "Return (NAME TYPE CLASS) of current routine."
2528 (idlwave-routines)
2529 (save-excursion
cca13260 2530 (idlwave-beginning-of-subprogram 'nomark)
15e42531
CD
2531 (if (looking-at "[ \t]*\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)")
2532 (let* ((type (if (string= (downcase (match-string 1)) "pro")
2533 'pro 'function))
2534 (class (idlwave-sintern-class (match-string 3)))
2535 (name (idlwave-sintern-routine-or-method (match-string 4) class)))
2536 (list name type class)))))
2537
f32b3b91
CD
2538(defvar idlwave-shell-prompt-pattern)
2539(defun idlwave-beginning-of-statement ()
2540 "Move to beginning of the current statement.
2541Skips back past statement continuations.
2542Point is placed at the beginning of the line whether or not this is an
2543actual statement."
2544 (interactive)
2545 (cond
2546 ((eq major-mode 'idlwave-shell-mode)
2547 (if (re-search-backward idlwave-shell-prompt-pattern nil t)
2548 (goto-char (match-end 0))))
4b1aaa8b 2549 (t
f32b3b91
CD
2550 (if (save-excursion (forward-line -1) (idlwave-is-continuation-line))
2551 (idlwave-previous-statement)
2552 (beginning-of-line)))))
2553
2554(defun idlwave-previous-statement ()
5a0c3f56 2555 "Move point to beginning of the previous statement.
f32b3b91
CD
2556Returns t if the current line before moving is the beginning of
2557the first non-comment statement in the file, and nil otherwise."
2558 (interactive)
2559 (let (first-statement)
2560 (if (not (= (forward-line -1) 0))
2561 ;; first line in file
2562 t
2563 ;; skip blank lines, label lines, include lines and line comments
2564 (while (and
2565 ;; The current statement is the first statement until we
2566 ;; reach another statement.
2567 (setq first-statement
2568 (or
2569 (looking-at idlwave-comment-line-start-skip)
2570 (looking-at "[ \t]*$")
2571 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2572 (looking-at "^@")))
2573 (= (forward-line -1) 0)))
2574 ;; skip continuation lines
2575 (while (and
2576 (save-excursion
2577 (forward-line -1)
2578 (idlwave-is-continuation-line))
2579 (= (forward-line -1) 0)))
2580 first-statement)))
2581
f32b3b91 2582(defun idlwave-end-of-statement ()
5a0c3f56 2583 "Move point to the end of the current IDL statement.
05a1abfc
CD
2584If not in a statement just moves to end of line. Returns position."
2585 (interactive)
2586 (while (and (idlwave-is-continuation-line)
2587 (= (forward-line 1) 0))
2588 (while (and (idlwave-is-comment-or-empty-line)
2589 (= (forward-line 1) 0))))
2590 (end-of-line)
2591 (point))
2592
2593(defun idlwave-end-of-statement0 ()
5a0c3f56
JB
2594 "Move point to the end of the current IDL statement.
2595If not in a statement just moves to end of line. Returns position."
f32b3b91
CD
2596 (interactive)
2597 (while (and (idlwave-is-continuation-line)
2598 (= (forward-line 1) 0)))
2599 (end-of-line)
2600 (point))
2601
2602(defun idlwave-next-statement ()
5a0c3f56
JB
2603 "Move point to beginning of the next IDL statement.
2604Returns t if that statement is the last non-comment IDL statement
2605in the file, and nil otherwise."
f32b3b91
CD
2606 (interactive)
2607 (let (last-statement)
2608 (idlwave-end-of-statement)
2609 ;; skip blank lines, label lines, include lines and line comments
2610 (while (and (= (forward-line 1) 0)
2611 ;; The current statement is the last statement until
2612 ;; we reach a new statement.
2613 (setq last-statement
2614 (or
2615 (looking-at idlwave-comment-line-start-skip)
2616 (looking-at "[ \t]*$")
2617 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2618 (looking-at "^@")))))
2619 last-statement))
2620
76959b77
S
2621(defun idlwave-skip-multi-commands (&optional lim)
2622 "Skip past multiple commands on a line (with `&')."
2623 (let ((save-point (point)))
2624 (when (re-search-forward ".*&" lim t)
2625 (goto-char (match-end 0))
4b1aaa8b 2626 (if (idlwave-quoted)
6b75c9af
S
2627 (goto-char save-point)
2628 (if (eq (char-after (- (point) 2)) ?&) (goto-char save-point))))
76959b77
S
2629 (point)))
2630
15e42531 2631(defun idlwave-skip-label-or-case ()
f32b3b91
CD
2632 "Skip label or case statement element.
2633Returns position after label.
2634If there is no label point is not moved and nil is returned."
15e42531
CD
2635 ;; Case expressions and labels are terminated by a colon.
2636 ;; So we find the first colon in the line and make sure
2637 ;; - no `?' is before it (might be a ? b : c)
2638 ;; - it is not in a comment
2639 ;; - not in a string constant
2640 ;; - not in parenthesis (like a[0:3])
5e72c6b2 2641 ;; - not followed by another ":" in explicit class, ala a->b::c
15e42531 2642 ;; As many in this mode, this function is heuristic and not an exact
4b1aaa8b 2643 ;; parser.
5e72c6b2
S
2644 (let* ((start (point))
2645 (eos (save-excursion (idlwave-end-of-statement) (point)))
2646 (end (idlwave-find-key ":" 1 'nomark eos)))
f32b3b91 2647 (if (and end
15e42531 2648 (= (nth 0 (parse-partial-sexp start end)) 0)
5e72c6b2
S
2649 (not (string-match "\\?" (buffer-substring start end)))
2650 (not (string-match "^::" (buffer-substring end eos))))
f32b3b91
CD
2651 (progn
2652 (forward-char)
2653 (point))
2654 (goto-char start)
2655 nil)))
2656
2657(defun idlwave-start-of-substatement (&optional pre)
2658 "Move to start of next IDL substatement after point.
2659Uses the type of the current IDL statement to determine if the next
2660statement is on a new line or is a subpart of the current statement.
2661Returns point at start of substatement modulo whitespace.
2662If optional argument is non-nil move to beginning of current
15e42531 2663substatement."
f32b3b91
CD
2664 (let ((orig (point))
2665 (eos (idlwave-end-of-statement))
2666 (ifnest 0)
2667 st nst last)
2668 (idlwave-beginning-of-statement)
15e42531 2669 (idlwave-skip-label-or-case)
52a244eb
S
2670 (if (< (point) orig)
2671 (idlwave-skip-multi-commands orig))
f32b3b91
CD
2672 (setq last (point))
2673 ;; Continue looking for substatements until we are past orig
2674 (while (and (<= (point) orig) (not (eobp)))
2675 (setq last (point))
2676 (setq nst (nth 1 (cdr (setq st (car (idlwave-statement-type))))))
2677 (if (equal (car st) 'if) (setq ifnest (1+ ifnest)))
2678 (cond ((and nst
2679 (idlwave-find-key nst 1 'nomark eos))
2680 (goto-char (match-end 0)))
2681 ((and (> ifnest 0) (idlwave-find-key "\\<else\\>" 1 'nomark eos))
2682 (setq ifnest (1- ifnest))
2683 (goto-char (match-end 0)))
2684 (t (setq ifnest 0)
2685 (idlwave-next-statement))))
2686 (if pre (goto-char last))
15e42531
CD
2687 ;; If a continuation line starts here, move to next line
2688 (if (looking-at "[ \t]*\\$\\([ \t]*\\(;\\|$\\)\\)")
2689 (beginning-of-line 2))
f32b3b91
CD
2690 (point)))
2691
2692(defun idlwave-statement-type ()
2693 "Return the type of the current IDL statement.
2694Uses `idlwave-statement-match' to return a cons of (type . point) with
5a0c3f56 2695point the ending position where the type was determined. Type is the
f32b3b91 2696association from `idlwave-statement-match', i.e. the cons cell from the
5a0c3f56 2697list not just the type symbol. Returns nil if not an identifiable
f32b3b91
CD
2698statement."
2699 (save-excursion
2700 ;; Skip whitespace within a statement which is spaces, tabs, continuations
76959b77
S
2701 ;; and possibly comments
2702 (while (looking-at "[ \t]*\\$")
f32b3b91
CD
2703 (forward-line 1))
2704 (skip-chars-forward " \t")
2705 (let ((st idlwave-statement-match)
2706 (case-fold-search t))
2707 (while (and (not (looking-at (nth 0 (cdr (car st)))))
2708 (setq st (cdr st))))
2709 (if st
2710 (append st (match-end 0))))))
2711
f66f03de 2712(defun idlwave-expand-equal (&optional before after is-action)
5a0c3f56
JB
2713 "Pad '=' with spaces.
2714Two cases: Assignment statement, and keyword assignment.
2715Which case is determined using `idlwave-start-of-substatement' and
2716`idlwave-statement-type'. The equal sign will be surrounded by BEFORE
2717and AFTER blanks. If `idlwave-pad-keyword' is t then keyword assignment
2718is treated just like assignment statements. When nil, spaces are
2719removed for keyword assignment. Any other value keeps the current space
2720around the `='. Limits in for loops are treated as keyword assignment.
52a244eb
S
2721
2722Starting with IDL 6.0, a number of op= assignments are available.
2723Since ambiguities of the form:
2724
2725r and= b
2726rand= b
2727
2728can occur, alphanumeric operator assignment will never be pre-padded,
2729only post-padded. You must use a space before these to disambiguate
2730\(not just for padding, but for proper parsing by IDL too!). Other
2731operators, such as ##=, ^=, etc., will be pre-padded.
2732
f66f03de
S
2733IS-ACTION is ignored.
2734
52a244eb 2735See `idlwave-surround'."
f32b3b91 2736 (if idlwave-surround-by-blank
4b1aaa8b 2737 (let
52a244eb 2738 ((non-an-ops "\\(##\\|\\*\\|\\+\\|-\\|/\\|<\\|>\\|\\^\\)\\=")
4b1aaa8b 2739 (an-ops
52a244eb
S
2740 "\\s-\\(AND\\|EQ\\|GE\\|GT\\|LE\\|LT\\|MOD\\|NE\\|OR\\|XOR\\)\\=")
2741 (len 1))
4b1aaa8b
PE
2742
2743 (save-excursion
52a244eb
S
2744 (let ((case-fold-search t))
2745 (backward-char)
4b1aaa8b 2746 (if (or
52a244eb
S
2747 (re-search-backward non-an-ops nil t)
2748 ;; Why doesn't ##? work for both?
4b1aaa8b 2749 (re-search-backward "\\(#\\)\\=" nil t))
52a244eb
S
2750 (setq len (1+ (length (match-string 1))))
2751 (when (re-search-backward an-ops nil t)
3938cb82 2752 ;(setq begin nil) ; won't modify begin
52a244eb 2753 (setq len (1+ (length (match-string 1))))))))
4b1aaa8b
PE
2754
2755 (if (eq t idlwave-pad-keyword)
52a244eb 2756 ;; Everything gets padded equally
f66f03de 2757 (idlwave-surround before after len)
52a244eb
S
2758 ;; Treating keywords/for variables specially...
2759 (let ((st (save-excursion ; To catch "for" variables
2760 (idlwave-start-of-substatement t)
2761 (idlwave-statement-type)))
2762 (what (save-excursion ; To catch keywords
2763 (skip-chars-backward "= \t")
2764 (nth 2 (idlwave-where)))))
2765 (cond ((or (memq what '(function-keyword procedure-keyword))
4b1aaa8b
PE
2766 (memq (caar st) '(for pdef)))
2767 (cond
52a244eb
S
2768 ((null idlwave-pad-keyword)
2769 (idlwave-surround 0 0)
2770 ) ; remove space
2771 (t))) ; leave any spaces alone
f66f03de 2772 (t (idlwave-surround before after len))))))))
4b1aaa8b 2773
f32b3b91 2774
5e72c6b2
S
2775(defun idlwave-indent-and-action (&optional arg)
2776 "Call `idlwave-indent-line' and do expand actions.
2777With prefix ARG non-nil, indent the entire sub-statement."
2778 (interactive "p")
05a1abfc 2779 (save-excursion
4b1aaa8b
PE
2780 (if (and idlwave-expand-generic-end
2781 (re-search-backward "\\<\\(end\\)\\s-*\\="
05a1abfc
CD
2782 (max 0 (- (point) 10)) t)
2783 (looking-at "\\(end\\)\\([ \n\t]\\|\\'\\)"))
2784 (progn (goto-char (match-end 1))
5e72c6b2
S
2785 ;;Expand the END abbreviation, just as RET or Space would have.
2786 (if abbrev-mode (expand-abbrev)
2787 (idlwave-show-begin)))))
52a244eb
S
2788 (when (and (not arg) current-prefix-arg)
2789 (setq arg current-prefix-arg)
2790 (setq current-prefix-arg nil))
4b1aaa8b 2791 (if arg
5e72c6b2
S
2792 (idlwave-indent-statement)
2793 (idlwave-indent-line t)))
f32b3b91
CD
2794
2795(defun idlwave-indent-line (&optional expand)
5a0c3f56 2796 "Indent current IDL line as code or as a comment.
f32b3b91
CD
2797The actions in `idlwave-indent-action-table' are performed.
2798If the optional argument EXPAND is non-nil then the actions in
2799`idlwave-indent-expand-table' are performed."
2800 (interactive)
2801 ;; Move point out of left margin.
2802 (if (save-excursion
2803 (skip-chars-backward " \t")
2804 (bolp))
2805 (skip-chars-forward " \t"))
2806 (let ((mloc (point-marker)))
2807 (save-excursion
2808 (beginning-of-line)
2809 (if (looking-at idlwave-comment-line-start-skip)
2810 ;; Indentation for a line comment
2811 (progn
2812 (skip-chars-forward " \t")
2813 (idlwave-indent-left-margin (idlwave-comment-hook)))
2814 ;;
2815 ;; Code Line
2816 ;;
2817 ;; Before indenting, run action routines.
2818 ;;
2819 (if (and expand idlwave-do-actions)
8ffcfb27 2820 (mapc 'idlwave-do-action idlwave-indent-expand-table))
f32b3b91
CD
2821 ;;
2822 (if idlwave-do-actions
8ffcfb27 2823 (mapc 'idlwave-do-action idlwave-indent-action-table))
f32b3b91
CD
2824 ;;
2825 ;; No longer expand abbrevs on the line. The user can do this
2826 ;; manually using expand-region-abbrevs.
2827 ;;
2828 ;; Indent for code line
2829 ;;
2830 (beginning-of-line)
2831 (if (or
2832 ;; a label line
2833 (looking-at (concat "^" idlwave-label "[ \t]*$"))
2834 ;; a batch command
2835 (looking-at "^[ \t]*@"))
2836 ;; leave flush left
2837 nil
2838 ;; indent the line
2839 (idlwave-indent-left-margin (idlwave-calculate-indent)))
2840 ;; Adjust parallel comment
76959b77
S
2841 (end-of-line)
2842 (if (idlwave-in-comment)
2843 ;; Emacs 21 is too smart with fill-column on comment indent
2844 (let ((fill-column (if (fboundp 'comment-indent-new-line)
2845 (1- (frame-width))
2846 fill-column)))
2847 (indent-for-comment)))))
f32b3b91
CD
2848 (goto-char mloc)
2849 ;; Get rid of marker
76959b77 2850 (set-marker mloc nil)))
f32b3b91
CD
2851
2852(defun idlwave-do-action (action)
5a0c3f56
JB
2853 "Perform an action repeatedly on a line.
2854ACTION is a list (REG . FUNC). REG is a regular expression. FUNC is
2855either a function name to be called with `funcall' or a list to be
2856evaluated with `eval'. The action performed by FUNC should leave
2857point after the match for REG - otherwise an infinite loop may be
2858entered. FUNC is always passed a final argument of 'is-action, so it
2859can discriminate between being run as an action, or a key binding."
f32b3b91
CD
2860 (let ((action-key (car action))
2861 (action-routine (cdr action)))
2862 (beginning-of-line)
2863 (while (idlwave-look-at action-key)
2864 (if (listp action-routine)
f66f03de
S
2865 (eval (append action-routine '('is-action)))
2866 (funcall action-routine 'is-action)))))
f32b3b91
CD
2867
2868(defun idlwave-indent-to (col &optional min)
2869 "Indent from point with spaces until column COL.
2870Inserts space before markers at point."
2871 (if (not min) (setq min 0))
2872 (insert-before-markers
15e42531 2873 (make-string (max min (- col (current-column))) ?\ )))
f32b3b91
CD
2874
2875(defun idlwave-indent-left-margin (col)
2876 "Indent the current line to column COL.
2877Indents such that first non-whitespace character is at column COL
2878Inserts spaces before markers at point."
2879 (save-excursion
2880 (beginning-of-line)
2881 (delete-horizontal-space)
2882 (idlwave-indent-to col)))
2883
2884(defun idlwave-indent-subprogram ()
5a0c3f56 2885 "Indent program unit which contains point."
f32b3b91
CD
2886 (interactive)
2887 (save-excursion
2888 (idlwave-end-of-statement)
2889 (idlwave-beginning-of-subprogram)
2890 (let ((beg (point)))
2891 (idlwave-forward-block)
2892 (message "Indenting subprogram...")
2893 (indent-region beg (point) nil))
2894 (message "Indenting subprogram...done.")))
2895
5e72c6b2
S
2896(defun idlwave-indent-statement ()
2897 "Indent current statement, including all continuation lines."
2898 (interactive)
2899 (save-excursion
2900 (idlwave-beginning-of-statement)
2901 (let ((beg (point)))
2902 (idlwave-end-of-statement)
2903 (indent-region beg (point) nil))))
2904
f32b3b91
CD
2905(defun idlwave-calculate-indent ()
2906 "Return appropriate indentation for current line as IDL code."
2907 (save-excursion
2908 (beginning-of-line)
2909 (cond
2910 ;; Check for beginning of unit - main (beginning of buffer), pro, or
2911 ;; function
2912 ((idlwave-look-at idlwave-begin-unit-reg)
2913 0)
2914 ;; Check for continuation line
2915 ((save-excursion
2916 (and (= (forward-line -1) 0)
2917 (idlwave-is-continuation-line)))
2918 (idlwave-calculate-cont-indent))
2919 ;; calculate indent based on previous and current statements
52a244eb
S
2920 (t (let* (beg-prev-pos
2921 (the-indent
2922 ;; calculate indent based on previous statement
2923 (save-excursion
2924 (cond
2925 ;; Beginning of file
4b1aaa8b 2926 ((prog1
52a244eb
S
2927 (idlwave-previous-statement)
2928 (setq beg-prev-pos (point)))
2929 0)
2930 ;; Main block
2931 ((idlwave-look-at idlwave-begin-unit-reg t)
2932 (+ (idlwave-current-statement-indent)
2933 idlwave-main-block-indent))
2934 ;; Begin block
2935 ((idlwave-look-at idlwave-begin-block-reg t)
4b1aaa8b 2936 (+ (idlwave-min-current-statement-indent)
52a244eb
S
2937 idlwave-block-indent))
2938 ;; End Block
2939 ((idlwave-look-at idlwave-end-block-reg t)
2940 (progn
2941 ;; Match to the *beginning* of the block opener
2942 (goto-char beg-prev-pos)
2943 (idlwave-block-jump-out -1 'nomark) ; go to begin block
2944 (idlwave-min-current-statement-indent)))
2945 ;; idlwave-end-offset
2946 ;; idlwave-block-indent))
4b1aaa8b 2947
52a244eb
S
2948 ;; Default to current indent
2949 ((idlwave-current-statement-indent))))))
f32b3b91
CD
2950 ;; adjust the indentation based on the current statement
2951 (cond
2952 ;; End block
5e72c6b2
S
2953 ((idlwave-look-at idlwave-end-block-reg)
2954 (+ the-indent idlwave-end-offset))
f32b3b91
CD
2955 (the-indent)))))))
2956
2957;;
52a244eb 2958;; Parentheses indent
f32b3b91
CD
2959;;
2960
5e72c6b2
S
2961(defun idlwave-calculate-paren-indent (beg-reg end-reg close-exp)
2962 "Calculate the continuation indent inside a paren group.
4b1aaa8b 2963Returns a cons-cell with (open . indent), where open is the
5a0c3f56 2964location of the open paren."
5e72c6b2
S
2965 (let ((open (nth 1 (parse-partial-sexp beg-reg end-reg))))
2966 ;; Found an innermost open paren.
2967 (when open
2968 (goto-char open)
2969 ;; Line up with next word unless this is a closing paren.
2970 (cons open
2971 (cond
52a244eb
S
2972 ;; Plain Kernighan-style nested indent
2973 (idlwave-indent-parens-nested
2974 (+ idlwave-continuation-indent (idlwave-current-indent)))
2975
5e72c6b2
S
2976 ;; This is a closed paren - line up under open paren.
2977 (close-exp
2978 (current-column))
52a244eb
S
2979
2980 ;; Empty (or just comment) follows -- revert to basic indent
5e72c6b2
S
2981 ((progn
2982 ;; Skip paren
2983 (forward-char 1)
2984 (looking-at "[ \t$]*\\(;.*\\)?$"))
52a244eb
S
2985 nil)
2986
2987 ;; Line up with first word after any blank space
5e72c6b2
S
2988 ((progn
2989 (skip-chars-forward " \t")
2990 (current-column))))))))
2991
f32b3b91 2992(defun idlwave-calculate-cont-indent ()
5a0c3f56
JB
2993 "Calculates the IDL continuation indent column from the previous statement.
2994Note that here previous statement usually means the beginning of the
2995current statement if this statement is a continuation of the previous
2996line. Various special types of continuations, including assignments,
2997routine definitions, and parenthetical groupings, are treated separately."
f32b3b91 2998 (save-excursion
52a244eb 2999 (let* ((case-fold-search t)
f32b3b91 3000 (end-reg (progn (beginning-of-line) (point)))
52a244eb
S
3001 (beg-last-statement (save-excursion (idlwave-previous-statement)
3002 (point)))
4b1aaa8b 3003 (beg-reg (progn (idlwave-start-of-substatement 'pre)
52a244eb
S
3004 (if (eq (line-beginning-position) end-reg)
3005 (goto-char beg-last-statement)
3006 (point))))
3007 (basic-indent (+ (idlwave-min-current-statement-indent end-reg)
3008 idlwave-continuation-indent))
3009 fancy-nonparen-indent fancy-paren-indent)
4b1aaa8b 3010 (cond
52a244eb
S
3011 ;; Align then with its matching if, etc.
3012 ((let ((matchers '(("\\<if\\>" . "[ \t]*then")
3013 ("\\<\\(if\\|end\\(if\\)?\\)\\>" . "[ \t]*else")
3014 ("\\<\\(for\\|while\\)\\>" . "[ \t]*do")
4b1aaa8b 3015 ("\\<\\(repeat\\|end\\(rep\\)?\\)\\>" .
52a244eb
S
3016 "[ \t]*until")
3017 ("\\<case\\>" . "[ \t]*of")))
3018 match cont-re)
3019 (goto-char end-reg)
4b1aaa8b 3020 (and
52a244eb
S
3021 (setq cont-re
3022 (catch 'exit
3023 (while (setq match (car matchers))
3024 (if (looking-at (cdr match))
3025 (throw 'exit (car match)))
3026 (setq matchers (cdr matchers)))))
3027 (idlwave-find-key cont-re -1 'nomark beg-last-statement)))
3028 (if (looking-at "end") ;; that one's special
4b1aaa8b 3029 (- (idlwave-current-indent)
52a244eb
S
3030 (+ idlwave-block-indent idlwave-end-offset))
3031 (idlwave-current-indent)))
3032
3033 ;; Indent in from the previous line for continuing statements
3034 ((let ((matchers '("\\<then\\>"
3035 "\\<do\\>"
3036 "\\<repeat\\>"
3037 "\\<else\\>"))
3038 match)
3039 (catch 'exit
3040 (goto-char end-reg)
3041 (if (/= (forward-line -1) 0)
3042 (throw 'exit nil))
3043 (while (setq match (car matchers))
3044 (if (looking-at (concat ".*" match "[ \t]*\\$[ \t]*"
3045 "\\(;.*\\)?$"))
3046 (throw 'exit t))
3047 (setq matchers (cdr matchers)))))
3048 (+ idlwave-continuation-indent (idlwave-current-indent)))
3049
3050 ;; Parenthetical indent, either traditional or Kernighan style
3051 ((setq fancy-paren-indent
3052 (let* ((end-reg end-reg)
3053 (close-exp (progn
3054 (goto-char end-reg)
4b1aaa8b 3055 (skip-chars-forward " \t")
52a244eb
S
3056 (looking-at "\\s)")))
3057 indent-cons)
3058 (catch 'loop
3059 (while (setq indent-cons (idlwave-calculate-paren-indent
3060 beg-reg end-reg close-exp))
3061 ;; First permitted containing paren
3062 (if (or
3063 idlwave-indent-to-open-paren
3064 idlwave-indent-parens-nested
3065 (null (cdr indent-cons))
3066 (< (- (cdr indent-cons) basic-indent)
3067 idlwave-max-extra-continuation-indent))
3068 (throw 'loop (cdr indent-cons)))
3069 (setq end-reg (car indent-cons))))))
5e72c6b2
S
3070 fancy-paren-indent)
3071
52a244eb
S
3072 ;; A continued assignment, or procedure call/definition
3073 ((and
3074 (> idlwave-max-extra-continuation-indent 0)
3075 (setq fancy-nonparen-indent
3076 (progn
3077 (goto-char beg-reg)
3078 (while (idlwave-look-at "&")) ; skip continued statements
3079 (cond
3080 ;; A continued Procedure call or definition
3081 ((progn
3082 (idlwave-look-at "^[ \t]*\\(pro\\|function\\)") ;skip over
3083 (looking-at "[ \t]*\\([a-zA-Z0-9.$_]+[ \t]*->[ \t]*\\)?[a-zA-Z][:a-zA-Z0-9$_]*[ \t]*\\(,\\)[ \t]*"))
3084 (goto-char (match-end 0))
3085 ;; Comment only, or blank line with "$"? Basic indent.
3086 (if (save-match-data (looking-at "[ \t$]*\\(;.*\\)?$"))
3087 nil
3088 (current-column)))
4b1aaa8b 3089
52a244eb
S
3090 ;; Continued assignment (with =):
3091 ((catch 'assign ;
3092 (while (looking-at "[^=\n\r]*\\(=\\)[ \t]*")
3093 (goto-char (match-end 0))
4b1aaa8b 3094 (if (null (idlwave-what-function beg-reg))
52a244eb
S
3095 (throw 'assign t))))
3096 (unless (or
3097 (idlwave-in-quote)
3098 (looking-at "[ \t$]*\\(;.*\\)?$") ; use basic
3099 (save-excursion
3100 (goto-char beg-last-statement)
3101 (eq (caar (idlwave-statement-type)) 'for)))
3102 (current-column))))))
3103 (< (- fancy-nonparen-indent basic-indent)
3104 idlwave-max-extra-continuation-indent))
3105 (if fancy-paren-indent ;calculated but disallowed paren indent
3106 (+ fancy-nonparen-indent idlwave-continuation-indent)
3107 fancy-nonparen-indent))
3108
3109 ;; Basic indent, by default
3110 (t basic-indent)))))
3111
3112
f32b3b91 3113
15e42531
CD
3114(defun idlwave-find-key (key-re &optional dir nomark limit)
3115 "Move to next match of the regular expression KEY-RE.
3116Matches inside comments or string constants will be ignored.
3117If DIR is negative, the search will be backwards.
3118At a successful match, the mark is pushed unless NOMARK is non-nil.
3119Searches are limited to LIMIT.
3120Searches are case-insensitive and use a special syntax table which
3121treats `$' and `_' as word characters.
3122Return value is the beginning of the match or (in case of failure) nil."
3123 (setq dir (or dir 0))
3124 (let ((case-fold-search t)
3125 (search-func (if (> dir 0) 're-search-forward 're-search-backward))
3126 found)
3127 (idlwave-with-special-syntax
3128 (save-excursion
3129 (catch 'exit
3130 (while (funcall search-func key-re limit t)
3131 (if (not (idlwave-quoted))
52a244eb
S
3132 (throw 'exit (setq found (match-beginning 0)))
3133 (if (or (and (> dir 0) (eobp))
3134 (and (< dir 0) (bobp)))
3135 (throw 'exit nil)))))))
15e42531
CD
3136 (if found
3137 (progn
3138 (if (not nomark) (push-mark))
3139 (goto-char found)
3140 found)
3141 nil)))
3142
f32b3b91
CD
3143(defun idlwave-block-jump-out (&optional dir nomark)
3144 "When optional argument DIR is non-negative, move forward to end of
3145current block using the `idlwave-begin-block-reg' and `idlwave-end-block-reg'
5a0c3f56
JB
3146regular expressions. When DIR is negative, move backwards to block beginning.
3147Recursively calls itself to skip over nested blocks. DIR defaults to
3148forward. Calls `push-mark' unless the optional argument NOMARK is
3149non-nil. Movement is limited by the start of program units because of
f32b3b91
CD
3150possibility of unbalanced blocks."
3151 (interactive "P")
3152 (or dir (setq dir 0))
3153 (let* ((here (point))
3154 (case-fold-search t)
3155 (limit (if (>= dir 0) (point-max) (point-min)))
4b1aaa8b 3156 (block-limit (if (>= dir 0)
f32b3b91
CD
3157 idlwave-begin-block-reg
3158 idlwave-end-block-reg))
3159 found
3160 (block-reg (concat idlwave-begin-block-reg "\\|"
3161 idlwave-end-block-reg))
3162 (unit-limit (or (save-excursion
3163 (if (< dir 0)
3164 (idlwave-find-key
3165 idlwave-begin-unit-reg dir t limit)
3166 (end-of-line)
4b1aaa8b 3167 (idlwave-find-key
f32b3b91
CD
3168 idlwave-end-unit-reg dir t limit)))
3169 limit)))
3170 (if (>= dir 0) (end-of-line)) ;Make sure we are in current block
3171 (if (setq found (idlwave-find-key block-reg dir t unit-limit))
3172 (while (and found (looking-at block-limit))
3173 (if (>= dir 0) (forward-word 1))
3174 (idlwave-block-jump-out dir t)
3175 (setq found (idlwave-find-key block-reg dir t unit-limit))))
3176 (if (not nomark) (push-mark here))
3177 (if (not found) (goto-char unit-limit)
3178 (if (>= dir 0) (forward-word 1)))))
3179
52a244eb
S
3180(defun idlwave-min-current-statement-indent (&optional end-reg)
3181 "The minimum indent in the current statement."
3182 (idlwave-beginning-of-statement)
3183 (if (not (idlwave-is-continuation-line))
3184 (idlwave-current-indent)
3185 (let ((min (idlwave-current-indent)) comm-or-empty)
3186 (while (and (= (forward-line 1) 0)
3187 (or (setq comm-or-empty (idlwave-is-comment-or-empty-line))
3188 (idlwave-is-continuation-line))
3189 (or (null end-reg) (< (point) end-reg)))
3190 (unless comm-or-empty (setq min (min min (idlwave-current-indent)))))
3191 (if (or comm-or-empty (and end-reg (>= (point) end-reg)))
4b1aaa8b 3192 min
52a244eb
S
3193 (min min (idlwave-current-indent))))))
3194
3195(defun idlwave-current-statement-indent (&optional last-line)
f32b3b91
CD
3196 "Return indentation of the current statement.
3197If in a statement, moves to beginning of statement before finding indent."
52a244eb
S
3198 (if last-line
3199 (idlwave-end-of-statement)
3200 (idlwave-beginning-of-statement))
f32b3b91
CD
3201 (idlwave-current-indent))
3202
3203(defun idlwave-current-indent ()
3204 "Return the column of the indentation of the current line.
5a0c3f56 3205Skips any whitespace. Returns 0 if the end-of-line follows the whitespace."
f32b3b91
CD
3206 (save-excursion
3207 (beginning-of-line)
3208 (skip-chars-forward " \t")
3209 ;; if we are at the end of blank line return 0
3210 (cond ((eolp) 0)
3211 ((current-column)))))
3212
3213(defun idlwave-is-continuation-line ()
5a0c3f56 3214 "Test if current line is continuation line.
5e72c6b2
S
3215Blank or comment-only lines following regular continuation lines (with
3216`$') count as continuations too."
0dc2be2f
S
3217 (let (p)
3218 (save-excursion
4b1aaa8b 3219 (or
0dc2be2f
S
3220 (idlwave-look-at "\\<\\$")
3221 (catch 'loop
4b1aaa8b 3222 (while (and (looking-at "^[ \t]*\\(;.*\\)?$")
0dc2be2f
S
3223 (eq (forward-line -1) 0))
3224 (if (setq p (idlwave-look-at "\\<\\$")) (throw 'loop p))))))))
f32b3b91
CD
3225
3226(defun idlwave-is-comment-line ()
5a0c3f56 3227 "Test if the current line is a comment line."
f32b3b91
CD
3228 (save-excursion
3229 (beginning-of-line 1)
3230 (looking-at "[ \t]*;")))
3231
05a1abfc 3232(defun idlwave-is-comment-or-empty-line ()
5a0c3f56 3233 "Test if the current line is a comment line."
05a1abfc
CD
3234 (save-excursion
3235 (beginning-of-line 1)
3236 (looking-at "[ \t]*[;\n]")))
3237
f32b3b91 3238(defun idlwave-look-at (regexp &optional cont beg)
5a0c3f56 3239 "Search current line from current point for REGEXP.
15e42531
CD
3240If optional argument CONT is non-nil, searches to the end of
3241the current statement.
3242If optional arg BEG is non-nil, search starts from the beginning of the
3243current statement.
3244Ignores matches that end in a comment or inside a string expression.
3245Returns point if successful, nil otherwise.
3246This function produces unexpected results if REGEXP contains quotes or
5a0c3f56 3247a comment delimiter. The search is case insensitive.
15e42531 3248If successful leaves point after the match, otherwise, does not move point."
f32b3b91 3249 (let ((here (point))
f32b3b91 3250 (case-fold-search t)
15e42531
CD
3251 (eos (save-excursion
3252 (if cont (idlwave-end-of-statement) (end-of-line))
3253 (point)))
f32b3b91 3254 found)
15e42531
CD
3255 (idlwave-with-special-syntax
3256 (if beg (idlwave-beginning-of-statement))
3257 (while (and (setq found (re-search-forward regexp eos t))
3258 (idlwave-quoted))))
f32b3b91
CD
3259 (if (not found) (goto-char here))
3260 found))
3261
3262(defun idlwave-fill-paragraph (&optional nohang)
5a0c3f56 3263 "Fill paragraphs in comments.
f32b3b91
CD
3264A paragraph is made up of all contiguous lines having the same comment
3265leader (the leading whitespace before the comment delimiter and the
3266comment delimiter). In addition, paragraphs are separated by blank
5a0c3f56 3267line comments. The indentation is given by the hanging indent of the
f32b3b91 3268first line, otherwise by the minimum indentation of the lines after
5a0c3f56
JB
3269the first line. The indentation of the first line does not change.
3270Does not effect code lines. Does not fill comments on the same line
f32b3b91 3271with code. The hanging indent is given by the end of the first match
5a0c3f56
JB
3272matching `idlwave-hang-indent-regexp' on the paragraph's first line.
3273If the optional argument NOHANG is non-nil then the hanging indent is
f32b3b91
CD
3274ignored."
3275 (interactive "P")
3276 ;; check if this is a line comment
3277 (if (save-excursion
3278 (beginning-of-line)
3279 (skip-chars-forward " \t")
3280 (looking-at comment-start))
3281 (let
3282 ((indent 999)
3283 pre here diff fill-prefix-reg bcl first-indent
3284 hang start end)
3285 ;; Change tabs to spaces in the surrounding paragraph.
3286 ;; The surrounding paragraph will be the largest containing block of
3287 ;; contiguous line comments. Thus, we may be changing tabs in
3288 ;; a much larger area than is needed, but this is the easiest
3289 ;; brute force way to do it.
3290 ;;
3291 ;; This has the undesirable side effect of replacing the tabs
3292 ;; permanently without the user's request or knowledge.
3293 (save-excursion
3294 (backward-paragraph)
3295 (setq start (point)))
3296 (save-excursion
3297 (forward-paragraph)
3298 (setq end (point)))
3299 (untabify start end)
3300 ;;
3301 (setq here (point))
3302 (beginning-of-line)
3303 (setq bcl (point))
e180ab9f
GM
3304 (re-search-forward (concat "^[ \t]*" comment-start "+")
3305 (point-at-eol) t)
f32b3b91
CD
3306 ;; Get the comment leader on the line and its length
3307 (setq pre (current-column))
3308 ;; the comment leader is the indentation plus exactly the
3309 ;; number of consecutive ";".
3310 (setq fill-prefix-reg
3311 (concat
3312 (setq fill-prefix
3313 (regexp-quote
3314 (buffer-substring (save-excursion
3315 (beginning-of-line) (point))
3316 (point))))
3317 "[^;]"))
4b1aaa8b 3318
f32b3b91
CD
3319 ;; Mark the beginning and end of the paragraph
3320 (goto-char bcl)
3321 (while (and (looking-at fill-prefix-reg)
3322 (not (looking-at paragraph-separate))
3323 (not (bobp)))
3324 (forward-line -1))
3325 ;; Move to first line of paragraph
3326 (if (/= (point) bcl)
3327 (forward-line 1))
3328 (setq start (point))
3329 (goto-char bcl)
3330 (while (and (looking-at fill-prefix-reg)
3331 (not (looking-at paragraph-separate))
3332 (not (eobp)))
3333 (forward-line 1))
3334 (beginning-of-line)
3335 (if (or (not (looking-at fill-prefix-reg))
3336 (looking-at paragraph-separate))
3337 (forward-line -1))
3338 (end-of-line)
3339 ;; if at end of buffer add a newline (need this because
3340 ;; fill-region needs END to be at the beginning of line after
3341 ;; the paragraph or it will add a line).
3342 (if (eobp)
3343 (progn (insert ?\n) (backward-char 1)))
3344 ;; Set END to the beginning of line after the paragraph
3345 ;; END is calculated as distance from end of buffer
3346 (setq end (- (point-max) (point) 1))
3347 ;;
3348 ;; Calculate the indentation for the paragraph.
3349 ;;
3350 ;; In the following while statements, after one iteration
3351 ;; point will be at the beginning of a line in which case
3352 ;; the while will not be executed for the
3353 ;; the first paragraph line and thus will not affect the
3354 ;; indentation.
3355 ;;
3356 ;; First check to see if indentation is based on hanging indent.
3357 (if (and (not nohang) idlwave-hanging-indent
3358 (setq hang
3359 (save-excursion
3360 (goto-char start)
3361 (idlwave-calc-hanging-indent))))
3362 ;; Adjust lines of paragraph by inserting spaces so that
3363 ;; each line's indent is at least as great as the hanging
3364 ;; indent. This is needed for fill-paragraph to work with
3365 ;; a fill-prefix.
3366 (progn
3367 (setq indent hang)
3368 (beginning-of-line)
3369 (while (> (point) start)
e180ab9f 3370 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91
CD
3371 (if (> (setq diff (- indent (current-column))) 0)
3372 (progn
3373 (if (>= here (point))
3374 ;; adjust the original location for the
3375 ;; inserted text.
3376 (setq here (+ here diff)))
15e42531 3377 (insert (make-string diff ?\ ))))
f32b3b91
CD
3378 (forward-line -1))
3379 )
4b1aaa8b 3380
f32b3b91
CD
3381 ;; No hang. Instead find minimum indentation of paragraph
3382 ;; after first line.
3383 ;; For the following while statement, since START is at the
aa87aafc 3384 ;; beginning of line and END is at the end of line
f32b3b91
CD
3385 ;; point is greater than START at least once (which would
3386 ;; be the case for a single line paragraph).
3387 (while (> (point) start)
3388 (beginning-of-line)
3389 (setq indent
3390 (min indent
3391 (progn
e180ab9f 3392 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91 3393 (current-column))))
e180ab9f 3394 (forward-line -1)))
f32b3b91
CD
3395 (setq fill-prefix (concat fill-prefix
3396 (make-string (- indent pre)
15e42531 3397 ?\ )))
f32b3b91
CD
3398 ;; first-line indent
3399 (setq first-indent
3400 (max
3401 (progn
e180ab9f 3402 (re-search-forward comment-start-skip (point-at-eol) t)
f32b3b91
CD
3403 (current-column))
3404 indent))
4b1aaa8b 3405
f32b3b91
CD
3406 ;; try to keep point at its original place
3407 (goto-char here)
3408
3409 ;; In place of the more modern fill-region-as-paragraph, a hack
3410 ;; to keep whitespace untouched on the first line within the
3411 ;; indent length and to preserve any indent on the first line
3412 ;; (first indent).
3413 (save-excursion
3414 (setq diff
3415 (buffer-substring start (+ start first-indent -1)))
15e42531 3416 (subst-char-in-region start (+ start first-indent -1) ?\ ?~ nil)
f32b3b91
CD
3417 (fill-region-as-paragraph
3418 start
3419 (- (point-max) end)
3420 (current-justification)
3421 nil)
3422 (delete-region start (+ start first-indent -1))
3423 (goto-char start)
3424 (insert diff))
3425 ;; When we want the point at the beginning of the comment
3426 ;; body fill-region will put it at the beginning of the line.
3427 (if (bolp) (skip-chars-forward (concat " \t" comment-start)))
3428 (setq fill-prefix nil))))
3429
3430(defun idlwave-calc-hanging-indent ()
5a0c3f56
JB
3431 "Calculate the position of the hanging indent for the comment paragraph.
3432The hanging indent position is given by the first match with the
3433`idlwave-hang-indent-regexp'. If `idlwave-use-last-hang-indent' is
3434non-nil then use last occurrence matching `idlwave-hang-indent-regexp'
3435on the line.
f32b3b91
CD
3436If not found returns nil."
3437 (if idlwave-use-last-hang-indent
3438 (save-excursion
3439 (end-of-line)
e180ab9f 3440 (if (re-search-backward idlwave-hang-indent-regexp (point-at-bol) t)
f32b3b91
CD
3441 (+ (current-column) (length idlwave-hang-indent-regexp))))
3442 (save-excursion
3443 (beginning-of-line)
e180ab9f 3444 (if (re-search-forward idlwave-hang-indent-regexp (point-at-eol) t)
f32b3b91
CD
3445 (current-column)))))
3446
3447(defun idlwave-auto-fill ()
4b1aaa8b 3448 "Called to break lines in auto fill mode.
52a244eb
S
3449Only fills non-comment lines if `idlwave-fill-comment-line-only' is
3450non-nil. Places a continuation character at the end of the line if
3451not in a comment. Splits strings with IDL concatenation operator `+'
3452if `idlwave-auto-fill-split-string' is non-nil."
f32b3b91
CD
3453 (if (<= (current-column) fill-column)
3454 nil ; do not to fill
3455 (if (or (not idlwave-fill-comment-line-only)
3456 (save-excursion
3457 ;; Check for comment line
3458 (beginning-of-line)
3459 (looking-at idlwave-comment-line-start-skip)))
3460 (let (beg)
3461 (idlwave-indent-line)
3462 ;; Prevent actions do-auto-fill which calls indent-line-function.
3463 (let (idlwave-do-actions
d6aac72d 3464 (paragraph-separate ".")
52a244eb
S
3465 (fill-nobreak-predicate
3466 (if (and (idlwave-in-quote)
3467 idlwave-auto-fill-split-string)
3468 (lambda () ;; We'll need 5 spaces for " ' + $"
3469 (<= (- fill-column (current-column)) 5)
3470 ))))
f32b3b91
CD
3471 (do-auto-fill))
3472 (save-excursion
3473 (end-of-line 0)
3474 ;; Indent the split line
a86bd650 3475 (idlwave-indent-line))
f32b3b91
CD
3476 (if (save-excursion
3477 (beginning-of-line)
3478 (looking-at idlwave-comment-line-start-skip))
3479 ;; A continued line comment
3480 ;; We treat continued line comments as part of a comment
3481 ;; paragraph. So we check for a hanging indent.
3482 (if idlwave-hanging-indent
3483 (let ((here (- (point-max) (point)))
3484 (indent
3485 (save-excursion
3486 (forward-line -1)
3487 (idlwave-calc-hanging-indent))))
e180ab9f
GM
3488 (when indent
3489 ;; Remove whitespace between comment delimiter and
3490 ;; text, insert spaces for appropriate indentation.
3491 (beginning-of-line)
3492 (re-search-forward comment-start-skip (point-at-eol) t)
3493 (delete-horizontal-space)
3494 (idlwave-indent-to indent)
3495 (goto-char (- (point-max) here)))))
f32b3b91
CD
3496 ;; Split code or comment?
3497 (if (save-excursion
3498 (end-of-line 0)
3499 (idlwave-in-comment))
52a244eb 3500 ;; Splitting a non-full-line comment.
f32b3b91
CD
3501 ;; Insert the comment delimiter from split line
3502 (progn
3503 (save-excursion
3504 (beginning-of-line)
3505 (skip-chars-forward " \t")
3506 ;; Insert blank to keep off beginning of line
3507 (insert " "
3508 (save-excursion
3509 (forward-line -1)
3510 (buffer-substring (idlwave-goto-comment)
3511 (progn
3512 (skip-chars-forward "; ")
3513 (point))))))
3514 (idlwave-indent-line))
3515 ;; Split code line - add continuation character
3516 (save-excursion
3517 (end-of-line 0)
3518 ;; Check to see if we split a string
3519 (if (and (setq beg (idlwave-in-quote))
3520 idlwave-auto-fill-split-string)
3521 ;; Split the string and concatenate.
3522 ;; The first extra space is for the space
3523 ;; the line was split. That space was removed.
3524 (insert " " (char-after beg) " +"))
3525 (insert " $"))
3526 (if beg
3527 (if idlwave-auto-fill-split-string
3528 ;; Make the second part of continued string
3529 (save-excursion
3530 (beginning-of-line)
3531 (skip-chars-forward " \t")
3532 (insert (char-after beg)))
3533 ;; Warning
3534 (beep)
3535 (message "Warning: continuation inside a string.")))
3536 ;; Although do-auto-fill (via indent-new-comment-line) calls
3537 ;; idlwave-indent-line for the new line, re-indent again
3538 ;; because of the addition of the continuation character.
3539 (idlwave-indent-line))
3540 )))))
3541
3542(defun idlwave-auto-fill-mode (arg)
3543 "Toggle auto-fill mode for IDL mode.
3544With arg, turn auto-fill mode on if arg is positive.
3545In auto-fill mode, inserting a space at a column beyond `fill-column'
3546automatically breaks the line at a previous space."
3547 (interactive "P")
3548 (prog1 (set idlwave-fill-function
3549 (if (if (null arg)
3550 (not (symbol-value idlwave-fill-function))
3551 (> (prefix-numeric-value arg) 0))
3552 'idlwave-auto-fill
3553 nil))
3554 ;; update mode-line
3555 (set-buffer-modified-p (buffer-modified-p))))
3556
52a244eb
S
3557;(defun idlwave-fill-routine-call ()
3558; "Fill a routine definition or statement, indenting appropriately."
3559; (let ((where (idlwave-where)))))
3560
3561
5a0c3f56 3562(defun idlwave-doc-header (&optional nomark)
f32b3b91 3563 "Insert a documentation header at the beginning of the unit.
5a0c3f56
JB
3564Inserts the value of the variable `idlwave-file-header'. Sets mark
3565before moving to do insertion unless the optional prefix argument
3566NOMARK is non-nil."
f32b3b91
CD
3567 (interactive "P")
3568 (or nomark (push-mark))
3569 ;; make sure we catch the current line if it begins the unit
5e72c6b2
S
3570 (if idlwave-header-to-beginning-of-file
3571 (goto-char (point-min))
3572 (end-of-line)
3573 (idlwave-beginning-of-subprogram)
3574 (beginning-of-line)
3575 ;; skip function or procedure line
3576 (if (idlwave-look-at "\\<\\(pro\\|function\\)\\>")
3577 (progn
3578 (idlwave-end-of-statement)
3579 (if (> (forward-line 1) 0) (insert "\n")))))
3580 (let ((pos (point)))
3581 (if idlwave-file-header
3582 (cond ((car idlwave-file-header)
a527b753 3583 (insert-file-contents (car idlwave-file-header)))
5e72c6b2
S
3584 ((stringp (car (cdr idlwave-file-header)))
3585 (insert (car (cdr idlwave-file-header))))))
3586 (goto-char pos)))
f32b3b91
CD
3587
3588(defun idlwave-default-insert-timestamp ()
5a0c3f56 3589 "Default timestamp insertion function."
f32b3b91
CD
3590 (insert (current-time-string))
3591 (insert ", " (user-full-name))
5e72c6b2 3592 (if (boundp 'user-mail-address)
4b1aaa8b 3593 (insert " <" user-mail-address ">")
5e72c6b2 3594 (insert " <" (user-login-name) "@" (system-name) ">"))
f32b3b91
CD
3595 ;; Remove extra spaces from line
3596 (idlwave-fill-paragraph)
3597 ;; Insert a blank line comment to separate from the date entry -
3598 ;; will keep the entry from flowing onto date line if re-filled.
5e72c6b2 3599 (insert "\n;\n;\t\t"))
f32b3b91
CD
3600
3601(defun idlwave-doc-modification ()
3602 "Insert a brief modification log at the beginning of the current program.
3603Looks for an occurrence of the value of user variable
5a0c3f56
JB
3604`idlwave-doc-modifications-keyword' if non-nil. Inserts time and user
3605name and places the point for the user to add a log. Before moving, saves
f32b3b91
CD
3606location on mark ring so that the user can return to previous point."
3607 (interactive)
3608 (push-mark)
05a1abfc
CD
3609 (let* (beg end)
3610 (if (and (or (re-search-backward idlwave-doclib-start nil t)
3611 (progn
3612 (goto-char (point-min))
3613 (re-search-forward idlwave-doclib-start nil t)))
3614 (setq beg (match-beginning 0))
3615 (re-search-forward idlwave-doclib-end nil t)
3616 (setq end (match-end 0)))
3617 (progn
3618 (goto-char beg)
4b1aaa8b 3619 (if (re-search-forward
05a1abfc
CD
3620 (concat idlwave-doc-modifications-keyword ":")
3621 end t)
3622 (end-of-line)
3623 (goto-char end)
3624 (end-of-line -1)
3625 (insert "\n" comment-start "\n")
3626 (insert comment-start " " idlwave-doc-modifications-keyword ":"))
3627 (insert "\n;\n;\t")
3628 (run-hooks 'idlwave-timestamp-hook))
3629 (error "No valid DOCLIB header"))))
f32b3b91 3630
e08734e2 3631
8d222148
SM
3632;; CJC 3/16/93
3633;; Interface to expand-region-abbrevs which did not work when the
3634;; abbrev hook associated with an abbrev moves point backwards
3635;; after abbrev expansion, e.g., as with the abbrev '.n'.
3636;; The original would enter an infinite loop in attempting to expand
3637;; .n (it would continually expand and unexpand the abbrev without expanding
3638;; because the point would keep going back to the beginning of the
3639;; abbrev instead of to the end of the abbrev). We now keep the
3640;; abbrev hook from moving backwards.
f32b3b91
CD
3641;;;
3642(defun idlwave-expand-region-abbrevs (start end)
3643 "Expand each abbrev occurrence in the region.
3644Calling from a program, arguments are START END."
3645 (interactive "r")
3646 (save-excursion
3647 (goto-char (min start end))
3648 (let ((idlwave-show-block nil) ;Do not blink
3649 (idlwave-abbrev-move nil)) ;Do not move
3650 (expand-region-abbrevs start end 'noquery))))
3651
3652(defun idlwave-quoted ()
5a0c3f56
JB
3653 "Return t if point is in a comment or quoted string.
3654Returns nil otherwise."
f32b3b91
CD
3655 (or (idlwave-in-comment) (idlwave-in-quote)))
3656
3657(defun idlwave-in-quote ()
5a0c3f56 3658 "Return location of the opening quote
f32b3b91
CD
3659if point is in a IDL string constant, nil otherwise.
3660Ignores comment delimiters on the current line.
3661Properly handles nested quotation marks and octal
3662constants - a double quote followed by an octal digit."
8d222148
SM
3663;; Treat an octal inside an apostrophe to be a normal string. Treat a
3664;; double quote followed by an octal digit to be an octal constant
3665;; rather than a string. Therefore, there is no terminating double
3666;; quote.
f32b3b91
CD
3667 (save-excursion
3668 ;; Because single and double quotes can quote each other we must
3669 ;; search for the string start from the beginning of line.
3670 (let* ((start (point))
3671 (eol (progn (end-of-line) (point)))
3672 (bq (progn (beginning-of-line) (point)))
3673 (endq (point))
3674 (data (match-data))
3675 delim
3676 found)
3677 (while (< endq start)
3678 ;; Find string start
3679 ;; Don't find an octal constant beginning with a double quote
52a244eb 3680 (if (re-search-forward "[\"']" eol 'lim)
f32b3b91
CD
3681 ;; Find the string end.
3682 ;; In IDL, two consecutive delimiters after the start of a
3683 ;; string act as an
3684 ;; escape for the delimiter in the string.
3685 ;; Two consecutive delimiters alone (i.e., not after the
aa87aafc 3686 ;; start of a string) is the null string.
f32b3b91
CD
3687 (progn
3688 ;; Move to position after quote
3689 (goto-char (1+ (match-beginning 0)))
3690 (setq bq (1- (point)))
3691 ;; Get the string delimiter
3692 (setq delim (char-to-string (preceding-char)))
3693 ;; Check for null string
3694 (if (looking-at delim)
3695 (progn (setq endq (point)) (forward-char 1))
3696 ;; Look for next unpaired delimiter
3697 (setq found (search-forward delim eol 'lim))
3698 (while (looking-at delim)
3699 (forward-char 1)
3700 (setq found (search-forward delim eol 'lim)))
8d222148 3701 (setq endq (if found (1- (point)) (point)))
f32b3b91
CD
3702 ))
3703 (progn (setq bq (point)) (setq endq (point)))))
3704 (store-match-data data)
3705 ;; return string beginning position or nil
3706 (if (> start bq) bq))))
3707
76959b77 3708(defun idlwave-is-pointer-dereference (&optional limit)
5a0c3f56 3709 "Determine if the character after point is a pointer dereference *."
8d222148
SM
3710 (and
3711 (eq (char-after) ?\*)
3712 (not (idlwave-in-quote))
3713 (save-excursion
3714 (forward-char)
3715 (re-search-backward (concat "\\(" idlwave-idl-keywords
3716 "\\|[[(*+-/=,^><]\\)\\s-*\\*") limit t))))
76959b77
S
3717
3718
f32b3b91
CD
3719;; Statement templates
3720
3721;; Replace these with a general template function, something like
3722;; expand.el (I think there was also something with a name similar to
3723;; dmacro.el)
3724
3725(defun idlwave-template (s1 s2 &optional prompt noindent)
3726 "Build a template with optional prompt expression.
3727
3728Opens a line if point is not followed by a newline modulo intervening
3729whitespace. S1 and S2 are strings. S1 is inserted at point followed
595ab50b 3730by S2. Point is inserted between S1 and S2. The case of S1 and S2 is
5a0c3f56
JB
3731adjusted according to `idlwave-abbrev-change-case'. If optional
3732argument PROMPT is a string then it is displayed as a message in the
f32b3b91
CD
3733minibuffer. The PROMPT serves as a reminder to the user of an
3734expression to enter.
3735
3736The lines containing S1 and S2 are reindented using `indent-region'
3737unless the optional second argument NOINDENT is non-nil."
15e42531 3738 (if (eq major-mode 'idlwave-shell-mode)
05a1abfc 3739 ;; This is a gross hack to avoit template abbrev expansion
15e42531
CD
3740 ;; in the shell. FIXME: This is a dirty hack.
3741 (if (and (eq this-command 'self-insert-command)
3742 (equal last-abbrev-location (point)))
3743 (insert last-abbrev-text)
3744 (error "No templates in idlwave-shell"))
3745 (cond ((eq idlwave-abbrev-change-case 'down)
3746 (setq s1 (downcase s1) s2 (downcase s2)))
3747 (idlwave-abbrev-change-case
3748 (setq s1 (upcase s1) s2 (upcase s2))))
e180ab9f 3749 (let ((beg (point-at-bol))
15e42531
CD
3750 end)
3751 (if (not (looking-at "\\s-*\n"))
3752 (open-line 1))
3753 (insert s1)
3754 (save-excursion
3755 (insert s2)
3756 (setq end (point)))
3757 (if (not noindent)
3758 (indent-region beg end nil))
3759 (if (stringp prompt)
274f1353 3760 (message "%s" prompt)))))
4b1aaa8b 3761
595ab50b
CD
3762(defun idlwave-rw-case (string)
3763 "Make STRING have the case required by `idlwave-reserved-word-upcase'."
3764 (if idlwave-reserved-word-upcase
3765 (upcase string)
3766 string))
3767
f32b3b91
CD
3768(defun idlwave-elif ()
3769 "Build skeleton IDL if-else block."
3770 (interactive)
595ab50b
CD
3771 (idlwave-template
3772 (idlwave-rw-case "if")
3773 (idlwave-rw-case " then begin\n\nendif else begin\n\nendelse")
3774 "Condition expression"))
f32b3b91
CD
3775
3776(defun idlwave-case ()
3777 "Build skeleton IDL case statement."
3778 (interactive)
4b1aaa8b 3779 (idlwave-template
595ab50b
CD
3780 (idlwave-rw-case "case")
3781 (idlwave-rw-case " of\n\nendcase")
3782 "Selector expression"))
f32b3b91 3783
05a1abfc
CD
3784(defun idlwave-switch ()
3785 "Build skeleton IDL switch statement."
3786 (interactive)
4b1aaa8b 3787 (idlwave-template
05a1abfc
CD
3788 (idlwave-rw-case "switch")
3789 (idlwave-rw-case " of\n\nendswitch")
3790 "Selector expression"))
3791
f32b3b91 3792(defun idlwave-for ()
5a0c3f56 3793 "Build skeleton IDL loop statement."
f32b3b91 3794 (interactive)
4b1aaa8b 3795 (idlwave-template
595ab50b
CD
3796 (idlwave-rw-case "for")
3797 (idlwave-rw-case " do begin\n\nendfor")
3798 "Loop expression"))
f32b3b91
CD
3799
3800(defun idlwave-if ()
5a0c3f56 3801 "Build skeleton IDL if statement."
f32b3b91 3802 (interactive)
595ab50b
CD
3803 (idlwave-template
3804 (idlwave-rw-case "if")
3805 (idlwave-rw-case " then begin\n\nendif")
3806 "Scalar logical expression"))
f32b3b91
CD
3807
3808(defun idlwave-procedure ()
3809 (interactive)
4b1aaa8b 3810 (idlwave-template
595ab50b
CD
3811 (idlwave-rw-case "pro")
3812 (idlwave-rw-case "\n\nreturn\nend")
3813 "Procedure name"))
f32b3b91
CD
3814
3815(defun idlwave-function ()
3816 (interactive)
4b1aaa8b 3817 (idlwave-template
595ab50b
CD
3818 (idlwave-rw-case "function")
3819 (idlwave-rw-case "\n\nreturn\nend")
3820 "Function name"))
f32b3b91
CD
3821
3822(defun idlwave-repeat ()
3823 (interactive)
595ab50b
CD
3824 (idlwave-template
3825 (idlwave-rw-case "repeat begin\n\nendrep until")
3826 (idlwave-rw-case "")
3827 "Exit condition"))
f32b3b91
CD
3828
3829(defun idlwave-while ()
3830 (interactive)
4b1aaa8b 3831 (idlwave-template
595ab50b
CD
3832 (idlwave-rw-case "while")
3833 (idlwave-rw-case " do begin\n\nendwhile")
3834 "Entry condition"))
f32b3b91
CD
3835
3836(defun idlwave-split-string (string &optional pattern)
3837 "Return a list of substrings of STRING which are separated by PATTERN.
3838If PATTERN is omitted, it defaults to \"[ \\f\\t\\n\\r\\v]+\"."
3839 (or pattern
3840 (setq pattern "[ \f\t\n\r\v]+"))
3841 (let (parts (start 0))
3842 (while (string-match pattern string start)
3843 (setq parts (cons (substring string start (match-beginning 0)) parts)
3844 start (match-end 0)))
3845 (nreverse (cons (substring string start) parts))))
3846
3847(defun idlwave-replace-string (string replace_string replace_with)
3848 (let* ((start 0)
3849 (last (length string))
3850 (ret_string "")
3851 end)
3852 (while (setq end (string-match replace_string string start))
3853 (setq ret_string
3854 (concat ret_string (substring string start end) replace_with))
3855 (setq start (match-end 0)))
3856 (setq ret_string (concat ret_string (substring string start last)))))
3857
3858(defun idlwave-get-buffer-visiting (file)
3859 ;; Return the buffer currently visiting FILE
3860 (cond
3861 ((boundp 'find-file-compare-truenames) ; XEmacs
3862 (let ((find-file-compare-truenames t))
3863 (get-file-buffer file)))
3864 ((fboundp 'find-buffer-visiting) ; Emacs
3865 (find-buffer-visiting file))
3866 (t (error "This should not happen (idlwave-get-buffer-visiting)"))))
3867
15e42531 3868(defvar idlwave-outlawed-buffers nil
5a0c3f56 3869 "List of buffers pulled up by IDLWAVE for special reasons.
15e42531
CD
3870Buffers in this list may be killed by `idlwave-kill-autoloaded-buffers'.")
3871
3872(defun idlwave-find-file-noselect (file &optional why)
f32b3b91
CD
3873 ;; Return a buffer visiting file.
3874 (or (idlwave-get-buffer-visiting file)
15e42531
CD
3875 (let ((buf (find-file-noselect file)))
3876 (if why (add-to-list 'idlwave-outlawed-buffers (cons buf why)))
3877 buf)))
3878
3879(defun idlwave-kill-autoloaded-buffers ()
52a244eb 3880 "Kill buffers created automatically by IDLWAVE.
15e42531
CD
3881Function prompts for a letter to identify the buffers to kill.
3882Possible letters are:
3883
3884f Buffers created by the command \\[idlwave-find-module] or mouse
3885 clicks in the routine info window.
3886s Buffers created by the IDLWAVE Shell to display where execution
3887 stopped or an error was found.
3888a Both of the above.
3889
5a0c3f56 3890Buffers containing unsaved changes require confirmation before they are killed."
15e42531
CD
3891 (interactive)
3892 (if (null idlwave-outlawed-buffers)
3893 (error "No IDLWAVE-created buffers available")
3894 (princ (format "Kill IDLWAVE-created buffers: [f]ind source(%d), [s]hell display(%d), [a]ll ? "
3895 (idlwave-count-outlawed-buffers 'find)
3896 (idlwave-count-outlawed-buffers 'shell)))
3897 (let ((c (read-char)))
3898 (cond
3899 ((member c '(?f ?\C-f))
3900 (idlwave-do-kill-autoloaded-buffers 'find))
3901 ((member c '(?s ?\C-s))
3902 (idlwave-do-kill-autoloaded-buffers 'shell))
3903 ((member c '(?a ?\C-a))
3904 (idlwave-do-kill-autoloaded-buffers t))
3905 (t (error "Abort"))))))
3906
3907(defun idlwave-count-outlawed-buffers (tag)
3908 "How many outlawed buffers have tag TAG?"
3909 (length (delq nil
4b1aaa8b
PE
3910 (mapcar
3911 (lambda (x) (eq (cdr x) tag))
15e42531
CD
3912 idlwave-outlawed-buffers))))
3913
3914(defun idlwave-do-kill-autoloaded-buffers (&rest reasons)
3915 "Kill all buffers pulled up by IDLWAVE matching REASONS."
3916 (let* ((list (copy-sequence idlwave-outlawed-buffers))
3917 (cnt 0)
3918 entry)
3919 (while (setq entry (pop list))
3920 (if (buffer-live-p (car entry))
3921 (and (or (memq t reasons)
3922 (memq (cdr entry) reasons))
3923 (kill-buffer (car entry))
3924 (incf cnt)
4b1aaa8b 3925 (setq idlwave-outlawed-buffers
15e42531 3926 (delq entry idlwave-outlawed-buffers)))
4b1aaa8b 3927 (setq idlwave-outlawed-buffers
15e42531
CD
3928 (delq entry idlwave-outlawed-buffers))))
3929 (message "%d buffer%s killed" cnt (if (= cnt 1) "" "s"))))
3930
3931(defun idlwave-revoke-license-to-kill ()
3932 "Remove BUFFER from the buffers which may be killed.
3933Killing would be done by `idlwave-do-kill-autoloaded-buffers'.
3934Intended for `after-save-hook'."
3935 (let* ((buf (current-buffer))
3936 (entry (assq buf idlwave-outlawed-buffers)))
3937 ;; Revoke license
3938 (if entry
4b1aaa8b 3939 (setq idlwave-outlawed-buffers
15e42531
CD
3940 (delq entry idlwave-outlawed-buffers)))
3941 ;; Remove this function from the hook.
3942 (remove-hook 'after-save-hook 'idlwave-revoke-license-to-kill 'local)))
3943
3944(defvar idlwave-path-alist)
3945(defun idlwave-locate-lib-file (file)
f32b3b91 3946 ;; Find FILE on the scanned lib path and return a buffer visiting it
15e42531 3947 (let* ((dirs idlwave-path-alist)
f32b3b91
CD
3948 dir efile)
3949 (catch 'exit
15e42531 3950 (while (setq dir (car (pop dirs)))
f32b3b91
CD
3951 (if (file-regular-p
3952 (setq efile (expand-file-name file dir)))
15e42531 3953 (throw 'exit efile))))))
52a244eb 3954
15e42531
CD
3955(defun idlwave-expand-lib-file-name (file)
3956 ;; Find FILE on the scanned lib path and return a buffer visiting it
52a244eb 3957 ;; This is for, e.g., finding source with no user catalog
4b1aaa8b 3958 (cond
15e42531 3959 ((null file) nil)
15e42531
CD
3960 ((file-name-absolute-p file) file)
3961 (t (idlwave-locate-lib-file file))))
f32b3b91
CD
3962
3963(defun idlwave-make-tags ()
5a0c3f56
JB
3964 "Create the IDL tags file IDLTAGS in the current directory from
3965the list of directories specified in the minibuffer. Directories may be
3966for example: . /usr/local/rsi/idl/lib. All the subdirectories of the
f32b3b91 3967specified top directories are searched if the directory name is prefixed
5a0c3f56 3968by @. Specify @ directories with care, it may take a long, long time if
f32b3b91
CD
3969you specify /."
3970 (interactive)
3971 (let (directory directories cmd append status numdirs dir getsubdirs
3972 buffer save_buffer files numfiles item errbuf)
4b1aaa8b 3973
f32b3b91
CD
3974 ;;
3975 ;; Read list of directories
3976 (setq directory (read-string "Tag Directories: " "."))
3977 (setq directories (idlwave-split-string directory "[ \t]+"))
3978 ;;
3979 ;; Set etags command, vars
3980 (setq cmd "etags --output=IDLTAGS --language=none --regex='/[
3981\\t]*[pP][Rr][Oo][ \\t]+\\([^ \\t,]+\\)/' --regex='/[
3982\\t]*[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn][ \\t]+\\([^ \\t,]+\\)/' ")
3983 (setq append " ")
3984 (setq status 0)
3985 ;;
3986 ;; For each directory
3987 (setq numdirs 0)
3988 (setq dir (nth numdirs directories))
3989 (while (and dir)
3990 ;;
3991 ;; Find the subdirectories
3992 (if (string-match "^[@]\\(.+\\)$" dir)
3993 (setq getsubdirs t) (setq getsubdirs nil))
3994 (if (and getsubdirs) (setq dir (substring dir 1 (length dir))))
3995 (setq dir (expand-file-name dir))
3996 (if (file-directory-p dir)
3997 (progn
3998 (if (and getsubdirs)
3999 (progn
4000 (setq buffer (get-buffer-create "*idltags*"))
4001 (call-process "sh" nil buffer nil "-c"
4002 (concat "find " dir " -type d -print"))
4003 (setq save_buffer (current-buffer))
4004 (set-buffer buffer)
4005 (setq files (idlwave-split-string
4006 (idlwave-replace-string
4007 (buffer-substring 1 (point-max))
4008 "\n" "/*.pro ")
4009 "[ \t]+"))
4010 (set-buffer save_buffer)
4011 (kill-buffer buffer))
4012 (setq files (list (concat dir "/*.pro"))))
4013 ;;
4014 ;; For each subdirectory
4015 (setq numfiles 0)
4016 (setq item (nth numfiles files))
4017 (while (and item)
4018 ;;
4019 ;; Call etags
4020 (if (not (string-match "^[ \\t]*$" item))
4021 (progn
29a4e67d 4022 (message "%s" (concat "Tagging " item "..."))
f32b3b91 4023 (setq errbuf (get-buffer-create "*idltags-error*"))
52a244eb 4024 (setq status (+ status
4b1aaa8b 4025 (if (eq 0 (call-process
52a244eb
S
4026 "sh" nil errbuf nil "-c"
4027 (concat cmd append item)))
4028 0
4029 1)))
f32b3b91
CD
4030 ;;
4031 ;; Append additional tags
4032 (setq append " --append ")
4033 (setq numfiles (1+ numfiles))
4034 (setq item (nth numfiles files)))
4035 (progn
4036 (setq numfiles (1+ numfiles))
4037 (setq item (nth numfiles files))
4038 )))
4b1aaa8b 4039
f32b3b91
CD
4040 (setq numdirs (1+ numdirs))
4041 (setq dir (nth numdirs directories)))
4042 (progn
4043 (setq numdirs (1+ numdirs))
4044 (setq dir (nth numdirs directories)))))
4b1aaa8b 4045
f32b3b91
CD
4046 (setq errbuf (get-buffer-create "*idltags-error*"))
4047 (if (= status 0)
4048 (kill-buffer errbuf))
4049 (message "")
4050 ))
4051
4052(defun idlwave-toggle-comment-region (beg end &optional n)
4053 "Comment the lines in the region if the first non-blank line is
5a0c3f56 4054commented, and conversely, uncomment region. If optional prefix arg
f32b3b91
CD
4055N is non-nil, then for N positive, add N comment delimiters or for N
4056negative, remove N comment delimiters.
4057Uses `comment-region' which does not place comment delimiters on
4058blank lines."
4059 (interactive "r\nP")
4060 (if n
4061 (comment-region beg end (prefix-numeric-value n))
4062 (save-excursion
4063 (goto-char beg)
4064 (beginning-of-line)
4065 ;; skip blank lines
4066 (skip-chars-forward " \t\n")
4067 (if (looking-at (concat "[ \t]*\\(" comment-start "+\\)"))
52a244eb
S
4068 (if (fboundp 'uncomment-region)
4069 (uncomment-region beg end)
4070 (comment-region beg end
4071 (- (length (buffer-substring
4072 (match-beginning 1)
4073 (match-end 1))))))
4074 (comment-region beg end)))))
f32b3b91
CD
4075
4076
4077;; ----------------------------------------------------------------------------
4078;; ----------------------------------------------------------------------------
4079;; ----------------------------------------------------------------------------
4080;; ----------------------------------------------------------------------------
4081;;
4082;; Completion and Routine Info
4083;;
4084
4085;; String "intern" functions
4086
4087;; For the completion and routine info function, we want to normalize
4088;; the case of procedure names etc. We do this by "interning" these
4089;; string is a hand-crafted way. Hashes are used to map the downcase
52a244eb
S
4090;; version of the strings to the cased versions. Most *-sint-*
4091;; variables consist of *two* hashes, a buffer+shell, followed by a
4092;; system hash. The former is re-scanned, and the latter takes case
4093;; precedence.
4094;;
4095;; Since these cased versions are really lisp objects, we can use `eq'
4096;; to search, which is a large performance boost. All new strings
4097;; need to be "sinterned". We do this as early as possible after
4098;; getting these strings from completion or buffer substrings. So
4099;; most of the code can simply assume to deal with "sinterned"
4100;; strings. The only exception is that the functions which scan whole
4101;; buffers for routine information do not intern the grabbed strings.
4102;; This is only done afterwards. Therefore in these functions it is
4103;; *not* safe to assume the strings can be compared with `eq' and be
4104;; fed into the routine assq functions.
f32b3b91
CD
4105
4106;; Here we define the hashing functions.
4107
4108;; The variables which hold the hashes.
4109(defvar idlwave-sint-routines '(nil))
4110(defvar idlwave-sint-keywords '(nil))
4111(defvar idlwave-sint-methods '(nil))
4112(defvar idlwave-sint-classes '(nil))
52a244eb
S
4113(defvar idlwave-sint-dirs '(nil))
4114(defvar idlwave-sint-libnames '(nil))
f32b3b91
CD
4115
4116(defun idlwave-reset-sintern (&optional what)
4117 "Reset all sintern hashes."
4118 ;; Make sure the hash functions are accessible.
8d222148
SM
4119 (unless (and (fboundp 'gethash)
4120 (fboundp 'puthash))
4121 (require 'cl)
4122 (or (fboundp 'puthash)
4123 (defalias 'puthash 'cl-puthash)))
f32b3b91
CD
4124 (let ((entries '((idlwave-sint-routines 1000 10)
4125 (idlwave-sint-keywords 1000 10)
4126 (idlwave-sint-methods 100 10)
4127 (idlwave-sint-classes 10 10))))
4128
4129 ;; Make sure these are lists
4130 (loop for entry in entries
4131 for var = (car entry)
4132 do (if (not (consp (symbol-value var))) (set var (list nil))))
4133
f66f03de 4134 ;; Reset the system & library hash
f32b3b91
CD
4135 (when (or (eq what t) (eq what 'syslib)
4136 (null (cdr idlwave-sint-routines)))
f32b3b91
CD
4137 (loop for entry in entries
4138 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4139 do (setcdr (symbol-value var)
f32b3b91 4140 (make-hash-table ':size size ':test 'equal)))
52a244eb
S
4141 (setq idlwave-sint-dirs nil
4142 idlwave-sint-libnames nil))
f32b3b91 4143
f66f03de 4144 ;; Reset the buffer & shell hash
f32b3b91
CD
4145 (when (or (eq what t) (eq what 'bufsh)
4146 (null (car idlwave-sint-routines)))
f32b3b91
CD
4147 (loop for entry in entries
4148 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4149 do (setcar (symbol-value var)
f32b3b91
CD
4150 (make-hash-table ':size size ':test 'equal))))))
4151
4152(defun idlwave-sintern-routine-or-method (name &optional class set)
4153 (if class
4154 (idlwave-sintern-method name set)
4155 (idlwave-sintern-routine name set)))
4156
4157(defun idlwave-sintern (stype &rest args)
4158 (apply (intern (concat "idlwave-sintern-" (symbol-name stype))) args))
4159
4160;;(defmacro idlwave-sintern (type var)
4161;; `(cond ((not (stringp name)) name)
4162;; ((gethash (downcase name) (cdr ,var)))
4163;; ((gethash (downcase name) (car ,var)))
4164;; (set (idlwave-sintern-set name ,type ,var set))
4165;; (name)))
4166
4167(defun idlwave-sintern-routine (name &optional set)
4168 (cond ((not (stringp name)) name)
4169 ((gethash (downcase name) (cdr idlwave-sint-routines)))
4170 ((gethash (downcase name) (car idlwave-sint-routines)))
4171 (set (idlwave-sintern-set name 'routine idlwave-sint-routines set))
4172 (name)))
4173(defun idlwave-sintern-keyword (name &optional set)
4174 (cond ((not (stringp name)) name)
4175 ((gethash (downcase name) (cdr idlwave-sint-keywords)))
4176 ((gethash (downcase name) (car idlwave-sint-keywords)))
4177 (set (idlwave-sintern-set name 'keyword idlwave-sint-keywords set))
4178 (name)))
4179(defun idlwave-sintern-method (name &optional set)
4180 (cond ((not (stringp name)) name)
4181 ((gethash (downcase name) (cdr idlwave-sint-methods)))
4182 ((gethash (downcase name) (car idlwave-sint-methods)))
4183 (set (idlwave-sintern-set name 'method idlwave-sint-methods set))
4184 (name)))
4185(defun idlwave-sintern-class (name &optional set)
4186 (cond ((not (stringp name)) name)
4187 ((gethash (downcase name) (cdr idlwave-sint-classes)))
4188 ((gethash (downcase name) (car idlwave-sint-classes)))
4189 (set (idlwave-sintern-set name 'class idlwave-sint-classes set))
4190 (name)))
4191
52a244eb
S
4192(defun idlwave-sintern-dir (dir &optional set)
4193 (car (or (member dir idlwave-sint-dirs)
4194 (setq idlwave-sint-dirs (cons dir idlwave-sint-dirs)))))
4195(defun idlwave-sintern-libname (name &optional set)
4196 (car (or (member name idlwave-sint-libnames)
4197 (setq idlwave-sint-libnames (cons name idlwave-sint-libnames)))))
f32b3b91
CD
4198
4199(defun idlwave-sintern-set (name type tables set)
4200 (let* ((func (or (cdr (assq type idlwave-completion-case))
4201 'identity))
4202 (iname (funcall (if (eq func 'preserve) 'identity func) name))
4203 (table (if (eq set 'sys) (cdr tables) (car tables))))
4204 (puthash (downcase name) iname table)
4205 iname))
4206
52a244eb
S
4207(defun idlwave-sintern-keyword-list (kwd-list &optional set)
4208 "Sintern a set of keywords (file (key . link) (key2 . link2) ...)"
8ffcfb27
GM
4209 (mapc (lambda(x)
4210 (setcar x (idlwave-sintern-keyword (car x) set)))
4211 (cdr kwd-list))
52a244eb
S
4212 kwd-list)
4213
4214(defun idlwave-sintern-rinfo-list (list &optional set default-dir)
5a0c3f56
JB
4215 "Sintern all strings in the rinfo LIST.
4216With optional parameter SET: also set new patterns. Probably this
4217will always have to be t. If DEFAULT-DIR is passed, it is used as
4218the base of the directory."
52a244eb 4219 (let (entry name type class kwds res source call new)
f32b3b91
CD
4220 (while list
4221 (setq entry (car list)
4222 list (cdr list)
4223 name (car entry)
4224 type (nth 1 entry)
4225 class (nth 2 entry)
4226 source (nth 3 entry)
4227 call (nth 4 entry)
52a244eb
S
4228 kwds (nthcdr 5 entry))
4229
4230 ;; The class and name
f32b3b91
CD
4231 (if class
4232 (progn
4233 (if (symbolp class) (setq class (symbol-name class)))
4234 (setq class (idlwave-sintern-class class set))
4235 (setq name (idlwave-sintern-method name set)))
4236 (setq name (idlwave-sintern-routine name set)))
4b1aaa8b 4237
52a244eb
S
4238 ;; The source
4239 (let ((source-type (car source))
4240 (source-file (nth 1 source))
4b1aaa8b 4241 (source-dir (if default-dir
52a244eb
S
4242 (file-name-as-directory default-dir)
4243 (nth 2 source)))
4244 (source-lib (nth 3 source)))
4245 (if (stringp source-dir)
4246 (setq source-dir (idlwave-sintern-dir source-dir set)))
4247 (if (stringp source-lib)
4248 (setq source-lib (idlwave-sintern-libname source-lib set)))
4249 (setq source (list source-type source-file source-dir source-lib)))
4b1aaa8b 4250
52a244eb
S
4251 ;; The keywords
4252 (setq kwds (mapcar (lambda (x)
4253 (idlwave-sintern-keyword-list x set))
4254 kwds))
4255
4256 ;; Build a canonicalized list
4257 (setq new (nconc (list name type class source call) kwds)
4258 res (cons new res)))
f32b3b91
CD
4259 (nreverse res)))
4260
05a1abfc
CD
4261;; Creating new sintern tables
4262
4263(defun idlwave-new-sintern-type (tag)
4264 "Define a variable and a function to sintern the new type TAG.
4265This defines the function `idlwave-sintern-TAG' and the variable
4266`idlwave-sint-TAGs'."
4267 (let* ((name (symbol-name tag))
4268 (names (concat name "s"))
4269 (var (intern (concat "idlwave-sint-" names)))
4270 (func (intern (concat "idlwave-sintern-" name))))
4271 (set var nil) ; initial value of the association list
4272 (fset func ; set the function
4273 `(lambda (name &optional set)
4274 (cond ((not (stringp name)) name)
4275 ((cdr (assoc (downcase name) ,var)))
4276 (set
4277 (setq ,var (cons (cons (downcase name) name) ,var))
4278 name)
4279 (name))))))
4280
4281(defun idlwave-reset-sintern-type (tag)
4282 "Reset the sintern variable associated with TAG."
4283 (set (intern (concat "idlwave-sint-" (symbol-name tag) "s")) nil))
4284
f32b3b91
CD
4285;;---------------------------------------------------------------------------
4286
4287
4288;; The variables which hold the information
15e42531 4289(defvar idlwave-system-routines nil
f32b3b91
CD
4290 "Holds the routine-info obtained by scanning buffers.")
4291(defvar idlwave-buffer-routines nil
4292 "Holds the routine-info obtained by scanning buffers.")
4293(defvar idlwave-compiled-routines nil
15e42531
CD
4294 "Holds the routine-info obtained by asking the shell.")
4295(defvar idlwave-unresolved-routines nil
4296 "Holds the unresolved routine-info obtained by asking the shell.")
52a244eb
S
4297(defvar idlwave-user-catalog-routines nil
4298 "Holds the procedure routine-info from the user scan.")
4299(defvar idlwave-library-catalog-routines nil
3938cb82
S
4300 "Holds the procedure routine-info from the .idlwave_catalog library files.")
4301(defvar idlwave-library-catalog-libname nil
4302 "Name of library catalog loaded from .idlwave_catalog files.")
15e42531 4303(defvar idlwave-path-alist nil
52a244eb
S
4304 "Alist with !PATH directories and zero or more flags if the dir has
4305been scanned in a user catalog ('user) or discovered in a library
4306catalog \('lib).")
15e42531
CD
4307(defvar idlwave-true-path-alist nil
4308 "Like `idlwave-path-alist', but with true filenames.")
f32b3b91 4309(defvar idlwave-routines nil
b9e4fbd3 4310 "Holds the combined procedure/function/method routine-info.")
f32b3b91
CD
4311(defvar idlwave-class-alist nil
4312 "Holds the class names known to IDLWAVE.")
4313(defvar idlwave-class-history nil
4314 "The history of classes selected with the minibuffer.")
4315(defvar idlwave-force-class-query nil)
4316(defvar idlwave-before-completion-wconf nil
4317 "The window configuration just before the completion buffer was displayed.")
15e42531
CD
4318(defvar idlwave-last-system-routine-info-cons-cell nil
4319 "The last cons cell in the system routine info.")
f32b3b91
CD
4320
4321;;
4322;; The code to get routine info from different sources.
4323
15e42531 4324(defvar idlwave-system-routines)
5e72c6b2
S
4325(defvar idlwave-catalog-process nil
4326 "The background process currently updating the catalog.")
4327
f32b3b91
CD
4328(defun idlwave-routines ()
4329 "Provide a list of IDL routines.
5a0c3f56
JB
4330This routine loads the builtin routines on the first call.
4331Later it only returns the value of the variable."
5e72c6b2
S
4332 (if (and idlwave-catalog-process
4333 (processp idlwave-catalog-process))
4334 (progn
4335 (cond
4336 ((equal (process-status idlwave-catalog-process) 'exit)
4337 (message "updating........")
4338 (setq idlwave-catalog-process nil)
4339 (idlwave-update-routine-info '(4)))
4340 ((equal (process-status idlwave-catalog-process) 'run)
4341 ;; Keep it running...
4342 )
4343 (t
4344 ;; Something is wrong, get rid of the process
4345 (message "Problem with catalog process") (beep)
4346 (condition-case nil
4347 (kill-process idlwave-catalog-process)
4348 (error nil))
4349 (setq idlwave-catalog-process nil)))))
f32b3b91
CD
4350 (or idlwave-routines
4351 (progn
4352 (idlwave-update-routine-info)
4353 ;; return the current value
4354 idlwave-routines)))
4355
05a1abfc
CD
4356(defvar idlwave-update-rinfo-hook nil
4357 "List of functions which should run after a global rinfo update.
4358Does not run after automatic updates of buffer or the shell.")
4359
5e72c6b2 4360(defun idlwave-rescan-catalog-directories ()
5a0c3f56 4361 "Rescan the previously selected directories. For batch processing."
5e72c6b2
S
4362 (idlwave-update-routine-info '(16)))
4363
4364(defun idlwave-rescan-asynchronously ()
8a6a28ac 4365 "Dispatch another Emacs instance to update the idlwave catalog.
5e72c6b2
S
4366After the process finishes normally, the first access to routine info
4367will re-read the catalog."
4368 (interactive)
4369 (if (processp idlwave-catalog-process)
4370 (if (eq (process-status idlwave-catalog-process) 'run)
4371 (if (yes-or-no-p "A catalog-updating process is running. Kill it? ")
4372 (progn
4373 (condition-case nil
4374 (kill-process idlwave-catalog-process)
4375 (error nil))
4376 (error "Process killed, no new process started"))
4377 (error "Quit"))
4378 (condition-case nil
4379 (kill-process idlwave-catalog-process)
4380 (error nil))))
52a244eb
S
4381 (if (or (not idlwave-user-catalog-file)
4382 (not (stringp idlwave-user-catalog-file))
4383 (not (file-regular-p idlwave-user-catalog-file)))
5e72c6b2 4384 (error "No catalog has been produced yet"))
4b1aaa8b 4385 (let* ((emacs (concat invocation-directory invocation-name))
5e72c6b2
S
4386 (args (list "-batch"
4387 "-l" (expand-file-name "~/.emacs")
4388 "-l" "idlwave"
4389 "-f" "idlwave-rescan-catalog-directories"))
4b1aaa8b 4390 (process (apply 'start-process "idlcat"
5e72c6b2
S
4391 nil emacs args)))
4392 (setq idlwave-catalog-process process)
4b1aaa8b 4393 (set-process-sentinel
5e72c6b2
S
4394 process
4395 (lambda (pro why)
4396 (when (string-match "finished" why)
4397 (setq idlwave-routines nil
4398 idlwave-system-routines nil
4399 idlwave-catalog-process nil)
4400 (or (idlwave-start-load-rinfo-timer)
4401 (idlwave-update-routine-info '(4))))))
4402 (message "Background job started to update catalog file")))
4403
4404
52a244eb
S
4405;; Format for all routine info user catalog, library catalogs, etc.:
4406;;
4407;; ("ROUTINE" type class
4408;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4409;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4410;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de 4411;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
52a244eb
S
4412;;
4413;; DIR will be supplied dynamically while loading library catalogs,
4414;; and is sinterned to save space, as is LIBNAME. PRO_FILE can be a
4415;; complete filepath, in which case DIR is unnecessary. HELPFILE can
4416;; be nil, as can LINK1, etc., if no HTML help is available.
4417
4418
5e72c6b2 4419(defvar idlwave-load-rinfo-idle-timer)
3938cb82
S
4420(defvar idlwave-shell-path-query)
4421
52a244eb 4422(defun idlwave-update-routine-info (&optional arg no-concatenate)
f32b3b91
CD
4423 "Update the internal routine-info lists.
4424These lists are used by `idlwave-routine-info' (\\[idlwave-routine-info])
4425and by `idlwave-complete' (\\[idlwave-complete]) to provide information
4426about individual routines.
4427
4428The information can come from 4 sources:
44291. IDL programs in the current editing session
44302. Compiled modules in an IDL shell running as Emacs subprocess
44313. A list which covers the IDL system routines.
44324. A list which covers the prescanned library files.
4433
4434Scans all IDLWAVE-mode buffers of the current editing session (see
4435`idlwave-scan-all-buffers-for-routine-info').
4436When an IDL shell is running, this command also queries the IDL program
4437for currently compiled routines.
4438
4439With prefix ARG, also reload the system and library lists.
52a244eb
S
4440With two prefix ARG's, also rescans the chosen user catalog tree.
4441With three prefix args, dispatch asynchronous process to do the update.
4442
4443If NO-CONCATENATE is non-nil, don't pre-concatenate the routine info
4444lists, but instead wait for the shell query to complete and
4445asynchronously finish updating routine info. This is set
4446automatically when called interactively. When you need routine
4447information updated immediately, leave NO-CONCATENATE nil."
751adbde 4448 (interactive "P\np")
5e72c6b2
S
4449 ;; Stop any idle processing
4450 (if (or (and (fboundp 'itimerp)
4451 (itimerp idlwave-load-rinfo-idle-timer))
4452 (and (fboundp 'timerp)
4453 (timerp idlwave-load-rinfo-idle-timer)))
4454 (cancel-timer idlwave-load-rinfo-idle-timer))
4455 (cond
4456 ((equal arg '(64))
4457 ;; Start a background process which updates the catalog.
4458 (idlwave-rescan-asynchronously))
4459 ((equal arg '(16))
52a244eb
S
4460 ;; Update the user catalog now, and wait for them.
4461 (idlwave-create-user-catalog-file t))
5e72c6b2
S
4462 (t
4463 (let* ((load (or arg
4464 idlwave-buffer-case-takes-precedence
4465 (null idlwave-routines)))
4466 ;; The override-idle means, even if the idle timer has done some
4467 ;; preparing work, load and renormalize everything anyway.
4468 (override-idle (or arg idlwave-buffer-case-takes-precedence)))
4b1aaa8b 4469
f32b3b91 4470 (setq idlwave-buffer-routines nil
15e42531
CD
4471 idlwave-compiled-routines nil
4472 idlwave-unresolved-routines nil)
f32b3b91 4473 ;; Reset the appropriate hashes
5e72c6b2
S
4474 (if (get 'idlwave-reset-sintern 'done-by-idle)
4475 ;; reset was already done in idle time, so skip this step now once
4476 (put 'idlwave-reset-sintern 'done-by-idle nil)
4477 (idlwave-reset-sintern (cond (load t)
4478 ((null idlwave-system-routines) t)
4479 (t 'bufsh))))
4b1aaa8b 4480
f32b3b91
CD
4481 (if idlwave-buffer-case-takes-precedence
4482 ;; We can safely scan the buffer stuff first
4483 (progn
4484 (idlwave-update-buffer-routine-info)
f66f03de 4485 (and load (idlwave-load-all-rinfo override-idle)))
f32b3b91 4486 ;; We first do the system info, and then the buffers
f66f03de 4487 (and load (idlwave-load-all-rinfo override-idle))
f32b3b91
CD
4488 (idlwave-update-buffer-routine-info))
4489
4490 ;; Let's see if there is a shell
4491 (let* ((shell-is-running (and (fboundp 'idlwave-shell-is-running)
4492 (idlwave-shell-is-running)))
4493 (ask-shell (and shell-is-running
4494 idlwave-query-shell-for-routine-info)))
4b1aaa8b 4495
52a244eb 4496 ;; Load the library catalogs again, first re-scanning the path
4b1aaa8b 4497 (when arg
52a244eb
S
4498 (if shell-is-running
4499 (idlwave-shell-send-command idlwave-shell-path-query
4500 '(progn
4501 (idlwave-shell-get-path-info)
4502 (idlwave-scan-library-catalogs))
4503 'hide)
4504 (idlwave-scan-library-catalogs)))
775591f7 4505
f32b3b91 4506 (if (or (not ask-shell)
52a244eb 4507 (not no-concatenate))
f32b3b91
CD
4508 ;; 1. If we are not going to ask the shell, we need to do the
4509 ;; concatenation now.
52a244eb
S
4510 ;; 2. When this function is called non-interactively, it
4511 ;; means that someone needs routine info *now*. The
4512 ;; shell update causes the concatenation to be
4513 ;; *delayed*, so not in time for the current command.
4514 ;; Therefore, we do a concatenation now, even though
4515 ;; the shell might do it again.
4516 (idlwave-concatenate-rinfo-lists nil 'run-hooks))
4b1aaa8b 4517
f32b3b91 4518 (when ask-shell
52a244eb 4519 ;; Ask the shell about the routines it knows of.
f32b3b91 4520 (message "Querying the shell")
5e72c6b2
S
4521 (idlwave-shell-update-routine-info nil t)))))))
4522
52a244eb
S
4523
4524(defvar idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4525(defvar idlwave-load-rinfo-idle-timer nil)
4526(defun idlwave-start-load-rinfo-timer ()
4527 (if (or (and (fboundp 'itimerp)
4528 (itimerp idlwave-load-rinfo-idle-timer))
4529 (and (fboundp 'timerp)
4530 (timerp idlwave-load-rinfo-idle-timer)))
4531 (cancel-timer idlwave-load-rinfo-idle-timer))
52a244eb 4532 (setq idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4533 (setq idlwave-load-rinfo-idle-timer nil)
4534 (if (and idlwave-init-rinfo-when-idle-after
4535 (numberp idlwave-init-rinfo-when-idle-after)
4536 (not (equal 0 idlwave-init-rinfo-when-idle-after))
4537 (not idlwave-routines))
4538 (condition-case nil
4539 (progn
4540 (setq idlwave-load-rinfo-idle-timer
4541 (run-with-idle-timer
4542 idlwave-init-rinfo-when-idle-after
4543 nil 'idlwave-load-rinfo-next-step)))
4544 (error nil))))
4545
3938cb82
S
4546(defvar idlwave-library-routines nil "Obsolete variable.")
4547
f66f03de
S
4548;;------ XML Help routine info system
4549(defun idlwave-load-system-routine-info ()
4550 ;; Load the system routine info from the cached routine info file,
4551 ;; which, if necessary, will be re-created from the XML file on
4552 ;; disk. As a last fallback, load the (likely outdated) idlw-rinfo
4553 ;; file distributed with older IDLWAVE versions (<6.0)
4b1aaa8b 4554 (unless (and (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4555 'noerror 'nomessage)
4556 (idlwave-xml-system-routine-info-up-to-date))
4557 ;; See if we can create it from XML source
4558 (condition-case nil
4559 (idlwave-convert-xml-system-routine-info)
4b1aaa8b
PE
4560 (error
4561 (unless (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4562 'noerror 'nomessage)
4563 (if idlwave-system-routines
4b1aaa8b 4564 (message
f66f03de 4565 "Failed to load converted routine info, using old conversion.")
4b1aaa8b 4566 (message
f66f03de
S
4567 "Failed to convert XML routine info, falling back on idlw-rinfo.")
4568 (if (not (load "idlw-rinfo" 'noerror 'nomessage))
4b1aaa8b 4569 (message
f66f03de
S
4570 "Could not locate any system routine information."))))))))
4571
4572(defun idlwave-xml-system-routine-info-up-to-date()
4b1aaa8b 4573 (let* ((dir (file-name-as-directory
f66f03de
S
4574 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4575 (catalog-file (expand-file-name "idl_catalog.xml" dir)))
4576 (file-newer-than-file-p ;converted file is newer than catalog
4577 idlwave-xml-system-rinfo-converted-file
4578 catalog-file)))
4579
4580(defvar idlwave-system-class-info nil) ; Gathered from idlw-rinfo
4581(defvar idlwave-system-variables-alist nil
4582 "Alist of system variables and the associated structure tags.
4583Gets set in cached XML rinfo, or `idlw-rinfo.el'.")
4584(defvar idlwave-executive-commands-alist nil
4585 "Alist of system variables and their help files.")
4586(defvar idlwave-help-special-topic-words nil)
4587
4b1aaa8b 4588
f66f03de 4589(defun idlwave-shorten-syntax (syntax name &optional class)
5a89f0a7 4590 ;; From a list of syntax statements, shorten with %s and group with "or"
f66f03de 4591 (let ((case-fold-search t))
4b1aaa8b 4592 (mapconcat
f66f03de
S
4593 (lambda (x)
4594 (while (string-match name x)
4595 (setq x (replace-match "%s" t t x)))
4b1aaa8b 4596 (if class
f66f03de
S
4597 (while (string-match class x)
4598 (setq x (replace-match "%s" t t x))))
4599 x)
4600 (nreverse syntax)
4601 " or ")))
4602
4603(defun idlwave-xml-create-class-method-lists (xml-entry)
4604 ;; Create a class list entry from the xml parsed list., returning a
4605 ;; cons of form (class-entry method-entries).
4606 (let* ((nameblock (nth 1 xml-entry))
4607 (class (cdr (assq 'name nameblock)))
4608 (link (cdr (assq 'link nameblock)))
4609 (params (cddr xml-entry))
4610 (case-fold-search t)
4611 class-entry
4612 method methods-entry extra-kwds
4613 props get-props set-props init-props inherits
4614 pelem ptype)
4615 (while params
4616 (setq pelem (car params))
4617 (when (listp pelem)
4618 (setq ptype (car pelem)
4619 props (car (cdr pelem)))
4620 (cond
4621 ((eq ptype 'SUPERCLASS)
58c8f915
S
4622 (let ((pname (cdr (assq 'name props)))
4623 (plink (cdr (assq 'link props))))
4624 (unless (and (string= pname "None")
4625 (string= plink "None"))
4626 (push pname inherits))))
f66f03de
S
4627
4628 ((eq ptype 'PROPERTY)
4629 (let ((pname (cdr (assq 'name props)))
4630 (plink (cdr (assq 'link props)))
4631 (get (string= (cdr (assq 'get props)) "Yes"))
4632 (set (string= (cdr (assq 'set props)) "Yes"))
4633 (init (string= (cdr (assq 'init props)) "Yes")))
4634 (if get (push (list pname plink) get-props))
4635 (if set (push (list pname plink) set-props))
4636 (if init (push (list pname plink) init-props))))
4637
4638 ((eq ptype 'METHOD)
4639 (setq method (cdr (assq 'name props)))
4640 (setq extra-kwds ;;Assume all property keywords are gathered already
4641 (cond
4642 ((string-match (concat class "::Init") method)
4643 (put 'init-props 'matched t)
4644 init-props)
4645 ((string-match (concat class "::GetProperty") method)
4646 (put 'get-props 'matched t)
4647 get-props)
4648 ((string-match (concat class "::SetProperty") method)
4649 (put 'set-props 'matched t)
4650 set-props)
4651 (t nil)))
4b1aaa8b
PE
4652 (setq methods-entry
4653 (nconc (idlwave-xml-create-rinfo-list pelem class extra-kwds)
f66f03de
S
4654 methods-entry)))
4655 (t)))
4656 (setq params (cdr params)))
8d222148
SM
4657 ;;(unless (get 'init-props 'matched)
4658 ;; (message "Failed to match Init in class %s" class))
4659 ;;(unless (get 'get-props 'matched)
4660 ;; (message "Failed to match GetProperty in class %s" class))
4661 ;;(unless (get 'set-props 'matched)
4662 ;; (message "Failed to match SetProperty in class %s" class))
4b1aaa8b
PE
4663 (setq class-entry
4664 (if inherits
f66f03de
S
4665 (list class (append '(inherits) inherits) (list 'link link))
4666 (list class (list 'link link))))
4667 (cons class-entry methods-entry)))
4b1aaa8b 4668
f66f03de
S
4669(defun idlwave-xml-create-rinfo-list (xml-entry &optional class extra-kws)
4670 ;; Create correctly structured list elements from ROUTINE or METHOD
4671 ;; XML list structures. Return a list of list elements, with more
4672 ;; than one sub-list possible if a routine can serve as both
4673 ;; procedure and function (e.g. call_method).
4674 (let* ((nameblock (nth 1 xml-entry))
4675 (name (cdr (assq 'name nameblock)))
4676 (link (cdr (assq 'link nameblock)))
4677 (params (cddr xml-entry))
4678 (syntax-vec (make-vector 3 nil)) ; procedure, function, exec command
4679 (case-fold-search t)
8d222148 4680 syntax kwd klink pref-list kwds pelem ptype props result type)
f66f03de
S
4681 (if class ;; strip out class name from class method name string
4682 (if (string-match (concat class "::") name)
4683 (setq name (substring name (match-end 0)))))
4684 (while params
4685 (setq pelem (car params))
4686 (when (listp pelem)
4687 (setq ptype (car pelem)
4688 props (car (cdr pelem)))
4689 (cond
4690 ((eq ptype 'SYNTAX)
4691 (setq syntax (cdr (assq 'name props)))
4692 (if (string-match "-&gt;" syntax)
4693 (setq syntax (replace-match "->" t nil syntax)))
4694 (setq type (cdr (assq 'type props)))
4695 (push syntax
4696 (aref syntax-vec (cond
4697 ((string-match "^pro" type) 0)
4698 ((string-match "^fun" type) 1)
4699 ((string-match "^exec" type) 2)))))
4700 ((eq ptype 'KEYWORD)
4701 (setq kwd (cdr (assq 'name props))
4702 klink (cdr (assq 'link props)))
4703 (if (string-match "^\\[XY\\(Z?\\)\\]" kwd)
4b1aaa8b
PE
4704 (progn
4705 (setq pref-list
f66f03de
S
4706 (if (match-string 1 kwd) '("X" "Y" "Z") '("X" "Y"))
4707 kwd (substring kwd (match-end 0)))
4708 (loop for x in pref-list do
4709 (push (list (concat x kwd) klink) kwds)))
4710 (push (list kwd klink) kwds)))
4711
4712 (t))); Do nothing for the others
4713 (setq params (cdr params)))
4b1aaa8b 4714
f66f03de 4715 ;; Debug
8d222148
SM
4716 ;; (if (and (null (aref syntax-vec 0))
4717 ;; (null (aref syntax-vec 1))
4718 ;; (null (aref syntax-vec 2)))
4719 ;; (with-current-buffer (get-buffer-create "IDL_XML_catalog_complaints")
4720 ;; (if class
4721 ;; (insert (format "Missing SYNTAX entry for %s::%s\n" class name))
4722 ;; (insert (message "Missing SYNTAX entry for %s\n" name)))))
f66f03de
S
4723
4724 ;; Executive commands are treated specially
4725 (if (aref syntax-vec 2)
4726 (cons (substring name 1) link)
4727 (if extra-kws (setq kwds (nconc kwds extra-kws)))
4728 (setq kwds (idlwave-rinfo-group-keywords kwds link))
4729 (loop for idx from 0 to 1 do
4730 (if (aref syntax-vec idx)
4b1aaa8b 4731 (push (append (list name (if (eq idx 0) 'pro 'fun)
f66f03de 4732 class '(system)
4b1aaa8b 4733 (idlwave-shorten-syntax
f66f03de
S
4734 (aref syntax-vec idx) name class))
4735 kwds) result)))
4736 result)))
4737
4738
4739(defun idlwave-rinfo-group-keywords (kwds master-link)
4b1aaa8b 4740 ;; Group keywords by link file, as a list with elements
f66f03de
S
4741 ;; (linkfile ( ("KWD1" . link1) ("KWD2" . link2))
4742 (let (kwd link anchor linkfiles block master-elt)
4743 (while kwds
4744 (setq kwd (car kwds)
4745 link (idlwave-split-link-target (nth 1 kwd))
4746 anchor (cdr link)
4747 link (car link)
4748 kwd (car kwd))
4749 (if (setq block (assoc link linkfiles))
4750 (push (cons kwd anchor) (cdr block))
4751 (push (list link (cons kwd anchor)) linkfiles))
4752 (setq kwds (cdr kwds)))
4753 ;; Ensure the master link is there
4754 (if (setq master-elt (assoc master-link linkfiles))
4755 (if (eq (car linkfiles) master-elt)
4756 linkfiles
4757 (cons master-elt (delq master-elt linkfiles)))
4758 (push (list master-link) linkfiles))))
4b1aaa8b 4759
f66f03de
S
4760(defun idlwave-convert-xml-clean-statement-aliases (aliases)
4761 ;; Clean up the syntax of routines which are actually aliases by
4762 ;; removing the "OR" from the statements
4763 (let (syntax entry)
4764 (loop for x in aliases do
4765 (setq entry (assoc x idlwave-system-routines))
4766 (when entry
4767 (while (string-match " +or +" (setq syntax (nth 4 entry)))
4768 (setf (nth 4 entry) (replace-match ", " t t syntax)))))))
4769
4770(defun idlwave-convert-xml-clean-routine-aliases (aliases)
4771 ;; Duplicate and trim original routine aliases from rinfo list
4b1aaa8b 4772 ;; This if for, e.g. OPENR/OPENW/OPENU
f66f03de
S
4773 (let (alias remove-list new parts all-parts)
4774 (loop for x in aliases do
4775 (when (setq parts (split-string (cdr x) "/"))
4776 (setq new (assoc (cdr x) all-parts))
4777 (unless new
4778 (setq new (cons (cdr x) parts))
4779 (push new all-parts))
4780 (setcdr new (delete (car x) (cdr new)))))
4b1aaa8b 4781
f66f03de
S
4782 ;; Add any missing aliases (separate by slashes)
4783 (loop for x in all-parts do
4784 (if (cdr x)
4785 (push (cons (nth 1 x) (car x)) aliases)))
4786
4787 (loop for x in aliases do
4788 (when (setq alias (assoc (cdr x) idlwave-system-routines))
4789 (unless (memq alias remove-list) (push alias remove-list))
4790 (setq alias (copy-sequence alias))
4791 (setcar alias (car x))
4792 (push alias idlwave-system-routines)))
4793 (loop for x in remove-list do
4794 (delq x idlwave-system-routines))))
4795
4796(defun idlwave-convert-xml-clean-sysvar-aliases (aliases)
4797 ;; Duplicate and trim original routine aliases from rinfo list
4798 ;; This if for, e.g. !X, !Y, !Z.
8d222148 4799 (let (alias remove-list)
f66f03de
S
4800 (loop for x in aliases do
4801 (when (setq alias (assoc (cdr x) idlwave-system-variables-alist))
4802 (unless (memq alias remove-list) (push alias remove-list))
4803 (setq alias (copy-sequence alias))
4804 (setcar alias (car x))
4805 (push alias idlwave-system-variables-alist)))
4806 (loop for x in remove-list do
4807 (delq x idlwave-system-variables-alist))))
4808
4809
4810(defun idlwave-xml-create-sysvar-alist (xml-entry)
4811 ;; Create a sysvar list entry from the xml parsed list.
4812 (let* ((nameblock (nth 1 xml-entry))
a86bd650 4813 (name (cdr (assq 'name nameblock)))
b9e4fbd3 4814 (sysvar (substring name (progn (string-match "^ *!" name)
a86bd650 4815 (match-end 0))))
f66f03de
S
4816 (link (cdr (assq 'link nameblock)))
4817 (params (cddr xml-entry))
4818 (case-fold-search t)
8d222148 4819 pelem ptype props tags)
f66f03de
S
4820 (while params
4821 (setq pelem (car params))
4822 (when (listp pelem)
4823 (setq ptype (car pelem)
4824 props (car (cdr pelem)))
4825 (cond
4826 ((eq ptype 'FIELD)
4b1aaa8b 4827 (push (cons (cdr (assq 'name props))
f66f03de
S
4828 (cdr
4829 (idlwave-split-link-target (cdr (assq 'link props)))))
4830 tags))))
4831 (setq params (cdr params)))
4832 (delq nil
4833 (list sysvar (if tags (cons 'tags tags)) (list 'link link)))))
4834
4835
4836(defvar idlwave-xml-routine-info-file nil)
4837
4838(defun idlwave-save-routine-info ()
4839 (if idlwave-xml-routine-info-file
4840 (with-temp-file idlwave-xml-system-rinfo-converted-file
4b1aaa8b 4841 (insert
f66f03de 4842 (concat ";; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
4b1aaa8b
PE
4843;; IDLWAVE Routine Information File (IDLWAVE version " idlwave-mode-version ")
4844;; Automatically generated from source file:
f66f03de
S
4845;; " idlwave-xml-routine-info-file "
4846;; on " (current-time-string) "
4847;; Do not edit."))
4848 (insert (format "\n(setq idlwave-xml-routine-info-file \n \"%s\")"
4849 idlwave-xml-routine-info-file))
4850 (insert "\n(setq idlwave-system-routines\n '")
4851 (prin1 idlwave-system-routines (current-buffer))
4852 (insert ")")
4853 (insert "\n(setq idlwave-system-variables-alist\n '")
4854 (prin1 idlwave-system-variables-alist (current-buffer))
4855 (insert ")")
4856 (insert "\n(setq idlwave-system-class-info\n '")
4857 (prin1 idlwave-system-class-info (current-buffer))
4858 (insert ")")
4859 (insert "\n(setq idlwave-executive-commands-alist\n '")
4860 (prin1 idlwave-executive-commands-alist (current-buffer))
4861 (insert ")")
4862 (insert "\n(setq idlwave-help-special-topic-words\n '")
4863 (prin1 idlwave-help-special-topic-words (current-buffer))
4864 (insert ")"))))
4865
4866(defun idlwave-convert-xml-system-routine-info ()
4867 "Convert XML supplied IDL routine info into internal form.
4868Cache to disk for quick recovery."
4869 (interactive)
4b1aaa8b 4870 (let* ((dir (file-name-as-directory
f66f03de
S
4871 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4872 (catalog-file (expand-file-name "idl_catalog.xml" dir))
4873 (elem-cnt 0)
4b1aaa8b 4874 props rinfo msg-cnt elem type nelem class-result alias
8d222148 4875 routines routine-aliases statement-aliases sysvar-aliases)
f66f03de
S
4876 (if (not (file-exists-p catalog-file))
4877 (error "No such XML routine info file: %s" catalog-file)
4878 (if (not (file-readable-p catalog-file))
4879 (error "Cannot read XML routine info file: %s" catalog-file)))
4b1aaa8b 4880 (message "Reading XML routine info...")
e08734e2 4881 (setq rinfo (xml-parse-file catalog-file))
f66f03de
S
4882 (message "Reading XML routine info...done")
4883 (setq rinfo (assq 'CATALOG rinfo))
4884 (unless rinfo (error "Failed to parse XML routine info"))
4885 ;;(setq rinfo (car rinfo)) ; Skip the catalog stuff.
4b1aaa8b 4886
8d222148 4887 (setq rinfo (cddr rinfo))
f66f03de
S
4888
4889 (setq nelem (length rinfo)
4890 msg-cnt (/ nelem 20))
4b1aaa8b 4891
f66f03de
S
4892 (setq idlwave-xml-routine-info-file nil)
4893 (message "Converting XML routine info...")
4894 (setq idlwave-system-routines nil
4895 idlwave-system-variables-alist nil
4896 idlwave-system-class-info nil
4897 idlwave-executive-commands-alist nil
4898 idlwave-help-special-topic-words nil)
4899
4900 (while rinfo
4901 (setq elem (car rinfo)
4902 rinfo (cdr rinfo))
4903 (incf elem-cnt)
4904 (when (listp elem)
4905 (setq type (car elem)
4906 props (car (cdr elem)))
4907 (if (= (mod elem-cnt msg-cnt) 0)
4b1aaa8b 4908 (message "Converting XML routine info...%2d%%"
f66f03de 4909 (/ (* elem-cnt 100) nelem)))
4b1aaa8b 4910 (cond
f66f03de
S
4911 ((eq type 'ROUTINE)
4912 (if (setq alias (assq 'alias_to props))
4b1aaa8b 4913 (push (cons (cdr (assq 'name props)) (cdr alias))
f66f03de
S
4914 routine-aliases)
4915 (setq routines (idlwave-xml-create-rinfo-list elem))
4916 (if (listp (cdr routines))
4917 (setq idlwave-system-routines
4918 (nconc idlwave-system-routines routines))
4919 ;; a cons cell is an executive commands
4920 (push routines idlwave-executive-commands-alist))))
4b1aaa8b 4921
f66f03de
S
4922 ((eq type 'CLASS)
4923 (setq class-result (idlwave-xml-create-class-method-lists elem))
4924 (push (car class-result) idlwave-system-class-info)
4925 (setq idlwave-system-routines
4926 (nconc idlwave-system-routines (cdr class-result))))
4927
4928 ((eq type 'STATEMENT)
4929 (push (cons (cdr (assq 'name props))
4930 (cdr (assq 'link props)))
4931 idlwave-help-special-topic-words)
4932 ;; Save the links to those which are statement aliases (not routines)
4933 (if (setq alias (assq 'alias_to props))
4934 (unless (member (cdr alias) statement-aliases)
4935 (push (cdr alias) statement-aliases))))
4936
4937 ((eq type 'SYSVAR)
4938 (if (setq alias (cdr (assq 'alias_to props)))
4b1aaa8b 4939 (push (cons (substring (cdr (assq 'name props)) 1)
f66f03de
S
4940 (substring alias 1))
4941 sysvar-aliases)
4b1aaa8b 4942 (push (idlwave-xml-create-sysvar-alist elem)
f66f03de
S
4943 idlwave-system-variables-alist)))
4944 (t))))
4945 (idlwave-convert-xml-clean-routine-aliases routine-aliases)
4946 (idlwave-convert-xml-clean-statement-aliases statement-aliases)
4947 (idlwave-convert-xml-clean-sysvar-aliases sysvar-aliases)
4948
4949 (setq idlwave-xml-routine-info-file catalog-file)
4950 (idlwave-save-routine-info)
4951 (message "Converting XML routine info...done")))
4b1aaa8b
PE
4952
4953
f66f03de
S
4954;; ("ROUTINE" type class
4955;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4956;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4957;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de
S
4958;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
4959
4960
5e72c6b2
S
4961(defun idlwave-load-rinfo-next-step ()
4962 (let ((inhibit-quit t)
4963 (arr idlwave-load-rinfo-steps-done))
f66f03de 4964 (if (catch 'exit
5e72c6b2 4965 (when (not (aref arr 0))
f66f03de
S
4966 (message "Loading system routine info in idle time...")
4967 (idlwave-load-system-routine-info)
4968 ;;(load "idlw-rinfo" 'noerror 'nomessage)
4969 (message "Loading system routine info in idle time...done")
5e72c6b2
S
4970 (aset arr 0 t)
4971 (throw 'exit t))
4b1aaa8b 4972
5e72c6b2
S
4973 (when (not (aref arr 1))
4974 (message "Normalizing idlwave-system-routines in idle time...")
4975 (idlwave-reset-sintern t)
4976 (put 'idlwave-reset-sintern 'done-by-idle t)
4977 (setq idlwave-system-routines
4978 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
4979 (message "Normalizing idlwave-system-routines in idle time...done")
4980 (aset arr 1 t)
4981 (throw 'exit t))
f66f03de 4982
5e72c6b2 4983 (when (not (aref arr 2))
52a244eb
S
4984 (when (and (stringp idlwave-user-catalog-file)
4985 (file-regular-p idlwave-user-catalog-file))
4986 (message "Loading user catalog in idle time...")
5e72c6b2 4987 (condition-case nil
52a244eb
S
4988 (load-file idlwave-user-catalog-file)
4989 (error (throw 'exit nil)))
4990 ;; Check for the old style catalog and warn
4991 (if (and
4992 (boundp 'idlwave-library-routines)
4993 idlwave-library-routines)
775591f7 4994 (progn
52a244eb
S
4995 (setq idlwave-library-routines nil)
4996 (ding)
4b1aaa8b 4997 (message "Outdated user catalog: %s... recreate"
52a244eb 4998 idlwave-user-catalog-file))
f66f03de
S
4999 (message "Loading user catalog in idle time...done")))
5000 (aset arr 2 t)
5001 (throw 'exit t))
5002
5e72c6b2 5003 (when (not (aref arr 3))
52a244eb
S
5004 (when idlwave-user-catalog-routines
5005 (message "Normalizing user catalog routines in idle time...")
4b1aaa8b 5006 (setq idlwave-user-catalog-routines
52a244eb
S
5007 (idlwave-sintern-rinfo-list
5008 idlwave-user-catalog-routines 'sys))
4b1aaa8b 5009 (message
52a244eb 5010 "Normalizing user catalog routines in idle time...done"))
5e72c6b2
S
5011 (aset arr 3 t)
5012 (throw 'exit t))
f66f03de 5013
5e72c6b2 5014 (when (not (aref arr 4))
4b1aaa8b 5015 (idlwave-scan-library-catalogs
52a244eb
S
5016 "Loading and normalizing library catalogs in idle time...")
5017 (aset arr 4 t)
5018 (throw 'exit t))
5019 (when (not (aref arr 5))
5e72c6b2
S
5020 (message "Finishing initialization in idle time...")
5021 (idlwave-routines)
5022 (message "Finishing initialization in idle time...done")
4b1aaa8b 5023 (aset arr 5 t)
5e72c6b2 5024 (throw 'exit nil)))
52a244eb
S
5025 ;; restart the timer
5026 (if (sit-for 1)
5027 (idlwave-load-rinfo-next-step)
5028 (setq idlwave-load-rinfo-idle-timer
5029 (run-with-idle-timer
5030 idlwave-init-rinfo-when-idle-after
5031 nil 'idlwave-load-rinfo-next-step))))))
5e72c6b2 5032
8d222148
SM
5033(defvar idlwave-after-load-rinfo-hook nil)
5034
f66f03de
S
5035(defun idlwave-load-all-rinfo (&optional force)
5036 ;; Load and case-treat the system, user catalog, and library routine
5037 ;; info files.
5038
5039 ;; System
5e72c6b2 5040 (when (or force (not (aref idlwave-load-rinfo-steps-done 0)))
f66f03de
S
5041 ;;(load "idlw-rinfo" 'noerror 'nomessage))
5042 (idlwave-load-system-routine-info))
5e72c6b2
S
5043 (when (or force (not (aref idlwave-load-rinfo-steps-done 1)))
5044 (message "Normalizing idlwave-system-routines...")
5045 (setq idlwave-system-routines
5046 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
5047 (message "Normalizing idlwave-system-routines...done"))
f66f03de
S
5048 (when idlwave-system-routines
5049 (setq idlwave-routines (copy-sequence idlwave-system-routines))
5050 (setq idlwave-last-system-routine-info-cons-cell
5051 (nthcdr (1- (length idlwave-routines)) idlwave-routines)))
5052
5053 ;; User catalog
52a244eb
S
5054 (when (and (stringp idlwave-user-catalog-file)
5055 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5056 (condition-case nil
52a244eb
S
5057 (when (or force (not (aref idlwave-load-rinfo-steps-done 2)))
5058 (load-file idlwave-user-catalog-file))
5059 (error nil))
4b1aaa8b 5060 (when (and
f66f03de
S
5061 (boundp 'idlwave-library-routines)
5062 idlwave-library-routines)
52a244eb 5063 (setq idlwave-library-routines nil)
4b1aaa8b 5064 (error "Outdated user catalog: %s... recreate"
f66f03de 5065 idlwave-user-catalog-file))
52a244eb
S
5066 (setq idlwave-true-path-alist nil)
5067 (when (or force (not (aref idlwave-load-rinfo-steps-done 3)))
5068 (message "Normalizing user catalog routines...")
4b1aaa8b
PE
5069 (setq idlwave-user-catalog-routines
5070 (idlwave-sintern-rinfo-list
52a244eb
S
5071 idlwave-user-catalog-routines 'sys))
5072 (message "Normalizing user catalog routines...done")))
f66f03de
S
5073
5074 ;; Library catalog
52a244eb
S
5075 (when (or force (not (aref idlwave-load-rinfo-steps-done 4)))
5076 (idlwave-scan-library-catalogs
5077 "Loading and normalizing library catalogs..."))
05a1abfc
CD
5078 (run-hooks 'idlwave-after-load-rinfo-hook))
5079
f32b3b91
CD
5080
5081(defun idlwave-update-buffer-routine-info ()
5082 (let (res)
4b1aaa8b 5083 (cond
15e42531
CD
5084 ((eq idlwave-scan-all-buffers-for-routine-info t)
5085 ;; Scan all buffers, current buffer last
5086 (message "Scanning all buffers...")
4b1aaa8b 5087 (setq res (idlwave-get-routine-info-from-buffers
15e42531
CD
5088 (reverse (buffer-list)))))
5089 ((null idlwave-scan-all-buffers-for-routine-info)
5090 ;; Don't scan any buffers
5091 (setq res nil))
5092 (t
f32b3b91
CD
5093 ;; Just scan this buffer
5094 (if (eq major-mode 'idlwave-mode)
5095 (progn
5096 (message "Scanning current buffer...")
5097 (setq res (idlwave-get-routine-info-from-buffers
15e42531 5098 (list (current-buffer))))))))
f32b3b91 5099 ;; Put the result into the correct variable
4b1aaa8b 5100 (setq idlwave-buffer-routines
52a244eb 5101 (idlwave-sintern-rinfo-list res 'set))))
f32b3b91 5102
05a1abfc 5103(defun idlwave-concatenate-rinfo-lists (&optional quiet run-hook)
f32b3b91 5104 "Put the different sources for routine information together."
4b1aaa8b 5105 ;; The sequence here is important because earlier definitions shadow
f32b3b91 5106 ;; later ones. We assume that if things in the buffers are newer
52a244eb 5107 ;; then in the shell of the system, they are meant to be different.
15e42531
CD
5108 (setcdr idlwave-last-system-routine-info-cons-cell
5109 (append idlwave-buffer-routines
5110 idlwave-compiled-routines
52a244eb
S
5111 idlwave-library-catalog-routines
5112 idlwave-user-catalog-routines))
f32b3b91 5113 (setq idlwave-class-alist nil)
15e42531 5114
f32b3b91 5115 ;; Give a message with information about the number of routines we have.
15e42531 5116 (unless quiet
4b1aaa8b 5117 (message
52a244eb 5118 "Routines Found: buffer(%d) compiled(%d) library(%d) user(%d) system(%d)"
15e42531
CD
5119 (length idlwave-buffer-routines)
5120 (length idlwave-compiled-routines)
52a244eb
S
5121 (length idlwave-library-catalog-routines)
5122 (length idlwave-user-catalog-routines)
05a1abfc
CD
5123 (length idlwave-system-routines)))
5124 (if run-hook
5125 (run-hooks 'idlwave-update-rinfo-hook)))
15e42531
CD
5126
5127(defun idlwave-class-alist ()
5128 "Return the class alist - make it if necessary."
5129 (or idlwave-class-alist
5130 (let (class)
5131 (loop for x in idlwave-routines do
5132 (when (and (setq class (nth 2 x))
5133 (not (assq class idlwave-class-alist)))
5134 (push (list class) idlwave-class-alist)))
4b1aaa8b 5135 idlwave-class-alist)))
15e42531
CD
5136
5137;; Three functions for the hooks
5138(defun idlwave-save-buffer-update ()
5139 (idlwave-update-current-buffer-info 'save-buffer))
5140(defun idlwave-kill-buffer-update ()
5141 (idlwave-update-current-buffer-info 'kill-buffer))
5142(defun idlwave-new-buffer-update ()
5143 (idlwave-update-current-buffer-info 'find-file))
5144
5145(defun idlwave-update-current-buffer-info (why)
5a0c3f56
JB
5146 "Update `idlwave-routines' for current buffer.
5147Can run from `after-save-hook'."
15e42531
CD
5148 (when (and (eq major-mode 'idlwave-mode)
5149 (or (eq t idlwave-auto-routine-info-updates)
5150 (memq why idlwave-auto-routine-info-updates))
5151 idlwave-scan-all-buffers-for-routine-info
5152 idlwave-routines)
5153 (condition-case nil
5154 (let (routines)
5155 (idlwave-replace-buffer-routine-info
5156 (buffer-file-name)
5157 (if (eq why 'kill-buffer)
5158 nil
5159 (setq routines
5160 (idlwave-sintern-rinfo-list
5161 (idlwave-get-routine-info-from-buffers
5162 (list (current-buffer))) 'set))))
5163 (idlwave-concatenate-rinfo-lists 'quiet)
5164 routines)
5165 (error nil))))
5166
5167(defun idlwave-replace-buffer-routine-info (file new)
5168 "Cut the part from FILE out of `idlwave-buffer-routines' and add NEW."
4b1aaa8b 5169 (let ((list idlwave-buffer-routines)
15e42531
CD
5170 found)
5171 (while list
5172 ;; The following test uses eq to make sure it works correctly
5173 ;; when two buffers visit the same file. Then the file names
5174 ;; will be equal, but not eq.
52a244eb 5175 (if (eq (idlwave-routine-source-file (nth 3 (car list))) file)
15e42531
CD
5176 (progn
5177 (setcar list nil)
5178 (setq found t))
5179 (if found
4b1aaa8b 5180 ;; End of that section reached. Jump.
15e42531
CD
5181 (setq list nil)))
5182 (setq list (cdr list)))
5183 (setq idlwave-buffer-routines
5184 (append new (delq nil idlwave-buffer-routines)))))
f32b3b91
CD
5185
5186;;----- Scanning buffers -------------------
5187
5188(defun idlwave-get-routine-info-from-buffers (buffers)
5189 "Call `idlwave-get-buffer-routine-info' on idlwave-mode buffers in BUFFERS."
5190 (let (buf routine-lists res)
5191 (save-excursion
5192 (while (setq buf (pop buffers))
5193 (set-buffer buf)
05a1abfc
CD
5194 (if (and (eq major-mode 'idlwave-mode)
5195 buffer-file-name)
f32b3b91
CD
5196 ;; yes, this buffer has the right mode.
5197 (progn (setq res (condition-case nil
5198 (idlwave-get-buffer-routine-info)
5199 (error nil)))
5200 (push res routine-lists)))))
5201 ;; Concatenate the individual lists and return the result
5202 (apply 'nconc routine-lists)))
5203
5204(defun idlwave-get-buffer-routine-info ()
5205 "Scan the current buffer for routine info. Return (PRO-LIST FUNC-LIST)."
5206 (let* ((case-fold-search t)
5207 routine-list string entry)
5208 (save-excursion
5209 (save-restriction
5210 (widen)
5211 (goto-char (point-min))
4b1aaa8b 5212 (while (re-search-forward
15e42531 5213 "^[ \t]*\\(pro\\|function\\)[ \t]" nil t)
76959b77 5214 (setq string (buffer-substring-no-properties
f32b3b91 5215 (match-beginning 0)
4b1aaa8b 5216 (progn
f32b3b91
CD
5217 (idlwave-end-of-statement)
5218 (point))))
5219 (setq entry (idlwave-parse-definition string))
5220 (push entry routine-list))))
5221 routine-list))
5222
15e42531 5223(defvar idlwave-scanning-lib-dir)
8d222148 5224(defvar idlwave-scanning-lib)
f32b3b91
CD
5225(defun idlwave-parse-definition (string)
5226 "Parse a module definition."
5227 (let ((case-fold-search t)
5228 start name args type keywords class)
5229 ;; Remove comments
5230 (while (string-match ";.*" string)
5231 (setq string (replace-match "" t t string)))
5232 ;; Remove the continuation line stuff
5233 (while (string-match "\\([^a-zA-Z0-9$_]\\)\\$[ \t]*\n" string)
5234 (setq string (replace-match "\\1 " t nil string)))
05a1abfc
CD
5235 (while (string-match "\n" string)
5236 (setq string (replace-match " " t nil string)))
f32b3b91
CD
5237 ;; Match the name and type.
5238 (when (string-match
5239 "\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)" string)
5240 (setq start (match-end 0))
5241 (setq type (downcase (match-string 1 string)))
5242 (if (match-beginning 3)
5243 (setq class (match-string 3 string)))
5244 (setq name (match-string 4 string)))
5245 ;; Match normal args and keyword args
5246 (while (string-match
15e42531 5247 ",\\s-*\\([a-zA-Z][a-zA-Z0-9$_]*\\|\\(_ref\\)?_extra\\)\\s-*\\(=\\)?"
f32b3b91
CD
5248 string start)
5249 (setq start (match-end 0))
15e42531 5250 (if (match-beginning 3)
f32b3b91
CD
5251 (push (match-string 1 string) keywords)
5252 (push (match-string 1 string) args)))
5253 ;; Normalize and sort.
5254 (setq args (nreverse args))
4b1aaa8b 5255 (setq keywords (sort keywords (lambda (a b)
f32b3b91
CD
5256 (string< (downcase a) (downcase b)))))
5257 ;; Make and return the entry
5258 ;; We don't know which argument are optional, so this information
5259 ;; will not be contained in the calling sequence.
5260 (list name
5261 (if (equal type "pro") 'pro 'fun)
5262 class
5263 (cond ((not (boundp 'idlwave-scanning-lib))
52a244eb 5264 (list 'buffer (buffer-file-name)))
4b1aaa8b 5265; ((string= (downcase
15e42531
CD
5266; (file-name-sans-extension
5267; (file-name-nondirectory (buffer-file-name))))
5268; (downcase name))
5269; (list 'lib))
5270; (t (cons 'lib (file-name-nondirectory (buffer-file-name))))
52a244eb
S
5271 (t (list 'user (file-name-nondirectory (buffer-file-name))
5272 idlwave-scanning-lib-dir "UserLib")))
4b1aaa8b 5273 (concat
f32b3b91
CD
5274 (if (string= type "function") "Result = " "")
5275 (if class "Obj ->[%s::]" "")
5276 "%s"
5277 (if args
5278 (concat
5279 (if (string= type "function") "(" ", ")
5280 (mapconcat 'identity args ", ")
5281 (if (string= type "function") ")" ""))))
5282 (if keywords
52a244eb 5283 (cons nil (mapcar 'list keywords)) ;No help file
f32b3b91
CD
5284 nil))))
5285
f32b3b91 5286
52a244eb 5287;;----- Scanning the user catalog -------------------
15e42531
CD
5288
5289(defun idlwave-sys-dir ()
5290 "Return the syslib directory, or a dummy that never matches."
3938cb82
S
5291 (cond
5292 ((and idlwave-system-directory
5293 (not (string= idlwave-system-directory "")))
5294 idlwave-system-directory)
5295 ((getenv "IDL_DIR"))
5296 (t "@@@@@@@@")))
5297
52a244eb 5298
52a244eb 5299(defun idlwave-create-user-catalog-file (&optional arg)
f32b3b91 5300 "Scan all files on selected dirs of IDL search path for routine information.
52a244eb
S
5301
5302A widget checklist will allow you to choose the directories. Write
5303the result as a file `idlwave-user-catalog-file'. When this file
5a0c3f56
JB
5304exists, it will be automatically loaded to give routine information
5305about library routines. With ARG, just rescan the same directories
5306as last time - so no widget will pop up."
f32b3b91
CD
5307 (interactive "P")
5308 ;; Make sure the file is loaded if it exists.
52a244eb
S
5309 (if (and (stringp idlwave-user-catalog-file)
5310 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5311 (condition-case nil
52a244eb 5312 (load-file idlwave-user-catalog-file)
f32b3b91
CD
5313 (error nil)))
5314 ;; Make sure the file name makes sense
52a244eb
S
5315 (unless (and (stringp idlwave-user-catalog-file)
5316 (> (length idlwave-user-catalog-file) 0)
f32b3b91 5317 (file-accessible-directory-p
52a244eb 5318 (file-name-directory idlwave-user-catalog-file))
4b1aaa8b 5319 (not (string= "" (file-name-nondirectory
52a244eb
S
5320 idlwave-user-catalog-file))))
5321 (error "`idlwave-user-catalog-file' does not point to a file in an accessible directory"))
4b1aaa8b 5322
f32b3b91 5323 (cond
f32b3b91 5324 ;; Rescan the known directories
52a244eb
S
5325 ((and arg idlwave-path-alist
5326 (consp (car idlwave-path-alist)))
5327 (idlwave-scan-user-lib-files idlwave-path-alist))
5328
5329 ;; Expand the directories from library-path and run the widget
f32b3b91 5330 (idlwave-library-path
52a244eb 5331 (idlwave-display-user-catalog-widget
4b1aaa8b 5332 (if idlwave-true-path-alist
52a244eb
S
5333 ;; Propagate any flags on the existing path-alist
5334 (mapcar (lambda (x)
5335 (let ((path-entry (assoc (file-truename x)
5336 idlwave-true-path-alist)))
5337 (if path-entry
4b1aaa8b 5338 (cons x (cdr path-entry))
52a244eb
S
5339 (list x))))
5340 (idlwave-expand-path idlwave-library-path))
5341 (mapcar 'list (idlwave-expand-path idlwave-library-path)))))
5342
5343 ;; Ask the shell for the path and then run the widget
f32b3b91 5344 (t
f32b3b91 5345 (message "Asking the shell for IDL path...")
15e42531
CD
5346 (require 'idlw-shell)
5347 (idlwave-shell-send-command idlwave-shell-path-query
52a244eb 5348 '(idlwave-user-catalog-command-hook nil)
15e42531 5349 'hide))))
f32b3b91 5350
52a244eb
S
5351
5352;; Parse shell path information and select among it.
5353(defun idlwave-user-catalog-command-hook (&optional arg)
5354 ;; Command hook used by `idlwave-create-user-catalog-file'.
f32b3b91
CD
5355 (if arg
5356 ;; Scan immediately
52a244eb
S
5357 (idlwave-scan-user-lib-files idlwave-path-alist)
5358 ;; Set the path and display the widget
5359 (idlwave-shell-get-path-info 'no-write) ; set to something path-alist
5360 (idlwave-scan-library-catalogs "Locating library catalogs..." 'no-load)
5361 (idlwave-display-user-catalog-widget idlwave-path-alist)))
5362
4b1aaa8b 5363(defconst idlwave-user-catalog-widget-help-string
52a244eb
S
5364 "This is the front-end to the creation of the IDLWAVE user catalog.
5365Please select the directories on IDL's search path from which you
5366would like to extract routine information, to be stored in the file:
f32b3b91
CD
5367
5368 %s
5369
52a244eb
S
5370If this is not the correct file, first set variable
5371`idlwave-user-catalog-file', and call this command again.
15e42531 5372
52a244eb
S
5373N.B. Many libraries include pre-scanned catalog files
5374\(\".idlwave_catalog\"). These are marked with \"[LIB]\", and need
5375not be scanned. You can scan your own libraries off-line using the
5376perl script `idlwave_catalog'.
15e42531 5377
f32b3b91
CD
5378After selecting the directories, choose [Scan & Save] to scan the library
5379directories and save the routine info.
5380\n")
5381
5382(defvar idlwave-widget)
5383(defvar widget-keymap)
52a244eb 5384(defun idlwave-display-user-catalog-widget (dirs-list)
f32b3b91
CD
5385 "Create the widget to select IDL search path directories for scanning."
5386 (interactive)
5387 (require 'widget)
5388 (require 'wid-edit)
52a244eb 5389 (unless dirs-list
f32b3b91
CD
5390 (error "Don't know IDL's search path"))
5391
f32b3b91
CD
5392 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
5393 (switch-to-buffer (get-buffer-create "*IDLWAVE Widget*"))
5394 (kill-all-local-variables)
5395 (make-local-variable 'idlwave-widget)
52a244eb
S
5396 (widget-insert (format idlwave-user-catalog-widget-help-string
5397 idlwave-user-catalog-file))
4b1aaa8b 5398
f32b3b91 5399 (widget-create 'push-button
52a244eb 5400 :notify 'idlwave-widget-scan-user-lib-files
f32b3b91
CD
5401 "Scan & Save")
5402 (widget-insert " ")
5403 (widget-create 'push-button
52a244eb 5404 :notify 'idlwave-delete-user-catalog-file
f32b3b91
CD
5405 "Delete File")
5406 (widget-insert " ")
5407 (widget-create 'push-button
4b1aaa8b 5408 :notify
8d222148
SM
5409 (lambda (&rest ignore)
5410 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5411 (dolist (x path-list)
5412 (unless (memq 'lib (cdr x))
5413 (idlwave-path-alist-add-flag x 'user)))
5414 (idlwave-display-user-catalog-widget path-list)))
52a244eb 5415 "Select All Non-Lib")
f32b3b91
CD
5416 (widget-insert " ")
5417 (widget-create 'push-button
4b1aaa8b 5418 :notify
8d222148
SM
5419 (lambda (&rest ignore)
5420 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5421 (dolist (x path-list)
5422 (idlwave-path-alist-remove-flag x 'user))
5423 (idlwave-display-user-catalog-widget path-list)))
f32b3b91 5424 "Deselect All")
52a244eb
S
5425 (widget-insert " ")
5426 (widget-create 'push-button
5427 :notify (lambda (&rest ignore)
5428 (kill-buffer (current-buffer)))
5429 "Quit")
f32b3b91
CD
5430 (widget-insert "\n\n")
5431
52a244eb 5432 (widget-insert "Select Directories: \n")
4b1aaa8b 5433
f32b3b91
CD
5434 (setq idlwave-widget
5435 (apply 'widget-create
5436 'checklist
4b1aaa8b
PE
5437 :value (delq nil (mapcar (lambda (x)
5438 (if (memq 'user (cdr x))
52a244eb
S
5439 (car x)))
5440 dirs-list))
f32b3b91
CD
5441 :greedy t
5442 :tag "List of directories"
4b1aaa8b
PE
5443 (mapcar (lambda (x)
5444 (list 'item
52a244eb
S
5445 (if (memq 'lib (cdr x))
5446 (concat "[LIB] " (car x) )
5447 (car x)))) dirs-list)))
5448 (widget-put idlwave-widget :path-dirs dirs-list)
f32b3b91
CD
5449 (widget-insert "\n")
5450 (use-local-map widget-keymap)
5451 (widget-setup)
5452 (goto-char (point-min))
5453 (delete-other-windows))
4b1aaa8b 5454
52a244eb 5455(defun idlwave-delete-user-catalog-file (&rest ignore)
f32b3b91 5456 (if (yes-or-no-p
52a244eb 5457 (format "Delete file %s " idlwave-user-catalog-file))
f32b3b91 5458 (progn
52a244eb
S
5459 (delete-file idlwave-user-catalog-file)
5460 (message "%s has been deleted" idlwave-user-catalog-file))))
f32b3b91 5461
52a244eb
S
5462(defun idlwave-widget-scan-user-lib-files (&rest ignore)
5463 ;; Call `idlwave-scan-user-lib-files' with data taken from the widget.
f32b3b91 5464 (let* ((widget idlwave-widget)
15e42531 5465 (selected-dirs (widget-value widget))
52a244eb
S
5466 (path-alist (widget-get widget :path-dirs))
5467 (this-path-alist path-alist)
5468 dir-entry)
5469 (while (setq dir-entry (pop this-path-alist))
4b1aaa8b 5470 (if (member
52a244eb
S
5471 (if (memq 'lib (cdr dir-entry))
5472 (concat "[LIB] " (car dir-entry))
5473 (car dir-entry))
5474 selected-dirs)
5475 (idlwave-path-alist-add-flag dir-entry 'user)
5476 (idlwave-path-alist-remove-flag dir-entry 'user)))
5477 (idlwave-scan-user-lib-files path-alist)))
f32b3b91
CD
5478
5479(defvar font-lock-mode)
52a244eb
S
5480(defun idlwave-scan-user-lib-files (path-alist)
5481 ;; Scan the PRO files in PATH-ALIST and store the info in the user catalog
f32b3b91 5482 (let* ((idlwave-scanning-lib t)
15e42531 5483 (idlwave-scanning-lib-dir "")
f32b3b91 5484 (idlwave-completion-case nil)
15e42531 5485 dirs-alist dir files file)
52a244eb
S
5486 (setq idlwave-user-catalog-routines nil
5487 idlwave-path-alist path-alist ; for library-path instead
5488 idlwave-true-path-alist nil)
5489 (if idlwave-auto-write-paths (idlwave-write-paths))
9a529312 5490 (with-current-buffer (get-buffer-create "*idlwave-scan.pro*")
f32b3b91 5491 (idlwave-mode)
15e42531
CD
5492 (setq dirs-alist (reverse path-alist))
5493 (while (setq dir (pop dirs-alist))
52a244eb 5494 (when (memq 'user (cdr dir)) ; Has it marked for scan?
15e42531 5495 (setq dir (car dir))
52a244eb 5496 (setq idlwave-scanning-lib-dir dir)
15e42531
CD
5497 (when (file-directory-p dir)
5498 (setq files (directory-files dir 'full "\\.[pP][rR][oO]\\'"))
5499 (while (setq file (pop files))
5500 (when (file-regular-p file)
5501 (if (not (file-readable-p file))
5502 (message "Skipping %s (no read permission)" file)
5503 (message "Scanning %s..." file)
5504 (erase-buffer)
5505 (insert-file-contents file 'visit)
52a244eb 5506 (setq idlwave-user-catalog-routines
15e42531
CD
5507 (append (idlwave-get-routine-info-from-buffers
5508 (list (current-buffer)))
52a244eb
S
5509 idlwave-user-catalog-routines)))))))))
5510 (message "Creating user catalog file...")
f32b3b91
CD
5511 (kill-buffer "*idlwave-scan.pro*")
5512 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
15e42531
CD
5513 (let ((font-lock-maximum-size 0)
5514 (auto-mode-alist nil))
52a244eb 5515 (find-file idlwave-user-catalog-file))
f32b3b91
CD
5516 (if (and (boundp 'font-lock-mode)
5517 font-lock-mode)
5518 (font-lock-mode 0))
5519 (erase-buffer)
52a244eb 5520 (insert ";; IDLWAVE user catalog file\n")
f32b3b91
CD
5521 (insert (format ";; Created %s\n\n" (current-time-string)))
5522
f32b3b91 5523 ;; Define the routine info list
52a244eb 5524 (insert "\n(setq idlwave-user-catalog-routines\n '(")
5e72c6b2 5525 (let ((standard-output (current-buffer)))
8ffcfb27
GM
5526 (mapc (lambda (x)
5527 (insert "\n ")
5528 (prin1 x)
5529 (goto-char (point-max)))
5530 idlwave-user-catalog-routines))
f32b3b91 5531 (insert (format "))\n\n;;; %s ends here\n"
52a244eb 5532 (file-name-nondirectory idlwave-user-catalog-file)))
f32b3b91
CD
5533 (goto-char (point-min))
5534 ;; Save the buffer
5535 (save-buffer 0)
5536 (kill-buffer (current-buffer)))
52a244eb 5537 (message "Creating user catalog file...done")
f32b3b91 5538 (message "Info for %d routines saved in %s"
52a244eb
S
5539 (length idlwave-user-catalog-routines)
5540 idlwave-user-catalog-file)
f32b3b91
CD
5541 (sit-for 2)
5542 (idlwave-update-routine-info t))
5543
52a244eb
S
5544(defun idlwave-read-paths ()
5545 (if (and (stringp idlwave-path-file)
5546 (file-regular-p idlwave-path-file))
5547 (condition-case nil
5548 (load idlwave-path-file t t t)
5549 (error nil))))
5550
5551(defun idlwave-write-paths ()
5552 (interactive)
5553 (when (and idlwave-path-alist idlwave-system-directory)
5554 (let ((font-lock-maximum-size 0)
5555 (auto-mode-alist nil))
5556 (find-file idlwave-path-file))
5557 (if (and (boundp 'font-lock-mode)
5558 font-lock-mode)
5559 (font-lock-mode 0))
5560 (erase-buffer)
5561 (insert ";; IDLWAVE paths\n")
5562 (insert (format ";; Created %s\n\n" (current-time-string)))
5563 ;; Define the variable which knows the value of "!DIR"
5564 (insert (format "\n(setq idlwave-system-directory \"%s\")\n"
5565 idlwave-system-directory))
4b1aaa8b 5566
52a244eb
S
5567 ;; Define the variable which contains a list of all scanned directories
5568 (insert "\n(setq idlwave-path-alist\n '(")
5569 (let ((standard-output (current-buffer)))
8ffcfb27
GM
5570 (mapc (lambda (x)
5571 (insert "\n ")
5572 (prin1 x)
5573 (goto-char (point-max)))
5574 idlwave-path-alist))
52a244eb
S
5575 (insert "))\n")
5576 (save-buffer 0)
5577 (kill-buffer (current-buffer))))
5578
5579
f32b3b91
CD
5580(defun idlwave-expand-path (path &optional default-dir)
5581 ;; Expand parts of path starting with '+' recursively into directory list.
5582 ;; Relative recursive path elements are expanded relative to DEFAULT-DIR.
5583 (message "Expanding path...")
5584 (let (path1 dir recursive)
5585 (while (setq dir (pop path))
5586 (if (setq recursive (string= (substring dir 0 1) "+"))
5587 (setq dir (substring dir 1)))
5588 (if (and recursive
5589 (not (file-name-absolute-p dir)))
5590 (setq dir (expand-file-name dir default-dir)))
5591 (if recursive
5592 ;; Expand recursively
5593 (setq path1 (append (idlwave-recursive-directory-list dir) path1))
5594 ;; Keep unchanged
5595 (push dir path1)))
5596 (message "Expanding path...done")
5597 (nreverse path1)))
5598
5599(defun idlwave-recursive-directory-list (dir)
5600 ;; Return a list of all directories below DIR, including DIR itself
5601 (let ((path (list dir)) path1 file files)
5602 (while (setq dir (pop path))
5603 (when (file-directory-p dir)
5604 (setq files (nreverse (directory-files dir t "[^.]")))
5605 (while (setq file (pop files))
4b1aaa8b 5606 (if (file-directory-p file)
f32b3b91
CD
5607 (push (file-name-as-directory file) path)))
5608 (push dir path1)))
5609 path1))
5610
52a244eb
S
5611
5612;;----- Scanning the library catalogs ------------------
5613
3938cb82
S
5614
5615
5616
52a244eb 5617(defun idlwave-scan-library-catalogs (&optional message-base no-load)
4b1aaa8b 5618 "Scan for library catalog files (.idlwave_catalog) and ingest.
52a244eb
S
5619
5620All directories on `idlwave-path-alist' (or `idlwave-library-path'
5621instead, if present) are searched. Print MESSAGE-BASE along with the
5622libraries being loaded, if passed, and skip loading/normalizing if
5623NO-LOAD is non-nil. The variable `idlwave-use-library-catalogs' can
5624be set to nil to disable library catalog scanning."
5625 (when idlwave-use-library-catalogs
4b1aaa8b 5626 (let ((dirs
52a244eb
S
5627 (if idlwave-library-path
5628 (idlwave-expand-path idlwave-library-path)
5629 (mapcar 'car idlwave-path-alist)))
5630 (old-libname "")
8d222148 5631 dir-entry dir catalog all-routines)
52a244eb
S
5632 (if message-base (message message-base))
5633 (while (setq dir (pop dirs))
5634 (catch 'continue
4b1aaa8b 5635 (when (file-readable-p
52a244eb
S
5636 (setq catalog (expand-file-name ".idlwave_catalog" dir)))
5637 (unless no-load
5638 (setq idlwave-library-catalog-routines nil)
5639 ;; Load the catalog file
5640 (condition-case nil
5641 (load catalog t t t)
5642 (error (throw 'continue t)))
4b1aaa8b
PE
5643 (when (and
5644 message-base
5645 (not (string= idlwave-library-catalog-libname
52a244eb 5646 old-libname)))
4b1aaa8b 5647 (message "%s" (concat message-base
f66f03de 5648 idlwave-library-catalog-libname))
52a244eb
S
5649 (setq old-libname idlwave-library-catalog-libname))
5650 (when idlwave-library-catalog-routines
5651 (setq all-routines
4b1aaa8b 5652 (append
52a244eb
S
5653 (idlwave-sintern-rinfo-list
5654 idlwave-library-catalog-routines 'sys dir)
5655 all-routines))))
4b1aaa8b 5656
52a244eb
S
5657 ;; Add a 'lib flag if on path-alist
5658 (when (and idlwave-path-alist
5659 (setq dir-entry (assoc dir idlwave-path-alist)))
5660 (idlwave-path-alist-add-flag dir-entry 'lib)))))
5661 (unless no-load (setq idlwave-library-catalog-routines all-routines))
5662 (if message-base (message (concat message-base "done"))))))
5663
5664;;----- Communicating with the Shell -------------------
f32b3b91
CD
5665
5666;; First, here is the idl program which can be used to query IDL for
4b1aaa8b 5667;; defined routines.
f32b3b91
CD
5668(defconst idlwave-routine-info.pro
5669 "
05a1abfc 5670;; START OF IDLWAVE SUPPORT ROUTINES
f66f03de
S
5671pro idlwave_print_safe,item,limit
5672 catch,err
5673 if err ne 0 then begin
5674 print,'Could not print item.'
5675 return
5676 endif
5677 if n_elements(item) gt limit then $
5678 print,item[0:limit-1],'<... truncated at ',strtrim(limit,2),' elements>' $
5679 else print,item
5680end
5681
15e42531 5682pro idlwave_print_info_entry,name,func=func,separator=sep
f32b3b91 5683 ;; See if it's an object method
15e42531 5684 if name eq '' then return
4b1aaa8b 5685 func = keyword_set(func)
f32b3b91
CD
5686 methsep = strpos(name,'::')
5687 meth = methsep ne -1
4b1aaa8b 5688
f32b3b91
CD
5689 ;; Get routine info
5690 pars = routine_info(name,/parameters,functions=func)
5691 source = routine_info(name,/source,functions=func)
5692 nargs = pars.num_args
5693 nkw = pars.num_kw_args
5694 if nargs gt 0 then args = pars.args
5695 if nkw gt 0 then kwargs = pars.kw_args
4b1aaa8b 5696
f32b3b91 5697 ;; Trim the class, and make the name
4b1aaa8b 5698 if meth then begin
f32b3b91
CD
5699 class = strmid(name,0,methsep)
5700 name = strmid(name,methsep+2,strlen(name)-1)
4b1aaa8b 5701 if nargs gt 0 then begin
f32b3b91
CD
5702 ;; remove the self argument
5703 wh = where(args ne 'SELF',nargs)
52a244eb 5704 if nargs gt 0 then args = args[wh]
f32b3b91
CD
5705 endif
5706 endif else begin
5707 ;; No class, just a normal routine.
5708 class = \"\"
5709 endelse
4b1aaa8b 5710
f32b3b91
CD
5711 ;; Calling sequence
5712 cs = \"\"
5713 if func then cs = 'Result = '
5714 if meth then cs = cs + 'Obj -> [' + '%s' + '::]'
5715 cs = cs + '%s'
5716 if func then cs = cs + '(' else if nargs gt 0 then cs = cs + ', '
5717 if nargs gt 0 then begin
5718 for j=0,nargs-1 do begin
52a244eb 5719 cs = cs + args[j]
f32b3b91
CD
5720 if j lt nargs-1 then cs = cs + ', '
5721 endfor
5722 end
5723 if func then cs = cs + ')'
5724 ;; Keyword arguments
5725 kwstring = ''
5726 if nkw gt 0 then begin
5727 for j=0,nkw-1 do begin
52a244eb 5728 kwstring = kwstring + ' ' + kwargs[j]
f32b3b91
CD
5729 endfor
5730 endif
4b1aaa8b 5731
52a244eb 5732 ret=(['IDLWAVE-PRO','IDLWAVE-FUN'])[func]
4b1aaa8b 5733
52a244eb 5734 print,ret + ': ' + name + sep + class + sep + source[0].path $
f32b3b91
CD
5735 + sep + cs + sep + kwstring
5736end
5737
f66f03de 5738pro idlwave_routine_info,file
52a244eb 5739 on_error,1
f32b3b91
CD
5740 sep = '<@>'
5741 print,'>>>BEGIN OF IDLWAVE ROUTINE INFO (\"' + sep + '\" IS THE SEPARATOR)'
5742 all = routine_info()
f66f03de
S
5743 fileQ=n_elements(file) ne 0
5744 if fileQ then file=strtrim(file,2)
4b1aaa8b
PE
5745 for i=0L,n_elements(all)-1L do begin
5746 if fileQ then begin
f66f03de
S
5747 if (routine_info(all[i],/SOURCE)).path eq file then $
5748 idlwave_print_info_entry,all[i],separator=sep
5749 endif else idlwave_print_info_entry,all[i],separator=sep
4b1aaa8b 5750 endfor
f32b3b91 5751 all = routine_info(/functions)
4b1aaa8b
PE
5752 for i=0L,n_elements(all)-1L do begin
5753 if fileQ then begin
f66f03de
S
5754 if (routine_info(all[i],/FUNCTIONS,/SOURCE)).path eq file then $
5755 idlwave_print_info_entry,all[i],separator=sep,/FUNC
5756 endif else idlwave_print_info_entry,all[i],separator=sep,/FUNC
4b1aaa8b 5757 endfor
f32b3b91
CD
5758 print,'>>>END OF IDLWAVE ROUTINE INFO'
5759end
05a1abfc
CD
5760
5761pro idlwave_get_sysvars
52a244eb 5762 on_error,1
05a1abfc
CD
5763 catch,error_status
5764 if error_status ne 0 then begin
5765 print, 'Cannot get info about system variables'
5766 endif else begin
5767 help,/brief,output=s,/system_variables ; ? unsafe use of OUTPUT=
5768 s = strtrim(strjoin(s,' ',/single),2) ; make one line
5769 v = strsplit(s,' +',/regex,/extract) ; get variables
f66f03de 5770 for i=0L,n_elements(v)-1 do begin
05a1abfc
CD
5771 t = [''] ; get tag list
5772 a=execute('if n_tags('+v[i]+') gt 0 then t=tag_names('+v[i]+')')
5773 print, 'IDLWAVE-SYSVAR: '+v[i]+' '+strjoin(t,' ',/single)
5774 endfor
5775 endelse
5776end
5777
5778pro idlwave_get_class_tags, class
5779 res = execute('tags=tag_names({'+class+'})')
5e72c6b2 5780 if res then print,'IDLWAVE-CLASS-TAGS: '+class+' '+strjoin(tags,' ',/single)
05a1abfc
CD
5781end
5782;; END OF IDLWAVE SUPPORT ROUTINES
4b1aaa8b 5783"
5a0c3f56 5784 "The IDL programs to get info from the shell.")
f32b3b91 5785
15e42531 5786(defvar idlwave-idlwave_routine_info-compiled nil
5a0c3f56 5787 "Remember if the routine info procedure is already compiled.")
f32b3b91
CD
5788
5789(defvar idlwave-shell-temp-pro-file)
15e42531 5790(defvar idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5791
5792(defun idlwave-shell-compile-helper-routines (&optional wait)
15e42531 5793 (unless (and idlwave-idlwave_routine_info-compiled
5e72c6b2 5794 (file-readable-p (idlwave-shell-temp-file 'rinfo)))
9a529312
SM
5795 (with-current-buffer (idlwave-find-file-noselect
5796 (idlwave-shell-temp-file 'pro))
15e42531
CD
5797 (erase-buffer)
5798 (insert idlwave-routine-info.pro)
5799 (save-buffer 0))
4b1aaa8b 5800 (idlwave-shell-send-command
f66f03de 5801 (concat ".run \"" idlwave-shell-temp-pro-file "\"")
52a244eb 5802 nil 'hide wait)
15e42531 5803 (idlwave-shell-send-command
4b1aaa8b 5804 (format "save,'idlwave_print_safe','idlwave_routine_info','idlwave_print_info_entry','idlwave_get_class_tags','idlwave_get_sysvars',FILE='%s',/ROUTINES"
5e72c6b2 5805 (idlwave-shell-temp-file 'rinfo))
f66f03de
S
5806 nil 'hide)
5807 (setq idlwave-idlwave_routine_info-compiled t))
15e42531 5808
f66f03de
S
5809 ;; Restore if necessary. Must use execute to hide lame routine_info
5810 ;; errors on undefinded routine
15e42531 5811 (idlwave-shell-send-command
f66f03de
S
5812 (format "if execute(\"_v=routine_info('idlwave_routine_info',/SOURCE)\") eq 0 then restore,'%s' else if _v.path eq '' then restore,'%s'"
5813 idlwave-shell-temp-rinfo-save-file
15e42531 5814 idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5815 nil 'hide))
5816
5817
5818(defun idlwave-shell-update-routine-info (&optional quiet run-hooks wait file)
5819 "Query the shell for routine_info of compiled modules and update the lists."
5820 ;; Save and compile the procedure. The compiled procedure is then
5821 ;; saved into an IDL SAVE file, to allow for fast RESTORE. We may
5822 ;; need to test for and possibly RESTORE the procedure each time we
5823 ;; use it, since the user may have killed or redefined it. In
5824 ;; particular, .RESET_SESSION will kill all user procedures. If
5825 ;; FILE is set, only update routine info for routines in that file.
5826
5827 (idlwave-shell-compile-helper-routines wait)
5828 ; execute the routine_info procedure, and analyze the output
5829 (idlwave-shell-send-command
5830 (format "idlwave_routine_info%s" (if file (concat ",'" file "'") ""))
15e42531
CD
5831 `(progn
5832 (idlwave-shell-routine-info-filter)
05a1abfc 5833 (idlwave-concatenate-rinfo-lists ,quiet ,run-hooks))
52a244eb 5834 'hide wait))
f32b3b91
CD
5835
5836;; ---------------------------------------------------------------------------
5837;;
5838;; Completion and displaying routine calling sequences
5839
15e42531 5840(defvar idlwave-completion-help-info nil)
52a244eb 5841(defvar idlwave-completion-help-links nil)
15e42531 5842(defvar idlwave-current-obj_new-class nil)
05a1abfc 5843(defvar idlwave-complete-special nil)
8d222148
SM
5844(defvar method-selector)
5845(defvar class-selector)
5846(defvar type-selector)
5847(defvar super-classes)
15e42531 5848
f32b3b91
CD
5849(defun idlwave-complete (&optional arg module class)
5850 "Complete a function, procedure or keyword name at point.
2e8b9c7d 5851This function is smart and figures out what can be completed
f32b3b91
CD
5852at this point.
5853- At the beginning of a statement it completes procedure names.
5854- In the middle of a statement it completes function names.
5a0c3f56 5855- After a `(' or `,' in the argument list of a function or procedure,
f32b3b91
CD
5856 it completes a keyword of the relevant function or procedure.
5857- In the first arg of `OBJ_NEW', it completes a class name.
5858
5a0c3f56
JB
5859When several completions are possible, a list will be displayed in
5860the *Completions* buffer. If this list is too long to fit into the
5e72c6b2
S
5861window, scrolling can be achieved by repeatedly pressing
5862\\[idlwave-complete].
f32b3b91
CD
5863
5864The function also knows about object methods. When it needs a class
5865name, the action depends upon `idlwave-query-class', which see. You
5e72c6b2
S
5866can force IDLWAVE to ask you for a class name with a
5867\\[universal-argument] prefix argument to this command.
f32b3b91
CD
5868
5869See also the variables `idlwave-keyword-completion-adds-equal' and
5870`idlwave-function-completion-adds-paren'.
5871
5872The optional ARG can be used to specify the completion type in order
5873to override IDLWAVE's idea of what should be completed at point.
5874Possible values are:
5875
58760 <=> query for the completion type
58771 <=> 'procedure
58782 <=> 'procedure-keyword
58793 <=> 'function
58804 <=> 'function-keyword
58815 <=> 'procedure-method
58826 <=> 'procedure-method-keyword
58837 <=> 'function-method
58848 <=> 'function-method-keyword
58859 <=> 'class
5886
5e72c6b2
S
5887As a special case, the universal argument C-u forces completion of
5888function names in places where the default would be a keyword.
5889
52a244eb
S
5890Two prefix argument, C-u C-u, prompts for a regexp by which to limit
5891completion.
5892
f32b3b91
CD
5893For Lisp programmers only:
5894When we force a keyword, optional argument MODULE can contain the module name.
5895When we force a method or a method keyword, CLASS can specify the class."
5896 (interactive "P")
5897 (idlwave-routines)
5898 (let* ((where-list
5899 (if (and arg
52a244eb 5900 (or (and (integerp arg) (not (equal arg '(16))))
f32b3b91
CD
5901 (symbolp arg)))
5902 (idlwave-make-force-complete-where-list arg module class)
5903 (idlwave-where)))
5904 (what (nth 2 where-list))
52a244eb
S
5905 (idlwave-force-class-query (equal arg '(4)))
5906 (completion-regexp-list
5907 (if (equal arg '(16))
5908 (list (read-string (concat "Completion Regexp: "))))))
4b1aaa8b 5909
f32b3b91
CD
5910 (if (and module (string-match "::" module))
5911 (setq class (substring module 0 (match-beginning 0))
5912 module (substring module (match-end 0))))
5913
5914 (cond
5915
5916 ((and (null arg)
5917 (eq (car-safe last-command) 'idlwave-display-completion-list)
595ab50b 5918 (get-buffer-window "*Completions*"))
f32b3b91
CD
5919 (setq this-command last-command)
5920 (idlwave-scroll-completions))
5921
52a244eb 5922 ;; Complete a filename in quotes
05a1abfc
CD
5923 ((and (idlwave-in-quote)
5924 (not (eq what 'class)))
5925 (idlwave-complete-filename))
5926
52a244eb
S
5927 ;; Check for any special completion functions
5928 ((and idlwave-complete-special
5929 (idlwave-call-special idlwave-complete-special)))
4b1aaa8b 5930
f32b3b91
CD
5931 ((null what)
5932 (error "Nothing to complete here"))
5933
52a244eb 5934 ;; Complete a class
f32b3b91 5935 ((eq what 'class)
15e42531 5936 (setq idlwave-completion-help-info '(class))
f32b3b91
CD
5937 (idlwave-complete-class))
5938
5939 ((eq what 'procedure)
5940 ;; Complete a procedure name
5e72c6b2
S
5941 (let* ((cw-list (nth 3 where-list))
5942 (class-selector (idlwave-determine-class cw-list 'pro))
5943 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5944 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5945 (isa (concat "procedure" (if class-selector "-method" "")))
5946 (type-selector 'pro))
4b1aaa8b 5947 (setq idlwave-completion-help-info
05a1abfc 5948 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5949 (idlwave-complete-in-buffer
5950 'procedure (if class-selector 'method 'routine)
5951 (idlwave-routines) 'idlwave-selector
5952 (format "Select a %s name%s"
5953 isa
5954 (if class-selector
4b1aaa8b
PE
5955 (format " (class is %s)"
5956 (if (eq class-selector t)
76959b77 5957 "unknown" class-selector))
f32b3b91
CD
5958 ""))
5959 isa
52a244eb 5960 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91
CD
5961
5962 ((eq what 'function)
5963 ;; Complete a function name
5e72c6b2
S
5964 (let* ((cw-list (nth 3 where-list))
5965 (class-selector (idlwave-determine-class cw-list 'fun))
5966 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5967 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5968 (isa (concat "function" (if class-selector "-method" "")))
5969 (type-selector 'fun))
4b1aaa8b 5970 (setq idlwave-completion-help-info
05a1abfc 5971 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5972 (idlwave-complete-in-buffer
5973 'function (if class-selector 'method 'routine)
5974 (idlwave-routines) 'idlwave-selector
5975 (format "Select a %s name%s"
5976 isa
5977 (if class-selector
4b1aaa8b 5978 (format " (class is %s)"
76959b77
S
5979 (if (eq class-selector t)
5980 "unknown" class-selector))
f32b3b91
CD
5981 ""))
5982 isa
52a244eb 5983 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91 5984
52a244eb 5985 ((and (memq what '(procedure-keyword function-keyword)) ; Special Case
5e72c6b2
S
5986 (equal arg '(4)))
5987 (idlwave-complete 3))
5988
f32b3b91
CD
5989 ((eq what 'procedure-keyword)
5990 ;; Complete a procedure keyword
5991 (let* ((where (nth 3 where-list))
5992 (name (car where))
5993 (method-selector name)
5994 (type-selector 'pro)
5995 (class (idlwave-determine-class where 'pro))
5996 (class-selector class)
05a1abfc 5997 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 5998 (isa (format "procedure%s-keyword" (if class "-method" "")))
15e42531 5999 (entry (idlwave-best-rinfo-assq
f32b3b91 6000 name 'pro class (idlwave-routines)))
3938cb82 6001 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 6002 (list (idlwave-entry-keywords entry 'do-link)))
f32b3b91
CD
6003 (unless (or entry (eq class t))
6004 (error "Nothing known about procedure %s"
6005 (idlwave-make-full-name class name)))
4b1aaa8b 6006 (setq list (idlwave-fix-keywords name 'pro class list
3938cb82 6007 super-classes system))
b6a97790 6008 (unless list (error "No keywords available for procedure %s"
3938cb82 6009 (idlwave-make-full-name class name)))
4b1aaa8b 6010 (setq idlwave-completion-help-info
52a244eb 6011 (list 'keyword name type-selector class-selector entry super-classes))
f32b3b91
CD
6012 (idlwave-complete-in-buffer
6013 'keyword 'keyword list nil
6014 (format "Select keyword for procedure %s%s"
6015 (idlwave-make-full-name class name)
15e42531 6016 (if (or (member '("_EXTRA") list)
4b1aaa8b 6017 (member '("_REF_EXTRA") list))
15e42531 6018 " (note _EXTRA)" ""))
f32b3b91
CD
6019 isa
6020 'idlwave-attach-keyword-classes)))
6021
6022 ((eq what 'function-keyword)
6023 ;; Complete a function keyword
6024 (let* ((where (nth 3 where-list))
6025 (name (car where))
6026 (method-selector name)
6027 (type-selector 'fun)
6028 (class (idlwave-determine-class where 'fun))
6029 (class-selector class)
05a1abfc 6030 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 6031 (isa (format "function%s-keyword" (if class "-method" "")))
15e42531 6032 (entry (idlwave-best-rinfo-assq
f32b3b91 6033 name 'fun class (idlwave-routines)))
3938cb82 6034 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 6035 (list (idlwave-entry-keywords entry 'do-link))
15e42531 6036 msg-name)
f32b3b91
CD
6037 (unless (or entry (eq class t))
6038 (error "Nothing known about function %s"
6039 (idlwave-make-full-name class name)))
4b1aaa8b 6040 (setq list (idlwave-fix-keywords name 'fun class list
3938cb82 6041 super-classes system))
15e42531
CD
6042 ;; OBJ_NEW: Messages mention the proper Init method
6043 (setq msg-name (if (and (null class)
6044 (string= (upcase name) "OBJ_NEW"))
6045 (concat idlwave-current-obj_new-class
6046 "::Init (via OBJ_NEW)")
6047 (idlwave-make-full-name class name)))
b6a97790 6048 (unless list (error "No keywords available for function %s"
3938cb82 6049 msg-name))
4b1aaa8b 6050 (setq idlwave-completion-help-info
05a1abfc 6051 (list 'keyword name type-selector class-selector nil super-classes))
f32b3b91
CD
6052 (idlwave-complete-in-buffer
6053 'keyword 'keyword list nil
15e42531
CD
6054 (format "Select keyword for function %s%s" msg-name
6055 (if (or (member '("_EXTRA") list)
4b1aaa8b 6056 (member '("_REF_EXTRA") list))
15e42531 6057 " (note _EXTRA)" ""))
f32b3b91
CD
6058 isa
6059 'idlwave-attach-keyword-classes)))
15e42531 6060
f32b3b91
CD
6061 (t (error "This should not happen (idlwave-complete)")))))
6062
05a1abfc
CD
6063(defvar idlwave-complete-special nil
6064 "List of special completion functions.
52a244eb
S
6065These functions are called for each completion. Each function must
6066check if its own special completion context is present. If yes, it
6067should use `idlwave-complete-in-buffer' to do some completion and
6068return t. If such a function returns t, *no further* attempts to
6069complete other contexts will be done. If the function returns nil,
6070other completions will be tried.")
76959b77
S
6071
6072(defun idlwave-call-special (functions &rest args)
6073 (let ((funcs functions)
6074 fun ret)
05a1abfc 6075 (catch 'exit
76959b77
S
6076 (while (setq fun (pop funcs))
6077 (if (setq ret (apply fun args))
6078 (throw 'exit ret)))
05a1abfc
CD
6079 nil)))
6080
f32b3b91
CD
6081(defun idlwave-make-force-complete-where-list (what &optional module class)
6082 ;; Return an artificial WHERE specification to force the completion
6083 ;; routine to complete a specific item independent of context.
6084 ;; WHAT is the prefix arg of `idlwave-complete', see there for details.
6085 ;; MODULE and CLASS can be used to specify the routine name and class.
6086 ;; The class name will also be found in MODULE if that is like "class::mod".
6087 (let* ((what-list '(("procedure") ("procedure-keyword")
6088 ("function") ("function-keyword")
6089 ("procedure-method") ("procedure-method-keyword")
6090 ("function-method") ("function-method-keyword")
6091 ("class")))
6092 (module (idlwave-sintern-routine-or-method module class))
6093 (class (idlwave-sintern-class class))
4b1aaa8b 6094 (what (cond
f32b3b91
CD
6095 ((equal what 0)
6096 (setq what
4b1aaa8b 6097 (intern (completing-read
f32b3b91
CD
6098 "Complete what? " what-list nil t))))
6099 ((integerp what)
6100 (setq what (intern (car (nth (1- what) what-list)))))
6101 ((and what
6102 (symbolp what)
6103 (assoc (symbol-name what) what-list))
6104 what)
eac9c0ef 6105 (t (error "Invalid WHAT"))))
f32b3b91
CD
6106 (nil-list '(nil nil nil nil))
6107 (class-list (list nil nil (or class t) nil)))
6108
6109 (cond
6110
6111 ((eq what 'procedure)
6112 (list nil-list nil-list 'procedure nil-list nil))
6113
6114 ((eq what 'procedure-keyword)
6115 (let* ((class-selector nil)
05a1abfc 6116 (super-classes nil)
f32b3b91
CD
6117 (type-selector 'pro)
6118 (pro (or module
4b1aaa8b 6119 (idlwave-completing-read
f32b3b91
CD
6120 "Procedure: " (idlwave-routines) 'idlwave-selector))))
6121 (setq pro (idlwave-sintern-routine pro))
6122 (list nil-list nil-list 'procedure-keyword
6123 (list pro nil nil nil) nil)))
6124
6125 ((eq what 'function)
6126 (list nil-list nil-list 'function nil-list nil))
6127
6128 ((eq what 'function-keyword)
6129 (let* ((class-selector nil)
05a1abfc 6130 (super-classes nil)
f32b3b91
CD
6131 (type-selector 'fun)
6132 (func (or module
4b1aaa8b 6133 (idlwave-completing-read
f32b3b91
CD
6134 "Function: " (idlwave-routines) 'idlwave-selector))))
6135 (setq func (idlwave-sintern-routine func))
6136 (list nil-list nil-list 'function-keyword
6137 (list func nil nil nil) nil)))
6138
6139 ((eq what 'procedure-method)
6140 (list nil-list nil-list 'procedure class-list nil))
6141
6142 ((eq what 'procedure-method-keyword)
6143 (let* ((class (idlwave-determine-class class-list 'pro))
6144 (class-selector class)
05a1abfc 6145 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6146 (type-selector 'pro)
6147 (pro (or module
6148 (idlwave-completing-read
6149 (format "Procedure in %s class: " class-selector)
6150 (idlwave-routines) 'idlwave-selector))))
6151 (setq pro (idlwave-sintern-method pro))
6152 (list nil-list nil-list 'procedure-keyword
6153 (list pro nil class nil) nil)))
6154
6155 ((eq what 'function-method)
6156 (list nil-list nil-list 'function class-list nil))
6157
6158 ((eq what 'function-method-keyword)
6159 (let* ((class (idlwave-determine-class class-list 'fun))
6160 (class-selector class)
05a1abfc 6161 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6162 (type-selector 'fun)
6163 (func (or module
6164 (idlwave-completing-read
6165 (format "Function in %s class: " class-selector)
6166 (idlwave-routines) 'idlwave-selector))))
6167 (setq func (idlwave-sintern-method func))
6168 (list nil-list nil-list 'function-keyword
6169 (list func nil class nil) nil)))
6170
6171 ((eq what 'class)
6172 (list nil-list nil-list 'class nil-list nil))
4b1aaa8b 6173
eac9c0ef 6174 (t (error "Invalid value for WHAT")))))
f32b3b91
CD
6175
6176(defun idlwave-completing-read (&rest args)
6177 ;; Completing read, case insensitive
6178 (let ((old-value (default-value 'completion-ignore-case)))
6179 (unwind-protect
6180 (progn
6181 (setq-default completion-ignore-case t)
6182 (apply 'completing-read args))
6183 (setq-default completion-ignore-case old-value))))
6184
05a1abfc
CD
6185(defvar idlwave-shell-default-directory)
6186(defun idlwave-complete-filename ()
6187 "Use the comint stuff to complete a file name."
6188 (require 'comint)
6189 (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-")
6190 (comint-completion-addsuffix nil)
6191 (default-directory
6192 (if (and (boundp 'idlwave-shell-default-directory)
6193 (stringp idlwave-shell-default-directory)
6194 (file-directory-p idlwave-shell-default-directory))
6195 idlwave-shell-default-directory
4b1aaa8b 6196 default-directory)))
05a1abfc
CD
6197 (comint-dynamic-complete-filename)))
6198
f32b3b91
CD
6199(defun idlwave-make-full-name (class name)
6200 ;; Make a fully qualified module name including the class name
6201 (concat (if class (format "%s::" class) "") name))
6202
15e42531
CD
6203(defun idlwave-rinfo-assoc (name type class list)
6204 "Like `idlwave-rinfo-assq', but sintern strings first."
4b1aaa8b 6205 (idlwave-rinfo-assq
15e42531
CD
6206 (idlwave-sintern-routine-or-method name class)
6207 type (idlwave-sintern-class class) list))
6208
f32b3b91
CD
6209(defun idlwave-rinfo-assq (name type class list)
6210 ;; Works like assq, but also checks type and class
6211 (catch 'exit
6212 (let (match)
6213 (while (setq match (assq name list))
6214 (and (or (eq type t)
6215 (eq (nth 1 match) type))
6216 (eq (nth 2 match) class)
6217 (throw 'exit match))
6218 (setq list (cdr (memq match list)))))))
6219
05a1abfc 6220(defun idlwave-rinfo-assq-any-class (name type class list)
52a244eb 6221 ;; Return the first matching method on the inheritance list
05a1abfc
CD
6222 (let* ((classes (cons class (idlwave-all-class-inherits class)))
6223 class rtn)
6224 (while classes
6225 (if (setq rtn (idlwave-rinfo-assq name type (pop classes) list))
6226 (setq classes nil)))
6227 rtn))
6228
4b1aaa8b 6229(defun idlwave-best-rinfo-assq (name type class list &optional with-file
52a244eb
S
6230 keep-system)
6231 "Like `idlwave-rinfo-assq', but get all twins and sort, then return first.
6232If WITH-FILE is passed, find the best rinfo entry with a file
6233included. If KEEP-SYSTEM is set, don't prune system for compiled
6234syslib files."
15e42531 6235 (let ((twins (idlwave-routine-twins
05a1abfc 6236 (idlwave-rinfo-assq-any-class name type class list)
15e42531
CD
6237 list))
6238 syslibp)
6239 (when (> (length twins) 1)
6240 (setq twins (sort twins 'idlwave-routine-entry-compare-twins))
52a244eb
S
6241 (if (and (null keep-system)
6242 (eq 'system (car (nth 3 (car twins))))
15e42531
CD
6243 (setq syslibp (idlwave-any-syslib (cdr twins)))
6244 (not (equal 1 syslibp)))
52a244eb
S
6245 ;; Its a compiled syslib, so we need to remove the system entry
6246 (setq twins (cdr twins)))
6247 (if with-file
6248 (setq twins (delq nil
6249 (mapcar (lambda (x)
6250 (if (nth 1 (nth 3 x)) x))
6251 twins)))))
15e42531
CD
6252 (car twins)))
6253
4b1aaa8b 6254(defun idlwave-best-rinfo-assoc (name type class list &optional with-file
52a244eb 6255 keep-system)
15e42531
CD
6256 "Like `idlwave-best-rinfo-assq', but sintern strings first."
6257 (idlwave-best-rinfo-assq
6258 (idlwave-sintern-routine-or-method name class)
52a244eb 6259 type (idlwave-sintern-class class) list with-file keep-system))
15e42531
CD
6260
6261(defun idlwave-any-syslib (entries)
6262 "Does the entry list ENTRIES contain a syslib entry?
6263If yes, return the index (>=1)."
6264 (let (file (cnt 0))
6265 (catch 'exit
6266 (while entries
6267 (incf cnt)
52a244eb
S
6268 (setq file (idlwave-routine-source-file (nth 3 (car entries))))
6269 (if (and file (idlwave-syslib-p file))
15e42531
CD
6270 (throw 'exit cnt)
6271 (setq entries (cdr entries))))
6272 nil)))
6273
f32b3b91
CD
6274(defun idlwave-all-assq (key list)
6275 "Return a list of all associations of Key in LIST."
6276 (let (rtn elt)
6277 (while (setq elt (assq key list))
6278 (push elt rtn)
6279 (setq list (cdr (memq elt list))))
6280 (nreverse rtn)))
6281
6282(defun idlwave-all-method-classes (method &optional type)
5a0c3f56
JB
6283 "Return all classes which have a method METHOD.
6284TYPE is 'fun or 'pro.
f32b3b91
CD
6285When TYPE is not specified, both procedures and functions will be considered."
6286 (if (null method)
15e42531 6287 (mapcar 'car (idlwave-class-alist))
f32b3b91 6288 (let (rtn)
8ffcfb27
GM
6289 (mapc (lambda (x)
6290 (and (nth 2 x)
6291 (or (not type)
6292 (eq type (nth 1 x)))
6293 (push (nth 2 x) rtn)))
6294 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6295 (idlwave-uniquify rtn))))
6296
6297(defun idlwave-all-method-keyword-classes (method keyword &optional type)
6298 "Return all classes which have a method METHOD with keyword KEYWORD.
6299TYPE is 'fun or 'pro.
6300When TYPE is not specified, both procedures and functions will be considered."
6301 (if (or (null method)
6302 (null keyword))
6303 nil
6304 (let (rtn)
8ffcfb27
GM
6305 (mapc (lambda (x)
6306 (and (nth 2 x) ; non-nil class
6307 (or (not type) ; correct or unspecified type
6308 (eq type (nth 1 x)))
6309 (assoc keyword (idlwave-entry-keywords x))
6310 (push (nth 2 x) rtn)))
6311 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6312 (idlwave-uniquify rtn))))
6313
05a1abfc
CD
6314(defun idlwave-members-only (list club)
6315 "Return list of all elements in LIST which are also in CLUB."
6316 (let (rtn)
6317 (while list
6318 (if (member (car list) club)
6319 (setq rtn (cons (car list) rtn)))
6320 (setq list (cdr list)))
6321 (nreverse rtn)))
6322
6323(defun idlwave-nonmembers-only (list club)
6324 "Return list of all elements in LIST which are not in CLUB."
6325 (let (rtn)
6326 (while list
6327 (if (member (car list) club)
6328 nil
6329 (setq rtn (cons (car list) rtn)))
6330 (setq list (cdr list)))
6331 (nreverse rtn)))
6332
5e72c6b2
S
6333(defun idlwave-explicit-class-listed (info)
6334 "Return whether or not the class is listed explicitly, ala a->b::c.
5a0c3f56 6335INFO is as returned by `idlwave-what-function' or `-procedure'."
5e72c6b2
S
6336 (let ((apos (nth 3 info)))
6337 (if apos
6338 (save-excursion (goto-char apos)
6339 (looking-at "->[a-zA-Z][a-zA-Z0-9$_]*::")))))
6340
76959b77
S
6341(defvar idlwave-determine-class-special nil
6342 "List of special functions for determining class.
5a0c3f56 6343Must accept two arguments: `apos' and `info'.")
76959b77 6344
f32b3b91 6345(defun idlwave-determine-class (info type)
4b1aaa8b 6346 ;; Determine the class of a routine call.
76959b77
S
6347 ;; INFO is the `cw-list' structure as returned by idlwave-where.
6348 ;; The second element in this structure is the class. When nil, we
6349 ;; return nil. When t, try to get the class from text properties at
6350 ;; the arrow. When the object is "self", we use the class of the
6351 ;; current routine. otherwise prompt the user for a class name.
6352 ;; Also stores the selected class as a text property at the arrow.
f32b3b91
CD
6353 ;; TYPE is 'fun or 'pro.
6354 (let* ((class (nth 2 info))
6355 (apos (nth 3 info))
6356 (nassoc (assoc (if (stringp (car info))
6357 (upcase (car info))
6358 (car info))
6359 idlwave-query-class))
6360 (dassoc (assq (if (car info) 'keyword-default 'method-default)
6361 idlwave-query-class))
6362 (query (cond (nassoc (cdr nassoc))
6363 (dassoc (cdr dassoc))
6364 (t t)))
6365 (arrow (and apos (string= (buffer-substring apos (+ 2 apos)) "->")))
4b1aaa8b 6366 (is-self
15e42531
CD
6367 (and arrow
6368 (save-excursion (goto-char apos)
6369 (forward-word -1)
6370 (let ((case-fold-search t))
6371 (looking-at "self\\>")))))
f32b3b91 6372 (force-query idlwave-force-class-query)
76959b77 6373 store special-class class-alist)
f32b3b91
CD
6374 (cond
6375 ((null class) nil)
6376 ((eq t class)
6377 ;; There is an object which would like to know its class
6378 (if (and arrow (get-text-property apos 'idlwave-class)
6379 idlwave-store-inquired-class
6380 (not force-query))
6381 (setq class (get-text-property apos 'idlwave-class)
6382 class (idlwave-sintern-class class)))
76959b77
S
6383 (if (and (eq t class) is-self)
6384 (setq class (or (nth 2 (idlwave-current-routine)) class)))
6385
6386 ;; Before prompting, try any special class determination routines
4b1aaa8b 6387 (when (and (eq t class)
76959b77
S
6388 idlwave-determine-class-special
6389 (not force-query))
4b1aaa8b 6390 (setq special-class
76959b77 6391 (idlwave-call-special idlwave-determine-class-special apos))
4b1aaa8b 6392 (if special-class
76959b77
S
6393 (setq class (idlwave-sintern-class special-class)
6394 store idlwave-store-inquired-class)))
4b1aaa8b 6395
76959b77 6396 ;; Prompt for a class, if we need to
f32b3b91
CD
6397 (when (and (eq class t)
6398 (or force-query query))
4b1aaa8b 6399 (setq class-alist
f32b3b91
CD
6400 (mapcar 'list (idlwave-all-method-classes (car info) type)))
6401 (setq class
6402 (idlwave-sintern-class
6403 (cond
6404 ((and (= (length class-alist) 0) (not force-query))
6405 (error "No classes available with method %s" (car info)))
6406 ((and (= (length class-alist) 1) (not force-query))
6407 (car (car class-alist)))
4b1aaa8b 6408 (t
f32b3b91 6409 (setq store idlwave-store-inquired-class)
4b1aaa8b 6410 (idlwave-completing-read
f32b3b91
CD
6411 (format "Class%s: " (if (stringp (car info))
6412 (format " for %s method %s"
6413 type (car info))
6414 ""))
6415 class-alist nil nil nil 'idlwave-class-history))))))
76959b77
S
6416
6417 ;; Store it, if requested
f32b3b91
CD
6418 (when (and class (not (eq t class)))
6419 ;; We have a real class here
6420 (when (and store arrow)
76959b77 6421 (condition-case ()
4b1aaa8b
PE
6422 (add-text-properties
6423 apos (+ apos 2)
6424 `(idlwave-class ,class face ,idlwave-class-arrow-face
76959b77
S
6425 rear-nonsticky t))
6426 (error nil)))
f32b3b91
CD
6427 (setf (nth 2 info) class))
6428 ;; Return the class
6429 class)
6430 ;; Default as fallback
6431 (t class))))
6432
f32b3b91
CD
6433(defun idlwave-selector (a)
6434 (and (eq (nth 1 a) type-selector)
6435 (or (and (nth 2 a) (eq class-selector t))
05a1abfc 6436 (eq (nth 2 a) class-selector)
52a244eb
S
6437 (memq (nth 2 a) super-classes))))
6438
6439(defun idlwave-add-file-link-selector (a)
6440 ;; Record a file link, if any, for the tested names during selection.
6441 (let ((sel (idlwave-selector a)) file)
6442 (if (and sel (setq file (idlwave-entry-has-help a)))
6443 (push (cons (car a) file) idlwave-completion-help-links))
6444 sel))
6445
f32b3b91
CD
6446
6447(defun idlwave-where ()
4b1aaa8b 6448 "Find out where we are.
f32b3b91 6449The return value is a list with the following stuff:
5e72c6b2 6450\(PRO-LIST FUNC-LIST COMPLETE-WHAT CW-LIST LAST-CHAR)
f32b3b91
CD
6451
6452PRO-LIST (PRO POINT CLASS ARROW)
6453FUNC-LIST (FUNC POINT CLASS ARROW)
6454COMPLETE-WHAT a symbol indicating what kind of completion makes sense here
4b1aaa8b 6455CW-LIST (PRO-OR-FUNC POINT CLASS ARROW) Like PRO-LIST, for what can
5e72c6b2 6456 be completed here.
f32b3b91
CD
6457LAST-CHAR last relevant character before point (non-white non-comment,
6458 not part of current identifier or leading slash).
6459
6460In the lists, we have these meanings:
6461PRO: Procedure name
6462FUNC: Function name
6463POINT: Where is this
6464CLASS: What class has the routine (nil=no, t=is method, but class unknown)
5e72c6b2 6465ARROW: Location of the arrow"
f32b3b91 6466 (idlwave-routines)
4b1aaa8b 6467 (let* (;(bos (save-excursion (idlwave-beginning-of-statement) (point)))
15e42531 6468 (bos (save-excursion (idlwave-start-of-substatement 'pre) (point)))
f32b3b91
CD
6469 (func-entry (idlwave-what-function bos))
6470 (func (car func-entry))
6471 (func-class (nth 1 func-entry))
6472 (func-arrow (nth 2 func-entry))
6473 (func-point (or (nth 3 func-entry) 0))
6474 (func-level (or (nth 4 func-entry) 0))
6475 (pro-entry (idlwave-what-procedure bos))
6476 (pro (car pro-entry))
6477 (pro-class (nth 1 pro-entry))
6478 (pro-arrow (nth 2 pro-entry))
6479 (pro-point (or (nth 3 pro-entry) 0))
6480 (last-char (idlwave-last-valid-char))
6481 (case-fold-search t)
52a244eb 6482 (match-string (buffer-substring bos (point)))
f32b3b91
CD
6483 cw cw-mod cw-arrow cw-class cw-point)
6484 (if (< func-point pro-point) (setq func nil))
6485 (cond
15e42531 6486 ((string-match "\\`[ \t]*\\(pro\\|function\\)[ \t]+[a-zA-Z0-9_]*\\'"
52a244eb 6487 match-string)
15e42531 6488 (setq cw 'class))
4b1aaa8b
PE
6489 ((string-match
6490 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'"
52a244eb
S
6491 (if (> pro-point 0)
6492 (buffer-substring pro-point (point))
6493 match-string))
f32b3b91
CD
6494 (setq cw 'procedure cw-class pro-class cw-point pro-point
6495 cw-arrow pro-arrow))
6496 ((string-match "\\`[ \t]*\\(pro\\|function\\)\\>"
52a244eb 6497 match-string)
f32b3b91 6498 nil)
05a1abfc 6499 ((string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6500 match-string)
4b1aaa8b 6501 (setq cw 'class))
05a1abfc 6502 ((string-match "\\<inherits\\s-+\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6503 match-string)
4b1aaa8b
PE
6504 (setq cw 'class))
6505 ((and func
f32b3b91
CD
6506 (> func-point pro-point)
6507 (= func-level 1)
6508 (memq last-char '(?\( ?,)))
6509 (setq cw 'function-keyword cw-mod func cw-point func-point
6510 cw-class func-class cw-arrow func-arrow))
6511 ((and pro (eq last-char ?,))
6512 (setq cw 'procedure-keyword cw-mod pro cw-point pro-point
6513 cw-class pro-class cw-arrow pro-arrow))
6514; ((member last-char '(?\' ?\) ?\] ?!))
6515; ;; after these chars, a function makes no sense
6516; ;; FIXME: I am sure there can be more in this list
6517; ;; FIXME: Do we want to do this at all?
6518; nil)
6519 ;; Everywhere else we try a function.
6520 (t
6521 (setq cw 'function)
6522 (save-excursion
52a244eb 6523 (if (re-search-backward "->[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\s-*\\)?\\(\\([$a-zA-Z0-9_]+\\)::\\)?[$a-zA-Z0-9_]*\\=" bos t)
76959b77 6524 (setq cw-arrow (copy-marker (match-beginning 0))
52a244eb
S
6525 cw-class (if (match-end 4)
6526 (idlwave-sintern-class (match-string 4))
5e72c6b2 6527 t))))))
f32b3b91
CD
6528 (list (list pro pro-point pro-class pro-arrow)
6529 (list func func-point func-class func-arrow)
6530 cw
6531 (list cw-mod cw-point cw-class cw-arrow)
6532 last-char)))
6533
6534(defun idlwave-this-word (&optional class)
6535 ;; Grab the word around point. CLASS is for the `skip-chars=...' functions
52a244eb 6536 (setq class (or class "a-zA-Z0-9$_."))
f32b3b91 6537 (save-excursion
52a244eb 6538 (buffer-substring
f32b3b91
CD
6539 (progn (skip-chars-backward class) (point))
6540 (progn (skip-chars-forward class) (point)))))
6541
f32b3b91
CD
6542(defun idlwave-what-function (&optional bound)
6543 ;; Find out if point is within the argument list of a function.
76959b77
S
6544 ;; The return value is ("function-name" class arrow-start (point) level).
6545 ;; Level is 1 on the top level parentheses, higher further down.
f32b3b91
CD
6546
6547 ;; If the optional BOUND is an integer, bound backwards directed
6548 ;; searches to this point.
6549
6550 (catch 'exit
4b1aaa8b 6551 (let (pos
f32b3b91 6552 func-point
f32b3b91
CD
6553 (cnt 0)
6554 func arrow-start class)
15e42531
CD
6555 (idlwave-with-special-syntax
6556 (save-restriction
6557 (save-excursion
6558 (narrow-to-region (max 1 (or bound 0)) (point-max))
6559 ;; move back out of the current parenthesis
6560 (while (condition-case nil
6561 (progn (up-list -1) t)
6562 (error nil))
6563 (setq pos (point))
6564 (incf cnt)
6565 (when (and (= (following-char) ?\()
4b1aaa8b 6566 (re-search-backward
15e42531
CD
6567 "\\(::\\|\\<\\)\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\="
6568 bound t))
6569 (setq func (match-string 2)
6570 func-point (goto-char (match-beginning 2))
6571 pos func-point)
4b1aaa8b 6572 (if (re-search-backward
15e42531 6573 "->[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\=" bound t)
76959b77 6574 (setq arrow-start (copy-marker (match-beginning 0))
15e42531 6575 class (or (match-string 2) t)))
4b1aaa8b
PE
6576 (throw
6577 'exit
15e42531
CD
6578 (list
6579 (idlwave-sintern-routine-or-method func class)
6580 (idlwave-sintern-class class)
6581 arrow-start func-point cnt)))
6582 (goto-char pos))
6583 (throw 'exit nil)))))))
f32b3b91
CD
6584
6585(defun idlwave-what-procedure (&optional bound)
6586 ;; Find out if point is within the argument list of a procedure.
6587 ;; The return value is ("procedure-name" class arrow-pos (point)).
6588
6589 ;; If the optional BOUND is an integer, bound backwards directed
6590 ;; searches to this point.
6591 (let ((pos (point)) pro-point
6592 pro class arrow-start string)
4b1aaa8b 6593 (save-excursion
05a1abfc 6594 ;;(idlwave-beginning-of-statement)
15e42531 6595 (idlwave-start-of-substatement 'pre)
f32b3b91 6596 (setq string (buffer-substring (point) pos))
4b1aaa8b 6597 (if (string-match
76959b77
S
6598 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\(,\\|\\'\\)" string)
6599 (setq pro (match-string 1 string)
6600 pro-point (+ (point) (match-beginning 1)))
f32b3b91
CD
6601 (if (and (idlwave-skip-object)
6602 (setq string (buffer-substring (point) pos))
4b1aaa8b
PE
6603 (string-match
6604 "\\`[ \t]*\\(->\\)[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\([a-zA-Z][a-zA-Z0-9$_]*\\)?[ \t]*\\(,\\|\\(\\$\\s *\\(;.*\\)?\\)?$\\)"
52a244eb 6605 string))
f32b3b91
CD
6606 (setq pro (if (match-beginning 4)
6607 (match-string 4 string))
6608 pro-point (if (match-beginning 4)
6609 (+ (point) (match-beginning 4))
6610 pos)
76959b77 6611 arrow-start (copy-marker (+ (point) (match-beginning 1)))
f32b3b91
CD
6612 class (or (match-string 3 string) t)))))
6613 (list (idlwave-sintern-routine-or-method pro class)
6614 (idlwave-sintern-class class)
6615 arrow-start
6616 pro-point)))
6617
6618(defun idlwave-skip-object ()
6619 ;; If there is an object at point, move over it and return t.
6620 (let ((pos (point)))
6621 (if (catch 'exit
6622 (save-excursion
6623 (skip-chars-forward " ") ; white space
6624 (skip-chars-forward "*") ; de-reference
6625 (cond
6626 ((looking-at idlwave-identifier)
6627 (goto-char (match-end 0)))
6628 ((eq (following-char) ?\()
6629 nil)
6630 (t (throw 'exit nil)))
6631 (catch 'endwhile
6632 (while t
6633 (cond ((eq (following-char) ?.)
6634 (forward-char 1)
6635 (if (not (looking-at idlwave-identifier))
6636 (throw 'exit nil))
6637 (goto-char (match-end 0)))
6638 ((memq (following-char) '(?\( ?\[))
6639 (condition-case nil
6640 (forward-list 1)
6641 (error (throw 'exit nil))))
6642 (t (throw 'endwhile t)))))
6643 (if (looking-at "[ \t]*->")
6644 (throw 'exit (setq pos (match-beginning 0)))
6645 (throw 'exit nil))))
6646 (goto-char pos)
6647 nil)))
4b1aaa8b 6648
f32b3b91
CD
6649(defun idlwave-last-valid-char ()
6650 "Return the last character before point which is not white or a comment
6651and also not part of the current identifier. Since we do this in
6652order to identify places where keywords are, we consider the initial
6653`/' of a keyword as part of the identifier.
6654This function is not general, can only be used for completion stuff."
6655 (catch 'exit
6656 (save-excursion
6657 ;; skip the current identifier
6658 (skip-chars-backward "a-zA-Z0-9_$")
6659 ;; also skip a leading slash which might be belong to the keyword
6660 (if (eq (preceding-char) ?/)
6661 (backward-char 1))
6662 ;; FIXME: does not check if this is a valid identifier
6663 (while t
6664 (skip-chars-backward " \t")
6665 (cond
6666 ((memq (preceding-char) '(?\; ?\$)) (throw 'exit nil))
6667 ((eq (preceding-char) ?\n)
6668 (beginning-of-line 0)
3938cb82 6669 (if (looking-at "\\([^\n]*\\)\\$[ \t]*\\(;[^\n]*\\)?\n")
f32b3b91
CD
6670 ;; continuation line
6671 (goto-char (match-end 1))
6672 (throw 'exit nil)))
6673 (t (throw 'exit (preceding-char))))))))
6674
6675(defvar idlwave-complete-after-success-form nil
6676 "A form to evaluate after successful completion.")
6677(defvar idlwave-complete-after-success-form-force nil
6678 "A form to evaluate after completion selection in *Completions* buffer.")
6679(defconst idlwave-completion-mark (make-marker)
6680 "A mark pointing to the beginning of the completion string.")
8d222148 6681(defvar completion-highlight-first-word-only) ;XEmacs.
f32b3b91
CD
6682
6683(defun idlwave-complete-in-buffer (type stype list selector prompt isa
52a244eb
S
6684 &optional prepare-display-function
6685 special-selector)
f32b3b91 6686 "Perform TYPE completion of word before point against LIST.
76959b77 6687SELECTOR is the PREDICATE argument for the completion function. Show
52a244eb 6688PROMPT in echo area. TYPE is one of the intern types, e.g. 'function,
5a0c3f56 6689'procedure, 'class-tag, 'keyword, 'sysvar, etc. SPECIAL-SELECTOR is
52a244eb
S
6690used only once, for `all-completions', and can be used to, e.g.,
6691accumulate information on matching completions."
f32b3b91
CD
6692 (let* ((completion-ignore-case t)
6693 beg (end (point)) slash part spart completion all-completions
6694 dpart dcompletion)
6695
6696 (unless list
6697 (error (concat prompt ": No completions available")))
6698
6699 ;; What is already in the buffer?
6700 (save-excursion
6701 (skip-chars-backward "a-zA-Z0-9_$")
6702 (setq slash (eq (preceding-char) ?/)
6703 beg (point)
6704 idlwave-complete-after-success-form
6705 (list 'idlwave-after-successful-completion
6706 (list 'quote type) slash beg)
6707 idlwave-complete-after-success-form-force
6708 (list 'idlwave-after-successful-completion
6709 (list 'quote type) slash (list 'quote 'force))))
6710
6711 ;; Try a completion
6712 (setq part (buffer-substring beg end)
6713 dpart (downcase part)
6714 spart (idlwave-sintern stype part)
6715 completion (try-completion part list selector)
52a244eb
S
6716 dcompletion (if (stringp completion) (downcase completion))
6717 idlwave-completion-help-links nil)
f32b3b91
CD
6718 (cond
6719 ((null completion)
6720 ;; nothing available.
76959b77 6721 (error (concat prompt ": no completion for \"%s\"") part))
f32b3b91
CD
6722 ((and (not (equal dpart dcompletion))
6723 (not (eq t completion)))
6724 ;; We can add something
6725 (delete-region beg end)
8d222148
SM
6726 (insert (if (and (string= part dpart)
6727 (or (not (string= part ""))
6728 idlwave-complete-empty-string-as-lower-case)
6729 (not idlwave-completion-force-default-case))
6730 dcompletion
6731 completion))
f32b3b91
CD
6732 (if (eq t (try-completion completion list selector))
6733 ;; Now this is a unique match
6734 (idlwave-after-successful-completion type slash beg))
6735 t)
6736 ((or (eq completion t)
52a244eb 6737 (and (= 1 (length (setq all-completions
f32b3b91 6738 (idlwave-uniquify
4b1aaa8b
PE
6739 (all-completions part list
6740 (or special-selector
52a244eb
S
6741 selector))))))
6742 (equal dpart dcompletion)))
f32b3b91
CD
6743 ;; This is already complete
6744 (idlwave-after-successful-completion type slash beg)
6745 (message "%s is already the complete %s" part isa)
6746 nil)
4b1aaa8b 6747 (t
f32b3b91
CD
6748 ;; We cannot add something - offer a list.
6749 (message "Making completion list...")
4b1aaa8b 6750
52a244eb 6751 (unless idlwave-completion-help-links ; already set somewhere?
9001c33f
GM
6752 (mapc (lambda (x) ; Pass link prop through to highlight-linked
6753 (let ((link (get-text-property 0 'link (car x))))
6754 (if link
6755 (push (cons (car x) link)
6756 idlwave-completion-help-links))))
6757 list))
f32b3b91 6758 (let* ((list all-completions)
05a1abfc 6759 ;; "complete" means, this is already a valid completion
f32b3b91 6760 (complete (memq spart all-completions))
52a244eb 6761 (completion-highlight-first-word-only t)) ; XEmacs
8d222148
SM
6762 ;; (completion-fixup-function ; Emacs
6763 ;; (lambda () (and (eq (preceding-char) ?>)
6764 ;; (re-search-backward " <" beg t)))))
4b1aaa8b 6765
f32b3b91
CD
6766 (setq list (sort list (lambda (a b)
6767 (string< (downcase a) (downcase b)))))
6768 (if prepare-display-function
6769 (setq list (funcall prepare-display-function list)))
6770 (if (and (string= part dpart)
6771 (or (not (string= part ""))
6772 idlwave-complete-empty-string-as-lower-case)
6773 (not idlwave-completion-force-default-case))
6774 (setq list (mapcar (lambda (x)
4b1aaa8b 6775 (if (listp x)
f32b3b91
CD
6776 (setcar x (downcase (car x)))
6777 (setq x (downcase x)))
6778 x)
6779 list)))
6780 (idlwave-display-completion-list list prompt beg complete))
6781 t))))
6782
6783(defun idlwave-complete-class ()
6784 "Complete a class at point."
6785 (interactive)
6786 ;; Call `idlwave-routines' to make sure the class list will be available
6787 (idlwave-routines)
15e42531
CD
6788 ;; Check for the special case of completing empty string after pro/function
6789 (if (let ((case-fold-search t))
6790 (save-excursion
6791 (and
6792 (re-search-backward "\\<\\(pro\\|function\\)[ \t]+\\="
6793 (- (point) 15) t)
6794 (goto-char (point-min))
4b1aaa8b 6795 (re-search-forward
15e42531
CD
6796 "^[ \t]*\\(pro\\|function\\)[ \t]+\\([a-zA-Z0-9_]+::\\)" nil t))))
6797 ;; Yank the full class specification
6798 (insert (match-string 2))
52a244eb 6799 ;; Do the completion, using list gathered from `idlwave-routines'
4b1aaa8b
PE
6800 (idlwave-complete-in-buffer
6801 'class 'class (idlwave-class-alist) nil
52a244eb 6802 "Select a class" "class"
8d222148
SM
6803 (lambda (list) ;; Push it to help-links if system help available
6804 (mapcar (lambda (x)
6805 (let* ((entry (idlwave-class-info x))
6806 (link (nth 1 (assq 'link entry))))
6807 (if link (push (cons x link)
6808 idlwave-completion-help-links))
6809 x))
6810 list)))))
f32b3b91 6811
76959b77 6812(defun idlwave-attach-classes (list type show-classes)
05a1abfc 6813 ;; Attach the proper class list to a LIST of completion items.
76959b77
S
6814 ;; TYPE, when 'kwd, shows classes for method keywords, when
6815 ;; 'class-tag, for class tags, and otherwise for methods.
f32b3b91 6816 ;; SHOW-CLASSES is the value of `idlwave-completion-show-classes'.
76959b77
S
6817 (if (or (null show-classes) ; don't want to see classes
6818 (null class-selector) ; not a method call
4b1aaa8b 6819 (and
76959b77
S
6820 (stringp class-selector) ; the class is already known
6821 (not super-classes))) ; no possibilities for inheritance
6822 ;; In these cases, we do not have to do anything
6823 list
05a1abfc
CD
6824 (let* ((do-prop (and (>= show-classes 0)
6825 (>= emacs-major-version 21)))
f32b3b91 6826 (do-buf (not (= show-classes 0)))
76959b77 6827 ;; (do-dots (featurep 'xemacs))
05a1abfc 6828 (do-dots t)
76959b77 6829 (inherit (if (and (not (eq type 'class-tag)) super-classes)
05a1abfc 6830 (cons class-selector super-classes)))
f32b3b91
CD
6831 (max (abs show-classes))
6832 (lmax (if do-dots (apply 'max (mapcar 'length list))))
6833 classes nclasses class-info space)
4b1aaa8b 6834 (mapcar
f32b3b91
CD
6835 (lambda (x)
6836 ;; get the classes
76959b77
S
6837 (if (eq type 'class-tag)
6838 ;; Just one class for tags
6839 (setq classes
4b1aaa8b 6840 (list
76959b77 6841 (idlwave-class-or-superclass-with-tag class-selector x)))
52a244eb 6842 ;; Multiple classes for method or method-keyword
76959b77
S
6843 (setq classes
6844 (if (eq type 'kwd)
6845 (idlwave-all-method-keyword-classes
6846 method-selector x type-selector)
6847 (idlwave-all-method-classes x type-selector)))
6848 (if inherit
4b1aaa8b 6849 (setq classes
76959b77
S
6850 (delq nil
6851 (mapcar (lambda (x) (if (memq x inherit) x nil))
6852 classes)))))
f32b3b91
CD
6853 (setq nclasses (length classes))
6854 ;; Make the separator between item and class-info
6855 (if do-dots
6856 (setq space (concat " " (make-string (- lmax (length x)) ?.)))
6857 (setq space " "))
6858 (if do-buf
6859 ;; We do want info in the buffer
6860 (if (<= nclasses max)
6861 (setq class-info (concat
6862 space
6863 "<" (mapconcat 'identity classes ",") ">"))
6864 (setq class-info (format "%s<%d classes>" space nclasses)))
6865 (setq class-info nil))
6866 (when do-prop
6867 ;; We do want properties
6868 (setq x (copy-sequence x))
6869 (put-text-property 0 (length x)
52a244eb
S
6870 'help-echo (mapconcat 'identity classes " ")
6871 x))
f32b3b91
CD
6872 (if class-info
6873 (list x class-info)
6874 x))
6875 list))))
6876
6877(defun idlwave-attach-method-classes (list)
6878 ;; Call idlwave-attach-classes with method parameters
76959b77 6879 (idlwave-attach-classes list 'method idlwave-completion-show-classes))
f32b3b91
CD
6880(defun idlwave-attach-keyword-classes (list)
6881 ;; Call idlwave-attach-classes with keyword parameters
76959b77
S
6882 (idlwave-attach-classes list 'kwd idlwave-completion-show-classes))
6883(defun idlwave-attach-class-tag-classes (list)
6884 ;; Call idlwave-attach-classes with class structure tags
6885 (idlwave-attach-classes list 'class-tag idlwave-completion-show-classes))
4b1aaa8b 6886
f32b3b91
CD
6887
6888;;----------------------------------------------------------------------
6889;;----------------------------------------------------------------------
6890;;----------------------------------------------------------------------
6891;;----------------------------------------------------------------------
6892;;----------------------------------------------------------------------
0b03a950
GM
6893(when (featurep 'xemacs)
6894 (defvar rtn)
6895 (defun idlwave-pset (item)
6896 (set 'rtn item)))
5e72c6b2
S
6897
6898(defun idlwave-popup-select (ev list title &optional sort)
6899 "Select an item in LIST with a popup menu.
6900TITLE is the title to put atop the popup. If SORT is non-nil,
5a0c3f56 6901sort the list before displaying."
5e72c6b2 6902 (let ((maxpopup idlwave-max-popup-menu-items)
8d222148 6903 rtn menu)
5e72c6b2
S
6904 (cond ((null list))
6905 ((= 1 (length list))
6906 (setq rtn (car list)))
6907 ((featurep 'xemacs)
4b1aaa8b 6908 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6909 (string< (upcase a) (upcase b))))))
6910 (setq menu
6911 (append (list title)
6912 (mapcar (lambda (x) (vector x (list 'idlwave-pset
6913 x)))
6914 list)))
6915 (setq menu (idlwave-split-menu-xemacs menu maxpopup))
8d222148
SM
6916 (let ((resp (get-popup-menu-response menu)))
6917 (funcall (event-function resp) (event-object resp))))
5e72c6b2 6918 (t
4b1aaa8b 6919 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6920 (string< (upcase a) (upcase b))))))
6921 (setq menu (cons title
6922 (list
6923 (append (list "")
6924 (mapcar (lambda(x) (cons x x)) list)))))
6925 (setq menu (idlwave-split-menu-emacs menu maxpopup))
6926 (setq rtn (x-popup-menu ev menu))))
6927 rtn))
6928
6929(defun idlwave-split-menu-xemacs (menu N)
6930 "Split the MENU into submenus of maximum length N."
6931 (if (<= (length menu) (1+ N))
6932 ;; No splitting needed
6933 menu
6934 (let* ((title (car menu))
6935 (entries (cdr menu))
6936 (menu (list title))
6937 (cnt 0)
6938 (nextmenu nil))
6939 (while entries
6940 (while (and entries (< cnt N))
6941 (setq cnt (1+ cnt)
6942 nextmenu (cons (car entries) nextmenu)
6943 entries (cdr entries)))
6944 (setq nextmenu (nreverse nextmenu))
6945 (setq nextmenu (cons (format "%s...%s"
6946 (aref (car nextmenu) 0)
6947 (aref (nth (1- cnt) nextmenu) 0))
6948 nextmenu))
6949 (setq menu (cons nextmenu menu)
6950 nextmenu nil
6951 cnt 0))
6952 (nreverse menu))))
6953
6954(defun idlwave-split-menu-emacs (menu N)
6955 "Split the MENU into submenus of maximum length N."
6956 (if (<= (length (nth 1 menu)) (1+ N))
6957 ;; No splitting needed
6958 menu
6959 (let* ((title (car menu))
6960 (entries (cdr (nth 1 menu)))
6961 (menu nil)
6962 (cnt 0)
6963 (nextmenu nil))
6964 (while entries
6965 (while (and entries (< cnt N))
6966 (setq cnt (1+ cnt)
6967 nextmenu (cons (car entries) nextmenu)
6968 entries (cdr entries)))
6969 (setq nextmenu (nreverse nextmenu))
6970 (prin1 nextmenu)
6971 (setq nextmenu (cons (format "%s...%s"
6972 (car (car nextmenu))
6973 (car (nth (1- cnt) nextmenu)))
6974 nextmenu))
6975 (setq menu (cons nextmenu menu)
6976 nextmenu nil
6977 cnt 0))
6978 (setq menu (nreverse menu))
6979 (setq menu (cons title menu))
6980 menu)))
f32b3b91 6981
15e42531
CD
6982(defvar idlwave-completion-setup-hook nil)
6983
f32b3b91
CD
6984(defun idlwave-scroll-completions (&optional message)
6985 "Scroll the completion window on this frame."
6986 (let ((cwin (get-buffer-window "*Completions*" 'visible))
6987 (win (selected-window)))
6988 (unwind-protect
6989 (progn
6990 (select-window cwin)
6991 (condition-case nil
6992 (scroll-up)
6993 (error (if (and (listp last-command)
6994 (nth 2 last-command))
6995 (progn
6996 (select-window win)
6997 (eval idlwave-complete-after-success-form))
6998 (set-window-start cwin (point-min)))))
274f1353 6999 (and message (message "%s" message)))
f32b3b91
CD
7000 (select-window win))))
7001
7002(defun idlwave-display-completion-list (list &optional message beg complete)
7003 "Display the completions in LIST in the completions buffer and echo MESSAGE."
7004 (unless (and (get-buffer-window "*Completions*")
7005 (idlwave-local-value 'idlwave-completion-p "*Completions*"))
7006 (move-marker idlwave-completion-mark beg)
7007 (setq idlwave-before-completion-wconf (current-window-configuration)))
7008
7009 (if (featurep 'xemacs)
4b1aaa8b 7010 (idlwave-display-completion-list-xemacs
15e42531 7011 list)
f32b3b91
CD
7012 (idlwave-display-completion-list-emacs list))
7013
7014 ;; Store a special value in `this-command'. When `idlwave-complete'
7015 ;; finds this in `last-command', it will scroll the *Completions* buffer.
7016 (setq this-command (list 'idlwave-display-completion-list message complete))
7017
7018 ;; Mark the completions buffer as created by cib
7019 (idlwave-set-local 'idlwave-completion-p t "*Completions*")
7020
7021 ;; Fontify the classes
7022 (if (and idlwave-completion-fontify-classes
7023 (consp (car list)))
7024 (idlwave-completion-fontify-classes))
7025
15e42531
CD
7026 ;; Run the hook
7027 (run-hooks 'idlwave-completion-setup-hook)
7028
f32b3b91 7029 ;; Display the message
274f1353 7030 (message "%s" (or message "Making completion list...done")))
f32b3b91
CD
7031
7032(defun idlwave-choose (function &rest args)
7033 "Call FUNCTION as a completion chooser and pass ARGS to it."
7034 (let ((completion-ignore-case t)) ; install correct value
7035 (apply function args))
15e42531
CD
7036 (if (and (eq major-mode 'idlwave-shell-mode)
7037 (boundp 'font-lock-mode)
7038 (not font-lock-mode))
52a244eb 7039 ;; For the shell, remove the fontification of the word before point
15e42531
CD
7040 (let ((beg (save-excursion
7041 (skip-chars-backward "a-zA-Z0-9_")
7042 (point))))
7043 (remove-text-properties beg (point) '(face nil))))
f32b3b91
CD
7044 (eval idlwave-complete-after-success-form-force))
7045
76959b77
S
7046(defun idlwave-keyboard-quit ()
7047 (interactive)
7048 (unwind-protect
7049 (if (eq (car-safe last-command) 'idlwave-display-completion-list)
7050 (idlwave-restore-wconf-after-completion))
7051 (keyboard-quit)))
7052
f32b3b91
CD
7053(defun idlwave-restore-wconf-after-completion ()
7054 "Restore the old (before completion) window configuration."
7055 (and idlwave-completion-restore-window-configuration
7056 idlwave-before-completion-wconf
7057 (set-window-configuration idlwave-before-completion-wconf)))
7058
52a244eb
S
7059(defun idlwave-one-key-select (sym prompt delay)
7060 "Make the user select an element from the alist in the variable SYM.
7061The keys of the alist are expected to be strings. The function returns the
7062car of the selected association.
d9271f41 7063To do this, PROMPT is displayed and the user must hit a letter key to
52a244eb
S
7064select an entry. If the user does not reply within DELAY seconds, a help
7065window with the options is displayed automatically.
7066The key which is associated with each option is generated automatically.
7067First, the strings are checked for preselected keys, like in \"[P]rint\".
7068If these don't exist, a letter in the string is automatically selected."
7069 (let* ((alist (symbol-value sym))
7070 (temp-buffer-show-hook (if (fboundp 'fit-window-to-buffer)
7071 '(fit-window-to-buffer)))
7072 keys-alist char)
7073 ;; First check the cache
7074 (if (and (eq (symbol-value sym) (get sym :one-key-alist-last)))
7075 (setq keys-alist (get sym :one-key-alist-cache))
7076 ;; Need to make new list
7077 (setq keys-alist (idlwave-make-one-key-alist alist))
7078 (put sym :one-key-alist-cache keys-alist)
7079 (put sym :one-key-alist-last alist))
7080 ;; Display prompt and wait for quick reply
7081 (message "%s[%s]" prompt
7082 (mapconcat (lambda(x) (char-to-string (car x)))
7083 keys-alist ""))
7084 (if (sit-for delay)
7085 ;; No quick reply: Show help
7086 (save-window-excursion
7087 (with-output-to-temp-buffer "*Completions*"
7088 (mapcar (lambda(x)
7089 (princ (nth 1 x))
7090 (princ "\n"))
4b1aaa8b 7091 keys-alist))
52a244eb
S
7092 (setq char (read-char)))
7093 (setq char (read-char)))
7094 (message nil)
7095 ;; Return the selected result
7096 (nth 2 (assoc char keys-alist))))
7097
7098;; Used for, e.g., electric debug super-examine.
7099(defun idlwave-make-one-key-alist (alist)
7100 "Make an alist for single key selection."
7101 (let ((l alist) keys-alist name start char help
7102 (cnt 0)
7103 (case-fold-search nil))
7104 (while l
7105 (setq name (car (car l))
7106 l (cdr l))
7107 (catch 'exit
7108 ;; First check if the configuration predetermined a key
7109 (if (string-match "\\[\\(.\\)\\]" name)
7110 (progn
7111 (setq char (string-to-char (downcase (match-string 1 name)))
7112 help (format "%c: %s" char name)
7113 keys-alist (cons (list char help name) keys-alist))
7114 (throw 'exit t)))
7115 ;; Then check for capital letters
7116 (setq start 0)
7117 (while (string-match "[A-Z]" name start)
7118 (setq start (match-end 0)
7119 char (string-to-char (downcase (match-string 0 name))))
7120 (if (not (assoc char keys-alist))
7121 (progn
7122 (setq help (format "%c: %s" char
7123 (replace-match
7124 (concat "[" (match-string 0 name) "]")
7125 t t name))
7126 keys-alist (cons (list char help name) keys-alist))
7127 (throw 'exit t))))
7128 ;; Now check for lowercase letters
7129 (setq start 0)
7130 (while (string-match "[a-z]" name start)
7131 (setq start (match-end 0)
7132 char (string-to-char (match-string 0 name)))
7133 (if (not (assoc char keys-alist))
7134 (progn
7135 (setq help (format "%c: %s" char
7136 (replace-match
7137 (concat "[" (match-string 0 name) "]")
7138 t t name))
7139 keys-alist (cons (list char help name) keys-alist))
7140 (throw 'exit t))))
7141 ;; Bummer, nothing found! Use a stupid number
7142 (setq char (string-to-char (int-to-string (setq cnt (1+ cnt))))
7143 help (format "%c: %s" char name)
7144 keys-alist (cons (list char help name) keys-alist))))
7145 (nreverse keys-alist)))
7146
f32b3b91
CD
7147(defun idlwave-set-local (var value &optional buffer)
7148 "Set the buffer-local value of VAR in BUFFER to VALUE."
9a529312 7149 (with-current-buffer (or buffer (current-buffer))
f32b3b91
CD
7150 (set (make-local-variable var) value)))
7151
7152(defun idlwave-local-value (var &optional buffer)
7153 "Return the value of VAR in BUFFER, but only if VAR is local to BUFFER."
9a529312 7154 (with-current-buffer (or buffer (current-buffer))
f32b3b91
CD
7155 (and (local-variable-p var (current-buffer))
7156 (symbol-value var))))
7157
15e42531
CD
7158;; In XEmacs, we can use :activate-callback directly to advice the
7159;; choose functions. We use the private keymap only for the online
7160;; help feature.
f32b3b91 7161
15e42531 7162(defvar idlwave-completion-map nil
5a0c3f56 7163 "Keymap for `completion-list-mode' with `idlwave-complete'.")
15e42531
CD
7164
7165(defun idlwave-display-completion-list-xemacs (list &rest cl-args)
f32b3b91 7166 (with-output-to-temp-buffer "*Completions*"
15e42531
CD
7167 (apply 'display-completion-list list
7168 ':activate-callback 'idlwave-default-choose-completion
7169 cl-args))
9a529312 7170 (with-current-buffer "*Completions*"
15e42531
CD
7171 (use-local-map
7172 (or idlwave-completion-map
7173 (setq idlwave-completion-map
7174 (idlwave-make-modified-completion-map-xemacs
7175 (current-local-map)))))))
f32b3b91
CD
7176
7177(defun idlwave-default-choose-completion (&rest args)
7178 "Execute `default-choose-completion' and then restore the win-conf."
7179 (apply 'idlwave-choose 'default-choose-completion args))
7180
15e42531
CD
7181(defun idlwave-make-modified-completion-map-xemacs (old-map)
7182 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7183 (let ((new-map (copy-keymap old-map)))
7184 (define-key new-map [button3up] 'idlwave-mouse-completion-help)
7185 (define-key new-map [button3] (lambda ()
7186 (interactive)
7187 (setq this-command last-command)))
7188 new-map))
f32b3b91 7189
76959b77 7190;; In Emacs we also replace keybindings in the completion
15e42531 7191;; map in order to install our wrappers.
f32b3b91
CD
7192
7193(defun idlwave-display-completion-list-emacs (list)
7194 "Display completion list and install the choose wrappers."
7195 (with-output-to-temp-buffer "*Completions*"
7196 (display-completion-list list))
9a529312 7197 (with-current-buffer "*Completions*"
f32b3b91
CD
7198 (use-local-map
7199 (or idlwave-completion-map
7200 (setq idlwave-completion-map
15e42531
CD
7201 (idlwave-make-modified-completion-map-emacs
7202 (current-local-map)))))))
7203
7204(defun idlwave-make-modified-completion-map-emacs (old-map)
f32b3b91
CD
7205 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7206 (let ((new-map (copy-keymap old-map)))
4b1aaa8b 7207 (substitute-key-definition
f32b3b91
CD
7208 'choose-completion 'idlwave-choose-completion new-map)
7209 (substitute-key-definition
7210 'mouse-choose-completion 'idlwave-mouse-choose-completion new-map)
15e42531 7211 (define-key new-map [mouse-3] 'idlwave-mouse-completion-help)
f32b3b91
CD
7212 new-map))
7213
7214(defun idlwave-choose-completion (&rest args)
7215 "Choose the completion that point is in or next to."
7216 (interactive)
7217 (apply 'idlwave-choose 'choose-completion args))
7218
7219(defun idlwave-mouse-choose-completion (&rest args)
7220 "Click on an alternative in the `*Completions*' buffer to choose it."
7221 (interactive "e")
7222 (apply 'idlwave-choose 'mouse-choose-completion args))
7223
7224;;----------------------------------------------------------------------
7225;;----------------------------------------------------------------------
7226
05a1abfc 7227;;; ------------------------------------------------------------------------
52a244eb 7228;;; Stucture parsing code, and code to manage class info
05a1abfc
CD
7229
7230;;
7231;; - Go again over the documentation how to write a completion
7232;; plugin. It is in self.el, but currently still very bad.
4b1aaa8b
PE
7233;; This could be in a separate file in the distribution, or
7234;; in an appendix for the manual.
52a244eb
S
7235
7236(defvar idlwave-struct-skip
7237 "[ \t]*\\(\\$.*\n\\(^[ \t]*\\(\\$[ \t]*\\)?\\(;.*\\)?\n\\)*\\)?[ \t]*"
5a0c3f56 7238 "Regexp for skipping continued blank or comment-only lines in structures.")
52a244eb
S
7239
7240(defvar idlwave-struct-tag-regexp
7241 (concat "[{,]" ;leading comma/brace
7242 idlwave-struct-skip ; 4 groups
7243 "\\([a-zA-Z][a-zA-Z0-9_]*\\)" ;the tag itself, group 5
7244 "[ \t]*:") ; the final colon
7245 "Regexp for structure tags.")
05a1abfc
CD
7246
7247(defun idlwave-struct-tags ()
7248 "Return a list of all tags in the structure defined at point.
7249Point is expected just before the opening `{' of the struct definition."
7250 (save-excursion
7251 (let* ((borders (idlwave-struct-borders))
7252 (beg (car borders))
7253 (end (cdr borders))
7254 tags)
7255 (goto-char beg)
52a244eb
S
7256 (save-restriction
7257 (narrow-to-region beg end)
7258 (while (re-search-forward idlwave-struct-tag-regexp end t)
7259 ;; Check if we are still on the top level of the structure.
7260 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7261 (= (point) beg))
7262 (push (match-string-no-properties 5) tags))
7263 (goto-char (match-end 0))))
7264 (nreverse tags))))
05a1abfc 7265
76959b77
S
7266(defun idlwave-find-struct-tag (tag)
7267 "Find a given TAG in the structure defined at point."
7268 (let* ((borders (idlwave-struct-borders))
76959b77
S
7269 (end (cdr borders))
7270 (case-fold-search t))
4b1aaa8b 7271 (re-search-forward (concat "\\(^[ \t]*\\|[,{][ \t]*\\)" tag "[ \t]*:")
76959b77
S
7272 end t)))
7273
05a1abfc
CD
7274(defun idlwave-struct-inherits ()
7275 "Return a list of all `inherits' names in the struct at point.
7276Point is expected just before the opening `{' of the struct definition."
7277 (save-excursion
7278 (let* ((borders (idlwave-struct-borders))
7279 (beg (car borders))
7280 (end (cdr borders))
7281 (case-fold-search t)
7282 names)
7283 (goto-char beg)
52a244eb
S
7284 (save-restriction
7285 (narrow-to-region beg end)
4b1aaa8b 7286 (while (re-search-forward
52a244eb
S
7287 (concat "[{,]" ;leading comma/brace
7288 idlwave-struct-skip ; 4 groups
7289 "inherits" ; The INHERITS tag
7290 idlwave-struct-skip ; 4 more
7291 "\\([a-zA-Z][a-zA-Z0-9_]*\\)") ; The super-group, #9
7292 end t)
7293 ;; Check if we are still on the top level of the structure.
7294 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7295 (= (point) beg))
7296 (push (match-string-no-properties 9) names))
7297 (goto-char (match-end 0))))
05a1abfc
CD
7298 (nreverse names))))
7299
5e72c6b2 7300(defun idlwave-in-structure ()
52a244eb 7301 "Return t if point is inside an IDL structure definition."
5e72c6b2
S
7302 (let ((beg (point)))
7303 (save-excursion
7304 (if (not (or (idlwave-in-comment) (idlwave-in-quote)))
7305 (if (idlwave-find-structure-definition nil nil 'back)
7306 (let ((borders (idlwave-struct-borders)))
7307 (or (= (car borders) (cdr borders)) ;; struct not yet closed...
7308 (and (> beg (car borders)) (< beg (cdr borders))))))))))
05a1abfc
CD
7309
7310(defun idlwave-struct-borders ()
7311 "Return the borders of the {...} after point as a cons cell."
7312 (let (beg)
7313 (save-excursion
7314 (skip-chars-forward "^{")
7315 (setq beg (point))
7316 (condition-case nil (forward-list 1)
7317 (error (goto-char beg)))
7318 (cons beg (point)))))
7319
7320(defun idlwave-find-structure-definition (&optional var name bound)
5a0c3f56
JB
7321 "Search forward for a structure definition.
7322If VAR is non-nil, search for a structure assigned to variable VAR.
7323If NAME is non-nil, search for a named structure NAME, if a string,
7324or a generic named structure otherwise. If BOUND is an integer, limit
7325the search. If BOUND is the symbol `all', we search first back and
7326then forward through the entire file. If BOUND is the symbol `back'
7327we search only backward."
76959b77 7328 (let* ((ws "[ \t]*\\(\\$.*\n[ \t]*\\)*")
05a1abfc
CD
7329 (case-fold-search t)
7330 (lim (if (integerp bound) bound nil))
7331 (re (concat
7332 (if var
7333 (concat "\\<" (regexp-quote (downcase var)) "\\>" ws)
7334 "\\(\\)")
7335 "=" ws "\\({\\)"
4b1aaa8b 7336 (if name
52a244eb 7337 (if (stringp name)
4b1aaa8b 7338 (concat ws "\\(\\<" (downcase name) "\\)[^a-zA-Z0-9_$]")
52a244eb
S
7339 ;; Just a generic name
7340 (concat ws "\\<\\([a-zA-Z_0-9$]+\\)" ws ","))
7341 ""))))
5e72c6b2 7342 (if (or (and (or (eq bound 'all) (eq bound 'back))
05a1abfc 7343 (re-search-backward re nil t))
5e72c6b2 7344 (and (not (eq bound 'back)) (re-search-forward re lim t)))
52a244eb
S
7345 (progn
7346 (goto-char (match-beginning 3))
7347 (match-string-no-properties 5)))))
7348
4b1aaa8b 7349(defvar idlwave-class-info nil)
52a244eb 7350(defvar idlwave-class-reset nil) ; to reset buffer-local classes
05a1abfc 7351
05a1abfc 7352(add-hook 'idlwave-update-rinfo-hook
52a244eb 7353 (lambda () (setq idlwave-class-reset t)))
05a1abfc
CD
7354(add-hook 'idlwave-after-load-rinfo-hook
7355 (lambda () (setq idlwave-class-info nil)))
7356
7357(defun idlwave-class-info (class)
7358 (let (list entry)
52a244eb
S
7359 (if idlwave-class-info
7360 (if idlwave-class-reset
4b1aaa8b 7361 (setq
52a244eb
S
7362 idlwave-class-reset nil
7363 idlwave-class-info ; Remove any visited in a buffer
4b1aaa8b
PE
7364 (delq nil (mapcar
7365 (lambda (x)
7366 (let ((filebuf
7367 (idlwave-class-file-or-buffer
52a244eb
S
7368 (or (cdr (assq 'found-in x)) (car x)))))
7369 (if (cdr filebuf)
7370 nil
7371 x)))
7372 idlwave-class-info))))
7373 ;; Info is nil, put in the system stuff to start.
05a1abfc
CD
7374 (setq idlwave-class-info idlwave-system-class-info)
7375 (setq list idlwave-class-info)
7376 (while (setq entry (pop list))
7377 (idlwave-sintern-class-info entry)))
7378 (setq class (idlwave-sintern-class class))
52a244eb
S
7379 (or (assq class idlwave-class-info)
7380 (progn (idlwave-scan-class-info class)
7381 (assq class idlwave-class-info)))))
05a1abfc
CD
7382
7383(defun idlwave-sintern-class-info (entry)
7384 "Sintern the class names in a class-info entry."
8d222148 7385 (let ((inherits (assq 'inherits entry)))
05a1abfc
CD
7386 (setcar entry (idlwave-sintern-class (car entry) 'set))
7387 (if inherits
7388 (setcdr inherits (mapcar (lambda (x) (idlwave-sintern-class x 'set))
7389 (cdr inherits))))))
7390
52a244eb 7391(defun idlwave-find-class-definition (class &optional all-hook alt-class)
5a0c3f56 7392 "Find class structure definition(s).
52a244eb
S
7393If ALL-HOOK is set, find all named structure definitions in a given
7394class__define routine, on which ALL-HOOK will be run. If ALT-CLASS is
7395set, look for the name__define pro, and inside of it, for the ALT-CLASS
5a0c3f56 7396class/struct definition."
8d222148 7397 (let ((case-fold-search t) end-lim name)
52a244eb
S
7398 (when (re-search-forward
7399 (concat "^[ \t]*pro[ \t]+" (downcase class) "__define" "\\>") nil t)
7400 (if all-hook
7401 (progn
7402 ;; For everything there
7403 (setq end-lim (save-excursion (idlwave-end-of-subprogram) (point)))
4b1aaa8b 7404 (while (setq name
52a244eb
S
7405 (idlwave-find-structure-definition nil t end-lim))
7406 (funcall all-hook name)))
7407 (idlwave-find-structure-definition nil (or alt-class class))))))
76959b77 7408
52a244eb
S
7409
7410(defun idlwave-class-file-or-buffer (class)
5a0c3f56 7411 "Find buffer visiting CLASS definition."
05a1abfc 7412 (let* ((pro (concat (downcase class) "__define"))
52a244eb
S
7413 (file (idlwave-routine-source-file
7414 (nth 3 (idlwave-rinfo-assoc pro 'pro nil
7415 (idlwave-routines))))))
7416 (cons file (if file (idlwave-get-buffer-visiting file)))))
7417
7418
7419(defun idlwave-scan-class-info (class)
5a0c3f56 7420 "Scan all class and named structure info in the class__define pro."
52a244eb
S
7421 (let* ((idlwave-auto-routine-info-updates nil)
7422 (filebuf (idlwave-class-file-or-buffer class))
7423 (file (car filebuf))
7424 (buf (cdr filebuf))
7425 (class (idlwave-sintern-class class)))
7426 (if (or
7427 (not file)
7428 (and ;; neither a regular file nor a visited buffer
7429 (not buf)
7430 (not (file-regular-p file))))
7431 nil ; Cannot find the file/buffer to get any info
05a1abfc 7432 (save-excursion
52a244eb
S
7433 (if buf (set-buffer buf)
7434 ;; Read the file in temporarily
05a1abfc
CD
7435 (set-buffer (get-buffer-create " *IDLWAVE-tmp*"))
7436 (erase-buffer)
7437 (unless (eq major-mode 'idlwave-mode)
7438 (idlwave-mode))
7439 (insert-file-contents file))
7440 (save-excursion
7441 (goto-char 1)
4b1aaa8b 7442 (idlwave-find-class-definition class
52a244eb
S
7443 ;; Scan all of the structures found there
7444 (lambda (name)
7445 (let* ((this-class (idlwave-sintern-class name))
4b1aaa8b 7446 (entry
52a244eb
S
7447 (list this-class
7448 (cons 'tags (idlwave-struct-tags))
7449 (cons 'inherits (idlwave-struct-inherits)))))
7450 (if (not (eq this-class class))
7451 (setq entry (nconc entry (list (cons 'found-in class)))))
7452 (idlwave-sintern-class-info entry)
7453 (push entry idlwave-class-info)))))))))
7454
7455(defun idlwave-class-found-in (class)
5a0c3f56 7456 "Return the FOUND-IN property of the CLASS."
52a244eb 7457 (cdr (assq 'found-in (idlwave-class-info class))))
05a1abfc
CD
7458(defun idlwave-class-tags (class)
7459 "Return the native tags in CLASS."
7460 (cdr (assq 'tags (idlwave-class-info class))))
7461(defun idlwave-class-inherits (class)
7462 "Return the direct superclasses of CLASS."
7463 (cdr (assq 'inherits (idlwave-class-info class))))
7464
52a244eb 7465
05a1abfc
CD
7466(defun idlwave-all-class-tags (class)
7467 "Return a list of native and inherited tags in CLASS."
76959b77
S
7468 (condition-case err
7469 (apply 'append (mapcar 'idlwave-class-tags
7470 (cons class (idlwave-all-class-inherits class))))
4b1aaa8b 7471 (error
76959b77
S
7472 (idlwave-class-tag-reset)
7473 (error "%s" (error-message-string err)))))
7474
05a1abfc
CD
7475
7476(defun idlwave-all-class-inherits (class)
7477 "Return a list of all superclasses of CLASS (recursively expanded).
5e72c6b2 7478The list is cached in `idlwave-class-info' for faster access."
05a1abfc
CD
7479 (cond
7480 ((not idlwave-support-inheritance) nil)
7481 ((eq class nil) nil)
7482 ((eq class t) nil)
7483 (t
7484 (let ((info (idlwave-class-info class))
7485 entry)
7486 (if (setq entry (assq 'all-inherits info))
7487 (cdr entry)
76959b77
S
7488 ;; Save the depth of inheritance scan to check for circular references
7489 (let ((inherits (mapcar (lambda (x) (cons x 0))
7490 (idlwave-class-inherits class)))
05a1abfc
CD
7491 rtn all-inherits cl)
7492 (while inherits
7493 (setq cl (pop inherits)
76959b77
S
7494 rtn (cons (car cl) rtn)
7495 inherits (append (mapcar (lambda (x)
7496 (cons x (1+ (cdr cl))))
7497 (idlwave-class-inherits (car cl)))
7498 inherits))
7499 (if (> (cdr cl) 999)
7500 (error
7501 "Class scan: inheritance depth exceeded. Circular inheritance?")
7502 ))
05a1abfc
CD
7503 (setq all-inherits (nreverse rtn))
7504 (nconc info (list (cons 'all-inherits all-inherits)))
7505 all-inherits))))))
7506
52a244eb 7507(defun idlwave-entry-keywords (entry &optional record-link)
4b1aaa8b 7508 "Return the flat entry keywords alist from routine-info entry.
52a244eb
S
7509If RECORD-LINK is non-nil, the keyword text is copied and a text
7510property indicating the link is added."
7511 (let (kwds)
8ffcfb27 7512 (mapc
4b1aaa8b 7513 (lambda (key-list)
52a244eb
S
7514 (let ((file (car key-list)))
7515 (mapcar (lambda (key-cons)
7516 (let ((key (car key-cons))
7517 (link (cdr key-cons)))
7518 (when (and record-link file)
7519 (setq key (copy-sequence key))
4b1aaa8b 7520 (put-text-property
52a244eb 7521 0 (length key)
4b1aaa8b
PE
7522 'link
7523 (concat
7524 file
7525 (if link
52a244eb
S
7526 (concat idlwave-html-link-sep
7527 (number-to-string link))))
7528 key))
7529 (push (list key) kwds)))
7530 (cdr key-list))))
7531 (nthcdr 5 entry))
7532 (nreverse kwds)))
7533
7534(defun idlwave-entry-find-keyword (entry keyword)
5a0c3f56 7535 "Find keyword KEYWORD in entry ENTRY, and return (with link) if set."
52a244eb
S
7536 (catch 'exit
7537 (mapc
4b1aaa8b 7538 (lambda (key-list)
52a244eb
S
7539 (let ((file (car key-list))
7540 (kwd (assoc keyword (cdr key-list))))
7541 (when kwd
4b1aaa8b 7542 (setq kwd (cons (car kwd)
52a244eb 7543 (if (and file (cdr kwd))
4b1aaa8b 7544 (concat file
52a244eb
S
7545 idlwave-html-link-sep
7546 (number-to-string (cdr kwd)))
7547 (cdr kwd))))
7548 (throw 'exit kwd))))
7549 (nthcdr 5 entry))))
05a1abfc
CD
7550
7551;;==========================================================================
7552;;
7553;; Completing class structure tags. This is a completion plugin.
7554;; The necessary taglist is constructed dynamically
7555
7556(defvar idlwave-current-tags-class nil)
7557(defvar idlwave-current-class-tags nil)
7558(defvar idlwave-current-native-class-tags nil)
76959b77 7559(defvar idlwave-sint-class-tags nil)
1a717047 7560(declare-function idlwave-sintern-class-tag "idlwave" t t)
76959b77 7561(idlwave-new-sintern-type 'class-tag)
05a1abfc 7562(add-to-list 'idlwave-complete-special 'idlwave-complete-class-structure-tag)
76959b77 7563(add-hook 'idlwave-update-rinfo-hook 'idlwave-class-tag-reset)
05a1abfc
CD
7564
7565(defun idlwave-complete-class-structure-tag ()
7566 "Complete a structure tag on a `self' argument in an object method."
7567 (interactive)
7568 (let ((pos (point))
7569 (case-fold-search t))
7570 (if (save-excursion
7571 ;; Check if the context is right
52a244eb 7572 (skip-chars-backward "a-zA-Z0-9._$")
05a1abfc
CD
7573 (and (< (point) (- pos 4))
7574 (looking-at "self\\.")))
76959b77
S
7575 (let* ((class-selector (nth 2 (idlwave-current-routine)))
7576 (super-classes (idlwave-all-class-inherits class-selector)))
05a1abfc 7577 ;; Check if we are in a class routine
76959b77 7578 (unless class-selector
e8af40ee 7579 (error "Not in a method procedure or function"))
05a1abfc 7580 ;; Check if we need to update the "current" class
76959b77
S
7581 (if (not (equal class-selector idlwave-current-tags-class))
7582 (idlwave-prepare-class-tag-completion class-selector))
4b1aaa8b 7583 (setq idlwave-completion-help-info
76959b77 7584 (list 'idlwave-complete-class-structure-tag-help
4b1aaa8b 7585 (idlwave-sintern-routine
76959b77
S
7586 (concat class-selector "__define"))
7587 nil))
8d222148 7588 ;; FIXME: idlwave-cpl-bold doesn't seem used anywhere.
05a1abfc
CD
7589 (let ((idlwave-cpl-bold idlwave-current-native-class-tags))
7590 (idlwave-complete-in-buffer
4b1aaa8b 7591 'class-tag 'class-tag
05a1abfc 7592 idlwave-current-class-tags nil
76959b77
S
7593 (format "Select a tag of class %s" class-selector)
7594 "class tag"
7595 'idlwave-attach-class-tag-classes))
05a1abfc
CD
7596 t) ; return t to skip other completions
7597 nil)))
7598
76959b77 7599(defun idlwave-class-tag-reset ()
05a1abfc
CD
7600 (setq idlwave-current-tags-class nil))
7601
7602(defun idlwave-prepare-class-tag-completion (class)
7603 "Find and parse the necessary class definitions for class structure tags."
76959b77 7604 (setq idlwave-sint-class-tags nil)
05a1abfc
CD
7605 (setq idlwave-current-tags-class class)
7606 (setq idlwave-current-class-tags
7607 (mapcar (lambda (x)
76959b77 7608 (list (idlwave-sintern-class-tag x 'set)))
05a1abfc
CD
7609 (idlwave-all-class-tags class)))
7610 (setq idlwave-current-native-class-tags
7611 (mapcar 'downcase (idlwave-class-tags class))))
7612
7613;===========================================================================
7614;;
7615;; Completing system variables and their structure fields
52a244eb 7616;; This is also a plugin.
05a1abfc
CD
7617
7618(defvar idlwave-sint-sysvars nil)
7619(defvar idlwave-sint-sysvartags nil)
1a717047
GM
7620(declare-function idlwave-sintern-sysvar "idlwave" t t)
7621(declare-function idlwave-sintern-sysvartag "idlwave" t t)
05a1abfc
CD
7622(idlwave-new-sintern-type 'sysvar)
7623(idlwave-new-sintern-type 'sysvartag)
7624(add-to-list 'idlwave-complete-special 'idlwave-complete-sysvar-or-tag)
7625(add-hook 'idlwave-update-rinfo-hook 'idlwave-sysvars-reset)
05a1abfc
CD
7626(add-hook 'idlwave-after-load-rinfo-hook 'idlwave-sintern-sysvar-alist)
7627
05a1abfc
CD
7628
7629(defun idlwave-complete-sysvar-or-tag ()
7630 "Complete a system variable."
7631 (interactive)
7632 (let ((pos (point))
7633 (case-fold-search t))
7634 (cond ((save-excursion
7635 ;; Check if the context is right for system variable
7636 (skip-chars-backward "[a-zA-Z0-9_$]")
7637 (equal (char-before) ?!))
7638 (setq idlwave-completion-help-info '(idlwave-complete-sysvar-help))
4b1aaa8b 7639 (idlwave-complete-in-buffer 'sysvar 'sysvar
05a1abfc
CD
7640 idlwave-system-variables-alist nil
7641 "Select a system variable"
7642 "system variable")
7643 t) ; return t to skip other completions
7644 ((save-excursion
7645 ;; Check if the context is right for sysvar tag
52a244eb 7646 (skip-chars-backward "a-zA-Z0-9_$.")
05a1abfc
CD
7647 (and (equal (char-before) ?!)
7648 (looking-at "\\([a-zA-Z][a-zA-Z0-9_$]*\\)\\.")
7649 (<= (match-end 0) pos)))
7650 ;; Complete a system variable tag
7651 (let* ((var (idlwave-sintern-sysvar (match-string 1)))
7652 (entry (assq var idlwave-system-variables-alist))
52a244eb
S
7653 (tags (cdr (assq 'tags entry))))
7654 (or entry (error "!%s is not a known system variable" var))
05a1abfc
CD
7655 (or tags (error "System variable !%s is not a structure" var))
7656 (setq idlwave-completion-help-info
52a244eb 7657 (list 'idlwave-complete-sysvar-tag-help var))
4b1aaa8b 7658 (idlwave-complete-in-buffer 'sysvartag 'sysvartag
05a1abfc
CD
7659 tags nil
7660 "Select a system variable tag"
7661 "system variable tag")
7662 t)) ; return t to skip other completions
7663 (t nil))))
7664
f66f03de 7665(defvar link) ;dynamic variables set by help callback
05a1abfc 7666(defun idlwave-complete-sysvar-help (mode word)
52a244eb
S
7667 (let ((word (or (nth 1 idlwave-completion-help-info) word))
7668 (entry (assoc word idlwave-system-variables-alist)))
7669 (cond
7670 ((eq mode 'test)
7671 (and (stringp word) entry (nth 1 (assq 'link entry))))
7672 ((eq mode 'set)
7673 (if entry (setq link (nth 1 (assq 'link entry))))) ;; setting dynamic!!!
7674 (t (error "This should not happen")))))
7675
7676(defun idlwave-complete-sysvar-tag-help (mode word)
7677 (let* ((var (nth 1 idlwave-completion-help-info))
7678 (entry (assoc var idlwave-system-variables-alist))
7679 (tags (cdr (assq 'tags entry)))
7680 (main (nth 1 (assq 'link entry)))
8d222148 7681 target)
52a244eb
S
7682 (cond
7683 ((eq mode 'test) ; we can at least link the main
7684 (and (stringp word) entry main))
7685 ((eq mode 'set)
4b1aaa8b
PE
7686 (if entry
7687 (setq link
e08734e2 7688 (if (setq target (cdr (assoc-string word tags t)))
52a244eb
S
7689 (idlwave-substitute-link-target main target)
7690 main)))) ;; setting dynamic!!!
7691 (t (error "This should not happen")))))
7692
f66f03de 7693(defun idlwave-split-link-target (link)
5a0c3f56 7694 "Split a given LINK into link file and anchor."
f66f03de
S
7695 (if (string-match idlwave-html-link-sep link)
7696 (cons (substring link 0 (match-beginning 0))
7697 (string-to-number (substring link (match-end 0))))))
7698
52a244eb 7699(defun idlwave-substitute-link-target (link target)
5a0c3f56 7700 "Substitute the TARGET anchor for the given LINK."
52a244eb
S
7701 (let (main-base)
7702 (setq main-base (if (string-match "#" link)
7703 (substring link 0 (match-beginning 0))
7704 link))
7705 (if target
7706 (concat main-base idlwave-html-link-sep (number-to-string target))
7707 link)))
76959b77
S
7708
7709;; Fake help in the source buffer for class structure tags.
52a244eb 7710;; KWD AND NAME ARE GLOBAL-VARIABLES HERE.
4b1aaa8b 7711(defvar name)
52a244eb 7712(defvar kwd)
76959b77
S
7713(defvar idlwave-help-do-class-struct-tag nil)
7714(defun idlwave-complete-class-structure-tag-help (mode word)
7715 (cond
7716 ((eq mode 'test) ; nothing gets fontified for class tags
7717 nil)
7718 ((eq mode 'set)
52a244eb 7719 (let (class-with found-in)
4b1aaa8b
PE
7720 (when (setq class-with
7721 (idlwave-class-or-superclass-with-tag
76959b77
S
7722 idlwave-current-tags-class
7723 word))
4b1aaa8b 7724 (if (assq (idlwave-sintern-class class-with)
76959b77 7725 idlwave-system-class-info)
ff689efd 7726 (error "No help available for system class tags"))
52a244eb
S
7727 (if (setq found-in (idlwave-class-found-in class-with))
7728 (setq name (cons (concat found-in "__define") class-with))
7729 (setq name (concat class-with "__define")))))
76959b77
S
7730 (setq kwd word
7731 idlwave-help-do-class-struct-tag t))
7732 (t (error "This should not happen"))))
7733
7734(defun idlwave-class-or-superclass-with-tag (class tag)
7735 "Find and return the CLASS or one of its superclass with the
7736associated TAG, if any."
e08734e2 7737 (let ((sclasses (cons class (idlwave-all-class-inherits class)))
76959b77
S
7738 cl)
7739 (catch 'exit
7740 (while sclasses
7741 (setq cl (pop sclasses))
7742 (let ((tags (idlwave-class-tags cl)))
7743 (while tags
7744 (if (eq t (compare-strings tag 0 nil (car tags) 0 nil t))
4b1aaa8b 7745 (throw 'exit cl))
76959b77
S
7746 (setq tags (cdr tags))))))))
7747
05a1abfc
CD
7748
7749(defun idlwave-sysvars-reset ()
7750 (if (and (fboundp 'idlwave-shell-is-running)
52a244eb
S
7751 (idlwave-shell-is-running)
7752 idlwave-idlwave_routine_info-compiled)
05a1abfc
CD
7753 (idlwave-shell-send-command "idlwave_get_sysvars"
7754 'idlwave-process-sysvars 'hide)))
7755
7756(defun idlwave-process-sysvars ()
7757 (idlwave-shell-filter-sysvars)
7758 (setq idlwave-sint-sysvars nil
7759 idlwave-sint-sysvartags nil)
7760 (idlwave-sintern-sysvar-alist))
7761
05a1abfc 7762(defun idlwave-sintern-sysvar-alist ()
52a244eb 7763 (let ((list idlwave-system-variables-alist) entry tags)
05a1abfc
CD
7764 (while (setq entry (pop list))
7765 (setcar entry (idlwave-sintern-sysvar (car entry) 'set))
52a244eb
S
7766 (setq tags (assq 'tags entry))
7767 (if tags
4b1aaa8b
PE
7768 (setcdr tags
7769 (mapcar (lambda (x)
52a244eb
S
7770 (cons (idlwave-sintern-sysvartag (car x) 'set)
7771 (cdr x)))
7772 (cdr tags)))))))
05a1abfc
CD
7773
7774(defvar idlwave-shell-command-output)
7775(defun idlwave-shell-filter-sysvars ()
52a244eb 7776 "Get any new system variables and tags."
05a1abfc
CD
7777 (let ((text idlwave-shell-command-output)
7778 (start 0)
7779 (old idlwave-system-variables-alist)
52a244eb 7780 var tags type name class link old-entry)
05a1abfc
CD
7781 (setq idlwave-system-variables-alist nil)
7782 (while (string-match "^IDLWAVE-SYSVAR: !\\([a-zA-Z0-9_$]+\\)\\( \\(.*\\)\\)?"
7783 text start)
7784 (setq start (match-end 0)
7785 var (match-string 1 text)
4b1aaa8b 7786 tags (if (match-end 3)
52a244eb
S
7787 (idlwave-split-string (match-string 3 text))))
7788 ;; Maintain old links, if present
7789 (setq old-entry (assq (idlwave-sintern-sysvar var) old))
7790 (setq link (assq 'link old-entry))
05a1abfc 7791 (setq idlwave-system-variables-alist
4b1aaa8b
PE
7792 (cons (list var
7793 (cons
7794 'tags
7795 (mapcar (lambda (x)
7796 (cons x
7797 (cdr (assq
7798 (idlwave-sintern-sysvartag x)
52a244eb
S
7799 (cdr (assq 'tags old-entry))))))
7800 tags)) link)
05a1abfc
CD
7801 idlwave-system-variables-alist)))
7802 ;; Keep the old value if query was not successful
7803 (setq idlwave-system-variables-alist
7804 (or idlwave-system-variables-alist old))))
7805
f32b3b91
CD
7806(defun idlwave-completion-fontify-classes ()
7807 "Goto the *Completions* buffer and fontify the class info."
7808 (when (featurep 'font-lock)
9a529312 7809 (with-current-buffer "*Completions*"
f32b3b91
CD
7810 (save-excursion
7811 (goto-char (point-min))
76959b77
S
7812 (let ((buffer-read-only nil))
7813 (while (re-search-forward "\\.*<[^>]+>" nil t)
7814 (put-text-property (match-beginning 0) (match-end 0)
7815 'face 'font-lock-string-face)))))))
f32b3b91
CD
7816
7817(defun idlwave-uniquify (list)
52a244eb 7818 (let ((ht (make-hash-table :size (length list) :test 'equal)))
4b1aaa8b 7819 (delq nil
52a244eb 7820 (mapcar (lambda (x)
4b1aaa8b 7821 (unless (gethash x ht)
52a244eb
S
7822 (puthash x t ht)
7823 x))
7824 list))))
f32b3b91
CD
7825
7826(defun idlwave-after-successful-completion (type slash &optional verify)
7827 "Add `=' or `(' after successful completion of keyword and function.
7828Restore the pre-completion window configuration if possible."
7829 (cond
7830 ((eq type 'procedure)
7831 nil)
7832 ((eq type 'function)
7833 (cond
7834 ((equal idlwave-function-completion-adds-paren nil) nil)
7835 ((or (equal idlwave-function-completion-adds-paren t)
7836 (equal idlwave-function-completion-adds-paren 1))
7837 (insert "("))
7838 ((equal idlwave-function-completion-adds-paren 2)
7839 (insert "()")
7840 (backward-char 1))
7841 (t nil)))
7842 ((eq type 'keyword)
7843 (if (and idlwave-keyword-completion-adds-equal
7844 (not slash))
7845 (progn (insert "=") t)
7846 nil)))
7847
7848 ;; Restore the pre-completion window configuration if this is safe.
4b1aaa8b
PE
7849
7850 (if (or (eq verify 'force) ; force
7851 (and
f32b3b91 7852 (get-buffer-window "*Completions*") ; visible
4b1aaa8b 7853 (idlwave-local-value 'idlwave-completion-p
f32b3b91
CD
7854 "*Completions*") ; cib-buffer
7855 (eq (marker-buffer idlwave-completion-mark)
7856 (current-buffer)) ; buffer OK
7857 (equal (marker-position idlwave-completion-mark)
7858 verify))) ; pos OK
7859 (idlwave-restore-wconf-after-completion))
7860 (move-marker idlwave-completion-mark nil)
7861 (setq idlwave-before-completion-wconf nil))
7862
15e42531
CD
7863(defun idlwave-mouse-context-help (ev &optional arg)
7864 "Call `idlwave-context-help' on the clicked location."
7865 (interactive "eP")
7866 (mouse-set-point ev)
7867 (idlwave-context-help arg))
7868
7869(defvar idlwave-last-context-help-pos nil)
7870(defun idlwave-context-help (&optional arg)
7871 "Display IDL Online Help on context.
76959b77
S
7872If point is on a keyword, help for that keyword will be shown. If
7873point is on a routine name or in the argument list of a routine, help
7874for that routine will be displayed. Works for system routines and
7875keywords, it pulls up text help. For other routies and keywords,
7876visits the source file, finding help in the header (if
7877`idlwave-help-source-try-header' is non-nil) or the routine definition
7878itself."
f32b3b91 7879 (interactive "P")
15e42531
CD
7880 (idlwave-do-context-help arg))
7881
7882(defun idlwave-mouse-completion-help (ev)
7883 "Display online help about the completion at point."
7884 (interactive "eP")
52a244eb
S
7885 ;; Restore last-command for next command, to make
7886 ;; scrolling/cancelling of completions work.
15e42531
CD
7887 (setq this-command last-command)
7888 (idlwave-do-mouse-completion-help ev))
15e42531 7889
f32b3b91 7890(defun idlwave-routine-info (&optional arg external)
5a0c3f56
JB
7891 "Display a routines calling sequence and list of keywords.
7892When point is on the name a function or procedure, or in the argument
7893list of a function or procedure, this command displays a help buffer with
52a244eb 7894the information. When called with prefix arg, enforce class query.
f32b3b91
CD
7895
7896When point is on an object operator `->', display the class stored in
5a0c3f56
JB
7897this arrow, if any (see `idlwave-store-inquired-class'). With a prefix
7898arg, the class property is cleared out."
f32b3b91
CD
7899
7900 (interactive "P")
7901 (idlwave-routines)
7902 (if (string-match "->" (buffer-substring
7903 (max (point-min) (1- (point)))
7904 (min (+ 2 (point)) (point-max))))
7905 ;; Cursor is on an arrow
7906 (if (get-text-property (point) 'idlwave-class)
7907 ;; arrow has class property
7908 (if arg
7909 ;; Remove property
7910 (save-excursion
7911 (backward-char 1)
7912 (when (looking-at ".?\\(->\\)")
7913 (remove-text-properties (match-beginning 1) (match-end 1)
7914 '(idlwave-class nil face nil))
7915 (message "Class property removed from arrow")))
7916 ;; Echo class property
7917 (message "Arrow has text property identifying object to be class %s"
7918 (get-text-property (point) 'idlwave-class)))
7919 ;; No property found
7920 (message "Arrow has no class text property"))
7921
7922 ;; Not on an arrow...
7923 (let* ((idlwave-query-class nil)
7924 (idlwave-force-class-query (equal arg '(4)))
7925 (module (idlwave-what-module)))
15e42531 7926 (if (car module)
05a1abfc
CD
7927 (apply 'idlwave-display-calling-sequence
7928 (idlwave-fix-module-if-obj_new module))
e8af40ee 7929 (error "Don't know which calling sequence to show")))))
f32b3b91
CD
7930
7931(defun idlwave-resolve (&optional arg)
52a244eb 7932 "Call RESOLVE_ROUTINE on the module name at point.
f32b3b91
CD
7933Like `idlwave-routine-info', this looks for a routine call at point.
7934After confirmation in the minibuffer, it will use the shell to issue
7935a RESOLVE call for this routine, to attempt to make it defined and its
7936routine info available for IDLWAVE. If the routine is a method call,
7937both `class__method' and `class__define' will be tried.
7938With ARG, enforce query for the class of object methods."
7939 (interactive "P")
7940 (let* ((idlwave-query-class nil)
7941 (idlwave-force-class-query (equal arg '(4)))
7942 (module (idlwave-what-module))
7943 (name (idlwave-make-full-name (nth 2 module) (car module)))
7944 (type (if (eq (nth 1 module) 'pro) "pro" "function"))
7945 (resolve (read-string "Resolve: " (format "%s %s" type name)))
7946 (kwd "")
7947 class)
7948 (if (string-match "\\(pro\\|function\\)[ \t]+\\(\\(.*\\)::\\)?\\(.*\\)"
7949 resolve)
7950 (setq type (match-string 1 resolve)
4b1aaa8b 7951 class (if (match-beginning 2)
f32b3b91
CD
7952 (match-string 3 resolve)
7953 nil)
7954 name (match-string 4 resolve)))
7955 (if (string= (downcase type) "function")
7956 (setq kwd ",/is_function"))
7957
7958 (cond
7959 ((null class)
4b1aaa8b 7960 (idlwave-shell-send-command
f32b3b91
CD
7961 (format "resolve_routine,'%s'%s" (downcase name) kwd)
7962 'idlwave-update-routine-info
7963 nil t))
7964 (t
4b1aaa8b 7965 (idlwave-shell-send-command
f32b3b91 7966 (format "resolve_routine,'%s__define'%s" (downcase class) kwd)
4b1aaa8b
PE
7967 (list 'idlwave-shell-send-command
7968 (format "resolve_routine,'%s__%s'%s"
f32b3b91
CD
7969 (downcase class) (downcase name) kwd)
7970 '(idlwave-update-routine-info)
7971 nil t))))))
7972
3938cb82
S
7973(defun idlwave-find-module-this-file ()
7974 (interactive)
7975 (idlwave-find-module '(4)))
7976
f32b3b91
CD
7977(defun idlwave-find-module (&optional arg)
7978 "Find the source code of an IDL module.
5a0c3f56
JB
7979Works for modules for which IDLWAVE has routine info available.
7980The function offers as default the module name `idlwave-routine-info'
52a244eb
S
7981would use. With ARG limit to this buffer. With two prefix ARG's
7982force class query for object methods."
f32b3b91
CD
7983 (interactive "P")
7984 (let* ((idlwave-query-class nil)
52a244eb
S
7985 (idlwave-force-class-query (equal arg '(16)))
7986 (this-buffer (equal arg '(4)))
05a1abfc 7987 (module (idlwave-fix-module-if-obj_new (idlwave-what-module)))
52a244eb 7988 (default (if module
4b1aaa8b 7989 (concat (idlwave-make-full-name
52a244eb
S
7990 (nth 2 module) (car module))
7991 (if (eq (nth 1 module) 'pro) "<p>" "<f>"))
7992 "none"))
4b1aaa8b 7993 (list
52a244eb
S
7994 (idlwave-uniquify
7995 (delq nil
4b1aaa8b 7996 (mapcar (lambda (x)
52a244eb
S
7997 (if (eq 'system (car-safe (nth 3 x)))
7998 ;; Take out system routines with no source.
7999 nil
8000 (list
4b1aaa8b 8001 (concat (idlwave-make-full-name
52a244eb
S
8002 (nth 2 x) (car x))
8003 (if (eq (nth 1 x) 'pro) "<p>" "<f>")))))
8004 (if this-buffer
8005 (idlwave-save-buffer-update)
8006 (idlwave-routines))))))
f32b3b91 8007 (name (idlwave-completing-read
52a244eb
S
8008 (if (or (not this-buffer)
8009 (assoc default list))
8010 (format "Module (Default %s): " default)
8011 (format "Module in this file: "))
f32b3b91
CD
8012 list))
8013 type class)
8014 (if (string-match "\\`\\s-*\\'" name)
8015 ;; Nothing, use the default.
8016 (setq name default))
8017 (if (string-match "<[fp]>" name)
8018 (setq type (substring name -2 -1)
8019 name (substring name 0 -3)))
8020 (if (string-match "\\(.*\\)::\\(.*\\)" name)
8021 (setq class (match-string 1 name)
8022 name (match-string 2 name)))
8023 (setq name (idlwave-sintern-routine-or-method name class)
8024 class (idlwave-sintern-class class)
8025 type (cond ((equal type "f") 'fun)
8026 ((equal type "p") 'pro)
8027 (t t)))
52a244eb 8028 (idlwave-do-find-module name type class nil this-buffer)))
f32b3b91 8029
4b1aaa8b 8030(defun idlwave-do-find-module (name type class
52a244eb 8031 &optional force-source this-buffer)
f32b3b91 8032 (let ((name1 (idlwave-make-full-name class name))
4b1aaa8b 8033 source buf1 entry
f32b3b91 8034 (buf (current-buffer))
05a1abfc 8035 (pos (point))
52a244eb
S
8036 file name2)
8037 (setq entry (idlwave-best-rinfo-assq name type class (idlwave-routines)
8038 'WITH-FILE)
05a1abfc
CD
8039 source (or force-source (nth 3 entry))
8040 name2 (if (nth 2 entry)
8041 (idlwave-make-full-name (nth 2 entry) name)
775591f7 8042 name1))
4b1aaa8b 8043 (if source
52a244eb
S
8044 (setq file (idlwave-routine-source-file source)))
8045 (unless file ; Try to find it on the path.
4b1aaa8b
PE
8046 (setq file
8047 (idlwave-expand-lib-file-name
52a244eb
S
8048 (if class
8049 (format "%s__define.pro" (downcase class))
8050 (format "%s.pro" (downcase name))))))
f32b3b91
CD
8051 (cond
8052 ((or (null name) (equal name ""))
8053 (error "Abort"))
f32b3b91 8054 ((eq (car source) 'system)
4b1aaa8b 8055 (error "Source code for system routine %s is not available"
05a1abfc 8056 name2))
52a244eb 8057 ((or (not file) (not (file-regular-p file)))
e8af40ee 8058 (error "Source code for routine %s is not available"
05a1abfc 8059 name2))
52a244eb
S
8060 (t
8061 (when (not this-buffer)
4b1aaa8b 8062 (setq buf1
52a244eb
S
8063 (idlwave-find-file-noselect file 'find))
8064 (pop-to-buffer buf1 t))
15e42531 8065 (goto-char (point-max))
f32b3b91 8066 (let ((case-fold-search t))
15e42531 8067 (if (re-search-backward
f32b3b91 8068 (concat "^[ \t]*\\<"
52a244eb
S
8069 (cond ((eq type 'fun) "function")
8070 ((eq type 'pro) "pro")
f32b3b91 8071 (t "\\(pro\\|function\\)"))
4b1aaa8b 8072 "\\>[ \t]+"
05a1abfc 8073 (regexp-quote (downcase name2))
f32b3b91
CD
8074 "[^a-zA-Z0-9_$]")
8075 nil t)
8076 (goto-char (match-beginning 0))
8077 (pop-to-buffer buf)
8078 (goto-char pos)
05a1abfc 8079 (error "Could not find routine %s" name2)))))))
f32b3b91
CD
8080
8081(defun idlwave-what-module ()
8082 "Return a default module for stuff near point.
8083Used by `idlwave-routine-info' and `idlwave-find-module'."
8084 (idlwave-routines)
15e42531
CD
8085 (if (let ((case-fold-search t))
8086 (save-excursion
8087 (idlwave-beginning-of-statement)
8088 (looking-at "[ \t]*\\(pro\\|function\\)[ \t]+\\(\\([a-zA-Z0-9_$]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)\\([, \t\n]\\|$\\)")))
8089 ;; This is a function or procedure definition statement
8090 ;; We return the defined routine as module.
8091 (list
52a244eb
S
8092 (idlwave-sintern-routine-or-method (match-string-no-properties 4)
8093 (match-string-no-properties 2))
15e42531
CD
8094 (if (equal (downcase (match-string 1)) "pro") 'pro 'fun)
8095 (idlwave-sintern-class (match-string 3)))
8096
52a244eb 8097 ;; Not a definition statement - analyze precise position.
15e42531
CD
8098 (let* ((where (idlwave-where))
8099 (cw (nth 2 where))
8100 (pro (car (nth 0 where)))
8101 (func (car (nth 1 where)))
8102 (this-word (idlwave-this-word "a-zA-Z0-9$_"))
8103 (next-char (save-excursion (skip-chars-forward "a-zA-Z0-9$_")
8104 (following-char)))
8105 )
8106 (cond
8107 ((and (eq cw 'procedure)
8108 (not (equal this-word "")))
4b1aaa8b 8109 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8110 this-word (nth 2 (nth 3 where))))
8111 (list this-word 'pro
4b1aaa8b 8112 (idlwave-determine-class
15e42531
CD
8113 (cons this-word (cdr (nth 3 where)))
8114 'pro)))
4b1aaa8b 8115 ((and (eq cw 'function)
15e42531
CD
8116 (not (equal this-word ""))
8117 (or (eq next-char ?\() ; exclude arrays, vars.
8118 (looking-at "[a-zA-Z0-9_]*[ \t]*(")))
4b1aaa8b 8119 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8120 this-word (nth 2 (nth 3 where))))
8121 (list this-word 'fun
8122 (idlwave-determine-class
8123 (cons this-word (cdr (nth 3 where)))
8124 'fun)))
8125 ((and (memq cw '(function-keyword procedure-keyword))
8126 (not (equal this-word ""))
8127 (eq next-char ?\()) ; A function!
8128 (setq this-word (idlwave-sintern-routine this-word))
8129 (list this-word 'fun nil))
8130 (func
8131 (list func 'fun (idlwave-determine-class (nth 1 where) 'fun)))
8132 (pro
8133 (list pro 'pro (idlwave-determine-class (nth 0 where) 'pro)))
8134 (t nil)))))
f32b3b91 8135
05a1abfc 8136(defun idlwave-what-module-find-class ()
5a0c3f56 8137 "Call `idlwave-what-module' and find the inherited class if necessary."
05a1abfc 8138 (let* ((module (idlwave-what-module))
8d222148 8139 (class (nth 2 module)))
05a1abfc
CD
8140 (if (and (= (length module) 3)
8141 (stringp class))
8142 (list (car module)
8143 (nth 1 module)
8144 (apply 'idlwave-find-inherited-class module))
8145 module)))
8146
8147(defun idlwave-find-inherited-class (name type class)
8148 "Find the class which defines TYPE NAME and is CLASS or inherited by CLASS."
8149 (let ((entry (idlwave-best-rinfo-assoc name type class (idlwave-routines))))
8150 (if entry
8151 (nth 2 entry)
8152 class)))
8153
8154(defun idlwave-fix-module-if-obj_new (module)
4b1aaa8b 8155 "Check if MODULE points to obj_new.
52a244eb
S
8156If yes, and if the cursor is in the keyword region, change to the
8157appropriate Init method."
05a1abfc
CD
8158 (let* ((name (car module))
8159 (pos (point))
8160 (case-fold-search t)
8161 string)
8162 (if (and (stringp name)
8163 (equal (downcase name) "obj_new")
8164 (save-excursion
8165 (idlwave-beginning-of-statement)
8166 (setq string (buffer-substring (point) pos))
8167 (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8168 string)))
8169 (let ((name "Init")
8170 (class (match-string 1 string)))
8171 (setq module (list (idlwave-sintern-method "Init")
8172 'fun
8173 (idlwave-sintern-class class)))))
8174 module))
8175
4b1aaa8b 8176(defun idlwave-fix-keywords (name type class keywords
3938cb82 8177 &optional super-classes system)
52a244eb
S
8178 "Update a list of keywords.
8179Translate OBJ_NEW, adding all super-class keywords, or all keywords
5a0c3f56 8180from all classes if CLASS equals t. If SYSTEM is non-nil, don't
3938cb82 8181demand _EXTRA in the keyword list."
5e72c6b2 8182 (let ((case-fold-search t))
f32b3b91
CD
8183
8184 ;; If this is the OBJ_NEW function, try to figure out the class and use
8185 ;; the keywords from the corresponding INIT method.
5e72c6b2 8186 (if (and (equal (upcase name) "OBJ_NEW")
05a1abfc
CD
8187 (or (eq major-mode 'idlwave-mode)
8188 (eq major-mode 'idlwave-shell-mode)))
f32b3b91
CD
8189 (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point)))
8190 (string (buffer-substring bos (point)))
8191 (case-fold-search t)
8192 class)
8193 (and (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8194 string)
8195 (setq class (idlwave-sintern-class (match-string 1 string)))
15e42531 8196 (setq idlwave-current-obj_new-class class)
4b1aaa8b
PE
8197 (setq keywords
8198 (append keywords
52a244eb
S
8199 (idlwave-entry-keywords
8200 (idlwave-rinfo-assq
8201 (idlwave-sintern-method "INIT")
8202 'fun
8203 class
8204 (idlwave-routines)) 'do-link))))))
4b1aaa8b 8205
f32b3b91
CD
8206 ;; If the class is `t', combine all keywords of all methods NAME
8207 (when (eq class t)
52a244eb
S
8208 (mapc (lambda (entry)
8209 (and
8210 (nth 2 entry) ; non-nil class
8211 (eq (nth 1 entry) type) ; correct type
4b1aaa8b
PE
8212 (setq keywords
8213 (append keywords
52a244eb
S
8214 (idlwave-entry-keywords entry 'do-link)))))
8215 (idlwave-all-assq name (idlwave-routines)))
5e72c6b2 8216 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8217
5e72c6b2 8218 ;; If we have inheritance, add all keywords from superclasses, if
52a244eb 8219 ;; the user indicated that method in `idlwave-keyword-class-inheritance'
4b1aaa8b 8220 (when (and
52a244eb 8221 super-classes
5e72c6b2
S
8222 idlwave-keyword-class-inheritance
8223 (stringp class)
4b1aaa8b 8224 (or
3938cb82
S
8225 system
8226 (assq (idlwave-sintern-keyword "_extra") keywords)
8227 (assq (idlwave-sintern-keyword "_ref_extra") keywords))
5e72c6b2
S
8228 ;; Check if one of the keyword-class regexps matches the name
8229 (let ((regexps idlwave-keyword-class-inheritance) re)
8230 (catch 'exit
8231 (while (setq re (pop regexps))
8232 (if (string-match re name) (throw 'exit t))))))
52a244eb
S
8233
8234 (loop for entry in (idlwave-routines) do
8235 (and (nth 2 entry) ; non-nil class
8236 (memq (nth 2 entry) super-classes) ; an inherited class
8237 (eq (nth 1 entry) type) ; correct type
8238 (eq (car entry) name) ; correct name
8ffcfb27
GM
8239 (mapc (lambda (k) (add-to-list 'keywords k))
8240 (idlwave-entry-keywords entry 'do-link))))
f32b3b91 8241 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8242
f32b3b91
CD
8243 ;; Return the final list
8244 keywords))
8245
15e42531 8246(defun idlwave-expand-keyword (keyword module)
2e8b9c7d 8247 "Expand KEYWORD to one of the valid keyword parameters of MODULE.
15e42531
CD
8248KEYWORD may be an exact match or an abbreviation of a keyword.
8249If the match is exact, KEYWORD itself is returned, even if there may be other
8250keywords of which KEYWORD is an abbreviation. This is necessary because some
8251system routines have keywords which are prefixes of other keywords.
8252If KEYWORD is an abbreviation of several keywords, a list of all possible
8253completions is returned.
8254If the abbreviation was unique, the correct keyword is returned.
8255If it cannot be a keyword, the function return nil.
8256If we do not know about MODULE, just return KEYWORD literally."
8257 (let* ((name (car module))
8258 (type (nth 1 module))
8259 (class (nth 2 module))
8260 (kwd (idlwave-sintern-keyword keyword))
8261 (entry (idlwave-best-rinfo-assoc name type class (idlwave-routines)))
52a244eb 8262 (kwd-alist (idlwave-entry-keywords entry))
15e42531
CD
8263 (extra (or (assq (idlwave-sintern-keyword "_EXTRA") kwd-alist)
8264 (assq (idlwave-sintern-keyword "_REF_EXTRA") kwd-alist)))
8265 (completion-ignore-case t)
8266 candidates)
4b1aaa8b 8267 (cond ((assq kwd kwd-alist)
15e42531
CD
8268 kwd)
8269 ((setq candidates (all-completions kwd kwd-alist))
8270 (if (= (length candidates) 1)
8271 (car candidates)
8272 candidates))
8273 ((and entry extra)
4b1aaa8b 8274 ;; Inheritance may cause this keyword to be correct
15e42531
CD
8275 keyword)
8276 (entry
8277 ;; We do know the function, which does not have the keyword.
8278 nil)
8279 (t
8280 ;; We do not know the function, so this just might be a correct
8281 ;; keyword - return it as it is.
8282 keyword))))
8283
8284(defvar idlwave-rinfo-mouse-map (make-sparse-keymap))
f32b3b91 8285(defvar idlwave-rinfo-map (make-sparse-keymap))
4b1aaa8b 8286(define-key idlwave-rinfo-mouse-map
f32b3b91
CD
8287 (if (featurep 'xemacs) [button2] [mouse-2])
8288 'idlwave-mouse-active-rinfo)
4b1aaa8b 8289(define-key idlwave-rinfo-mouse-map
15e42531
CD
8290 (if (featurep 'xemacs) [(shift button2)] [(shift mouse-2)])
8291 'idlwave-mouse-active-rinfo-shift)
4b1aaa8b 8292(define-key idlwave-rinfo-mouse-map
f32b3b91
CD
8293 (if (featurep 'xemacs) [button3] [mouse-3])
8294 'idlwave-mouse-active-rinfo-right)
15e42531
CD
8295(define-key idlwave-rinfo-mouse-map " " 'idlwave-active-rinfo-space)
8296(define-key idlwave-rinfo-map "q" 'idlwave-quit-help)
8297(define-key idlwave-rinfo-mouse-map "q" 'idlwave-quit-help)
8298(defvar idlwave-popup-source nil)
8299(defvar idlwave-rinfo-marker (make-marker))
8300
8301(defun idlwave-quit-help ()
8302 (interactive)
8303 (let ((ri-window (get-buffer-window "*Help*"))
8304 (olh-window (get-buffer-window "*IDLWAVE Help*")))
8305 (when (and olh-window
8306 (fboundp 'idlwave-help-quit))
8307 (select-window olh-window)
8308 (idlwave-help-quit))
8309 (when (window-live-p ri-window)
8310 (delete-window ri-window))))
f32b3b91 8311
05a1abfc
CD
8312(defun idlwave-display-calling-sequence (name type class
8313 &optional initial-class)
f32b3b91 8314 ;; Display the calling sequence of module NAME, type TYPE in class CLASS.
05a1abfc
CD
8315 (let* ((initial-class (or initial-class class))
8316 (entry (or (idlwave-best-rinfo-assq name type class
15e42531 8317 (idlwave-routines))
4b1aaa8b 8318 (idlwave-rinfo-assq name type class
15e42531 8319 idlwave-unresolved-routines)))
f32b3b91
CD
8320 (name (or (car entry) name))
8321 (class (or (nth 2 entry) class))
05a1abfc 8322 (superclasses (idlwave-all-class-inherits initial-class))
15e42531
CD
8323 (twins (idlwave-routine-twins entry))
8324 (dtwins (idlwave-study-twins twins))
8325 (all dtwins)
52a244eb 8326 (system (eq (car (nth 3 entry)) 'system))
f32b3b91 8327 (calling-seq (nth 4 entry))
52a244eb
S
8328 (keywords (idlwave-entry-keywords entry 'do-link))
8329 (html-file (car (nth 5 entry)))
15e42531 8330 (help-echo-kwd
52a244eb 8331 "Button2: Insert KEYWORD (SHIFT=`/KEYWORD') | Button3: Online Help ")
15e42531 8332 (help-echo-use
52a244eb 8333 "Button2/3: Online Help")
15e42531 8334 (help-echo-src
52a244eb 8335 "Button2: Jump to source and back | Button3: Source in Help window.")
05a1abfc
CD
8336 (help-echo-class
8337 "Button2: Display info about same method in superclass")
f32b3b91 8338 (col 0)
52a244eb 8339 (data (list name type class (current-buffer) nil initial-class))
f32b3b91 8340 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
f66f03de 8341 (face 'idlwave-help-link)
15e42531 8342 beg props win cnt total)
4b1aaa8b 8343 ;; Fix keywords, but don't add chained super-classes, since these
52a244eb 8344 ;; are shown separately for that super-class
f32b3b91
CD
8345 (setq keywords (idlwave-fix-keywords name type class keywords))
8346 (cond
8347 ((null entry)
05a1abfc
CD
8348 (error "No %s %s known %s" type name
8349 (if initial-class (concat "in class " initial-class) "")))
f32b3b91 8350 ((or (null name) (equal name ""))
e8af40ee 8351 (error "No function or procedure call at point"))
f32b3b91 8352 ((null calling-seq)
52a244eb 8353 (error "Calling sequence of %s %s not available" type name))
f32b3b91 8354 (t
9a529312
SM
8355 (move-marker idlwave-rinfo-marker (point))
8356 (with-current-buffer (get-buffer-create "*Help*")
15e42531 8357 (use-local-map idlwave-rinfo-map)
f32b3b91
CD
8358 (setq buffer-read-only nil)
8359 (erase-buffer)
8360 (set (make-local-variable 'idlwave-popup-source) nil)
15e42531
CD
8361 (set (make-local-variable 'idlwave-current-obj_new-class)
8362 idlwave-current-obj_new-class)
05a1abfc
CD
8363 (when superclasses
8364 (setq props (list 'mouse-face 'highlight
8365 km-prop idlwave-rinfo-mouse-map
8366 'help-echo help-echo-class
8367 'data (cons 'class data)))
8368 (let ((classes (cons initial-class superclasses)) c)
8369 (insert "Classes: ")
8370 (while (setq c (pop classes))
8371 (insert " ")
8372 (setq beg (point))
8373 (insert c)
8374 (if (equal (downcase c) (downcase class))
8375 (add-text-properties beg (point) (list 'face 'bold))
52a244eb 8376 ;; If Method exists in a different class link it
05a1abfc
CD
8377 (if (idlwave-rinfo-assq name type c (idlwave-routines))
8378 (add-text-properties beg (point) props))))
8379 (insert "\n")))
52a244eb
S
8380 (setq props (list 'mouse-face 'highlight
8381 km-prop idlwave-rinfo-mouse-map
8382 'help-echo help-echo-use
8383 'data (cons 'usage data)))
4b1aaa8b 8384 (if html-file (setq props (append (list 'face face 'link html-file)
52a244eb 8385 props)))
f32b3b91
CD
8386 (insert "Usage: ")
8387 (setq beg (point))
8388 (insert (if class
52a244eb
S
8389 (format calling-seq class name class name class name)
8390 (format calling-seq name name name name))
f32b3b91
CD
8391 "\n")
8392 (add-text-properties beg (point) props)
4b1aaa8b 8393
f32b3b91
CD
8394 (insert "Keywords:")
8395 (if (null keywords)
8396 (insert " No keywords accepted.")
8397 (setq col 9)
8ffcfb27 8398 (mapc
f32b3b91 8399 (lambda (x)
4b1aaa8b 8400 (if (>= (+ col 1 (length (car x)))
f32b3b91
CD
8401 (window-width))
8402 (progn
8403 (insert "\n ")
8404 (setq col 9)))
8405 (insert " ")
8406 (setq beg (point)
52a244eb 8407 ;; Relevant keywords already have link property attached
f32b3b91 8408 props (list 'mouse-face 'highlight
15e42531 8409 km-prop idlwave-rinfo-mouse-map
f32b3b91 8410 'data (cons 'keyword data)
15e42531 8411 'help-echo help-echo-kwd
f32b3b91 8412 'keyword (car x)))
52a244eb 8413 (if system (setq props (append (list 'face face) props)))
f32b3b91
CD
8414 (insert (car x))
8415 (add-text-properties beg (point) props)
8416 (setq col (+ col 1 (length (car x)))))
8417 keywords))
4b1aaa8b 8418
15e42531 8419 (setq cnt 1 total (length all))
52a244eb 8420 ;; Here entry is (key file (list of type-conses))
15e42531
CD
8421 (while (setq entry (pop all))
8422 (setq props (list 'mouse-face 'highlight
8423 km-prop idlwave-rinfo-mouse-map
8424 'help-echo help-echo-src
52a244eb
S
8425 'source (list (car (car (nth 2 entry))) ;type
8426 (nth 1 entry)
8427 nil
8428 (cdr (car (nth 2 entry))))
15e42531
CD
8429 'data (cons 'source data)))
8430 (idlwave-insert-source-location
4b1aaa8b 8431 (format "\n%-8s %s"
15e42531
CD
8432 (if (equal cnt 1)
8433 (if (> total 1) "Sources:" "Source:")
8434 "")
8435 (if (> total 1) "- " ""))
8436 entry props)
8437 (incf cnt)
8438 (when (and all (> cnt idlwave-rinfo-max-source-lines))
8439 ;; No more source lines, please
4b1aaa8b 8440 (insert (format
15e42531
CD
8441 "\n Source information truncated to %d entries."
8442 idlwave-rinfo-max-source-lines))
8443 (setq all nil)))
10c8e594 8444 (goto-char (point-min))
f32b3b91
CD
8445 (setq buffer-read-only t))
8446 (display-buffer "*Help*")
8447 (if (and (setq win (get-buffer-window "*Help*"))
8448 idlwave-resize-routine-help-window)
8449 (progn
8450 (let ((ww (selected-window)))
8451 (unwind-protect
8452 (progn
8453 (select-window win)
4b1aaa8b 8454 (enlarge-window (- (/ (frame-height) 2)
f32b3b91
CD
8455 (window-height)))
8456 (shrink-window-if-larger-than-buffer))
8457 (select-window ww)))))))))
8458
15e42531
CD
8459(defun idlwave-insert-source-location (prefix entry &optional file-props)
8460 "Insert a source location into the routine info buffer.
5a0c3f56
JB
8461Start line with PREFIX. If a file name is inserted, add FILE-PROPS
8462to it."
15e42531
CD
8463 (let* ((key (car entry))
8464 (file (nth 1 entry))
8465 (types (nth 2 entry))
52a244eb
S
8466 (shell-flag (assq 'compiled types))
8467 (buffer-flag (assq 'buffer types))
8468 (user-flag (assq 'user types))
8469 (lib-flag (assq 'lib types))
8470 (ndupl (or (and buffer-flag (idlwave-count-memq 'buffer types))
8471 (and user-flag (idlwave-count-memq 'user types))
8472 (and lib-flag (idlwave-count-memq 'lib types))
15e42531
CD
8473 1))
8474 (doflags t)
8475 beg special)
8476
8477 (insert prefix)
8478
8479 (cond
8480 ((eq key 'system)
8481 (setq doflags nil)
52a244eb
S
8482 (insert "System "))
8483
15e42531
CD
8484 ((eq key 'builtin)
8485 (setq doflags nil)
52a244eb
S
8486 (insert "Builtin "))
8487
15e42531 8488 ((and (not file) shell-flag)
52a244eb
S
8489 (insert "Unresolved"))
8490
4b1aaa8b 8491 ((null file)
52a244eb 8492 (insert "ERROR"))
4b1aaa8b 8493
15e42531
CD
8494 ((idlwave-syslib-p file)
8495 (if (string-match "obsolete" (file-name-directory file))
52a244eb
S
8496 (insert "Obsolete ")
8497 (insert "SystemLib ")))
8498
8499 ;; New special syntax: taken directly from routine-info for
8500 ;; library catalog routines
8501 ((setq special (or (cdr lib-flag) (cdr user-flag)))
8502 (insert (format "%-10s" special)))
8503
8504 ;; Old special syntax: a matching regexp
8505 ((setq special (idlwave-special-lib-test file))
8506 (insert (format "%-10s" special)))
4b1aaa8b 8507
52a244eb 8508 ;; Catch-all with file
15e42531 8509 ((idlwave-lib-p file) (insert "Library "))
52a244eb
S
8510
8511 ;; Sanity catch all
15e42531
CD
8512 (t (insert "Other ")))
8513
8514 (when doflags
8515 (insert (concat
8516 " ["
52a244eb
S
8517 (if lib-flag "L" "-")
8518 (if user-flag "C" "-")
15e42531
CD
8519 (if shell-flag "S" "-")
8520 (if buffer-flag "B" "-")
8521 "] ")))
4b1aaa8b 8522 (when (> ndupl 1)
15e42531
CD
8523 (setq beg (point))
8524 (insert (format "(%dx) " ndupl))
8525 (add-text-properties beg (point) (list 'face 'bold)))
8526 (when (and file (not (equal file "")))
8527 (setq beg (point))
8528 (insert (apply 'abbreviate-file-name
8529 (if (featurep 'xemacs) (list file t) (list file))))
8530 (if file-props
8531 (add-text-properties beg (point) file-props)))))
8532
8533(defun idlwave-special-lib-test (file)
8534 "Check the path of FILE against the regexps which define special libs.
8535Return the name of the special lib if there is a match."
8536 (let ((alist idlwave-special-lib-alist)
8537 entry rtn)
8538 (cond
8539 ((stringp file)
8540 (while (setq entry (pop alist))
8541 (if (string-match (car entry) file)
8542 (setq rtn (cdr entry)
8543 alist nil)))
8544 rtn)
8545 (t nil))))
4b1aaa8b 8546
f32b3b91
CD
8547(defun idlwave-mouse-active-rinfo-right (ev)
8548 (interactive "e")
8549 (idlwave-mouse-active-rinfo ev 'right))
8550
15e42531 8551(defun idlwave-mouse-active-rinfo-shift (ev)
f32b3b91 8552 (interactive "e")
15e42531
CD
8553 (idlwave-mouse-active-rinfo ev nil 'shift))
8554
8555(defun idlwave-active-rinfo-space ()
8556 (interactive)
8557 (idlwave-mouse-active-rinfo nil 'right))
8558
8559(defun idlwave-mouse-active-rinfo (ev &optional right shift)
5a0c3f56 8560 "Do the mouse actions in the routine info buffer.
15e42531
CD
8561Optional args RIGHT and SHIFT indicate, if mouse-3 was used, and if SHIFT
8562was pressed."
8563 (interactive "e")
8564 (if ev (mouse-set-point ev))
4b1aaa8b 8565 (let (data id name type class buf bufwin source link keyword
3938cb82 8566 word initial-class)
f32b3b91 8567 (setq data (get-text-property (point) 'data)
15e42531 8568 source (get-text-property (point) 'source)
f32b3b91 8569 keyword (get-text-property (point) 'keyword)
52a244eb 8570 link (get-text-property (point) 'link)
f32b3b91 8571 id (car data)
15e42531 8572 name (nth 1 data) type (nth 2 data) class (nth 3 data)
f32b3b91 8573 buf (nth 4 data)
05a1abfc
CD
8574 initial-class (nth 6 data)
8575 word (idlwave-this-word)
f32b3b91 8576 bufwin (get-buffer-window buf t))
52a244eb
S
8577
8578 (cond ((eq id 'class) ; Switch class being displayed
05a1abfc 8579 (if (window-live-p bufwin) (select-window bufwin))
4b1aaa8b 8580 (idlwave-display-calling-sequence
05a1abfc 8581 (idlwave-sintern-method name)
4b1aaa8b 8582 type (idlwave-sintern-class word)
05a1abfc 8583 initial-class))
52a244eb
S
8584 ((eq id 'usage) ; Online help on this routine
8585 (idlwave-online-help link name type class))
8586 ((eq id 'source) ; Source in help or buffer
8587 (if right ; In help
15e42531
CD
8588 (let ((idlwave-extra-help-function 'idlwave-help-with-source)
8589 (idlwave-help-source-try-header nil)
52a244eb 8590 ;; Fake idlwave-routines so help will find the right entry
15e42531 8591 (idlwave-routines
52a244eb 8592 (list (list name type class source ""))))
15e42531 8593 (idlwave-help-get-special-help name type class nil))
52a244eb 8594 ;; Otherwise just pop to the source
f32b3b91
CD
8595 (setq idlwave-popup-source (not idlwave-popup-source))
8596 (if idlwave-popup-source
8597 (condition-case err
15e42531 8598 (idlwave-do-find-module name type class source)
f32b3b91
CD
8599 (error
8600 (setq idlwave-popup-source nil)
8601 (if (window-live-p bufwin) (select-window bufwin))
8602 (error (nth 1 err))))
8603 (if bufwin
8604 (select-window bufwin)
15e42531
CD
8605 (pop-to-buffer buf))
8606 (goto-char (marker-position idlwave-rinfo-marker)))))
f32b3b91
CD
8607 ((eq id 'keyword)
8608 (if right
52a244eb 8609 (idlwave-online-help link name type class keyword)
15e42531
CD
8610 (idlwave-rinfo-insert-keyword keyword buf shift))))))
8611
8612(defun idlwave-rinfo-insert-keyword (keyword buffer &optional shift)
8613 "Insert KEYWORD in BUFFER. Make sure buffer is displayed in a window."
8614 (let ((bwin (get-buffer-window buffer)))
8615 (if idlwave-complete-empty-string-as-lower-case
8616 (setq keyword (downcase keyword)))
8617 (if bwin
8618 (select-window bwin)
8619 (pop-to-buffer buffer)
8620 (setq bwin (get-buffer-window buffer)))
8621 (if (eq (preceding-char) ?/)
8622 (insert keyword)
4b1aaa8b 8623 (unless (save-excursion
15e42531 8624 (re-search-backward
4b1aaa8b 8625 "[(,][ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\)?[ \t]*\\="
15e42531
CD
8626 (min (- (point) 100) (point-min)) t))
8627 (insert ", "))
8628 (if shift (insert "/"))
8629 (insert keyword)
8630 (if (and (not shift)
8631 idlwave-keyword-completion-adds-equal)
8632 (insert "=")))))
8633
8634(defun idlwave-list-buffer-load-path-shadows (&optional arg)
8635 "List the load path shadows of all routines defined in current buffer."
8636 (interactive "P")
8637 (idlwave-routines)
8638 (if (eq major-mode 'idlwave-mode)
8639 (idlwave-list-load-path-shadows
8640 nil (idlwave-update-current-buffer-info 'save-buffer)
8641 "in current buffer")
8642 (error "Current buffer is not in idlwave-mode")))
8643
8644(defun idlwave-list-shell-load-path-shadows (&optional arg)
8645 "List the load path shadows of all routines compiled under the shell.
8646This is very useful for checking an IDL application. Just compile the
8647application, do RESOLVE_ALL, and `C-c C-i' to compile all referenced
8648routines and update IDLWAVE internal info. Then check for shadowing
8649with this command."
8650 (interactive "P")
8651 (cond
8652 ((or (not (fboundp 'idlwave-shell-is-running))
8653 (not (idlwave-shell-is-running)))
8654 (error "Shell is not running"))
8655 ((null idlwave-compiled-routines)
e8af40ee 8656 (error "No compiled routines. Maybe you need to update with `C-c C-i'"))
15e42531
CD
8657 (t
8658 (idlwave-list-load-path-shadows nil idlwave-compiled-routines
8659 "in the shell"))))
8660
8661(defun idlwave-list-all-load-path-shadows (&optional arg)
8662 "List the load path shadows of all routines known to IDLWAVE."
8663 (interactive "P")
8664 (idlwave-list-load-path-shadows nil nil "globally"))
8665
8d222148
SM
8666(defvar idlwave-sort-prefer-buffer-info t
8667 "Internal variable used to influence `idlwave-routine-twin-compare'.")
8668
15e42531
CD
8669(defun idlwave-list-load-path-shadows (arg &optional special-routines loc)
8670 "List the routines which are defined multiple times.
8671Search the information IDLWAVE has about IDL routines for multiple
8672definitions.
8673When SPECIAL-ROUTINES in non-nil, only look for shadows of these routines.
8674
8675When IDL hits a routine call which is not defined, it will search on
5a0c3f56
JB
8676the load path in order to find a definition. The output of this command
8677can be used to detect possible name clashes during this process."
15e42531 8678 (idlwave-routines) ; Make sure everything is loaded.
52a244eb 8679 (unless (or idlwave-user-catalog-routines idlwave-library-catalog-routines)
4b1aaa8b 8680 (or (y-or-n-p
52a244eb 8681 "You don't have any user or library catalogs. Continue anyway? ")
15e42531
CD
8682 (error "Abort")))
8683 (let* ((routines (append idlwave-system-routines
8684 idlwave-compiled-routines
52a244eb
S
8685 idlwave-library-catalog-routines
8686 idlwave-user-catalog-routines
15e42531
CD
8687 idlwave-buffer-routines
8688 nil))
8689 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
8690 (keymap (make-sparse-keymap))
8691 (props (list 'mouse-face 'highlight
8692 km-prop keymap
4b1aaa8b 8693 'help-echo "Mouse2: Find source"))
15e42531 8694 (nroutines (length (or special-routines routines)))
f66f03de 8695 (step (/ nroutines 100))
15e42531 8696 (n 0)
15e42531
CD
8697 (cnt 0)
8698 (idlwave-sort-prefer-buffer-info nil)
8699 routine twins dtwins twin done props1 lroutines)
8700
8701 (if special-routines
8702 ;; Just looking for shadows of a few special routines
8703 (setq lroutines routines
8704 routines special-routines))
8705
8706 (message "Sorting routines...")
8707 (setq routines (sort routines
8708 (lambda (a b)
8709 (string< (downcase (idlwave-make-full-name
8710 (nth 2 a) (car a)))
8711 (downcase (idlwave-make-full-name
8712 (nth 2 b) (car b)))))))
8713 (message "Sorting routines...done")
8714
8715 (define-key keymap (if (featurep 'xemacs) [(button2)] [(mouse-2)])
4b1aaa8b 8716 (lambda (ev)
15e42531
CD
8717 (interactive "e")
8718 (mouse-set-point ev)
8719 (apply 'idlwave-do-find-module
8720 (get-text-property (point) 'find-args))))
8721 (define-key keymap [(return)]
4b1aaa8b 8722 (lambda ()
15e42531
CD
8723 (interactive)
8724 (apply 'idlwave-do-find-module
8725 (get-text-property (point) 'find-args))))
8726 (message "Compiling list...( 0%%)")
9a529312 8727 (with-current-buffer (get-buffer-create "*Shadows*")
15e42531
CD
8728 (setq buffer-read-only nil)
8729 (erase-buffer)
8730 (while (setq routine (pop routines))
f66f03de
S
8731 (if (= (mod (setq n (1+ n)) step) 0)
8732 (message "Compiling list...(%2d%%)" (/ (* n 100) nroutines)))
8733
15e42531
CD
8734 ;; Get a list of all twins
8735 (setq twins (idlwave-routine-twins routine (or lroutines routines)))
8736 (if (memq routine done)
8737 (setq dtwins nil)
8738 (setq dtwins (idlwave-study-twins twins)))
5e72c6b2 8739 ;; Mark all twins as dealt with
15e42531
CD
8740 (setq done (append twins done))
8741 (when (or (> (length dtwins) 1)
52a244eb
S
8742 (> (idlwave-count-memq 'lib (nth 2 (car dtwins))) 1)
8743 (> (idlwave-count-memq 'user (nth 2 (car dtwins))) 1)
8744 (> (idlwave-count-memq 'buffer (nth 2 (car dtwins))) 1))
15e42531
CD
8745 (incf cnt)
8746 (insert (format "\n%s%s"
4b1aaa8b 8747 (idlwave-make-full-name (nth 2 routine)
52a244eb 8748 (car routine))
15e42531
CD
8749 (if (eq (nth 1 routine) 'fun) "()" "")))
8750 (while (setq twin (pop dtwins))
8751 (setq props1 (append (list 'find-args
4b1aaa8b
PE
8752 (list (nth 0 routine)
8753 (nth 1 routine)
52a244eb 8754 (nth 2 routine)))
15e42531
CD
8755 props))
8756 (idlwave-insert-source-location "\n - " twin props1))))
8757 (goto-char (point-min))
8758 (setq buffer-read-only t))
8759 (setq loc (or loc ""))
8760 (if (> cnt 0)
8761 (progn
8762 (display-buffer (get-buffer "*Shadows*"))
8763 (message "%d case%s of shadowing found %s"
8764 cnt (if (= cnt 1) "" "s") loc))
8765 (message "No shadowing conflicts found %s" loc))))
8766
8767(defun idlwave-print-source (routine)
8768 (let* ((source (nth 3 routine))
8769 (stype (car source))
52a244eb
S
8770 (sfile (idlwave-routine-source-file source)))
8771 (if (idlwave-syslib-p sfile) (setq stype 'syslib))
15e42531
CD
8772 (if (and (eq stype 'compiled)
8773 (or (not (stringp sfile))
8774 (not (string-match "\\S-" sfile))))
8775 (setq stype 'unresolved))
4b1aaa8b 8776 (princ (format " %-10s %s\n"
15e42531
CD
8777 stype
8778 (if sfile sfile "No source code available")))))
8779
8780(defun idlwave-routine-twins (entry &optional list)
8781 "Return all twin entries of ENTRY in LIST.
8782LIST defaults to `idlwave-routines'.
8783Twin entries are those which have the same name, type, and class.
8784ENTRY will also be returned, as the first item of this list."
8785 (let* ((name (car entry))
8786 (type (nth 1 entry))
8787 (class (nth 2 entry))
8788 (candidates (idlwave-all-assq name (or list (idlwave-routines))))
8789 twins candidate)
8790 (while (setq candidate (pop candidates))
8791 (if (and (not (eq candidate entry))
8792 (eq type (nth 1 candidate))
8793 (eq class (nth 2 candidate)))
8794 (push candidate twins)))
4b1aaa8b 8795 (if (setq candidate (idlwave-rinfo-assq name type class
15e42531
CD
8796 idlwave-unresolved-routines))
8797 (push candidate twins))
8798 (cons entry (nreverse twins))))
8799
8800(defun idlwave-study-twins (entries)
4b1aaa8b 8801 "Return dangerous twins of first entry in ENTRIES.
52a244eb
S
8802Dangerous twins are routines with same name, but in different files on
8803the load path. If a file is in the system library and has an entry in
8804the `idlwave-system-routines' list, we omit the latter as
8805non-dangerous because many IDL routines are implemented as library
8806routines, and may have been scanned."
15e42531 8807 (let* ((entry (car entries))
4b1aaa8b 8808 (name (car entry)) ;
15e42531 8809 (type (nth 1 entry)) ; Must be bound for
e2a9c0bc 8810 (idlwave-twin-class (nth 2 entry)) ; idlwave-routine-twin-compare
15e42531 8811 (cnt 0)
52a244eb 8812 source type type-cons file alist syslibp key)
15e42531
CD
8813 (while (setq entry (pop entries))
8814 (incf cnt)
8815 (setq source (nth 3 entry)
8816 type (car source)
52a244eb
S
8817 type-cons (cons type (nth 3 source))
8818 file (idlwave-routine-source-file source))
8819
15e42531
CD
8820 ;; Make KEY to index entry properly
8821 (setq key (cond ((eq type 'system) type)
8822 (file (file-truename file))
8823 (t 'unresolved)))
52a244eb
S
8824
8825 ;; Check for an entry in the system library
4b1aaa8b 8826 (if (and file
15e42531
CD
8827 (not syslibp)
8828 (idlwave-syslib-p file))
15e42531 8829 (setq syslibp t))
4b1aaa8b 8830
52a244eb
S
8831 ;; If there's more than one matching entry for the same file, just
8832 ;; append the type-cons to the type list.
15e42531 8833 (if (setq entry (assoc key alist))
52a244eb
S
8834 (push type-cons (nth 2 entry))
8835 (push (list key file (list type-cons)) alist)))
4b1aaa8b 8836
15e42531 8837 (setq alist (nreverse alist))
4b1aaa8b 8838
15e42531 8839 (when syslibp
52a244eb
S
8840 ;; File is in system *library* - remove any 'system entry
8841 (setq alist (delq (assq 'system alist) alist)))
4b1aaa8b 8842
52a244eb
S
8843 ;; If 'system remains and we've scanned the syslib, it's a builtin
8844 ;; (rather than a !DIR/lib/.pro file bundled as source).
15e42531
CD
8845 (when (and (idlwave-syslib-scanned-p)
8846 (setq entry (assoc 'system alist)))
8847 (setcar entry 'builtin))
8848 (sort alist 'idlwave-routine-twin-compare)))
8849
8d222148
SM
8850;; FIXME: Dynamically scoped vars need to use the `idlwave-' prefix.
8851;; (defvar type)
15e42531
CD
8852(defmacro idlwave-xor (a b)
8853 `(and (or ,a ,b)
8854 (not (and ,a ,b))))
8855
8856(defun idlwave-routine-entry-compare (a b)
5a0c3f56
JB
8857 "Compare two routine info entries for sorting.
8858This is the general case. It first compares class, names, and type.
8859If it turns out that A and B are twins (same name, class, and type),
8860calls another routine which compares twins on the basis of their file
8861names and path locations."
15e42531
CD
8862 (let ((name (car a)) (type (nth 1 a)) (class (nth 2 a)))
8863 (cond
8864 ((not (equal (idlwave-downcase-safe class)
8865 (idlwave-downcase-safe (nth 2 b))))
8866 ;; Class decides
8867 (cond ((null (nth 2 b)) nil)
8868 ((null class) t)
8869 (t (string< (downcase class) (downcase (nth 2 b))))))
8870 ((not (equal (downcase name) (downcase (car b))))
8871 ;; Name decides
8872 (string< (downcase name) (downcase (car b))))
8873 ((not (eq type (nth 1 b)))
8874 ;; Type decides
8875 (< (if (eq type 'fun) 1 0) (if (eq (nth 1 b) 'fun) 1 0)))
4b1aaa8b 8876 (t
15e42531
CD
8877 ;; A and B are twins - so the decision is more complicated.
8878 ;; Call twin-compare with the proper arguments.
8879 (idlwave-routine-entry-compare-twins a b)))))
8880
8881(defun idlwave-routine-entry-compare-twins (a b)
5a0c3f56
JB
8882 "Compare two routine entries, under the assumption that they are twins.
8883This basically calls `idlwave-routine-twin-compare' with the correct args."
e2a9c0bc
GM
8884 (let* ((name (car a))
8885 (type (nth 1 a))
8886 (idlwave-twin-class (nth 2 a)) ; used in idlwave-routine-twin-compare
52a244eb
S
8887 (asrc (nth 3 a))
8888 (atype (car asrc))
8889 (bsrc (nth 3 b))
8890 (btype (car bsrc))
8891 (afile (idlwave-routine-source-file asrc))
8892 (bfile (idlwave-routine-source-file bsrc)))
15e42531
CD
8893 (idlwave-routine-twin-compare
8894 (if (stringp afile)
8895 (list (file-truename afile) afile (list atype))
8896 (list atype afile (list atype)))
8897 (if (stringp bfile)
8898 (list (file-truename bfile) bfile (list btype))
e2a9c0bc 8899 (list btype bfile (list btype))))))
15e42531 8900
627e0a14 8901;; Bound in idlwave-study-twins,idlwave-routine-entry-compare-twins.
e2a9c0bc 8902(defvar idlwave-twin-class)
627e0a14 8903
15e42531
CD
8904(defun idlwave-routine-twin-compare (a b)
8905 "Compare two routine twin entries for sorting.
8906In here, A and B are not normal routine info entries, but special
8907lists (KEY FILENAME (TYPES...)).
e2a9c0bc 8908This expects NAME TYPE IDLWAVE-TWIN-CLASS to be bound to the right values."
15e42531
CD
8909 (let* (;; Dis-assemble entries
8910 (akey (car a)) (bkey (car b))
8911 (afile (nth 1 a)) (bfile (nth 1 b))
8912 (atypes (nth 2 a)) (btypes (nth 2 b))
8913 ;; System routines?
8914 (asysp (memq akey '(builtin system)))
8915 (bsysp (memq bkey '(builtin system)))
8916 ;; Compiled routines?
8917 (acompp (memq 'compiled atypes))
8918 (bcompp (memq 'compiled btypes))
8919 ;; Unresolved?
8920 (aunresp (or (eq akey 'unresolved)
8921 (and acompp (not afile))))
8922 (bunresp (or (eq bkey 'unresolved)
8923 (and bcompp (not bfile))))
8924 ;; Buffer info available?
8925 (abufp (memq 'buffer atypes))
8926 (bbufp (memq 'buffer btypes))
8927 ;; On search path?
8928 (tpath-alist (idlwave-true-path-alist))
52a244eb
S
8929 (apathp (and (stringp akey)
8930 (assoc (file-name-directory akey) tpath-alist)))
4b1aaa8b 8931 (bpathp (and (stringp bkey)
52a244eb 8932 (assoc (file-name-directory bkey) tpath-alist)))
15e42531
CD
8933 ;; How early on search path? High number means early since we
8934 ;; measure the tail of the path list
8935 (anpath (length (memq apathp tpath-alist)))
8936 (bnpath (length (memq bpathp tpath-alist)))
8937 ;; Look at file names
8938 (aname (if (stringp afile) (downcase (file-name-nondirectory afile)) ""))
8939 (bname (if (stringp bfile) (downcase (file-name-nondirectory bfile)) ""))
e2a9c0bc
GM
8940 (fname-re (if idlwave-twin-class
8941 (format "\\`%s__\\(%s\\|define\\)\\.pro\\'"
8942 (regexp-quote (downcase idlwave-twin-class))
8943 (regexp-quote (downcase name)))
15e42531
CD
8944 (format "\\`%s\\.pro" (regexp-quote (downcase name)))))
8945 ;; Is file name derived from the routine name?
8946 ;; Method file or class definition file?
8947 (anamep (string-match fname-re aname))
e2a9c0bc
GM
8948 (adefp (and idlwave-twin-class anamep
8949 (string= "define" (match-string 1 aname))))
15e42531 8950 (bnamep (string-match fname-re bname))
e2a9c0bc
GM
8951 (bdefp (and idlwave-twin-class bnamep
8952 (string= "define" (match-string 1 bname)))))
15e42531
CD
8953
8954 ;; Now: follow JD's ideas about sorting. Looks really simple now,
8955 ;; doesn't it? The difficult stuff is hidden above...
8956 (cond
8957 ((idlwave-xor asysp bsysp) asysp) ; System entries first
8958 ((idlwave-xor aunresp bunresp) bunresp) ; Unresolved last
8959 ((and idlwave-sort-prefer-buffer-info
8960 (idlwave-xor abufp bbufp)) abufp) ; Buffers before non-buffers
8961 ((idlwave-xor acompp bcompp) acompp) ; Compiled entries
8962 ((idlwave-xor apathp bpathp) apathp) ; Library before non-library
8963 ((idlwave-xor anamep bnamep) anamep) ; Correct file names first
e2a9c0bc 8964 ((and idlwave-twin-class anamep bnamep ; both file names match ->
15e42531
CD
8965 (idlwave-xor adefp bdefp)) bdefp) ; __define after __method
8966 ((> anpath bnpath) t) ; Who is first on path?
8967 (t nil)))) ; Default
8968
52a244eb 8969(defun idlwave-routine-source-file (source)
4b1aaa8b 8970 (if (nth 2 source)
52a244eb
S
8971 (expand-file-name (nth 1 source) (nth 2 source))
8972 (nth 1 source)))
8973
15e42531
CD
8974(defun idlwave-downcase-safe (string)
8975 "Donwcase if string, else return unchanged."
8976 (if (stringp string)
8977 (downcase string)
8978 string))
8979
8980(defun idlwave-count-eq (elt list)
8981 "How often is ELT in LIST?"
8982 (length (delq nil (mapcar (lambda (x) (eq x elt)) list))))
8983
52a244eb
S
8984(defun idlwave-count-memq (elt alist)
8985 "How often is ELT a key in ALIST?"
8986 (length (delq nil (mapcar (lambda (x) (eq (car x) elt)) alist))))
8987
15e42531 8988(defun idlwave-syslib-p (file)
52a244eb 8989 "Non-nil if FILE is in the system library."
15e42531
CD
8990 (let* ((true-syslib (file-name-as-directory
8991 (file-truename
8992 (expand-file-name "lib" (idlwave-sys-dir)))))
8993 (true-file (file-truename file)))
8994 (string-match (concat "^" (regexp-quote true-syslib)) true-file)))
8995
8996(defun idlwave-lib-p (file)
5a0c3f56 8997 "Non-nil if FILE is in the library."
15e42531
CD
8998 (let ((true-dir (file-name-directory (file-truename file))))
8999 (assoc true-dir (idlwave-true-path-alist))))
9000
52a244eb
S
9001(defun idlwave-path-alist-add-flag (list-entry flag)
9002 "Add a flag to the path list entry, if not set."
9003 (let ((flags (cdr list-entry)))
9004 (add-to-list 'flags flag)
9005 (setcdr list-entry flags)))
9006
9007(defun idlwave-path-alist-remove-flag (list-entry flag)
9008 "Remove a flag to the path list entry, if set."
9009 (let ((flags (delq flag (cdr list-entry))))
9010 (setcdr list-entry flags)))
9011
15e42531
CD
9012(defun idlwave-true-path-alist ()
9013 "Return `idlwave-path-alist' alist with true-names.
52a244eb 9014Info is cached, but relies on the functions setting `idlwave-path-alist'
15e42531
CD
9015to reset the variable `idlwave-true-path-alist' to nil."
9016 (or idlwave-true-path-alist
9017 (setq idlwave-true-path-alist
9018 (mapcar (lambda(x) (cons
9019 (file-name-as-directory
9020 (file-truename
9021 (directory-file-name
9022 (car x))))
9023 (cdr x)))
9024 idlwave-path-alist))))
9025
9026(defun idlwave-syslib-scanned-p ()
9027 "Non-nil if the system lib file !DIR/lib has been scanned."
9028 (let* ((true-syslib (file-name-as-directory
9029 (file-truename
9030 (expand-file-name "lib" (idlwave-sys-dir))))))
9031 (cdr (assoc true-syslib (idlwave-true-path-alist)))))
9032
9033;; ----------------------------------------------------------------------------
9034;;
9035;; Online Help display
9036
f32b3b91
CD
9037
9038;; ----------------------------------------------------------------------------
9039;;
9040;; Additions for use with imenu.el and func-menu.el
9041;; (pop-up a list of IDL units in the current file).
9042;;
9043
9044(defun idlwave-prev-index-position ()
9045 "Search for the previous procedure or function.
9046Return nil if not found. For use with imenu.el."
9047 (save-match-data
9048 (cond
9049 ((idlwave-find-key "\\<\\(pro\\|function\\)\\>" -1 'nomark))
9050 ;; ((idlwave-find-key idlwave-begin-unit-reg 1 'nomark)
9051 (t nil))))
9052
9053(defun idlwave-unit-name ()
9054 "Return the unit name.
9055Assumes that point is at the beginning of the unit as found by
9056`idlwave-prev-index-position'."
9057 (forward-sexp 2)
9058 (forward-sexp -1)
9059 (let ((begin (point)))
4b1aaa8b 9060 (re-search-forward
52a244eb 9061 "[a-zA-Z_][a-zA-Z0-9$_]+\\(::[a-zA-Z_][a-zA-Z0-9$_]+\\)?")
f32b3b91
CD
9062 (if (fboundp 'buffer-substring-no-properties)
9063 (buffer-substring-no-properties begin (point))
9064 (buffer-substring begin (point)))))
9065
facebc7b
S
9066(defalias 'idlwave-function-menu
9067 (condition-case nil
f32b3b91
CD
9068 (progn
9069 (require 'func-menu)
facebc7b
S
9070 'function-menu)
9071 (error (condition-case nil
9072 (progn
9073 (require 'imenu)
9074 'imenu)
9075 (error nil)))))
f32b3b91 9076
52a244eb 9077;; Here we hack func-menu.el in order to support this new mode.
f32b3b91
CD
9078;; The latest versions of func-menu.el already have this stuff in, so
9079;; we hack only if it is not already there.
9080(when (fboundp 'eval-after-load)
9081 (eval-after-load "func-menu"
9082 '(progn
9083 (or (assq 'idlwave-mode fume-function-name-regexp-alist)
9084 (not (boundp 'fume-function-name-regexp-idl)) ; avoid problems
9085 (setq fume-function-name-regexp-alist
9086 (cons '(idlwave-mode . fume-function-name-regexp-idl)
9087 fume-function-name-regexp-alist)))
9088 (or (assq 'idlwave-mode fume-find-function-name-method-alist)
9089 (not (fboundp 'fume-find-next-idl-function-name)) ; avoid problems
9090 (setq fume-find-function-name-method-alist
9091 (cons '(idlwave-mode . fume-find-next-idl-function-name)
9092 fume-find-function-name-method-alist))))))
9093
9094(defun idlwave-edit-in-idlde ()
9095 "Edit the current file in IDL Development environment."
9096 (interactive)
9097 (start-process "idldeclient" nil
9098 idlwave-shell-explicit-file-name "-c" "-e"
f66f03de 9099 (buffer-file-name)))
4b1aaa8b 9100
f66f03de 9101(defvar idlwave-help-use-assistant)
f32b3b91
CD
9102(defun idlwave-launch-idlhelp ()
9103 "Start the IDLhelp application."
9104 (interactive)
f66f03de
S
9105 (if idlwave-help-use-assistant
9106 (idlwave-help-assistant-raise)
9107 (start-process "idlhelp" nil idlwave-help-application)))
4b1aaa8b 9108
f32b3b91
CD
9109;; Menus - using easymenu.el
9110(defvar idlwave-mode-menu-def
9111 `("IDLWAVE"
9112 ["PRO/FUNC menu" idlwave-function-menu t]
9113 ("Motion"
9114 ["Subprogram Start" idlwave-beginning-of-subprogram t]
9115 ["Subprogram End" idlwave-end-of-subprogram t]
9116 ["Block Start" idlwave-beginning-of-block t]
9117 ["Block End" idlwave-end-of-block t]
9118 ["Up Block" idlwave-backward-up-block t]
9119 ["Down Block" idlwave-down-block t]
9120 ["Skip Block Backward" idlwave-backward-block t]
9121 ["Skip Block Forward" idlwave-forward-block t])
9122 ("Mark"
9123 ["Subprogram" idlwave-mark-subprogram t]
9124 ["Block" idlwave-mark-block t]
9125 ["Header" idlwave-mark-doclib t])
9126 ("Format"
4b1aaa8b 9127 ["Indent Entire Statement" idlwave-indent-statement
f66f03de 9128 :active t :keys "C-u \\[indent-for-tab-command]" ]
f32b3b91 9129 ["Indent Subprogram" idlwave-indent-subprogram t]
f66f03de 9130 ["(Un)Comment Region" idlwave-toggle-comment-region t]
f32b3b91
CD
9131 ["Continue/Split line" idlwave-split-line t]
9132 "--"
9133 ["Toggle Auto Fill" idlwave-auto-fill-mode :style toggle
9134 :selected (symbol-value idlwave-fill-function)])
9135 ("Templates"
9136 ["Procedure" idlwave-procedure t]
9137 ["Function" idlwave-function t]
9138 ["Doc Header" idlwave-doc-header t]
9139 ["Log" idlwave-doc-modification t]
9140 "--"
9141 ["Case" idlwave-case t]
9142 ["For" idlwave-for t]
9143 ["Repeat" idlwave-repeat t]
9144 ["While" idlwave-while t]
9145 "--"
9146 ["Close Block" idlwave-close-block t])
15e42531 9147 ("Completion"
f32b3b91 9148 ["Complete" idlwave-complete t]
f66f03de 9149 ("Complete Specific"
f32b3b91
CD
9150 ["1 Procedure Name" (idlwave-complete 'procedure) t]
9151 ["2 Procedure Keyword" (idlwave-complete 'procedure-keyword) t]
9152 "--"
9153 ["3 Function Name" (idlwave-complete 'function) t]
9154 ["4 Function Keyword" (idlwave-complete 'function-keyword) t]
9155 "--"
9156 ["5 Procedure Method Name" (idlwave-complete 'procedure-method) t]
9157 ["6 Procedure Method Keyword" (idlwave-complete 'procedure-method-keyword) t]
9158 "--"
9159 ["7 Function Method Name" (idlwave-complete 'function-method) t]
9160 ["8 Function Method Keyword" (idlwave-complete 'function-method-keyword) t]
9161 "--"
15e42531
CD
9162 ["9 Class Name" idlwave-complete-class t]))
9163 ("Routine Info"
f32b3b91 9164 ["Show Routine Info" idlwave-routine-info t]
52a244eb 9165 ["Online Context Help" idlwave-context-help t]
f32b3b91
CD
9166 "--"
9167 ["Find Routine Source" idlwave-find-module t]
15e42531 9168 ["Resolve Routine" idlwave-resolve (featurep 'idlw-shell)]
f32b3b91
CD
9169 "--"
9170 ["Update Routine Info" idlwave-update-routine-info t]
f66f03de 9171 ["Rescan XML Help Catalog" idlwave-convert-xml-system-routine-info t]
f32b3b91 9172 "--"
52a244eb
S
9173 "IDL User Catalog"
9174 ["Select Catalog Directories" (idlwave-create-user-catalog-file nil) t]
15e42531 9175 ["Scan Directories" (idlwave-update-routine-info '(16))
5e72c6b2
S
9176 (and idlwave-path-alist (not idlwave-catalog-process))]
9177 ["Scan Directories &" (idlwave-update-routine-info '(64))
9178 (and idlwave-path-alist (not idlwave-catalog-process))]
15e42531
CD
9179 "--"
9180 "Routine Shadows"
9181 ["Check Current Buffer" idlwave-list-buffer-load-path-shadows t]
9182 ["Check Compiled Routines" idlwave-list-shell-load-path-shadows t]
9183 ["Check Everything" idlwave-list-all-load-path-shadows t])
9184 ("Misc"
9185 ["Kill auto-created buffers" idlwave-kill-autoloaded-buffers t]
9186 "--"
9187 ["Insert TAB character" idlwave-hard-tab t])
f32b3b91
CD
9188 "--"
9189 ("External"
f32b3b91
CD
9190 ["Start IDL shell" idlwave-shell t]
9191 ["Edit file in IDLDE" idlwave-edit-in-idlde t]
9192 ["Launch IDL Help" idlwave-launch-idlhelp t])
9193 "--"
9194 ("Customize"
9195 ["Browse IDLWAVE Group" idlwave-customize t]
9196 "--"
4b1aaa8b 9197 ["Build Full Customize Menu" idlwave-create-customize-menu
f32b3b91
CD
9198 (fboundp 'customize-menu-create)])
9199 ("Documentation"
9200 ["Describe Mode" describe-mode t]
9201 ["Abbreviation List" idlwave-list-abbrevs t]
9202 "--"
9203 ["Commentary in idlwave.el" idlwave-show-commentary t]
595ab50b 9204 ["Commentary in idlw-shell.el" idlwave-shell-show-commentary t]
f32b3b91
CD
9205 "--"
9206 ["Info" idlwave-info t]
9207 "--"
8c43762b 9208 ["Help with Topic" idlwave-help-assistant-help-with-topic
e08734e2 9209 idlwave-help-use-assistant]
f32b3b91
CD
9210 ["Launch IDL Help" idlwave-launch-idlhelp t])))
9211
9212(defvar idlwave-mode-debug-menu-def
9213 '("Debug"
9214 ["Start IDL shell" idlwave-shell t]
9215 ["Save and .RUN buffer" idlwave-shell-save-and-run
4b1aaa8b 9216 (and (boundp 'idlwave-shell-automatic-start)
f32b3b91
CD
9217 idlwave-shell-automatic-start)]))
9218
9219(if (or (featurep 'easymenu) (load "easymenu" t))
9220 (progn
4b1aaa8b
PE
9221 (easy-menu-define idlwave-mode-menu idlwave-mode-map
9222 "IDL and WAVE CL editing menu"
f32b3b91 9223 idlwave-mode-menu-def)
4b1aaa8b
PE
9224 (easy-menu-define idlwave-mode-debug-menu idlwave-mode-map
9225 "IDL and WAVE CL editing menu"
f32b3b91
CD
9226 idlwave-mode-debug-menu-def)))
9227
9228(defun idlwave-customize ()
5a0c3f56 9229 "Call the customize function with `idlwave' as argument."
f32b3b91 9230 (interactive)
4b1aaa8b 9231 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9232 ;; as well.
22d5821d
CD
9233 (or (featurep 'idlw-shell)
9234 (load "idlw-shell" t))
f32b3b91
CD
9235 (customize-browse 'idlwave))
9236
9237(defun idlwave-create-customize-menu ()
9238 "Create a full customization menu for IDLWAVE, insert it into the menu."
9239 (interactive)
9240 (if (fboundp 'customize-menu-create)
9241 (progn
4b1aaa8b 9242 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9243 ;; as well.
22d5821d
CD
9244 (or (featurep 'idlw-shell)
9245 (load "idlw-shell" t))
4b1aaa8b 9246 (easy-menu-change
f32b3b91
CD
9247 '("IDLWAVE") "Customize"
9248 `(["Browse IDLWAVE group" idlwave-customize t]
9249 "--"
9250 ,(customize-menu-create 'idlwave)
9251 ["Set" Custom-set t]
9252 ["Save" Custom-save t]
9253 ["Reset to Current" Custom-reset-current t]
9254 ["Reset to Saved" Custom-reset-saved t]
9255 ["Reset to Standard Settings" Custom-reset-standard t]))
9256 (message "\"IDLWAVE\"-menu now contains full customization menu"))
9257 (error "Cannot expand menu (outdated version of cus-edit.el)")))
9258
9259(defun idlwave-show-commentary ()
9260 "Use the finder to view the file documentation from `idlwave.el'."
9261 (interactive)
f32b3b91
CD
9262 (finder-commentary "idlwave.el"))
9263
9264(defun idlwave-shell-show-commentary ()
595ab50b 9265 "Use the finder to view the file documentation from `idlw-shell.el'."
f32b3b91 9266 (interactive)
595ab50b 9267 (finder-commentary "idlw-shell.el"))
f32b3b91
CD
9268
9269(defun idlwave-info ()
9270 "Read documentation for IDLWAVE in the info system."
9271 (interactive)
d6a277d0 9272 (info "idlwave"))
f32b3b91
CD
9273
9274(defun idlwave-list-abbrevs (arg)
9275 "Show the code abbreviations define in IDLWAVE mode.
9276This lists all abbrevs where the replacement text differs from the input text.
9277These are the ones the users want to learn to speed up their writing.
9278
9279The function does *not* list abbrevs which replace a word with itself
9280to call a hook. These hooks are used to change the case of words or
9281to blink the matching `begin', and the user does not need to know them.
9282
9283With arg, list all abbrevs with the corresponding hook.
9284
9285This function was written since `list-abbrevs' looks terrible for IDLWAVE mode."
9286
9287 (interactive "P")
9288 (let ((table (symbol-value 'idlwave-mode-abbrev-table))
9289 abbrevs
9290 str rpl func fmt (len-str 0) (len-rpl 0))
4b1aaa8b 9291 (mapatoms
f32b3b91
CD
9292 (lambda (sym)
9293 (if (symbol-value sym)
9294 (progn
9295 (setq str (symbol-name sym)
9296 rpl (symbol-value sym)
9297 func (symbol-function sym))
9298 (if arg
9299 (setq func (prin1-to-string func))
9300 (if (and (listp func) (stringp (nth 2 func)))
9301 (setq rpl (concat "EVAL: " (nth 2 func))
9302 func "")
9303 (setq func "")))
9304 (if (or arg (not (string= rpl str)))
9305 (progn
9306 (setq len-str (max len-str (length str)))
9307 (setq len-rpl (max len-rpl (length rpl)))
9308 (setq abbrevs (cons (list str rpl func) abbrevs)))))))
9309 table)
9310 ;; sort the list
9311 (setq abbrevs (sort abbrevs (lambda (a b) (string< (car a) (car b)))))
9312 ;; Make the format
9313 (setq fmt (format "%%-%ds %%-%ds %%s\n" len-str len-rpl))
9314 (with-output-to-temp-buffer "*Help*"
9315 (if arg
9316 (progn
4b1aaa8b 9317 (princ "Abbreviations and Actions in IDLWAVE-Mode\n")
f32b3b91
CD
9318 (princ "=========================================\n\n")
9319 (princ (format fmt "KEY" "REPLACE" "HOOK"))
9320 (princ (format fmt "---" "-------" "----")))
9321 (princ "Code Abbreviations and Templates in IDLWAVE-Mode\n")
9322 (princ "================================================\n\n")
9323 (princ (format fmt "KEY" "ACTION" ""))
9324 (princ (format fmt "---" "------" "")))
9325 (mapcar
9326 (lambda (list)
9327 (setq str (car list)
9328 rpl (nth 1 list)
9329 func (nth 2 list))
9330 (princ (format fmt str rpl func)))
9331 abbrevs)))
9332 ;; Make sure each abbreviation uses only one display line
9a529312 9333 (with-current-buffer "*Help*"
f32b3b91
CD
9334 (setq truncate-lines t)))
9335
5e72c6b2
S
9336;; Add .pro files to speedbar for support, if it's loaded
9337(eval-after-load "speedbar" '(speedbar-add-supported-extension ".pro"))
9338
5e72c6b2
S
9339;; Set an idle timer to load the routine info.
9340;; Will only work on systems which support this.
9341(or idlwave-routines (idlwave-start-load-rinfo-timer))
9342
15e42531 9343;; Run the hook
f32b3b91
CD
9344(run-hooks 'idlwave-load-hook)
9345
9346(provide 'idlwave)
9347
9348;;; idlwave.el ends here