118 lines
5.4 KiB
Plaintext
118 lines
5.4 KiB
Plaintext
This document is designed to be a quick (and incomplete) description of how
|
|
modes work, how you might create one yourself, etc.
|
|
|
|
1. What are modes?
|
|
|
|
Pmacs uses modes to determine what which keys should apply which actions, how
|
|
the buffer should be highlighted, how the buffer should be indented, and any
|
|
other per-buffer configuration. The default mode ("Fundamental") provides the
|
|
base functionality which all other modes inherit.
|
|
|
|
2. Where do they come from?
|
|
|
|
Modes are loaded and installed by application.py, or by $HOME/.pmc/conf, which
|
|
is sourced by application.py. To install a mode, import its class from the
|
|
package in which it resides, and call install() on the class, passing the
|
|
application as the only argument. Example:
|
|
|
|
import mymode
|
|
mymode.ModeClass.install(self)
|
|
|
|
This code can be run in application.py's constructor, or in $HOME/.pmc/conf.
|
|
|
|
3. How do they work?
|
|
|
|
The one thing every mode has in common is that they map key bindings to actions
|
|
to be taken. They do this via self.bindings, a dictionary mapping action names
|
|
(i.e. 'page-down') to a tuple of key bindings (i.e. ('C-v', 'PG_DN',)).
|
|
|
|
Modes subclass mode.Fundamental, and they call mode.Fundamental.__init__ to run
|
|
the standard mode initialization (including building the default bindings
|
|
dictionary); they can later modify or overwrite this dictionary if they choose.
|
|
|
|
There are at least 3 optional behaviors modes can make use of:
|
|
|
|
1. Syntax highlighting
|
|
2. Indentation level detection
|
|
3. Tag (parenthesis, brace, bracket, etc.) matching
|
|
|
|
Not all modes can (or should) make use of these features: they are primarily
|
|
useful in modes having to do with programming languages, or other structured
|
|
documents.
|
|
|
|
4. Syntax highlighting
|
|
|
|
Syntax highlighting uses a hybrid lexing/parsing process to break each line of
|
|
the buffer down into one or more lexical tokens; these tokens are primarily
|
|
used to highlight parts of the buffer different colors. The colors to use are
|
|
defined by application.colors, a dictionary mapping token-names to a tuple
|
|
consisting of at least a foreground color and a background color. Modes can
|
|
define a self.colors dictionary which will be added to the application; however,
|
|
modes are not permitted to override the global defaults in this way (the user
|
|
can override them via ~/.pmc/conf in whatever way is desired).
|
|
|
|
Modes are encouraged to use "generic" token names when appropriate; modes can
|
|
also "namespace" their tokens to allow for mode-specific customization.
|
|
|
|
Explaining how to write a Grammar is outside the scope of this document; see
|
|
lex.py, mode.py and mode/*.py for examples. Some important points to note:
|
|
|
|
* Regexes are applied to only one line of the document at a time.
|
|
* All regexes must match at least one character (the newline counts).
|
|
* All tokens must consist of at least one character.
|
|
* A rule that matches must generate one or more tokens.
|
|
* Any input not matched by a rule will end up in a "null" token.
|
|
* Tokens can't "look" for other tokens (but they can use 0-width assertions
|
|
to test for data on the current line).
|
|
* Regions of text which begin and end with recognizable tokens can be
|
|
lexed using a different sub-grammar using RegionRule, etc. This nesting
|
|
can be arbitrarily deep.
|
|
* sub-grammars can be dynamically specified in modes that provide one or
|
|
more OverridePatternRule objects.
|
|
|
|
5. Indentation level detection
|
|
|
|
Indentation level detection hooks into the basic 'insert-tab' action; rather
|
|
than inserting 4 spaces (which is the default), it will instead determine the
|
|
correct "tab depth" for this line of the buffer, and insert/remove spaces from
|
|
the beginning of the line in order to reach the correct number.
|
|
|
|
[NOTE: 4 spaces is the norm; some modes default to 2. The Mode.tabwidth variable
|
|
determines this, and can be customized either during startup, or via the methods
|
|
set-tab-width (to set the width for a single buffer) and set-mode-tab-width (to
|
|
set the default width for all buffers using the given mode).]
|
|
|
|
To implement this, you must create a Tabber class and assign it to self.tabber
|
|
in the mode. At a minimum, a tabber must support the following methods:
|
|
|
|
* __init__(self, mode)
|
|
* get_level(self, y)
|
|
* region_added(self, p, lines)
|
|
* region_removed(self, p1, p2)
|
|
|
|
Tabber classes can often be tricky to implement correctly. tab.Tabber provides
|
|
a lot of base functionality that is probably useful; also, you may want to try
|
|
looking at tab.StackTabber, which provides even more base support.
|
|
|
|
6. Tag matching
|
|
|
|
Tag matching allows a closing tag (such as ")") to "show" the corresponding
|
|
opening tag (such as an earlier "(") to help orient the user. Currently, tags
|
|
are assumed to be single characters, although in the future it's easy to imagine
|
|
multi-character tags being useful. Currently they are not supported.
|
|
|
|
This support is very easy to add, assuming that the mode has a grammar. In most
|
|
cases, here is how it works:
|
|
|
|
a. In the mode, create 4 tuples:
|
|
* opentokens: tuple of lexical tokens which can be opentags
|
|
* opentags: dictionary mapping opentag strings to closetag strings
|
|
* closetokens: tuple of lexical tokens which can be closetags
|
|
* closetags: dictionary mapping closetag strings to opentag strings
|
|
|
|
b. Also in the mode, create or instantiate actions who subclass
|
|
method.CloseTag (e.g. method.CloseParen) and assign them the appropriate
|
|
binding (e.g. '(').
|
|
|
|
c. Enjoy!
|