Skip to content

Purpose Configuration

Bar edited this page Jul 3, 2015 · 13 revisions

Table of Contents

Layers

The "purpose configuration" is a collection of variables that define the purpose of each buffer. The configuration is split into 3 layers: user settings, extensions settings, and default settings. Furthermore, each layer of settings is built from 3 mappings: mode-purposes, name-purposes and regexp-purposes. On top of that, each layer has 2 sets of variables: compiled and interface (non-compiled).

User Settings

The user settings are intended for use by Emacs users (duh!). These settings take precedence over extensions settings and default settings.

Interface variables:

  • purpose-user-mode-purposes: a mapping of modes to purposes. This is an alist that satisfies purpose-mode-alist-p. This is a custom variable and can be changed through the customize interface. When setting this variable from elisp code, you should call purpose-compile-user-configuration for the changes to take effect.
  • purpose-user-name-purposes: a mapping of names to purposes. This is an alist that satisfies purpose-name-alist-p. This is a custom variable and can be changed through the customize interface. When setting this variable from elisp code, you should call purpose-compile-user-configuration for the changes to take effect.
  • purpose-user-regexp-purposes: a mapping of regexps to purposes. This is an alist that satisfies purpose-regexp-alist-p. This is a custom variable and can be changed through the customize interface. When setting this variable from elisp code, you should call purpose-compile-user-configuration for the changes to take effect.

Compiled variables:

  • purpose--user-mode-purposes: this variable is compiled according to purposer-user-mode-purposes when purpose-compile-user-configuration is called, or when purpose-user-mode-purposes is changed through the customize interface. This is a hash-table of mode-purpose pairs.
  • purpose--user-name-purposes: this variable is compiled according to purposer-user-name-purposes when purpose-compile-user-configuration is called, or when purpose-user-name-purposes is changed through the customize interface. This is a hash-table of name-purpose pairs.
  • purpose--user-regexp-purposes: this variable is compiled according to purposer-user-regexp-purposes when purpose-compile-user-configuration is called, or when purpose-user-regexp-purposes is changed through the customize interface. This is a hash-table of regexp-purpose pairs.

Extensions Settings

The extensions settings should be used by code and packages that extend Purpose or need to define their own purpose configuration. The extensions settings have a lower priority than the user settings, and a higher priority than the default settings.

Interface:

  • purpose-conf: purpose-conf is a class (defined with defclass), that contains three members: mode-purposes (satisfies purpose-mode-alist-p), name-purposes (satisfies purpose-name-alist-p) and regexp-purposes (satisfies purpose-regexp-alist-p).
  • purpose-extended-configuration: this is a plist (property-list) that contains purpose-conf objects. You shouldn't change it directly, but use one of the two functions given below. If you do change it directly, you should call purpose-compile-extended-configuration for the changes to take effect.
    For example, the contents of purpose-extended-configuration may look like this:
(list :python (purpose-conf 
                :mode-purposes '((python-mode . python)
                                (python-inferior-mode . interpreter)))
       :popups (purpose-conf
                :mode-purposes '((help-mode . right)
                                 (occur-mode . bottom)
                                 (grep-mode . bottom))))
  • purpose-set-extension-configuration: use this function to add a purpose-conf object to purpose-extended-configuration. This function also calls purpose-compile-extended-configuration.
  • purpose-del-extension-configuration: use this function to remove a purpose-conf object from purpose-extended-configuration. This function also calls purpose-compile-extended-configuration.
  • purpose-compile-extended-configuration: compiles the extensions settings. This generats purpose--extended-mode-purposes, purpose--extended-name-purposes and purpose--extended-regexp-purposes according to purpose-extended-configuration. You don't need to call this function directly - call the two functions given above instead.

Compiled variables:

  • purpose--extended-mode-purposes: this is a hash-table of mode-purpose pairs. It contains the combined mode settings of all purpose-conf objects in purpose-extended-configuration.
  • purpose--extended-name-purposes: this is a hash-table of name-purpose pairs. It contains the combined name settings of all purpose-conf objects in purpose-extended-configuration.
  • purpose--extended-regexp-purposes: this is a hash-table of regexp-purpose pairs. It contains the combined regexp settings of all purpose-conf objects in purpose-extended-configuration.

Example

;; set configuration for some python-related extension
(purpose-set-extension-configuration :python
    (purpose-conf :mode-purposes '((python-mode . python)
                                   (python-inferior-mode . interpreter))
                  :regexp-purposes '(("^test-.*\\.py$" . test))))
;; delete :python's configuration
(purpose-del-extension-configuration :python)

Default Settings

These are the default settings, which have a lower priority than the user settings and extensions settings. The default settings are hard-coded, and are not meant to be changed (though you could if you try hard). The exact default configuration is listed in the function purpose-compile-default-configuration.

  • purpose--default-mode-purposes: a hash-table of mode-purpose pairs.
  • purpose--default-name-purposes: a hash-table of name-purpose pairs.
  • purpose--default-regexp-purposes: a hash-table of regexp-purpose pairs.
  • purpose-compile-default-configuration: this function generates the contents of purpose--default-mode-purposes, purpose--default-name-purposes and purpose--default-regexp-purposes.
  • purpose-use-default-configuration: toggles whether the default settings sould be considered when getting a buffer's purpose. When this is nil, the default settings are ignored. When this is non-nil, the default settings are used for cases where the user settings and the extensions settings don't produce a purpose. This is a custom variable and can be changed through the customize interface.

Default Purpose

Variable default-purpose is the default purpose to use, when a buffer's purpose couldn't be determined by the configuration. This is a custom variable and can be changed through the customize interface. The default value for default-purpose is 'general.


Handling Uniquify

Uniquify is a built-in Emacs feature that ensures different buffer names for buffers that are visiting different files with the same base file name. For example, if you opened two files named "foo.txt", you won't have two buffers with the name "foo.txt". One buffer may be named "foo.txt<bar>", while the other could be named "foo.txt<baz>". The exact style is described in the Emacs manual.

If your purpose configuration depends on buffer names, a buffer's purpose might change when the buffer is uniquified. To overcome this difficulty, copy and use the functions uniquified-rx-for-name and uniquified-rx-for-regexp implemented below:

(defun uniquified-rx-for-name (filename)
  (cl-case uniquify-buffer-name-style
    ;; "name" -> "dir/name"
    (forward (concat "^\\(.*/\\)?" (regexp-quote filename) "$"))
    ;;  "name" -> "name\\dir"
    (reverse (concat "^" (regexp-quote filename) "\\(.*\\)?$"))
    ;;  "name" -> "name|dir"
    (post-forward (concat "^" (regexp-quote filename) "\\(|.*\\)?$"))
    ;; "name" -> "name<dir>"
    (post-forward-angle-brackets (concat "^" (regexp-quote filename) "\\(<.*>\\)?$"))
    ;; "name" -> "name<number>"
    (otherwise (concat "^" (regexp-quote filename) "\\(<.*>\\)?$"))))

(defun uniquified-rx-for-regexp (name-regexp)
  (let ((file-start ".*")
        (file-end ".*"))
    (when (string-prefix-p "^" name-regexp)
      (setq file-start "")
      (setq name-regexp (substring name-regexp 1)))
    (when (and (string-suffix-p "$" name-regexp)
               (not (string-suffix-p "\\$" name-regexp)))
      (setq file-end "")
      (setq name-regexp (substring name-regexp 0 -1)))
    (cl-case uniquify-buffer-name-style
      ;; "name" -> "^dir/.*name.*$"; "^name$" -> "^dir/name$"
      (forward (concat "^\\(.*/\\)?" file-start name-regexp file-end "$"))
      ;;  "name" -> "^.*name.*\\dir$"; "^name$" -> "^name\\dir$"
      (reverse (concat "^" file-start name-regexp file-end "\\(.*\\)?$"))
      ;;  "name" -> "^.*name.*|dir$"; "^name$" -> "^name|dir$"
      (post-forward (concat "^" file-start name-regexp file-end "\\(|.*\\)?$"))
      ;; "name" -> "^.*name.*<dir>$"; "^name$" -> "^name<dir>$"
      (post-forward-angle-brackets (concat "^" file-start name-regexp file-end "\\(<.*>\\)?$"))
      ;; "name" -> "^.*name.*<number>$"; "^name$" -> "^name<number>$"
      (otherwise (concat "^" file-start name-regexp file-end "\\(<.*>\\)?$")))))

And this is how to use them:

;;; neither of these will match "foo.txt<bar>" successfully
(push '("foo.txt" . txt) purpose-user-name-purposes)
(push '("\\.txt$" . txt) purpose-user-regexp-purposes)
(purpose-compile-user-configuration)

;;; fixed configuration using uniqufied regular expressions
;; note `purpose-user-regexp-purposes' is used instead of `purpose-user-name-purposes'
;; will match "foo.txt" and "foo.txt<bar>", but not "baz.txt<bar>"
(push (cons (uniquified-rx-for-name "foo.txt") 'txt) purpose-user-regexp-purposes)
;; will match "foo.txt", "foo.txt<bar>" and "baz.txt<bar>"
(push (cons (uniquified-rx-for-regexp "\\.txt$") 'txt) purpose-user-regexp-purposes)
(purpose-compile-user-configuration)

Some Technical Information

Types

  • purpose-mode-alist-entry-p: a predicate that checks if a given object is a cons cell whose car and cdr are both non-nil symbols. This checks that an object is a valid entry in an alist such as purpose-user-mode-purposes.
  • purpose-mode-alist-p: a predicate that checks if a given object can serve as an alist mapping modes to purposes, such as purpose-user-mode-purposes. This works by checking that the given object is a list whose all elements satisfy purpose-mode-alist-entry-p.
  • purpose-name-alist-entry-p: like purpose-mode-alist-entry-p, but the car should be a string. This checks that an object is a valid entry in an alist such as purpose-user-name-purposes.
  • purpose-name-alist-p: like purpose-mode-alist-p, but for mappings of names to purposes, such as purpose-user-name-purposes.
  • purpose-regexp-alist-entry-p: like purpose-mode-alist-entry-p, but the car should be a regexp (actually, a string). This checks that an object is a valid entry in an alist such as purpose-user-regexp-purposes.
  • purpose-regexp-alist-p: like purpose-mode-alist-p, but for mappings of regexps to purposes, such as purpose-user-regexp-purposes.

Getting a Buffer's Purpose

See also Determining a buffer's purpose.

When Purpose needs to get a buffer's purpose, it checks the purpose configuration. The checks are performed in this order:

  1. is the buffer a dummy buffer?

  2. does the buffer's name match a name in the user's name-purpose settings?

  3. does the buffer's name match a regexp in the user's regexp-purpose settings?

  4. does the buffer's major mode match a mode in the user's mode-purpose settings?

  5. does the buffer's name match a name in the extensions' name-purpose settings?

  6. does the buffer's name match a regexp in the extensions' regexp-purpose settings?

  7. does the buffer's major mode match a mode in the extensions' mode-purpose settings?

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

  9. does the buffer's name match a name in the default name-purpose settings?

  10. does the buffer's name match a regexp in the default regexp-purpose settings?

  11. does the buffer's major mode match a mode in the default mode-purpose settings?

  12. All of the above failed, use default-purpose