==========
Handy Emacs utilities
+
+I've been using Emacs since it was publicly available (1985 or 1986), and have contributed some items which are included with Emacs, notably the [Allout outliner](http://myriadicity.net/software-and-systems/craft/emacs-allout), [icomplete mode](http://www.emacswiki.org/emacs/IcompleteMode). And python-mode's [pdbtrack functionality](http://myriadicity.net/software-and-systems/craft/crafty-hacks#section-1). Like many long-time Emacs users, I've got some personal custom code, some of which I wouldn't do without. Here's some - I hope to include more that I think would be useful to others, as time allows.
+
+* **pdbtrack.el**
+ * Add sensitivity to comint shells so the source file lines are automatically
+ presented in a separate window when the Python PDB debugger steps to them.
+
+ This is derived from the pdb tracking code, which I originally wrote, and
+ which has been included in (various) official Emacs Python modes. I wanted
+ a version that I could more easily tweak and maintain, independently of
+ the python-mode code.
+
+ It would eventually be nice to generalize this code, to work for things
+ like the node.js debugger. We'll see if I (or anyone) ever gets around to
+ that.
+
+
+* **poptoshell.el**
+ * I use the emacs shell a lot. This code enables me to streamline and
+ extend how I can a single one, or multiple ones in a project-oriented
+ fashion:
+
+ * It simplifies getting to the input prompt, by doing the right thing when
+ I hit the key I have bound to pop-to-shell (I use [M-space], ie
+ meta-space:
+ * If the cursor is in a buffer that has no subprocess, pop the window to
+ the primary shell buffer
+ * If there is no shell buffer, start one.
+ * If the cursor is in a buffer which has a process, move the cursor to
+ the process input point.
+ * With a universal argument, even if the current buffer has a subprocess,
+ solicit the name of the target shell buffer - defaulting to the current
+ main one - and pop to that.
+ * This enables starting an alternate shell buffer, for instance, and/or
+ switching between the main and alternate ones.
+ * (The expected name is without the surrounding asterisks, and
+ completion is done against existing shell buffer names stripped of
+ their asterisks.)
+ * With a doubled universal arg, prompt for the target shell buffer and
+ set the provided name as the primary shell buffer.
+ The last few things enable a kind of project-focus mode. I often have
+ various shell buffers, each one associated with a project. As I switch
+ which project is currently my primary focus, I use the double universal
+ argument to switch which shell buffer is the default. I can still use the
+ single universal argument to easily switch to any of the shells, but most
+ easily to my current primary.
--- /dev/null
+;;; pdbtrack - Track source file lines as you run python/pdb in an emacs shell.
+
+;;; Standalone Python PDB dynamic file tracking.
+
+(define-minor-mode pdbtrack-minor-mode
+ "Show lines in source file when Python PDB debugger steps through them."
+ nil ":PDBtrack" :require 'pdbtrack :version "2.1"
+
+ (add-hook 'comint-output-filter-functions
+ 'pdbtrack-comint-output-filter-function)
+ (make-local-variable 'pdbtrack-buffers-to-kill)
+ (make-local-variable 'pdbtrack-tracked-buffer)
+)
+
+(defcustom pdbtrack-stacktrace-info-regexp
+ "> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
+ "Regular Expression matching stacktrace information.
+Used to extract the current line and module being inspected."
+ :type 'string
+ :group 'python
+ :safe 'stringp)
+
+(defvar pdbtrack-tracked-buffer nil
+ "Variable containing the value of the current tracked buffer.
+Never set this variable directly, use
+`pdbtrack-set-tracked-buffer' instead.")
+
+(defcustom pdbtrack-remove-new-buffers-after-tracking t
+ "Remove buffers visited for the sake of tracking, on pdb session conclusion."
+ :type 'boolean
+ :group 'python)
+(defvar pdbtrack-buffers-to-kill nil
+ "List of buffers to be deleted after tracking finishes.")
+
+(defun pdbtrack-set-tracked-buffer (file-name)
+ "Set the buffer for FILE-NAME as the tracked buffer.
+Internally it uses the `pdbtrack-tracked-buffer' variable.
+Returns the tracked buffer."
+ (let ((file-buffer (get-file-buffer
+ (concat (file-remote-p default-directory)
+ file-name))))
+ (if file-buffer
+ (setq pdbtrack-tracked-buffer file-buffer)
+ (setq file-buffer (find-file-noselect file-name))
+ (when (not (member file-buffer pdbtrack-buffers-to-kill))
+ (add-to-list 'pdbtrack-buffers-to-kill file-buffer)))
+ file-buffer))
+
+(defun pdbtrack-comint-output-filter-function (output)
+ "Move overlay arrow to current pdb line in tracked buffer.
+Argument OUTPUT is a string with the output from the comint process."
+ (when (and pdbtrack-minor-mode (not (string= output "")))
+ (let* ((full-output (ansi-color-filter-apply
+ (buffer-substring comint-last-input-end (point-max))))
+ (line-number)
+ (file-name
+ (with-temp-buffer
+ (insert full-output)
+ ;; When the debugger encounters a pdb.set_trace()
+ ;; command, it prints a single stack frame. Sometimes
+ ;; it prints a bit of extra information about the
+ ;; arguments of the present function. When ipdb
+ ;; encounters an exception, it prints the _entire_ stack
+ ;; trace. To handle all of these cases, we want to find
+ ;; the _last_ stack frame printed in the most recent
+ ;; batch of output, then jump to the corresponding
+ ;; file/line number.
+ (goto-char (point-max))
+ (when (re-search-backward pdbtrack-stacktrace-info-regexp nil t)
+ (setq line-number (string-to-number
+ (match-string-no-properties 2)))
+ (match-string-no-properties 1)))))
+ (if (and file-name line-number)
+ (let* ((tracked-buffer
+ (pdbtrack-set-tracked-buffer file-name))
+ (shell-buffer (current-buffer))
+ (tracked-buffer-window (get-buffer-window tracked-buffer))
+ (tracked-buffer-line-pos))
+ (with-current-buffer tracked-buffer
+ (set (make-local-variable 'overlay-arrow-string) "=>")
+ (set (make-local-variable 'overlay-arrow-position) (make-marker))
+ (setq tracked-buffer-line-pos (progn
+ (goto-char (point-min))
+ (forward-line (1- line-number))
+ (point-marker)))
+ (when tracked-buffer-window
+ (set-window-point
+ tracked-buffer-window tracked-buffer-line-pos))
+ (set-marker overlay-arrow-position tracked-buffer-line-pos))
+ (pop-to-buffer tracked-buffer)
+ (switch-to-buffer-other-window shell-buffer))
+ (when pdbtrack-tracked-buffer
+ (with-current-buffer pdbtrack-tracked-buffer
+ (set-marker overlay-arrow-position nil))
+ (when (not pdbtrack-remove-new-buffers-after-tracking)
+ (mapc #'(lambda (buffer)
+ (ignore-errors (kill-buffer buffer)))
+ pdbtrack-buffers-to-kill))
+ (setq pdbtrack-tracked-buffer nil
+ pdbtrack-buffers-to-kill nil)))))
+ output)
+
+(provide 'pdbtrack)
--- /dev/null
+;;; poptoshell.el --- get to the process buffer and input mark
+
+;; Copyright (C) 1999-2011 Free Software Foundation, Inc. and Ken Manheimer
+
+;; Author: Ken Manheimer <ken dot manheimer at gmail...>
+;; Maintainer: Ken Manheimer <ken dot manheimer at gmail...>
+;; Created: 1999 -- first public release
+;; Keywords: processes
+;; Website: http://myriadicity.net/software-and-systems/craft/crafty-hacks/emacs-sundries/poptoshell.el/view
+;;
+;;; Commentary:
+;;
+;; I bind to M-<space>, via eg: (global-set-key "\M- " 'pop-to-shell)
+;; See the pop-to-shell docstring for details.
+;;
+;; klm, 02/09/1999.
+
+(defvar non-interactive-process-buffers '("*compilation*" "*grep*"))
+
+(require 'comint)
+(require 'shell)
+
+(provide 'poptoshell)
+
+(defcustom pop-to-shell-frame nil
+ "*If non-nil, jump to a frame already showing the shell, if any.
+
+Otherwise, open a new window in the current frame."
+ :type 'boolean
+ :group 'comint)
+
+(defvar pop-to-shell-primary-name "*shell*"
+ "Shell name to use for un-modified pop-to-shell buffer target.")
+
+(defun pop-to-shell (&optional arg)
+
+ "Navigate to or within a shell buffer.
+
+Use this command from within a shell subprocess buffer to get to
+the shell input point, or from outside a shell buffer to pop to a
+shell buffer, without displacing the current buffer.
+
+Specifically, like 'shell' command but:
+
+ - If the current buffer is associated with a subprocess (and one not
+ among those named on `non-interactive-process-buffers'), then focus
+ is moved to the process input point, else...
+ - Goes to a window that is already showing a shell buffer, if any.
+ In this case, the cursor is left in its prior position in the shell
+ buffer. (Repeating the command will then go to the process input
+ point, by the behavior mentioned just before this.) Else...
+ - Pops open a new shell buffer, if none is around.
+
+In any cases where the shell buffer already existed, the process
+is resumed if it was stopped.
+
+Further,
+
+ - With a universal argument, the user is prompted for the buffer name to
+ use (it will be bracketed by asterisks - a regrettable comint
+ requirement), defaulting to 'shell'. This makes it easy to switch
+ between multiple process-associated buffers.
+ - A double universal argument will set the default target shell buffer name
+ to the provided one, making the target shell subsequently primary."
+
+ (interactive "P")
+
+ (if (not (boundp 'shell-buffer-name))
+ (setq shell-buffer-name "*shell*"))
+
+ (let* ((from (current-buffer))
+ (doublearg (equal arg '(16)))
+ (temp (if arg
+ (read-shell-buffer-name-sans
+ (format "Shell buffer name [%s]%s "
+ pop-to-shell-primary-name
+ (if doublearg " <==" ":"))
+ pop-to-shell-primary-name)
+ pop-to-shell-primary-name))
+ ;; Make sure it is bracketed with asterisks; silly.
+ (target-shell-buffer-name (if (string= temp "")
+ pop-to-shell-primary-name
+ (bracket-asterisks temp)))
+ (curr-buff-proc (or (get-buffer-process from)
+ (and (fboundp 'rcirc-buffer-process)
+ (rcirc-buffer-process))
+ (and (boundp 'erc-process)
+ erc-process)))
+ (buff (if (and curr-buff-proc
+ (not (member (buffer-name from)
+ non-interactive-process-buffers)))
+ from
+ (get-buffer target-shell-buffer-name)))
+ (inwin nil)
+ (num 0)
+ already-there)
+ (when doublearg
+ (setq pop-to-shell-primary-name target-shell-buffer-name))
+ (if (and curr-buff-proc
+ (not arg)
+ (eq from buff)
+ (not (eq target-shell-buffer-name (buffer-name from))))
+ ;; We're in a buffer with a shell process, but not named shell
+ ;; - stick with it, but go to end:
+ (setq already-there t)
+ (cond
+ ; Already in the shell buffer:
+ ((string= (buffer-name) target-shell-buffer-name)
+ (setq already-there t))
+ ((or (not buff)
+ (not (catch 'got-a-vis
+ (my-walk-windows
+ (function (lambda (win)
+ (if (and (eq (window-buffer win) buff)
+ (equal (frame-parameter
+ (selected-frame) 'display)
+ (frame-parameter
+ (window-frame win) 'display)))
+ (progn (setq inwin win)
+ (throw 'got-a-vis win))
+ (setq num (1+ num)))))
+ nil 'visible t)
+ nil)))
+ ;; No preexisting shell buffer, or not in a visible window:
+ (pop-to-buffer target-shell-buffer-name pop-up-windows))
+ ;; Buffer exists and already has a window - jump to it:
+ (t (if (and pop-to-shell-frame
+ inwin
+ (not (equal (window-frame (selected-window))
+ (window-frame inwin))))
+ (select-frame-set-input-focus (window-frame inwin)))
+ (if (not (string= (buffer-name (current-buffer))
+ target-shell-buffer-name))
+ (pop-to-buffer target-shell-buffer-name t))))
+ (condition-case err
+ (if (not (comint-check-proc (current-buffer)))
+ (start-shell-in-buffer (buffer-name (current-buffer))))
+ (file-error
+ ;; Whoops - can't get to the default directory, keep trying
+ ;; superior ones till we get somewhere that's acceptable:
+ (while (and (not (string= default-directory ""))
+ (not (condition-case err (progn (shell) t)
+ (file-error nil))))
+ (setq default-directory
+ (file-name-directory
+ (substring default-directory
+ 0
+ (1- (length default-directory)))))))
+ ))
+ ;; If the destination buffer has a stopped process, resume it:
+ (let ((process (get-buffer-process (current-buffer))))
+ (if (and process (equal 'stop (process-status process)))
+ (continue-process process)))
+ (if (and (not already-there)
+ (not (equal (current-buffer) from)))
+ t
+ (goto-char (point-max))
+ (and (get-buffer-process from)
+ (goto-char (process-mark (get-buffer-process from)))))
+ )
+ )
+(defun my-walk-windows (func &optional minibuf all-frames selected)
+ (if (featurep 'xemacs)
+ (walk-windows func minibuf all-frames (selected-device))
+ (walk-windows func minibuf all-frames)))
+
+(defun my-set-mouse-position (window x y)
+ "Adapt for both xemacs and fsf emacs"
+ (if (string= (substring (emacs-version) 0 6) "XEmacs")
+ (set-mouse-position window x y)
+ (let ((frame (window-frame window)))
+ (select-frame-set-input-focus frame))))
+
+
+(defun read-shell-buffer-name-sans (prompt default)
+ "Obtain name without asterisks of shell buffer, adding the asterisks.
+
+Return indicated default on empty input."
+ (let ((got
+ (completing-read
+ prompt
+ (filter 'identity
+ (mapcar (lambda (buffer)
+ (let ((name (buffer-name buffer)))
+ (if (with-current-buffer buffer
+ (eq major-mode 'shell-mode))
+ (if (> (length name) 2)
+ (substring name 1 (1- (length
+ name)))
+ name))))
+ (buffer-list))))))
+ (if (not (string= got "")) (bracket-asterisks got) default)))
+
+(defun bracket-asterisks (name)
+ "Return a copy of name, ensuring it has an asterisk at the beginning and end."
+ (if (not (string= (substring name 0 1) "*"))
+ (setq name (concat "*" name)))
+ (if (not (string= (substring name -1) "*"))
+ (setq name (concat name "*")))
+ name)
+(defun unbracket-asterisks (name)
+ "Return a copy of name, removing asterisks at beg and end, if any."
+ (if (string= (substring name 0 1) "*")
+ (setq name (substring name 1)))
+ (if (string= (substring name -1) "*")
+ (setq name (substring name 0 -1)))
+ name)
+(defun start-shell-in-buffer (buffer-name)
+ ;; Damn comint requires buffer name be bracketed by "*" asterisks.
+ (require 'comint)
+ (require 'shell)
+
+ (let* ((buffer buffer-name)
+ (prog (or explicit-shell-file-name
+ (getenv "ESHELL")
+ (getenv "SHELL")
+ "/bin/sh"))
+ (name (file-name-nondirectory prog))
+ (startfile (concat "~/.emacs_" name))
+ (xargs-name (intern-soft (concat "explicit-" name "-args"))))
+ (setq buffer (set-buffer (apply 'make-comint
+ (unbracket-asterisks buffer-name)
+ prog
+ (if (file-exists-p startfile)
+ startfile)
+ (if (and xargs-name
+ (boundp xargs-name))
+ (symbol-value xargs-name)
+ '("-i")))))
+ (set-buffer buffer-name)
+ (shell-mode)))