* erc-stamp.el (erc-echo-timestamp):
[bpt/emacs.git] / lisp / progmodes / idlwave.el
CommitLineData
52a244eb 1;; idlwave.el --- IDL editing mode for GNU Emacs
d7a0267c
GM
2
3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
4;; 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>
e08734e2 10;; Version: 6.1_em22
f32b3b91
CD
11;; Keywords: languages
12
e8af40ee 13;; This file is part of GNU Emacs.
f32b3b91
CD
14
15;; GNU Emacs is free software; you can redistribute it and/or modify
16;; it under the terms of the GNU General Public License as published by
1a484753 17;; the Free Software Foundation; either version 3, or (at your option)
f32b3b91
CD
18;; any later version.
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
26;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
27;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28;; Boston, MA 02110-1301, USA.
f32b3b91
CD
29
30;;; Commentary:
31
f66f03de
S
32;; IDLWAVE enables feature-rich development and interaction with IDL,
33;; the Interactive Data Language. It provides a compelling,
34;; full-featured alternative to the IDLDE development environment
35;; bundled with IDL.
3938cb82 36
52a244eb
S
37;; In the remotely distant past, based on pascal.el, though bears
38;; little resemblance to it now.
f32b3b91
CD
39;;
40;; Incorporates many ideas, such as abbrevs, action routines, and
41;; continuation line indenting, from wave.el.
42;; wave.el original written by Lubos Pochman, Precision Visuals, Boulder.
43;;
44;; See the mode description ("C-h m" in idlwave-mode or "C-h f idlwave-mode")
45;; for features, key bindings, and info.
46;; Also, Info format documentation is available with `M-x idlwave-info'
47;;
5e72c6b2
S
48;; New versions of IDLWAVE, documentation, and more information
49;; available from:
50;; http://idlwave.org
f32b3b91
CD
51;;
52;; INSTALLATION
53;; ============
54;;
55;; Follow the instructions in the INSTALL file of the distribution.
56;; In short, put this file on your load path and add the following
57;; lines to your .emacs file:
58;;
59;; (autoload 'idlwave-mode "idlwave" "IDLWAVE Mode" t)
8c7b4ec8 60;; (autoload 'idlwave-shell "idlw-shell" "IDLWAVE Shell" t)
f32b3b91
CD
61;; (setq auto-mode-alist (cons '("\\.pro\\'" . idlwave-mode) auto-mode-alist))
62;;
63;;
64;; SOURCE
65;; ======
66;;
76959b77 67;; The newest version of this file is available from the maintainer's
52a244eb 68;; Webpage:
f32b3b91 69;;
5e72c6b2 70;; http://idlwave.org
f32b3b91
CD
71;;
72;; DOCUMENTATION
73;; =============
74;;
52a244eb
S
75;; IDLWAVE is documented online in info format. A printable version
76;; of the documentation is available from the maintainers webpage (see
77;; SOURCE).
775591f7 78;;
4b1aaa8b 79;;
f32b3b91
CD
80;; ACKNOWLEDGMENTS
81;; ===============
82;;
83;; Thanks to the following people for their contributions and comments:
84;;
52a244eb
S
85;; Ulrik Dickow <dickow_at_nbi.dk>
86;; Eric E. Dors <edors_at_lanl.gov>
87;; Stein Vidar H. Haugan <s.v.h.haugan_at_astro.uio.no>
88;; David Huenemoerder <dph_at_space.mit.edu>
89;; Kevin Ivory <Kevin.Ivory_at_linmpi.mpg.de>
90;; Dick Jackson <dick_at_d-jackson.com>
91;; Xuyong Liu <liu_at_stsci.edu>
92;; Simon Marshall <Simon.Marshall_at_esrin.esa.it>
93;; Laurent Mugnier <mugnier_at_onera.fr>
94;; Lubos Pochman <lubos_at_rsinc.com>
95;; Bob Portmann <portmann_at_al.noaa.gov>
96;; Patrick M. Ryan <pat_at_jaameri.gsfc.nasa.gov>
97;; Marty Ryba <ryba_at_ll.mit.edu>
98;; Paul Sorenson <aardvark62_at_msn.com>
99;; Phil Sterne <sterne_at_dublin.llnl.gov>
100;; Phil Williams <williams_at_irc.chmcc.org>
f32b3b91
CD
101;;
102;; CUSTOMIZATION:
103;; =============
104;;
52a244eb
S
105;; IDLWAVE has extensive customize support; to learn about the
106;; variables which control the mode's behavior, use `M-x
107;; idlwave-customize'.
f32b3b91
CD
108;;
109;; You can set your own preferred values with Customize, or with Lisp
110;; code in .emacs. For an example of what to put into .emacs, check
52a244eb
S
111;; the TexInfo documentation or see a complete .emacs available at the
112;; website.
f32b3b91
CD
113;;
114;; KNOWN PROBLEMS:
115;; ==============
116;;
76959b77
S
117;; IDLWAVE support for the IDL-derived PV-WAVE CL language of Visual
118;; Numerics, Inc. is growing less and less complete as the two
119;; languages grow increasingly apart. The mode probably shouldn't
3938cb82 120;; even have "WAVE" in its title, but it's catchy, and was required
52a244eb 121;; to avoid conflict with the CORBA idl.el mode. Caveat WAVEor.
76959b77 122;;
f32b3b91
CD
123;; Moving the point backwards in conjunction with abbrev expansion
124;; does not work as I would like it, but this is a problem with
125;; emacs abbrev expansion done by the self-insert-command. It ends
126;; up inserting the character that expanded the abbrev after moving
127;; point backward, e.g., "\cl" expanded with a space becomes
128;; "LONG( )" with point before the close paren. This is solved by
4b1aaa8b 129;; using a temporary function in `post-command-hook' - not pretty,
595ab50b 130;; but it works.
f32b3b91
CD
131;;
132;; Tabs and spaces are treated equally as whitespace when filling a
133;; comment paragraph. To accomplish this, tabs are permanently
134;; replaced by spaces in the text surrounding the paragraph, which
135;; may be an undesirable side-effect. Replacing tabs with spaces is
136;; limited to comments only and occurs only when a comment
137;; paragraph is filled via `idlwave-fill-paragraph'.
138;;
52a244eb
S
139;; Muti-statement lines (using "&") on block begin and end lines can
140;; ruin the formatting. For example, multiple end statements on a
141;; line: endif & endif. Using "&" outside of block begin/end lines
142;; should be okay.
f32b3b91 143;;
76959b77
S
144;; Determining the expression at point for printing and other
145;; examination commands is somewhat rough: currently only fairly
146;; simple entities are found. You can always drag-select or examine
52a244eb 147;; a pre-selected region.
f32b3b91 148;;
f32b3b91
CD
149;; When forcing completion of method keywords, the initial
150;; query for a method has multiple entries for some methods. Would
595ab50b 151;; be too difficult to fix this hardly used case.
f32b3b91
CD
152;;
153\f
154;;; Code:
155
52a244eb 156
f32b3b91 157(eval-when-compile (require 'cl))
52a244eb
S
158(require 'idlw-help)
159
160;; For XEmacs
161(unless (fboundp 'line-beginning-position)
162 (defalias 'line-beginning-position 'point-at-bol))
163(unless (fboundp 'line-end-position)
164 (defalias 'line-end-position 'point-at-eol))
165(unless (fboundp 'char-valid-p)
166 (defalias 'char-valid-p 'characterp))
f66f03de
S
167(unless (fboundp 'match-string-no-properties)
168 (defalias 'match-string-no-properties 'match-string))
f32b3b91 169
3938cb82
S
170(if (not (fboundp 'cancel-timer))
171 (condition-case nil
172 (require 'timer)
173 (error nil)))
174
f32b3b91 175(eval-and-compile
5e72c6b2
S
176 ;; Kludge to allow `defcustom' for Emacs 19.
177 (condition-case () (require 'custom) (error nil))
178 (if (and (featurep 'custom) (fboundp 'custom-declare-variable))
179 nil ;; We've got what we needed
180 ;; We have the old or no custom-library, hack around it!
181 (defmacro defgroup (&rest args) nil)
4b1aaa8b 182 (defmacro defcustom (var value doc &rest args)
5e72c6b2 183 `(defvar ,var ,value ,doc))))
f32b3b91 184
73e72da4
DN
185(declare-function idlwave-shell-get-path-info "idlw-shell")
186(declare-function idlwave-shell-temp-file "idlw-shell")
187(declare-function idlwave-shell-is-running "idlw-shell")
188(declare-function widget-value "wid-edit" (widget))
189(declare-function comint-dynamic-complete-filename "comint" ())
190(declare-function Info-goto-node "info" (nodename &optional fork))
191
f32b3b91 192(defgroup idlwave nil
31b58798 193 "Major mode for editing IDL .pro files."
f32b3b91 194 :tag "IDLWAVE"
4b1aaa8b 195 :link '(url-link :tag "Home Page"
5e72c6b2 196 "http://idlwave.org")
595ab50b
CD
197 :link '(emacs-commentary-link :tag "Commentary in idlw-shell.el"
198 "idlw-shell.el")
f32b3b91
CD
199 :link '(emacs-commentary-link :tag "Commentary in idlwave.el" "idlwave.el")
200 :link '(custom-manual "(idlwave)Top")
201 :prefix "idlwave"
202 :group 'languages)
203
52a244eb 204
f32b3b91
CD
205;;; Variables for indentation behavior ---------------------------------------
206
207(defgroup idlwave-code-formatting nil
208 "Indentation and formatting options for IDLWAVE mode."
209 :group 'idlwave)
210
f66f03de 211(defcustom idlwave-main-block-indent 2
f32b3b91
CD
212 "*Extra indentation for the main block of code.
213That is the block between the FUNCTION/PRO statement and the END
214statement for that program unit."
215 :group 'idlwave-code-formatting
216 :type 'integer)
217
f66f03de 218(defcustom idlwave-block-indent 3
f32b3b91
CD
219 "*Extra indentation applied to block lines.
220If you change this, you probably also want to change `idlwave-end-offset'."
221 :group 'idlwave-code-formatting
222 :type 'integer)
223
f66f03de 224(defcustom idlwave-end-offset -3
f32b3b91
CD
225 "*Extra indentation applied to block END lines.
226A value equal to negative `idlwave-block-indent' will make END lines
227line up with the block BEGIN lines."
228 :group 'idlwave-code-formatting
229 :type 'integer)
230
f66f03de 231(defcustom idlwave-continuation-indent 3
f32b3b91
CD
232 "*Extra indentation applied to continuation lines.
233This extra offset applies to the first of a set of continuation lines.
5e72c6b2
S
234The following lines receive the same indentation as the first."
235 :group 'idlwave-code-formatting
236 :type 'integer)
237
f66f03de 238(defcustom idlwave-max-extra-continuation-indent 40
5e72c6b2
S
239 "*Maximum additional indentation for special continuation indent.
240Several special indentations are tried to help line up continuation
241lines in routine calls or definitions, other statements with
134b6671 242parentheses, or assignment statements. This variable specifies a
5e72c6b2
S
243maximum amount by which this special indentation can exceed the
244standard continuation indentation, otherwise defaulting to a fixed
245offset. Set to 0 to effectively disable all special continuation
246indentation, or to a large number (like 100) to enable it in all
52a244eb 247cases. See also `idlwave-indent-to-open-paren', which can override
5e72c6b2 248this variable."
f32b3b91
CD
249 :group 'idlwave-code-formatting
250 :type 'integer)
251
5e72c6b2
S
252(defcustom idlwave-indent-to-open-paren t
253 "*Non-nil means, indent continuation lines to innermost open
254parenthesis. This indentation occurs even if otherwise disallowed by
255`idlwave-max-extra-continuation-indent'. Matching parens and the
256interleaving args are lined up. Example:
257
258 x = function_a(function_b(function_c( a, b, [1,2,3, $
259 4,5,6 $
260 ], $
261 c, d $
262 )))
263
264When this variable is nil, paren alignment may still occur, based on
265the value of `max-extra-continuation-indent', which, if zero, would
266yield:
267
268 x = function_a(function_b(function_c( a, b, [1,2,3, $
269 4,5,6 $
270 ], $
271 c, d $
272 )))"
273 :group 'idlwave-code-formatting
274 :type 'boolean)
275
52a244eb
S
276(defcustom idlwave-indent-parens-nested nil
277 "*Non-nil means, indent continuation lines with parens by nesting
278lines at consecutively deeper levels."
279 :group 'idlwave-code-formatting
280 :type 'boolean)
281
282
f32b3b91
CD
283(defcustom idlwave-hanging-indent t
284 "*If set non-nil then comment paragraphs are indented under the
285hanging indent given by `idlwave-hang-indent-regexp' match in the first line
286of the paragraph."
287 :group 'idlwave-code-formatting
288 :type 'boolean)
289
290(defcustom idlwave-hang-indent-regexp "- "
291 "*Regular expression matching the position of the hanging indent
292in the first line of a comment paragraph. The size of the indent
293extends to the end of the match for the regular expression."
294 :group 'idlwave-code-formatting
295 :type 'regexp)
296
297(defcustom idlwave-use-last-hang-indent nil
298 "*If non-nil then use last match on line for `idlwave-indent-regexp'."
299 :group 'idlwave-code-formatting
300 :type 'boolean)
301
302(defcustom idlwave-fill-comment-line-only t
303 "*If non-nil then auto fill will only operate on comment lines."
304 :group 'idlwave-code-formatting
305 :type 'boolean)
306
307(defcustom idlwave-auto-fill-split-string t
308 "*If non-nil then auto fill will split strings with the IDL `+' operator.
4b1aaa8b
PE
309When the line end falls within a string, string concatenation with the
310'+' operator will be used to distribute a long string over lines.
f32b3b91
CD
311If nil and a string is split then a terminal beep and warning are issued.
312
313This variable is ignored when `idlwave-fill-comment-line-only' is
314non-nil, since in this case code is not auto-filled."
315 :group 'idlwave-code-formatting
316 :type 'boolean)
317
318(defcustom idlwave-split-line-string t
319 "*If non-nil then `idlwave-split-line' will split strings with `+'.
320When the splitting point of a line falls inside a string, split the string
321using the `+' string concatenation operator. If nil and a string is
322split then a terminal beep and warning are issued."
323 :group 'idlwave-code-formatting
324 :type 'boolean)
325
326(defcustom idlwave-no-change-comment ";;;"
327 "*The indentation of a comment that starts with this regular
328expression will not be changed. Note that the indentation of a comment
329at the beginning of a line is never changed."
330 :group 'idlwave-code-formatting
331 :type 'string)
332
333(defcustom idlwave-begin-line-comment nil
334 "*A comment anchored at the beginning of line.
335A comment matching this regular expression will not have its
336indentation changed. If nil the default is \"^;\", i.e., any line
337beginning with a \";\". Expressions for comments at the beginning of
338the line should begin with \"^\"."
339 :group 'idlwave-code-formatting
340 :type '(choice (const :tag "Any line beginning with `;'" nil)
341 'regexp))
342
343(defcustom idlwave-code-comment ";;[^;]"
344 "*A comment that starts with this regular expression on a line by
345itself is indented as if it is a part of IDL code. As a result if
346the comment is not preceded by whitespace it is unchanged."
347 :group 'idlwave-code-formatting
348 :type 'regexp)
349
350;; Comments not matching any of the above will be indented as a
351;; right-margin comment, i.e., to a minimum of `comment-column'.
352
f32b3b91
CD
353;;; Routine Info and Completion ---------------------------------------
354
15e42531
CD
355(defgroup idlwave-routine-info nil
356 "Routine Info options for IDLWAVE mode."
f32b3b91
CD
357 :group 'idlwave)
358
52a244eb
S
359(defcustom idlwave-use-library-catalogs t
360 "*Non-nil means search the IDL path for library catalog files.
361
362These files, named .idlwave_catalog, document routine information for
363individual directories and libraries of IDL .pro files. Many popular
364libraries come with catalog files by default, so leaving this on is a
365usually a good idea.."
366 :group 'idlwave-routine-info
367 :type 'boolean)
5e72c6b2
S
368
369(defcustom idlwave-init-rinfo-when-idle-after 10
f66f03de
S
370 "*Seconds of idle time before routine info is automatically
371initialized. Initializing the routine info can take a long time, in
372particular if a large number of library catalogs are involved. When
373Emacs is idle for more than the number of seconds specified by this
374variable, it starts the initialization. The process is split into
375five steps, in order to keep work interruption as short as possible.
376If one of the steps finishes, and no user input has arrived in the
377mean time, initialization proceeds immediately to the next step. A
378good value for this variable is about 1/3 of the time initialization
379take in your setup. So if you have a fast machine and no problems
380with a slow network connection, don't hesitate to set this to 2
381seconds. A Value of 0 means, don't initialize automatically, but
382instead wait until routine information is needed, and initialize
383then."
5e72c6b2
S
384 :group 'idlwave-routine-info
385 :type 'number)
386
f32b3b91 387(defcustom idlwave-scan-all-buffers-for-routine-info t
15e42531
CD
388 "*Non-nil means, scan buffers for IDL programs when updating info.
389The scanning is done by the command `idlwave-update-routine-info'.
390The following values are allowed:
391
392nil Don't scan any buffers.
393t Scan all idlwave-mode buffers in the current editing session.
394current Scan only the current buffer, but no other buffers."
395 :group 'idlwave-routine-info
396 :type '(choice
397 (const :tag "No buffer" nil)
398 (const :tag "All buffers" t)
399 (const :tag "Current buffer only" 'current)))
f32b3b91
CD
400
401(defcustom idlwave-query-shell-for-routine-info t
402 "*Non-nil means query the shell for info about compiled routines.
403Querying the shell is useful to get information about compiled modules,
404and it is turned on by default. However, when you have a complete library
405scan, this is not necessary."
15e42531 406 :group 'idlwave-routine-info
f32b3b91
CD
407 :type 'boolean)
408
15e42531
CD
409(defcustom idlwave-auto-routine-info-updates
410 '(find-file save-buffer kill-buffer compile-buffer)
411 "*Controls under what circumstances routine info is updated automatically.
412Possible values:
413nil Never
414t All available
facebc7b 415\(...) A list of circumstances. Allowed members are:
15e42531
CD
416 find-file Add info for new IDLWAVE buffers.
417 save-buffer Update buffer info when buffer is saved
418 kill-buffer Remove buffer info when buffer gets killed
419 compile-buffer Update shell info after `idlwave-shell-save-and...'"
420 :group 'idlwave-routine-info
421 :type '(choice
422 (const :tag "Never" nil)
423 (const :tag "As often as possible" t)
424 (set :tag "Checklist" :greedy t
425 (const :tag "When visiting a file" find-file)
426 (const :tag "When saving a buffer" save-buffer)
427 (const :tag "After a buffer was killed" kill-buffer)
428 (const :tag "After a buffer was compiled successfully, update shell info" compile-buffer))))
4b1aaa8b 429
15e42531
CD
430(defcustom idlwave-rinfo-max-source-lines 5
431 "*Maximum number of source files displayed in the Routine Info window.
432When an integer, it is the maximum number of source files displayed.
433t means to show all source files."
434 :group 'idlwave-routine-info
435 :type 'integer)
436
f32b3b91 437(defcustom idlwave-library-path nil
8c43762b 438 "Library path for Windows and MacOS (OS9). Not needed under UNIX.
f66f03de
S
439When selecting the directories to scan for IDL user catalog routine
440info, IDLWAVE can, under UNIX, query the shell for the exact search
441path \(the value of !PATH). However, under Windows and MacOS
8c43762b 442\(pre-OSX), the IDLWAVE shell does not work. In this case, this
f66f03de
S
443variable can be set to specify the paths where IDLWAVE can find PRO
444files. The shell will only be asked for a list of paths when this
445variable is nil. The value is a list of directories. A directory
446preceeded by a `+' will be searched recursively. If you set this
447variable on a UNIX system, the shell will not be queried. See also
448`idlwave-system-directory'."
15e42531 449 :group 'idlwave-routine-info
f32b3b91
CD
450 :type '(repeat (directory)))
451
15e42531 452(defcustom idlwave-system-directory ""
52a244eb
S
453 "The IDL system directory for Windows and MacOS. Not needed under
454UNIX. Set this to the value of the `!DIR' system variable in IDL.
455IDLWAVE uses this to find out which of the library routines belong to
456the official system library. All files inside the `lib' subdirectory
457are considered system library files - so don't install private stuff
458in this directory. On UNIX systems, IDLWAVE queries the shell for the
459value of `!DIR'. See also `idlwave-library-path'."
15e42531
CD
460 :group 'idlwave-routine-info
461 :type 'directory)
462
f66f03de 463;; Configuration files
4b1aaa8b 464(defcustom idlwave-config-directory
52a244eb
S
465 (convert-standard-filename "~/.idlwave")
466 "*Directory for configuration files and user-library catalog."
15e42531 467 :group 'idlwave-routine-info
f32b3b91
CD
468 :type 'file)
469
52a244eb 470(defvar idlwave-user-catalog-file "idlusercat.el")
f66f03de 471(defvar idlwave-xml-system-rinfo-converted-file "idl_xml_rinfo.el")
52a244eb
S
472(defvar idlwave-path-file "idlpath.el")
473
474(defvar idlwave-libinfo-file nil
475 "*Obsolete variable, no longer used.")
476
15e42531
CD
477(defcustom idlwave-special-lib-alist nil
478 "Alist of regular expressions matching special library directories.
479When listing routine source locations, IDLWAVE gives a short hint where
4b1aaa8b 480the file defining the routine is located. By default it lists `SystemLib'
15e42531
CD
481for routines in the system library `!DIR/lib' and `Library' for anything
482else. This variable can define additional types. The car of each entry
483is a regular expression matching the file name (they normally will match
484on the path). The cdr is the string to be used as identifier. Max 10
485chars are allowed."
486 :group 'idlwave-routine-info
487 :type '(repeat
488 (cons regexp string)))
489
52a244eb 490(defcustom idlwave-auto-write-paths t
4b1aaa8b 491 "Write out path (!PATH) and system directory (!DIR) info automatically.
52a244eb
S
492Path info is needed to locate library catalog files. If non-nil,
493whenever the path-list changes as a result of shell-query, etc., it is
494written to file. Otherwise, the menu option \"Write Paths\" can be
495used to force a write."
496 :group 'idlwave-routine-info
05a1abfc 497 :type 'boolean)
775591f7 498
15e42531
CD
499(defgroup idlwave-completion nil
500 "Completion options for IDLWAVE mode."
501 :prefix "idlwave"
502 :group 'idlwave)
503
f32b3b91
CD
504(eval-and-compile
505 (defconst idlwave-tmp
506 '(choice :tag "by applying the function"
507 (const upcase)
508 (const downcase)
509 (const capitalize)
510 (const preserve)
511 (symbol :tag "Other"))))
512
f32b3b91
CD
513(defcustom idlwave-completion-case '((routine . upcase)
514 (keyword . upcase)
515 (class . preserve)
516 (method . preserve))
517 "Association list setting the case of completed words.
518
519This variable determines the case (UPPER/lower/Capitalized...) of
520words inserted into the buffer by completion. The preferred case can
521be specified separately for routine names, keywords, classes and
4b1aaa8b 522methods.
f32b3b91
CD
523This alist should therefore have entries for `routine' (normal
524functions and procedures, i.e. non-methods), `keyword', `class', and
525`method'. Plausible values are
526
527upcase upcase whole word, like `BOX_CURSOR'
528downcase downcase whole word, like `read_ppm'
529capitalize capitalize each part, like `Widget_Control'
530preserve preserve case as is, like `IDLgrView'
531
532The value can also be any Emacs Lisp function which transforms the
533case of characters in a string.
534
535A value of `preserve' means that the case of the completed word is
536identical to the way it was written in the definition statement of the
537routine. This was implemented to allow for mixed-case completion, in
538particular of object classes and methods.
539If a completable word is defined in multiple locations, the meaning of
540`preserve' is not unique since the different definitions might be
541cased differently. Therefore IDLWAVE always takes the case of the
542*first* definition it encounters during routine info collection and
543uses the case derived from it consistently.
544
545Note that a lowercase-only string in the buffer will always be completed in
546lower case (but see the variable `idlwave-completion-force-default-case').
547
548After changing this variable, you need to either restart Emacs or press
549`C-u C-c C-i' to update the internal lists."
15e42531 550 :group 'idlwave-completion
f32b3b91
CD
551 :type `(repeat
552 (cons (symbol :tag "Derive completion case for")
553 ,idlwave-tmp)))
554
555(defcustom idlwave-completion-force-default-case nil
556 "*Non-nil means, completion will always honor `idlwave-completion-case'.
557When nil, only the completion of a mixed case or upper case string
558will honor the default settings in `idlwave-completion-case', while
559the completion of lower case strings will be completed entirely in
560lower case."
15e42531 561 :group 'idlwave-completion
f32b3b91
CD
562 :type 'boolean)
563
564(defcustom idlwave-complete-empty-string-as-lower-case nil
565 "*Non-nil means, the empty string is considered downcase for completion.
566The case of what is already in the buffer determines the case of completions.
567When this variable is non-nil, the empty string is considered to be downcase.
568Completing on the empty string then offers downcase versions of the possible
569completions."
15e42531 570 :group 'idlwave-completion
f32b3b91
CD
571 :type 'boolean)
572
573(defvar idlwave-default-completion-case-is-down nil
574 "Obsolete variable. See `idlwave-complete-empty-string-as-lower-case' and
575`idlwave-completion-case'.")
576
577(defcustom idlwave-buffer-case-takes-precedence nil
578 "*Non-nil means, the case of tokens in buffers dominates over system stuff.
579To make this possible, we need to re-case everything each time we update
580the routine info from the buffers. This is slow.
581The default is to consider the case given in the system and library files
582first which makes updating much faster."
15e42531
CD
583 :group 'idlwave-completion
584 :type 'boolean)
585
586(defcustom idlwave-highlight-help-links-in-completion t
587 "*Non-nil means, highlight completions for which system help is available.
588Help can then be accessed with mouse-3.
589This option is only effective when the online help system is installed."
590 :group 'idlwave-completion
f32b3b91
CD
591 :type 'boolean)
592
05a1abfc
CD
593(defcustom idlwave-support-inheritance t
594 "Non-nil means, treat inheritance with completion, online help etc.
cef6cafe 595When nil, IDLWAVE only knows about the native methods and tags of a class,
05a1abfc
CD
596not about inherited ones."
597 :group 'idlwave-routine-info
598 :type 'boolean)
599
5e72c6b2
S
600(defcustom idlwave-keyword-class-inheritance '("^[gs]etproperty$" "^init$")
601 "List of regular expressions for class-driven keyword inheritance.
602Keyword inheritance is often tied to class inheritance by \"chaining\"
603up the class tree. While it cannot be assumed that the presence of an
604_EXTRA or _REF_EXTRA symbol guarantees such chaining will occur, for
605certain methods this assumption is almost always true. The methods
606for which to assume this can be set here."
607 :group 'idlwave-routine-info
608 :type '(repeat (regexp :tag "Match method:")))
4b1aaa8b 609
5e72c6b2 610
f32b3b91
CD
611(defcustom idlwave-completion-show-classes 1
612 "*Number of classes to show when completing object methods and keywords.
613When completing methods or keywords for an object with unknown class,
2e8b9c7d 614the *Completions* buffer will show the valid classes for each completion
f32b3b91
CD
615like this:
616
617MyMethod <Class1,Class2,Class3>
618
619The value of this variable may be nil to inhibit display, or an integer to
620indicate the maximum number of classes to display.
621
622On XEmacs, a full list of classes will also be placed into a `help-echo'
623property on the competion items, so that the list of classes for the current
624item is displayed in the echo area. If the value of this variable is a
625negative integer, the `help-echo' property will be suppressed."
15e42531 626 :group 'idlwave-completion
f32b3b91
CD
627 :type '(choice (const :tag "Don't show" nil)
628 (integer :tag "Number of classes shown" 1)))
629
630(defcustom idlwave-completion-fontify-classes t
631 "*Non-nil means, fontify the classes in completions buffer.
632This makes it easier to distinguish the completion items from the extra
633class info listed. See `idlwave-completion-show-classes'."
15e42531 634 :group 'idlwave-completion
f32b3b91
CD
635 :type 'boolean)
636
637(defcustom idlwave-query-class '((method-default . nil)
638 (keyword-default . nil))
639 "Association list governing specification of object classes for completion.
640
5e72c6b2
S
641When IDLWAVE tries to complete object-oriented methods, it usually
642cannot determine the class of a given object from context. In order
643to provide the user with a correct list of methods or keywords, it
76959b77
S
644needs to determine the appropriate class. IDLWAVE has two ways of
645doing this (well, three ways if you count the shell... see
646`idlwave-shell-query-for-class'):
647
6481. Combine the items of all available classes which contain this
649 method for the purpose of completion. So when completing a method,
650 all methods of all known classes are available, and when completing
651 a keyword, all keywords allowed for this method in any class are
652 shown. This behavior is very much like normal completion and is
653 therefore the default. It works much better than one might think -
654 only for the INIT, GETPROPERTY and SETPROPERTY the keyword lists
655 become uncomfortably long. See also
5e72c6b2 656 `idlwave-completion-show-classes'.
f32b3b91
CD
657
6582. The second possibility is to ask the user on each occasion. To
659 make this less interruptive, IDLWAVE can store the class as a text
660 property on the object operator `->'. For a given object in the
661 source code, class selection will then be needed only once
662 - for example to complete the method. Keywords to the method can
663 then be completed directly, because the class is already known.
664 You will have to turn on the storage of the selected class
665 explicitly with the variable `idlwave-store-inquired-class'.
666
5e72c6b2
S
667This variable allows you to configure IDLWAVE's method and
668method-keyword completion behavior. Its value is an alist, which
669should contain at least two elements: (method-default . VALUE) and
facebc7b 670\(keyword-default . VALUE), where VALUE is either t or nil. These
5e72c6b2
S
671specify if the class should be found during method and keyword
672completion, respectively.
f32b3b91 673
4b1aaa8b 674The alist may have additional entries specifying exceptions from the
f32b3b91
CD
675keyword completion rule for specific methods, like INIT or
676GETPROPERTY. In order to turn on class specification for the INIT
677method, add an entry (\"INIT\" . t). The method name must be ALL-CAPS."
15e42531 678 :group 'idlwave-completion
f32b3b91
CD
679 :type '(list
680 (cons (const method-default)
681 (boolean :tag "Determine class when completing METHODS "))
682 (cons (const keyword-default)
683 (boolean :tag "Determine class when completing KEYWORDS "))
684 (repeat
685 :tag "Exceptions to defaults"
686 :inline t
687 (cons (string :tag "MODULE" :value "")
688 (boolean :tag "Determine class for this method")))))
689
f66f03de 690(defcustom idlwave-store-inquired-class t
f32b3b91
CD
691 "*Non-nil means, store class of a method call as text property on `->'.
692IDLWAVE sometimes has to ask the user for the class associated with a
693particular object method call. This happens during the commands
694`idlwave-routine-info' and `idlwave-complete', depending upon the
695value of the variable `idlwave-query-class'.
696
697When you specify a class, this information can be stored as a text
4b1aaa8b 698property on the `->' arrow in the source code, so that during the same
f32b3b91
CD
699editing session, IDLWAVE will not have to ask again. When this
700variable is non-nil, IDLWAVE will store and reuse the class information.
701The class stored can be checked and removed with `\\[idlwave-routine-info]'
702on the arrow.
703
704The default of this variable is nil, since the result of commands then
705is more predictable. However, if you know what you are doing, it can
706be nice to turn this on.
707
708An arrow which knows the class will be highlighted with
709`idlwave-class-arrow-face'. The command \\[idlwave-routine-info]
710displays (with prefix arg: deletes) the class stored on the arrow
711at point."
15e42531 712 :group 'idlwave-completion
f32b3b91
CD
713 :type 'boolean)
714
715(defcustom idlwave-class-arrow-face 'bold
716 "*Face to highlight object operator arrows `->' which carry a class property.
717When IDLWAVE stores a class name as text property on an object arrow
facebc7b 718\(see variable `idlwave-store-inquired-class', it highlights the arrow
f32b3b91 719with this font in order to remind the user that this arrow is special."
15e42531 720 :group 'idlwave-completion
f32b3b91
CD
721 :type 'symbol)
722
723(defcustom idlwave-resize-routine-help-window t
724 "*Non-nil means, resize the Routine-info *Help* window to fit the content."
15e42531 725 :group 'idlwave-completion
f32b3b91
CD
726 :type 'boolean)
727
728(defcustom idlwave-keyword-completion-adds-equal t
729 "*Non-nil means, completion automatically adds `=' after completed keywords."
15e42531 730 :group 'idlwave-completion
f32b3b91
CD
731 :type 'boolean)
732
733(defcustom idlwave-function-completion-adds-paren t
734 "*Non-nil means, completion automatically adds `(' after completed function.
0ff9b955 735nil means, don't add anything.
f32b3b91
CD
736A value of `2' means, also add the closing parenthesis and position cursor
737between the two."
15e42531 738 :group 'idlwave-completion
f32b3b91
CD
739 :type '(choice (const :tag "Nothing" nil)
740 (const :tag "(" t)
741 (const :tag "()" 2)))
742
743(defcustom idlwave-completion-restore-window-configuration t
744 "*Non-nil means, try to restore the window configuration after completion.
745When completion is not unique, Emacs displays a list of completions.
746This messes up your window configuration. With this variable set, IDLWAVE
747restores the old configuration after successful completion."
15e42531 748 :group 'idlwave-completion
f32b3b91
CD
749 :type 'boolean)
750
751;;; Variables for abbrev and action behavior -----------------------------
752
753(defgroup idlwave-abbrev-and-indent-action nil
754 "IDLWAVE performs actions when expanding abbreviations or indenting lines.
755The variables in this group govern this."
756 :group 'idlwave)
757
758(defcustom idlwave-do-actions nil
759 "*Non-nil means performs actions when indenting.
760The actions that can be performed are listed in `idlwave-indent-action-table'."
761 :group 'idlwave-abbrev-and-indent-action
762 :type 'boolean)
763
764(defcustom idlwave-abbrev-start-char "\\"
765 "*A single character string used to start abbreviations in abbrev mode.
766Possible characters to chose from: ~`\%
767or even '?'. '.' is not a good choice because it can make structure
768field names act like abbrevs in certain circumstances.
769
770Changes to this in `idlwave-mode-hook' will have no effect. Instead a user
771must set it directly using `setq' in the .emacs file before idlwave.el
772is loaded."
773 :group 'idlwave-abbrev-and-indent-action
774 :type 'string)
775
776(defcustom idlwave-surround-by-blank nil
777 "*Non-nil means, enable `idlwave-surround'.
595ab50b 778If non-nil, `=',`<',`>',`&',`,', `->' are surrounded with spaces by
f32b3b91
CD
779`idlwave-surround'.
780See help for `idlwave-indent-action-table' for symbols using `idlwave-surround'.
781
782Also see the default key bindings for keys using `idlwave-surround'.
783Keys are bound and made into actions calling `idlwave-surround' with
784`idlwave-action-and-binding'.
785See help for `idlwave-action-and-binding' for examples.
786
787Also see help for `idlwave-surround'."
788 :group 'idlwave-abbrev-and-indent-action
789 :type 'boolean)
790
791(defcustom idlwave-pad-keyword t
52a244eb
S
792 "*Non-nil means pad '=' in keywords (routine calls or defs) like assignment.
793Whenever `idlwave-surround' is non-nil then this affects how '=' is
794padded for keywords and for variables. If t, pad the same as for
795assignments. If nil then spaces are removed. With any other value,
796spaces are left unchanged."
f32b3b91 797 :group 'idlwave-abbrev-and-indent-action
15e42531
CD
798 :type '(choice
799 (const :tag "Pad like assignments" t)
800 (const :tag "Remove space near `='" nil)
801 (const :tag "Keep space near `='" 'keep)))
f32b3b91
CD
802
803(defcustom idlwave-show-block t
804 "*Non-nil means point blinks to block beginning for `idlwave-show-begin'."
805 :group 'idlwave-abbrev-and-indent-action
806 :type 'boolean)
807
808(defcustom idlwave-expand-generic-end nil
809 "*Non-nil means expand generic END to ENDIF/ENDELSE/ENDWHILE etc."
810 :group 'idlwave-abbrev-and-indent-action
811 :type 'boolean)
812
15e42531
CD
813(defcustom idlwave-reindent-end t
814 "*Non-nil means re-indent line after END was typed."
815 :group 'idlwave-abbrev-and-indent-action
816 :type 'boolean)
817
f32b3b91
CD
818(defcustom idlwave-abbrev-move t
819 "*Non-nil means the abbrev hook can move point.
820Set to nil by `idlwave-expand-region-abbrevs'. To see the abbrev
821definitions, use the command `list-abbrevs', for abbrevs that move
822point. Moving point is useful, for example, to place point between
823parentheses of expanded functions.
824
825See `idlwave-check-abbrev'."
826 :group 'idlwave-abbrev-and-indent-action
827 :type 'boolean)
828
829(defcustom idlwave-abbrev-change-case nil
830 "*Non-nil means all abbrevs will be forced to either upper or lower case.
831If the value t, all expanded abbrevs will be upper case.
832If the value is 'down then abbrevs will be forced to lower case.
833If nil, the case will not change.
834If `idlwave-reserved-word-upcase' is non-nil, reserved words will always be
835upper case, regardless of this variable."
836 :group 'idlwave-abbrev-and-indent-action
837 :type 'boolean)
838
839(defcustom idlwave-reserved-word-upcase nil
840 "*Non-nil means, reserved words will be made upper case via abbrev expansion.
841If nil case of reserved words is controlled by `idlwave-abbrev-change-case'.
842Has effect only if in abbrev-mode."
843 :group 'idlwave-abbrev-and-indent-action
844 :type 'boolean)
845
846;;; Action/Expand Tables.
847;;
848;; The average user may have difficulty modifying this directly. It
849;; can be modified/set in idlwave-mode-hook, but it is easier to use
850;; idlwave-action-and-binding. See help for idlwave-action-and-binding for
851;; examples of how to add an action.
852;;
853;; The action table is used by `idlwave-indent-line' whereas both the
854;; action and expand tables are used by `idlwave-indent-and-action'. In
855;; general, the expand table is only used when a line is explicitly
856;; indented. Whereas, in addition to being used when the expand table
857;; is used, the action table is used when a line is indirectly
858;; indented via line splitting, auto-filling or a new line creation.
859;;
860;; Example actions:
861;;
862;; Capitalize system vars
863;; (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)
864;;
865;; Capitalize procedure name
866;; (idlwave-action-and-binding "\\<\\(pro\\|function\\)\\>[ \t]*\\<"
867;; '(capitalize-word 1) t)
868;;
869;; Capitalize common block name
870;; (idlwave-action-and-binding "\\<common\\>[ \t]+\\<"
871;; '(capitalize-word 1) t)
872;; Capitalize label
873;; (idlwave-action-and-binding (concat "^[ \t]*" idlwave-label)
874;; '(capitalize-word -1) t)
875
876(defvar idlwave-indent-action-table nil
877 "*Associated array containing action lists of search string (car),
878and function as a cdr. This table is used by `idlwave-indent-line'.
879See documentation for `idlwave-do-action' for a complete description of
880the action lists.
881
882Additions to the table are made with `idlwave-action-and-binding' when a
883binding is not requested.
884See help on `idlwave-action-and-binding' for examples.")
885
886(defvar idlwave-indent-expand-table nil
887 "*Associated array containing action lists of search string (car),
888and function as a cdr. The table is used by the
889`idlwave-indent-and-action' function. See documentation for
890`idlwave-do-action' for a complete description of the action lists.
891
892Additions to the table are made with `idlwave-action-and-binding' when a
893binding is requested.
894See help on `idlwave-action-and-binding' for examples.")
895
896;;; Documentation header and history keyword ---------------------------------
897
898(defgroup idlwave-documentation nil
899 "Options for documenting IDLWAVE files."
900 :group 'idlwave)
901
902;; FIXME: make defcustom?
903(defvar idlwave-file-header
904 (list nil
905 ";+
906; NAME:
907;
908;
909;
910; PURPOSE:
911;
912;
913;
914; CATEGORY:
915;
916;
917;
918; CALLING SEQUENCE:
919;
920;
921;
922; INPUTS:
923;
924;
925;
926; OPTIONAL INPUTS:
927;
928;
929;
930; KEYWORD PARAMETERS:
931;
932;
933;
934; OUTPUTS:
935;
936;
937;
938; OPTIONAL OUTPUTS:
939;
940;
941;
942; COMMON BLOCKS:
943;
944;
945;
946; SIDE EFFECTS:
947;
948;
949;
950; RESTRICTIONS:
951;
952;
953;
954; PROCEDURE:
955;
956;
957;
958; EXAMPLE:
959;
960;
961;
962; MODIFICATION HISTORY:
963;
964;-
965")
966 "*A list (PATHNAME STRING) specifying the doc-header template to use for
967summarizing a file. If PATHNAME is non-nil then this file will be included.
0ff9b955 968Otherwise STRING is used. If nil, the file summary will be omitted.
f32b3b91
CD
969For example you might set PATHNAME to the path for the
970lib_template.pro file included in the IDL distribution.")
971
f66f03de 972(defcustom idlwave-header-to-beginning-of-file t
5e72c6b2
S
973 "*Non-nil means, the documentation header will always be at start of file.
974When nil, the header is positioned between the PRO/FUNCTION line of
975the current routine and the code, allowing several routine headers in
976a file."
977 :group 'idlwave-documentation
978 :type 'boolean)
979
f32b3b91
CD
980(defcustom idlwave-timestamp-hook 'idlwave-default-insert-timestamp
981 "*The hook function used to update the timestamp of a function."
982 :group 'idlwave-documentation
983 :type 'function)
984
985(defcustom idlwave-doc-modifications-keyword "HISTORY"
986 "*The modifications keyword to use with the log documentation commands.
987A ':' is added to the keyword end.
988Inserted by doc-header and used to position logs by doc-modification.
989If nil it will not be inserted."
990 :group 'idlwave-documentation
991 :type 'string)
992
993(defcustom idlwave-doclib-start "^;+\\+"
994 "*Regexp matching the start of a document library header."
995 :group 'idlwave-documentation
996 :type 'regexp)
997
998(defcustom idlwave-doclib-end "^;+-"
999 "*Regexp matching the end of a document library header."
1000 :group 'idlwave-documentation
1001 :type 'regexp)
1002
1003;;; External Programs -------------------------------------------------------
1004
1005(defgroup idlwave-external-programs nil
05a1abfc 1006 "Path locations of external commands used by IDLWAVE."
f32b3b91
CD
1007 :group 'idlwave)
1008
f32b3b91 1009(defcustom idlwave-shell-explicit-file-name "idl"
5e72c6b2 1010 "*If non-nil, this is the command to run IDL.
f32b3b91 1011Should be an absolute file path or path relative to the current environment
5e72c6b2
S
1012execution search path. If you want to specify command line switches
1013for the idl program, use `idlwave-shell-command-line-options'.
1014
1015I know the name of this variable is badly chosen, but I cannot change
1016it without compromizing backwards-compatibility."
f32b3b91
CD
1017 :group 'idlwave-external-programs
1018 :type 'string)
1019
f32b3b91 1020(defcustom idlwave-shell-command-line-options nil
5e72c6b2
S
1021 "*A list of command line options for calling the IDL program.
1022Since IDL is executed directly without going through a shell like /bin/sh,
1023this should be a list of strings like '(\"-rt=file\" \"-nw\") with a separate
1024string for each argument. But you may also give a single string which
1025contains the options whitespace-separated. Emacs will be kind enough to
1026split it for you."
1027 :type '(choice
1028 string
1029 (repeat (string :value "")))
f32b3b91
CD
1030 :group 'idlwave-external-programs)
1031
1032(defcustom idlwave-help-application "idlhelp"
f66f03de
S
1033 "*The external application providing reference help for programming.
1034Obsolete, if the IDL Assistant is being used for help."
f32b3b91
CD
1035 :group 'idlwave-external-programs
1036 :type 'string)
1037
05a1abfc
CD
1038;;; Some Shell variables which must be defined here.-----------------------
1039
1040(defcustom idlwave-shell-debug-modifiers '()
1041 "List of modifiers to be used for the debugging commands.
1042Will be used to bind debugging commands in the shell buffer and in all
1043source buffers. These are additional convenience bindings, the debugging
1044commands are always available with the `C-c C-d' prefix.
1045If you set this to '(control shift), this means setting a breakpoint will
1046be on `C-S-b', compiling a source file on `C-S-c' etc. Possible modifiers
1047are `control', `meta', `super', `hyper', `alt', and `shift'."
1048 :group 'idlwave-shell-general-setup
1049 :type '(set :tag "Specify modifiers"
1050 (const control)
1051 (const meta)
1052 (const super)
1053 (const hyper)
1054 (const alt)
1055 (const shift)))
1056
1057(defcustom idlwave-shell-automatic-start nil
1058 "*If non-nil attempt invoke idlwave-shell if not already running.
1059This is checked when an attempt to send a command to an
1060IDL process is made."
1061 :group 'idlwave-shell-general-setup
1062 :type 'boolean)
1063
f32b3b91
CD
1064;;; Miscellaneous variables -------------------------------------------------
1065
1066(defgroup idlwave-misc nil
1067 "Miscellaneous options for IDLWAVE mode."
8ec3bce0 1068 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
f32b3b91
CD
1069 :group 'idlwave)
1070
1071(defcustom idlwave-startup-message t
1072 "*Non-nil displays a startup message when `idlwave-mode' is first called."
1073 :group 'idlwave-misc
1074 :type 'boolean)
1075
4b1aaa8b 1076(defcustom idlwave-default-font-lock-items
facebc7b 1077 '(pros-and-functions batch-files idlwave-idl-keywords label goto
f32b3b91
CD
1078 common-blocks class-arrows)
1079 "Items which should be fontified on the default fontification level 2.
1080IDLWAVE defines 3 levels of fontification. Level 1 is very little, level 3
1081is everything and level 2 is specified by this list.
1082This variable must be set before IDLWAVE gets loaded. It is
1083a list of symbols, the following symbols are allowed.
1084
1085pros-and-functions Procedure and Function definitions
1086batch-files Batch Files
facebc7b 1087idlwave-idl-keywords IDL Keywords
f32b3b91
CD
1088label Statement Labels
1089goto Goto Statements
1090common-blocks Common Blocks
1091keyword-parameters Keyword Parameters in routine definitions and calls
1092system-variables System Variables
1093fixme FIXME: Warning in comments (on XEmacs only v. 21.0 and up)
1094class-arrows Object Arrows with class property"
1095 :group 'idlwave-misc
1096 :type '(set
1097 :inline t :greedy t
1098 (const :tag "Procedure and Function definitions" pros-and-functions)
facebc7b
S
1099 (const :tag "Batch Files" batch-files)
1100 (const :tag "IDL Keywords (reserved words)" idlwave-idl-keywords)
1101 (const :tag "Statement Labels" label)
1102 (const :tag "Goto Statements" goto)
1103 (const :tag "Tags in Structure Definition" structtag)
1104 (const :tag "Structure Name" structname)
1105 (const :tag "Common Blocks" common-blocks)
1106 (const :tag "Keyword Parameters" keyword-parameters)
1107 (const :tag "System Variables" system-variables)
1108 (const :tag "FIXME: Warning" fixme)
f32b3b91
CD
1109 (const :tag "Object Arrows with class property " class-arrows)))
1110
1111(defcustom idlwave-mode-hook nil
1112 "Normal hook. Executed when a buffer is put into `idlwave-mode'."
1113 :group 'idlwave-misc
1114 :type 'hook)
1115
1116(defcustom idlwave-load-hook nil
1117 "Normal hook. Executed when idlwave.el is loaded."
1118 :group 'idlwave-misc
1119 :type 'hook)
1120
15e42531
CD
1121(defvar idlwave-experimental nil
1122 "Non-nil means turn on a few experimental features.
1123This variable is only for the maintainer, to test difficult stuff,
1124while still distributing stable releases.
1125As a user, you should not set this to t.")
1126
f32b3b91
CD
1127;;;
1128;;; End customization variables section
1129;;;
1130
1131;;; Non customization variables
1132
1133;;; font-lock mode - Additions by Phil Williams, Ulrik Dickow and
52a244eb 1134;;; Simon Marshall <simon_at_gnu.ai.mit.edu>
f32b3b91
CD
1135;;; and Carsten Dominik...
1136
76959b77 1137;; The following are the reserved words in IDL. Maybe we should
4b1aaa8b 1138;; highlight some more stuff as well?
76959b77
S
1139;; Procedure declarations. Fontify keyword plus procedure name.
1140(defvar idlwave-idl-keywords
4b1aaa8b 1141 ;; To update this regexp, update the list of keywords and
76959b77 1142 ;; evaluate the form.
4b1aaa8b 1143 ;; (insert
76959b77 1144 ;; (prin1-to-string
4b1aaa8b 1145 ;; (concat
76959b77 1146 ;; "\\<\\("
4b1aaa8b 1147 ;; (regexp-opt
52a244eb 1148 ;; '("||" "&&" "and" "or" "xor" "not"
4b1aaa8b 1149 ;; "eq" "ge" "gt" "le" "lt" "ne"
76959b77 1150 ;; "for" "do" "endfor"
4b1aaa8b 1151 ;; "if" "then" "endif" "else" "endelse"
76959b77
S
1152 ;; "case" "of" "endcase"
1153 ;; "switch" "break" "continue" "endswitch"
1154 ;; "begin" "end"
1155 ;; "repeat" "until" "endrep"
4b1aaa8b 1156 ;; "while" "endwhile"
76959b77
S
1157 ;; "goto" "return"
1158 ;; "inherits" "mod"
1159 ;; "compile_opt" "forward_function"
1160 ;; "on_error" "on_ioerror")) ; on_error is not officially reserved
1161 ;; "\\)\\>")))
52a244eb
S
1162 "\\<\\(&&\\|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\\|||\\)\\>")
1163
76959b77 1164
facebc7b 1165(let* (;; Procedure declarations. Fontify keyword plus procedure name.
f32b3b91
CD
1166 ;; Function declarations. Fontify keyword plus function name.
1167 (pros-and-functions
1168 '("\\<\\(function\\|pro\\)\\>[ \t]+\\(\\sw+\\(::\\sw+\\)?\\)"
1169 (1 font-lock-keyword-face)
1170 (2 font-lock-function-name-face nil t)))
1171
1172 ;; Common blocks
1173 (common-blocks
1174 '("\\<\\(common\\)\\>[ \t]*\\(\\sw+\\)?[ \t]*,?"
1175 (1 font-lock-keyword-face) ; "common"
1176 (2 font-lock-reference-face nil t) ; block name
f66f03de 1177 ("[ \t]*\\(\\sw+\\)[ ,]*"
f32b3b91 1178 ;; Start with point after block name and comma
4b1aaa8b 1179 (goto-char (match-end 0)) ; needed for XEmacs, could be nil
f32b3b91
CD
1180 nil
1181 (1 font-lock-variable-name-face) ; variable names
1182 )))
1183
1184 ;; Batch files
1185 (batch-files
1186 '("^[ \t]*\\(@[^ \t\n]+\\)" (1 font-lock-string-face)))
1187
1188 ;; FIXME warning.
1189 (fixme
1190 '("\\<FIXME:" (0 font-lock-warning-face t)))
1191
1192 ;; Labels
1193 (label
1194 '("^[ \t]*\\([a-zA-Z]\\sw*:\\)" (1 font-lock-reference-face)))
1195
1196 ;; The goto statement and its label
1197 (goto
1198 '("\\(goto\\)[ \t]*,[ \t]*\\([a-zA-Z]\\sw*\\)"
1199 (1 font-lock-keyword-face)
1200 (2 font-lock-reference-face)))
1201
52a244eb
S
1202 ;; Tags in structure definitions. Note that this definition
1203 ;; actually collides with labels, so we have to use the same
1204 ;; face. It also matches named subscript ranges,
1205 ;; e.g. vec{bottom:top]. No good way around this.
05a1abfc
CD
1206 (structtag
1207 '("\\<\\([a-zA-Z][a-zA-Z0-9_]*:\\)[^:]" (1 font-lock-reference-face)))
1208
1209 ;; Structure names
1210 (structname
1211 '("\\({\\|\\<inherits\\s-\\)\\s-*\\([a-zA-Z][a-zA-Z0-9_]*\\)[},\t \n]"
1212 (2 font-lock-function-name-face)))
1213
52a244eb 1214 ;; Keyword parameters, like /xlog or ,xrange=[]
f32b3b91 1215 ;; This is anchored to the comma preceeding the keyword.
595ab50b
CD
1216 ;; Treats continuation lines, works only during whole buffer
1217 ;; fontification. Slow, use it only in fancy fontification.
f32b3b91 1218 (keyword-parameters
0dc2be2f
S
1219 '("\\(,\\|[a-zA-Z0-9_](\\)[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\([ \t]*\\(;.*\\)?\n\\)*[ \t]*\\)?\\(/[a-zA-Z_]\\sw*\\|[a-zA-Z_]\\sw*[ \t]*=\\)"
1220 (6 font-lock-reference-face)))
f32b3b91 1221
595ab50b 1222 ;; System variables start with a bang.
f32b3b91 1223 (system-variables
15e42531 1224 '("\\(![a-zA-Z_0-9]+\\(\\.\\sw+\\)?\\)"
f32b3b91
CD
1225 (1 font-lock-variable-name-face)))
1226
1227 ;; Special and unusual operators (not used because too noisy)
1228 (special-operators
1229 '("[<>#]" (0 font-lock-keyword-face)))
1230
1231 ;; All operators (not used because too noisy)
1232 (all-operators
1233 '("[-*^#+<>/]" (0 font-lock-keyword-face)))
4b1aaa8b 1234
f32b3b91
CD
1235 ;; Arrows with text property `idlwave-class'
1236 (class-arrows
facebc7b
S
1237 '(idlwave-match-class-arrows (0 idlwave-class-arrow-face))))
1238
1239 (defconst idlwave-font-lock-keywords-1
1240 (list pros-and-functions batch-files)
1241 "Subdued level highlighting for IDLWAVE mode.")
f32b3b91 1242
facebc7b
S
1243 (defconst idlwave-font-lock-keywords-2
1244 (mapcar 'symbol-value idlwave-default-font-lock-items)
1245 "Medium level highlighting for IDLWAVE mode.")
f32b3b91 1246
facebc7b 1247 (defconst idlwave-font-lock-keywords-3
f32b3b91
CD
1248 (list pros-and-functions
1249 batch-files
76959b77 1250 idlwave-idl-keywords
f32b3b91 1251 label goto
05a1abfc
CD
1252 structtag
1253 structname
f32b3b91
CD
1254 common-blocks
1255 keyword-parameters
1256 system-variables
facebc7b
S
1257 class-arrows)
1258 "Gaudy level highlighting for IDLWAVE mode."))
f32b3b91
CD
1259
1260(defun idlwave-match-class-arrows (limit)
1261 ;; Match an object arrow with class property
1262 (and idlwave-store-inquired-class
1263 (re-search-forward "->" limit 'limit)
1264 (get-text-property (match-beginning 0) 'idlwave-class)))
1265
1266(defvar idlwave-font-lock-keywords idlwave-font-lock-keywords-2
1267 "Default expressions to highlight in IDLWAVE mode.")
1268
1269(defvar idlwave-font-lock-defaults
1270 '((idlwave-font-lock-keywords
4b1aaa8b 1271 idlwave-font-lock-keywords-1
f32b3b91
CD
1272 idlwave-font-lock-keywords-2
1273 idlwave-font-lock-keywords-3)
4b1aaa8b
PE
1274 nil t
1275 ((?$ . "w") (?_ . "w") (?. . "w") (?| . "w") (?& . "w"))
f32b3b91
CD
1276 beginning-of-line))
1277
4b1aaa8b 1278(put 'idlwave-mode 'font-lock-defaults
f32b3b91
CD
1279 idlwave-font-lock-defaults) ; XEmacs
1280
1281(defconst idlwave-comment-line-start-skip "^[ \t]*;"
1282 "Regexp to match the start of a full-line comment.
1283That is the _beginning_ of a line containing a comment delimiter `;' preceded
1284only by whitespace.")
1285
4b1aaa8b 1286(defconst idlwave-begin-block-reg
05a1abfc 1287 "\\<\\(pro\\|function\\|begin\\|case\\|switch\\)\\>"
f32b3b91
CD
1288 "Regular expression to find the beginning of a block. The case does
1289not matter. The search skips matches in comments.")
1290
52a244eb 1291(defconst idlwave-begin-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\`"
f32b3b91
CD
1292 "Regular expression to find the beginning of a unit. The case does
1293not matter.")
1294
52a244eb 1295(defconst idlwave-end-unit-reg "^\\s-*\\(pro\\|function\\)\\>\\|\\'"
f32b3b91
CD
1296 "Regular expression to find the line that indicates the end of unit.
1297This line is the end of buffer or the start of another unit. The case does
1298not matter. The search skips matches in comments.")
1299
1300(defconst idlwave-continue-line-reg "\\<\\$"
1301 "Regular expression to match a continued line.")
1302
1303(defconst idlwave-end-block-reg
05a1abfc 1304 "\\<end\\(\\|case\\|switch\\|else\\|for\\|if\\|rep\\|while\\)\\>"
f32b3b91
CD
1305 "Regular expression to find the end of a block. The case does
1306not matter. The search skips matches found in comments.")
1307
1308(defconst idlwave-block-matches
1309 '(("pro" . "end")
1310 ("function" . "end")
1311 ("case" . "endcase")
1312 ("else" . "endelse")
1313 ("for" . "endfor")
1314 ("then" . "endif")
1315 ("repeat" . "endrep")
05a1abfc 1316 ("switch" . "endswitch")
f32b3b91
CD
1317 ("while" . "endwhile"))
1318 "Matches between statements and the corresponding END variant.
1319The cars are the reserved words starting a block. If the block really
1320begins with BEGIN, the cars are the reserved words before the begin
1321which can be used to identify the block type.
1322This is used to check for the correct END type, to close blocks and
1323to expand generic end statements to their detailed form.")
1324
1325(defconst idlwave-block-match-regexp
1326 "\\<\\(else\\|for\\|then\\|repeat\\|while\\)\\>"
1327"Regular expression matching reserved words which can stand before
1328blocks starting with a BEGIN statement. The matches must have associations
1329`idlwave-block-matches'")
1330
52a244eb 1331(defconst idlwave-identifier "[a-zA-Z_][a-zA-Z0-9$_]*"
f32b3b91
CD
1332 "Regular expression matching an IDL identifier.")
1333
1334(defconst idlwave-sysvar (concat "!" idlwave-identifier)
1335 "Regular expression matching IDL system variables.")
1336
1337(defconst idlwave-variable (concat idlwave-identifier "\\|" idlwave-sysvar)
1338 "Regular expression matching IDL variable names.")
1339
1340(defconst idlwave-label (concat idlwave-identifier ":")
1341 "Regular expression matching IDL labels.")
1342
52a244eb
S
1343(defconst idlwave-method-call (concat idlwave-identifier "\\s *->"
1344 "\\(\\s *" idlwave-identifier "::\\)?"
1345))
1346
f32b3b91
CD
1347(defconst idlwave-statement-match
1348 (list
aa87aafc 1349 ;; "endif else" is the only possible "end" that can be
f32b3b91
CD
1350 ;; followed by a statement on the same line.
1351 '(endelse . ("end\\(\\|if\\)\\s +else" "end\\(\\|if\\)\\s +else"))
1352 ;; all other "end"s can not be followed by a statement.
1353 (cons 'end (list idlwave-end-block-reg nil))
1354 '(if . ("if\\>" "then"))
1355 '(for . ("for\\>" "do"))
1356 '(begin . ("begin\\>" nil))
1357 '(pdef . ("pro\\>\\|function\\>" nil))
1358 '(while . ("while\\>" "do"))
1359 '(repeat . ("repeat\\>" "repeat"))
1360 '(goto . ("goto\\>" nil))
1361 '(case . ("case\\>" nil))
05a1abfc 1362 '(switch . ("switch\\>" nil))
4b1aaa8b 1363 (cons 'call (list (concat "\\(" idlwave-variable "\\) *= *"
52a244eb
S
1364 "\\(" idlwave-method-call "\\s *\\)?"
1365 idlwave-identifier
1366 "\\s *(") nil))
4b1aaa8b 1367 (cons 'call (list (concat
52a244eb 1368 "\\(" idlwave-method-call "\\s *\\)?"
4b1aaa8b 1369 idlwave-identifier
52a244eb 1370 "\\( *\\($\\|\\$\\)\\|\\s *,\\)") nil))
4b1aaa8b 1371 (cons 'assign (list (concat
52a244eb 1372 "\\(" idlwave-variable "\\) *=") nil)))
4b1aaa8b 1373
f32b3b91
CD
1374 "Associated list of statement matching regular expressions.
1375Each regular expression matches the start of an IDL statement. The
1376first element of each association is a symbol giving the statement
1377type. The associated value is a list. The first element of this list
1378is a regular expression matching the start of an IDL statement for
1379identifying the statement type. The second element of this list is a
1380regular expression for finding a substatement for the type. The
1381substatement starts after the end of the found match modulo
1382whitespace. If it is nil then the statement has no substatement. The
1383list order matters since matching an assignment statement exactly is
1384not possible without parsing. Thus assignment statement become just
15e42531 1385the leftover unidentified statements containing an equal sign." )
f32b3b91
CD
1386
1387(defvar idlwave-fill-function 'auto-fill-function
1388 "IDL mode auto fill function.")
1389
1390(defvar idlwave-comment-indent-function 'comment-indent-function
1391 "IDL mode comment indent function.")
1392
1393;; Note that this is documented in the v18 manuals as being a string
1394;; of length one rather than a single character.
1395;; The code in this file accepts either format for compatibility.
4b1aaa8b 1396(defvar idlwave-comment-indent-char ?\
f32b3b91
CD
1397 "Character to be inserted for IDL comment indentation.
1398Normally a space.")
1399
1400(defconst idlwave-continuation-char ?$
1401 "Character which is inserted as a last character on previous line by
1402 \\[idlwave-split-line] to begin a continuation line. Normally $.")
1403
e08734e2 1404(defconst idlwave-mode-version "6.1_em22")
f32b3b91
CD
1405
1406(defmacro idlwave-keyword-abbrev (&rest args)
1407 "Creates a function for abbrev hooks to call `idlwave-check-abbrev' with args."
8a946354 1408 `(quote (lambda ()
5e72c6b2 1409 ,(append '(idlwave-check-abbrev) args))))
f32b3b91
CD
1410
1411;; If I take the time I can replace idlwave-keyword-abbrev with
1412;; idlwave-code-abbrev and remove the quoted abbrev check from
1413;; idlwave-check-abbrev. Then, e.g, (idlwave-keyword-abbrev 0 t) becomes
1414;; (idlwave-code-abbrev idlwave-check-abbrev 0 t). In fact I should change
1415;; the name of idlwave-check-abbrev to something like idlwave-modify-abbrev.
1416
1417(defmacro idlwave-code-abbrev (&rest args)
1418 "Creates a function for abbrev hooks that ensures abbrevs are not quoted.
1419Specifically, if the abbrev is in a comment or string it is unexpanded.
1420Otherwise ARGS forms a list that is evaluated."
8a946354 1421 `(quote (lambda ()
5e72c6b2
S
1422 ,(prin1-to-string args) ;; Puts the code in the doc string
1423 (if (idlwave-quoted)
1424 (progn (unexpand-abbrev) nil)
1425 ,(append args)))))
f32b3b91
CD
1426
1427(defvar idlwave-mode-map (make-sparse-keymap)
1428 "Keymap used in IDL mode.")
1429
1430(defvar idlwave-mode-syntax-table (make-syntax-table)
1431 "Syntax table in use in `idlwave-mode' buffers.")
1432
1433(modify-syntax-entry ?+ "." idlwave-mode-syntax-table)
1434(modify-syntax-entry ?- "." idlwave-mode-syntax-table)
1435(modify-syntax-entry ?* "." idlwave-mode-syntax-table)
1436(modify-syntax-entry ?/ "." idlwave-mode-syntax-table)
1437(modify-syntax-entry ?^ "." idlwave-mode-syntax-table)
1438(modify-syntax-entry ?# "." idlwave-mode-syntax-table)
1439(modify-syntax-entry ?= "." idlwave-mode-syntax-table)
1440(modify-syntax-entry ?% "." idlwave-mode-syntax-table)
1441(modify-syntax-entry ?< "." idlwave-mode-syntax-table)
1442(modify-syntax-entry ?> "." idlwave-mode-syntax-table)
1443(modify-syntax-entry ?\' "\"" idlwave-mode-syntax-table)
1444(modify-syntax-entry ?\" "\"" idlwave-mode-syntax-table)
1445(modify-syntax-entry ?\\ "." idlwave-mode-syntax-table)
1446(modify-syntax-entry ?_ "_" idlwave-mode-syntax-table)
1447(modify-syntax-entry ?{ "(}" idlwave-mode-syntax-table)
1448(modify-syntax-entry ?} "){" idlwave-mode-syntax-table)
1449(modify-syntax-entry ?$ "_" idlwave-mode-syntax-table)
1450(modify-syntax-entry ?. "." idlwave-mode-syntax-table)
1451(modify-syntax-entry ?\; "<" idlwave-mode-syntax-table)
1452(modify-syntax-entry ?\n ">" idlwave-mode-syntax-table)
1453(modify-syntax-entry ?\f ">" idlwave-mode-syntax-table)
1454
1455(defvar idlwave-find-symbol-syntax-table
1456 (copy-syntax-table idlwave-mode-syntax-table)
1457 "Syntax table that treats symbol characters as word characters.")
1458
05a1abfc
CD
1459(modify-syntax-entry ?$ "w" idlwave-find-symbol-syntax-table)
1460(modify-syntax-entry ?_ "w" idlwave-find-symbol-syntax-table)
1461(modify-syntax-entry ?! "w" idlwave-find-symbol-syntax-table)
1462(modify-syntax-entry ?. "w" idlwave-find-symbol-syntax-table)
1463
76959b77
S
1464(defmacro idlwave-with-special-syntax (&rest body)
1465 "Execute BODY with a different syntax table."
05a1abfc
CD
1466 `(let ((saved-syntax (syntax-table)))
1467 (unwind-protect
1468 (progn
1469 (set-syntax-table idlwave-find-symbol-syntax-table)
1470 ,@body)
1471 (set-syntax-table saved-syntax))))
1472
76959b77
S
1473;(defmacro idlwave-with-special-syntax1 (&rest body)
1474; "Execute BODY with a different syntax table."
1475; `(let ((saved-syntax (syntax-table)))
1476; (unwind-protect
1477; (progn
1478; (set-syntax-table idlwave-find-symbol-syntax-table)
1479; ,@body)
1480; (set-syntax-table saved-syntax))))
1481
f32b3b91
CD
1482(defun idlwave-action-and-binding (key cmd &optional select)
1483 "KEY and CMD are made into a key binding and an indent action.
1484KEY is a string - same as for the `define-key' function. CMD is a
1485function of no arguments or a list to be evaluated. CMD is bound to
1486KEY in `idlwave-mode-map' by defining an anonymous function calling
1487`self-insert-command' followed by CMD. If KEY contains more than one
1488character a binding will only be set if SELECT is 'both.
1489
5e72c6b2 1490\(KEY . CMD\) is also placed in the `idlwave-indent-expand-table',
f32b3b91
CD
1491replacing any previous value for KEY. If a binding is not set then it
1492will instead be placed in `idlwave-indent-action-table'.
1493
1494If the optional argument SELECT is nil then an action and binding are
1495created. If SELECT is 'noaction, then a binding is always set and no
1496action is created. If SELECT is 'both then an action and binding
1497will both be created even if KEY contains more than one character.
1498Otherwise, if SELECT is non-nil then only an action is created.
1499
1500Some examples:
1501No spaces before and 1 after a comma
1502 (idlwave-action-and-binding \",\" '(idlwave-surround 0 1))
1503A minimum of 1 space before and after `=' (see `idlwave-expand-equal').
1504 (idlwave-action-and-binding \"=\" '(idlwave-expand-equal -1 -1))
1505Capitalize system variables - action only
1506 (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)"
1507 (if (not (equal select 'noaction))
1508 ;; Add action
1509 (let* ((table (if select 'idlwave-indent-action-table
1510 'idlwave-indent-expand-table))
3938cb82
S
1511 (table-key (regexp-quote key))
1512 (cell (assoc table-key (eval table))))
f32b3b91
CD
1513 (if cell
1514 ;; Replace action command
1515 (setcdr cell cmd)
1516 ;; New action
3938cb82 1517 (set table (append (eval table) (list (cons table-key cmd)))))))
f32b3b91
CD
1518 ;; Make key binding for action
1519 (if (or (and (null select) (= (length key) 1))
1520 (equal select 'noaction)
1521 (equal select 'both))
1522 (define-key idlwave-mode-map key
1523 (append '(lambda ()
1524 (interactive)
1525 (self-insert-command 1))
1526 (list (if (listp cmd)
1527 cmd
1528 (list cmd)))))))
1529
1530(fset 'idlwave-debug-map (make-sparse-keymap))
1531
595ab50b 1532(define-key idlwave-mode-map "\C-c " 'idlwave-hard-tab)
15e42531 1533(define-key idlwave-mode-map [(control tab)] 'idlwave-hard-tab)
595ab50b 1534;(define-key idlwave-mode-map "\C-c\C- " 'idlwave-hard-tab)
f32b3b91
CD
1535(define-key idlwave-mode-map "'" 'idlwave-show-matching-quote)
1536(define-key idlwave-mode-map "\"" 'idlwave-show-matching-quote)
76959b77 1537(define-key idlwave-mode-map "\C-g" 'idlwave-keyboard-quit)
f32b3b91
CD
1538(define-key idlwave-mode-map "\C-c;" 'idlwave-toggle-comment-region)
1539(define-key idlwave-mode-map "\C-\M-a" 'idlwave-beginning-of-subprogram)
1540(define-key idlwave-mode-map "\C-\M-e" 'idlwave-end-of-subprogram)
1541(define-key idlwave-mode-map "\C-c{" 'idlwave-beginning-of-block)
1542(define-key idlwave-mode-map "\C-c}" 'idlwave-end-of-block)
1543(define-key idlwave-mode-map "\C-c]" 'idlwave-close-block)
3938cb82 1544(define-key idlwave-mode-map [(meta control h)] 'idlwave-mark-subprogram)
f32b3b91
CD
1545(define-key idlwave-mode-map "\M-\C-n" 'idlwave-forward-block)
1546(define-key idlwave-mode-map "\M-\C-p" 'idlwave-backward-block)
1547(define-key idlwave-mode-map "\M-\C-d" 'idlwave-down-block)
1548(define-key idlwave-mode-map "\M-\C-u" 'idlwave-backward-up-block)
1549(define-key idlwave-mode-map "\M-\r" 'idlwave-split-line)
1550(define-key idlwave-mode-map "\M-\C-q" 'idlwave-indent-subprogram)
1551(define-key idlwave-mode-map "\C-c\C-p" 'idlwave-previous-statement)
1552(define-key idlwave-mode-map "\C-c\C-n" 'idlwave-next-statement)
1553;; (define-key idlwave-mode-map "\r" 'idlwave-newline)
1554;; (define-key idlwave-mode-map "\t" 'idlwave-indent-line)
f66f03de 1555(define-key idlwave-mode-map [(shift iso-lefttab)] 'idlwave-indent-statement)
f32b3b91
CD
1556(define-key idlwave-mode-map "\C-c\C-a" 'idlwave-auto-fill-mode)
1557(define-key idlwave-mode-map "\M-q" 'idlwave-fill-paragraph)
1558(define-key idlwave-mode-map "\M-s" 'idlwave-edit-in-idlde)
1559(define-key idlwave-mode-map "\C-c\C-h" 'idlwave-doc-header)
1560(define-key idlwave-mode-map "\C-c\C-m" 'idlwave-doc-modification)
1561(define-key idlwave-mode-map "\C-c\C-c" 'idlwave-case)
1562(define-key idlwave-mode-map "\C-c\C-d" 'idlwave-debug-map)
05a1abfc
CD
1563(when (and (boundp 'idlwave-shell-debug-modifiers)
1564 (listp idlwave-shell-debug-modifiers)
1565 (not (equal idlwave-shell-debug-modifiers '())))
1566 ;; Bind the debug commands also with the special modifiers.
1567 (let ((shift (memq 'shift idlwave-shell-debug-modifiers))
4b1aaa8b 1568 (mods-noshift (delq 'shift
05a1abfc 1569 (copy-sequence idlwave-shell-debug-modifiers))))
4b1aaa8b 1570 (define-key idlwave-mode-map
05a1abfc
CD
1571 (vector (append mods-noshift (list (if shift ?C ?c))))
1572 'idlwave-shell-save-and-run)
4b1aaa8b 1573 (define-key idlwave-mode-map
05a1abfc 1574 (vector (append mods-noshift (list (if shift ?B ?b))))
52a244eb 1575 'idlwave-shell-break-here)
4b1aaa8b 1576 (define-key idlwave-mode-map
52a244eb
S
1577 (vector (append mods-noshift (list (if shift ?E ?e))))
1578 'idlwave-shell-run-region)))
1579(define-key idlwave-mode-map "\C-c\C-d\C-c" 'idlwave-shell-save-and-run)
1580(define-key idlwave-mode-map "\C-c\C-d\C-b" 'idlwave-shell-break-here)
1581(define-key idlwave-mode-map "\C-c\C-d\C-e" 'idlwave-shell-run-region)
f32b3b91
CD
1582(define-key idlwave-mode-map "\C-c\C-f" 'idlwave-for)
1583;; (define-key idlwave-mode-map "\C-c\C-f" 'idlwave-function)
1584;; (define-key idlwave-mode-map "\C-c\C-p" 'idlwave-procedure)
1585(define-key idlwave-mode-map "\C-c\C-r" 'idlwave-repeat)
1586(define-key idlwave-mode-map "\C-c\C-w" 'idlwave-while)
15e42531 1587(define-key idlwave-mode-map "\C-c\C-k" 'idlwave-kill-autoloaded-buffers)
f32b3b91
CD
1588(define-key idlwave-mode-map "\C-c\C-s" 'idlwave-shell)
1589(define-key idlwave-mode-map "\C-c\C-l" 'idlwave-shell-recenter-shell-window)
15e42531 1590(define-key idlwave-mode-map "\C-c\C-b" 'idlwave-list-buffer-load-path-shadows)
22d5821d
CD
1591(autoload 'idlwave-shell "idlw-shell"
1592 "Run an inferior IDL, with I/O through buffer `(idlwave-shell-buffer)'." t)
8c7b4ec8
EZ
1593(autoload 'idlwave-shell-send-command "idlw-shell")
1594(autoload 'idlwave-shell-recenter-shell-window "idlw-shell"
f32b3b91 1595 "Run `idlwave-shell' and switch back to current window" t)
8c7b4ec8 1596(autoload 'idlwave-shell-save-and-run "idlw-shell"
f32b3b91 1597 "Save and run buffer under the shell." t)
15e42531
CD
1598(autoload 'idlwave-shell-break-here "idlw-shell"
1599 "Set breakpoint in current line." t)
52a244eb
S
1600(autoload 'idlwave-shell-run-region "idlw-shell"
1601 "Compile and run the region." t)
f32b3b91 1602(define-key idlwave-mode-map "\C-c\C-v" 'idlwave-find-module)
3938cb82 1603(define-key idlwave-mode-map "\C-c\C-t" 'idlwave-find-module-this-file)
f32b3b91 1604(define-key idlwave-mode-map "\C-c?" 'idlwave-routine-info)
15e42531 1605(define-key idlwave-mode-map "\M-?" 'idlwave-context-help)
8c43762b 1606(define-key idlwave-mode-map [(control meta ?\?)]
e08734e2 1607 'idlwave-help-assistant-help-with-topic)
52a244eb 1608;; Pickup both forms of Esc/Meta binding
f32b3b91 1609(define-key idlwave-mode-map [(meta tab)] 'idlwave-complete)
52a244eb
S
1610(define-key idlwave-mode-map [?\e?\t] 'idlwave-complete)
1611(define-key idlwave-mode-map "\M-\C-i" 'idlwave-complete)
1612(define-key idlwave-mode-map "\C-c\C-i" 'idlwave-update-routine-info)
1613(define-key idlwave-mode-map "\C-c=" 'idlwave-resolve)
4b1aaa8b 1614(define-key idlwave-mode-map
15e42531
CD
1615 (if (featurep 'xemacs) [(shift button3)] [(shift mouse-3)])
1616 'idlwave-mouse-context-help)
f32b3b91
CD
1617
1618;; Set action and key bindings.
1619;; See description of the function `idlwave-action-and-binding'.
1620;; Automatically add spaces for the following characters
f66f03de
S
1621
1622;; Actions for & are complicated by &&
1623(idlwave-action-and-binding "&" 'idlwave-custom-ampersand-surround)
1624
1625;; Automatically add spaces to equal sign if not keyword. This needs
1626;; to go ahead of > and <, so >= and <= will be treated correctly
f32b3b91
CD
1627(idlwave-action-and-binding "=" '(idlwave-expand-equal -1 -1))
1628
4b1aaa8b 1629;; Actions for > and < are complicated by >=, <=, and ->...
f66f03de
S
1630(idlwave-action-and-binding "<" '(idlwave-custom-ltgtr-surround nil))
1631(idlwave-action-and-binding ">" '(idlwave-custom-ltgtr-surround 'gtr))
1632
1633(idlwave-action-and-binding "," '(idlwave-surround 0 -1 1))
1634
1635
f32b3b91
CD
1636;;;
1637;;; Abbrev Section
1638;;;
1639;;; When expanding abbrevs and the abbrev hook moves backward, an extra
1640;;; space is inserted (this is the space typed by the user to expanded
1641;;; the abbrev).
1642;;;
5e72c6b2
S
1643(defvar idlwave-mode-abbrev-table nil
1644 "Abbreviation table used for IDLWAVE mode")
1645(define-abbrev-table 'idlwave-mode-abbrev-table ())
1646
1647(defun idlwave-define-abbrev (name expansion hook &optional noprefix table)
1648 "Define-abbrev with backward compatibility.
1649
1650If NOPREFIX is non-nil, don't prepend prefix character. Installs into
1651idlwave-mode-abbrev-table unless TABLE is non-nil."
1652 (let ((abbrevs-changed nil) ;; mask the current value to avoid save
1653 (args (list (or table idlwave-mode-abbrev-table)
1654 (if noprefix name (concat idlwave-abbrev-start-char name))
1655 expansion
1656 hook)))
1657 (condition-case nil
1658 (apply 'define-abbrev (append args '(0 t)))
1659 (error (apply 'define-abbrev args)))))
f32b3b91
CD
1660
1661(condition-case nil
4b1aaa8b 1662 (modify-syntax-entry (string-to-char idlwave-abbrev-start-char)
f32b3b91
CD
1663 "w" idlwave-mode-syntax-table)
1664 (error nil))
1665
5e72c6b2
S
1666;;
1667;; Templates
1668;;
1669(idlwave-define-abbrev "c" "" (idlwave-code-abbrev idlwave-case))
1670(idlwave-define-abbrev "sw" "" (idlwave-code-abbrev idlwave-switch))
1671(idlwave-define-abbrev "f" "" (idlwave-code-abbrev idlwave-for))
1672(idlwave-define-abbrev "fu" "" (idlwave-code-abbrev idlwave-function))
1673(idlwave-define-abbrev "pr" "" (idlwave-code-abbrev idlwave-procedure))
1674(idlwave-define-abbrev "r" "" (idlwave-code-abbrev idlwave-repeat))
1675(idlwave-define-abbrev "w" "" (idlwave-code-abbrev idlwave-while))
1676(idlwave-define-abbrev "i" "" (idlwave-code-abbrev idlwave-if))
1677(idlwave-define-abbrev "elif" "" (idlwave-code-abbrev idlwave-elif))
1678;;
1679;; Keywords, system functions, conversion routines
1680;;
1681(idlwave-define-abbrev "ap" "arg_present()" (idlwave-keyword-abbrev 1))
1682(idlwave-define-abbrev "b" "begin" (idlwave-keyword-abbrev 0 t))
1683(idlwave-define-abbrev "co" "common" (idlwave-keyword-abbrev 0 t))
1684(idlwave-define-abbrev "cb" "byte()" (idlwave-keyword-abbrev 1))
1685(idlwave-define-abbrev "cx" "fix()" (idlwave-keyword-abbrev 1))
1686(idlwave-define-abbrev "cl" "long()" (idlwave-keyword-abbrev 1))
1687(idlwave-define-abbrev "cf" "float()" (idlwave-keyword-abbrev 1))
1688(idlwave-define-abbrev "cs" "string()" (idlwave-keyword-abbrev 1))
1689(idlwave-define-abbrev "cc" "complex()" (idlwave-keyword-abbrev 1))
1690(idlwave-define-abbrev "cd" "double()" (idlwave-keyword-abbrev 1))
1691(idlwave-define-abbrev "e" "else" (idlwave-keyword-abbrev 0 t))
1692(idlwave-define-abbrev "ec" "endcase" 'idlwave-show-begin)
1693(idlwave-define-abbrev "es" "endswitch" 'idlwave-show-begin)
1694(idlwave-define-abbrev "ee" "endelse" 'idlwave-show-begin)
1695(idlwave-define-abbrev "ef" "endfor" 'idlwave-show-begin)
1696(idlwave-define-abbrev "ei" "endif else if" 'idlwave-show-begin)
1697(idlwave-define-abbrev "el" "endif else" 'idlwave-show-begin)
1698(idlwave-define-abbrev "en" "endif" 'idlwave-show-begin)
1699(idlwave-define-abbrev "er" "endrep" 'idlwave-show-begin)
1700(idlwave-define-abbrev "ew" "endwhile" 'idlwave-show-begin)
1701(idlwave-define-abbrev "g" "goto," (idlwave-keyword-abbrev 0 t))
1702(idlwave-define-abbrev "h" "help," (idlwave-keyword-abbrev 0))
1703(idlwave-define-abbrev "k" "keyword_set()" (idlwave-keyword-abbrev 1))
1704(idlwave-define-abbrev "n" "n_elements()" (idlwave-keyword-abbrev 1))
1705(idlwave-define-abbrev "on" "on_error," (idlwave-keyword-abbrev 0))
1706(idlwave-define-abbrev "oi" "on_ioerror," (idlwave-keyword-abbrev 0 1))
1707(idlwave-define-abbrev "ow" "openw," (idlwave-keyword-abbrev 0))
1708(idlwave-define-abbrev "or" "openr," (idlwave-keyword-abbrev 0))
1709(idlwave-define-abbrev "ou" "openu," (idlwave-keyword-abbrev 0))
1710(idlwave-define-abbrev "p" "print," (idlwave-keyword-abbrev 0))
1711(idlwave-define-abbrev "pt" "plot," (idlwave-keyword-abbrev 0))
1712(idlwave-define-abbrev "re" "read," (idlwave-keyword-abbrev 0))
1713(idlwave-define-abbrev "rf" "readf," (idlwave-keyword-abbrev 0))
1714(idlwave-define-abbrev "ru" "readu," (idlwave-keyword-abbrev 0))
1715(idlwave-define-abbrev "rt" "return" (idlwave-keyword-abbrev 0))
1716(idlwave-define-abbrev "sc" "strcompress()" (idlwave-keyword-abbrev 1))
1717(idlwave-define-abbrev "sn" "strlen()" (idlwave-keyword-abbrev 1))
1718(idlwave-define-abbrev "sl" "strlowcase()" (idlwave-keyword-abbrev 1))
1719(idlwave-define-abbrev "su" "strupcase()" (idlwave-keyword-abbrev 1))
1720(idlwave-define-abbrev "sm" "strmid()" (idlwave-keyword-abbrev 1))
1721(idlwave-define-abbrev "sp" "strpos()" (idlwave-keyword-abbrev 1))
1722(idlwave-define-abbrev "st" "strput()" (idlwave-keyword-abbrev 1))
1723(idlwave-define-abbrev "sr" "strtrim()" (idlwave-keyword-abbrev 1))
1724(idlwave-define-abbrev "t" "then" (idlwave-keyword-abbrev 0 t))
1725(idlwave-define-abbrev "u" "until" (idlwave-keyword-abbrev 0 t))
1726(idlwave-define-abbrev "wu" "writeu," (idlwave-keyword-abbrev 0))
1727(idlwave-define-abbrev "iap" "if arg_present() then" (idlwave-keyword-abbrev 6))
1728(idlwave-define-abbrev "ik" "if keyword_set() then" (idlwave-keyword-abbrev 6))
1729(idlwave-define-abbrev "ine" "if n_elements() eq 0 then" (idlwave-keyword-abbrev 11))
1730(idlwave-define-abbrev "inn" "if n_elements() ne 0 then" (idlwave-keyword-abbrev 11))
1731(idlwave-define-abbrev "np" "n_params()" (idlwave-keyword-abbrev 0))
1732(idlwave-define-abbrev "s" "size()" (idlwave-keyword-abbrev 1))
1733(idlwave-define-abbrev "wi" "widget_info()" (idlwave-keyword-abbrev 1))
1734(idlwave-define-abbrev "wc" "widget_control," (idlwave-keyword-abbrev 0))
3938cb82
S
1735(idlwave-define-abbrev "pv" "ptr_valid()" (idlwave-keyword-abbrev 1))
1736(idlwave-define-abbrev "ipv" "if ptr_valid() then" (idlwave-keyword-abbrev 6))
ff689efd 1737
5e72c6b2
S
1738;; This section is reserved words only. (From IDL user manual)
1739;;
1740(idlwave-define-abbrev "and" "and" (idlwave-keyword-abbrev 0 t) t)
1741(idlwave-define-abbrev "begin" "begin" (idlwave-keyword-abbrev 0 t) t)
1742(idlwave-define-abbrev "break" "break" (idlwave-keyword-abbrev 0 t) t)
1743(idlwave-define-abbrev "case" "case" (idlwave-keyword-abbrev 0 t) t)
1744(idlwave-define-abbrev "common" "common" (idlwave-keyword-abbrev 0 t) t)
1745(idlwave-define-abbrev "continue" "continue" (idlwave-keyword-abbrev 0 t) t)
1746(idlwave-define-abbrev "do" "do" (idlwave-keyword-abbrev 0 t) t)
1747(idlwave-define-abbrev "else" "else" (idlwave-keyword-abbrev 0 t) t)
1748(idlwave-define-abbrev "end" "end" 'idlwave-show-begin-check t)
1749(idlwave-define-abbrev "endcase" "endcase" 'idlwave-show-begin-check t)
1750(idlwave-define-abbrev "endelse" "endelse" 'idlwave-show-begin-check t)
1751(idlwave-define-abbrev "endfor" "endfor" 'idlwave-show-begin-check t)
1752(idlwave-define-abbrev "endif" "endif" 'idlwave-show-begin-check t)
1753(idlwave-define-abbrev "endrep" "endrep" 'idlwave-show-begin-check t)
1754(idlwave-define-abbrev "endswitch" "endswitch" 'idlwave-show-begin-check t)
1755(idlwave-define-abbrev "endwhi" "endwhi" 'idlwave-show-begin-check t)
1756(idlwave-define-abbrev "endwhile" "endwhile" 'idlwave-show-begin-check t)
1757(idlwave-define-abbrev "eq" "eq" (idlwave-keyword-abbrev 0 t) t)
1758(idlwave-define-abbrev "for" "for" (idlwave-keyword-abbrev 0 t) t)
1759(idlwave-define-abbrev "function" "function" (idlwave-keyword-abbrev 0 t) t)
1760(idlwave-define-abbrev "ge" "ge" (idlwave-keyword-abbrev 0 t) t)
1761(idlwave-define-abbrev "goto" "goto" (idlwave-keyword-abbrev 0 t) t)
1762(idlwave-define-abbrev "gt" "gt" (idlwave-keyword-abbrev 0 t) t)
1763(idlwave-define-abbrev "if" "if" (idlwave-keyword-abbrev 0 t) t)
1764(idlwave-define-abbrev "le" "le" (idlwave-keyword-abbrev 0 t) t)
1765(idlwave-define-abbrev "lt" "lt" (idlwave-keyword-abbrev 0 t) t)
1766(idlwave-define-abbrev "mod" "mod" (idlwave-keyword-abbrev 0 t) t)
1767(idlwave-define-abbrev "ne" "ne" (idlwave-keyword-abbrev 0 t) t)
1768(idlwave-define-abbrev "not" "not" (idlwave-keyword-abbrev 0 t) t)
1769(idlwave-define-abbrev "of" "of" (idlwave-keyword-abbrev 0 t) t)
1770(idlwave-define-abbrev "on_ioerror" "on_ioerror" (idlwave-keyword-abbrev 0 t) t)
1771(idlwave-define-abbrev "or" "or" (idlwave-keyword-abbrev 0 t) t)
1772(idlwave-define-abbrev "pro" "pro" (idlwave-keyword-abbrev 0 t) t)
1773(idlwave-define-abbrev "repeat" "repeat" (idlwave-keyword-abbrev 0 t) t)
1774(idlwave-define-abbrev "switch" "switch" (idlwave-keyword-abbrev 0 t) t)
1775(idlwave-define-abbrev "then" "then" (idlwave-keyword-abbrev 0 t) t)
1776(idlwave-define-abbrev "until" "until" (idlwave-keyword-abbrev 0 t) t)
1777(idlwave-define-abbrev "while" "while" (idlwave-keyword-abbrev 0 t) t)
1778(idlwave-define-abbrev "xor" "xor" (idlwave-keyword-abbrev 0 t) t)
f32b3b91
CD
1779
1780(defvar imenu-create-index-function)
1781(defvar extract-index-name-function)
1782(defvar prev-index-position-function)
1783(defvar imenu-extract-index-name-function)
1784(defvar imenu-prev-index-position-function)
5e72c6b2 1785;; defined later - so just make the compiler hush
4b1aaa8b 1786(defvar idlwave-mode-menu)
f32b3b91
CD
1787(defvar idlwave-mode-debug-menu)
1788
1789;;;###autoload
1790(defun idlwave-mode ()
e08734e2 1791 "Major mode for editing IDL source files (version 6.1_em22).
f32b3b91
CD
1792
1793The main features of this mode are
1794
17951. Indentation and Formatting
1796 --------------------------
1797 Like other Emacs programming modes, C-j inserts a newline and indents.
1798 TAB is used for explicit indentation of the current line.
1799
5e72c6b2
S
1800 To start a continuation line, use \\[idlwave-split-line]. This
1801 function can also be used in the middle of a line to split the line
1802 at that point. When used inside a long constant string, the string
1803 is split at that point with the `+' concatenation operator.
f32b3b91
CD
1804
1805 Comments are indented as follows:
1806
1807 `;;;' Indentation remains unchanged.
1808 `;;' Indent like the surrounding code
1809 `;' Indent to a minimum column.
1810
1811 The indentation of comments starting in column 0 is never changed.
1812
5e72c6b2
S
1813 Use \\[idlwave-fill-paragraph] to refill a paragraph inside a
1814 comment. The indentation of the second line of the paragraph
1815 relative to the first will be retained. Use
1816 \\[idlwave-auto-fill-mode] to toggle auto-fill mode for these
1817 comments. When the variable `idlwave-fill-comment-line-only' is
52a244eb 1818 nil, code can also be auto-filled and auto-indented.
f32b3b91
CD
1819
1820 To convert pre-existing IDL code to your formatting style, mark the
5e72c6b2
S
1821 entire buffer with \\[mark-whole-buffer] and execute
1822 \\[idlwave-expand-region-abbrevs]. Then mark the entire buffer
1823 again followed by \\[indent-region] (`indent-region').
f32b3b91
CD
1824
18252. Routine Info
1826 ------------
5e72c6b2
S
1827 IDLWAVE displays information about the calling sequence and the
1828 accepted keyword parameters of a procedure or function with
1829 \\[idlwave-routine-info]. \\[idlwave-find-module] jumps to the
1830 source file of a module. These commands know about system
1831 routines, all routines in idlwave-mode buffers and (when the
1832 idlwave-shell is active) about all modules currently compiled under
52a244eb
S
1833 this shell. It also makes use of pre-compiled or custom-scanned
1834 user and library catalogs many popular libraries ship with by
1835 default. Use \\[idlwave-update-routine-info] to update this
15e42531
CD
1836 information, which is also used for completion (see item 4).
1837
18383. Online IDL Help
1839 ---------------
f66f03de 1840
15e42531 1841 \\[idlwave-context-help] displays the IDL documentation relevant
f66f03de
S
1842 for the system variable, keyword, or routines at point. A single
1843 key stroke gets you directly to the right place in the docs. See
52a244eb 1844 the manual to configure where and how the HTML help is displayed.
f32b3b91 1845
15e42531 18464. Completion
f32b3b91 1847 ----------
15e42531 1848 \\[idlwave-complete] completes the names of procedures, functions
52a244eb
S
1849 class names, keyword parameters, system variables and tags, class
1850 tags, structure tags, filenames and much more. It is context
1851 sensitive and figures out what is expected at point. Lower case
1852 strings are completed in lower case, other strings in mixed or
1853 upper case.
f32b3b91 1854
15e42531 18555. Code Templates and Abbreviations
f32b3b91
CD
1856 --------------------------------
1857 Many Abbreviations are predefined to expand to code fragments and templates.
1858 The abbreviations start generally with a `\\`. Some examples
1859
1860 \\pr PROCEDURE template
1861 \\fu FUNCTION template
1862 \\c CASE statement template
05a1abfc 1863 \\sw SWITCH statement template
f32b3b91
CD
1864 \\f FOR loop template
1865 \\r REPEAT Loop template
1866 \\w WHILE loop template
1867 \\i IF statement template
1868 \\elif IF-ELSE statement template
1869 \\b BEGIN
4b1aaa8b 1870
52a244eb
S
1871 For a full list, use \\[idlwave-list-abbrevs]. Some templates also
1872 have direct keybindings - see the list of keybindings below.
775591f7 1873
52a244eb
S
1874 \\[idlwave-doc-header] inserts a documentation header at the
1875 beginning of the current program unit (pro, function or main).
1876 Change log entries can be added to the current program unit with
1877 \\[idlwave-doc-modification].
f32b3b91 1878
15e42531 18796. Automatic Case Conversion
f32b3b91
CD
1880 -------------------------
1881 The case of reserved words and some abbrevs is controlled by
1882 `idlwave-reserved-word-upcase' and `idlwave-abbrev-change-case'.
1883
15e42531 18847. Automatic END completion
f32b3b91
CD
1885 ------------------------
1886 If the variable `idlwave-expand-generic-end' is non-nil, each END typed
1887 will be converted to the specific version, like ENDIF, ENDFOR, etc.
1888
15e42531 18898. Hooks
f32b3b91
CD
1890 -----
1891 Loading idlwave.el runs `idlwave-load-hook'.
1892 Turning on `idlwave-mode' runs `idlwave-mode-hook'.
1893
15e42531 18949. Documentation and Customization
f32b3b91 1895 -------------------------------
5e72c6b2
S
1896 Info documentation for this package is available. Use
1897 \\[idlwave-info] to display (complain to your sysadmin if that does
1898 not work). For Postscript, PDF, and HTML versions of the
1899 documentation, check IDLWAVE's homepage at `http://idlwave.org'.
f32b3b91
CD
1900 IDLWAVE has customize support - see the group `idlwave'.
1901
15e42531 190210.Keybindings
f32b3b91
CD
1903 -----------
1904 Here is a list of all keybindings of this mode.
1905 If some of the key bindings below show with ??, use \\[describe-key]
1906 followed by the key sequence to see what the key sequence does.
1907
1908\\{idlwave-mode-map}"
1909
1910 (interactive)
1911 (kill-all-local-variables)
4b1aaa8b 1912
f32b3b91
CD
1913 (if idlwave-startup-message
1914 (message "Emacs IDLWAVE mode version %s." idlwave-mode-version))
1915 (setq idlwave-startup-message nil)
4b1aaa8b 1916
f32b3b91
CD
1917 (setq local-abbrev-table idlwave-mode-abbrev-table)
1918 (set-syntax-table idlwave-mode-syntax-table)
4b1aaa8b 1919
f32b3b91 1920 (set (make-local-variable 'indent-line-function) 'idlwave-indent-and-action)
4b1aaa8b 1921
f32b3b91
CD
1922 (make-local-variable idlwave-comment-indent-function)
1923 (set idlwave-comment-indent-function 'idlwave-comment-hook)
4b1aaa8b 1924
f32b3b91
CD
1925 (set (make-local-variable 'comment-start-skip) ";+[ \t]*")
1926 (set (make-local-variable 'comment-start) ";")
0dc2be2f 1927 (set (make-local-variable 'comment-add) 1) ; ";;" for new and regions
f66f03de 1928 (set (make-local-variable 'require-final-newline) t)
f32b3b91
CD
1929 (set (make-local-variable 'abbrev-all-caps) t)
1930 (set (make-local-variable 'indent-tabs-mode) nil)
1931 (set (make-local-variable 'completion-ignore-case) t)
4b1aaa8b 1932
f32b3b91
CD
1933 (use-local-map idlwave-mode-map)
1934
1935 (when (featurep 'easymenu)
1936 (easy-menu-add idlwave-mode-menu idlwave-mode-map)
1937 (easy-menu-add idlwave-mode-debug-menu idlwave-mode-map))
1938
1939 (setq mode-name "IDLWAVE")
1940 (setq major-mode 'idlwave-mode)
1941 (setq abbrev-mode t)
4b1aaa8b 1942
f32b3b91
CD
1943 (set (make-local-variable idlwave-fill-function) 'idlwave-auto-fill)
1944 (setq comment-end "")
1945 (set (make-local-variable 'comment-multi-line) nil)
4b1aaa8b 1946 (set (make-local-variable 'paragraph-separate)
5e72c6b2 1947 "[ \t\f]*$\\|[ \t]*;+[ \t]*$\\|;+[+=-_*]+$")
f32b3b91
CD
1948 (set (make-local-variable 'paragraph-start) "[ \t\f]\\|[ \t]*;+[ \t]")
1949 (set (make-local-variable 'paragraph-ignore-fill-prefix) nil)
76959b77 1950 (set (make-local-variable 'parse-sexp-ignore-comments) t)
775591f7 1951
e08734e2 1952 ;; ChangeLog
8c43762b 1953 (set (make-local-variable 'add-log-current-defun-function)
e08734e2
S
1954 'idlwave-current-routine-fullname)
1955
f32b3b91
CD
1956 ;; Set tag table list to use IDLTAGS as file name.
1957 (if (boundp 'tag-table-alist)
1958 (add-to-list 'tag-table-alist '("\\.pro$" . "IDLTAGS")))
4b1aaa8b 1959
e08734e2 1960 ;; Font-lock additions
52a244eb 1961 ;; Following line is for Emacs - XEmacs uses the corresponding property
f32b3b91
CD
1962 ;; on the `idlwave-mode' symbol.
1963 (set (make-local-variable 'font-lock-defaults) idlwave-font-lock-defaults)
0dc2be2f
S
1964 (set (make-local-variable 'font-lock-mark-block-function)
1965 'idlwave-mark-subprogram)
1966 (set (make-local-variable 'font-lock-fontify-region-function)
1967 'idlwave-font-lock-fontify-region)
f32b3b91
CD
1968
1969 ;; Imenu setup
1970 (set (make-local-variable 'imenu-create-index-function)
1971 'imenu-default-create-index-function)
1972 (set (make-local-variable 'imenu-extract-index-name-function)
1973 'idlwave-unit-name)
1974 (set (make-local-variable 'imenu-prev-index-position-function)
1975 'idlwave-prev-index-position)
1976
0dc2be2f
S
1977 ;; HideShow setup
1978 (add-to-list 'hs-special-modes-alist
1979 (list 'idlwave-mode
1980 idlwave-begin-block-reg
1981 idlwave-end-block-reg
1982 ";"
1983 'idlwave-forward-block nil))
4b1aaa8b 1984
f32b3b91 1985 ;; Make a local post-command-hook and add our hook to it
f66f03de
S
1986 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1987 ;; (make-local-hook 'post-command-hook)
15e42531
CD
1988 (add-hook 'post-command-hook 'idlwave-command-hook nil 'local)
1989
1990 ;; Make local hooks for buffer updates
f66f03de
S
1991 ;; NB: `make-local-hook' needed for older/alternative Emacs compatibility
1992 ;; (make-local-hook 'kill-buffer-hook)
15e42531 1993 (add-hook 'kill-buffer-hook 'idlwave-kill-buffer-update nil 'local)
f66f03de 1994 ;; (make-local-hook 'after-save-hook)
e08734e2 1995 (add-hook 'after-save-hook 'idlwave-save-buffer-update nil 'local)
15e42531
CD
1996 (add-hook 'after-save-hook 'idlwave-revoke-license-to-kill nil 'local)
1997
52a244eb
S
1998 ;; Setup directories and file, if necessary
1999 (idlwave-setup)
2000
15e42531
CD
2001 ;; Update the routine info with info about current buffer?
2002 (idlwave-new-buffer-update)
f32b3b91 2003
f66f03de
S
2004 ;; Check help location
2005 (idlwave-help-check-locations)
2006
f32b3b91 2007 ;; Run the mode hook
9a969196 2008 (run-mode-hooks 'idlwave-mode-hook))
f32b3b91 2009
52a244eb
S
2010(defvar idlwave-setup-done nil)
2011(defun idlwave-setup ()
2012 (unless idlwave-setup-done
2013 (if (not (file-directory-p idlwave-config-directory))
2014 (make-directory idlwave-config-directory))
4b1aaa8b
PE
2015 (setq
2016 idlwave-user-catalog-file (expand-file-name
2017 idlwave-user-catalog-file
f66f03de 2018 idlwave-config-directory)
4b1aaa8b
PE
2019 idlwave-xml-system-rinfo-converted-file
2020 (expand-file-name
f66f03de
S
2021 idlwave-xml-system-rinfo-converted-file
2022 idlwave-config-directory)
4b1aaa8b
PE
2023 idlwave-path-file (expand-file-name
2024 idlwave-path-file
f66f03de 2025 idlwave-config-directory))
52a244eb
S
2026 (idlwave-read-paths) ; we may need these early
2027 (setq idlwave-setup-done t)))
2028
0dc2be2f
S
2029(defun idlwave-font-lock-fontify-region (beg end &optional verbose)
2030 "Fontify continuation lines correctly."
2031 (let (pos)
2032 (save-excursion
2033 (goto-char beg)
2034 (forward-line -1)
2035 (when (setq pos (idlwave-is-continuation-line))
2036 (goto-char pos)
2037 (idlwave-beginning-of-statement)
2038 (setq beg (point)))))
2039 (font-lock-default-fontify-region beg end verbose))
2040
f32b3b91 2041;;
52a244eb 2042;; Code Formatting ----------------------------------------------------
4b1aaa8b 2043;;
f32b3b91 2044
f32b3b91
CD
2045(defun idlwave-hard-tab ()
2046 "Inserts TAB in buffer in current position."
2047 (interactive)
2048 (insert "\t"))
2049
2050;;; This stuff is experimental
2051
2052(defvar idlwave-command-hook nil
2053 "If non-nil, a list that can be evaluated using `eval'.
2054It is evaluated in the lisp function `idlwave-command-hook' which is
2055placed in `post-command-hook'.")
2056
2057(defun idlwave-command-hook ()
2058 "Command run after every command.
2059Evaluates a non-nil value of the *variable* `idlwave-command-hook' and
2060sets the variable to zero afterwards."
2061 (and idlwave-command-hook
2062 (listp idlwave-command-hook)
2063 (condition-case nil
2064 (eval idlwave-command-hook)
2065 (error nil)))
2066 (setq idlwave-command-hook nil))
2067
2068;;; End experiment
2069
2070;; It would be better to use expand.el for better abbrev handling and
2071;; versatility.
2072
2073(defun idlwave-check-abbrev (arg &optional reserved)
2074 "Reverses abbrev expansion if in comment or string.
2075Argument ARG is the number of characters to move point
2076backward if `idlwave-abbrev-move' is non-nil.
2077If optional argument RESERVED is non-nil then the expansion
2078consists of reserved words, which will be capitalized if
2079`idlwave-reserved-word-upcase' is non-nil.
2080Otherwise, the abbrev will be capitalized if `idlwave-abbrev-change-case'
2081is non-nil, unless its value is \`down in which case the abbrev will be
2082made into all lowercase.
2083Returns non-nil if abbrev is left expanded."
2084 (if (idlwave-quoted)
2085 (progn (unexpand-abbrev)
2086 nil)
2087 (if (and reserved idlwave-reserved-word-upcase)
2088 (upcase-region last-abbrev-location (point))
2089 (cond
2090 ((equal idlwave-abbrev-change-case 'down)
2091 (downcase-region last-abbrev-location (point)))
2092 (idlwave-abbrev-change-case
2093 (upcase-region last-abbrev-location (point)))))
2094 (if (and idlwave-abbrev-move (> arg 0))
2095 (if (boundp 'post-command-hook)
2096 (setq idlwave-command-hook (list 'backward-char (1+ arg)))
2097 (backward-char arg)))
2098 t))
2099
2100(defun idlwave-in-comment ()
2101 "Returns t if point is inside a comment, nil otherwise."
2102 (save-excursion
2103 (let ((here (point)))
2104 (and (idlwave-goto-comment) (> here (point))))))
2105
2106(defun idlwave-goto-comment ()
2107 "Move to start of comment delimiter on current line.
2108Moves to end of line if there is no comment delimiter.
2109Ignores comment delimiters in strings.
2110Returns point if comment found and nil otherwise."
2111 (let ((eos (progn (end-of-line) (point)))
2112 (data (match-data))
2113 found)
2114 ;; Look for first comment delimiter not in a string
2115 (beginning-of-line)
2116 (setq found (search-forward comment-start eos 'lim))
2117 (while (and found (idlwave-in-quote))
2118 (setq found (search-forward comment-start eos 'lim)))
2119 (store-match-data data)
2120 (and found (not (idlwave-in-quote))
2121 (progn
2122 (backward-char 1)
2123 (point)))))
2124
5e72c6b2
S
2125(defvar transient-mark-mode)
2126(defvar zmacs-regions)
2127(defvar mark-active)
2128(defun idlwave-region-active-p ()
52a244eb 2129 "Is transient-mark-mode on and the region active?
5e72c6b2
S
2130Works on both Emacs and XEmacs."
2131 (if (featurep 'xemacs)
2132 (and zmacs-regions (region-active-p))
2133 (and transient-mark-mode mark-active)))
2134
f32b3b91
CD
2135(defun idlwave-show-matching-quote ()
2136 "Insert quote and show matching quote if this is end of a string."
2137 (interactive)
2138 (let ((bq (idlwave-in-quote))
2139 (inq last-command-char))
2140 (if (and bq (not (idlwave-in-comment)))
2141 (let ((delim (char-after bq)))
2142 (insert inq)
2143 (if (eq inq delim)
2144 (save-excursion
2145 (goto-char bq)
2146 (sit-for 1))))
2147 ;; Not the end of a string
2148 (insert inq))))
2149
2150(defun idlwave-show-begin-check ()
2151 "Ensure that the previous word was a token before `idlwave-show-begin'.
2152An END token must be preceded by whitespace."
5e72c6b2
S
2153 (if (not (idlwave-quoted))
2154 (if
2155 (save-excursion
2156 (backward-word 1)
2157 (backward-char 1)
2158 (looking-at "[ \t\n\f]"))
2159 (idlwave-show-begin))))
f32b3b91
CD
2160
2161(defun idlwave-show-begin ()
2162 "Finds the start of current block and blinks to it for a second.
2163Also checks if the correct end statement has been used."
2164 ;; All end statements are reserved words
76959b77 2165 ;; Re-indent end line
52a244eb
S
2166 ;;(insert-char ?\ 1) ;; So indent, etc. work well
2167 ;;(backward-char 1)
76959b77
S
2168 (let* ((pos (point-marker))
2169 (last-abbrev-marker (copy-marker last-abbrev-location))
2170 (eol-pos (save-excursion (end-of-line) (point)))
2171 begin-pos end-pos end end1 )
2172 (if idlwave-reindent-end (idlwave-indent-line))
52a244eb 2173 (setq last-abbrev-location (marker-position last-abbrev-marker))
f32b3b91
CD
2174 (when (and (idlwave-check-abbrev 0 t)
2175 idlwave-show-block)
2176 (save-excursion
2177 ;; Move inside current block
76959b77 2178 (goto-char last-abbrev-marker)
f32b3b91 2179 (idlwave-block-jump-out -1 'nomark)
76959b77
S
2180 (setq begin-pos (point))
2181 (idlwave-block-jump-out 1 'nomark)
2182 (setq end-pos (point))
2183 (if (> end-pos eol-pos)
2184 (setq end-pos pos))
2185 (goto-char end-pos)
4b1aaa8b 2186 (setq end (buffer-substring
76959b77
S
2187 (progn
2188 (skip-chars-backward "a-zA-Z")
2189 (point))
2190 end-pos))
2191 (goto-char begin-pos)
f32b3b91
CD
2192 (when (setq end1 (cdr (idlwave-block-master)))
2193 (cond
5e72c6b2 2194 ((null end1)) ; no-operation
f32b3b91
CD
2195 ((string= (downcase end) (downcase end1))
2196 (sit-for 1))
2197 ((string= (downcase end) "end")
2198 ;; A generic end
2199 (if idlwave-expand-generic-end
2200 (save-excursion
2201 (goto-char pos)
2202 (backward-char 3)
2203 (insert (if (string= end "END") (upcase end1) end1))
2204 (delete-char 3)))
2205 (sit-for 1))
2206 (t
2207 (beep)
4b1aaa8b 2208 (message "Warning: Shouldn't this be \"%s\" instead of \"%s\"?"
f32b3b91 2209 end1 end)
52a244eb
S
2210 (sit-for 1))))))))
2211 ;;(delete-char 1))
f32b3b91
CD
2212
2213(defun idlwave-block-master ()
2214 (let ((case-fold-search t))
2215 (save-excursion
2216 (cond
05a1abfc 2217 ((looking-at "pro\\|case\\|switch\\|function\\>")
f32b3b91
CD
2218 (assoc (downcase (match-string 0)) idlwave-block-matches))
2219 ((looking-at "begin\\>")
4b1aaa8b
PE
2220 (let ((limit (save-excursion
2221 (idlwave-beginning-of-statement)
f32b3b91
CD
2222 (point))))
2223 (cond
52a244eb
S
2224 ((re-search-backward ":[ \t]*\\=" limit t)
2225 ;; seems to be a case thing
2226 '("begin" . "end"))
f32b3b91
CD
2227 ((re-search-backward idlwave-block-match-regexp limit t)
2228 (assoc (downcase (match-string 1))
2229 idlwave-block-matches))
f32b3b91 2230 (t
52a244eb 2231 ;; Just a normal block
f32b3b91
CD
2232 '("begin" . "end")))))
2233 (t nil)))))
2234
2235(defun idlwave-close-block ()
2236 "Terminate the current block with the correct END statement."
2237 (interactive)
f32b3b91
CD
2238 ;; Start new line if we are not in a new line
2239 (unless (save-excursion
2240 (skip-chars-backward " \t")
2241 (bolp))
2242 (let ((idlwave-show-block nil))
2243 (newline-and-indent)))
5e72c6b2
S
2244 (let ((last-abbrev-location (point))) ; for upcasing
2245 (insert "end")
2246 (idlwave-show-begin)))
2247
f66f03de
S
2248(defun idlwave-custom-ampersand-surround (&optional is-action)
2249 "Surround &, leaving room for && (which surrround as well)."
2250 (let* ((prev-char (char-after (- (point) 2)))
2251 (next-char (char-after (point)))
2252 (amp-left (eq prev-char ?&))
2253 (amp-right (eq next-char ?&))
2254 (len (if amp-left 2 1)))
2255 (unless amp-right ;no need to do it twice, amp-left will catch it.
2256 (idlwave-surround -1 (if (or is-action amp-left) -1) len))))
2257
2258(defun idlwave-custom-ltgtr-surround (gtr &optional is-action)
2259 "Surround > and < by blanks, leaving room for >= and <=, and considering ->."
2260 (let* ((prev-char (char-after (- (point) 2)))
2261 (next-char (char-after (point)))
2262 (method-invoke (and gtr (eq prev-char ?-)))
2263 (len (if method-invoke 2 1)))
2264 (unless (eq next-char ?=)
2265 ;; Key binding: pad only on left, to save for possible >=/<=
2266 (idlwave-surround -1 (if (or is-action method-invoke) -1) len))))
2267
2268(defun idlwave-surround (&optional before after length is-action)
595ab50b
CD
2269 "Surround the LENGTH characters before point with blanks.
2270LENGTH defaults to 1.
f32b3b91 2271Optional arguments BEFORE and AFTER affect the behavior before and
595ab50b
CD
2272after the characters (see also description of `idlwave-make-space'):
2273
2274nil do nothing
22750 force no spaces
2276integer > 0 force exactly n spaces
2277integer < 0 at least |n| spaces
f32b3b91
CD
2278
2279The function does nothing if any of the following conditions is true:
2280- `idlwave-surround-by-blank' is nil
f66f03de 2281- the character before point is inside a string or comment"
5e72c6b2 2282 (when (and idlwave-surround-by-blank (not (idlwave-quoted)))
f66f03de
S
2283 (let ((length (or length 1))) ; establish a default for LENGTH
2284 (backward-char length)
2285 (save-restriction
2286 (let ((here (point)))
2287 (skip-chars-backward " \t")
2288 (if (bolp)
2289 ;; avoid clobbering indent
2290 (progn
2291 (move-to-column (idlwave-calculate-indent))
2292 (if (<= (point) here)
2293 (narrow-to-region (point) here))
2294 (goto-char here)))
2295 (idlwave-make-space before))
2296 (skip-chars-forward " \t"))
2297 (forward-char length)
2298 (idlwave-make-space after)
2299 ;; Check to see if the line should auto wrap
2300 (if (and (equal (char-after (1- (point))) ?\ )
2301 (> (current-column) fill-column))
2302 (funcall auto-fill-function)))))
f32b3b91
CD
2303
2304(defun idlwave-make-space (n)
2305 "Make space at point.
2306The space affected is all the spaces and tabs around point.
2307If n is non-nil then point is left abs(n) spaces from the beginning of
2308the contiguous space.
2309The amount of space at point is determined by N.
2310If the value of N is:
2311nil - do nothing.
595ab50b
CD
2312> 0 - exactly N spaces.
2313< 0 - a minimum of -N spaces, i.e., do not change if there are
2314 already -N spaces.
23150 - no spaces (i.e. remove any existing space)."
f32b3b91
CD
2316 (if (integerp n)
2317 (let
2318 ((start-col (progn (skip-chars-backward " \t") (current-column)))
2319 (left (point))
2320 (end-col (progn (skip-chars-forward " \t") (current-column))))
2321 (delete-horizontal-space)
2322 (cond
2323 ((> n 0)
2324 (idlwave-indent-to (+ start-col n))
2325 (goto-char (+ left n)))
2326 ((< n 0)
2327 (idlwave-indent-to end-col (- n))
2328 (goto-char (- left n)))
2329 ;; n = 0, done
2330 ))))
2331
2332(defun idlwave-newline ()
2333 "Inserts a newline and indents the current and previous line."
2334 (interactive)
2335 ;;
2336 ;; Handle unterminated single and double quotes
2337 ;; If not in a comment and in a string then insertion of a newline
2338 ;; will mean unbalanced quotes.
2339 ;;
2340 (if (and (not (idlwave-in-comment)) (idlwave-in-quote))
2341 (progn (beep)
2342 (message "Warning: unbalanced quotes?")))
2343 (newline)
2344 ;;
2345 ;; The current line is being split, the cursor should be at the
2346 ;; beginning of the new line skipping the leading indentation.
2347 ;;
2348 ;; The reason we insert the new line before indenting is that the
2349 ;; indenting could be confused by keywords (e.g. END) on the line
2350 ;; after the split point. This prevents us from just using
2351 ;; `indent-for-tab-command' followed by `newline-and-indent'.
2352 ;;
2353 (beginning-of-line 0)
2354 (idlwave-indent-line)
2355 (forward-line)
2356 (idlwave-indent-line))
2357
2358;;
2359;; Use global variable 'comment-column' to set parallel comment
2360;;
2361;; Modeled on lisp.el
2362;; Emacs Lisp and IDL (Wave CL) have identical comment syntax
2363(defun idlwave-comment-hook ()
2364 "Compute indent for the beginning of the IDL comment delimiter."
2365 (if (or (looking-at idlwave-no-change-comment)
2366 (if idlwave-begin-line-comment
2367 (looking-at idlwave-begin-line-comment)
2368 (looking-at "^;")))
2369 (current-column)
2370 (if (looking-at idlwave-code-comment)
2371 (if (save-excursion (skip-chars-backward " \t") (bolp))
2372 ;; On line by itself, indent as code
2373 (let ((tem (idlwave-calculate-indent)))
2374 (if (listp tem) (car tem) tem))
2375 ;; after code - do not change
2376 (current-column))
2377 (skip-chars-backward " \t")
2378 (max (if (bolp) 0 (1+ (current-column)))
2379 comment-column))))
2380
2381(defun idlwave-split-line ()
2382 "Continue line by breaking line at point and indent the lines.
2383For a code line insert continuation marker. If the line is a line comment
2384then the new line will contain a comment with the same indentation.
2385Splits strings with the IDL operator `+' if `idlwave-split-line-string' is
2386non-nil."
2387 (interactive)
15e42531
CD
2388 ;; Expand abbreviation, just like normal RET would.
2389 (and abbrev-mode (expand-abbrev))
f32b3b91
CD
2390 (let (beg)
2391 (if (not (idlwave-in-comment))
2392 ;; For code line add continuation.
2393 ;; Check if splitting a string.
2394 (progn
2395 (if (setq beg (idlwave-in-quote))
2396 (if idlwave-split-line-string
2397 ;; Split the string.
2398 (progn (insert (setq beg (char-after beg)) " + "
2399 idlwave-continuation-char beg)
5e72c6b2
S
2400 (backward-char 1)
2401 (newline-and-indent)
2402 (forward-char 1))
f32b3b91
CD
2403 ;; Do not split the string.
2404 (beep)
2405 (message "Warning: continuation inside string!!")
2406 (insert " " idlwave-continuation-char))
2407 ;; Not splitting a string.
15e42531
CD
2408 (if (not (member (char-before) '(?\ ?\t)))
2409 (insert " "))
5e72c6b2
S
2410 (insert idlwave-continuation-char)
2411 (newline-and-indent)))
f32b3b91
CD
2412 (indent-new-comment-line))
2413 ;; Indent previous line
2414 (setq beg (- (point-max) (point)))
2415 (forward-line -1)
2416 (idlwave-indent-line)
2417 (goto-char (- (point-max) beg))
2418 ;; Reindent new line
2419 (idlwave-indent-line)))
2420
cca13260
S
2421(defun idlwave-beginning-of-subprogram (&optional nomark)
2422 "Moves point to the beginning of the current program unit.
2423If NOMARK is non-nil, do not push mark."
f32b3b91 2424 (interactive)
cca13260 2425 (idlwave-find-key idlwave-begin-unit-reg -1 nomark))
f32b3b91 2426
cca13260
S
2427(defun idlwave-end-of-subprogram (&optional nomark)
2428 "Moves point to the start of the next program unit.
2429If NOMARK is non-nil, do not push mark."
f32b3b91
CD
2430 (interactive)
2431 (idlwave-end-of-statement)
cca13260 2432 (idlwave-find-key idlwave-end-unit-reg 1 nomark))
f32b3b91
CD
2433
2434(defun idlwave-mark-statement ()
2435 "Mark current IDL statement."
2436 (interactive)
2437 (idlwave-end-of-statement)
2438 (let ((end (point)))
2439 (idlwave-beginning-of-statement)
0dc2be2f 2440 (push-mark end nil t)))
f32b3b91
CD
2441
2442(defun idlwave-mark-block ()
2443 "Mark containing block."
2444 (interactive)
2445 (idlwave-end-of-statement)
2446 (idlwave-backward-up-block -1)
2447 (idlwave-end-of-statement)
2448 (let ((end (point)))
2449 (idlwave-backward-block)
2450 (idlwave-beginning-of-statement)
0dc2be2f 2451 (push-mark end nil t)))
f32b3b91
CD
2452
2453
2454(defun idlwave-mark-subprogram ()
2455 "Put mark at beginning of program, point at end.
2456The marks are pushed."
2457 (interactive)
2458 (idlwave-end-of-statement)
2459 (idlwave-beginning-of-subprogram)
2460 (let ((beg (point)))
2461 (idlwave-forward-block)
0dc2be2f 2462 (push-mark beg nil t))
f32b3b91
CD
2463 (exchange-point-and-mark))
2464
2465(defun idlwave-backward-up-block (&optional arg)
2466 "Move to beginning of enclosing block if prefix ARG >= 0.
2467If prefix ARG < 0 then move forward to enclosing block end."
2468 (interactive "p")
2469 (idlwave-block-jump-out (- arg) 'nomark))
2470
2471(defun idlwave-beginning-of-block ()
2472 "Go to the beginning of the current block."
2473 (interactive)
2474 (idlwave-block-jump-out -1 'nomark)
2475 (forward-word 1))
2476
2477(defun idlwave-end-of-block ()
2478 "Go to the beginning of the current block."
2479 (interactive)
2480 (idlwave-block-jump-out 1 'nomark)
2481 (backward-word 1))
2482
0dc2be2f 2483(defun idlwave-forward-block (&optional arg)
f32b3b91
CD
2484 "Move across next nested block."
2485 (interactive)
0dc2be2f
S
2486 (let ((arg (or arg 1)))
2487 (if (idlwave-down-block arg)
2488 (idlwave-block-jump-out arg 'nomark))))
f32b3b91
CD
2489
2490(defun idlwave-backward-block ()
2491 "Move backward across previous nested block."
2492 (interactive)
2493 (if (idlwave-down-block -1)
2494 (idlwave-block-jump-out -1 'nomark)))
2495
2496(defun idlwave-down-block (&optional arg)
2497 "Go down a block.
2498With ARG: ARG >= 0 go forwards, ARG < 0 go backwards.
2499Returns non-nil if successfull."
2500 (interactive "p")
2501 (let (status)
2502 (if (< arg 0)
2503 ;; Backward
2504 (let ((eos (save-excursion
2505 (idlwave-block-jump-out -1 'nomark)
2506 (point))))
4b1aaa8b 2507 (if (setq status (idlwave-find-key
f32b3b91
CD
2508 idlwave-end-block-reg -1 'nomark eos))
2509 (idlwave-beginning-of-statement)
2510 (message "No nested block before beginning of containing block.")))
2511 ;; Forward
2512 (let ((eos (save-excursion
2513 (idlwave-block-jump-out 1 'nomark)
2514 (point))))
4b1aaa8b 2515 (if (setq status (idlwave-find-key
f32b3b91
CD
2516 idlwave-begin-block-reg 1 'nomark eos))
2517 (idlwave-end-of-statement)
2518 (message "No nested block before end of containing block."))))
2519 status))
2520
2521(defun idlwave-mark-doclib ()
2522 "Put point at beginning of doc library header, mark at end.
2523The marks are pushed."
2524 (interactive)
2525 (let (beg
2526 (here (point)))
2527 (goto-char (point-max))
2528 (if (re-search-backward idlwave-doclib-start nil t)
4b1aaa8b 2529 (progn
f32b3b91
CD
2530 (setq beg (progn (beginning-of-line) (point)))
2531 (if (re-search-forward idlwave-doclib-end nil t)
2532 (progn
2533 (forward-line 1)
0dc2be2f 2534 (push-mark beg nil t)
f32b3b91
CD
2535 (message "Could not find end of doc library header.")))
2536 (message "Could not find doc library header start.")
2537 (goto-char here)))))
2538
e08734e2
S
2539(defun idlwave-current-routine-fullname ()
2540 (let ((name (idlwave-current-routine)))
2541 (idlwave-make-full-name (nth 2 name) (car name))))
2542
15e42531
CD
2543(defun idlwave-current-routine ()
2544 "Return (NAME TYPE CLASS) of current routine."
2545 (idlwave-routines)
2546 (save-excursion
cca13260 2547 (idlwave-beginning-of-subprogram 'nomark)
15e42531
CD
2548 (if (looking-at "[ \t]*\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)")
2549 (let* ((type (if (string= (downcase (match-string 1)) "pro")
2550 'pro 'function))
2551 (class (idlwave-sintern-class (match-string 3)))
2552 (name (idlwave-sintern-routine-or-method (match-string 4) class)))
2553 (list name type class)))))
2554
f32b3b91
CD
2555(defvar idlwave-shell-prompt-pattern)
2556(defun idlwave-beginning-of-statement ()
2557 "Move to beginning of the current statement.
2558Skips back past statement continuations.
2559Point is placed at the beginning of the line whether or not this is an
2560actual statement."
2561 (interactive)
2562 (cond
2563 ((eq major-mode 'idlwave-shell-mode)
2564 (if (re-search-backward idlwave-shell-prompt-pattern nil t)
2565 (goto-char (match-end 0))))
4b1aaa8b 2566 (t
f32b3b91
CD
2567 (if (save-excursion (forward-line -1) (idlwave-is-continuation-line))
2568 (idlwave-previous-statement)
2569 (beginning-of-line)))))
2570
2571(defun idlwave-previous-statement ()
2572 "Moves point to beginning of the previous statement.
2573Returns t if the current line before moving is the beginning of
2574the first non-comment statement in the file, and nil otherwise."
2575 (interactive)
2576 (let (first-statement)
2577 (if (not (= (forward-line -1) 0))
2578 ;; first line in file
2579 t
2580 ;; skip blank lines, label lines, include lines and line comments
2581 (while (and
2582 ;; The current statement is the first statement until we
2583 ;; reach another statement.
2584 (setq first-statement
2585 (or
2586 (looking-at idlwave-comment-line-start-skip)
2587 (looking-at "[ \t]*$")
2588 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2589 (looking-at "^@")))
2590 (= (forward-line -1) 0)))
2591 ;; skip continuation lines
2592 (while (and
2593 (save-excursion
2594 (forward-line -1)
2595 (idlwave-is-continuation-line))
2596 (= (forward-line -1) 0)))
2597 first-statement)))
2598
f32b3b91
CD
2599(defun idlwave-end-of-statement ()
2600 "Moves point to the end of the current IDL statement.
05a1abfc
CD
2601If not in a statement just moves to end of line. Returns position."
2602 (interactive)
2603 (while (and (idlwave-is-continuation-line)
2604 (= (forward-line 1) 0))
2605 (while (and (idlwave-is-comment-or-empty-line)
2606 (= (forward-line 1) 0))))
2607 (end-of-line)
2608 (point))
2609
2610(defun idlwave-end-of-statement0 ()
2611 "Moves point to the end of the current IDL statement.
f32b3b91
CD
2612If not in a statement just moves to end of line. Returns position."
2613 (interactive)
2614 (while (and (idlwave-is-continuation-line)
2615 (= (forward-line 1) 0)))
2616 (end-of-line)
2617 (point))
2618
2619(defun idlwave-next-statement ()
2620 "Moves point to beginning of the next IDL statement.
2621 Returns t if that statement is the last
2622 non-comment IDL statement in the file, and nil otherwise."
2623 (interactive)
2624 (let (last-statement)
2625 (idlwave-end-of-statement)
2626 ;; skip blank lines, label lines, include lines and line comments
2627 (while (and (= (forward-line 1) 0)
2628 ;; The current statement is the last statement until
2629 ;; we reach a new statement.
2630 (setq last-statement
2631 (or
2632 (looking-at idlwave-comment-line-start-skip)
2633 (looking-at "[ \t]*$")
2634 (looking-at (concat "[ \t]*" idlwave-label "[ \t]*$"))
2635 (looking-at "^@")))))
2636 last-statement))
2637
76959b77
S
2638(defun idlwave-skip-multi-commands (&optional lim)
2639 "Skip past multiple commands on a line (with `&')."
2640 (let ((save-point (point)))
2641 (when (re-search-forward ".*&" lim t)
2642 (goto-char (match-end 0))
4b1aaa8b 2643 (if (idlwave-quoted)
6b75c9af
S
2644 (goto-char save-point)
2645 (if (eq (char-after (- (point) 2)) ?&) (goto-char save-point))))
76959b77
S
2646 (point)))
2647
15e42531 2648(defun idlwave-skip-label-or-case ()
f32b3b91
CD
2649 "Skip label or case statement element.
2650Returns position after label.
2651If there is no label point is not moved and nil is returned."
15e42531
CD
2652 ;; Case expressions and labels are terminated by a colon.
2653 ;; So we find the first colon in the line and make sure
2654 ;; - no `?' is before it (might be a ? b : c)
2655 ;; - it is not in a comment
2656 ;; - not in a string constant
2657 ;; - not in parenthesis (like a[0:3])
5e72c6b2 2658 ;; - not followed by another ":" in explicit class, ala a->b::c
15e42531 2659 ;; As many in this mode, this function is heuristic and not an exact
4b1aaa8b 2660 ;; parser.
5e72c6b2
S
2661 (let* ((start (point))
2662 (eos (save-excursion (idlwave-end-of-statement) (point)))
2663 (end (idlwave-find-key ":" 1 'nomark eos)))
f32b3b91 2664 (if (and end
15e42531 2665 (= (nth 0 (parse-partial-sexp start end)) 0)
5e72c6b2
S
2666 (not (string-match "\\?" (buffer-substring start end)))
2667 (not (string-match "^::" (buffer-substring end eos))))
f32b3b91
CD
2668 (progn
2669 (forward-char)
2670 (point))
2671 (goto-char start)
2672 nil)))
2673
2674(defun idlwave-start-of-substatement (&optional pre)
2675 "Move to start of next IDL substatement after point.
2676Uses the type of the current IDL statement to determine if the next
2677statement is on a new line or is a subpart of the current statement.
2678Returns point at start of substatement modulo whitespace.
2679If optional argument is non-nil move to beginning of current
15e42531 2680substatement."
f32b3b91
CD
2681 (let ((orig (point))
2682 (eos (idlwave-end-of-statement))
2683 (ifnest 0)
2684 st nst last)
2685 (idlwave-beginning-of-statement)
15e42531 2686 (idlwave-skip-label-or-case)
52a244eb
S
2687 (if (< (point) orig)
2688 (idlwave-skip-multi-commands orig))
f32b3b91
CD
2689 (setq last (point))
2690 ;; Continue looking for substatements until we are past orig
2691 (while (and (<= (point) orig) (not (eobp)))
2692 (setq last (point))
2693 (setq nst (nth 1 (cdr (setq st (car (idlwave-statement-type))))))
2694 (if (equal (car st) 'if) (setq ifnest (1+ ifnest)))
2695 (cond ((and nst
2696 (idlwave-find-key nst 1 'nomark eos))
2697 (goto-char (match-end 0)))
2698 ((and (> ifnest 0) (idlwave-find-key "\\<else\\>" 1 'nomark eos))
2699 (setq ifnest (1- ifnest))
2700 (goto-char (match-end 0)))
2701 (t (setq ifnest 0)
2702 (idlwave-next-statement))))
2703 (if pre (goto-char last))
15e42531
CD
2704 ;; If a continuation line starts here, move to next line
2705 (if (looking-at "[ \t]*\\$\\([ \t]*\\(;\\|$\\)\\)")
2706 (beginning-of-line 2))
f32b3b91
CD
2707 (point)))
2708
2709(defun idlwave-statement-type ()
2710 "Return the type of the current IDL statement.
2711Uses `idlwave-statement-match' to return a cons of (type . point) with
2712point the ending position where the type was determined. Type is the
2713association from `idlwave-statement-match', i.e. the cons cell from the
2714list not just the type symbol. Returns nil if not an identifiable
2715statement."
2716 (save-excursion
2717 ;; Skip whitespace within a statement which is spaces, tabs, continuations
76959b77
S
2718 ;; and possibly comments
2719 (while (looking-at "[ \t]*\\$")
f32b3b91
CD
2720 (forward-line 1))
2721 (skip-chars-forward " \t")
2722 (let ((st idlwave-statement-match)
2723 (case-fold-search t))
2724 (while (and (not (looking-at (nth 0 (cdr (car st)))))
2725 (setq st (cdr st))))
2726 (if st
2727 (append st (match-end 0))))))
2728
f66f03de 2729(defun idlwave-expand-equal (&optional before after is-action)
52a244eb
S
2730 "Pad '=' with spaces. Two cases: Assignment statement, and keyword
2731assignment. Which case is determined using
2732`idlwave-start-of-substatement' and `idlwave-statement-type'. The
2733equal sign will be surrounded by BEFORE and AFTER blanks. If
2734`idlwave-pad-keyword' is t then keyword assignment is treated just
15e42531
CD
2735like assignment statements. When nil, spaces are removed for keyword
2736assignment. Any other value keeps the current space around the `='.
4b1aaa8b 2737Limits in for loops are treated as keyword assignment.
52a244eb
S
2738
2739Starting with IDL 6.0, a number of op= assignments are available.
2740Since ambiguities of the form:
2741
2742r and= b
2743rand= b
2744
2745can occur, alphanumeric operator assignment will never be pre-padded,
2746only post-padded. You must use a space before these to disambiguate
2747\(not just for padding, but for proper parsing by IDL too!). Other
2748operators, such as ##=, ^=, etc., will be pre-padded.
2749
f66f03de
S
2750IS-ACTION is ignored.
2751
52a244eb 2752See `idlwave-surround'."
f32b3b91 2753 (if idlwave-surround-by-blank
4b1aaa8b 2754 (let
52a244eb 2755 ((non-an-ops "\\(##\\|\\*\\|\\+\\|-\\|/\\|<\\|>\\|\\^\\)\\=")
4b1aaa8b 2756 (an-ops
52a244eb
S
2757 "\\s-\\(AND\\|EQ\\|GE\\|GT\\|LE\\|LT\\|MOD\\|NE\\|OR\\|XOR\\)\\=")
2758 (len 1))
4b1aaa8b
PE
2759
2760 (save-excursion
52a244eb
S
2761 (let ((case-fold-search t))
2762 (backward-char)
4b1aaa8b 2763 (if (or
52a244eb
S
2764 (re-search-backward non-an-ops nil t)
2765 ;; Why doesn't ##? work for both?
4b1aaa8b 2766 (re-search-backward "\\(#\\)\\=" nil t))
52a244eb
S
2767 (setq len (1+ (length (match-string 1))))
2768 (when (re-search-backward an-ops nil t)
3938cb82 2769 ;(setq begin nil) ; won't modify begin
52a244eb 2770 (setq len (1+ (length (match-string 1))))))))
4b1aaa8b
PE
2771
2772 (if (eq t idlwave-pad-keyword)
52a244eb 2773 ;; Everything gets padded equally
f66f03de 2774 (idlwave-surround before after len)
52a244eb
S
2775 ;; Treating keywords/for variables specially...
2776 (let ((st (save-excursion ; To catch "for" variables
2777 (idlwave-start-of-substatement t)
2778 (idlwave-statement-type)))
2779 (what (save-excursion ; To catch keywords
2780 (skip-chars-backward "= \t")
2781 (nth 2 (idlwave-where)))))
2782 (cond ((or (memq what '(function-keyword procedure-keyword))
4b1aaa8b
PE
2783 (memq (caar st) '(for pdef)))
2784 (cond
52a244eb
S
2785 ((null idlwave-pad-keyword)
2786 (idlwave-surround 0 0)
2787 ) ; remove space
2788 (t))) ; leave any spaces alone
f66f03de 2789 (t (idlwave-surround before after len))))))))
4b1aaa8b 2790
f32b3b91 2791
5e72c6b2
S
2792(defun idlwave-indent-and-action (&optional arg)
2793 "Call `idlwave-indent-line' and do expand actions.
2794With prefix ARG non-nil, indent the entire sub-statement."
2795 (interactive "p")
05a1abfc 2796 (save-excursion
4b1aaa8b
PE
2797 (if (and idlwave-expand-generic-end
2798 (re-search-backward "\\<\\(end\\)\\s-*\\="
05a1abfc
CD
2799 (max 0 (- (point) 10)) t)
2800 (looking-at "\\(end\\)\\([ \n\t]\\|\\'\\)"))
2801 (progn (goto-char (match-end 1))
5e72c6b2
S
2802 ;;Expand the END abbreviation, just as RET or Space would have.
2803 (if abbrev-mode (expand-abbrev)
2804 (idlwave-show-begin)))))
52a244eb
S
2805 (when (and (not arg) current-prefix-arg)
2806 (setq arg current-prefix-arg)
2807 (setq current-prefix-arg nil))
4b1aaa8b 2808 (if arg
5e72c6b2
S
2809 (idlwave-indent-statement)
2810 (idlwave-indent-line t)))
f32b3b91
CD
2811
2812(defun idlwave-indent-line (&optional expand)
2813 "Indents current IDL line as code or as a comment.
2814The actions in `idlwave-indent-action-table' are performed.
2815If the optional argument EXPAND is non-nil then the actions in
2816`idlwave-indent-expand-table' are performed."
2817 (interactive)
2818 ;; Move point out of left margin.
2819 (if (save-excursion
2820 (skip-chars-backward " \t")
2821 (bolp))
2822 (skip-chars-forward " \t"))
2823 (let ((mloc (point-marker)))
2824 (save-excursion
2825 (beginning-of-line)
2826 (if (looking-at idlwave-comment-line-start-skip)
2827 ;; Indentation for a line comment
2828 (progn
2829 (skip-chars-forward " \t")
2830 (idlwave-indent-left-margin (idlwave-comment-hook)))
2831 ;;
2832 ;; Code Line
2833 ;;
2834 ;; Before indenting, run action routines.
2835 ;;
2836 (if (and expand idlwave-do-actions)
8ffcfb27 2837 (mapc 'idlwave-do-action idlwave-indent-expand-table))
f32b3b91
CD
2838 ;;
2839 (if idlwave-do-actions
8ffcfb27 2840 (mapc 'idlwave-do-action idlwave-indent-action-table))
f32b3b91
CD
2841 ;;
2842 ;; No longer expand abbrevs on the line. The user can do this
2843 ;; manually using expand-region-abbrevs.
2844 ;;
2845 ;; Indent for code line
2846 ;;
2847 (beginning-of-line)
2848 (if (or
2849 ;; a label line
2850 (looking-at (concat "^" idlwave-label "[ \t]*$"))
2851 ;; a batch command
2852 (looking-at "^[ \t]*@"))
2853 ;; leave flush left
2854 nil
2855 ;; indent the line
2856 (idlwave-indent-left-margin (idlwave-calculate-indent)))
2857 ;; Adjust parallel comment
76959b77
S
2858 (end-of-line)
2859 (if (idlwave-in-comment)
2860 ;; Emacs 21 is too smart with fill-column on comment indent
2861 (let ((fill-column (if (fboundp 'comment-indent-new-line)
2862 (1- (frame-width))
2863 fill-column)))
2864 (indent-for-comment)))))
f32b3b91
CD
2865 (goto-char mloc)
2866 ;; Get rid of marker
76959b77 2867 (set-marker mloc nil)))
f32b3b91
CD
2868
2869(defun idlwave-do-action (action)
f66f03de
S
2870 "Perform an action repeatedly on a line. ACTION is a list (REG
2871. FUNC). REG is a regular expression. FUNC is either a function name
2872to be called with `funcall' or a list to be evaluated with `eval'.
2873The action performed by FUNC should leave point after the match for
2874REG - otherwise an infinite loop may be entered. FUNC is always
2875passed a final argument of 'is-action, so it can discriminate between
2876being run as an action, or a key binding"
f32b3b91
CD
2877 (let ((action-key (car action))
2878 (action-routine (cdr action)))
2879 (beginning-of-line)
2880 (while (idlwave-look-at action-key)
2881 (if (listp action-routine)
f66f03de
S
2882 (eval (append action-routine '('is-action)))
2883 (funcall action-routine 'is-action)))))
f32b3b91
CD
2884
2885(defun idlwave-indent-to (col &optional min)
2886 "Indent from point with spaces until column COL.
2887Inserts space before markers at point."
2888 (if (not min) (setq min 0))
2889 (insert-before-markers
15e42531 2890 (make-string (max min (- col (current-column))) ?\ )))
f32b3b91
CD
2891
2892(defun idlwave-indent-left-margin (col)
2893 "Indent the current line to column COL.
2894Indents such that first non-whitespace character is at column COL
2895Inserts spaces before markers at point."
2896 (save-excursion
2897 (beginning-of-line)
2898 (delete-horizontal-space)
2899 (idlwave-indent-to col)))
2900
2901(defun idlwave-indent-subprogram ()
2902 "Indents program unit which contains point."
2903 (interactive)
2904 (save-excursion
2905 (idlwave-end-of-statement)
2906 (idlwave-beginning-of-subprogram)
2907 (let ((beg (point)))
2908 (idlwave-forward-block)
2909 (message "Indenting subprogram...")
2910 (indent-region beg (point) nil))
2911 (message "Indenting subprogram...done.")))
2912
5e72c6b2
S
2913(defun idlwave-indent-statement ()
2914 "Indent current statement, including all continuation lines."
2915 (interactive)
2916 (save-excursion
2917 (idlwave-beginning-of-statement)
2918 (let ((beg (point)))
2919 (idlwave-end-of-statement)
2920 (indent-region beg (point) nil))))
2921
f32b3b91
CD
2922(defun idlwave-calculate-indent ()
2923 "Return appropriate indentation for current line as IDL code."
2924 (save-excursion
2925 (beginning-of-line)
2926 (cond
2927 ;; Check for beginning of unit - main (beginning of buffer), pro, or
2928 ;; function
2929 ((idlwave-look-at idlwave-begin-unit-reg)
2930 0)
2931 ;; Check for continuation line
2932 ((save-excursion
2933 (and (= (forward-line -1) 0)
2934 (idlwave-is-continuation-line)))
2935 (idlwave-calculate-cont-indent))
2936 ;; calculate indent based on previous and current statements
52a244eb
S
2937 (t (let* (beg-prev-pos
2938 (the-indent
2939 ;; calculate indent based on previous statement
2940 (save-excursion
2941 (cond
2942 ;; Beginning of file
4b1aaa8b 2943 ((prog1
52a244eb
S
2944 (idlwave-previous-statement)
2945 (setq beg-prev-pos (point)))
2946 0)
2947 ;; Main block
2948 ((idlwave-look-at idlwave-begin-unit-reg t)
2949 (+ (idlwave-current-statement-indent)
2950 idlwave-main-block-indent))
2951 ;; Begin block
2952 ((idlwave-look-at idlwave-begin-block-reg t)
4b1aaa8b 2953 (+ (idlwave-min-current-statement-indent)
52a244eb
S
2954 idlwave-block-indent))
2955 ;; End Block
2956 ((idlwave-look-at idlwave-end-block-reg t)
2957 (progn
2958 ;; Match to the *beginning* of the block opener
2959 (goto-char beg-prev-pos)
2960 (idlwave-block-jump-out -1 'nomark) ; go to begin block
2961 (idlwave-min-current-statement-indent)))
2962 ;; idlwave-end-offset
2963 ;; idlwave-block-indent))
4b1aaa8b 2964
52a244eb
S
2965 ;; Default to current indent
2966 ((idlwave-current-statement-indent))))))
f32b3b91
CD
2967 ;; adjust the indentation based on the current statement
2968 (cond
2969 ;; End block
5e72c6b2
S
2970 ((idlwave-look-at idlwave-end-block-reg)
2971 (+ the-indent idlwave-end-offset))
f32b3b91
CD
2972 (the-indent)))))))
2973
2974;;
52a244eb 2975;; Parentheses indent
f32b3b91
CD
2976;;
2977
5e72c6b2
S
2978(defun idlwave-calculate-paren-indent (beg-reg end-reg close-exp)
2979 "Calculate the continuation indent inside a paren group.
4b1aaa8b 2980Returns a cons-cell with (open . indent), where open is the
5e72c6b2
S
2981location of the open paren"
2982 (let ((open (nth 1 (parse-partial-sexp beg-reg end-reg))))
2983 ;; Found an innermost open paren.
2984 (when open
2985 (goto-char open)
2986 ;; Line up with next word unless this is a closing paren.
2987 (cons open
2988 (cond
52a244eb
S
2989 ;; Plain Kernighan-style nested indent
2990 (idlwave-indent-parens-nested
2991 (+ idlwave-continuation-indent (idlwave-current-indent)))
2992
5e72c6b2
S
2993 ;; This is a closed paren - line up under open paren.
2994 (close-exp
2995 (current-column))
52a244eb
S
2996
2997 ;; Empty (or just comment) follows -- revert to basic indent
5e72c6b2
S
2998 ((progn
2999 ;; Skip paren
3000 (forward-char 1)
3001 (looking-at "[ \t$]*\\(;.*\\)?$"))
52a244eb
S
3002 nil)
3003
3004 ;; Line up with first word after any blank space
5e72c6b2
S
3005 ((progn
3006 (skip-chars-forward " \t")
3007 (current-column))))))))
3008
f32b3b91 3009(defun idlwave-calculate-cont-indent ()
52a244eb
S
3010 "Calculates the IDL continuation indent column from the previous
3011statement. Note that here previous statement usually means the
3012beginning of the current statement if this statement is a continuation
3013of the previous line. Various special types of continuations,
3014including assignments, routine definitions, and parenthetical
3015groupings, are treated separately."
f32b3b91 3016 (save-excursion
52a244eb 3017 (let* ((case-fold-search t)
f32b3b91 3018 (end-reg (progn (beginning-of-line) (point)))
52a244eb
S
3019 (beg-last-statement (save-excursion (idlwave-previous-statement)
3020 (point)))
4b1aaa8b 3021 (beg-reg (progn (idlwave-start-of-substatement 'pre)
52a244eb
S
3022 (if (eq (line-beginning-position) end-reg)
3023 (goto-char beg-last-statement)
3024 (point))))
3025 (basic-indent (+ (idlwave-min-current-statement-indent end-reg)
3026 idlwave-continuation-indent))
3027 fancy-nonparen-indent fancy-paren-indent)
4b1aaa8b 3028 (cond
52a244eb
S
3029 ;; Align then with its matching if, etc.
3030 ((let ((matchers '(("\\<if\\>" . "[ \t]*then")
3031 ("\\<\\(if\\|end\\(if\\)?\\)\\>" . "[ \t]*else")
3032 ("\\<\\(for\\|while\\)\\>" . "[ \t]*do")
4b1aaa8b 3033 ("\\<\\(repeat\\|end\\(rep\\)?\\)\\>" .
52a244eb
S
3034 "[ \t]*until")
3035 ("\\<case\\>" . "[ \t]*of")))
3036 match cont-re)
3037 (goto-char end-reg)
4b1aaa8b 3038 (and
52a244eb
S
3039 (setq cont-re
3040 (catch 'exit
3041 (while (setq match (car matchers))
3042 (if (looking-at (cdr match))
3043 (throw 'exit (car match)))
3044 (setq matchers (cdr matchers)))))
3045 (idlwave-find-key cont-re -1 'nomark beg-last-statement)))
3046 (if (looking-at "end") ;; that one's special
4b1aaa8b 3047 (- (idlwave-current-indent)
52a244eb
S
3048 (+ idlwave-block-indent idlwave-end-offset))
3049 (idlwave-current-indent)))
3050
3051 ;; Indent in from the previous line for continuing statements
3052 ((let ((matchers '("\\<then\\>"
3053 "\\<do\\>"
3054 "\\<repeat\\>"
3055 "\\<else\\>"))
3056 match)
3057 (catch 'exit
3058 (goto-char end-reg)
3059 (if (/= (forward-line -1) 0)
3060 (throw 'exit nil))
3061 (while (setq match (car matchers))
3062 (if (looking-at (concat ".*" match "[ \t]*\\$[ \t]*"
3063 "\\(;.*\\)?$"))
3064 (throw 'exit t))
3065 (setq matchers (cdr matchers)))))
3066 (+ idlwave-continuation-indent (idlwave-current-indent)))
3067
3068 ;; Parenthetical indent, either traditional or Kernighan style
3069 ((setq fancy-paren-indent
3070 (let* ((end-reg end-reg)
3071 (close-exp (progn
3072 (goto-char end-reg)
4b1aaa8b 3073 (skip-chars-forward " \t")
52a244eb
S
3074 (looking-at "\\s)")))
3075 indent-cons)
3076 (catch 'loop
3077 (while (setq indent-cons (idlwave-calculate-paren-indent
3078 beg-reg end-reg close-exp))
3079 ;; First permitted containing paren
3080 (if (or
3081 idlwave-indent-to-open-paren
3082 idlwave-indent-parens-nested
3083 (null (cdr indent-cons))
3084 (< (- (cdr indent-cons) basic-indent)
3085 idlwave-max-extra-continuation-indent))
3086 (throw 'loop (cdr indent-cons)))
3087 (setq end-reg (car indent-cons))))))
5e72c6b2
S
3088 fancy-paren-indent)
3089
52a244eb
S
3090 ;; A continued assignment, or procedure call/definition
3091 ((and
3092 (> idlwave-max-extra-continuation-indent 0)
3093 (setq fancy-nonparen-indent
3094 (progn
3095 (goto-char beg-reg)
3096 (while (idlwave-look-at "&")) ; skip continued statements
3097 (cond
3098 ;; A continued Procedure call or definition
3099 ((progn
3100 (idlwave-look-at "^[ \t]*\\(pro\\|function\\)") ;skip over
3101 (looking-at "[ \t]*\\([a-zA-Z0-9.$_]+[ \t]*->[ \t]*\\)?[a-zA-Z][:a-zA-Z0-9$_]*[ \t]*\\(,\\)[ \t]*"))
3102 (goto-char (match-end 0))
3103 ;; Comment only, or blank line with "$"? Basic indent.
3104 (if (save-match-data (looking-at "[ \t$]*\\(;.*\\)?$"))
3105 nil
3106 (current-column)))
4b1aaa8b 3107
52a244eb
S
3108 ;; Continued assignment (with =):
3109 ((catch 'assign ;
3110 (while (looking-at "[^=\n\r]*\\(=\\)[ \t]*")
3111 (goto-char (match-end 0))
4b1aaa8b 3112 (if (null (idlwave-what-function beg-reg))
52a244eb
S
3113 (throw 'assign t))))
3114 (unless (or
3115 (idlwave-in-quote)
3116 (looking-at "[ \t$]*\\(;.*\\)?$") ; use basic
3117 (save-excursion
3118 (goto-char beg-last-statement)
3119 (eq (caar (idlwave-statement-type)) 'for)))
3120 (current-column))))))
3121 (< (- fancy-nonparen-indent basic-indent)
3122 idlwave-max-extra-continuation-indent))
3123 (if fancy-paren-indent ;calculated but disallowed paren indent
3124 (+ fancy-nonparen-indent idlwave-continuation-indent)
3125 fancy-nonparen-indent))
3126
3127 ;; Basic indent, by default
3128 (t basic-indent)))))
3129
3130
f32b3b91 3131
15e42531
CD
3132(defun idlwave-find-key (key-re &optional dir nomark limit)
3133 "Move to next match of the regular expression KEY-RE.
3134Matches inside comments or string constants will be ignored.
3135If DIR is negative, the search will be backwards.
3136At a successful match, the mark is pushed unless NOMARK is non-nil.
3137Searches are limited to LIMIT.
3138Searches are case-insensitive and use a special syntax table which
3139treats `$' and `_' as word characters.
3140Return value is the beginning of the match or (in case of failure) nil."
3141 (setq dir (or dir 0))
3142 (let ((case-fold-search t)
3143 (search-func (if (> dir 0) 're-search-forward 're-search-backward))
3144 found)
3145 (idlwave-with-special-syntax
3146 (save-excursion
3147 (catch 'exit
3148 (while (funcall search-func key-re limit t)
3149 (if (not (idlwave-quoted))
52a244eb
S
3150 (throw 'exit (setq found (match-beginning 0)))
3151 (if (or (and (> dir 0) (eobp))
3152 (and (< dir 0) (bobp)))
3153 (throw 'exit nil)))))))
15e42531
CD
3154 (if found
3155 (progn
3156 (if (not nomark) (push-mark))
3157 (goto-char found)
3158 found)
3159 nil)))
3160
f32b3b91
CD
3161(defun idlwave-block-jump-out (&optional dir nomark)
3162 "When optional argument DIR is non-negative, move forward to end of
3163current block using the `idlwave-begin-block-reg' and `idlwave-end-block-reg'
3164regular expressions. When DIR is negative, move backwards to block beginning.
3165Recursively calls itself to skip over nested blocks. DIR defaults to
3166forward. Calls `push-mark' unless the optional argument NOMARK is
3167non-nil. Movement is limited by the start of program units because of
3168possibility of unbalanced blocks."
3169 (interactive "P")
3170 (or dir (setq dir 0))
3171 (let* ((here (point))
3172 (case-fold-search t)
3173 (limit (if (>= dir 0) (point-max) (point-min)))
4b1aaa8b 3174 (block-limit (if (>= dir 0)
f32b3b91
CD
3175 idlwave-begin-block-reg
3176 idlwave-end-block-reg))
3177 found
3178 (block-reg (concat idlwave-begin-block-reg "\\|"
3179 idlwave-end-block-reg))
3180 (unit-limit (or (save-excursion
3181 (if (< dir 0)
3182 (idlwave-find-key
3183 idlwave-begin-unit-reg dir t limit)
3184 (end-of-line)
4b1aaa8b 3185 (idlwave-find-key
f32b3b91
CD
3186 idlwave-end-unit-reg dir t limit)))
3187 limit)))
3188 (if (>= dir 0) (end-of-line)) ;Make sure we are in current block
3189 (if (setq found (idlwave-find-key block-reg dir t unit-limit))
3190 (while (and found (looking-at block-limit))
3191 (if (>= dir 0) (forward-word 1))
3192 (idlwave-block-jump-out dir t)
3193 (setq found (idlwave-find-key block-reg dir t unit-limit))))
3194 (if (not nomark) (push-mark here))
3195 (if (not found) (goto-char unit-limit)
3196 (if (>= dir 0) (forward-word 1)))))
3197
52a244eb
S
3198(defun idlwave-min-current-statement-indent (&optional end-reg)
3199 "The minimum indent in the current statement."
3200 (idlwave-beginning-of-statement)
3201 (if (not (idlwave-is-continuation-line))
3202 (idlwave-current-indent)
3203 (let ((min (idlwave-current-indent)) comm-or-empty)
3204 (while (and (= (forward-line 1) 0)
3205 (or (setq comm-or-empty (idlwave-is-comment-or-empty-line))
3206 (idlwave-is-continuation-line))
3207 (or (null end-reg) (< (point) end-reg)))
3208 (unless comm-or-empty (setq min (min min (idlwave-current-indent)))))
3209 (if (or comm-or-empty (and end-reg (>= (point) end-reg)))
4b1aaa8b 3210 min
52a244eb
S
3211 (min min (idlwave-current-indent))))))
3212
3213(defun idlwave-current-statement-indent (&optional last-line)
f32b3b91
CD
3214 "Return indentation of the current statement.
3215If in a statement, moves to beginning of statement before finding indent."
52a244eb
S
3216 (if last-line
3217 (idlwave-end-of-statement)
3218 (idlwave-beginning-of-statement))
f32b3b91
CD
3219 (idlwave-current-indent))
3220
3221(defun idlwave-current-indent ()
3222 "Return the column of the indentation of the current line.
3223Skips any whitespace. Returns 0 if the end-of-line follows the whitespace."
3224 (save-excursion
3225 (beginning-of-line)
3226 (skip-chars-forward " \t")
3227 ;; if we are at the end of blank line return 0
3228 (cond ((eolp) 0)
3229 ((current-column)))))
3230
3231(defun idlwave-is-continuation-line ()
5e72c6b2
S
3232 "Tests if current line is continuation line.
3233Blank or comment-only lines following regular continuation lines (with
3234`$') count as continuations too."
0dc2be2f
S
3235 (let (p)
3236 (save-excursion
4b1aaa8b 3237 (or
0dc2be2f
S
3238 (idlwave-look-at "\\<\\$")
3239 (catch 'loop
4b1aaa8b 3240 (while (and (looking-at "^[ \t]*\\(;.*\\)?$")
0dc2be2f
S
3241 (eq (forward-line -1) 0))
3242 (if (setq p (idlwave-look-at "\\<\\$")) (throw 'loop p))))))))
f32b3b91
CD
3243
3244(defun idlwave-is-comment-line ()
05a1abfc 3245 "Tests if the current line is a comment line."
f32b3b91
CD
3246 (save-excursion
3247 (beginning-of-line 1)
3248 (looking-at "[ \t]*;")))
3249
05a1abfc
CD
3250(defun idlwave-is-comment-or-empty-line ()
3251 "Tests if the current line is a comment line."
3252 (save-excursion
3253 (beginning-of-line 1)
3254 (looking-at "[ \t]*[;\n]")))
3255
f32b3b91 3256(defun idlwave-look-at (regexp &optional cont beg)
15e42531
CD
3257 "Searches current line from current point for REGEXP.
3258If optional argument CONT is non-nil, searches to the end of
3259the current statement.
3260If optional arg BEG is non-nil, search starts from the beginning of the
3261current statement.
3262Ignores matches that end in a comment or inside a string expression.
3263Returns point if successful, nil otherwise.
3264This function produces unexpected results if REGEXP contains quotes or
3265a comment delimiter. The search is case insensitive.
3266If successful leaves point after the match, otherwise, does not move point."
f32b3b91 3267 (let ((here (point))
f32b3b91 3268 (case-fold-search t)
15e42531
CD
3269 (eos (save-excursion
3270 (if cont (idlwave-end-of-statement) (end-of-line))
3271 (point)))
f32b3b91 3272 found)
15e42531
CD
3273 (idlwave-with-special-syntax
3274 (if beg (idlwave-beginning-of-statement))
3275 (while (and (setq found (re-search-forward regexp eos t))
3276 (idlwave-quoted))))
f32b3b91
CD
3277 (if (not found) (goto-char here))
3278 found))
3279
3280(defun idlwave-fill-paragraph (&optional nohang)
3281 "Fills paragraphs in comments.
3282A paragraph is made up of all contiguous lines having the same comment
3283leader (the leading whitespace before the comment delimiter and the
3284comment delimiter). In addition, paragraphs are separated by blank
3285line comments. The indentation is given by the hanging indent of the
3286first line, otherwise by the minimum indentation of the lines after
3287the first line. The indentation of the first line does not change.
3288Does not effect code lines. Does not fill comments on the same line
3289with code. The hanging indent is given by the end of the first match
3290matching `idlwave-hang-indent-regexp' on the paragraph's first line . If the
3291optional argument NOHANG is non-nil then the hanging indent is
3292ignored."
3293 (interactive "P")
3294 ;; check if this is a line comment
3295 (if (save-excursion
3296 (beginning-of-line)
3297 (skip-chars-forward " \t")
3298 (looking-at comment-start))
3299 (let
3300 ((indent 999)
3301 pre here diff fill-prefix-reg bcl first-indent
3302 hang start end)
3303 ;; Change tabs to spaces in the surrounding paragraph.
3304 ;; The surrounding paragraph will be the largest containing block of
3305 ;; contiguous line comments. Thus, we may be changing tabs in
3306 ;; a much larger area than is needed, but this is the easiest
3307 ;; brute force way to do it.
3308 ;;
3309 ;; This has the undesirable side effect of replacing the tabs
3310 ;; permanently without the user's request or knowledge.
3311 (save-excursion
3312 (backward-paragraph)
3313 (setq start (point)))
3314 (save-excursion
3315 (forward-paragraph)
3316 (setq end (point)))
3317 (untabify start end)
3318 ;;
3319 (setq here (point))
3320 (beginning-of-line)
3321 (setq bcl (point))
3322 (re-search-forward
3323 (concat "^[ \t]*" comment-start "+")
3324 (save-excursion (end-of-line) (point))
3325 t)
3326 ;; Get the comment leader on the line and its length
3327 (setq pre (current-column))
3328 ;; the comment leader is the indentation plus exactly the
3329 ;; number of consecutive ";".
3330 (setq fill-prefix-reg
3331 (concat
3332 (setq fill-prefix
3333 (regexp-quote
3334 (buffer-substring (save-excursion
3335 (beginning-of-line) (point))
3336 (point))))
3337 "[^;]"))
4b1aaa8b 3338
f32b3b91
CD
3339 ;; Mark the beginning and end of the paragraph
3340 (goto-char bcl)
3341 (while (and (looking-at fill-prefix-reg)
3342 (not (looking-at paragraph-separate))
3343 (not (bobp)))
3344 (forward-line -1))
3345 ;; Move to first line of paragraph
3346 (if (/= (point) bcl)
3347 (forward-line 1))
3348 (setq start (point))
3349 (goto-char bcl)
3350 (while (and (looking-at fill-prefix-reg)
3351 (not (looking-at paragraph-separate))
3352 (not (eobp)))
3353 (forward-line 1))
3354 (beginning-of-line)
3355 (if (or (not (looking-at fill-prefix-reg))
3356 (looking-at paragraph-separate))
3357 (forward-line -1))
3358 (end-of-line)
3359 ;; if at end of buffer add a newline (need this because
3360 ;; fill-region needs END to be at the beginning of line after
3361 ;; the paragraph or it will add a line).
3362 (if (eobp)
3363 (progn (insert ?\n) (backward-char 1)))
3364 ;; Set END to the beginning of line after the paragraph
3365 ;; END is calculated as distance from end of buffer
3366 (setq end (- (point-max) (point) 1))
3367 ;;
3368 ;; Calculate the indentation for the paragraph.
3369 ;;
3370 ;; In the following while statements, after one iteration
3371 ;; point will be at the beginning of a line in which case
3372 ;; the while will not be executed for the
3373 ;; the first paragraph line and thus will not affect the
3374 ;; indentation.
3375 ;;
3376 ;; First check to see if indentation is based on hanging indent.
3377 (if (and (not nohang) idlwave-hanging-indent
3378 (setq hang
3379 (save-excursion
3380 (goto-char start)
3381 (idlwave-calc-hanging-indent))))
3382 ;; Adjust lines of paragraph by inserting spaces so that
3383 ;; each line's indent is at least as great as the hanging
3384 ;; indent. This is needed for fill-paragraph to work with
3385 ;; a fill-prefix.
3386 (progn
3387 (setq indent hang)
3388 (beginning-of-line)
3389 (while (> (point) start)
3390 (re-search-forward comment-start-skip
3391 (save-excursion (end-of-line) (point))
3392 t)
3393 (if (> (setq diff (- indent (current-column))) 0)
3394 (progn
3395 (if (>= here (point))
3396 ;; adjust the original location for the
3397 ;; inserted text.
3398 (setq here (+ here diff)))
15e42531 3399 (insert (make-string diff ?\ ))))
f32b3b91
CD
3400 (forward-line -1))
3401 )
4b1aaa8b 3402
f32b3b91
CD
3403 ;; No hang. Instead find minimum indentation of paragraph
3404 ;; after first line.
3405 ;; For the following while statement, since START is at the
aa87aafc 3406 ;; beginning of line and END is at the end of line
f32b3b91
CD
3407 ;; point is greater than START at least once (which would
3408 ;; be the case for a single line paragraph).
3409 (while (> (point) start)
3410 (beginning-of-line)
3411 (setq indent
3412 (min indent
3413 (progn
3414 (re-search-forward
3415 comment-start-skip
3416 (save-excursion (end-of-line) (point))
3417 t)
3418 (current-column))))
3419 (forward-line -1))
3420 )
3421 (setq fill-prefix (concat fill-prefix
3422 (make-string (- indent pre)
15e42531 3423 ?\ )))
f32b3b91
CD
3424 ;; first-line indent
3425 (setq first-indent
3426 (max
3427 (progn
3428 (re-search-forward
3429 comment-start-skip
3430 (save-excursion (end-of-line) (point))
3431 t)
3432 (current-column))
3433 indent))
4b1aaa8b 3434
f32b3b91
CD
3435 ;; try to keep point at its original place
3436 (goto-char here)
3437
3438 ;; In place of the more modern fill-region-as-paragraph, a hack
3439 ;; to keep whitespace untouched on the first line within the
3440 ;; indent length and to preserve any indent on the first line
3441 ;; (first indent).
3442 (save-excursion
3443 (setq diff
3444 (buffer-substring start (+ start first-indent -1)))
15e42531 3445 (subst-char-in-region start (+ start first-indent -1) ?\ ?~ nil)
f32b3b91
CD
3446 (fill-region-as-paragraph
3447 start
3448 (- (point-max) end)
3449 (current-justification)
3450 nil)
3451 (delete-region start (+ start first-indent -1))
3452 (goto-char start)
3453 (insert diff))
3454 ;; When we want the point at the beginning of the comment
3455 ;; body fill-region will put it at the beginning of the line.
3456 (if (bolp) (skip-chars-forward (concat " \t" comment-start)))
3457 (setq fill-prefix nil))))
3458
3459(defun idlwave-calc-hanging-indent ()
3460 "Calculate the position of the hanging indent for the comment
3461paragraph. The hanging indent position is given by the first match
3462with the `idlwave-hang-indent-regexp'. If `idlwave-use-last-hang-indent' is
3463non-nil then use last occurrence matching `idlwave-hang-indent-regexp' on
3464the line.
3465If not found returns nil."
3466 (if idlwave-use-last-hang-indent
3467 (save-excursion
3468 (end-of-line)
3469 (if (re-search-backward
3470 idlwave-hang-indent-regexp
3471 (save-excursion (beginning-of-line) (point))
3472 t)
3473 (+ (current-column) (length idlwave-hang-indent-regexp))))
3474 (save-excursion
3475 (beginning-of-line)
3476 (if (re-search-forward
3477 idlwave-hang-indent-regexp
3478 (save-excursion (end-of-line) (point))
3479 t)
3480 (current-column)))))
3481
3482(defun idlwave-auto-fill ()
4b1aaa8b 3483 "Called to break lines in auto fill mode.
52a244eb
S
3484Only fills non-comment lines if `idlwave-fill-comment-line-only' is
3485non-nil. Places a continuation character at the end of the line if
3486not in a comment. Splits strings with IDL concatenation operator `+'
3487if `idlwave-auto-fill-split-string' is non-nil."
f32b3b91
CD
3488 (if (<= (current-column) fill-column)
3489 nil ; do not to fill
3490 (if (or (not idlwave-fill-comment-line-only)
3491 (save-excursion
3492 ;; Check for comment line
3493 (beginning-of-line)
3494 (looking-at idlwave-comment-line-start-skip)))
3495 (let (beg)
3496 (idlwave-indent-line)
3497 ;; Prevent actions do-auto-fill which calls indent-line-function.
3498 (let (idlwave-do-actions
d6aac72d 3499 (paragraph-separate ".")
52a244eb
S
3500 (fill-nobreak-predicate
3501 (if (and (idlwave-in-quote)
3502 idlwave-auto-fill-split-string)
3503 (lambda () ;; We'll need 5 spaces for " ' + $"
3504 (<= (- fill-column (current-column)) 5)
3505 ))))
f32b3b91
CD
3506 (do-auto-fill))
3507 (save-excursion
3508 (end-of-line 0)
3509 ;; Indent the split line
a86bd650 3510 (idlwave-indent-line))
f32b3b91
CD
3511 (if (save-excursion
3512 (beginning-of-line)
3513 (looking-at idlwave-comment-line-start-skip))
3514 ;; A continued line comment
3515 ;; We treat continued line comments as part of a comment
3516 ;; paragraph. So we check for a hanging indent.
3517 (if idlwave-hanging-indent
3518 (let ((here (- (point-max) (point)))
3519 (indent
3520 (save-excursion
3521 (forward-line -1)
3522 (idlwave-calc-hanging-indent))))
3523 (if indent
3524 (progn
3525 ;; Remove whitespace between comment delimiter and
3526 ;; text, insert spaces for appropriate indentation.
3527 (beginning-of-line)
3528 (re-search-forward
3529 comment-start-skip
3530 (save-excursion (end-of-line) (point)) t)
3531 (delete-horizontal-space)
3532 (idlwave-indent-to indent)
3533 (goto-char (- (point-max) here)))
3534 )))
3535 ;; Split code or comment?
3536 (if (save-excursion
3537 (end-of-line 0)
3538 (idlwave-in-comment))
52a244eb 3539 ;; Splitting a non-full-line comment.
f32b3b91
CD
3540 ;; Insert the comment delimiter from split line
3541 (progn
3542 (save-excursion
3543 (beginning-of-line)
3544 (skip-chars-forward " \t")
3545 ;; Insert blank to keep off beginning of line
3546 (insert " "
3547 (save-excursion
3548 (forward-line -1)
3549 (buffer-substring (idlwave-goto-comment)
3550 (progn
3551 (skip-chars-forward "; ")
3552 (point))))))
3553 (idlwave-indent-line))
3554 ;; Split code line - add continuation character
3555 (save-excursion
3556 (end-of-line 0)
3557 ;; Check to see if we split a string
3558 (if (and (setq beg (idlwave-in-quote))
3559 idlwave-auto-fill-split-string)
3560 ;; Split the string and concatenate.
3561 ;; The first extra space is for the space
3562 ;; the line was split. That space was removed.
3563 (insert " " (char-after beg) " +"))
3564 (insert " $"))
3565 (if beg
3566 (if idlwave-auto-fill-split-string
3567 ;; Make the second part of continued string
3568 (save-excursion
3569 (beginning-of-line)
3570 (skip-chars-forward " \t")
3571 (insert (char-after beg)))
3572 ;; Warning
3573 (beep)
3574 (message "Warning: continuation inside a string.")))
3575 ;; Although do-auto-fill (via indent-new-comment-line) calls
3576 ;; idlwave-indent-line for the new line, re-indent again
3577 ;; because of the addition of the continuation character.
3578 (idlwave-indent-line))
3579 )))))
3580
3581(defun idlwave-auto-fill-mode (arg)
3582 "Toggle auto-fill mode for IDL mode.
3583With arg, turn auto-fill mode on if arg is positive.
3584In auto-fill mode, inserting a space at a column beyond `fill-column'
3585automatically breaks the line at a previous space."
3586 (interactive "P")
3587 (prog1 (set idlwave-fill-function
3588 (if (if (null arg)
3589 (not (symbol-value idlwave-fill-function))
3590 (> (prefix-numeric-value arg) 0))
3591 'idlwave-auto-fill
3592 nil))
3593 ;; update mode-line
3594 (set-buffer-modified-p (buffer-modified-p))))
3595
52a244eb
S
3596;(defun idlwave-fill-routine-call ()
3597; "Fill a routine definition or statement, indenting appropriately."
3598; (let ((where (idlwave-where)))))
3599
3600
f32b3b91
CD
3601(defun idlwave-doc-header (&optional nomark )
3602 "Insert a documentation header at the beginning of the unit.
3603Inserts the value of the variable idlwave-file-header. Sets mark before
3604moving to do insertion unless the optional prefix argument NOMARK
3605is non-nil."
3606 (interactive "P")
3607 (or nomark (push-mark))
3608 ;; make sure we catch the current line if it begins the unit
5e72c6b2
S
3609 (if idlwave-header-to-beginning-of-file
3610 (goto-char (point-min))
3611 (end-of-line)
3612 (idlwave-beginning-of-subprogram)
3613 (beginning-of-line)
3614 ;; skip function or procedure line
3615 (if (idlwave-look-at "\\<\\(pro\\|function\\)\\>")
3616 (progn
3617 (idlwave-end-of-statement)
3618 (if (> (forward-line 1) 0) (insert "\n")))))
3619 (let ((pos (point)))
3620 (if idlwave-file-header
3621 (cond ((car idlwave-file-header)
a527b753 3622 (insert-file-contents (car idlwave-file-header)))
5e72c6b2
S
3623 ((stringp (car (cdr idlwave-file-header)))
3624 (insert (car (cdr idlwave-file-header))))))
3625 (goto-char pos)))
f32b3b91
CD
3626
3627(defun idlwave-default-insert-timestamp ()
3628 "Default timestamp insertion function"
3629 (insert (current-time-string))
3630 (insert ", " (user-full-name))
5e72c6b2 3631 (if (boundp 'user-mail-address)
4b1aaa8b 3632 (insert " <" user-mail-address ">")
5e72c6b2 3633 (insert " <" (user-login-name) "@" (system-name) ">"))
f32b3b91
CD
3634 ;; Remove extra spaces from line
3635 (idlwave-fill-paragraph)
3636 ;; Insert a blank line comment to separate from the date entry -
3637 ;; will keep the entry from flowing onto date line if re-filled.
5e72c6b2 3638 (insert "\n;\n;\t\t"))
f32b3b91
CD
3639
3640(defun idlwave-doc-modification ()
3641 "Insert a brief modification log at the beginning of the current program.
3642Looks for an occurrence of the value of user variable
3643`idlwave-doc-modifications-keyword' if non-nil. Inserts time and user name
3644and places the point for the user to add a log. Before moving, saves
3645location on mark ring so that the user can return to previous point."
3646 (interactive)
3647 (push-mark)
05a1abfc
CD
3648 (let* (beg end)
3649 (if (and (or (re-search-backward idlwave-doclib-start nil t)
3650 (progn
3651 (goto-char (point-min))
3652 (re-search-forward idlwave-doclib-start nil t)))
3653 (setq beg (match-beginning 0))
3654 (re-search-forward idlwave-doclib-end nil t)
3655 (setq end (match-end 0)))
3656 (progn
3657 (goto-char beg)
4b1aaa8b 3658 (if (re-search-forward
05a1abfc
CD
3659 (concat idlwave-doc-modifications-keyword ":")
3660 end t)
3661 (end-of-line)
3662 (goto-char end)
3663 (end-of-line -1)
3664 (insert "\n" comment-start "\n")
3665 (insert comment-start " " idlwave-doc-modifications-keyword ":"))
3666 (insert "\n;\n;\t")
3667 (run-hooks 'idlwave-timestamp-hook))
3668 (error "No valid DOCLIB header"))))
f32b3b91 3669
e08734e2 3670
f32b3b91
CD
3671;;; CJC 3/16/93
3672;;; Interface to expand-region-abbrevs which did not work when the
3673;;; abbrev hook associated with an abbrev moves point backwards
3674;;; after abbrev expansion, e.g., as with the abbrev '.n'.
3675;;; The original would enter an infinite loop in attempting to expand
3676;;; .n (it would continually expand and unexpand the abbrev without expanding
3677;;; because the point would keep going back to the beginning of the
3678;;; abbrev instead of to the end of the abbrev). We now keep the
3679;;; abbrev hook from moving backwards.
3680;;;
3681(defun idlwave-expand-region-abbrevs (start end)
3682 "Expand each abbrev occurrence in the region.
3683Calling from a program, arguments are START END."
3684 (interactive "r")
3685 (save-excursion
3686 (goto-char (min start end))
3687 (let ((idlwave-show-block nil) ;Do not blink
3688 (idlwave-abbrev-move nil)) ;Do not move
3689 (expand-region-abbrevs start end 'noquery))))
3690
3691(defun idlwave-quoted ()
3692 "Returns t if point is in a comment or quoted string.
3693nil otherwise."
3694 (or (idlwave-in-comment) (idlwave-in-quote)))
3695
3696(defun idlwave-in-quote ()
3697 "Returns location of the opening quote
3698if point is in a IDL string constant, nil otherwise.
3699Ignores comment delimiters on the current line.
3700Properly handles nested quotation marks and octal
3701constants - a double quote followed by an octal digit."
3702;;; Treat an octal inside an apostrophe to be a normal string. Treat a
3703;;; double quote followed by an octal digit to be an octal constant
3704;;; rather than a string. Therefore, there is no terminating double
3705;;; quote.
3706 (save-excursion
3707 ;; Because single and double quotes can quote each other we must
3708 ;; search for the string start from the beginning of line.
3709 (let* ((start (point))
3710 (eol (progn (end-of-line) (point)))
3711 (bq (progn (beginning-of-line) (point)))
3712 (endq (point))
3713 (data (match-data))
3714 delim
3715 found)
3716 (while (< endq start)
3717 ;; Find string start
3718 ;; Don't find an octal constant beginning with a double quote
52a244eb 3719 (if (re-search-forward "[\"']" eol 'lim)
f32b3b91
CD
3720 ;; Find the string end.
3721 ;; In IDL, two consecutive delimiters after the start of a
3722 ;; string act as an
3723 ;; escape for the delimiter in the string.
3724 ;; Two consecutive delimiters alone (i.e., not after the
aa87aafc 3725 ;; start of a string) is the null string.
f32b3b91
CD
3726 (progn
3727 ;; Move to position after quote
3728 (goto-char (1+ (match-beginning 0)))
3729 (setq bq (1- (point)))
3730 ;; Get the string delimiter
3731 (setq delim (char-to-string (preceding-char)))
3732 ;; Check for null string
3733 (if (looking-at delim)
3734 (progn (setq endq (point)) (forward-char 1))
3735 ;; Look for next unpaired delimiter
3736 (setq found (search-forward delim eol 'lim))
3737 (while (looking-at delim)
3738 (forward-char 1)
3739 (setq found (search-forward delim eol 'lim)))
3740 (if found
3741 (setq endq (- (point) 1))
3742 (setq endq (point)))
3743 ))
3744 (progn (setq bq (point)) (setq endq (point)))))
3745 (store-match-data data)
3746 ;; return string beginning position or nil
3747 (if (> start bq) bq))))
3748
76959b77
S
3749(defun idlwave-is-pointer-dereference (&optional limit)
3750 "Determines if the character after point is a pointer dereference *."
3751 (let ((pos (point)))
3752 (and
3753 (eq (char-after) ?\*)
3754 (not (idlwave-in-quote))
3755 (save-excursion
3756 (forward-char)
4b1aaa8b 3757 (re-search-backward (concat "\\(" idlwave-idl-keywords
76959b77
S
3758 "\\|[[(*+-/=,^><]\\)\\s-*\\*") limit t)))))
3759
3760
f32b3b91
CD
3761;; Statement templates
3762
3763;; Replace these with a general template function, something like
3764;; expand.el (I think there was also something with a name similar to
3765;; dmacro.el)
3766
3767(defun idlwave-template (s1 s2 &optional prompt noindent)
3768 "Build a template with optional prompt expression.
3769
3770Opens a line if point is not followed by a newline modulo intervening
3771whitespace. S1 and S2 are strings. S1 is inserted at point followed
595ab50b
CD
3772by S2. Point is inserted between S1 and S2. The case of S1 and S2 is
3773adjusted according to `idlwave-abbrev-change-case'. If optional argument
f32b3b91
CD
3774PROMPT is a string then it is displayed as a message in the
3775minibuffer. The PROMPT serves as a reminder to the user of an
3776expression to enter.
3777
3778The lines containing S1 and S2 are reindented using `indent-region'
3779unless the optional second argument NOINDENT is non-nil."
15e42531 3780 (if (eq major-mode 'idlwave-shell-mode)
05a1abfc 3781 ;; This is a gross hack to avoit template abbrev expansion
15e42531
CD
3782 ;; in the shell. FIXME: This is a dirty hack.
3783 (if (and (eq this-command 'self-insert-command)
3784 (equal last-abbrev-location (point)))
3785 (insert last-abbrev-text)
3786 (error "No templates in idlwave-shell"))
3787 (cond ((eq idlwave-abbrev-change-case 'down)
3788 (setq s1 (downcase s1) s2 (downcase s2)))
3789 (idlwave-abbrev-change-case
3790 (setq s1 (upcase s1) s2 (upcase s2))))
3791 (let ((beg (save-excursion (beginning-of-line) (point)))
3792 end)
3793 (if (not (looking-at "\\s-*\n"))
3794 (open-line 1))
3795 (insert s1)
3796 (save-excursion
3797 (insert s2)
3798 (setq end (point)))
3799 (if (not noindent)
3800 (indent-region beg end nil))
3801 (if (stringp prompt)
274f1353 3802 (message "%s" prompt)))))
4b1aaa8b 3803
595ab50b
CD
3804(defun idlwave-rw-case (string)
3805 "Make STRING have the case required by `idlwave-reserved-word-upcase'."
3806 (if idlwave-reserved-word-upcase
3807 (upcase string)
3808 string))
3809
f32b3b91
CD
3810(defun idlwave-elif ()
3811 "Build skeleton IDL if-else block."
3812 (interactive)
595ab50b
CD
3813 (idlwave-template
3814 (idlwave-rw-case "if")
3815 (idlwave-rw-case " then begin\n\nendif else begin\n\nendelse")
3816 "Condition expression"))
f32b3b91
CD
3817
3818(defun idlwave-case ()
3819 "Build skeleton IDL case statement."
3820 (interactive)
4b1aaa8b 3821 (idlwave-template
595ab50b
CD
3822 (idlwave-rw-case "case")
3823 (idlwave-rw-case " of\n\nendcase")
3824 "Selector expression"))
f32b3b91 3825
05a1abfc
CD
3826(defun idlwave-switch ()
3827 "Build skeleton IDL switch statement."
3828 (interactive)
4b1aaa8b 3829 (idlwave-template
05a1abfc
CD
3830 (idlwave-rw-case "switch")
3831 (idlwave-rw-case " of\n\nendswitch")
3832 "Selector expression"))
3833
f32b3b91
CD
3834(defun idlwave-for ()
3835 "Build skeleton for loop statment."
3836 (interactive)
4b1aaa8b 3837 (idlwave-template
595ab50b
CD
3838 (idlwave-rw-case "for")
3839 (idlwave-rw-case " do begin\n\nendfor")
3840 "Loop expression"))
f32b3b91
CD
3841
3842(defun idlwave-if ()
3843 "Build skeleton for loop statment."
3844 (interactive)
595ab50b
CD
3845 (idlwave-template
3846 (idlwave-rw-case "if")
3847 (idlwave-rw-case " then begin\n\nendif")
3848 "Scalar logical expression"))
f32b3b91
CD
3849
3850(defun idlwave-procedure ()
3851 (interactive)
4b1aaa8b 3852 (idlwave-template
595ab50b
CD
3853 (idlwave-rw-case "pro")
3854 (idlwave-rw-case "\n\nreturn\nend")
3855 "Procedure name"))
f32b3b91
CD
3856
3857(defun idlwave-function ()
3858 (interactive)
4b1aaa8b 3859 (idlwave-template
595ab50b
CD
3860 (idlwave-rw-case "function")
3861 (idlwave-rw-case "\n\nreturn\nend")
3862 "Function name"))
f32b3b91
CD
3863
3864(defun idlwave-repeat ()
3865 (interactive)
595ab50b
CD
3866 (idlwave-template
3867 (idlwave-rw-case "repeat begin\n\nendrep until")
3868 (idlwave-rw-case "")
3869 "Exit condition"))
f32b3b91
CD
3870
3871(defun idlwave-while ()
3872 (interactive)
4b1aaa8b 3873 (idlwave-template
595ab50b
CD
3874 (idlwave-rw-case "while")
3875 (idlwave-rw-case " do begin\n\nendwhile")
3876 "Entry condition"))
f32b3b91
CD
3877
3878(defun idlwave-split-string (string &optional pattern)
3879 "Return a list of substrings of STRING which are separated by PATTERN.
3880If PATTERN is omitted, it defaults to \"[ \\f\\t\\n\\r\\v]+\"."
3881 (or pattern
3882 (setq pattern "[ \f\t\n\r\v]+"))
3883 (let (parts (start 0))
3884 (while (string-match pattern string start)
3885 (setq parts (cons (substring string start (match-beginning 0)) parts)
3886 start (match-end 0)))
3887 (nreverse (cons (substring string start) parts))))
3888
3889(defun idlwave-replace-string (string replace_string replace_with)
3890 (let* ((start 0)
3891 (last (length string))
3892 (ret_string "")
3893 end)
3894 (while (setq end (string-match replace_string string start))
3895 (setq ret_string
3896 (concat ret_string (substring string start end) replace_with))
3897 (setq start (match-end 0)))
3898 (setq ret_string (concat ret_string (substring string start last)))))
3899
3900(defun idlwave-get-buffer-visiting (file)
3901 ;; Return the buffer currently visiting FILE
3902 (cond
3903 ((boundp 'find-file-compare-truenames) ; XEmacs
3904 (let ((find-file-compare-truenames t))
3905 (get-file-buffer file)))
3906 ((fboundp 'find-buffer-visiting) ; Emacs
3907 (find-buffer-visiting file))
3908 (t (error "This should not happen (idlwave-get-buffer-visiting)"))))
3909
15e42531
CD
3910(defvar idlwave-outlawed-buffers nil
3911 "List of buffer pulled up by idlwave for special reasons.
3912Buffers in this list may be killed by `idlwave-kill-autoloaded-buffers'.")
3913
3914(defun idlwave-find-file-noselect (file &optional why)
f32b3b91
CD
3915 ;; Return a buffer visiting file.
3916 (or (idlwave-get-buffer-visiting file)
15e42531
CD
3917 (let ((buf (find-file-noselect file)))
3918 (if why (add-to-list 'idlwave-outlawed-buffers (cons buf why)))
3919 buf)))
3920
3921(defun idlwave-kill-autoloaded-buffers ()
52a244eb 3922 "Kill buffers created automatically by IDLWAVE.
15e42531
CD
3923Function prompts for a letter to identify the buffers to kill.
3924Possible letters are:
3925
3926f Buffers created by the command \\[idlwave-find-module] or mouse
3927 clicks in the routine info window.
3928s Buffers created by the IDLWAVE Shell to display where execution
3929 stopped or an error was found.
3930a Both of the above.
3931
3932Buffer containing unsaved changes require confirmation before they are killed."
3933 (interactive)
3934 (if (null idlwave-outlawed-buffers)
3935 (error "No IDLWAVE-created buffers available")
3936 (princ (format "Kill IDLWAVE-created buffers: [f]ind source(%d), [s]hell display(%d), [a]ll ? "
3937 (idlwave-count-outlawed-buffers 'find)
3938 (idlwave-count-outlawed-buffers 'shell)))
3939 (let ((c (read-char)))
3940 (cond
3941 ((member c '(?f ?\C-f))
3942 (idlwave-do-kill-autoloaded-buffers 'find))
3943 ((member c '(?s ?\C-s))
3944 (idlwave-do-kill-autoloaded-buffers 'shell))
3945 ((member c '(?a ?\C-a))
3946 (idlwave-do-kill-autoloaded-buffers t))
3947 (t (error "Abort"))))))
3948
3949(defun idlwave-count-outlawed-buffers (tag)
3950 "How many outlawed buffers have tag TAG?"
3951 (length (delq nil
4b1aaa8b
PE
3952 (mapcar
3953 (lambda (x) (eq (cdr x) tag))
15e42531
CD
3954 idlwave-outlawed-buffers))))
3955
3956(defun idlwave-do-kill-autoloaded-buffers (&rest reasons)
3957 "Kill all buffers pulled up by IDLWAVE matching REASONS."
3958 (let* ((list (copy-sequence idlwave-outlawed-buffers))
3959 (cnt 0)
3960 entry)
3961 (while (setq entry (pop list))
3962 (if (buffer-live-p (car entry))
3963 (and (or (memq t reasons)
3964 (memq (cdr entry) reasons))
3965 (kill-buffer (car entry))
3966 (incf cnt)
4b1aaa8b 3967 (setq idlwave-outlawed-buffers
15e42531 3968 (delq entry idlwave-outlawed-buffers)))
4b1aaa8b 3969 (setq idlwave-outlawed-buffers
15e42531
CD
3970 (delq entry idlwave-outlawed-buffers))))
3971 (message "%d buffer%s killed" cnt (if (= cnt 1) "" "s"))))
3972
3973(defun idlwave-revoke-license-to-kill ()
3974 "Remove BUFFER from the buffers which may be killed.
3975Killing would be done by `idlwave-do-kill-autoloaded-buffers'.
3976Intended for `after-save-hook'."
3977 (let* ((buf (current-buffer))
3978 (entry (assq buf idlwave-outlawed-buffers)))
3979 ;; Revoke license
3980 (if entry
4b1aaa8b 3981 (setq idlwave-outlawed-buffers
15e42531
CD
3982 (delq entry idlwave-outlawed-buffers)))
3983 ;; Remove this function from the hook.
3984 (remove-hook 'after-save-hook 'idlwave-revoke-license-to-kill 'local)))
3985
3986(defvar idlwave-path-alist)
3987(defun idlwave-locate-lib-file (file)
f32b3b91 3988 ;; Find FILE on the scanned lib path and return a buffer visiting it
15e42531 3989 (let* ((dirs idlwave-path-alist)
f32b3b91
CD
3990 dir efile)
3991 (catch 'exit
15e42531 3992 (while (setq dir (car (pop dirs)))
f32b3b91
CD
3993 (if (file-regular-p
3994 (setq efile (expand-file-name file dir)))
15e42531 3995 (throw 'exit efile))))))
52a244eb 3996
15e42531
CD
3997(defun idlwave-expand-lib-file-name (file)
3998 ;; Find FILE on the scanned lib path and return a buffer visiting it
52a244eb 3999 ;; This is for, e.g., finding source with no user catalog
4b1aaa8b 4000 (cond
15e42531 4001 ((null file) nil)
15e42531
CD
4002 ((file-name-absolute-p file) file)
4003 (t (idlwave-locate-lib-file file))))
f32b3b91
CD
4004
4005(defun idlwave-make-tags ()
4006 "Creates the IDL tags file IDLTAGS in the current directory from
4007the list of directories specified in the minibuffer. Directories may be
4008for example: . /usr/local/rsi/idl/lib. All the subdirectories of the
4009specified top directories are searched if the directory name is prefixed
4010by @. Specify @ directories with care, it may take a long, long time if
4011you specify /."
4012 (interactive)
4013 (let (directory directories cmd append status numdirs dir getsubdirs
4014 buffer save_buffer files numfiles item errbuf)
4b1aaa8b 4015
f32b3b91
CD
4016 ;;
4017 ;; Read list of directories
4018 (setq directory (read-string "Tag Directories: " "."))
4019 (setq directories (idlwave-split-string directory "[ \t]+"))
4020 ;;
4021 ;; Set etags command, vars
4022 (setq cmd "etags --output=IDLTAGS --language=none --regex='/[
4023\\t]*[pP][Rr][Oo][ \\t]+\\([^ \\t,]+\\)/' --regex='/[
4024\\t]*[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn][ \\t]+\\([^ \\t,]+\\)/' ")
4025 (setq append " ")
4026 (setq status 0)
4027 ;;
4028 ;; For each directory
4029 (setq numdirs 0)
4030 (setq dir (nth numdirs directories))
4031 (while (and dir)
4032 ;;
4033 ;; Find the subdirectories
4034 (if (string-match "^[@]\\(.+\\)$" dir)
4035 (setq getsubdirs t) (setq getsubdirs nil))
4036 (if (and getsubdirs) (setq dir (substring dir 1 (length dir))))
4037 (setq dir (expand-file-name dir))
4038 (if (file-directory-p dir)
4039 (progn
4040 (if (and getsubdirs)
4041 (progn
4042 (setq buffer (get-buffer-create "*idltags*"))
4043 (call-process "sh" nil buffer nil "-c"
4044 (concat "find " dir " -type d -print"))
4045 (setq save_buffer (current-buffer))
4046 (set-buffer buffer)
4047 (setq files (idlwave-split-string
4048 (idlwave-replace-string
4049 (buffer-substring 1 (point-max))
4050 "\n" "/*.pro ")
4051 "[ \t]+"))
4052 (set-buffer save_buffer)
4053 (kill-buffer buffer))
4054 (setq files (list (concat dir "/*.pro"))))
4055 ;;
4056 ;; For each subdirectory
4057 (setq numfiles 0)
4058 (setq item (nth numfiles files))
4059 (while (and item)
4060 ;;
4061 ;; Call etags
4062 (if (not (string-match "^[ \\t]*$" item))
4063 (progn
29a4e67d 4064 (message "%s" (concat "Tagging " item "..."))
f32b3b91 4065 (setq errbuf (get-buffer-create "*idltags-error*"))
52a244eb 4066 (setq status (+ status
4b1aaa8b 4067 (if (eq 0 (call-process
52a244eb
S
4068 "sh" nil errbuf nil "-c"
4069 (concat cmd append item)))
4070 0
4071 1)))
f32b3b91
CD
4072 ;;
4073 ;; Append additional tags
4074 (setq append " --append ")
4075 (setq numfiles (1+ numfiles))
4076 (setq item (nth numfiles files)))
4077 (progn
4078 (setq numfiles (1+ numfiles))
4079 (setq item (nth numfiles files))
4080 )))
4b1aaa8b 4081
f32b3b91
CD
4082 (setq numdirs (1+ numdirs))
4083 (setq dir (nth numdirs directories)))
4084 (progn
4085 (setq numdirs (1+ numdirs))
4086 (setq dir (nth numdirs directories)))))
4b1aaa8b 4087
f32b3b91
CD
4088 (setq errbuf (get-buffer-create "*idltags-error*"))
4089 (if (= status 0)
4090 (kill-buffer errbuf))
4091 (message "")
4092 ))
4093
4094(defun idlwave-toggle-comment-region (beg end &optional n)
4095 "Comment the lines in the region if the first non-blank line is
4096commented, and conversely, uncomment region. If optional prefix arg
4097N is non-nil, then for N positive, add N comment delimiters or for N
4098negative, remove N comment delimiters.
4099Uses `comment-region' which does not place comment delimiters on
4100blank lines."
4101 (interactive "r\nP")
4102 (if n
4103 (comment-region beg end (prefix-numeric-value n))
4104 (save-excursion
4105 (goto-char beg)
4106 (beginning-of-line)
4107 ;; skip blank lines
4108 (skip-chars-forward " \t\n")
4109 (if (looking-at (concat "[ \t]*\\(" comment-start "+\\)"))
52a244eb
S
4110 (if (fboundp 'uncomment-region)
4111 (uncomment-region beg end)
4112 (comment-region beg end
4113 (- (length (buffer-substring
4114 (match-beginning 1)
4115 (match-end 1))))))
4116 (comment-region beg end)))))
f32b3b91
CD
4117
4118
4119;; ----------------------------------------------------------------------------
4120;; ----------------------------------------------------------------------------
4121;; ----------------------------------------------------------------------------
4122;; ----------------------------------------------------------------------------
4123;;
4124;; Completion and Routine Info
4125;;
4126
4127;; String "intern" functions
4128
4129;; For the completion and routine info function, we want to normalize
4130;; the case of procedure names etc. We do this by "interning" these
4131;; string is a hand-crafted way. Hashes are used to map the downcase
52a244eb
S
4132;; version of the strings to the cased versions. Most *-sint-*
4133;; variables consist of *two* hashes, a buffer+shell, followed by a
4134;; system hash. The former is re-scanned, and the latter takes case
4135;; precedence.
4136;;
4137;; Since these cased versions are really lisp objects, we can use `eq'
4138;; to search, which is a large performance boost. All new strings
4139;; need to be "sinterned". We do this as early as possible after
4140;; getting these strings from completion or buffer substrings. So
4141;; most of the code can simply assume to deal with "sinterned"
4142;; strings. The only exception is that the functions which scan whole
4143;; buffers for routine information do not intern the grabbed strings.
4144;; This is only done afterwards. Therefore in these functions it is
4145;; *not* safe to assume the strings can be compared with `eq' and be
4146;; fed into the routine assq functions.
f32b3b91
CD
4147
4148;; Here we define the hashing functions.
4149
4150;; The variables which hold the hashes.
4151(defvar idlwave-sint-routines '(nil))
4152(defvar idlwave-sint-keywords '(nil))
4153(defvar idlwave-sint-methods '(nil))
4154(defvar idlwave-sint-classes '(nil))
52a244eb
S
4155(defvar idlwave-sint-dirs '(nil))
4156(defvar idlwave-sint-libnames '(nil))
f32b3b91
CD
4157
4158(defun idlwave-reset-sintern (&optional what)
4159 "Reset all sintern hashes."
4160 ;; Make sure the hash functions are accessible.
4161 (if (or (not (fboundp 'gethash))
4162 (not (fboundp 'puthash)))
4b1aaa8b 4163 (progn
f32b3b91
CD
4164 (require 'cl)
4165 (or (fboundp 'puthash)
4166 (defalias 'puthash 'cl-puthash))))
4167 (let ((entries '((idlwave-sint-routines 1000 10)
4168 (idlwave-sint-keywords 1000 10)
4169 (idlwave-sint-methods 100 10)
4170 (idlwave-sint-classes 10 10))))
4171
4172 ;; Make sure these are lists
4173 (loop for entry in entries
4174 for var = (car entry)
4175 do (if (not (consp (symbol-value var))) (set var (list nil))))
4176
f66f03de 4177 ;; Reset the system & library hash
f32b3b91
CD
4178 (when (or (eq what t) (eq what 'syslib)
4179 (null (cdr idlwave-sint-routines)))
f32b3b91
CD
4180 (loop for entry in entries
4181 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4182 do (setcdr (symbol-value var)
f32b3b91 4183 (make-hash-table ':size size ':test 'equal)))
52a244eb
S
4184 (setq idlwave-sint-dirs nil
4185 idlwave-sint-libnames nil))
f32b3b91 4186
f66f03de 4187 ;; Reset the buffer & shell hash
f32b3b91
CD
4188 (when (or (eq what t) (eq what 'bufsh)
4189 (null (car idlwave-sint-routines)))
f32b3b91
CD
4190 (loop for entry in entries
4191 for var = (car entry) for size = (nth 1 entry)
4b1aaa8b 4192 do (setcar (symbol-value var)
f32b3b91
CD
4193 (make-hash-table ':size size ':test 'equal))))))
4194
4195(defun idlwave-sintern-routine-or-method (name &optional class set)
4196 (if class
4197 (idlwave-sintern-method name set)
4198 (idlwave-sintern-routine name set)))
4199
4200(defun idlwave-sintern (stype &rest args)
4201 (apply (intern (concat "idlwave-sintern-" (symbol-name stype))) args))
4202
4203;;(defmacro idlwave-sintern (type var)
4204;; `(cond ((not (stringp name)) name)
4205;; ((gethash (downcase name) (cdr ,var)))
4206;; ((gethash (downcase name) (car ,var)))
4207;; (set (idlwave-sintern-set name ,type ,var set))
4208;; (name)))
4209
4210(defun idlwave-sintern-routine (name &optional set)
4211 (cond ((not (stringp name)) name)
4212 ((gethash (downcase name) (cdr idlwave-sint-routines)))
4213 ((gethash (downcase name) (car idlwave-sint-routines)))
4214 (set (idlwave-sintern-set name 'routine idlwave-sint-routines set))
4215 (name)))
4216(defun idlwave-sintern-keyword (name &optional set)
4217 (cond ((not (stringp name)) name)
4218 ((gethash (downcase name) (cdr idlwave-sint-keywords)))
4219 ((gethash (downcase name) (car idlwave-sint-keywords)))
4220 (set (idlwave-sintern-set name 'keyword idlwave-sint-keywords set))
4221 (name)))
4222(defun idlwave-sintern-method (name &optional set)
4223 (cond ((not (stringp name)) name)
4224 ((gethash (downcase name) (cdr idlwave-sint-methods)))
4225 ((gethash (downcase name) (car idlwave-sint-methods)))
4226 (set (idlwave-sintern-set name 'method idlwave-sint-methods set))
4227 (name)))
4228(defun idlwave-sintern-class (name &optional set)
4229 (cond ((not (stringp name)) name)
4230 ((gethash (downcase name) (cdr idlwave-sint-classes)))
4231 ((gethash (downcase name) (car idlwave-sint-classes)))
4232 (set (idlwave-sintern-set name 'class idlwave-sint-classes set))
4233 (name)))
4234
52a244eb
S
4235(defun idlwave-sintern-dir (dir &optional set)
4236 (car (or (member dir idlwave-sint-dirs)
4237 (setq idlwave-sint-dirs (cons dir idlwave-sint-dirs)))))
4238(defun idlwave-sintern-libname (name &optional set)
4239 (car (or (member name idlwave-sint-libnames)
4240 (setq idlwave-sint-libnames (cons name idlwave-sint-libnames)))))
f32b3b91
CD
4241
4242(defun idlwave-sintern-set (name type tables set)
4243 (let* ((func (or (cdr (assq type idlwave-completion-case))
4244 'identity))
4245 (iname (funcall (if (eq func 'preserve) 'identity func) name))
4246 (table (if (eq set 'sys) (cdr tables) (car tables))))
4247 (puthash (downcase name) iname table)
4248 iname))
4249
52a244eb
S
4250(defun idlwave-sintern-keyword-list (kwd-list &optional set)
4251 "Sintern a set of keywords (file (key . link) (key2 . link2) ...)"
8ffcfb27
GM
4252 (mapc (lambda(x)
4253 (setcar x (idlwave-sintern-keyword (car x) set)))
4254 (cdr kwd-list))
52a244eb
S
4255 kwd-list)
4256
4257(defun idlwave-sintern-rinfo-list (list &optional set default-dir)
4258 "Sintern all strings in the rinfo LIST. With optional parameter
4259SET: also set new patterns. Probably this will always have to be t.
4260If DEFAULT-DIR is passed, it is used as the base of the directory"
4261 (let (entry name type class kwds res source call new)
f32b3b91
CD
4262 (while list
4263 (setq entry (car list)
4264 list (cdr list)
4265 name (car entry)
4266 type (nth 1 entry)
4267 class (nth 2 entry)
4268 source (nth 3 entry)
4269 call (nth 4 entry)
52a244eb
S
4270 kwds (nthcdr 5 entry))
4271
4272 ;; The class and name
f32b3b91
CD
4273 (if class
4274 (progn
4275 (if (symbolp class) (setq class (symbol-name class)))
4276 (setq class (idlwave-sintern-class class set))
4277 (setq name (idlwave-sintern-method name set)))
4278 (setq name (idlwave-sintern-routine name set)))
4b1aaa8b 4279
52a244eb
S
4280 ;; The source
4281 (let ((source-type (car source))
4282 (source-file (nth 1 source))
4b1aaa8b 4283 (source-dir (if default-dir
52a244eb
S
4284 (file-name-as-directory default-dir)
4285 (nth 2 source)))
4286 (source-lib (nth 3 source)))
4287 (if (stringp source-dir)
4288 (setq source-dir (idlwave-sintern-dir source-dir set)))
4289 (if (stringp source-lib)
4290 (setq source-lib (idlwave-sintern-libname source-lib set)))
4291 (setq source (list source-type source-file source-dir source-lib)))
4b1aaa8b 4292
52a244eb
S
4293 ;; The keywords
4294 (setq kwds (mapcar (lambda (x)
4295 (idlwave-sintern-keyword-list x set))
4296 kwds))
4297
4298 ;; Build a canonicalized list
4299 (setq new (nconc (list name type class source call) kwds)
4300 res (cons new res)))
f32b3b91
CD
4301 (nreverse res)))
4302
05a1abfc
CD
4303;; Creating new sintern tables
4304
4305(defun idlwave-new-sintern-type (tag)
4306 "Define a variable and a function to sintern the new type TAG.
4307This defines the function `idlwave-sintern-TAG' and the variable
4308`idlwave-sint-TAGs'."
4309 (let* ((name (symbol-name tag))
4310 (names (concat name "s"))
4311 (var (intern (concat "idlwave-sint-" names)))
4312 (func (intern (concat "idlwave-sintern-" name))))
4313 (set var nil) ; initial value of the association list
4314 (fset func ; set the function
4315 `(lambda (name &optional set)
4316 (cond ((not (stringp name)) name)
4317 ((cdr (assoc (downcase name) ,var)))
4318 (set
4319 (setq ,var (cons (cons (downcase name) name) ,var))
4320 name)
4321 (name))))))
4322
4323(defun idlwave-reset-sintern-type (tag)
4324 "Reset the sintern variable associated with TAG."
4325 (set (intern (concat "idlwave-sint-" (symbol-name tag) "s")) nil))
4326
f32b3b91
CD
4327;;---------------------------------------------------------------------------
4328
4329
4330;; The variables which hold the information
15e42531 4331(defvar idlwave-system-routines nil
f32b3b91
CD
4332 "Holds the routine-info obtained by scanning buffers.")
4333(defvar idlwave-buffer-routines nil
4334 "Holds the routine-info obtained by scanning buffers.")
4335(defvar idlwave-compiled-routines nil
15e42531
CD
4336 "Holds the routine-info obtained by asking the shell.")
4337(defvar idlwave-unresolved-routines nil
4338 "Holds the unresolved routine-info obtained by asking the shell.")
52a244eb
S
4339(defvar idlwave-user-catalog-routines nil
4340 "Holds the procedure routine-info from the user scan.")
4341(defvar idlwave-library-catalog-routines nil
3938cb82
S
4342 "Holds the procedure routine-info from the .idlwave_catalog library files.")
4343(defvar idlwave-library-catalog-libname nil
4344 "Name of library catalog loaded from .idlwave_catalog files.")
15e42531 4345(defvar idlwave-path-alist nil
52a244eb
S
4346 "Alist with !PATH directories and zero or more flags if the dir has
4347been scanned in a user catalog ('user) or discovered in a library
4348catalog \('lib).")
15e42531
CD
4349(defvar idlwave-true-path-alist nil
4350 "Like `idlwave-path-alist', but with true filenames.")
f32b3b91 4351(defvar idlwave-routines nil
b9e4fbd3 4352 "Holds the combined procedure/function/method routine-info.")
f32b3b91
CD
4353(defvar idlwave-class-alist nil
4354 "Holds the class names known to IDLWAVE.")
4355(defvar idlwave-class-history nil
4356 "The history of classes selected with the minibuffer.")
4357(defvar idlwave-force-class-query nil)
4358(defvar idlwave-before-completion-wconf nil
4359 "The window configuration just before the completion buffer was displayed.")
15e42531
CD
4360(defvar idlwave-last-system-routine-info-cons-cell nil
4361 "The last cons cell in the system routine info.")
f32b3b91
CD
4362
4363;;
4364;; The code to get routine info from different sources.
4365
15e42531 4366(defvar idlwave-system-routines)
5e72c6b2
S
4367(defvar idlwave-catalog-process nil
4368 "The background process currently updating the catalog.")
4369
f32b3b91
CD
4370(defun idlwave-routines ()
4371 "Provide a list of IDL routines.
4372This routine loads the builtin routines on the first call. Later it
4373only returns the value of the variable."
5e72c6b2
S
4374 (if (and idlwave-catalog-process
4375 (processp idlwave-catalog-process))
4376 (progn
4377 (cond
4378 ((equal (process-status idlwave-catalog-process) 'exit)
4379 (message "updating........")
4380 (setq idlwave-catalog-process nil)
4381 (idlwave-update-routine-info '(4)))
4382 ((equal (process-status idlwave-catalog-process) 'run)
4383 ;; Keep it running...
4384 )
4385 (t
4386 ;; Something is wrong, get rid of the process
4387 (message "Problem with catalog process") (beep)
4388 (condition-case nil
4389 (kill-process idlwave-catalog-process)
4390 (error nil))
4391 (setq idlwave-catalog-process nil)))))
f32b3b91
CD
4392 (or idlwave-routines
4393 (progn
4394 (idlwave-update-routine-info)
4395 ;; return the current value
4396 idlwave-routines)))
4397
05a1abfc
CD
4398(defvar idlwave-update-rinfo-hook nil
4399 "List of functions which should run after a global rinfo update.
4400Does not run after automatic updates of buffer or the shell.")
4401
5e72c6b2
S
4402(defun idlwave-rescan-catalog-directories ()
4403 "Rescan the previously selected directories. For batch processing."
4404 (idlwave-update-routine-info '(16)))
4405
4406(defun idlwave-rescan-asynchronously ()
8a6a28ac 4407 "Dispatch another Emacs instance to update the idlwave catalog.
5e72c6b2
S
4408After the process finishes normally, the first access to routine info
4409will re-read the catalog."
4410 (interactive)
4411 (if (processp idlwave-catalog-process)
4412 (if (eq (process-status idlwave-catalog-process) 'run)
4413 (if (yes-or-no-p "A catalog-updating process is running. Kill it? ")
4414 (progn
4415 (condition-case nil
4416 (kill-process idlwave-catalog-process)
4417 (error nil))
4418 (error "Process killed, no new process started"))
4419 (error "Quit"))
4420 (condition-case nil
4421 (kill-process idlwave-catalog-process)
4422 (error nil))))
52a244eb
S
4423 (if (or (not idlwave-user-catalog-file)
4424 (not (stringp idlwave-user-catalog-file))
4425 (not (file-regular-p idlwave-user-catalog-file)))
5e72c6b2 4426 (error "No catalog has been produced yet"))
4b1aaa8b 4427 (let* ((emacs (concat invocation-directory invocation-name))
5e72c6b2
S
4428 (args (list "-batch"
4429 "-l" (expand-file-name "~/.emacs")
4430 "-l" "idlwave"
4431 "-f" "idlwave-rescan-catalog-directories"))
4b1aaa8b 4432 (process (apply 'start-process "idlcat"
5e72c6b2
S
4433 nil emacs args)))
4434 (setq idlwave-catalog-process process)
4b1aaa8b 4435 (set-process-sentinel
5e72c6b2
S
4436 process
4437 (lambda (pro why)
4438 (when (string-match "finished" why)
4439 (setq idlwave-routines nil
4440 idlwave-system-routines nil
4441 idlwave-catalog-process nil)
4442 (or (idlwave-start-load-rinfo-timer)
4443 (idlwave-update-routine-info '(4))))))
4444 (message "Background job started to update catalog file")))
4445
4446
52a244eb
S
4447;; Format for all routine info user catalog, library catalogs, etc.:
4448;;
4449;; ("ROUTINE" type class
4450;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
4451;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 4452;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de 4453;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
52a244eb
S
4454;;
4455;; DIR will be supplied dynamically while loading library catalogs,
4456;; and is sinterned to save space, as is LIBNAME. PRO_FILE can be a
4457;; complete filepath, in which case DIR is unnecessary. HELPFILE can
4458;; be nil, as can LINK1, etc., if no HTML help is available.
4459
4460
5e72c6b2 4461(defvar idlwave-load-rinfo-idle-timer)
3938cb82
S
4462(defvar idlwave-shell-path-query)
4463
52a244eb 4464(defun idlwave-update-routine-info (&optional arg no-concatenate)
f32b3b91
CD
4465 "Update the internal routine-info lists.
4466These lists are used by `idlwave-routine-info' (\\[idlwave-routine-info])
4467and by `idlwave-complete' (\\[idlwave-complete]) to provide information
4468about individual routines.
4469
4470The information can come from 4 sources:
44711. IDL programs in the current editing session
44722. Compiled modules in an IDL shell running as Emacs subprocess
44733. A list which covers the IDL system routines.
44744. A list which covers the prescanned library files.
4475
4476Scans all IDLWAVE-mode buffers of the current editing session (see
4477`idlwave-scan-all-buffers-for-routine-info').
4478When an IDL shell is running, this command also queries the IDL program
4479for currently compiled routines.
4480
4481With prefix ARG, also reload the system and library lists.
52a244eb
S
4482With two prefix ARG's, also rescans the chosen user catalog tree.
4483With three prefix args, dispatch asynchronous process to do the update.
4484
4485If NO-CONCATENATE is non-nil, don't pre-concatenate the routine info
4486lists, but instead wait for the shell query to complete and
4487asynchronously finish updating routine info. This is set
4488automatically when called interactively. When you need routine
4489information updated immediately, leave NO-CONCATENATE nil."
751adbde 4490 (interactive "P\np")
5e72c6b2
S
4491 ;; Stop any idle processing
4492 (if (or (and (fboundp 'itimerp)
4493 (itimerp idlwave-load-rinfo-idle-timer))
4494 (and (fboundp 'timerp)
4495 (timerp idlwave-load-rinfo-idle-timer)))
4496 (cancel-timer idlwave-load-rinfo-idle-timer))
4497 (cond
4498 ((equal arg '(64))
4499 ;; Start a background process which updates the catalog.
4500 (idlwave-rescan-asynchronously))
4501 ((equal arg '(16))
52a244eb
S
4502 ;; Update the user catalog now, and wait for them.
4503 (idlwave-create-user-catalog-file t))
5e72c6b2
S
4504 (t
4505 (let* ((load (or arg
4506 idlwave-buffer-case-takes-precedence
4507 (null idlwave-routines)))
4508 ;; The override-idle means, even if the idle timer has done some
4509 ;; preparing work, load and renormalize everything anyway.
4510 (override-idle (or arg idlwave-buffer-case-takes-precedence)))
4b1aaa8b 4511
f32b3b91 4512 (setq idlwave-buffer-routines nil
15e42531
CD
4513 idlwave-compiled-routines nil
4514 idlwave-unresolved-routines nil)
f32b3b91 4515 ;; Reset the appropriate hashes
5e72c6b2
S
4516 (if (get 'idlwave-reset-sintern 'done-by-idle)
4517 ;; reset was already done in idle time, so skip this step now once
4518 (put 'idlwave-reset-sintern 'done-by-idle nil)
4519 (idlwave-reset-sintern (cond (load t)
4520 ((null idlwave-system-routines) t)
4521 (t 'bufsh))))
4b1aaa8b 4522
f32b3b91
CD
4523 (if idlwave-buffer-case-takes-precedence
4524 ;; We can safely scan the buffer stuff first
4525 (progn
4526 (idlwave-update-buffer-routine-info)
f66f03de 4527 (and load (idlwave-load-all-rinfo override-idle)))
f32b3b91 4528 ;; We first do the system info, and then the buffers
f66f03de 4529 (and load (idlwave-load-all-rinfo override-idle))
f32b3b91
CD
4530 (idlwave-update-buffer-routine-info))
4531
4532 ;; Let's see if there is a shell
4533 (let* ((shell-is-running (and (fboundp 'idlwave-shell-is-running)
4534 (idlwave-shell-is-running)))
4535 (ask-shell (and shell-is-running
4536 idlwave-query-shell-for-routine-info)))
4b1aaa8b 4537
52a244eb 4538 ;; Load the library catalogs again, first re-scanning the path
4b1aaa8b 4539 (when arg
52a244eb
S
4540 (if shell-is-running
4541 (idlwave-shell-send-command idlwave-shell-path-query
4542 '(progn
4543 (idlwave-shell-get-path-info)
4544 (idlwave-scan-library-catalogs))
4545 'hide)
4546 (idlwave-scan-library-catalogs)))
775591f7 4547
f32b3b91 4548 (if (or (not ask-shell)
52a244eb 4549 (not no-concatenate))
f32b3b91
CD
4550 ;; 1. If we are not going to ask the shell, we need to do the
4551 ;; concatenation now.
52a244eb
S
4552 ;; 2. When this function is called non-interactively, it
4553 ;; means that someone needs routine info *now*. The
4554 ;; shell update causes the concatenation to be
4555 ;; *delayed*, so not in time for the current command.
4556 ;; Therefore, we do a concatenation now, even though
4557 ;; the shell might do it again.
4558 (idlwave-concatenate-rinfo-lists nil 'run-hooks))
4b1aaa8b 4559
f32b3b91 4560 (when ask-shell
52a244eb 4561 ;; Ask the shell about the routines it knows of.
f32b3b91 4562 (message "Querying the shell")
5e72c6b2
S
4563 (idlwave-shell-update-routine-info nil t)))))))
4564
52a244eb
S
4565
4566(defvar idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4567(defvar idlwave-load-rinfo-idle-timer nil)
4568(defun idlwave-start-load-rinfo-timer ()
4569 (if (or (and (fboundp 'itimerp)
4570 (itimerp idlwave-load-rinfo-idle-timer))
4571 (and (fboundp 'timerp)
4572 (timerp idlwave-load-rinfo-idle-timer)))
4573 (cancel-timer idlwave-load-rinfo-idle-timer))
52a244eb 4574 (setq idlwave-load-rinfo-steps-done (make-vector 6 nil))
5e72c6b2
S
4575 (setq idlwave-load-rinfo-idle-timer nil)
4576 (if (and idlwave-init-rinfo-when-idle-after
4577 (numberp idlwave-init-rinfo-when-idle-after)
4578 (not (equal 0 idlwave-init-rinfo-when-idle-after))
4579 (not idlwave-routines))
4580 (condition-case nil
4581 (progn
4582 (setq idlwave-load-rinfo-idle-timer
4583 (run-with-idle-timer
4584 idlwave-init-rinfo-when-idle-after
4585 nil 'idlwave-load-rinfo-next-step)))
4586 (error nil))))
4587
3938cb82
S
4588(defvar idlwave-library-routines nil "Obsolete variable.")
4589
f66f03de
S
4590;;------ XML Help routine info system
4591(defun idlwave-load-system-routine-info ()
4592 ;; Load the system routine info from the cached routine info file,
4593 ;; which, if necessary, will be re-created from the XML file on
4594 ;; disk. As a last fallback, load the (likely outdated) idlw-rinfo
4595 ;; file distributed with older IDLWAVE versions (<6.0)
4b1aaa8b 4596 (unless (and (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4597 'noerror 'nomessage)
4598 (idlwave-xml-system-routine-info-up-to-date))
4599 ;; See if we can create it from XML source
4600 (condition-case nil
4601 (idlwave-convert-xml-system-routine-info)
4b1aaa8b
PE
4602 (error
4603 (unless (load idlwave-xml-system-rinfo-converted-file
f66f03de
S
4604 'noerror 'nomessage)
4605 (if idlwave-system-routines
4b1aaa8b 4606 (message
f66f03de 4607 "Failed to load converted routine info, using old conversion.")
4b1aaa8b 4608 (message
f66f03de
S
4609 "Failed to convert XML routine info, falling back on idlw-rinfo.")
4610 (if (not (load "idlw-rinfo" 'noerror 'nomessage))
4b1aaa8b 4611 (message
f66f03de
S
4612 "Could not locate any system routine information."))))))))
4613
4614(defun idlwave-xml-system-routine-info-up-to-date()
4b1aaa8b 4615 (let* ((dir (file-name-as-directory
f66f03de
S
4616 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4617 (catalog-file (expand-file-name "idl_catalog.xml" dir)))
4618 (file-newer-than-file-p ;converted file is newer than catalog
4619 idlwave-xml-system-rinfo-converted-file
4620 catalog-file)))
4621
4622(defvar idlwave-system-class-info nil) ; Gathered from idlw-rinfo
4623(defvar idlwave-system-variables-alist nil
4624 "Alist of system variables and the associated structure tags.
4625Gets set in cached XML rinfo, or `idlw-rinfo.el'.")
4626(defvar idlwave-executive-commands-alist nil
4627 "Alist of system variables and their help files.")
4628(defvar idlwave-help-special-topic-words nil)
4629
4b1aaa8b 4630
f66f03de
S
4631(defun idlwave-shorten-syntax (syntax name &optional class)
4632 ;; From a list of syntax statments, shorten with %s and group with "or"
4633 (let ((case-fold-search t))
4b1aaa8b 4634 (mapconcat
f66f03de
S
4635 (lambda (x)
4636 (while (string-match name x)
4637 (setq x (replace-match "%s" t t x)))
4b1aaa8b 4638 (if class
f66f03de
S
4639 (while (string-match class x)
4640 (setq x (replace-match "%s" t t x))))
4641 x)
4642 (nreverse syntax)
4643 " or ")))
4644
4645(defun idlwave-xml-create-class-method-lists (xml-entry)
4646 ;; Create a class list entry from the xml parsed list., returning a
4647 ;; cons of form (class-entry method-entries).
4648 (let* ((nameblock (nth 1 xml-entry))
4649 (class (cdr (assq 'name nameblock)))
4650 (link (cdr (assq 'link nameblock)))
4651 (params (cddr xml-entry))
4652 (case-fold-search t)
4653 class-entry
4654 method methods-entry extra-kwds
4655 props get-props set-props init-props inherits
4656 pelem ptype)
4657 (while params
4658 (setq pelem (car params))
4659 (when (listp pelem)
4660 (setq ptype (car pelem)
4661 props (car (cdr pelem)))
4662 (cond
4663 ((eq ptype 'SUPERCLASS)
58c8f915
S
4664 (let ((pname (cdr (assq 'name props)))
4665 (plink (cdr (assq 'link props))))
4666 (unless (and (string= pname "None")
4667 (string= plink "None"))
4668 (push pname inherits))))
f66f03de
S
4669
4670 ((eq ptype 'PROPERTY)
4671 (let ((pname (cdr (assq 'name props)))
4672 (plink (cdr (assq 'link props)))
4673 (get (string= (cdr (assq 'get props)) "Yes"))
4674 (set (string= (cdr (assq 'set props)) "Yes"))
4675 (init (string= (cdr (assq 'init props)) "Yes")))
4676 (if get (push (list pname plink) get-props))
4677 (if set (push (list pname plink) set-props))
4678 (if init (push (list pname plink) init-props))))
4679
4680 ((eq ptype 'METHOD)
4681 (setq method (cdr (assq 'name props)))
4682 (setq extra-kwds ;;Assume all property keywords are gathered already
4683 (cond
4684 ((string-match (concat class "::Init") method)
4685 (put 'init-props 'matched t)
4686 init-props)
4687 ((string-match (concat class "::GetProperty") method)
4688 (put 'get-props 'matched t)
4689 get-props)
4690 ((string-match (concat class "::SetProperty") method)
4691 (put 'set-props 'matched t)
4692 set-props)
4693 (t nil)))
4b1aaa8b
PE
4694 (setq methods-entry
4695 (nconc (idlwave-xml-create-rinfo-list pelem class extra-kwds)
f66f03de
S
4696 methods-entry)))
4697 (t)))
4698 (setq params (cdr params)))
4699 ;(unless (get 'init-props 'matched)
4700 ; (message "Failed to match Init in class %s" class))
4701 ;(unless (get 'get-props 'matched)
4702 ; (message "Failed to match GetProperty in class %s" class))
4703 ;(unless (get 'set-props 'matched)
4704 ; (message "Failed to match SetProperty in class %s" class))
4b1aaa8b
PE
4705 (setq class-entry
4706 (if inherits
f66f03de
S
4707 (list class (append '(inherits) inherits) (list 'link link))
4708 (list class (list 'link link))))
4709 (cons class-entry methods-entry)))
4b1aaa8b 4710
f66f03de
S
4711(defun idlwave-xml-create-rinfo-list (xml-entry &optional class extra-kws)
4712 ;; Create correctly structured list elements from ROUTINE or METHOD
4713 ;; XML list structures. Return a list of list elements, with more
4714 ;; than one sub-list possible if a routine can serve as both
4715 ;; procedure and function (e.g. call_method).
4716 (let* ((nameblock (nth 1 xml-entry))
4717 (name (cdr (assq 'name nameblock)))
4718 (link (cdr (assq 'link nameblock)))
4719 (params (cddr xml-entry))
4720 (syntax-vec (make-vector 3 nil)) ; procedure, function, exec command
4721 (case-fold-search t)
4722 syntax kwd klink pref-list kwds pelem ptype entry props result type)
4723 (if class ;; strip out class name from class method name string
4724 (if (string-match (concat class "::") name)
4725 (setq name (substring name (match-end 0)))))
4726 (while params
4727 (setq pelem (car params))
4728 (when (listp pelem)
4729 (setq ptype (car pelem)
4730 props (car (cdr pelem)))
4731 (cond
4732 ((eq ptype 'SYNTAX)
4733 (setq syntax (cdr (assq 'name props)))
4734 (if (string-match "-&gt;" syntax)
4735 (setq syntax (replace-match "->" t nil syntax)))
4736 (setq type (cdr (assq 'type props)))
4737 (push syntax
4738 (aref syntax-vec (cond
4739 ((string-match "^pro" type) 0)
4740 ((string-match "^fun" type) 1)
4741 ((string-match "^exec" type) 2)))))
4742 ((eq ptype 'KEYWORD)
4743 (setq kwd (cdr (assq 'name props))
4744 klink (cdr (assq 'link props)))
4745 (if (string-match "^\\[XY\\(Z?\\)\\]" kwd)
4b1aaa8b
PE
4746 (progn
4747 (setq pref-list
f66f03de
S
4748 (if (match-string 1 kwd) '("X" "Y" "Z") '("X" "Y"))
4749 kwd (substring kwd (match-end 0)))
4750 (loop for x in pref-list do
4751 (push (list (concat x kwd) klink) kwds)))
4752 (push (list kwd klink) kwds)))
4753
4754 (t))); Do nothing for the others
4755 (setq params (cdr params)))
4b1aaa8b 4756
f66f03de
S
4757 ;; Debug
4758; (if (and (null (aref syntax-vec 0))
4759; (null (aref syntax-vec 1))
4760; (null (aref syntax-vec 2)))
4761; (with-current-buffer (get-buffer-create "IDL_XML_catalog_complaints")
4762; (if class
4763; (insert (format "Missing SYNTAX entry for %s::%s\n" class name))
4764; (insert (message "Missing SYNTAX entry for %s\n" name)))))
4765
4766 ;; Executive commands are treated specially
4767 (if (aref syntax-vec 2)
4768 (cons (substring name 1) link)
4769 (if extra-kws (setq kwds (nconc kwds extra-kws)))
4770 (setq kwds (idlwave-rinfo-group-keywords kwds link))
4771 (loop for idx from 0 to 1 do
4772 (if (aref syntax-vec idx)
4b1aaa8b 4773 (push (append (list name (if (eq idx 0) 'pro 'fun)
f66f03de 4774 class '(system)
4b1aaa8b 4775 (idlwave-shorten-syntax
f66f03de
S
4776 (aref syntax-vec idx) name class))
4777 kwds) result)))
4778 result)))
4779
4780
4781(defun idlwave-rinfo-group-keywords (kwds master-link)
4b1aaa8b 4782 ;; Group keywords by link file, as a list with elements
f66f03de
S
4783 ;; (linkfile ( ("KWD1" . link1) ("KWD2" . link2))
4784 (let (kwd link anchor linkfiles block master-elt)
4785 (while kwds
4786 (setq kwd (car kwds)
4787 link (idlwave-split-link-target (nth 1 kwd))
4788 anchor (cdr link)
4789 link (car link)
4790 kwd (car kwd))
4791 (if (setq block (assoc link linkfiles))
4792 (push (cons kwd anchor) (cdr block))
4793 (push (list link (cons kwd anchor)) linkfiles))
4794 (setq kwds (cdr kwds)))
4795 ;; Ensure the master link is there
4796 (if (setq master-elt (assoc master-link linkfiles))
4797 (if (eq (car linkfiles) master-elt)
4798 linkfiles
4799 (cons master-elt (delq master-elt linkfiles)))
4800 (push (list master-link) linkfiles))))
4b1aaa8b 4801
f66f03de
S
4802(defun idlwave-convert-xml-clean-statement-aliases (aliases)
4803 ;; Clean up the syntax of routines which are actually aliases by
4804 ;; removing the "OR" from the statements
4805 (let (syntax entry)
4806 (loop for x in aliases do
4807 (setq entry (assoc x idlwave-system-routines))
4808 (when entry
4809 (while (string-match " +or +" (setq syntax (nth 4 entry)))
4810 (setf (nth 4 entry) (replace-match ", " t t syntax)))))))
4811
4812(defun idlwave-convert-xml-clean-routine-aliases (aliases)
4813 ;; Duplicate and trim original routine aliases from rinfo list
4b1aaa8b 4814 ;; This if for, e.g. OPENR/OPENW/OPENU
f66f03de
S
4815 (let (alias remove-list new parts all-parts)
4816 (loop for x in aliases do
4817 (when (setq parts (split-string (cdr x) "/"))
4818 (setq new (assoc (cdr x) all-parts))
4819 (unless new
4820 (setq new (cons (cdr x) parts))
4821 (push new all-parts))
4822 (setcdr new (delete (car x) (cdr new)))))
4b1aaa8b 4823
f66f03de
S
4824 ;; Add any missing aliases (separate by slashes)
4825 (loop for x in all-parts do
4826 (if (cdr x)
4827 (push (cons (nth 1 x) (car x)) aliases)))
4828
4829 (loop for x in aliases do
4830 (when (setq alias (assoc (cdr x) idlwave-system-routines))
4831 (unless (memq alias remove-list) (push alias remove-list))
4832 (setq alias (copy-sequence alias))
4833 (setcar alias (car x))
4834 (push alias idlwave-system-routines)))
4835 (loop for x in remove-list do
4836 (delq x idlwave-system-routines))))
4837
4838(defun idlwave-convert-xml-clean-sysvar-aliases (aliases)
4839 ;; Duplicate and trim original routine aliases from rinfo list
4840 ;; This if for, e.g. !X, !Y, !Z.
4841 (let (alias remove-list new parts all-parts)
4842 (loop for x in aliases do
4843 (when (setq alias (assoc (cdr x) idlwave-system-variables-alist))
4844 (unless (memq alias remove-list) (push alias remove-list))
4845 (setq alias (copy-sequence alias))
4846 (setcar alias (car x))
4847 (push alias idlwave-system-variables-alist)))
4848 (loop for x in remove-list do
4849 (delq x idlwave-system-variables-alist))))
4850
4851
4852(defun idlwave-xml-create-sysvar-alist (xml-entry)
4853 ;; Create a sysvar list entry from the xml parsed list.
4854 (let* ((nameblock (nth 1 xml-entry))
a86bd650 4855 (name (cdr (assq 'name nameblock)))
b9e4fbd3 4856 (sysvar (substring name (progn (string-match "^ *!" name)
a86bd650 4857 (match-end 0))))
f66f03de
S
4858 (link (cdr (assq 'link nameblock)))
4859 (params (cddr xml-entry))
4860 (case-fold-search t)
4861 pelem ptype props fields tags)
4862 (while params
4863 (setq pelem (car params))
4864 (when (listp pelem)
4865 (setq ptype (car pelem)
4866 props (car (cdr pelem)))
4867 (cond
4868 ((eq ptype 'FIELD)
4b1aaa8b 4869 (push (cons (cdr (assq 'name props))
f66f03de
S
4870 (cdr
4871 (idlwave-split-link-target (cdr (assq 'link props)))))
4872 tags))))
4873 (setq params (cdr params)))
4874 (delq nil
4875 (list sysvar (if tags (cons 'tags tags)) (list 'link link)))))
4876
4877
4878(defvar idlwave-xml-routine-info-file nil)
4879
4880(defun idlwave-save-routine-info ()
4881 (if idlwave-xml-routine-info-file
4882 (with-temp-file idlwave-xml-system-rinfo-converted-file
4b1aaa8b 4883 (insert
f66f03de 4884 (concat ";; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
4b1aaa8b
PE
4885;; IDLWAVE Routine Information File (IDLWAVE version " idlwave-mode-version ")
4886;; Automatically generated from source file:
f66f03de
S
4887;; " idlwave-xml-routine-info-file "
4888;; on " (current-time-string) "
4889;; Do not edit."))
4890 (insert (format "\n(setq idlwave-xml-routine-info-file \n \"%s\")"
4891 idlwave-xml-routine-info-file))
4892 (insert "\n(setq idlwave-system-routines\n '")
4893 (prin1 idlwave-system-routines (current-buffer))
4894 (insert ")")
4895 (insert "\n(setq idlwave-system-variables-alist\n '")
4896 (prin1 idlwave-system-variables-alist (current-buffer))
4897 (insert ")")
4898 (insert "\n(setq idlwave-system-class-info\n '")
4899 (prin1 idlwave-system-class-info (current-buffer))
4900 (insert ")")
4901 (insert "\n(setq idlwave-executive-commands-alist\n '")
4902 (prin1 idlwave-executive-commands-alist (current-buffer))
4903 (insert ")")
4904 (insert "\n(setq idlwave-help-special-topic-words\n '")
4905 (prin1 idlwave-help-special-topic-words (current-buffer))
4906 (insert ")"))))
4907
4908(defun idlwave-convert-xml-system-routine-info ()
4909 "Convert XML supplied IDL routine info into internal form.
4910Cache to disk for quick recovery."
4911 (interactive)
4b1aaa8b 4912 (let* ((dir (file-name-as-directory
f66f03de
S
4913 (expand-file-name "help/online_help" (idlwave-sys-dir))))
4914 (catalog-file (expand-file-name "idl_catalog.xml" dir))
4915 (elem-cnt 0)
4b1aaa8b 4916 props rinfo msg-cnt elem type nelem class-result alias
f66f03de 4917 routines routine-aliases statement-aliases sysvar-aliases
e08734e2 4918 version-string)
f66f03de
S
4919 (if (not (file-exists-p catalog-file))
4920 (error "No such XML routine info file: %s" catalog-file)
4921 (if (not (file-readable-p catalog-file))
4922 (error "Cannot read XML routine info file: %s" catalog-file)))
4923 (require 'xml)
4b1aaa8b 4924 (message "Reading XML routine info...")
e08734e2 4925 (setq rinfo (xml-parse-file catalog-file))
f66f03de
S
4926 (message "Reading XML routine info...done")
4927 (setq rinfo (assq 'CATALOG rinfo))
4928 (unless rinfo (error "Failed to parse XML routine info"))
4929 ;;(setq rinfo (car rinfo)) ; Skip the catalog stuff.
4b1aaa8b 4930
f66f03de
S
4931 (setq version-string (cdr (assq 'version (nth 1 rinfo)))
4932 rinfo (cddr rinfo))
4933
4934 (setq nelem (length rinfo)
4935 msg-cnt (/ nelem 20))
4b1aaa8b 4936
f66f03de
S
4937 (setq idlwave-xml-routine-info-file nil)
4938 (message "Converting XML routine info...")
4939 (setq idlwave-system-routines nil
4940 idlwave-system-variables-alist nil
4941 idlwave-system-class-info nil
4942 idlwave-executive-commands-alist nil
4943 idlwave-help-special-topic-words nil)
4944
4945 (while rinfo
4946 (setq elem (car rinfo)
4947 rinfo (cdr rinfo))
4948 (incf elem-cnt)
4949 (when (listp elem)
4950 (setq type (car elem)
4951 props (car (cdr elem)))
4952 (if (= (mod elem-cnt msg-cnt) 0)
4b1aaa8b 4953 (message "Converting XML routine info...%2d%%"
f66f03de 4954 (/ (* elem-cnt 100) nelem)))
4b1aaa8b 4955 (cond
f66f03de
S
4956 ((eq type 'ROUTINE)
4957 (if (setq alias (assq 'alias_to props))
4b1aaa8b 4958 (push (cons (cdr (assq 'name props)) (cdr alias))
f66f03de
S
4959 routine-aliases)
4960 (setq routines (idlwave-xml-create-rinfo-list elem))
4961 (if (listp (cdr routines))
4962 (setq idlwave-system-routines
4963 (nconc idlwave-system-routines routines))
4964 ;; a cons cell is an executive commands
4965 (push routines idlwave-executive-commands-alist))))
4b1aaa8b 4966
f66f03de
S
4967 ((eq type 'CLASS)
4968 (setq class-result (idlwave-xml-create-class-method-lists elem))
4969 (push (car class-result) idlwave-system-class-info)
4970 (setq idlwave-system-routines
4971 (nconc idlwave-system-routines (cdr class-result))))
4972
4973 ((eq type 'STATEMENT)
4974 (push (cons (cdr (assq 'name props))
4975 (cdr (assq 'link props)))
4976 idlwave-help-special-topic-words)
4977 ;; Save the links to those which are statement aliases (not routines)
4978 (if (setq alias (assq 'alias_to props))
4979 (unless (member (cdr alias) statement-aliases)
4980 (push (cdr alias) statement-aliases))))
4981
4982 ((eq type 'SYSVAR)
4983 (if (setq alias (cdr (assq 'alias_to props)))
4b1aaa8b 4984 (push (cons (substring (cdr (assq 'name props)) 1)
f66f03de
S
4985 (substring alias 1))
4986 sysvar-aliases)
4b1aaa8b 4987 (push (idlwave-xml-create-sysvar-alist elem)
f66f03de
S
4988 idlwave-system-variables-alist)))
4989 (t))))
4990 (idlwave-convert-xml-clean-routine-aliases routine-aliases)
4991 (idlwave-convert-xml-clean-statement-aliases statement-aliases)
4992 (idlwave-convert-xml-clean-sysvar-aliases sysvar-aliases)
4993
4994 (setq idlwave-xml-routine-info-file catalog-file)
4995 (idlwave-save-routine-info)
4996 (message "Converting XML routine info...done")))
4b1aaa8b
PE
4997
4998
f66f03de
S
4999;; ("ROUTINE" type class
5000;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") |
5001;; (buffer pro_file dir) | (compiled pro_file dir)
4b1aaa8b 5002;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))
f66f03de
S
5003;; ("HELPFILE2" (("KWD2" . link) ...)) ...)
5004
5005
5e72c6b2
S
5006(defun idlwave-load-rinfo-next-step ()
5007 (let ((inhibit-quit t)
5008 (arr idlwave-load-rinfo-steps-done))
f66f03de 5009 (if (catch 'exit
5e72c6b2 5010 (when (not (aref arr 0))
f66f03de
S
5011 (message "Loading system routine info in idle time...")
5012 (idlwave-load-system-routine-info)
5013 ;;(load "idlw-rinfo" 'noerror 'nomessage)
5014 (message "Loading system routine info in idle time...done")
5e72c6b2
S
5015 (aset arr 0 t)
5016 (throw 'exit t))
4b1aaa8b 5017
5e72c6b2
S
5018 (when (not (aref arr 1))
5019 (message "Normalizing idlwave-system-routines in idle time...")
5020 (idlwave-reset-sintern t)
5021 (put 'idlwave-reset-sintern 'done-by-idle t)
5022 (setq idlwave-system-routines
5023 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
5024 (message "Normalizing idlwave-system-routines in idle time...done")
5025 (aset arr 1 t)
5026 (throw 'exit t))
f66f03de 5027
5e72c6b2 5028 (when (not (aref arr 2))
52a244eb
S
5029 (when (and (stringp idlwave-user-catalog-file)
5030 (file-regular-p idlwave-user-catalog-file))
5031 (message "Loading user catalog in idle time...")
5e72c6b2 5032 (condition-case nil
52a244eb
S
5033 (load-file idlwave-user-catalog-file)
5034 (error (throw 'exit nil)))
5035 ;; Check for the old style catalog and warn
5036 (if (and
5037 (boundp 'idlwave-library-routines)
5038 idlwave-library-routines)
775591f7 5039 (progn
52a244eb
S
5040 (setq idlwave-library-routines nil)
5041 (ding)
4b1aaa8b 5042 (message "Outdated user catalog: %s... recreate"
52a244eb 5043 idlwave-user-catalog-file))
f66f03de
S
5044 (message "Loading user catalog in idle time...done")))
5045 (aset arr 2 t)
5046 (throw 'exit t))
5047
5e72c6b2 5048 (when (not (aref arr 3))
52a244eb
S
5049 (when idlwave-user-catalog-routines
5050 (message "Normalizing user catalog routines in idle time...")
4b1aaa8b 5051 (setq idlwave-user-catalog-routines
52a244eb
S
5052 (idlwave-sintern-rinfo-list
5053 idlwave-user-catalog-routines 'sys))
4b1aaa8b 5054 (message
52a244eb 5055 "Normalizing user catalog routines in idle time...done"))
5e72c6b2
S
5056 (aset arr 3 t)
5057 (throw 'exit t))
f66f03de 5058
5e72c6b2 5059 (when (not (aref arr 4))
4b1aaa8b 5060 (idlwave-scan-library-catalogs
52a244eb
S
5061 "Loading and normalizing library catalogs in idle time...")
5062 (aset arr 4 t)
5063 (throw 'exit t))
5064 (when (not (aref arr 5))
5e72c6b2
S
5065 (message "Finishing initialization in idle time...")
5066 (idlwave-routines)
5067 (message "Finishing initialization in idle time...done")
4b1aaa8b 5068 (aset arr 5 t)
5e72c6b2 5069 (throw 'exit nil)))
52a244eb
S
5070 ;; restart the timer
5071 (if (sit-for 1)
5072 (idlwave-load-rinfo-next-step)
5073 (setq idlwave-load-rinfo-idle-timer
5074 (run-with-idle-timer
5075 idlwave-init-rinfo-when-idle-after
5076 nil 'idlwave-load-rinfo-next-step))))))
5e72c6b2 5077
f66f03de
S
5078(defun idlwave-load-all-rinfo (&optional force)
5079 ;; Load and case-treat the system, user catalog, and library routine
5080 ;; info files.
5081
5082 ;; System
5e72c6b2 5083 (when (or force (not (aref idlwave-load-rinfo-steps-done 0)))
f66f03de
S
5084 ;;(load "idlw-rinfo" 'noerror 'nomessage))
5085 (idlwave-load-system-routine-info))
5e72c6b2
S
5086 (when (or force (not (aref idlwave-load-rinfo-steps-done 1)))
5087 (message "Normalizing idlwave-system-routines...")
5088 (setq idlwave-system-routines
5089 (idlwave-sintern-rinfo-list idlwave-system-routines 'sys))
5090 (message "Normalizing idlwave-system-routines...done"))
f66f03de
S
5091 (when idlwave-system-routines
5092 (setq idlwave-routines (copy-sequence idlwave-system-routines))
5093 (setq idlwave-last-system-routine-info-cons-cell
5094 (nthcdr (1- (length idlwave-routines)) idlwave-routines)))
5095
5096 ;; User catalog
52a244eb
S
5097 (when (and (stringp idlwave-user-catalog-file)
5098 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5099 (condition-case nil
52a244eb
S
5100 (when (or force (not (aref idlwave-load-rinfo-steps-done 2)))
5101 (load-file idlwave-user-catalog-file))
5102 (error nil))
4b1aaa8b 5103 (when (and
f66f03de
S
5104 (boundp 'idlwave-library-routines)
5105 idlwave-library-routines)
52a244eb 5106 (setq idlwave-library-routines nil)
4b1aaa8b 5107 (error "Outdated user catalog: %s... recreate"
f66f03de 5108 idlwave-user-catalog-file))
52a244eb
S
5109 (setq idlwave-true-path-alist nil)
5110 (when (or force (not (aref idlwave-load-rinfo-steps-done 3)))
5111 (message "Normalizing user catalog routines...")
4b1aaa8b
PE
5112 (setq idlwave-user-catalog-routines
5113 (idlwave-sintern-rinfo-list
52a244eb
S
5114 idlwave-user-catalog-routines 'sys))
5115 (message "Normalizing user catalog routines...done")))
f66f03de
S
5116
5117 ;; Library catalog
52a244eb
S
5118 (when (or force (not (aref idlwave-load-rinfo-steps-done 4)))
5119 (idlwave-scan-library-catalogs
5120 "Loading and normalizing library catalogs..."))
05a1abfc
CD
5121 (run-hooks 'idlwave-after-load-rinfo-hook))
5122
f32b3b91
CD
5123
5124(defun idlwave-update-buffer-routine-info ()
5125 (let (res)
4b1aaa8b 5126 (cond
15e42531
CD
5127 ((eq idlwave-scan-all-buffers-for-routine-info t)
5128 ;; Scan all buffers, current buffer last
5129 (message "Scanning all buffers...")
4b1aaa8b 5130 (setq res (idlwave-get-routine-info-from-buffers
15e42531
CD
5131 (reverse (buffer-list)))))
5132 ((null idlwave-scan-all-buffers-for-routine-info)
5133 ;; Don't scan any buffers
5134 (setq res nil))
5135 (t
f32b3b91
CD
5136 ;; Just scan this buffer
5137 (if (eq major-mode 'idlwave-mode)
5138 (progn
5139 (message "Scanning current buffer...")
5140 (setq res (idlwave-get-routine-info-from-buffers
15e42531 5141 (list (current-buffer))))))))
f32b3b91 5142 ;; Put the result into the correct variable
4b1aaa8b 5143 (setq idlwave-buffer-routines
52a244eb 5144 (idlwave-sintern-rinfo-list res 'set))))
f32b3b91 5145
05a1abfc 5146(defun idlwave-concatenate-rinfo-lists (&optional quiet run-hook)
f32b3b91 5147 "Put the different sources for routine information together."
4b1aaa8b 5148 ;; The sequence here is important because earlier definitions shadow
f32b3b91 5149 ;; later ones. We assume that if things in the buffers are newer
52a244eb 5150 ;; then in the shell of the system, they are meant to be different.
15e42531
CD
5151 (setcdr idlwave-last-system-routine-info-cons-cell
5152 (append idlwave-buffer-routines
5153 idlwave-compiled-routines
52a244eb
S
5154 idlwave-library-catalog-routines
5155 idlwave-user-catalog-routines))
f32b3b91 5156 (setq idlwave-class-alist nil)
15e42531 5157
f32b3b91 5158 ;; Give a message with information about the number of routines we have.
15e42531 5159 (unless quiet
4b1aaa8b 5160 (message
52a244eb 5161 "Routines Found: buffer(%d) compiled(%d) library(%d) user(%d) system(%d)"
15e42531
CD
5162 (length idlwave-buffer-routines)
5163 (length idlwave-compiled-routines)
52a244eb
S
5164 (length idlwave-library-catalog-routines)
5165 (length idlwave-user-catalog-routines)
05a1abfc
CD
5166 (length idlwave-system-routines)))
5167 (if run-hook
5168 (run-hooks 'idlwave-update-rinfo-hook)))
15e42531
CD
5169
5170(defun idlwave-class-alist ()
5171 "Return the class alist - make it if necessary."
5172 (or idlwave-class-alist
5173 (let (class)
5174 (loop for x in idlwave-routines do
5175 (when (and (setq class (nth 2 x))
5176 (not (assq class idlwave-class-alist)))
5177 (push (list class) idlwave-class-alist)))
4b1aaa8b 5178 idlwave-class-alist)))
15e42531
CD
5179
5180;; Three functions for the hooks
5181(defun idlwave-save-buffer-update ()
5182 (idlwave-update-current-buffer-info 'save-buffer))
5183(defun idlwave-kill-buffer-update ()
5184 (idlwave-update-current-buffer-info 'kill-buffer))
5185(defun idlwave-new-buffer-update ()
5186 (idlwave-update-current-buffer-info 'find-file))
5187
5188(defun idlwave-update-current-buffer-info (why)
52a244eb 5189 "Update idlwave-routines for current buffer. Can run from after-save-hook."
15e42531
CD
5190 (when (and (eq major-mode 'idlwave-mode)
5191 (or (eq t idlwave-auto-routine-info-updates)
5192 (memq why idlwave-auto-routine-info-updates))
5193 idlwave-scan-all-buffers-for-routine-info
5194 idlwave-routines)
5195 (condition-case nil
5196 (let (routines)
5197 (idlwave-replace-buffer-routine-info
5198 (buffer-file-name)
5199 (if (eq why 'kill-buffer)
5200 nil
5201 (setq routines
5202 (idlwave-sintern-rinfo-list
5203 (idlwave-get-routine-info-from-buffers
5204 (list (current-buffer))) 'set))))
5205 (idlwave-concatenate-rinfo-lists 'quiet)
5206 routines)
5207 (error nil))))
5208
5209(defun idlwave-replace-buffer-routine-info (file new)
5210 "Cut the part from FILE out of `idlwave-buffer-routines' and add NEW."
4b1aaa8b 5211 (let ((list idlwave-buffer-routines)
15e42531
CD
5212 found)
5213 (while list
5214 ;; The following test uses eq to make sure it works correctly
5215 ;; when two buffers visit the same file. Then the file names
5216 ;; will be equal, but not eq.
52a244eb 5217 (if (eq (idlwave-routine-source-file (nth 3 (car list))) file)
15e42531
CD
5218 (progn
5219 (setcar list nil)
5220 (setq found t))
5221 (if found
4b1aaa8b 5222 ;; End of that section reached. Jump.
15e42531
CD
5223 (setq list nil)))
5224 (setq list (cdr list)))
5225 (setq idlwave-buffer-routines
5226 (append new (delq nil idlwave-buffer-routines)))))
f32b3b91
CD
5227
5228;;----- Scanning buffers -------------------
5229
5230(defun idlwave-get-routine-info-from-buffers (buffers)
5231 "Call `idlwave-get-buffer-routine-info' on idlwave-mode buffers in BUFFERS."
5232 (let (buf routine-lists res)
5233 (save-excursion
5234 (while (setq buf (pop buffers))
5235 (set-buffer buf)
05a1abfc
CD
5236 (if (and (eq major-mode 'idlwave-mode)
5237 buffer-file-name)
f32b3b91
CD
5238 ;; yes, this buffer has the right mode.
5239 (progn (setq res (condition-case nil
5240 (idlwave-get-buffer-routine-info)
5241 (error nil)))
5242 (push res routine-lists)))))
5243 ;; Concatenate the individual lists and return the result
5244 (apply 'nconc routine-lists)))
5245
5246(defun idlwave-get-buffer-routine-info ()
5247 "Scan the current buffer for routine info. Return (PRO-LIST FUNC-LIST)."
5248 (let* ((case-fold-search t)
5249 routine-list string entry)
5250 (save-excursion
5251 (save-restriction
5252 (widen)
5253 (goto-char (point-min))
4b1aaa8b 5254 (while (re-search-forward
15e42531 5255 "^[ \t]*\\(pro\\|function\\)[ \t]" nil t)
76959b77 5256 (setq string (buffer-substring-no-properties
f32b3b91 5257 (match-beginning 0)
4b1aaa8b 5258 (progn
f32b3b91
CD
5259 (idlwave-end-of-statement)
5260 (point))))
5261 (setq entry (idlwave-parse-definition string))
5262 (push entry routine-list))))
5263 routine-list))
5264
15e42531 5265(defvar idlwave-scanning-lib-dir)
f32b3b91
CD
5266(defun idlwave-parse-definition (string)
5267 "Parse a module definition."
5268 (let ((case-fold-search t)
5269 start name args type keywords class)
5270 ;; Remove comments
5271 (while (string-match ";.*" string)
5272 (setq string (replace-match "" t t string)))
5273 ;; Remove the continuation line stuff
5274 (while (string-match "\\([^a-zA-Z0-9$_]\\)\\$[ \t]*\n" string)
5275 (setq string (replace-match "\\1 " t nil string)))
05a1abfc
CD
5276 (while (string-match "\n" string)
5277 (setq string (replace-match " " t nil string)))
f32b3b91
CD
5278 ;; Match the name and type.
5279 (when (string-match
5280 "\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)" string)
5281 (setq start (match-end 0))
5282 (setq type (downcase (match-string 1 string)))
5283 (if (match-beginning 3)
5284 (setq class (match-string 3 string)))
5285 (setq name (match-string 4 string)))
5286 ;; Match normal args and keyword args
5287 (while (string-match
15e42531 5288 ",\\s-*\\([a-zA-Z][a-zA-Z0-9$_]*\\|\\(_ref\\)?_extra\\)\\s-*\\(=\\)?"
f32b3b91
CD
5289 string start)
5290 (setq start (match-end 0))
15e42531 5291 (if (match-beginning 3)
f32b3b91
CD
5292 (push (match-string 1 string) keywords)
5293 (push (match-string 1 string) args)))
5294 ;; Normalize and sort.
5295 (setq args (nreverse args))
4b1aaa8b 5296 (setq keywords (sort keywords (lambda (a b)
f32b3b91
CD
5297 (string< (downcase a) (downcase b)))))
5298 ;; Make and return the entry
5299 ;; We don't know which argument are optional, so this information
5300 ;; will not be contained in the calling sequence.
5301 (list name
5302 (if (equal type "pro") 'pro 'fun)
5303 class
5304 (cond ((not (boundp 'idlwave-scanning-lib))
52a244eb 5305 (list 'buffer (buffer-file-name)))
4b1aaa8b 5306; ((string= (downcase
15e42531
CD
5307; (file-name-sans-extension
5308; (file-name-nondirectory (buffer-file-name))))
5309; (downcase name))
5310; (list 'lib))
5311; (t (cons 'lib (file-name-nondirectory (buffer-file-name))))
52a244eb
S
5312 (t (list 'user (file-name-nondirectory (buffer-file-name))
5313 idlwave-scanning-lib-dir "UserLib")))
4b1aaa8b 5314 (concat
f32b3b91
CD
5315 (if (string= type "function") "Result = " "")
5316 (if class "Obj ->[%s::]" "")
5317 "%s"
5318 (if args
5319 (concat
5320 (if (string= type "function") "(" ", ")
5321 (mapconcat 'identity args ", ")
5322 (if (string= type "function") ")" ""))))
5323 (if keywords
52a244eb 5324 (cons nil (mapcar 'list keywords)) ;No help file
f32b3b91
CD
5325 nil))))
5326
f32b3b91 5327
52a244eb 5328;;----- Scanning the user catalog -------------------
15e42531
CD
5329
5330(defun idlwave-sys-dir ()
5331 "Return the syslib directory, or a dummy that never matches."
3938cb82
S
5332 (cond
5333 ((and idlwave-system-directory
5334 (not (string= idlwave-system-directory "")))
5335 idlwave-system-directory)
5336 ((getenv "IDL_DIR"))
5337 (t "@@@@@@@@")))
5338
52a244eb 5339
52a244eb 5340(defun idlwave-create-user-catalog-file (&optional arg)
f32b3b91 5341 "Scan all files on selected dirs of IDL search path for routine information.
52a244eb
S
5342
5343A widget checklist will allow you to choose the directories. Write
5344the result as a file `idlwave-user-catalog-file'. When this file
5345exists, will be automatically loaded to give routine information about
5346library routines. With ARG, just rescan the same directories as last
5347time - so no widget will pop up."
f32b3b91
CD
5348 (interactive "P")
5349 ;; Make sure the file is loaded if it exists.
52a244eb
S
5350 (if (and (stringp idlwave-user-catalog-file)
5351 (file-regular-p idlwave-user-catalog-file))
f32b3b91 5352 (condition-case nil
52a244eb 5353 (load-file idlwave-user-catalog-file)
f32b3b91
CD
5354 (error nil)))
5355 ;; Make sure the file name makes sense
52a244eb
S
5356 (unless (and (stringp idlwave-user-catalog-file)
5357 (> (length idlwave-user-catalog-file) 0)
f32b3b91 5358 (file-accessible-directory-p
52a244eb 5359 (file-name-directory idlwave-user-catalog-file))
4b1aaa8b 5360 (not (string= "" (file-name-nondirectory
52a244eb
S
5361 idlwave-user-catalog-file))))
5362 (error "`idlwave-user-catalog-file' does not point to a file in an accessible directory"))
4b1aaa8b 5363
f32b3b91 5364 (cond
f32b3b91 5365 ;; Rescan the known directories
52a244eb
S
5366 ((and arg idlwave-path-alist
5367 (consp (car idlwave-path-alist)))
5368 (idlwave-scan-user-lib-files idlwave-path-alist))
5369
5370 ;; Expand the directories from library-path and run the widget
f32b3b91 5371 (idlwave-library-path
52a244eb 5372 (idlwave-display-user-catalog-widget
4b1aaa8b 5373 (if idlwave-true-path-alist
52a244eb
S
5374 ;; Propagate any flags on the existing path-alist
5375 (mapcar (lambda (x)
5376 (let ((path-entry (assoc (file-truename x)
5377 idlwave-true-path-alist)))
5378 (if path-entry
4b1aaa8b 5379 (cons x (cdr path-entry))
52a244eb
S
5380 (list x))))
5381 (idlwave-expand-path idlwave-library-path))
5382 (mapcar 'list (idlwave-expand-path idlwave-library-path)))))
5383
5384 ;; Ask the shell for the path and then run the widget
f32b3b91 5385 (t
f32b3b91 5386 (message "Asking the shell for IDL path...")
15e42531
CD
5387 (require 'idlw-shell)
5388 (idlwave-shell-send-command idlwave-shell-path-query
52a244eb 5389 '(idlwave-user-catalog-command-hook nil)
15e42531 5390 'hide))))
f32b3b91 5391
52a244eb
S
5392
5393;; Parse shell path information and select among it.
5394(defun idlwave-user-catalog-command-hook (&optional arg)
5395 ;; Command hook used by `idlwave-create-user-catalog-file'.
f32b3b91
CD
5396 (if arg
5397 ;; Scan immediately
52a244eb
S
5398 (idlwave-scan-user-lib-files idlwave-path-alist)
5399 ;; Set the path and display the widget
5400 (idlwave-shell-get-path-info 'no-write) ; set to something path-alist
5401 (idlwave-scan-library-catalogs "Locating library catalogs..." 'no-load)
5402 (idlwave-display-user-catalog-widget idlwave-path-alist)))
5403
4b1aaa8b 5404(defconst idlwave-user-catalog-widget-help-string
52a244eb
S
5405 "This is the front-end to the creation of the IDLWAVE user catalog.
5406Please select the directories on IDL's search path from which you
5407would like to extract routine information, to be stored in the file:
f32b3b91
CD
5408
5409 %s
5410
52a244eb
S
5411If this is not the correct file, first set variable
5412`idlwave-user-catalog-file', and call this command again.
15e42531 5413
52a244eb
S
5414N.B. Many libraries include pre-scanned catalog files
5415\(\".idlwave_catalog\"). These are marked with \"[LIB]\", and need
5416not be scanned. You can scan your own libraries off-line using the
5417perl script `idlwave_catalog'.
15e42531 5418
f32b3b91
CD
5419After selecting the directories, choose [Scan & Save] to scan the library
5420directories and save the routine info.
5421\n")
5422
5423(defvar idlwave-widget)
5424(defvar widget-keymap)
52a244eb 5425(defun idlwave-display-user-catalog-widget (dirs-list)
f32b3b91
CD
5426 "Create the widget to select IDL search path directories for scanning."
5427 (interactive)
5428 (require 'widget)
5429 (require 'wid-edit)
52a244eb 5430 (unless dirs-list
f32b3b91
CD
5431 (error "Don't know IDL's search path"))
5432
f32b3b91
CD
5433 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
5434 (switch-to-buffer (get-buffer-create "*IDLWAVE Widget*"))
5435 (kill-all-local-variables)
5436 (make-local-variable 'idlwave-widget)
52a244eb
S
5437 (widget-insert (format idlwave-user-catalog-widget-help-string
5438 idlwave-user-catalog-file))
4b1aaa8b 5439
f32b3b91 5440 (widget-create 'push-button
52a244eb 5441 :notify 'idlwave-widget-scan-user-lib-files
f32b3b91
CD
5442 "Scan & Save")
5443 (widget-insert " ")
5444 (widget-create 'push-button
52a244eb 5445 :notify 'idlwave-delete-user-catalog-file
f32b3b91
CD
5446 "Delete File")
5447 (widget-insert " ")
5448 (widget-create 'push-button
4b1aaa8b 5449 :notify
52a244eb
S
5450 '(lambda (&rest ignore)
5451 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5452 (mapcar (lambda (x)
5453 (unless (memq 'lib (cdr x))
5454 (idlwave-path-alist-add-flag x 'user)))
5455 path-list)
5456 (idlwave-display-user-catalog-widget path-list)))
5457 "Select All Non-Lib")
f32b3b91
CD
5458 (widget-insert " ")
5459 (widget-create 'push-button
4b1aaa8b 5460 :notify
52a244eb
S
5461 '(lambda (&rest ignore)
5462 (let ((path-list (widget-get idlwave-widget :path-dirs)))
5463 (mapcar (lambda (x)
5464 (idlwave-path-alist-remove-flag x 'user))
5465 path-list)
5466 (idlwave-display-user-catalog-widget path-list)))
f32b3b91 5467 "Deselect All")
52a244eb
S
5468 (widget-insert " ")
5469 (widget-create 'push-button
5470 :notify (lambda (&rest ignore)
5471 (kill-buffer (current-buffer)))
5472 "Quit")
f32b3b91
CD
5473 (widget-insert "\n\n")
5474
52a244eb 5475 (widget-insert "Select Directories: \n")
4b1aaa8b 5476
f32b3b91
CD
5477 (setq idlwave-widget
5478 (apply 'widget-create
5479 'checklist
4b1aaa8b
PE
5480 :value (delq nil (mapcar (lambda (x)
5481 (if (memq 'user (cdr x))
52a244eb
S
5482 (car x)))
5483 dirs-list))
f32b3b91
CD
5484 :greedy t
5485 :tag "List of directories"
4b1aaa8b
PE
5486 (mapcar (lambda (x)
5487 (list 'item
52a244eb
S
5488 (if (memq 'lib (cdr x))
5489 (concat "[LIB] " (car x) )
5490 (car x)))) dirs-list)))
5491 (widget-put idlwave-widget :path-dirs dirs-list)
f32b3b91
CD
5492 (widget-insert "\n")
5493 (use-local-map widget-keymap)
5494 (widget-setup)
5495 (goto-char (point-min))
5496 (delete-other-windows))
4b1aaa8b 5497
52a244eb 5498(defun idlwave-delete-user-catalog-file (&rest ignore)
f32b3b91 5499 (if (yes-or-no-p
52a244eb 5500 (format "Delete file %s " idlwave-user-catalog-file))
f32b3b91 5501 (progn
52a244eb
S
5502 (delete-file idlwave-user-catalog-file)
5503 (message "%s has been deleted" idlwave-user-catalog-file))))
f32b3b91 5504
52a244eb
S
5505(defun idlwave-widget-scan-user-lib-files (&rest ignore)
5506 ;; Call `idlwave-scan-user-lib-files' with data taken from the widget.
f32b3b91 5507 (let* ((widget idlwave-widget)
15e42531 5508 (selected-dirs (widget-value widget))
52a244eb
S
5509 (path-alist (widget-get widget :path-dirs))
5510 (this-path-alist path-alist)
5511 dir-entry)
5512 (while (setq dir-entry (pop this-path-alist))
4b1aaa8b 5513 (if (member
52a244eb
S
5514 (if (memq 'lib (cdr dir-entry))
5515 (concat "[LIB] " (car dir-entry))
5516 (car dir-entry))
5517 selected-dirs)
5518 (idlwave-path-alist-add-flag dir-entry 'user)
5519 (idlwave-path-alist-remove-flag dir-entry 'user)))
5520 (idlwave-scan-user-lib-files path-alist)))
f32b3b91
CD
5521
5522(defvar font-lock-mode)
52a244eb
S
5523(defun idlwave-scan-user-lib-files (path-alist)
5524 ;; Scan the PRO files in PATH-ALIST and store the info in the user catalog
f32b3b91 5525 (let* ((idlwave-scanning-lib t)
15e42531 5526 (idlwave-scanning-lib-dir "")
f32b3b91 5527 (idlwave-completion-case nil)
15e42531 5528 dirs-alist dir files file)
52a244eb
S
5529 (setq idlwave-user-catalog-routines nil
5530 idlwave-path-alist path-alist ; for library-path instead
5531 idlwave-true-path-alist nil)
5532 (if idlwave-auto-write-paths (idlwave-write-paths))
f32b3b91
CD
5533 (save-excursion
5534 (set-buffer (get-buffer-create "*idlwave-scan.pro*"))
5535 (idlwave-mode)
15e42531
CD
5536 (setq dirs-alist (reverse path-alist))
5537 (while (setq dir (pop dirs-alist))
52a244eb 5538 (when (memq 'user (cdr dir)) ; Has it marked for scan?
15e42531 5539 (setq dir (car dir))
52a244eb 5540 (setq idlwave-scanning-lib-dir dir)
15e42531
CD
5541 (when (file-directory-p dir)
5542 (setq files (directory-files dir 'full "\\.[pP][rR][oO]\\'"))
5543 (while (setq file (pop files))
5544 (when (file-regular-p file)
5545 (if (not (file-readable-p file))
5546 (message "Skipping %s (no read permission)" file)
5547 (message "Scanning %s..." file)
5548 (erase-buffer)
5549 (insert-file-contents file 'visit)
52a244eb 5550 (setq idlwave-user-catalog-routines
15e42531
CD
5551 (append (idlwave-get-routine-info-from-buffers
5552 (list (current-buffer)))
52a244eb
S
5553 idlwave-user-catalog-routines)))))))))
5554 (message "Creating user catalog file...")
f32b3b91
CD
5555 (kill-buffer "*idlwave-scan.pro*")
5556 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
15e42531
CD
5557 (let ((font-lock-maximum-size 0)
5558 (auto-mode-alist nil))
52a244eb 5559 (find-file idlwave-user-catalog-file))
f32b3b91
CD
5560 (if (and (boundp 'font-lock-mode)
5561 font-lock-mode)
5562 (font-lock-mode 0))
5563 (erase-buffer)
52a244eb 5564 (insert ";; IDLWAVE user catalog file\n")
f32b3b91
CD
5565 (insert (format ";; Created %s\n\n" (current-time-string)))
5566
f32b3b91 5567 ;; Define the routine info list
52a244eb 5568 (insert "\n(setq idlwave-user-catalog-routines\n '(")
5e72c6b2 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-user-catalog-routines))
f32b3b91 5575 (insert (format "))\n\n;;; %s ends here\n"
52a244eb 5576 (file-name-nondirectory idlwave-user-catalog-file)))
f32b3b91
CD
5577 (goto-char (point-min))
5578 ;; Save the buffer
5579 (save-buffer 0)
5580 (kill-buffer (current-buffer)))
52a244eb 5581 (message "Creating user catalog file...done")
f32b3b91 5582 (message "Info for %d routines saved in %s"
52a244eb
S
5583 (length idlwave-user-catalog-routines)
5584 idlwave-user-catalog-file)
f32b3b91
CD
5585 (sit-for 2)
5586 (idlwave-update-routine-info t))
5587
52a244eb
S
5588(defun idlwave-read-paths ()
5589 (if (and (stringp idlwave-path-file)
5590 (file-regular-p idlwave-path-file))
5591 (condition-case nil
5592 (load idlwave-path-file t t t)
5593 (error nil))))
5594
5595(defun idlwave-write-paths ()
5596 (interactive)
5597 (when (and idlwave-path-alist idlwave-system-directory)
5598 (let ((font-lock-maximum-size 0)
5599 (auto-mode-alist nil))
5600 (find-file idlwave-path-file))
5601 (if (and (boundp 'font-lock-mode)
5602 font-lock-mode)
5603 (font-lock-mode 0))
5604 (erase-buffer)
5605 (insert ";; IDLWAVE paths\n")
5606 (insert (format ";; Created %s\n\n" (current-time-string)))
5607 ;; Define the variable which knows the value of "!DIR"
5608 (insert (format "\n(setq idlwave-system-directory \"%s\")\n"
5609 idlwave-system-directory))
4b1aaa8b 5610
52a244eb
S
5611 ;; Define the variable which contains a list of all scanned directories
5612 (insert "\n(setq idlwave-path-alist\n '(")
5613 (let ((standard-output (current-buffer)))
8ffcfb27
GM
5614 (mapc (lambda (x)
5615 (insert "\n ")
5616 (prin1 x)
5617 (goto-char (point-max)))
5618 idlwave-path-alist))
52a244eb
S
5619 (insert "))\n")
5620 (save-buffer 0)
5621 (kill-buffer (current-buffer))))
5622
5623
f32b3b91
CD
5624(defun idlwave-expand-path (path &optional default-dir)
5625 ;; Expand parts of path starting with '+' recursively into directory list.
5626 ;; Relative recursive path elements are expanded relative to DEFAULT-DIR.
5627 (message "Expanding path...")
5628 (let (path1 dir recursive)
5629 (while (setq dir (pop path))
5630 (if (setq recursive (string= (substring dir 0 1) "+"))
5631 (setq dir (substring dir 1)))
5632 (if (and recursive
5633 (not (file-name-absolute-p dir)))
5634 (setq dir (expand-file-name dir default-dir)))
5635 (if recursive
5636 ;; Expand recursively
5637 (setq path1 (append (idlwave-recursive-directory-list dir) path1))
5638 ;; Keep unchanged
5639 (push dir path1)))
5640 (message "Expanding path...done")
5641 (nreverse path1)))
5642
5643(defun idlwave-recursive-directory-list (dir)
5644 ;; Return a list of all directories below DIR, including DIR itself
5645 (let ((path (list dir)) path1 file files)
5646 (while (setq dir (pop path))
5647 (when (file-directory-p dir)
5648 (setq files (nreverse (directory-files dir t "[^.]")))
5649 (while (setq file (pop files))
4b1aaa8b 5650 (if (file-directory-p file)
f32b3b91
CD
5651 (push (file-name-as-directory file) path)))
5652 (push dir path1)))
5653 path1))
5654
52a244eb
S
5655
5656;;----- Scanning the library catalogs ------------------
5657
3938cb82
S
5658
5659
5660
52a244eb 5661(defun idlwave-scan-library-catalogs (&optional message-base no-load)
4b1aaa8b 5662 "Scan for library catalog files (.idlwave_catalog) and ingest.
52a244eb
S
5663
5664All directories on `idlwave-path-alist' (or `idlwave-library-path'
5665instead, if present) are searched. Print MESSAGE-BASE along with the
5666libraries being loaded, if passed, and skip loading/normalizing if
5667NO-LOAD is non-nil. The variable `idlwave-use-library-catalogs' can
5668be set to nil to disable library catalog scanning."
5669 (when idlwave-use-library-catalogs
4b1aaa8b 5670 (let ((dirs
52a244eb
S
5671 (if idlwave-library-path
5672 (idlwave-expand-path idlwave-library-path)
5673 (mapcar 'car idlwave-path-alist)))
5674 (old-libname "")
5675 dir-entry dir flags catalog all-routines)
5676 (if message-base (message message-base))
5677 (while (setq dir (pop dirs))
5678 (catch 'continue
4b1aaa8b 5679 (when (file-readable-p
52a244eb
S
5680 (setq catalog (expand-file-name ".idlwave_catalog" dir)))
5681 (unless no-load
5682 (setq idlwave-library-catalog-routines nil)
5683 ;; Load the catalog file
5684 (condition-case nil
5685 (load catalog t t t)
5686 (error (throw 'continue t)))
4b1aaa8b
PE
5687 (when (and
5688 message-base
5689 (not (string= idlwave-library-catalog-libname
52a244eb 5690 old-libname)))
4b1aaa8b 5691 (message "%s" (concat message-base
f66f03de 5692 idlwave-library-catalog-libname))
52a244eb
S
5693 (setq old-libname idlwave-library-catalog-libname))
5694 (when idlwave-library-catalog-routines
5695 (setq all-routines
4b1aaa8b 5696 (append
52a244eb
S
5697 (idlwave-sintern-rinfo-list
5698 idlwave-library-catalog-routines 'sys dir)
5699 all-routines))))
4b1aaa8b 5700
52a244eb
S
5701 ;; Add a 'lib flag if on path-alist
5702 (when (and idlwave-path-alist
5703 (setq dir-entry (assoc dir idlwave-path-alist)))
5704 (idlwave-path-alist-add-flag dir-entry 'lib)))))
5705 (unless no-load (setq idlwave-library-catalog-routines all-routines))
5706 (if message-base (message (concat message-base "done"))))))
5707
5708;;----- Communicating with the Shell -------------------
f32b3b91
CD
5709
5710;; First, here is the idl program which can be used to query IDL for
4b1aaa8b 5711;; defined routines.
f32b3b91
CD
5712(defconst idlwave-routine-info.pro
5713 "
05a1abfc 5714;; START OF IDLWAVE SUPPORT ROUTINES
f66f03de
S
5715pro idlwave_print_safe,item,limit
5716 catch,err
5717 if err ne 0 then begin
5718 print,'Could not print item.'
5719 return
5720 endif
5721 if n_elements(item) gt limit then $
5722 print,item[0:limit-1],'<... truncated at ',strtrim(limit,2),' elements>' $
5723 else print,item
5724end
5725
15e42531 5726pro idlwave_print_info_entry,name,func=func,separator=sep
f32b3b91 5727 ;; See if it's an object method
15e42531 5728 if name eq '' then return
4b1aaa8b 5729 func = keyword_set(func)
f32b3b91
CD
5730 methsep = strpos(name,'::')
5731 meth = methsep ne -1
4b1aaa8b 5732
f32b3b91
CD
5733 ;; Get routine info
5734 pars = routine_info(name,/parameters,functions=func)
5735 source = routine_info(name,/source,functions=func)
5736 nargs = pars.num_args
5737 nkw = pars.num_kw_args
5738 if nargs gt 0 then args = pars.args
5739 if nkw gt 0 then kwargs = pars.kw_args
4b1aaa8b 5740
f32b3b91 5741 ;; Trim the class, and make the name
4b1aaa8b 5742 if meth then begin
f32b3b91
CD
5743 class = strmid(name,0,methsep)
5744 name = strmid(name,methsep+2,strlen(name)-1)
4b1aaa8b 5745 if nargs gt 0 then begin
f32b3b91
CD
5746 ;; remove the self argument
5747 wh = where(args ne 'SELF',nargs)
52a244eb 5748 if nargs gt 0 then args = args[wh]
f32b3b91
CD
5749 endif
5750 endif else begin
5751 ;; No class, just a normal routine.
5752 class = \"\"
5753 endelse
4b1aaa8b 5754
f32b3b91
CD
5755 ;; Calling sequence
5756 cs = \"\"
5757 if func then cs = 'Result = '
5758 if meth then cs = cs + 'Obj -> [' + '%s' + '::]'
5759 cs = cs + '%s'
5760 if func then cs = cs + '(' else if nargs gt 0 then cs = cs + ', '
5761 if nargs gt 0 then begin
5762 for j=0,nargs-1 do begin
52a244eb 5763 cs = cs + args[j]
f32b3b91
CD
5764 if j lt nargs-1 then cs = cs + ', '
5765 endfor
5766 end
5767 if func then cs = cs + ')'
5768 ;; Keyword arguments
5769 kwstring = ''
5770 if nkw gt 0 then begin
5771 for j=0,nkw-1 do begin
52a244eb 5772 kwstring = kwstring + ' ' + kwargs[j]
f32b3b91
CD
5773 endfor
5774 endif
4b1aaa8b 5775
52a244eb 5776 ret=(['IDLWAVE-PRO','IDLWAVE-FUN'])[func]
4b1aaa8b 5777
52a244eb 5778 print,ret + ': ' + name + sep + class + sep + source[0].path $
f32b3b91
CD
5779 + sep + cs + sep + kwstring
5780end
5781
f66f03de 5782pro idlwave_routine_info,file
52a244eb 5783 on_error,1
f32b3b91
CD
5784 sep = '<@>'
5785 print,'>>>BEGIN OF IDLWAVE ROUTINE INFO (\"' + sep + '\" IS THE SEPARATOR)'
5786 all = routine_info()
f66f03de
S
5787 fileQ=n_elements(file) ne 0
5788 if fileQ then file=strtrim(file,2)
4b1aaa8b
PE
5789 for i=0L,n_elements(all)-1L do begin
5790 if fileQ then begin
f66f03de
S
5791 if (routine_info(all[i],/SOURCE)).path eq file then $
5792 idlwave_print_info_entry,all[i],separator=sep
5793 endif else idlwave_print_info_entry,all[i],separator=sep
4b1aaa8b 5794 endfor
f32b3b91 5795 all = routine_info(/functions)
4b1aaa8b
PE
5796 for i=0L,n_elements(all)-1L do begin
5797 if fileQ then begin
f66f03de
S
5798 if (routine_info(all[i],/FUNCTIONS,/SOURCE)).path eq file then $
5799 idlwave_print_info_entry,all[i],separator=sep,/FUNC
5800 endif else idlwave_print_info_entry,all[i],separator=sep,/FUNC
4b1aaa8b 5801 endfor
f32b3b91
CD
5802 print,'>>>END OF IDLWAVE ROUTINE INFO'
5803end
05a1abfc
CD
5804
5805pro idlwave_get_sysvars
52a244eb 5806 on_error,1
05a1abfc
CD
5807 catch,error_status
5808 if error_status ne 0 then begin
5809 print, 'Cannot get info about system variables'
5810 endif else begin
5811 help,/brief,output=s,/system_variables ; ? unsafe use of OUTPUT=
5812 s = strtrim(strjoin(s,' ',/single),2) ; make one line
5813 v = strsplit(s,' +',/regex,/extract) ; get variables
f66f03de 5814 for i=0L,n_elements(v)-1 do begin
05a1abfc
CD
5815 t = [''] ; get tag list
5816 a=execute('if n_tags('+v[i]+') gt 0 then t=tag_names('+v[i]+')')
5817 print, 'IDLWAVE-SYSVAR: '+v[i]+' '+strjoin(t,' ',/single)
5818 endfor
5819 endelse
5820end
5821
5822pro idlwave_get_class_tags, class
5823 res = execute('tags=tag_names({'+class+'})')
5e72c6b2 5824 if res then print,'IDLWAVE-CLASS-TAGS: '+class+' '+strjoin(tags,' ',/single)
05a1abfc
CD
5825end
5826;; END OF IDLWAVE SUPPORT ROUTINES
4b1aaa8b 5827"
05a1abfc 5828 "The idl programs to get info from the shell.")
f32b3b91 5829
15e42531
CD
5830(defvar idlwave-idlwave_routine_info-compiled nil
5831 "Remembers if the routine info procedure is already compiled.")
f32b3b91
CD
5832
5833(defvar idlwave-shell-temp-pro-file)
15e42531 5834(defvar idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5835
5836(defun idlwave-shell-compile-helper-routines (&optional wait)
15e42531 5837 (unless (and idlwave-idlwave_routine_info-compiled
5e72c6b2 5838 (file-readable-p (idlwave-shell-temp-file 'rinfo)))
15e42531
CD
5839 (save-excursion
5840 (set-buffer (idlwave-find-file-noselect
5e72c6b2 5841 (idlwave-shell-temp-file 'pro)))
15e42531
CD
5842 (erase-buffer)
5843 (insert idlwave-routine-info.pro)
5844 (save-buffer 0))
4b1aaa8b 5845 (idlwave-shell-send-command
f66f03de 5846 (concat ".run \"" idlwave-shell-temp-pro-file "\"")
52a244eb 5847 nil 'hide wait)
15e42531 5848 (idlwave-shell-send-command
4b1aaa8b 5849 (format "save,'idlwave_print_safe','idlwave_routine_info','idlwave_print_info_entry','idlwave_get_class_tags','idlwave_get_sysvars',FILE='%s',/ROUTINES"
5e72c6b2 5850 (idlwave-shell-temp-file 'rinfo))
f66f03de
S
5851 nil 'hide)
5852 (setq idlwave-idlwave_routine_info-compiled t))
15e42531 5853
f66f03de
S
5854 ;; Restore if necessary. Must use execute to hide lame routine_info
5855 ;; errors on undefinded routine
15e42531 5856 (idlwave-shell-send-command
f66f03de
S
5857 (format "if execute(\"_v=routine_info('idlwave_routine_info',/SOURCE)\") eq 0 then restore,'%s' else if _v.path eq '' then restore,'%s'"
5858 idlwave-shell-temp-rinfo-save-file
15e42531 5859 idlwave-shell-temp-rinfo-save-file)
f66f03de
S
5860 nil 'hide))
5861
5862
5863(defun idlwave-shell-update-routine-info (&optional quiet run-hooks wait file)
5864 "Query the shell for routine_info of compiled modules and update the lists."
5865 ;; Save and compile the procedure. The compiled procedure is then
5866 ;; saved into an IDL SAVE file, to allow for fast RESTORE. We may
5867 ;; need to test for and possibly RESTORE the procedure each time we
5868 ;; use it, since the user may have killed or redefined it. In
5869 ;; particular, .RESET_SESSION will kill all user procedures. If
5870 ;; FILE is set, only update routine info for routines in that file.
5871
5872 (idlwave-shell-compile-helper-routines wait)
5873 ; execute the routine_info procedure, and analyze the output
5874 (idlwave-shell-send-command
5875 (format "idlwave_routine_info%s" (if file (concat ",'" file "'") ""))
15e42531
CD
5876 `(progn
5877 (idlwave-shell-routine-info-filter)
05a1abfc 5878 (idlwave-concatenate-rinfo-lists ,quiet ,run-hooks))
52a244eb 5879 'hide wait))
f32b3b91
CD
5880
5881;; ---------------------------------------------------------------------------
5882;;
5883;; Completion and displaying routine calling sequences
5884
15e42531 5885(defvar idlwave-completion-help-info nil)
52a244eb 5886(defvar idlwave-completion-help-links nil)
15e42531 5887(defvar idlwave-current-obj_new-class nil)
05a1abfc 5888(defvar idlwave-complete-special nil)
15e42531 5889
f32b3b91
CD
5890(defun idlwave-complete (&optional arg module class)
5891 "Complete a function, procedure or keyword name at point.
2e8b9c7d 5892This function is smart and figures out what can be completed
f32b3b91
CD
5893at this point.
5894- At the beginning of a statement it completes procedure names.
5895- In the middle of a statement it completes function names.
5896- after a `(' or `,' in the argument list of a function or procedure,
5897 it completes a keyword of the relevant function or procedure.
5898- In the first arg of `OBJ_NEW', it completes a class name.
5899
5e72c6b2 5900When several completions are possible, a list will be displayed in the
f32b3b91 5901*Completions* buffer. If this list is too long to fit into the
5e72c6b2
S
5902window, scrolling can be achieved by repeatedly pressing
5903\\[idlwave-complete].
f32b3b91
CD
5904
5905The function also knows about object methods. When it needs a class
5906name, the action depends upon `idlwave-query-class', which see. You
5e72c6b2
S
5907can force IDLWAVE to ask you for a class name with a
5908\\[universal-argument] prefix argument to this command.
f32b3b91
CD
5909
5910See also the variables `idlwave-keyword-completion-adds-equal' and
5911`idlwave-function-completion-adds-paren'.
5912
5913The optional ARG can be used to specify the completion type in order
5914to override IDLWAVE's idea of what should be completed at point.
5915Possible values are:
5916
59170 <=> query for the completion type
59181 <=> 'procedure
59192 <=> 'procedure-keyword
59203 <=> 'function
59214 <=> 'function-keyword
59225 <=> 'procedure-method
59236 <=> 'procedure-method-keyword
59247 <=> 'function-method
59258 <=> 'function-method-keyword
59269 <=> 'class
5927
5e72c6b2
S
5928As a special case, the universal argument C-u forces completion of
5929function names in places where the default would be a keyword.
5930
52a244eb
S
5931Two prefix argument, C-u C-u, prompts for a regexp by which to limit
5932completion.
5933
f32b3b91
CD
5934For Lisp programmers only:
5935When we force a keyword, optional argument MODULE can contain the module name.
5936When we force a method or a method keyword, CLASS can specify the class."
5937 (interactive "P")
5938 (idlwave-routines)
5939 (let* ((where-list
5940 (if (and arg
52a244eb 5941 (or (and (integerp arg) (not (equal arg '(16))))
f32b3b91
CD
5942 (symbolp arg)))
5943 (idlwave-make-force-complete-where-list arg module class)
5944 (idlwave-where)))
5945 (what (nth 2 where-list))
52a244eb
S
5946 (idlwave-force-class-query (equal arg '(4)))
5947 (completion-regexp-list
5948 (if (equal arg '(16))
5949 (list (read-string (concat "Completion Regexp: "))))))
4b1aaa8b 5950
f32b3b91
CD
5951 (if (and module (string-match "::" module))
5952 (setq class (substring module 0 (match-beginning 0))
5953 module (substring module (match-end 0))))
5954
5955 (cond
5956
5957 ((and (null arg)
5958 (eq (car-safe last-command) 'idlwave-display-completion-list)
595ab50b 5959 (get-buffer-window "*Completions*"))
f32b3b91
CD
5960 (setq this-command last-command)
5961 (idlwave-scroll-completions))
5962
52a244eb 5963 ;; Complete a filename in quotes
05a1abfc
CD
5964 ((and (idlwave-in-quote)
5965 (not (eq what 'class)))
5966 (idlwave-complete-filename))
5967
52a244eb
S
5968 ;; Check for any special completion functions
5969 ((and idlwave-complete-special
5970 (idlwave-call-special idlwave-complete-special)))
4b1aaa8b 5971
f32b3b91
CD
5972 ((null what)
5973 (error "Nothing to complete here"))
5974
52a244eb 5975 ;; Complete a class
f32b3b91 5976 ((eq what 'class)
15e42531 5977 (setq idlwave-completion-help-info '(class))
f32b3b91
CD
5978 (idlwave-complete-class))
5979
5980 ((eq what 'procedure)
5981 ;; Complete a procedure name
5e72c6b2
S
5982 (let* ((cw-list (nth 3 where-list))
5983 (class-selector (idlwave-determine-class cw-list 'pro))
5984 (super-classes (unless (idlwave-explicit-class-listed cw-list)
5985 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
5986 (isa (concat "procedure" (if class-selector "-method" "")))
5987 (type-selector 'pro))
4b1aaa8b 5988 (setq idlwave-completion-help-info
05a1abfc 5989 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
5990 (idlwave-complete-in-buffer
5991 'procedure (if class-selector 'method 'routine)
5992 (idlwave-routines) 'idlwave-selector
5993 (format "Select a %s name%s"
5994 isa
5995 (if class-selector
4b1aaa8b
PE
5996 (format " (class is %s)"
5997 (if (eq class-selector t)
76959b77 5998 "unknown" class-selector))
f32b3b91
CD
5999 ""))
6000 isa
52a244eb 6001 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91
CD
6002
6003 ((eq what 'function)
6004 ;; Complete a function name
5e72c6b2
S
6005 (let* ((cw-list (nth 3 where-list))
6006 (class-selector (idlwave-determine-class cw-list 'fun))
6007 (super-classes (unless (idlwave-explicit-class-listed cw-list)
6008 (idlwave-all-class-inherits class-selector)))
f32b3b91
CD
6009 (isa (concat "function" (if class-selector "-method" "")))
6010 (type-selector 'fun))
4b1aaa8b 6011 (setq idlwave-completion-help-info
05a1abfc 6012 (list 'routine nil type-selector class-selector nil super-classes))
f32b3b91
CD
6013 (idlwave-complete-in-buffer
6014 'function (if class-selector 'method 'routine)
6015 (idlwave-routines) 'idlwave-selector
6016 (format "Select a %s name%s"
6017 isa
6018 (if class-selector
4b1aaa8b 6019 (format " (class is %s)"
76959b77
S
6020 (if (eq class-selector t)
6021 "unknown" class-selector))
f32b3b91
CD
6022 ""))
6023 isa
52a244eb 6024 'idlwave-attach-method-classes 'idlwave-add-file-link-selector)))
f32b3b91 6025
52a244eb 6026 ((and (memq what '(procedure-keyword function-keyword)) ; Special Case
5e72c6b2
S
6027 (equal arg '(4)))
6028 (idlwave-complete 3))
6029
f32b3b91
CD
6030 ((eq what 'procedure-keyword)
6031 ;; Complete a procedure keyword
6032 (let* ((where (nth 3 where-list))
6033 (name (car where))
6034 (method-selector name)
6035 (type-selector 'pro)
6036 (class (idlwave-determine-class where 'pro))
6037 (class-selector class)
05a1abfc 6038 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 6039 (isa (format "procedure%s-keyword" (if class "-method" "")))
15e42531 6040 (entry (idlwave-best-rinfo-assq
f32b3b91 6041 name 'pro class (idlwave-routines)))
3938cb82 6042 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 6043 (list (idlwave-entry-keywords entry 'do-link)))
f32b3b91
CD
6044 (unless (or entry (eq class t))
6045 (error "Nothing known about procedure %s"
6046 (idlwave-make-full-name class name)))
4b1aaa8b 6047 (setq list (idlwave-fix-keywords name 'pro class list
3938cb82 6048 super-classes system))
b6a97790 6049 (unless list (error "No keywords available for procedure %s"
3938cb82 6050 (idlwave-make-full-name class name)))
4b1aaa8b 6051 (setq idlwave-completion-help-info
52a244eb 6052 (list 'keyword name type-selector class-selector entry super-classes))
f32b3b91
CD
6053 (idlwave-complete-in-buffer
6054 'keyword 'keyword list nil
6055 (format "Select keyword for procedure %s%s"
6056 (idlwave-make-full-name class name)
15e42531 6057 (if (or (member '("_EXTRA") list)
4b1aaa8b 6058 (member '("_REF_EXTRA") list))
15e42531 6059 " (note _EXTRA)" ""))
f32b3b91
CD
6060 isa
6061 'idlwave-attach-keyword-classes)))
6062
6063 ((eq what 'function-keyword)
6064 ;; Complete a function keyword
6065 (let* ((where (nth 3 where-list))
6066 (name (car where))
6067 (method-selector name)
6068 (type-selector 'fun)
6069 (class (idlwave-determine-class where 'fun))
6070 (class-selector class)
05a1abfc 6071 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91 6072 (isa (format "function%s-keyword" (if class "-method" "")))
15e42531 6073 (entry (idlwave-best-rinfo-assq
f32b3b91 6074 name 'fun class (idlwave-routines)))
3938cb82 6075 (system (if entry (eq (car (nth 3 entry)) 'system)))
52a244eb 6076 (list (idlwave-entry-keywords entry 'do-link))
15e42531 6077 msg-name)
f32b3b91
CD
6078 (unless (or entry (eq class t))
6079 (error "Nothing known about function %s"
6080 (idlwave-make-full-name class name)))
4b1aaa8b 6081 (setq list (idlwave-fix-keywords name 'fun class list
3938cb82 6082 super-classes system))
15e42531
CD
6083 ;; OBJ_NEW: Messages mention the proper Init method
6084 (setq msg-name (if (and (null class)
6085 (string= (upcase name) "OBJ_NEW"))
6086 (concat idlwave-current-obj_new-class
6087 "::Init (via OBJ_NEW)")
6088 (idlwave-make-full-name class name)))
b6a97790 6089 (unless list (error "No keywords available for function %s"
3938cb82 6090 msg-name))
4b1aaa8b 6091 (setq idlwave-completion-help-info
05a1abfc 6092 (list 'keyword name type-selector class-selector nil super-classes))
f32b3b91
CD
6093 (idlwave-complete-in-buffer
6094 'keyword 'keyword list nil
15e42531
CD
6095 (format "Select keyword for function %s%s" msg-name
6096 (if (or (member '("_EXTRA") list)
4b1aaa8b 6097 (member '("_REF_EXTRA") list))
15e42531 6098 " (note _EXTRA)" ""))
f32b3b91
CD
6099 isa
6100 'idlwave-attach-keyword-classes)))
15e42531 6101
f32b3b91
CD
6102 (t (error "This should not happen (idlwave-complete)")))))
6103
05a1abfc
CD
6104(defvar idlwave-complete-special nil
6105 "List of special completion functions.
52a244eb
S
6106These functions are called for each completion. Each function must
6107check if its own special completion context is present. If yes, it
6108should use `idlwave-complete-in-buffer' to do some completion and
6109return t. If such a function returns t, *no further* attempts to
6110complete other contexts will be done. If the function returns nil,
6111other completions will be tried.")
76959b77
S
6112
6113(defun idlwave-call-special (functions &rest args)
6114 (let ((funcs functions)
6115 fun ret)
05a1abfc 6116 (catch 'exit
76959b77
S
6117 (while (setq fun (pop funcs))
6118 (if (setq ret (apply fun args))
6119 (throw 'exit ret)))
05a1abfc
CD
6120 nil)))
6121
f32b3b91
CD
6122(defun idlwave-make-force-complete-where-list (what &optional module class)
6123 ;; Return an artificial WHERE specification to force the completion
6124 ;; routine to complete a specific item independent of context.
6125 ;; WHAT is the prefix arg of `idlwave-complete', see there for details.
6126 ;; MODULE and CLASS can be used to specify the routine name and class.
6127 ;; The class name will also be found in MODULE if that is like "class::mod".
6128 (let* ((what-list '(("procedure") ("procedure-keyword")
6129 ("function") ("function-keyword")
6130 ("procedure-method") ("procedure-method-keyword")
6131 ("function-method") ("function-method-keyword")
6132 ("class")))
6133 (module (idlwave-sintern-routine-or-method module class))
6134 (class (idlwave-sintern-class class))
4b1aaa8b 6135 (what (cond
f32b3b91
CD
6136 ((equal what 0)
6137 (setq what
4b1aaa8b 6138 (intern (completing-read
f32b3b91
CD
6139 "Complete what? " what-list nil t))))
6140 ((integerp what)
6141 (setq what (intern (car (nth (1- what) what-list)))))
6142 ((and what
6143 (symbolp what)
6144 (assoc (symbol-name what) what-list))
6145 what)
eac9c0ef 6146 (t (error "Invalid WHAT"))))
f32b3b91
CD
6147 (nil-list '(nil nil nil nil))
6148 (class-list (list nil nil (or class t) nil)))
6149
6150 (cond
6151
6152 ((eq what 'procedure)
6153 (list nil-list nil-list 'procedure nil-list nil))
6154
6155 ((eq what 'procedure-keyword)
6156 (let* ((class-selector nil)
05a1abfc 6157 (super-classes nil)
f32b3b91
CD
6158 (type-selector 'pro)
6159 (pro (or module
4b1aaa8b 6160 (idlwave-completing-read
f32b3b91
CD
6161 "Procedure: " (idlwave-routines) 'idlwave-selector))))
6162 (setq pro (idlwave-sintern-routine pro))
6163 (list nil-list nil-list 'procedure-keyword
6164 (list pro nil nil nil) nil)))
6165
6166 ((eq what 'function)
6167 (list nil-list nil-list 'function nil-list nil))
6168
6169 ((eq what 'function-keyword)
6170 (let* ((class-selector nil)
05a1abfc 6171 (super-classes nil)
f32b3b91
CD
6172 (type-selector 'fun)
6173 (func (or module
4b1aaa8b 6174 (idlwave-completing-read
f32b3b91
CD
6175 "Function: " (idlwave-routines) 'idlwave-selector))))
6176 (setq func (idlwave-sintern-routine func))
6177 (list nil-list nil-list 'function-keyword
6178 (list func nil nil nil) nil)))
6179
6180 ((eq what 'procedure-method)
6181 (list nil-list nil-list 'procedure class-list nil))
6182
6183 ((eq what 'procedure-method-keyword)
6184 (let* ((class (idlwave-determine-class class-list 'pro))
6185 (class-selector class)
05a1abfc 6186 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6187 (type-selector 'pro)
6188 (pro (or module
6189 (idlwave-completing-read
6190 (format "Procedure in %s class: " class-selector)
6191 (idlwave-routines) 'idlwave-selector))))
6192 (setq pro (idlwave-sintern-method pro))
6193 (list nil-list nil-list 'procedure-keyword
6194 (list pro nil class nil) nil)))
6195
6196 ((eq what 'function-method)
6197 (list nil-list nil-list 'function class-list nil))
6198
6199 ((eq what 'function-method-keyword)
6200 (let* ((class (idlwave-determine-class class-list 'fun))
6201 (class-selector class)
05a1abfc 6202 (super-classes (idlwave-all-class-inherits class-selector))
f32b3b91
CD
6203 (type-selector 'fun)
6204 (func (or module
6205 (idlwave-completing-read
6206 (format "Function in %s class: " class-selector)
6207 (idlwave-routines) 'idlwave-selector))))
6208 (setq func (idlwave-sintern-method func))
6209 (list nil-list nil-list 'function-keyword
6210 (list func nil class nil) nil)))
6211
6212 ((eq what 'class)
6213 (list nil-list nil-list 'class nil-list nil))
4b1aaa8b 6214
eac9c0ef 6215 (t (error "Invalid value for WHAT")))))
f32b3b91
CD
6216
6217(defun idlwave-completing-read (&rest args)
6218 ;; Completing read, case insensitive
6219 (let ((old-value (default-value 'completion-ignore-case)))
6220 (unwind-protect
6221 (progn
6222 (setq-default completion-ignore-case t)
6223 (apply 'completing-read args))
6224 (setq-default completion-ignore-case old-value))))
6225
05a1abfc
CD
6226(defvar idlwave-shell-default-directory)
6227(defun idlwave-complete-filename ()
6228 "Use the comint stuff to complete a file name."
6229 (require 'comint)
6230 (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-")
6231 (comint-completion-addsuffix nil)
6232 (default-directory
6233 (if (and (boundp 'idlwave-shell-default-directory)
6234 (stringp idlwave-shell-default-directory)
6235 (file-directory-p idlwave-shell-default-directory))
6236 idlwave-shell-default-directory
4b1aaa8b 6237 default-directory)))
05a1abfc
CD
6238 (comint-dynamic-complete-filename)))
6239
f32b3b91
CD
6240(defun idlwave-make-full-name (class name)
6241 ;; Make a fully qualified module name including the class name
6242 (concat (if class (format "%s::" class) "") name))
6243
15e42531
CD
6244(defun idlwave-rinfo-assoc (name type class list)
6245 "Like `idlwave-rinfo-assq', but sintern strings first."
4b1aaa8b 6246 (idlwave-rinfo-assq
15e42531
CD
6247 (idlwave-sintern-routine-or-method name class)
6248 type (idlwave-sintern-class class) list))
6249
f32b3b91
CD
6250(defun idlwave-rinfo-assq (name type class list)
6251 ;; Works like assq, but also checks type and class
6252 (catch 'exit
6253 (let (match)
6254 (while (setq match (assq name list))
6255 (and (or (eq type t)
6256 (eq (nth 1 match) type))
6257 (eq (nth 2 match) class)
6258 (throw 'exit match))
6259 (setq list (cdr (memq match list)))))))
6260
05a1abfc 6261(defun idlwave-rinfo-assq-any-class (name type class list)
52a244eb 6262 ;; Return the first matching method on the inheritance list
05a1abfc
CD
6263 (let* ((classes (cons class (idlwave-all-class-inherits class)))
6264 class rtn)
6265 (while classes
6266 (if (setq rtn (idlwave-rinfo-assq name type (pop classes) list))
6267 (setq classes nil)))
6268 rtn))
6269
4b1aaa8b 6270(defun idlwave-best-rinfo-assq (name type class list &optional with-file
52a244eb
S
6271 keep-system)
6272 "Like `idlwave-rinfo-assq', but get all twins and sort, then return first.
6273If WITH-FILE is passed, find the best rinfo entry with a file
6274included. If KEEP-SYSTEM is set, don't prune system for compiled
6275syslib files."
15e42531 6276 (let ((twins (idlwave-routine-twins
05a1abfc 6277 (idlwave-rinfo-assq-any-class name type class list)
15e42531
CD
6278 list))
6279 syslibp)
6280 (when (> (length twins) 1)
6281 (setq twins (sort twins 'idlwave-routine-entry-compare-twins))
52a244eb
S
6282 (if (and (null keep-system)
6283 (eq 'system (car (nth 3 (car twins))))
15e42531
CD
6284 (setq syslibp (idlwave-any-syslib (cdr twins)))
6285 (not (equal 1 syslibp)))
52a244eb
S
6286 ;; Its a compiled syslib, so we need to remove the system entry
6287 (setq twins (cdr twins)))
6288 (if with-file
6289 (setq twins (delq nil
6290 (mapcar (lambda (x)
6291 (if (nth 1 (nth 3 x)) x))
6292 twins)))))
15e42531
CD
6293 (car twins)))
6294
4b1aaa8b 6295(defun idlwave-best-rinfo-assoc (name type class list &optional with-file
52a244eb 6296 keep-system)
15e42531
CD
6297 "Like `idlwave-best-rinfo-assq', but sintern strings first."
6298 (idlwave-best-rinfo-assq
6299 (idlwave-sintern-routine-or-method name class)
52a244eb 6300 type (idlwave-sintern-class class) list with-file keep-system))
15e42531
CD
6301
6302(defun idlwave-any-syslib (entries)
6303 "Does the entry list ENTRIES contain a syslib entry?
6304If yes, return the index (>=1)."
6305 (let (file (cnt 0))
6306 (catch 'exit
6307 (while entries
6308 (incf cnt)
52a244eb
S
6309 (setq file (idlwave-routine-source-file (nth 3 (car entries))))
6310 (if (and file (idlwave-syslib-p file))
15e42531
CD
6311 (throw 'exit cnt)
6312 (setq entries (cdr entries))))
6313 nil)))
6314
f32b3b91
CD
6315(defun idlwave-all-assq (key list)
6316 "Return a list of all associations of Key in LIST."
6317 (let (rtn elt)
6318 (while (setq elt (assq key list))
6319 (push elt rtn)
6320 (setq list (cdr (memq elt list))))
6321 (nreverse rtn)))
6322
6323(defun idlwave-all-method-classes (method &optional type)
6324 "Return all classes which have a method METHOD. TYPE is 'fun or 'pro.
6325When TYPE is not specified, both procedures and functions will be considered."
6326 (if (null method)
15e42531 6327 (mapcar 'car (idlwave-class-alist))
f32b3b91 6328 (let (rtn)
8ffcfb27
GM
6329 (mapc (lambda (x)
6330 (and (nth 2 x)
6331 (or (not type)
6332 (eq type (nth 1 x)))
6333 (push (nth 2 x) rtn)))
6334 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6335 (idlwave-uniquify rtn))))
6336
6337(defun idlwave-all-method-keyword-classes (method keyword &optional type)
6338 "Return all classes which have a method METHOD with keyword KEYWORD.
6339TYPE is 'fun or 'pro.
6340When TYPE is not specified, both procedures and functions will be considered."
6341 (if (or (null method)
6342 (null keyword))
6343 nil
6344 (let (rtn)
8ffcfb27
GM
6345 (mapc (lambda (x)
6346 (and (nth 2 x) ; non-nil class
6347 (or (not type) ; correct or unspecified type
6348 (eq type (nth 1 x)))
6349 (assoc keyword (idlwave-entry-keywords x))
6350 (push (nth 2 x) rtn)))
6351 (idlwave-all-assq method (idlwave-routines)))
f32b3b91
CD
6352 (idlwave-uniquify rtn))))
6353
05a1abfc
CD
6354(defun idlwave-members-only (list club)
6355 "Return list of all elements in LIST which are also in CLUB."
6356 (let (rtn)
6357 (while list
6358 (if (member (car list) club)
6359 (setq rtn (cons (car list) rtn)))
6360 (setq list (cdr list)))
6361 (nreverse rtn)))
6362
6363(defun idlwave-nonmembers-only (list club)
6364 "Return list of all elements in LIST which are not in CLUB."
6365 (let (rtn)
6366 (while list
6367 (if (member (car list) club)
6368 nil
6369 (setq rtn (cons (car list) rtn)))
6370 (setq list (cdr list)))
6371 (nreverse rtn)))
6372
5e72c6b2
S
6373(defun idlwave-explicit-class-listed (info)
6374 "Return whether or not the class is listed explicitly, ala a->b::c.
6375INFO is as returned by idlwave-what-function or -procedure."
6376 (let ((apos (nth 3 info)))
6377 (if apos
6378 (save-excursion (goto-char apos)
6379 (looking-at "->[a-zA-Z][a-zA-Z0-9$_]*::")))))
6380
76959b77
S
6381(defvar idlwave-determine-class-special nil
6382 "List of special functions for determining class.
6383Must accept two arguments: `apos' and `info'")
6384
f32b3b91 6385(defun idlwave-determine-class (info type)
4b1aaa8b 6386 ;; Determine the class of a routine call.
76959b77
S
6387 ;; INFO is the `cw-list' structure as returned by idlwave-where.
6388 ;; The second element in this structure is the class. When nil, we
6389 ;; return nil. When t, try to get the class from text properties at
6390 ;; the arrow. When the object is "self", we use the class of the
6391 ;; current routine. otherwise prompt the user for a class name.
6392 ;; Also stores the selected class as a text property at the arrow.
f32b3b91
CD
6393 ;; TYPE is 'fun or 'pro.
6394 (let* ((class (nth 2 info))
6395 (apos (nth 3 info))
6396 (nassoc (assoc (if (stringp (car info))
6397 (upcase (car info))
6398 (car info))
6399 idlwave-query-class))
6400 (dassoc (assq (if (car info) 'keyword-default 'method-default)
6401 idlwave-query-class))
6402 (query (cond (nassoc (cdr nassoc))
6403 (dassoc (cdr dassoc))
6404 (t t)))
6405 (arrow (and apos (string= (buffer-substring apos (+ 2 apos)) "->")))
4b1aaa8b 6406 (is-self
15e42531
CD
6407 (and arrow
6408 (save-excursion (goto-char apos)
6409 (forward-word -1)
6410 (let ((case-fold-search t))
6411 (looking-at "self\\>")))))
f32b3b91 6412 (force-query idlwave-force-class-query)
76959b77 6413 store special-class class-alist)
f32b3b91
CD
6414 (cond
6415 ((null class) nil)
6416 ((eq t class)
6417 ;; There is an object which would like to know its class
6418 (if (and arrow (get-text-property apos 'idlwave-class)
6419 idlwave-store-inquired-class
6420 (not force-query))
6421 (setq class (get-text-property apos 'idlwave-class)
6422 class (idlwave-sintern-class class)))
76959b77
S
6423 (if (and (eq t class) is-self)
6424 (setq class (or (nth 2 (idlwave-current-routine)) class)))
6425
6426 ;; Before prompting, try any special class determination routines
4b1aaa8b 6427 (when (and (eq t class)
76959b77
S
6428 idlwave-determine-class-special
6429 (not force-query))
4b1aaa8b 6430 (setq special-class
76959b77 6431 (idlwave-call-special idlwave-determine-class-special apos))
4b1aaa8b 6432 (if special-class
76959b77
S
6433 (setq class (idlwave-sintern-class special-class)
6434 store idlwave-store-inquired-class)))
4b1aaa8b 6435
76959b77 6436 ;; Prompt for a class, if we need to
f32b3b91
CD
6437 (when (and (eq class t)
6438 (or force-query query))
4b1aaa8b 6439 (setq class-alist
f32b3b91
CD
6440 (mapcar 'list (idlwave-all-method-classes (car info) type)))
6441 (setq class
6442 (idlwave-sintern-class
6443 (cond
6444 ((and (= (length class-alist) 0) (not force-query))
6445 (error "No classes available with method %s" (car info)))
6446 ((and (= (length class-alist) 1) (not force-query))
6447 (car (car class-alist)))
4b1aaa8b 6448 (t
f32b3b91 6449 (setq store idlwave-store-inquired-class)
4b1aaa8b 6450 (idlwave-completing-read
f32b3b91
CD
6451 (format "Class%s: " (if (stringp (car info))
6452 (format " for %s method %s"
6453 type (car info))
6454 ""))
6455 class-alist nil nil nil 'idlwave-class-history))))))
76959b77
S
6456
6457 ;; Store it, if requested
f32b3b91
CD
6458 (when (and class (not (eq t class)))
6459 ;; We have a real class here
6460 (when (and store arrow)
76959b77 6461 (condition-case ()
4b1aaa8b
PE
6462 (add-text-properties
6463 apos (+ apos 2)
6464 `(idlwave-class ,class face ,idlwave-class-arrow-face
76959b77
S
6465 rear-nonsticky t))
6466 (error nil)))
f32b3b91
CD
6467 (setf (nth 2 info) class))
6468 ;; Return the class
6469 class)
6470 ;; Default as fallback
6471 (t class))))
6472
6473(defvar type-selector)
6474(defvar class-selector)
6475(defvar method-selector)
05a1abfc 6476(defvar super-classes)
f32b3b91
CD
6477(defun idlwave-selector (a)
6478 (and (eq (nth 1 a) type-selector)
6479 (or (and (nth 2 a) (eq class-selector t))
05a1abfc 6480 (eq (nth 2 a) class-selector)
52a244eb
S
6481 (memq (nth 2 a) super-classes))))
6482
6483(defun idlwave-add-file-link-selector (a)
6484 ;; Record a file link, if any, for the tested names during selection.
6485 (let ((sel (idlwave-selector a)) file)
6486 (if (and sel (setq file (idlwave-entry-has-help a)))
6487 (push (cons (car a) file) idlwave-completion-help-links))
6488 sel))
6489
f32b3b91
CD
6490
6491(defun idlwave-where ()
4b1aaa8b 6492 "Find out where we are.
f32b3b91 6493The return value is a list with the following stuff:
5e72c6b2 6494\(PRO-LIST FUNC-LIST COMPLETE-WHAT CW-LIST LAST-CHAR)
f32b3b91
CD
6495
6496PRO-LIST (PRO POINT CLASS ARROW)
6497FUNC-LIST (FUNC POINT CLASS ARROW)
6498COMPLETE-WHAT a symbol indicating what kind of completion makes sense here
4b1aaa8b 6499CW-LIST (PRO-OR-FUNC POINT CLASS ARROW) Like PRO-LIST, for what can
5e72c6b2 6500 be completed here.
f32b3b91
CD
6501LAST-CHAR last relevant character before point (non-white non-comment,
6502 not part of current identifier or leading slash).
6503
6504In the lists, we have these meanings:
6505PRO: Procedure name
6506FUNC: Function name
6507POINT: Where is this
6508CLASS: What class has the routine (nil=no, t=is method, but class unknown)
5e72c6b2 6509ARROW: Location of the arrow"
f32b3b91 6510 (idlwave-routines)
4b1aaa8b 6511 (let* (;(bos (save-excursion (idlwave-beginning-of-statement) (point)))
15e42531 6512 (bos (save-excursion (idlwave-start-of-substatement 'pre) (point)))
f32b3b91
CD
6513 (func-entry (idlwave-what-function bos))
6514 (func (car func-entry))
6515 (func-class (nth 1 func-entry))
6516 (func-arrow (nth 2 func-entry))
6517 (func-point (or (nth 3 func-entry) 0))
6518 (func-level (or (nth 4 func-entry) 0))
6519 (pro-entry (idlwave-what-procedure bos))
6520 (pro (car pro-entry))
6521 (pro-class (nth 1 pro-entry))
6522 (pro-arrow (nth 2 pro-entry))
6523 (pro-point (or (nth 3 pro-entry) 0))
6524 (last-char (idlwave-last-valid-char))
6525 (case-fold-search t)
52a244eb 6526 (match-string (buffer-substring bos (point)))
f32b3b91
CD
6527 cw cw-mod cw-arrow cw-class cw-point)
6528 (if (< func-point pro-point) (setq func nil))
6529 (cond
15e42531 6530 ((string-match "\\`[ \t]*\\(pro\\|function\\)[ \t]+[a-zA-Z0-9_]*\\'"
52a244eb 6531 match-string)
15e42531 6532 (setq cw 'class))
4b1aaa8b
PE
6533 ((string-match
6534 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'"
52a244eb
S
6535 (if (> pro-point 0)
6536 (buffer-substring pro-point (point))
6537 match-string))
f32b3b91
CD
6538 (setq cw 'procedure cw-class pro-class cw-point pro-point
6539 cw-arrow pro-arrow))
6540 ((string-match "\\`[ \t]*\\(pro\\|function\\)\\>"
52a244eb 6541 match-string)
f32b3b91 6542 nil)
05a1abfc 6543 ((string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6544 match-string)
4b1aaa8b 6545 (setq cw 'class))
05a1abfc 6546 ((string-match "\\<inherits\\s-+\\([a-zA-Z0-9$_]*\\)?\\'"
52a244eb 6547 match-string)
4b1aaa8b
PE
6548 (setq cw 'class))
6549 ((and func
f32b3b91
CD
6550 (> func-point pro-point)
6551 (= func-level 1)
6552 (memq last-char '(?\( ?,)))
6553 (setq cw 'function-keyword cw-mod func cw-point func-point
6554 cw-class func-class cw-arrow func-arrow))
6555 ((and pro (eq last-char ?,))
6556 (setq cw 'procedure-keyword cw-mod pro cw-point pro-point
6557 cw-class pro-class cw-arrow pro-arrow))
6558; ((member last-char '(?\' ?\) ?\] ?!))
6559; ;; after these chars, a function makes no sense
6560; ;; FIXME: I am sure there can be more in this list
6561; ;; FIXME: Do we want to do this at all?
6562; nil)
6563 ;; Everywhere else we try a function.
6564 (t
6565 (setq cw 'function)
6566 (save-excursion
52a244eb 6567 (if (re-search-backward "->[ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\s-*\\)?\\(\\([$a-zA-Z0-9_]+\\)::\\)?[$a-zA-Z0-9_]*\\=" bos t)
76959b77 6568 (setq cw-arrow (copy-marker (match-beginning 0))
52a244eb
S
6569 cw-class (if (match-end 4)
6570 (idlwave-sintern-class (match-string 4))
5e72c6b2 6571 t))))))
f32b3b91
CD
6572 (list (list pro pro-point pro-class pro-arrow)
6573 (list func func-point func-class func-arrow)
6574 cw
6575 (list cw-mod cw-point cw-class cw-arrow)
6576 last-char)))
6577
6578(defun idlwave-this-word (&optional class)
6579 ;; Grab the word around point. CLASS is for the `skip-chars=...' functions
52a244eb 6580 (setq class (or class "a-zA-Z0-9$_."))
f32b3b91 6581 (save-excursion
52a244eb 6582 (buffer-substring
f32b3b91
CD
6583 (progn (skip-chars-backward class) (point))
6584 (progn (skip-chars-forward class) (point)))))
6585
f32b3b91
CD
6586(defun idlwave-what-function (&optional bound)
6587 ;; Find out if point is within the argument list of a function.
76959b77
S
6588 ;; The return value is ("function-name" class arrow-start (point) level).
6589 ;; Level is 1 on the top level parentheses, higher further down.
f32b3b91
CD
6590
6591 ;; If the optional BOUND is an integer, bound backwards directed
6592 ;; searches to this point.
6593
6594 (catch 'exit
4b1aaa8b 6595 (let (pos
f32b3b91 6596 func-point
f32b3b91
CD
6597 (cnt 0)
6598 func arrow-start class)
15e42531
CD
6599 (idlwave-with-special-syntax
6600 (save-restriction
6601 (save-excursion
6602 (narrow-to-region (max 1 (or bound 0)) (point-max))
6603 ;; move back out of the current parenthesis
6604 (while (condition-case nil
6605 (progn (up-list -1) t)
6606 (error nil))
6607 (setq pos (point))
6608 (incf cnt)
6609 (when (and (= (following-char) ?\()
4b1aaa8b 6610 (re-search-backward
15e42531
CD
6611 "\\(::\\|\\<\\)\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\="
6612 bound t))
6613 (setq func (match-string 2)
6614 func-point (goto-char (match-beginning 2))
6615 pos func-point)
4b1aaa8b 6616 (if (re-search-backward
15e42531 6617 "->[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\=" bound t)
76959b77 6618 (setq arrow-start (copy-marker (match-beginning 0))
15e42531 6619 class (or (match-string 2) t)))
4b1aaa8b
PE
6620 (throw
6621 'exit
15e42531
CD
6622 (list
6623 (idlwave-sintern-routine-or-method func class)
6624 (idlwave-sintern-class class)
6625 arrow-start func-point cnt)))
6626 (goto-char pos))
6627 (throw 'exit nil)))))))
f32b3b91
CD
6628
6629(defun idlwave-what-procedure (&optional bound)
6630 ;; Find out if point is within the argument list of a procedure.
6631 ;; The return value is ("procedure-name" class arrow-pos (point)).
6632
6633 ;; If the optional BOUND is an integer, bound backwards directed
6634 ;; searches to this point.
6635 (let ((pos (point)) pro-point
6636 pro class arrow-start string)
4b1aaa8b 6637 (save-excursion
05a1abfc 6638 ;;(idlwave-beginning-of-statement)
15e42531 6639 (idlwave-start-of-substatement 'pre)
f32b3b91 6640 (setq string (buffer-substring (point) pos))
4b1aaa8b 6641 (if (string-match
76959b77
S
6642 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\(,\\|\\'\\)" string)
6643 (setq pro (match-string 1 string)
6644 pro-point (+ (point) (match-beginning 1)))
f32b3b91
CD
6645 (if (and (idlwave-skip-object)
6646 (setq string (buffer-substring (point) pos))
4b1aaa8b
PE
6647 (string-match
6648 "\\`[ \t]*\\(->\\)[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\([a-zA-Z][a-zA-Z0-9$_]*\\)?[ \t]*\\(,\\|\\(\\$\\s *\\(;.*\\)?\\)?$\\)"
52a244eb 6649 string))
f32b3b91
CD
6650 (setq pro (if (match-beginning 4)
6651 (match-string 4 string))
6652 pro-point (if (match-beginning 4)
6653 (+ (point) (match-beginning 4))
6654 pos)
76959b77 6655 arrow-start (copy-marker (+ (point) (match-beginning 1)))
f32b3b91
CD
6656 class (or (match-string 3 string) t)))))
6657 (list (idlwave-sintern-routine-or-method pro class)
6658 (idlwave-sintern-class class)
6659 arrow-start
6660 pro-point)))
6661
6662(defun idlwave-skip-object ()
6663 ;; If there is an object at point, move over it and return t.
6664 (let ((pos (point)))
6665 (if (catch 'exit
6666 (save-excursion
6667 (skip-chars-forward " ") ; white space
6668 (skip-chars-forward "*") ; de-reference
6669 (cond
6670 ((looking-at idlwave-identifier)
6671 (goto-char (match-end 0)))
6672 ((eq (following-char) ?\()
6673 nil)
6674 (t (throw 'exit nil)))
6675 (catch 'endwhile
6676 (while t
6677 (cond ((eq (following-char) ?.)
6678 (forward-char 1)
6679 (if (not (looking-at idlwave-identifier))
6680 (throw 'exit nil))
6681 (goto-char (match-end 0)))
6682 ((memq (following-char) '(?\( ?\[))
6683 (condition-case nil
6684 (forward-list 1)
6685 (error (throw 'exit nil))))
6686 (t (throw 'endwhile t)))))
6687 (if (looking-at "[ \t]*->")
6688 (throw 'exit (setq pos (match-beginning 0)))
6689 (throw 'exit nil))))
6690 (goto-char pos)
6691 nil)))
4b1aaa8b 6692
f32b3b91
CD
6693(defun idlwave-last-valid-char ()
6694 "Return the last character before point which is not white or a comment
6695and also not part of the current identifier. Since we do this in
6696order to identify places where keywords are, we consider the initial
6697`/' of a keyword as part of the identifier.
6698This function is not general, can only be used for completion stuff."
6699 (catch 'exit
6700 (save-excursion
6701 ;; skip the current identifier
6702 (skip-chars-backward "a-zA-Z0-9_$")
6703 ;; also skip a leading slash which might be belong to the keyword
6704 (if (eq (preceding-char) ?/)
6705 (backward-char 1))
6706 ;; FIXME: does not check if this is a valid identifier
6707 (while t
6708 (skip-chars-backward " \t")
6709 (cond
6710 ((memq (preceding-char) '(?\; ?\$)) (throw 'exit nil))
6711 ((eq (preceding-char) ?\n)
6712 (beginning-of-line 0)
3938cb82 6713 (if (looking-at "\\([^\n]*\\)\\$[ \t]*\\(;[^\n]*\\)?\n")
f32b3b91
CD
6714 ;; continuation line
6715 (goto-char (match-end 1))
6716 (throw 'exit nil)))
6717 (t (throw 'exit (preceding-char))))))))
6718
6719(defvar idlwave-complete-after-success-form nil
6720 "A form to evaluate after successful completion.")
6721(defvar idlwave-complete-after-success-form-force nil
6722 "A form to evaluate after completion selection in *Completions* buffer.")
6723(defconst idlwave-completion-mark (make-marker)
6724 "A mark pointing to the beginning of the completion string.")
6725
6726(defun idlwave-complete-in-buffer (type stype list selector prompt isa
52a244eb
S
6727 &optional prepare-display-function
6728 special-selector)
f32b3b91 6729 "Perform TYPE completion of word before point against LIST.
76959b77 6730SELECTOR is the PREDICATE argument for the completion function. Show
52a244eb
S
6731PROMPT in echo area. TYPE is one of the intern types, e.g. 'function,
6732'procedure, 'class-tag, 'keyword, 'sysvar, etc.. SPECIAL-SELECTOR is
6733used only once, for `all-completions', and can be used to, e.g.,
6734accumulate information on matching completions."
f32b3b91
CD
6735 (let* ((completion-ignore-case t)
6736 beg (end (point)) slash part spart completion all-completions
6737 dpart dcompletion)
6738
6739 (unless list
6740 (error (concat prompt ": No completions available")))
6741
6742 ;; What is already in the buffer?
6743 (save-excursion
6744 (skip-chars-backward "a-zA-Z0-9_$")
6745 (setq slash (eq (preceding-char) ?/)
6746 beg (point)
6747 idlwave-complete-after-success-form
6748 (list 'idlwave-after-successful-completion
6749 (list 'quote type) slash beg)
6750 idlwave-complete-after-success-form-force
6751 (list 'idlwave-after-successful-completion
6752 (list 'quote type) slash (list 'quote 'force))))
6753
6754 ;; Try a completion
6755 (setq part (buffer-substring beg end)
6756 dpart (downcase part)
6757 spart (idlwave-sintern stype part)
6758 completion (try-completion part list selector)
52a244eb
S
6759 dcompletion (if (stringp completion) (downcase completion))
6760 idlwave-completion-help-links nil)
f32b3b91
CD
6761 (cond
6762 ((null completion)
6763 ;; nothing available.
76959b77 6764 (error (concat prompt ": no completion for \"%s\"") part))
f32b3b91
CD
6765 ((and (not (equal dpart dcompletion))
6766 (not (eq t completion)))
6767 ;; We can add something
6768 (delete-region beg end)
6769 (if (and (string= part dpart)
6770 (or (not (string= part ""))
6771 idlwave-complete-empty-string-as-lower-case)
6772 (not idlwave-completion-force-default-case))
6773 (insert dcompletion)
6774 (insert completion))
6775 (if (eq t (try-completion completion list selector))
6776 ;; Now this is a unique match
6777 (idlwave-after-successful-completion type slash beg))
6778 t)
6779 ((or (eq completion t)
52a244eb 6780 (and (= 1 (length (setq all-completions
f32b3b91 6781 (idlwave-uniquify
4b1aaa8b
PE
6782 (all-completions part list
6783 (or special-selector
52a244eb
S
6784 selector))))))
6785 (equal dpart dcompletion)))
f32b3b91
CD
6786 ;; This is already complete
6787 (idlwave-after-successful-completion type slash beg)
6788 (message "%s is already the complete %s" part isa)
6789 nil)
4b1aaa8b 6790 (t
f32b3b91
CD
6791 ;; We cannot add something - offer a list.
6792 (message "Making completion list...")
4b1aaa8b 6793
52a244eb 6794 (unless idlwave-completion-help-links ; already set somewhere?
9001c33f
GM
6795 (mapc (lambda (x) ; Pass link prop through to highlight-linked
6796 (let ((link (get-text-property 0 'link (car x))))
6797 (if link
6798 (push (cons (car x) link)
6799 idlwave-completion-help-links))))
6800 list))
f32b3b91 6801 (let* ((list all-completions)
05a1abfc 6802 ;; "complete" means, this is already a valid completion
f32b3b91 6803 (complete (memq spart all-completions))
52a244eb
S
6804 (completion-highlight-first-word-only t)) ; XEmacs
6805; (completion-fixup-function ; Emacs
6806; (lambda () (and (eq (preceding-char) ?>)
6807; (re-search-backward " <" beg t)))))
4b1aaa8b 6808
f32b3b91
CD
6809 (setq list (sort list (lambda (a b)
6810 (string< (downcase a) (downcase b)))))
6811 (if prepare-display-function
6812 (setq list (funcall prepare-display-function list)))
6813 (if (and (string= part dpart)
6814 (or (not (string= part ""))
6815 idlwave-complete-empty-string-as-lower-case)
6816 (not idlwave-completion-force-default-case))
6817 (setq list (mapcar (lambda (x)
4b1aaa8b 6818 (if (listp x)
f32b3b91
CD
6819 (setcar x (downcase (car x)))
6820 (setq x (downcase x)))
6821 x)
6822 list)))
6823 (idlwave-display-completion-list list prompt beg complete))
6824 t))))
6825
6826(defun idlwave-complete-class ()
6827 "Complete a class at point."
6828 (interactive)
6829 ;; Call `idlwave-routines' to make sure the class list will be available
6830 (idlwave-routines)
15e42531
CD
6831 ;; Check for the special case of completing empty string after pro/function
6832 (if (let ((case-fold-search t))
6833 (save-excursion
6834 (and
6835 (re-search-backward "\\<\\(pro\\|function\\)[ \t]+\\="
6836 (- (point) 15) t)
6837 (goto-char (point-min))
4b1aaa8b 6838 (re-search-forward
15e42531
CD
6839 "^[ \t]*\\(pro\\|function\\)[ \t]+\\([a-zA-Z0-9_]+::\\)" nil t))))
6840 ;; Yank the full class specification
6841 (insert (match-string 2))
52a244eb 6842 ;; Do the completion, using list gathered from `idlwave-routines'
4b1aaa8b
PE
6843 (idlwave-complete-in-buffer
6844 'class 'class (idlwave-class-alist) nil
52a244eb
S
6845 "Select a class" "class"
6846 '(lambda (list) ;; Push it to help-links if system help available
6847 (mapcar (lambda (x)
6848 (let* ((entry (idlwave-class-info x))
6849 (link (nth 1 (assq 'link entry))))
4b1aaa8b 6850 (if link (push (cons x link)
52a244eb
S
6851 idlwave-completion-help-links))
6852 x))
6853 list)))))
f32b3b91 6854
76959b77 6855(defun idlwave-attach-classes (list type show-classes)
05a1abfc 6856 ;; Attach the proper class list to a LIST of completion items.
76959b77
S
6857 ;; TYPE, when 'kwd, shows classes for method keywords, when
6858 ;; 'class-tag, for class tags, and otherwise for methods.
f32b3b91 6859 ;; SHOW-CLASSES is the value of `idlwave-completion-show-classes'.
76959b77
S
6860 (if (or (null show-classes) ; don't want to see classes
6861 (null class-selector) ; not a method call
4b1aaa8b 6862 (and
76959b77
S
6863 (stringp class-selector) ; the class is already known
6864 (not super-classes))) ; no possibilities for inheritance
6865 ;; In these cases, we do not have to do anything
6866 list
05a1abfc
CD
6867 (let* ((do-prop (and (>= show-classes 0)
6868 (>= emacs-major-version 21)))
f32b3b91 6869 (do-buf (not (= show-classes 0)))
76959b77 6870 ;; (do-dots (featurep 'xemacs))
05a1abfc 6871 (do-dots t)
76959b77 6872 (inherit (if (and (not (eq type 'class-tag)) super-classes)
05a1abfc 6873 (cons class-selector super-classes)))
f32b3b91
CD
6874 (max (abs show-classes))
6875 (lmax (if do-dots (apply 'max (mapcar 'length list))))
6876 classes nclasses class-info space)
4b1aaa8b 6877 (mapcar
f32b3b91
CD
6878 (lambda (x)
6879 ;; get the classes
76959b77
S
6880 (if (eq type 'class-tag)
6881 ;; Just one class for tags
6882 (setq classes
4b1aaa8b 6883 (list
76959b77 6884 (idlwave-class-or-superclass-with-tag class-selector x)))
52a244eb 6885 ;; Multiple classes for method or method-keyword
76959b77
S
6886 (setq classes
6887 (if (eq type 'kwd)
6888 (idlwave-all-method-keyword-classes
6889 method-selector x type-selector)
6890 (idlwave-all-method-classes x type-selector)))
6891 (if inherit
4b1aaa8b 6892 (setq classes
76959b77
S
6893 (delq nil
6894 (mapcar (lambda (x) (if (memq x inherit) x nil))
6895 classes)))))
f32b3b91
CD
6896 (setq nclasses (length classes))
6897 ;; Make the separator between item and class-info
6898 (if do-dots
6899 (setq space (concat " " (make-string (- lmax (length x)) ?.)))
6900 (setq space " "))
6901 (if do-buf
6902 ;; We do want info in the buffer
6903 (if (<= nclasses max)
6904 (setq class-info (concat
6905 space
6906 "<" (mapconcat 'identity classes ",") ">"))
6907 (setq class-info (format "%s<%d classes>" space nclasses)))
6908 (setq class-info nil))
6909 (when do-prop
6910 ;; We do want properties
6911 (setq x (copy-sequence x))
6912 (put-text-property 0 (length x)
52a244eb
S
6913 'help-echo (mapconcat 'identity classes " ")
6914 x))
f32b3b91
CD
6915 (if class-info
6916 (list x class-info)
6917 x))
6918 list))))
6919
6920(defun idlwave-attach-method-classes (list)
6921 ;; Call idlwave-attach-classes with method parameters
76959b77 6922 (idlwave-attach-classes list 'method idlwave-completion-show-classes))
f32b3b91
CD
6923(defun idlwave-attach-keyword-classes (list)
6924 ;; Call idlwave-attach-classes with keyword parameters
76959b77
S
6925 (idlwave-attach-classes list 'kwd idlwave-completion-show-classes))
6926(defun idlwave-attach-class-tag-classes (list)
6927 ;; Call idlwave-attach-classes with class structure tags
6928 (idlwave-attach-classes list 'class-tag idlwave-completion-show-classes))
4b1aaa8b 6929
f32b3b91
CD
6930
6931;;----------------------------------------------------------------------
6932;;----------------------------------------------------------------------
6933;;----------------------------------------------------------------------
6934;;----------------------------------------------------------------------
6935;;----------------------------------------------------------------------
5e72c6b2
S
6936(defvar rtn)
6937(defun idlwave-pset (item)
6938 (set 'rtn item))
6939
6940(defun idlwave-popup-select (ev list title &optional sort)
6941 "Select an item in LIST with a popup menu.
6942TITLE is the title to put atop the popup. If SORT is non-nil,
6943sort the list before displaying"
6944 (let ((maxpopup idlwave-max-popup-menu-items)
6945 rtn menu resp)
6946 (cond ((null list))
6947 ((= 1 (length list))
6948 (setq rtn (car list)))
6949 ((featurep 'xemacs)
4b1aaa8b 6950 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6951 (string< (upcase a) (upcase b))))))
6952 (setq menu
6953 (append (list title)
6954 (mapcar (lambda (x) (vector x (list 'idlwave-pset
6955 x)))
6956 list)))
6957 (setq menu (idlwave-split-menu-xemacs menu maxpopup))
6958 (setq resp (get-popup-menu-response menu))
6959 (funcall (event-function resp) (event-object resp)))
6960 (t
4b1aaa8b 6961 (if sort (setq list (sort list (lambda (a b)
5e72c6b2
S
6962 (string< (upcase a) (upcase b))))))
6963 (setq menu (cons title
6964 (list
6965 (append (list "")
6966 (mapcar (lambda(x) (cons x x)) list)))))
6967 (setq menu (idlwave-split-menu-emacs menu maxpopup))
6968 (setq rtn (x-popup-menu ev menu))))
6969 rtn))
6970
6971(defun idlwave-split-menu-xemacs (menu N)
6972 "Split the MENU into submenus of maximum length N."
6973 (if (<= (length menu) (1+ N))
6974 ;; No splitting needed
6975 menu
6976 (let* ((title (car menu))
6977 (entries (cdr menu))
6978 (menu (list title))
6979 (cnt 0)
6980 (nextmenu nil))
6981 (while entries
6982 (while (and entries (< cnt N))
6983 (setq cnt (1+ cnt)
6984 nextmenu (cons (car entries) nextmenu)
6985 entries (cdr entries)))
6986 (setq nextmenu (nreverse nextmenu))
6987 (setq nextmenu (cons (format "%s...%s"
6988 (aref (car nextmenu) 0)
6989 (aref (nth (1- cnt) nextmenu) 0))
6990 nextmenu))
6991 (setq menu (cons nextmenu menu)
6992 nextmenu nil
6993 cnt 0))
6994 (nreverse menu))))
6995
6996(defun idlwave-split-menu-emacs (menu N)
6997 "Split the MENU into submenus of maximum length N."
6998 (if (<= (length (nth 1 menu)) (1+ N))
6999 ;; No splitting needed
7000 menu
7001 (let* ((title (car menu))
7002 (entries (cdr (nth 1 menu)))
7003 (menu nil)
7004 (cnt 0)
7005 (nextmenu nil))
7006 (while entries
7007 (while (and entries (< cnt N))
7008 (setq cnt (1+ cnt)
7009 nextmenu (cons (car entries) nextmenu)
7010 entries (cdr entries)))
7011 (setq nextmenu (nreverse nextmenu))
7012 (prin1 nextmenu)
7013 (setq nextmenu (cons (format "%s...%s"
7014 (car (car nextmenu))
7015 (car (nth (1- cnt) nextmenu)))
7016 nextmenu))
7017 (setq menu (cons nextmenu menu)
7018 nextmenu nil
7019 cnt 0))
7020 (setq menu (nreverse menu))
7021 (setq menu (cons title menu))
7022 menu)))
f32b3b91 7023
15e42531
CD
7024(defvar idlwave-completion-setup-hook nil)
7025
f32b3b91
CD
7026(defun idlwave-scroll-completions (&optional message)
7027 "Scroll the completion window on this frame."
7028 (let ((cwin (get-buffer-window "*Completions*" 'visible))
7029 (win (selected-window)))
7030 (unwind-protect
7031 (progn
7032 (select-window cwin)
7033 (condition-case nil
7034 (scroll-up)
7035 (error (if (and (listp last-command)
7036 (nth 2 last-command))
7037 (progn
7038 (select-window win)
7039 (eval idlwave-complete-after-success-form))
7040 (set-window-start cwin (point-min)))))
274f1353 7041 (and message (message "%s" message)))
f32b3b91
CD
7042 (select-window win))))
7043
7044(defun idlwave-display-completion-list (list &optional message beg complete)
7045 "Display the completions in LIST in the completions buffer and echo MESSAGE."
7046 (unless (and (get-buffer-window "*Completions*")
7047 (idlwave-local-value 'idlwave-completion-p "*Completions*"))
7048 (move-marker idlwave-completion-mark beg)
7049 (setq idlwave-before-completion-wconf (current-window-configuration)))
7050
7051 (if (featurep 'xemacs)
4b1aaa8b 7052 (idlwave-display-completion-list-xemacs
15e42531 7053 list)
f32b3b91
CD
7054 (idlwave-display-completion-list-emacs list))
7055
7056 ;; Store a special value in `this-command'. When `idlwave-complete'
7057 ;; finds this in `last-command', it will scroll the *Completions* buffer.
7058 (setq this-command (list 'idlwave-display-completion-list message complete))
7059
7060 ;; Mark the completions buffer as created by cib
7061 (idlwave-set-local 'idlwave-completion-p t "*Completions*")
7062
7063 ;; Fontify the classes
7064 (if (and idlwave-completion-fontify-classes
7065 (consp (car list)))
7066 (idlwave-completion-fontify-classes))
7067
15e42531
CD
7068 ;; Run the hook
7069 (run-hooks 'idlwave-completion-setup-hook)
7070
f32b3b91 7071 ;; Display the message
274f1353 7072 (message "%s" (or message "Making completion list...done")))
f32b3b91
CD
7073
7074(defun idlwave-choose (function &rest args)
7075 "Call FUNCTION as a completion chooser and pass ARGS to it."
7076 (let ((completion-ignore-case t)) ; install correct value
7077 (apply function args))
15e42531
CD
7078 (if (and (eq major-mode 'idlwave-shell-mode)
7079 (boundp 'font-lock-mode)
7080 (not font-lock-mode))
52a244eb 7081 ;; For the shell, remove the fontification of the word before point
15e42531
CD
7082 (let ((beg (save-excursion
7083 (skip-chars-backward "a-zA-Z0-9_")
7084 (point))))
7085 (remove-text-properties beg (point) '(face nil))))
f32b3b91
CD
7086 (eval idlwave-complete-after-success-form-force))
7087
76959b77
S
7088(defun idlwave-keyboard-quit ()
7089 (interactive)
7090 (unwind-protect
7091 (if (eq (car-safe last-command) 'idlwave-display-completion-list)
7092 (idlwave-restore-wconf-after-completion))
7093 (keyboard-quit)))
7094
f32b3b91
CD
7095(defun idlwave-restore-wconf-after-completion ()
7096 "Restore the old (before completion) window configuration."
7097 (and idlwave-completion-restore-window-configuration
7098 idlwave-before-completion-wconf
7099 (set-window-configuration idlwave-before-completion-wconf)))
7100
52a244eb
S
7101(defun idlwave-one-key-select (sym prompt delay)
7102 "Make the user select an element from the alist in the variable SYM.
7103The keys of the alist are expected to be strings. The function returns the
7104car of the selected association.
d9271f41 7105To do this, PROMPT is displayed and the user must hit a letter key to
52a244eb
S
7106select an entry. If the user does not reply within DELAY seconds, a help
7107window with the options is displayed automatically.
7108The key which is associated with each option is generated automatically.
7109First, the strings are checked for preselected keys, like in \"[P]rint\".
7110If these don't exist, a letter in the string is automatically selected."
7111 (let* ((alist (symbol-value sym))
7112 (temp-buffer-show-hook (if (fboundp 'fit-window-to-buffer)
7113 '(fit-window-to-buffer)))
7114 keys-alist char)
7115 ;; First check the cache
7116 (if (and (eq (symbol-value sym) (get sym :one-key-alist-last)))
7117 (setq keys-alist (get sym :one-key-alist-cache))
7118 ;; Need to make new list
7119 (setq keys-alist (idlwave-make-one-key-alist alist))
7120 (put sym :one-key-alist-cache keys-alist)
7121 (put sym :one-key-alist-last alist))
7122 ;; Display prompt and wait for quick reply
7123 (message "%s[%s]" prompt
7124 (mapconcat (lambda(x) (char-to-string (car x)))
7125 keys-alist ""))
7126 (if (sit-for delay)
7127 ;; No quick reply: Show help
7128 (save-window-excursion
7129 (with-output-to-temp-buffer "*Completions*"
7130 (mapcar (lambda(x)
7131 (princ (nth 1 x))
7132 (princ "\n"))
4b1aaa8b 7133 keys-alist))
52a244eb
S
7134 (setq char (read-char)))
7135 (setq char (read-char)))
7136 (message nil)
7137 ;; Return the selected result
7138 (nth 2 (assoc char keys-alist))))
7139
7140;; Used for, e.g., electric debug super-examine.
7141(defun idlwave-make-one-key-alist (alist)
7142 "Make an alist for single key selection."
7143 (let ((l alist) keys-alist name start char help
7144 (cnt 0)
7145 (case-fold-search nil))
7146 (while l
7147 (setq name (car (car l))
7148 l (cdr l))
7149 (catch 'exit
7150 ;; First check if the configuration predetermined a key
7151 (if (string-match "\\[\\(.\\)\\]" name)
7152 (progn
7153 (setq char (string-to-char (downcase (match-string 1 name)))
7154 help (format "%c: %s" char name)
7155 keys-alist (cons (list char help name) keys-alist))
7156 (throw 'exit t)))
7157 ;; Then check for capital letters
7158 (setq start 0)
7159 (while (string-match "[A-Z]" name start)
7160 (setq start (match-end 0)
7161 char (string-to-char (downcase (match-string 0 name))))
7162 (if (not (assoc char keys-alist))
7163 (progn
7164 (setq help (format "%c: %s" char
7165 (replace-match
7166 (concat "[" (match-string 0 name) "]")
7167 t t name))
7168 keys-alist (cons (list char help name) keys-alist))
7169 (throw 'exit t))))
7170 ;; Now check for lowercase letters
7171 (setq start 0)
7172 (while (string-match "[a-z]" name start)
7173 (setq start (match-end 0)
7174 char (string-to-char (match-string 0 name)))
7175 (if (not (assoc char keys-alist))
7176 (progn
7177 (setq help (format "%c: %s" char
7178 (replace-match
7179 (concat "[" (match-string 0 name) "]")
7180 t t name))
7181 keys-alist (cons (list char help name) keys-alist))
7182 (throw 'exit t))))
7183 ;; Bummer, nothing found! Use a stupid number
7184 (setq char (string-to-char (int-to-string (setq cnt (1+ cnt))))
7185 help (format "%c: %s" char name)
7186 keys-alist (cons (list char help name) keys-alist))))
7187 (nreverse keys-alist)))
7188
f32b3b91
CD
7189(defun idlwave-set-local (var value &optional buffer)
7190 "Set the buffer-local value of VAR in BUFFER to VALUE."
7191 (save-excursion
7192 (set-buffer (or buffer (current-buffer)))
7193 (set (make-local-variable var) value)))
7194
7195(defun idlwave-local-value (var &optional buffer)
7196 "Return the value of VAR in BUFFER, but only if VAR is local to BUFFER."
7197 (save-excursion
7198 (set-buffer (or buffer (current-buffer)))
7199 (and (local-variable-p var (current-buffer))
7200 (symbol-value var))))
7201
15e42531
CD
7202;; In XEmacs, we can use :activate-callback directly to advice the
7203;; choose functions. We use the private keymap only for the online
7204;; help feature.
f32b3b91 7205
15e42531
CD
7206(defvar idlwave-completion-map nil
7207 "Keymap for completion-list-mode with idlwave-complete.")
7208
7209(defun idlwave-display-completion-list-xemacs (list &rest cl-args)
f32b3b91 7210 (with-output-to-temp-buffer "*Completions*"
15e42531
CD
7211 (apply 'display-completion-list list
7212 ':activate-callback 'idlwave-default-choose-completion
7213 cl-args))
7214 (save-excursion
7215 (set-buffer "*Completions*")
7216 (use-local-map
7217 (or idlwave-completion-map
7218 (setq idlwave-completion-map
7219 (idlwave-make-modified-completion-map-xemacs
7220 (current-local-map)))))))
f32b3b91
CD
7221
7222(defun idlwave-default-choose-completion (&rest args)
7223 "Execute `default-choose-completion' and then restore the win-conf."
7224 (apply 'idlwave-choose 'default-choose-completion args))
7225
15e42531
CD
7226(defun idlwave-make-modified-completion-map-xemacs (old-map)
7227 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7228 (let ((new-map (copy-keymap old-map)))
7229 (define-key new-map [button3up] 'idlwave-mouse-completion-help)
7230 (define-key new-map [button3] (lambda ()
7231 (interactive)
7232 (setq this-command last-command)))
7233 new-map))
f32b3b91 7234
76959b77 7235;; In Emacs we also replace keybindings in the completion
15e42531 7236;; map in order to install our wrappers.
f32b3b91
CD
7237
7238(defun idlwave-display-completion-list-emacs (list)
7239 "Display completion list and install the choose wrappers."
7240 (with-output-to-temp-buffer "*Completions*"
7241 (display-completion-list list))
7242 (save-excursion
7243 (set-buffer "*Completions*")
7244 (use-local-map
7245 (or idlwave-completion-map
7246 (setq idlwave-completion-map
15e42531
CD
7247 (idlwave-make-modified-completion-map-emacs
7248 (current-local-map)))))))
7249
7250(defun idlwave-make-modified-completion-map-emacs (old-map)
f32b3b91
CD
7251 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
7252 (let ((new-map (copy-keymap old-map)))
4b1aaa8b 7253 (substitute-key-definition
f32b3b91
CD
7254 'choose-completion 'idlwave-choose-completion new-map)
7255 (substitute-key-definition
7256 'mouse-choose-completion 'idlwave-mouse-choose-completion new-map)
15e42531 7257 (define-key new-map [mouse-3] 'idlwave-mouse-completion-help)
f32b3b91
CD
7258 new-map))
7259
7260(defun idlwave-choose-completion (&rest args)
7261 "Choose the completion that point is in or next to."
7262 (interactive)
7263 (apply 'idlwave-choose 'choose-completion args))
7264
7265(defun idlwave-mouse-choose-completion (&rest args)
7266 "Click on an alternative in the `*Completions*' buffer to choose it."
7267 (interactive "e")
7268 (apply 'idlwave-choose 'mouse-choose-completion args))
7269
7270;;----------------------------------------------------------------------
7271;;----------------------------------------------------------------------
7272
05a1abfc 7273;;; ------------------------------------------------------------------------
52a244eb 7274;;; Stucture parsing code, and code to manage class info
05a1abfc
CD
7275
7276;;
7277;; - Go again over the documentation how to write a completion
7278;; plugin. It is in self.el, but currently still very bad.
4b1aaa8b
PE
7279;; This could be in a separate file in the distribution, or
7280;; in an appendix for the manual.
52a244eb
S
7281
7282(defvar idlwave-struct-skip
7283 "[ \t]*\\(\\$.*\n\\(^[ \t]*\\(\\$[ \t]*\\)?\\(;.*\\)?\n\\)*\\)?[ \t]*"
7284 "Regexp for skipping continued blank or comment-only lines in
7285structures")
7286
7287(defvar idlwave-struct-tag-regexp
7288 (concat "[{,]" ;leading comma/brace
7289 idlwave-struct-skip ; 4 groups
7290 "\\([a-zA-Z][a-zA-Z0-9_]*\\)" ;the tag itself, group 5
7291 "[ \t]*:") ; the final colon
7292 "Regexp for structure tags.")
05a1abfc
CD
7293
7294(defun idlwave-struct-tags ()
7295 "Return a list of all tags in the structure defined at point.
7296Point is expected just before the opening `{' of the struct definition."
7297 (save-excursion
7298 (let* ((borders (idlwave-struct-borders))
7299 (beg (car borders))
7300 (end (cdr borders))
7301 tags)
7302 (goto-char beg)
52a244eb
S
7303 (save-restriction
7304 (narrow-to-region beg end)
7305 (while (re-search-forward idlwave-struct-tag-regexp end t)
7306 ;; Check if we are still on the top level of the structure.
7307 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7308 (= (point) beg))
7309 (push (match-string-no-properties 5) tags))
7310 (goto-char (match-end 0))))
7311 (nreverse tags))))
05a1abfc 7312
76959b77
S
7313(defun idlwave-find-struct-tag (tag)
7314 "Find a given TAG in the structure defined at point."
7315 (let* ((borders (idlwave-struct-borders))
7316 (beg (car borders))
7317 (end (cdr borders))
7318 (case-fold-search t))
4b1aaa8b 7319 (re-search-forward (concat "\\(^[ \t]*\\|[,{][ \t]*\\)" tag "[ \t]*:")
76959b77
S
7320 end t)))
7321
05a1abfc
CD
7322(defun idlwave-struct-inherits ()
7323 "Return a list of all `inherits' names in the struct at point.
7324Point is expected just before the opening `{' of the struct definition."
7325 (save-excursion
7326 (let* ((borders (idlwave-struct-borders))
7327 (beg (car borders))
7328 (end (cdr borders))
7329 (case-fold-search t)
7330 names)
7331 (goto-char beg)
52a244eb
S
7332 (save-restriction
7333 (narrow-to-region beg end)
4b1aaa8b 7334 (while (re-search-forward
52a244eb
S
7335 (concat "[{,]" ;leading comma/brace
7336 idlwave-struct-skip ; 4 groups
7337 "inherits" ; The INHERITS tag
7338 idlwave-struct-skip ; 4 more
7339 "\\([a-zA-Z][a-zA-Z0-9_]*\\)") ; The super-group, #9
7340 end t)
7341 ;; Check if we are still on the top level of the structure.
7342 (if (and (condition-case nil (progn (up-list -1) t) (error nil))
7343 (= (point) beg))
7344 (push (match-string-no-properties 9) names))
7345 (goto-char (match-end 0))))
05a1abfc
CD
7346 (nreverse names))))
7347
5e72c6b2 7348(defun idlwave-in-structure ()
52a244eb 7349 "Return t if point is inside an IDL structure definition."
5e72c6b2
S
7350 (let ((beg (point)))
7351 (save-excursion
7352 (if (not (or (idlwave-in-comment) (idlwave-in-quote)))
7353 (if (idlwave-find-structure-definition nil nil 'back)
7354 (let ((borders (idlwave-struct-borders)))
7355 (or (= (car borders) (cdr borders)) ;; struct not yet closed...
7356 (and (> beg (car borders)) (< beg (cdr borders))))))))))
05a1abfc
CD
7357
7358(defun idlwave-struct-borders ()
7359 "Return the borders of the {...} after point as a cons cell."
7360 (let (beg)
7361 (save-excursion
7362 (skip-chars-forward "^{")
7363 (setq beg (point))
7364 (condition-case nil (forward-list 1)
7365 (error (goto-char beg)))
7366 (cons beg (point)))))
7367
7368(defun idlwave-find-structure-definition (&optional var name bound)
52a244eb
S
7369 "Search forward for a structure definition. If VAR is non-nil,
7370search for a structure assigned to variable VAR. If NAME is non-nil,
7371search for a named structure NAME, if a string, or a generic named
7372structure otherwise. If BOUND is an integer, limit the search. If
7373BOUND is the symbol `all', we search first back and then forward
7374through the entire file. If BOUND is the symbol `back' we search only
7375backward."
76959b77 7376 (let* ((ws "[ \t]*\\(\\$.*\n[ \t]*\\)*")
05a1abfc
CD
7377 (case-fold-search t)
7378 (lim (if (integerp bound) bound nil))
7379 (re (concat
7380 (if var
7381 (concat "\\<" (regexp-quote (downcase var)) "\\>" ws)
7382 "\\(\\)")
7383 "=" ws "\\({\\)"
4b1aaa8b 7384 (if name
52a244eb 7385 (if (stringp name)
4b1aaa8b 7386 (concat ws "\\(\\<" (downcase name) "\\)[^a-zA-Z0-9_$]")
52a244eb
S
7387 ;; Just a generic name
7388 (concat ws "\\<\\([a-zA-Z_0-9$]+\\)" ws ","))
7389 ""))))
5e72c6b2 7390 (if (or (and (or (eq bound 'all) (eq bound 'back))
05a1abfc 7391 (re-search-backward re nil t))
5e72c6b2 7392 (and (not (eq bound 'back)) (re-search-forward re lim t)))
52a244eb
S
7393 (progn
7394 (goto-char (match-beginning 3))
7395 (match-string-no-properties 5)))))
7396
4b1aaa8b 7397(defvar idlwave-class-info nil)
52a244eb 7398(defvar idlwave-class-reset nil) ; to reset buffer-local classes
05a1abfc 7399
05a1abfc 7400(add-hook 'idlwave-update-rinfo-hook
52a244eb 7401 (lambda () (setq idlwave-class-reset t)))
05a1abfc
CD
7402(add-hook 'idlwave-after-load-rinfo-hook
7403 (lambda () (setq idlwave-class-info nil)))
7404
7405(defun idlwave-class-info (class)
7406 (let (list entry)
52a244eb
S
7407 (if idlwave-class-info
7408 (if idlwave-class-reset
4b1aaa8b 7409 (setq
52a244eb
S
7410 idlwave-class-reset nil
7411 idlwave-class-info ; Remove any visited in a buffer
4b1aaa8b
PE
7412 (delq nil (mapcar
7413 (lambda (x)
7414 (let ((filebuf
7415 (idlwave-class-file-or-buffer
52a244eb
S
7416 (or (cdr (assq 'found-in x)) (car x)))))
7417 (if (cdr filebuf)
7418 nil
7419 x)))
7420 idlwave-class-info))))
7421 ;; Info is nil, put in the system stuff to start.
05a1abfc
CD
7422 (setq idlwave-class-info idlwave-system-class-info)
7423 (setq list idlwave-class-info)
7424 (while (setq entry (pop list))
7425 (idlwave-sintern-class-info entry)))
7426 (setq class (idlwave-sintern-class class))
52a244eb
S
7427 (or (assq class idlwave-class-info)
7428 (progn (idlwave-scan-class-info class)
7429 (assq class idlwave-class-info)))))
05a1abfc
CD
7430
7431(defun idlwave-sintern-class-info (entry)
7432 "Sintern the class names in a class-info entry."
7433 (let ((taglist (assq 'tags entry))
7434 (inherits (assq 'inherits entry)))
7435 (setcar entry (idlwave-sintern-class (car entry) 'set))
7436 (if inherits
7437 (setcdr inherits (mapcar (lambda (x) (idlwave-sintern-class x 'set))
7438 (cdr inherits))))))
7439
52a244eb
S
7440(defun idlwave-find-class-definition (class &optional all-hook alt-class)
7441 "Find class structure definition(s)
7442If ALL-HOOK is set, find all named structure definitions in a given
7443class__define routine, on which ALL-HOOK will be run. If ALT-CLASS is
7444set, look for the name__define pro, and inside of it, for the ALT-CLASS
7445class/struct definition"
7446 (let ((case-fold-search t) end-lim list name)
7447 (when (re-search-forward
7448 (concat "^[ \t]*pro[ \t]+" (downcase class) "__define" "\\>") nil t)
7449 (if all-hook
7450 (progn
7451 ;; For everything there
7452 (setq end-lim (save-excursion (idlwave-end-of-subprogram) (point)))
4b1aaa8b 7453 (while (setq name
52a244eb
S
7454 (idlwave-find-structure-definition nil t end-lim))
7455 (funcall all-hook name)))
7456 (idlwave-find-structure-definition nil (or alt-class class))))))
76959b77 7457
52a244eb
S
7458
7459(defun idlwave-class-file-or-buffer (class)
7460 "Find buffer visiting CLASS definition"
05a1abfc 7461 (let* ((pro (concat (downcase class) "__define"))
52a244eb
S
7462 (file (idlwave-routine-source-file
7463 (nth 3 (idlwave-rinfo-assoc pro 'pro nil
7464 (idlwave-routines))))))
7465 (cons file (if file (idlwave-get-buffer-visiting file)))))
7466
7467
7468(defun idlwave-scan-class-info (class)
7469 "Scan all class and named structure info in the class__define pro"
7470 (let* ((idlwave-auto-routine-info-updates nil)
7471 (filebuf (idlwave-class-file-or-buffer class))
7472 (file (car filebuf))
7473 (buf (cdr filebuf))
7474 (class (idlwave-sintern-class class)))
7475 (if (or
7476 (not file)
7477 (and ;; neither a regular file nor a visited buffer
7478 (not buf)
7479 (not (file-regular-p file))))
7480 nil ; Cannot find the file/buffer to get any info
05a1abfc 7481 (save-excursion
52a244eb
S
7482 (if buf (set-buffer buf)
7483 ;; Read the file in temporarily
05a1abfc
CD
7484 (set-buffer (get-buffer-create " *IDLWAVE-tmp*"))
7485 (erase-buffer)
7486 (unless (eq major-mode 'idlwave-mode)
7487 (idlwave-mode))
7488 (insert-file-contents file))
7489 (save-excursion
7490 (goto-char 1)
4b1aaa8b 7491 (idlwave-find-class-definition class
52a244eb
S
7492 ;; Scan all of the structures found there
7493 (lambda (name)
7494 (let* ((this-class (idlwave-sintern-class name))
4b1aaa8b 7495 (entry
52a244eb
S
7496 (list this-class
7497 (cons 'tags (idlwave-struct-tags))
7498 (cons 'inherits (idlwave-struct-inherits)))))
7499 (if (not (eq this-class class))
7500 (setq entry (nconc entry (list (cons 'found-in class)))))
7501 (idlwave-sintern-class-info entry)
7502 (push entry idlwave-class-info)))))))))
7503
7504(defun idlwave-class-found-in (class)
7505 "Return the FOUND-IN property of the class."
7506 (cdr (assq 'found-in (idlwave-class-info class))))
05a1abfc
CD
7507(defun idlwave-class-tags (class)
7508 "Return the native tags in CLASS."
7509 (cdr (assq 'tags (idlwave-class-info class))))
7510(defun idlwave-class-inherits (class)
7511 "Return the direct superclasses of CLASS."
7512 (cdr (assq 'inherits (idlwave-class-info class))))
7513
52a244eb 7514
05a1abfc
CD
7515(defun idlwave-all-class-tags (class)
7516 "Return a list of native and inherited tags in CLASS."
76959b77
S
7517 (condition-case err
7518 (apply 'append (mapcar 'idlwave-class-tags
7519 (cons class (idlwave-all-class-inherits class))))
4b1aaa8b 7520 (error
76959b77
S
7521 (idlwave-class-tag-reset)
7522 (error "%s" (error-message-string err)))))
7523
05a1abfc
CD
7524
7525(defun idlwave-all-class-inherits (class)
7526 "Return a list of all superclasses of CLASS (recursively expanded).
5e72c6b2 7527The list is cached in `idlwave-class-info' for faster access."
05a1abfc
CD
7528 (cond
7529 ((not idlwave-support-inheritance) nil)
7530 ((eq class nil) nil)
7531 ((eq class t) nil)
7532 (t
7533 (let ((info (idlwave-class-info class))
7534 entry)
7535 (if (setq entry (assq 'all-inherits info))
7536 (cdr entry)
76959b77
S
7537 ;; Save the depth of inheritance scan to check for circular references
7538 (let ((inherits (mapcar (lambda (x) (cons x 0))
7539 (idlwave-class-inherits class)))
05a1abfc
CD
7540 rtn all-inherits cl)
7541 (while inherits
7542 (setq cl (pop inherits)
76959b77
S
7543 rtn (cons (car cl) rtn)
7544 inherits (append (mapcar (lambda (x)
7545 (cons x (1+ (cdr cl))))
7546 (idlwave-class-inherits (car cl)))
7547 inherits))
7548 (if (> (cdr cl) 999)
7549 (error
7550 "Class scan: inheritance depth exceeded. Circular inheritance?")
7551 ))
05a1abfc
CD
7552 (setq all-inherits (nreverse rtn))
7553 (nconc info (list (cons 'all-inherits all-inherits)))
7554 all-inherits))))))
7555
52a244eb 7556(defun idlwave-entry-keywords (entry &optional record-link)
4b1aaa8b 7557 "Return the flat entry keywords alist from routine-info entry.
52a244eb
S
7558If RECORD-LINK is non-nil, the keyword text is copied and a text
7559property indicating the link is added."
7560 (let (kwds)
8ffcfb27 7561 (mapc
4b1aaa8b 7562 (lambda (key-list)
52a244eb
S
7563 (let ((file (car key-list)))
7564 (mapcar (lambda (key-cons)
7565 (let ((key (car key-cons))
7566 (link (cdr key-cons)))
7567 (when (and record-link file)
7568 (setq key (copy-sequence key))
4b1aaa8b 7569 (put-text-property
52a244eb 7570 0 (length key)
4b1aaa8b
PE
7571 'link
7572 (concat
7573 file
7574 (if link
52a244eb
S
7575 (concat idlwave-html-link-sep
7576 (number-to-string link))))
7577 key))
7578 (push (list key) kwds)))
7579 (cdr key-list))))
7580 (nthcdr 5 entry))
7581 (nreverse kwds)))
7582
7583(defun idlwave-entry-find-keyword (entry keyword)
7584 "Find keyword KEYWORD in entry ENTRY, and return (with link) if set"
7585 (catch 'exit
7586 (mapc
4b1aaa8b 7587 (lambda (key-list)
52a244eb
S
7588 (let ((file (car key-list))
7589 (kwd (assoc keyword (cdr key-list))))
7590 (when kwd
4b1aaa8b 7591 (setq kwd (cons (car kwd)
52a244eb 7592 (if (and file (cdr kwd))
4b1aaa8b 7593 (concat file
52a244eb
S
7594 idlwave-html-link-sep
7595 (number-to-string (cdr kwd)))
7596 (cdr kwd))))
7597 (throw 'exit kwd))))
7598 (nthcdr 5 entry))))
05a1abfc
CD
7599
7600;;==========================================================================
7601;;
7602;; Completing class structure tags. This is a completion plugin.
7603;; The necessary taglist is constructed dynamically
7604
7605(defvar idlwave-current-tags-class nil)
7606(defvar idlwave-current-class-tags nil)
7607(defvar idlwave-current-native-class-tags nil)
76959b77 7608(defvar idlwave-sint-class-tags nil)
1a717047 7609(declare-function idlwave-sintern-class-tag "idlwave" t t)
76959b77 7610(idlwave-new-sintern-type 'class-tag)
05a1abfc 7611(add-to-list 'idlwave-complete-special 'idlwave-complete-class-structure-tag)
76959b77 7612(add-hook 'idlwave-update-rinfo-hook 'idlwave-class-tag-reset)
05a1abfc
CD
7613
7614(defun idlwave-complete-class-structure-tag ()
7615 "Complete a structure tag on a `self' argument in an object method."
7616 (interactive)
7617 (let ((pos (point))
7618 (case-fold-search t))
7619 (if (save-excursion
7620 ;; Check if the context is right
52a244eb 7621 (skip-chars-backward "a-zA-Z0-9._$")
05a1abfc
CD
7622 (and (< (point) (- pos 4))
7623 (looking-at "self\\.")))
76959b77
S
7624 (let* ((class-selector (nth 2 (idlwave-current-routine)))
7625 (super-classes (idlwave-all-class-inherits class-selector)))
05a1abfc 7626 ;; Check if we are in a class routine
76959b77 7627 (unless class-selector
e8af40ee 7628 (error "Not in a method procedure or function"))
05a1abfc 7629 ;; Check if we need to update the "current" class
76959b77
S
7630 (if (not (equal class-selector idlwave-current-tags-class))
7631 (idlwave-prepare-class-tag-completion class-selector))
4b1aaa8b 7632 (setq idlwave-completion-help-info
76959b77 7633 (list 'idlwave-complete-class-structure-tag-help
4b1aaa8b 7634 (idlwave-sintern-routine
76959b77
S
7635 (concat class-selector "__define"))
7636 nil))
05a1abfc
CD
7637 (let ((idlwave-cpl-bold idlwave-current-native-class-tags))
7638 (idlwave-complete-in-buffer
4b1aaa8b 7639 'class-tag 'class-tag
05a1abfc 7640 idlwave-current-class-tags nil
76959b77
S
7641 (format "Select a tag of class %s" class-selector)
7642 "class tag"
7643 'idlwave-attach-class-tag-classes))
05a1abfc
CD
7644 t) ; return t to skip other completions
7645 nil)))
7646
76959b77 7647(defun idlwave-class-tag-reset ()
05a1abfc
CD
7648 (setq idlwave-current-tags-class nil))
7649
7650(defun idlwave-prepare-class-tag-completion (class)
7651 "Find and parse the necessary class definitions for class structure tags."
76959b77 7652 (setq idlwave-sint-class-tags nil)
05a1abfc
CD
7653 (setq idlwave-current-tags-class class)
7654 (setq idlwave-current-class-tags
7655 (mapcar (lambda (x)
76959b77 7656 (list (idlwave-sintern-class-tag x 'set)))
05a1abfc
CD
7657 (idlwave-all-class-tags class)))
7658 (setq idlwave-current-native-class-tags
7659 (mapcar 'downcase (idlwave-class-tags class))))
7660
7661;===========================================================================
7662;;
7663;; Completing system variables and their structure fields
52a244eb 7664;; This is also a plugin.
05a1abfc
CD
7665
7666(defvar idlwave-sint-sysvars nil)
7667(defvar idlwave-sint-sysvartags nil)
1a717047
GM
7668(declare-function idlwave-sintern-sysvar "idlwave" t t)
7669(declare-function idlwave-sintern-sysvartag "idlwave" t t)
05a1abfc
CD
7670(idlwave-new-sintern-type 'sysvar)
7671(idlwave-new-sintern-type 'sysvartag)
7672(add-to-list 'idlwave-complete-special 'idlwave-complete-sysvar-or-tag)
7673(add-hook 'idlwave-update-rinfo-hook 'idlwave-sysvars-reset)
05a1abfc
CD
7674(add-hook 'idlwave-after-load-rinfo-hook 'idlwave-sintern-sysvar-alist)
7675
05a1abfc
CD
7676
7677(defun idlwave-complete-sysvar-or-tag ()
7678 "Complete a system variable."
7679 (interactive)
7680 (let ((pos (point))
7681 (case-fold-search t))
7682 (cond ((save-excursion
7683 ;; Check if the context is right for system variable
7684 (skip-chars-backward "[a-zA-Z0-9_$]")
7685 (equal (char-before) ?!))
7686 (setq idlwave-completion-help-info '(idlwave-complete-sysvar-help))
4b1aaa8b 7687 (idlwave-complete-in-buffer 'sysvar 'sysvar
05a1abfc
CD
7688 idlwave-system-variables-alist nil
7689 "Select a system variable"
7690 "system variable")
7691 t) ; return t to skip other completions
7692 ((save-excursion
7693 ;; Check if the context is right for sysvar tag
52a244eb 7694 (skip-chars-backward "a-zA-Z0-9_$.")
05a1abfc
CD
7695 (and (equal (char-before) ?!)
7696 (looking-at "\\([a-zA-Z][a-zA-Z0-9_$]*\\)\\.")
7697 (<= (match-end 0) pos)))
7698 ;; Complete a system variable tag
7699 (let* ((var (idlwave-sintern-sysvar (match-string 1)))
7700 (entry (assq var idlwave-system-variables-alist))
52a244eb
S
7701 (tags (cdr (assq 'tags entry))))
7702 (or entry (error "!%s is not a known system variable" var))
05a1abfc
CD
7703 (or tags (error "System variable !%s is not a structure" var))
7704 (setq idlwave-completion-help-info
52a244eb 7705 (list 'idlwave-complete-sysvar-tag-help var))
4b1aaa8b 7706 (idlwave-complete-in-buffer 'sysvartag 'sysvartag
05a1abfc
CD
7707 tags nil
7708 "Select a system variable tag"
7709 "system variable tag")
7710 t)) ; return t to skip other completions
7711 (t nil))))
7712
f66f03de
S
7713(defvar link) ;dynamic variables set by help callback
7714(defvar props)
05a1abfc 7715(defun idlwave-complete-sysvar-help (mode word)
52a244eb
S
7716 (let ((word (or (nth 1 idlwave-completion-help-info) word))
7717 (entry (assoc word idlwave-system-variables-alist)))
7718 (cond
7719 ((eq mode 'test)
7720 (and (stringp word) entry (nth 1 (assq 'link entry))))
7721 ((eq mode 'set)
7722 (if entry (setq link (nth 1 (assq 'link entry))))) ;; setting dynamic!!!
7723 (t (error "This should not happen")))))
7724
7725(defun idlwave-complete-sysvar-tag-help (mode word)
7726 (let* ((var (nth 1 idlwave-completion-help-info))
7727 (entry (assoc var idlwave-system-variables-alist))
7728 (tags (cdr (assq 'tags entry)))
7729 (main (nth 1 (assq 'link entry)))
7730 target main-base)
7731 (cond
7732 ((eq mode 'test) ; we can at least link the main
7733 (and (stringp word) entry main))
7734 ((eq mode 'set)
4b1aaa8b
PE
7735 (if entry
7736 (setq link
e08734e2 7737 (if (setq target (cdr (assoc-string word tags t)))
52a244eb
S
7738 (idlwave-substitute-link-target main target)
7739 main)))) ;; setting dynamic!!!
7740 (t (error "This should not happen")))))
7741
f66f03de
S
7742(defun idlwave-split-link-target (link)
7743 "Split a given link into link file and anchor."
7744 (if (string-match idlwave-html-link-sep link)
7745 (cons (substring link 0 (match-beginning 0))
7746 (string-to-number (substring link (match-end 0))))))
7747
52a244eb
S
7748(defun idlwave-substitute-link-target (link target)
7749 "Substitute the target anchor for the given link."
7750 (let (main-base)
7751 (setq main-base (if (string-match "#" link)
7752 (substring link 0 (match-beginning 0))
7753 link))
7754 (if target
7755 (concat main-base idlwave-html-link-sep (number-to-string target))
7756 link)))
76959b77
S
7757
7758;; Fake help in the source buffer for class structure tags.
52a244eb 7759;; KWD AND NAME ARE GLOBAL-VARIABLES HERE.
4b1aaa8b 7760(defvar name)
52a244eb 7761(defvar kwd)
76959b77
S
7762(defvar idlwave-help-do-class-struct-tag nil)
7763(defun idlwave-complete-class-structure-tag-help (mode word)
7764 (cond
7765 ((eq mode 'test) ; nothing gets fontified for class tags
7766 nil)
7767 ((eq mode 'set)
52a244eb 7768 (let (class-with found-in)
4b1aaa8b
PE
7769 (when (setq class-with
7770 (idlwave-class-or-superclass-with-tag
76959b77
S
7771 idlwave-current-tags-class
7772 word))
4b1aaa8b 7773 (if (assq (idlwave-sintern-class class-with)
76959b77 7774 idlwave-system-class-info)
ff689efd 7775 (error "No help available for system class tags"))
52a244eb
S
7776 (if (setq found-in (idlwave-class-found-in class-with))
7777 (setq name (cons (concat found-in "__define") class-with))
7778 (setq name (concat class-with "__define")))))
76959b77
S
7779 (setq kwd word
7780 idlwave-help-do-class-struct-tag t))
7781 (t (error "This should not happen"))))
7782
7783(defun idlwave-class-or-superclass-with-tag (class tag)
7784 "Find and return the CLASS or one of its superclass with the
7785associated TAG, if any."
e08734e2 7786 (let ((sclasses (cons class (idlwave-all-class-inherits class)))
76959b77
S
7787 cl)
7788 (catch 'exit
7789 (while sclasses
7790 (setq cl (pop sclasses))
7791 (let ((tags (idlwave-class-tags cl)))
7792 (while tags
7793 (if (eq t (compare-strings tag 0 nil (car tags) 0 nil t))
4b1aaa8b 7794 (throw 'exit cl))
76959b77
S
7795 (setq tags (cdr tags))))))))
7796
05a1abfc
CD
7797
7798(defun idlwave-sysvars-reset ()
7799 (if (and (fboundp 'idlwave-shell-is-running)
52a244eb
S
7800 (idlwave-shell-is-running)
7801 idlwave-idlwave_routine_info-compiled)
05a1abfc
CD
7802 (idlwave-shell-send-command "idlwave_get_sysvars"
7803 'idlwave-process-sysvars 'hide)))
7804
7805(defun idlwave-process-sysvars ()
7806 (idlwave-shell-filter-sysvars)
7807 (setq idlwave-sint-sysvars nil
7808 idlwave-sint-sysvartags nil)
7809 (idlwave-sintern-sysvar-alist))
7810
05a1abfc 7811(defun idlwave-sintern-sysvar-alist ()
52a244eb 7812 (let ((list idlwave-system-variables-alist) entry tags)
05a1abfc
CD
7813 (while (setq entry (pop list))
7814 (setcar entry (idlwave-sintern-sysvar (car entry) 'set))
52a244eb
S
7815 (setq tags (assq 'tags entry))
7816 (if tags
4b1aaa8b
PE
7817 (setcdr tags
7818 (mapcar (lambda (x)
52a244eb
S
7819 (cons (idlwave-sintern-sysvartag (car x) 'set)
7820 (cdr x)))
7821 (cdr tags)))))))
05a1abfc
CD
7822
7823(defvar idlwave-shell-command-output)
7824(defun idlwave-shell-filter-sysvars ()
52a244eb 7825 "Get any new system variables and tags."
05a1abfc
CD
7826 (let ((text idlwave-shell-command-output)
7827 (start 0)
7828 (old idlwave-system-variables-alist)
52a244eb 7829 var tags type name class link old-entry)
05a1abfc
CD
7830 (setq idlwave-system-variables-alist nil)
7831 (while (string-match "^IDLWAVE-SYSVAR: !\\([a-zA-Z0-9_$]+\\)\\( \\(.*\\)\\)?"
7832 text start)
7833 (setq start (match-end 0)
7834 var (match-string 1 text)
4b1aaa8b 7835 tags (if (match-end 3)
52a244eb
S
7836 (idlwave-split-string (match-string 3 text))))
7837 ;; Maintain old links, if present
7838 (setq old-entry (assq (idlwave-sintern-sysvar var) old))
7839 (setq link (assq 'link old-entry))
05a1abfc 7840 (setq idlwave-system-variables-alist
4b1aaa8b
PE
7841 (cons (list var
7842 (cons
7843 'tags
7844 (mapcar (lambda (x)
7845 (cons x
7846 (cdr (assq
7847 (idlwave-sintern-sysvartag x)
52a244eb
S
7848 (cdr (assq 'tags old-entry))))))
7849 tags)) link)
05a1abfc
CD
7850 idlwave-system-variables-alist)))
7851 ;; Keep the old value if query was not successful
7852 (setq idlwave-system-variables-alist
7853 (or idlwave-system-variables-alist old))))
7854
f32b3b91
CD
7855(defun idlwave-completion-fontify-classes ()
7856 "Goto the *Completions* buffer and fontify the class info."
7857 (when (featurep 'font-lock)
7858 (save-excursion
7859 (set-buffer "*Completions*")
7860 (save-excursion
7861 (goto-char (point-min))
76959b77
S
7862 (let ((buffer-read-only nil))
7863 (while (re-search-forward "\\.*<[^>]+>" nil t)
7864 (put-text-property (match-beginning 0) (match-end 0)
7865 'face 'font-lock-string-face)))))))
f32b3b91
CD
7866
7867(defun idlwave-uniquify (list)
52a244eb 7868 (let ((ht (make-hash-table :size (length list) :test 'equal)))
4b1aaa8b 7869 (delq nil
52a244eb 7870 (mapcar (lambda (x)
4b1aaa8b 7871 (unless (gethash x ht)
52a244eb
S
7872 (puthash x t ht)
7873 x))
7874 list))))
f32b3b91
CD
7875
7876(defun idlwave-after-successful-completion (type slash &optional verify)
7877 "Add `=' or `(' after successful completion of keyword and function.
7878Restore the pre-completion window configuration if possible."
7879 (cond
7880 ((eq type 'procedure)
7881 nil)
7882 ((eq type 'function)
7883 (cond
7884 ((equal idlwave-function-completion-adds-paren nil) nil)
7885 ((or (equal idlwave-function-completion-adds-paren t)
7886 (equal idlwave-function-completion-adds-paren 1))
7887 (insert "("))
7888 ((equal idlwave-function-completion-adds-paren 2)
7889 (insert "()")
7890 (backward-char 1))
7891 (t nil)))
7892 ((eq type 'keyword)
7893 (if (and idlwave-keyword-completion-adds-equal
7894 (not slash))
7895 (progn (insert "=") t)
7896 nil)))
7897
7898 ;; Restore the pre-completion window configuration if this is safe.
4b1aaa8b
PE
7899
7900 (if (or (eq verify 'force) ; force
7901 (and
f32b3b91 7902 (get-buffer-window "*Completions*") ; visible
4b1aaa8b 7903 (idlwave-local-value 'idlwave-completion-p
f32b3b91
CD
7904 "*Completions*") ; cib-buffer
7905 (eq (marker-buffer idlwave-completion-mark)
7906 (current-buffer)) ; buffer OK
7907 (equal (marker-position idlwave-completion-mark)
7908 verify))) ; pos OK
7909 (idlwave-restore-wconf-after-completion))
7910 (move-marker idlwave-completion-mark nil)
7911 (setq idlwave-before-completion-wconf nil))
7912
15e42531
CD
7913(defun idlwave-mouse-context-help (ev &optional arg)
7914 "Call `idlwave-context-help' on the clicked location."
7915 (interactive "eP")
7916 (mouse-set-point ev)
7917 (idlwave-context-help arg))
7918
7919(defvar idlwave-last-context-help-pos nil)
7920(defun idlwave-context-help (&optional arg)
7921 "Display IDL Online Help on context.
76959b77
S
7922If point is on a keyword, help for that keyword will be shown. If
7923point is on a routine name or in the argument list of a routine, help
7924for that routine will be displayed. Works for system routines and
7925keywords, it pulls up text help. For other routies and keywords,
7926visits the source file, finding help in the header (if
7927`idlwave-help-source-try-header' is non-nil) or the routine definition
7928itself."
f32b3b91 7929 (interactive "P")
15e42531
CD
7930 (idlwave-do-context-help arg))
7931
7932(defun idlwave-mouse-completion-help (ev)
7933 "Display online help about the completion at point."
7934 (interactive "eP")
52a244eb
S
7935 ;; Restore last-command for next command, to make
7936 ;; scrolling/cancelling of completions work.
15e42531
CD
7937 (setq this-command last-command)
7938 (idlwave-do-mouse-completion-help ev))
15e42531 7939
f32b3b91 7940(defun idlwave-routine-info (&optional arg external)
52a244eb
S
7941 "Display a routines calling sequence and list of keywords. When
7942point is on the name a function or procedure, or in the argument list
7943of a function or procedure, this command displays a help buffer with
7944the information. When called with prefix arg, enforce class query.
f32b3b91
CD
7945
7946When point is on an object operator `->', display the class stored in
7947this arrow, if any (see `idlwave-store-inquired-class'). With a
7948prefix arg, the class property is cleared out."
7949
7950 (interactive "P")
7951 (idlwave-routines)
7952 (if (string-match "->" (buffer-substring
7953 (max (point-min) (1- (point)))
7954 (min (+ 2 (point)) (point-max))))
7955 ;; Cursor is on an arrow
7956 (if (get-text-property (point) 'idlwave-class)
7957 ;; arrow has class property
7958 (if arg
7959 ;; Remove property
7960 (save-excursion
7961 (backward-char 1)
7962 (when (looking-at ".?\\(->\\)")
7963 (remove-text-properties (match-beginning 1) (match-end 1)
7964 '(idlwave-class nil face nil))
7965 (message "Class property removed from arrow")))
7966 ;; Echo class property
7967 (message "Arrow has text property identifying object to be class %s"
7968 (get-text-property (point) 'idlwave-class)))
7969 ;; No property found
7970 (message "Arrow has no class text property"))
7971
7972 ;; Not on an arrow...
7973 (let* ((idlwave-query-class nil)
7974 (idlwave-force-class-query (equal arg '(4)))
7975 (module (idlwave-what-module)))
15e42531 7976 (if (car module)
05a1abfc
CD
7977 (apply 'idlwave-display-calling-sequence
7978 (idlwave-fix-module-if-obj_new module))
e8af40ee 7979 (error "Don't know which calling sequence to show")))))
f32b3b91
CD
7980
7981(defun idlwave-resolve (&optional arg)
52a244eb 7982 "Call RESOLVE_ROUTINE on the module name at point.
f32b3b91
CD
7983Like `idlwave-routine-info', this looks for a routine call at point.
7984After confirmation in the minibuffer, it will use the shell to issue
7985a RESOLVE call for this routine, to attempt to make it defined and its
7986routine info available for IDLWAVE. If the routine is a method call,
7987both `class__method' and `class__define' will be tried.
7988With ARG, enforce query for the class of object methods."
7989 (interactive "P")
7990 (let* ((idlwave-query-class nil)
7991 (idlwave-force-class-query (equal arg '(4)))
7992 (module (idlwave-what-module))
7993 (name (idlwave-make-full-name (nth 2 module) (car module)))
7994 (type (if (eq (nth 1 module) 'pro) "pro" "function"))
7995 (resolve (read-string "Resolve: " (format "%s %s" type name)))
7996 (kwd "")
7997 class)
7998 (if (string-match "\\(pro\\|function\\)[ \t]+\\(\\(.*\\)::\\)?\\(.*\\)"
7999 resolve)
8000 (setq type (match-string 1 resolve)
4b1aaa8b 8001 class (if (match-beginning 2)
f32b3b91
CD
8002 (match-string 3 resolve)
8003 nil)
8004 name (match-string 4 resolve)))
8005 (if (string= (downcase type) "function")
8006 (setq kwd ",/is_function"))
8007
8008 (cond
8009 ((null class)
4b1aaa8b 8010 (idlwave-shell-send-command
f32b3b91
CD
8011 (format "resolve_routine,'%s'%s" (downcase name) kwd)
8012 'idlwave-update-routine-info
8013 nil t))
8014 (t
4b1aaa8b 8015 (idlwave-shell-send-command
f32b3b91 8016 (format "resolve_routine,'%s__define'%s" (downcase class) kwd)
4b1aaa8b
PE
8017 (list 'idlwave-shell-send-command
8018 (format "resolve_routine,'%s__%s'%s"
f32b3b91
CD
8019 (downcase class) (downcase name) kwd)
8020 '(idlwave-update-routine-info)
8021 nil t))))))
8022
3938cb82
S
8023(defun idlwave-find-module-this-file ()
8024 (interactive)
8025 (idlwave-find-module '(4)))
8026
f32b3b91
CD
8027(defun idlwave-find-module (&optional arg)
8028 "Find the source code of an IDL module.
52a244eb
S
8029Works for modules for which IDLWAVE has routine info available. The
8030function offers as default the module name `idlwave-routine-info'
8031would use. With ARG limit to this buffer. With two prefix ARG's
8032force class query for object methods."
f32b3b91
CD
8033 (interactive "P")
8034 (let* ((idlwave-query-class nil)
52a244eb
S
8035 (idlwave-force-class-query (equal arg '(16)))
8036 (this-buffer (equal arg '(4)))
05a1abfc 8037 (module (idlwave-fix-module-if-obj_new (idlwave-what-module)))
52a244eb 8038 (default (if module
4b1aaa8b 8039 (concat (idlwave-make-full-name
52a244eb
S
8040 (nth 2 module) (car module))
8041 (if (eq (nth 1 module) 'pro) "<p>" "<f>"))
8042 "none"))
4b1aaa8b 8043 (list
52a244eb
S
8044 (idlwave-uniquify
8045 (delq nil
4b1aaa8b 8046 (mapcar (lambda (x)
52a244eb
S
8047 (if (eq 'system (car-safe (nth 3 x)))
8048 ;; Take out system routines with no source.
8049 nil
8050 (list
4b1aaa8b 8051 (concat (idlwave-make-full-name
52a244eb
S
8052 (nth 2 x) (car x))
8053 (if (eq (nth 1 x) 'pro) "<p>" "<f>")))))
8054 (if this-buffer
8055 (idlwave-save-buffer-update)
8056 (idlwave-routines))))))
f32b3b91 8057 (name (idlwave-completing-read
52a244eb
S
8058 (if (or (not this-buffer)
8059 (assoc default list))
8060 (format "Module (Default %s): " default)
8061 (format "Module in this file: "))
f32b3b91
CD
8062 list))
8063 type class)
8064 (if (string-match "\\`\\s-*\\'" name)
8065 ;; Nothing, use the default.
8066 (setq name default))
8067 (if (string-match "<[fp]>" name)
8068 (setq type (substring name -2 -1)
8069 name (substring name 0 -3)))
8070 (if (string-match "\\(.*\\)::\\(.*\\)" name)
8071 (setq class (match-string 1 name)
8072 name (match-string 2 name)))
8073 (setq name (idlwave-sintern-routine-or-method name class)
8074 class (idlwave-sintern-class class)
8075 type (cond ((equal type "f") 'fun)
8076 ((equal type "p") 'pro)
8077 (t t)))
52a244eb 8078 (idlwave-do-find-module name type class nil this-buffer)))
f32b3b91 8079
4b1aaa8b 8080(defun idlwave-do-find-module (name type class
52a244eb 8081 &optional force-source this-buffer)
f32b3b91 8082 (let ((name1 (idlwave-make-full-name class name))
4b1aaa8b 8083 source buf1 entry
f32b3b91 8084 (buf (current-buffer))
05a1abfc 8085 (pos (point))
52a244eb
S
8086 file name2)
8087 (setq entry (idlwave-best-rinfo-assq name type class (idlwave-routines)
8088 'WITH-FILE)
05a1abfc
CD
8089 source (or force-source (nth 3 entry))
8090 name2 (if (nth 2 entry)
8091 (idlwave-make-full-name (nth 2 entry) name)
775591f7 8092 name1))
4b1aaa8b 8093 (if source
52a244eb
S
8094 (setq file (idlwave-routine-source-file source)))
8095 (unless file ; Try to find it on the path.
4b1aaa8b
PE
8096 (setq file
8097 (idlwave-expand-lib-file-name
52a244eb
S
8098 (if class
8099 (format "%s__define.pro" (downcase class))
8100 (format "%s.pro" (downcase name))))))
f32b3b91
CD
8101 (cond
8102 ((or (null name) (equal name ""))
8103 (error "Abort"))
f32b3b91 8104 ((eq (car source) 'system)
4b1aaa8b 8105 (error "Source code for system routine %s is not available"
05a1abfc 8106 name2))
52a244eb 8107 ((or (not file) (not (file-regular-p file)))
e8af40ee 8108 (error "Source code for routine %s is not available"
05a1abfc 8109 name2))
52a244eb
S
8110 (t
8111 (when (not this-buffer)
4b1aaa8b 8112 (setq buf1
52a244eb
S
8113 (idlwave-find-file-noselect file 'find))
8114 (pop-to-buffer buf1 t))
15e42531 8115 (goto-char (point-max))
f32b3b91 8116 (let ((case-fold-search t))
15e42531 8117 (if (re-search-backward
f32b3b91 8118 (concat "^[ \t]*\\<"
52a244eb
S
8119 (cond ((eq type 'fun) "function")
8120 ((eq type 'pro) "pro")
f32b3b91 8121 (t "\\(pro\\|function\\)"))
4b1aaa8b 8122 "\\>[ \t]+"
05a1abfc 8123 (regexp-quote (downcase name2))
f32b3b91
CD
8124 "[^a-zA-Z0-9_$]")
8125 nil t)
8126 (goto-char (match-beginning 0))
8127 (pop-to-buffer buf)
8128 (goto-char pos)
05a1abfc 8129 (error "Could not find routine %s" name2)))))))
f32b3b91
CD
8130
8131(defun idlwave-what-module ()
8132 "Return a default module for stuff near point.
8133Used by `idlwave-routine-info' and `idlwave-find-module'."
8134 (idlwave-routines)
15e42531
CD
8135 (if (let ((case-fold-search t))
8136 (save-excursion
8137 (idlwave-beginning-of-statement)
8138 (looking-at "[ \t]*\\(pro\\|function\\)[ \t]+\\(\\([a-zA-Z0-9_$]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)\\([, \t\n]\\|$\\)")))
8139 ;; This is a function or procedure definition statement
8140 ;; We return the defined routine as module.
8141 (list
52a244eb
S
8142 (idlwave-sintern-routine-or-method (match-string-no-properties 4)
8143 (match-string-no-properties 2))
15e42531
CD
8144 (if (equal (downcase (match-string 1)) "pro") 'pro 'fun)
8145 (idlwave-sintern-class (match-string 3)))
8146
52a244eb 8147 ;; Not a definition statement - analyze precise position.
15e42531
CD
8148 (let* ((where (idlwave-where))
8149 (cw (nth 2 where))
8150 (pro (car (nth 0 where)))
8151 (func (car (nth 1 where)))
8152 (this-word (idlwave-this-word "a-zA-Z0-9$_"))
8153 (next-char (save-excursion (skip-chars-forward "a-zA-Z0-9$_")
8154 (following-char)))
8155 )
8156 (cond
8157 ((and (eq cw 'procedure)
8158 (not (equal this-word "")))
4b1aaa8b 8159 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8160 this-word (nth 2 (nth 3 where))))
8161 (list this-word 'pro
4b1aaa8b 8162 (idlwave-determine-class
15e42531
CD
8163 (cons this-word (cdr (nth 3 where)))
8164 'pro)))
4b1aaa8b 8165 ((and (eq cw 'function)
15e42531
CD
8166 (not (equal this-word ""))
8167 (or (eq next-char ?\() ; exclude arrays, vars.
8168 (looking-at "[a-zA-Z0-9_]*[ \t]*(")))
4b1aaa8b 8169 (setq this-word (idlwave-sintern-routine-or-method
15e42531
CD
8170 this-word (nth 2 (nth 3 where))))
8171 (list this-word 'fun
8172 (idlwave-determine-class
8173 (cons this-word (cdr (nth 3 where)))
8174 'fun)))
8175 ((and (memq cw '(function-keyword procedure-keyword))
8176 (not (equal this-word ""))
8177 (eq next-char ?\()) ; A function!
8178 (setq this-word (idlwave-sintern-routine this-word))
8179 (list this-word 'fun nil))
8180 (func
8181 (list func 'fun (idlwave-determine-class (nth 1 where) 'fun)))
8182 (pro
8183 (list pro 'pro (idlwave-determine-class (nth 0 where) 'pro)))
8184 (t nil)))))
f32b3b91 8185
05a1abfc
CD
8186(defun idlwave-what-module-find-class ()
8187 "Call idlwave-what-module and find the inherited class if necessary."
8188 (let* ((module (idlwave-what-module))
8189 (class (nth 2 module))
8190 classes)
8191 (if (and (= (length module) 3)
8192 (stringp class))
8193 (list (car module)
8194 (nth 1 module)
8195 (apply 'idlwave-find-inherited-class module))
8196 module)))
8197
8198(defun idlwave-find-inherited-class (name type class)
8199 "Find the class which defines TYPE NAME and is CLASS or inherited by CLASS."
8200 (let ((entry (idlwave-best-rinfo-assoc name type class (idlwave-routines))))
8201 (if entry
8202 (nth 2 entry)
8203 class)))
8204
8205(defun idlwave-fix-module-if-obj_new (module)
4b1aaa8b 8206 "Check if MODULE points to obj_new.
52a244eb
S
8207If yes, and if the cursor is in the keyword region, change to the
8208appropriate Init method."
05a1abfc
CD
8209 (let* ((name (car module))
8210 (pos (point))
8211 (case-fold-search t)
8212 string)
8213 (if (and (stringp name)
8214 (equal (downcase name) "obj_new")
8215 (save-excursion
8216 (idlwave-beginning-of-statement)
8217 (setq string (buffer-substring (point) pos))
8218 (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8219 string)))
8220 (let ((name "Init")
8221 (class (match-string 1 string)))
8222 (setq module (list (idlwave-sintern-method "Init")
8223 'fun
8224 (idlwave-sintern-class class)))))
8225 module))
8226
4b1aaa8b 8227(defun idlwave-fix-keywords (name type class keywords
3938cb82 8228 &optional super-classes system)
52a244eb
S
8229 "Update a list of keywords.
8230Translate OBJ_NEW, adding all super-class keywords, or all keywords
3938cb82
S
8231from all classes if class equals t. If SYSTEM is non-nil, don't
8232demand _EXTRA in the keyword list."
5e72c6b2 8233 (let ((case-fold-search t))
f32b3b91
CD
8234
8235 ;; If this is the OBJ_NEW function, try to figure out the class and use
8236 ;; the keywords from the corresponding INIT method.
5e72c6b2 8237 (if (and (equal (upcase name) "OBJ_NEW")
05a1abfc
CD
8238 (or (eq major-mode 'idlwave-mode)
8239 (eq major-mode 'idlwave-shell-mode)))
f32b3b91
CD
8240 (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point)))
8241 (string (buffer-substring bos (point)))
8242 (case-fold-search t)
8243 class)
8244 (and (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
8245 string)
8246 (setq class (idlwave-sintern-class (match-string 1 string)))
15e42531 8247 (setq idlwave-current-obj_new-class class)
4b1aaa8b
PE
8248 (setq keywords
8249 (append keywords
52a244eb
S
8250 (idlwave-entry-keywords
8251 (idlwave-rinfo-assq
8252 (idlwave-sintern-method "INIT")
8253 'fun
8254 class
8255 (idlwave-routines)) 'do-link))))))
4b1aaa8b 8256
f32b3b91
CD
8257 ;; If the class is `t', combine all keywords of all methods NAME
8258 (when (eq class t)
52a244eb
S
8259 (mapc (lambda (entry)
8260 (and
8261 (nth 2 entry) ; non-nil class
8262 (eq (nth 1 entry) type) ; correct type
4b1aaa8b
PE
8263 (setq keywords
8264 (append keywords
52a244eb
S
8265 (idlwave-entry-keywords entry 'do-link)))))
8266 (idlwave-all-assq name (idlwave-routines)))
5e72c6b2 8267 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8268
5e72c6b2 8269 ;; If we have inheritance, add all keywords from superclasses, if
52a244eb 8270 ;; the user indicated that method in `idlwave-keyword-class-inheritance'
4b1aaa8b 8271 (when (and
52a244eb 8272 super-classes
5e72c6b2
S
8273 idlwave-keyword-class-inheritance
8274 (stringp class)
4b1aaa8b 8275 (or
3938cb82
S
8276 system
8277 (assq (idlwave-sintern-keyword "_extra") keywords)
8278 (assq (idlwave-sintern-keyword "_ref_extra") keywords))
5e72c6b2
S
8279 ;; Check if one of the keyword-class regexps matches the name
8280 (let ((regexps idlwave-keyword-class-inheritance) re)
8281 (catch 'exit
8282 (while (setq re (pop regexps))
8283 (if (string-match re name) (throw 'exit t))))))
52a244eb
S
8284
8285 (loop for entry in (idlwave-routines) do
8286 (and (nth 2 entry) ; non-nil class
8287 (memq (nth 2 entry) super-classes) ; an inherited class
8288 (eq (nth 1 entry) type) ; correct type
8289 (eq (car entry) name) ; correct name
8ffcfb27
GM
8290 (mapc (lambda (k) (add-to-list 'keywords k))
8291 (idlwave-entry-keywords entry 'do-link))))
f32b3b91 8292 (setq keywords (idlwave-uniquify keywords)))
4b1aaa8b 8293
f32b3b91
CD
8294 ;; Return the final list
8295 keywords))
8296
15e42531 8297(defun idlwave-expand-keyword (keyword module)
2e8b9c7d 8298 "Expand KEYWORD to one of the valid keyword parameters of MODULE.
15e42531
CD
8299KEYWORD may be an exact match or an abbreviation of a keyword.
8300If the match is exact, KEYWORD itself is returned, even if there may be other
8301keywords of which KEYWORD is an abbreviation. This is necessary because some
8302system routines have keywords which are prefixes of other keywords.
8303If KEYWORD is an abbreviation of several keywords, a list of all possible
8304completions is returned.
8305If the abbreviation was unique, the correct keyword is returned.
8306If it cannot be a keyword, the function return nil.
8307If we do not know about MODULE, just return KEYWORD literally."
8308 (let* ((name (car module))
8309 (type (nth 1 module))
8310 (class (nth 2 module))
8311 (kwd (idlwave-sintern-keyword keyword))
8312 (entry (idlwave-best-rinfo-assoc name type class (idlwave-routines)))
52a244eb 8313 (kwd-alist (idlwave-entry-keywords entry))
15e42531
CD
8314 (extra (or (assq (idlwave-sintern-keyword "_EXTRA") kwd-alist)
8315 (assq (idlwave-sintern-keyword "_REF_EXTRA") kwd-alist)))
8316 (completion-ignore-case t)
8317 candidates)
4b1aaa8b 8318 (cond ((assq kwd kwd-alist)
15e42531
CD
8319 kwd)
8320 ((setq candidates (all-completions kwd kwd-alist))
8321 (if (= (length candidates) 1)
8322 (car candidates)
8323 candidates))
8324 ((and entry extra)
4b1aaa8b 8325 ;; Inheritance may cause this keyword to be correct
15e42531
CD
8326 keyword)
8327 (entry
8328 ;; We do know the function, which does not have the keyword.
8329 nil)
8330 (t
8331 ;; We do not know the function, so this just might be a correct
8332 ;; keyword - return it as it is.
8333 keyword))))
8334
8335(defvar idlwave-rinfo-mouse-map (make-sparse-keymap))
f32b3b91 8336(defvar idlwave-rinfo-map (make-sparse-keymap))
4b1aaa8b 8337(define-key idlwave-rinfo-mouse-map
f32b3b91
CD
8338 (if (featurep 'xemacs) [button2] [mouse-2])
8339 'idlwave-mouse-active-rinfo)
4b1aaa8b 8340(define-key idlwave-rinfo-mouse-map
15e42531
CD
8341 (if (featurep 'xemacs) [(shift button2)] [(shift mouse-2)])
8342 'idlwave-mouse-active-rinfo-shift)
4b1aaa8b 8343(define-key idlwave-rinfo-mouse-map
f32b3b91
CD
8344 (if (featurep 'xemacs) [button3] [mouse-3])
8345 'idlwave-mouse-active-rinfo-right)
15e42531
CD
8346(define-key idlwave-rinfo-mouse-map " " 'idlwave-active-rinfo-space)
8347(define-key idlwave-rinfo-map "q" 'idlwave-quit-help)
8348(define-key idlwave-rinfo-mouse-map "q" 'idlwave-quit-help)
8349(defvar idlwave-popup-source nil)
8350(defvar idlwave-rinfo-marker (make-marker))
8351
8352(defun idlwave-quit-help ()
8353 (interactive)
8354 (let ((ri-window (get-buffer-window "*Help*"))
8355 (olh-window (get-buffer-window "*IDLWAVE Help*")))
8356 (when (and olh-window
8357 (fboundp 'idlwave-help-quit))
8358 (select-window olh-window)
8359 (idlwave-help-quit))
8360 (when (window-live-p ri-window)
8361 (delete-window ri-window))))
f32b3b91 8362
05a1abfc
CD
8363(defun idlwave-display-calling-sequence (name type class
8364 &optional initial-class)
f32b3b91 8365 ;; Display the calling sequence of module NAME, type TYPE in class CLASS.
05a1abfc
CD
8366 (let* ((initial-class (or initial-class class))
8367 (entry (or (idlwave-best-rinfo-assq name type class
15e42531 8368 (idlwave-routines))
4b1aaa8b 8369 (idlwave-rinfo-assq name type class
15e42531 8370 idlwave-unresolved-routines)))
f32b3b91
CD
8371 (name (or (car entry) name))
8372 (class (or (nth 2 entry) class))
05a1abfc 8373 (superclasses (idlwave-all-class-inherits initial-class))
15e42531
CD
8374 (twins (idlwave-routine-twins entry))
8375 (dtwins (idlwave-study-twins twins))
8376 (all dtwins)
52a244eb 8377 (system (eq (car (nth 3 entry)) 'system))
f32b3b91 8378 (calling-seq (nth 4 entry))
52a244eb
S
8379 (keywords (idlwave-entry-keywords entry 'do-link))
8380 (html-file (car (nth 5 entry)))
15e42531 8381 (help-echo-kwd
52a244eb 8382 "Button2: Insert KEYWORD (SHIFT=`/KEYWORD') | Button3: Online Help ")
15e42531 8383 (help-echo-use
52a244eb 8384 "Button2/3: Online Help")
15e42531 8385 (help-echo-src
52a244eb 8386 "Button2: Jump to source and back | Button3: Source in Help window.")
05a1abfc
CD
8387 (help-echo-class
8388 "Button2: Display info about same method in superclass")
f32b3b91 8389 (col 0)
52a244eb 8390 (data (list name type class (current-buffer) nil initial-class))
f32b3b91 8391 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
f66f03de 8392 (face 'idlwave-help-link)
15e42531 8393 beg props win cnt total)
4b1aaa8b 8394 ;; Fix keywords, but don't add chained super-classes, since these
52a244eb 8395 ;; are shown separately for that super-class
f32b3b91
CD
8396 (setq keywords (idlwave-fix-keywords name type class keywords))
8397 (cond
8398 ((null entry)
05a1abfc
CD
8399 (error "No %s %s known %s" type name
8400 (if initial-class (concat "in class " initial-class) "")))
f32b3b91 8401 ((or (null name) (equal name ""))
e8af40ee 8402 (error "No function or procedure call at point"))
f32b3b91 8403 ((null calling-seq)
52a244eb 8404 (error "Calling sequence of %s %s not available" type name))
f32b3b91
CD
8405 (t
8406 (save-excursion
15e42531 8407 (move-marker idlwave-rinfo-marker (point))
f32b3b91 8408 (set-buffer (get-buffer-create "*Help*"))
15e42531 8409 (use-local-map idlwave-rinfo-map)
f32b3b91
CD
8410 (setq buffer-read-only nil)
8411 (erase-buffer)
8412 (set (make-local-variable 'idlwave-popup-source) nil)
15e42531
CD
8413 (set (make-local-variable 'idlwave-current-obj_new-class)
8414 idlwave-current-obj_new-class)
05a1abfc
CD
8415 (when superclasses
8416 (setq props (list 'mouse-face 'highlight
8417 km-prop idlwave-rinfo-mouse-map
8418 'help-echo help-echo-class
8419 'data (cons 'class data)))
8420 (let ((classes (cons initial-class superclasses)) c)
8421 (insert "Classes: ")
8422 (while (setq c (pop classes))
8423 (insert " ")
8424 (setq beg (point))
8425 (insert c)
8426 (if (equal (downcase c) (downcase class))
8427 (add-text-properties beg (point) (list 'face 'bold))
52a244eb 8428 ;; If Method exists in a different class link it
05a1abfc
CD
8429 (if (idlwave-rinfo-assq name type c (idlwave-routines))
8430 (add-text-properties beg (point) props))))
8431 (insert "\n")))
52a244eb
S
8432 (setq props (list 'mouse-face 'highlight
8433 km-prop idlwave-rinfo-mouse-map
8434 'help-echo help-echo-use
8435 'data (cons 'usage data)))
4b1aaa8b 8436 (if html-file (setq props (append (list 'face face 'link html-file)
52a244eb 8437 props)))
f32b3b91
CD
8438 (insert "Usage: ")
8439 (setq beg (point))
8440 (insert (if class
52a244eb
S
8441 (format calling-seq class name class name class name)
8442 (format calling-seq name name name name))
f32b3b91
CD
8443 "\n")
8444 (add-text-properties beg (point) props)
4b1aaa8b 8445
f32b3b91
CD
8446 (insert "Keywords:")
8447 (if (null keywords)
8448 (insert " No keywords accepted.")
8449 (setq col 9)
8ffcfb27 8450 (mapc
f32b3b91 8451 (lambda (x)
4b1aaa8b 8452 (if (>= (+ col 1 (length (car x)))
f32b3b91
CD
8453 (window-width))
8454 (progn
8455 (insert "\n ")
8456 (setq col 9)))
8457 (insert " ")
8458 (setq beg (point)
52a244eb 8459 ;; Relevant keywords already have link property attached
f32b3b91 8460 props (list 'mouse-face 'highlight
15e42531 8461 km-prop idlwave-rinfo-mouse-map
f32b3b91 8462 'data (cons 'keyword data)
15e42531 8463 'help-echo help-echo-kwd
f32b3b91 8464 'keyword (car x)))
52a244eb 8465 (if system (setq props (append (list 'face face) props)))
f32b3b91
CD
8466 (insert (car x))
8467 (add-text-properties beg (point) props)
8468 (setq col (+ col 1 (length (car x)))))
8469 keywords))
4b1aaa8b 8470
15e42531 8471 (setq cnt 1 total (length all))
52a244eb 8472 ;; Here entry is (key file (list of type-conses))
15e42531
CD
8473 (while (setq entry (pop all))
8474 (setq props (list 'mouse-face 'highlight
8475 km-prop idlwave-rinfo-mouse-map
8476 'help-echo help-echo-src
52a244eb
S
8477 'source (list (car (car (nth 2 entry))) ;type
8478 (nth 1 entry)
8479 nil
8480 (cdr (car (nth 2 entry))))
15e42531
CD
8481 'data (cons 'source data)))
8482 (idlwave-insert-source-location
4b1aaa8b 8483 (format "\n%-8s %s"
15e42531
CD
8484 (if (equal cnt 1)
8485 (if (> total 1) "Sources:" "Source:")
8486 "")
8487 (if (> total 1) "- " ""))
8488 entry props)
8489 (incf cnt)
8490 (when (and all (> cnt idlwave-rinfo-max-source-lines))
8491 ;; No more source lines, please
4b1aaa8b 8492 (insert (format
15e42531
CD
8493 "\n Source information truncated to %d entries."
8494 idlwave-rinfo-max-source-lines))
8495 (setq all nil)))
10c8e594 8496 (goto-char (point-min))
f32b3b91
CD
8497 (setq buffer-read-only t))
8498 (display-buffer "*Help*")
8499 (if (and (setq win (get-buffer-window "*Help*"))
8500 idlwave-resize-routine-help-window)
8501 (progn
8502 (let ((ww (selected-window)))
8503 (unwind-protect
8504 (progn
8505 (select-window win)
4b1aaa8b 8506 (enlarge-window (- (/ (frame-height) 2)
f32b3b91
CD
8507 (window-height)))
8508 (shrink-window-if-larger-than-buffer))
8509 (select-window ww)))))))))
8510
15e42531
CD
8511(defun idlwave-insert-source-location (prefix entry &optional file-props)
8512 "Insert a source location into the routine info buffer.
52a244eb
S
8513Start line with PREFIX. If a file name is inserted, add FILE-PROPS to
8514it."
15e42531
CD
8515 (let* ((key (car entry))
8516 (file (nth 1 entry))
8517 (types (nth 2 entry))
52a244eb
S
8518 (shell-flag (assq 'compiled types))
8519 (buffer-flag (assq 'buffer types))
8520 (user-flag (assq 'user types))
8521 (lib-flag (assq 'lib types))
8522 (ndupl (or (and buffer-flag (idlwave-count-memq 'buffer types))
8523 (and user-flag (idlwave-count-memq 'user types))
8524 (and lib-flag (idlwave-count-memq 'lib types))
15e42531
CD
8525 1))
8526 (doflags t)
8527 beg special)
8528
8529 (insert prefix)
8530
8531 (cond
8532 ((eq key 'system)
8533 (setq doflags nil)
52a244eb
S
8534 (insert "System "))
8535
15e42531
CD
8536 ((eq key 'builtin)
8537 (setq doflags nil)
52a244eb
S
8538 (insert "Builtin "))
8539
15e42531 8540 ((and (not file) shell-flag)
52a244eb
S
8541 (insert "Unresolved"))
8542
4b1aaa8b 8543 ((null file)
52a244eb 8544 (insert "ERROR"))
4b1aaa8b 8545
15e42531
CD
8546 ((idlwave-syslib-p file)
8547 (if (string-match "obsolete" (file-name-directory file))
52a244eb
S
8548 (insert "Obsolete ")
8549 (insert "SystemLib ")))
8550
8551 ;; New special syntax: taken directly from routine-info for
8552 ;; library catalog routines
8553 ((setq special (or (cdr lib-flag) (cdr user-flag)))
8554 (insert (format "%-10s" special)))
8555
8556 ;; Old special syntax: a matching regexp
8557 ((setq special (idlwave-special-lib-test file))
8558 (insert (format "%-10s" special)))
4b1aaa8b 8559
52a244eb 8560 ;; Catch-all with file
15e42531 8561 ((idlwave-lib-p file) (insert "Library "))
52a244eb
S
8562
8563 ;; Sanity catch all
15e42531
CD
8564 (t (insert "Other ")))
8565
8566 (when doflags
8567 (insert (concat
8568 " ["
52a244eb
S
8569 (if lib-flag "L" "-")
8570 (if user-flag "C" "-")
15e42531
CD
8571 (if shell-flag "S" "-")
8572 (if buffer-flag "B" "-")
8573 "] ")))
4b1aaa8b 8574 (when (> ndupl 1)
15e42531
CD
8575 (setq beg (point))
8576 (insert (format "(%dx) " ndupl))
8577 (add-text-properties beg (point) (list 'face 'bold)))
8578 (when (and file (not (equal file "")))
8579 (setq beg (point))
8580 (insert (apply 'abbreviate-file-name
8581 (if (featurep 'xemacs) (list file t) (list file))))
8582 (if file-props
8583 (add-text-properties beg (point) file-props)))))
8584
8585(defun idlwave-special-lib-test (file)
8586 "Check the path of FILE against the regexps which define special libs.
8587Return the name of the special lib if there is a match."
8588 (let ((alist idlwave-special-lib-alist)
8589 entry rtn)
8590 (cond
8591 ((stringp file)
8592 (while (setq entry (pop alist))
8593 (if (string-match (car entry) file)
8594 (setq rtn (cdr entry)
8595 alist nil)))
8596 rtn)
8597 (t nil))))
4b1aaa8b 8598
f32b3b91
CD
8599(defun idlwave-mouse-active-rinfo-right (ev)
8600 (interactive "e")
8601 (idlwave-mouse-active-rinfo ev 'right))
8602
15e42531 8603(defun idlwave-mouse-active-rinfo-shift (ev)
f32b3b91 8604 (interactive "e")
15e42531
CD
8605 (idlwave-mouse-active-rinfo ev nil 'shift))
8606
8607(defun idlwave-active-rinfo-space ()
8608 (interactive)
8609 (idlwave-mouse-active-rinfo nil 'right))
8610
8611(defun idlwave-mouse-active-rinfo (ev &optional right shift)
8612 "Does the mouse actions in the routine info buffer.
8613Optional args RIGHT and SHIFT indicate, if mouse-3 was used, and if SHIFT
8614was pressed."
8615 (interactive "e")
8616 (if ev (mouse-set-point ev))
4b1aaa8b 8617 (let (data id name type class buf bufwin source link keyword
3938cb82 8618 word initial-class)
f32b3b91 8619 (setq data (get-text-property (point) 'data)
15e42531 8620 source (get-text-property (point) 'source)
f32b3b91 8621 keyword (get-text-property (point) 'keyword)
52a244eb 8622 link (get-text-property (point) 'link)
f32b3b91 8623 id (car data)
15e42531 8624 name (nth 1 data) type (nth 2 data) class (nth 3 data)
f32b3b91 8625 buf (nth 4 data)
05a1abfc
CD
8626 initial-class (nth 6 data)
8627 word (idlwave-this-word)
f32b3b91 8628 bufwin (get-buffer-window buf t))
52a244eb
S
8629
8630 (cond ((eq id 'class) ; Switch class being displayed
05a1abfc 8631 (if (window-live-p bufwin) (select-window bufwin))
4b1aaa8b 8632 (idlwave-display-calling-sequence
05a1abfc 8633 (idlwave-sintern-method name)
4b1aaa8b 8634 type (idlwave-sintern-class word)
05a1abfc 8635 initial-class))
52a244eb
S
8636 ((eq id 'usage) ; Online help on this routine
8637 (idlwave-online-help link name type class))
8638 ((eq id 'source) ; Source in help or buffer
8639 (if right ; In help
15e42531
CD
8640 (let ((idlwave-extra-help-function 'idlwave-help-with-source)
8641 (idlwave-help-source-try-header nil)
52a244eb 8642 ;; Fake idlwave-routines so help will find the right entry
15e42531 8643 (idlwave-routines
52a244eb 8644 (list (list name type class source ""))))
15e42531 8645 (idlwave-help-get-special-help name type class nil))
52a244eb 8646 ;; Otherwise just pop to the source
f32b3b91
CD
8647 (setq idlwave-popup-source (not idlwave-popup-source))
8648 (if idlwave-popup-source
8649 (condition-case err
15e42531 8650 (idlwave-do-find-module name type class source)
f32b3b91
CD
8651 (error
8652 (setq idlwave-popup-source nil)
8653 (if (window-live-p bufwin) (select-window bufwin))
8654 (error (nth 1 err))))
8655 (if bufwin
8656 (select-window bufwin)
15e42531
CD
8657 (pop-to-buffer buf))
8658 (goto-char (marker-position idlwave-rinfo-marker)))))
f32b3b91
CD
8659 ((eq id 'keyword)
8660 (if right
52a244eb 8661 (idlwave-online-help link name type class keyword)
15e42531
CD
8662 (idlwave-rinfo-insert-keyword keyword buf shift))))))
8663
8664(defun idlwave-rinfo-insert-keyword (keyword buffer &optional shift)
8665 "Insert KEYWORD in BUFFER. Make sure buffer is displayed in a window."
8666 (let ((bwin (get-buffer-window buffer)))
8667 (if idlwave-complete-empty-string-as-lower-case
8668 (setq keyword (downcase keyword)))
8669 (if bwin
8670 (select-window bwin)
8671 (pop-to-buffer buffer)
8672 (setq bwin (get-buffer-window buffer)))
8673 (if (eq (preceding-char) ?/)
8674 (insert keyword)
4b1aaa8b 8675 (unless (save-excursion
15e42531 8676 (re-search-backward
4b1aaa8b 8677 "[(,][ \t]*\\(\\$[ \t]*\\(;.*\\)?\n\\)?[ \t]*\\="
15e42531
CD
8678 (min (- (point) 100) (point-min)) t))
8679 (insert ", "))
8680 (if shift (insert "/"))
8681 (insert keyword)
8682 (if (and (not shift)
8683 idlwave-keyword-completion-adds-equal)
8684 (insert "=")))))
8685
8686(defun idlwave-list-buffer-load-path-shadows (&optional arg)
8687 "List the load path shadows of all routines defined in current buffer."
8688 (interactive "P")
8689 (idlwave-routines)
8690 (if (eq major-mode 'idlwave-mode)
8691 (idlwave-list-load-path-shadows
8692 nil (idlwave-update-current-buffer-info 'save-buffer)
8693 "in current buffer")
8694 (error "Current buffer is not in idlwave-mode")))
8695
8696(defun idlwave-list-shell-load-path-shadows (&optional arg)
8697 "List the load path shadows of all routines compiled under the shell.
8698This is very useful for checking an IDL application. Just compile the
8699application, do RESOLVE_ALL, and `C-c C-i' to compile all referenced
8700routines and update IDLWAVE internal info. Then check for shadowing
8701with this command."
8702 (interactive "P")
8703 (cond
8704 ((or (not (fboundp 'idlwave-shell-is-running))
8705 (not (idlwave-shell-is-running)))
8706 (error "Shell is not running"))
8707 ((null idlwave-compiled-routines)
e8af40ee 8708 (error "No compiled routines. Maybe you need to update with `C-c C-i'"))
15e42531
CD
8709 (t
8710 (idlwave-list-load-path-shadows nil idlwave-compiled-routines
8711 "in the shell"))))
8712
8713(defun idlwave-list-all-load-path-shadows (&optional arg)
8714 "List the load path shadows of all routines known to IDLWAVE."
8715 (interactive "P")
8716 (idlwave-list-load-path-shadows nil nil "globally"))
8717
8718(defun idlwave-list-load-path-shadows (arg &optional special-routines loc)
8719 "List the routines which are defined multiple times.
8720Search the information IDLWAVE has about IDL routines for multiple
8721definitions.
8722When SPECIAL-ROUTINES in non-nil, only look for shadows of these routines.
8723
8724When IDL hits a routine call which is not defined, it will search on
8725the load path in order to find a definition. The output of this
8726command can be used to detect possible name clashes during this process."
8727 (idlwave-routines) ; Make sure everything is loaded.
52a244eb 8728 (unless (or idlwave-user-catalog-routines idlwave-library-catalog-routines)
4b1aaa8b 8729 (or (y-or-n-p
52a244eb 8730 "You don't have any user or library catalogs. Continue anyway? ")
15e42531
CD
8731 (error "Abort")))
8732 (let* ((routines (append idlwave-system-routines
8733 idlwave-compiled-routines
52a244eb
S
8734 idlwave-library-catalog-routines
8735 idlwave-user-catalog-routines
15e42531
CD
8736 idlwave-buffer-routines
8737 nil))
8738 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
8739 (keymap (make-sparse-keymap))
8740 (props (list 'mouse-face 'highlight
8741 km-prop keymap
4b1aaa8b 8742 'help-echo "Mouse2: Find source"))
15e42531 8743 (nroutines (length (or special-routines routines)))
f66f03de 8744 (step (/ nroutines 100))
15e42531 8745 (n 0)
15e42531
CD
8746 (cnt 0)
8747 (idlwave-sort-prefer-buffer-info nil)
8748 routine twins dtwins twin done props1 lroutines)
8749
8750 (if special-routines
8751 ;; Just looking for shadows of a few special routines
8752 (setq lroutines routines
8753 routines special-routines))
8754
8755 (message "Sorting routines...")
8756 (setq routines (sort routines
8757 (lambda (a b)
8758 (string< (downcase (idlwave-make-full-name
8759 (nth 2 a) (car a)))
8760 (downcase (idlwave-make-full-name
8761 (nth 2 b) (car b)))))))
8762 (message "Sorting routines...done")
8763
8764 (define-key keymap (if (featurep 'xemacs) [(button2)] [(mouse-2)])
4b1aaa8b 8765 (lambda (ev)
15e42531
CD
8766 (interactive "e")
8767 (mouse-set-point ev)
8768 (apply 'idlwave-do-find-module
8769 (get-text-property (point) 'find-args))))
8770 (define-key keymap [(return)]
4b1aaa8b 8771 (lambda ()
15e42531
CD
8772 (interactive)
8773 (apply 'idlwave-do-find-module
8774 (get-text-property (point) 'find-args))))
8775 (message "Compiling list...( 0%%)")
8776 (save-excursion
8777 (set-buffer (get-buffer-create "*Shadows*"))
8778 (setq buffer-read-only nil)
8779 (erase-buffer)
8780 (while (setq routine (pop routines))
f66f03de
S
8781 (if (= (mod (setq n (1+ n)) step) 0)
8782 (message "Compiling list...(%2d%%)" (/ (* n 100) nroutines)))
8783
15e42531
CD
8784 ;; Get a list of all twins
8785 (setq twins (idlwave-routine-twins routine (or lroutines routines)))
8786 (if (memq routine done)
8787 (setq dtwins nil)
8788 (setq dtwins (idlwave-study-twins twins)))
5e72c6b2 8789 ;; Mark all twins as dealt with
15e42531
CD
8790 (setq done (append twins done))
8791 (when (or (> (length dtwins) 1)
52a244eb
S
8792 (> (idlwave-count-memq 'lib (nth 2 (car dtwins))) 1)
8793 (> (idlwave-count-memq 'user (nth 2 (car dtwins))) 1)
8794 (> (idlwave-count-memq 'buffer (nth 2 (car dtwins))) 1))
15e42531
CD
8795 (incf cnt)
8796 (insert (format "\n%s%s"
4b1aaa8b 8797 (idlwave-make-full-name (nth 2 routine)
52a244eb 8798 (car routine))
15e42531
CD
8799 (if (eq (nth 1 routine) 'fun) "()" "")))
8800 (while (setq twin (pop dtwins))
8801 (setq props1 (append (list 'find-args
4b1aaa8b
PE
8802 (list (nth 0 routine)
8803 (nth 1 routine)
52a244eb 8804 (nth 2 routine)))
15e42531
CD
8805 props))
8806 (idlwave-insert-source-location "\n - " twin props1))))
8807 (goto-char (point-min))
8808 (setq buffer-read-only t))
8809 (setq loc (or loc ""))
8810 (if (> cnt 0)
8811 (progn
8812 (display-buffer (get-buffer "*Shadows*"))
8813 (message "%d case%s of shadowing found %s"
8814 cnt (if (= cnt 1) "" "s") loc))
8815 (message "No shadowing conflicts found %s" loc))))
8816
8817(defun idlwave-print-source (routine)
8818 (let* ((source (nth 3 routine))
8819 (stype (car source))
52a244eb
S
8820 (sfile (idlwave-routine-source-file source)))
8821 (if (idlwave-syslib-p sfile) (setq stype 'syslib))
15e42531
CD
8822 (if (and (eq stype 'compiled)
8823 (or (not (stringp sfile))
8824 (not (string-match "\\S-" sfile))))
8825 (setq stype 'unresolved))
4b1aaa8b 8826 (princ (format " %-10s %s\n"
15e42531
CD
8827 stype
8828 (if sfile sfile "No source code available")))))
8829
8830(defun idlwave-routine-twins (entry &optional list)
8831 "Return all twin entries of ENTRY in LIST.
8832LIST defaults to `idlwave-routines'.
8833Twin entries are those which have the same name, type, and class.
8834ENTRY will also be returned, as the first item of this list."
8835 (let* ((name (car entry))
8836 (type (nth 1 entry))
8837 (class (nth 2 entry))
8838 (candidates (idlwave-all-assq name (or list (idlwave-routines))))
8839 twins candidate)
8840 (while (setq candidate (pop candidates))
8841 (if (and (not (eq candidate entry))
8842 (eq type (nth 1 candidate))
8843 (eq class (nth 2 candidate)))
8844 (push candidate twins)))
4b1aaa8b 8845 (if (setq candidate (idlwave-rinfo-assq name type class
15e42531
CD
8846 idlwave-unresolved-routines))
8847 (push candidate twins))
8848 (cons entry (nreverse twins))))
8849
8850(defun idlwave-study-twins (entries)
4b1aaa8b 8851 "Return dangerous twins of first entry in ENTRIES.
52a244eb
S
8852Dangerous twins are routines with same name, but in different files on
8853the load path. If a file is in the system library and has an entry in
8854the `idlwave-system-routines' list, we omit the latter as
8855non-dangerous because many IDL routines are implemented as library
8856routines, and may have been scanned."
15e42531 8857 (let* ((entry (car entries))
4b1aaa8b 8858 (name (car entry)) ;
15e42531
CD
8859 (type (nth 1 entry)) ; Must be bound for
8860 (class (nth 2 entry)) ; idlwave-routine-twin-compare
8861 (cnt 0)
52a244eb 8862 source type type-cons file alist syslibp key)
15e42531
CD
8863 (while (setq entry (pop entries))
8864 (incf cnt)
8865 (setq source (nth 3 entry)
8866 type (car source)
52a244eb
S
8867 type-cons (cons type (nth 3 source))
8868 file (idlwave-routine-source-file source))
8869
15e42531
CD
8870 ;; Make KEY to index entry properly
8871 (setq key (cond ((eq type 'system) type)
8872 (file (file-truename file))
8873 (t 'unresolved)))
52a244eb
S
8874
8875 ;; Check for an entry in the system library
4b1aaa8b 8876 (if (and file
15e42531
CD
8877 (not syslibp)
8878 (idlwave-syslib-p file))
15e42531 8879 (setq syslibp t))
4b1aaa8b 8880
52a244eb
S
8881 ;; If there's more than one matching entry for the same file, just
8882 ;; append the type-cons to the type list.
15e42531 8883 (if (setq entry (assoc key alist))
52a244eb
S
8884 (push type-cons (nth 2 entry))
8885 (push (list key file (list type-cons)) alist)))
4b1aaa8b 8886
15e42531 8887 (setq alist (nreverse alist))
4b1aaa8b 8888
15e42531 8889 (when syslibp
52a244eb
S
8890 ;; File is in system *library* - remove any 'system entry
8891 (setq alist (delq (assq 'system alist) alist)))
4b1aaa8b 8892
52a244eb
S
8893 ;; If 'system remains and we've scanned the syslib, it's a builtin
8894 ;; (rather than a !DIR/lib/.pro file bundled as source).
15e42531
CD
8895 (when (and (idlwave-syslib-scanned-p)
8896 (setq entry (assoc 'system alist)))
8897 (setcar entry 'builtin))
8898 (sort alist 'idlwave-routine-twin-compare)))
8899
15e42531
CD
8900(defvar type)
8901(defvar class)
8902(defvar idlwave-sort-prefer-buffer-info t
8903 "Internal variable used to influence `idlwave-routine-twin-compare'.")
8904
8905(defmacro idlwave-xor (a b)
8906 `(and (or ,a ,b)
8907 (not (and ,a ,b))))
8908
8909(defun idlwave-routine-entry-compare (a b)
8910 "Compare two routine info entries for sortiung. This is the general case.
8911It first compates class, names, and type. If it turns out that A and B
cef6cafe 8912are twins (same name, class, and type), calls another routine which
15e42531
CD
8913compares twins on the basis of their file names and path locations."
8914 (let ((name (car a)) (type (nth 1 a)) (class (nth 2 a)))
8915 (cond
8916 ((not (equal (idlwave-downcase-safe class)
8917 (idlwave-downcase-safe (nth 2 b))))
8918 ;; Class decides
8919 (cond ((null (nth 2 b)) nil)
8920 ((null class) t)
8921 (t (string< (downcase class) (downcase (nth 2 b))))))
8922 ((not (equal (downcase name) (downcase (car b))))
8923 ;; Name decides
8924 (string< (downcase name) (downcase (car b))))
8925 ((not (eq type (nth 1 b)))
8926 ;; Type decides
8927 (< (if (eq type 'fun) 1 0) (if (eq (nth 1 b) 'fun) 1 0)))
4b1aaa8b 8928 (t
15e42531
CD
8929 ;; A and B are twins - so the decision is more complicated.
8930 ;; Call twin-compare with the proper arguments.
8931 (idlwave-routine-entry-compare-twins a b)))))
8932
8933(defun idlwave-routine-entry-compare-twins (a b)
52a244eb
S
8934 "Compare two routine entries, under the assumption that they are
8935twins. This basically calls `idlwave-routine-twin-compare' with the
8936correct args."
8937 (let* ((name (car a)) (type (nth 1 a)) (class (nth 2 a)) ; needed outside
8938 (asrc (nth 3 a))
8939 (atype (car asrc))
8940 (bsrc (nth 3 b))
8941 (btype (car bsrc))
8942 (afile (idlwave-routine-source-file asrc))
8943 (bfile (idlwave-routine-source-file bsrc)))
15e42531
CD
8944 (idlwave-routine-twin-compare
8945 (if (stringp afile)
8946 (list (file-truename afile) afile (list atype))
8947 (list atype afile (list atype)))
8948 (if (stringp bfile)
8949 (list (file-truename bfile) bfile (list btype))
8950 (list btype bfile (list btype))))
8951 ))
8952
8953(defun idlwave-routine-twin-compare (a b)
8954 "Compare two routine twin entries for sorting.
8955In here, A and B are not normal routine info entries, but special
8956lists (KEY FILENAME (TYPES...)).
8957This expects NAME TYPE CLASS to be bound to the right values."
8958 (let* (;; Dis-assemble entries
8959 (akey (car a)) (bkey (car b))
8960 (afile (nth 1 a)) (bfile (nth 1 b))
8961 (atypes (nth 2 a)) (btypes (nth 2 b))
8962 ;; System routines?
8963 (asysp (memq akey '(builtin system)))
8964 (bsysp (memq bkey '(builtin system)))
8965 ;; Compiled routines?
8966 (acompp (memq 'compiled atypes))
8967 (bcompp (memq 'compiled btypes))
8968 ;; Unresolved?
8969 (aunresp (or (eq akey 'unresolved)
8970 (and acompp (not afile))))
8971 (bunresp (or (eq bkey 'unresolved)
8972 (and bcompp (not bfile))))
8973 ;; Buffer info available?
8974 (abufp (memq 'buffer atypes))
8975 (bbufp (memq 'buffer btypes))
8976 ;; On search path?
8977 (tpath-alist (idlwave-true-path-alist))
52a244eb
S
8978 (apathp (and (stringp akey)
8979 (assoc (file-name-directory akey) tpath-alist)))
4b1aaa8b 8980 (bpathp (and (stringp bkey)
52a244eb 8981 (assoc (file-name-directory bkey) tpath-alist)))
15e42531
CD
8982 ;; How early on search path? High number means early since we
8983 ;; measure the tail of the path list
8984 (anpath (length (memq apathp tpath-alist)))
8985 (bnpath (length (memq bpathp tpath-alist)))
8986 ;; Look at file names
8987 (aname (if (stringp afile) (downcase (file-name-nondirectory afile)) ""))
8988 (bname (if (stringp bfile) (downcase (file-name-nondirectory bfile)) ""))
8989 (fname-re (if class (format "\\`%s__\\(%s\\|define\\)\\.pro\\'"
8990 (regexp-quote (downcase class))
8991 (regexp-quote (downcase name)))
8992 (format "\\`%s\\.pro" (regexp-quote (downcase name)))))
8993 ;; Is file name derived from the routine name?
8994 ;; Method file or class definition file?
8995 (anamep (string-match fname-re aname))
8996 (adefp (and class anamep (string= "define" (match-string 1 aname))))
8997 (bnamep (string-match fname-re bname))
8998 (bdefp (and class bnamep (string= "define" (match-string 1 bname)))))
8999
9000 ;; Now: follow JD's ideas about sorting. Looks really simple now,
9001 ;; doesn't it? The difficult stuff is hidden above...
9002 (cond
9003 ((idlwave-xor asysp bsysp) asysp) ; System entries first
9004 ((idlwave-xor aunresp bunresp) bunresp) ; Unresolved last
9005 ((and idlwave-sort-prefer-buffer-info
9006 (idlwave-xor abufp bbufp)) abufp) ; Buffers before non-buffers
9007 ((idlwave-xor acompp bcompp) acompp) ; Compiled entries
9008 ((idlwave-xor apathp bpathp) apathp) ; Library before non-library
9009 ((idlwave-xor anamep bnamep) anamep) ; Correct file names first
9010 ((and class anamep bnamep ; both file names match ->
9011 (idlwave-xor adefp bdefp)) bdefp) ; __define after __method
9012 ((> anpath bnpath) t) ; Who is first on path?
9013 (t nil)))) ; Default
9014
52a244eb 9015(defun idlwave-routine-source-file (source)
4b1aaa8b 9016 (if (nth 2 source)
52a244eb
S
9017 (expand-file-name (nth 1 source) (nth 2 source))
9018 (nth 1 source)))
9019
15e42531
CD
9020(defun idlwave-downcase-safe (string)
9021 "Donwcase if string, else return unchanged."
9022 (if (stringp string)
9023 (downcase string)
9024 string))
9025
9026(defun idlwave-count-eq (elt list)
9027 "How often is ELT in LIST?"
9028 (length (delq nil (mapcar (lambda (x) (eq x elt)) list))))
9029
52a244eb
S
9030(defun idlwave-count-memq (elt alist)
9031 "How often is ELT a key in ALIST?"
9032 (length (delq nil (mapcar (lambda (x) (eq (car x) elt)) alist))))
9033
15e42531 9034(defun idlwave-syslib-p (file)
52a244eb 9035 "Non-nil if FILE is in the system library."
15e42531
CD
9036 (let* ((true-syslib (file-name-as-directory
9037 (file-truename
9038 (expand-file-name "lib" (idlwave-sys-dir)))))
9039 (true-file (file-truename file)))
9040 (string-match (concat "^" (regexp-quote true-syslib)) true-file)))
9041
9042(defun idlwave-lib-p (file)
9043 "Non-nil if file is in the library"
9044 (let ((true-dir (file-name-directory (file-truename file))))
9045 (assoc true-dir (idlwave-true-path-alist))))
9046
52a244eb
S
9047(defun idlwave-path-alist-add-flag (list-entry flag)
9048 "Add a flag to the path list entry, if not set."
9049 (let ((flags (cdr list-entry)))
9050 (add-to-list 'flags flag)
9051 (setcdr list-entry flags)))
9052
9053(defun idlwave-path-alist-remove-flag (list-entry flag)
9054 "Remove a flag to the path list entry, if set."
9055 (let ((flags (delq flag (cdr list-entry))))
9056 (setcdr list-entry flags)))
9057
15e42531
CD
9058(defun idlwave-true-path-alist ()
9059 "Return `idlwave-path-alist' alist with true-names.
52a244eb 9060Info is cached, but relies on the functions setting `idlwave-path-alist'
15e42531
CD
9061to reset the variable `idlwave-true-path-alist' to nil."
9062 (or idlwave-true-path-alist
9063 (setq idlwave-true-path-alist
9064 (mapcar (lambda(x) (cons
9065 (file-name-as-directory
9066 (file-truename
9067 (directory-file-name
9068 (car x))))
9069 (cdr x)))
9070 idlwave-path-alist))))
9071
9072(defun idlwave-syslib-scanned-p ()
9073 "Non-nil if the system lib file !DIR/lib has been scanned."
9074 (let* ((true-syslib (file-name-as-directory
9075 (file-truename
9076 (expand-file-name "lib" (idlwave-sys-dir))))))
9077 (cdr (assoc true-syslib (idlwave-true-path-alist)))))
9078
9079;; ----------------------------------------------------------------------------
9080;;
9081;; Online Help display
9082
f32b3b91
CD
9083
9084;; ----------------------------------------------------------------------------
9085;;
9086;; Additions for use with imenu.el and func-menu.el
9087;; (pop-up a list of IDL units in the current file).
9088;;
9089
9090(defun idlwave-prev-index-position ()
9091 "Search for the previous procedure or function.
9092Return nil if not found. For use with imenu.el."
9093 (save-match-data
9094 (cond
9095 ((idlwave-find-key "\\<\\(pro\\|function\\)\\>" -1 'nomark))
9096 ;; ((idlwave-find-key idlwave-begin-unit-reg 1 'nomark)
9097 (t nil))))
9098
9099(defun idlwave-unit-name ()
9100 "Return the unit name.
9101Assumes that point is at the beginning of the unit as found by
9102`idlwave-prev-index-position'."
9103 (forward-sexp 2)
9104 (forward-sexp -1)
9105 (let ((begin (point)))
4b1aaa8b 9106 (re-search-forward
52a244eb 9107 "[a-zA-Z_][a-zA-Z0-9$_]+\\(::[a-zA-Z_][a-zA-Z0-9$_]+\\)?")
f32b3b91
CD
9108 (if (fboundp 'buffer-substring-no-properties)
9109 (buffer-substring-no-properties begin (point))
9110 (buffer-substring begin (point)))))
9111
facebc7b
S
9112(defalias 'idlwave-function-menu
9113 (condition-case nil
f32b3b91
CD
9114 (progn
9115 (require 'func-menu)
facebc7b
S
9116 'function-menu)
9117 (error (condition-case nil
9118 (progn
9119 (require 'imenu)
9120 'imenu)
9121 (error nil)))))
f32b3b91 9122
52a244eb 9123;; Here we hack func-menu.el in order to support this new mode.
f32b3b91
CD
9124;; The latest versions of func-menu.el already have this stuff in, so
9125;; we hack only if it is not already there.
9126(when (fboundp 'eval-after-load)
9127 (eval-after-load "func-menu"
9128 '(progn
9129 (or (assq 'idlwave-mode fume-function-name-regexp-alist)
9130 (not (boundp 'fume-function-name-regexp-idl)) ; avoid problems
9131 (setq fume-function-name-regexp-alist
9132 (cons '(idlwave-mode . fume-function-name-regexp-idl)
9133 fume-function-name-regexp-alist)))
9134 (or (assq 'idlwave-mode fume-find-function-name-method-alist)
9135 (not (fboundp 'fume-find-next-idl-function-name)) ; avoid problems
9136 (setq fume-find-function-name-method-alist
9137 (cons '(idlwave-mode . fume-find-next-idl-function-name)
9138 fume-find-function-name-method-alist))))))
9139
9140(defun idlwave-edit-in-idlde ()
9141 "Edit the current file in IDL Development environment."
9142 (interactive)
9143 (start-process "idldeclient" nil
9144 idlwave-shell-explicit-file-name "-c" "-e"
f66f03de 9145 (buffer-file-name)))
4b1aaa8b 9146
f66f03de 9147(defvar idlwave-help-use-assistant)
f32b3b91
CD
9148(defun idlwave-launch-idlhelp ()
9149 "Start the IDLhelp application."
9150 (interactive)
f66f03de
S
9151 (if idlwave-help-use-assistant
9152 (idlwave-help-assistant-raise)
9153 (start-process "idlhelp" nil idlwave-help-application)))
4b1aaa8b 9154
f32b3b91
CD
9155;; Menus - using easymenu.el
9156(defvar idlwave-mode-menu-def
9157 `("IDLWAVE"
9158 ["PRO/FUNC menu" idlwave-function-menu t]
9159 ("Motion"
9160 ["Subprogram Start" idlwave-beginning-of-subprogram t]
9161 ["Subprogram End" idlwave-end-of-subprogram t]
9162 ["Block Start" idlwave-beginning-of-block t]
9163 ["Block End" idlwave-end-of-block t]
9164 ["Up Block" idlwave-backward-up-block t]
9165 ["Down Block" idlwave-down-block t]
9166 ["Skip Block Backward" idlwave-backward-block t]
9167 ["Skip Block Forward" idlwave-forward-block t])
9168 ("Mark"
9169 ["Subprogram" idlwave-mark-subprogram t]
9170 ["Block" idlwave-mark-block t]
9171 ["Header" idlwave-mark-doclib t])
9172 ("Format"
4b1aaa8b 9173 ["Indent Entire Statement" idlwave-indent-statement
f66f03de 9174 :active t :keys "C-u \\[indent-for-tab-command]" ]
f32b3b91 9175 ["Indent Subprogram" idlwave-indent-subprogram t]
f66f03de 9176 ["(Un)Comment Region" idlwave-toggle-comment-region t]
f32b3b91
CD
9177 ["Continue/Split line" idlwave-split-line t]
9178 "--"
9179 ["Toggle Auto Fill" idlwave-auto-fill-mode :style toggle
9180 :selected (symbol-value idlwave-fill-function)])
9181 ("Templates"
9182 ["Procedure" idlwave-procedure t]
9183 ["Function" idlwave-function t]
9184 ["Doc Header" idlwave-doc-header t]
9185 ["Log" idlwave-doc-modification t]
9186 "--"
9187 ["Case" idlwave-case t]
9188 ["For" idlwave-for t]
9189 ["Repeat" idlwave-repeat t]
9190 ["While" idlwave-while t]
9191 "--"
9192 ["Close Block" idlwave-close-block t])
15e42531 9193 ("Completion"
f32b3b91 9194 ["Complete" idlwave-complete t]
f66f03de 9195 ("Complete Specific"
f32b3b91
CD
9196 ["1 Procedure Name" (idlwave-complete 'procedure) t]
9197 ["2 Procedure Keyword" (idlwave-complete 'procedure-keyword) t]
9198 "--"
9199 ["3 Function Name" (idlwave-complete 'function) t]
9200 ["4 Function Keyword" (idlwave-complete 'function-keyword) t]
9201 "--"
9202 ["5 Procedure Method Name" (idlwave-complete 'procedure-method) t]
9203 ["6 Procedure Method Keyword" (idlwave-complete 'procedure-method-keyword) t]
9204 "--"
9205 ["7 Function Method Name" (idlwave-complete 'function-method) t]
9206 ["8 Function Method Keyword" (idlwave-complete 'function-method-keyword) t]
9207 "--"
15e42531
CD
9208 ["9 Class Name" idlwave-complete-class t]))
9209 ("Routine Info"
f32b3b91 9210 ["Show Routine Info" idlwave-routine-info t]
52a244eb 9211 ["Online Context Help" idlwave-context-help t]
f32b3b91
CD
9212 "--"
9213 ["Find Routine Source" idlwave-find-module t]
15e42531 9214 ["Resolve Routine" idlwave-resolve (featurep 'idlw-shell)]
f32b3b91
CD
9215 "--"
9216 ["Update Routine Info" idlwave-update-routine-info t]
f66f03de 9217 ["Rescan XML Help Catalog" idlwave-convert-xml-system-routine-info t]
f32b3b91 9218 "--"
52a244eb
S
9219 "IDL User Catalog"
9220 ["Select Catalog Directories" (idlwave-create-user-catalog-file nil) t]
15e42531 9221 ["Scan Directories" (idlwave-update-routine-info '(16))
5e72c6b2
S
9222 (and idlwave-path-alist (not idlwave-catalog-process))]
9223 ["Scan Directories &" (idlwave-update-routine-info '(64))
9224 (and idlwave-path-alist (not idlwave-catalog-process))]
15e42531
CD
9225 "--"
9226 "Routine Shadows"
9227 ["Check Current Buffer" idlwave-list-buffer-load-path-shadows t]
9228 ["Check Compiled Routines" idlwave-list-shell-load-path-shadows t]
9229 ["Check Everything" idlwave-list-all-load-path-shadows t])
9230 ("Misc"
9231 ["Kill auto-created buffers" idlwave-kill-autoloaded-buffers t]
9232 "--"
9233 ["Insert TAB character" idlwave-hard-tab t])
f32b3b91
CD
9234 "--"
9235 ("External"
f32b3b91
CD
9236 ["Start IDL shell" idlwave-shell t]
9237 ["Edit file in IDLDE" idlwave-edit-in-idlde t]
9238 ["Launch IDL Help" idlwave-launch-idlhelp t])
9239 "--"
9240 ("Customize"
9241 ["Browse IDLWAVE Group" idlwave-customize t]
9242 "--"
4b1aaa8b 9243 ["Build Full Customize Menu" idlwave-create-customize-menu
f32b3b91
CD
9244 (fboundp 'customize-menu-create)])
9245 ("Documentation"
9246 ["Describe Mode" describe-mode t]
9247 ["Abbreviation List" idlwave-list-abbrevs t]
9248 "--"
9249 ["Commentary in idlwave.el" idlwave-show-commentary t]
595ab50b 9250 ["Commentary in idlw-shell.el" idlwave-shell-show-commentary t]
f32b3b91
CD
9251 "--"
9252 ["Info" idlwave-info t]
9253 "--"
8c43762b 9254 ["Help with Topic" idlwave-help-assistant-help-with-topic
e08734e2 9255 idlwave-help-use-assistant]
f32b3b91
CD
9256 ["Launch IDL Help" idlwave-launch-idlhelp t])))
9257
9258(defvar idlwave-mode-debug-menu-def
9259 '("Debug"
9260 ["Start IDL shell" idlwave-shell t]
9261 ["Save and .RUN buffer" idlwave-shell-save-and-run
4b1aaa8b 9262 (and (boundp 'idlwave-shell-automatic-start)
f32b3b91
CD
9263 idlwave-shell-automatic-start)]))
9264
9265(if (or (featurep 'easymenu) (load "easymenu" t))
9266 (progn
4b1aaa8b
PE
9267 (easy-menu-define idlwave-mode-menu idlwave-mode-map
9268 "IDL and WAVE CL editing menu"
f32b3b91 9269 idlwave-mode-menu-def)
4b1aaa8b
PE
9270 (easy-menu-define idlwave-mode-debug-menu idlwave-mode-map
9271 "IDL and WAVE CL editing menu"
f32b3b91
CD
9272 idlwave-mode-debug-menu-def)))
9273
9274(defun idlwave-customize ()
9275 "Call the customize function with idlwave as argument."
9276 (interactive)
4b1aaa8b 9277 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9278 ;; as well.
22d5821d
CD
9279 (or (featurep 'idlw-shell)
9280 (load "idlw-shell" t))
f32b3b91
CD
9281 (customize-browse 'idlwave))
9282
9283(defun idlwave-create-customize-menu ()
9284 "Create a full customization menu for IDLWAVE, insert it into the menu."
9285 (interactive)
9286 (if (fboundp 'customize-menu-create)
9287 (progn
4b1aaa8b 9288 ;; Try to load the code for the shell, so that we can customize it
f32b3b91 9289 ;; as well.
22d5821d
CD
9290 (or (featurep 'idlw-shell)
9291 (load "idlw-shell" t))
4b1aaa8b 9292 (easy-menu-change
f32b3b91
CD
9293 '("IDLWAVE") "Customize"
9294 `(["Browse IDLWAVE group" idlwave-customize t]
9295 "--"
9296 ,(customize-menu-create 'idlwave)
9297 ["Set" Custom-set t]
9298 ["Save" Custom-save t]
9299 ["Reset to Current" Custom-reset-current t]
9300 ["Reset to Saved" Custom-reset-saved t]
9301 ["Reset to Standard Settings" Custom-reset-standard t]))
9302 (message "\"IDLWAVE\"-menu now contains full customization menu"))
9303 (error "Cannot expand menu (outdated version of cus-edit.el)")))
9304
9305(defun idlwave-show-commentary ()
9306 "Use the finder to view the file documentation from `idlwave.el'."
9307 (interactive)
9308 (require 'finder)
9309 (finder-commentary "idlwave.el"))
9310
9311(defun idlwave-shell-show-commentary ()
595ab50b 9312 "Use the finder to view the file documentation from `idlw-shell.el'."
f32b3b91
CD
9313 (interactive)
9314 (require 'finder)
595ab50b 9315 (finder-commentary "idlw-shell.el"))
f32b3b91
CD
9316
9317(defun idlwave-info ()
9318 "Read documentation for IDLWAVE in the info system."
9319 (interactive)
9320 (require 'info)
9321 (Info-goto-node "(idlwave)"))
9322
9323(defun idlwave-list-abbrevs (arg)
9324 "Show the code abbreviations define in IDLWAVE mode.
9325This lists all abbrevs where the replacement text differs from the input text.
9326These are the ones the users want to learn to speed up their writing.
9327
9328The function does *not* list abbrevs which replace a word with itself
9329to call a hook. These hooks are used to change the case of words or
9330to blink the matching `begin', and the user does not need to know them.
9331
9332With arg, list all abbrevs with the corresponding hook.
9333
9334This function was written since `list-abbrevs' looks terrible for IDLWAVE mode."
9335
9336 (interactive "P")
9337 (let ((table (symbol-value 'idlwave-mode-abbrev-table))
9338 abbrevs
9339 str rpl func fmt (len-str 0) (len-rpl 0))
4b1aaa8b 9340 (mapatoms
f32b3b91
CD
9341 (lambda (sym)
9342 (if (symbol-value sym)
9343 (progn
9344 (setq str (symbol-name sym)
9345 rpl (symbol-value sym)
9346 func (symbol-function sym))
9347 (if arg
9348 (setq func (prin1-to-string func))
9349 (if (and (listp func) (stringp (nth 2 func)))
9350 (setq rpl (concat "EVAL: " (nth 2 func))
9351 func "")
9352 (setq func "")))
9353 (if (or arg (not (string= rpl str)))
9354 (progn
9355 (setq len-str (max len-str (length str)))
9356 (setq len-rpl (max len-rpl (length rpl)))
9357 (setq abbrevs (cons (list str rpl func) abbrevs)))))))
9358 table)
9359 ;; sort the list
9360 (setq abbrevs (sort abbrevs (lambda (a b) (string< (car a) (car b)))))
9361 ;; Make the format
9362 (setq fmt (format "%%-%ds %%-%ds %%s\n" len-str len-rpl))
9363 (with-output-to-temp-buffer "*Help*"
9364 (if arg
9365 (progn
4b1aaa8b 9366 (princ "Abbreviations and Actions in IDLWAVE-Mode\n")
f32b3b91
CD
9367 (princ "=========================================\n\n")
9368 (princ (format fmt "KEY" "REPLACE" "HOOK"))
9369 (princ (format fmt "---" "-------" "----")))
9370 (princ "Code Abbreviations and Templates in IDLWAVE-Mode\n")
9371 (princ "================================================\n\n")
9372 (princ (format fmt "KEY" "ACTION" ""))
9373 (princ (format fmt "---" "------" "")))
9374 (mapcar
9375 (lambda (list)
9376 (setq str (car list)
9377 rpl (nth 1 list)
9378 func (nth 2 list))
9379 (princ (format fmt str rpl func)))
9380 abbrevs)))
9381 ;; Make sure each abbreviation uses only one display line
9382 (save-excursion
9383 (set-buffer "*Help*")
9384 (setq truncate-lines t)))
9385
5e72c6b2
S
9386;; Add .pro files to speedbar for support, if it's loaded
9387(eval-after-load "speedbar" '(speedbar-add-supported-extension ".pro"))
9388
5e72c6b2
S
9389;; Set an idle timer to load the routine info.
9390;; Will only work on systems which support this.
9391(or idlwave-routines (idlwave-start-load-rinfo-timer))
9392
f66f03de 9393;;;###autoload (add-to-list 'auto-mode-alist '("\\.[Pp][Rr][Oo]\\'" . idlwave-mode))
52a244eb 9394
15e42531 9395;; Run the hook
f32b3b91
CD
9396(run-hooks 'idlwave-load-hook)
9397
9398(provide 'idlwave)
9399
8df608c1 9400;; arch-tag: f77f3b0c-c37c-424f-a328-0886fd42b6fb
f32b3b91 9401;;; idlwave.el ends here