1 ;;; idlwave.el --- IDL and WAVE CL editing mode for GNU Emacs
2 ;; Copyright (c) 1994-1997 Chris Chase
3 ;; Copyright (c) 1999 Carsten Dominik
4 ;; Copyright (c) 1999 Free Software Foundation
6 ;; Author: Chris Chase <chase@att.com>
7 ;; Maintainer: Carsten Dominik <dominik@strw.leidenuniv.nl>
9 ;; Date: $Date: 1999/12/16 10:42:46 $
10 ;; Keywords: languages
12 ;; This file is part of the GNU Emacs.
14 ;; GNU Emacs is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; GNU Emacs is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
31 ;; In distant past, based on pascal.el. Though bears little
32 ;; resemblance to that now.
34 ;; Incorporates many ideas, such as abbrevs, action routines, and
35 ;; continuation line indenting, from wave.el.
36 ;; wave.el original written by Lubos Pochman, Precision Visuals, Boulder.
38 ;; See the mode description ("C-h m" in idlwave-mode or "C-h f idlwave-mode")
39 ;; for features, key bindings, and info.
40 ;; Also, Info format documentation is available with `M-x idlwave-info'
46 ;; Follow the instructions in the INSTALL file of the distribution.
47 ;; In short, put this file on your load path and add the following
48 ;; lines to your .emacs file:
50 ;; (autoload 'idlwave-mode "idlwave" "IDLWAVE Mode" t)
51 ;; (autoload 'idlwave-shell "idlwave-shell" "IDLWAVE Shell" t)
52 ;; (setq auto-mode-alist (cons '("\\.pro\\'" . idlwave-mode) auto-mode-alist))
58 ;; The newest version of this file is available from the maintainers
61 ;; http://www.strw.leidenuniv.el/~dominik/Tools/idlwave
66 ;; IDLWAVE is documented online in info format.
67 ;; A printable version of the documentation is available from the
68 ;; maintainers webpage (see under SOURCE)
74 ;; Thanks to the following people for their contributions and comments:
76 ;; Ulrik Dickow <dickow@nbi.dk>
77 ;; Eric E. Dors <edors@lanl.gov>
78 ;; Stein Vidar H. Haugan <s.v.h.haugan@astro.uio.no>
79 ;; David Huenemoerder <dph@space.mit.edu>
80 ;; Kevin Ivory <Kevin.Ivory@linmpi.mpg.de>
81 ;; Xuyong Liu <liu@stsci.edu>
82 ;; Simon Marshall <Simon.Marshall@esrin.esa.it>
83 ;; Laurent Mugnier <mugnier@onera.fr>
84 ;; Lubos Pochman <lubos@rsinc.com>
85 ;; Patrick M. Ryan <pat@jaameri.gsfc.nasa.gov>
86 ;; Marty Ryba <ryba@ll.mit.edu>
87 ;; Phil Williams <williams@irc.chmcc.org>
88 ;; J.D. Smith <jdsmith@astrosun.tn.cornell.edu>
89 ;; Phil Sterne <sterne@dublin.llnl.gov>
94 ;; IDLWAVE has customize support - so if you want to learn about the
95 ;; variables which control the behavior of the mode, use
96 ;; `M-x idlwave-customize'.
98 ;; You can set your own preferred values with Customize, or with Lisp
99 ;; code in .emacs. For an example of what to put into .emacs, check
100 ;; the TexInfo documentation.
105 ;; Moving the point backwards in conjunction with abbrev expansion
106 ;; does not work as I would like it, but this is a problem with
107 ;; emacs abbrev expansion done by the self-insert-command. It ends
108 ;; up inserting the character that expanded the abbrev after moving
109 ;; point backward, e.g., "\cl" expanded with a space becomes
110 ;; "LONG( )" with point before the close paren. This is solved by
111 ;; using a temporary function in `post-command-hook' - not pretty,
114 ;; Tabs and spaces are treated equally as whitespace when filling a
115 ;; comment paragraph. To accomplish this, tabs are permanently
116 ;; replaced by spaces in the text surrounding the paragraph, which
117 ;; may be an undesirable side-effect. Replacing tabs with spaces is
118 ;; limited to comments only and occurs only when a comment
119 ;; paragraph is filled via `idlwave-fill-paragraph'.
121 ;; "&" is ignored when parsing statements.
122 ;; Avoid muti-statement lines (using "&") on block begin and end
123 ;; lines. Multi-statement lines can mess up the formatting, for
124 ;; example, multiple end statements on a line: endif & endif.
125 ;; Using "&" outside of block begin/end lines should be okay.
127 ;; It is possible that the parser which decides what to complete has
128 ;; problems with pointer dereferencing statements. I don't use
129 ;; pointers often enough to find out - please report any problems.
131 ;; Completion of keywords for SETPROPERTY and GETPROPERTY assumes that
132 ;; all INIT keywords are allowed in these methods as well. In some
133 ;; cases, there are exceptions to this rule and IDLWAVE will offer
134 ;; a few illegal keyword parameters.
136 ;; Completion and Routine Info do not know about inheritance. Thus,
137 ;; Keywords inherited from superclasses are not displayed and cannot
140 ;; When forcing completion of method keywords, the initial
141 ;; query for a method has multiple entries for some methods. Would
142 ;; be too difficult to fix this hardly used problem.
147 (eval-when-compile (require 'cl
))
150 ;; Kludge to allow `defcustom' for Emacs 19.
151 (condition-case () (require 'custom
) (error nil
))
152 (if (and (featurep 'custom
) (fboundp 'custom-declare-variable
))
153 nil
;; We've got what we needed
154 ;; We have the old or no custom-library, hack around it!
155 (defmacro defgroup
(&rest args
) nil
)
156 (defmacro defcustom
(var value doc
&rest args
)
157 (` (defvar (, var
) (, value
) (, doc
))))))
159 (defgroup idlwave nil
160 "Major mode for editing IDL/WAVE CL .pro files"
162 :link
'(url-link :tag
"Home Page"
163 "http://strw.leidenuniv.nl/~dominik/Tools/idlwave")
164 :link
'(emacs-commentary-link :tag
"Commentary in idlwave-shell.el"
166 :link
'(emacs-commentary-link :tag
"Commentary in idlwave.el" "idlwave.el")
167 :link
'(custom-manual "(idlwave)Top")
171 ;;; Variables for indentation behavior ---------------------------------------
173 (defgroup idlwave-code-formatting nil
174 "Indentation and formatting options for IDLWAVE mode."
177 (defcustom idlwave-main-block-indent
0
178 "*Extra indentation for the main block of code.
179 That is the block between the FUNCTION/PRO statement and the END
180 statement for that program unit."
181 :group
'idlwave-code-formatting
184 (defcustom idlwave-block-indent
4
185 "*Extra indentation applied to block lines.
186 If you change this, you probably also want to change `idlwave-end-offset'."
187 :group
'idlwave-code-formatting
190 (defcustom idlwave-end-offset -
4
191 "*Extra indentation applied to block END lines.
192 A value equal to negative `idlwave-block-indent' will make END lines
193 line up with the block BEGIN lines."
194 :group
'idlwave-code-formatting
197 (defcustom idlwave-continuation-indent
2
198 "*Extra indentation applied to continuation lines.
199 This extra offset applies to the first of a set of continuation lines.
200 The following lines receive the same indentation as the first.
201 Also, the value of this variable applies to continuation lines inside
202 parenthesis. When the current line contains an open unmatched ([{,
203 the next line is indented to that parenthesis plus the value of this variable."
204 :group
'idlwave-code-formatting
207 (defcustom idlwave-hanging-indent t
208 "*If set non-nil then comment paragraphs are indented under the
209 hanging indent given by `idlwave-hang-indent-regexp' match in the first line
211 :group
'idlwave-code-formatting
214 (defcustom idlwave-hang-indent-regexp
"- "
215 "*Regular expression matching the position of the hanging indent
216 in the first line of a comment paragraph. The size of the indent
217 extends to the end of the match for the regular expression."
218 :group
'idlwave-code-formatting
221 (defcustom idlwave-use-last-hang-indent nil
222 "*If non-nil then use last match on line for `idlwave-indent-regexp'."
223 :group
'idlwave-code-formatting
226 (defcustom idlwave-fill-comment-line-only t
227 "*If non-nil then auto fill will only operate on comment lines."
228 :group
'idlwave-code-formatting
231 (defcustom idlwave-auto-fill-split-string t
232 "*If non-nil then auto fill will split strings with the IDL `+' operator.
233 When the line end falls within a string, string concatenation with the
234 '+' operator will be used to distribute a long string over lines.
235 If nil and a string is split then a terminal beep and warning are issued.
237 This variable is ignored when `idlwave-fill-comment-line-only' is
238 non-nil, since in this case code is not auto-filled."
239 :group
'idlwave-code-formatting
242 (defcustom idlwave-split-line-string t
243 "*If non-nil then `idlwave-split-line' will split strings with `+'.
244 When the splitting point of a line falls inside a string, split the string
245 using the `+' string concatenation operator. If nil and a string is
246 split then a terminal beep and warning are issued."
247 :group
'idlwave-code-formatting
250 (defcustom idlwave-no-change-comment
";;;"
251 "*The indentation of a comment that starts with this regular
252 expression will not be changed. Note that the indentation of a comment
253 at the beginning of a line is never changed."
254 :group
'idlwave-code-formatting
257 (defcustom idlwave-begin-line-comment nil
258 "*A comment anchored at the beginning of line.
259 A comment matching this regular expression will not have its
260 indentation changed. If nil the default is \"^;\", i.e., any line
261 beginning with a \";\". Expressions for comments at the beginning of
262 the line should begin with \"^\"."
263 :group
'idlwave-code-formatting
264 :type
'(choice (const :tag
"Any line beginning with `;'" nil
)
267 (defcustom idlwave-code-comment
";;[^;]"
268 "*A comment that starts with this regular expression on a line by
269 itself is indented as if it is a part of IDL code. As a result if
270 the comment is not preceded by whitespace it is unchanged."
271 :group
'idlwave-code-formatting
274 ;; Comments not matching any of the above will be indented as a
275 ;; right-margin comment, i.e., to a minimum of `comment-column'.
278 ;;; Routine Info and Completion ---------------------------------------
280 (defgroup idlwave-routine-info-and-completion nil
281 "Routine info and name/keyword completion options for IDLWAVE mode."
284 (defcustom idlwave-scan-all-buffers-for-routine-info t
285 "*Non-nil means, scan all buffers for IDL programs when updating info.
286 `idlwave-update-routine-info' scans buffers of the current Emacs session
287 for routine definitions. When this variable is nil, it only parses the
288 current buffer. When non-nil, all buffers are searched.
289 A prefix to \\[idlwave-update-routine-info] toggles the meaning of this
290 variable for the duration of the command."
291 :group
'idlwave-routine-info-and-completion
294 (defcustom idlwave-query-shell-for-routine-info t
295 "*Non-nil means query the shell for info about compiled routines.
296 Querying the shell is useful to get information about compiled modules,
297 and it is turned on by default. However, when you have a complete library
298 scan, this is not necessary."
299 :group
'idlwave-routine-info-and-completion
302 (defcustom idlwave-library-path nil
303 "Library path for Windows and MacOS. Not needed under Unix.
304 When selecting the directories to scan for IDL library routine info,
305 IDLWAVE can under UNIX query the shell for the exact search path.
306 However, under Windows and MacOS, the IDLWAVE shell does not work. In this
307 case, this variable specifies the path where IDLWAVE can find library files.
308 The shell will only be asked when this variable is nil.
309 The value is a list of directories. A directory preceeded by a `+' will
310 be search recursively."
311 :group
'idlwave-routine-info-and-completion
312 :type
'(repeat (directory)))
314 (defcustom idlwave-libinfo-file nil
315 "*File for routine information of the IDL library.
316 When this points to a file, the file will be loaded when IDLWAVE first
317 accesses routine info (or does completion).
318 When you scan the library with `idlwave-create-libinfo-file', this file
319 will be used to store the result."
320 :group
'idlwave-routine-info-and-completion
324 (defconst idlwave-tmp
325 '(choice :tag
"by applying the function"
330 (symbol :tag
"Other"))))
333 (defcustom idlwave-completion-case
'((routine . upcase
)
337 "Association list setting the case of completed words.
339 This variable determines the case (UPPER/lower/Capitalized...) of
340 words inserted into the buffer by completion. The preferred case can
341 be specified separately for routine names, keywords, classes and
343 This alist should therefore have entries for `routine' (normal
344 functions and procedures, i.e. non-methods), `keyword', `class', and
345 `method'. Plausible values are
347 upcase upcase whole word, like `BOX_CURSOR'
348 downcase downcase whole word, like `read_ppm'
349 capitalize capitalize each part, like `Widget_Control'
350 preserve preserve case as is, like `IDLgrView'
352 The value can also be any Emacs Lisp function which transforms the
353 case of characters in a string.
355 A value of `preserve' means that the case of the completed word is
356 identical to the way it was written in the definition statement of the
357 routine. This was implemented to allow for mixed-case completion, in
358 particular of object classes and methods.
359 If a completable word is defined in multiple locations, the meaning of
360 `preserve' is not unique since the different definitions might be
361 cased differently. Therefore IDLWAVE always takes the case of the
362 *first* definition it encounters during routine info collection and
363 uses the case derived from it consistently.
365 Note that a lowercase-only string in the buffer will always be completed in
366 lower case (but see the variable `idlwave-completion-force-default-case').
368 After changing this variable, you need to either restart Emacs or press
369 `C-u C-c C-i' to update the internal lists."
370 :group
'idlwave-routine-info-and-completion
372 (cons (symbol :tag
"Derive completion case for")
375 (defcustom idlwave-completion-force-default-case nil
376 "*Non-nil means, completion will always honor `idlwave-completion-case'.
377 When nil, only the completion of a mixed case or upper case string
378 will honor the default settings in `idlwave-completion-case', while
379 the completion of lower case strings will be completed entirely in
381 :group
'idlwave-routine-info-and-completion
384 (defcustom idlwave-complete-empty-string-as-lower-case nil
385 "*Non-nil means, the empty string is considered downcase for completion.
386 The case of what is already in the buffer determines the case of completions.
387 When this variable is non-nil, the empty string is considered to be downcase.
388 Completing on the empty string then offers downcase versions of the possible
390 :group
'idlwave-routine-info-and-completion
393 (defvar idlwave-default-completion-case-is-down nil
394 "Obsolete variable. See `idlwave-complete-empty-string-as-lower-case' and
395 `idlwave-completion-case'.")
397 (defcustom idlwave-buffer-case-takes-precedence nil
398 "*Non-nil means, the case of tokens in buffers dominates over system stuff.
399 To make this possible, we need to re-case everything each time we update
400 the routine info from the buffers. This is slow.
401 The default is to consider the case given in the system and library files
402 first which makes updating much faster."
403 :group
'idlwave-routine-info-and-completion
406 (defcustom idlwave-completion-show-classes
1
407 "*Number of classes to show when completing object methods and keywords.
408 When completing methods or keywords for an object with unknown class,
409 the *Completions* buffer will show the legal classes for each completion
412 MyMethod <Class1,Class2,Class3>
414 The value of this variable may be nil to inhibit display, or an integer to
415 indicate the maximum number of classes to display.
417 On XEmacs, a full list of classes will also be placed into a `help-echo'
418 property on the competion items, so that the list of classes for the current
419 item is displayed in the echo area. If the value of this variable is a
420 negative integer, the `help-echo' property will be suppressed."
421 :group
'idlwave-routine-info-and-completion
422 :type
'(choice (const :tag
"Don't show" nil
)
423 (integer :tag
"Number of classes shown" 1)))
425 (defcustom idlwave-completion-fontify-classes t
426 "*Non-nil means, fontify the classes in completions buffer.
427 This makes it easier to distinguish the completion items from the extra
428 class info listed. See `idlwave-completion-show-classes'."
429 :group
'idlwave-routine-info-and-completion
432 (defcustom idlwave-query-class
'((method-default . nil
)
433 (keyword-default . nil
))
434 "Association list governing specification of object classes for completion.
436 When IDLWAVE is trying to complete items which belong to the object
437 oriented part of IDL, it usually cannot determine the class of a given
438 object from context. In order to provide the user with a correct list
439 of methods or keywords, it would have to determine the appropriate
440 class. IDLWAVE has two ways to deal with this problem.
442 1. One possibility is to combine the items of all available
443 classes for the purpose of completion. So when completing a
444 method, all methods of all classes are available, and when
445 completing a keyword, all keywords allowed for this method in any
446 class will be possible completions. This behavior is very much
447 like normal completion and is therefore the default. It works much
448 better than one might think - only for the INIT, GETPROPERTY and
449 SETPROPERTY the keyword lists become uncomfortably long.
450 See also `idlwave-completion-show-classes'.
452 2. The second possibility is to ask the user on each occasion. To
453 make this less interruptive, IDLWAVE can store the class as a text
454 property on the object operator `->'. For a given object in the
455 source code, class selection will then be needed only once
456 - for example to complete the method. Keywords to the method can
457 then be completed directly, because the class is already known.
458 You will have to turn on the storage of the selected class
459 explicitly with the variable `idlwave-store-inquired-class'.
461 This variable allows to configure IDLWAVE's behavior during
462 completion. Its value is an alist, which should contain at least two
463 elements: (method-default . VALUE) and (keyword-default . VALUE),
464 where VALUE is either t or nil. These specify if the class should be
465 determined during method and keyword completion, respectively.
467 The alist may have additional entries specifying exceptions from the
468 keyword completion rule for specific methods, like INIT or
469 GETPROPERTY. In order to turn on class specification for the INIT
470 method, add an entry (\"INIT\" . t). The method name must be ALL-CAPS."
471 :group
'idlwave-routine-info-and-completion
473 (cons (const method-default
)
474 (boolean :tag
"Determine class when completing METHODS "))
475 (cons (const keyword-default
)
476 (boolean :tag
"Determine class when completing KEYWORDS "))
478 :tag
"Exceptions to defaults"
480 (cons (string :tag
"MODULE" :value
"")
481 (boolean :tag
"Determine class for this method")))))
483 (defcustom idlwave-store-inquired-class nil
484 "*Non-nil means, store class of a method call as text property on `->'.
485 IDLWAVE sometimes has to ask the user for the class associated with a
486 particular object method call. This happens during the commands
487 `idlwave-routine-info' and `idlwave-complete', depending upon the
488 value of the variable `idlwave-query-class'.
490 When you specify a class, this information can be stored as a text
491 property on the `->' arrow in the source code, so that during the same
492 editing session, IDLWAVE will not have to ask again. When this
493 variable is non-nil, IDLWAVE will store and reuse the class information.
494 The class stored can be checked and removed with `\\[idlwave-routine-info]'
497 The default of this variable is nil, since the result of commands then
498 is more predictable. However, if you know what you are doing, it can
499 be nice to turn this on.
501 An arrow which knows the class will be highlighted with
502 `idlwave-class-arrow-face'. The command \\[idlwave-routine-info]
503 displays (with prefix arg: deletes) the class stored on the arrow
505 :group
'idlwave-routine-info-and-completion
508 (defcustom idlwave-class-arrow-face
'bold
509 "*Face to highlight object operator arrows `->' which carry a class property.
510 When IDLWAVE stores a class name as text property on an object arrow
511 (see variable `idlwave-store-inquired-class', it highlights the arrow
512 with this font in order to remind the user that this arrow is special."
513 :group
'idlwave-routine-info-and-completion
516 (defcustom idlwave-resize-routine-help-window t
517 "*Non-nil means, resize the Routine-info *Help* window to fit the content."
518 :group
'idlwave-routine-info-and-completion
521 (defcustom idlwave-keyword-completion-adds-equal t
522 "*Non-nil means, completion automatically adds `=' after completed keywords."
523 :group
'idlwave-routine-info
526 (defcustom idlwave-function-completion-adds-paren t
527 "*Non-nil means, completion automatically adds `(' after completed function.
528 Nil means, don't add anything.
529 A value of `2' means, also add the closing parenthesis and position cursor
531 :group
'idlwave-routine-info
532 :type
'(choice (const :tag
"Nothing" nil
)
534 (const :tag
"()" 2)))
536 (defcustom idlwave-completion-restore-window-configuration t
537 "*Non-nil means, try to restore the window configuration after completion.
538 When completion is not unique, Emacs displays a list of completions.
539 This messes up your window configuration. With this variable set, IDLWAVE
540 restores the old configuration after successful completion."
541 :group
'idlwave-routine-info-and-completion
544 ;;; Variables for abbrev and action behavior -----------------------------
546 (defgroup idlwave-abbrev-and-indent-action nil
547 "IDLWAVE performs actions when expanding abbreviations or indenting lines.
548 The variables in this group govern this."
551 (defcustom idlwave-do-actions nil
552 "*Non-nil means performs actions when indenting.
553 The actions that can be performed are listed in `idlwave-indent-action-table'."
554 :group
'idlwave-abbrev-and-indent-action
557 (defcustom idlwave-abbrev-start-char
"\\"
558 "*A single character string used to start abbreviations in abbrev mode.
559 Possible characters to chose from: ~`\%
560 or even '?'. '.' is not a good choice because it can make structure
561 field names act like abbrevs in certain circumstances.
563 Changes to this in `idlwave-mode-hook' will have no effect. Instead a user
564 must set it directly using `setq' in the .emacs file before idlwave.el
566 :group
'idlwave-abbrev-and-indent-action
569 (defcustom idlwave-surround-by-blank nil
570 "*Non-nil means, enable `idlwave-surround'.
571 If non-nil, `=',`<',`>',`&',`,' are surrounded with spaces by
573 See help for `idlwave-indent-action-table' for symbols using `idlwave-surround'.
575 Also see the default key bindings for keys using `idlwave-surround'.
576 Keys are bound and made into actions calling `idlwave-surround' with
577 `idlwave-action-and-binding'.
578 See help for `idlwave-action-and-binding' for examples.
580 Also see help for `idlwave-surround'."
581 :group
'idlwave-abbrev-and-indent-action
584 (defcustom idlwave-pad-keyword t
585 "*Non-nil means pad '=' for keywords like assignments.
586 Whenever `idlwave-surround' is non-nil then this affects how '=' is padded
587 for keywords. If non-nil it is padded the same as for assignments.
588 If nil then spaces are removed."
589 :group
'idlwave-abbrev-and-indent-action
592 (defcustom idlwave-show-block t
593 "*Non-nil means point blinks to block beginning for `idlwave-show-begin'."
594 :group
'idlwave-abbrev-and-indent-action
597 (defcustom idlwave-expand-generic-end nil
598 "*Non-nil means expand generic END to ENDIF/ENDELSE/ENDWHILE etc."
599 :group
'idlwave-abbrev-and-indent-action
602 (defcustom idlwave-abbrev-move t
603 "*Non-nil means the abbrev hook can move point.
604 Set to nil by `idlwave-expand-region-abbrevs'. To see the abbrev
605 definitions, use the command `list-abbrevs', for abbrevs that move
606 point. Moving point is useful, for example, to place point between
607 parentheses of expanded functions.
609 See `idlwave-check-abbrev'."
610 :group
'idlwave-abbrev-and-indent-action
613 (defcustom idlwave-abbrev-change-case nil
614 "*Non-nil means all abbrevs will be forced to either upper or lower case.
615 If the value t, all expanded abbrevs will be upper case.
616 If the value is 'down then abbrevs will be forced to lower case.
617 If nil, the case will not change.
618 If `idlwave-reserved-word-upcase' is non-nil, reserved words will always be
619 upper case, regardless of this variable."
620 :group
'idlwave-abbrev-and-indent-action
623 (defcustom idlwave-reserved-word-upcase nil
624 "*Non-nil means, reserved words will be made upper case via abbrev expansion.
625 If nil case of reserved words is controlled by `idlwave-abbrev-change-case'.
626 Has effect only if in abbrev-mode."
627 :group
'idlwave-abbrev-and-indent-action
630 ;;; Action/Expand Tables.
632 ;; The average user may have difficulty modifying this directly. It
633 ;; can be modified/set in idlwave-mode-hook, but it is easier to use
634 ;; idlwave-action-and-binding. See help for idlwave-action-and-binding for
635 ;; examples of how to add an action.
637 ;; The action table is used by `idlwave-indent-line' whereas both the
638 ;; action and expand tables are used by `idlwave-indent-and-action'. In
639 ;; general, the expand table is only used when a line is explicitly
640 ;; indented. Whereas, in addition to being used when the expand table
641 ;; is used, the action table is used when a line is indirectly
642 ;; indented via line splitting, auto-filling or a new line creation.
646 ;; Capitalize system vars
647 ;; (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)
649 ;; Capitalize procedure name
650 ;; (idlwave-action-and-binding "\\<\\(pro\\|function\\)\\>[ \t]*\\<"
651 ;; '(capitalize-word 1) t)
653 ;; Capitalize common block name
654 ;; (idlwave-action-and-binding "\\<common\\>[ \t]+\\<"
655 ;; '(capitalize-word 1) t)
657 ;; (idlwave-action-and-binding (concat "^[ \t]*" idlwave-label)
658 ;; '(capitalize-word -1) t)
660 (defvar idlwave-indent-action-table nil
661 "*Associated array containing action lists of search string (car),
662 and function as a cdr. This table is used by `idlwave-indent-line'.
663 See documentation for `idlwave-do-action' for a complete description of
666 Additions to the table are made with `idlwave-action-and-binding' when a
667 binding is not requested.
668 See help on `idlwave-action-and-binding' for examples.")
670 (defvar idlwave-indent-expand-table nil
671 "*Associated array containing action lists of search string (car),
672 and function as a cdr. The table is used by the
673 `idlwave-indent-and-action' function. See documentation for
674 `idlwave-do-action' for a complete description of the action lists.
676 Additions to the table are made with `idlwave-action-and-binding' when a
677 binding is requested.
678 See help on `idlwave-action-and-binding' for examples.")
680 ;;; Documentation header and history keyword ---------------------------------
682 (defgroup idlwave-documentation nil
683 "Options for documenting IDLWAVE files."
686 ;; FIXME: make defcustom?
687 (defvar idlwave-file-header
714 ; KEYWORD PARAMETERS:
746 ; MODIFICATION HISTORY:
750 "*A list (PATHNAME STRING) specifying the doc-header template to use for
751 summarizing a file. If PATHNAME is non-nil then this file will be included.
752 Otherwise STRING is used. If NIL, the file summary will be omitted.
753 For example you might set PATHNAME to the path for the
754 lib_template.pro file included in the IDL distribution.")
756 (defcustom idlwave-timestamp-hook
'idlwave-default-insert-timestamp
757 "*The hook function used to update the timestamp of a function."
758 :group
'idlwave-documentation
761 (defcustom idlwave-doc-modifications-keyword
"HISTORY"
762 "*The modifications keyword to use with the log documentation commands.
763 A ':' is added to the keyword end.
764 Inserted by doc-header and used to position logs by doc-modification.
765 If nil it will not be inserted."
766 :group
'idlwave-documentation
769 (defcustom idlwave-doclib-start
"^;+\\+"
770 "*Regexp matching the start of a document library header."
771 :group
'idlwave-documentation
774 (defcustom idlwave-doclib-end
"^;+-"
775 "*Regexp matching the end of a document library header."
776 :group
'idlwave-documentation
779 ;;; External Programs -------------------------------------------------------
781 (defgroup idlwave-external-programs nil
782 "Miscellaneous options for IDLWAVE mode."
785 ;; WARNING: The following variable has recently been moved from
786 ;; idlwave-shell.el to this file. I hope this does not break
789 (defcustom idlwave-shell-explicit-file-name
"idl"
790 "*If non-nil, is the command to run IDL.
791 Should be an absolute file path or path relative to the current environment
792 execution search path."
793 :group
'idlwave-external-programs
796 ;; FIXME: Document a case when is this needed.
797 (defcustom idlwave-shell-command-line-options nil
798 "*A list of command line options for calling the IDL program."
799 :type
'(repeat (string :value
""))
800 :group
'idlwave-external-programs
)
802 (defcustom idlwave-help-application
"idlhelp"
803 "*The external application providing reference help for programming."
804 :group
'idlwave-external-programs
807 ;;; Miscellaneous variables -------------------------------------------------
809 (defgroup idlwave-misc nil
810 "Miscellaneous options for IDLWAVE mode."
813 (defcustom idlwave-startup-message t
814 "*Non-nil displays a startup message when `idlwave-mode' is first called."
818 (defcustom idlwave-default-font-lock-items
819 '(pros-and-functions batch-files idl-keywords label goto
820 common-blocks class-arrows
)
821 "Items which should be fontified on the default fontification level 2.
822 IDLWAVE defines 3 levels of fontification. Level 1 is very little, level 3
823 is everything and level 2 is specified by this list.
824 This variable must be set before IDLWAVE gets loaded. It is
825 a list of symbols, the following symbols are allowed.
827 pros-and-functions Procedure and Function definitions
828 batch-files Batch Files
829 idl-keywords IDL Keywords
830 label Statement Labels
832 common-blocks Common Blocks
833 keyword-parameters Keyword Parameters in routine definitions and calls
834 system-variables System Variables
835 fixme FIXME: Warning in comments (on XEmacs only v. 21.0 and up)
836 class-arrows Object Arrows with class property"
840 (const :tag
"Procedure and Function definitions" pros-and-functions
)
841 (const :tag
"Batch Files" batch-files
)
842 (const :tag
"IDL Keywords (reserved words)" idl-keywords
)
843 (const :tag
"Statement Labels" label
)
844 (const :tag
"Goto Statements" goto
)
845 (const :tag
"Common Blocks" common-blocks
)
846 (const :tag
"Keyword Parameters" keyword-parameters
)
847 (const :tag
"System Variables" system-variables
)
848 (const :tag
"FIXME: Warning" fixme
)
849 (const :tag
"Object Arrows with class property " class-arrows
)))
851 (defcustom idlwave-mode-hook nil
852 "Normal hook. Executed when a buffer is put into `idlwave-mode'."
856 (defcustom idlwave-load-hook nil
857 "Normal hook. Executed when idlwave.el is loaded."
862 ;;; End customization variables section
865 ;;; Non customization variables
867 ;;; font-lock mode - Additions by Phil Williams, Ulrik Dickow and
868 ;;; Simon Marshall <simon@gnu.ai.mit.edu>
869 ;;; and Carsten Dominik...
871 (defconst idlwave-font-lock-keywords-1 nil
872 "Subdued level highlighting for IDLWAVE mode.")
874 (defconst idlwave-font-lock-keywords-2 nil
875 "Medium level highlighting for IDLWAVE mode.")
877 (defconst idlwave-font-lock-keywords-3 nil
878 "Gaudy level highlighting for IDLWAVE mode.")
880 (let* ((oldp (or (string-match "Lucid" emacs-version
)
881 (not (boundp 'emacs-minor-version
))
882 (and (<= emacs-major-version
19)
883 (<= emacs-minor-version
29))))
885 ;; The following are the reserved words in IDL. Maybe we should
886 ;; highlight some more stuff as well?
888 ; '("and" "or" "xor" "not"
889 ; "eq" "ge" "gt" "le" "lt" "ne"
890 ; "for" "do" "endfor"
891 ; "if" "then" "endif" "else" "endelse"
892 ; "case" "of" "endcase"
894 ; "repeat" "until" "endrep"
897 ; "inherits" "mod" "on_error" "on_ioerror") ;; on_error is not reserved
899 "and\\|begin\\|case\\|do\\|e\\(lse\\|nd\\(case\\|else\\|"
900 "for\\|if\\|rep\\|while\\)?\\|q\\)\\|for\\|g\\(oto\\|[et]\\)"
901 "\\|i\\(f\\|nherits\\)\\|l[et]\\|mod\\|n\\(e\\|ot\\)\\|"
902 "o\\(n_ioerror\\|[fr]\\)\\|re\\(peat\\|turn\\)\\|then\\|"
903 "until\\|while\\|xor"
906 ;; Procedure declarations. Fontify keyword plus procedure name.
907 ;; Function declarations. Fontify keyword plus function name.
909 '("\\<\\(function\\|pro\\)\\>[ \t]+\\(\\sw+\\(::\\sw+\\)?\\)"
910 (1 font-lock-keyword-face
)
911 (2 font-lock-function-name-face nil t
)))
915 '("\\<\\(common\\)\\>[ \t]*\\(\\sw+\\)?[ \t]*,?"
916 (1 font-lock-keyword-face
) ; "common"
917 (2 font-lock-reference-face nil t
) ; block name
918 (font-lock-match-c++-style-declaration-item-and-skip-to-next
919 ;; Start with point after block name and comma
920 (goto-char (match-end 0)) ; needed for XEmacs, could be nil
922 (1 font-lock-variable-name-face
) ; variable names
927 '("^[ \t]*\\(@[^ \t\n]+\\)" (1 font-lock-string-face
)))
931 '("\\<FIXME:" (0 font-lock-warning-face t
)))
935 '("^[ \t]*\\([a-zA-Z]\\sw*:\\)" (1 font-lock-reference-face
)))
937 ;; The goto statement and its label
939 '("\\(goto\\)[ \t]*,[ \t]*\\([a-zA-Z]\\sw*\\)"
940 (1 font-lock-keyword-face
)
941 (2 font-lock-reference-face
)))
943 ;; Named parameters, like /xlog or ,xrange=[]
944 ;; This is anchored to the comma preceeding the keyword.
945 ;; With continuation lines, works only during whole buffer fontification.
947 '("[(,][ \t]*\\(\\$[ \t]*\n[ \t]*\\)?\\(/[a-zA-Z_]\\sw*\\|[a-zA-Z_]\\sw*[ \t]*=\\)"
948 (2 font-lock-reference-face
)))
950 ;; System variables stars with a bang.
952 '("\\(![a-zA-Z_]+\\(\\.\\sw+\\)?\\)"
953 (1 font-lock-variable-name-face
)))
955 ;; Special and unusual operators (not used because too noisy)
957 '("[<>#]" (0 font-lock-keyword-face
)))
959 ;; All operators (not used because too noisy)
961 '("[-*^#+<>/]" (0 font-lock-keyword-face
)))
963 ;; Arrows with text property `idlwave-class'
965 (list 'idlwave-match-class-arrows
966 (list 0 (if (featurep 'xemacs
)
967 idlwave-class-arrow-face
968 'idlwave-class-arrow-face
))))
972 ;; The following lines are just a dummy to make the compiler shut up
973 ;; about variables bound but not used.
975 idl-keywords idl-keywords
976 pros-and-functions pros-and-functions
977 common-blocks common-blocks
978 batch-files batch-files
982 keyword-parameters keyword-parameters
983 system-variables system-variables
984 special-operators special-operators
985 all-operators all-operators
986 class-arrows class-arrows
)
988 (setq idlwave-font-lock-keywords-1
989 (list pros-and-functions
993 (setq idlwave-font-lock-keywords-2
994 (mapcar 'symbol-value idlwave-default-font-lock-items
))
996 (setq idlwave-font-lock-keywords-3
997 (list pros-and-functions
1008 (defun idlwave-match-class-arrows (limit)
1009 ;; Match an object arrow with class property
1010 (and idlwave-store-inquired-class
1011 (re-search-forward "->" limit
'limit
)
1012 (get-text-property (match-beginning 0) 'idlwave-class
)))
1014 (defvar idlwave-font-lock-keywords idlwave-font-lock-keywords-2
1015 "Default expressions to highlight in IDLWAVE mode.")
1017 (defvar idlwave-font-lock-defaults
1018 '((idlwave-font-lock-keywords
1019 idlwave-font-lock-keywords-1
1020 idlwave-font-lock-keywords-2
1021 idlwave-font-lock-keywords-3
)
1023 ((?$ .
"w") (?_ .
"w") (?. .
"w"))
1026 (put 'idlwave-mode
'font-lock-defaults
1027 idlwave-font-lock-defaults
) ; XEmacs
1029 (defconst idlwave-comment-line-start-skip
"^[ \t]*;"
1030 "Regexp to match the start of a full-line comment.
1031 That is the _beginning_ of a line containing a comment delimiter `;' preceded
1032 only by whitespace.")
1034 (defconst idlwave-begin-block-reg
"\\<\\(pro\\|function\\|begin\\|case\\)\\>"
1035 "Regular expression to find the beginning of a block. The case does
1036 not matter. The search skips matches in comments.")
1038 (defconst idlwave-begin-unit-reg
"\\<\\(pro\\|function\\)\\>\\|\\`"
1039 "Regular expression to find the beginning of a unit. The case does
1042 (defconst idlwave-end-unit-reg
"\\<\\(pro\\|function\\)\\>\\|\\'"
1043 "Regular expression to find the line that indicates the end of unit.
1044 This line is the end of buffer or the start of another unit. The case does
1045 not matter. The search skips matches in comments.")
1047 (defconst idlwave-continue-line-reg
"\\<\\$"
1048 "Regular expression to match a continued line.")
1050 (defconst idlwave-end-block-reg
1051 "\\<end\\(\\|case\\|else\\|for\\|if\\|rep\\|while\\)\\>"
1052 "Regular expression to find the end of a block. The case does
1053 not matter. The search skips matches found in comments.")
1055 (defconst idlwave-block-matches
1057 ("function" .
"end")
1058 ("case" .
"endcase")
1059 ("else" .
"endelse")
1062 ("repeat" .
"endrep")
1063 ("while" .
"endwhile"))
1064 "Matches between statements and the corresponding END variant.
1065 The cars are the reserved words starting a block. If the block really
1066 begins with BEGIN, the cars are the reserved words before the begin
1067 which can be used to identify the block type.
1068 This is used to check for the correct END type, to close blocks and
1069 to expand generic end statements to their detailed form.")
1071 (defconst idlwave-block-match-regexp
1072 "\\<\\(else\\|for\\|then\\|repeat\\|while\\)\\>"
1073 "Regular expression matching reserved words which can stand before
1074 blocks starting with a BEGIN statement. The matches must have associations
1075 `idlwave-block-matches'")
1077 (defconst idlwave-identifier
"[a-zA-Z][a-zA-Z0-9$_]*"
1078 "Regular expression matching an IDL identifier.")
1080 (defconst idlwave-sysvar
(concat "!" idlwave-identifier
)
1081 "Regular expression matching IDL system variables.")
1083 (defconst idlwave-variable
(concat idlwave-identifier
"\\|" idlwave-sysvar
)
1084 "Regular expression matching IDL variable names.")
1086 (defconst idlwave-label
(concat idlwave-identifier
":")
1087 "Regular expression matching IDL labels.")
1089 (defconst idlwave-statement-match
1091 ;; "endif else" is the the only possible "end" that can be
1092 ;; followed by a statement on the same line.
1093 '(endelse .
("end\\(\\|if\\)\\s +else" "end\\(\\|if\\)\\s +else"))
1094 ;; all other "end"s can not be followed by a statement.
1095 (cons 'end
(list idlwave-end-block-reg nil
))
1096 '(if .
("if\\>" "then"))
1097 '(for .
("for\\>" "do"))
1098 '(begin .
("begin\\>" nil
))
1099 '(pdef .
("pro\\>\\|function\\>" nil
))
1100 '(while .
("while\\>" "do"))
1101 '(repeat .
("repeat\\>" "repeat"))
1102 '(goto .
("goto\\>" nil
))
1103 '(case .
("case\\>" nil
))
1104 (cons 'call
(list (concat idlwave-identifier
"\\(\\s *$\\|\\s *,\\)") nil
))
1105 '(assign .
("[^=\n]*=" nil
)))
1107 "Associated list of statement matching regular expressions.
1108 Each regular expression matches the start of an IDL statement. The
1109 first element of each association is a symbol giving the statement
1110 type. The associated value is a list. The first element of this list
1111 is a regular expression matching the start of an IDL statement for
1112 identifying the statement type. The second element of this list is a
1113 regular expression for finding a substatement for the type. The
1114 substatement starts after the end of the found match modulo
1115 whitespace. If it is nil then the statement has no substatement. The
1116 list order matters since matching an assignment statement exactly is
1117 not possible without parsing. Thus assignment statement become just
1118 the leftover unidentified statements containing and equal sign. " )
1120 (defvar idlwave-fill-function
'auto-fill-function
1121 "IDL mode auto fill function.")
1123 (defvar idlwave-comment-indent-function
'comment-indent-function
1124 "IDL mode comment indent function.")
1126 ;; Note that this is documented in the v18 manuals as being a string
1127 ;; of length one rather than a single character.
1128 ;; The code in this file accepts either format for compatibility.
1129 (defvar idlwave-comment-indent-char ?\
1130 "Character to be inserted for IDL comment indentation.
1133 (defconst idlwave-continuation-char ?$
1134 "Character which is inserted as a last character on previous line by
1135 \\[idlwave-split-line] to begin a continuation line. Normally $.")
1137 (defconst idlwave-mode-version
" 3.11")
1139 (defmacro idlwave-keyword-abbrev
(&rest args
)
1140 "Creates a function for abbrev hooks to call `idlwave-check-abbrev' with args."
1141 (` (quote (lambda ()
1142 (, (append '(idlwave-check-abbrev) args
))))))
1144 ;; If I take the time I can replace idlwave-keyword-abbrev with
1145 ;; idlwave-code-abbrev and remove the quoted abbrev check from
1146 ;; idlwave-check-abbrev. Then, e.g, (idlwave-keyword-abbrev 0 t) becomes
1147 ;; (idlwave-code-abbrev idlwave-check-abbrev 0 t). In fact I should change
1148 ;; the name of idlwave-check-abbrev to something like idlwave-modify-abbrev.
1150 (defmacro idlwave-code-abbrev
(&rest args
)
1151 "Creates a function for abbrev hooks that ensures abbrevs are not quoted.
1152 Specifically, if the abbrev is in a comment or string it is unexpanded.
1153 Otherwise ARGS forms a list that is evaluated."
1154 (` (quote (lambda ()
1155 (, (prin1-to-string args
)) ;; Puts the code in the doc string
1156 (if (idlwave-quoted) (progn (unexpand-abbrev) nil
)
1157 (, (append args
)))))))
1159 (defvar idlwave-mode-map
(make-sparse-keymap)
1160 "Keymap used in IDL mode.")
1162 (defvar idlwave-mode-syntax-table
(make-syntax-table)
1163 "Syntax table in use in `idlwave-mode' buffers.")
1165 (modify-syntax-entry ?
+ "." idlwave-mode-syntax-table
)
1166 (modify-syntax-entry ?-
"." idlwave-mode-syntax-table
)
1167 (modify-syntax-entry ?
* "." idlwave-mode-syntax-table
)
1168 (modify-syntax-entry ?
/ "." idlwave-mode-syntax-table
)
1169 (modify-syntax-entry ?^
"." idlwave-mode-syntax-table
)
1170 (modify-syntax-entry ?
# "." idlwave-mode-syntax-table
)
1171 (modify-syntax-entry ?
= "." idlwave-mode-syntax-table
)
1172 (modify-syntax-entry ?%
"." idlwave-mode-syntax-table
)
1173 (modify-syntax-entry ?
< "." idlwave-mode-syntax-table
)
1174 (modify-syntax-entry ?
> "." idlwave-mode-syntax-table
)
1175 (modify-syntax-entry ?
\' "\"" idlwave-mode-syntax-table
)
1176 (modify-syntax-entry ?
\" "\"" idlwave-mode-syntax-table
)
1177 (modify-syntax-entry ?
\\ "." idlwave-mode-syntax-table
)
1178 (modify-syntax-entry ?_
"_" idlwave-mode-syntax-table
)
1179 (modify-syntax-entry ?
{ "(}" idlwave-mode-syntax-table
)
1180 (modify-syntax-entry ?
} "){" idlwave-mode-syntax-table
)
1181 (modify-syntax-entry ?$
"_" idlwave-mode-syntax-table
)
1182 (modify-syntax-entry ?.
"." idlwave-mode-syntax-table
)
1183 (modify-syntax-entry ?\
; "<" idlwave-mode-syntax-table)
1184 (modify-syntax-entry ?
\n ">" idlwave-mode-syntax-table
)
1185 (modify-syntax-entry ?
\f ">" idlwave-mode-syntax-table
)
1187 (defvar idlwave-find-symbol-syntax-table
1188 (copy-syntax-table idlwave-mode-syntax-table
)
1189 "Syntax table that treats symbol characters as word characters.")
1191 (modify-syntax-entry ?$
"w" idlwave-find-symbol-syntax-table
)
1192 (modify-syntax-entry ?_
"w" idlwave-find-symbol-syntax-table
)
1194 (defun idlwave-action-and-binding (key cmd
&optional select
)
1195 "KEY and CMD are made into a key binding and an indent action.
1196 KEY is a string - same as for the `define-key' function. CMD is a
1197 function of no arguments or a list to be evaluated. CMD is bound to
1198 KEY in `idlwave-mode-map' by defining an anonymous function calling
1199 `self-insert-command' followed by CMD. If KEY contains more than one
1200 character a binding will only be set if SELECT is 'both.
1202 (KEY . CMD\ is also placed in the `idlwave-indent-expand-table',
1203 replacing any previous value for KEY. If a binding is not set then it
1204 will instead be placed in `idlwave-indent-action-table'.
1206 If the optional argument SELECT is nil then an action and binding are
1207 created. If SELECT is 'noaction, then a binding is always set and no
1208 action is created. If SELECT is 'both then an action and binding
1209 will both be created even if KEY contains more than one character.
1210 Otherwise, if SELECT is non-nil then only an action is created.
1213 No spaces before and 1 after a comma
1214 (idlwave-action-and-binding \",\" '(idlwave-surround 0 1))
1215 A minimum of 1 space before and after `=' (see `idlwave-expand-equal').
1216 (idlwave-action-and-binding \"=\" '(idlwave-expand-equal -1 -1))
1217 Capitalize system variables - action only
1218 (idlwave-action-and-binding idlwave-sysvar '(capitalize-word 1) t)"
1219 (if (not (equal select
'noaction
))
1221 (let* ((table (if select
'idlwave-indent-action-table
1222 'idlwave-indent-expand-table
))
1223 (cell (assoc key
(eval table
))))
1225 ;; Replace action command
1228 (set table
(append (eval table
) (list (cons key cmd
)))))))
1229 ;; Make key binding for action
1230 (if (or (and (null select
) (= (length key
) 1))
1231 (equal select
'noaction
)
1232 (equal select
'both
))
1233 (define-key idlwave-mode-map key
1236 (self-insert-command 1))
1237 (list (if (listp cmd
)
1241 (fset 'idlwave-debug-map
(make-sparse-keymap))
1243 (define-key idlwave-mode-map
"'" 'idlwave-show-matching-quote
)
1244 (define-key idlwave-mode-map
"\"" 'idlwave-show-matching-quote
)
1245 (define-key idlwave-mode-map
"\C-c;" 'idlwave-toggle-comment-region
)
1246 (define-key idlwave-mode-map
"\C-\M-a" 'idlwave-beginning-of-subprogram
)
1247 (define-key idlwave-mode-map
"\C-\M-e" 'idlwave-end-of-subprogram
)
1248 (define-key idlwave-mode-map
"\C-c{" 'idlwave-beginning-of-block
)
1249 (define-key idlwave-mode-map
"\C-c}" 'idlwave-end-of-block
)
1250 (define-key idlwave-mode-map
"\C-c]" 'idlwave-close-block
)
1251 (define-key idlwave-mode-map
"\M-\C-h" 'idlwave-mark-subprogram
)
1252 (define-key idlwave-mode-map
"\M-\C-n" 'idlwave-forward-block
)
1253 (define-key idlwave-mode-map
"\M-\C-p" 'idlwave-backward-block
)
1254 (define-key idlwave-mode-map
"\M-\C-d" 'idlwave-down-block
)
1255 (define-key idlwave-mode-map
"\M-\C-u" 'idlwave-backward-up-block
)
1256 (define-key idlwave-mode-map
"\M-\r" 'idlwave-split-line
)
1257 (define-key idlwave-mode-map
"\M-\C-q" 'idlwave-indent-subprogram
)
1258 (define-key idlwave-mode-map
"\C-c\C-p" 'idlwave-previous-statement
)
1259 (define-key idlwave-mode-map
"\C-c\C-n" 'idlwave-next-statement
)
1260 ;; (define-key idlwave-mode-map "\r" 'idlwave-newline)
1261 ;; (define-key idlwave-mode-map "\t" 'idlwave-indent-line)
1262 (define-key idlwave-mode-map
"\C-c\C-a" 'idlwave-auto-fill-mode
)
1263 (define-key idlwave-mode-map
"\M-q" 'idlwave-fill-paragraph
)
1264 (define-key idlwave-mode-map
"\M-s" 'idlwave-edit-in-idlde
)
1265 (define-key idlwave-mode-map
"\C-c\C-h" 'idlwave-doc-header
)
1266 (define-key idlwave-mode-map
"\C-c\C-m" 'idlwave-doc-modification
)
1267 (define-key idlwave-mode-map
"\C-c\C-c" 'idlwave-case
)
1268 (define-key idlwave-mode-map
"\C-c\C-d" 'idlwave-debug-map
)
1269 (define-key idlwave-mode-map
"\C-c\C-d\C-c" 'idlwave-shell-save-and-run
)
1270 (define-key idlwave-mode-map
"\C-c\C-f" 'idlwave-for
)
1271 ;; (define-key idlwave-mode-map "\C-c\C-f" 'idlwave-function)
1272 ;; (define-key idlwave-mode-map "\C-c\C-p" 'idlwave-procedure)
1273 (define-key idlwave-mode-map
"\C-c\C-r" 'idlwave-repeat
)
1274 (define-key idlwave-mode-map
"\C-c\C-w" 'idlwave-while
)
1275 (define-key idlwave-mode-map
"\C-c\C-s" 'idlwave-shell
)
1276 (define-key idlwave-mode-map
"\C-c\C-l" 'idlwave-shell-recenter-shell-window
)
1277 (autoload 'idlwave-shell-send-command
"idlwave-shell")
1278 (autoload 'idlwave-shell-recenter-shell-window
"idlwave-shell"
1279 "Run `idlwave-shell' and switch back to current window" t
)
1280 (autoload 'idlwave-shell-save-and-run
"idlwave-shell"
1281 "Save and run buffer under the shell." t
)
1282 (define-key idlwave-mode-map
"\C-c\C-v" 'idlwave-find-module
)
1283 (define-key idlwave-mode-map
"\C-c?" 'idlwave-routine-info
)
1284 (define-key idlwave-mode-map
"\M-?" 'idlwave-routine-info-from-idlhelp
)
1285 (define-key idlwave-mode-map
[(meta tab
)] 'idlwave-complete
)
1286 (define-key idlwave-mode-map
"\C-c\C-i" 'idlwave-update-routine-info
)
1287 (define-key idlwave-mode-map
"\C-c=" 'idlwave-resolve
)
1289 ;; Set action and key bindings.
1290 ;; See description of the function `idlwave-action-and-binding'.
1291 ;; Automatically add spaces for the following characters
1292 (idlwave-action-and-binding "&" '(idlwave-surround -
1 -
1))
1293 (idlwave-action-and-binding "<" '(idlwave-surround -
1 -
1))
1294 (idlwave-action-and-binding ">" '(idlwave-surround -
1 -
1 '(?-
)))
1295 (idlwave-action-and-binding "," '(idlwave-surround 0 -
1))
1296 ;; Automatically add spaces to equal sign if not keyword
1297 (idlwave-action-and-binding "=" '(idlwave-expand-equal -
1 -
1))
1302 ;;; When expanding abbrevs and the abbrev hook moves backward, an extra
1303 ;;; space is inserted (this is the space typed by the user to expanded
1308 (modify-syntax-entry (string-to-char idlwave-abbrev-start-char
)
1309 "w" idlwave-mode-syntax-table
)
1312 (defvar idlwave-mode-abbrev-table nil
1313 "Abbreviation table used for IDLWAVE mode")
1314 (define-abbrev-table 'idlwave-mode-abbrev-table
())
1315 (let ((abbrevs-changed nil
) ;; mask the current value to avoid save
1316 (tb idlwave-mode-abbrev-table
)
1317 (c idlwave-abbrev-start-char
))
1321 (define-abbrev tb
(concat c
"c") "" (idlwave-code-abbrev idlwave-case
))
1322 (define-abbrev tb
(concat c
"f") "" (idlwave-code-abbrev idlwave-for
))
1323 (define-abbrev tb
(concat c
"fu") "" (idlwave-code-abbrev idlwave-function
))
1324 (define-abbrev tb
(concat c
"pr") "" (idlwave-code-abbrev idlwave-procedure
))
1325 (define-abbrev tb
(concat c
"r") "" (idlwave-code-abbrev idlwave-repeat
))
1326 (define-abbrev tb
(concat c
"w") "" (idlwave-code-abbrev idlwave-while
))
1327 (define-abbrev tb
(concat c
"i") "" (idlwave-code-abbrev idlwave-if
))
1328 (define-abbrev tb
(concat c
"elif") "" (idlwave-code-abbrev idlwave-elif
))
1330 ;; Keywords, system functions, conversion routines
1332 (define-abbrev tb
(concat c
"b") "begin" (idlwave-keyword-abbrev 0 t
))
1333 (define-abbrev tb
(concat c
"co") "common" (idlwave-keyword-abbrev 0 t
))
1334 (define-abbrev tb
(concat c
"cb") "byte()" (idlwave-keyword-abbrev 1))
1335 (define-abbrev tb
(concat c
"cx") "fix()" (idlwave-keyword-abbrev 1))
1336 (define-abbrev tb
(concat c
"cl") "long()" (idlwave-keyword-abbrev 1))
1337 (define-abbrev tb
(concat c
"cf") "float()" (idlwave-keyword-abbrev 1))
1338 (define-abbrev tb
(concat c
"cs") "string()" (idlwave-keyword-abbrev 1))
1339 (define-abbrev tb
(concat c
"cc") "complex()" (idlwave-keyword-abbrev 1))
1340 (define-abbrev tb
(concat c
"cd") "double()" (idlwave-keyword-abbrev 1))
1341 (define-abbrev tb
(concat c
"e") "else" (idlwave-keyword-abbrev 0 t
))
1342 (define-abbrev tb
(concat c
"ec") "endcase" 'idlwave-show-begin
)
1343 (define-abbrev tb
(concat c
"ee") "endelse" 'idlwave-show-begin
)
1344 (define-abbrev tb
(concat c
"ef") "endfor" 'idlwave-show-begin
)
1345 (define-abbrev tb
(concat c
"ei") "endif else if" 'idlwave-show-begin
)
1346 (define-abbrev tb
(concat c
"el") "endif else" 'idlwave-show-begin
)
1347 (define-abbrev tb
(concat c
"en") "endif" 'idlwave-show-begin
)
1348 (define-abbrev tb
(concat c
"er") "endrep" 'idlwave-show-begin
)
1349 (define-abbrev tb
(concat c
"ew") "endwhile" 'idlwave-show-begin
)
1350 (define-abbrev tb
(concat c
"g") "goto," (idlwave-keyword-abbrev 0 t
))
1351 (define-abbrev tb
(concat c
"h") "help," (idlwave-keyword-abbrev 0))
1352 (define-abbrev tb
(concat c
"k") "keyword_set()" (idlwave-keyword-abbrev 1))
1353 (define-abbrev tb
(concat c
"n") "n_elements()" (idlwave-keyword-abbrev 1))
1354 (define-abbrev tb
(concat c
"on") "on_error," (idlwave-keyword-abbrev 0))
1355 (define-abbrev tb
(concat c
"oi") "on_ioerror," (idlwave-keyword-abbrev 0 1))
1356 (define-abbrev tb
(concat c
"ow") "openw," (idlwave-keyword-abbrev 0))
1357 (define-abbrev tb
(concat c
"or") "openr," (idlwave-keyword-abbrev 0))
1358 (define-abbrev tb
(concat c
"ou") "openu," (idlwave-keyword-abbrev 0))
1359 (define-abbrev tb
(concat c
"p") "print," (idlwave-keyword-abbrev 0))
1360 (define-abbrev tb
(concat c
"pt") "plot," (idlwave-keyword-abbrev 0))
1361 (define-abbrev tb
(concat c
"re") "read," (idlwave-keyword-abbrev 0))
1362 (define-abbrev tb
(concat c
"rf") "readf," (idlwave-keyword-abbrev 0))
1363 (define-abbrev tb
(concat c
"ru") "readu," (idlwave-keyword-abbrev 0))
1364 (define-abbrev tb
(concat c
"rt") "return" (idlwave-keyword-abbrev 0))
1365 (define-abbrev tb
(concat c
"sc") "strcompress()" (idlwave-keyword-abbrev 1))
1366 (define-abbrev tb
(concat c
"sn") "strlen()" (idlwave-keyword-abbrev 1))
1367 (define-abbrev tb
(concat c
"sl") "strlowcase()" (idlwave-keyword-abbrev 1))
1368 (define-abbrev tb
(concat c
"su") "strupcase()" (idlwave-keyword-abbrev 1))
1369 (define-abbrev tb
(concat c
"sm") "strmid()" (idlwave-keyword-abbrev 1))
1370 (define-abbrev tb
(concat c
"sp") "strpos()" (idlwave-keyword-abbrev 1))
1371 (define-abbrev tb
(concat c
"st") "strput()" (idlwave-keyword-abbrev 1))
1372 (define-abbrev tb
(concat c
"sr") "strtrim()" (idlwave-keyword-abbrev 1))
1373 (define-abbrev tb
(concat c
"t") "then" (idlwave-keyword-abbrev 0 t
))
1374 (define-abbrev tb
(concat c
"u") "until" (idlwave-keyword-abbrev 0 t
))
1375 (define-abbrev tb
(concat c
"wu") "writeu," (idlwave-keyword-abbrev 0))
1376 (define-abbrev tb
(concat c
"ine") "if n_elements() eq 0 then"
1377 (idlwave-keyword-abbrev 11))
1378 (define-abbrev tb
(concat c
"inn") "if n_elements() ne 0 then"
1379 (idlwave-keyword-abbrev 11))
1380 (define-abbrev tb
(concat c
"np") "n_params()" (idlwave-keyword-abbrev 0))
1381 (define-abbrev tb
(concat c
"s") "size()" (idlwave-keyword-abbrev 1))
1382 (define-abbrev tb
(concat c
"wi") "widget_info()" (idlwave-keyword-abbrev 1))
1383 (define-abbrev tb
(concat c
"wc") "widget_control," (idlwave-keyword-abbrev 0))
1385 ;; This section is reserved words only. (From IDL user manual)
1387 (define-abbrev tb
"and" "and" (idlwave-keyword-abbrev 0 t
))
1388 (define-abbrev tb
"begin" "begin" (idlwave-keyword-abbrev 0 t
))
1389 (define-abbrev tb
"case" "case" (idlwave-keyword-abbrev 0 t
))
1390 (define-abbrev tb
"common" "common" (idlwave-keyword-abbrev 0 t
))
1391 (define-abbrev tb
"do" "do" (idlwave-keyword-abbrev 0 t
))
1392 (define-abbrev tb
"else" "else" (idlwave-keyword-abbrev 0 t
))
1393 (define-abbrev tb
"end" "end" 'idlwave-show-begin-check
)
1394 (define-abbrev tb
"endcase" "endcase" 'idlwave-show-begin-check
)
1395 (define-abbrev tb
"endelse" "endelse" 'idlwave-show-begin-check
)
1396 (define-abbrev tb
"endfor" "endfor" 'idlwave-show-begin-check
)
1397 (define-abbrev tb
"endif" "endif" 'idlwave-show-begin-check
)
1398 (define-abbrev tb
"endrep" "endrep" 'idlwave-show-begin-check
)
1399 (define-abbrev tb
"endwhi" "endwhi" 'idlwave-show-begin-check
)
1400 (define-abbrev tb
"endwhile" "endwhile" 'idlwave-show-begin-check
)
1401 (define-abbrev tb
"eq" "eq" (idlwave-keyword-abbrev 0 t
))
1402 (define-abbrev tb
"for" "for" (idlwave-keyword-abbrev 0 t
))
1403 (define-abbrev tb
"function" "function" (idlwave-keyword-abbrev 0 t
))
1404 (define-abbrev tb
"ge" "ge" (idlwave-keyword-abbrev 0 t
))
1405 (define-abbrev tb
"goto" "goto" (idlwave-keyword-abbrev 0 t
))
1406 (define-abbrev tb
"gt" "gt" (idlwave-keyword-abbrev 0 t
))
1407 (define-abbrev tb
"if" "if" (idlwave-keyword-abbrev 0 t
))
1408 (define-abbrev tb
"le" "le" (idlwave-keyword-abbrev 0 t
))
1409 (define-abbrev tb
"lt" "lt" (idlwave-keyword-abbrev 0 t
))
1410 (define-abbrev tb
"mod" "mod" (idlwave-keyword-abbrev 0 t
))
1411 (define-abbrev tb
"ne" "ne" (idlwave-keyword-abbrev 0 t
))
1412 (define-abbrev tb
"not" "not" (idlwave-keyword-abbrev 0 t
))
1413 (define-abbrev tb
"of" "of" (idlwave-keyword-abbrev 0 t
))
1414 (define-abbrev tb
"on_ioerror" "on_ioerror" (idlwave-keyword-abbrev 0 t
))
1415 (define-abbrev tb
"or" "or" (idlwave-keyword-abbrev 0 t
))
1416 (define-abbrev tb
"pro" "pro" (idlwave-keyword-abbrev 0 t
))
1417 (define-abbrev tb
"repeat" "repeat" (idlwave-keyword-abbrev 0 t
))
1418 (define-abbrev tb
"then" "then" (idlwave-keyword-abbrev 0 t
))
1419 (define-abbrev tb
"until" "until" (idlwave-keyword-abbrev 0 t
))
1420 (define-abbrev tb
"while" "while" (idlwave-keyword-abbrev 0 t
))
1421 (define-abbrev tb
"xor" "xor" (idlwave-keyword-abbrev 0 t
)))
1423 (defvar imenu-create-index-function
)
1424 (defvar extract-index-name-function
)
1425 (defvar prev-index-position-function
)
1426 (defvar imenu-extract-index-name-function
)
1427 (defvar imenu-prev-index-position-function
)
1428 ;; defined later - so just make the compiler shut up
1429 (defvar idlwave-mode-menu
)
1430 (defvar idlwave-mode-debug-menu
)
1433 (defun idlwave-mode ()
1434 "Major mode for editing IDL and WAVE CL .pro files.
1436 The main features of this mode are
1438 1. Indentation and Formatting
1439 --------------------------
1440 Like other Emacs programming modes, C-j inserts a newline and indents.
1441 TAB is used for explicit indentation of the current line.
1443 To start a continuation line, use \\[idlwave-split-line]. This function can also
1444 be used in the middle of a line to split the line at that point.
1445 When used inside a long constant string, the string is split at
1446 that point with the `+' concatenation operator.
1448 Comments are indented as follows:
1450 `;;;' Indentation remains unchanged.
1451 `;;' Indent like the surrounding code
1452 `;' Indent to a minimum column.
1454 The indentation of comments starting in column 0 is never changed.
1456 Use \\[idlwave-fill-paragraph] to refill a paragraph inside a comment. The indentation
1457 of the second line of the paragraph relative to the first will be
1458 retained. Use \\[idlwave-auto-fill-mode] to toggle auto-fill mode for these comments.
1459 When the variable `idlwave-fill-comment-line-only' is nil, code
1460 can also be auto-filled and auto-indented (not recommended).
1462 To convert pre-existing IDL code to your formatting style, mark the
1463 entire buffer with \\[mark-whole-buffer] and execute \\[idlwave-expand-region-abbrevs].
1464 Then mark the entire buffer again followed by \\[indent-region] (`indent-region').
1468 IDLWAVE displays information about the calling sequence and the accepted
1469 keyword parameters of a procedure or function with \\[idlwave-routine-info].
1470 \\[idlwave-find-module] jumps to the source file of a module.
1471 These commands know about system routines, all routines in idlwave-mode
1472 buffers and (when the idlwave-shell is active) about all modules
1473 currently compiled under this shell. Use \\[idlwave-update-routine-info] to update this
1474 information, which is also used for completion (see next item).
1478 \\[idlwave-complete] completes the names of procedures, functions and
1479 keyword parameters. It is context sensitive and figures out what
1480 is expected at point (procedure/function/keyword). Lower case
1481 strings are completed in lower case, other strings in mixed or
1484 4. Code Templates and Abbreviations
1485 --------------------------------
1486 Many Abbreviations are predefined to expand to code fragments and templates.
1487 The abbreviations start generally with a `\\`. Some examples
1489 \\pr PROCEDURE template
1490 \\fu FUNCTION template
1491 \\c CASE statement template
1492 \\f FOR loop template
1493 \\r REPEAT Loop template
1494 \\w WHILE loop template
1495 \\i IF statement template
1496 \\elif IF-ELSE statement template
1499 For a full list, use \\[idlwave-list-abbrevs]. Some templates also have
1500 direct keybindings - see the list of keybindings below.
1502 \\[idlwave-doc-header] inserts a documentation header at the beginning of the
1503 current program unit (pro, function or main). Change log entries
1504 can be added to the current program unit with \\[idlwave-doc-modification].
1506 5. Automatic Case Conversion
1507 -------------------------
1508 The case of reserved words and some abbrevs is controlled by
1509 `idlwave-reserved-word-upcase' and `idlwave-abbrev-change-case'.
1511 6. Automatic END completion
1512 ------------------------
1513 If the variable `idlwave-expand-generic-end' is non-nil, each END typed
1514 will be converted to the specific version, like ENDIF, ENDFOR, etc.
1518 Loading idlwave.el runs `idlwave-load-hook'.
1519 Turning on `idlwave-mode' runs `idlwave-mode-hook'.
1521 8. Documentation and Customization
1522 -------------------------------
1523 Info documentation for this package is available. Use \\[idlwave-info]
1524 to display (complain to your sysadmin if that does not work).
1525 For Postscript and HTML versions of the documentation, check IDLWAVE's
1526 homepage at `http://www.strw.leidenuniv.nl/~dominik/Tools/idlwave'.
1527 IDLWAVE has customize support - see the group `idlwave'.
1531 Here is a list of all keybindings of this mode.
1532 If some of the key bindings below show with ??, use \\[describe-key]
1533 followed by the key sequence to see what the key sequence does.
1535 \\{idlwave-mode-map}"
1538 (kill-all-local-variables)
1540 (if idlwave-startup-message
1541 (message "Emacs IDLWAVE mode version %s." idlwave-mode-version
))
1542 (setq idlwave-startup-message nil
)
1544 (setq local-abbrev-table idlwave-mode-abbrev-table
)
1545 (set-syntax-table idlwave-mode-syntax-table
)
1547 (set (make-local-variable 'indent-line-function
) 'idlwave-indent-and-action
)
1549 (make-local-variable idlwave-comment-indent-function
)
1550 (set idlwave-comment-indent-function
'idlwave-comment-hook
)
1552 (set (make-local-variable 'comment-start-skip
) ";+[ \t]*")
1553 (set (make-local-variable 'comment-start
) ";")
1554 (set (make-local-variable 'require-final-newline
) t
)
1555 (set (make-local-variable 'abbrev-all-caps
) t
)
1556 (set (make-local-variable 'indent-tabs-mode
) nil
)
1557 (set (make-local-variable 'completion-ignore-case
) t
)
1559 (use-local-map idlwave-mode-map
)
1561 (when (featurep 'easymenu
)
1562 (easy-menu-add idlwave-mode-menu idlwave-mode-map
)
1563 (easy-menu-add idlwave-mode-debug-menu idlwave-mode-map
))
1565 (setq mode-name
"IDLWAVE")
1566 (setq major-mode
'idlwave-mode
)
1567 (setq abbrev-mode t
)
1569 (set (make-local-variable idlwave-fill-function
) 'idlwave-auto-fill
)
1570 (setq comment-end
"")
1571 (set (make-local-variable 'comment-multi-line
) nil
)
1572 (set (make-local-variable 'paragraph-separate
) "[ \t\f]*$\\|[ \t]*;+[ \t]*$")
1573 (set (make-local-variable 'paragraph-start
) "[ \t\f]\\|[ \t]*;+[ \t]")
1574 (set (make-local-variable 'paragraph-ignore-fill-prefix
) nil
)
1575 (set (make-local-variable 'parse-sexp-ignore-comments
) nil
)
1577 ;; Set tag table list to use IDLTAGS as file name.
1578 (if (boundp 'tag-table-alist
)
1579 (add-to-list 'tag-table-alist
'("\\.pro$" .
"IDLTAGS")))
1581 ;; Font-lock additions - originally Phil Williams, then Ulrik Dickow
1582 ;; Following line is for Emacs - XEmacs uses the corresponding porperty
1583 ;; on the `idlwave-mode' symbol.
1584 (set (make-local-variable 'font-lock-defaults
) idlwave-font-lock-defaults
)
1587 (set (make-local-variable 'imenu-create-index-function
)
1588 'imenu-default-create-index-function
)
1589 (set (make-local-variable 'imenu-extract-index-name-function
)
1591 (set (make-local-variable 'imenu-prev-index-position-function
)
1592 'idlwave-prev-index-position
)
1594 ;; Make a local post-command-hook and add our hook to it
1595 (make-local-hook 'post-command-hook
)
1596 (add-hook 'post-command-hook
'idlwave-command-hook nil t
)
1598 ;; Run the mode hook
1599 (run-hooks 'idlwave-mode-hook
))
1602 ;; Done with start up and initialization code.
1603 ;; The remaining routines are the code formatting functions.
1606 (defun idlwave-push-mark (&rest rest
)
1607 "Push mark for compatibility with Emacs 18/19."
1608 (if (fboundp 'iconify-frame
)
1609 (apply 'push-mark rest
)
1612 (defun idlwave-hard-tab ()
1613 "Inserts TAB in buffer in current position."
1617 ;;; This stuff is experimental
1619 (defvar idlwave-command-hook nil
1620 "If non-nil, a list that can be evaluated using `eval'.
1621 It is evaluated in the lisp function `idlwave-command-hook' which is
1622 placed in `post-command-hook'.")
1624 (defun idlwave-command-hook ()
1625 "Command run after every command.
1626 Evaluates a non-nil value of the *variable* `idlwave-command-hook' and
1627 sets the variable to zero afterwards."
1628 (and idlwave-command-hook
1629 (listp idlwave-command-hook
)
1631 (eval idlwave-command-hook
)
1633 (setq idlwave-command-hook nil
))
1637 ;; It would be better to use expand.el for better abbrev handling and
1640 (defun idlwave-check-abbrev (arg &optional reserved
)
1641 "Reverses abbrev expansion if in comment or string.
1642 Argument ARG is the number of characters to move point
1643 backward if `idlwave-abbrev-move' is non-nil.
1644 If optional argument RESERVED is non-nil then the expansion
1645 consists of reserved words, which will be capitalized if
1646 `idlwave-reserved-word-upcase' is non-nil.
1647 Otherwise, the abbrev will be capitalized if `idlwave-abbrev-change-case'
1648 is non-nil, unless its value is \`down in which case the abbrev will be
1649 made into all lowercase.
1650 Returns non-nil if abbrev is left expanded."
1651 (if (idlwave-quoted)
1652 (progn (unexpand-abbrev)
1654 (if (and reserved idlwave-reserved-word-upcase
)
1655 (upcase-region last-abbrev-location
(point))
1657 ((equal idlwave-abbrev-change-case
'down
)
1658 (downcase-region last-abbrev-location
(point)))
1659 (idlwave-abbrev-change-case
1660 (upcase-region last-abbrev-location
(point)))))
1661 (if (and idlwave-abbrev-move
(> arg
0))
1662 (if (boundp 'post-command-hook
)
1663 (setq idlwave-command-hook
(list 'backward-char
(1+ arg
)))
1664 (backward-char arg
)))
1667 (defun idlwave-in-comment ()
1668 "Returns t if point is inside a comment, nil otherwise."
1670 (let ((here (point)))
1671 (and (idlwave-goto-comment) (> here
(point))))))
1673 (defun idlwave-goto-comment ()
1674 "Move to start of comment delimiter on current line.
1675 Moves to end of line if there is no comment delimiter.
1676 Ignores comment delimiters in strings.
1677 Returns point if comment found and nil otherwise."
1678 (let ((eos (progn (end-of-line) (point)))
1681 ;; Look for first comment delimiter not in a string
1683 (setq found
(search-forward comment-start eos
'lim
))
1684 (while (and found
(idlwave-in-quote))
1685 (setq found
(search-forward comment-start eos
'lim
)))
1686 (store-match-data data
)
1687 (and found
(not (idlwave-in-quote))
1692 (defun idlwave-show-matching-quote ()
1693 "Insert quote and show matching quote if this is end of a string."
1695 (let ((bq (idlwave-in-quote))
1696 (inq last-command-char
))
1697 (if (and bq
(not (idlwave-in-comment)))
1698 (let ((delim (char-after bq
)))
1704 ;; Not the end of a string
1707 (defun idlwave-show-begin-check ()
1708 "Ensure that the previous word was a token before `idlwave-show-begin'.
1709 An END token must be preceded by whitespace."
1714 (looking-at "[ \t\n\f]"))
1715 (idlwave-show-begin)))
1717 (defun idlwave-show-begin ()
1718 "Finds the start of current block and blinks to it for a second.
1719 Also checks if the correct end statement has been used."
1720 ;; All end statements are reserved words
1721 (let* ((pos (point))
1723 (when (and (idlwave-check-abbrev 0 t
)
1726 ;; Move inside current block
1727 (setq end
(buffer-substring
1728 (save-excursion (skip-chars-backward "a-zA-Z")
1731 (idlwave-beginning-of-statement)
1732 (idlwave-block-jump-out -
1 'nomark
)
1733 (when (setq end1
(cdr (idlwave-block-master)))
1735 ((null end1
)) ; no-opeartion
1736 ((string= (downcase end
) (downcase end1
))
1738 ((string= (downcase end
) "end")
1740 (if idlwave-expand-generic-end
1744 (insert (if (string= end
"END") (upcase end1
) end1
))
1749 (message "Warning: Shouldn't this be \"%s\" instead of \"%s\"?"
1753 (defun idlwave-block-master ()
1754 (let ((case-fold-search t
))
1757 ((looking-at "pro\\|case\\|function\\>")
1758 (assoc (downcase (match-string 0)) idlwave-block-matches
))
1759 ((looking-at "begin\\>")
1760 (let ((limit (save-excursion
1761 (idlwave-beginning-of-statement)
1764 ((re-search-backward idlwave-block-match-regexp limit t
)
1765 (assoc (downcase (match-string 1))
1766 idlwave-block-matches
))
1767 ;;((re-search-backward ":[ \t]*\\=" limit t)
1768 ;; ;; seems to be a case thing
1769 ;; '("begin" . "end"))
1771 ;; Just a nromal block
1772 '("begin" .
"end")))))
1775 (defun idlwave-close-block ()
1776 "Terminate the current block with the correct END statement."
1779 ;; Start new line if we are not in a new line
1780 (unless (save-excursion
1781 (skip-chars-backward " \t")
1783 (let ((idlwave-show-block nil
))
1784 (newline-and-indent)))
1786 ;; Check which end is needed and insert it.
1787 (let ((case-fold-search t
) end
)
1789 (idlwave-beginning-of-statement)
1790 (idlwave-block-jump-out -
1 'nomark
)
1791 (if (setq end
(idlwave-block-master))
1792 (setq end
(cdr end
))
1793 (error "Cannot close block")))
1797 (defun idlwave-surround (&optional before after escape-chars
)
1798 "Surround the character before point with blanks.
1799 Optional arguments BEFORE and AFTER affect the behavior before and
1800 after the previous character. See description of `idlwave-make-space'.
1802 The function does nothing if any of the following conditions is true:
1803 - `idlwave-surround-by-blank' is nil
1804 - the character before point is inside a string or comment
1806 When the character 2 positions before point is a member of
1807 ESCAPE-CHARS, BEFORE is forced to nil."
1809 (if (and idlwave-surround-by-blank
1810 (not (idlwave-quoted)))
1812 (if (memq (char-after (- (point) 2)) escape-chars
)
1816 (let ((here (point)))
1817 (skip-chars-backward " \t")
1819 ;; avoid clobbering indent
1821 (move-to-column (idlwave-calculate-indent))
1822 (if (<= (point) here
)
1823 (narrow-to-region (point) here
))
1825 (idlwave-make-space before
))
1826 (skip-chars-forward " \t"))
1828 (idlwave-make-space after
)
1829 ;; Check to see if the line should auto wrap
1830 (if (and (equal (char-after (1- (point))) ?
)
1831 (> (current-column) fill-column
))
1832 (funcall auto-fill-function
)))))
1834 (defun idlwave-make-space (n)
1835 "Make space at point.
1836 The space affected is all the spaces and tabs around point.
1837 If n is non-nil then point is left abs(n) spaces from the beginning of
1838 the contiguous space.
1839 The amount of space at point is determined by N.
1840 If the value of N is:
1842 c > 0 - exactly c spaces.
1843 c < 0 - a minimum of -c spaces, i.e., do not change if there are
1848 ((start-col (progn (skip-chars-backward " \t") (current-column)))
1850 (end-col (progn (skip-chars-forward " \t") (current-column))))
1851 (delete-horizontal-space)
1854 (idlwave-indent-to (+ start-col n
))
1855 (goto-char (+ left n
)))
1857 (idlwave-indent-to end-col
(- n
))
1858 (goto-char (- left n
)))
1862 (defun idlwave-newline ()
1863 "Inserts a newline and indents the current and previous line."
1866 ;; Handle unterminated single and double quotes
1867 ;; If not in a comment and in a string then insertion of a newline
1868 ;; will mean unbalanced quotes.
1870 (if (and (not (idlwave-in-comment)) (idlwave-in-quote))
1872 (message "Warning: unbalanced quotes?")))
1875 ;; The current line is being split, the cursor should be at the
1876 ;; beginning of the new line skipping the leading indentation.
1878 ;; The reason we insert the new line before indenting is that the
1879 ;; indenting could be confused by keywords (e.g. END) on the line
1880 ;; after the split point. This prevents us from just using
1881 ;; `indent-for-tab-command' followed by `newline-and-indent'.
1883 (beginning-of-line 0)
1884 (idlwave-indent-line)
1886 (idlwave-indent-line))
1889 ;; Use global variable 'comment-column' to set parallel comment
1891 ;; Modeled on lisp.el
1892 ;; Emacs Lisp and IDL (Wave CL) have identical comment syntax
1893 (defun idlwave-comment-hook ()
1894 "Compute indent for the beginning of the IDL comment delimiter."
1895 (if (or (looking-at idlwave-no-change-comment
)
1896 (if idlwave-begin-line-comment
1897 (looking-at idlwave-begin-line-comment
)
1900 (if (looking-at idlwave-code-comment
)
1901 (if (save-excursion (skip-chars-backward " \t") (bolp))
1902 ;; On line by itself, indent as code
1903 (let ((tem (idlwave-calculate-indent)))
1904 (if (listp tem
) (car tem
) tem
))
1905 ;; after code - do not change
1907 (skip-chars-backward " \t")
1908 (max (if (bolp) 0 (1+ (current-column)))
1911 (defun idlwave-split-line ()
1912 "Continue line by breaking line at point and indent the lines.
1913 For a code line insert continuation marker. If the line is a line comment
1914 then the new line will contain a comment with the same indentation.
1915 Splits strings with the IDL operator `+' if `idlwave-split-line-string' is
1919 (if (not (idlwave-in-comment))
1920 ;; For code line add continuation.
1921 ;; Check if splitting a string.
1923 (if (setq beg
(idlwave-in-quote))
1924 (if idlwave-split-line-string
1925 ;; Split the string.
1926 (progn (insert (setq beg
(char-after beg
)) " + "
1927 idlwave-continuation-char beg
)
1929 ;; Do not split the string.
1931 (message "Warning: continuation inside string!!")
1932 (insert " " idlwave-continuation-char
))
1933 ;; Not splitting a string.
1934 (insert " " idlwave-continuation-char
))
1935 (newline-and-indent))
1936 (indent-new-comment-line))
1937 ;; Indent previous line
1938 (setq beg
(- (point-max) (point)))
1940 (idlwave-indent-line)
1941 (goto-char (- (point-max) beg
))
1942 ;; Reindent new line
1943 (idlwave-indent-line)))
1945 (defun idlwave-beginning-of-subprogram ()
1946 "Moves point to the beginning of the current program unit."
1948 (idlwave-find-key idlwave-begin-unit-reg -
1))
1950 (defun idlwave-end-of-subprogram ()
1951 "Moves point to the start of the next program unit."
1953 (idlwave-end-of-statement)
1954 (idlwave-find-key idlwave-end-unit-reg
1))
1956 (defun idlwave-mark-statement ()
1957 "Mark current IDL statement."
1959 (idlwave-end-of-statement)
1960 (let ((end (point)))
1961 (idlwave-beginning-of-statement)
1962 (idlwave-push-mark end nil t
)))
1964 (defun idlwave-mark-block ()
1965 "Mark containing block."
1967 (idlwave-end-of-statement)
1968 (idlwave-backward-up-block -
1)
1969 (idlwave-end-of-statement)
1970 (let ((end (point)))
1971 (idlwave-backward-block)
1972 (idlwave-beginning-of-statement)
1973 (idlwave-push-mark end nil t
)))
1976 (defun idlwave-mark-subprogram ()
1977 "Put mark at beginning of program, point at end.
1978 The marks are pushed."
1980 (idlwave-end-of-statement)
1981 (idlwave-beginning-of-subprogram)
1982 (let ((beg (point)))
1983 (idlwave-forward-block)
1984 (idlwave-push-mark beg nil t
))
1985 (exchange-point-and-mark))
1987 (defun idlwave-backward-up-block (&optional arg
)
1988 "Move to beginning of enclosing block if prefix ARG >= 0.
1989 If prefix ARG < 0 then move forward to enclosing block end."
1991 (idlwave-block-jump-out (- arg
) 'nomark
))
1993 (defun idlwave-beginning-of-block ()
1994 "Go to the beginning of the current block."
1996 (idlwave-block-jump-out -
1 'nomark
)
1999 (defun idlwave-end-of-block ()
2000 "Go to the beginning of the current block."
2002 (idlwave-block-jump-out 1 'nomark
)
2005 (defun idlwave-forward-block ()
2006 "Move across next nested block."
2008 (if (idlwave-down-block 1)
2009 (idlwave-block-jump-out 1 'nomark
)))
2011 (defun idlwave-backward-block ()
2012 "Move backward across previous nested block."
2014 (if (idlwave-down-block -
1)
2015 (idlwave-block-jump-out -
1 'nomark
)))
2017 (defun idlwave-down-block (&optional arg
)
2019 With ARG: ARG >= 0 go forwards, ARG < 0 go backwards.
2020 Returns non-nil if successfull."
2025 (let ((eos (save-excursion
2026 (idlwave-block-jump-out -
1 'nomark
)
2028 (if (setq status
(idlwave-find-key
2029 idlwave-end-block-reg -
1 'nomark eos
))
2030 (idlwave-beginning-of-statement)
2031 (message "No nested block before beginning of containing block.")))
2033 (let ((eos (save-excursion
2034 (idlwave-block-jump-out 1 'nomark
)
2036 (if (setq status
(idlwave-find-key
2037 idlwave-begin-block-reg
1 'nomark eos
))
2038 (idlwave-end-of-statement)
2039 (message "No nested block before end of containing block."))))
2042 (defun idlwave-mark-doclib ()
2043 "Put point at beginning of doc library header, mark at end.
2044 The marks are pushed."
2048 (goto-char (point-max))
2049 (if (re-search-backward idlwave-doclib-start nil t
)
2051 (setq beg
(progn (beginning-of-line) (point)))
2052 (if (re-search-forward idlwave-doclib-end nil t
)
2055 (idlwave-push-mark beg nil t
)
2056 (message "Could not find end of doc library header.")))
2057 (message "Could not find doc library header start.")
2058 (goto-char here
)))))
2060 (defvar idlwave-shell-prompt-pattern
)
2061 (defun idlwave-beginning-of-statement ()
2062 "Move to beginning of the current statement.
2063 Skips back past statement continuations.
2064 Point is placed at the beginning of the line whether or not this is an
2068 ((eq major-mode
'idlwave-shell-mode
)
2069 (if (re-search-backward idlwave-shell-prompt-pattern nil t
)
2070 (goto-char (match-end 0))))
2072 (if (save-excursion (forward-line -
1) (idlwave-is-continuation-line))
2073 (idlwave-previous-statement)
2074 (beginning-of-line)))))
2076 (defun idlwave-previous-statement ()
2077 "Moves point to beginning of the previous statement.
2078 Returns t if the current line before moving is the beginning of
2079 the first non-comment statement in the file, and nil otherwise."
2081 (let (first-statement)
2082 (if (not (= (forward-line -
1) 0))
2083 ;; first line in file
2085 ;; skip blank lines, label lines, include lines and line comments
2087 ;; The current statement is the first statement until we
2088 ;; reach another statement.
2089 (setq first-statement
2091 (looking-at idlwave-comment-line-start-skip
)
2092 (looking-at "[ \t]*$")
2093 (looking-at (concat "[ \t]*" idlwave-label
"[ \t]*$"))
2095 (= (forward-line -
1) 0)))
2096 ;; skip continuation lines
2100 (idlwave-is-continuation-line))
2101 (= (forward-line -
1) 0)))
2104 ;; FIXME: end-of-statement does not work correctly when comment lines
2105 ;; are inside the statement. It does work correctly for line-end
2106 ;; comments, though.
2107 (defun idlwave-end-of-statement ()
2108 "Moves point to the end of the current IDL statement.
2109 If not in a statement just moves to end of line. Returns position."
2111 (while (and (idlwave-is-continuation-line)
2112 (= (forward-line 1) 0)))
2116 (defun idlwave-next-statement ()
2117 "Moves point to beginning of the next IDL statement.
2118 Returns t if that statement is the last
2119 non-comment IDL statement in the file, and nil otherwise."
2121 (let (last-statement)
2122 (idlwave-end-of-statement)
2123 ;; skip blank lines, label lines, include lines and line comments
2124 (while (and (= (forward-line 1) 0)
2125 ;; The current statement is the last statement until
2126 ;; we reach a new statement.
2127 (setq last-statement
2129 (looking-at idlwave-comment-line-start-skip
)
2130 (looking-at "[ \t]*$")
2131 (looking-at (concat "[ \t]*" idlwave-label
"[ \t]*$"))
2132 (looking-at "^@")))))
2135 (defun idlwave-skip-label ()
2136 "Skip label or case statement element.
2137 Returns position after label.
2138 If there is no label point is not moved and nil is returned."
2139 ;; Just look for the first non quoted colon and check to see if it
2140 ;; is inside a sexp. If is not in a sexp it must be part of a label
2141 ;; or case statement element.
2142 (let ((start (point))
2143 (end (idlwave-find-key ":" 1 'nomark
2145 (idlwave-end-of-statement) (point)))))
2147 (= (nth 0 (parse-partial-sexp start end
)) 0))
2154 (defun idlwave-start-of-substatement (&optional pre
)
2155 "Move to start of next IDL substatement after point.
2156 Uses the type of the current IDL statement to determine if the next
2157 statement is on a new line or is a subpart of the current statement.
2158 Returns point at start of substatement modulo whitespace.
2159 If optional argument is non-nil move to beginning of current
2161 (let ((orig (point))
2162 (eos (idlwave-end-of-statement))
2165 (idlwave-beginning-of-statement)
2166 (idlwave-skip-label)
2168 ;; Continue looking for substatements until we are past orig
2169 (while (and (<= (point) orig
) (not (eobp)))
2171 (setq nst
(nth 1 (cdr (setq st
(car (idlwave-statement-type))))))
2172 (if (equal (car st
) 'if
) (setq ifnest
(1+ ifnest
)))
2174 (idlwave-find-key nst
1 'nomark eos
))
2175 (goto-char (match-end 0)))
2176 ((and (> ifnest
0) (idlwave-find-key "\\<else\\>" 1 'nomark eos
))
2177 (setq ifnest
(1- ifnest
))
2178 (goto-char (match-end 0)))
2180 (idlwave-next-statement))))
2181 (if pre
(goto-char last
))
2184 (defun idlwave-statement-type ()
2185 "Return the type of the current IDL statement.
2186 Uses `idlwave-statement-match' to return a cons of (type . point) with
2187 point the ending position where the type was determined. Type is the
2188 association from `idlwave-statement-match', i.e. the cons cell from the
2189 list not just the type symbol. Returns nil if not an identifiable
2192 ;; Skip whitespace within a statement which is spaces, tabs, continuations
2193 (while (looking-at "[ \t]*\\<\\$")
2195 (skip-chars-forward " \t")
2196 (let ((st idlwave-statement-match
)
2197 (case-fold-search t
))
2198 (while (and (not (looking-at (nth 0 (cdr (car st
)))))
2199 (setq st
(cdr st
))))
2201 (append st
(match-end 0))))))
2203 (defun idlwave-expand-equal (&optional before after
)
2204 "Pad '=' with spaces.
2205 Two cases: Assignment statement, and keyword assignment.
2206 The case is determined using `idlwave-start-of-substatement' and
2207 `idlwave-statement-type'.
2208 The equal sign will be surrounded by BEFORE and AFTER blanks.
2209 If `idlwave-pad-keyword' is non-nil then keyword
2210 assignment is treated just like assignment statements. Otherwise,
2211 spaces are removed for keyword assignment.
2212 Limits in for loops are treated as keyword assignment.
2213 See `idlwave-surround'. "
2214 ;; Even though idlwave-surround checks `idlwave-surround-by-blank' this
2215 ;; check saves the time of finding the statement type.
2216 (if idlwave-surround-by-blank
2217 (let ((st (save-excursion
2218 (idlwave-start-of-substatement t
)
2219 (idlwave-statement-type))))
2221 (and (equal (car (car st
)) 'assign
)
2222 (equal (cdr st
) (point)))
2223 idlwave-pad-keyword
)
2224 ;; An assignment statement
2225 (idlwave-surround before after
)
2226 (idlwave-surround 0 0)))))
2228 (defun idlwave-indent-and-action ()
2229 "Call `idlwave-indent-line' and do expand actions."
2231 (idlwave-indent-line t
)
2234 (defun idlwave-indent-line (&optional expand
)
2235 "Indents current IDL line as code or as a comment.
2236 The actions in `idlwave-indent-action-table' are performed.
2237 If the optional argument EXPAND is non-nil then the actions in
2238 `idlwave-indent-expand-table' are performed."
2240 ;; Move point out of left margin.
2242 (skip-chars-backward " \t")
2244 (skip-chars-forward " \t"))
2245 (let ((mloc (point-marker)))
2248 (if (looking-at idlwave-comment-line-start-skip
)
2249 ;; Indentation for a line comment
2251 (skip-chars-forward " \t")
2252 (idlwave-indent-left-margin (idlwave-comment-hook)))
2256 ;; Before indenting, run action routines.
2258 (if (and expand idlwave-do-actions
)
2259 (mapcar 'idlwave-do-action idlwave-indent-expand-table
))
2261 (if idlwave-do-actions
2262 (mapcar 'idlwave-do-action idlwave-indent-action-table
))
2264 ;; No longer expand abbrevs on the line. The user can do this
2265 ;; manually using expand-region-abbrevs.
2267 ;; Indent for code line
2272 (looking-at (concat "^" idlwave-label
"[ \t]*$"))
2274 (looking-at "^[ \t]*@"))
2278 (idlwave-indent-left-margin (idlwave-calculate-indent)))
2279 ;; Adjust parallel comment
2281 (if (idlwave-in-comment)
2282 (indent-for-comment))))
2284 ;; Get rid of marker
2285 (set-marker mloc nil
)
2288 (defun idlwave-do-action (action)
2289 "Perform an action repeatedly on a line.
2290 ACTION is a list (REG . FUNC). REG is a regular expression. FUNC is
2291 either a function name to be called with `funcall' or a list to be
2292 evaluated with `eval'. The action performed by FUNC should leave point
2293 after the match for REG - otherwise an infinite loop may be entered."
2294 (let ((action-key (car action
))
2295 (action-routine (cdr action
)))
2297 (while (idlwave-look-at action-key
)
2298 (if (listp action-routine
)
2299 (eval action-routine
)
2300 (funcall action-routine
)))))
2302 (defun idlwave-indent-to (col &optional min
)
2303 "Indent from point with spaces until column COL.
2304 Inserts space before markers at point."
2305 (if (not min
) (setq min
0))
2306 (insert-before-markers
2307 (make-string (max min
(- col
(current-column))) ?
)))
2309 (defun idlwave-indent-left-margin (col)
2310 "Indent the current line to column COL.
2311 Indents such that first non-whitespace character is at column COL
2312 Inserts spaces before markers at point."
2315 (delete-horizontal-space)
2316 (idlwave-indent-to col
)))
2318 (defun idlwave-indent-subprogram ()
2319 "Indents program unit which contains point."
2322 (idlwave-end-of-statement)
2323 (idlwave-beginning-of-subprogram)
2324 (let ((beg (point)))
2325 (idlwave-forward-block)
2326 (message "Indenting subprogram...")
2327 (indent-region beg
(point) nil
))
2328 (message "Indenting subprogram...done.")))
2330 (defun idlwave-calculate-indent ()
2331 "Return appropriate indentation for current line as IDL code."
2335 ;; Check for beginning of unit - main (beginning of buffer), pro, or
2337 ((idlwave-look-at idlwave-begin-unit-reg
)
2339 ;; Check for continuation line
2341 (and (= (forward-line -
1) 0)
2342 (idlwave-is-continuation-line)))
2343 (idlwave-calculate-cont-indent))
2344 ;; calculate indent based on previous and current statements
2345 (t (let ((the-indent
2346 ;; calculate indent based on previous statement
2349 ((idlwave-previous-statement)
2352 ((idlwave-look-at idlwave-begin-unit-reg t
)
2353 (+ (idlwave-current-statement-indent)
2354 idlwave-main-block-indent
))
2356 ((idlwave-look-at idlwave-begin-block-reg t
)
2357 (+ (idlwave-current-statement-indent)
2358 idlwave-block-indent
))
2359 ((idlwave-look-at idlwave-end-block-reg t
)
2360 (- (idlwave-current-statement-indent)
2362 idlwave-block-indent
))
2363 ((idlwave-current-statement-indent))))))
2364 ;; adjust the indentation based on the current statement
2367 ((idlwave-look-at idlwave-end-block-reg t
)
2368 (+ the-indent idlwave-end-offset
))
2372 ;; Parenthesses balacing/indent
2375 (defun idlwave-calculate-cont-indent ()
2376 "Calculates the IDL continuation indent column from the previous statement.
2377 Note that here previous statement means the beginning of the current
2378 statement if this statement is a continuation of the previous line.
2379 Intervening comments or comments within the previous statement can
2380 screw things up if the comments contain parentheses characters."
2383 (case-fold-search t
)
2384 (end-reg (progn (beginning-of-line) (point)))
2385 (close-exp (progn (skip-chars-forward " \t") (looking-at "\\s)")))
2386 (beg-reg (progn (idlwave-previous-statement) (point))))
2388 ;; If PRO or FUNCTION declaration indent after name, and first comma.
2390 (if (idlwave-look-at "\\<\\(pro\\|function\\)\\>")
2393 (if (looking-at "[ \t]*,[ \t]*")
2394 (goto-char (match-end 0)))
2397 ;; Not a PRO or FUNCTION
2399 ;; Look for innermost unmatched open paren
2401 (if (setq open
(car (cdr (parse-partial-sexp beg-reg end-reg
))))
2402 ;; Found innermost open paren.
2405 ;; Line up with next word unless this is a closing paren.
2407 ;; This is a closed paren - line up under open paren.
2410 ;; Empty - just add regular indent. Take into account
2415 (looking-at "[ \t$]*$"))
2416 (+ (current-column) idlwave-continuation-indent -
1))
2417 ;; Line up with first word
2419 (skip-chars-forward " \t")
2420 (current-column)))))
2421 ;; No unmatched open paren. Just a simple continuation.
2423 (+ (idlwave-current-indent)
2424 ;; Make adjustments based on current line
2429 (skip-chars-forward " \t")
2430 (looking-at "else"))
2432 ;; Ordinary continuation
2433 (idlwave-continuation-indent))))))))
2435 (defun idlwave-find-key (key-reg &optional dir nomark limit
)
2436 "Move in direction of the optional second argument DIR to the
2437 next keyword not contained in a comment or string and occurring before
2438 optional fourth argument LIMIT. DIR defaults to forward direction. If
2439 DIR is negative the search is backwards, otherwise, it is
2440 forward. LIMIT defaults to the beginning or end of the buffer
2441 according to the direction of the search. The keyword is given by the
2442 regular expression argument KEY-REG. The search is case insensitive.
2443 Returns position if successful and nil otherwise. If found
2444 `push-mark' is executed unless the optional third argument NOMARK is
2445 non-nil. If found, the point is left at the keyword beginning."
2446 (or dir
(setq dir
0))
2447 (or limit
(setq limit
(cond ((>= dir
0) (point-max)) ((point-min)))))
2449 (old-syntax-table (syntax-table))
2450 (case-fold-search t
))
2453 (set-syntax-table idlwave-find-symbol-syntax-table
)
2455 (while (and (setq found
(and
2456 (re-search-forward key-reg limit t
)
2457 (match-beginning 0)))
2460 (while (and (setq found
(and
2461 (re-search-backward key-reg limit t
)
2462 (match-beginning 0)))
2465 (set-syntax-table old-syntax-table
))
2467 (if (not nomark
) (push-mark))
2468 (goto-char found
)))))
2470 (defun idlwave-block-jump-out (&optional dir nomark
)
2471 "When optional argument DIR is non-negative, move forward to end of
2472 current block using the `idlwave-begin-block-reg' and `idlwave-end-block-reg'
2473 regular expressions. When DIR is negative, move backwards to block beginning.
2474 Recursively calls itself to skip over nested blocks. DIR defaults to
2475 forward. Calls `push-mark' unless the optional argument NOMARK is
2476 non-nil. Movement is limited by the start of program units because of
2477 possibility of unbalanced blocks."
2479 (or dir
(setq dir
0))
2480 (let* ((here (point))
2481 (case-fold-search t
)
2482 (limit (if (>= dir
0) (point-max) (point-min)))
2483 (block-limit (if (>= dir
0)
2484 idlwave-begin-block-reg
2485 idlwave-end-block-reg
))
2487 (block-reg (concat idlwave-begin-block-reg
"\\|"
2488 idlwave-end-block-reg
))
2489 (unit-limit (or (save-excursion
2492 idlwave-begin-unit-reg dir t limit
)
2495 idlwave-end-unit-reg dir t limit
)))
2497 (if (>= dir
0) (end-of-line)) ;Make sure we are in current block
2498 (if (setq found
(idlwave-find-key block-reg dir t unit-limit
))
2499 (while (and found
(looking-at block-limit
))
2500 (if (>= dir
0) (forward-word 1))
2501 (idlwave-block-jump-out dir t
)
2502 (setq found
(idlwave-find-key block-reg dir t unit-limit
))))
2503 (if (not nomark
) (push-mark here
))
2504 (if (not found
) (goto-char unit-limit
)
2505 (if (>= dir
0) (forward-word 1)))))
2507 (defun idlwave-current-statement-indent ()
2508 "Return indentation of the current statement.
2509 If in a statement, moves to beginning of statement before finding indent."
2510 (idlwave-beginning-of-statement)
2511 (idlwave-current-indent))
2513 (defun idlwave-current-indent ()
2514 "Return the column of the indentation of the current line.
2515 Skips any whitespace. Returns 0 if the end-of-line follows the whitespace."
2518 (skip-chars-forward " \t")
2519 ;; if we are at the end of blank line return 0
2521 ((current-column)))))
2523 (defun idlwave-is-continuation-line ()
2524 "Tests if current line is continuation line."
2526 (idlwave-look-at "\\<\\$")))
2528 (defun idlwave-is-comment-line ()
2530 (beginning-of-line 1)
2531 (looking-at "[ \t]*;")))
2533 (defun idlwave-look-at (regexp &optional cont beg
)
2534 "Searches current line from current point for the regular expression
2535 REGEXP. If optional argument CONT is non-nil, searches to the end of
2536 the current statement. If optional arg BEG is non-nil, search starts
2537 from the beginning of the current statement. Ignores matches that end
2538 in a comment or inside a string expression. Returns point if
2539 successful, nil otherwise. This function produces unexpected results
2540 if REGEXP contains quotes or a comment delimiter. The search is case
2541 insensitive. If successful leaves point after the match, otherwise,
2542 does not move point."
2543 (let ((here (point))
2544 (old-syntax-table (syntax-table))
2545 (case-fold-search t
)
2550 (set-syntax-table idlwave-find-symbol-syntax-table
)
2553 (save-excursion (idlwave-end-of-statement) (point))
2554 (save-excursion (end-of-line) (point))))
2555 (if beg
(idlwave-beginning-of-statement))
2556 (while (and (setq found
(re-search-forward regexp eos t
))
2558 (set-syntax-table old-syntax-table
))
2559 (if (not found
) (goto-char here
))
2562 (defun idlwave-fill-paragraph (&optional nohang
)
2563 "Fills paragraphs in comments.
2564 A paragraph is made up of all contiguous lines having the same comment
2565 leader (the leading whitespace before the comment delimiter and the
2566 comment delimiter). In addition, paragraphs are separated by blank
2567 line comments. The indentation is given by the hanging indent of the
2568 first line, otherwise by the minimum indentation of the lines after
2569 the first line. The indentation of the first line does not change.
2570 Does not effect code lines. Does not fill comments on the same line
2571 with code. The hanging indent is given by the end of the first match
2572 matching `idlwave-hang-indent-regexp' on the paragraph's first line . If the
2573 optional argument NOHANG is non-nil then the hanging indent is
2576 ;; check if this is a line comment
2579 (skip-chars-forward " \t")
2580 (looking-at comment-start
))
2583 pre here diff fill-prefix-reg bcl first-indent
2585 ;; Change tabs to spaces in the surrounding paragraph.
2586 ;; The surrounding paragraph will be the largest containing block of
2587 ;; contiguous line comments. Thus, we may be changing tabs in
2588 ;; a much larger area than is needed, but this is the easiest
2589 ;; brute force way to do it.
2591 ;; This has the undesirable side effect of replacing the tabs
2592 ;; permanently without the user's request or knowledge.
2594 (backward-paragraph)
2595 (setq start
(point)))
2599 (untabify start end
)
2605 (concat "^[ \t]*" comment-start
"+")
2606 (save-excursion (end-of-line) (point))
2608 ;; Get the comment leader on the line and its length
2609 (setq pre
(current-column))
2610 ;; the comment leader is the indentation plus exactly the
2611 ;; number of consecutive ";".
2612 (setq fill-prefix-reg
2616 (buffer-substring (save-excursion
2617 (beginning-of-line) (point))
2621 ;; Mark the beginning and end of the paragraph
2623 (while (and (looking-at fill-prefix-reg
)
2624 (not (looking-at paragraph-separate
))
2627 ;; Move to first line of paragraph
2628 (if (/= (point) bcl
)
2630 (setq start
(point))
2632 (while (and (looking-at fill-prefix-reg
)
2633 (not (looking-at paragraph-separate
))
2637 (if (or (not (looking-at fill-prefix-reg
))
2638 (looking-at paragraph-separate
))
2641 ;; if at end of buffer add a newline (need this because
2642 ;; fill-region needs END to be at the beginning of line after
2643 ;; the paragraph or it will add a line).
2645 (progn (insert ?
\n) (backward-char 1)))
2646 ;; Set END to the beginning of line after the paragraph
2647 ;; END is calculated as distance from end of buffer
2648 (setq end
(- (point-max) (point) 1))
2650 ;; Calculate the indentation for the paragraph.
2652 ;; In the following while statements, after one iteration
2653 ;; point will be at the beginning of a line in which case
2654 ;; the while will not be executed for the
2655 ;; the first paragraph line and thus will not affect the
2658 ;; First check to see if indentation is based on hanging indent.
2659 (if (and (not nohang
) idlwave-hanging-indent
2663 (idlwave-calc-hanging-indent))))
2664 ;; Adjust lines of paragraph by inserting spaces so that
2665 ;; each line's indent is at least as great as the hanging
2666 ;; indent. This is needed for fill-paragraph to work with
2671 (while (> (point) start
)
2672 (re-search-forward comment-start-skip
2673 (save-excursion (end-of-line) (point))
2675 (if (> (setq diff
(- indent
(current-column))) 0)
2677 (if (>= here
(point))
2678 ;; adjust the original location for the
2680 (setq here
(+ here diff
)))
2681 (insert (make-string diff ?
))))
2685 ;; No hang. Instead find minimum indentation of paragraph
2686 ;; after first line.
2687 ;; For the following while statement, since START is at the
2688 ;; beginning of line and END is at the the end of line
2689 ;; point is greater than START at least once (which would
2690 ;; be the case for a single line paragraph).
2691 (while (> (point) start
)
2698 (save-excursion (end-of-line) (point))
2703 (setq fill-prefix
(concat fill-prefix
2704 (make-string (- indent pre
)
2706 ;; first-line indent
2712 (save-excursion (end-of-line) (point))
2717 ;; try to keep point at its original place
2720 ;; In place of the more modern fill-region-as-paragraph, a hack
2721 ;; to keep whitespace untouched on the first line within the
2722 ;; indent length and to preserve any indent on the first line
2726 (buffer-substring start
(+ start first-indent -
1)))
2727 (subst-char-in-region start
(+ start first-indent -
1) ? ?~ nil
)
2728 (fill-region-as-paragraph
2731 (current-justification)
2733 (delete-region start
(+ start first-indent -
1))
2736 ;; When we want the point at the beginning of the comment
2737 ;; body fill-region will put it at the beginning of the line.
2738 (if (bolp) (skip-chars-forward (concat " \t" comment-start
)))
2739 (setq fill-prefix nil
))))
2741 (defun idlwave-calc-hanging-indent ()
2742 "Calculate the position of the hanging indent for the comment
2743 paragraph. The hanging indent position is given by the first match
2744 with the `idlwave-hang-indent-regexp'. If `idlwave-use-last-hang-indent' is
2745 non-nil then use last occurrence matching `idlwave-hang-indent-regexp' on
2747 If not found returns nil."
2748 (if idlwave-use-last-hang-indent
2751 (if (re-search-backward
2752 idlwave-hang-indent-regexp
2753 (save-excursion (beginning-of-line) (point))
2755 (+ (current-column) (length idlwave-hang-indent-regexp
))))
2758 (if (re-search-forward
2759 idlwave-hang-indent-regexp
2760 (save-excursion (end-of-line) (point))
2762 (current-column)))))
2764 (defun idlwave-auto-fill ()
2765 "Called to break lines in auto fill mode.
2766 Only fills comment lines if `idlwave-fill-comment-line-only' is non-nil.
2767 Places a continuation character at the end of the line
2768 if not in a comment. Splits strings with IDL concatenation operator
2769 `+' if `idlwave-auto-fill-split-string is non-nil."
2770 (if (<= (current-column) fill-column
)
2771 nil
; do not to fill
2772 (if (or (not idlwave-fill-comment-line-only
)
2774 ;; Check for comment line
2776 (looking-at idlwave-comment-line-start-skip
)))
2778 (idlwave-indent-line)
2779 ;; Prevent actions do-auto-fill which calls indent-line-function.
2780 (let (idlwave-do-actions
2781 (paragraph-start ".")
2782 (paragraph-separate "."))
2786 ;; Indent the split line
2787 (idlwave-indent-line)
2791 (looking-at idlwave-comment-line-start-skip
))
2792 ;; A continued line comment
2793 ;; We treat continued line comments as part of a comment
2794 ;; paragraph. So we check for a hanging indent.
2795 (if idlwave-hanging-indent
2796 (let ((here (- (point-max) (point)))
2800 (idlwave-calc-hanging-indent))))
2803 ;; Remove whitespace between comment delimiter and
2804 ;; text, insert spaces for appropriate indentation.
2808 (save-excursion (end-of-line) (point)) t
)
2809 (delete-horizontal-space)
2810 (idlwave-indent-to indent
)
2811 (goto-char (- (point-max) here
)))
2813 ;; Split code or comment?
2816 (idlwave-in-comment))
2817 ;; Splitting a non-line comment.
2818 ;; Insert the comment delimiter from split line
2822 (skip-chars-forward " \t")
2823 ;; Insert blank to keep off beginning of line
2827 (buffer-substring (idlwave-goto-comment)
2829 (skip-chars-forward "; ")
2831 (idlwave-indent-line))
2832 ;; Split code line - add continuation character
2835 ;; Check to see if we split a string
2836 (if (and (setq beg
(idlwave-in-quote))
2837 idlwave-auto-fill-split-string
)
2838 ;; Split the string and concatenate.
2839 ;; The first extra space is for the space
2840 ;; the line was split. That space was removed.
2841 (insert " " (char-after beg
) " +"))
2844 (if idlwave-auto-fill-split-string
2845 ;; Make the second part of continued string
2848 (skip-chars-forward " \t")
2849 (insert (char-after beg
)))
2852 (message "Warning: continuation inside a string.")))
2853 ;; Although do-auto-fill (via indent-new-comment-line) calls
2854 ;; idlwave-indent-line for the new line, re-indent again
2855 ;; because of the addition of the continuation character.
2856 (idlwave-indent-line))
2859 (defun idlwave-auto-fill-mode (arg)
2860 "Toggle auto-fill mode for IDL mode.
2861 With arg, turn auto-fill mode on if arg is positive.
2862 In auto-fill mode, inserting a space at a column beyond `fill-column'
2863 automatically breaks the line at a previous space."
2865 (prog1 (set idlwave-fill-function
2867 (not (symbol-value idlwave-fill-function
))
2868 (> (prefix-numeric-value arg
) 0))
2872 (set-buffer-modified-p (buffer-modified-p))))
2874 (defun idlwave-doc-header (&optional nomark
)
2875 "Insert a documentation header at the beginning of the unit.
2876 Inserts the value of the variable idlwave-file-header. Sets mark before
2877 moving to do insertion unless the optional prefix argument NOMARK
2880 (or nomark
(push-mark))
2881 ;; make sure we catch the current line if it begins the unit
2883 (idlwave-beginning-of-subprogram)
2885 ;; skip function or procedure line
2886 (if (idlwave-look-at "\\<\\(pro\\|function\\)\\>")
2888 (idlwave-end-of-statement)
2889 (if (> (forward-line 1) 0) (insert "\n"))))
2890 (if idlwave-file-header
2891 (cond ((car idlwave-file-header
)
2892 (insert-file (car idlwave-file-header
)))
2893 ((stringp (car (cdr idlwave-file-header
)))
2894 (insert (car (cdr idlwave-file-header
)))))))
2897 (defun idlwave-default-insert-timestamp ()
2898 "Default timestamp insertion function"
2899 (insert (current-time-string))
2900 (insert ", " (user-full-name))
2901 (insert " <" (user-login-name) "@" (system-name) ">")
2902 ;; Remove extra spaces from line
2903 (idlwave-fill-paragraph)
2904 ;; Insert a blank line comment to separate from the date entry -
2905 ;; will keep the entry from flowing onto date line if re-filled.
2906 (insert "\n;\n;\t\t"))
2908 (defun idlwave-doc-modification ()
2909 "Insert a brief modification log at the beginning of the current program.
2910 Looks for an occurrence of the value of user variable
2911 `idlwave-doc-modifications-keyword' if non-nil. Inserts time and user name
2912 and places the point for the user to add a log. Before moving, saves
2913 location on mark ring so that the user can return to previous point."
2916 ;; make sure we catch the current line if it begins the unit
2918 (idlwave-beginning-of-subprogram)
2919 (let ((pro (idlwave-look-at "\\<\\(function\\|pro\\)\\>"))
2920 (case-fold-search nil
))
2921 (if (re-search-forward
2922 (concat idlwave-doc-modifications-keyword
":")
2923 ;; set search limit at next unit beginning
2924 (save-excursion (idlwave-end-of-subprogram) (point))
2927 ;; keyword not present, insert keyword
2928 (if pro
(idlwave-next-statement)) ; skip past pro or function statement
2930 (insert "\n" comment-start
"\n")
2932 (insert comment-start
" " idlwave-doc-modifications-keyword
":")))
2936 (run-hooks 'idlwave-timestamp-hook
))
2939 ;;; Interface to expand-region-abbrevs which did not work when the
2940 ;;; abbrev hook associated with an abbrev moves point backwards
2941 ;;; after abbrev expansion, e.g., as with the abbrev '.n'.
2942 ;;; The original would enter an infinite loop in attempting to expand
2943 ;;; .n (it would continually expand and unexpand the abbrev without expanding
2944 ;;; because the point would keep going back to the beginning of the
2945 ;;; abbrev instead of to the end of the abbrev). We now keep the
2946 ;;; abbrev hook from moving backwards.
2948 (defun idlwave-expand-region-abbrevs (start end
)
2949 "Expand each abbrev occurrence in the region.
2950 Calling from a program, arguments are START END."
2953 (goto-char (min start end
))
2954 (let ((idlwave-show-block nil
) ;Do not blink
2955 (idlwave-abbrev-move nil
)) ;Do not move
2956 (expand-region-abbrevs start end
'noquery
))))
2958 (defun idlwave-quoted ()
2959 "Returns t if point is in a comment or quoted string.
2961 (or (idlwave-in-comment) (idlwave-in-quote)))
2963 (defun idlwave-in-quote ()
2964 "Returns location of the opening quote
2965 if point is in a IDL string constant, nil otherwise.
2966 Ignores comment delimiters on the current line.
2967 Properly handles nested quotation marks and octal
2968 constants - a double quote followed by an octal digit."
2969 ;;; Treat an octal inside an apostrophe to be a normal string. Treat a
2970 ;;; double quote followed by an octal digit to be an octal constant
2971 ;;; rather than a string. Therefore, there is no terminating double
2974 ;; Because single and double quotes can quote each other we must
2975 ;; search for the string start from the beginning of line.
2976 (let* ((start (point))
2977 (eol (progn (end-of-line) (point)))
2978 (bq (progn (beginning-of-line) (point)))
2983 (while (< endq start
)
2984 ;; Find string start
2985 ;; Don't find an octal constant beginning with a double quote
2986 (if (re-search-forward "\"[^0-7]\\|'\\|\"$" eol
'lim
)
2987 ;; Find the string end.
2988 ;; In IDL, two consecutive delimiters after the start of a
2990 ;; escape for the delimiter in the string.
2991 ;; Two consecutive delimiters alone (i.e., not after the
2992 ;; start of a string) is the the null string.
2994 ;; Move to position after quote
2995 (goto-char (1+ (match-beginning 0)))
2996 (setq bq
(1- (point)))
2997 ;; Get the string delimiter
2998 (setq delim
(char-to-string (preceding-char)))
2999 ;; Check for null string
3000 (if (looking-at delim
)
3001 (progn (setq endq
(point)) (forward-char 1))
3002 ;; Look for next unpaired delimiter
3003 (setq found
(search-forward delim eol
'lim
))
3004 (while (looking-at delim
)
3006 (setq found
(search-forward delim eol
'lim
)))
3008 (setq endq
(- (point) 1))
3009 (setq endq
(point)))
3011 (progn (setq bq
(point)) (setq endq
(point)))))
3012 (store-match-data data
)
3013 ;; return string beginning position or nil
3014 (if (> start bq
) bq
))))
3016 ;; Statement templates
3018 ;; Replace these with a general template function, something like
3019 ;; expand.el (I think there was also something with a name similar to
3022 (defun idlwave-template (s1 s2
&optional prompt noindent
)
3023 "Build a template with optional prompt expression.
3025 Opens a line if point is not followed by a newline modulo intervening
3026 whitespace. S1 and S2 are strings. S1 is inserted at point followed
3027 by S2. Point is inserted between S1 and S2. If optional argument
3028 PROMPT is a string then it is displayed as a message in the
3029 minibuffer. The PROMPT serves as a reminder to the user of an
3030 expression to enter.
3032 The lines containing S1 and S2 are reindented using `indent-region'
3033 unless the optional second argument NOINDENT is non-nil."
3034 (let ((beg (save-excursion (beginning-of-line) (point)))
3036 (if (not (looking-at "\\s-*\n"))
3043 (indent-region beg end nil
))
3044 (if (stringp prompt
)
3047 (defun idlwave-elif ()
3048 "Build skeleton IDL if-else block."
3050 (idlwave-template "if"
3051 " then begin\n\nendif else begin\n\nendelse"
3052 "Condition expression"))
3054 (defun idlwave-case ()
3055 "Build skeleton IDL case statement."
3057 (idlwave-template "case" " of\n\nendcase" "Selector expression"))
3059 (defun idlwave-for ()
3060 "Build skeleton for loop statment."
3062 (idlwave-template "for" " do begin\n\nendfor" "Loop expression"))
3064 (defun idlwave-if ()
3065 "Build skeleton for loop statment."
3067 (idlwave-template "if" " then begin\n\nendif" "Scalar logical expression"))
3069 (defun idlwave-procedure ()
3071 (idlwave-template "pro" "\n\nreturn\nend" "Procedure name"))
3073 (defun idlwave-function ()
3075 (idlwave-template "function" "\n\nreturn\nend" "Function name"))
3077 (defun idlwave-repeat ()
3079 (idlwave-template "repeat begin\n\nendrep until" "" "Exit condition"))
3081 (defun idlwave-while ()
3083 (idlwave-template "while" " do begin\n\nendwhile" "Entry condition"))
3085 (defun idlwave-split-string (string &optional pattern
)
3086 "Return a list of substrings of STRING which are separated by PATTERN.
3087 If PATTERN is omitted, it defaults to \"[ \\f\\t\\n\\r\\v]+\"."
3089 (setq pattern
"[ \f\t\n\r\v]+"))
3090 (let (parts (start 0))
3091 (while (string-match pattern string start
)
3092 (setq parts
(cons (substring string start
(match-beginning 0)) parts
)
3093 start
(match-end 0)))
3094 (nreverse (cons (substring string start
) parts
))))
3096 (defun idlwave-replace-string (string replace_string replace_with
)
3098 (last (length string
))
3101 (while (setq end
(string-match replace_string string start
))
3103 (concat ret_string
(substring string start end
) replace_with
))
3104 (setq start
(match-end 0)))
3105 (setq ret_string
(concat ret_string
(substring string start last
)))))
3107 (defun idlwave-get-buffer-visiting (file)
3108 ;; Return the buffer currently visiting FILE
3110 ((boundp 'find-file-compare-truenames
) ; XEmacs
3111 (let ((find-file-compare-truenames t
))
3112 (get-file-buffer file
)))
3113 ((fboundp 'find-buffer-visiting
) ; Emacs
3114 (find-buffer-visiting file
))
3115 (t (error "This should not happen (idlwave-get-buffer-visiting)"))))
3117 (defun idlwave-find-file-noselect (file)
3118 ;; Return a buffer visiting file.
3119 (or (idlwave-get-buffer-visiting file
)
3120 (find-file-noselect file
)))
3122 (defvar idlwave-scanned-lib-directories
)
3123 (defun idlwave-find-lib-file-noselet (file)
3124 ;; Find FILE on the scanned lib path and return a buffer visiting it
3125 (let* ((dirs idlwave-scanned-lib-directories
)
3128 (while (setq dir
(pop dirs
))
3130 (setq efile
(expand-file-name file dir
)))
3131 (throw 'exit
(idlwave-find-file-noselect efile
)))))))
3133 (defun idlwave-make-tags ()
3134 "Creates the IDL tags file IDLTAGS in the current directory from
3135 the list of directories specified in the minibuffer. Directories may be
3136 for example: . /usr/local/rsi/idl/lib. All the subdirectories of the
3137 specified top directories are searched if the directory name is prefixed
3138 by @. Specify @ directories with care, it may take a long, long time if
3141 (let (directory directories cmd append status numdirs dir getsubdirs
3142 buffer save_buffer files numfiles item errbuf
)
3145 ;; Read list of directories
3146 (setq directory
(read-string "Tag Directories: " "."))
3147 (setq directories
(idlwave-split-string directory
"[ \t]+"))
3149 ;; Set etags command, vars
3150 (setq cmd
"etags --output=IDLTAGS --language=none --regex='/[
3151 \\t]*[pP][Rr][Oo][ \\t]+\\([^ \\t,]+\\)/' --regex='/[
3152 \\t]*[Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn][ \\t]+\\([^ \\t,]+\\)/' ")
3156 ;; For each directory
3158 (setq dir
(nth numdirs directories
))
3161 ;; Find the subdirectories
3162 (if (string-match "^[@]\\(.+\\)$" dir
)
3163 (setq getsubdirs t
) (setq getsubdirs nil
))
3164 (if (and getsubdirs
) (setq dir
(substring dir
1 (length dir
))))
3165 (setq dir
(expand-file-name dir
))
3166 (if (file-directory-p dir
)
3168 (if (and getsubdirs
)
3170 (setq buffer
(get-buffer-create "*idltags*"))
3171 (call-process "sh" nil buffer nil
"-c"
3172 (concat "find " dir
" -type d -print"))
3173 (setq save_buffer
(current-buffer))
3175 (setq files
(idlwave-split-string
3176 (idlwave-replace-string
3177 (buffer-substring 1 (point-max))
3180 (set-buffer save_buffer
)
3181 (kill-buffer buffer
))
3182 (setq files
(list (concat dir
"/*.pro"))))
3184 ;; For each subdirectory
3186 (setq item
(nth numfiles files
))
3190 (if (not (string-match "^[ \\t]*$" item
))
3192 (message (concat "Tagging " item
"..."))
3193 (setq errbuf
(get-buffer-create "*idltags-error*"))
3194 (setq status
(+ status
3195 (call-process "sh" nil errbuf nil
"-c"
3196 (concat cmd append item
))))
3198 ;; Append additional tags
3199 (setq append
" --append ")
3200 (setq numfiles
(1+ numfiles
))
3201 (setq item
(nth numfiles files
)))
3203 (setq numfiles
(1+ numfiles
))
3204 (setq item
(nth numfiles files
))
3207 (setq numdirs
(1+ numdirs
))
3208 (setq dir
(nth numdirs directories
)))
3210 (setq numdirs
(1+ numdirs
))
3211 (setq dir
(nth numdirs directories
)))))
3213 (setq errbuf
(get-buffer-create "*idltags-error*"))
3215 (kill-buffer errbuf
))
3219 (defun idlwave-toggle-comment-region (beg end
&optional n
)
3220 "Comment the lines in the region if the first non-blank line is
3221 commented, and conversely, uncomment region. If optional prefix arg
3222 N is non-nil, then for N positive, add N comment delimiters or for N
3223 negative, remove N comment delimiters.
3224 Uses `comment-region' which does not place comment delimiters on
3226 (interactive "r\nP")
3228 (comment-region beg end
(prefix-numeric-value n
))
3233 (skip-chars-forward " \t\n")
3234 (if (looking-at (concat "[ \t]*\\(" comment-start
"+\\)"))
3235 (comment-region beg end
3236 (- (length (buffer-substring
3239 (comment-region beg end
)))))
3242 ;; ----------------------------------------------------------------------------
3243 ;; ----------------------------------------------------------------------------
3244 ;; ----------------------------------------------------------------------------
3245 ;; ----------------------------------------------------------------------------
3247 ;; Completion and Routine Info
3250 ;; String "intern" functions
3252 ;; For the completion and routine info function, we want to normalize
3253 ;; the case of procedure names etc. We do this by "interning" these
3254 ;; string is a hand-crafted way. Hashes are used to map the downcase
3255 ;; version of the strings to the cased versions. Since these cased
3256 ;; versions are really lisp objects, we can use `eq' to search, which
3257 ;; is a large performance boost.
3258 ;; All new strings need to be "sinterned". We do this as early as
3259 ;; possible after getting these strings from completion or buffer
3260 ;; substrings. So most of the code can simply assume to deal with
3261 ;; "sinterned" strings. The only exception is that the functions
3262 ;; which scan whole buffers for routine information do not intern the
3263 ;; grabbed strings. This is only done afterwards. Therefore in these
3264 ;; functions it is *not* save to assume the strings can be compared
3265 ;; with `eq' and be fed into the routine assq functions.
3267 ;; Here we define the hashing functions.
3269 ;; The variables which hold the hashes.
3270 (defvar idlwave-sint-routines
'(nil))
3271 (defvar idlwave-sint-keywords
'(nil))
3272 (defvar idlwave-sint-methods
'(nil))
3273 (defvar idlwave-sint-classes
'(nil))
3274 (defvar idlwave-sint-files
'(nil))
3276 (defun idlwave-reset-sintern (&optional what
)
3277 "Reset all sintern hashes."
3278 ;; Make sure the hash functions are accessible.
3279 (if (or (not (fboundp 'gethash
))
3280 (not (fboundp 'puthash
)))
3283 (or (fboundp 'puthash
)
3284 (defalias 'puthash
'cl-puthash
))))
3285 (let ((entries '((idlwave-sint-routines 1000 10)
3286 (idlwave-sint-keywords 1000 10)
3287 (idlwave-sint-methods 100 10)
3288 (idlwave-sint-classes 10 10))))
3290 ;; Make sure these are lists
3291 (loop for entry in entries
3292 for var
= (car entry
)
3293 do
(if (not (consp (symbol-value var
))) (set var
(list nil
))))
3295 (when (or (eq what t
) (eq what
'syslib
)
3296 (null (cdr idlwave-sint-routines
)))
3297 ;; Reset the system & library hash
3298 (loop for entry in entries
3299 for var
= (car entry
) for size
= (nth 1 entry
)
3300 do
(setcdr (symbol-value var
)
3301 (make-hash-table ':size size
':test
'equal
)))
3302 (setq idlwave-sint-files nil
))
3304 (when (or (eq what t
) (eq what
'bufsh
)
3305 (null (car idlwave-sint-routines
)))
3306 ;; Reset the buffer & shell hash
3307 (loop for entry in entries
3308 for var
= (car entry
) for size
= (nth 1 entry
)
3309 do
(setcar (symbol-value var
)
3310 (make-hash-table ':size size
':test
'equal
))))))
3312 (defun idlwave-sintern-routine-or-method (name &optional class set
)
3314 (idlwave-sintern-method name set
)
3315 (idlwave-sintern-routine name set
)))
3317 (defun idlwave-sintern (stype &rest args
)
3318 (apply (intern (concat "idlwave-sintern-" (symbol-name stype
))) args
))
3320 ;;(defmacro idlwave-sintern (type var)
3321 ;; `(cond ((not (stringp name)) name)
3322 ;; ((gethash (downcase name) (cdr ,var)))
3323 ;; ((gethash (downcase name) (car ,var)))
3324 ;; (set (idlwave-sintern-set name ,type ,var set))
3327 (defun idlwave-sintern-routine (name &optional set
)
3328 (cond ((not (stringp name
)) name
)
3329 ((gethash (downcase name
) (cdr idlwave-sint-routines
)))
3330 ((gethash (downcase name
) (car idlwave-sint-routines
)))
3331 (set (idlwave-sintern-set name
'routine idlwave-sint-routines set
))
3333 (defun idlwave-sintern-keyword (name &optional set
)
3334 (cond ((not (stringp name
)) name
)
3335 ((gethash (downcase name
) (cdr idlwave-sint-keywords
)))
3336 ((gethash (downcase name
) (car idlwave-sint-keywords
)))
3337 (set (idlwave-sintern-set name
'keyword idlwave-sint-keywords set
))
3339 (defun idlwave-sintern-method (name &optional set
)
3340 (cond ((not (stringp name
)) name
)
3341 ((gethash (downcase name
) (cdr idlwave-sint-methods
)))
3342 ((gethash (downcase name
) (car idlwave-sint-methods
)))
3343 (set (idlwave-sintern-set name
'method idlwave-sint-methods set
))
3345 (defun idlwave-sintern-class (name &optional set
)
3346 (cond ((not (stringp name
)) name
)
3347 ((gethash (downcase name
) (cdr idlwave-sint-classes
)))
3348 ((gethash (downcase name
) (car idlwave-sint-classes
)))
3349 (set (idlwave-sintern-set name
'class idlwave-sint-classes set
))
3352 (defun idlwave-sintern-file (name &optional set
)
3353 (car (or (member name idlwave-sint-files
)
3354 (setq idlwave-sint-files
(cons name idlwave-sint-files
)))))
3356 (defun idlwave-sintern-set (name type tables set
)
3357 (let* ((func (or (cdr (assq type idlwave-completion-case
))
3359 (iname (funcall (if (eq func
'preserve
) 'identity func
) name
))
3360 (table (if (eq set
'sys
) (cdr tables
) (car tables
))))
3361 (puthash (downcase name
) iname table
)
3364 (defun idlwave-sintern-rinfo-list (list &optional set
)
3365 "Sintern all strings in the rinfo LIST. With optional parameter SET:
3366 also set new patterns. Probably this will always have to be t."
3367 (let (entry name type class kwds res source call olh new
)
3369 (setq entry
(car list
)
3374 source
(nth 3 entry
)
3378 (setq kwds
(mapcar (lambda (x)
3379 (list (idlwave-sintern-keyword (car x
) set
)))
3383 (if (symbolp class
) (setq class
(symbol-name class
)))
3384 (setq class
(idlwave-sintern-class class set
))
3385 (setq name
(idlwave-sintern-method name set
)))
3386 (setq name
(idlwave-sintern-routine name set
)))
3387 (if (stringp (cdr source
))
3388 (setcdr source
(idlwave-sintern-file (cdr source
) t
)))
3390 (list name type class source call kwds olh
)
3391 (list name type class source call kwds
)))
3392 (setq res
(cons new res
)))
3395 ;;---------------------------------------------------------------------------
3398 ;; The variables which hold the information
3399 (defvar idlwave-builtin-routines nil
3400 "Holds the routine-info obtained by scanning buffers.")
3401 (defvar idlwave-buffer-routines nil
3402 "Holds the routine-info obtained by scanning buffers.")
3403 (defvar idlwave-compiled-routines nil
3404 "Holds the procedure routine-info obtained by asking the shell.")
3405 (defvar idlwave-library-routines nil
3406 "Holds the procedure routine-info from the library scan.")
3407 (defvar idlwave-scanned-lib-directories nil
3408 "The directories scanned to get libinfo.")
3409 (defvar idlwave-routines nil
3410 "Holds the combinded procedure routine-info.")
3411 (defvar idlwave-class-alist nil
3412 "Holds the class names known to IDLWAVE.")
3413 (defvar idlwave-class-history nil
3414 "The history of classes selected with the minibuffer.")
3415 (defvar idlwave-force-class-query nil
)
3416 (defvar idlwave-before-completion-wconf nil
3417 "The window configuration just before the completion buffer was displayed.")
3420 ;; The code to get routine info from different sources.
3422 (defvar idlwave-builtin-routines
)
3423 (defun idlwave-routines ()
3424 "Provide a list of IDL routines.
3425 This routine loads the builtin routines on the first call. Later it
3426 only returns the value of the variable."
3427 (or idlwave-routines
3429 (idlwave-update-routine-info)
3430 ;; return the current value
3433 (defun idlwave-update-routine-info (&optional arg
)
3434 "Update the internal routine-info lists.
3435 These lists are used by `idlwave-routine-info' (\\[idlwave-routine-info])
3436 and by `idlwave-complete' (\\[idlwave-complete]) to provide information
3437 about individual routines.
3439 The information can come from 4 sources:
3440 1. IDL programs in the current editing session
3441 2. Compiled modules in an IDL shell running as Emacs subprocess
3442 3. A list which covers the IDL system routines.
3443 4. A list which covers the prescanned library files.
3445 Scans all IDLWAVE-mode buffers of the current editing session (see
3446 `idlwave-scan-all-buffers-for-routine-info').
3447 When an IDL shell is running, this command also queries the IDL program
3448 for currently compiled routines.
3450 With prefix ARG, also reload the system and library lists.
3451 With two prefix ARG's, also rescans the library tree."
3453 (if (equal arg
'(16))
3454 (idlwave-create-libinfo-file t
)
3455 (let* ((reload (or arg
3456 idlwave-buffer-case-takes-precedence
3457 (null idlwave-builtin-routines
))))
3459 (setq idlwave-buffer-routines nil
3460 idlwave-compiled-routines nil
)
3461 ;; Reset the appropriate hashes
3462 (idlwave-reset-sintern (cond (reload t
)
3463 ((null idlwave-builtin-routines
) t
)
3466 (if idlwave-buffer-case-takes-precedence
3467 ;; We can safely scan the buffer stuff first
3469 (idlwave-update-buffer-routine-info)
3470 (and reload
(idlwave-load-system-rinfo)))
3471 ;; We first do the system info, and then the buffers
3472 (and reload
(idlwave-load-system-rinfo))
3473 (idlwave-update-buffer-routine-info))
3475 ;; Let's see if there is a shell
3476 (let* ((shell-is-running (and (fboundp 'idlwave-shell-is-running
)
3477 (idlwave-shell-is-running)))
3478 (ask-shell (and shell-is-running
3479 idlwave-query-shell-for-routine-info
)))
3481 (if (or (not ask-shell
)
3482 (not (interactive-p)))
3483 ;; 1. If we are not going to ask the shell, we need to do the
3484 ;; concatenation now.
3485 ;; 2. When this function is called non-interactively, it means
3486 ;; that someone needs routine info *now*. The shell update
3487 ;; causes the concatenation *delayed*, so not in time for
3488 ;; the current command. Therefore, we do a concatenation
3489 ;; now, even though the shell might do it again.
3490 (idlwave-concatenate-rinfo-lists))
3493 ;; Ask the shell about the routines it knows.
3494 (message "Querying the shell")
3495 (idlwave-shell-update-routine-info))))))
3497 (defun idlwave-load-system-rinfo ()
3498 ;; Load and case-treat the system and lib info files.
3499 (load "idlwave-rinfo" t
)
3500 (message "Normalizing idlwave-builtin-routines...")
3501 (setq idlwave-builtin-routines
3502 (idlwave-sintern-rinfo-list idlwave-builtin-routines
'sys
))
3503 (message "Normalizing idlwave-builtin-routines...done")
3504 (setq idlwave-routines idlwave-builtin-routines
)
3505 (when (and (stringp idlwave-libinfo-file
)
3506 (file-regular-p idlwave-libinfo-file
))
3509 (load-file idlwave-libinfo-file
)
3510 (message "Normalizing idlwave-library-routines...")
3511 (setq idlwave-library-routines
(idlwave-sintern-rinfo-list
3512 idlwave-library-routines
'sys
))
3513 (message "Normalizing idlwave-library-routines...done"))
3516 (defun idlwave-update-buffer-routine-info ()
3518 (if idlwave-scan-all-buffers-for-routine-info
3520 ;; Scan all buffers, current buffer last
3521 (message "Scanning all buffers...")
3522 (setq res
(idlwave-get-routine-info-from-buffers
3523 (reverse (buffer-list)))))
3524 ;; Just scan this buffer
3525 (if (eq major-mode
'idlwave-mode
)
3527 (message "Scanning current buffer...")
3528 (setq res
(idlwave-get-routine-info-from-buffers
3529 (list (current-buffer)))))))
3530 ;; Put the result into the correct variable
3531 (setq idlwave-buffer-routines
3532 (idlwave-sintern-rinfo-list res t
))))
3534 (defun idlwave-concatenate-rinfo-lists ()
3535 "Put the different sources for routine information together."
3536 ;; The sequence here is important because earlier definitions shadow
3537 ;; later ones. We assume that if things in the buffers are newer
3538 ;; then in the shell of the system, it is meant to be different.
3539 ;; FIXME: should the builtin stuff be before the library?
3540 ;; This is how IDL searches, the user may also have
3541 ;; functions overloading system stuff, and then the lib
3542 ;; should be first. Difficult to find a general solution.
3543 ;; FIXME: can't we use nconc here in some way, to save memory?
3544 ;; This is possible for buffer abd shell stuff, but these are
3545 ;; small anyway, and so it is not so critical.
3546 (setq idlwave-routines
(append idlwave-buffer-routines
3547 idlwave-compiled-routines
3548 idlwave-library-routines
3549 idlwave-builtin-routines
))
3550 (setq idlwave-class-alist nil
)
3552 (loop for x in idlwave-routines do
3553 (when (and (setq class
(nth 2 x
))
3554 (not (assq class idlwave-class-alist
)))
3555 (push (list class
) idlwave-class-alist
))))
3556 ;; Give a message with information about the number of routines we have.
3558 "Routine info updated: buffer(%d) compiled(%d) library(%d) system(%d)"
3559 (length idlwave-buffer-routines
)
3560 (length idlwave-compiled-routines
)
3561 (length idlwave-library-routines
)
3562 (length idlwave-builtin-routines
)))
3564 ;;----- Scanning buffers -------------------
3566 (defun idlwave-get-routine-info-from-buffers (buffers)
3567 "Call `idlwave-get-buffer-routine-info' on idlwave-mode buffers in BUFFERS."
3568 (let (buf routine-lists res
)
3570 (while (setq buf
(pop buffers
))
3572 (if (eq major-mode
'idlwave-mode
)
3573 ;; yes, this buffer has the right mode.
3574 (progn (setq res
(condition-case nil
3575 (idlwave-get-buffer-routine-info)
3577 (push res routine-lists
)))))
3578 ;; Concatenate the individual lists and return the result
3579 (apply 'nconc routine-lists
)))
3581 (defun idlwave-get-buffer-routine-info ()
3582 "Scan the current buffer for routine info. Return (PRO-LIST FUNC-LIST)."
3583 (let* ((case-fold-search t
)
3584 routine-list string entry
)
3588 (goto-char (point-min))
3589 (while (re-search-forward
3590 "^[ \t]*\\<\\(pro\\|function\\)\\>" nil t
)
3591 (setq string
(buffer-substring
3594 (idlwave-end-of-statement)
3596 (setq entry
(idlwave-parse-definition string
))
3597 (push entry routine-list
))))
3600 (defun idlwave-parse-definition (string)
3601 "Parse a module definition."
3602 (let ((case-fold-search t
)
3603 start name args type keywords class
)
3605 (while (string-match ";.*" string
)
3606 (setq string
(replace-match "" t t string
)))
3607 ;; Remove the continuation line stuff
3608 (while (string-match "\\([^a-zA-Z0-9$_]\\)\\$[ \t]*\n" string
)
3609 (setq string
(replace-match "\\1 " t nil string
)))
3610 ;; Match the name and type.
3612 "\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)" string
)
3613 (setq start
(match-end 0))
3614 (setq type
(downcase (match-string 1 string
)))
3615 (if (match-beginning 3)
3616 (setq class
(match-string 3 string
)))
3617 (setq name
(match-string 4 string
)))
3618 ;; Match normal args and keyword args
3619 (while (string-match
3620 ",\\s-*\\([a-zA-Z][a-zA-Z0-9$_]*\\|_extra\\)\\s-*\\(=\\)?"
3622 (setq start
(match-end 0))
3623 (if (match-beginning 2)
3624 (push (match-string 1 string
) keywords
)
3625 (push (match-string 1 string
) args
)))
3626 ;; Normalize and sort.
3627 (setq args
(nreverse args
))
3628 (setq keywords
(sort keywords
(lambda (a b
)
3629 (string< (downcase a
) (downcase b
)))))
3630 ;; Make and return the entry
3631 ;; We don't know which argument are optional, so this information
3632 ;; will not be contained in the calling sequence.
3634 (if (equal type
"pro") 'pro
'fun
)
3636 (cond ((not (boundp 'idlwave-scanning-lib
))
3637 (cons 'buffer
(buffer-file-name)))
3639 (file-name-sans-extension
3640 (file-name-nondirectory (buffer-file-name))))
3643 (t (cons 'lib
(file-name-nondirectory (buffer-file-name)))))
3645 (if (string= type
"function") "Result = " "")
3646 (if class
"Obj ->[%s::]" "")
3650 (if (string= type
"function") "(" ", ")
3651 (mapconcat 'identity args
", ")
3652 (if (string= type
"function") ")" ""))))
3654 (mapcar 'list keywords
)
3657 ;;----- Scanning the library -------------------
3659 (defun idlwave-create-libinfo-file (&optional arg
)
3660 "Scan all files on selected dirs of IDL search path for routine information.
3661 A widget checklist will allow you to choose the directories.
3662 Write the result as a file `idlwave-libinfo-file'. When this file exists,
3663 will be automatically loaded to give routine information about library
3665 With ARG, just rescan the same directories as last time - so no widget
3668 ;; Make sure the file is loaded if it exists.
3669 (if (and (stringp idlwave-libinfo-file
)
3670 (file-regular-p idlwave-libinfo-file
))
3672 (load-file idlwave-libinfo-file
)
3674 ;; Make sure the file name makes sense
3675 (unless (and (stringp idlwave-libinfo-file
)
3676 (file-accessible-directory-p
3677 (file-name-directory idlwave-libinfo-file
))
3678 (not (string= "" (file-name-nondirectory
3679 idlwave-libinfo-file
))))
3680 (error "`idlwave-libinfo-file' does not point to file in accessible directory."))
3683 ((and arg idlwave-scanned-lib-directories
)
3684 ;; Rescan the known directories
3685 (idlwave-scan-lib-files idlwave-scanned-lib-directories
))
3686 (idlwave-library-path
3687 ;; Get the directories from that variable
3688 (idlwave-display-libinfo-widget
3689 (idlwave-expand-path idlwave-library-path
)
3690 idlwave-scanned-lib-directories
))
3692 ;; Ask the shell for the path and run the widget
3693 (message "Asking the shell for IDL path...")
3694 (idlwave-shell-send-command
3695 "__pa=expand_path(!path,/array)&for i=0,n_elements(__pa)-1 do print,'PATH:',__pa[i]"
3696 '(idlwave-libinfo-command-hook nil
)
3699 (defun idlwave-libinfo-command-hook (&optional arg
)
3700 ;; Command hook used by `idlwave-create-libinfo-file'.
3703 (idlwave-scan-lib-files idlwave-scanned-lib-directories
)
3704 ;; Display the widget
3705 (idlwave-display-libinfo-widget (idlwave-shell-path-filter)
3706 idlwave-scanned-lib-directories
)))
3708 (defvar idlwave-shell-command-output
)
3709 (defun idlwave-shell-path-filter ()
3710 ;; Convert the output of the path query into a list of directories
3711 (let ((path-string idlwave-shell-command-output
)
3712 (case-fold-search t
)
3715 (while (string-match "^PATH:[ \t]*\\(.*\\)\n" path-string start
)
3716 (push (match-string 1 path-string
) dirs
)
3717 (setq start
(match-end 0)))
3720 (defconst idlwave-libinfo-widget-help-string
3721 "This is the front-end to the creation of IDLWAVE library routine info.
3722 Please select below the directories on IDL's search path from which you
3723 would like to extract routine information, which will be stored in the file
3727 If this is not the correct file, first set variable `idlwave-libinfo-file'.
3728 Then call this command again.
3729 After selecting the directories, choose [Scan & Save] to scan the library
3730 directories and save the routine info.
3733 (defvar idlwave-widget
)
3734 (defvar widget-keymap
)
3735 (defun idlwave-display-libinfo-widget (dirs selected-dirs
)
3736 "Create the widget to select IDL search path directories for scanning."
3741 (error "Don't know IDL's search path"))
3743 ;; Allow only those directories to be selected which are in the path.
3744 (setq selected-dirs
(delq nil
(mapcar (lambda (x)
3745 (if (member x dirs
) x nil
))
3747 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
3748 (switch-to-buffer (get-buffer-create "*IDLWAVE Widget*"))
3749 (kill-all-local-variables)
3750 (make-local-variable 'idlwave-widget
)
3751 (widget-insert (format idlwave-libinfo-widget-help-string
3752 idlwave-libinfo-file
))
3754 (widget-create 'push-button
3755 :notify
'idlwave-widget-scan-lib-files
3756 :help-echo
"testing"
3759 (widget-create 'push-button
3760 :notify
(lambda (&rest ignore
)
3761 (kill-buffer (current-buffer)))
3764 (widget-create 'push-button
3765 :notify
'idlwave-delete-libinfo-file
3768 (widget-create 'push-button
3769 :notify
'(lambda (&rest ignore
)
3770 (idlwave-display-libinfo-widget
3771 (widget-get idlwave-widget
:path-dirs
)
3772 (widget-get idlwave-widget
:path-dirs
)))
3775 (widget-create 'push-button
3776 :notify
'(lambda (&rest ignore
)
3777 (idlwave-display-libinfo-widget
3778 (widget-get idlwave-widget
:path-dirs
)
3781 (widget-insert "\n\n")
3783 (widget-insert "Select Directories\n")
3785 (setq idlwave-widget
3786 (apply 'widget-create
3788 :value selected-dirs
3790 :tag
"List of directories"
3791 (mapcar (lambda (x) (list 'item x
)) dirs
)))
3792 (widget-put idlwave-widget
:path-dirs dirs
)
3793 (widget-insert "\n")
3794 (use-local-map widget-keymap
)
3796 (goto-char (point-min))
3797 (delete-other-windows))
3799 (defun idlwave-delete-libinfo-file (&rest ignore
)
3801 (format "Delete file %s " idlwave-libinfo-file
))
3803 (delete-file idlwave-libinfo-file
)
3804 (message "%s has been deleted" idlwave-libinfo-file
))))
3806 (defun idlwave-widget-scan-lib-files (&rest ignore
)
3807 ;; Call `idlwave-scan-lib-files' with data taken from the widget.
3808 (let* ((widget idlwave-widget
)
3809 (selected-dirs (widget-value widget
)))
3810 (idlwave-scan-lib-files selected-dirs
)))
3812 (defvar font-lock-mode
)
3813 (defun idlwave-scan-lib-files (selected-dirs)
3814 ;; Scan the files in SELECTED-DIRS and store the info in a file
3815 (let* ((idlwave-scanning-lib t
)
3816 (idlwave-completion-case nil
)
3817 dirs dir files file
)
3818 (setq idlwave-library-routines nil
)
3819 (setq idlwave-scanned-lib-directories selected-dirs
)
3821 (set-buffer (get-buffer-create "*idlwave-scan.pro*"))
3823 (setq dirs
(reverse selected-dirs
))
3824 (while (setq dir
(pop dirs
))
3825 (when (file-directory-p dir
)
3826 (setq files
(directory-files dir
'full
"\\.[pP][rR][oO]\\'"))
3827 (while (setq file
(pop files
))
3828 (when (file-regular-p file
)
3829 (if (not (file-readable-p file
))
3830 (message "Skipping %s (no read permission)" file
)
3831 (message "Scanning %s..." file
)
3833 (insert-file-contents file
'visit
)
3834 (setq idlwave-library-routines
3835 (append (idlwave-get-routine-info-from-buffers
3836 (list (current-buffer)))
3837 idlwave-library-routines
)))
3839 (kill-buffer "*idlwave-scan.pro*")
3840 (kill-buffer (get-buffer-create "*IDLWAVE Widget*"))
3841 (let ((font-lock-maximum-size 0))
3842 (find-file idlwave-libinfo-file
))
3843 (if (and (boundp 'font-lock-mode
)
3847 (insert ";; IDLWAVE libinfo file\n")
3848 (insert (format ";; Created %s\n\n" (current-time-string)))
3850 ;; Define the variable which contains a list of all scanned directories
3851 (insert "\n(setq idlwave-scanned-lib-directories\n '(")
3853 (insert (format "\n \"%s\"" x
)))
3856 ;; Define the routine info list
3857 (insert "\n(setq idlwave-library-routines\n '(")
3860 (insert (with-output-to-string (prin1 x
))))
3861 idlwave-library-routines
)
3862 (insert (format "))\n\n;;; %s ends here\n"
3863 (file-name-nondirectory idlwave-libinfo-file
)))
3864 (goto-char (point-min))
3867 (kill-buffer (current-buffer)))
3868 (message "Info for %d routines saved in %s"
3869 (length idlwave-library-routines
)
3870 idlwave-libinfo-file
)
3872 (idlwave-update-routine-info t
))
3874 (defun idlwave-expand-path (path &optional default-dir
)
3875 ;; Expand parts of path starting with '+' recursively into directory list.
3876 ;; Relative recursive path elements are expanded relative to DEFAULT-DIR.
3877 (message "Expanding path...")
3878 (let (path1 dir recursive
)
3879 (while (setq dir
(pop path
))
3880 (if (setq recursive
(string= (substring dir
0 1) "+"))
3881 (setq dir
(substring dir
1)))
3883 (not (file-name-absolute-p dir
)))
3884 (setq dir
(expand-file-name dir default-dir
)))
3886 ;; Expand recursively
3887 (setq path1
(append (idlwave-recursive-directory-list dir
) path1
))
3890 (message "Expanding path...done")
3893 (defun idlwave-recursive-directory-list (dir)
3894 ;; Return a list of all directories below DIR, including DIR itself
3895 (let ((path (list dir
)) path1 file files
)
3896 (while (setq dir
(pop path
))
3897 (when (file-directory-p dir
)
3898 (setq files
(nreverse (directory-files dir t
"[^.]")))
3899 (while (setq file
(pop files
))
3900 (if (file-directory-p file
)
3901 (push (file-name-as-directory file
) path
)))
3905 ;;----- Asking the shell -------------------
3907 ;; First, here is the idl program which can be used to query IDL for
3908 ;; defined routines.
3909 (defconst idlwave-routine-info.pro
3911 function idlwave_make_info_entry,name,func=func,separator=sep
3912 ;; See if it's an object method
3913 func = keyword_set(func)
3914 methsep = strpos(name,'::')
3915 meth = methsep ne -1
3918 pars = routine_info(name,/parameters,functions=func)
3919 source = routine_info(name,/source,functions=func)
3920 nargs = pars.num_args
3921 nkw = pars.num_kw_args
3922 if nargs gt 0 then args = pars.args
3923 if nkw gt 0 then kwargs = pars.kw_args
3925 ;; Trim the class, and make the name
3927 class = strmid(name,0,methsep)
3928 name = strmid(name,methsep+2,strlen(name)-1)
3929 if nargs gt 0 then begin
3930 ;; remove the self argument
3931 wh = where(args ne 'SELF',nargs)
3932 if nargs gt 0 then args = args(wh)
3935 ;; No class, just a normal routine.
3941 if func then cs = 'Result = '
3942 if meth then cs = cs + 'Obj -> [' + '%s' + '::]'
3944 if func then cs = cs + '(' else if nargs gt 0 then cs = cs + ', '
3945 if nargs gt 0 then begin
3946 for j=0,nargs-1 do begin
3948 if j lt nargs-1 then cs = cs + ', '
3951 if func then cs = cs + ')'
3952 ;; Keyword arguments
3954 if nkw gt 0 then begin
3955 for j=0,nkw-1 do begin
3956 kwstring = kwstring + ' ' + kwargs(j)
3960 ret=(['IDLWAVE-PRO','IDLWAVE-FUN', $
3961 'IDLWAVE-PRO','IDLWAVE-FUN'])(func+2*meth)
3963 return,ret + ': ' + name + sep + class + sep + source(0).path $
3964 + sep + cs + sep + kwstring
3967 pro idlwave_routine_info
3969 print,'>>>BEGIN OF IDLWAVE ROUTINE INFO (\"' + sep + '\" IS THE SEPARATOR)'
3970 all = routine_info()
3971 for i=0,n_elements(all)-1 do $
3972 print,idlwave_make_info_entry(all(i),separator=sep)
3973 all = routine_info(/functions)
3974 for i=0,n_elements(all)-1 do $
3975 print,idlwave_make_info_entry(all(i),/func,separator=sep)
3976 print,'>>>END OF IDLWAVE ROUTINE INFO'
3979 "The idl program to get the routine info stuff.
3980 The output of this program is parsed by `idlwave-shell-routine-info-filter'.")
3982 (defun idlwave-shell-routine-info-filter ()
3983 "Function which parses the special output from idlwave_routine_info.pro."
3984 (let ((text idlwave-shell-command-output
)
3986 sep sep-re file type spec specs name cs key keys class
)
3987 ;; Initialize variables
3988 (setq idlwave-compiled-routines nil
)
3989 ;; Cut out the correct part of the output.
3991 "^>>>BEGIN OF IDLWAVE ROUTINE INFO (\"\\(.+\\)\" IS THE SEPARATOR.*"
3993 (setq sep
(match-string 1 text
)
3994 sep-re
(concat (regexp-quote sep
) " *")
3995 text
(substring text
(match-end 0)))
3996 (error "Routine Info error: No match for BEGIN line"))
3997 (if (string-match "^>>>END OF IDLWAVE ROUTINE INFO.*" text
)
3998 (setq text
(substring text
0 (match-beginning 0)))
3999 (error "Routine Info error: No match for END line"))
4000 ;; Match the output lines
4001 (while (string-match "^IDLWAVE-\\(PRO\\|FUN\\): \\(.*\\)" text start
)
4002 (setq start
(match-end 0))
4003 (setq type
(match-string 1 text
)
4004 spec
(match-string 2 text
)
4005 specs
(idlwave-split-string spec sep-re
)
4007 class
(if (equal (nth 1 specs
) "") nil
(nth 1 specs
))
4011 keys
(if (and (stringp key
)
4012 (not (string-match "\\` *\\'" key
)))
4014 (delete "" (idlwave-split-string key
" +")))))
4015 (setq name
(idlwave-sintern-routine-or-method name class t
)
4016 class
(idlwave-sintern-class class t
)
4017 keys
(mapcar (lambda (x)
4018 (list (idlwave-sintern-keyword (car x
) t
))) keys
))
4019 ;; Make sure we use the same string object for the same file
4020 (setq file
(idlwave-sintern-file file t
))
4021 ;; FIXME: What should I do with routines from the temp file???
4022 ;; Maybe just leave it in - there is a chance that the
4023 ;; routine is still in there.
4024 ;; (if (equal file idlwave-shell-temp-pro-file)
4027 ;; In the following ignore routines already defined in buffers,
4028 ;; assuming that if the buffer stuff differs, it is a "new"
4030 ;; We could do the same for the library to avoid duplicates -
4031 ;; but I think frequently a user might have several versions of
4032 ;; the same function in different programs, and in this case the
4033 ;; compiled one will be the best guess of all version.
4034 ;; Therefore, we leave duplicates of library routines in.
4036 (cond ((string= name
"$MAIN$")) ; ignore this one
4037 ((and (string= type
"PRO")
4038 ;; FIXME: is it OK to make the buffer routines dominate?
4039 (not (idlwave-rinfo-assq name
'pro class
4040 idlwave-buffer-routines
))
4041 ;; FIXME: is it OK to make the library routines dominate?
4042 ;;(not (idlwave-rinfo-assq name 'pro class
4043 ;; idlwave-library-routines))
4045 (push (list name
'pro class
(cons 'compiled file
) cs keys
)
4046 idlwave-compiled-routines
))
4047 ((and (string= type
"FUN")
4048 ;; FIXME: is it OK to make the buffer routines dominate?
4049 (not (idlwave-rinfo-assq name
'fun class
4050 idlwave-buffer-routines
))
4051 ;; FIXME: is it OK to make the library routines dominate?
4052 ;; (not (idlwave-rinfo-assq name 'fun class
4053 ;; idlwave-library-routines))
4055 (push (list name
'fun class
(cons 'compiled file
) cs keys
)
4056 idlwave-compiled-routines
)))))
4057 ;; Reverse the definitions so that they are alphabetically sorted.
4058 (setq idlwave-compiled-routines
4059 (nreverse idlwave-compiled-routines
)))
4061 (defvar idlwave-shell-temp-pro-file
)
4062 (defun idlwave-shell-update-routine-info ()
4063 "Query the shell for routine_info of compiled modules and update the lists."
4064 ;; Save and compile the procedure
4066 (set-buffer (idlwave-find-file-noselect
4067 idlwave-shell-temp-pro-file
))
4069 (insert idlwave-routine-info.pro
)
4071 (idlwave-shell-send-command (concat ".run " idlwave-shell-temp-pro-file
)
4074 ;; Execute the procedure and analyze the output
4075 (idlwave-shell-send-command "idlwave_routine_info"
4077 (idlwave-shell-routine-info-filter)
4078 (idlwave-concatenate-rinfo-lists))
4081 ;; ---------------------------------------------------------------------------
4083 ;; Completion and displaying routine calling sequences
4085 (defun idlwave-complete (&optional arg module class
)
4086 "Complete a function, procedure or keyword name at point.
4087 This function is smart and figures out what can be legally completed
4089 - At the beginning of a statement it completes procedure names.
4090 - In the middle of a statement it completes function names.
4091 - after a `(' or `,' in the argument list of a function or procedure,
4092 it completes a keyword of the relevant function or procedure.
4093 - In the first arg of `OBJ_NEW', it completes a class name.
4095 When several completions are possible, a list will be displayed in the
4096 *Completions* buffer. If this list is too long to fit into the
4097 window, scrolling can be achieved by repeatedly pressing \\[idlwave-complete].
4099 The function also knows about object methods. When it needs a class
4100 name, the action depends upon `idlwave-query-class', which see. You
4101 can force IDLWAVE to ask you for a class name with a \\[universal-argument] prefix
4102 argument to this command.
4104 See also the variables `idlwave-keyword-completion-adds-equal' and
4105 `idlwave-function-completion-adds-paren'.
4107 The optional ARG can be used to specify the completion type in order
4108 to override IDLWAVE's idea of what should be completed at point.
4109 Possible values are:
4111 0 <=> query for the completion type
4113 2 <=> 'procedure-keyword
4115 4 <=> 'function-keyword
4116 5 <=> 'procedure-method
4117 6 <=> 'procedure-method-keyword
4118 7 <=> 'function-method
4119 8 <=> 'function-method-keyword
4122 For Lisp programmers only:
4123 When we force a keyword, optional argument MODULE can contain the module name.
4124 When we force a method or a method keyword, CLASS can specify the class."
4131 (idlwave-make-force-complete-where-list arg module class
)
4133 (what (nth 2 where-list
))
4134 (idlwave-force-class-query (equal arg
'(4)))
4137 (if (and module
(string-match "::" module
))
4138 (setq class
(substring module
0 (match-beginning 0))
4139 module
(substring module
(match-end 0))))
4144 (eq (car-safe last-command
) 'idlwave-display-completion-list
)
4145 (setq cwin
(get-buffer-window "*Completions*")))
4146 (setq this-command last-command
)
4147 (idlwave-scroll-completions))
4150 (error "Nothing to complete here"))
4153 (idlwave-complete-class))
4155 ((eq what
'procedure
)
4156 ;; Complete a procedure name
4157 (let* ((class-selector (idlwave-determine-class (nth 3 where-list
) 'pro
))
4158 (isa (concat "procedure" (if class-selector
"-method" "")))
4159 (type-selector 'pro
))
4160 (idlwave-complete-in-buffer
4161 'procedure
(if class-selector
'method
'routine
)
4162 (idlwave-routines) 'idlwave-selector
4163 (format "Select a %s name%s"
4166 (format " (class is %s)" class-selector
)
4169 'idlwave-attach-method-classes
)))
4171 ((eq what
'function
)
4172 ;; Complete a function name
4173 (let* ((class-selector (idlwave-determine-class (nth 3 where-list
) 'fun
))
4174 (isa (concat "function" (if class-selector
"-method" "")))
4175 (type-selector 'fun
))
4176 (idlwave-complete-in-buffer
4177 'function
(if class-selector
'method
'routine
)
4178 (idlwave-routines) 'idlwave-selector
4179 (format "Select a %s name%s"
4182 (format " (class is %s)" class-selector
)
4185 'idlwave-attach-method-classes
)))
4187 ((eq what
'procedure-keyword
)
4188 ;; Complete a procedure keyword
4189 (let* ((where (nth 3 where-list
))
4191 (method-selector name
)
4192 (type-selector 'pro
)
4193 (class (idlwave-determine-class where
'pro
))
4194 (class-selector class
)
4195 (isa (format "procedure%s-keyword" (if class
"-method" "")))
4196 (entry (idlwave-rinfo-assq
4197 name
'pro class
(idlwave-routines)))
4198 (list (nth 5 entry
)))
4199 (unless (or entry
(eq class t
))
4200 (error "Nothing known about procedure %s"
4201 (idlwave-make-full-name class name
)))
4202 (setq list
(idlwave-fix-keywords name
'pro class list
))
4203 (unless list
(error (format "No keywords available for procedure %s"
4204 (idlwave-make-full-name class name
))))
4205 (idlwave-complete-in-buffer
4206 'keyword
'keyword list nil
4207 (format "Select keyword for procedure %s%s"
4208 (idlwave-make-full-name class name
)
4209 (if (member '("_EXTRA") list
) " (note _EXTRA)" ""))
4211 'idlwave-attach-keyword-classes
)))
4213 ((eq what
'function-keyword
)
4214 ;; Complete a function keyword
4215 (let* ((where (nth 3 where-list
))
4217 (method-selector name
)
4218 (type-selector 'fun
)
4219 (class (idlwave-determine-class where
'fun
))
4220 (class-selector class
)
4221 (isa (format "function%s-keyword" (if class
"-method" "")))
4222 (entry (idlwave-rinfo-assq
4223 name
'fun class
(idlwave-routines)))
4224 (list (nth 5 entry
)))
4225 (unless (or entry
(eq class t
))
4226 (error "Nothing known about function %s"
4227 (idlwave-make-full-name class name
)))
4228 (setq list
(idlwave-fix-keywords name
'fun class list
))
4229 (unless list
(error (format "No keywords available for function %s"
4230 (idlwave-make-full-name class name
))))
4231 (idlwave-complete-in-buffer
4232 'keyword
'keyword list nil
4233 (format "Select keyword for function %s%s"
4234 (idlwave-make-full-name class name
)
4235 (if (member '("_EXTRA") list
) " (note _EXTRA)" ""))
4237 'idlwave-attach-keyword-classes
)))
4239 (t (error "This should not happen (idlwave-complete)")))))
4241 (defun idlwave-make-force-complete-where-list (what &optional module class
)
4242 ;; Return an artificial WHERE specification to force the completion
4243 ;; routine to complete a specific item independent of context.
4244 ;; WHAT is the prefix arg of `idlwave-complete', see there for details.
4245 ;; MODULE and CLASS can be used to specify the routine name and class.
4246 ;; The class name will also be found in MODULE if that is like "class::mod".
4247 (let* ((what-list '(("procedure") ("procedure-keyword")
4248 ("function") ("function-keyword")
4249 ("procedure-method") ("procedure-method-keyword")
4250 ("function-method") ("function-method-keyword")
4252 (module (idlwave-sintern-routine-or-method module class
))
4253 (class (idlwave-sintern-class class
))
4257 (intern (completing-read
4258 "Complete what? " what-list nil t
))))
4260 (setq what
(intern (car (nth (1- what
) what-list
)))))
4263 (assoc (symbol-name what
) what-list
))
4265 (t (error "Illegal WHAT"))))
4266 (nil-list '(nil nil nil nil
))
4267 (class-list (list nil nil
(or class t
) nil
)))
4271 ((eq what
'procedure
)
4272 (list nil-list nil-list
'procedure nil-list nil
))
4274 ((eq what
'procedure-keyword
)
4275 (let* ((class-selector nil
)
4276 (type-selector 'pro
)
4278 (idlwave-completing-read
4279 "Procedure: " (idlwave-routines) 'idlwave-selector
))))
4280 (setq pro
(idlwave-sintern-routine pro
))
4281 (list nil-list nil-list
'procedure-keyword
4282 (list pro nil nil nil
) nil
)))
4284 ((eq what
'function
)
4285 (list nil-list nil-list
'function nil-list nil
))
4287 ((eq what
'function-keyword
)
4288 (let* ((class-selector nil
)
4289 (type-selector 'fun
)
4291 (idlwave-completing-read
4292 "Function: " (idlwave-routines) 'idlwave-selector
))))
4293 (setq func
(idlwave-sintern-routine func
))
4294 (list nil-list nil-list
'function-keyword
4295 (list func nil nil nil
) nil
)))
4297 ((eq what
'procedure-method
)
4298 (list nil-list nil-list
'procedure class-list nil
))
4300 ((eq what
'procedure-method-keyword
)
4301 (let* ((class (idlwave-determine-class class-list
'pro
))
4302 (class-selector class
)
4303 (type-selector 'pro
)
4305 (idlwave-completing-read
4306 (format "Procedure in %s class: " class-selector
)
4307 (idlwave-routines) 'idlwave-selector
))))
4308 (setq pro
(idlwave-sintern-method pro
))
4309 (list nil-list nil-list
'procedure-keyword
4310 (list pro nil class nil
) nil
)))
4312 ((eq what
'function-method
)
4313 (list nil-list nil-list
'function class-list nil
))
4315 ((eq what
'function-method-keyword
)
4316 (let* ((class (idlwave-determine-class class-list
'fun
))
4317 (class-selector class
)
4318 (type-selector 'fun
)
4320 (idlwave-completing-read
4321 (format "Function in %s class: " class-selector
)
4322 (idlwave-routines) 'idlwave-selector
))))
4323 (setq func
(idlwave-sintern-method func
))
4324 (list nil-list nil-list
'function-keyword
4325 (list func nil class nil
) nil
)))
4328 (list nil-list nil-list
'class nil-list nil
))
4330 (t (error "Illegal value for WHAT")))))
4332 (defun idlwave-completing-read (&rest args
)
4333 ;; Completing read, case insensitive
4334 (let ((old-value (default-value 'completion-ignore-case
)))
4337 (setq-default completion-ignore-case t
)
4338 (apply 'completing-read args
))
4339 (setq-default completion-ignore-case old-value
))))
4341 (defun idlwave-make-full-name (class name
)
4342 ;; Make a fully qualified module name including the class name
4343 (concat (if class
(format "%s::" class
) "") name
))
4345 (defun idlwave-rinfo-assq (name type class list
)
4346 ;; Works like assq, but also checks type and class
4349 (while (setq match
(assq name list
))
4350 (and (or (eq type t
)
4351 (eq (nth 1 match
) type
))
4352 (eq (nth 2 match
) class
)
4353 (throw 'exit match
))
4354 (setq list
(cdr (memq match list
)))))))
4356 (defun idlwave-all-assq (key list
)
4357 "Return a list of all associations of Key in LIST."
4359 (while (setq elt
(assq key list
))
4361 (setq list
(cdr (memq elt list
))))
4364 (defun idlwave-all-method-classes (method &optional type
)
4365 "Return all classes which have a method METHOD. TYPE is 'fun or 'pro.
4366 When TYPE is not specified, both procedures and functions will be considered."
4368 (mapcar 'car idlwave-class-alist
)
4373 (eq type
(nth 1 x
)))
4374 (push (nth 2 x
) rtn
)))
4375 (idlwave-all-assq method
(idlwave-routines)))
4376 (idlwave-uniquify rtn
))))
4378 (defun idlwave-all-method-keyword-classes (method keyword
&optional type
)
4379 "Return all classes which have a method METHOD with keyword KEYWORD.
4380 TYPE is 'fun or 'pro.
4381 When TYPE is not specified, both procedures and functions will be considered."
4382 (if (or (null method
)
4389 (eq type
(nth 1 x
)))
4390 (assoc keyword
(nth 5 x
))
4391 (push (nth 2 x
) rtn
)))
4392 (idlwave-all-assq method
(idlwave-routines)))
4393 (idlwave-uniquify rtn
))))
4395 (defun idlwave-determine-class (info type
)
4396 ;; Determine the class of a routine call. INFO is the structure returned
4397 ;; `idlwave-what-function' or `idlwave-what-procedure'.
4398 ;; The third element in this structure is the class. When nil, we return nil.
4399 ;; When t, try to get the class from text properties at the arrow,
4400 ;; otherwise prompt the user for a class name. Also stores the selected
4401 ;; class as a text property at the arrow.
4402 ;; TYPE is 'fun or 'pro.
4403 (let* ((class (nth 2 info
))
4405 (nassoc (assoc (if (stringp (car info
))
4408 idlwave-query-class
))
4409 (dassoc (assq (if (car info
) 'keyword-default
'method-default
)
4410 idlwave-query-class
))
4411 (query (cond (nassoc (cdr nassoc
))
4412 (dassoc (cdr dassoc
))
4414 (arrow (and apos
(string= (buffer-substring apos
(+ 2 apos
)) "->")))
4415 (force-query idlwave-force-class-query
)
4420 ;; There is an object which would like to know its class
4421 (if (and arrow
(get-text-property apos
'idlwave-class
)
4422 idlwave-store-inquired-class
4424 (setq class
(get-text-property apos
'idlwave-class
)
4425 class
(idlwave-sintern-class class
)))
4426 (when (and (eq class t
)
4427 (or force-query query
))
4429 (mapcar 'list
(idlwave-all-method-classes (car info
) type
)))
4431 (idlwave-sintern-class
4433 ((and (= (length class-alist
) 0) (not force-query
))
4434 (error "No classes available with method %s" (car info
)))
4435 ((and (= (length class-alist
) 1) (not force-query
))
4436 (car (car class-alist
)))
4438 (setq store idlwave-store-inquired-class
)
4439 (idlwave-completing-read
4440 (format "Class%s: " (if (stringp (car info
))
4441 (format " for %s method %s"
4444 class-alist nil nil nil
'idlwave-class-history
))))))
4445 (when (and class
(not (eq t class
)))
4446 ;; We have a real class here
4447 (when (and store arrow
)
4448 (put-text-property apos
(+ apos
2) 'idlwave-class class
)
4449 (put-text-property apos
(+ apos
2) 'face idlwave-class-arrow-face
))
4450 (setf (nth 2 info
) class
))
4453 ;; Default as fallback
4456 (defvar type-selector
)
4457 (defvar class-selector
)
4458 (defvar method-selector
)
4459 (defun idlwave-selector (a)
4460 (and (eq (nth 1 a
) type-selector
)
4461 (or (and (nth 2 a
) (eq class-selector t
))
4462 (eq (nth 2 a
) class-selector
))))
4464 (defun idlwave-where ()
4465 "Find out where we are.
4466 The return value is a list with the following stuff:
4467 (PRO-LIST FUNC-LIST COMPLETE-WHAT CW-LIST LAST-CHAR)
4469 PRO-LIST (PRO POINT CLASS ARROW)
4470 FUNC-LIST (FUNC POINT CLASS ARROW)
4471 COMPLETE-WHAT a symbol indicating what kind of completion makes sense here
4472 CW-LIST Like PRO-LIST, for what can be copmpleted here.
4473 LAST-CHAR last relevant character before point (non-white non-comment,
4474 not part of current identifier or leading slash).
4476 In the lists, we have these meanings:
4479 POINT: Where is this
4480 CLASS: What class has the routine (nil=no, t=is method, but class unknown)
4481 ARROW: Where is the arrow?"
4483 (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point)))
4484 (func-entry (idlwave-what-function bos
))
4485 (func (car func-entry
))
4486 (func-class (nth 1 func-entry
))
4487 (func-arrow (nth 2 func-entry
))
4488 (func-point (or (nth 3 func-entry
) 0))
4489 (func-level (or (nth 4 func-entry
) 0))
4490 (pro-entry (idlwave-what-procedure bos
))
4491 (pro (car pro-entry
))
4492 (pro-class (nth 1 pro-entry
))
4493 (pro-arrow (nth 2 pro-entry
))
4494 (pro-point (or (nth 3 pro-entry
) 0))
4495 (last-char (idlwave-last-valid-char))
4496 (case-fold-search t
)
4497 cw cw-mod cw-arrow cw-class cw-point
)
4498 (if (< func-point pro-point
) (setq func nil
))
4501 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'"
4502 (buffer-substring (if (> pro-point
0) pro-point bos
) (point)))
4503 (setq cw
'procedure cw-class pro-class cw-point pro-point
4504 cw-arrow pro-arrow
))
4505 ((string-match "\\`[ \t]*\\(pro\\|function\\)\\>"
4506 (buffer-substring bos
(point)))
4508 ((string-match "OBJ_NEW([ \t]*'\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'"
4509 (buffer-substring bos
(point)))
4512 (> func-point pro-point
)
4514 (memq last-char
'(?\
( ?
,)))
4515 (setq cw
'function-keyword cw-mod func cw-point func-point
4516 cw-class func-class cw-arrow func-arrow
))
4517 ((and pro
(eq last-char ?
,))
4518 (setq cw
'procedure-keyword cw-mod pro cw-point pro-point
4519 cw-class pro-class cw-arrow pro-arrow
))
4520 ; ((member last-char '(?\' ?\) ?\] ?!))
4521 ; ;; after these chars, a function makes no sense
4522 ; ;; FIXME: I am sure there can be more in this list
4523 ; ;; FIXME: Do we want to do this at all?
4525 ;; Everywhere else we try a function.
4529 (if (re-search-backward "->[ \t]*\\(\\([$a-zA-Z0-9_]+\\)::\\)?[$a-zA-Z0-9_]*\\=" bos t
)
4530 (setq cw-arrow
(match-beginning 0)
4531 cw-class
(if (match-end 2)
4532 (idlwave-sintern-class (match-string 2))
4534 (list (list pro pro-point pro-class pro-arrow
)
4535 (list func func-point func-class func-arrow
)
4537 (list cw-mod cw-point cw-class cw-arrow
)
4540 (defun idlwave-this-word (&optional class
)
4541 ;; Grab the word around point. CLASS is for the `skip-chars=...' functions
4542 (setq class
(or class
"a-zA-Z0-9$_"))
4544 (buffer-substring-no-properties
4545 (progn (skip-chars-backward class
) (point))
4546 (progn (skip-chars-forward class
) (point)))))
4548 (defvar idlwave-find-symbol-syntax-table
)
4549 (defun idlwave-what-function (&optional bound
)
4550 ;; Find out if point is within the argument list of a function.
4551 ;; The return value is ("function-name" (point) level).
4552 ;; Level is 1 on the to level parenthesis, higher further down.
4554 ;; If the optional BOUND is an integer, bound backwards directed
4555 ;; searches to this point.
4560 (old-syntax (syntax-table))
4562 func arrow-start class
)
4566 (set-syntax-table idlwave-find-symbol-syntax-table
)
4567 (narrow-to-region (max 1 (or bound
0)) (point-max))
4568 ;; move back out of the current parenthesis
4569 (while (condition-case nil
4570 (progn (up-list -
1) t
)
4574 (when (and (= (following-char) ?\
()
4576 "\\(::\\|\\<\\)\\([a-zA-Z][a-zA-Z0-9$_]*\\)\\="
4578 (setq func
(match-string 2)
4579 func-point
(goto-char (match-beginning 2))
4581 (if (re-search-backward
4582 "->[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\=" bound t
)
4583 (setq arrow-start
(match-beginning 0)
4584 class
(or (match-string 2) t
)))
4588 (idlwave-sintern-routine-or-method func class
)
4589 (idlwave-sintern-class class
)
4590 arrow-start func-point cnt
)))
4593 (set-syntax-table old-syntax
)))))
4595 (defun idlwave-what-procedure (&optional bound
)
4596 ;; Find out if point is within the argument list of a procedure.
4597 ;; The return value is ("procedure-name" class arrow-pos (point)).
4599 ;; If the optional BOUND is an integer, bound backwards directed
4600 ;; searches to this point.
4601 (let ((pos (point)) pro-point
4602 pro class arrow-start string
)
4604 (idlwave-beginning-of-statement)
4605 (setq string
(buffer-substring (point) pos
))
4607 "\\`[ \t]*\\([a-zA-Z][a-zA-Z0-9$_]*\\)[ \t]*\\(,\\|\\'\\)" string
)
4608 (setq pro
(match-string 1 string
)
4609 pro-point
(+ (point) (match-beginning 1)))
4610 (if (and (idlwave-skip-object)
4611 (setq string
(buffer-substring (point) pos
))
4613 "\\`[ \t]*\\(->\\)[ \t]*\\(\\([a-zA-Z][a-zA-Z0-9$_]*\\)::\\)?\\([a-zA-Z][a-zA-Z0-9$_]*\\)?[ \t]*\\(,\\|\\'\\)" string
))
4614 (setq pro
(if (match-beginning 4)
4615 (match-string 4 string
))
4616 pro-point
(if (match-beginning 4)
4617 (+ (point) (match-beginning 4))
4619 arrow-start
(+ (point) (match-beginning 1))
4620 class
(or (match-string 3 string
) t
)))))
4621 (list (idlwave-sintern-routine-or-method pro class
)
4622 (idlwave-sintern-class class
)
4626 (defun idlwave-skip-object ()
4627 ;; If there is an object at point, move over it and return t.
4628 (let ((pos (point)))
4631 (skip-chars-forward " ") ; white space
4632 (skip-chars-forward "*") ; de-reference
4634 ((looking-at idlwave-identifier
)
4635 (goto-char (match-end 0)))
4636 ((eq (following-char) ?\
()
4638 (t (throw 'exit nil
)))
4641 (cond ((eq (following-char) ?.
)
4643 (if (not (looking-at idlwave-identifier
))
4645 (goto-char (match-end 0)))
4646 ((memq (following-char) '(?\
( ?\
[))
4649 (error (throw 'exit nil
))))
4650 (t (throw 'endwhile t
)))))
4651 (if (looking-at "[ \t]*->")
4652 (throw 'exit
(setq pos
(match-beginning 0)))
4653 (throw 'exit nil
))))
4658 (defun idlwave-last-valid-char ()
4659 "Return the last character before point which is not white or a comment
4660 and also not part of the current identifier. Since we do this in
4661 order to identify places where keywords are, we consider the initial
4662 `/' of a keyword as part of the identifier.
4663 This function is not general, can only be used for completion stuff."
4666 ;; skip the current identifier
4667 (skip-chars-backward "a-zA-Z0-9_$")
4668 ;; also skip a leading slash which might be belong to the keyword
4669 (if (eq (preceding-char) ?
/)
4671 ;; FIXME: does not check if this is a valid identifier
4673 (skip-chars-backward " \t")
4675 ((memq (preceding-char) '(?\
; ?\$)) (throw 'exit nil))
4676 ((eq (preceding-char) ?
\n)
4677 (beginning-of-line 0)
4678 (if (looking-at "\\([^;]\\)*\\$[ \t]*\\(;.*\\)?\n")
4679 ;; continuation line
4680 (goto-char (match-end 1))
4682 (t (throw 'exit
(preceding-char))))))))
4684 (defvar idlwave-complete-after-success-form nil
4685 "A form to evaluate after successful completion.")
4686 (defvar idlwave-complete-after-success-form-force nil
4687 "A form to evaluate after completion selection in *Completions* buffer.")
4688 (defconst idlwave-completion-mark
(make-marker)
4689 "A mark pointing to the beginning of the completion string.")
4691 (defun idlwave-complete-in-buffer (type stype list selector prompt isa
4692 &optional prepare-display-function
)
4693 "Perform TYPE completion of word before point against LIST.
4694 SELECTOR is the PREDICATE argument for the completion function.
4695 Show PROMPT in echo area. TYPE is one of 'function, 'procedure or 'keyword."
4696 (let* ((completion-ignore-case t
)
4697 beg
(end (point)) slash part spart completion all-completions
4701 (error (concat prompt
": No completions available")))
4703 ;; What is already in the buffer?
4705 (skip-chars-backward "a-zA-Z0-9_$")
4706 (setq slash
(eq (preceding-char) ?
/)
4708 idlwave-complete-after-success-form
4709 (list 'idlwave-after-successful-completion
4710 (list 'quote type
) slash beg
)
4711 idlwave-complete-after-success-form-force
4712 (list 'idlwave-after-successful-completion
4713 (list 'quote type
) slash
(list 'quote
'force
))))
4716 (setq part
(buffer-substring beg end
)
4717 dpart
(downcase part
)
4718 spart
(idlwave-sintern stype part
)
4719 completion
(try-completion part list selector
)
4720 dcompletion
(if (stringp completion
) (downcase completion
)))
4723 ;; nothing available.
4724 (error "Can't find %s completion for \"%s\"" isa part
))
4725 ((and (not (equal dpart dcompletion
))
4726 (not (eq t completion
)))
4727 ;; We can add something
4728 (delete-region beg end
)
4729 (if (and (string= part dpart
)
4730 (or (not (string= part
""))
4731 idlwave-complete-empty-string-as-lower-case
)
4732 (not idlwave-completion-force-default-case
))
4733 (insert dcompletion
)
4734 (insert completion
))
4735 (if (eq t
(try-completion completion list selector
))
4736 ;; Now this is a unique match
4737 (idlwave-after-successful-completion type slash beg
))
4739 ((or (eq completion t
)
4740 (and (equal dpart dcompletion
)
4741 (= 1 (length (setq all-completions
4743 (all-completions part list selector
)))))))
4744 ;; This is already complete
4745 (idlwave-after-successful-completion type slash beg
)
4746 (message "%s is already the complete %s" part isa
)
4749 ;; We cannot add something - offer a list.
4750 (message "Making completion list...")
4751 (let* ((list all-completions
)
4752 (complete (memq spart all-completions
))
4753 (completion-highlight-first-word-only t
) ; XEmacs
4754 (completion-fixup-function ; Emacs
4755 (lambda () (and (eq (preceding-char) ?
>)
4756 (re-search-backward " <" beg t
)))))
4757 (setq list
(sort list
(lambda (a b
)
4758 (string< (downcase a
) (downcase b
)))))
4759 (if prepare-display-function
4760 (setq list
(funcall prepare-display-function list
)))
4761 (if (and (string= part dpart
)
4762 (or (not (string= part
""))
4763 idlwave-complete-empty-string-as-lower-case
)
4764 (not idlwave-completion-force-default-case
))
4765 (setq list
(mapcar (lambda (x)
4767 (setcar x
(downcase (car x
)))
4768 (setq x
(downcase x
)))
4771 (idlwave-display-completion-list list prompt beg complete
))
4774 (defun idlwave-complete-class ()
4775 "Complete a class at point."
4777 ;; Call `idlwave-routines' to make sure the class list will be available
4779 ;; Now do the completion
4780 (idlwave-complete-in-buffer 'class
'class idlwave-class-alist nil
4781 "Select a class" "class"))
4784 (defun idlwave-attach-classes (list is-kwd show-classes
)
4785 ;; attach the proper class list to a LIST of completion items.
4786 ;; IS-KWD, when non-nil, shows its keywords - otherwise its methods
4787 ;; SHOW-CLASSES is the value of `idlwave-completion-show-classes'.
4789 (if (or (null show-classes
) ; don't wnat to see classes
4790 (null class-selector
) ; not a method call
4791 (stringp class-selector
)) ; the class is already known
4792 ;; In these cases, we do not have to do anything
4795 ;; The property and dots stuff currently only make sense with XEmacs
4796 ;; because Emacs drops text properties when filling the *Completions*
4798 (let* ((do-prop (and (featurep 'xemacs
) (>= show-classes
0)))
4799 (do-buf (not (= show-classes
0)))
4800 (do-dots (featurep 'xemacs
))
4801 (max (abs show-classes
))
4802 (lmax (if do-dots
(apply 'max
(mapcar 'length list
))))
4803 classes nclasses class-info space
)
4809 (idlwave-all-method-keyword-classes
4810 method-selector x type-selector
)
4811 (idlwave-all-method-classes x type-selector
)))
4812 (setq nclasses
(length classes
))
4813 ;; Make the separator between item and class-info
4815 (setq space
(concat " " (make-string (- lmax
(length x
)) ?.
)))
4818 ;; We do want info in the buffer
4819 (if (<= nclasses max
)
4820 (setq class-info
(concat
4822 "<" (mapconcat 'identity classes
",") ">"))
4823 (setq class-info
(format "%s<%d classes>" space nclasses
)))
4824 (setq class-info nil
))
4826 ;; We do want properties
4827 (setq x
(copy-sequence x
))
4828 (put-text-property 0 (length x
)
4829 'help-echo
(mapconcat 'identity classes
" ")
4836 (defun idlwave-attach-method-classes (list)
4837 ;; Call idlwave-attach-classes with method parameters
4838 (idlwave-attach-classes list nil idlwave-completion-show-classes
))
4839 (defun idlwave-attach-keyword-classes (list)
4840 ;; Call idlwave-attach-classes with keyword parameters
4841 (idlwave-attach-classes list t idlwave-completion-show-classes
))
4843 ;;----------------------------------------------------------------------
4844 ;;----------------------------------------------------------------------
4845 ;;----------------------------------------------------------------------
4846 ;;----------------------------------------------------------------------
4847 ;;----------------------------------------------------------------------
4849 (defun idlwave-scroll-completions (&optional message
)
4850 "Scroll the completion window on this frame."
4851 (let ((cwin (get-buffer-window "*Completions*" 'visible
))
4852 (win (selected-window)))
4855 (select-window cwin
)
4858 (error (if (and (listp last-command
)
4859 (nth 2 last-command
))
4862 (eval idlwave-complete-after-success-form
))
4863 (set-window-start cwin
(point-min)))))
4864 (and message
(message message
)))
4865 (select-window win
))))
4867 (defun idlwave-display-completion-list (list &optional message beg complete
)
4868 "Display the completions in LIST in the completions buffer and echo MESSAGE."
4869 (unless (and (get-buffer-window "*Completions*")
4870 (idlwave-local-value 'idlwave-completion-p
"*Completions*"))
4871 (move-marker idlwave-completion-mark beg
)
4872 (setq idlwave-before-completion-wconf
(current-window-configuration)))
4874 (if (featurep 'xemacs
)
4875 (idlwave-display-completion-list-xemacs list
)
4876 (idlwave-display-completion-list-emacs list
))
4878 ;; Store a special value in `this-command'. When `idlwave-complete'
4879 ;; finds this in `last-command', it will scroll the *Completions* buffer.
4880 (setq this-command
(list 'idlwave-display-completion-list message complete
))
4882 ;; Mark the completions buffer as created by cib
4883 (idlwave-set-local 'idlwave-completion-p t
"*Completions*")
4885 ;; Fontify the classes
4886 (if (and idlwave-completion-fontify-classes
4888 (idlwave-completion-fontify-classes))
4890 ;; Display the message
4891 (message (or message
"Making completion list...done")))
4893 (defun idlwave-choose (function &rest args
)
4894 "Call FUNCTION as a completion chooser and pass ARGS to it."
4895 (let ((completion-ignore-case t
)) ; install correct value
4896 (apply function args
))
4897 (eval idlwave-complete-after-success-form-force
))
4899 (defun idlwave-restore-wconf-after-completion ()
4900 "Restore the old (before completion) window configuration."
4901 (and idlwave-completion-restore-window-configuration
4902 idlwave-before-completion-wconf
4903 (set-window-configuration idlwave-before-completion-wconf
)))
4905 (defun idlwave-set-local (var value
&optional buffer
)
4906 "Set the buffer-local value of VAR in BUFFER to VALUE."
4908 (set-buffer (or buffer
(current-buffer)))
4909 (set (make-local-variable var
) value
)))
4911 (defun idlwave-local-value (var &optional buffer
)
4912 "Return the value of VAR in BUFFER, but only if VAR is local to BUFFER."
4914 (set-buffer (or buffer
(current-buffer)))
4915 (and (local-variable-p var
(current-buffer))
4916 (symbol-value var
))))
4918 ;; In XEmacs, we can use :activate-callback directly
4920 (defun idlwave-display-completion-list-xemacs (list)
4921 (with-output-to-temp-buffer "*Completions*"
4922 (display-completion-list list
:activate-callback
4923 'idlwave-default-choose-completion
)))
4925 (defun idlwave-default-choose-completion (&rest args
)
4926 "Execute `default-choose-completion' and then restore the win-conf."
4927 (apply 'idlwave-choose
'default-choose-completion args
))
4929 ;; In Emacs we have to replace the keymap in the *Completions* buffer
4930 ;; in order to install our wrappers.
4932 (defvar idlwave-completion-map nil
4933 "Keymap for completion-list-mode with idlwave-complete.")
4935 (defun idlwave-display-completion-list-emacs (list)
4936 "Display completion list and install the choose wrappers."
4937 (with-output-to-temp-buffer "*Completions*"
4938 (display-completion-list list
))
4940 (set-buffer "*Completions*")
4942 (or idlwave-completion-map
4943 (setq idlwave-completion-map
4944 (idlwave-make-modified-completion-map (current-local-map)))))))
4946 (defun idlwave-make-modified-completion-map (old-map)
4947 "Replace `choose-completion' and `mouse-choose-completion' in OLD-MAP."
4948 (let ((new-map (copy-keymap old-map
)))
4949 (substitute-key-definition
4950 'choose-completion
'idlwave-choose-completion new-map
)
4951 (substitute-key-definition
4952 'mouse-choose-completion
'idlwave-mouse-choose-completion new-map
)
4955 (defun idlwave-choose-completion (&rest args
)
4956 "Choose the completion that point is in or next to."
4958 (apply 'idlwave-choose
'choose-completion args
))
4960 (defun idlwave-mouse-choose-completion (&rest args
)
4961 "Click on an alternative in the `*Completions*' buffer to choose it."
4963 (apply 'idlwave-choose
'mouse-choose-completion args
))
4965 ;;----------------------------------------------------------------------
4966 ;;----------------------------------------------------------------------
4968 (defun idlwave-completion-fontify-classes ()
4969 "Goto the *Completions* buffer and fontify the class info."
4970 (when (featurep 'font-lock
)
4972 (set-buffer "*Completions*")
4974 (goto-char (point-min))
4975 (while (re-search-forward "\\.*<[^>]+>" nil t
)
4976 (put-text-property (match-beginning 0) (match-end 0)
4977 'face
'font-lock-string-face
))))))
4979 (defun idlwave-uniquify (list)
4981 (loop for x in list do
4982 (add-to-list 'nlist x
))
4985 (defun idlwave-after-successful-completion (type slash
&optional verify
)
4986 "Add `=' or `(' after successful completion of keyword and function.
4987 Restore the pre-completion window configuration if possible."
4989 ((eq type
'procedure
)
4991 ((eq type
'function
)
4993 ((equal idlwave-function-completion-adds-paren nil
) nil
)
4994 ((or (equal idlwave-function-completion-adds-paren t
)
4995 (equal idlwave-function-completion-adds-paren
1))
4997 ((equal idlwave-function-completion-adds-paren
2)
5002 (if (and idlwave-keyword-completion-adds-equal
5004 (progn (insert "=") t
)
5007 ;; Restore the pre-completion window configuration if this is safe.
5009 (if (or (eq verify
'force
) ; force
5011 (get-buffer-window "*Completions*") ; visible
5012 (idlwave-local-value 'idlwave-completion-p
5013 "*Completions*") ; cib-buffer
5014 (eq (marker-buffer idlwave-completion-mark
)
5015 (current-buffer)) ; buffer OK
5016 (equal (marker-position idlwave-completion-mark
)
5018 (idlwave-restore-wconf-after-completion))
5019 (move-marker idlwave-completion-mark nil
)
5020 (setq idlwave-before-completion-wconf nil
))
5022 (defun idlwave-routine-info-from-idlhelp (&optional arg
)
5023 "Make IDLHELP display the online documentation about the routine at point.
5024 Sends the command `? MODULE' to the IDLWAVE-Shell. Shell must be running,
5025 it does not autostart for this task."
5027 (idlwave-routine-info arg
'external
))
5029 (defun idlwave-routine-info (&optional arg external
)
5030 "Display a routines calling sequence and list of keywords.
5031 When point is on the name a function or procedure, or in the argument
5032 list of a function or procedure, this command displays a help buffer
5033 with the information. When called with prefix arg, enforce class
5036 When point is on an object operator `->', display the class stored in
5037 this arrow, if any (see `idlwave-store-inquired-class'). With a
5038 prefix arg, the class property is cleared out."
5042 (if (string-match "->" (buffer-substring
5043 (max (point-min) (1- (point)))
5044 (min (+ 2 (point)) (point-max))))
5045 ;; Cursor is on an arrow
5046 (if (get-text-property (point) 'idlwave-class
)
5047 ;; arrow has class property
5052 (when (looking-at ".?\\(->\\)")
5053 (remove-text-properties (match-beginning 1) (match-end 1)
5054 '(idlwave-class nil face nil
))
5055 (message "Class property removed from arrow")))
5056 ;; Echo class property
5057 (message "Arrow has text property identifying object to be class %s"
5058 (get-text-property (point) 'idlwave-class
)))
5059 ;; No property found
5060 (message "Arrow has no class text property"))
5062 ;; Not on an arrow...
5063 (let* ((idlwave-query-class nil
)
5064 (idlwave-force-class-query (equal arg
'(4)))
5065 (module (idlwave-what-module)))
5068 (apply 'idlwave-search-online-help module
)
5069 (apply 'idlwave-display-calling-sequence module
)))
5071 (error "Don't know which calling sequence to show."))))))
5073 (defun idlwave-search-online-help (name &optional type class olh
)
5074 "Tell IDL to lookup CLASS::NAME with type TYPE in the online help.
5075 If TYPE and CLASS are both nil, just look up NAME in the default help file."
5076 ;; If only the IDLHELP application was better designed, so that
5077 ;; we could make it open the right thing right away. As things are,
5078 ;; we need to pipe the stuff through the help search engine, and we
5079 ;; cannot enter a space.
5080 (let* (extra book full string cmd
)
5082 ;; Try to find a clue for the right help book
5083 (if (and type
(not olh
))
5084 (setq olh
(or (nth 6 (idlwave-rinfo-assq
5085 name type class idlwave-builtin-routines
))
5086 (nth 6 (idlwave-rinfo-assq
5087 name type class idlwave-routines
)))))
5089 ;; Sometimes the book is given as a symbol - make it a string
5090 (if (and olh
(symbolp olh
)) (setq olh
(symbol-name olh
)))
5091 (setq book
(or olh
"idl")) ; We need a default
5092 ;; Add the FULL_PATH keyword if appropriate
5093 (if (and (file-name-absolute-p book
)
5094 (file-exists-p book
))
5095 (setq full
",/FULL_PATH")
5098 ;; We would like to add "Method" or so, but stupid IDL online help
5099 ;; command treats a space as a separator and interprets the next thing as
5101 ;; (setq extra (cond ((eq type 'kwd) " keyword")
5102 ;; (class " method")
5103 ;; ((eq type 'pro) " procedure")
5104 ;; ((eq type 'fun) " function")
5108 ;; Methods are subitems of classes, the separator is a single `:'
5109 (if (and name class
(not (eq type
'kwd
)))
5110 (setq name
(concat class
":" name
)))
5111 ;; FIXME: We used to use book, but in idl5.3, all help is in idl.hlp
5112 (setq string
(concat name extra
)
5113 cmd
(format "ONLINE_HELP,'%s',BOOK='%s'%s" string
"idl" full
))
5114 ; cmd (format "ONLINE_HELP,'%s',BOOK='%s'%s" string book full))
5115 (message "Sending to IDL: %s" cmd
) (sit-for 2)
5116 (idlwave-shell-send-command cmd
)))
5118 (defun idlwave-resolve (&optional arg
)
5119 "Call RESOLVE on the module name at point.
5120 Like `idlwave-routine-info', this looks for a routine call at point.
5121 After confirmation in the minibuffer, it will use the shell to issue
5122 a RESOLVE call for this routine, to attempt to make it defined and its
5123 routine info available for IDLWAVE. If the routine is a method call,
5124 both `class__method' and `class__define' will be tried.
5125 With ARG, enforce query for the class of object methods."
5127 (let* ((idlwave-query-class nil
)
5128 (idlwave-force-class-query (equal arg
'(4)))
5129 (module (idlwave-what-module))
5130 (name (idlwave-make-full-name (nth 2 module
) (car module
)))
5131 (type (if (eq (nth 1 module
) 'pro
) "pro" "function"))
5132 (resolve (read-string "Resolve: " (format "%s %s" type name
)))
5135 (if (string-match "\\(pro\\|function\\)[ \t]+\\(\\(.*\\)::\\)?\\(.*\\)"
5137 (setq type
(match-string 1 resolve
)
5138 class
(if (match-beginning 2)
5139 (match-string 3 resolve
)
5141 name
(match-string 4 resolve
)))
5142 (if (string= (downcase type
) "function")
5143 (setq kwd
",/is_function"))
5147 (idlwave-shell-send-command
5148 (format "resolve_routine,'%s'%s" (downcase name
) kwd
)
5149 'idlwave-update-routine-info
5152 (idlwave-shell-send-command
5153 (format "resolve_routine,'%s__define'%s" (downcase class
) kwd
)
5154 (list 'idlwave-shell-send-command
5155 (format "resolve_routine,'%s__%s'%s"
5156 (downcase class
) (downcase name
) kwd
)
5157 '(idlwave-update-routine-info)
5160 (defun idlwave-find-module (&optional arg
)
5161 "Find the source code of an IDL module.
5162 Works for modules for which IDLWAVE has routine info available.
5163 The function offers as default the module name `idlwave-routine-info' would
5164 use. With ARG force class query for object methods."
5166 (let* ((idlwave-query-class nil
)
5167 (idlwave-force-class-query (equal arg
'(4)))
5168 (module (idlwave-what-module))
5169 (default (concat (idlwave-make-full-name (nth 2 module
) (car module
))
5170 (if (eq (nth 1 module
) 'pro
) "<p>" "<f>")))
5174 (if (eq 'system
(car-safe (nth 3 x
)))
5175 ;; Take out system routines with no source.
5178 (concat (idlwave-make-full-name (nth 2 x
) (car x
))
5179 (if (eq (nth 1 x
) 'pro
) "<p>" "<f>"))
5181 (idlwave-routines))))
5182 (name (idlwave-completing-read
5183 (format "Module (Default %s): "
5184 (if default default
"none"))
5187 (if (string-match "\\`\\s-*\\'" name
)
5188 ;; Nothing, use the default.
5189 (setq name default
))
5190 (if (string-match "<[fp]>" name
)
5191 (setq type
(substring name -
2 -
1)
5192 name
(substring name
0 -
3)))
5193 (if (string-match "\\(.*\\)::\\(.*\\)" name
)
5194 (setq class
(match-string 1 name
)
5195 name
(match-string 2 name
)))
5196 (setq name
(idlwave-sintern-routine-or-method name class
)
5197 class
(idlwave-sintern-class class
)
5198 type
(cond ((equal type
"f") 'fun
)
5199 ((equal type
"p") 'pro
)
5201 (idlwave-do-find-module name type class
)))
5203 (defun idlwave-do-find-module (name type class
)
5204 (let ((name1 (idlwave-make-full-name class name
))
5206 (buf (current-buffer))
5208 (setq entry
(idlwave-rinfo-assq name type class
(idlwave-routines))
5209 source
(nth 3 entry
))
5211 ((or (null name
) (equal name
""))
5214 (error "Nothing known about a module %s" name1
))
5215 ((eq (car source
) 'system
)
5216 (error "Source code for system routine %s is not available."
5218 ((equal (cdr source
) "")
5219 (error "Source code for routine %s is not available."
5221 ((memq (car source
) '(buffer lib compiled
))
5223 (if (eq (car source
) 'lib
)
5224 (idlwave-find-lib-file-noselet
5226 (format "%s.pro" (downcase name
))))
5227 (idlwave-find-file-noselect (cdr source
))))
5228 (pop-to-buffer buf1
)
5230 (let ((case-fold-search t
))
5231 (if (re-search-forward
5232 (concat "^[ \t]*\\<"
5233 (cond ((equal type
"f") "function")
5234 ((equal type
"p") "pro")
5235 (t "\\(pro\\|function\\)"))
5237 (regexp-quote (downcase name1
))
5240 (goto-char (match-beginning 0))
5243 (error "Could not find routine %s" name1
)))))))
5245 (defun idlwave-what-module ()
5246 "Return a default module for stuff near point.
5247 Used by `idlwave-routine-info' and `idlwave-find-module'."
5249 (let* ((where (idlwave-where))
5251 (pro (car (nth 0 where
)))
5252 (func (car (nth 1 where
)))
5253 (this-word (idlwave-this-word "a-zA-Z0-9$_"))
5254 (next-char (save-excursion (skip-chars-forward "a-zA-Z0-9$_")
5258 ((and (eq cw
'procedure
)
5259 (not (equal this-word
"")))
5260 (setq this-word
(idlwave-sintern-routine-or-method
5261 this-word
(nth 2 (nth 3 where
))))
5262 (list this-word
'pro
5263 (idlwave-determine-class
5264 (cons this-word
(cdr (nth 3 where
)))
5266 ((and (eq cw
'function
)
5267 (not (equal this-word
""))
5268 (eq next-char ?\
()) ; exclude arrays, vars.
5269 (setq this-word
(idlwave-sintern-routine-or-method
5270 this-word
(nth 2 (nth 3 where
))))
5271 (list this-word
'fun
5272 (idlwave-determine-class
5273 (cons this-word
(cdr (nth 3 where
)))
5276 (list func
'fun
(idlwave-determine-class (nth 1 where
) 'fun
)))
5278 (list pro
'pro
(idlwave-determine-class (nth 0 where
) 'pro
)))
5281 (defun idlwave-fix-keywords (name type class keywords
)
5282 ;; This fixes the list of keywords.
5283 (let ((case-fold-search t
)
5286 ;; If this is the OBJ_NEW function, try to figure out the class and use
5287 ;; the keywords from the corresponding INIT method.
5288 (if (and (equal name
"OBJ_NEW")
5289 (eq major-mode
'idlwave-mode
))
5290 (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point)))
5291 (string (buffer-substring bos
(point)))
5292 (case-fold-search t
)
5294 (and (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)"
5296 (setq class
(idlwave-sintern-class (match-string 1 string
)))
5299 (nth 5 (idlwave-rinfo-assq
5300 (idlwave-sintern-method "INIT")
5303 (idlwave-routines))))))))
5305 ;; If the class is `t', combine all keywords of all methods NAME
5307 (loop for x in
(idlwave-routines) do
5308 (and (nth 2 x
) ; non-nil class
5309 (or (and (eq (nth 1 x
) type
) ; default type
5310 (eq (car x
) name
)) ; default name
5311 (and (eq (nth 1 x
) type1
) ; backup type
5312 (eq (car x
) name1
))) ; backup name
5313 (mapcar (lambda (k) (add-to-list 'keywords k
))
5315 (setq keywords
(idlwave-uniquify keywords
)))
5316 ;; Return the final list
5319 (defvar idlwave-rinfo-map
(make-sparse-keymap))
5320 (define-key idlwave-rinfo-map
5321 (if (featurep 'xemacs
) [button2] [mouse-2])
5322 'idlwave-mouse-active-rinfo)
5323 (define-key idlwave-rinfo-map
5324 (if (featurep 'xemacs) [button3] [mouse-3])
5325 'idlwave-mouse-active-rinfo-right)
5326 (defvar idlwave-popup-source)
5328 (defun idlwave-display-calling-sequence (name type class)
5329 ;; Display the calling sequence of module NAME, type TYPE in class CLASS.
5330 (let* ((entry (idlwave-rinfo-assq
5331 name type class (idlwave-routines)))
5332 (name (or (car entry) name))
5333 (class (or (nth 2 entry) class))
5334 (source (nth 3 entry))
5335 ;;(system (eq (car source) 'system))
5336 (calling-seq (nth 4 entry))
5337 (keywords (nth 5 entry))
5339 (help-echo3 " Button3: IDL Online Help")
5340 (help-echo23 "Button2: Pop to source and back. Button3: IDL Online Help")
5342 (data (list name type class (current-buffer) olh))
5343 (km-prop (if (featurep 'xemacs) 'keymap 'local-map))
5345 (setq keywords (idlwave-fix-keywords name type class keywords))
5348 (error "No %s %s known" type name))
5349 ((or (null name) (equal name ""))
5350 (error "No function or procedure call at point."))
5352 (error "Calling sequence of %s %s is not available" type name))
5355 (set-buffer (get-buffer-create "*Help*"))
5356 (setq buffer-read-only nil)
5358 (set (make-local-variable 'idlwave-popup-source) nil)
5359 (setq props (list 'mouse-face 'highlight
5360 km-prop idlwave-rinfo-map
5361 'help-echo help-echo23
5362 'data (cons 'usage data)))
5366 (format calling-seq class name)
5367 (format calling-seq name))
5369 (add-text-properties beg (point) props)
5371 (insert "Keywords:")
5373 (insert " No keywords accepted.")
5377 (if (>= (+ col 1 (length (car x)))
5384 props (list 'mouse-face 'highlight
5385 km-prop idlwave-rinfo-map
5386 'data (cons 'keyword data)
5387 'help-echo help-echo3
5390 (add-text-properties beg (point) props)
5391 (setq col (+ col 1 (length (car x)))))
5397 props (list 'mouse-face 'highlight
5398 km-prop idlwave-rinfo-map
5399 'help-echo help-echo23
5400 'data (cons 'origin data)))
5402 ((eq (car source) 'system)
5403 (insert "system routine"))
5404 ((equal source '(lib))
5405 (insert (format "library file %s.pro" (downcase name))))
5406 ((eq (car source) 'lib)
5407 (insert "library file ")
5408 (insert (cdr source)))
5409 ((eq (car source) 'buffer)
5410 (insert "buffer visiting ")
5411 (insert (abbreviate-file-name (cdr source))))
5412 ((eq (car source) 'compiled)
5413 (insert "compiled from ")
5414 (insert (cdr source))))
5415 (add-text-properties beg (point) props)
5416 (setq buffer-read-only t))
5417 (display-buffer "*Help*")
5418 (if (and (setq win (get-buffer-window "*Help*"))
5419 idlwave-resize-routine-help-window)
5421 (let ((ww (selected-window)))
5425 (enlarge-window (- (/ (frame-height) 2)
5427 (shrink-window-if-larger-than-buffer))
5428 (select-window ww)))))))))
5430 (defun idlwave-mouse-active-rinfo-right (ev)
5432 (idlwave-mouse-active-rinfo ev 'right))
5434 (defun idlwave-mouse-active-rinfo (ev &optional right)
5436 (mouse-set-point ev)
5437 (let (data id name type class buf keyword olh bufwin)
5438 (setq data (get-text-property (point) 'data)
5439 keyword (get-text-property (point) 'keyword)
5446 bufwin (get-buffer-window buf t))
5447 (cond ((or (eq id 'usage) (eq id 'origin))
5449 (idlwave-search-online-help name type class)
5450 (setq idlwave-popup-source (not idlwave-popup-source))
5451 (if idlwave-popup-source
5453 (idlwave-do-find-module name type class)
5455 (setq idlwave-popup-source nil)
5456 (if (window-live-p bufwin) (select-window bufwin))
5457 (error (nth 1 err))))
5459 (select-window bufwin)
5460 (pop-to-buffer buf)))))
5463 (idlwave-search-online-help keyword 'kwd class olh)
5464 (error "Button2 not active for keywords"))))))
5466 ;; ----------------------------------------------------------------------------
5468 ;; Additions for use with imenu.el and func-menu.el
5469 ;; (pop-up a list of IDL units in the current file).
5472 (defun idlwave-prev-index-position ()
5473 "Search for the previous procedure or function.
5474 Return nil if not found. For use with imenu.el."
5477 ((idlwave-find-key "\\<\\(pro\\|function\\)\\>" -1 'nomark))
5478 ;; ((idlwave-find-key idlwave-begin-unit-reg 1 'nomark)
5481 (defun idlwave-unit-name ()
5482 "Return the unit name.
5483 Assumes that point is at the beginning of the unit as found by
5484 `idlwave-prev-index-position'."
5487 (let ((begin (point)))
5488 (re-search-forward "[a-zA-Z][a-zA-Z0-9$_]+\\(::[a-zA-Z][a-zA-Z0-9$_]+\\)?")
5489 (if (fboundp 'buffer-substring-no-properties)
5490 (buffer-substring-no-properties begin (point))
5491 (buffer-substring begin (point)))))
5493 (defun idlwave-function-menu ()
5494 "Use `imenu' or `function-menu' to jump to a procedure or function."
5496 (if (string-match "XEmacs" emacs-version)
5498 (require 'func-menu)
5501 (imenu (imenu-choose-buffer-index))))
5503 ;; Here we kack func-menu.el in order to support this new mode.
5504 ;; The latest versions of func-menu.el already have this stuff in, so
5505 ;; we hack only if it is not already there.
5506 (when (fboundp 'eval-after-load)
5507 (eval-after-load "func-menu"
5509 (or (assq 'idlwave-mode fume-function-name-regexp-alist)
5510 (not (boundp 'fume-function-name-regexp-idl)) ; avoid problems
5511 (setq fume-function-name-regexp-alist
5512 (cons '(idlwave-mode . fume-function-name-regexp-idl)
5513 fume-function-name-regexp-alist)))
5514 (or (assq 'idlwave-mode fume-find-function-name-method-alist)
5515 (not (fboundp 'fume-find-next-idl-function-name)) ; avoid problems
5516 (setq fume-find-function-name-method-alist
5517 (cons '(idlwave-mode . fume-find-next-idl-function-name)
5518 fume-find-function-name-method-alist))))))
5520 (defun idlwave-edit-in-idlde ()
5521 "Edit the current file in IDL Development environment."
5523 (start-process "idldeclient" nil
5524 idlwave-shell-explicit-file-name "-c" "-e"
5525 (buffer-file-name) "&"))
5527 (defun idlwave-launch-idlhelp ()
5528 "Start the IDLhelp application."
5530 (start-process "idlhelp" nil idlwave-help-application))
5532 ;; Menus - using easymenu.el
5533 (defvar idlwave-mode-menu-def
5535 ["PRO/FUNC menu" idlwave-function-menu t]
5537 ["Subprogram Start" idlwave-beginning-of-subprogram t]
5538 ["Subprogram End" idlwave-end-of-subprogram t]
5539 ["Block Start" idlwave-beginning-of-block t]
5540 ["Block End" idlwave-end-of-block t]
5541 ["Up Block" idlwave-backward-up-block t]
5542 ["Down Block" idlwave-down-block t]
5543 ["Skip Block Backward" idlwave-backward-block t]
5544 ["Skip Block Forward" idlwave-forward-block t])
5546 ["Subprogram" idlwave-mark-subprogram t]
5547 ["Block" idlwave-mark-block t]
5548 ["Header" idlwave-mark-doclib t])
5550 ["Indent Subprogram" idlwave-indent-subprogram t]
5551 ["(Un)Comment Region" idlwave-toggle-comment-region "C-c ;"]
5552 ["Continue/Split line" idlwave-split-line t]
5554 ["Toggle Auto Fill" idlwave-auto-fill-mode :style toggle
5555 :selected (symbol-value idlwave-fill-function)])
5557 ["Procedure" idlwave-procedure t]
5558 ["Function" idlwave-function t]
5559 ["Doc Header" idlwave-doc-header t]
5560 ["Log" idlwave-doc-modification t]
5562 ["Case" idlwave-case t]
5563 ["For" idlwave-for t]
5564 ["Repeat" idlwave-repeat t]
5565 ["While" idlwave-while t]
5567 ["Close Block" idlwave-close-block t])
5568 ("Completion / RInfo"
5569 ["Complete" idlwave-complete t]
5571 ["1 Procedure Name" (idlwave-complete 'procedure) t]
5572 ["2 Procedure Keyword" (idlwave-complete 'procedure-keyword) t]
5574 ["3 Function Name" (idlwave-complete 'function) t]
5575 ["4 Function Keyword" (idlwave-complete 'function-keyword) t]
5577 ["5 Procedure Method Name" (idlwave-complete 'procedure-method) t]
5578 ["6 Procedure Method Keyword" (idlwave-complete 'procedure-method-keyword) t]
5580 ["7 Function Method Name" (idlwave-complete 'function-method) t]
5581 ["8 Function Method Keyword" (idlwave-complete 'function-method-keyword) t]
5583 ["9 Class Name" idlwave-complete-class t])
5585 ["Show Routine Info" idlwave-routine-info t]
5586 ["Show Routine Doc with IDLHELP" idlwave-routine-info-from-idlhelp t]
5588 ["Find Routine Source" idlwave-find-module t]
5590 ["Update Routine Info" idlwave-update-routine-info t]
5592 "IDL Library Routine Info"
5593 ["Select Library Directories" idlwave-create-libinfo-file t]
5594 ["Scan Directories" (idlwave-update-routine-info '(16)) idlwave-scanned-lib-directories])
5597 ["Generate IDL tags" idlwave-make-tags t]
5598 ["Start IDL shell" idlwave-shell t]
5599 ["Edit file in IDLDE" idlwave-edit-in-idlde t]
5600 ["Launch IDL Help" idlwave-launch-idlhelp t])
5603 ["Browse IDLWAVE Group" idlwave-customize t]
5605 ["Build Full Customize Menu" idlwave-create-customize-menu
5606 (fboundp 'customize-menu-create)])
5608 ["Describe Mode" describe-mode t]
5609 ["Abbreviation List" idlwave-list-abbrevs t]
5611 ["Commentary in idlwave.el" idlwave-show-commentary t]
5612 ["Commentary in idlwave-shell.el" idlwave-shell-show-commentary t]
5614 ["Info" idlwave-info t]
5616 ["Launch IDL Help" idlwave-launch-idlhelp t])))
5618 (defvar idlwave-mode-debug-menu-def
5620 ["Start IDL shell" idlwave-shell t]
5621 ["Save and .RUN buffer" idlwave-shell-save-and-run
5622 (and (boundp 'idlwave-shell-automatic-start)
5623 idlwave-shell-automatic-start)]))
5625 (if (or (featurep 'easymenu) (load "easymenu" t))
5627 (easy-menu-define idlwave-mode-menu idlwave-mode-map
5628 "IDL and WAVE CL editing menu"
5629 idlwave-mode-menu-def)
5630 (easy-menu-define idlwave-mode-debug-menu idlwave-mode-map
5631 "IDL and WAVE CL editing menu"
5632 idlwave-mode-debug-menu-def)))
5634 (defun idlwave-customize ()
5635 "Call the customize function with idlwave as argument."
5637 ;; Try to load the code for the shell, so that we can customize it
5639 (or (featurep 'idlwave-shell)
5640 (load "idlwave-shell" t))
5641 (customize-browse 'idlwave))
5643 (defun idlwave-create-customize-menu ()
5644 "Create a full customization menu for IDLWAVE, insert it into the menu."
5646 (if (fboundp 'customize-menu-create)
5648 ;; Try to load the code for the shell, so that we can customize it
5650 (or (featurep 'idlwave-shell)
5651 (load "idlwave-shell" t))
5653 '("IDLWAVE") "Customize"
5654 `(["Browse IDLWAVE group" idlwave-customize t]
5656 ,(customize-menu-create 'idlwave)
5657 ["Set" Custom-set t]
5658 ["Save" Custom-save t]
5659 ["Reset to Current" Custom-reset-current t]
5660 ["Reset to Saved" Custom-reset-saved t]
5661 ["Reset to Standard Settings" Custom-reset-standard t]))
5662 (message "\"IDLWAVE\"-menu now contains full customization menu"))
5663 (error "Cannot expand menu (outdated version of cus-edit.el)")))
5665 (defun idlwave-show-commentary ()
5666 "Use the finder to view the file documentation from `idlwave.el'."
5669 (finder-commentary "idlwave.el"))
5671 (defun idlwave-shell-show-commentary ()
5672 "Use the finder to view the file documentation from `idlwave-shell.el'."
5675 (finder-commentary "idlwave-shell.el"))
5677 (defun idlwave-info ()
5678 "Read documentation for IDLWAVE in the info system."
5681 (Info-goto-node "(idlwave)"))
5683 (defun idlwave-list-abbrevs (arg)
5684 "Show the code abbreviations define in IDLWAVE mode.
5685 This lists all abbrevs where the replacement text differs from the input text.
5686 These are the ones the users want to learn to speed up their writing.
5688 The function does *not* list abbrevs which replace a word with itself
5689 to call a hook. These hooks are used to change the case of words or
5690 to blink the matching `begin', and the user does not need to know them.
5692 With arg, list all abbrevs with the corresponding hook.
5694 This function was written since `list-abbrevs' looks terrible for IDLWAVE mode."
5697 (let ((table (symbol-value 'idlwave-mode-abbrev-table))
5699 str rpl func fmt (len-str 0) (len-rpl 0))
5702 (if (symbol-value sym)
5704 (setq str (symbol-name sym)
5705 rpl (symbol-value sym)
5706 func (symbol-function sym))
5708 (setq func (prin1-to-string func))
5709 (if (and (listp func) (stringp (nth 2 func)))
5710 (setq rpl (concat "EVAL: " (nth 2 func))
5713 (if (or arg (not (string= rpl str)))
5715 (setq len-str (max len-str (length str)))
5716 (setq len-rpl (max len-rpl (length rpl)))
5717 (setq abbrevs (cons (list str rpl func) abbrevs)))))))
5720 (setq abbrevs (sort abbrevs (lambda (a b) (string< (car a) (car b)))))
5722 (setq fmt (format "%%-%ds %%-%ds %%s\n" len-str len-rpl))
5723 (with-output-to-temp-buffer "*Help*"
5726 (princ "Abbreviations and Actions in IDLWAVE-Mode\n")
5727 (princ "=========================================\n\n")
5728 (princ (format fmt "KEY" "REPLACE" "HOOK"))
5729 (princ (format fmt "---" "-------" "----")))
5730 (princ "Code Abbreviations and Templates in IDLWAVE-Mode\n")
5731 (princ "================================================\n\n")
5732 (princ (format fmt "KEY" "ACTION" ""))
5733 (princ (format fmt "---" "------" "")))
5736 (setq str (car list)
5739 (princ (format fmt str rpl func)))
5741 ;; Make sure each abbreviation uses only one display line
5743 (set-buffer "*Help*")
5744 (setq truncate-lines t)))
5746 (run-hooks 'idlwave-load-hook)
5750 ;;; idlwave.el ends here