Location>code7788 >text

Slate Document Editor - WrapNode Data Structures and Operations Transformations

Popularity:399 ℃/2024-11-18 10:36:39

Slate Document Editor - WrapNode Data Structures and Operations Transformations

Earlier we chatted a bit about theslateThe basic concepts of rich-text engines and a good understanding of the concepts of rich-text engines based on theslateThe realization of some of the plug-in capabilities of the document editor design, type of expansion, specific programs, etc. were explored, then the next we focus more on the details of the document editor, from shallow to deep talk about the design of the relevant capabilities of the document editor.

  • Online Editor./DocEditor
  • Open source address./WindrunnerMax/DocEditor

with respect toslateRelated posts to document editor project.

  • Building a document editor based on Slate
  • Slate Document Editor - WrapNode Data Structures and Operations Transformations
  • Slate Document Editor - TS Type Expansion and Node Type Checking
  • Slate Document Editor - Decorator Decorator Render Scheduler

Normalize

existslateThe regularization of the data structures in this project is a troublesome matter, especially for structures that need to be nested, such as those that exist in this projectQuotecap (a poem)ListThen there are various options when regularizing the data structure, again taking these two data structures as an example, each of theWrapThere must be a correspondingPairThe structure of the nested, then for the data structure has the following program. In fact, I think it is very difficult to solve this type of problem, nested data structures are not so efficient for additions, deletions, modifications and checks, so in the absence of best practices related to the input case, can only continue to figure out.

The first is to reuse the current block structure, which means that theQuote Keycap (a poem)List KeyIt's all leveled and the same itsPair KeyAlso reuse up, the advantage is that there will not be too many levels of nested relationships, for the content of the search and related processing will be much simpler. But the same problem will also occur, if in theQuotecap (a poem)ListThe case of non-matching, that is to say, the case where it is not an exactly equivalent relationship, would require the presence of aPairmismatchWrapsituation, at which point it becomes difficult to ensureNormalize, because we are in need of predictable structures.

{
    "quote-wrap": true,
    "list-wrap": true,
    children: [
        { "quote-pair": true, "list-pair": 1, children: [/* ... */] },
        { "quote-pair": true, "list-pair": 2, children: [/* ... */] },
        { "quote-pair": true,  children: [/* ... */] },
        { "quote-pair": true, "list-pair": 1, children: [/* ... */] },
        { "quote-pair": true, "list-pair": 2, children: [/* ... */] },
    ]
}

Then if we don't do very sophisticated control of the content in theslateis processed using the default behavior in , then its data structure expression will appear as follows, in which case the data structure is predictable, then theNormalizeIt would not be a problem, and since this is its default behavior, there would not be much operational data processing to be concerned about. But the problem is also more obvious, in this case the data is predictable, but the processing is particularly cumbersome, when we maintain the correspondence, we must recursively process all the child nodes, in the case of particularly many levels of nesting, this amount of computation is quite complex, if in the case of support for the structure of tables and so on, it becomes even more difficult to control.

{
    "quote-wrap": true,
    children: [
        {
            "list-wrap": true,
            children: [
                { "quote-pair": true, "list-pair": 1, children: [/* ... */] },
                { "quote-pair": true, "list-pair": 2, children: [/* ... */] },
            ]
        },
        { "quote-pair": true,  children: [/* ... */] },
        { "quote-pair": true,  children: [/* ... */] },
    ]
}

Then this data structure isn't actually perfect, and its biggest problem is that thewrap - pairinterval is too large, such a treatment will appear more boundary problems, to take a more extreme example, suppose we have the outermost layer of the existence of the reference block, in the reference block and nested in the table, the table is nested in the highlighting block, the highlighting block is nested in the reference block, in this case we have thewrapNeed to passNMultiple layers to matchpairThe biggest impact of this situation is theNormalizeWe need to have very deepDFSProcessing to be able to deal with it, processing not only requires performance intensive deep traversal, but also prone to cause a lot of problems due to poor processing.

In this case, then, we can simplify the nesting of the hierarchy as much as possible, which means that we need to avoid thewrap - pairof the spacing problem, then it is obvious that we directly and strictly specify that thewrapallchildrenmust bepairIn this case we doNormalizeIt's a lot easier to just add thewrapand traversing its children in the case of thepairIn this case, we can just check the parent node if it's the same as the parent node. Of course, this solution is not without its drawbacks, which makes us have more stringent requirements for the accuracy of data manipulation, because here we will not go to the default behavior, but all need to control themselves, especially all the nesting relationships and boundaries need to be strictly defined, which also has higher requirements for the design of the editor behavior.

{
    "quote-wrap": true,
    children: [
        {
            "list-wrap": true,
            "quote-pair": true,
            children: [
                { "list-pair": 1, children: [/* ... */] },
                { "list-pair": 2, children: [/* ... */] },
                { "list-pair": 3, children: [/* ... */] },
            ]
        },
        { "quote-pair": true,  children: [/* ... */] },
        { "quote-pair": true,  children: [/* ... */] },
        { "quote-pair": true,  children: [/* ... */] },
    ]
}

So why does the data structure get complicated? Take the above structure as an example.list-pair: 2This node is disarmed.list-wrapnode's nested structure, then we need to change the node to the following type, and we can see that the difference in structure will be more significant here, except for the fact that in addition to changing thelist-wrapBeyond splitting into two, we need to deal with otherlist-pairThere are more operations to do here, so if we want to implement the more genericSchemaIt would require more design and specification.

And one of the most overlooked points here is that we need to provide the originallist-pair: 2This node joins the"quote-pair": truebecause at this point the line becomesquote-wrapchild elements, which in summary means that we need to change the elements that were originally in thelist-wrapattribute and then make a copy of it to thelist-pair: 2to maintain the correct nesting structure. So why isn't this done with the help of thenormalizeto passively add it but rather to actively copy it, for the simple reason that if thequote-pairis fine if it's a passive process, but if it's a passive process then it's set directly to thetrueThat's fine, but if it'slist-pairWe have no way of knowing what the data structure of this value should look like if we implement it, and this implementation can only be attributed to the plugin'snormalizeto realize it.

{
    "quote-wrap": true,
    children: [
        {
            "list-wrap": true,
            "quote-pair": true,
            children: [
                { "list-pair": 1, children: [/* ... */] },
            ]
        },
        { "quote-pair": true,  children: [/* ... */] },
        {
            "list-wrap": true,
            "quote-pair": true,
            children: [
                { "list-pair": 1, children: [/* ... */] },
            ]
        },
        { "quote-pair": true,  children: [/* ... */] },
        { "quote-pair": true,  children: [/* ... */] },
        { "quote-pair": true,  children: [/* ... */] },
    ]
}

Transformers

As mentioned earlier, there are default behaviors in nested data structures, and there were not many data handling problems before because the default behaviors were always followed, however, when the data structure was changed, it was found that the data structure was not so easy to control in many cases. Previously, when dealing withSetBlockWhen I do, I usually go through thematchparameter matchingBlocktype of node, since nothing usually goes wrong with this processing in the case of the default behavior.

However, in the process of changing the data structure, the handling of theNormalizeIt had a problem with matching block elements that did not behave as expected, which resulted in the data it processed not being processed correctly all the time.NormalizeIt also fails to complete until it throws an exception. The main problem here is caused by the fact that its iteration order doesn't match what I expected, for example in theDEMOExecute on page[...(editor, {at: [9, 1, 0] })], which returns the result of the topEditorbottomNodeAnd, of course, this will include all of the scope of theLeafThe nodes are equivalent to theRange

[]          Editor
[9]         Wrap
[9, 1]      List
[9, 1, 9]   Line
[9, 1, 0]   Text

In fact in this case if the original(path, at)is not a problem, here is too much reliance on its default behavior before, which leads to too poor control of the accuracy of the data, we should need to have predictability in the processing of data, rather than relying on the default behavior. In addition, theslateThe documentation is still too concise, many details are not mentioned, in this case you still need to read the source code to have a better understanding of the data processing, for example, looking at the source code here let me understand that every time I do an operation I take theRangeAll eligible elements performmatch, which may occur multiple times in a single call to theOpDispatch.

In addition, since this processing is mainly for nested element support, theunwrapNodesor a related data processing feature, when I call theunwrapNodeswhenatThe values passed in are not the same, respectivelyA-[3, 1, 0]cap (a poem)B-[3, 1, 0, 0], a key point here is that at the time of matching we are all strictly equal to the[3, 1, 0], but the result of the call is different, in that theAcenter[3, 1, 0]All elements have beenunwrapIt's up, and theBjust[3, 1, 0, 0](indicates passive-voice clauses)unwrapNow, here's what we can guaranteematchThe results are identical, so the problem is that theaton. At this point, if you don't understandslateIf the model of data manipulation, we must go to the source code, in the reading of the source code we can find that there will beHelped us narrow it down, so here it isatvalue will then affect the final result.

unwrapNodes(editor, { match: (_, p) => (p, [3, 1, 0]), at: [3, 1, 0] }); // A
unwrapNodes(editor, { match: (_, p) => (p, [3, 1, 0]), at: [3, 1, 0, 0] }); // B

The above problem also means that all our data should not be passed around, and we should be very clear about the data we want to manipulate and its structure. In fact, the previous also mentioned a problem that is difficult to deal with the situation of multi-level nesting, which actually involves an editorial boundary, making the maintenance of the data has become complex. As an example, join this time we have a table nested in moreCell, if we are multi-instanceCellstructure, at which point we filter theEditorAny data processed after the instance does not affect otherEditorinstance, whereas if we were at this pointJSONNested expression of the structure, we may have more than the operation of the boundary and affect other data, especially the parent data structure of the situation. So we must also pay attention to the handling of boundary conditions, that is, as mentioned earlier, we need to be very clear about the data structure to be processed, and clearly delineate the operation nodes and ranges.

{
    children: [
        {
            BLOCK_EDGE: true, // block structure boundaries
            children: [
                { children: [/* ... */] }, { children: [/* ...
                { children: [/* ... */] }, [ { children: [/* ... */] }, { children: [/* ...
            ]
        }, { children: [/* ... */] }, ]
        { children: [/* ... */] }, { children: [/* ...
        { children: [/* ... */] }, { children: [/* ... */] }, { children: [/* ...
    ]
}

Additionally, debugging code in pages already on the thread can be a challenge, especially when theeditorIt wasn't exposed towindowIn the case that you want to get an instance of the editor directly, you need to reproduce the online environment locally, in which case you can do so with the help of theReactwouldFiberThe actual writing is inDOMThe node is characterized by theDOMThe node directly obtainsEditorinstance, though the nativeslateThe use of a large number ofWeakMapto store the data, in which case there is no good solution for the time being unless theeditoractually references such an object or has an instance of it, otherwise it can only be accessed via thedebugbreakpoints, and then the object is temporarily stored as a global variable for use during debugging.

const el = (`[data-slate-editor="true"]`);
const key = (el).find(it => ("__react"));
const editor = el[key].;

ultimate

Here we chat aboutWrapNodeData Structures and Operational Transformations, mainly for nested types of data structures need to be concerned about, and in fact the types of nodes can be divided into a variety of other things, we can have in the broader contextBlockNodeTextBlockNodeTextNodeinBlockNodeIn this we can again divideBaseNodeWrapNodePairNodeInlineBlockNodeVoidNodeInstanceNodeetc., so that what is recounted in the text is still of a relatively basic nature in terms ofslateThere are many additional concepts and operations to focus on in theRangeOperationEditorElementPathetc. So in the latter article we'll talk mostly about the issues in theslatecenterPathexpression, as well as in theReactHow do you control the expression and proper maintenance of your content?PathpathwayElementContent rendered.