Mercurial > emacs
comparison lisp/progmodes/idlwave-shell.el @ 26958:35105166b1c9
Shell mode for interaction with the idl program (idl = Interactive
Data Language)
author | Carsten Dominik <dominik@science.uva.nl> |
---|---|
date | Mon, 20 Dec 1999 11:12:17 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
26957:3abce8d1097b | 26958:35105166b1c9 |
---|---|
1 ;;; idlwave-shell.el --- Run IDL or WAVE as an inferior process of Emacs. | |
2 ;; Copyright (c) 1994-1996 Chris Chase | |
3 ;; Copyright (c) 1999 Carsten Dominik | |
4 ;; Copyright (c) 1999 Free Software Foundation | |
5 | |
6 ;; Author: Chris Chase <chase@att.com> | |
7 ;; Maintainer: Carsten Dominik <dominik@strw.leidenuniv.nl> | |
8 ;; Version: 3.11 | |
9 ;; Date: $Date: 1999/12/06 08:13:16 $ | |
10 ;; Keywords: processes | |
11 | |
12 ;; This file is part of GNU Emacs. | |
13 | |
14 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
15 ;; it under the terms of the GNU General Public License as published by | |
16 ;; the Free Software Foundation; either version 2, or (at your option) | |
17 ;; any later version. | |
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 | |
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
27 ;; Boston, MA 02111-1307, USA. | |
28 | |
29 ;;; Commentary: | |
30 | |
31 ;; This mode is for IDL version 4 or later. It should work on Emacs | |
32 ;; or XEmacs version 19 or later. | |
33 | |
34 ;; Runs IDL as an inferior process of Emacs, much like the emacs | |
35 ;; `shell' or `telnet' commands. Provides command history and | |
36 ;; searching. Provides debugging commands available in buffers | |
37 ;; visiting IDL procedure files, e.g., breakpoint setting, stepping, | |
38 ;; execution until a certain line, printing expressions under point, | |
39 ;; visual line pointer for current execution line, etc. | |
40 ;; | |
41 ;; Documentation should be available online with `M-x idlwave-info'. | |
42 | |
43 ;; INSTALLATION: | |
44 ;; ============= | |
45 ;; | |
46 ;; Follow the instructions in the INSTALL file of the distribution. | |
47 ;; In short, put this file on your load path and add the following | |
48 ;; lines to your .emacs file: | |
49 ;; | |
50 ;; (autoload 'idlwave-shell "idlwave-shell" "IDLWAVE Shell" t) | |
51 ;; | |
52 ;; | |
53 ;; SOURCE | |
54 ;; ====== | |
55 ;; | |
56 ;; The newest version of this file can be found on the maintainers | |
57 ;; web site. | |
58 ;; | |
59 ;; http://www.strw.leidenuniv.el/~dominik/Tools/idlwave | |
60 ;; | |
61 ;; DOCUMENTATION | |
62 ;; ============= | |
63 ;; | |
64 ;; IDLWAVE is documented online in info format. | |
65 ;; A printable version of the documentation is available from the | |
66 ;; maintainers webpage (see under SOURCE) | |
67 ;; | |
68 ;; | |
69 ;; KNOWN PROBLEMS | |
70 ;; ============== | |
71 ;; | |
72 ;; The idlwave-shell buffer seems to occasionally lose output from the IDL | |
73 ;; process. I have not been able to consistently observe this. I | |
74 ;; do not know if it is a problem with idlwave-shell, comint, or Emacs | |
75 ;; handling of subprocesses. | |
76 ;; | |
77 ;; I don't plan on implementing directory tracking by watching the IDL | |
78 ;; commands entered at the prompt, since too often an IDL procedure | |
79 ;; will change the current directory. If you want the the idl process | |
80 ;; buffer to match the IDL current working just execute `M-x | |
81 ;; idlwave-shell-resync-dirs' (bound to "\C-c\C-d\C-w" by default.) | |
82 ;; | |
83 ;; The stack motion commands `idlwave-shell-stack-up' and | |
84 ;; `idlwave-shell-stack-down' only display the calling frame but | |
85 ;; cannot show the values of variables in higher frames correctly. I | |
86 ;; just don't know how to get these values from IDL. Anyone knows the | |
87 ;; magic word to do this? | |
88 ;; Also, the stack pointer stays at the level where is was and is not | |
89 ;; reset correctly when you type executive commands in the shell buffer | |
90 ;; yourself. However, using the executive commands bound to key sequences | |
91 ;; does the reset correctly. As a workaround, just jump down when needed. | |
92 ;; | |
93 ;; Under XEmacs the Debug menu in the shell does not display the | |
94 ;; keybindings in the prefix map. There bindings are available anyway - so | |
95 ;; it is a bug in XEmacs. | |
96 ;; The Debug menu in source buffers does display the bindings correctly. | |
97 ;; | |
98 ;; | |
99 ;; CUSTOMIZATION VARIABLES | |
100 ;; ======================= | |
101 ;; | |
102 ;; IDLWAVE has customize support - so if you want to learn about | |
103 ;; the variables which control the behavior of the mode, use | |
104 ;; `M-x idlwave-customize'. | |
105 ;; | |
106 ;;-------------------------------------------------------------------------- | |
107 ;; | |
108 ;; | |
109 | |
110 ;;; Code: | |
111 | |
112 (require 'comint) | |
113 (require 'idlwave) | |
114 | |
115 (eval-when-compile (require 'cl)) | |
116 | |
117 (defvar idlwave-shell-have-new-custom nil) | |
118 (eval-and-compile | |
119 ;; Kludge to allow `defcustom' for Emacs 19. | |
120 (condition-case () (require 'custom) (error nil)) | |
121 (if (and (featurep 'custom) | |
122 (fboundp 'custom-declare-variable) | |
123 (fboundp 'defface)) | |
124 ;; We've got what we needed | |
125 (setq idlwave-shell-have-new-custom t) | |
126 ;; We have the old or no custom-library, hack around it! | |
127 (defmacro defgroup (&rest args) nil) | |
128 (defmacro defcustom (var value doc &rest args) | |
129 (` (defvar (, var) (, value) (, doc)))))) | |
130 | |
131 ;;; Customizations: idlwave-shell group | |
132 | |
133 (defgroup idlwave-shell-general-setup nil | |
134 "Indentation options for IDL/WAVE mode." | |
135 :prefix "idlwave" | |
136 :group 'idlwave) | |
137 | |
138 (defcustom idlwave-shell-prompt-pattern "^ ?IDL> " | |
139 "*Regexp to match IDL prompt at beginning of a line. | |
140 For example, \"^IDL> \" or \"^WAVE> \". | |
141 The \"^\" means beginning of line. | |
142 This variable is used to initialise `comint-prompt-regexp' in the | |
143 process buffer. | |
144 | |
145 This is a fine thing to set in your `.emacs' file." | |
146 :group 'idlwave-shell-general-setup | |
147 :type 'regexp) | |
148 | |
149 (defcustom idlwave-shell-process-name "idl" | |
150 "*Name to be associated with the IDL process. The buffer for the | |
151 process output is made by surrounding this name with `*'s." | |
152 :group 'idlwave-shell-general-setup | |
153 :type 'string) | |
154 | |
155 (defcustom idlwave-shell-automatic-start nil | |
156 "*If non-nil attempt invoke idlwave-shell if not already running. | |
157 This is checked when an attempt to send a command to an | |
158 IDL process is made." | |
159 :group 'idlwave-shell-general-setup | |
160 :type 'boolean) | |
161 | |
162 (defcustom idlwave-shell-initial-commands "!more=0" | |
163 "Initial commands, separated by newlines, to send to IDL. | |
164 This string is sent to the IDL process by `idlwave-shell-mode' which is | |
165 invoked by `idlwave-shell'." | |
166 :group 'idlwave-shell-initial-commands | |
167 :type 'string) | |
168 | |
169 (defcustom idlwave-shell-use-dedicated-frame nil | |
170 "*Non-nil means, IDLWAVE should use a special frame to display shell buffer." | |
171 :group 'idlwave-shell-general-setup | |
172 :type 'boolean) | |
173 | |
174 (defcustom idlwave-shell-frame-parameters | |
175 '((height . 30) (unsplittable . nil)) | |
176 "The frame parameters for a dedicated idlwave-shell frame. | |
177 See also `idlwave-shell-use-dedicated-frame'. | |
178 The default makes the frame splittable, so that completion works correctly." | |
179 :group 'idlwave-shell-general-setup | |
180 :type '(repeat | |
181 (cons symbol sexp))) | |
182 | |
183 (defcustom idlwave-shell-use-toolbar t | |
184 "Non-nil means, use the debugging toolbar in all IDL related buffers. | |
185 Available on XEmacs and on Emacs 21.x or later. | |
186 Needs to be set at load-time, so don't try to do this in the hook." | |
187 :group 'idlwave-shell-general-setup | |
188 :type 'boolean) | |
189 | |
190 (defcustom idlwave-shell-temp-pro-prefix "/tmp/idltemp" | |
191 "*The prefix for temporary IDL files used when compiling regions. | |
192 It should be an absolute pathname. | |
193 The full temporary file name is obtained by to using `make-temp-name' | |
194 so that the name will be unique among multiple Emacs processes." | |
195 :group 'idlwave-shell-general-setup | |
196 :type 'string) | |
197 | |
198 (defvar idlwave-shell-fix-inserted-breaks nil | |
199 "*OBSOLETE VARIABLE, is no longer used. | |
200 | |
201 The documentation of this variable used to be: | |
202 If non-nil then run `idlwave-shell-remove-breaks' to clean up IDL messages.") | |
203 | |
204 (defcustom idlwave-shell-prefix-key "\C-c\C-d" | |
205 "*The prefix key for the debugging map `idlwave-shell-mode-prefix-map'. | |
206 This variable must already be set when idlwave-shell.el is loaded. | |
207 Seting it in the mode-hook is too late." | |
208 :group 'idlwave-shell-general-setup | |
209 :type 'string) | |
210 | |
211 (defcustom idlwave-shell-activate-prefix-keybindings t | |
212 "Non-nil means, the debug commands will be bound to the prefix key. | |
213 The prefix key itself is given in the option `idlwave-shell-prefix-key'. | |
214 So by default setting a breakpoint will be on C-c C-d C-b." | |
215 :group 'idlwave-shell-general-setup | |
216 :type 'boolean) | |
217 | |
218 (defcustom idlwave-shell-activate-alt-keybindings nil | |
219 "Non-nil means, the debug commands will be bound to alternate keys. | |
220 So for example setting a breakpoint will be on A-b." | |
221 :group 'idlwave-shell-general-setup | |
222 :type 'boolean) | |
223 | |
224 (defcustom idlwave-shell-use-truename nil | |
225 "*Non-nil means, use use `file-truename' when looking for buffers. | |
226 If this variable is non-nil, Emacs will use the function `file-truename' to | |
227 resolve symbolic links in the file paths printed by e.g., STOP commands. | |
228 This means, unvisited files will be loaded under their truename. | |
229 However, when a file is already visited under a deffernet name, IDLWAVE will | |
230 reuse that buffer. | |
231 This option was once introduced in order to avoid multiple buffers visiting | |
232 the same file. However, IDLWAVE no longer makes this mistake, so it is safe | |
233 to set this option to nil." | |
234 :group 'idlwave-shell-general-setup | |
235 :type 'boolean) | |
236 | |
237 (defcustom idlwave-shell-file-name-chars "~/A-Za-z0-9+@:_.$#%={}-" | |
238 "The characters allowed in file names, as a string. | |
239 Used for file name completion. Must not contain `'', `,' and `\"' | |
240 because these are used as separators by IDL." | |
241 :group 'idlwave-shell-general-setup | |
242 :type 'string) | |
243 | |
244 (defcustom idlwave-shell-mode-hook '() | |
245 "*Hook for customising `idlwave-shell-mode'." | |
246 :group 'idlwave-shell-general-setup | |
247 :type 'hook) | |
248 | |
249 ;;; Breakpoint Overlays etc | |
250 | |
251 (defgroup idlwave-shell-highlighting-and-faces nil | |
252 "Indentation options for IDL/WAVE mode." | |
253 :prefix "idlwave" | |
254 :group 'idlwave) | |
255 | |
256 (defcustom idlwave-shell-mark-stop-line t | |
257 "*Non-nil means, mark the source code line where IDL is currently stopped. | |
258 Value decides about the method which is used to mark the line. Legal values | |
259 are: | |
260 | |
261 nil Do not mark the line | |
262 'arrow Use the overlay arrow | |
263 'face Use `idlwave-shell-stop-line-face' to highlight the line. | |
264 t Use what IDLWAVE things is best. Will be a face where possible, | |
265 otherwise the overlay arrow. | |
266 The overlay-arrow has the disadvantage to hide the first chars of a line. | |
267 Since many people do not have the main block of IDL programs indented, | |
268 a face highlighting may be better. | |
269 On Emacs 21, the overlay arrow is displayed in a special area and never | |
270 hides any code, so setting this to 'arrow on Emacs 21 sounds like a good idea." | |
271 :group 'idlwave-shell-highlighting-and-faces | |
272 :type '(choice | |
273 (const :tag "No marking" nil) | |
274 (const :tag "Use overlay arrow" arrow) | |
275 (const :tag "Highlight with face" face) | |
276 (const :tag "Face or arrow." t))) | |
277 | |
278 (defcustom idlwave-shell-overlay-arrow ">" | |
279 "*The overlay arrow to display at source lines where execution halts. | |
280 We use a single character by default, since the main block of IDL procedures | |
281 often has no indentation. Where possible, IDLWAVE will use overlays to | |
282 display the stop-lines. The arrow is only used on character-based terminals. | |
283 See also `idlwave-shell-use-overlay-arrow'." | |
284 :group 'idlwave-shell-highlighting-and-faces | |
285 :type 'string) | |
286 | |
287 (defcustom idlwave-shell-stop-line-face 'highlight | |
288 "*The face for `idlwave-shell-stop-line-overlay'. | |
289 Allows you to choose the font, color and other properties for | |
290 line where IDL is stopped. See also `idlwave-shell-mark-stop-line'." | |
291 :group 'idlwave-shell-highlighting-and-faces | |
292 :type 'symbol) | |
293 | |
294 (defcustom idlwave-shell-expression-face 'secondary-selection | |
295 "*The face for `idlwave-shell-expression-overlay'. | |
296 Allows you to choose the font, color and other properties for | |
297 the expression printed by IDL." | |
298 :group 'idlwave-shell-highlighting-and-faces | |
299 :type 'symbol) | |
300 | |
301 (defcustom idlwave-shell-mark-breakpoints t | |
302 "*Non-nil means, mark breakpoints in the source files. | |
303 Legal values are: | |
304 nil Do not mark breakpoints. | |
305 'face Highlight line with `idlwave-shell-breakpoint-face'. | |
306 'glyph Red dot at the beginning of line. If the display does not | |
307 support glyphs, will use 'face instead. | |
308 t Glyph when possible, otherwise face (same effect as 'glyph)." | |
309 :group 'idlwave-shell-highlighting-and-faces | |
310 :type '(choice | |
311 (const :tag "No marking" nil) | |
312 (const :tag "Highlight with face" face) | |
313 (const :tag "Display glyph (red dot)" glyph) | |
314 (const :tag "Glyph or face." t))) | |
315 | |
316 (defvar idlwave-shell-use-breakpoint-glyph t | |
317 "Obsolete variable. See `idlwave-shell-mark-breakpoints.") | |
318 | |
319 (defcustom idlwave-shell-breakpoint-face 'idlwave-shell-bp-face | |
320 "*The face for breakpoint lines in the source code. | |
321 Allows you to choose the font, color and other properties for | |
322 lines which have a breakpoint. See also `idlwave-shell-mark-breakpoints'." | |
323 :group 'idlwave-shell-highlighting-and-faces | |
324 :type 'symbol) | |
325 | |
326 (if idlwave-shell-have-new-custom | |
327 ;; We have the new customize - use it to define a customizable face | |
328 (defface idlwave-shell-bp-face | |
329 '((((class color)) (:foreground "Black" :background "Pink")) | |
330 (t (:underline t))) | |
331 "Face for highlighting lines-with-breakpoints." | |
332 :group 'idlwave-shell-highlighting-and-faces) | |
333 ;; Just copy the underline face to be on the safe side. | |
334 (copy-face 'underline 'idlwave-shell-bp-face)) | |
335 | |
336 ;;; End user customization variables | |
337 | |
338 ;;; External variables | |
339 (defvar comint-last-input-start) | |
340 (defvar comint-last-input-end) | |
341 | |
342 ;; Other variables | |
343 | |
344 (defvar idlwave-shell-temp-pro-file nil | |
345 "Absolute pathname for temporary IDL file for compiling regions") | |
346 | |
347 (defvar idlwave-shell-dirstack-query "printd" | |
348 "Command used by `idlwave-shell-resync-dirs' to query IDL for | |
349 the directory stack.") | |
350 | |
351 (defvar idlwave-shell-default-directory nil | |
352 "The default directory in the idlwave-shell buffer, of outside use.") | |
353 | |
354 (defvar idlwave-shell-last-save-and-action-file nil | |
355 "The last file which was compiled with `idlwave-shell-save-and-...'.") | |
356 | |
357 ;; Highlighting uses overlays. When necessary, require the emulation. | |
358 (if (not (fboundp 'make-overlay)) | |
359 (condition-case nil | |
360 (require 'overlay) | |
361 (error nil))) | |
362 | |
363 (defvar idlwave-shell-stop-line-overlay nil | |
364 "The overlay for where IDL is currently stopped.") | |
365 (defvar idlwave-shell-expression-overlay nil | |
366 "The overlay for where IDL is currently stopped.") | |
367 ;; If these were already overlays, delete them. This probably means that we | |
368 ;; are reloading this file. | |
369 (if (overlayp idlwave-shell-stop-line-overlay) | |
370 (delete-overlay idlwave-shell-stop-line-overlay)) | |
371 (if (overlayp idlwave-shell-expression-overlay) | |
372 (delete-overlay idlwave-shell-expression-overlay)) | |
373 ;; Set to nil initially | |
374 (setq idlwave-shell-stop-line-overlay nil | |
375 idlwave-shell-expression-overlay nil) | |
376 | |
377 ;; Define the shell stop overlay. When left nil, the arrow will be used. | |
378 (cond | |
379 ((or (null idlwave-shell-mark-stop-line) | |
380 (eq idlwave-shell-mark-stop-line 'arrow)) | |
381 ;; Leave the overlay nil | |
382 nil) | |
383 | |
384 ((eq idlwave-shell-mark-stop-line 'face) | |
385 ;; Try to use a face. If not possible, arrow will be used anyway | |
386 ;; So who can display faces? | |
387 (when (or (featurep 'xemacs) ; XEmacs can do also ttys | |
388 (fboundp 'tty-defined-colors) ; Emacs 21 as well | |
389 window-system) ; Window systems always | |
390 (progn | |
391 (setq idlwave-shell-stop-line-overlay (make-overlay 1 1)) | |
392 (overlay-put idlwave-shell-stop-line-overlay | |
393 'face idlwave-shell-stop-line-face)))) | |
394 | |
395 (t | |
396 ;; IDLWAVE may decide. Will use a face on window systems, arrow elsewhere | |
397 (if window-system | |
398 (progn | |
399 (setq idlwave-shell-stop-line-overlay (make-overlay 1 1)) | |
400 (overlay-put idlwave-shell-stop-line-overlay | |
401 'face idlwave-shell-stop-line-face))))) | |
402 | |
403 ;; Now the expression overlay | |
404 (setq idlwave-shell-expression-overlay (make-overlay 1 1)) | |
405 (overlay-put idlwave-shell-expression-overlay | |
406 'face idlwave-shell-expression-face) | |
407 | |
408 (defvar idlwave-shell-bp-query "help,/breakpoints" | |
409 "Command to obtain list of breakpoints") | |
410 | |
411 (defvar idlwave-shell-command-output nil | |
412 "String for accumulating current command output.") | |
413 | |
414 (defvar idlwave-shell-post-command-hook nil | |
415 "Lisp list expression or function to run when an IDL command is finished. | |
416 The current command is finished when the IDL prompt is displayed. | |
417 This is evaluated if it is a list or called with funcall.") | |
418 | |
419 (defvar idlwave-shell-hide-output nil | |
420 "If non-nil the process output is not inserted into the output | |
421 buffer.") | |
422 | |
423 (defvar idlwave-shell-accumulation nil | |
424 "Accumulate last line of output.") | |
425 | |
426 (defvar idlwave-shell-command-line-to-execute nil) | |
427 (defvar idlwave-shell-cleanup-hook nil | |
428 "List of functions to do cleanup when the shell exits.") | |
429 | |
430 (defvar idlwave-shell-pending-commands nil | |
431 "List of commands to be sent to IDL. | |
432 Each element of the list is list of \(CMD PCMD HIDE\), where CMD is a | |
433 string to be sent to IDL and PCMD is a post-command to be placed on | |
434 `idlwave-shell-post-command-hook'. If HIDE is non-nil, hide the output | |
435 from command CMD. PCMD and HIDE are optional.") | |
436 | |
437 (defun idlwave-shell-buffer () | |
438 "Name of buffer associated with IDL process. | |
439 The name of the buffer is made by surrounding `idlwave-shell-process-name | |
440 with `*'s." | |
441 (concat "*" idlwave-shell-process-name "*")) | |
442 | |
443 (defvar idlwave-shell-ready nil | |
444 "If non-nil can send next command to IDL process.") | |
445 | |
446 ;;; The following are the types of messages we attempt to catch to | |
447 ;;; resync our idea of where IDL execution currently is. | |
448 ;;; | |
449 | |
450 (defvar idlwave-shell-halt-frame nil | |
451 "The frame associated with halt/breakpoint messages.") | |
452 | |
453 (defvar idlwave-shell-step-frame nil | |
454 "The frame associated with step messages.") | |
455 | |
456 (defvar idlwave-shell-trace-frame nil | |
457 "The frame associated with trace messages.") | |
458 | |
459 (defconst idlwave-shell-halt-messages | |
460 '("^% Execution halted at" | |
461 "^% Interrupted at:" | |
462 "^% Stepped to:" | |
463 "^% At " | |
464 "^% Stop encountered:" | |
465 ) | |
466 "*A list of regular expressions matching IDL messages. | |
467 These are the messages containing file and line information where | |
468 IDL is currently stopped.") | |
469 | |
470 (defconst idlwave-shell-halt-messages-re | |
471 (mapconcat 'identity idlwave-shell-halt-messages "\\|") | |
472 "The regular expression computed from idlwave-shell-halt-messages") | |
473 | |
474 (defconst idlwave-shell-trace-messages | |
475 '("^% At " ;; First line of a trace message | |
476 ) | |
477 "*A list of regular expressions matching IDL trace messages. | |
478 These are the messages containing file and line information where | |
479 IDL will begin looking for the next statement to execute.") | |
480 | |
481 (defconst idlwave-shell-step-messages | |
482 '("^% Stepped to:" | |
483 ) | |
484 "*A list of regular expressions matching stepped execution messages. | |
485 These are IDL messages containing file and line information where | |
486 IDL has currently stepped.") | |
487 | |
488 (defvar idlwave-shell-break-message "^% Breakpoint at:" | |
489 "*Regular expression matching an IDL breakpoint message line.") | |
490 | |
491 | |
492 (defvar idlwave-shell-bp-alist) | |
493 ;(defvar idlwave-shell-post-command-output) | |
494 (defvar idlwave-shell-sources-alist) | |
495 (defvar idlwave-shell-menu-def) | |
496 (defvar idlwave-shell-mode-menu) | |
497 (defvar idlwave-shell-initial-commands) | |
498 (defvar idlwave-shell-syntax-error) | |
499 (defvar idlwave-shell-other-error) | |
500 (defvar idlwave-shell-error-buffer) | |
501 (defvar idlwave-shell-error-last) | |
502 (defvar idlwave-shell-bp-buffer) | |
503 (defvar idlwave-shell-sources-query) | |
504 (defvar idlwave-shell-mode-map) | |
505 | |
506 (defun idlwave-shell-mode () | |
507 "Major mode for interacting with an inferior IDL process. | |
508 | |
509 1. Shell Interaction | |
510 ----------------- | |
511 RET after the end of the process' output sends the text from the | |
512 end of process to the end of the current line. RET before end of | |
513 process output copies the current line (except for the prompt) to the | |
514 end of the buffer. | |
515 | |
516 Command history, searching of previous commands, command line | |
517 editing are available via the comint-mode key bindings, by default | |
518 mostly on the key `C-c'. | |
519 | |
520 2. Completion | |
521 ---------- | |
522 | |
523 TAB and M-TAB do completion of IDL routines and keywords - similar | |
524 to M-TAB in `idlwave-mode'. In executive commands and strings, | |
525 it completes file names. | |
526 | |
527 3. Routine Info | |
528 ------------ | |
529 `\\[idlwave-routine-info]' displays information about an IDL routine near point, | |
530 just like in `idlwave-mode'. The module used is the one at point or | |
531 the one whose argument list is being edited. | |
532 To update IDLWAVE's knowledge about compiled or edited modules, use | |
533 \\[idlwave-update-routine-info]. | |
534 \\[idlwave-find-module] find the source of a module. | |
535 \\[idlwave-resolve] tells IDL to compile an unresolved module. | |
536 | |
537 4. Debugging | |
538 --------- | |
539 A complete set of commands for compiling and debugging IDL programs | |
540 is available from the menu. Also keybindings starting with a | |
541 `C-c C-d' prefix are available for most commands in the *idl* buffer | |
542 and also in source buffers. The best place to learn about the | |
543 keybindings is again the menu. | |
544 | |
545 On Emacs versions where this is possible, a debugging toolbar is | |
546 installed. | |
547 | |
548 When IDL is halted in the middle of a procedure, the corresponding | |
549 line of that procedure file is displayed with an overlay in another | |
550 window. Breakpoints are also highlighted in the source. | |
551 | |
552 \\[idlwave-shell-resync-dirs] queries IDL in order to change Emacs current directory | |
553 to correspond to the IDL process current directory. | |
554 | |
555 5. Hooks | |
556 ----- | |
557 Turning on `idlwave-shell-mode' runs `comint-mode-hook' and | |
558 `idlwave-shell-mode-hook' (in that order). | |
559 | |
560 6. Documentation and Customization | |
561 ------------------------------- | |
562 Info documentation for this package is available. Use \\[idlwave-info] | |
563 to display (complain to your sysadmin if that does not work). | |
564 For Postscript and HTML versions of the documentation, check IDLWAVE's | |
565 homepage at `http://www.strw.leidenuniv.nl/~dominik/Tools/idlwave'. | |
566 IDLWAVE has customize support - see the group `idlwave'. | |
567 | |
568 7. Keybindings | |
569 ----------- | |
570 \\{idlwave-shell-mode-map}" | |
571 | |
572 (interactive) | |
573 (setq comint-prompt-regexp idlwave-shell-prompt-pattern) | |
574 (setq comint-process-echoes t) | |
575 ;; Can not use history expansion because "!" is used for system variables. | |
576 (setq comint-input-autoexpand nil) | |
577 (setq comint-input-ring-size 64) | |
578 (make-local-variable 'comint-completion-addsuffix) | |
579 (set (make-local-variable 'completion-ignore-case) t) | |
580 (setq comint-completion-addsuffix '("/" . "")) | |
581 (setq comint-input-ignoredups t) | |
582 (setq major-mode 'idlwave-shell-mode) | |
583 (setq mode-name "IDL-Shell") | |
584 ;; (make-local-variable 'idlwave-shell-bp-alist) | |
585 (setq idlwave-shell-halt-frame nil | |
586 idlwave-shell-trace-frame nil | |
587 idlwave-shell-command-output nil | |
588 idlwave-shell-step-frame nil) | |
589 (idlwave-shell-display-line nil) | |
590 ;; Make sure comint-last-input-end does not go to beginning of | |
591 ;; buffer (in case there were other processes already in this buffer). | |
592 (set-marker comint-last-input-end (point)) | |
593 (setq idlwave-shell-ready nil) | |
594 (setq idlwave-shell-bp-alist nil) | |
595 (idlwave-shell-update-bp-overlays) ; Throw away old overlays | |
596 (setq idlwave-shell-sources-alist nil) | |
597 (setq idlwave-shell-default-directory default-directory) | |
598 ;; (make-local-variable 'idlwave-shell-temp-pro-file) | |
599 (setq idlwave-shell-hide-output nil | |
600 idlwave-shell-temp-pro-file | |
601 (concat (make-temp-name idlwave-shell-temp-pro-prefix) ".pro")) | |
602 (make-local-hook 'kill-buffer-hook) | |
603 (add-hook 'kill-buffer-hook 'idlwave-shell-kill-shell-buffer-confirm | |
604 nil 'local) | |
605 (use-local-map idlwave-shell-mode-map) | |
606 (easy-menu-add idlwave-shell-mode-menu idlwave-shell-mode-map) | |
607 (run-hooks 'idlwave-shell-mode-hook) | |
608 (idlwave-shell-send-command idlwave-shell-initial-commands nil 'hide) | |
609 ) | |
610 | |
611 (if (not (fboundp 'idl-shell)) | |
612 (fset 'idl-shell 'idlwave-shell)) | |
613 | |
614 (defvar idlwave-shell-idl-wframe nil | |
615 "Frame for displaying the idl shell window.") | |
616 (defvar idlwave-shell-display-wframe nil | |
617 "Frame for displaying the idl source files.") | |
618 | |
619 (defvar idlwave-shell-last-calling-stack nil | |
620 "Caches the last calling stack, so that we can compare.") | |
621 (defvar idlwave-shell-calling-stack-index 0) | |
622 | |
623 (defun idlwave-shell-source-frame () | |
624 "Return the frame to be used for source display." | |
625 (if idlwave-shell-use-dedicated-frame | |
626 ;; We want separate frames for source and shell | |
627 (if (frame-live-p idlwave-shell-display-wframe) | |
628 ;; The frame exists, so we use it. | |
629 idlwave-shell-display-wframe | |
630 ;; The frame does not exist. We use the current frame. | |
631 ;; However, if the current is the shell frame, we make a new frame. | |
632 (setq idlwave-shell-display-wframe | |
633 (if (eq (selected-frame) idlwave-shell-idl-wframe) | |
634 (make-frame) | |
635 (selected-frame)))))) | |
636 | |
637 (defun idlwave-shell-shell-frame () | |
638 "Return the frame to be used for the shell buffer." | |
639 (if idlwave-shell-use-dedicated-frame | |
640 ;; We want a dedicated frame | |
641 (if (frame-live-p idlwave-shell-idl-wframe) | |
642 ;; It does exist, so we use it. | |
643 idlwave-shell-idl-wframe | |
644 ;; It does not exist. Check if we have a source frame. | |
645 (if (not (frame-live-p idlwave-shell-display-wframe)) | |
646 ;; We do not have a source frame, so we use this one. | |
647 (setq idlwave-shell-display-wframe (selected-frame))) | |
648 ;; Return a new frame | |
649 (setq idlwave-shell-idl-wframe | |
650 (make-frame idlwave-shell-frame-parameters))))) | |
651 | |
652 ;;;###autoload | |
653 (defun idlwave-shell (&optional arg) | |
654 "Run an inferior IDL, with I/O through buffer `(idlwave-shell-buffer)'. | |
655 If buffer exists but shell process is not running, start new IDL. | |
656 If buffer exists and shell process is running, just switch to the buffer. | |
657 | |
658 When called with a prefix ARG, or when `idlwave-shell-use-dedicated-frame' | |
659 is non-nil, the shell buffer and the source buffers will be in | |
660 separate frames. | |
661 | |
662 The command to run comes from variable `idlwave-shell-explicit-file-name'. | |
663 | |
664 The buffer is put in `idlwave-shell-mode', providing commands for sending | |
665 input and controlling the IDL job. See help on `idlwave-shell-mode'. | |
666 See also the variable `idlwave-shell-prompt-pattern'. | |
667 | |
668 \(Type \\[describe-mode] in the shell buffer for a list of commands.)" | |
669 (interactive "P") | |
670 | |
671 ;; A non-nil arg means, we want a dedicated frame. This will last | |
672 ;; for the current editing session. | |
673 (if arg (setq idlwave-shell-use-dedicated-frame t)) | |
674 (if (equal arg '(16)) (setq idlwave-shell-use-dedicated-frame nil)) | |
675 | |
676 ;; Check if the process still exists. If not, create it. | |
677 (unless (comint-check-proc (idlwave-shell-buffer)) | |
678 (let* ((prg (or idlwave-shell-explicit-file-name "idl")) | |
679 (buf (apply 'make-comint | |
680 idlwave-shell-process-name prg nil | |
681 idlwave-shell-command-line-options)) | |
682 ;; FIXME: the next line can go? | |
683 ;(buf (make-comint idlwave-shell-process-name prg)) | |
684 (process (get-buffer-process buf))) | |
685 (set-process-filter process 'idlwave-shell-filter) | |
686 (set-process-sentinel process 'idlwave-shell-sentinel) | |
687 (set-buffer buf) | |
688 (idlwave-shell-mode))) | |
689 (let ((window (idlwave-display-buffer (idlwave-shell-buffer) nil | |
690 (idlwave-shell-shell-frame))) | |
691 (current-window (selected-window))) | |
692 (select-window window) | |
693 (goto-char (point-max)) | |
694 (select-window current-window) | |
695 (raise-frame (window-frame window)) | |
696 (if (eq (selected-frame) (window-frame window)) | |
697 (select-window window)) | |
698 )) | |
699 | |
700 (defun idlwave-shell-recenter-shell-window (&optional arg) | |
701 "Run `idlwave-shell', but make sure the current window stays selected." | |
702 (interactive "P") | |
703 (let ((window (selected-window))) | |
704 (idlwave-shell arg) | |
705 (select-window window))) | |
706 | |
707 (defun idlwave-shell-send-command (&optional cmd pcmd hide preempt) | |
708 "Send a command to IDL process. | |
709 | |
710 \(CMD PCMD HIDE\) are placed at the end of `idlwave-shell-pending-commands'. | |
711 If IDL is ready the first command, CMD, in | |
712 `idlwave-shell-pending-commands' is sent to the IDL process. If optional | |
713 second argument PCMD is non-nil it will be placed on | |
714 `idlwave-shell-post-command-hook' when CMD is executed. If the optional | |
715 third argument HIDE is non-nil, then hide output from CMD. | |
716 If optional fourth argument PREEMPT is non-nil CMD is put at front of | |
717 `idlwave-shell-pending-commands'. | |
718 | |
719 IDL is considered ready if the prompt is present | |
720 and if `idlwave-shell-ready' is non-nil." | |
721 | |
722 ;(setq hide nil) ; FIXME: turn this on for debugging only | |
723 (let (buf proc) | |
724 ;; Get or make the buffer and its process | |
725 (if (or (not (setq buf (get-buffer (idlwave-shell-buffer)))) | |
726 (not (setq proc (get-buffer-process buf)))) | |
727 (if (not idlwave-shell-automatic-start) | |
728 (error | |
729 (substitute-command-keys | |
730 "You need to first start an IDL shell with \\[idlwave-shell]")) | |
731 (idlwave-shell-recenter-shell-window) | |
732 (setq buf (get-buffer (idlwave-shell-buffer))) | |
733 (if (or (not (setq buf (get-buffer (idlwave-shell-buffer)))) | |
734 (not (setq proc (get-buffer-process buf)))) | |
735 ;; Still nothing | |
736 (error "Problem with autostarting IDL shell")))) | |
737 | |
738 (save-excursion | |
739 (set-buffer buf) | |
740 (goto-char (process-mark proc)) | |
741 ;; To make this easy, always push CMD onto pending commands | |
742 (if cmd | |
743 (setq idlwave-shell-pending-commands | |
744 (if preempt | |
745 ;; Put at front. | |
746 (append (list (list cmd pcmd hide)) | |
747 idlwave-shell-pending-commands) | |
748 ;; Put at end. | |
749 (append idlwave-shell-pending-commands | |
750 (list (list cmd pcmd hide)))))) | |
751 ;; Check if IDL ready | |
752 (if (and idlwave-shell-ready | |
753 ;; Check for IDL prompt | |
754 (save-excursion | |
755 (beginning-of-line) | |
756 (looking-at idlwave-shell-prompt-pattern))) | |
757 ;; IDL ready for command | |
758 (if idlwave-shell-pending-commands | |
759 ;; execute command | |
760 (let* ((lcmd (car idlwave-shell-pending-commands)) | |
761 (cmd (car lcmd)) | |
762 (pcmd (nth 1 lcmd)) | |
763 (hide (nth 2 lcmd))) | |
764 ;; If this is an executive command, reset the stack pointer | |
765 (if (eq (string-to-char cmd) ?.) | |
766 (setq idlwave-shell-calling-stack-index 0)) | |
767 ;; Set post-command | |
768 (setq idlwave-shell-post-command-hook pcmd) | |
769 ;; Output hiding | |
770 ;;; Debug code | |
771 ;;; (setq idlwave-shell-hide-output nil) | |
772 (setq idlwave-shell-hide-output hide) | |
773 ;; Pop command | |
774 (setq idlwave-shell-pending-commands | |
775 (cdr idlwave-shell-pending-commands)) | |
776 ;; Send command for execution | |
777 (set-marker comint-last-input-start (point)) | |
778 (set-marker comint-last-input-end (point)) | |
779 (comint-simple-send proc cmd) | |
780 (setq idlwave-shell-ready nil))))))) | |
781 | |
782 ;; There was a report that a newer version of comint.el changed the | |
783 ;; name of comint-filter to comint-output-filter. Unfortunately, we | |
784 ;; have yet to upgrade. | |
785 | |
786 (defun idlwave-shell-comint-filter (process string) nil) | |
787 (if (fboundp 'comint-output-filter) | |
788 (fset 'idlwave-shell-comint-filter (symbol-function 'comint-output-filter)) | |
789 (fset 'idlwave-shell-comint-filter (symbol-function 'comint-filter))) | |
790 | |
791 (defun idlwave-shell-is-running () | |
792 "Return t if the shell process is running." | |
793 (eq (process-status idlwave-shell-process-name) 'run)) | |
794 | |
795 (defun idlwave-shell-filter (proc string) | |
796 "Replace Carriage returns in output. Watch for prompt. | |
797 When the IDL prompt is received executes `idlwave-shell-post-command-hook' | |
798 and then calls `idlwave-shell-send-command' for any pending commands." | |
799 ;; We no longer do the cleanup here - this is done by the process sentinel | |
800 (when (eq (process-status idlwave-shell-process-name) 'run) | |
801 ;; OK, process is still running, so we can use it. | |
802 (let ((data (match-data))) | |
803 (unwind-protect | |
804 (progn | |
805 ;; May change the original match data. | |
806 (let (p) | |
807 (while (setq p (string-match "\C-M" string)) | |
808 (aset string p ? ))) | |
809 ;;; Test/Debug code | |
810 ;; (save-excursion (set-buffer (get-buffer-create "*test*")) | |
811 ;; (goto-char (point-max)) | |
812 ;; (insert "%%%" string)) | |
813 ;; | |
814 ;; Keep output | |
815 | |
816 ; Should not keep output because the concat is costly. If hidden put | |
817 ; the output in a hide-buffer. Then when the output is needed in post | |
818 ; processing can access either the hide buffer or the idlwave-shell | |
819 ; buffer. Then watching for the prompt is easier. Furthermore, if it | |
820 ; is hidden and there is no post command, could throw away output. | |
821 ; (setq idlwave-shell-command-output | |
822 ; (concat idlwave-shell-command-output string)) | |
823 ;; Insert the string. Do this before getting the | |
824 ;; state. | |
825 (if idlwave-shell-hide-output | |
826 (save-excursion | |
827 (set-buffer | |
828 (get-buffer-create "*idlwave-shell-hidden-output*")) | |
829 (goto-char (point-max)) | |
830 (insert string)) | |
831 (idlwave-shell-comint-filter proc string)) | |
832 ;; Watch for prompt - need to accumulate the current line | |
833 ;; since it may not be sent all at once. | |
834 (if (string-match "\n" string) | |
835 (setq idlwave-shell-accumulation | |
836 (substring string | |
837 (progn (string-match "\\(.*\n\\)*" string) | |
838 (match-end 0)))) | |
839 (setq idlwave-shell-accumulation | |
840 (concat idlwave-shell-accumulation string))) | |
841 ;; Check for prompt in current line | |
842 (if (setq idlwave-shell-ready | |
843 (string-match idlwave-shell-prompt-pattern | |
844 idlwave-shell-accumulation)) | |
845 (progn | |
846 (if idlwave-shell-hide-output | |
847 (save-excursion | |
848 (set-buffer "*idlwave-shell-hidden-output*") | |
849 (goto-char (point-min)) | |
850 (re-search-forward idlwave-shell-prompt-pattern nil t) | |
851 (setq idlwave-shell-command-output | |
852 (buffer-substring (point-min) (point))) | |
853 (delete-region (point-min) (point))) | |
854 (setq idlwave-shell-command-output | |
855 (save-excursion | |
856 (set-buffer | |
857 (process-buffer proc)) | |
858 (buffer-substring | |
859 (progn | |
860 (goto-char (process-mark proc)) | |
861 (beginning-of-line nil) | |
862 (point)) | |
863 comint-last-input-end)))) | |
864 ;;; Test/Debug code | |
865 ;; (save-excursion (set-buffer | |
866 ;; (get-buffer-create "*idlwave-shell-output*")) | |
867 ;; (goto-char (point-max)) | |
868 ;; (insert "%%%" string)) | |
869 ;; Scan for state and do post command - bracket them | |
870 ;; with idlwave-shell-ready=nil since they | |
871 ;; may call idlwave-shell-send-command. | |
872 (let ((idlwave-shell-ready nil)) | |
873 (idlwave-shell-scan-for-state) | |
874 ;; Unset idlwave-shell-ready to prevent sending | |
875 ;; commands to IDL while running hook. | |
876 (if (listp idlwave-shell-post-command-hook) | |
877 (eval idlwave-shell-post-command-hook) | |
878 (funcall idlwave-shell-post-command-hook)) | |
879 ;; Reset to default state for next command. | |
880 ;; Also we do not want to find this prompt again. | |
881 (setq idlwave-shell-accumulation nil | |
882 idlwave-shell-command-output nil | |
883 idlwave-shell-post-command-hook nil | |
884 idlwave-shell-hide-output nil)) | |
885 ;; Done with post command. Do pending command if | |
886 ;; any. | |
887 (idlwave-shell-send-command)))) | |
888 (store-match-data data))))) | |
889 | |
890 (defun idlwave-shell-sentinel (process event) | |
891 "The sentinel function for the IDLWAVE shell process." | |
892 (let* ((buf (idlwave-shell-buffer)) | |
893 (win (get-buffer-window buf))) | |
894 (when (get-buffer buf) | |
895 (save-excursion | |
896 (set-buffer (idlwave-shell-buffer)) | |
897 (goto-char (point-max)) | |
898 (insert (format "\n\n Process %s %s" process event)))) | |
899 (when (and (> (length (frame-list)) 1) | |
900 (frame-live-p idlwave-shell-idl-wframe)) | |
901 (delete-frame idlwave-shell-idl-wframe) | |
902 (setq idlwave-shell-idl-wframe nil | |
903 idlwave-shell-display-wframe nil)) | |
904 (when (window-live-p win) | |
905 (delete-window win)) | |
906 (idlwave-shell-cleanup))) | |
907 | |
908 (defun idlwave-shell-scan-for-state () | |
909 "Scan for state info. | |
910 Looks for messages in output from last IDL command indicating where | |
911 IDL has stopped. The types of messages we are interested in are | |
912 execution halted, stepped, breakpoint, interrupted at and trace | |
913 messages. We ignore error messages otherwise. | |
914 For breakpoint messages process any attached count or command | |
915 parameters. | |
916 Update the windows if a message is found." | |
917 (let (update) | |
918 (cond | |
919 ;; Make sure we have output | |
920 ((not idlwave-shell-command-output)) | |
921 | |
922 ;; Various types of HALT messages. | |
923 ((string-match idlwave-shell-halt-messages-re | |
924 idlwave-shell-command-output) | |
925 ;; Grab the file and line state info. | |
926 (setq idlwave-shell-halt-frame | |
927 (idlwave-shell-parse-line | |
928 (substring idlwave-shell-command-output (match-end 0))) | |
929 update t)) | |
930 | |
931 ;; Handle breakpoints separately | |
932 ((string-match idlwave-shell-break-message | |
933 idlwave-shell-command-output) | |
934 (setq idlwave-shell-halt-frame | |
935 (idlwave-shell-parse-line | |
936 (substring idlwave-shell-command-output (match-end 0))) | |
937 update t) | |
938 ;; We used to to counting hits on breakpoints | |
939 ;; this is no longer supported since IDL breakpoints | |
940 ;; have learned counting. | |
941 ;; Do breakpoint command processing | |
942 (let ((bp (assoc | |
943 (list | |
944 (nth 0 idlwave-shell-halt-frame) | |
945 (nth 1 idlwave-shell-halt-frame)) | |
946 idlwave-shell-bp-alist))) | |
947 (if bp | |
948 (let ((cmd (idlwave-shell-bp-get bp 'cmd))) | |
949 (if cmd | |
950 ;; Execute command | |
951 (if (listp cmd) | |
952 (eval cmd) | |
953 (funcall cmd)))) | |
954 ;; A breakpoint that we did not know about - perhaps it was | |
955 ;; set by the user or IDL isn't reporting breakpoints like | |
956 ;; we expect. Lets update our list. | |
957 (idlwave-shell-bp-query))))) | |
958 | |
959 ;; Handle compilation errors in addition to the above | |
960 (if (and idlwave-shell-command-output | |
961 (or (string-match | |
962 idlwave-shell-syntax-error idlwave-shell-command-output) | |
963 (string-match | |
964 idlwave-shell-other-error idlwave-shell-command-output))) | |
965 (progn | |
966 (save-excursion | |
967 (set-buffer | |
968 (get-buffer-create idlwave-shell-error-buffer)) | |
969 (erase-buffer) | |
970 (insert idlwave-shell-command-output) | |
971 (goto-char (point-min)) | |
972 (setq idlwave-shell-error-last (point))) | |
973 (idlwave-shell-goto-next-error))) | |
974 | |
975 ;; Do update | |
976 (when update | |
977 (idlwave-shell-display-line (idlwave-shell-pc-frame))))) | |
978 | |
979 | |
980 (defvar idlwave-shell-error-buffer | |
981 "*idlwave-shell-errors*" | |
982 "Buffer containing syntax errors from IDL compilations.") | |
983 | |
984 ;; FIXME: the following two variables do not currently allow line breaks | |
985 ;; in module and file names. I am not sure if it will be necessary to | |
986 ;; change this. Currently it seems to work the way it is. | |
987 (defvar idlwave-shell-syntax-error | |
988 "^% Syntax error.\\s-*\n\\s-*At:\\s-*\\(.*\\),\\s-*Line\\s-*\\(.*\\)" | |
989 "A regular expression to match an IDL syntax error. | |
990 The first \(..\) pair should match the file name. The second pair | |
991 should match the line number.") | |
992 | |
993 (defvar idlwave-shell-other-error | |
994 "^% .*\n\\s-*At:\\s-*\\(.*\\),\\s-*Line\\s-*\\(.*\\)" | |
995 "A regular expression to match any IDL error. | |
996 The first \(..\) pair should match the file name. The second pair | |
997 should match the line number.") | |
998 | |
999 (defvar idlwave-shell-file-line-message | |
1000 (concat | |
1001 "\\(" ; program name group (1) | |
1002 "\\<[a-zA-Z][a-zA-Z0-9_$:]*" ; start with a letter, followed by [..] | |
1003 "\\([ \t]*\n[ \t]*[a-zA-Z0-9_$:]+\\)*"; continuation lines program name (2) | |
1004 "\\)" ; end program name group (1) | |
1005 "[ \t\n]+" ; white space | |
1006 "\\(" ; line number group (3) | |
1007 "[0-9]+" ; the line number (the fix point) | |
1008 "\\([ \t]*\n[ \t]*[0-9]+\\)*" ; continuation lines number (4) | |
1009 "\\)" ; end line number group (3) | |
1010 "[ \t\n]+" ; white space | |
1011 "\\(" ; file name group (5) | |
1012 "[^ \t\n]+" ; file names can contain any non-white | |
1013 "\\([ \t]*\n[ \t]*[^ \t\n]+\\)*" ; continuation lines file name (6) | |
1014 "\\)" ; end line number group (5) | |
1015 ) | |
1016 "*A regular expression to parse out the file name and line number. | |
1017 The 1st group should match the subroutine name. | |
1018 The 3rd group is the line number. | |
1019 The 5th group is the file name. | |
1020 All parts may contain linebreaks surrounded by spaces. This is important | |
1021 in IDL5 which inserts random linebreaks in long module and file names.") | |
1022 | |
1023 (defun idlwave-shell-parse-line (string) | |
1024 "Parse IDL message for the subroutine, file name and line number. | |
1025 We need to work hard here to remove the stupid line breaks inserted by | |
1026 IDL5. These line breaks can be right in the middle of procedure | |
1027 or file names. | |
1028 It is very difficult to come up with a robust solution. This one seems | |
1029 to be pretty good though. | |
1030 | |
1031 Here is in what ways it improves over the previous solution: | |
1032 | |
1033 1. The procedure name can be split and will be restored. | |
1034 2. The number can be split. I have never seen this, but who knows. | |
1035 3. We do not require the `.pro' extension for files. | |
1036 | |
1037 This function can still break when the file name ends on a end line | |
1038 and the message line contains an additional line with garbage. Then | |
1039 the first part of that garbage will be added to the file name. | |
1040 However, the function checks the existence of the files with and | |
1041 without this last part - thus the function only breaks if file name | |
1042 plus garbage match an existing regular file. This is hopefully very | |
1043 unlikely." | |
1044 | |
1045 (let (number procedure file) | |
1046 (when (string-match idlwave-shell-file-line-message string) | |
1047 (setq procedure (match-string 1 string) | |
1048 number (match-string 3 string) | |
1049 file (match-string 5 string)) | |
1050 | |
1051 ;; Repair the strings | |
1052 (setq procedure (idlwave-shell-repair-string procedure)) | |
1053 (setq number (idlwave-shell-repair-string number)) | |
1054 (setq file (idlwave-shell-repair-file-name file)) | |
1055 | |
1056 ;; If we have a file, return the frame list | |
1057 (if file | |
1058 (list (idlwave-shell-file-name file) | |
1059 (string-to-int number) | |
1060 procedure) | |
1061 ;; No success finding a file | |
1062 nil)))) | |
1063 | |
1064 (defun idlwave-shell-repair-string (string) | |
1065 "Repair a string by taking out all linebreaks. This is destructive!" | |
1066 (while (string-match "[ \t]*\n[ \t]*" string) | |
1067 (setq string (replace-match "" t t string))) | |
1068 string) | |
1069 | |
1070 (defun idlwave-shell-repair-file-name (file) | |
1071 "Repair a file name string by taking out all linebreaks. | |
1072 The last line of STRING may be garbage - we check which one makes a valid | |
1073 file name." | |
1074 (let ((file1 "") (file2 "") (start 0)) | |
1075 ;; We scan no further than to the next "^%" line | |
1076 (if (string-match "^%" file) | |
1077 (setq file (substring file 0 (match-beginning 0)))) | |
1078 ;; Take out the line breaks | |
1079 (while (string-match "[ \t]*\n[ \t]*" file start) | |
1080 (setq file1 (concat file1 (substring file start (match-beginning 0))) | |
1081 start (match-end 0))) | |
1082 (setq file2 (concat file1 (substring file start))) | |
1083 (cond | |
1084 ((file-regular-p file2) file2) | |
1085 ((file-regular-p file1) file1) | |
1086 ;; If we cannot veryfy the existence of the file, we return the shorter | |
1087 ;; name. The idea behind this is that this may be a relative file name | |
1088 ;; and our idea about the current working directory may be wrong. | |
1089 ;; If it is a relative file name, it hopefully is short. | |
1090 ((not (string= "" file1)) file1) | |
1091 ((not (string= "" file2)) file2) | |
1092 (t nil)))) | |
1093 | |
1094 (defun idlwave-shell-cleanup () | |
1095 "Do necessary cleanup for a terminated IDL process." | |
1096 (setq idlwave-shell-step-frame nil | |
1097 idlwave-shell-halt-frame nil | |
1098 idlwave-shell-pending-commands nil | |
1099 idlwave-shell-command-line-to-execute nil | |
1100 idlwave-shell-bp-alist nil | |
1101 idlwave-shell-calling-stack-index 0) | |
1102 (idlwave-shell-display-line nil) | |
1103 (idlwave-shell-update-bp-overlays) ; kill old overlays | |
1104 (idlwave-shell-kill-buffer "*idlwave-shell-hidden-output*") | |
1105 (idlwave-shell-kill-buffer idlwave-shell-bp-buffer) | |
1106 (idlwave-shell-kill-buffer idlwave-shell-error-buffer) | |
1107 ;; (idlwave-shell-kill-buffer (idlwave-shell-buffer)) | |
1108 (and (get-buffer (idlwave-shell-buffer)) | |
1109 (bury-buffer (get-buffer (idlwave-shell-buffer)))) | |
1110 (run-hooks 'idlwave-shell-cleanup-hook)) | |
1111 | |
1112 (defun idlwave-shell-kill-buffer (buf) | |
1113 "Kill buffer BUF if it exists." | |
1114 (if (setq buf (get-buffer buf)) | |
1115 (kill-buffer buf))) | |
1116 | |
1117 (defun idlwave-shell-kill-shell-buffer-confirm () | |
1118 (when (idlwave-shell-is-running) | |
1119 (ding) | |
1120 (unless (y-or-n-p "IDL shell is running. Are you sure you want to kill the buffer? ") | |
1121 (error "Abort")) | |
1122 (message "Killing buffer *idl* and the associated process"))) | |
1123 | |
1124 (defun idlwave-shell-resync-dirs () | |
1125 "Resync the buffer's idea of the current directory stack. | |
1126 This command queries IDL with the command bound to | |
1127 `idlwave-shell-dirstack-query' (default \"printd\"), reads the | |
1128 output for the new directory stack." | |
1129 (interactive) | |
1130 (idlwave-shell-send-command idlwave-shell-dirstack-query | |
1131 'idlwave-shell-filter-directory | |
1132 'hide)) | |
1133 | |
1134 (defun idlwave-shell-retall (&optional arg) | |
1135 "Return from the entire calling stack." | |
1136 (interactive "P") | |
1137 (idlwave-shell-send-command "retall")) | |
1138 | |
1139 (defun idlwave-shell-closeall (&optional arg) | |
1140 "Close all open files." | |
1141 (interactive "P") | |
1142 (idlwave-shell-send-command "close,/all")) | |
1143 | |
1144 (defun idlwave-shell-quit (&optional arg) | |
1145 "Exit the idl process after confirmation. | |
1146 With prefix ARG, exit without confirmation." | |
1147 (interactive "P") | |
1148 (if (not (idlwave-shell-is-running)) | |
1149 (error "Shell is not running") | |
1150 (if (or arg (y-or-n-p "Exit the IDLWAVE Shell? ")) | |
1151 (condition-case nil | |
1152 (idlwave-shell-send-command "exit") | |
1153 (error nil))))) | |
1154 | |
1155 (defun idlwave-shell-reset (&optional visible) | |
1156 "Reset IDL. Return to main level and destroy the leaftover variables. | |
1157 This issues the following commands: | |
1158 RETALL | |
1159 WIDGET_CONTROL,/RESET | |
1160 CLOSE, /ALL | |
1161 HEAP_GC, /VERBOSE" | |
1162 ;; OBJ_DESTROY, OBJ_VALID() FIXME: should this be added? | |
1163 (interactive "P") | |
1164 (message "Resetting IDL") | |
1165 (idlwave-shell-send-command "retall" nil (not visible)) | |
1166 (idlwave-shell-send-command "widget_control,/reset" nil (not visible)) | |
1167 (idlwave-shell-send-command "close,/all" nil (not visible)) | |
1168 ;; (idlwave-shell-send-command "obj_destroy, obj_valid()" nil (not visible)) | |
1169 (idlwave-shell-send-command "heap_gc,/verbose" nil (not visible)) | |
1170 (setq idlwave-shell-calling-stack-index 0)) | |
1171 | |
1172 (defun idlwave-shell-filter-directory () | |
1173 "Get the current directory from `idlwave-shell-command-output'. | |
1174 Change the default directory for the process buffer to concur." | |
1175 (save-excursion | |
1176 (set-buffer (idlwave-shell-buffer)) | |
1177 (if (string-match "Current Directory: *\\(\\S-*\\) *$" | |
1178 idlwave-shell-command-output) | |
1179 (let ((dir (substring idlwave-shell-command-output | |
1180 (match-beginning 1) (match-end 1)))) | |
1181 (message "Setting Emacs wd to %s" dir) | |
1182 (setq idlwave-shell-default-directory dir) | |
1183 (setq default-directory (file-name-as-directory dir)))))) | |
1184 | |
1185 (defun idlwave-shell-complete (&optional arg) | |
1186 "Do completion in the idlwave-shell buffer. | |
1187 Calls `idlwave-shell-complete-filename' after some executive commands or | |
1188 in strings. Otherwise, calls `idlwave-complete' to complete modules and | |
1189 keywords." | |
1190 ;;FIXME: batch files? | |
1191 (interactive "P") | |
1192 (let (cmd) | |
1193 (cond | |
1194 ((setq cmd (idlwave-shell-executive-command)) | |
1195 ;; We are in a command line with an executive command | |
1196 (if (member (upcase cmd) | |
1197 '(".R" ".RU" ".RUN" ".RN" ".RNE" ".RNEW" | |
1198 ".COM" ".COMP" ".COMPI" ".COMPIL" ".COMPILE")) | |
1199 ;; This command expects file names | |
1200 (idlwave-shell-complete-filename))) | |
1201 ((idlwave-shell-filename-string) | |
1202 ;; In a string, could be a file name to here | |
1203 (idlwave-shell-complete-filename)) | |
1204 (t | |
1205 ;; Default completion of modules and keywords | |
1206 (idlwave-complete))))) | |
1207 | |
1208 (defun idlwave-shell-complete-filename (&optional arg) | |
1209 "Complete a file name at point if after a file name. | |
1210 We assume that we are after a file name when completing one of the | |
1211 args of an executive .run, .rnew or .compile. Also, in a string | |
1212 constant we complete file names. Otherwise return nil, so that | |
1213 other completion functions can do thier work." | |
1214 (let* ((comint-file-name-chars idlwave-shell-file-name-chars) | |
1215 (completion-ignore-case (default-value 'completion-ignore-case))) | |
1216 (comint-dynamic-complete-filename))) | |
1217 | |
1218 (defun idlwave-shell-executive-command () | |
1219 "Return the name of the current executive command, if any." | |
1220 (save-excursion | |
1221 (idlwave-beginning-of-statement) | |
1222 (if (looking-at "[ \t]*\\([.][^ \t\n\r]*\\)") | |
1223 (match-string 1)))) | |
1224 | |
1225 (defun idlwave-shell-filename-string () | |
1226 "Return t if in a string and after what could be a file name." | |
1227 (let ((limit (save-excursion (beginning-of-line) (point)))) | |
1228 (save-excursion | |
1229 ;; Skip backwards over file name chars | |
1230 (skip-chars-backward idlwave-shell-file-name-chars limit) | |
1231 ;; Check of the next char is a string delimiter | |
1232 (memq (preceding-char) '(?\' ?\"))))) | |
1233 | |
1234 ;;; | |
1235 ;;; This section contains code for debugging IDL programs. -------------------- | |
1236 ;;; | |
1237 | |
1238 (defun idlwave-shell-redisplay (&optional hide) | |
1239 "Tries to resync the display with where execution has stopped. | |
1240 Issues a \"help,/trace\" command followed by a call to | |
1241 `idlwave-shell-display-line'. Also updates the breakpoint | |
1242 overlays." | |
1243 (interactive) | |
1244 (idlwave-shell-send-command | |
1245 "help,/trace" | |
1246 '(idlwave-shell-display-line | |
1247 (idlwave-shell-pc-frame)) | |
1248 hide) | |
1249 (idlwave-shell-bp-query)) | |
1250 | |
1251 (defun idlwave-shell-display-level-in-calling-stack (&optional hide) | |
1252 (idlwave-shell-send-command | |
1253 "help,/trace" | |
1254 'idlwave-shell-parse-stack-and-display | |
1255 hide)) | |
1256 | |
1257 (defun idlwave-shell-parse-stack-and-display () | |
1258 (let* ((lines (delete "" (idlwave-split-string | |
1259 idlwave-shell-command-output "^%"))) | |
1260 (stack (delq nil (mapcar 'idlwave-shell-parse-line lines))) | |
1261 (nmax (1- (length stack))) | |
1262 (nmin 0) message) | |
1263 ; ;; Reset the stack to zero if it is a new stack. | |
1264 ; (if (not (equal stack idlwave-shell-last-calling-stack)) | |
1265 ; (setq idlwave-shell-calling-stack-index 0)) | |
1266 ; (setq idlwave-shell-last-calling-stack stack) | |
1267 (cond | |
1268 ((< nmax nmin) | |
1269 (setq idlwave-shell-calling-stack-index 0) | |
1270 (error "Problem with calling stack")) | |
1271 ((> idlwave-shell-calling-stack-index nmax) | |
1272 (setq idlwave-shell-calling-stack-index nmax | |
1273 message (format "%d is the highest level on the calling stack" | |
1274 nmax))) | |
1275 ((< idlwave-shell-calling-stack-index nmin) | |
1276 (setq idlwave-shell-calling-stack-index nmin | |
1277 message (format "%d is the lowest level on the calling stack" | |
1278 nmin)))) | |
1279 (idlwave-shell-display-line | |
1280 (nth idlwave-shell-calling-stack-index stack)) | |
1281 (message (or message | |
1282 (format "On stack level %d" | |
1283 idlwave-shell-calling-stack-index))))) | |
1284 | |
1285 (defun idlwave-shell-stack-up () | |
1286 "Display the source code one step up the calling stack." | |
1287 (interactive) | |
1288 (incf idlwave-shell-calling-stack-index) | |
1289 (idlwave-shell-display-level-in-calling-stack 'hide)) | |
1290 (defun idlwave-shell-stack-down () | |
1291 "Display the source code one step down the calling stack." | |
1292 (interactive) | |
1293 (decf idlwave-shell-calling-stack-index) | |
1294 (idlwave-shell-display-level-in-calling-stack 'hide)) | |
1295 | |
1296 (defun idlwave-shell-goto-frame (&optional frame) | |
1297 "Set buffer to FRAME with point at the frame line. | |
1298 If the optional argument FRAME is nil then idlwave-shell-pc-frame is | |
1299 used. Does nothing if the resulting frame is nil." | |
1300 (if frame () | |
1301 (setq frame (idlwave-shell-pc-frame))) | |
1302 (cond | |
1303 (frame | |
1304 (set-buffer (idlwave-find-file-noselect (car frame))) | |
1305 (widen) | |
1306 (goto-line (nth 1 frame))))) | |
1307 | |
1308 (defun idlwave-shell-pc-frame () | |
1309 "Returns the frame for IDL execution." | |
1310 (and idlwave-shell-halt-frame | |
1311 (list (nth 0 idlwave-shell-halt-frame) | |
1312 (nth 1 idlwave-shell-halt-frame)))) | |
1313 | |
1314 (defun idlwave-shell-valid-frame (frame) | |
1315 "Check that frame is for an existing file." | |
1316 (file-readable-p (car frame))) | |
1317 | |
1318 (defun idlwave-shell-display-line (frame &optional col) | |
1319 "Display FRAME file in other window with overlay arrow. | |
1320 | |
1321 FRAME is a list of file name, line number, and subroutine name. | |
1322 If FRAME is nil then remove overlay." | |
1323 (if (not frame) | |
1324 ;; Remove stop-line overlay from old position | |
1325 (progn | |
1326 (setq overlay-arrow-string nil) | |
1327 (if idlwave-shell-stop-line-overlay | |
1328 (delete-overlay idlwave-shell-stop-line-overlay))) | |
1329 (if (not (idlwave-shell-valid-frame frame)) | |
1330 (error (concat "Invalid frame - unable to access file: " (car frame))) | |
1331 ;;; | |
1332 ;;; buffer : the buffer to display a line in. | |
1333 ;;; select-shell: current buffer is the shell. | |
1334 ;;; | |
1335 (let* ((buffer (idlwave-find-file-noselect (car frame))) | |
1336 (select-shell (equal (buffer-name) (idlwave-shell-buffer))) | |
1337 window pos) | |
1338 | |
1339 ;; First make sure the shell window is visible | |
1340 (idlwave-display-buffer (idlwave-shell-buffer) | |
1341 nil (idlwave-shell-shell-frame)) | |
1342 | |
1343 ;; Now display the buffer and remember which window it is. | |
1344 (setq window (idlwave-display-buffer buffer | |
1345 nil (idlwave-shell-source-frame))) | |
1346 | |
1347 ;; Enter the buffer and mark the line | |
1348 (save-excursion | |
1349 (set-buffer buffer) | |
1350 (save-restriction | |
1351 (widen) | |
1352 (goto-line (nth 1 frame)) | |
1353 (setq pos (point)) | |
1354 (if idlwave-shell-stop-line-overlay | |
1355 ;; Move overlay | |
1356 (move-overlay idlwave-shell-stop-line-overlay | |
1357 (point) (save-excursion (end-of-line) (point)) | |
1358 (current-buffer)) | |
1359 ;; Use the arrow instead, but only if marking is wanted. | |
1360 (if idlwave-shell-mark-stop-line | |
1361 (setq overlay-arrow-string idlwave-shell-overlay-arrow)) | |
1362 (or overlay-arrow-position ; create the marker if necessary | |
1363 (setq overlay-arrow-position (make-marker))) | |
1364 (set-marker overlay-arrow-position (point) buffer))) | |
1365 | |
1366 ;; If the point is outside the restriction, widen the buffer. | |
1367 (if (or (< pos (point-min)) (> pos (point-max))) | |
1368 (progn | |
1369 (widen) | |
1370 (goto-char pos))) | |
1371 | |
1372 ;; If we have the column of the error, move the cursor there. | |
1373 (if col (move-to-column col)) | |
1374 (setq pos (point))) | |
1375 | |
1376 ;; Make sure pos is really displayed in the window. | |
1377 (set-window-point window pos) | |
1378 | |
1379 ;; FIXME: the following frame redraw was taken out because it | |
1380 ;; flashes. I think it is not needed. The code is left here in | |
1381 ;; case we have to put it back in. | |
1382 ;; (redraw-frame (window-frame window)) | |
1383 | |
1384 ;; If we came from the shell, go back there. Otherwise select | |
1385 ;; the window where the error is displayed. | |
1386 (if (and (equal (buffer-name) (idlwave-shell-buffer)) | |
1387 (not select-shell)) | |
1388 (select-window window)))))) | |
1389 | |
1390 | |
1391 (defun idlwave-shell-step (arg) | |
1392 "Step one source line. If given prefix argument ARG, step ARG source lines." | |
1393 (interactive "p") | |
1394 (or (not arg) (< arg 1) | |
1395 (setq arg 1)) | |
1396 (idlwave-shell-send-command | |
1397 (concat ".s " (if (integerp arg) (int-to-string arg) arg)))) | |
1398 | |
1399 (defun idlwave-shell-stepover (arg) | |
1400 "Stepover one source line. | |
1401 If given prefix argument ARG, step ARG source lines. | |
1402 Uses IDL's stepover executive command which does not enter called functions." | |
1403 (interactive "p") | |
1404 (or (not arg) (< arg 1) | |
1405 (setq arg 1)) | |
1406 (idlwave-shell-send-command | |
1407 (concat ".so " (if (integerp arg) (int-to-string arg) arg)))) | |
1408 | |
1409 (defun idlwave-shell-break-here (&optional count cmd) | |
1410 "Set breakpoint at current line. | |
1411 | |
1412 If Count is nil then an ordinary breakpoint is set. We treat a count | |
1413 of 1 as a temporary breakpoint using the ONCE keyword. Counts greater | |
1414 than 1 use the IDL AFTER=count keyword to break only after reaching | |
1415 the statement count times. | |
1416 | |
1417 Optional argument CMD is a list or function to evaluate upon reaching | |
1418 the breakpoint." | |
1419 | |
1420 (interactive "P") | |
1421 (if (listp count) | |
1422 (setq count nil)) | |
1423 (idlwave-shell-set-bp | |
1424 ;; Create breakpoint | |
1425 (idlwave-shell-bp (idlwave-shell-current-frame) | |
1426 (list count cmd) | |
1427 (idlwave-shell-current-module)))) | |
1428 | |
1429 (defun idlwave-shell-set-bp-check (bp) | |
1430 "Check for failure to set breakpoint. | |
1431 This is run on `idlwave-shell-post-command-hook'. | |
1432 Offers to recompile the procedure if we failed. This usually fixes | |
1433 the problem with not being able to set the breakpoint." | |
1434 ;; Scan for message | |
1435 (if (and idlwave-shell-command-output | |
1436 (string-match "% BREAKPOINT: *Unable to find code" | |
1437 idlwave-shell-command-output)) | |
1438 ;; Offer to recompile | |
1439 (progn | |
1440 (if (progn | |
1441 (beep) | |
1442 (y-or-n-p | |
1443 (concat "Okay to recompile file " | |
1444 (idlwave-shell-bp-get bp 'file) " "))) | |
1445 ;; Recompile | |
1446 (progn | |
1447 ;; Clean up before retrying | |
1448 (idlwave-shell-command-failure) | |
1449 (idlwave-shell-send-command | |
1450 (concat ".run " (idlwave-shell-bp-get bp 'file)) nil nil) | |
1451 ;; Try setting breakpoint again | |
1452 (idlwave-shell-set-bp bp)) | |
1453 (beep) | |
1454 (message "Unable to set breakpoint.") | |
1455 (idlwave-shell-command-failure) | |
1456 ) | |
1457 ;; return non-nil if no error found | |
1458 nil) | |
1459 'okay)) | |
1460 | |
1461 (defun idlwave-shell-command-failure () | |
1462 "Do any necessary clean up when an IDL command fails. | |
1463 Call this from a function attached to `idlwave-shell-post-command-hook' | |
1464 that detects the failure of a command. | |
1465 For example, this is called from `idlwave-shell-set-bp-check' when a | |
1466 breakpoint can not be set." | |
1467 ;; Clear pending commands | |
1468 (setq idlwave-shell-pending-commands nil)) | |
1469 | |
1470 (defun idlwave-shell-cont () | |
1471 "Continue executing." | |
1472 (interactive) | |
1473 (idlwave-shell-send-command ".c" '(idlwave-shell-redisplay 'hide))) | |
1474 | |
1475 (defun idlwave-shell-go () | |
1476 "Run .GO. This starts the main program of the last compiled file." | |
1477 (interactive) | |
1478 (idlwave-shell-send-command ".go" '(idlwave-shell-redisplay 'hide))) | |
1479 | |
1480 (defun idlwave-shell-return () | |
1481 "Run .RETURN (continue to next return, but stay in subprogram)." | |
1482 (interactive) | |
1483 (idlwave-shell-send-command ".return" '(idlwave-shell-redisplay 'hide))) | |
1484 | |
1485 (defun idlwave-shell-skip () | |
1486 "Run .SKIP (skip one line, then step)." | |
1487 (interactive) | |
1488 (idlwave-shell-send-command ".skip" '(idlwave-shell-redisplay 'hide))) | |
1489 | |
1490 (defun idlwave-shell-clear-bp (bp) | |
1491 "Clear breakpoint BP. | |
1492 Clears in IDL and in `idlwave-shell-bp-alist'." | |
1493 (let ((index (idlwave-shell-bp-get bp))) | |
1494 (if index | |
1495 (progn | |
1496 (idlwave-shell-send-command | |
1497 (concat "breakpoint,/clear," | |
1498 (if (integerp index) (int-to-string index) index))) | |
1499 (idlwave-shell-bp-query))))) | |
1500 | |
1501 (defun idlwave-shell-current-frame () | |
1502 "Return a list containing the current file name and line point is in. | |
1503 If in the IDL shell buffer, returns `idlwave-shell-pc-frame'." | |
1504 (if (eq (current-buffer) (get-buffer (idlwave-shell-buffer))) | |
1505 ;; In IDL shell | |
1506 (idlwave-shell-pc-frame) | |
1507 ;; In source | |
1508 (list (idlwave-shell-file-name (buffer-file-name)) | |
1509 (save-restriction | |
1510 (widen) | |
1511 (save-excursion | |
1512 (beginning-of-line) | |
1513 (1+ (count-lines 1 (point)))))))) | |
1514 | |
1515 (defun idlwave-shell-current-module () | |
1516 "Return the name of the module for the current file. | |
1517 Returns nil if unable to obtain a module name." | |
1518 (if (eq (current-buffer) (get-buffer (idlwave-shell-buffer))) | |
1519 ;; In IDL shell | |
1520 (nth 2 idlwave-shell-halt-frame) | |
1521 ;; In pro file | |
1522 (save-restriction | |
1523 (widen) | |
1524 (save-excursion | |
1525 (if (idlwave-prev-index-position) | |
1526 (upcase (idlwave-unit-name))))))) | |
1527 | |
1528 (defun idlwave-shell-clear-current-bp () | |
1529 "Remove breakpoint at current line. | |
1530 This command can be called from the shell buffer if IDL is currently stopped | |
1531 at a breakpoint." | |
1532 (interactive) | |
1533 (let ((bp (idlwave-shell-find-bp (idlwave-shell-current-frame)))) | |
1534 (if bp (idlwave-shell-clear-bp bp) | |
1535 ;; Try moving to beginning of statement | |
1536 (save-excursion | |
1537 (idlwave-shell-goto-frame) | |
1538 (idlwave-beginning-of-statement) | |
1539 (setq bp (idlwave-shell-find-bp (idlwave-shell-current-frame))) | |
1540 (if bp (idlwave-shell-clear-bp bp) | |
1541 (beep) | |
1542 (message "Cannot identify breakpoint for this line")))))) | |
1543 | |
1544 (defun idlwave-shell-to-here () | |
1545 "Set a breakpoint with count 1 then continue." | |
1546 (interactive) | |
1547 (idlwave-shell-break-here 1) | |
1548 (idlwave-shell-cont)) | |
1549 | |
1550 (defun idlwave-shell-break-in (&optional module) | |
1551 "Look for a module name near point and set a break point for it. | |
1552 The command looks for an identifier near point and sets a breakpoint | |
1553 for the first line of the corresponding module." | |
1554 (interactive) | |
1555 ;; get the identifier | |
1556 (let (module) | |
1557 (save-excursion | |
1558 (skip-chars-backward "a-zA-Z0-9_$") | |
1559 (if (looking-at idlwave-identifier) | |
1560 (setq module (match-string 0)) | |
1561 (error "No identifier at point"))) | |
1562 (idlwave-shell-send-command | |
1563 idlwave-shell-sources-query | |
1564 `(progn | |
1565 (idlwave-shell-sources-filter) | |
1566 (idlwave-shell-set-bp-in-module ,module)) | |
1567 'hide))) | |
1568 | |
1569 (defun idlwave-shell-set-bp-in-module (module) | |
1570 "Set breakpoint in module. Assumes that `idlwave-shell-sources-alist' | |
1571 contains an entry for that module." | |
1572 (let ((source-file (car-safe | |
1573 (cdr-safe | |
1574 (assoc (upcase module) | |
1575 idlwave-shell-sources-alist)))) | |
1576 buf) | |
1577 (if (or (not source-file) | |
1578 (not (file-regular-p source-file)) | |
1579 (not (setq buf | |
1580 (or (idlwave-get-buffer-visiting source-file) | |
1581 (find-file-noselect source-file))))) | |
1582 (progn | |
1583 (message "The source file for module %s is probably not compiled" | |
1584 module) | |
1585 (beep)) | |
1586 (save-excursion | |
1587 (set-buffer buf) | |
1588 (save-excursion | |
1589 (goto-char (point-min)) | |
1590 (let ((case-fold-search t)) | |
1591 (if (re-search-forward | |
1592 (concat "^[ \t]*\\(pro\\|function\\)[ \t]+" | |
1593 (downcase module) | |
1594 "[ \t\n,]") nil t) | |
1595 (progn | |
1596 (goto-char (match-beginning 1)) | |
1597 (message "Setting breakpoint for module %s" module) | |
1598 (idlwave-shell-break-here)) | |
1599 (message "Cannot find module %s in file %s" module source-file) | |
1600 (beep)))))))) | |
1601 | |
1602 (defun idlwave-shell-up () | |
1603 "Run to end of current block. | |
1604 Sets a breakpoint with count 1 at end of block, then continues." | |
1605 (interactive) | |
1606 (if (idlwave-shell-pc-frame) | |
1607 (save-excursion | |
1608 (idlwave-shell-goto-frame) | |
1609 ;; find end of subprogram | |
1610 (let ((eos (save-excursion | |
1611 (idlwave-beginning-of-subprogram) | |
1612 (idlwave-forward-block) | |
1613 (point)))) | |
1614 (idlwave-backward-up-block -1) | |
1615 ;; move beyond end block line - IDL will not break there. | |
1616 ;; That is, you can put a breakpoint there but when IDL does | |
1617 ;; break it will report that it is at the next line. | |
1618 (idlwave-next-statement) | |
1619 (idlwave-end-of-statement) | |
1620 ;; Make sure we are not beyond subprogram | |
1621 (if (< (point) eos) | |
1622 ;; okay | |
1623 () | |
1624 ;; Move back inside subprogram | |
1625 (goto-char eos) | |
1626 (idlwave-previous-statement)) | |
1627 (idlwave-shell-to-here))))) | |
1628 | |
1629 (defun idlwave-shell-out () | |
1630 "Attempt to run until this procedure exits. | |
1631 Runs to the last statement and then steps 1 statement. Use the .out command." | |
1632 (interactive) | |
1633 (idlwave-shell-send-command (concat ".o"))) | |
1634 | |
1635 (defun idlwave-shell-help-expression () | |
1636 "Print help on current expression. See `idlwave-shell-print'." | |
1637 (interactive) | |
1638 (idlwave-shell-print 'help)) | |
1639 | |
1640 (defun idlwave-shell-mouse-print (event) | |
1641 "Call `idlwave-shell-print' at the mouse position." | |
1642 (interactive "e") | |
1643 (mouse-set-point event) | |
1644 (idlwave-shell-print)) | |
1645 | |
1646 (defun idlwave-shell-mouse-help (event) | |
1647 "Call `idlwave-shell-print' at the mouse position." | |
1648 (interactive "e") | |
1649 (mouse-set-point event) | |
1650 (idlwave-shell-help-expression)) | |
1651 | |
1652 (defun idlwave-shell-print (&optional help special) | |
1653 "Print current expression. With are HELP, show help on expression. | |
1654 An expression is an identifier plus 1 pair of matched parentheses | |
1655 directly following the identifier - an array or function | |
1656 call. Alternatively, an expression is the contents of any matched | |
1657 parentheses when the open parentheses is not directly preceded by an | |
1658 identifier. If point is at the beginning or within an expression | |
1659 return the inner-most containing expression, otherwise, return the | |
1660 preceding expression." | |
1661 (interactive "P") | |
1662 (save-excursion | |
1663 (let (beg end) | |
1664 ;; Move to beginning of current or previous expression | |
1665 (if (looking-at "\\<\\|(") | |
1666 ;; At beginning of expression, don't move backwards unless | |
1667 ;; this is at the end of an indentifier. | |
1668 (if (looking-at "\\>") | |
1669 (backward-sexp)) | |
1670 (backward-sexp)) | |
1671 (if (looking-at "\\>") | |
1672 ;; Move to beginning of identifier - must be an array or | |
1673 ;; function expression. | |
1674 (backward-sexp)) | |
1675 ;; Move to end of expression | |
1676 (setq beg (point)) | |
1677 (forward-sexp) | |
1678 (while (looking-at "\\>(\\|\\.") | |
1679 ;; an array | |
1680 (forward-sexp)) | |
1681 (setq end (point)) | |
1682 (when idlwave-shell-expression-overlay | |
1683 (move-overlay idlwave-shell-expression-overlay beg end) | |
1684 (add-hook 'pre-command-hook 'idlwave-shell-delete-expression-overlay)) | |
1685 (if special | |
1686 (idlwave-shell-send-command | |
1687 (concat (if help "help," "print,") (buffer-substring beg end)) | |
1688 `(idlwave-shell-process-print-output ,(buffer-substring beg end) | |
1689 idlwave-shell-command-output | |
1690 ,special) | |
1691 'hide) | |
1692 (idlwave-shell-recenter-shell-window) | |
1693 (idlwave-shell-send-command | |
1694 (concat (if help "help," "print,") (buffer-substring beg end))))))) | |
1695 | |
1696 (defun idlwave-shell-delete-expression-overlay () | |
1697 (condition-case nil | |
1698 (if idlwave-shell-expression-overlay | |
1699 (delete-overlay idlwave-shell-expression-overlay)) | |
1700 (error nil)) | |
1701 (remove-hook 'pre-command-hook 'idlwave-shell-delete-expression-overlay)) | |
1702 | |
1703 (defvar idlwave-shell-bp-alist nil | |
1704 "Alist of breakpoints. | |
1705 A breakpoint is a cons cell \(\(file line\) . \(\(index module\) data\)\) | |
1706 | |
1707 The car is the frame for the breakpoint: | |
1708 file - full path file name. | |
1709 line - line number of breakpoint - integer. | |
1710 | |
1711 The first element of the cdr is a list of internal IDL data: | |
1712 index - the index number of the breakpoint internal to IDL. | |
1713 module - the module for breakpoint internal to IDL. | |
1714 | |
1715 Remaining elements of the cdr: | |
1716 data - Data associated with the breakpoint by idlwave-shell currently | |
1717 contains two items: | |
1718 | |
1719 count - number of times to execute breakpoint. When count reaches 0 | |
1720 the breakpoint is cleared and removed from the alist. | |
1721 command - command to execute when breakpoint is reached, either a | |
1722 lisp function to be called with `funcall' with no arguments or a | |
1723 list to be evaluated with `eval'.") | |
1724 | |
1725 (defun idlwave-shell-run-region (beg end &optional n) | |
1726 "Compile and run the region using the IDL process. | |
1727 Copies the region to a temporary file `idlwave-shell-temp-pro-file' | |
1728 and issues the IDL .run command for the file. Because the | |
1729 region is compiled and run as a main program there is no | |
1730 problem with begin-end blocks extending over multiple | |
1731 lines - which would be a problem if `idlwave-shell-evaluate-region' | |
1732 was used. An END statement is appended to the region if necessary. | |
1733 | |
1734 If there is a prefix argument, display IDL process." | |
1735 (interactive "r\nP") | |
1736 (let ((oldbuf (current-buffer))) | |
1737 (save-excursion | |
1738 (set-buffer (idlwave-find-file-noselect | |
1739 idlwave-shell-temp-pro-file)) | |
1740 (erase-buffer) | |
1741 (insert-buffer-substring oldbuf beg end) | |
1742 (if (not (save-excursion | |
1743 (idlwave-previous-statement) | |
1744 (idlwave-look-at "\\<end\\>"))) | |
1745 (insert "\nend\n")) | |
1746 (save-buffer 0))) | |
1747 (idlwave-shell-send-command (concat ".run " idlwave-shell-temp-pro-file)) | |
1748 (if n | |
1749 (idlwave-display-buffer (idlwave-shell-buffer) | |
1750 nil (idlwave-shell-shell-frame)))) | |
1751 | |
1752 (defun idlwave-shell-evaluate-region (beg end &optional n) | |
1753 "Send region to the IDL process. | |
1754 If there is a prefix argument, display IDL process. | |
1755 Does not work for a region with multiline blocks - use | |
1756 `idlwave-shell-run-region' for this." | |
1757 (interactive "r\nP") | |
1758 (idlwave-shell-send-command (buffer-substring beg end)) | |
1759 (if n | |
1760 (idlwave-display-buffer (idlwave-shell-buffer) | |
1761 nil (idlwave-shell-shell-frame)))) | |
1762 | |
1763 (defun idlwave-display-buffer (buf not-this-window-p &optional frame) | |
1764 (if (or (< emacs-major-version 20) | |
1765 (and (= emacs-major-version 20) | |
1766 (< emacs-minor-version 3))) | |
1767 ;; Only two args. | |
1768 (display-buffer buf not-this-window-p) | |
1769 ;; Three ares possible. | |
1770 (display-buffer buf not-this-window-p frame))) | |
1771 | |
1772 (defvar idlwave-shell-bp-buffer "*idlwave-shell-bp*" | |
1773 "Scratch buffer for parsing IDL breakpoint lists and other stuff.") | |
1774 | |
1775 (defun idlwave-shell-bp-query () | |
1776 "Reconcile idlwave-shell's breakpoint list with IDL's. | |
1777 Queries IDL using the string in `idlwave-shell-bp-query'." | |
1778 (interactive) | |
1779 (idlwave-shell-send-command idlwave-shell-bp-query | |
1780 'idlwave-shell-filter-bp | |
1781 'hide)) | |
1782 | |
1783 (defun idlwave-shell-bp-get (bp &optional item) | |
1784 "Get a value for a breakpoint. | |
1785 BP has the form of elements in idlwave-shell-bp-alist. | |
1786 Optional second arg ITEM is the particular value to retrieve. | |
1787 ITEM can be 'file, 'line, 'index, 'module, 'count, 'cmd, or 'data. | |
1788 'data returns a list of 'count and 'cmd. | |
1789 Defaults to 'index." | |
1790 (cond | |
1791 ;; Frame | |
1792 ((eq item 'line) (nth 1 (car bp))) | |
1793 ((eq item 'file) (nth 0 (car bp))) | |
1794 ;; idlwave-shell breakpoint data | |
1795 ((eq item 'data) (cdr (cdr bp))) | |
1796 ((eq item 'count) (nth 0 (cdr (cdr bp)))) | |
1797 ((eq item 'cmd) (nth 1 (cdr (cdr bp)))) | |
1798 ;; IDL breakpoint info | |
1799 ((eq item 'module) (nth 1 (car (cdr bp)))) | |
1800 ;; index - default | |
1801 (t (nth 0 (car (cdr bp)))))) | |
1802 | |
1803 (defun idlwave-shell-filter-bp () | |
1804 "Get the breakpoints from `idlwave-shell-command-output'. | |
1805 Create `idlwave-shell-bp-alist' updating breakpoint count and command data | |
1806 from previous breakpoint list." | |
1807 (save-excursion | |
1808 (set-buffer (get-buffer-create idlwave-shell-bp-buffer)) | |
1809 (erase-buffer) | |
1810 (insert idlwave-shell-command-output) | |
1811 (goto-char (point-min)) | |
1812 (let ((old-bp-alist idlwave-shell-bp-alist)) | |
1813 (setq idlwave-shell-bp-alist (list nil)) | |
1814 (if (re-search-forward "^\\s-*Index.*\n\\s-*-" nil t) | |
1815 (while (and | |
1816 (not (progn (forward-line) (eobp))) | |
1817 ;; Parse breakpoint line. | |
1818 ;; Breakpoints have the form: | |
1819 ;; Index Module Line File | |
1820 ;; All seperated by whitespace. | |
1821 ;; | |
1822 ;; Add the breakpoint info to the list | |
1823 (re-search-forward | |
1824 "\\s-*\\(\\S-+\\)\\s-+\\(\\S-+\\)\\s-+\\(\\S-+\\)\\s-+\\(\\S-+\\)" nil t)) | |
1825 (nconc idlwave-shell-bp-alist | |
1826 (list | |
1827 (cons | |
1828 (list | |
1829 (save-match-data | |
1830 (idlwave-shell-file-name | |
1831 (buffer-substring ; file | |
1832 (match-beginning 4) (match-end 4)))) | |
1833 (string-to-int ; line | |
1834 (buffer-substring | |
1835 (match-beginning 3) (match-end 3)))) | |
1836 (list | |
1837 (list | |
1838 (buffer-substring ; index | |
1839 (match-beginning 1) (match-end 1)) | |
1840 (buffer-substring ; module | |
1841 (match-beginning 2) (match-end 2))) | |
1842 ;; idlwave-shell data: count, command | |
1843 nil nil)))))) | |
1844 (setq idlwave-shell-bp-alist (cdr idlwave-shell-bp-alist)) | |
1845 ;; Update count, commands of breakpoints | |
1846 (mapcar 'idlwave-shell-update-bp old-bp-alist))) | |
1847 ;; Update the breakpoint overlays | |
1848 (idlwave-shell-update-bp-overlays) | |
1849 ;; Return the new list | |
1850 idlwave-shell-bp-alist) | |
1851 | |
1852 (defun idlwave-shell-update-bp (bp) | |
1853 "Update BP data in breakpoint list. | |
1854 If BP frame is in `idlwave-shell-bp-alist' updates the breakpoint data." | |
1855 (let ((match (assoc (car bp) idlwave-shell-bp-alist))) | |
1856 (if match (setcdr (cdr match) (cdr (cdr bp)))))) | |
1857 | |
1858 (defun idlwave-shell-set-bp-data (bp data) | |
1859 "Set the data of BP to DATA." | |
1860 (setcdr (cdr bp) data)) | |
1861 | |
1862 (defun idlwave-shell-bp (frame &optional data module) | |
1863 "Create a breakpoint structure containing FRAME and DATA. Second | |
1864 and third args, DATA and MODULE, are optional. Returns a breakpoint | |
1865 of the format used in `idlwave-shell-bp-alist'. Can be used in commands | |
1866 attempting match a breakpoint in `idlwave-shell-bp-alist'." | |
1867 (cons frame (cons (list nil module) data))) | |
1868 | |
1869 (defvar idlwave-shell-old-bp nil | |
1870 "List of breakpoints previous to setting a new breakpoint.") | |
1871 | |
1872 (defun idlwave-shell-sources-bp (bp) | |
1873 "Check `idlwave-shell-sources-alist' for source of breakpoint using BP. | |
1874 If an equivalency is found, return the IDL internal source name. | |
1875 Otherwise return the filename in bp." | |
1876 (let* | |
1877 ((bp-file (idlwave-shell-bp-get bp 'file)) | |
1878 (bp-module (idlwave-shell-bp-get bp 'module)) | |
1879 (internal-file-list (cdr (assoc bp-module idlwave-shell-sources-alist)))) | |
1880 (if (and internal-file-list | |
1881 (equal bp-file (nth 0 internal-file-list))) | |
1882 (nth 1 internal-file-list) | |
1883 bp-file))) | |
1884 | |
1885 (defun idlwave-shell-set-bp (bp) | |
1886 "Try to set a breakpoint BP. | |
1887 | |
1888 The breakpoint will be placed at the beginning of the statement on the | |
1889 line specified by BP or at the next IDL statement if that line is not | |
1890 a statement. | |
1891 Determines IDL's internal representation for the breakpoint which may | |
1892 have occured at a different line then used with the breakpoint | |
1893 command." | |
1894 | |
1895 ;; Get and save the old breakpoints | |
1896 (idlwave-shell-send-command | |
1897 idlwave-shell-bp-query | |
1898 '(progn | |
1899 (idlwave-shell-filter-bp) | |
1900 (setq idlwave-shell-old-bp idlwave-shell-bp-alist)) | |
1901 'hide) | |
1902 ;; Get sources for IDL compiled procedures followed by setting | |
1903 ;; breakpoint. | |
1904 (idlwave-shell-send-command | |
1905 idlwave-shell-sources-query | |
1906 (` (progn | |
1907 (idlwave-shell-sources-filter) | |
1908 (idlwave-shell-set-bp2 (quote (, bp))))) | |
1909 'hide)) | |
1910 | |
1911 (defun idlwave-shell-set-bp2 (bp) | |
1912 "Use results of breakpoint and sources query to set bp. | |
1913 Use the count argument with IDLs breakpoint command. | |
1914 We treat a count of 1 as a temporary breakpoint. | |
1915 Counts greater than 1 use the IDL AFTER=count keyword to break | |
1916 only after reaching the statement count times." | |
1917 (let* | |
1918 ((arg (idlwave-shell-bp-get bp 'count)) | |
1919 (key (cond | |
1920 ((not (and arg (numberp arg))) "") | |
1921 ((= arg 1) | |
1922 ",/once") | |
1923 ((> arg 1) | |
1924 (format ",after=%d" arg)))) | |
1925 (line (idlwave-shell-bp-get bp 'line))) | |
1926 (idlwave-shell-send-command | |
1927 (concat "breakpoint,'" | |
1928 (idlwave-shell-sources-bp bp) "'," | |
1929 (if (integerp line) (setq line (int-to-string line))) | |
1930 key) | |
1931 ;; Check for failure and look for breakpoint in IDL's list | |
1932 (` (progn | |
1933 (if (idlwave-shell-set-bp-check (quote (, bp))) | |
1934 (idlwave-shell-set-bp3 (quote (, bp))))) | |
1935 ) | |
1936 ;; do not hide output | |
1937 nil | |
1938 'preempt))) | |
1939 | |
1940 (defun idlwave-shell-set-bp3 (bp) | |
1941 "Find the breakpoint in IDL's internal list of breakpoints." | |
1942 (idlwave-shell-send-command idlwave-shell-bp-query | |
1943 (` (progn | |
1944 (idlwave-shell-filter-bp) | |
1945 (idlwave-shell-new-bp (quote (, bp))))) | |
1946 'hide | |
1947 'preempt)) | |
1948 | |
1949 (defun idlwave-shell-find-bp (frame) | |
1950 "Return breakpoint from `idlwave-shell-bp-alist' for frame. | |
1951 Returns nil if frame not found." | |
1952 (assoc frame idlwave-shell-bp-alist)) | |
1953 | |
1954 (defun idlwave-shell-new-bp (bp) | |
1955 "Find the new breakpoint in IDL's list and update with DATA. | |
1956 The actual line number for a breakpoint in IDL may be different than | |
1957 the line number used with the IDL breakpoint command. | |
1958 Looks for a new breakpoint index number in the list. This is | |
1959 considered the new breakpoint if the file name of frame matches." | |
1960 (let ((obp-index (mapcar 'idlwave-shell-bp-get idlwave-shell-old-bp)) | |
1961 (bpl idlwave-shell-bp-alist)) | |
1962 (while (and (member (idlwave-shell-bp-get (car bpl)) obp-index) | |
1963 (setq bpl (cdr bpl)))) | |
1964 (if (and | |
1965 (not bpl) | |
1966 ;; No additional breakpoint. | |
1967 ;; Need to check if we are just replacing a breakpoint. | |
1968 (setq bpl (assoc (car bp) idlwave-shell-bp-alist))) | |
1969 (setq bpl (list bpl))) | |
1970 (if (and bpl | |
1971 (equal (idlwave-shell-bp-get (setq bpl (car bpl)) 'file) | |
1972 (idlwave-shell-bp-get bp 'file))) | |
1973 ;; Got the breakpoint - add count, command to it. | |
1974 ;; This updates `idlwave-shell-bp-alist' because a deep copy was | |
1975 ;; not done for bpl. | |
1976 (idlwave-shell-set-bp-data bpl (idlwave-shell-bp-get bp 'data)) | |
1977 (beep) | |
1978 (message "Failed to identify breakpoint in IDL")))) | |
1979 | |
1980 (defvar idlwave-shell-bp-overlays nil | |
1981 "List of overlays marking breakpoints") | |
1982 | |
1983 (defun idlwave-shell-update-bp-overlays () | |
1984 "Update the overlays which mark breakpoints in the source code. | |
1985 Existing overlays are recycled, in order to minimize consumption." | |
1986 ;; FIXME: we could cache them all, but that would be more work. | |
1987 (when idlwave-shell-mark-breakpoints | |
1988 (let ((bp-list idlwave-shell-bp-alist) | |
1989 (ov-list idlwave-shell-bp-overlays) | |
1990 ov bp) | |
1991 ;; Delete the old overlays from their buffers | |
1992 (while (setq ov (pop ov-list)) | |
1993 (delete-overlay ov)) | |
1994 (setq ov-list idlwave-shell-bp-overlays | |
1995 idlwave-shell-bp-overlays nil) | |
1996 (while (setq bp (pop bp-list)) | |
1997 (save-excursion | |
1998 (idlwave-shell-goto-frame (car bp)) | |
1999 (let* ((end (progn (end-of-line 1) (point))) | |
2000 (beg (progn (beginning-of-line 1) (point))) | |
2001 (ov (or (pop ov-list) | |
2002 (idlwave-shell-make-new-bp-overlay)))) | |
2003 (move-overlay ov beg end) | |
2004 (push ov idlwave-shell-bp-overlays))))))) | |
2005 | |
2006 (defvar idlwave-shell-bp-glyph) | |
2007 (defun idlwave-shell-make-new-bp-overlay () | |
2008 "Make a new overlay for highlighting breakpoints. | |
2009 This stuff is stringly dependant upon the version of Emacs." | |
2010 (let ((ov (make-overlay 1 1))) | |
2011 (if (featurep 'xemacs) | |
2012 ;; This is XEmacs | |
2013 (progn | |
2014 (cond | |
2015 ((eq (console-type) 'tty) | |
2016 ;; tty's cannot display glyphs | |
2017 (set-extent-property ov 'face 'idlwave-shell-bp-face)) | |
2018 ((and (memq idlwave-shell-mark-breakpoints '(t glyph)) | |
2019 idlwave-shell-bp-glyph) | |
2020 ;; use the glyph | |
2021 (set-extent-property ov 'begin-glyph idlwave-shell-bp-glyph)) | |
2022 (idlwave-shell-mark-breakpoints | |
2023 ;; use the face | |
2024 (set-extent-property ov 'face 'idlwave-shell-bp-face)) | |
2025 (t | |
2026 ;; no marking | |
2027 nil)) | |
2028 (set-extent-priority ov -1)) ; make stop line face prevail | |
2029 ;; This is Emacs | |
2030 (cond | |
2031 (window-system | |
2032 (if (and (memq idlwave-shell-mark-breakpoints '(t glyph)) | |
2033 idlwave-shell-bp-glyph) ; this var knows if glyph's possible | |
2034 ;; use a glyph | |
2035 (let ((string "@")) | |
2036 (put-text-property 0 1 | |
2037 'display (cons nil idlwave-shell-bp-glyph) | |
2038 string) | |
2039 (overlay-put ov 'before-string string)) | |
2040 (overlay-put ov 'face 'idlwave-shell-bp-face))) | |
2041 (idlwave-shell-mark-breakpoints | |
2042 ;; use a face | |
2043 (overlay-put ov 'face 'idlwave-shell-bp-face)) | |
2044 (t | |
2045 ;; No marking | |
2046 nil))) | |
2047 ov)) | |
2048 | |
2049 (defun idlwave-shell-edit-default-command-line (arg) | |
2050 "Edit the current execute command." | |
2051 (interactive "P") | |
2052 (setq idlwave-shell-command-line-to-execute | |
2053 (read-string "IDL> " idlwave-shell-command-line-to-execute))) | |
2054 | |
2055 (defun idlwave-shell-execute-default-command-line (arg) | |
2056 "Execute a command line. On first use, ask for the command. | |
2057 Also with prefix arg, ask for the command. You can also uase the command | |
2058 `idlwave-shell-edit-default-command-line' to edit the line." | |
2059 (interactive "P") | |
2060 (if (or (not idlwave-shell-command-line-to-execute) | |
2061 arg) | |
2062 (setq idlwave-shell-command-line-to-execute | |
2063 (read-string "IDL> " idlwave-shell-command-line-to-execute))) | |
2064 (idlwave-shell-reset nil) | |
2065 (idlwave-shell-send-command idlwave-shell-command-line-to-execute | |
2066 '(idlwave-shell-redisplay 'hide))) | |
2067 | |
2068 (defun idlwave-shell-save-and-run () | |
2069 "Save file and run it in IDL. | |
2070 Runs `save-buffer' and sends a '.RUN' command for the associated file to IDL. | |
2071 When called from the shell buffer, re-run the file which was last handled by | |
2072 one of the save-and-.. commands." | |
2073 (interactive) | |
2074 (idlwave-shell-save-and-action 'run)) | |
2075 | |
2076 (defun idlwave-shell-save-and-compile () | |
2077 "Save file and run it in IDL. | |
2078 Runs `save-buffer' and sends '.COMPILE' command for the associated file to IDL. | |
2079 When called from the shell buffer, re-compile the file which was last handled by | |
2080 one of the save-and-.. commands." | |
2081 (interactive) | |
2082 (idlwave-shell-save-and-action 'compile)) | |
2083 | |
2084 (defun idlwave-shell-save-and-batch () | |
2085 "Save file and batch it in IDL. | |
2086 Runs `save-buffer' and sends a '@file' command for the associated file to IDL. | |
2087 When called from the shell buffer, re-batch the file which was last handled by | |
2088 one of the save-and-.. commands." | |
2089 (interactive) | |
2090 (idlwave-shell-save-and-action 'batch)) | |
2091 | |
2092 (defun idlwave-shell-save-and-action (action) | |
2093 "Save file and compile it in IDL. | |
2094 Runs `save-buffer' and sends a '.RUN' command for the associated file to IDL. | |
2095 When called from the shell buffer, re-compile the file which was last | |
2096 handled by this command." | |
2097 ;; Remove the stop overlay. | |
2098 (if idlwave-shell-stop-line-overlay | |
2099 (delete-overlay idlwave-shell-stop-line-overlay)) | |
2100 (setq overlay-arrow-string nil) | |
2101 (let (buf) | |
2102 (cond | |
2103 ((eq major-mode 'idlwave-mode) | |
2104 (save-buffer) | |
2105 (setq idlwave-shell-last-save-and-action-file (buffer-file-name))) | |
2106 (idlwave-shell-last-save-and-action-file | |
2107 (if (setq buf (idlwave-get-buffer-visiting | |
2108 idlwave-shell-last-save-and-action-file)) | |
2109 (save-excursion | |
2110 (set-buffer buf) | |
2111 (save-buffer)))) | |
2112 (t (setq idlwave-shell-last-save-and-action-file | |
2113 (read-file-name "File: "))))) | |
2114 (if (file-regular-p idlwave-shell-last-save-and-action-file) | |
2115 (progn | |
2116 (idlwave-shell-send-command | |
2117 (concat (cond ((eq action 'run) ".run ") | |
2118 ((eq action 'compile) ".compile ") | |
2119 ((eq action 'batch) "@") | |
2120 (t (error "Unknown action %s" action))) | |
2121 idlwave-shell-last-save-and-action-file) | |
2122 nil nil) | |
2123 (idlwave-shell-bp-query)) | |
2124 (let ((msg (format "No such file %s" | |
2125 idlwave-shell-last-save-and-action-file))) | |
2126 (setq idlwave-shell-last-save-and-action-file nil) | |
2127 (error msg)))) | |
2128 | |
2129 (defvar idlwave-shell-sources-query "help,/source" | |
2130 "IDL command to obtain source files for compiled procedures.") | |
2131 | |
2132 (defvar idlwave-shell-sources-alist nil | |
2133 "Alist of IDL procedure names and compiled source files. | |
2134 Elements of the alist have the form: | |
2135 | |
2136 (module name . (source-file-truename idlwave-internal-filename)).") | |
2137 | |
2138 (defun idlwave-shell-sources-query () | |
2139 "Determine source files for IDL compiled procedures. | |
2140 Queries IDL using the string in `idlwave-shell-sources-query'." | |
2141 (interactive) | |
2142 (idlwave-shell-send-command idlwave-shell-sources-query | |
2143 'idlwave-shell-sources-filter | |
2144 'hide)) | |
2145 | |
2146 (defun idlwave-shell-sources-filter () | |
2147 "Get source files from `idlwave-shell-sources-query' output. | |
2148 Create `idlwave-shell-sources-alist' consisting of | |
2149 list elements of the form: | |
2150 (module name . (source-file-truename idlwave-internal-filename))." | |
2151 (save-excursion | |
2152 (set-buffer (get-buffer-create idlwave-shell-bp-buffer)) | |
2153 (erase-buffer) | |
2154 (insert idlwave-shell-command-output) | |
2155 (goto-char (point-min)) | |
2156 (let (cpro cfun) | |
2157 (if (re-search-forward "Compiled Procedures:" nil t) | |
2158 (progn | |
2159 (forward-line) ; Skip $MAIN$ | |
2160 (setq cpro (point)))) | |
2161 (if (re-search-forward "Compiled Functions:" nil t) | |
2162 (progn | |
2163 (setq cfun (point)) | |
2164 (setq idlwave-shell-sources-alist | |
2165 (append | |
2166 ;; compiled procedures | |
2167 (progn | |
2168 (beginning-of-line) | |
2169 (narrow-to-region cpro (point)) | |
2170 (goto-char (point-min)) | |
2171 (idlwave-shell-sources-grep)) | |
2172 ;; compiled functions | |
2173 (progn | |
2174 (widen) | |
2175 (goto-char cfun) | |
2176 (idlwave-shell-sources-grep))))))))) | |
2177 | |
2178 (defun idlwave-shell-sources-grep () | |
2179 (save-excursion | |
2180 (let ((al (list nil))) | |
2181 (while (and | |
2182 (not (progn (forward-line) (eobp))) | |
2183 (re-search-forward | |
2184 "\\s-*\\(\\S-+\\)\\s-+\\(\\S-+\\)" nil t)) | |
2185 (nconc al | |
2186 (list | |
2187 (cons | |
2188 (buffer-substring ; name | |
2189 (match-beginning 1) (match-end 1)) | |
2190 (let ((internal-filename | |
2191 (buffer-substring ; source | |
2192 (match-beginning 2) (match-end 2)))) | |
2193 (list | |
2194 (idlwave-shell-file-name internal-filename) | |
2195 internal-filename)) | |
2196 )))) | |
2197 (cdr al)))) | |
2198 | |
2199 | |
2200 (defun idlwave-shell-clear-all-bp () | |
2201 "Remove all breakpoints in IDL." | |
2202 (interactive) | |
2203 (idlwave-shell-send-command | |
2204 idlwave-shell-bp-query | |
2205 '(progn | |
2206 (idlwave-shell-filter-bp) | |
2207 (mapcar 'idlwave-shell-clear-bp idlwave-shell-bp-alist)) | |
2208 'hide)) | |
2209 | |
2210 (defun idlwave-shell-list-all-bp () | |
2211 "List all breakpoints in IDL." | |
2212 (interactive) | |
2213 (idlwave-shell-send-command | |
2214 idlwave-shell-bp-query)) | |
2215 | |
2216 (defvar idlwave-shell-error-last 0 | |
2217 "Position of last syntax error in `idlwave-shell-error-buffer'.") | |
2218 | |
2219 (defun idlwave-shell-goto-next-error () | |
2220 "Move point to next IDL syntax error." | |
2221 (interactive) | |
2222 (let (frame col) | |
2223 (save-excursion | |
2224 (set-buffer idlwave-shell-error-buffer) | |
2225 (goto-char idlwave-shell-error-last) | |
2226 (if (or (re-search-forward idlwave-shell-syntax-error nil t) | |
2227 (re-search-forward idlwave-shell-other-error nil t)) | |
2228 (progn | |
2229 (setq frame | |
2230 (list | |
2231 (save-match-data | |
2232 (idlwave-shell-file-name | |
2233 (buffer-substring (match-beginning 1) (match-end 1)))) | |
2234 (string-to-int | |
2235 (buffer-substring (match-beginning 2) | |
2236 (match-end 2))))) | |
2237 ;; Try to find the column of the error | |
2238 (save-excursion | |
2239 (setq col | |
2240 (if (re-search-backward "\\^" nil t) | |
2241 (current-column) | |
2242 0))))) | |
2243 (setq idlwave-shell-error-last (point))) | |
2244 (if frame | |
2245 (progn | |
2246 (idlwave-shell-display-line frame col)) | |
2247 (beep) | |
2248 (message "No more errors.")))) | |
2249 | |
2250 (defun idlwave-shell-file-name (name) | |
2251 "If idlwave-shell-use-truename is non-nil, convert file name to true name. | |
2252 Otherwise, just expand the file name." | |
2253 (let ((def-dir (if (eq major-mode 'idlwave-shell-mode) | |
2254 default-directory | |
2255 idlwave-shell-default-directory))) | |
2256 (if idlwave-shell-use-truename | |
2257 (file-truename name def-dir) | |
2258 (expand-file-name name def-dir)))) | |
2259 | |
2260 | |
2261 ;; Keybindings -------------------------------------------------------------- | |
2262 | |
2263 (defvar idlwave-shell-mode-map (copy-keymap comint-mode-map) | |
2264 "Keymap for idlwave-mode.") | |
2265 (defvar idlwave-shell-mode-prefix-map (make-sparse-keymap)) | |
2266 (fset 'idlwave-shell-mode-prefix-map idlwave-shell-mode-prefix-map) | |
2267 | |
2268 ;(define-key idlwave-shell-mode-map "\M-?" 'comint-dynamic-list-completions) | |
2269 ;(define-key idlwave-shell-mode-map "\t" 'comint-dynamic-complete) | |
2270 (define-key idlwave-shell-mode-map "\t" 'idlwave-shell-complete) | |
2271 (define-key idlwave-shell-mode-map "\M-\t" 'idlwave-shell-complete) | |
2272 (define-key idlwave-shell-mode-map "\C-c\C-s" 'idlwave-shell) | |
2273 (define-key idlwave-shell-mode-map "\C-c?" 'idlwave-routine-info) | |
2274 (define-key idlwave-shell-mode-map "\C-c\C-i" 'idlwave-update-routine-info) | |
2275 (define-key idlwave-shell-mode-map "\C-c=" 'idlwave-resolve) | |
2276 (define-key idlwave-shell-mode-map "\C-c\C-v" 'idlwave-find-module) | |
2277 (define-key idlwave-shell-mode-map idlwave-shell-prefix-key | |
2278 'idlwave-shell-debug-map) | |
2279 | |
2280 ;; The following set of bindings is used to bind the debugging keys. | |
2281 ;; If `idlwave-shell-activate-prefix-keybindings' is non-nil, the first key | |
2282 ;; in the list gets bound the C-c C-d prefix map. | |
2283 ;; If `idlwave-shell-activate-alt-keybindings' is non-nil, the second key | |
2284 ;; in the list gets bound directly in both idlwave-mode-map and | |
2285 ;; idlwave-shell-mode-map. | |
2286 | |
2287 ;; Used keys: abcde hi klmnopqrs u wxyz | |
2288 ;; Unused keys: fg j t v | |
2289 (let ((specs | |
2290 '(([(control ?b)] [(alt ?b)] idlwave-shell-break-here) | |
2291 ([(control ?i)] [(alt ?i)] idlwave-shell-break-in) | |
2292 ([(control ?d)] [(alt ?d)] idlwave-shell-clear-current-bp) | |
2293 ([(control ?a)] [(alt ?a)] idlwave-shell-clear-all-bp) | |
2294 ([(control ?s)] [(alt ?s)] idlwave-shell-step) | |
2295 ([(control ?n)] [(alt ?n)] idlwave-shell-stepover) | |
2296 ([(control ?k)] [(alt ?k)] idlwave-shell-skip) | |
2297 ([(control ?u)] [(alt ?u)] idlwave-shell-up) | |
2298 ([(control ?o)] [(alt ?o)] idlwave-shell-out) | |
2299 ([(control ?m)] [(alt ?m)] idlwave-shell-return) | |
2300 ([(control ?h)] [(alt ?h)] idlwave-shell-to-here) | |
2301 ([(control ?r)] [(alt ?r)] idlwave-shell-cont) | |
2302 ([(control ?y)] [(alt ?y)] idlwave-shell-execute-default-command-line) | |
2303 ([(control ?z)] [(alt ?z)] idlwave-shell-reset) | |
2304 ([(control ?q)] [(alt ?q)] idlwave-shell-quit) | |
2305 ([(control ?p)] [(alt ?p)] idlwave-shell-print) | |
2306 ([(??)] [(alt ??)] idlwave-shell-help-expression) | |
2307 ([(control ?c)] [(alt ?c)] idlwave-shell-save-and-run) | |
2308 ([( ?@)] [(alt ?@)] idlwave-shell-save-and-batch) | |
2309 ([(control ?x)] [(alt ?x)] idlwave-shell-goto-next-error) | |
2310 ([(control ?e)] [(alt ?e)] idlwave-shell-run-region) | |
2311 ([(control ?w)] [(alt ?w)] idlwave-shell-resync-dirs) | |
2312 ([(control ?l)] [(alt ?l)] idlwave-shell-redisplay) | |
2313 ([(control ?t)] [(alt ?t)] idlwave-shell-toggle-toolbar) | |
2314 ([(control up)] [(alt up)] idlwave-shell-stack-up) | |
2315 ([(control down)] [(alt down)] idlwave-shell-stack-down))) | |
2316 s k1 k2 cmd) | |
2317 (while (setq s (pop specs)) | |
2318 (setq k1 (nth 0 s) | |
2319 k2 (nth 1 s) | |
2320 cmd (nth 2 s)) | |
2321 (when idlwave-shell-activate-prefix-keybindings | |
2322 (and k1 (define-key idlwave-shell-mode-prefix-map k1 cmd))) | |
2323 (when idlwave-shell-activate-alt-keybindings | |
2324 (and k2 (define-key idlwave-mode-map k2 cmd)) | |
2325 (and k2 (define-key idlwave-shell-mode-map k2 cmd))))) | |
2326 | |
2327 ;; Enter the prefix map at the two places. | |
2328 (fset 'idlwave-debug-map idlwave-shell-mode-prefix-map) | |
2329 (fset 'idlwave-shell-debug-map idlwave-shell-mode-prefix-map) | |
2330 | |
2331 ;; The Menus -------------------------------------------------------------- | |
2332 | |
2333 (defvar idlwave-shell-menu-def | |
2334 '("Debug" | |
2335 ["Save and .RUN" idlwave-shell-save-and-run | |
2336 (or (eq major-mode 'idlwave-mode) | |
2337 idlwave-shell-last-save-and-action-file)] | |
2338 ["Save and .COMPILE" idlwave-shell-save-and-compile | |
2339 (or (eq major-mode 'idlwave-mode) | |
2340 idlwave-shell-last-save-and-action-file)] | |
2341 ["Save and @Batch" idlwave-shell-save-and-batch | |
2342 (or (eq major-mode 'idlwave-mode) | |
2343 idlwave-shell-last-save-and-action-file)] | |
2344 ["Goto Next Error" idlwave-shell-goto-next-error t] | |
2345 "--" | |
2346 ["Execute Default Cmd" idlwave-shell-execute-default-command-line t] | |
2347 ["Edit Default Cmd" idlwave-shell-edit-default-command-line t] | |
2348 "--" | |
2349 ["Set Breakpoint" idlwave-shell-break-here | |
2350 (eq major-mode 'idlwave-mode)] | |
2351 ["Break in Module" idlwave-shell-break-in t] | |
2352 ["Clear Breakpoint" idlwave-shell-clear-current-bp t] | |
2353 ["Clear All Breakpoints" idlwave-shell-clear-all-bp t] | |
2354 ["List All Breakpoints" idlwave-shell-list-all-bp t] | |
2355 "--" | |
2356 ["Step (into)" idlwave-shell-step t] | |
2357 ["Step (over)" idlwave-shell-stepover t] | |
2358 ["Skip One Statement" idlwave-shell-skip t] | |
2359 ["Continue" idlwave-shell-cont t] | |
2360 ("Continue to" | |
2361 ["End of Block" idlwave-shell-up t] | |
2362 ["End of Subprog" idlwave-shell-return t] | |
2363 ["End of Subprog+1" idlwave-shell-out t] | |
2364 ["Here (Cursor Line)" idlwave-shell-to-here | |
2365 (eq major-mode 'idlwave-mode)]) | |
2366 "--" | |
2367 ["Print expression" idlwave-shell-print t] | |
2368 ["Help on expression" idlwave-shell-help-expression t] | |
2369 ["Evaluate Region" idlwave-shell-evaluate-region | |
2370 (eq major-mode 'idlwave-mode)] | |
2371 ["Run Region" idlwave-shell-run-region (eq major-mode 'idlwave-mode)] | |
2372 "--" | |
2373 ["Redisplay" idlwave-shell-redisplay t] | |
2374 ["Stack Up" idlwave-shell-stack-up t] | |
2375 ["Stack Down" idlwave-shell-stack-down t] | |
2376 "--" | |
2377 ["Update Working Dir" idlwave-shell-resync-dirs t] | |
2378 ["Reset IDL" idlwave-shell-reset t] | |
2379 "--" | |
2380 ["Toggle Toolbar" idlwave-shell-toggle-toolbar t] | |
2381 ["Exit IDL" idlwave-shell-quit t])) | |
2382 | |
2383 (if (or (featurep 'easymenu) (load "easymenu" t)) | |
2384 (progn | |
2385 (easy-menu-define | |
2386 idlwave-shell-mode-menu idlwave-shell-mode-map "IDL shell menus" | |
2387 idlwave-shell-menu-def) | |
2388 (easy-menu-define | |
2389 idlwave-mode-debug-menu idlwave-mode-map "IDL debugging menus" | |
2390 idlwave-shell-menu-def) | |
2391 (save-excursion | |
2392 (mapcar (lambda (buf) | |
2393 (set-buffer buf) | |
2394 (if (eq major-mode 'idlwave-mode) | |
2395 (progn | |
2396 (easy-menu-remove idlwave-mode-debug-menu) | |
2397 (easy-menu-add idlwave-mode-debug-menu)))) | |
2398 (buffer-list))))) | |
2399 | |
2400 ;; The Breakpoint Glyph ------------------------------------------------------- | |
2401 | |
2402 (defvar idlwave-shell-bp-glyph nil | |
2403 "The glyph to mark breakpoint lines in the source code.") | |
2404 | |
2405 (let ((image-string "/* XPM */ | |
2406 static char * file[] = { | |
2407 \"14 12 3 1\", | |
2408 \" c #FFFFFFFFFFFF s backgroundColor\", | |
2409 \". c #4B4B4B4B4B4B\", | |
2410 \"R c #FFFF00000000\", | |
2411 \" \", | |
2412 \" \", | |
2413 \" RRRR \", | |
2414 \" RRRRRR \", | |
2415 \" RRRRRRRR \", | |
2416 \" RRRRRRRR \", | |
2417 \" RRRRRRRR \", | |
2418 \" RRRRRRRR \", | |
2419 \" RRRRRR \", | |
2420 \" RRRR \", | |
2421 \" \", | |
2422 \" \"};")) | |
2423 | |
2424 (setq idlwave-shell-bp-glyph | |
2425 (cond ((and (featurep 'xemacs) | |
2426 (featurep 'xpm)) | |
2427 (make-glyph image-string)) | |
2428 ((and (not (featurep 'xemacs)) | |
2429 (fboundp 'image-type-available-p) | |
2430 (image-type-available-p 'xpm)) | |
2431 (list 'image :type 'xpm :data image-string)) | |
2432 (t nil)))) | |
2433 | |
2434 (provide 'idlwave-shell) | |
2435 | |
2436 ;;; Load the toolbar when wanted by the user. | |
2437 | |
2438 (defun idlwave-shell-toggle-toolbar () | |
2439 "Toggle the display of the debugging toolbar." | |
2440 (interactive) | |
2441 (if (featurep 'idlwave-toolbar) | |
2442 (idlwave-toolbar-toggle) | |
2443 (require 'idlwave-toolbar) | |
2444 (idlwave-toolbar-toggle))) | |
2445 | |
2446 | |
2447 (when idlwave-shell-use-toolbar | |
2448 (or (load "idlwave-toolbar" t) | |
2449 (message | |
2450 "Tried to load file `idlwave-toolbar.el', but file does not exist"))) | |
2451 | |
2452 ;;; idlwave-shell.el ends here | |
2453 | |
2454 |