Differ
MiniLatex uses a number of optimizations. One of these is to re-parse and re-render only paragraphs which have changed. This is optimization is important for the editing process, since instantaneous feedback is essential for a satisfying user experience. Such differential transformations are more important for parsing than for rendering, since parsing is by far the most expensive process in terms of execution time.
The strategy is as follows. Let u
and v
be two lists of strings. Write them as u = a x b
, v = u y b
, where a
is the greatest common prefix and b
is the greatest common suffix. Supposed that we have in hand u' = render u = a' x' b'
, where a' = render a
, etc. Let y' = render y
. Then v' = diff u = a' y' b'
.
The strategy just outlined is far from optimal for arbitrary changes to the list v
. However, a real human editor generally makes small, localized changes to v
, so in "real life," the strategy is near-optimal.
NOTE. I would like to thank Ilias van Peer, who recommended using a diffing strategy for performance optimization.
Implementing the diff stategy
The stuctures u = a x b
, v = u y b
map directly onto the DiffRecord
type:
The implementation of
is straightforward.
Edit records
The function diff
carries out the transformation a x b => a y b
. To effect the transformation a' x' b' => a' y' b'
, one needs the notion of an EditRecord
, which carries the needed state:
The rendering function applied to an EditRecord
takes paragraphs
and LatexState
as arguments and returns renderedParagraphs
. The idList
is a list of ids
, one for each rendered paragraph. When an updated EditRecord
is formed, the ids
of the changed paragraphs are changed. This is done in order to help both the Elm renderer and MathJax to be as efficient as possible. Again: speed optimizations. The initial ids
are like this p.0.1
, p.0.2
, p.0.3
, etc. That is, they are sequential in the third component. Suppose that the paragraphs with ids
p.0.17
and p.0.18
are changed. The corresponding ids
in the new rendered text are something like p.3785.17
and p.3785.18
, where 3785
is randomly generated.
Differential rendering
Below is the definition of differentialRender
. It takes a rendering function, a DiffRecord
, and an EditRecord
as input and produces a list of rendered paragraphs as output.
Updating an EditRecord
Updating an EditRecord
is a multistep process. The Differ.update
function takes as input a random number seed, a rendering function, and EditRecord
, and a string of source text. The output is an updated EditRecord
. Here are the steps:
Apply
logicalParagraphify
to transform the input source text string into a list of logical paragraphs.Apply
diff
to the "old paragraphs" in theEditRecord
and the "new paragraphs" from (1) to produce aDiffRecord
.Apply
differentialRender
to theDiffRecord
from step (2) and the list of logical paragraphs frome step (1) to producn updated list of rendererd paragraphs.Apply
differentialIdList
to the random seed, theDiffRecord
, and "old"idList
from theEditRecord
to create a new list of paragraphids
.
The final step is to used the information harvested in 1--4 to create a new EditRecord
.
Last updated