--- /dev/null
+;;; idlwave-shell.el --- Run IDL or WAVE as an inferior process of Emacs.
+;; Copyright (c) 1994-1996 Chris Chase
+;; Copyright (c) 1999 Carsten Dominik
+;; Copyright (c) 1999 Free Software Foundation
+
+;; Author: Chris Chase <chase@att.com>
+;; Maintainer: Carsten Dominik <dominik@strw.leidenuniv.nl>
+;; Version: 3.11
+;; Date: $Date: 1999/12/06 08:13:16 $
+;; Keywords: processes
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; This mode is for IDL version 4 or later. It should work on Emacs
+;; or XEmacs version 19 or later.
+
+;; Runs IDL as an inferior process of Emacs, much like the emacs
+;; `shell' or `telnet' commands. Provides command history and
+;; searching. Provides debugging commands available in buffers
+;; visiting IDL procedure files, e.g., breakpoint setting, stepping,
+;; execution until a certain line, printing expressions under point,
+;; visual line pointer for current execution line, etc.
+;;
+;; Documentation should be available online with `M-x idlwave-info'.
+
+;; INSTALLATION:
+;; =============
+;;
+;; Follow the instructions in the INSTALL file of the distribution.
+;; In short, put this file on your load path and add the following
+;; lines to your .emacs file:
+;;
+;; (autoload 'idlwave-shell "idlwave-shell" "IDLWAVE Shell" t)
+;;
+;;
+;; SOURCE
+;; ======
+;;
+;; The newest version of this file can be found on the maintainers
+;; web site.
+;;
+;; http://www.strw.leidenuniv.el/~dominik/Tools/idlwave
+;;
+;; DOCUMENTATION
+;; =============
+;;
+;; IDLWAVE is documented online in info format.
+;; A printable version of the documentation is available from the
+;; maintainers webpage (see under SOURCE)
+;;
+;;
+;; KNOWN PROBLEMS
+;; ==============
+;;
+;; The idlwave-shell buffer seems to occasionally lose output from the IDL
+;; process. I have not been able to consistently observe this. I
+;; do not know if it is a problem with idlwave-shell, comint, or Emacs
+;; handling of subprocesses.
+;;
+;; I don't plan on implementing directory tracking by watching the IDL
+;; commands entered at the prompt, since too often an IDL procedure
+;; will change the current directory. If you want the the idl process
+;; buffer to match the IDL current working just execute `M-x
+;; idlwave-shell-resync-dirs' (bound to "\C-c\C-d\C-w" by default.)
+;;
+;; The stack motion commands `idlwave-shell-stack-up' and
+;; `idlwave-shell-stack-down' only display the calling frame but
+;; cannot show the values of variables in higher frames correctly. I
+;; just don't know how to get these values from IDL. Anyone knows the
+;; magic word to do this?
+;; Also, the stack pointer stays at the level where is was and is not
+;; reset correctly when you type executive commands in the shell buffer
+;; yourself. However, using the executive commands bound to key sequences
+;; does the reset correctly. As a workaround, just jump down when needed.
+;;
+;; Under XEmacs the Debug menu in the shell does not display the
+;; keybindings in the prefix map. There bindings are available anyway - so
+;; it is a bug in XEmacs.
+;; The Debug menu in source buffers does display the bindings correctly.
+;;
+;;
+;; CUSTOMIZATION VARIABLES
+;; =======================
+;;
+;; IDLWAVE has customize support - so if you want to learn about
+;; the variables which control the behavior of the mode, use
+;; `M-x idlwave-customize'.
+;;
+;;--------------------------------------------------------------------------
+;;
+;;
+\f
+;;; Code:
+
+(require 'comint)
+(require 'idlwave)
+
+(eval-when-compile (require 'cl))
+
+(defvar idlwave-shell-have-new-custom nil)
+(eval-and-compile
+ ;; Kludge to allow `defcustom' for Emacs 19.
+ (condition-case () (require 'custom) (error nil))
+ (if (and (featurep 'custom)
+ (fboundp 'custom-declare-variable)
+ (fboundp 'defface))
+ ;; We've got what we needed
+ (setq idlwave-shell-have-new-custom t)
+ ;; We have the old or no custom-library, hack around it!
+ (defmacro defgroup (&rest args) nil)
+ (defmacro defcustom (var value doc &rest args)
+ (` (defvar (, var) (, value) (, doc))))))
+
+;;; Customizations: idlwave-shell group
+
+(defgroup idlwave-shell-general-setup nil
+ "Indentation options for IDL/WAVE mode."
+ :prefix "idlwave"
+ :group 'idlwave)
+
+(defcustom idlwave-shell-prompt-pattern "^ ?IDL> "
+ "*Regexp to match IDL prompt at beginning of a line.
+For example, \"^IDL> \" or \"^WAVE> \".
+The \"^\" means beginning of line.
+This variable is used to initialise `comint-prompt-regexp' in the
+process buffer.
+
+This is a fine thing to set in your `.emacs' file."
+ :group 'idlwave-shell-general-setup
+ :type 'regexp)
+
+(defcustom idlwave-shell-process-name "idl"
+ "*Name to be associated with the IDL process. The buffer for the
+process output is made by surrounding this name with `*'s."
+ :group 'idlwave-shell-general-setup
+ :type 'string)
+
+(defcustom idlwave-shell-automatic-start nil
+ "*If non-nil attempt invoke idlwave-shell if not already running.
+This is checked when an attempt to send a command to an
+IDL process is made."
+ :group 'idlwave-shell-general-setup
+ :type 'boolean)
+
+(defcustom idlwave-shell-initial-commands "!more=0"
+ "Initial commands, separated by newlines, to send to IDL.
+This string is sent to the IDL process by `idlwave-shell-mode' which is
+invoked by `idlwave-shell'."
+ :group 'idlwave-shell-initial-commands
+ :type 'string)
+
+(defcustom idlwave-shell-use-dedicated-frame nil
+ "*Non-nil means, IDLWAVE should use a special frame to display shell buffer."
+ :group 'idlwave-shell-general-setup
+ :type 'boolean)
+
+(defcustom idlwave-shell-frame-parameters
+ '((height . 30) (unsplittable . nil))
+ "The frame parameters for a dedicated idlwave-shell frame.
+See also `idlwave-shell-use-dedicated-frame'.
+The default makes the frame splittable, so that completion works correctly."
+ :group 'idlwave-shell-general-setup
+ :type '(repeat
+ (cons symbol sexp)))
+
+(defcustom idlwave-shell-use-toolbar t
+ "Non-nil means, use the debugging toolbar in all IDL related buffers.
+Available on XEmacs and on Emacs 21.x or later.
+Needs to be set at load-time, so don't try to do this in the hook."
+ :group 'idlwave-shell-general-setup
+ :type 'boolean)
+
+(defcustom idlwave-shell-temp-pro-prefix "/tmp/idltemp"
+ "*The prefix for temporary IDL files used when compiling regions.
+It should be an absolute pathname.
+The full temporary file name is obtained by to using `make-temp-name'
+so that the name will be unique among multiple Emacs processes."
+ :group 'idlwave-shell-general-setup
+ :type 'string)
+
+(defvar idlwave-shell-fix-inserted-breaks nil
+ "*OBSOLETE VARIABLE, is no longer used.
+
+The documentation of this variable used to be:
+If non-nil then run `idlwave-shell-remove-breaks' to clean up IDL messages.")
+
+(defcustom idlwave-shell-prefix-key "\C-c\C-d"
+ "*The prefix key for the debugging map `idlwave-shell-mode-prefix-map'.
+This variable must already be set when idlwave-shell.el is loaded.
+Seting it in the mode-hook is too late."
+ :group 'idlwave-shell-general-setup
+ :type 'string)
+
+(defcustom idlwave-shell-activate-prefix-keybindings t
+ "Non-nil means, the debug commands will be bound to the prefix key.
+The prefix key itself is given in the option `idlwave-shell-prefix-key'.
+So by default setting a breakpoint will be on C-c C-d C-b."
+ :group 'idlwave-shell-general-setup
+ :type 'boolean)
+
+(defcustom idlwave-shell-activate-alt-keybindings nil
+ "Non-nil means, the debug commands will be bound to alternate keys.
+So for example setting a breakpoint will be on A-b."
+ :group 'idlwave-shell-general-setup
+ :type 'boolean)
+
+(defcustom idlwave-shell-use-truename nil
+ "*Non-nil means, use use `file-truename' when looking for buffers.
+If this variable is non-nil, Emacs will use the function `file-truename' to
+resolve symbolic links in the file paths printed by e.g., STOP commands.
+This means, unvisited files will be loaded under their truename.
+However, when a file is already visited under a deffernet name, IDLWAVE will
+reuse that buffer.
+This option was once introduced in order to avoid multiple buffers visiting
+the same file. However, IDLWAVE no longer makes this mistake, so it is safe
+to set this option to nil."
+ :group 'idlwave-shell-general-setup
+ :type 'boolean)
+
+(defcustom idlwave-shell-file-name-chars "~/A-Za-z0-9+@:_.$#%={}-"
+ "The characters allowed in file names, as a string.
+Used for file name completion. Must not contain `'', `,' and `\"'
+because these are used as separators by IDL."
+ :group 'idlwave-shell-general-setup
+ :type 'string)
+
+(defcustom idlwave-shell-mode-hook '()
+ "*Hook for customising `idlwave-shell-mode'."
+ :group 'idlwave-shell-general-setup
+ :type 'hook)
+
+;;; Breakpoint Overlays etc
+
+(defgroup idlwave-shell-highlighting-and-faces nil
+ "Indentation options for IDL/WAVE mode."
+ :prefix "idlwave"
+ :group 'idlwave)
+
+(defcustom idlwave-shell-mark-stop-line t
+ "*Non-nil means, mark the source code line where IDL is currently stopped.
+Value decides about the method which is used to mark the line. Legal values
+are:
+
+nil Do not mark the line
+'arrow Use the overlay arrow
+'face Use `idlwave-shell-stop-line-face' to highlight the line.
+t Use what IDLWAVE things is best. Will be a face where possible,
+ otherwise the overlay arrow.
+The overlay-arrow has the disadvantage to hide the first chars of a line.
+Since many people do not have the main block of IDL programs indented,
+a face highlighting may be better.
+On Emacs 21, the overlay arrow is displayed in a special area and never
+hides any code, so setting this to 'arrow on Emacs 21 sounds like a good idea."
+ :group 'idlwave-shell-highlighting-and-faces
+ :type '(choice
+ (const :tag "No marking" nil)
+ (const :tag "Use overlay arrow" arrow)
+ (const :tag "Highlight with face" face)
+ (const :tag "Face or arrow." t)))
+
+(defcustom idlwave-shell-overlay-arrow ">"
+ "*The overlay arrow to display at source lines where execution halts.
+We use a single character by default, since the main block of IDL procedures
+often has no indentation. Where possible, IDLWAVE will use overlays to
+display the stop-lines. The arrow is only used on character-based terminals.
+See also `idlwave-shell-use-overlay-arrow'."
+ :group 'idlwave-shell-highlighting-and-faces
+ :type 'string)
+
+(defcustom idlwave-shell-stop-line-face 'highlight
+ "*The face for `idlwave-shell-stop-line-overlay'.
+Allows you to choose the font, color and other properties for
+line where IDL is stopped. See also `idlwave-shell-mark-stop-line'."
+ :group 'idlwave-shell-highlighting-and-faces
+ :type 'symbol)
+
+(defcustom idlwave-shell-expression-face 'secondary-selection
+ "*The face for `idlwave-shell-expression-overlay'.
+Allows you to choose the font, color and other properties for
+the expression printed by IDL."
+ :group 'idlwave-shell-highlighting-and-faces
+ :type 'symbol)
+
+(defcustom idlwave-shell-mark-breakpoints t
+ "*Non-nil means, mark breakpoints in the source files.
+Legal values are:
+nil Do not mark breakpoints.
+'face Highlight line with `idlwave-shell-breakpoint-face'.
+'glyph Red dot at the beginning of line. If the display does not
+ support glyphs, will use 'face instead.
+t Glyph when possible, otherwise face (same effect as 'glyph)."
+ :group 'idlwave-shell-highlighting-and-faces
+ :type '(choice
+ (const :tag "No marking" nil)
+ (const :tag "Highlight with face" face)
+ (const :tag "Display glyph (red dot)" glyph)
+ (const :tag "Glyph or face." t)))
+
+(defvar idlwave-shell-use-breakpoint-glyph t
+ "Obsolete variable. See `idlwave-shell-mark-breakpoints.")
+
+(defcustom idlwave-shell-breakpoint-face 'idlwave-shell-bp-face
+ "*The face for breakpoint lines in the source code.
+Allows you to choose the font, color and other properties for
+lines which have a breakpoint. See also `idlwave-shell-mark-breakpoints'."
+ :group 'idlwave-shell-highlighting-and-faces
+ :type 'symbol)
+
+(if idlwave-shell-have-new-custom
+ ;; We have the new customize - use it to define a customizable face
+ (defface idlwave-shell-bp-face
+ '((((class color)) (:foreground "Black" :background "Pink"))
+ (t (:underline t)))
+ "Face for highlighting lines-with-breakpoints."
+ :group 'idlwave-shell-highlighting-and-faces)
+ ;; Just copy the underline face to be on the safe side.
+ (copy-face 'underline 'idlwave-shell-bp-face))
+
+;;; End user customization variables
+
+;;; External variables
+(defvar comint-last-input-start)
+(defvar comint-last-input-end)
+
+;; Other variables
+
+(defvar idlwave-shell-temp-pro-file nil
+ "Absolute pathname for temporary IDL file for compiling regions")
+
+(defvar idlwave-shell-dirstack-query "printd"
+ "Command used by `idlwave-shell-resync-dirs' to query IDL for
+the directory stack.")
+
+(defvar idlwave-shell-default-directory nil
+ "The default directory in the idlwave-shell buffer, of outside use.")
+
+(defvar idlwave-shell-last-save-and-action-file nil
+ "The last file which was compiled with `idlwave-shell-save-and-...'.")
+
+;; Highlighting uses overlays. When necessary, require the emulation.
+(if (not (fboundp 'make-overlay))
+ (condition-case nil
+ (require 'overlay)
+ (error nil)))
+
+(defvar idlwave-shell-stop-line-overlay nil
+ "The overlay for where IDL is currently stopped.")
+(defvar idlwave-shell-expression-overlay nil
+ "The overlay for where IDL is currently stopped.")
+;; If these were already overlays, delete them. This probably means that we
+;; are reloading this file.
+(if (overlayp idlwave-shell-stop-line-overlay)
+ (delete-overlay idlwave-shell-stop-line-overlay))
+(if (overlayp idlwave-shell-expression-overlay)
+ (delete-overlay idlwave-shell-expression-overlay))
+;; Set to nil initially
+(setq idlwave-shell-stop-line-overlay nil
+ idlwave-shell-expression-overlay nil)
+
+;; Define the shell stop overlay. When left nil, the arrow will be used.
+(cond
+ ((or (null idlwave-shell-mark-stop-line)
+ (eq idlwave-shell-mark-stop-line 'arrow))
+ ;; Leave the overlay nil
+ nil)
+
+ ((eq idlwave-shell-mark-stop-line 'face)
+ ;; Try to use a face. If not possible, arrow will be used anyway
+ ;; So who can display faces?
+ (when (or (featurep 'xemacs) ; XEmacs can do also ttys
+ (fboundp 'tty-defined-colors) ; Emacs 21 as well
+ window-system) ; Window systems always
+ (progn
+ (setq idlwave-shell-stop-line-overlay (make-overlay 1 1))
+ (overlay-put idlwave-shell-stop-line-overlay
+ 'face idlwave-shell-stop-line-face))))
+
+ (t
+ ;; IDLWAVE may decide. Will use a face on window systems, arrow elsewhere
+ (if window-system
+ (progn
+ (setq idlwave-shell-stop-line-overlay (make-overlay 1 1))
+ (overlay-put idlwave-shell-stop-line-overlay
+ 'face idlwave-shell-stop-line-face)))))
+
+;; Now the expression overlay
+(setq idlwave-shell-expression-overlay (make-overlay 1 1))
+(overlay-put idlwave-shell-expression-overlay
+ 'face idlwave-shell-expression-face)
+
+(defvar idlwave-shell-bp-query "help,/breakpoints"
+ "Command to obtain list of breakpoints")
+
+(defvar idlwave-shell-command-output nil
+ "String for accumulating current command output.")
+
+(defvar idlwave-shell-post-command-hook nil
+ "Lisp list expression or function to run when an IDL command is finished.
+The current command is finished when the IDL prompt is displayed.
+This is evaluated if it is a list or called with funcall.")
+
+(defvar idlwave-shell-hide-output nil
+ "If non-nil the process output is not inserted into the output
+ buffer.")
+
+(defvar idlwave-shell-accumulation nil
+ "Accumulate last line of output.")
+
+(defvar idlwave-shell-command-line-to-execute nil)
+(defvar idlwave-shell-cleanup-hook nil
+ "List of functions to do cleanup when the shell exits.")
+
+(defvar idlwave-shell-pending-commands nil
+ "List of commands to be sent to IDL.
+Each element of the list is list of \(CMD PCMD HIDE\), where CMD is a
+string to be sent to IDL and PCMD is a post-command to be placed on
+`idlwave-shell-post-command-hook'. If HIDE is non-nil, hide the output
+from command CMD. PCMD and HIDE are optional.")
+
+(defun idlwave-shell-buffer ()
+ "Name of buffer associated with IDL process.
+The name of the buffer is made by surrounding `idlwave-shell-process-name
+with `*'s."
+ (concat "*" idlwave-shell-process-name "*"))
+
+(defvar idlwave-shell-ready nil
+ "If non-nil can send next command to IDL process.")
+
+;;; The following are the types of messages we attempt to catch to
+;;; resync our idea of where IDL execution currently is.
+;;;
+
+(defvar idlwave-shell-halt-frame nil
+ "The frame associated with halt/breakpoint messages.")
+
+(defvar idlwave-shell-step-frame nil
+ "The frame associated with step messages.")
+
+(defvar idlwave-shell-trace-frame nil
+ "The frame associated with trace messages.")
+
+(defconst idlwave-shell-halt-messages
+ '("^% Execution halted at"
+ "^% Interrupted at:"
+ "^% Stepped to:"
+ "^% At "
+ "^% Stop encountered:"
+ )
+ "*A list of regular expressions matching IDL messages.
+These are the messages containing file and line information where
+IDL is currently stopped.")
+
+(defconst idlwave-shell-halt-messages-re
+ (mapconcat 'identity idlwave-shell-halt-messages "\\|")
+ "The regular expression computed from idlwave-shell-halt-messages")
+
+(defconst idlwave-shell-trace-messages
+ '("^% At " ;; First line of a trace message
+ )
+ "*A list of regular expressions matching IDL trace messages.
+These are the messages containing file and line information where
+IDL will begin looking for the next statement to execute.")
+
+(defconst idlwave-shell-step-messages
+ '("^% Stepped to:"
+ )
+ "*A list of regular expressions matching stepped execution messages.
+These are IDL messages containing file and line information where
+IDL has currently stepped.")
+
+(defvar idlwave-shell-break-message "^% Breakpoint at:"
+ "*Regular expression matching an IDL breakpoint message line.")
+
+
+(defvar idlwave-shell-bp-alist)
+;(defvar idlwave-shell-post-command-output)
+(defvar idlwave-shell-sources-alist)
+(defvar idlwave-shell-menu-def)
+(defvar idlwave-shell-mode-menu)
+(defvar idlwave-shell-initial-commands)
+(defvar idlwave-shell-syntax-error)
+(defvar idlwave-shell-other-error)
+(defvar idlwave-shell-error-buffer)
+(defvar idlwave-shell-error-last)
+(defvar idlwave-shell-bp-buffer)
+(defvar idlwave-shell-sources-query)
+(defvar idlwave-shell-mode-map)
+
+(defun idlwave-shell-mode ()
+ "Major mode for interacting with an inferior IDL process.
+
+1. Shell Interaction
+ -----------------
+ RET after the end of the process' output sends the text from the
+ end of process to the end of the current line. RET before end of
+ process output copies the current line (except for the prompt) to the
+ end of the buffer.
+
+ Command history, searching of previous commands, command line
+ editing are available via the comint-mode key bindings, by default
+ mostly on the key `C-c'.
+
+2. Completion
+ ----------
+
+ TAB and M-TAB do completion of IDL routines and keywords - similar
+ to M-TAB in `idlwave-mode'. In executive commands and strings,
+ it completes file names.
+
+3. Routine Info
+ ------------
+ `\\[idlwave-routine-info]' displays information about an IDL routine near point,
+ just like in `idlwave-mode'. The module used is the one at point or
+ the one whose argument list is being edited.
+ To update IDLWAVE's knowledge about compiled or edited modules, use
+ \\[idlwave-update-routine-info].
+ \\[idlwave-find-module] find the source of a module.
+ \\[idlwave-resolve] tells IDL to compile an unresolved module.
+
+4. Debugging
+ ---------
+ A complete set of commands for compiling and debugging IDL programs
+ is available from the menu. Also keybindings starting with a
+ `C-c C-d' prefix are available for most commands in the *idl* buffer
+ and also in source buffers. The best place to learn about the
+ keybindings is again the menu.
+
+ On Emacs versions where this is possible, a debugging toolbar is
+ installed.
+
+ When IDL is halted in the middle of a procedure, the corresponding
+ line of that procedure file is displayed with an overlay in another
+ window. Breakpoints are also highlighted in the source.
+
+ \\[idlwave-shell-resync-dirs] queries IDL in order to change Emacs current directory
+ to correspond to the IDL process current directory.
+
+5. Hooks
+ -----
+ Turning on `idlwave-shell-mode' runs `comint-mode-hook' and
+ `idlwave-shell-mode-hook' (in that order).
+
+6. Documentation and Customization
+ -------------------------------
+ Info documentation for this package is available. Use \\[idlwave-info]
+ to display (complain to your sysadmin if that does not work).
+ For Postscript and HTML versions of the documentation, check IDLWAVE's
+ homepage at `http://www.strw.leidenuniv.nl/~dominik/Tools/idlwave'.
+ IDLWAVE has customize support - see the group `idlwave'.
+
+7. Keybindings
+ -----------
+\\{idlwave-shell-mode-map}"
+
+ (interactive)
+ (setq comint-prompt-regexp idlwave-shell-prompt-pattern)
+ (setq comint-process-echoes t)
+ ;; Can not use history expansion because "!" is used for system variables.
+ (setq comint-input-autoexpand nil)
+ (setq comint-input-ring-size 64)
+ (make-local-variable 'comint-completion-addsuffix)
+ (set (make-local-variable 'completion-ignore-case) t)
+ (setq comint-completion-addsuffix '("/" . ""))
+ (setq comint-input-ignoredups t)
+ (setq major-mode 'idlwave-shell-mode)
+ (setq mode-name "IDL-Shell")
+ ;; (make-local-variable 'idlwave-shell-bp-alist)
+ (setq idlwave-shell-halt-frame nil
+ idlwave-shell-trace-frame nil
+ idlwave-shell-command-output nil
+ idlwave-shell-step-frame nil)
+ (idlwave-shell-display-line nil)
+ ;; Make sure comint-last-input-end does not go to beginning of
+ ;; buffer (in case there were other processes already in this buffer).
+ (set-marker comint-last-input-end (point))
+ (setq idlwave-shell-ready nil)
+ (setq idlwave-shell-bp-alist nil)
+ (idlwave-shell-update-bp-overlays) ; Throw away old overlays
+ (setq idlwave-shell-sources-alist nil)
+ (setq idlwave-shell-default-directory default-directory)
+ ;; (make-local-variable 'idlwave-shell-temp-pro-file)
+ (setq idlwave-shell-hide-output nil
+ idlwave-shell-temp-pro-file
+ (concat (make-temp-name idlwave-shell-temp-pro-prefix) ".pro"))
+ (make-local-hook 'kill-buffer-hook)
+ (add-hook 'kill-buffer-hook 'idlwave-shell-kill-shell-buffer-confirm
+ nil 'local)
+ (use-local-map idlwave-shell-mode-map)
+ (easy-menu-add idlwave-shell-mode-menu idlwave-shell-mode-map)
+ (run-hooks 'idlwave-shell-mode-hook)
+ (idlwave-shell-send-command idlwave-shell-initial-commands nil 'hide)
+ )
+
+(if (not (fboundp 'idl-shell))
+ (fset 'idl-shell 'idlwave-shell))
+
+(defvar idlwave-shell-idl-wframe nil
+ "Frame for displaying the idl shell window.")
+(defvar idlwave-shell-display-wframe nil
+ "Frame for displaying the idl source files.")
+
+(defvar idlwave-shell-last-calling-stack nil
+ "Caches the last calling stack, so that we can compare.")
+(defvar idlwave-shell-calling-stack-index 0)
+
+(defun idlwave-shell-source-frame ()
+ "Return the frame to be used for source display."
+ (if idlwave-shell-use-dedicated-frame
+ ;; We want separate frames for source and shell
+ (if (frame-live-p idlwave-shell-display-wframe)
+ ;; The frame exists, so we use it.
+ idlwave-shell-display-wframe
+ ;; The frame does not exist. We use the current frame.
+ ;; However, if the current is the shell frame, we make a new frame.
+ (setq idlwave-shell-display-wframe
+ (if (eq (selected-frame) idlwave-shell-idl-wframe)
+ (make-frame)
+ (selected-frame))))))
+
+(defun idlwave-shell-shell-frame ()
+ "Return the frame to be used for the shell buffer."
+ (if idlwave-shell-use-dedicated-frame
+ ;; We want a dedicated frame
+ (if (frame-live-p idlwave-shell-idl-wframe)
+ ;; It does exist, so we use it.
+ idlwave-shell-idl-wframe
+ ;; It does not exist. Check if we have a source frame.
+ (if (not (frame-live-p idlwave-shell-display-wframe))
+ ;; We do not have a source frame, so we use this one.
+ (setq idlwave-shell-display-wframe (selected-frame)))
+ ;; Return a new frame
+ (setq idlwave-shell-idl-wframe
+ (make-frame idlwave-shell-frame-parameters)))))
+
+;;;###autoload
+(defun idlwave-shell (&optional arg)
+ "Run an inferior IDL, with I/O through buffer `(idlwave-shell-buffer)'.
+If buffer exists but shell process is not running, start new IDL.
+If buffer exists and shell process is running, just switch to the buffer.
+
+When called with a prefix ARG, or when `idlwave-shell-use-dedicated-frame'
+is non-nil, the shell buffer and the source buffers will be in
+separate frames.
+
+The command to run comes from variable `idlwave-shell-explicit-file-name'.
+
+The buffer is put in `idlwave-shell-mode', providing commands for sending
+input and controlling the IDL job. See help on `idlwave-shell-mode'.
+See also the variable `idlwave-shell-prompt-pattern'.
+
+\(Type \\[describe-mode] in the shell buffer for a list of commands.)"
+ (interactive "P")
+
+ ;; A non-nil arg means, we want a dedicated frame. This will last
+ ;; for the current editing session.
+ (if arg (setq idlwave-shell-use-dedicated-frame t))
+ (if (equal arg '(16)) (setq idlwave-shell-use-dedicated-frame nil))
+
+ ;; Check if the process still exists. If not, create it.
+ (unless (comint-check-proc (idlwave-shell-buffer))
+ (let* ((prg (or idlwave-shell-explicit-file-name "idl"))
+ (buf (apply 'make-comint
+ idlwave-shell-process-name prg nil
+ idlwave-shell-command-line-options))
+ ;; FIXME: the next line can go?
+ ;(buf (make-comint idlwave-shell-process-name prg))
+ (process (get-buffer-process buf)))
+ (set-process-filter process 'idlwave-shell-filter)
+ (set-process-sentinel process 'idlwave-shell-sentinel)
+ (set-buffer buf)
+ (idlwave-shell-mode)))
+ (let ((window (idlwave-display-buffer (idlwave-shell-buffer) nil
+ (idlwave-shell-shell-frame)))
+ (current-window (selected-window)))
+ (select-window window)
+ (goto-char (point-max))
+ (select-window current-window)
+ (raise-frame (window-frame window))
+ (if (eq (selected-frame) (window-frame window))
+ (select-window window))
+ ))
+
+(defun idlwave-shell-recenter-shell-window (&optional arg)
+ "Run `idlwave-shell', but make sure the current window stays selected."
+ (interactive "P")
+ (let ((window (selected-window)))
+ (idlwave-shell arg)
+ (select-window window)))
+
+(defun idlwave-shell-send-command (&optional cmd pcmd hide preempt)
+ "Send a command to IDL process.
+
+\(CMD PCMD HIDE\) are placed at the end of `idlwave-shell-pending-commands'.
+If IDL is ready the first command, CMD, in
+`idlwave-shell-pending-commands' is sent to the IDL process. If optional
+second argument PCMD is non-nil it will be placed on
+`idlwave-shell-post-command-hook' when CMD is executed. If the optional
+third argument HIDE is non-nil, then hide output from CMD.
+If optional fourth argument PREEMPT is non-nil CMD is put at front of
+`idlwave-shell-pending-commands'.
+
+IDL is considered ready if the prompt is present
+and if `idlwave-shell-ready' is non-nil."
+
+ ;(setq hide nil) ; FIXME: turn this on for debugging only
+ (let (buf proc)
+ ;; Get or make the buffer and its process
+ (if (or (not (setq buf (get-buffer (idlwave-shell-buffer))))
+ (not (setq proc (get-buffer-process buf))))
+ (if (not idlwave-shell-automatic-start)
+ (error
+ (substitute-command-keys
+ "You need to first start an IDL shell with \\[idlwave-shell]"))
+ (idlwave-shell-recenter-shell-window)
+ (setq buf (get-buffer (idlwave-shell-buffer)))
+ (if (or (not (setq buf (get-buffer (idlwave-shell-buffer))))
+ (not (setq proc (get-buffer-process buf))))
+ ;; Still nothing
+ (error "Problem with autostarting IDL shell"))))
+
+ (save-excursion
+ (set-buffer buf)
+ (goto-char (process-mark proc))
+ ;; To make this easy, always push CMD onto pending commands
+ (if cmd
+ (setq idlwave-shell-pending-commands
+ (if preempt
+ ;; Put at front.
+ (append (list (list cmd pcmd hide))
+ idlwave-shell-pending-commands)
+ ;; Put at end.
+ (append idlwave-shell-pending-commands
+ (list (list cmd pcmd hide))))))
+ ;; Check if IDL ready
+ (if (and idlwave-shell-ready
+ ;; Check for IDL prompt
+ (save-excursion
+ (beginning-of-line)
+ (looking-at idlwave-shell-prompt-pattern)))
+ ;; IDL ready for command
+ (if idlwave-shell-pending-commands
+ ;; execute command
+ (let* ((lcmd (car idlwave-shell-pending-commands))
+ (cmd (car lcmd))
+ (pcmd (nth 1 lcmd))
+ (hide (nth 2 lcmd)))
+ ;; If this is an executive command, reset the stack pointer
+ (if (eq (string-to-char cmd) ?.)
+ (setq idlwave-shell-calling-stack-index 0))
+ ;; Set post-command
+ (setq idlwave-shell-post-command-hook pcmd)
+ ;; Output hiding
+;;; Debug code
+;;; (setq idlwave-shell-hide-output nil)
+ (setq idlwave-shell-hide-output hide)
+ ;; Pop command
+ (setq idlwave-shell-pending-commands
+ (cdr idlwave-shell-pending-commands))
+ ;; Send command for execution
+ (set-marker comint-last-input-start (point))
+ (set-marker comint-last-input-end (point))
+ (comint-simple-send proc cmd)
+ (setq idlwave-shell-ready nil)))))))
+
+;; There was a report that a newer version of comint.el changed the
+;; name of comint-filter to comint-output-filter. Unfortunately, we
+;; have yet to upgrade.
+
+(defun idlwave-shell-comint-filter (process string) nil)
+(if (fboundp 'comint-output-filter)
+ (fset 'idlwave-shell-comint-filter (symbol-function 'comint-output-filter))
+ (fset 'idlwave-shell-comint-filter (symbol-function 'comint-filter)))
+
+(defun idlwave-shell-is-running ()
+ "Return t if the shell process is running."
+ (eq (process-status idlwave-shell-process-name) 'run))
+
+(defun idlwave-shell-filter (proc string)
+ "Replace Carriage returns in output. Watch for prompt.
+When the IDL prompt is received executes `idlwave-shell-post-command-hook'
+and then calls `idlwave-shell-send-command' for any pending commands."
+ ;; We no longer do the cleanup here - this is done by the process sentinel
+ (when (eq (process-status idlwave-shell-process-name) 'run)
+ ;; OK, process is still running, so we can use it.
+ (let ((data (match-data)))
+ (unwind-protect
+ (progn
+ ;; May change the original match data.
+ (let (p)
+ (while (setq p (string-match "\C-M" string))
+ (aset string p ? )))
+;;; Test/Debug code
+;; (save-excursion (set-buffer (get-buffer-create "*test*"))
+;; (goto-char (point-max))
+;; (insert "%%%" string))
+ ;;
+ ;; Keep output
+
+; Should not keep output because the concat is costly. If hidden put
+; the output in a hide-buffer. Then when the output is needed in post
+; processing can access either the hide buffer or the idlwave-shell
+; buffer. Then watching for the prompt is easier. Furthermore, if it
+; is hidden and there is no post command, could throw away output.
+; (setq idlwave-shell-command-output
+; (concat idlwave-shell-command-output string))
+ ;; Insert the string. Do this before getting the
+ ;; state.
+ (if idlwave-shell-hide-output
+ (save-excursion
+ (set-buffer
+ (get-buffer-create "*idlwave-shell-hidden-output*"))
+ (goto-char (point-max))
+ (insert string))
+ (idlwave-shell-comint-filter proc string))
+ ;; Watch for prompt - need to accumulate the current line
+ ;; since it may not be sent all at once.
+ (if (string-match "\n" string)
+ (setq idlwave-shell-accumulation
+ (substring string
+ (progn (string-match "\\(.*\n\\)*" string)
+ (match-end 0))))
+ (setq idlwave-shell-accumulation
+ (concat idlwave-shell-accumulation string)))
+ ;; Check for prompt in current line
+ (if (setq idlwave-shell-ready
+ (string-match idlwave-shell-prompt-pattern
+ idlwave-shell-accumulation))
+ (progn
+ (if idlwave-shell-hide-output
+ (save-excursion
+ (set-buffer "*idlwave-shell-hidden-output*")
+ (goto-char (point-min))
+ (re-search-forward idlwave-shell-prompt-pattern nil t)
+ (setq idlwave-shell-command-output
+ (buffer-substring (point-min) (point)))
+ (delete-region (point-min) (point)))
+ (setq idlwave-shell-command-output
+ (save-excursion
+ (set-buffer
+ (process-buffer proc))
+ (buffer-substring
+ (progn
+ (goto-char (process-mark proc))
+ (beginning-of-line nil)
+ (point))
+ comint-last-input-end))))
+;;; Test/Debug code
+;; (save-excursion (set-buffer
+;; (get-buffer-create "*idlwave-shell-output*"))
+;; (goto-char (point-max))
+;; (insert "%%%" string))
+ ;; Scan for state and do post command - bracket them
+ ;; with idlwave-shell-ready=nil since they
+ ;; may call idlwave-shell-send-command.
+ (let ((idlwave-shell-ready nil))
+ (idlwave-shell-scan-for-state)
+ ;; Unset idlwave-shell-ready to prevent sending
+ ;; commands to IDL while running hook.
+ (if (listp idlwave-shell-post-command-hook)
+ (eval idlwave-shell-post-command-hook)
+ (funcall idlwave-shell-post-command-hook))
+ ;; Reset to default state for next command.
+ ;; Also we do not want to find this prompt again.
+ (setq idlwave-shell-accumulation nil
+ idlwave-shell-command-output nil
+ idlwave-shell-post-command-hook nil
+ idlwave-shell-hide-output nil))
+ ;; Done with post command. Do pending command if
+ ;; any.
+ (idlwave-shell-send-command))))
+ (store-match-data data)))))
+
+(defun idlwave-shell-sentinel (process event)
+ "The sentinel function for the IDLWAVE shell process."
+ (let* ((buf (idlwave-shell-buffer))
+ (win (get-buffer-window buf)))
+ (when (get-buffer buf)
+ (save-excursion
+ (set-buffer (idlwave-shell-buffer))
+ (goto-char (point-max))
+ (insert (format "\n\n Process %s %s" process event))))
+ (when (and (> (length (frame-list)) 1)
+ (frame-live-p idlwave-shell-idl-wframe))
+ (delete-frame idlwave-shell-idl-wframe)
+ (setq idlwave-shell-idl-wframe nil
+ idlwave-shell-display-wframe nil))
+ (when (window-live-p win)
+ (delete-window win))
+ (idlwave-shell-cleanup)))
+
+(defun idlwave-shell-scan-for-state ()
+ "Scan for state info.
+Looks for messages in output from last IDL command indicating where
+IDL has stopped. The types of messages we are interested in are
+execution halted, stepped, breakpoint, interrupted at and trace
+messages. We ignore error messages otherwise.
+For breakpoint messages process any attached count or command
+parameters.
+Update the windows if a message is found."
+ (let (update)
+ (cond
+ ;; Make sure we have output
+ ((not idlwave-shell-command-output))
+
+ ;; Various types of HALT messages.
+ ((string-match idlwave-shell-halt-messages-re
+ idlwave-shell-command-output)
+ ;; Grab the file and line state info.
+ (setq idlwave-shell-halt-frame
+ (idlwave-shell-parse-line
+ (substring idlwave-shell-command-output (match-end 0)))
+ update t))
+
+ ;; Handle breakpoints separately
+ ((string-match idlwave-shell-break-message
+ idlwave-shell-command-output)
+ (setq idlwave-shell-halt-frame
+ (idlwave-shell-parse-line
+ (substring idlwave-shell-command-output (match-end 0)))
+ update t)
+ ;; We used to to counting hits on breakpoints
+ ;; this is no longer supported since IDL breakpoints
+ ;; have learned counting.
+ ;; Do breakpoint command processing
+ (let ((bp (assoc
+ (list
+ (nth 0 idlwave-shell-halt-frame)
+ (nth 1 idlwave-shell-halt-frame))
+ idlwave-shell-bp-alist)))
+ (if bp
+ (let ((cmd (idlwave-shell-bp-get bp 'cmd)))
+ (if cmd
+ ;; Execute command
+ (if (listp cmd)
+ (eval cmd)
+ (funcall cmd))))
+ ;; A breakpoint that we did not know about - perhaps it was
+ ;; set by the user or IDL isn't reporting breakpoints like
+ ;; we expect. Lets update our list.
+ (idlwave-shell-bp-query)))))
+
+ ;; Handle compilation errors in addition to the above
+ (if (and idlwave-shell-command-output
+ (or (string-match
+ idlwave-shell-syntax-error idlwave-shell-command-output)
+ (string-match
+ idlwave-shell-other-error idlwave-shell-command-output)))
+ (progn
+ (save-excursion
+ (set-buffer
+ (get-buffer-create idlwave-shell-error-buffer))
+ (erase-buffer)
+ (insert idlwave-shell-command-output)
+ (goto-char (point-min))
+ (setq idlwave-shell-error-last (point)))
+ (idlwave-shell-goto-next-error)))
+
+ ;; Do update
+ (when update
+ (idlwave-shell-display-line (idlwave-shell-pc-frame)))))
+
+
+(defvar idlwave-shell-error-buffer
+ "*idlwave-shell-errors*"
+ "Buffer containing syntax errors from IDL compilations.")
+
+;; FIXME: the following two variables do not currently allow line breaks
+;; in module and file names. I am not sure if it will be necessary to
+;; change this. Currently it seems to work the way it is.
+(defvar idlwave-shell-syntax-error
+ "^% Syntax error.\\s-*\n\\s-*At:\\s-*\\(.*\\),\\s-*Line\\s-*\\(.*\\)"
+ "A regular expression to match an IDL syntax error.
+The first \(..\) pair should match the file name. The second pair
+should match the line number.")
+
+(defvar idlwave-shell-other-error
+ "^% .*\n\\s-*At:\\s-*\\(.*\\),\\s-*Line\\s-*\\(.*\\)"
+ "A regular expression to match any IDL error.
+The first \(..\) pair should match the file name. The second pair
+should match the line number.")
+
+(defvar idlwave-shell-file-line-message
+ (concat
+ "\\(" ; program name group (1)
+ "\\<[a-zA-Z][a-zA-Z0-9_$:]*" ; start with a letter, followed by [..]
+ "\\([ \t]*\n[ \t]*[a-zA-Z0-9_$:]+\\)*"; continuation lines program name (2)
+ "\\)" ; end program name group (1)
+ "[ \t\n]+" ; white space
+ "\\(" ; line number group (3)
+ "[0-9]+" ; the line number (the fix point)
+ "\\([ \t]*\n[ \t]*[0-9]+\\)*" ; continuation lines number (4)
+ "\\)" ; end line number group (3)
+ "[ \t\n]+" ; white space
+ "\\(" ; file name group (5)
+ "[^ \t\n]+" ; file names can contain any non-white
+ "\\([ \t]*\n[ \t]*[^ \t\n]+\\)*" ; continuation lines file name (6)
+ "\\)" ; end line number group (5)
+ )
+ "*A regular expression to parse out the file name and line number.
+The 1st group should match the subroutine name.
+The 3rd group is the line number.
+The 5th group is the file name.
+All parts may contain linebreaks surrounded by spaces. This is important
+in IDL5 which inserts random linebreaks in long module and file names.")
+
+(defun idlwave-shell-parse-line (string)
+ "Parse IDL message for the subroutine, file name and line number.
+We need to work hard here to remove the stupid line breaks inserted by
+IDL5. These line breaks can be right in the middle of procedure
+or file names.
+It is very difficult to come up with a robust solution. This one seems
+to be pretty good though.
+
+Here is in what ways it improves over the previous solution:
+
+1. The procedure name can be split and will be restored.
+2. The number can be split. I have never seen this, but who knows.
+3. We do not require the `.pro' extension for files.
+
+This function can still break when the file name ends on a end line
+and the message line contains an additional line with garbage. Then
+the first part of that garbage will be added to the file name.
+However, the function checks the existence of the files with and
+without this last part - thus the function only breaks if file name
+plus garbage match an existing regular file. This is hopefully very
+unlikely."
+
+ (let (number procedure file)
+ (when (string-match idlwave-shell-file-line-message string)
+ (setq procedure (match-string 1 string)
+ number (match-string 3 string)
+ file (match-string 5 string))
+
+ ;; Repair the strings
+ (setq procedure (idlwave-shell-repair-string procedure))
+ (setq number (idlwave-shell-repair-string number))
+ (setq file (idlwave-shell-repair-file-name file))
+
+ ;; If we have a file, return the frame list
+ (if file
+ (list (idlwave-shell-file-name file)
+ (string-to-int number)
+ procedure)
+ ;; No success finding a file
+ nil))))
+
+(defun idlwave-shell-repair-string (string)
+ "Repair a string by taking out all linebreaks. This is destructive!"
+ (while (string-match "[ \t]*\n[ \t]*" string)
+ (setq string (replace-match "" t t string)))
+ string)
+
+(defun idlwave-shell-repair-file-name (file)
+ "Repair a file name string by taking out all linebreaks.
+The last line of STRING may be garbage - we check which one makes a valid
+file name."
+ (let ((file1 "") (file2 "") (start 0))
+ ;; We scan no further than to the next "^%" line
+ (if (string-match "^%" file)
+ (setq file (substring file 0 (match-beginning 0))))
+ ;; Take out the line breaks
+ (while (string-match "[ \t]*\n[ \t]*" file start)
+ (setq file1 (concat file1 (substring file start (match-beginning 0)))
+ start (match-end 0)))
+ (setq file2 (concat file1 (substring file start)))
+ (cond
+ ((file-regular-p file2) file2)
+ ((file-regular-p file1) file1)
+ ;; If we cannot veryfy the existence of the file, we return the shorter
+ ;; name. The idea behind this is that this may be a relative file name
+ ;; and our idea about the current working directory may be wrong.
+ ;; If it is a relative file name, it hopefully is short.
+ ((not (string= "" file1)) file1)
+ ((not (string= "" file2)) file2)
+ (t nil))))
+
+(defun idlwave-shell-cleanup ()
+ "Do necessary cleanup for a terminated IDL process."
+ (setq idlwave-shell-step-frame nil
+ idlwave-shell-halt-frame nil
+ idlwave-shell-pending-commands nil
+ idlwave-shell-command-line-to-execute nil
+ idlwave-shell-bp-alist nil
+ idlwave-shell-calling-stack-index 0)
+ (idlwave-shell-display-line nil)
+ (idlwave-shell-update-bp-overlays) ; kill old overlays
+ (idlwave-shell-kill-buffer "*idlwave-shell-hidden-output*")
+ (idlwave-shell-kill-buffer idlwave-shell-bp-buffer)
+ (idlwave-shell-kill-buffer idlwave-shell-error-buffer)
+ ;; (idlwave-shell-kill-buffer (idlwave-shell-buffer))
+ (and (get-buffer (idlwave-shell-buffer))
+ (bury-buffer (get-buffer (idlwave-shell-buffer))))
+ (run-hooks 'idlwave-shell-cleanup-hook))
+
+(defun idlwave-shell-kill-buffer (buf)
+ "Kill buffer BUF if it exists."
+ (if (setq buf (get-buffer buf))
+ (kill-buffer buf)))
+
+(defun idlwave-shell-kill-shell-buffer-confirm ()
+ (when (idlwave-shell-is-running)
+ (ding)
+ (unless (y-or-n-p "IDL shell is running. Are you sure you want to kill the buffer? ")
+ (error "Abort"))
+ (message "Killing buffer *idl* and the associated process")))
+
+(defun idlwave-shell-resync-dirs ()
+ "Resync the buffer's idea of the current directory stack.
+This command queries IDL with the command bound to
+`idlwave-shell-dirstack-query' (default \"printd\"), reads the
+output for the new directory stack."
+ (interactive)
+ (idlwave-shell-send-command idlwave-shell-dirstack-query
+ 'idlwave-shell-filter-directory
+ 'hide))
+
+(defun idlwave-shell-retall (&optional arg)
+ "Return from the entire calling stack."
+ (interactive "P")
+ (idlwave-shell-send-command "retall"))
+
+(defun idlwave-shell-closeall (&optional arg)
+ "Close all open files."
+ (interactive "P")
+ (idlwave-shell-send-command "close,/all"))
+
+(defun idlwave-shell-quit (&optional arg)
+ "Exit the idl process after confirmation.
+With prefix ARG, exit without confirmation."
+ (interactive "P")
+ (if (not (idlwave-shell-is-running))
+ (error "Shell is not running")
+ (if (or arg (y-or-n-p "Exit the IDLWAVE Shell? "))
+ (condition-case nil
+ (idlwave-shell-send-command "exit")
+ (error nil)))))
+
+(defun idlwave-shell-reset (&optional visible)
+ "Reset IDL. Return to main level and destroy the leaftover variables.
+This issues the following commands:
+RETALL
+WIDGET_CONTROL,/RESET
+CLOSE, /ALL
+HEAP_GC, /VERBOSE"
+ ;; OBJ_DESTROY, OBJ_VALID() FIXME: should this be added?
+ (interactive "P")
+ (message "Resetting IDL")
+ (idlwave-shell-send-command "retall" nil (not visible))
+ (idlwave-shell-send-command "widget_control,/reset" nil (not visible))
+ (idlwave-shell-send-command "close,/all" nil (not visible))
+ ;; (idlwave-shell-send-command "obj_destroy, obj_valid()" nil (not visible))
+ (idlwave-shell-send-command "heap_gc,/verbose" nil (not visible))
+ (setq idlwave-shell-calling-stack-index 0))
+
+(defun idlwave-shell-filter-directory ()
+ "Get the current directory from `idlwave-shell-command-output'.
+Change the default directory for the process buffer to concur."
+ (save-excursion
+ (set-buffer (idlwave-shell-buffer))
+ (if (string-match "Current Directory: *\\(\\S-*\\) *$"
+ idlwave-shell-command-output)
+ (let ((dir (substring idlwave-shell-command-output
+ (match-beginning 1) (match-end 1))))
+ (message "Setting Emacs wd to %s" dir)
+ (setq idlwave-shell-default-directory dir)
+ (setq default-directory (file-name-as-directory dir))))))
+
+(defun idlwave-shell-complete (&optional arg)
+ "Do completion in the idlwave-shell buffer.
+Calls `idlwave-shell-complete-filename' after some executive commands or
+in strings. Otherwise, calls `idlwave-complete' to complete modules and
+keywords."
+;;FIXME: batch files?
+ (interactive "P")
+ (let (cmd)
+ (cond
+ ((setq cmd (idlwave-shell-executive-command))
+ ;; We are in a command line with an executive command
+ (if (member (upcase cmd)
+ '(".R" ".RU" ".RUN" ".RN" ".RNE" ".RNEW"
+ ".COM" ".COMP" ".COMPI" ".COMPIL" ".COMPILE"))
+ ;; This command expects file names
+ (idlwave-shell-complete-filename)))
+ ((idlwave-shell-filename-string)
+ ;; In a string, could be a file name to here
+ (idlwave-shell-complete-filename))
+ (t
+ ;; Default completion of modules and keywords
+ (idlwave-complete)))))
+
+(defun idlwave-shell-complete-filename (&optional arg)
+ "Complete a file name at point if after a file name.
+We assume that we are after a file name when completing one of the
+args of an executive .run, .rnew or .compile. Also, in a string
+constant we complete file names. Otherwise return nil, so that
+other completion functions can do thier work."
+ (let* ((comint-file-name-chars idlwave-shell-file-name-chars)
+ (completion-ignore-case (default-value 'completion-ignore-case)))
+ (comint-dynamic-complete-filename)))
+
+(defun idlwave-shell-executive-command ()
+ "Return the name of the current executive command, if any."
+ (save-excursion
+ (idlwave-beginning-of-statement)
+ (if (looking-at "[ \t]*\\([.][^ \t\n\r]*\\)")
+ (match-string 1))))
+
+(defun idlwave-shell-filename-string ()
+ "Return t if in a string and after what could be a file name."
+ (let ((limit (save-excursion (beginning-of-line) (point))))
+ (save-excursion
+ ;; Skip backwards over file name chars
+ (skip-chars-backward idlwave-shell-file-name-chars limit)
+ ;; Check of the next char is a string delimiter
+ (memq (preceding-char) '(?\' ?\")))))
+
+;;;
+;;; This section contains code for debugging IDL programs. --------------------
+;;;
+
+(defun idlwave-shell-redisplay (&optional hide)
+ "Tries to resync the display with where execution has stopped.
+Issues a \"help,/trace\" command followed by a call to
+`idlwave-shell-display-line'. Also updates the breakpoint
+overlays."
+ (interactive)
+ (idlwave-shell-send-command
+ "help,/trace"
+ '(idlwave-shell-display-line
+ (idlwave-shell-pc-frame))
+ hide)
+ (idlwave-shell-bp-query))
+
+(defun idlwave-shell-display-level-in-calling-stack (&optional hide)
+ (idlwave-shell-send-command
+ "help,/trace"
+ 'idlwave-shell-parse-stack-and-display
+ hide))
+
+(defun idlwave-shell-parse-stack-and-display ()
+ (let* ((lines (delete "" (idlwave-split-string
+ idlwave-shell-command-output "^%")))
+ (stack (delq nil (mapcar 'idlwave-shell-parse-line lines)))
+ (nmax (1- (length stack)))
+ (nmin 0) message)
+; ;; Reset the stack to zero if it is a new stack.
+; (if (not (equal stack idlwave-shell-last-calling-stack))
+; (setq idlwave-shell-calling-stack-index 0))
+; (setq idlwave-shell-last-calling-stack stack)
+ (cond
+ ((< nmax nmin)
+ (setq idlwave-shell-calling-stack-index 0)
+ (error "Problem with calling stack"))
+ ((> idlwave-shell-calling-stack-index nmax)
+ (setq idlwave-shell-calling-stack-index nmax
+ message (format "%d is the highest level on the calling stack"
+ nmax)))
+ ((< idlwave-shell-calling-stack-index nmin)
+ (setq idlwave-shell-calling-stack-index nmin
+ message (format "%d is the lowest level on the calling stack"
+ nmin))))
+ (idlwave-shell-display-line
+ (nth idlwave-shell-calling-stack-index stack))
+ (message (or message
+ (format "On stack level %d"
+ idlwave-shell-calling-stack-index)))))
+
+(defun idlwave-shell-stack-up ()
+ "Display the source code one step up the calling stack."
+ (interactive)
+ (incf idlwave-shell-calling-stack-index)
+ (idlwave-shell-display-level-in-calling-stack 'hide))
+(defun idlwave-shell-stack-down ()
+ "Display the source code one step down the calling stack."
+ (interactive)
+ (decf idlwave-shell-calling-stack-index)
+ (idlwave-shell-display-level-in-calling-stack 'hide))
+
+(defun idlwave-shell-goto-frame (&optional frame)
+ "Set buffer to FRAME with point at the frame line.
+If the optional argument FRAME is nil then idlwave-shell-pc-frame is
+used. Does nothing if the resulting frame is nil."
+ (if frame ()
+ (setq frame (idlwave-shell-pc-frame)))
+ (cond
+ (frame
+ (set-buffer (idlwave-find-file-noselect (car frame)))
+ (widen)
+ (goto-line (nth 1 frame)))))
+
+(defun idlwave-shell-pc-frame ()
+ "Returns the frame for IDL execution."
+ (and idlwave-shell-halt-frame
+ (list (nth 0 idlwave-shell-halt-frame)
+ (nth 1 idlwave-shell-halt-frame))))
+
+(defun idlwave-shell-valid-frame (frame)
+ "Check that frame is for an existing file."
+ (file-readable-p (car frame)))
+
+(defun idlwave-shell-display-line (frame &optional col)
+ "Display FRAME file in other window with overlay arrow.
+
+FRAME is a list of file name, line number, and subroutine name.
+If FRAME is nil then remove overlay."
+ (if (not frame)
+ ;; Remove stop-line overlay from old position
+ (progn
+ (setq overlay-arrow-string nil)
+ (if idlwave-shell-stop-line-overlay
+ (delete-overlay idlwave-shell-stop-line-overlay)))
+ (if (not (idlwave-shell-valid-frame frame))
+ (error (concat "Invalid frame - unable to access file: " (car frame)))
+;;;
+;;; buffer : the buffer to display a line in.
+;;; select-shell: current buffer is the shell.
+;;;
+ (let* ((buffer (idlwave-find-file-noselect (car frame)))
+ (select-shell (equal (buffer-name) (idlwave-shell-buffer)))
+ window pos)
+
+ ;; First make sure the shell window is visible
+ (idlwave-display-buffer (idlwave-shell-buffer)
+ nil (idlwave-shell-shell-frame))
+
+ ;; Now display the buffer and remember which window it is.
+ (setq window (idlwave-display-buffer buffer
+ nil (idlwave-shell-source-frame)))
+
+ ;; Enter the buffer and mark the line
+ (save-excursion
+ (set-buffer buffer)
+ (save-restriction
+ (widen)
+ (goto-line (nth 1 frame))
+ (setq pos (point))
+ (if idlwave-shell-stop-line-overlay
+ ;; Move overlay
+ (move-overlay idlwave-shell-stop-line-overlay
+ (point) (save-excursion (end-of-line) (point))
+ (current-buffer))
+ ;; Use the arrow instead, but only if marking is wanted.
+ (if idlwave-shell-mark-stop-line
+ (setq overlay-arrow-string idlwave-shell-overlay-arrow))
+ (or overlay-arrow-position ; create the marker if necessary
+ (setq overlay-arrow-position (make-marker)))
+ (set-marker overlay-arrow-position (point) buffer)))
+
+ ;; If the point is outside the restriction, widen the buffer.
+ (if (or (< pos (point-min)) (> pos (point-max)))
+ (progn
+ (widen)
+ (goto-char pos)))
+
+ ;; If we have the column of the error, move the cursor there.
+ (if col (move-to-column col))
+ (setq pos (point)))
+
+ ;; Make sure pos is really displayed in the window.
+ (set-window-point window pos)
+
+ ;; FIXME: the following frame redraw was taken out because it
+ ;; flashes. I think it is not needed. The code is left here in
+ ;; case we have to put it back in.
+ ;; (redraw-frame (window-frame window))
+
+ ;; If we came from the shell, go back there. Otherwise select
+ ;; the window where the error is displayed.
+ (if (and (equal (buffer-name) (idlwave-shell-buffer))
+ (not select-shell))
+ (select-window window))))))
+
+
+(defun idlwave-shell-step (arg)
+ "Step one source line. If given prefix argument ARG, step ARG source lines."
+ (interactive "p")
+ (or (not arg) (< arg 1)
+ (setq arg 1))
+ (idlwave-shell-send-command
+ (concat ".s " (if (integerp arg) (int-to-string arg) arg))))
+
+(defun idlwave-shell-stepover (arg)
+ "Stepover one source line.
+If given prefix argument ARG, step ARG source lines.
+Uses IDL's stepover executive command which does not enter called functions."
+ (interactive "p")
+ (or (not arg) (< arg 1)
+ (setq arg 1))
+ (idlwave-shell-send-command
+ (concat ".so " (if (integerp arg) (int-to-string arg) arg))))
+
+(defun idlwave-shell-break-here (&optional count cmd)
+ "Set breakpoint at current line.
+
+If Count is nil then an ordinary breakpoint is set. We treat a count
+of 1 as a temporary breakpoint using the ONCE keyword. Counts greater
+than 1 use the IDL AFTER=count keyword to break only after reaching
+the statement count times.
+
+Optional argument CMD is a list or function to evaluate upon reaching
+the breakpoint."
+
+ (interactive "P")
+ (if (listp count)
+ (setq count nil))
+ (idlwave-shell-set-bp
+ ;; Create breakpoint
+ (idlwave-shell-bp (idlwave-shell-current-frame)
+ (list count cmd)
+ (idlwave-shell-current-module))))
+
+(defun idlwave-shell-set-bp-check (bp)
+ "Check for failure to set breakpoint.
+This is run on `idlwave-shell-post-command-hook'.
+Offers to recompile the procedure if we failed. This usually fixes
+the problem with not being able to set the breakpoint."
+ ;; Scan for message
+ (if (and idlwave-shell-command-output
+ (string-match "% BREAKPOINT: *Unable to find code"
+ idlwave-shell-command-output))
+ ;; Offer to recompile
+ (progn
+ (if (progn
+ (beep)
+ (y-or-n-p
+ (concat "Okay to recompile file "
+ (idlwave-shell-bp-get bp 'file) " ")))
+ ;; Recompile
+ (progn
+ ;; Clean up before retrying
+ (idlwave-shell-command-failure)
+ (idlwave-shell-send-command
+ (concat ".run " (idlwave-shell-bp-get bp 'file)) nil nil)
+ ;; Try setting breakpoint again
+ (idlwave-shell-set-bp bp))
+ (beep)
+ (message "Unable to set breakpoint.")
+ (idlwave-shell-command-failure)
+ )
+ ;; return non-nil if no error found
+ nil)
+ 'okay))
+
+(defun idlwave-shell-command-failure ()
+ "Do any necessary clean up when an IDL command fails.
+Call this from a function attached to `idlwave-shell-post-command-hook'
+that detects the failure of a command.
+For example, this is called from `idlwave-shell-set-bp-check' when a
+breakpoint can not be set."
+ ;; Clear pending commands
+ (setq idlwave-shell-pending-commands nil))
+
+(defun idlwave-shell-cont ()
+ "Continue executing."
+ (interactive)
+ (idlwave-shell-send-command ".c" '(idlwave-shell-redisplay 'hide)))
+
+(defun idlwave-shell-go ()
+ "Run .GO. This starts the main program of the last compiled file."
+ (interactive)
+ (idlwave-shell-send-command ".go" '(idlwave-shell-redisplay 'hide)))
+
+(defun idlwave-shell-return ()
+ "Run .RETURN (continue to next return, but stay in subprogram)."
+ (interactive)
+ (idlwave-shell-send-command ".return" '(idlwave-shell-redisplay 'hide)))
+
+(defun idlwave-shell-skip ()
+ "Run .SKIP (skip one line, then step)."
+ (interactive)
+ (idlwave-shell-send-command ".skip" '(idlwave-shell-redisplay 'hide)))
+
+(defun idlwave-shell-clear-bp (bp)
+ "Clear breakpoint BP.
+Clears in IDL and in `idlwave-shell-bp-alist'."
+ (let ((index (idlwave-shell-bp-get bp)))
+ (if index
+ (progn
+ (idlwave-shell-send-command
+ (concat "breakpoint,/clear,"
+ (if (integerp index) (int-to-string index) index)))
+ (idlwave-shell-bp-query)))))
+
+(defun idlwave-shell-current-frame ()
+ "Return a list containing the current file name and line point is in.
+If in the IDL shell buffer, returns `idlwave-shell-pc-frame'."
+ (if (eq (current-buffer) (get-buffer (idlwave-shell-buffer)))
+ ;; In IDL shell
+ (idlwave-shell-pc-frame)
+ ;; In source
+ (list (idlwave-shell-file-name (buffer-file-name))
+ (save-restriction
+ (widen)
+ (save-excursion
+ (beginning-of-line)
+ (1+ (count-lines 1 (point))))))))
+
+(defun idlwave-shell-current-module ()
+ "Return the name of the module for the current file.
+Returns nil if unable to obtain a module name."
+ (if (eq (current-buffer) (get-buffer (idlwave-shell-buffer)))
+ ;; In IDL shell
+ (nth 2 idlwave-shell-halt-frame)
+ ;; In pro file
+ (save-restriction
+ (widen)
+ (save-excursion
+ (if (idlwave-prev-index-position)
+ (upcase (idlwave-unit-name)))))))
+
+(defun idlwave-shell-clear-current-bp ()
+ "Remove breakpoint at current line.
+This command can be called from the shell buffer if IDL is currently stopped
+at a breakpoint."
+ (interactive)
+ (let ((bp (idlwave-shell-find-bp (idlwave-shell-current-frame))))
+ (if bp (idlwave-shell-clear-bp bp)
+ ;; Try moving to beginning of statement
+ (save-excursion
+ (idlwave-shell-goto-frame)
+ (idlwave-beginning-of-statement)
+ (setq bp (idlwave-shell-find-bp (idlwave-shell-current-frame)))
+ (if bp (idlwave-shell-clear-bp bp)
+ (beep)
+ (message "Cannot identify breakpoint for this line"))))))
+
+(defun idlwave-shell-to-here ()
+ "Set a breakpoint with count 1 then continue."
+ (interactive)
+ (idlwave-shell-break-here 1)
+ (idlwave-shell-cont))
+
+(defun idlwave-shell-break-in (&optional module)
+ "Look for a module name near point and set a break point for it.
+The command looks for an identifier near point and sets a breakpoint
+for the first line of the corresponding module."
+ (interactive)
+ ;; get the identifier
+ (let (module)
+ (save-excursion
+ (skip-chars-backward "a-zA-Z0-9_$")
+ (if (looking-at idlwave-identifier)
+ (setq module (match-string 0))
+ (error "No identifier at point")))
+ (idlwave-shell-send-command
+ idlwave-shell-sources-query
+ `(progn
+ (idlwave-shell-sources-filter)
+ (idlwave-shell-set-bp-in-module ,module))
+ 'hide)))
+
+(defun idlwave-shell-set-bp-in-module (module)
+ "Set breakpoint in module. Assumes that `idlwave-shell-sources-alist'
+contains an entry for that module."
+ (let ((source-file (car-safe
+ (cdr-safe
+ (assoc (upcase module)
+ idlwave-shell-sources-alist))))
+ buf)
+ (if (or (not source-file)
+ (not (file-regular-p source-file))
+ (not (setq buf
+ (or (idlwave-get-buffer-visiting source-file)
+ (find-file-noselect source-file)))))
+ (progn
+ (message "The source file for module %s is probably not compiled"
+ module)
+ (beep))
+ (save-excursion
+ (set-buffer buf)
+ (save-excursion
+ (goto-char (point-min))
+ (let ((case-fold-search t))
+ (if (re-search-forward
+ (concat "^[ \t]*\\(pro\\|function\\)[ \t]+"
+ (downcase module)
+ "[ \t\n,]") nil t)
+ (progn
+ (goto-char (match-beginning 1))
+ (message "Setting breakpoint for module %s" module)
+ (idlwave-shell-break-here))
+ (message "Cannot find module %s in file %s" module source-file)
+ (beep))))))))
+
+(defun idlwave-shell-up ()
+ "Run to end of current block.
+Sets a breakpoint with count 1 at end of block, then continues."
+ (interactive)
+ (if (idlwave-shell-pc-frame)
+ (save-excursion
+ (idlwave-shell-goto-frame)
+ ;; find end of subprogram
+ (let ((eos (save-excursion
+ (idlwave-beginning-of-subprogram)
+ (idlwave-forward-block)
+ (point))))
+ (idlwave-backward-up-block -1)
+ ;; move beyond end block line - IDL will not break there.
+ ;; That is, you can put a breakpoint there but when IDL does
+ ;; break it will report that it is at the next line.
+ (idlwave-next-statement)
+ (idlwave-end-of-statement)
+ ;; Make sure we are not beyond subprogram
+ (if (< (point) eos)
+ ;; okay
+ ()
+ ;; Move back inside subprogram
+ (goto-char eos)
+ (idlwave-previous-statement))
+ (idlwave-shell-to-here)))))
+
+(defun idlwave-shell-out ()
+ "Attempt to run until this procedure exits.
+Runs to the last statement and then steps 1 statement. Use the .out command."
+ (interactive)
+ (idlwave-shell-send-command (concat ".o")))
+
+(defun idlwave-shell-help-expression ()
+ "Print help on current expression. See `idlwave-shell-print'."
+ (interactive)
+ (idlwave-shell-print 'help))
+
+(defun idlwave-shell-mouse-print (event)
+ "Call `idlwave-shell-print' at the mouse position."
+ (interactive "e")
+ (mouse-set-point event)
+ (idlwave-shell-print))
+
+(defun idlwave-shell-mouse-help (event)
+ "Call `idlwave-shell-print' at the mouse position."
+ (interactive "e")
+ (mouse-set-point event)
+ (idlwave-shell-help-expression))
+
+(defun idlwave-shell-print (&optional help special)
+ "Print current expression. With are HELP, show help on expression.
+An expression is an identifier plus 1 pair of matched parentheses
+directly following the identifier - an array or function
+call. Alternatively, an expression is the contents of any matched
+parentheses when the open parentheses is not directly preceded by an
+identifier. If point is at the beginning or within an expression
+return the inner-most containing expression, otherwise, return the
+preceding expression."
+ (interactive "P")
+ (save-excursion
+ (let (beg end)
+ ;; Move to beginning of current or previous expression
+ (if (looking-at "\\<\\|(")
+ ;; At beginning of expression, don't move backwards unless
+ ;; this is at the end of an indentifier.
+ (if (looking-at "\\>")
+ (backward-sexp))
+ (backward-sexp))
+ (if (looking-at "\\>")
+ ;; Move to beginning of identifier - must be an array or
+ ;; function expression.
+ (backward-sexp))
+ ;; Move to end of expression
+ (setq beg (point))
+ (forward-sexp)
+ (while (looking-at "\\>(\\|\\.")
+ ;; an array
+ (forward-sexp))
+ (setq end (point))
+ (when idlwave-shell-expression-overlay
+ (move-overlay idlwave-shell-expression-overlay beg end)
+ (add-hook 'pre-command-hook 'idlwave-shell-delete-expression-overlay))
+ (if special
+ (idlwave-shell-send-command
+ (concat (if help "help," "print,") (buffer-substring beg end))
+ `(idlwave-shell-process-print-output ,(buffer-substring beg end)
+ idlwave-shell-command-output
+ ,special)
+ 'hide)
+ (idlwave-shell-recenter-shell-window)
+ (idlwave-shell-send-command
+ (concat (if help "help," "print,") (buffer-substring beg end)))))))
+
+(defun idlwave-shell-delete-expression-overlay ()
+ (condition-case nil
+ (if idlwave-shell-expression-overlay
+ (delete-overlay idlwave-shell-expression-overlay))
+ (error nil))
+ (remove-hook 'pre-command-hook 'idlwave-shell-delete-expression-overlay))
+
+(defvar idlwave-shell-bp-alist nil
+ "Alist of breakpoints.
+A breakpoint is a cons cell \(\(file line\) . \(\(index module\) data\)\)
+
+The car is the frame for the breakpoint:
+file - full path file name.
+line - line number of breakpoint - integer.
+
+The first element of the cdr is a list of internal IDL data:
+index - the index number of the breakpoint internal to IDL.
+module - the module for breakpoint internal to IDL.
+
+Remaining elements of the cdr:
+data - Data associated with the breakpoint by idlwave-shell currently
+contains two items:
+
+count - number of times to execute breakpoint. When count reaches 0
+the breakpoint is cleared and removed from the alist.
+command - command to execute when breakpoint is reached, either a
+lisp function to be called with `funcall' with no arguments or a
+list to be evaluated with `eval'.")
+
+(defun idlwave-shell-run-region (beg end &optional n)
+ "Compile and run the region using the IDL process.
+Copies the region to a temporary file `idlwave-shell-temp-pro-file'
+and issues the IDL .run command for the file. Because the
+region is compiled and run as a main program there is no
+problem with begin-end blocks extending over multiple
+lines - which would be a problem if `idlwave-shell-evaluate-region'
+was used. An END statement is appended to the region if necessary.
+
+If there is a prefix argument, display IDL process."
+ (interactive "r\nP")
+ (let ((oldbuf (current-buffer)))
+ (save-excursion
+ (set-buffer (idlwave-find-file-noselect
+ idlwave-shell-temp-pro-file))
+ (erase-buffer)
+ (insert-buffer-substring oldbuf beg end)
+ (if (not (save-excursion
+ (idlwave-previous-statement)
+ (idlwave-look-at "\\<end\\>")))
+ (insert "\nend\n"))
+ (save-buffer 0)))
+ (idlwave-shell-send-command (concat ".run " idlwave-shell-temp-pro-file))
+ (if n
+ (idlwave-display-buffer (idlwave-shell-buffer)
+ nil (idlwave-shell-shell-frame))))
+
+(defun idlwave-shell-evaluate-region (beg end &optional n)
+ "Send region to the IDL process.
+If there is a prefix argument, display IDL process.
+Does not work for a region with multiline blocks - use
+`idlwave-shell-run-region' for this."
+ (interactive "r\nP")
+ (idlwave-shell-send-command (buffer-substring beg end))
+ (if n
+ (idlwave-display-buffer (idlwave-shell-buffer)
+ nil (idlwave-shell-shell-frame))))
+
+(defun idlwave-display-buffer (buf not-this-window-p &optional frame)
+ (if (or (< emacs-major-version 20)
+ (and (= emacs-major-version 20)
+ (< emacs-minor-version 3)))
+ ;; Only two args.
+ (display-buffer buf not-this-window-p)
+ ;; Three ares possible.
+ (display-buffer buf not-this-window-p frame)))
+
+(defvar idlwave-shell-bp-buffer "*idlwave-shell-bp*"
+ "Scratch buffer for parsing IDL breakpoint lists and other stuff.")
+
+(defun idlwave-shell-bp-query ()
+ "Reconcile idlwave-shell's breakpoint list with IDL's.
+Queries IDL using the string in `idlwave-shell-bp-query'."
+ (interactive)
+ (idlwave-shell-send-command idlwave-shell-bp-query
+ 'idlwave-shell-filter-bp
+ 'hide))
+
+(defun idlwave-shell-bp-get (bp &optional item)
+ "Get a value for a breakpoint.
+BP has the form of elements in idlwave-shell-bp-alist.
+Optional second arg ITEM is the particular value to retrieve.
+ITEM can be 'file, 'line, 'index, 'module, 'count, 'cmd, or 'data.
+'data returns a list of 'count and 'cmd.
+Defaults to 'index."
+ (cond
+ ;; Frame
+ ((eq item 'line) (nth 1 (car bp)))
+ ((eq item 'file) (nth 0 (car bp)))
+ ;; idlwave-shell breakpoint data
+ ((eq item 'data) (cdr (cdr bp)))
+ ((eq item 'count) (nth 0 (cdr (cdr bp))))
+ ((eq item 'cmd) (nth 1 (cdr (cdr bp))))
+ ;; IDL breakpoint info
+ ((eq item 'module) (nth 1 (car (cdr bp))))
+ ;; index - default
+ (t (nth 0 (car (cdr bp))))))
+
+(defun idlwave-shell-filter-bp ()
+ "Get the breakpoints from `idlwave-shell-command-output'.
+Create `idlwave-shell-bp-alist' updating breakpoint count and command data
+from previous breakpoint list."
+ (save-excursion
+ (set-buffer (get-buffer-create idlwave-shell-bp-buffer))
+ (erase-buffer)
+ (insert idlwave-shell-command-output)
+ (goto-char (point-min))
+ (let ((old-bp-alist idlwave-shell-bp-alist))
+ (setq idlwave-shell-bp-alist (list nil))
+ (if (re-search-forward "^\\s-*Index.*\n\\s-*-" nil t)
+ (while (and
+ (not (progn (forward-line) (eobp)))
+ ;; Parse breakpoint line.
+ ;; Breakpoints have the form:
+ ;; Index Module Line File
+ ;; All seperated by whitespace.
+ ;;
+ ;; Add the breakpoint info to the list
+ (re-search-forward
+ "\\s-*\\(\\S-+\\)\\s-+\\(\\S-+\\)\\s-+\\(\\S-+\\)\\s-+\\(\\S-+\\)" nil t))
+ (nconc idlwave-shell-bp-alist
+ (list
+ (cons
+ (list
+ (save-match-data
+ (idlwave-shell-file-name
+ (buffer-substring ; file
+ (match-beginning 4) (match-end 4))))
+ (string-to-int ; line
+ (buffer-substring
+ (match-beginning 3) (match-end 3))))
+ (list
+ (list
+ (buffer-substring ; index
+ (match-beginning 1) (match-end 1))
+ (buffer-substring ; module
+ (match-beginning 2) (match-end 2)))
+ ;; idlwave-shell data: count, command
+ nil nil))))))
+ (setq idlwave-shell-bp-alist (cdr idlwave-shell-bp-alist))
+ ;; Update count, commands of breakpoints
+ (mapcar 'idlwave-shell-update-bp old-bp-alist)))
+ ;; Update the breakpoint overlays
+ (idlwave-shell-update-bp-overlays)
+ ;; Return the new list
+ idlwave-shell-bp-alist)
+
+(defun idlwave-shell-update-bp (bp)
+ "Update BP data in breakpoint list.
+If BP frame is in `idlwave-shell-bp-alist' updates the breakpoint data."
+ (let ((match (assoc (car bp) idlwave-shell-bp-alist)))
+ (if match (setcdr (cdr match) (cdr (cdr bp))))))
+
+(defun idlwave-shell-set-bp-data (bp data)
+ "Set the data of BP to DATA."
+ (setcdr (cdr bp) data))
+
+(defun idlwave-shell-bp (frame &optional data module)
+ "Create a breakpoint structure containing FRAME and DATA. Second
+and third args, DATA and MODULE, are optional. Returns a breakpoint
+of the format used in `idlwave-shell-bp-alist'. Can be used in commands
+attempting match a breakpoint in `idlwave-shell-bp-alist'."
+ (cons frame (cons (list nil module) data)))
+
+(defvar idlwave-shell-old-bp nil
+ "List of breakpoints previous to setting a new breakpoint.")
+
+(defun idlwave-shell-sources-bp (bp)
+ "Check `idlwave-shell-sources-alist' for source of breakpoint using BP.
+If an equivalency is found, return the IDL internal source name.
+Otherwise return the filename in bp."
+ (let*
+ ((bp-file (idlwave-shell-bp-get bp 'file))
+ (bp-module (idlwave-shell-bp-get bp 'module))
+ (internal-file-list (cdr (assoc bp-module idlwave-shell-sources-alist))))
+ (if (and internal-file-list
+ (equal bp-file (nth 0 internal-file-list)))
+ (nth 1 internal-file-list)
+ bp-file)))
+
+(defun idlwave-shell-set-bp (bp)
+ "Try to set a breakpoint BP.
+
+The breakpoint will be placed at the beginning of the statement on the
+line specified by BP or at the next IDL statement if that line is not
+a statement.
+Determines IDL's internal representation for the breakpoint which may
+have occured at a different line then used with the breakpoint
+command."
+
+ ;; Get and save the old breakpoints
+ (idlwave-shell-send-command
+ idlwave-shell-bp-query
+ '(progn
+ (idlwave-shell-filter-bp)
+ (setq idlwave-shell-old-bp idlwave-shell-bp-alist))
+ 'hide)
+ ;; Get sources for IDL compiled procedures followed by setting
+ ;; breakpoint.
+ (idlwave-shell-send-command
+ idlwave-shell-sources-query
+ (` (progn
+ (idlwave-shell-sources-filter)
+ (idlwave-shell-set-bp2 (quote (, bp)))))
+ 'hide))
+
+(defun idlwave-shell-set-bp2 (bp)
+ "Use results of breakpoint and sources query to set bp.
+Use the count argument with IDLs breakpoint command.
+We treat a count of 1 as a temporary breakpoint.
+Counts greater than 1 use the IDL AFTER=count keyword to break
+only after reaching the statement count times."
+ (let*
+ ((arg (idlwave-shell-bp-get bp 'count))
+ (key (cond
+ ((not (and arg (numberp arg))) "")
+ ((= arg 1)
+ ",/once")
+ ((> arg 1)
+ (format ",after=%d" arg))))
+ (line (idlwave-shell-bp-get bp 'line)))
+ (idlwave-shell-send-command
+ (concat "breakpoint,'"
+ (idlwave-shell-sources-bp bp) "',"
+ (if (integerp line) (setq line (int-to-string line)))
+ key)
+ ;; Check for failure and look for breakpoint in IDL's list
+ (` (progn
+ (if (idlwave-shell-set-bp-check (quote (, bp)))
+ (idlwave-shell-set-bp3 (quote (, bp)))))
+ )
+ ;; do not hide output
+ nil
+ 'preempt)))
+
+(defun idlwave-shell-set-bp3 (bp)
+ "Find the breakpoint in IDL's internal list of breakpoints."
+ (idlwave-shell-send-command idlwave-shell-bp-query
+ (` (progn
+ (idlwave-shell-filter-bp)
+ (idlwave-shell-new-bp (quote (, bp)))))
+ 'hide
+ 'preempt))
+
+(defun idlwave-shell-find-bp (frame)
+ "Return breakpoint from `idlwave-shell-bp-alist' for frame.
+Returns nil if frame not found."
+ (assoc frame idlwave-shell-bp-alist))
+
+(defun idlwave-shell-new-bp (bp)
+ "Find the new breakpoint in IDL's list and update with DATA.
+The actual line number for a breakpoint in IDL may be different than
+the line number used with the IDL breakpoint command.
+Looks for a new breakpoint index number in the list. This is
+considered the new breakpoint if the file name of frame matches."
+ (let ((obp-index (mapcar 'idlwave-shell-bp-get idlwave-shell-old-bp))
+ (bpl idlwave-shell-bp-alist))
+ (while (and (member (idlwave-shell-bp-get (car bpl)) obp-index)
+ (setq bpl (cdr bpl))))
+ (if (and
+ (not bpl)
+ ;; No additional breakpoint.
+ ;; Need to check if we are just replacing a breakpoint.
+ (setq bpl (assoc (car bp) idlwave-shell-bp-alist)))
+ (setq bpl (list bpl)))
+ (if (and bpl
+ (equal (idlwave-shell-bp-get (setq bpl (car bpl)) 'file)
+ (idlwave-shell-bp-get bp 'file)))
+ ;; Got the breakpoint - add count, command to it.
+ ;; This updates `idlwave-shell-bp-alist' because a deep copy was
+ ;; not done for bpl.
+ (idlwave-shell-set-bp-data bpl (idlwave-shell-bp-get bp 'data))
+ (beep)
+ (message "Failed to identify breakpoint in IDL"))))
+
+(defvar idlwave-shell-bp-overlays nil
+ "List of overlays marking breakpoints")
+
+(defun idlwave-shell-update-bp-overlays ()
+ "Update the overlays which mark breakpoints in the source code.
+Existing overlays are recycled, in order to minimize consumption."
+ ;; FIXME: we could cache them all, but that would be more work.
+ (when idlwave-shell-mark-breakpoints
+ (let ((bp-list idlwave-shell-bp-alist)
+ (ov-list idlwave-shell-bp-overlays)
+ ov bp)
+ ;; Delete the old overlays from their buffers
+ (while (setq ov (pop ov-list))
+ (delete-overlay ov))
+ (setq ov-list idlwave-shell-bp-overlays
+ idlwave-shell-bp-overlays nil)
+ (while (setq bp (pop bp-list))
+ (save-excursion
+ (idlwave-shell-goto-frame (car bp))
+ (let* ((end (progn (end-of-line 1) (point)))
+ (beg (progn (beginning-of-line 1) (point)))
+ (ov (or (pop ov-list)
+ (idlwave-shell-make-new-bp-overlay))))
+ (move-overlay ov beg end)
+ (push ov idlwave-shell-bp-overlays)))))))
+
+(defvar idlwave-shell-bp-glyph)
+(defun idlwave-shell-make-new-bp-overlay ()
+ "Make a new overlay for highlighting breakpoints.
+This stuff is stringly dependant upon the version of Emacs."
+ (let ((ov (make-overlay 1 1)))
+ (if (featurep 'xemacs)
+ ;; This is XEmacs
+ (progn
+ (cond
+ ((eq (console-type) 'tty)
+ ;; tty's cannot display glyphs
+ (set-extent-property ov 'face 'idlwave-shell-bp-face))
+ ((and (memq idlwave-shell-mark-breakpoints '(t glyph))
+ idlwave-shell-bp-glyph)
+ ;; use the glyph
+ (set-extent-property ov 'begin-glyph idlwave-shell-bp-glyph))
+ (idlwave-shell-mark-breakpoints
+ ;; use the face
+ (set-extent-property ov 'face 'idlwave-shell-bp-face))
+ (t
+ ;; no marking
+ nil))
+ (set-extent-priority ov -1)) ; make stop line face prevail
+ ;; This is Emacs
+ (cond
+ (window-system
+ (if (and (memq idlwave-shell-mark-breakpoints '(t glyph))
+ idlwave-shell-bp-glyph) ; this var knows if glyph's possible
+ ;; use a glyph
+ (let ((string "@"))
+ (put-text-property 0 1
+ 'display (cons nil idlwave-shell-bp-glyph)
+ string)
+ (overlay-put ov 'before-string string))
+ (overlay-put ov 'face 'idlwave-shell-bp-face)))
+ (idlwave-shell-mark-breakpoints
+ ;; use a face
+ (overlay-put ov 'face 'idlwave-shell-bp-face))
+ (t
+ ;; No marking
+ nil)))
+ ov))
+
+(defun idlwave-shell-edit-default-command-line (arg)
+ "Edit the current execute command."
+ (interactive "P")
+ (setq idlwave-shell-command-line-to-execute
+ (read-string "IDL> " idlwave-shell-command-line-to-execute)))
+
+(defun idlwave-shell-execute-default-command-line (arg)
+ "Execute a command line. On first use, ask for the command.
+Also with prefix arg, ask for the command. You can also uase the command
+`idlwave-shell-edit-default-command-line' to edit the line."
+ (interactive "P")
+ (if (or (not idlwave-shell-command-line-to-execute)
+ arg)
+ (setq idlwave-shell-command-line-to-execute
+ (read-string "IDL> " idlwave-shell-command-line-to-execute)))
+ (idlwave-shell-reset nil)
+ (idlwave-shell-send-command idlwave-shell-command-line-to-execute
+ '(idlwave-shell-redisplay 'hide)))
+
+(defun idlwave-shell-save-and-run ()
+ "Save file and run it in IDL.
+Runs `save-buffer' and sends a '.RUN' command for the associated file to IDL.
+When called from the shell buffer, re-run the file which was last handled by
+one of the save-and-.. commands."
+ (interactive)
+ (idlwave-shell-save-and-action 'run))
+
+(defun idlwave-shell-save-and-compile ()
+ "Save file and run it in IDL.
+Runs `save-buffer' and sends '.COMPILE' command for the associated file to IDL.
+When called from the shell buffer, re-compile the file which was last handled by
+one of the save-and-.. commands."
+ (interactive)
+ (idlwave-shell-save-and-action 'compile))
+
+(defun idlwave-shell-save-and-batch ()
+ "Save file and batch it in IDL.
+Runs `save-buffer' and sends a '@file' command for the associated file to IDL.
+When called from the shell buffer, re-batch the file which was last handled by
+one of the save-and-.. commands."
+ (interactive)
+ (idlwave-shell-save-and-action 'batch))
+
+(defun idlwave-shell-save-and-action (action)
+ "Save file and compile it in IDL.
+Runs `save-buffer' and sends a '.RUN' command for the associated file to IDL.
+When called from the shell buffer, re-compile the file which was last
+handled by this command."
+ ;; Remove the stop overlay.
+ (if idlwave-shell-stop-line-overlay
+ (delete-overlay idlwave-shell-stop-line-overlay))
+ (setq overlay-arrow-string nil)
+ (let (buf)
+ (cond
+ ((eq major-mode 'idlwave-mode)
+ (save-buffer)
+ (setq idlwave-shell-last-save-and-action-file (buffer-file-name)))
+ (idlwave-shell-last-save-and-action-file
+ (if (setq buf (idlwave-get-buffer-visiting
+ idlwave-shell-last-save-and-action-file))
+ (save-excursion
+ (set-buffer buf)
+ (save-buffer))))
+ (t (setq idlwave-shell-last-save-and-action-file
+ (read-file-name "File: ")))))
+ (if (file-regular-p idlwave-shell-last-save-and-action-file)
+ (progn
+ (idlwave-shell-send-command
+ (concat (cond ((eq action 'run) ".run ")
+ ((eq action 'compile) ".compile ")
+ ((eq action 'batch) "@")
+ (t (error "Unknown action %s" action)))
+ idlwave-shell-last-save-and-action-file)
+ nil nil)
+ (idlwave-shell-bp-query))
+ (let ((msg (format "No such file %s"
+ idlwave-shell-last-save-and-action-file)))
+ (setq idlwave-shell-last-save-and-action-file nil)
+ (error msg))))
+
+(defvar idlwave-shell-sources-query "help,/source"
+ "IDL command to obtain source files for compiled procedures.")
+
+(defvar idlwave-shell-sources-alist nil
+ "Alist of IDL procedure names and compiled source files.
+Elements of the alist have the form:
+
+ (module name . (source-file-truename idlwave-internal-filename)).")
+
+(defun idlwave-shell-sources-query ()
+ "Determine source files for IDL compiled procedures.
+Queries IDL using the string in `idlwave-shell-sources-query'."
+ (interactive)
+ (idlwave-shell-send-command idlwave-shell-sources-query
+ 'idlwave-shell-sources-filter
+ 'hide))
+
+(defun idlwave-shell-sources-filter ()
+ "Get source files from `idlwave-shell-sources-query' output.
+Create `idlwave-shell-sources-alist' consisting of
+list elements of the form:
+ (module name . (source-file-truename idlwave-internal-filename))."
+ (save-excursion
+ (set-buffer (get-buffer-create idlwave-shell-bp-buffer))
+ (erase-buffer)
+ (insert idlwave-shell-command-output)
+ (goto-char (point-min))
+ (let (cpro cfun)
+ (if (re-search-forward "Compiled Procedures:" nil t)
+ (progn
+ (forward-line) ; Skip $MAIN$
+ (setq cpro (point))))
+ (if (re-search-forward "Compiled Functions:" nil t)
+ (progn
+ (setq cfun (point))
+ (setq idlwave-shell-sources-alist
+ (append
+ ;; compiled procedures
+ (progn
+ (beginning-of-line)
+ (narrow-to-region cpro (point))
+ (goto-char (point-min))
+ (idlwave-shell-sources-grep))
+ ;; compiled functions
+ (progn
+ (widen)
+ (goto-char cfun)
+ (idlwave-shell-sources-grep)))))))))
+
+(defun idlwave-shell-sources-grep ()
+ (save-excursion
+ (let ((al (list nil)))
+ (while (and
+ (not (progn (forward-line) (eobp)))
+ (re-search-forward
+ "\\s-*\\(\\S-+\\)\\s-+\\(\\S-+\\)" nil t))
+ (nconc al
+ (list
+ (cons
+ (buffer-substring ; name
+ (match-beginning 1) (match-end 1))
+ (let ((internal-filename
+ (buffer-substring ; source
+ (match-beginning 2) (match-end 2))))
+ (list
+ (idlwave-shell-file-name internal-filename)
+ internal-filename))
+ ))))
+ (cdr al))))
+
+
+(defun idlwave-shell-clear-all-bp ()
+ "Remove all breakpoints in IDL."
+ (interactive)
+ (idlwave-shell-send-command
+ idlwave-shell-bp-query
+ '(progn
+ (idlwave-shell-filter-bp)
+ (mapcar 'idlwave-shell-clear-bp idlwave-shell-bp-alist))
+ 'hide))
+
+(defun idlwave-shell-list-all-bp ()
+ "List all breakpoints in IDL."
+ (interactive)
+ (idlwave-shell-send-command
+ idlwave-shell-bp-query))
+
+(defvar idlwave-shell-error-last 0
+ "Position of last syntax error in `idlwave-shell-error-buffer'.")
+
+(defun idlwave-shell-goto-next-error ()
+ "Move point to next IDL syntax error."
+ (interactive)
+ (let (frame col)
+ (save-excursion
+ (set-buffer idlwave-shell-error-buffer)
+ (goto-char idlwave-shell-error-last)
+ (if (or (re-search-forward idlwave-shell-syntax-error nil t)
+ (re-search-forward idlwave-shell-other-error nil t))
+ (progn
+ (setq frame
+ (list
+ (save-match-data
+ (idlwave-shell-file-name
+ (buffer-substring (match-beginning 1) (match-end 1))))
+ (string-to-int
+ (buffer-substring (match-beginning 2)
+ (match-end 2)))))
+ ;; Try to find the column of the error
+ (save-excursion
+ (setq col
+ (if (re-search-backward "\\^" nil t)
+ (current-column)
+ 0)))))
+ (setq idlwave-shell-error-last (point)))
+ (if frame
+ (progn
+ (idlwave-shell-display-line frame col))
+ (beep)
+ (message "No more errors."))))
+
+(defun idlwave-shell-file-name (name)
+ "If idlwave-shell-use-truename is non-nil, convert file name to true name.
+Otherwise, just expand the file name."
+ (let ((def-dir (if (eq major-mode 'idlwave-shell-mode)
+ default-directory
+ idlwave-shell-default-directory)))
+ (if idlwave-shell-use-truename
+ (file-truename name def-dir)
+ (expand-file-name name def-dir))))
+
+
+;; Keybindings --------------------------------------------------------------
+
+(defvar idlwave-shell-mode-map (copy-keymap comint-mode-map)
+ "Keymap for idlwave-mode.")
+(defvar idlwave-shell-mode-prefix-map (make-sparse-keymap))
+(fset 'idlwave-shell-mode-prefix-map idlwave-shell-mode-prefix-map)
+
+;(define-key idlwave-shell-mode-map "\M-?" 'comint-dynamic-list-completions)
+;(define-key idlwave-shell-mode-map "\t" 'comint-dynamic-complete)
+(define-key idlwave-shell-mode-map "\t" 'idlwave-shell-complete)
+(define-key idlwave-shell-mode-map "\M-\t" 'idlwave-shell-complete)
+(define-key idlwave-shell-mode-map "\C-c\C-s" 'idlwave-shell)
+(define-key idlwave-shell-mode-map "\C-c?" 'idlwave-routine-info)
+(define-key idlwave-shell-mode-map "\C-c\C-i" 'idlwave-update-routine-info)
+(define-key idlwave-shell-mode-map "\C-c=" 'idlwave-resolve)
+(define-key idlwave-shell-mode-map "\C-c\C-v" 'idlwave-find-module)
+(define-key idlwave-shell-mode-map idlwave-shell-prefix-key
+ 'idlwave-shell-debug-map)
+
+;; The following set of bindings is used to bind the debugging keys.
+;; If `idlwave-shell-activate-prefix-keybindings' is non-nil, the first key
+;; in the list gets bound the C-c C-d prefix map.
+;; If `idlwave-shell-activate-alt-keybindings' is non-nil, the second key
+;; in the list gets bound directly in both idlwave-mode-map and
+;; idlwave-shell-mode-map.
+
+;; Used keys: abcde hi klmnopqrs u wxyz
+;; Unused keys: fg j t v
+(let ((specs
+ '(([(control ?b)] [(alt ?b)] idlwave-shell-break-here)
+ ([(control ?i)] [(alt ?i)] idlwave-shell-break-in)
+ ([(control ?d)] [(alt ?d)] idlwave-shell-clear-current-bp)
+ ([(control ?a)] [(alt ?a)] idlwave-shell-clear-all-bp)
+ ([(control ?s)] [(alt ?s)] idlwave-shell-step)
+ ([(control ?n)] [(alt ?n)] idlwave-shell-stepover)
+ ([(control ?k)] [(alt ?k)] idlwave-shell-skip)
+ ([(control ?u)] [(alt ?u)] idlwave-shell-up)
+ ([(control ?o)] [(alt ?o)] idlwave-shell-out)
+ ([(control ?m)] [(alt ?m)] idlwave-shell-return)
+ ([(control ?h)] [(alt ?h)] idlwave-shell-to-here)
+ ([(control ?r)] [(alt ?r)] idlwave-shell-cont)
+ ([(control ?y)] [(alt ?y)] idlwave-shell-execute-default-command-line)
+ ([(control ?z)] [(alt ?z)] idlwave-shell-reset)
+ ([(control ?q)] [(alt ?q)] idlwave-shell-quit)
+ ([(control ?p)] [(alt ?p)] idlwave-shell-print)
+ ([(??)] [(alt ??)] idlwave-shell-help-expression)
+ ([(control ?c)] [(alt ?c)] idlwave-shell-save-and-run)
+ ([( ?@)] [(alt ?@)] idlwave-shell-save-and-batch)
+ ([(control ?x)] [(alt ?x)] idlwave-shell-goto-next-error)
+ ([(control ?e)] [(alt ?e)] idlwave-shell-run-region)
+ ([(control ?w)] [(alt ?w)] idlwave-shell-resync-dirs)
+ ([(control ?l)] [(alt ?l)] idlwave-shell-redisplay)
+ ([(control ?t)] [(alt ?t)] idlwave-shell-toggle-toolbar)
+ ([(control up)] [(alt up)] idlwave-shell-stack-up)
+ ([(control down)] [(alt down)] idlwave-shell-stack-down)))
+ s k1 k2 cmd)
+ (while (setq s (pop specs))
+ (setq k1 (nth 0 s)
+ k2 (nth 1 s)
+ cmd (nth 2 s))
+ (when idlwave-shell-activate-prefix-keybindings
+ (and k1 (define-key idlwave-shell-mode-prefix-map k1 cmd)))
+ (when idlwave-shell-activate-alt-keybindings
+ (and k2 (define-key idlwave-mode-map k2 cmd))
+ (and k2 (define-key idlwave-shell-mode-map k2 cmd)))))
+
+;; Enter the prefix map at the two places.
+(fset 'idlwave-debug-map idlwave-shell-mode-prefix-map)
+(fset 'idlwave-shell-debug-map idlwave-shell-mode-prefix-map)
+
+;; The Menus --------------------------------------------------------------
+
+(defvar idlwave-shell-menu-def
+ '("Debug"
+ ["Save and .RUN" idlwave-shell-save-and-run
+ (or (eq major-mode 'idlwave-mode)
+ idlwave-shell-last-save-and-action-file)]
+ ["Save and .COMPILE" idlwave-shell-save-and-compile
+ (or (eq major-mode 'idlwave-mode)
+ idlwave-shell-last-save-and-action-file)]
+ ["Save and @Batch" idlwave-shell-save-and-batch
+ (or (eq major-mode 'idlwave-mode)
+ idlwave-shell-last-save-and-action-file)]
+ ["Goto Next Error" idlwave-shell-goto-next-error t]
+ "--"
+ ["Execute Default Cmd" idlwave-shell-execute-default-command-line t]
+ ["Edit Default Cmd" idlwave-shell-edit-default-command-line t]
+ "--"
+ ["Set Breakpoint" idlwave-shell-break-here
+ (eq major-mode 'idlwave-mode)]
+ ["Break in Module" idlwave-shell-break-in t]
+ ["Clear Breakpoint" idlwave-shell-clear-current-bp t]
+ ["Clear All Breakpoints" idlwave-shell-clear-all-bp t]
+ ["List All Breakpoints" idlwave-shell-list-all-bp t]
+ "--"
+ ["Step (into)" idlwave-shell-step t]
+ ["Step (over)" idlwave-shell-stepover t]
+ ["Skip One Statement" idlwave-shell-skip t]
+ ["Continue" idlwave-shell-cont t]
+ ("Continue to"
+ ["End of Block" idlwave-shell-up t]
+ ["End of Subprog" idlwave-shell-return t]
+ ["End of Subprog+1" idlwave-shell-out t]
+ ["Here (Cursor Line)" idlwave-shell-to-here
+ (eq major-mode 'idlwave-mode)])
+ "--"
+ ["Print expression" idlwave-shell-print t]
+ ["Help on expression" idlwave-shell-help-expression t]
+ ["Evaluate Region" idlwave-shell-evaluate-region
+ (eq major-mode 'idlwave-mode)]
+ ["Run Region" idlwave-shell-run-region (eq major-mode 'idlwave-mode)]
+ "--"
+ ["Redisplay" idlwave-shell-redisplay t]
+ ["Stack Up" idlwave-shell-stack-up t]
+ ["Stack Down" idlwave-shell-stack-down t]
+ "--"
+ ["Update Working Dir" idlwave-shell-resync-dirs t]
+ ["Reset IDL" idlwave-shell-reset t]
+ "--"
+ ["Toggle Toolbar" idlwave-shell-toggle-toolbar t]
+ ["Exit IDL" idlwave-shell-quit t]))
+
+(if (or (featurep 'easymenu) (load "easymenu" t))
+ (progn
+ (easy-menu-define
+ idlwave-shell-mode-menu idlwave-shell-mode-map "IDL shell menus"
+ idlwave-shell-menu-def)
+ (easy-menu-define
+ idlwave-mode-debug-menu idlwave-mode-map "IDL debugging menus"
+ idlwave-shell-menu-def)
+ (save-excursion
+ (mapcar (lambda (buf)
+ (set-buffer buf)
+ (if (eq major-mode 'idlwave-mode)
+ (progn
+ (easy-menu-remove idlwave-mode-debug-menu)
+ (easy-menu-add idlwave-mode-debug-menu))))
+ (buffer-list)))))
+
+;; The Breakpoint Glyph -------------------------------------------------------
+
+(defvar idlwave-shell-bp-glyph nil
+ "The glyph to mark breakpoint lines in the source code.")
+
+(let ((image-string "/* XPM */
+static char * file[] = {
+\"14 12 3 1\",
+\" c #FFFFFFFFFFFF s backgroundColor\",
+\". c #4B4B4B4B4B4B\",
+\"R c #FFFF00000000\",
+\" \",
+\" \",
+\" RRRR \",
+\" RRRRRR \",
+\" RRRRRRRR \",
+\" RRRRRRRR \",
+\" RRRRRRRR \",
+\" RRRRRRRR \",
+\" RRRRRR \",
+\" RRRR \",
+\" \",
+\" \"};"))
+
+ (setq idlwave-shell-bp-glyph
+ (cond ((and (featurep 'xemacs)
+ (featurep 'xpm))
+ (make-glyph image-string))
+ ((and (not (featurep 'xemacs))
+ (fboundp 'image-type-available-p)
+ (image-type-available-p 'xpm))
+ (list 'image :type 'xpm :data image-string))
+ (t nil))))
+
+(provide 'idlwave-shell)
+
+;;; Load the toolbar when wanted by the user.
+
+(defun idlwave-shell-toggle-toolbar ()
+ "Toggle the display of the debugging toolbar."
+ (interactive)
+ (if (featurep 'idlwave-toolbar)
+ (idlwave-toolbar-toggle)
+ (require 'idlwave-toolbar)
+ (idlwave-toolbar-toggle)))
+
+
+(when idlwave-shell-use-toolbar
+ (or (load "idlwave-toolbar" t)
+ (message
+ "Tried to load file `idlwave-toolbar.el', but file does not exist")))
+
+;;; idlwave-shell.el ends here
+
+