diff options
Diffstat (limited to 'lisp/progmodes/idlwave.el')
-rw-r--r-- | lisp/progmodes/idlwave.el | 971 |
1 files changed, 824 insertions, 147 deletions
diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el index 0dda942d6c5..a921fbe81f9 100644 --- a/lisp/progmodes/idlwave.el +++ b/lisp/progmodes/idlwave.el @@ -1,11 +1,12 @@ ;;; idlwave.el --- IDL and WAVE CL editing mode for GNU Emacs - -;; Copyright (c) 1994, 1995, 1997, 1999, 2000 Free Software Foundation +;; Copyright (c) 1994-1997 Chris Chase +;; Copyright (c) 1999 Carsten Dominik +;; Copyright (c) 1999, 2000 Free Software Foundation ;; Author: Chris Chase <chase@att.com> ;; Maintainer: Carsten Dominik <dominik@strw.leidenuniv.nl> -;; Version: 4.2 -;; Date: $Date: 2000/12/06 19:46:15 $ +;; Version: 4.7 +;; Date: $Date: 2000/12/07 20:47:51 $ ;; Keywords: languages ;; This file is part of the GNU Emacs. @@ -450,6 +451,14 @@ definition is displayed instead." "Face for highlighting links into IDLWAVE online help." :group 'idlwave-online-help) +(defcustom idlwave-help-activate-links-agressively t + "*Non-nil means, make all possible links in help active. +This just activates all words which are also a help topic - some links may +be misleading." + :group 'idlwave-online-help + :type 'boolean) + + (defgroup idlwave-completion nil "Completion options for IDLWAVE mode." :prefix "idlwave" @@ -544,6 +553,13 @@ This option is only effective when the online help system is installed." :group 'idlwave-completion :type 'boolean) +(defcustom idlwave-support-inheritance t + "Non-nil means, treat inheritance with completion, online help etc. +When nil, IDLWAVE only knows about the native methods and tags of a class, +not about inherited ones." + :group 'idlwave-routine-info + :type 'boolean) + (defcustom idlwave-completion-show-classes 1 "*Number of classes to show when completing object methods and keywords. When completing methods or keywords for an object with unknown class, @@ -929,7 +945,7 @@ If nil it will not be inserted." ;;; External Programs ------------------------------------------------------- (defgroup idlwave-external-programs nil - "Miscellaneous options for IDLWAVE mode." + "Path locations of external commands used by IDLWAVE." :group 'idlwave) ;; WARNING: The following variable has recently been moved from @@ -954,6 +970,32 @@ execution search path." :group 'idlwave-external-programs :type 'string) +;;; Some Shell variables which must be defined here.----------------------- + +(defcustom idlwave-shell-debug-modifiers '() + "List of modifiers to be used for the debugging commands. +Will be used to bind debugging commands in the shell buffer and in all +source buffers. These are additional convenience bindings, the debugging +commands are always available with the `C-c C-d' prefix. +If you set this to '(control shift), this means setting a breakpoint will +be on `C-S-b', compiling a source file on `C-S-c' etc. Possible modifiers +are `control', `meta', `super', `hyper', `alt', and `shift'." + :group 'idlwave-shell-general-setup + :type '(set :tag "Specify modifiers" + (const control) + (const meta) + (const super) + (const hyper) + (const alt) + (const shift))) + +(defcustom idlwave-shell-automatic-start nil + "*If non-nil attempt invoke idlwave-shell if not already running. +This is checked when an attempt to send a command to an +IDL process is made." + :group 'idlwave-shell-general-setup + :type 'boolean) + ;;; Miscellaneous variables ------------------------------------------------- (defgroup idlwave-misc nil @@ -992,6 +1034,8 @@ class-arrows Object Arrows with class property" (const :tag "IDL Keywords (reserved words)" idl-keywords) (const :tag "Statement Labels" label) (const :tag "Goto Statements" goto) + (const :tag "Tags in Structure Definition" structtag) + (const :tag "Structure Name" structname) (const :tag "Common Blocks" common-blocks) (const :tag "Keyword Parameters" keyword-parameters) (const :tag "System Variables" system-variables) @@ -1044,28 +1088,26 @@ As a user, you should not set this to t.") ;; To update this regexp, update the list of keywords and ;; evaluate the form. ; (insert -; (concat -; "\"\\\\<" -; (regexp-opt -; '("and" "or" "xor" "not" -; "eq" "ge" "gt" "le" "lt" "ne" -; "for" "do" "endfor" -; "if" "then" "endif" "else" "endelse" -; "case" "of" "endcase" -; "begin" "end" -; "repeat" "until" "endrep" -; "while" "endwhile" -; "goto" "return" -; "inherits" "mod" -; "on_error" "on_ioerror")) ; on_error is not officially reserved -; "\\\\>\"")) - (concat "\\<\\(" - "and\\|begin\\|case\\|do\\|e\\(lse\\|nd\\(case\\|else\\|" - "for\\|if\\|rep\\|while\\)?\\|q\\)\\|for\\|g\\(oto\\|[et]\\)" - "\\|i\\(f\\|nherits\\)\\|l[et]\\|mod\\|n\\(e\\|ot\\)\\|" - "o\\(n_ioerror\\|[fr]\\)\\|re\\(peat\\|turn\\)\\|then\\|" - "until\\|while\\|xor" - "\\)\\>")) +; (prin1-to-string +; (concat +; "\\<\\(" +; (regexp-opt +; '("and" "or" "xor" "not" +; "eq" "ge" "gt" "le" "lt" "ne" +; "for" "do" "endfor" +; "if" "then" "endif" "else" "endelse" +; "case" "of" "endcase" +; "switch" "break" "continue" "endswitch" +; "begin" "end" +; "repeat" "until" "endrep" +; "while" "endwhile" +; "goto" "return" +; "inherits" "mod" +; "compile_opt" "forward_function" +; "on_error" "on_ioerror")) ; on_error is not officially reserved +; "\\)\\>"))) + + "\\<\\(and\\|b\\(egin\\|reak\\)\\|c\\(ase\\|o\\(mpile_opt\\|ntinue\\)\\)\\|do\\|e\\(lse\\|nd\\(case\\|else\\|for\\|if\\|rep\\|switch\\|while\\)?\\|q\\)\\|for\\(ward_function\\)?\\|g\\(oto\\|[et]\\)\\|i\\(f\\|nherits\\)\\|l[et]\\|mod\\|n\\(e\\|ot\\)\\|o\\(n_\\(error\\|ioerror\\)\\|[fr]\\)\\|re\\(peat\\|turn\\)\\|switch\\|then\\|until\\|while\\|xor\\)\\>") ;; Procedure declarations. Fontify keyword plus procedure name. ;; Function declarations. Fontify keyword plus function name. @@ -1104,6 +1146,16 @@ As a user, you should not set this to t.") (1 font-lock-keyword-face) (2 font-lock-reference-face))) + ;; Tags in structure definitions. Note that this definition actually + ;; collides with labels, so we have to use the same face. + (structtag + '("\\<\\([a-zA-Z][a-zA-Z0-9_]*:\\)[^:]" (1 font-lock-reference-face))) + + ;; Structure names + (structname + '("\\({\\|\\<inherits\\s-\\)\\s-*\\([a-zA-Z][a-zA-Z0-9_]*\\)[},\t \n]" + (2 font-lock-function-name-face))) + ;; Named parameters, like /xlog or ,xrange=[] ;; This is anchored to the comma preceeding the keyword. ;; Treats continuation lines, works only during whole buffer @@ -1144,6 +1196,8 @@ As a user, you should not set this to t.") fixme fixme label label goto goto + structtag structtag + structname structname keyword-parameters keyword-parameters system-variables system-variables special-operators special-operators @@ -1163,6 +1217,8 @@ As a user, you should not set this to t.") batch-files idl-keywords label goto + structtag + structname common-blocks keyword-parameters system-variables @@ -1196,7 +1252,8 @@ As a user, you should not set this to t.") That is the _beginning_ of a line containing a comment delimiter `;' preceded only by whitespace.") -(defconst idlwave-begin-block-reg "\\<\\(pro\\|function\\|begin\\|case\\)\\>" +(defconst idlwave-begin-block-reg + "\\<\\(pro\\|function\\|begin\\|case\\|switch\\)\\>" "Regular expression to find the beginning of a block. The case does not matter. The search skips matches in comments.") @@ -1213,7 +1270,7 @@ not matter. The search skips matches in comments.") "Regular expression to match a continued line.") (defconst idlwave-end-block-reg - "\\<end\\(\\|case\\|else\\|for\\|if\\|rep\\|while\\)\\>" + "\\<end\\(\\|case\\|switch\\|else\\|for\\|if\\|rep\\|while\\)\\>" "Regular expression to find the end of a block. The case does not matter. The search skips matches found in comments.") @@ -1225,6 +1282,7 @@ not matter. The search skips matches found in comments.") ("for" . "endfor") ("then" . "endif") ("repeat" . "endrep") + ("switch" . "endswitch") ("while" . "endwhile")) "Matches between statements and the corresponding END variant. The cars are the reserved words starting a block. If the block really @@ -1266,6 +1324,7 @@ blocks starting with a BEGIN statement. The matches must have associations '(repeat . ("repeat\\>" "repeat")) '(goto . ("goto\\>" nil)) '(case . ("case\\>" nil)) + '(switch . ("switch\\>" nil)) (cons 'call (list (concat idlwave-identifier "\\(\\s *$\\|\\s *,\\)") nil)) '(assign . ("[^=>\n]*=" nil))) @@ -1299,7 +1358,7 @@ Normally a space.") "Character which is inserted as a last character on previous line by \\[idlwave-split-line] to begin a continuation line. Normally $.") -(defconst idlwave-mode-version " 4.2") +(defconst idlwave-mode-version " 4.7") (defmacro idlwave-keyword-abbrev (&rest args) "Creates a function for abbrev hooks to call `idlwave-check-abbrev' with args." @@ -1366,6 +1425,24 @@ Otherwise ARGS forms a list that is evaluated." ,@body) (set-syntax-table saved-syntax)))) +(defvar idlwave-print-symbol-syntax-table + (copy-syntax-table idlwave-mode-syntax-table) + "Syntax table that treats symbol characters as word characters.") + +(modify-syntax-entry ?$ "w" idlwave-find-symbol-syntax-table) +(modify-syntax-entry ?_ "w" idlwave-find-symbol-syntax-table) +(modify-syntax-entry ?! "w" idlwave-find-symbol-syntax-table) +(modify-syntax-entry ?. "w" idlwave-find-symbol-syntax-table) + +(defmacro idlwave-with-special-syntax1 (&rest body) + "Execute BODY with a different systax table." + `(let ((saved-syntax (syntax-table))) + (unwind-protect + (progn + (set-syntax-table idlwave-find-symbol-syntax-table) + ,@body) + (set-syntax-table saved-syntax)))) + (defun idlwave-action-and-binding (key cmd &optional select) "KEY and CMD are made into a key binding and an indent action. KEY is a string - same as for the `define-key' function. CMD is a @@ -1446,6 +1523,19 @@ Capitalize system variables - action only (define-key idlwave-mode-map "\C-c\C-d" 'idlwave-debug-map) (define-key idlwave-mode-map "\C-c\C-d\C-c" 'idlwave-shell-save-and-run) (define-key idlwave-mode-map "\C-c\C-d\C-b" 'idlwave-shell-break-here) +(when (and (boundp 'idlwave-shell-debug-modifiers) + (listp idlwave-shell-debug-modifiers) + (not (equal idlwave-shell-debug-modifiers '()))) + ;; Bind the debug commands also with the special modifiers. + (let ((shift (memq 'shift idlwave-shell-debug-modifiers)) + (mods-noshift (delq 'shift + (copy-sequence idlwave-shell-debug-modifiers)))) + (define-key idlwave-mode-map + (vector (append mods-noshift (list (if shift ?C ?c)))) + 'idlwave-shell-save-and-run) + (define-key idlwave-mode-map + (vector (append mods-noshift (list (if shift ?B ?b)))) + 'idlwave-shell-break-here))) (define-key idlwave-mode-map "\C-c\C-f" 'idlwave-for) ;; (define-key idlwave-mode-map "\C-c\C-f" 'idlwave-function) ;; (define-key idlwave-mode-map "\C-c\C-p" 'idlwave-procedure) @@ -1508,6 +1598,7 @@ Capitalize system variables - action only ;; Templates ;; (define-abbrev tb (concat c "c") "" (idlwave-code-abbrev idlwave-case)) + (define-abbrev tb (concat c "sw") "" (idlwave-code-abbrev idlwave-switch)) (define-abbrev tb (concat c "f") "" (idlwave-code-abbrev idlwave-for)) (define-abbrev tb (concat c "fu") "" (idlwave-code-abbrev idlwave-function)) (define-abbrev tb (concat c "pr") "" (idlwave-code-abbrev idlwave-procedure)) @@ -1529,6 +1620,7 @@ Capitalize system variables - action only (define-abbrev tb (concat c "cd") "double()" (idlwave-keyword-abbrev 1)) (define-abbrev tb (concat c "e") "else" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb (concat c "ec") "endcase" 'idlwave-show-begin) + (define-abbrev tb (concat c "es") "endswitch" 'idlwave-show-begin) (define-abbrev tb (concat c "ee") "endelse" 'idlwave-show-begin) (define-abbrev tb (concat c "ef") "endfor" 'idlwave-show-begin) (define-abbrev tb (concat c "ei") "endif else if" 'idlwave-show-begin) @@ -1575,8 +1667,10 @@ Capitalize system variables - action only ;; (define-abbrev tb "and" "and" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "begin" "begin" (idlwave-keyword-abbrev 0 t)) + (define-abbrev tb "break" "break" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "case" "case" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "common" "common" (idlwave-keyword-abbrev 0 t)) + (define-abbrev tb "continue" "continue" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "do" "do" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "else" "else" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "end" "end" 'idlwave-show-begin-check) @@ -1585,6 +1679,7 @@ Capitalize system variables - action only (define-abbrev tb "endfor" "endfor" 'idlwave-show-begin-check) (define-abbrev tb "endif" "endif" 'idlwave-show-begin-check) (define-abbrev tb "endrep" "endrep" 'idlwave-show-begin-check) + (define-abbrev tb "endswitch" "endswitch" 'idlwave-show-begin-check) (define-abbrev tb "endwhi" "endwhi" 'idlwave-show-begin-check) (define-abbrev tb "endwhile" "endwhile" 'idlwave-show-begin-check) (define-abbrev tb "eq" "eq" (idlwave-keyword-abbrev 0 t)) @@ -1604,6 +1699,7 @@ Capitalize system variables - action only (define-abbrev tb "or" "or" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "pro" "pro" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "repeat" "repeat" (idlwave-keyword-abbrev 0 t)) + (define-abbrev tb "switch" "switch" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "then" "then" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "until" "until" (idlwave-keyword-abbrev 0 t)) (define-abbrev tb "while" "while" (idlwave-keyword-abbrev 0 t)) @@ -1686,6 +1782,7 @@ The main features of this mode are \\pr PROCEDURE template \\fu FUNCTION template \\c CASE statement template + \\sw SWITCH statement template \\f FOR loop template \\r REPEAT Loop template \\w WHILE loop template @@ -1964,7 +2061,7 @@ Also checks if the correct end statement has been used." (let ((case-fold-search t)) (save-excursion (cond - ((looking-at "pro\\|case\\|function\\>") + ((looking-at "pro\\|case\\|switch\\|function\\>") (assoc (downcase (match-string 0)) idlwave-block-matches)) ((looking-at "begin\\>") (let ((limit (save-excursion @@ -1992,17 +2089,8 @@ Also checks if the correct end statement has been used." (bolp)) (let ((idlwave-show-block nil)) (newline-and-indent))) - - ;; Check which end is needed and insert it. - (let ((case-fold-search t) end) - (save-excursion - (idlwave-beginning-of-statement) - (idlwave-block-jump-out -1 'nomark) - (if (setq end (idlwave-block-master)) - (setq end (cdr end)) - (error "Cannot close block"))) - (insert end) - (idlwave-newline))) + (insert "end") + (idlwave-show-begin)) (defun idlwave-surround (&optional before after escape-chars length) "Surround the LENGTH characters before point with blanks. @@ -2334,11 +2422,19 @@ the first non-comment statement in the file, and nil otherwise." (= (forward-line -1) 0))) first-statement))) -;; FIXME: end-of-statement does not work correctly when comment lines -;; are inside the statement. It does work correctly for line-end -;; comments, though. (defun idlwave-end-of-statement () "Moves point to the end of the current IDL statement. +If not in a statement just moves to end of line. Returns position." + (interactive) + (while (and (idlwave-is-continuation-line) + (= (forward-line 1) 0)) + (while (and (idlwave-is-comment-or-empty-line) + (= (forward-line 1) 0)))) + (end-of-line) + (point)) + +(defun idlwave-end-of-statement0 () + "Moves point to the end of the current IDL statement. If not in a statement just moves to end of line. Returns position." (interactive) (while (and (idlwave-is-continuation-line) @@ -2473,6 +2569,13 @@ See `idlwave-surround'. " (defun idlwave-indent-and-action () "Call `idlwave-indent-line' and do expand actions." (interactive) + (save-excursion + (if (and idlwave-expand-generic-end + (re-search-backward "\\<\\(end\\)\\s-*\\=" + (max 0 (- (point) 10)) t) + (looking-at "\\(end\\)\\([ \n\t]\\|\\'\\)")) + (progn (goto-char (match-end 1)) + (idlwave-show-begin)))) (idlwave-indent-line t) ) @@ -2677,40 +2780,6 @@ screw things up if the comments contain parentheses characters." ;; Ordinary continuation (idlwave-continuation-indent)))))))) -(defun idlwave-find-key-old (key-reg &optional dir nomark limit) - "Move in direction of the optional second argument DIR to the -next keyword not contained in a comment or string and occurring before -optional fourth argument LIMIT. DIR defaults to forward direction. If -DIR is negative the search is backwards, otherwise, it is -forward. LIMIT defaults to the beginning or end of the buffer -according to the direction of the search. The keyword is given by the -regular expression argument KEY-REG. The search is case insensitive. -Returns position if successful and nil otherwise. If found -`push-mark' is executed unless the optional third argument NOMARK is -non-nil. If found, the point is left at the keyword beginning." - (or dir (setq dir 0)) - (or limit (setq limit (cond ((>= dir 0) (point-max)) ((point-min))))) - (let (found - (case-fold-search t)) - (idlwave-with-special-syntax - (save-excursion - (if (>= dir 0) - (while (and (setq found (and - (re-search-forward key-reg limit t) - (match-beginning 0))) - (idlwave-quoted) - (not (eobp)))) - (while (and (setq found (and - (re-search-backward key-reg limit t) - (match-beginning 0))) - (idlwave-quoted) - (not (bobp))))))) - (if found (progn - (if (not nomark) (push-mark)) - (goto-char found))))) - -;; FIXME: The following is an experimental re-write of the previous -;; function. Still needs to be tested. (defun idlwave-find-key (key-re &optional dir nomark limit) "Move to next match of the regular expression KEY-RE. Matches inside comments or string constants will be ignored. @@ -2796,10 +2865,17 @@ Skips any whitespace. Returns 0 if the end-of-line follows the whitespace." (idlwave-look-at "\\<\\$"))) (defun idlwave-is-comment-line () + "Tests if the current line is a comment line." (save-excursion (beginning-of-line 1) (looking-at "[ \t]*;"))) +(defun idlwave-is-comment-or-empty-line () + "Tests if the current line is a comment line." + (save-excursion + (beginning-of-line 1) + (looking-at "[ \t]*[;\n]"))) + (defun idlwave-look-at (regexp &optional cont beg) "Searches current line from current point for REGEXP. If optional argument CONT is non-nil, searches to the end of @@ -3168,7 +3244,7 @@ is non-nil." (idlwave-fill-paragraph) ;; Insert a blank line comment to separate from the date entry - ;; will keep the entry from flowing onto date line if re-filled. - (insert "\n;\n;\t\t")) + (insert "\n;\n;\t\t"))t (defun idlwave-doc-modification () "Insert a brief modification log at the beginning of the current program. @@ -3178,27 +3254,27 @@ and places the point for the user to add a log. Before moving, saves location on mark ring so that the user can return to previous point." (interactive) (push-mark) - ;; make sure we catch the current line if it begins the unit - (end-of-line) - (idlwave-beginning-of-subprogram) - (let ((pro (idlwave-look-at "\\<\\(function\\|pro\\)\\>")) - (case-fold-search nil)) - (if (re-search-forward - (concat idlwave-doc-modifications-keyword ":") - ;; set search limit at next unit beginning - (save-excursion (idlwave-end-of-subprogram) (point)) - t) - (end-of-line) - ;; keyword not present, insert keyword - (if pro (idlwave-next-statement)) ; skip past pro or function statement - (beginning-of-line) - (insert "\n" comment-start "\n") - (forward-line -2) - (insert comment-start " " idlwave-doc-modifications-keyword ":"))) - (idlwave-newline) - (beginning-of-line) - (insert ";\n;\t") - (run-hooks 'idlwave-timestamp-hook)) + (let* (beg end) + (if (and (or (re-search-backward idlwave-doclib-start nil t) + (progn + (goto-char (point-min)) + (re-search-forward idlwave-doclib-start nil t))) + (setq beg (match-beginning 0)) + (re-search-forward idlwave-doclib-end nil t) + (setq end (match-end 0))) + (progn + (goto-char beg) + (if (re-search-forward + (concat idlwave-doc-modifications-keyword ":") + end t) + (end-of-line) + (goto-char end) + (end-of-line -1) + (insert "\n" comment-start "\n") + (insert comment-start " " idlwave-doc-modifications-keyword ":")) + (insert "\n;\n;\t") + (run-hooks 'idlwave-timestamp-hook)) + (error "No valid DOCLIB header")))) ;;; CJC 3/16/93 ;;; Interface to expand-region-abbrevs which did not work when the @@ -3298,7 +3374,7 @@ expression to enter. The lines containing S1 and S2 are reindented using `indent-region' unless the optional second argument NOINDENT is non-nil." (if (eq major-mode 'idlwave-shell-mode) - ;; This is a gross hack to avoit template abbrev expasion + ;; This is a gross hack to avoit template abbrev expansion ;; in the shell. FIXME: This is a dirty hack. (if (and (eq this-command 'self-insert-command) (equal last-abbrev-location (point))) @@ -3343,6 +3419,14 @@ unless the optional second argument NOINDENT is non-nil." (idlwave-rw-case " of\n\nendcase") "Selector expression")) +(defun idlwave-switch () + "Build skeleton IDL switch statement." + (interactive) + (idlwave-template + (idlwave-rw-case "switch") + (idlwave-rw-case " of\n\nendswitch") + "Selector expression")) + (defun idlwave-for () "Build skeleton for loop statment." (interactive) @@ -3778,6 +3862,30 @@ also set new patterns. Probably this will always have to be t." (setq res (cons new res))) (nreverse res))) +;; Creating new sintern tables + +(defun idlwave-new-sintern-type (tag) + "Define a variable and a function to sintern the new type TAG. +This defines the function `idlwave-sintern-TAG' and the variable +`idlwave-sint-TAGs'." + (let* ((name (symbol-name tag)) + (names (concat name "s")) + (var (intern (concat "idlwave-sint-" names))) + (func (intern (concat "idlwave-sintern-" name)))) + (set var nil) ; initial value of the association list + (fset func ; set the function + `(lambda (name &optional set) + (cond ((not (stringp name)) name) + ((cdr (assoc (downcase name) ,var))) + (set + (setq ,var (cons (cons (downcase name) name) ,var)) + name) + (name)))))) + +(defun idlwave-reset-sintern-type (tag) + "Reset the sintern variable associated with TAG." + (set (intern (concat "idlwave-sint-" (symbol-name tag) "s")) nil)) + ;;--------------------------------------------------------------------------- @@ -3822,6 +3930,10 @@ only returns the value of the variable." ;; return the current value idlwave-routines))) +(defvar idlwave-update-rinfo-hook nil + "List of functions which should run after a global rinfo update. +Does not run after automatic updates of buffer or the shell.") + (defun idlwave-update-routine-info (&optional arg) "Update the internal routine-info lists. These lists are used by `idlwave-routine-info' (\\[idlwave-routine-info]) @@ -3880,12 +3992,12 @@ With two prefix ARG's, also rescans the library tree." ;; causes the concatenation *delayed*, so not in time for ;; the current command. Therefore, we do a concatenation ;; now, even though the shell might do it again. - (idlwave-concatenate-rinfo-lists)) + (idlwave-concatenate-rinfo-lists nil t)) (when ask-shell ;; Ask the shell about the routines it knows. (message "Querying the shell") - (idlwave-shell-update-routine-info)))))) + (idlwave-shell-update-routine-info nil t)))))) (defun idlwave-load-system-rinfo () ;; Load and case-treat the system and lib info files. @@ -3907,7 +4019,9 @@ With two prefix ARG's, also rescans the library tree." (setq idlwave-library-routines (idlwave-sintern-rinfo-list idlwave-library-routines 'sys)) (message "Normalizing idlwave-library-routines...done")) - (error nil)))) + (error nil))) + (run-hooks 'idlwave-after-load-rinfo-hook)) + (defun idlwave-update-buffer-routine-info () (let (res) @@ -3931,7 +4045,7 @@ With two prefix ARG's, also rescans the library tree." (setq idlwave-buffer-routines (idlwave-sintern-rinfo-list res t)))) -(defun idlwave-concatenate-rinfo-lists (&optional quiet) +(defun idlwave-concatenate-rinfo-lists (&optional quiet run-hook) "Put the different sources for routine information together." ;; The sequence here is important because earlier definitions shadow ;; later ones. We assume that if things in the buffers are newer @@ -3950,7 +4064,9 @@ With two prefix ARG's, also rescans the library tree." (length idlwave-buffer-routines) (length idlwave-compiled-routines) (length idlwave-library-routines) - (length idlwave-system-routines)))) + (length idlwave-system-routines))) + (if run-hook + (run-hooks 'idlwave-update-rinfo-hook))) (defun idlwave-class-alist () "Return the class alist - make it if necessary." @@ -4018,7 +4134,8 @@ With two prefix ARG's, also rescans the library tree." (save-excursion (while (setq buf (pop buffers)) (set-buffer buf) - (if (eq major-mode 'idlwave-mode) + (if (and (eq major-mode 'idlwave-mode) + buffer-file-name) ;; yes, this buffer has the right mode. (progn (setq res (condition-case nil (idlwave-get-buffer-routine-info) @@ -4057,6 +4174,8 @@ With two prefix ARG's, also rescans the library tree." ;; Remove the continuation line stuff (while (string-match "\\([^a-zA-Z0-9$_]\\)\\$[ \t]*\n" string) (setq string (replace-match "\\1 " t nil string))) + (while (string-match "\n" string) + (setq string (replace-match " " t nil string))) ;; Match the name and type. (when (string-match "\\<\\(pro\\|function\\)\\>\\s-+\\(\\([a-zA-Z0-9$_]+\\)::\\)?\\([a-zA-Z0-9$_]+\\)" string) @@ -4407,6 +4526,7 @@ directories and save the routine info. ;; defined routines. (defconst idlwave-routine-info.pro " +;; START OF IDLWAVE SUPPORT ROUTINES pro idlwave_print_info_entry,name,func=func,separator=sep ;; See if it's an object method if name eq '' then return @@ -4474,16 +4594,38 @@ pro idlwave_routine_info idlwave_print_info_entry,all(i),/func,separator=sep print,'>>>END OF IDLWAVE ROUTINE INFO' end + +pro idlwave_get_sysvars + forward_function strjoin,strtrim,strsplit + catch,error_status + if error_status ne 0 then begin + print, 'Cannot get info about system variables' + endif else begin + help,/brief,output=s,/system_variables ; ? unsafe use of OUTPUT= + s = strtrim(strjoin(s,' ',/single),2) ; make one line + v = strsplit(s,' +',/regex,/extract) ; get variables + for i=0,n_elements(v)-1 do begin + t = [''] ; get tag list + a=execute('if n_tags('+v[i]+') gt 0 then t=tag_names('+v[i]+')') + print, 'IDLWAVE-SYSVAR: '+v[i]+' '+strjoin(t,' ',/single) + endfor + endelse +end + +pro idlwave_get_class_tags, class + res = execute('tags=tag_names({'+class+'})') + if res then print,'IDLWAVE-CLASS-TAGS: '+class+string(format='(1000(\" \",A))',tags) +end +;; END OF IDLWAVE SUPPORT ROUTINES " - "The idl program to get the routine info stuff. -The output of this program is parsed by `idlwave-shell-routine-info-filter'.") + "The idl programs to get info from the shell.") (defvar idlwave-idlwave_routine_info-compiled nil "Remembers if the routine info procedure is already compiled.") (defvar idlwave-shell-temp-pro-file) (defvar idlwave-shell-temp-rinfo-save-file) -(defun idlwave-shell-update-routine-info (&optional quiet) +(defun idlwave-shell-update-routine-info (&optional quiet run-hooks) "Query the shell for routine_info of compiled modules and update the lists." ;; Save and compile the procedure. The compiled procedure is then ;; saved into an IDL SAVE file, to allow for fast RESTORE. @@ -4512,7 +4654,7 @@ The output of this program is parsed by `idlwave-shell-routine-info-filter'.") idlwave-shell-temp-rinfo-save-file) `(progn (idlwave-shell-routine-info-filter) - (idlwave-concatenate-rinfo-lists ,quiet)) + (idlwave-concatenate-rinfo-lists ,quiet ,run-hooks)) 'hide)) ;; --------------------------------------------------------------------------- @@ -4521,6 +4663,7 @@ The output of this program is parsed by `idlwave-shell-routine-info-filter'.") (defvar idlwave-completion-help-info nil) (defvar idlwave-current-obj_new-class nil) +(defvar idlwave-complete-special nil) (defun idlwave-complete (&optional arg module class) "Complete a function, procedure or keyword name at point. @@ -4585,6 +4728,14 @@ When we force a method or a method keyword, CLASS can specify the class." (setq this-command last-command) (idlwave-scroll-completions)) + ;; Check for any special completion functions + ((and idlwave-complete-special + (idlwave-complete-special))) + + ((and (idlwave-in-quote) + (not (eq what 'class))) + (idlwave-complete-filename)) + ((null what) (error "Nothing to complete here")) @@ -4595,10 +4746,11 @@ When we force a method or a method keyword, CLASS can specify the class." ((eq what 'procedure) ;; Complete a procedure name (let* ((class-selector (idlwave-determine-class (nth 3 where-list) 'pro)) + (super-classes (idlwave-all-class-inherits class-selector)) (isa (concat "procedure" (if class-selector "-method" ""))) (type-selector 'pro)) (setq idlwave-completion-help-info - (list 'routine nil type-selector class-selector)) + (list 'routine nil type-selector class-selector nil super-classes)) (idlwave-complete-in-buffer 'procedure (if class-selector 'method 'routine) (idlwave-routines) 'idlwave-selector @@ -4613,10 +4765,11 @@ When we force a method or a method keyword, CLASS can specify the class." ((eq what 'function) ;; Complete a function name (let* ((class-selector (idlwave-determine-class (nth 3 where-list) 'fun)) + (super-classes (idlwave-all-class-inherits class-selector)) (isa (concat "function" (if class-selector "-method" ""))) (type-selector 'fun)) (setq idlwave-completion-help-info - (list 'routine nil type-selector class-selector)) + (list 'routine nil type-selector class-selector nil super-classes)) (idlwave-complete-in-buffer 'function (if class-selector 'method 'routine) (idlwave-routines) 'idlwave-selector @@ -4636,6 +4789,7 @@ When we force a method or a method keyword, CLASS can specify the class." (type-selector 'pro) (class (idlwave-determine-class where 'pro)) (class-selector class) + (super-classes (idlwave-all-class-inherits class-selector)) (isa (format "procedure%s-keyword" (if class "-method" ""))) (entry (idlwave-best-rinfo-assq name 'pro class (idlwave-routines))) @@ -4647,7 +4801,7 @@ When we force a method or a method keyword, CLASS can specify the class." (unless list (error (format "No keywords available for procedure %s" (idlwave-make-full-name class name)))) (setq idlwave-completion-help-info - (list 'keyword name type-selector class-selector)) + (list 'keyword name type-selector class-selector nil super-classes)) (idlwave-complete-in-buffer 'keyword 'keyword list nil (format "Select keyword for procedure %s%s" @@ -4666,6 +4820,7 @@ When we force a method or a method keyword, CLASS can specify the class." (type-selector 'fun) (class (idlwave-determine-class where 'fun)) (class-selector class) + (super-classes (idlwave-all-class-inherits class-selector)) (isa (format "function%s-keyword" (if class "-method" ""))) (entry (idlwave-best-rinfo-assq name 'fun class (idlwave-routines))) @@ -4684,7 +4839,7 @@ When we force a method or a method keyword, CLASS can specify the class." (unless list (error (format "No keywords available for function %s" msg-name))) (setq idlwave-completion-help-info - (list 'keyword name type-selector class-selector)) + (list 'keyword name type-selector class-selector nil super-classes)) (idlwave-complete-in-buffer 'keyword 'keyword list nil (format "Select keyword for function %s%s" msg-name @@ -4696,6 +4851,23 @@ When we force a method or a method keyword, CLASS can specify the class." (t (error "This should not happen (idlwave-complete)"))))) +(defvar idlwave-complete-special nil + "List of special completion functions. +These functions are called for each completion. Each function must check +if its own special completion context is present. If yes, it should +use `idlwave-complete-in-buffer' to do some completion and return `t'. +If such a function returns `t', *no further* attempts to complete +other contexts will be done. If the function returns `nil', other completions +will be tried.") +(defun idlwave-complete-special () + (let ((functions idlwave-complete-special) + fun) + (catch 'exit + (while (setq fun (pop functions)) + (if (funcall fun) + (throw 'exit t))) + nil))) + (defun idlwave-make-force-complete-where-list (what &optional module class) ;; Return an artificial WHERE specification to force the completion ;; routine to complete a specific item independent of context. @@ -4731,6 +4903,7 @@ When we force a method or a method keyword, CLASS can specify the class." ((eq what 'procedure-keyword) (let* ((class-selector nil) + (super-classes nil) (type-selector 'pro) (pro (or module (idlwave-completing-read @@ -4744,6 +4917,7 @@ When we force a method or a method keyword, CLASS can specify the class." ((eq what 'function-keyword) (let* ((class-selector nil) + (super-classes nil) (type-selector 'fun) (func (or module (idlwave-completing-read @@ -4758,6 +4932,7 @@ When we force a method or a method keyword, CLASS can specify the class." ((eq what 'procedure-method-keyword) (let* ((class (idlwave-determine-class class-list 'pro)) (class-selector class) + (super-classes (idlwave-all-class-inherits class-selector)) (type-selector 'pro) (pro (or module (idlwave-completing-read @@ -4773,6 +4948,7 @@ When we force a method or a method keyword, CLASS can specify the class." ((eq what 'function-method-keyword) (let* ((class (idlwave-determine-class class-list 'fun)) (class-selector class) + (super-classes (idlwave-all-class-inherits class-selector)) (type-selector 'fun) (func (or module (idlwave-completing-read @@ -4796,6 +4972,20 @@ When we force a method or a method keyword, CLASS can specify the class." (apply 'completing-read args)) (setq-default completion-ignore-case old-value)))) +(defvar idlwave-shell-default-directory) +(defun idlwave-complete-filename () + "Use the comint stuff to complete a file name." + (require 'comint) + (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-") + (comint-completion-addsuffix nil) + (default-directory + (if (and (boundp 'idlwave-shell-default-directory) + (stringp idlwave-shell-default-directory) + (file-directory-p idlwave-shell-default-directory)) + idlwave-shell-default-directory + default-directory))) + (comint-dynamic-complete-filename))) + (defun idlwave-make-full-name (class name) ;; Make a fully qualified module name including the class name (concat (if class (format "%s::" class) "") name)) @@ -4817,10 +5007,18 @@ When we force a method or a method keyword, CLASS can specify the class." (throw 'exit match)) (setq list (cdr (memq match list))))))) +(defun idlwave-rinfo-assq-any-class (name type class list) + (let* ((classes (cons class (idlwave-all-class-inherits class))) + class rtn) + (while classes + (if (setq rtn (idlwave-rinfo-assq name type (pop classes) list)) + (setq classes nil))) + rtn)) + (defun idlwave-best-rinfo-assq (name type class list) "Like `idlwave-rinfo-assq', but get all twins and sort, then return first." (let ((twins (idlwave-routine-twins - (idlwave-rinfo-assq name type class list) + (idlwave-rinfo-assq-any-class name type class list) list)) syslibp) (when (> (length twins) 1) @@ -4892,6 +5090,25 @@ When TYPE is not specified, both procedures and functions will be considered." (idlwave-all-assq method (idlwave-routines))) (idlwave-uniquify rtn)))) +(defun idlwave-members-only (list club) + "Return list of all elements in LIST which are also in CLUB." + (let (rtn) + (while list + (if (member (car list) club) + (setq rtn (cons (car list) rtn))) + (setq list (cdr list))) + (nreverse rtn))) + +(defun idlwave-nonmembers-only (list club) + "Return list of all elements in LIST which are not in CLUB." + (let (rtn) + (while list + (if (member (car list) club) + nil + (setq rtn (cons (car list) rtn))) + (setq list (cdr list))) + (nreverse rtn))) + (defun idlwave-determine-class (info type) ;; Determine the class of a routine call. INFO is the structure returned ;; `idlwave-what-function' or `idlwave-what-procedure'. @@ -4966,10 +5183,13 @@ When TYPE is not specified, both procedures and functions will be considered." (defvar type-selector) (defvar class-selector) (defvar method-selector) +(defvar super-classes) (defun idlwave-selector (a) (and (eq (nth 1 a) type-selector) (or (and (nth 2 a) (eq class-selector t)) - (eq (nth 2 a) class-selector)))) + (eq (nth 2 a) class-selector) + (memq (nth 2 a) super-classes) + ))) (defun idlwave-where () "Find out where we are. @@ -4991,7 +5211,6 @@ CLASS: What class has the routine (nil=no, t=is method, but class unknown) ARROW: Where is the arrow?" (idlwave-routines) (let* (;(bos (save-excursion (idlwave-beginning-of-statement) (point))) - ;; FIXME: WAS THIS CHANGE CORRECT??? Answer: yes. (bos (save-excursion (idlwave-start-of-substatement 'pre) (point))) (func-entry (idlwave-what-function bos)) (func (car func-entry)) @@ -5020,7 +5239,10 @@ ARROW: Where is the arrow?" ((string-match "\\`[ \t]*\\(pro\\|function\\)\\>" (buffer-substring bos (point))) nil) - ((string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z][a-zA-Z0-9$_]*\\)?\\'" + ((string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z0-9$_]*\\)?\\'" + (buffer-substring bos (point))) + (setq cw 'class)) + ((string-match "\\<inherits\\s-+\\([a-zA-Z0-9$_]*\\)?\\'" (buffer-substring bos (point))) (setq cw 'class)) ((and func @@ -5112,8 +5334,7 @@ ARROW: Where is the arrow?" (let ((pos (point)) pro-point pro class arrow-start string) (save-excursion - ;????(idlwave-beginning-of-statement) - ;; FIXME: WAS THIS CHANGE CORRECT: Answer: yes + ;;(idlwave-beginning-of-statement) (idlwave-start-of-substatement 'pre) (setq string (buffer-substring (point) pos)) (if (string-match @@ -5262,6 +5483,7 @@ Show PROMPT in echo area. TYPE is one of 'function, 'procedure or 'keyword." ;; We cannot add something - offer a list. (message "Making completion list...") (let* ((list all-completions) + ;; "complete" means, this is already a valid completion (complete (memq spart all-completions)) (completion-highlight-first-word-only t) ; XEmacs (completion-fixup-function ; Emacs @@ -5305,22 +5527,24 @@ Show PROMPT in echo area. TYPE is one of 'function, 'procedure or 'keyword." "Select a class" "class"))) (defun idlwave-attach-classes (list is-kwd show-classes) - ;; attach the proper class list to a LIST of completion items. + ;; Attach the proper class list to a LIST of completion items. ;; IS-KWD, when non-nil, shows its keywords - otherwise its methods ;; SHOW-CLASSES is the value of `idlwave-completion-show-classes'. (catch 'exit - (if (or (null show-classes) ; don't wnat to see classes - (null class-selector) ; not a method call - (stringp class-selector)) ; the class is already known + (if (or (null show-classes) ; don't want to see classes + (null class-selector) ; not a method call + (and (stringp class-selector) ; the class is already known + (not super-classes))) ; no possibilities for inheritance ;; In these cases, we do not have to do anything (throw 'exit list)) - ;; The property and dots stuff currently only make sense with XEmacs - ;; because Emacs drops text properties when filling the *Completions* - ;; buffer. - (let* ((do-prop (and (featurep 'xemacs) (>= show-classes 0))) + (let* ((do-prop (and (>= show-classes 0) + (>= emacs-major-version 21))) (do-buf (not (= show-classes 0))) - (do-dots (featurep 'xemacs)) + ; (do-dots (featurep 'xemacs)) + (do-dots t) + (inherit (if super-classes + (cons class-selector super-classes))) (max (abs show-classes)) (lmax (if do-dots (apply 'max (mapcar 'length list)))) classes nclasses class-info space) @@ -5332,6 +5556,11 @@ Show PROMPT in echo area. TYPE is one of 'function, 'procedure or 'keyword." (idlwave-all-method-keyword-classes method-selector x type-selector) (idlwave-all-method-classes x type-selector))) + (if inherit + (setq classes + (delq nil + (mapcar (lambda (x) (if (memq x inherit) x nil)) + classes)))) (setq nclasses (length classes)) ;; Make the separator between item and class-info (if do-dots @@ -5523,6 +5752,357 @@ Show PROMPT in echo area. TYPE is one of 'function, 'procedure or 'keyword." ;;---------------------------------------------------------------------- ;;---------------------------------------------------------------------- +;;; ------------------------------------------------------------------------ +;;; Sturucture parsing code, and code to manage class info + +;; +;; - Go again over the documentation how to write a completion +;; plugin. It is in self.el, but currently still very bad. +;; This could be in a separate file in the distribution, or +;; in an appendix for the manual. + +(defun idlwave-struct-tags () + "Return a list of all tags in the structure defined at point. +Point is expected just before the opening `{' of the struct definition." + (save-excursion + (let* ((borders (idlwave-struct-borders)) + (beg (car borders)) + (end (cdr borders)) + tags) + (goto-char beg) + (while (re-search-forward "[{,][ \t]*\\(\\$.*\n[ \t]*\\)?\\([a-zA-Z][a-zA-Z0-9_]*\\)[ \t]*:" end t) + ;; Check if we are still on the top level of the structure. + (if (and (condition-case nil (progn (up-list -1) t) (error nil)) + (= (point) beg)) + (push (match-string 2) tags)) + (goto-char (match-end 0))) + (nreverse tags)))) + +(defun idlwave-struct-inherits () + "Return a list of all `inherits' names in the struct at point. +Point is expected just before the opening `{' of the struct definition." + (save-excursion + (let* ((borders (idlwave-struct-borders)) + (beg (car borders)) + (end (cdr borders)) + (case-fold-search t) + names) + (goto-char beg) + (while (re-search-forward "[{,][ \t]*\\(\\$.*\n[ \t]*\\)?inherits[ \t]*\\(\\$.*\n[ \t]*\\)?\\([a-zA-Z][a-zA-Z0-9_]*\\)" end t) + ;; Check if we are still on the top level of the structure. + (if (and (condition-case nil (progn (up-list -1) t) (error nil)) + (= (point) beg)) + (push (match-string 3) names)) + (goto-char (match-end 0))) + (nreverse names)))) + + +(defun idlwave-struct-borders () + "Return the borders of the {...} after point as a cons cell." + (let (beg) + (save-excursion + (skip-chars-forward "^{") + (setq beg (point)) + (condition-case nil (forward-list 1) + (error (goto-char beg))) + (cons beg (point))))) + +(defun idlwave-find-structure-definition (&optional var name bound) + "Search forward for a structure definition. +If VAR is non-nil, search for a structure assigned to variable VAR. +If NAME is non-nil, search for a named structure NAME. +If BOUND is an integer, limit the search. +If BOUND is the symbol `all', we search first back and then forward +through the entire file." + (let* ((ws "[ \t]*\\(\\$.*\n[ \t]*\\)?") + (case-fold-search t) + (lim (if (integerp bound) bound nil)) + (re (concat + (if var + (concat "\\<" (regexp-quote (downcase var)) "\\>" ws) + "\\(\\)") + "=" ws "\\({\\)" + (if name (concat ws "\\<" (downcase name) "[^a-zA-Z0-9_$]") "")))) + (if (or (and (eq bound 'all) + (re-search-backward re nil t)) + (re-search-forward re lim t)) + (goto-char (match-beginning 3))))) + +(defvar idlwave-class-info nil) +(defvar idlwave-system-class-info nil) +(add-hook 'idlwave-update-rinfo-hook + (lambda () (setq idlwave-class-info nil))) +(add-hook 'idlwave-after-load-rinfo-hook + (lambda () (setq idlwave-class-info nil))) + +(defun idlwave-class-info (class) + (let (list entry) + (unless idlwave-class-info + ;; Info is nil, put in the system stuff. + (setq idlwave-class-info idlwave-system-class-info) + (setq list idlwave-class-info) + (while (setq entry (pop list)) + (idlwave-sintern-class-info entry))) + (setq class (idlwave-sintern-class class)) + (setq entry (assq class idlwave-class-info)) + (unless entry + (setq entry (idlwave-find-class-info class)) + (when entry + ;; Sintern and cache the info + (idlwave-sintern-class-info entry) + (push entry idlwave-class-info))) + entry)) + +(defun idlwave-sintern-class-info (entry) + "Sintern the class names in a class-info entry." + (let ((taglist (assq 'tags entry)) + (inherits (assq 'inherits entry))) + (setcar entry (idlwave-sintern-class (car entry) 'set)) + (if inherits + (setcdr inherits (mapcar (lambda (x) (idlwave-sintern-class x 'set)) + (cdr inherits)))))) + +(defun idlwave-find-class-info (class) + "Find the __define procedure for a class structure and return info entry." + (let* ((pro (concat (downcase class) "__define")) + (class (idlwave-sintern-class class)) + (idlwave-auto-routine-info-updates nil) + (file (cdr (nth 3 (idlwave-rinfo-assoc pro 'pro nil + (idlwave-routines))))) + buf) + (if (or (not file) + (not (file-regular-p + (setq file (idlwave-expand-lib-file-name file))))) + nil ; Cannot get info + (save-excursion + (if (setq buf (idlwave-get-buffer-visiting file)) + (set-buffer buf) + (set-buffer (get-buffer-create " *IDLWAVE-tmp*")) + (erase-buffer) + (unless (eq major-mode 'idlwave-mode) + (idlwave-mode)) + (insert-file-contents file)) + (save-excursion + (goto-char 1) + (setq case-fold-search t) + (when (and (re-search-forward + (concat "^[ \t]*pro[ \t]+" pro "\\>") nil t) + ;; FIXME: should we limit to end of pro here? + (idlwave-find-structure-definition nil class)) + (list class + (cons 'tags (idlwave-struct-tags)) + (cons 'inherits (idlwave-struct-inherits))))))))) + +(defun idlwave-class-tags (class) + "Return the native tags in CLASS." + (cdr (assq 'tags (idlwave-class-info class)))) +(defun idlwave-class-inherits (class) + "Return the direct superclasses of CLASS." + (cdr (assq 'inherits (idlwave-class-info class)))) + +(defun idlwave-all-class-tags (class) + "Return a list of native and inherited tags in CLASS." + (apply 'append (mapcar 'idlwave-class-tags + (cons class (idlwave-all-class-inherits class))))) + +(defun idlwave-all-class-inherits (class) + "Return a list of all superclasses of CLASS (recursively expanded). +The list is cashed in `idlwave-class-info' for faster access." + (cond + ((not idlwave-support-inheritance) nil) + ((eq class nil) nil) + ((eq class t) nil) + (t + (let ((info (idlwave-class-info class)) + entry) + (if (setq entry (assq 'all-inherits info)) + (cdr entry) + (let ((inherits (idlwave-class-inherits class)) + rtn all-inherits cl) + (while inherits + (setq cl (pop inherits) + rtn (cons cl rtn) + inherits (append inherits (idlwave-class-inherits cl)))) + (setq all-inherits (nreverse rtn)) + (nconc info (list (cons 'all-inherits all-inherits))) + all-inherits)))))) + + +;;========================================================================== +;; +;; Completing class structure tags. This is a completion plugin. +;; The necessary taglist is constructed dynamically + +(defvar idlwave-current-tags-class nil) +(defvar idlwave-current-class-tags nil) +(defvar idlwave-current-native-class-tags nil) +(defvar idlwave-sint-classtags nil) +(idlwave-new-sintern-type 'classtag) +(add-to-list 'idlwave-complete-special 'idlwave-complete-class-structure-tag) +(add-hook 'idlwave-update-rinfo-hook 'idlwave-classtag-reset) + +(defun idlwave-complete-class-structure-tag () + "Complete a structure tag on a `self' argument in an object method." + (interactive) + (let ((pos (point)) + (case-fold-search t)) + (if (save-excursion + ;; Check if the context is right + (skip-chars-backward "[a-zA-Z0-9._$]") + (and (< (point) (- pos 4)) + (looking-at "self\\."))) + (let* ((class (nth 2 (idlwave-current-routine)))) + ;; Check if we are in a class routine + (unless class + (error "Not in a method procedure or function.")) + ;; Check if we need to update the "current" class + (if (not (equal class idlwave-current-tags-class)) + (idlwave-prepare-class-tag-completion class)) + (setq idlwave-completion-help-info nil) + (let ((idlwave-cpl-bold idlwave-current-native-class-tags)) + (idlwave-complete-in-buffer + 'classtag 'classtag + idlwave-current-class-tags nil + (format "Select a tag of class %s" class) + "class tag")) + t) ; return t to skip other completions + nil))) + +(defun idlwave-classtag-reset () + (setq idlwave-current-tags-class nil)) + +(defun idlwave-prepare-class-tag-completion (class) + "Find and parse the necessary class definitions for class structure tags." + (setq idlwave-sint-classtags nil) + (setq idlwave-current-tags-class class) + (setq idlwave-current-class-tags + (mapcar (lambda (x) + (list (idlwave-sintern-classtag x 'set))) + (idlwave-all-class-tags class))) + (setq idlwave-current-native-class-tags + (mapcar 'downcase (idlwave-class-tags class)))) + +;=========================================================================== +;; +;; Completing system variables and their structure fields +;; This is also a plugin. It is a bit bigger since we support loading +;; current system variables from the shell and highlighting in the +;; completions buffer. + +(defvar idlwave-sint-sysvars nil) +(defvar idlwave-sint-sysvartags nil) +(idlwave-new-sintern-type 'sysvar) +(idlwave-new-sintern-type 'sysvartag) +(add-to-list 'idlwave-complete-special 'idlwave-complete-sysvar-or-tag) +(add-hook 'idlwave-update-rinfo-hook 'idlwave-sysvars-reset) +(add-hook 'idlwave-after-load-rinfo-hook 'idlwave-remember-builtin-sysvars) +(add-hook 'idlwave-after-load-rinfo-hook 'idlwave-sintern-sysvar-alist) + +(defvar idlwave-system-variables-alist nil + "Alist of system variables and the associated structure tags. +Gets set in `idlw-rinfo.el'.") +(defvar idlwave-builtin-system-variables nil) + +(defun idlwave-complete-sysvar-or-tag () + "Complete a system variable." + (interactive) + (let ((pos (point)) + (case-fold-search t)) + (cond ((save-excursion + ;; Check if the context is right for system variable + (skip-chars-backward "[a-zA-Z0-9_$]") + (equal (char-before) ?!)) + (setq idlwave-completion-help-info '(idlwave-complete-sysvar-help)) + (idlwave-complete-in-buffer 'sysvar 'sysvar + idlwave-system-variables-alist nil + "Select a system variable" + "system variable") + t) ; return t to skip other completions + ((save-excursion + ;; Check if the context is right for sysvar tag + (skip-chars-backward "[a-zA-Z0-9_$.]") + (and (equal (char-before) ?!) + (looking-at "\\([a-zA-Z][a-zA-Z0-9_$]*\\)\\.") + (<= (match-end 0) pos))) + ;; Complete a system variable tag + (let* ((var (idlwave-sintern-sysvar (match-string 1))) + (entry (assq var idlwave-system-variables-alist)) + (tags (cdr entry))) + (or entry (error "!%s is not know to be a system variable" var)) + (or tags (error "System variable !%s is not a structure" var)) + (setq idlwave-completion-help-info + (list 'idlwave-complete-sysvar-help var)) + (idlwave-complete-in-buffer 'sysvartag 'sysvartag + tags nil + "Select a system variable tag" + "system variable tag") + t)) ; return t to skip other completions + (t nil)))) + +(defvar name) +(defvar kwd) +(defun idlwave-complete-sysvar-help (mode word) + (cond + ((eq mode 'test) + (or (and (eq nil (nth 1 idlwave-completion-help-info)) + (member (downcase word) idlwave-builtin-system-variables)) + (and (stringp (nth 1 idlwave-completion-help-info)) + (member (downcase (nth 1 idlwave-completion-help-info)) + idlwave-builtin-system-variables)))) + ((eq mode 'set) + (setq name "system variables" + kwd (concat "!" + (if (stringp (nth 1 idlwave-completion-help-info)) + (nth 1 idlwave-completion-help-info) + word)))) + (t (error "This should not happen")))) + + +(defun idlwave-sysvars-reset () + (if (and (fboundp 'idlwave-shell-is-running) + (idlwave-shell-is-running)) + (idlwave-shell-send-command "idlwave_get_sysvars" + 'idlwave-process-sysvars 'hide))) + +(defun idlwave-process-sysvars () + (idlwave-shell-filter-sysvars) + (setq idlwave-sint-sysvars nil + idlwave-sint-sysvartags nil) + (idlwave-sintern-sysvar-alist)) + +(defun idlwave-remember-builtin-sysvars () + (setq idlwave-builtin-system-variables + (mapcar 'downcase + (mapcar 'car idlwave-system-variables-alist)))) + +(defun idlwave-sintern-sysvar-alist () + (let ((list idlwave-system-variables-alist) entry) + (while (setq entry (pop list)) + (setcar entry (idlwave-sintern-sysvar (car entry) 'set)) + (setcdr entry (mapcar (lambda (x) + (list (idlwave-sintern-sysvartag (car x) 'set))) + (cdr entry)))))) + +(defvar idlwave-shell-command-output) +(defun idlwave-shell-filter-sysvars () + "Get the system variables and structure tags." + (let ((text idlwave-shell-command-output) + (start 0) + (old idlwave-system-variables-alist) + var tags type name class) + (setq idlwave-system-variables-alist nil) + (while (string-match "^IDLWAVE-SYSVAR: !\\([a-zA-Z0-9_$]+\\)\\( \\(.*\\)\\)?" + text start) + (setq start (match-end 0) + var (match-string 1 text) + tags (if (match-end 3) (idlwave-split-string (match-string 3 text)))) + (setq idlwave-system-variables-alist + (cons (cons var (mapcar 'list tags)) + idlwave-system-variables-alist))) + ;; Keep the old value if query was not successful + (setq idlwave-system-variables-alist + (or idlwave-system-variables-alist old)))) + (defun idlwave-completion-fontify-classes () "Goto the *Completions* buffer and fontify the class info." (when (featurep 'font-lock) @@ -5680,7 +6260,8 @@ prefix arg, the class property is cleared out." (idlwave-force-class-query (equal arg '(4))) (module (idlwave-what-module))) (if (car module) - (apply 'idlwave-display-calling-sequence module) + (apply 'idlwave-display-calling-sequence + (idlwave-fix-module-if-obj_new module)) (error "Don't know which calling sequence to show."))))) (defun idlwave-resolve (&optional arg) @@ -5733,7 +6314,7 @@ use. With ARG force class query for object methods." (interactive "P") (let* ((idlwave-query-class nil) (idlwave-force-class-query (equal arg '(4))) - (module (idlwave-what-module)) + (module (idlwave-fix-module-if-obj_new (idlwave-what-module))) (default (concat (idlwave-make-full-name (nth 2 module) (car module)) (if (eq (nth 1 module) 'pro) "<p>" "<f>"))) (list @@ -5772,20 +6353,24 @@ use. With ARG force class query for object methods." (let ((name1 (idlwave-make-full-name class name)) source buf1 entry (buf (current-buffer)) - (pos (point))) + (pos (point)) + name2) (setq entry (idlwave-best-rinfo-assq name type class (idlwave-routines)) - source (or force-source (nth 3 entry))) + source (or force-source (nth 3 entry)) + name2 (if (nth 2 entry) + (idlwave-make-full-name (nth 2 entry) name) + name1)) (cond ((or (null name) (equal name "")) (error "Abort")) ((null entry) - (error "Nothing known about a module %s" name1)) + (error "Nothing known about a module %s" name2)) ((eq (car source) 'system) (error "Source code for system routine %s is not available." - name1)) + name2)) ((equal (cdr source) "") (error "Source code for routine %s is not available." - name1)) + name2)) ((memq (car source) '(buffer lib compiled)) (setq buf1 (if (eq (car source) 'lib) @@ -5803,13 +6388,13 @@ use. With ARG force class query for object methods." ((equal type "p") "pro") (t "\\(pro\\|function\\)")) "\\>[ \t]+" - (regexp-quote (downcase name1)) + (regexp-quote (downcase name2)) "[^a-zA-Z0-9_$]") nil t) (goto-char (match-beginning 0)) (pop-to-buffer buf) (goto-char pos) - (error "Could not find routine %s" name1))))))) + (error "Could not find routine %s" name2))))))) (defun idlwave-what-module () "Return a default module for stuff near point. @@ -5866,6 +6451,47 @@ Used by `idlwave-routine-info' and `idlwave-find-module'." (list pro 'pro (idlwave-determine-class (nth 0 where) 'pro))) (t nil))))) +(defun idlwave-what-module-find-class () + "Call idlwave-what-module and find the inherited class if necessary." + (let* ((module (idlwave-what-module)) + (class (nth 2 module)) + classes) + (if (and (= (length module) 3) + (stringp class)) + (list (car module) + (nth 1 module) + (apply 'idlwave-find-inherited-class module)) + module))) + +(defun idlwave-find-inherited-class (name type class) + "Find the class which defines TYPE NAME and is CLASS or inherited by CLASS." + (let ((entry (idlwave-best-rinfo-assoc name type class (idlwave-routines)))) + (if entry + (nth 2 entry) + class))) + +(defun idlwave-fix-module-if-obj_new (module) + "Check if MODULE points to obj_new. If yes, and if the cursor is in the +keyword region, change to the appropriate Init method." + (let* ((name (car module)) + (pos (point)) + (case-fold-search t) + string) + (if (and (stringp name) + (equal (downcase name) "obj_new") + (save-excursion + (idlwave-beginning-of-statement) + (setq string (buffer-substring (point) pos)) + (string-match "obj_new([^'\"]*['\"]\\([a-zA-Z0-9_]+\\)" + string))) + (let ((name "Init") + (class (match-string 1 string))) + (setq module (list (idlwave-sintern-method "Init") + 'fun + (idlwave-sintern-class class))))) + module)) + + (defun idlwave-fix-keywords (name type class keywords) ;; This fixes the list of keywords. (let ((case-fold-search t) @@ -5874,7 +6500,8 @@ Used by `idlwave-routine-info' and `idlwave-find-module'." ;; If this is the OBJ_NEW function, try to figure out the class and use ;; the keywords from the corresponding INIT method. (if (and (equal name "OBJ_NEW") - (eq major-mode 'idlwave-mode)) + (or (eq major-mode 'idlwave-mode) + (eq major-mode 'idlwave-shell-mode))) (let* ((bos (save-excursion (idlwave-beginning-of-statement) (point))) (string (buffer-substring bos (point))) (case-fold-search t) @@ -5902,6 +6529,25 @@ Used by `idlwave-routine-info' and `idlwave-find-module'." (mapcar (lambda (k) (add-to-list 'keywords k)) (nth 5 x)))) (setq keywords (idlwave-uniquify keywords))) + + ;; If we have inheritance, add all keywords from superclasses + ;; :-( Taken out because JD says it does not work this way. +; (when (and (stringp class) +; (or (assq (idlwave-sintern-keyword "_extra") keywords) +; (assq (idlwave-sintern-keyword "_ref_extra") keywords)) +; (boundp 'super-classes)) +; (loop for x in (idlwave-routines) do +; (and (nth 2 x) ; non-nil class +; (or (eq (nth 2 x) class) ; the right class +; (memq (nth 2 x) super-classes)) ; an inherited class +; (or (and (eq (nth 1 x) type) ; default type +; (eq (car x) name)) ; default name +; (and (eq (nth 1 x) type1) ; backup type +; (eq (car x) name1))) ; backup name +; (mapcar (lambda (k) (add-to-list 'keywords k)) +; (nth 5 x)))) +; (setq keywords (idlwave-uniquify keywords))) + ;; Return the final list keywords)) @@ -5971,14 +6617,17 @@ If we do not know about MODULE, just return KEYWORD literally." (when (window-live-p ri-window) (delete-window ri-window)))) -(defun idlwave-display-calling-sequence (name type class) +(defun idlwave-display-calling-sequence (name type class + &optional initial-class) ;; Display the calling sequence of module NAME, type TYPE in class CLASS. - (let* ((entry (or (idlwave-best-rinfo-assq name type class + (let* ((initial-class (or initial-class class)) + (entry (or (idlwave-best-rinfo-assq name type class (idlwave-routines)) (idlwave-rinfo-assq name type class idlwave-unresolved-routines))) (name (or (car entry) name)) (class (or (nth 2 entry) class)) + (superclasses (idlwave-all-class-inherits initial-class)) (twins (idlwave-routine-twins entry)) (dtwins (idlwave-study-twins twins)) (all dtwins) @@ -6003,15 +6652,18 @@ If we do not know about MODULE, just return KEYWORD literally." (if (idlwave-help-directory) "Button2: Pop to source and back. Button3: Source in Help window." "Button2: Pop to source and back.")) + (help-echo-class + "Button2: Display info about same method in superclass") (col 0) - (data (list name type class (current-buffer) olh)) + (data (list name type class (current-buffer) olh initial-class)) (km-prop (if (featurep 'xemacs) 'keymap 'local-map)) (face 'idlwave-help-link-face) beg props win cnt total) (setq keywords (idlwave-fix-keywords name type class keywords)) (cond ((null entry) - (error "No %s %s known" type name)) + (error "No %s %s known %s" type name + (if initial-class (concat "in class " initial-class) ""))) ((or (null name) (equal name "")) (error "No function or procedure call at point.")) ((null calling-seq) @@ -6026,6 +6678,22 @@ If we do not know about MODULE, just return KEYWORD literally." (set (make-local-variable 'idlwave-popup-source) nil) (set (make-local-variable 'idlwave-current-obj_new-class) idlwave-current-obj_new-class) + (when superclasses + (setq props (list 'mouse-face 'highlight + km-prop idlwave-rinfo-mouse-map + 'help-echo help-echo-class + 'data (cons 'class data))) + (let ((classes (cons initial-class superclasses)) c) + (insert "Classes: ") + (while (setq c (pop classes)) + (insert " ") + (setq beg (point)) + (insert c) + (if (equal (downcase c) (downcase class)) + (add-text-properties beg (point) (list 'face 'bold)) + (if (idlwave-rinfo-assq name type c (idlwave-routines)) + (add-text-properties beg (point) props)))) + (insert "\n"))) (setq props (if have-olh (list 'mouse-face 'highlight km-prop idlwave-rinfo-mouse-map @@ -6188,15 +6856,23 @@ Optional args RIGHT and SHIFT indicate, if mouse-3 was used, and if SHIFT was pressed." (interactive "e") (if ev (mouse-set-point ev)) - (let (data id name type class buf keyword bufwin source) + (let (data id name type class buf keyword bufwin source word initial-class) (setq data (get-text-property (point) 'data) source (get-text-property (point) 'source) keyword (get-text-property (point) 'keyword) id (car data) name (nth 1 data) type (nth 2 data) class (nth 3 data) buf (nth 4 data) + initial-class (nth 6 data) + word (idlwave-this-word) bufwin (get-buffer-window buf t)) - (cond ((eq id 'usage) + (cond ((eq id 'class) + (if (window-live-p bufwin) (select-window bufwin)) + (idlwave-display-calling-sequence + (idlwave-sintern-method name) + type (idlwave-sintern-class word) + initial-class)) + ((eq id 'usage) (idlwave-require-online-help) (idlwave-online-help nil name type class)) ((eq id 'source) @@ -6930,3 +7606,4 @@ This function was written since `list-abbrevs' looks terrible for IDLWAVE mode." ;;; idlwave.el ends here + |