-
Notifications
You must be signed in to change notification settings - Fork 23
Purpose Configuration
Table of Contents
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).
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 satisfiespurpose-mode-alist-p
. This is a custom variable and can be changed through thecustomize
interface. When setting this variable from elisp code, you should callpurpose-compile-user-configuration
for the changes to take effect. -
purpose-user-name-purposes
: a mapping of names to purposes. This is an alist that satisfiespurpose-name-alist-p
. This is a custom variable and can be changed through thecustomize
interface. When setting this variable from elisp code, you should callpurpose-compile-user-configuration
for the changes to take effect. -
purpose-user-regexp-purposes
: a mapping of regexps to purposes. This is an alist that satisfiespurpose-regexp-alist-p
. This is a custom variable and can be changed through thecustomize
interface. When setting this variable from elisp code, you should callpurpose-compile-user-configuration
for the changes to take effect.
Compiled variables:
-
purpose--user-mode-purposes
: this variable is compiled according topurposer-user-mode-purposes
whenpurpose-compile-user-configuration
is called, or whenpurpose-user-mode-purposes
is changed through thecustomize
interface. This is a hash-table of mode-purpose pairs. -
purpose--user-name-purposes
: this variable is compiled according topurposer-user-name-purposes
whenpurpose-compile-user-configuration
is called, or whenpurpose-user-name-purposes
is changed through thecustomize
interface. This is a hash-table of name-purpose pairs. -
purpose--user-regexp-purposes
: this variable is compiled according topurposer-user-regexp-purposes
whenpurpose-compile-user-configuration
is called, or whenpurpose-user-regexp-purposes
is changed through thecustomize
interface. This is a hash-table of regexp-purpose pairs.
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 withdefclass
), that contains three members: mode-purposes (satisfiespurpose-mode-alist-p
), name-purposes (satisfiespurpose-name-alist-p
) and regexp-purposes (satisfiespurpose-regexp-alist-p
). -
purpose-extended-configuration
: this is a plist (property-list) that containspurpose-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 callpurpose-compile-extended-configuration
for the changes to take effect.
For example, the contents ofpurpose-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 apurpose-conf
object topurpose-extended-configuration
. This function also callspurpose-compile-extended-configuration
. -
purpose-del-extension-configuration
: use this function to remove apurpose-conf
object frompurpose-extended-configuration
. This function also callspurpose-compile-extended-configuration
. -
purpose-compile-extended-configuration
: compiles the extensions settings. This generatspurpose--extended-mode-purposes
,purpose--extended-name-purposes
andpurpose--extended-regexp-purposes
according topurpose-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 allpurpose-conf
objects inpurpose-extended-configuration
. -
purpose--extended-name-purposes
: this is a hash-table of name-purpose pairs. It contains the combined name settings of allpurpose-conf
objects inpurpose-extended-configuration
. -
purpose--extended-regexp-purposes
: this is a hash-table of regexp-purpose pairs. It contains the combined regexp settings of allpurpose-conf
objects inpurpose-extended-configuration
.
;; 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)
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 ofpurpose--default-mode-purposes
,purpose--default-name-purposes
andpurpose--default-regexp-purposes
. -
purpose-use-default-configuration
: toggles whether the default settings sould be considered when getting a buffer's purpose. When this isnil
, 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 thecustomize
interface.
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
.
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)
-
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 aspurpose-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 aspurpose-user-mode-purposes
. This works by checking that the given object is a list whose all elements satisfypurpose-mode-alist-entry-p
. -
purpose-name-alist-entry-p
: likepurpose-mode-alist-entry-p
, but the car should be a string. This checks that an object is a valid entry in an alist such aspurpose-user-name-purposes
. -
purpose-name-alist-p
: likepurpose-mode-alist-p
, but for mappings of names to purposes, such aspurpose-user-name-purposes
. -
purpose-regexp-alist-entry-p
: likepurpose-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 aspurpose-user-regexp-purposes
. -
purpose-regexp-alist-p
: likepurpose-mode-alist-p
, but for mappings of regexps to purposes, such aspurpose-user-regexp-purposes
.
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:
-
is the buffer a dummy buffer?
-
does the buffer's name match a name in the user's name-purpose settings?
-
does the buffer's name match a regexp in the user's regexp-purpose settings?
-
does the buffer's major mode match a mode in the user's mode-purpose settings?
-
does the buffer's name match a name in the extensions' name-purpose settings?
-
does the buffer's name match a regexp in the extensions' regexp-purpose settings?
-
does the buffer's major mode match a mode in the extensions' mode-purpose settings?
-
If
purpose-use-default-configuration
is non-nil, check also: -
does the buffer's name match a name in the default name-purpose settings?
-
does the buffer's name match a regexp in the default regexp-purpose settings?
-
does the buffer's major mode match a mode in the default mode-purpose settings?
-
All of the above failed, use
default-purpose