branch: externals/idlwave commit bba75c7901de1e8f1862e02f57fd581b8d8b7501 Author: jdsmith <jdsmith> Commit: jdsmith <jdsmith>
- Added new XML routines to parse XML into idl_xml_rinfo.el. - Load and parse XML by default, before falling back onto idlw-rinfo. By default, only parse XML if catalog file is newer than idl_xml_rinfo.el. - Improved handling of <,>,<],>=,->,&,&&, for degenerate surround and action. - Added "indent entire statement" menu entry to Format. - Renamed load-system-rinfo load-all-rinfo, and added load-sytem-routine-info, for loading, and XML converting. - Have "Launch idl Help" menu start the assistant, if it's called for. - Renamed "Complete Specific". - Removed make-tags menu item - Added menu entry to rescan XML catalog. - Use shift iso-lefttab to make Shift-Tab work as C-u Tab. --- idlwave.el | 665 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 537 insertions(+), 128 deletions(-) diff --git a/idlwave.el b/idlwave.el index 269ad269fe..1cd2fa751d 100644 --- a/idlwave.el +++ b/idlwave.el @@ -7,7 +7,7 @@ ;; Chris Chase <ch...@att.com> ;; Maintainer: J.D. Smith <jdsm...@as.arizona.edu> ;; Version: VERSIONTAG -;; Date: $Date: 2005/12/20 21:31:47 $ +;; Date: $Date: 2006/01/09 19:23:25 $ ;; Keywords: languages ;; This file is part of GNU Emacs. @@ -358,17 +358,20 @@ usually a good idea.." :type 'boolean) (defcustom idlwave-init-rinfo-when-idle-after 10 - "*Seconds of idle time before routine info is automatically initialized. -Initializing the routine info can take long, in particular if a large -library catalog is involved. When Emacs is idle for more than the number -of seconds specified by this variable, it starts the initialization. -The process is split into five steps, in order to keep possible work -interruption as short as possible. If one of the steps finishes, and no -user input has arrived in the mean time, initialization proceeds immediately -to the next step. -A good value for this variable is about 1/3 of the time initialization -take in you setup. So if you have a fast machine and no problems with a slow network connection, don't hesitate to set this to 2 seconds. -A Value of 0 means, don't initialize automatically." + "*Seconds of idle time before routine info is automatically +initialized. Initializing the routine info can take a long time, in +particular if a large number of library catalogs are involved. When +Emacs is idle for more than the number of seconds specified by this +variable, it starts the initialization. The process is split into +five steps, in order to keep work interruption as short as possible. +If one of the steps finishes, and no user input has arrived in the +mean time, initialization proceeds immediately to the next step. A +good value for this variable is about 1/3 of the time initialization +take in your setup. So if you have a fast machine and no problems +with a slow network connection, don't hesitate to set this to 2 +seconds. A Value of 0 means, don't initialize automatically, but +instead wait until routine information is needed, and initialize +then." :group 'idlwave-routine-info :type 'number) @@ -423,16 +426,17 @@ t means to show all source files." :type 'integer) (defcustom idlwave-library-path nil - "Library path for Windows and MacOS. Not needed under Unix. When -selecting the directories to scan for IDL user catalog routine info, -IDLWAVE can, under UNIX, query the shell for the exact search path -\(the value of !PATH). However, under Windows and MacOS (pre-OSX), -the IDLWAVE shell does not work. In this case, this variable can be -set to specify the paths where IDLWAVE can find PRO files. The shell -will only be asked for a list of paths when this variable is nil. The -value is a list of directories. A directory preceeded by a `+' will -be searched recursively. If you set this variable on a UNIX system, -the shell will not be queried. See also `idlwave-system-directory'." + "Library path for Windows and MacOS (OS9). Not needed under Unix. +When selecting the directories to scan for IDL user catalog routine +info, IDLWAVE can, under UNIX, query the shell for the exact search +path \(the value of !PATH). However, under Windows and MacOS +(pre-OSX), the IDLWAVE shell does not work. In this case, this +variable can be set to specify the paths where IDLWAVE can find PRO +files. The shell will only be asked for a list of paths when this +variable is nil. The value is a list of directories. A directory +preceeded by a `+' will be searched recursively. If you set this +variable on a UNIX system, the shell will not be queried. See also +`idlwave-system-directory'." :group 'idlwave-routine-info :type '(repeat (directory))) @@ -447,6 +451,7 @@ value of `!DIR'. See also `idlwave-library-path'." :group 'idlwave-routine-info :type 'directory) +;; Configuration files (defcustom idlwave-config-directory (convert-standard-filename "~/.idlwave") "*Directory for configuration files and user-library catalog." @@ -454,6 +459,7 @@ value of `!DIR'. See also `idlwave-library-path'." :type 'file) (defvar idlwave-user-catalog-file "idlusercat.el") +(defvar idlwave-xml-system-rinfo-converted-file "idl_xml_rinfo.el") (defvar idlwave-path-file "idlpath.el") (defvar idlwave-libinfo-file nil @@ -991,10 +997,6 @@ If nil it will not be inserted." "Path locations of external commands used by IDLWAVE." :group 'idlwave) -;; WARNING: The following variable has recently been moved from -;; idlw-shell.el to this file. I hope this does not break -;; anything. - (defcustom idlwave-shell-explicit-file-name "idl" "*If non-nil, this is the command to run IDL. Should be an absolute file path or path relative to the current environment @@ -1019,7 +1021,8 @@ split it for you." :group 'idlwave-external-programs) (defcustom idlwave-help-application "idlhelp" - "*The external application providing reference help for programming." + "*The external application providing reference help for programming. +Obsolete, if the IDL Assistant is being used for help." :group 'idlwave-external-programs :type 'string) @@ -1053,6 +1056,7 @@ IDL process is made." (defgroup idlwave-misc nil "Miscellaneous options for IDLWAVE mode." + :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) :group 'idlwave) (defcustom idlwave-startup-message t @@ -1539,7 +1543,7 @@ Capitalize system variables - action only (define-key idlwave-mode-map "\C-c\C-n" 'idlwave-next-statement) ;; (define-key idlwave-mode-map "\r" 'idlwave-newline) ;; (define-key idlwave-mode-map "\t" 'idlwave-indent-line) -(define-key idlwave-mode-map [(shift tab)] 'idlwave-indent-statement) +(define-key idlwave-mode-map [(shift iso-lefttab)] 'idlwave-indent-statement) (define-key idlwave-mode-map "\C-c\C-a" 'idlwave-auto-fill-mode) (define-key idlwave-mode-map "\M-q" 'idlwave-fill-paragraph) (define-key idlwave-mode-map "\M-s" 'idlwave-edit-in-idlde) @@ -1604,18 +1608,21 @@ Capitalize system variables - action only ;; Set action and key bindings. ;; See description of the function `idlwave-action-and-binding'. ;; Automatically add spaces for the following characters -;(idlwave-action-and-binding "&" '(idlwave-surround -1 -1 '(?&) 1 -; (lambda (char) 0))) -(idlwave-action-and-binding "<" '(idlwave-surround -1 -1)) -;; Binding works for both > and ->, by changing the length of the token. -(idlwave-action-and-binding ">" '(idlwave-surround -1 -1 '(?-) 1 - 'idlwave-gtr-pad-hook)) -(idlwave-action-and-binding "->" '(idlwave-surround -1 -1 nil 2) t) -(idlwave-action-and-binding "," '(idlwave-surround 0 -1)) - -;; Automatically add spaces to equal sign if not keyword + +;; Actions for & are complicated by && +(idlwave-action-and-binding "&" 'idlwave-custom-ampersand-surround) + +;; Automatically add spaces to equal sign if not keyword. This needs +;; to go ahead of > and <, so >= and <= will be treated correctly (idlwave-action-and-binding "=" '(idlwave-expand-equal -1 -1)) +;; Actions for > and < are complicated by >=, <=, and ->... +(idlwave-action-and-binding "<" '(idlwave-custom-ltgtr-surround nil)) +(idlwave-action-and-binding ">" '(idlwave-custom-ltgtr-surround 'gtr)) + +(idlwave-action-and-binding "," '(idlwave-surround 0 -1 1)) + + ;;; ;;; Abbrev Section ;;; @@ -1820,11 +1827,10 @@ The main features of this mode are 3. Online IDL Help --------------- + \\[idlwave-context-help] displays the IDL documentation relevant - for the system variable, keyword, or routine at point. A single - key stroke gets you directly to the right place in the docs. The - HTML help files package must be installed for this to work -- check - the IDLWAVE webpage for the correct package for your version. See + for the system variable, keyword, or routines at point. A single + key stroke gets you directly to the right place in the docs. See the manual to configure where and how the HTML help is displayed. 4. Completion @@ -1976,12 +1982,17 @@ The main features of this mode are (unless idlwave-setup-done (if (not (file-directory-p idlwave-config-directory)) (make-directory idlwave-config-directory)) - (setq idlwave-user-catalog-file (expand-file-name - idlwave-user-catalog-file - idlwave-config-directory) - idlwave-path-file (expand-file-name - idlwave-path-file - idlwave-config-directory)) + (setq + idlwave-user-catalog-file (expand-file-name + idlwave-user-catalog-file + idlwave-config-directory) + idlwave-xml-system-rinfo-converted-file + (expand-file-name + idlwave-xml-system-rinfo-converted-file + idlwave-config-directory) + idlwave-path-file (expand-file-name + idlwave-path-file + idlwave-config-directory)) (idlwave-read-paths) ; we may need these early (setq idlwave-setup-done t))) @@ -2188,7 +2199,6 @@ Also checks if the correct end statement has been used." (defun idlwave-close-block () "Terminate the current block with the correct END statement." (interactive) - ;; Start new line if we are not in a new line (unless (save-excursion (skip-chars-backward " \t") @@ -2199,12 +2209,27 @@ Also checks if the correct end statement has been used." (insert "end") (idlwave-show-begin))) -(defun idlwave-gtr-pad-hook (char) - "Let the > symbol expand around -> if present. The new token length -is returned." - 2) - -(defun idlwave-surround (&optional before after escape-chars length ec-hook) +(defun idlwave-custom-ampersand-surround (&optional is-action) + "Surround &, leaving room for && (which surrround as well)." + (let* ((prev-char (char-after (- (point) 2))) + (next-char (char-after (point))) + (amp-left (eq prev-char ?&)) + (amp-right (eq next-char ?&)) + (len (if amp-left 2 1))) + (unless amp-right ;no need to do it twice, amp-left will catch it. + (idlwave-surround -1 (if (or is-action amp-left) -1) len)))) + +(defun idlwave-custom-ltgtr-surround (gtr &optional is-action) + "Surround > and < by blanks, leaving room for >= and <=, and considering ->." + (let* ((prev-char (char-after (- (point) 2))) + (next-char (char-after (point))) + (method-invoke (and gtr (eq prev-char ?-))) + (len (if method-invoke 2 1))) + (unless (eq next-char ?=) + ;; Key binding: pad only on left, to save for possible >=/<= + (idlwave-surround -1 (if (or is-action method-invoke) -1) len)))) + +(defun idlwave-surround (&optional before after length is-action) "Surround the LENGTH characters before point with blanks. LENGTH defaults to 1. Optional arguments BEFORE and AFTER affect the behavior before and @@ -2217,42 +2242,28 @@ integer < 0 at least |n| spaces The function does nothing if any of the following conditions is true: - `idlwave-surround-by-blank' is nil -- the character before point is inside a string or comment -- the char preceeding the string to be surrounded is a member of ESCAPE-CHARS. - This hack is used to avoid padding of `>' when it is part of - the '->' operator. In this case, ESCAPE-CHARS would be '(?-). - -If a function is passed in EC-HOOK, and an ESCAPE-CHARS match occurs, -the named function will be called with a single argument of the -preceeding character. Then idlwave-surround will run as usual if -EC-HOOK returns non-nil, and a new length will be taken from the -return value." +- the character before point is inside a string or comment" (when (and idlwave-surround-by-blank (not (idlwave-quoted))) - (let* ((length (or length 1)) ; establish a default for LENGTH - (prev-char (char-after (- (point) (1+ length))))) - (when (or (not (memq prev-char escape-chars)) - (and (fboundp ec-hook) - (setq length - (save-excursion (funcall ec-hook prev-char))))) - (backward-char length) - (save-restriction - (let ((here (point))) - (skip-chars-backward " \t") - (if (bolp) - ;; avoid clobbering indent - (progn - (move-to-column (idlwave-calculate-indent)) - (if (<= (point) here) - (narrow-to-region (point) here)) - (goto-char here))) - (idlwave-make-space before)) - (skip-chars-forward " \t")) - (forward-char length) - (idlwave-make-space after) - ;; Check to see if the line should auto wrap - (if (and (equal (char-after (1- (point))) ?\ ) - (> (current-column) fill-column)) - (funcall auto-fill-function)))))) + (let ((length (or length 1))) ; establish a default for LENGTH + (backward-char length) + (save-restriction + (let ((here (point))) + (skip-chars-backward " \t") + (if (bolp) + ;; avoid clobbering indent + (progn + (move-to-column (idlwave-calculate-indent)) + (if (<= (point) here) + (narrow-to-region (point) here)) + (goto-char here))) + (idlwave-make-space before)) + (skip-chars-forward " \t")) + (forward-char length) + (idlwave-make-space after) + ;; Check to see if the line should auto wrap + (if (and (equal (char-after (1- (point))) ?\ ) + (> (current-column) fill-column)) + (funcall auto-fill-function))))) (defun idlwave-make-space (n) "Make space at point. @@ -2673,7 +2684,7 @@ statement." (if st (append st (match-end 0)))))) -(defun idlwave-expand-equal (&optional before after) +(defun idlwave-expand-equal (&optional before after is-action) "Pad '=' with spaces. Two cases: Assignment statement, and keyword assignment. Which case is determined using `idlwave-start-of-substatement' and `idlwave-statement-type'. The @@ -2694,6 +2705,8 @@ only post-padded. You must use a space before these to disambiguate \(not just for padding, but for proper parsing by IDL too!). Other operators, such as ##=, ^=, etc., will be pre-padded. +IS-ACTION is ignored. + See `idlwave-surround'." (if idlwave-surround-by-blank (let @@ -2716,7 +2729,7 @@ See `idlwave-surround'." (if (eq t idlwave-pad-keyword) ;; Everything gets padded equally - (idlwave-surround before after nil len) + (idlwave-surround before after len) ;; Treating keywords/for variables specially... (let ((st (save-excursion ; To catch "for" variables (idlwave-start-of-substatement t) @@ -2731,7 +2744,7 @@ See `idlwave-surround'." (idlwave-surround 0 0) ) ; remove space (t))) ; leave any spaces alone - (t (idlwave-surround before after nil len)))))))) + (t (idlwave-surround before after len)))))))) (defun idlwave-indent-and-action (&optional arg) @@ -2812,18 +2825,20 @@ If the optional argument EXPAND is non-nil then the actions in (set-marker mloc nil))) (defun idlwave-do-action (action) - "Perform an action repeatedly on a line. -ACTION is a list (REG . FUNC). REG is a regular expression. FUNC is -either a function name to be called with `funcall' or a list to be -evaluated with `eval'. The action performed by FUNC should leave point -after the match for REG - otherwise an infinite loop may be entered." + "Perform an action repeatedly on a line. ACTION is a list (REG +. FUNC). REG is a regular expression. FUNC is either a function name +to be called with `funcall' or a list to be evaluated with `eval'. +The action performed by FUNC should leave point after the match for +REG - otherwise an infinite loop may be entered. FUNC is always +passed a final argument of 'is-action, so it can discriminate between +being run as an action, or a key binding" (let ((action-key (car action)) (action-routine (cdr action))) (beginning-of-line) (while (idlwave-look-at action-key) (if (listp action-routine) - (eval action-routine) - (funcall action-routine))))) + (eval (append action-routine '('is-action))) + (funcall action-routine 'is-action))))) (defun idlwave-indent-to (col &optional min) "Indent from point with spaces until column COL. @@ -4004,7 +4019,7 @@ you specify /." ;; Call etags (if (not (string-match "^[ \\t]*$" item)) (progn - (message (concat "Tagging " item "...")) + (message "%s" (concat "Tagging " item "...")) (setq errbuf (get-buffer-create "*idltags-error*")) (setq status (+ status (if (eq 0 (call-process @@ -4117,9 +4132,9 @@ blank lines." for var = (car entry) do (if (not (consp (symbol-value var))) (set var (list nil)))) + ;; Reset the system & library hash (when (or (eq what t) (eq what 'syslib) (null (cdr idlwave-sint-routines))) - ;; Reset the system & library hash (loop for entry in entries for var = (car entry) for size = (nth 1 entry) do (setcdr (symbol-value var) @@ -4127,9 +4142,9 @@ blank lines." (setq idlwave-sint-dirs nil idlwave-sint-libnames nil)) + ;; Reset the buffer & shell hash (when (or (eq what t) (eq what 'bufsh) (null (car idlwave-sint-routines))) - ;; Reset the buffer & shell hash (loop for entry in entries for var = (car entry) for size = (nth 1 entry) do (setcar (symbol-value var) @@ -4392,7 +4407,8 @@ will re-read the catalog." ;; ("ROUTINE" type class ;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") | ;; (buffer pro_file dir) | (compiled pro_file dir) -;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...))) +;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...)) +;; ("HELPFILE2" (("KWD2" . link) ...)) ...) ;; ;; DIR will be supplied dynamically while loading library catalogs, ;; and is sinterned to save space, as is LIBNAME. PRO_FILE can be a @@ -4466,9 +4482,9 @@ information updated immediately, leave NO-CONCATENATE nil." ;; We can safely scan the buffer stuff first (progn (idlwave-update-buffer-routine-info) - (and load (idlwave-load-system-rinfo override-idle))) + (and load (idlwave-load-all-rinfo override-idle))) ;; We first do the system info, and then the buffers - (and load (idlwave-load-system-rinfo override-idle)) + (and load (idlwave-load-all-rinfo override-idle)) (idlwave-update-buffer-routine-info)) ;; Let's see if there is a shell @@ -4529,14 +4545,396 @@ information updated immediately, leave NO-CONCATENATE nil." (defvar idlwave-library-routines nil "Obsolete variable.") +;;------ XML Help routine info system +(defun idlwave-load-system-routine-info () + ;; Load the system routine info from the cached routine info file, + ;; which, if necessary, will be re-created from the XML file on + ;; disk. As a last fallback, load the (likely outdated) idlw-rinfo + ;; file distributed with older IDLWAVE versions (<6.0) + (unless (and (load idlwave-xml-system-rinfo-converted-file + 'noerror 'nomessage) + (idlwave-xml-system-routine-info-up-to-date)) + ;; See if we can create it from XML source + (condition-case nil + (idlwave-convert-xml-system-routine-info) + (error + (unless (load idlwave-xml-system-rinfo-converted-file + 'noerror 'nomessage) + (if idlwave-system-routines + (message + "Failed to load converted routine info, using old conversion.") + (message + "Failed to convert XML routine info, falling back on idlw-rinfo.") + (if (not (load "idlw-rinfo" 'noerror 'nomessage)) + (message + "Could not locate any system routine information.")))))))) + +(defun idlwave-xml-system-routine-info-up-to-date() + (let* ((dir (file-name-as-directory + (expand-file-name "help/online_help" (idlwave-sys-dir)))) + (catalog-file (expand-file-name "idl_catalog.xml" dir))) + (file-newer-than-file-p ;converted file is newer than catalog + idlwave-xml-system-rinfo-converted-file + catalog-file))) + +(defvar idlwave-system-class-info nil) ; Gathered from idlw-rinfo +(defvar idlwave-system-variables-alist nil + "Alist of system variables and the associated structure tags. +Gets set in cached XML rinfo, or `idlw-rinfo.el'.") +(defvar idlwave-executive-commands-alist nil + "Alist of system variables and their help files.") +(defvar idlwave-help-special-topic-words nil) + + +(defun idlwave-shorten-syntax (syntax name &optional class) + ;; From a list of syntax statments, shorten with %s and group with "or" + (let ((case-fold-search t)) + (mapconcat + (lambda (x) + (while (string-match name x) + (setq x (replace-match "%s" t t x))) + (if class + (while (string-match class x) + (setq x (replace-match "%s" t t x)))) + x) + (nreverse syntax) + " or "))) + +(defun idlwave-xml-create-class-method-lists (xml-entry) + ;; Create a class list entry from the xml parsed list., returning a + ;; cons of form (class-entry method-entries). + (let* ((nameblock (nth 1 xml-entry)) + (class (cdr (assq 'name nameblock))) + (link (cdr (assq 'link nameblock))) + (params (cddr xml-entry)) + (case-fold-search t) + class-entry + method methods-entry extra-kwds + get-props set-props init-props inherits + pelem ptype) + (while params + (setq pelem (car params) + ptype (car pelem) + props (car (cdr pelem))) + (cond + + ((eq ptype 'SUPERCLASS) + (push (cdr (assq 'name props)) inherits)) + + ((eq ptype 'PROPERTY) + (let ((pname (cdr (assq 'name props))) + (plink (cdr (assq 'link props))) + (get (string= (cdr (assq 'get props)) "Yes")) + (set (string= (cdr (assq 'set props)) "Yes")) + (init (string= (cdr (assq 'init props)) "Yes"))) + (if get (push (list pname plink) get-props)) + (if set (push (list pname plink) set-props)) + (if init (push (list pname plink) init-props)))) + + ((eq ptype 'METHOD) + (setq method (cdr (assq 'name props))) + (setq extra-kwds ;;Assume all property keywords are gathered already + (cond + ((string-match (concat class "::Init") method) + (put 'init-props 'matched t) + init-props) + ((string-match (concat class "::GetProperty") method) + (put 'get-props 'matched t) + get-props) + ((string-match (concat class "::SetProperty") method) + (put 'set-props 'matched t) + set-props) + (t nil))) + (setq methods-entry + (nconc (idlwave-xml-create-rinfo-list pelem class extra-kwds) + methods-entry))) + (t)) + (setq params (cdr params))) + ;(unless (get 'init-props 'matched) + ; (message "Failed to match Init in class %s" class)) + ;(unless (get 'get-props 'matched) + ; (message "Failed to match GetProperty in class %s" class)) + ;(unless (get 'set-props 'matched) + ; (message "Failed to match SetProperty in class %s" class)) + (setq class-entry + (if inherits + (list class (append '(inherits) inherits) (list 'link link)) + (list class (list 'link link)))) + (cons class-entry methods-entry))) + +(defun idlwave-xml-create-rinfo-list (xml-entry &optional class extra-kws) + ;; Create correctly structured list elements from ROUTINE or METHOD + ;; XML list structures. Return a list of list elements, with more + ;; than one sub-list possible if a routine can serve as both + ;; procedure and function (e.g. call_method). + (let* ((nameblock (nth 1 xml-entry)) + (name (cdr (assq 'name nameblock))) + (link (cdr (assq 'link nameblock))) + (params (cddr xml-entry)) + (syntax-vec (make-vector 3 nil)) ; procedure, function, exec command + (case-fold-search t) + syntax kwds pelem ptype entry props result type) + (if class ;; strip out class name from class method name string + (if (string-match (concat class "::") name) + (setq name (substring name (match-end 0))))) + (while params + (setq pelem (car params) + ptype (car pelem) + props (car (cdr pelem))) + (cond + ((eq ptype 'SYNTAX) + (setq syntax (cdr (assq 'name props))) + (if (string-match "->" syntax) + (setq syntax (replace-match "->" t nil syntax))) + (setq type (cdr (assq 'type props))) + (push syntax + (aref syntax-vec (cond + ((string-match "^pro" type) 0) + ((string-match "^fun" type) 1) + ((string-match "^exec" type) 2))))) + ((eq ptype 'KEYWORD) + (push (list (cdr (assq 'name props)) + (cdr (assq 'link props))) kwds)) + (t)); Do nothing for the others + (setq params (cdr params))) + + ;; Debug +; (if (and (null (aref syntax-vec 0)) +; (null (aref syntax-vec 1)) +; (null (aref syntax-vec 2))) +; (with-current-buffer (get-buffer-create "XML_complaints") +; (if class +; (insert (format "Missing SYNTAX entry for %s::%s\n" class name)) +; (insert (message "Missing SYNTAX entry for %s\n" name))))) + + ;; Executive commands are treated specially + (if (aref syntax-vec 2) + (cons (substring name 1) link) + (if extra-kws (setq kwds (nconc kwds extra-kws))) + (setq kwds (idlwave-rinfo-group-keywords kwds link)) + (loop for idx from 0 to 1 do + (if (aref syntax-vec idx) + (push (append (list name (if (eq idx 0) 'pro 'fun) + class '(system) + (idlwave-shorten-syntax + (aref syntax-vec idx) name class)) + kwds) result))) + result))) + + +(defun idlwave-rinfo-group-keywords (kwds master-link) + ;; Group keywords by link file, as a list with elements + ;; (linkfile ( ("KWD1" . link1) ("KWD2" . link2)) + (let (kwd link anchor linkfiles block master-elt) + (while kwds + (setq kwd (car kwds) + link (idlwave-split-link-target (nth 1 kwd)) + anchor (cdr link) + link (car link) + kwd (car kwd)) + (if (setq block (assoc link linkfiles)) + (push (cons kwd anchor) (cdr block)) + (push (list link (cons kwd anchor)) linkfiles)) + (setq kwds (cdr kwds))) + ;; Ensure the master link is there + (if (setq master-elt (assoc master-link linkfiles)) + (if (eq (car linkfiles) master-elt) + linkfiles + (cons master-elt (delq master-elt linkfiles))) + (push (list master-link) linkfiles)))) + +(defun idlwave-convert-xml-clean-statement-aliases (aliases) + ;; Clean up the syntax of routines which are actually aliases by + ;; removing the "OR" from the statements + (let (syntax entry) + (loop for x in aliases do + (setq entry (assoc x idlwave-system-routines)) + (when entry + (while (string-match " +or +" (setq syntax (nth 4 entry))) + (setf (nth 4 entry) (replace-match ", " t t syntax))))))) + +(defun idlwave-convert-xml-clean-routine-aliases (aliases) + ;; Duplicate and trim original routine aliases from rinfo list + ;; This if for, e.g. OPENR/OPENW/OPENU + (let (alias remove-list new parts all-parts) + (loop for x in aliases do + (when (setq parts (split-string (cdr x) "/")) + (setq new (assoc (cdr x) all-parts)) + (unless new + (setq new (cons (cdr x) parts)) + (push new all-parts)) + (setcdr new (delete (car x) (cdr new))))) + + ;; Add any missing aliases (separate by slashes) + (loop for x in all-parts do + (if (cdr x) + (push (cons (nth 1 x) (car x)) aliases))) + + (loop for x in aliases do + (when (setq alias (assoc (cdr x) idlwave-system-routines)) + (unless (memq alias remove-list) (push alias remove-list)) + (setq alias (copy-sequence alias)) + (setcar alias (car x)) + (push alias idlwave-system-routines))) + (loop for x in remove-list do + (delq x idlwave-system-routines)))) + + +(defun idlwave-xml-create-sysvar-alist (xml-entry) + ;; Create a sysvar list entry from the xml parsed list. + (let* ((nameblock (nth 1 xml-entry)) + (sysvar (substring (cdr (assq 'name nameblock)) 1)) + (link (cdr (assq 'link nameblock))) + (params (cddr xml-entry)) + (case-fold-search t) + pelem ptype props fields tags) + (while params + (setq pelem (car params) + ptype (car pelem) + props (car (cdr pelem))) + (cond + ((eq ptype 'FIELD) + (push (cons (cdr (assq 'name props)) + (cdr + (idlwave-split-link-target (cdr (assq 'link props))))) + tags))) + (setq params (cdr params))) + (delq nil + (list sysvar (if tags (cons 'tags tags)) (list 'link link))))) + + +(defvar idlwave-xml-routine-info-file nil) + +(defun idlwave-save-routine-info () + (if idlwave-xml-routine-info-file + (with-temp-file idlwave-xml-system-rinfo-converted-file + (insert + (concat ";; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +;; IDLWAVE Routine Information File (IDLWAVE version " idlwave-mode-version ") +;; Automatically generated from source file: +;; " idlwave-xml-routine-info-file " +;; on " (current-time-string) " +;; Do not edit.")) + (insert (format "\n(setq idlwave-xml-routine-info-file \n \"%s\")" + idlwave-xml-routine-info-file)) + (insert "\n(setq idlwave-system-routines\n '") + (prin1 idlwave-system-routines (current-buffer)) + (insert ")") + (insert "\n(setq idlwave-system-variables-alist\n '") + (prin1 idlwave-system-variables-alist (current-buffer)) + (insert ")") + (insert "\n(setq idlwave-system-class-info\n '") + (prin1 idlwave-system-class-info (current-buffer)) + (insert ")") + (insert "\n(setq idlwave-executive-commands-alist\n '") + (prin1 idlwave-executive-commands-alist (current-buffer)) + (insert ")") + (insert "\n(setq idlwave-help-special-topic-words\n '") + (prin1 idlwave-help-special-topic-words (current-buffer)) + (insert ")")))) + +(defun idlwave-convert-xml-system-routine-info () + ;; Convert XML supplied routine info into internal SEXP form, and + ;; cache to disk for quick recovery. + (let* ((dir (file-name-as-directory + (expand-file-name "help/online_help" (idlwave-sys-dir)))) + (catalog-file (expand-file-name "idl_catalog.xml" dir)) + (elem-cnt 0) + rinfo msg-cnt elem type nelem class-result alias + routines routine-aliases statement-aliases + buf version-string) + (if (not (file-exists-p catalog-file)) + (error "No such XML routine info file: %s" catalog-file) + (if (not (file-readable-p catalog-file)) + (error "Cannot read XML routine info file: %s" catalog-file))) + (require 'xml) + (message "Reading XML routine info...") + (unwind-protect + (progn + ;; avoid warnings about read-only files + (setq buf (find-file-noselect catalog-file 'nowarn)) + (setq rinfo (xml-parse-file catalog-file))) + (if (bufferp buf) (kill-buffer buf))) + (message "Reading XML routine info...done") + (setq rinfo (assq 'CATALOG rinfo)) + (unless rinfo (message "Failed to parse XML routine info")) + ;;(setq rinfo (car rinfo)) ; Skip the catalog stuff. + + (setq version-string (cdr (assq 'version (nth 1 rinfo))) + rinfo (cddr rinfo)) + + (setq nelem (length rinfo) + msg-cnt (/ nelem 100)) + + (setq idlwave-xml-routine-info-file nil) + (message "Converting XML routine info...") + (setq idlwave-system-routines nil + idlwave-system-variables-alist nil + idlwave-system-class-info nil + idlwave-executive-commands-alist nil + idlwave-help-special-topic-words nil) + (while rinfo + (setq elem (car rinfo) + type (car elem) + rinfo (cdr rinfo)) + + (incf elem-cnt) + (if (= (mod elem-cnt msg-cnt) 0) + (message "Converting XML routine info...%2d%%" + (/ (* elem-cnt 100) nelem))) + (cond + ((eq type 'ROUTINE) + (if (setq alias (assq 'alias_to (nth 1 elem))) + (push (cons (cdr (assq 'name (nth 1 elem))) (cdr alias)) + routine-aliases) + (setq routines (idlwave-xml-create-rinfo-list elem)) + (if (listp (cdr routines)) + (setq idlwave-system-routines + (nconc idlwave-system-routines routines)) + ;; a cons cell is an executive commands + (push routines idlwave-executive-commands-alist)))) + + ((eq type 'CLASS) + (setq class-result (idlwave-xml-create-class-method-lists elem)) + (push (car class-result) idlwave-system-class-info) + (setq idlwave-system-routines + (nconc idlwave-system-routines (cdr class-result)))) + + ((eq type 'STATEMENT) + (push (cons (cdr (assq 'name (nth 1 elem))) + (cdr (assq 'link (nth 1 elem)))) + idlwave-help-special-topic-words) + (if (setq alias (assq 'alias_to (nth 1 elem))) + (unless (member (cdr alias) statement-aliases) + (push (cdr alias) statement-aliases)))) + + ((eq type 'SYSVAR) + (push (idlwave-xml-create-sysvar-alist elem) + idlwave-system-variables-alist)) + (t))) + (idlwave-convert-xml-clean-routine-aliases routine-aliases) + (idlwave-convert-xml-clean-statement-aliases statement-aliases) + (setq idlwave-xml-routine-info-file catalog-file) + (idlwave-save-routine-info) + (message "Converting XML routine info...done"))) + + +;; ("ROUTINE" type class +;; (system) | (lib pro_file dir "LIBNAME") | (user pro_file dir "USERLIB") | +;; (buffer pro_file dir) | (compiled pro_file dir) +;; "calling_string" ("HELPFILE" (("KWD1" . link1) ...)) +;; ("HELPFILE2" (("KWD2" . link) ...)) ...) + + (defun idlwave-load-rinfo-next-step () (let ((inhibit-quit t) (arr idlwave-load-rinfo-steps-done)) (when (catch 'exit (when (not (aref arr 0)) - (message "Loading idlw-rinfo.el in idle time...") - (load "idlw-rinfo" 'noerror 'nomessage) - (message "Loading idlw-rinfo.el in idle time...done") + (message "Loading system routine info in idle time...") + (idlwave-load-system-routine-info) + ;;(load "idlw-rinfo" 'noerror 'nomessage) + (message "Loading system routine info in idle time...done") (aset arr 0 t) (throw 'exit t)) (when (not (aref arr 1)) @@ -4595,10 +4993,14 @@ information updated immediately, leave NO-CONCATENATE nil." idlwave-init-rinfo-when-idle-after nil 'idlwave-load-rinfo-next-step)))))) -(defun idlwave-load-system-rinfo (&optional force) - ;; Load and case-treat the system and catalog files. +(defun idlwave-load-all-rinfo (&optional force) + ;; Load and case-treat the system, user catalog, and library routine + ;; info files. + + ;; System (when (or force (not (aref idlwave-load-rinfo-steps-done 0))) - (load "idlw-rinfo" 'noerror 'nomessage)) + ;;(load "idlw-rinfo" 'noerror 'nomessage)) + (idlwave-load-system-routine-info)) (when (or force (not (aref idlwave-load-rinfo-steps-done 1))) (message "Normalizing idlwave-system-routines...") (setq idlwave-system-routines @@ -4607,6 +5009,8 @@ information updated immediately, leave NO-CONCATENATE nil." (setq idlwave-routines (copy-sequence idlwave-system-routines)) (setq idlwave-last-system-routine-info-cons-cell (nthcdr (1- (length idlwave-routines)) idlwave-routines)) + + ;; User catalog (when (and (stringp idlwave-user-catalog-file) (file-regular-p idlwave-user-catalog-file)) (condition-case nil @@ -4626,6 +5030,8 @@ information updated immediately, leave NO-CONCATENATE nil." (idlwave-sintern-rinfo-list idlwave-user-catalog-routines 'sys)) (message "Normalizing user catalog routines...done"))) + + ;; Library catalog (when (or force (not (aref idlwave-load-rinfo-steps-done 4))) (idlwave-scan-library-catalogs "Loading and normalizing library catalogs...")) @@ -4848,7 +5254,6 @@ information updated immediately, leave NO-CONCATENATE nil." (t "@@@@@@@@"))) - (defun idlwave-create-user-catalog-file (&optional arg) "Scan all files on selected dirs of IDL search path for routine information. @@ -5200,8 +5605,8 @@ be set to nil to disable library catalog scanning." message-base (not (string= idlwave-library-catalog-libname old-libname))) - (message (concat message-base - idlwave-library-catalog-libname)) + (message "%s" (concat message-base + idlwave-library-catalog-libname)) (setq old-libname idlwave-library-catalog-libname)) (when idlwave-library-catalog-routines (setq all-routines @@ -6874,7 +7279,6 @@ backward." (match-string-no-properties 5))))) (defvar idlwave-class-info nil) -(defvar idlwave-system-class-info nil) ; Gathered from idlw-rinfo (defvar idlwave-class-reset nil) ; to reset buffer-local classes (add-hook 'idlwave-update-rinfo-hook @@ -7150,12 +7554,6 @@ property indicating the link is added." (add-hook 'idlwave-update-rinfo-hook 'idlwave-sysvars-reset) (add-hook 'idlwave-after-load-rinfo-hook 'idlwave-sintern-sysvar-alist) -(defvar idlwave-executive-commands-alist nil - "Alist of system variables and their help files.") - -(defvar idlwave-system-variables-alist nil - "Alist of system variables and the associated structure tags. -Gets set in `idlw-rinfo.el'.") (defun idlwave-complete-sysvar-or-tag () "Complete a system variable." @@ -7221,6 +7619,12 @@ Gets set in `idlw-rinfo.el'.") main)))) ;; setting dynamic!!! (t (error "This should not happen"))))) +(defun idlwave-split-link-target (link) + "Split a given link into link file and anchor." + (if (string-match idlwave-html-link-sep link) + (cons (substring link 0 (match-beginning 0)) + (string-to-number (substring link (match-end 0)))))) + (defun idlwave-substitute-link-target (link target) "Substitute the target anchor for the given link." (let (main-base) @@ -8622,12 +9026,15 @@ Assumes that point is at the beginning of the unit as found by (interactive) (start-process "idldeclient" nil idlwave-shell-explicit-file-name "-c" "-e" - (buffer-file-name) "&")) - + (buffer-file-name))) + +(defvar idlwave-help-use-assistant) (defun idlwave-launch-idlhelp () "Start the IDLhelp application." (interactive) - (start-process "idlhelp" nil idlwave-help-application)) + (if idlwave-help-use-assistant + (idlwave-help-assistant-raise) + (start-process "idlhelp" nil idlwave-help-application))) ;; Menus - using easymenu.el (defvar idlwave-mode-menu-def @@ -8647,8 +9054,10 @@ Assumes that point is at the beginning of the unit as found by ["Block" idlwave-mark-block t] ["Header" idlwave-mark-doclib t]) ("Format" + ["Indent Entire Statement" idlwave-indent-statement + :active t :keys "C-u \\[indent-for-tab-command]" ] ["Indent Subprogram" idlwave-indent-subprogram t] - ["(Un)Comment Region" idlwave-toggle-comment-region "C-c ;"] + ["(Un)Comment Region" idlwave-toggle-comment-region t] ["Continue/Split line" idlwave-split-line t] "--" ["Toggle Auto Fill" idlwave-auto-fill-mode :style toggle @@ -8667,7 +9076,7 @@ Assumes that point is at the beginning of the unit as found by ["Close Block" idlwave-close-block t]) ("Completion" ["Complete" idlwave-complete t] - ("Complete Special" + ("Complete Specific" ["1 Procedure Name" (idlwave-complete 'procedure) t] ["2 Procedure Keyword" (idlwave-complete 'procedure-keyword) t] "--" @@ -8689,6 +9098,7 @@ Assumes that point is at the beginning of the unit as found by ["Resolve Routine" idlwave-resolve (featurep 'idlw-shell)] "--" ["Update Routine Info" idlwave-update-routine-info t] + ["Rescan XML Help Catalog" idlwave-convert-xml-system-routine-info t] "--" "IDL User Catalog" ["Select Catalog Directories" (idlwave-create-user-catalog-file nil) t] @@ -8707,7 +9117,6 @@ Assumes that point is at the beginning of the unit as found by ["Insert TAB character" idlwave-hard-tab t]) "--" ("External" - ["Generate IDL tags" idlwave-make-tags t] ["Start IDL shell" idlwave-shell t] ["Edit file in IDLDE" idlwave-edit-in-idlde t] ["Launch IDL Help" idlwave-launch-idlhelp t])