Skip to content
Bar edited this page Oct 30, 2015 · 10 revisions

Table of Contents

Purpose detection

For Purpose to work, it needs to determine the purpose of buffers and windows. The following functions are of interest:

  • purpose-window-purpose: return a window's purpose
  • purpose-buffer-purpose: return a buffer's purpose
  • purpose-windows-with-purpose: return a list of windows that have a certain purpose
  • purpose-buffers-with-purpose: return a list of buffers that have a certain purpose

Determining a window's purpose

Determining a window's purpose is easy - it's always the same as the purpose of the buffer displayed in it.
If a window's buffer was changed from a buffer with purpose 'foo to a buffer with purpose 'bar, then the window's purpose also changed from 'foo to 'bar.

Determining a buffer's purpose

Determining a buffer's purpose is more tricky, and is decided by the buffer's name and by its major mode. There are several checks, and the first check that finds a proper purpose - that's the buffer's purpose.
These are the checks:

  1. Is it a dummy buffer?
    If the buffer's name starts with "*pu-dummy-" and ends with "*", then its a dummy buffer. For example, a buffer named *pu-dummy-edit* has the purpose 'edit

  2. Is the buffer's exact name matched to a purpose (user settings)?
    The variable purpose--user-name-purposes holds a mapping of names to purposes. If the buffer's name matches exactly one of the names in purpose--user-name-purposes, return the name's purpose.
    For example, if purpose--user-name-purposes maps "*shell*" to purpose 'terminal, then a buffer named *shell* has the purpose 'terminal.

  3. Does the buffer's name match a regular expression (user settings)?
    The variable purpose--user-regexp-purposes holds a mapping of regexps to purposes. If the buffer's name matches a regexp in purpose--user-regexp-purposes, return the regexp's purpose.
    For example, if purpose--userregexp-purposes maps ".py$" to purpose 'python, then a buffer named foo.py has the purpose 'python.
    If a name matches more than one regexp, then the purpose of one of those regexps will be returned, by which one is not defined.

  4. Is the buffer's major mode matched to a purpose (user settings)?
    The variable purpose--user-mode-purposes holds a mapping of major modes to purposes. If the buffer's mode is one of the modes in purpose--user-mode-purposes, or is derived from one them, return that mode's purpose. If a mode matches more than one entry in purpose--user-mode-purposes, then the purpose of the most direct mode is used.
    For example, if purpose--user-mode-purposes maps 'prog-mode to purpose 'edit, and maps 'python-mode to purpose 'python, then a buffer with major mode 'emacs-lisp-mode has the purpose 'edit, but a buffer with major mode 'python-mode has the purpose 'python.

  5. Is the buffer's exact name matched to a purpose (extensions settings)?
    Like step 2, but check purpose--extended-name-purposes instead of purpose--user-name-purposes.

  6. Does the buffer's name match a regular expression (extensions settings)?
    Like step 3, but check purpose--extended-regexp-purposes instead of purpose--user-regexp-purposes.

  7. Is the buffer's major mode matched to a purpose (extensions settings)?
    Like step 4, but check purpose--extended-mode-purposes instead of purpose--user-mode-purposes.

  8. If purpose-use-default-configuration is non-nil, check also:

  9. Is the buffer's exact name matched to a purpose (default settings)?
    Like step 2, but check purpose--default-name-purposes instead of purpose--user-name-purposes.

  10. Does the buffer's name match a regular expression (default settings)?
    Like step 3, but check purpose--default-regexp-purposes instead of purpose--user-regexp-purposes.

  11. Is the buffer's major mode matched to a purpose (default settings)?
    Like step 4, but check purpose--default-mode-purposes instead of purpose--user-mode-purposes.

  12. If the buffer doesn't match any of the above, return default-purpose (equals 'general, unless customized by the user).


Display and window choice

Window dedication

A window can be dedicated to its buffer or to its purpose. A window that is dedicated to its buffer will not be used for displaying other buffers. A window that is dedicated to its purpose can be used for displaying other buffers, but only if they have the same purpose as the window's current buffer.
The relevant functions are:

  • window-dedicated-p, set-window-dedicated-p: get/set the window's buffer-dedication state
  • purpose-window-purpose-dedicated-p, purpose-set-window-purpose-dedicated-p: get/set the window's purpose-dedication state

Display and selection functions

Display functions (level 1)

Display functions are the functions that do the actual job of finding a window and displaying a buffer in it. However, each of these display functions implements one way to search for a window. For example, the function purpose-display-reuse-window-purpose only searches for existing windows that already have the same purpose as the buffer we want to display.
A display function takes two arguments: BUFFER - the buffer to display, and ALIST - an alist generated by display-buffer which holds some parameters that may affect the display function's behavior. Each display function returns the window that was used to display the buffer, or nil if it couldn't find a window.

purpose--action-function and purpose-select-buffer (level 2)

purpose--action-function

The function purpose--action-function does the sophisticated job of coordinating which display functions to try. It receives two arguments: BUFFER and ALIST - the same arguments that are passed to the display functions.

The list of display functions that purpose--action-function is going to use is called the action-sequence. The possible action sequences are contained in variable purpose-action-sequences. If ALIST has a action-order entry, then the matching action sequence in purpose-action-sequences will be selected. For example, for action order switch-to-buffer, the action sequence (purpose-display-reuse-window-buffer purpose-display-reuse-window-purpose purpose-display-maybe-same-window purpose-display-maybe-other-window purpose-display-maybe-other-frame purpose-display-maybe-pop-up-window purpose-display-maybe-pop-up-frame) will be used. The default action order is stored in variable purpose-default-action-sequence.

If no display function in the action sequence was able to display the buffer, then a default action is performed. The default fallback action is chosen by the value of purpose-display-fallback. For value pop-up-frame, a new frame is created to display the buffer; for value pop-up-window, a new window is created; for value nil, purpose--action-function returns nil and Emacs' original display mechanism takes over.

special action sequences

Special action sequences are action sequences that are applied only to certain types of buffers and in certain circumstances. In purpose--action-function, if any special action sequences match the buffer to display, then Purpose will atempt to display the buffer using these sequences before trying the normal action sequence. The variable purpose-special-action-sequences contains all existing special action sequences, and can be customized by the user or by extensions. See purpose-special-action-sequences documentation for how to customize it.

user action sequences

If display-buffer receives a non-empty list as its action argument, then the display functions specified by the action argument are considered the "user action sequence". Purpose tries to display the buffer using the user action sequence, before trying the special action sequence and the normal action sequence. This way, the action argument for pop-to-buffer and display-buffer should behave same as in stock Emacs.

purpose-select-buffer

The function purpose-select-buffer displays a buffer by calling display-buffer (which calls purpose--action-function), and then selecting the window returned by display-buffer (which is the same window returned by one of the display functions).
purpose-select-buffer is the bridge between the selection functions (level 3) and purpose--action-function. purpose-select-buffer receives three arguments: BUFFER-OR-NAME, ACTION-ORDER and NORECORD. Argument ACTION-ORDER is passed to purpose--action-function by let-binding purpose--alist, and the other arguments have the same meaning as in switch-to-buffer. All of the selection functions work by calling purpose-select-buffer with the proper ACTION-ORDER argument.

After purpose-select-buffer finishes, it runs the hook purpose-select-buffer-hook.

Selection functions (level 3)

Selection function are functions that the user uses in order to select a new buffer. These functions include:

  • purpose-switch-buffer: Purpose's equivalent for switch-to-buffer
  • purpose-switch-buffer-other-window: Purpose's equivalent for switch-to-buffer-other-window
  • purpose-pop-buffer: Purpose's equivalent for pop-to-buffer
  • purpose-switch-buffer-with-purpose: like purpose-switch-buffer, but only allows the user to choose a buffer that has the same purpose as the current buffer

Overrides and advices

Purpose replaces the original window switching mechanism behavior of Emacs, and because of that it has to override some of Emacs' functions. All the overrides are conditional: if purpose--active-p is t, then Purpose's functions are used, but if purpose--active-p is nil, the original functions are used.
The overriden functions are:

  • switch-to-buffer: Replaced by purpose-switch-buffer, through the advice mechanism
  • switch-to-buffer-other-window: Replaced by purpose-switch-buffer-other-window, through the advice mechanism
  • switch-to-buffer-other-frame: Replaced by purpose-switch-buffer-other-frame, through the advice mechanism
  • pop-to-buffer: Replaced by purpose-pop-buffer, through the advice mechanism
  • pop-to-buffer-same-window: Replaced by purpose-pop-buffer-same-window, through the advice mechanism
  • display-buffer:
    1. Replaced by purpose--action-function, through the variable display-buffer-overriding-action.
    2. Through the advice mechanism, purpose--alist may change according to display-buffer's arguments, before calling display-buffer.

Purpose also provides a way to turn off Purpose temporarily while executing some code:

  • without-purpose: execute some code with Purpose turned off (let-binds purpose--active-p to nil)
  • without-purpose-command: receives a COMMAND as an argument, and returns a new command that calls COMMAND while Purpose is temporarily turned off. Basically, this is (without-purpose (call-interactively COMMAND)).