Code Coloring InternalsEpsilon's code coloring routines use the character coloring primitives above to do code coloring for various languages like C, TeX, and HTML. There are some general purpose code coloring functions that manage code coloring and decide what sections of a buffer need to be colored. Then, for each language, there are functions that know how to color text in that language.
The general purpose section maintains information on what parts of each buffer have already been colored. It divides each buffer into sections that are already correctly colored, and sections that may not be correctly colored. When the buffer changes, it moves its divisions so that the modified text is no longer marked "correctly colored". Whenever Epsilon displays part of a buffer, this part of code coloring recolors sections of the buffer as needed, and marks them so they won't be colored again unless the buffer changes. Epsilon only displays the buffer after the appropriate section has been correctly colored. This part also arranges to color additional sections of the buffer whenever Epsilon is idle, until the buffer has been completely colored.
The other part of code coloring does the actual coloring of C, TeX, and HTML buffers. You can write new EEL functions to tell Epsilon how to color other languages, and use the code coloring package's mechanisms for remembering which parts of the buffer have already been colored, and which need to be recolored. This section describes how to do this. (Also see Defining Language Modes.)
The first function, which must be stored in the buffer-specific
The recolor_range function may decide to mark some characters
in the range "uncolored", by calling set_character_color( )
with a color class of
Epsilon remembers which parts of the buffer require coloring by using
a tagged region (see Character Coloring) named
When the buffer's modified, some of its coloring becomes invalid, and must be recomputed the next time it's needed. Normally Epsilon invalidates a few lines surrounding the changed section. Some language modes tell Epsilon to automatically invalidate more of the buffer by setting flags in the buffer-specific coloring_flags variable. (Other flags in this variable aren't normally set by language modes; code coloring uses them for bookkeeping purposes.)
COLOR_INVALIDATE_FORWARD indicates that after the user modifies a buffer, any syntax highlighting information after the modified region should be invalidated. COLOR_INVALIDATE_BACKWARD indicates that syntax highlighting information before the modified region should be invalidated.
COLOR_INVALIDATE_RESETS tells Epsilon that
whenever it invalidates syntax highlighting in a region, it should
also set the color of all text in that region to the default of
For many languages, starting to color at an arbitrary place in the
buffer requires a lot of unnecessary work. For example, the C
language has comments that can span many lines. A coloring function
must know whether it's inside a comment before it can begin coloring.
Similarly, a coloring function that began looking from the third
character in the C identifier
To simplify this problem, the coloring routines ensure that coloring begins at a safe place. We call a buffer position safe if the code coloring function can color the buffer beginning at that point, without looking at any earlier characters in the buffer.
When Epsilon calls the function in recolor_range, the value of
When Epsilon needs to color more of the buffer, it generally starts
from a known safe place: either a value returned by the buffer's
recolor_range function, or a boundary between characters of
different colors. But when Epsilon first begins working on a part of
the buffer that hasn't been colored before, it must determine a safe
starting point. The second function you must provide, stored in the
The buffer's recolor_from_here function looks backward from
point for a safe position and returns it. This may involve a search
back to the start of the buffer. If Epsilon knows of a safe position
before point in the buffer, it passes this as the parameter
Epsilon provides two standard recolor_from_here functions that coloring extensions can use. The recolor_by_lines( ) subroutine is good for buffers where coloring is line-based, such as dired buffers. In such buffers the coloring needed for a line doesn't depend at all on the contents of previous lines. The recolor_from_top( ) subroutine has just the opposite effect; it forces Epsilon to start from the beginning of the buffer (or an already-colored place). This may be all that's needed if a mode's coloring function is very simple and quick.
Epsilon runs the code coloring functions while it's refreshing the screen, so running the EEL debugger on code coloring functions is difficult, since the debugger itself needs to refresh the screen. The best way to debug such functions is to test them out by calling them explicitly, using test-bed functions like these:
To help find bugs in a code-coloring function, you
can have Epsilon check whether it has colored every character in the
range. This requires two steps. First, define
Bits in the want-debug-coloring variable enable features. The
Detecting uncolored-region bugs only works on those coloring subroutines that are designed to color every character in their given range (plus any additional range they report they've colored, when they return a value greater than the range start...end they were given). But some modes' coloring functions (for example, HTML mode) keep track of which buffer regions require coloring in a different way, and other modes intentionally only apply colors to certain parts of the buffer. (For example, process mode uses this method only for user input, and colors process output as it arrives, based on any ANSI escpe sequrnces it contains.) Color debugging isn't useful for spotting bugs in such modes, but its logging ability can be useful.
Epsilon sets the first_window_refresh variable prior to
calling the when_displaying subroutine to indicate whether or
not this is the first time a particular buffer has been displayed
during a particular screen refresh. When a buffer appears in more
than one window, Epsilon sets this variable to 1 before calling the
when_displaying subroutine during the display of the first
window, and sets it to zero before calling that subroutine during the
display of the remaining windows. Epsilon sets the variable to
In a buffer with code coloring turned on, the when_displaying variable points to a subroutine named recolor_partial_code( ). Epsilon passes two values to the subroutine that specify the range of the buffer that was modified since the last time the buffer was displayed. The standard recolor_partial_code( ) subroutine provided with Epsilon uses this information to discard any saved coloring data for the modified region of the buffer in the data structures it maintains. It then calls the two language-specific subroutines described at the beginning of this section as needed to color parts of the buffer.
You can tell Epsilon to run a function at display time by calling the add_buffer_when_displaying( ) subroutine. It arranges for the specified function to be called after code coloring has been done when displaying any window showing the specified buffer. The function will be called with no parameters. The delete_buffer_when_displaying( ) removes the specified function from that buffer's list of functions to be called at display time.
The recolor_partial_code( ) subroutine calls the default_when_displaying( ) function, which calls each such function set by add_buffer_when_displaying( ). In most buffers without code coloring turned on, the when_displaying variable points to the default_when_displaying( ) function directly. Other functions assigned to when_displaying should call default_when_displaying( ) too.
The drop_all_colored_regions( ) subroutine discards coloring information collected for the current buffer. The next time Epsilon needs to display the buffer, it will begin coloring the buffer again. The drop_coloring( ) subroutine is similar, but lets you specify the buffer number. It also discards some data structures, so it's more suitable when the buffer is about to be deleted.