Commit | Line | Data |
---|---|---|
3938cb82 | 1 | ;;; idlw-complete-structtag.el --- Completion of structure tags. |
d7a0267c | 2 | |
e9bffc61 GM |
3 | ;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, |
4 | ;; 2010, 2011 Free Software Foundation, Inc. | |
3938cb82 | 5 | |
1af82535 | 6 | ;; Author: Carsten Dominik <dominik@astro.uva.nl> |
3938cb82 S |
7 | ;; Maintainer: J.D. Smith <jdsmith@as.arizona.edu> |
8 | ;; Version: 1.2 | |
9 | ;; Keywords: languages | |
aad4679e | 10 | ;; Package: idlwave |
3938cb82 S |
11 | |
12 | ;; This file is part of GNU Emacs. | |
13 | ||
b1fc2b50 | 14 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
3938cb82 | 15 | ;; it under the terms of the GNU General Public License as published by |
b1fc2b50 GM |
16 | ;; the Free Software Foundation, either version 3 of the License, or |
17 | ;; (at your option) any later version. | |
3938cb82 S |
18 | |
19 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | ;; GNU General Public License for more details. | |
23 | ||
24 | ;; You should have received a copy of the GNU General Public License | |
b1fc2b50 | 25 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
3938cb82 S |
26 | |
27 | ;;; Commentary: | |
28 | ||
29 | ;; Completion of structure tags can be done automatically in the | |
30 | ;; shell, since the list of tags can be determined dynamically through | |
31 | ;; interaction with IDL. | |
32 | ||
33 | ;; Completion of structure tags in a source buffer is highly ambiguous | |
34 | ;; since you never know what kind of structure a variable will hold at | |
35 | ;; runtime. To make this feature useful in source buffers, we need a | |
36 | ;; special assumption/convention. We will assume that the structure is | |
37 | ;; defined in the same buffer and directly assigned to the correct | |
38 | ;; variable. This is mainly useful for applications in which there is one | |
39 | ;; main structure which contains a large amount of information (and many | |
40 | ;; tags). For example, many widget applications define a "state" structure | |
41 | ;; that contains all important data about the application. The different | |
42 | ;; routines called by the event handler then use this structure. If you | |
43 | ;; use the same variable name for this structure throughout your | |
44 | ;; application (a good idea for many reasons), IDLWAVE can support | |
45 | ;; completion for its tags. | |
46 | ;; | |
47 | ;; This file is a completion plugin which implements this kind of | |
48 | ;; completion. It is also an example which shows how completion plugins | |
49 | ;; should be programmed. | |
50 | ;; | |
51 | ;; New versions of IDLWAVE, documentation, and more information available | |
52 | ;; from: | |
53 | ;; http://idlwave.org | |
54 | ;; | |
55 | ;; INSTALLATION | |
56 | ;; ============ | |
1af82535 | 57 | ;; Put this file on the emacs load path and load it with the following |
3938cb82 S |
58 | ;; line in your .emacs file: |
59 | ;; | |
1af82535 | 60 | ;; (add-hook 'idlwave-load-hook |
3938cb82 S |
61 | ;; (lambda () (require 'idlw-complete-structtag))) |
62 | ;; | |
63 | ;; DESCRIPTION | |
64 | ;; =========== | |
65 | ;; Suppose your IDL program contains something like | |
66 | ;; | |
67 | ;; myvar = state.a* | |
68 | ;; | |
69 | ;; where the star marks the cursor position. If you now press the | |
70 | ;; completion key M-TAB, IDLWAVE searches the current file for a | |
71 | ;; structure definition | |
72 | ;; | |
73 | ;; state = {tag1:val1, tag2:val2, ...} | |
74 | ;; | |
75 | ;; and offers the tags for completion. | |
76 | ;; | |
77 | ;; In the idlwave shell, idlwave sends a "print,tag_names()" for the | |
78 | ;; variable to idl and determines the current tag list dynamically. | |
79 | ;; | |
80 | ;; Notes | |
81 | ;; ----- | |
82 | ;; - The structure definition assignment "state = {...}" must use the | |
b4dc7d98 | 83 | ;; same variable name as the completion location "state.*". |
3938cb82 S |
84 | ;; - The structure definition must be in the same file. |
85 | ;; - The structure definition is searched backwards and then forward | |
86 | ;; from the current position, until a definition with tags is found. | |
87 | ;; - The file is parsed again for each new completion variable and location. | |
88 | ;; - You can force an update of the tag list with the usual command | |
89 | ;; to update routine info in IDLWAVE: C-c C-i | |
90 | ||
1af82535 | 91 | (require 'idlwave) |
3938cb82 | 92 | |
73e72da4 DN |
93 | (declare-function idlwave-shell-buffer "idlw-shell") |
94 | ||
3938cb82 S |
95 | ;; Some variables to identify the previously used structure |
96 | (defvar idlwave-current-tags-var nil) | |
97 | (defvar idlwave-current-tags-buffer nil) | |
98 | (defvar idlwave-current-tags-completion-pos nil) | |
99 | ||
100 | ;; The tag list used for completion will be stored in the following vars | |
101 | (defvar idlwave-current-struct-tags nil) | |
102 | (defvar idlwave-sint-structtags nil) | |
103 | ||
104 | ;; Create the sintern type for structure talks | |
51066128 | 105 | (declare-function idlwave-sintern-structtag "idlw-complete-structtag" t t) |
3938cb82 S |
106 | (idlwave-new-sintern-type 'structtag) |
107 | ||
108 | ;; Hook the plugin into idlwave | |
109 | (add-to-list 'idlwave-complete-special 'idlwave-complete-structure-tag) | |
110 | (add-hook 'idlwave-update-rinfo-hook 'idlwave-structtag-reset) | |
111 | ||
112 | ;;; The main code follows below | |
1af82535 | 113 | (defvar idlwave-completion-help-info) |
3938cb82 S |
114 | (defun idlwave-complete-structure-tag () |
115 | "Complete a structure tag. | |
116 | This works by looking in the current file for a structure assignment to a | |
117 | variable with the same name and takes the tags from there. Quite useful | |
118 | for big structures like the state variables of a widget application. | |
119 | ||
120 | In the idlwave shell, the current content of the variable is used to get | |
121 | an up-to-date completion list." | |
122 | (interactive) | |
123 | (let ((pos (point)) | |
124 | start | |
125 | (case-fold-search t)) | |
126 | (if (save-excursion | |
127 | ;; Check if the context is right. | |
128 | ;; In the shell, this could be extended to expressions like | |
129 | ;; x[i+4].name.g*. But it is complicated because we would have | |
130 | ;; to really parse this expression. For now, we allow only | |
131 | ;; substructures, like "aaa.bbb.ccc.ddd" | |
132 | (skip-chars-backward "[a-zA-Z0-9._$]") | |
133 | (setq start (point)) ;; remember the start of the completion pos. | |
134 | (and (< (point) pos) | |
135 | (not (equal (char-before) ?!)) ; no sysvars | |
136 | (looking-at "\\([a-zA-Z][.a-zA-Z0-9_]*\\)\\.") | |
137 | (>= pos (match-end 0)) | |
138 | (not (string= (downcase (match-string 1)) "self")))) | |
139 | (let* ((var (downcase (match-string 1)))) | |
140 | ;; Check if we need to update the "current" structure. Basically we | |
141 | ;; do it always, except for subsequent completions at the same | |
142 | ;; spot, to save a bit of time. Implementation: We require | |
143 | ;; an update if | |
144 | ;; - the variable is different or | |
145 | ;; - the buffer is different or | |
146 | ;; - we are completing at a different position | |
147 | (if (or (not (string= var (or idlwave-current-tags-var "@"))) | |
148 | (not (eq (current-buffer) idlwave-current-tags-buffer)) | |
149 | (not (equal start idlwave-current-tags-completion-pos))) | |
150 | (idlwave-prepare-structure-tag-completion var)) | |
151 | (setq idlwave-current-tags-completion-pos start) | |
1af82535 | 152 | (setq idlwave-completion-help-info |
3938cb82 | 153 | (list 'idlwave-complete-structure-tag-help)) |
1af82535 | 154 | (idlwave-complete-in-buffer 'structtag 'structtag |
3938cb82 S |
155 | idlwave-current-struct-tags nil |
156 | "Select a structure tag" "structure tag") | |
157 | t) ; we did the completion: return t to skip other completions | |
158 | nil))) ; return nil to allow looking for other ways to complete | |
159 | ||
160 | (defun idlwave-structtag-reset () | |
161 | "Force an update of the current structure tag list upon next use." | |
162 | (setq idlwave-current-tags-buffer nil)) | |
163 | ||
164 | (defvar idlwave-structtag-struct-location nil | |
165 | "The location of the structure definition, for help display.") | |
166 | ||
167 | (defun idlwave-prepare-structure-tag-completion (var) | |
168 | "Find and parse the tag list for structure tag completion." | |
169 | ;; This works differently in source buffers and in the shell | |
175069ef | 170 | (if (derived-mode-p 'idlwave-shell-mode) |
3938cb82 S |
171 | ;; OK, we are in the shell, do it dynamically |
172 | (progn | |
1af82535 | 173 | (message "preparing shell tags") |
3938cb82 S |
174 | ;; The following call puts the tags into `idlwave-current-struct-tags' |
175 | (idlwave-complete-structure-tag-query-shell var) | |
176 | ;; initialize | |
177 | (setq idlwave-sint-structtags nil | |
178 | idlwave-current-tags-buffer (current-buffer) | |
179 | idlwave-current-tags-var var | |
180 | idlwave-structtag-struct-location (point) | |
181 | idlwave-current-struct-tags | |
182 | (mapcar (lambda (x) | |
183 | (list (idlwave-sintern-structtag x 'set))) | |
184 | idlwave-current-struct-tags)) | |
185 | (if (not idlwave-current-struct-tags) | |
186 | (error "Cannot complete structure tags of variable %s" var))) | |
187 | ;; Not the shell, so probably a source buffer. | |
188 | (unless | |
189 | (catch 'exit | |
190 | (save-excursion | |
191 | (goto-char (point-max)) | |
192 | ;; Find possible definitions of the structure. | |
193 | (while (idlwave-find-structure-definition var nil 'all) | |
194 | (let ((tags (idlwave-struct-tags))) | |
1af82535 | 195 | (when tags |
3938cb82 S |
196 | ;; initialize |
197 | (setq idlwave-sint-structtags nil | |
198 | idlwave-current-tags-buffer (current-buffer) | |
199 | idlwave-current-tags-var var | |
200 | idlwave-structtag-struct-location (point) | |
201 | idlwave-current-struct-tags | |
202 | (mapcar (lambda (x) | |
203 | (list (idlwave-sintern-structtag x 'set))) | |
204 | tags)) | |
205 | (throw 'exit t)))))) | |
206 | (error "Cannot complete structure tags of variable %s" var)))) | |
207 | ||
208 | (defun idlwave-complete-structure-tag-query-shell (var) | |
209 | "Ask the shell for the tags of the structure in variable or expression VAR." | |
210 | (idlwave-shell-send-command | |
211 | (format "if size(%s,/TYPE) eq 8 then print,tag_names(%s)" var var) | |
212 | 'idlwave-complete-structure-tag-get-tags-from-help | |
213 | 'hide 'wait)) | |
214 | ||
215 | (defvar idlwave-shell-prompt-pattern) | |
216 | (defvar idlwave-shell-command-output) | |
217 | (defun idlwave-complete-structure-tag-get-tags-from-help () | |
218 | "Filter structure tag name output, result to `idlwave-current-struct-tags'." | |
219 | (setq idlwave-current-struct-tags | |
220 | (if (string-match (concat "tag_names(.*) *\n" | |
221 | "\\(\\(.*[\r\n]?\\)*\\)" | |
222 | "\\(" idlwave-shell-prompt-pattern "\\)") | |
223 | idlwave-shell-command-output) | |
224 | (split-string (match-string 1 idlwave-shell-command-output))))) | |
225 | ||
226 | ||
227 | ;; Fake help in the source buffer for structure tags. | |
e7c4fb1e GM |
228 | ;; idlw-help-kwd is a global-variable (from idlwave-do-mouse-completion-help). |
229 | (defvar idlw-help-kwd) | |
3938cb82 S |
230 | (defvar idlwave-help-do-struct-tag) |
231 | (defun idlwave-complete-structure-tag-help (mode word) | |
232 | (cond | |
233 | ((eq mode 'test) | |
234 | ;; fontify only in source buffers, not in the shell. | |
235 | (not (equal idlwave-current-tags-buffer | |
236 | (get-buffer (idlwave-shell-buffer))))) | |
237 | ((eq mode 'set) | |
e7c4fb1e | 238 | (setq idlw-help-kwd word |
3938cb82 S |
239 | idlwave-help-do-struct-tag idlwave-structtag-struct-location)) |
240 | (t (error "This should not happen")))) | |
241 | ||
242 | (provide 'idlw-complete-structtag) | |
243 | ||
244 | ;;; idlw-complete-structtag.el ends here |