Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

split up marks, decorations, and annotations #2745

Closed
ianstormtaylor opened this issue May 8, 2019 · 0 comments · Fixed by #2747
Closed

split up marks, decorations, and annotations #2745

ianstormtaylor opened this issue May 8, 2019 · 0 comments · Fixed by #2747

Comments

@ianstormtaylor
Copy link
Owner

Do you want to request a feature or report a bug?

Improvement.

What's the current behavior?

Right now there are three ways that mark-level formatting can be applied in Slate:

  1. Via mark objects that are embedded in the document itself.

  2. Via decoration ranges (which contain a mark object) that are added to a specific node by the decorateNode middleware. These never actually live in the data model, and are a render-time-only construct.

  3. Via value.decorations which contains a group of decorations that live in the data model, but not necessarily inside the individual text nodes. They are rendered similarly to the other decorations.

Each one of these has their own uses cases. Marks are for classic formatting like bold, italic, etc. Render-time decorations are for code highlighting, markdown highlighting, etc. And top-level decorations are for comments, suggestions, collaborative cursors, etc.

But there are also some confusing overlaps between the three, which leads to sloppier code and less performance.

For example, decorations are usually rendered at the block level, but their ranges are relative to the entire document, which makes isolating them while rendering less performant than it could be. And top-level decorations are stored in an immutable list, when many use cases require referencing them by an identifier, which requires slowly traversing the entire list.

What's the expected behavior?

I think we need to split them apart.

MARKS

The current marks use case is the most solid and widely used, and it will stay as is.

DECORATIONS

The render-time decorations use case (which was the first use of "decorations") will also stay very similar.

Except, the paths on a decoration will change to be scoped to the specific node that they are decorating, instead of relative to the document itself. This makes it easy to use paths only (and drop keys support in the future). It also means that they can be compared more quickly while rendering the tree of nodes.

In addition, instead of using a nested decoration.mark property, and overloading the renderMark middleware, they contain that formatting information as top-level properties. So a decoration will contain:

{
  type: String,
  data: Map,
  anchor: Point,
  focus: Point,
}

And it will be rendered with a new renderDecoration middleware.

ANNOTATIONS

The top-level value.decorations use case is the most problematic. It solves the "search highlighting" use case fine, but it isn't well suited to other cases like attaching comments and suggestions, or rendering collaborative cursors.

The first change will be to introduce a new name called "annotations" to separate the concept from the render-time "decorations". They are really two separate concepts, and although they share some similarities, it makes more sense to give them first-class treatment.

The second change will be to add a new annotation.key property, which is a unique identifier for the annotations. Since they will be stored as a Map instead of a List. This way adding, removing and updating annotations can all happen in O(1) time instead of O(n).

Annotations, like decorations, will stop overloading marks. And will expose their properties at the top-level:

{
  key: String,
  type: String,
  data: Map,
  anchor: Point,
  focus: Point,
}

And they will use the new renderAnnotation middleware to render themselves.

And since they are a new type of data in our data model, we'll also add three new operations to deal with them: add_annotation, remove_annotation and set_annotation—which share a common structure to the existing *_mark operations.


Although these are big changes if you're currently using decorations, for most people who are just using marks things won't be different. But they do make the decoration and annotation behaviors and data model much clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant