As it turns out, a good rich text editor not only allows the user to manually apply different styles to text in a document. It can also automatically apply certain styles based on the context of user actions.
Some very common examples include autoformatting of links or inserting
a new list item when user presses Enter
key.
In Notus (document model used by Zefyr editor), such rules are called heuristic rules. There are two main purposes for heuristic rules:
Let’s cover the second item in more detail.
Say, a user is editing following document (cursor position is indicated
by pipe |
character):
Document| title styled as h3 heading
Regular paragraph with bold text.
User decides to change the first line style from h3
to h2
. If we
were to apply this change to the document in code it would look like
this:
var doc = getDocument();
var cursorPosition = 8; // after the word "Document"
var selectionLength = 0; // selection is collapsed.
var change = doc.format(
cursorPosition, selectionLength, NotusAttribute.heading.level2);
If we try to apply this change as-is it would have no effect or, more
likely, result in an AssertionError
because we are trying to apply line style
to a character in the middle of a line. This is why all methods in
NotusDocument
have an extra step which applies heuristic rules to
the change (there is one method which skips this step, compose
,
read more on it later) before actually composing it.
The NotusDocument.format
method returns an instance of Delta
which
was actually applied to the document. For the above scenario it would
look like this:
[
{"retain": 35},
{"retain": 1, "attributes": {"heading": 2} }
]
The above change applies h2
style to the 36th character in the
document, that’s the newline character of the first line, exactly
what user intended to do.
There are more similar scenarios which are covered by heuristic rules to ensure consistency with the document model and provide better UX.
NotusDocument.compose()
and skipping heuristic rulesThe compose()
method is the only method which skips the step of
applying heuristic rules and therefore should be used with great
care as it can result in corrupted document state.
Use this method when you are sure that the change you are about to compose conforms to the document model and data format semantics.
This method exists mostly to enable following use cases:
When composing a change which came from a different site or server make
sure to use ChangeSource.remote
when calling compose()
. This allows
you to distinguish such changes from local changes made by the user
when listening on NotusDocument.changes
stream.