本文研究表示编辑器状态的prosemirror-state包。

状态集合

prosemirror-state(pm-state)是ProseMirror的核心模块,它的行为边界是由prosemirror-model(pm-model)定义的。pm-state用来处理编辑器的状态。所谓状态,其实包含若干个部分,比如插件状态以及选段(selection)状态。

pm-state所处理的状态是immutable,也就是状态对象的属性是不可变的,若状态发生变化,生成一个新的状态对象,来替换原有状态。

pm-state通过EditorState.create来创建实例,而不是直接使用new EditorState。这是因为创建实例后,pm-state要对实例进行配置,所以需要一个包装函数。所谓配置,主要是把插件加载到状态集合中。EditorState的实例有一个reconfigure方法,可以对状态进行重新配置。

把pm-state的主状态其实是由各个插件的状态一起构成的。默认情况下,EditorState会初始化以下插件:

  • doc
  • selection
  • storedMarks
  • scrollToSelection

用户也可以在EditorState.create的时候提供自己的插件。每个插件都向总状态贡献一份⁠StateField的实例,所以主状态其实是StateField的集合。一个StateField要包含以下属性:

  • init(config: Object, instance: EditorState) → T
  • apply(tr: Transaction, value: T, oldState: EditorState, newState: EditorState) → T
  • toJSON: ?⁠fn(value: T) → any
  • fromJSON: ?⁠fn(config: Object, value: any, state: EditorState) → T

首先init方法是插件初始化时候的回调函数,它接收一个config对象。这个config对象就是从传给EditorState.create做参数的那个对象扩展出来的,出了包含最初传入EditorState.create的那些属性以外,还包括所有插件(包括内置的插件)。也就是说所有插件看到的是一样的config,这个config对象包含了所以其他插件。init方法是在EditorState.create的时候被调用的。

其他的,apply方法用来表明这个插件如何影响事务过程(编辑操作是以事务的方式提交的)。toJSONfromJSON是可选的,用来面向JSON的序列化,比较主状态是一个插件集合,每个插件最知道自己如果序列化。

每个插件都有自己的key作为标识。key可以在PluginSpec中指定。如果没有指定的话,pm-state会生成默认的key。

选段(selection)

ProseMirror的选段是基于其对位移(position)和位置(resolved position)的定义。ProseMirror定义了一套虚拟的坐标系统,位移就是该坐标系统中针对0坐标的距离;位置则不仅包含了位移,还包括节点深度信息。

EditorState.create的时候,可以指定一个选段。不过不指定,默认的选段会设置在文档的开始位置,并且长度为0,坍缩成一个光标。

一个选段(Selection)可以包含若干个范围(SelectionRange),但至少包含一个。一个Selection由基点anchor和头部head构成,一个SelectionRange由起始位置$from和结束为止$to构成。一个选段则有若干个属性:

  • 基础节点anchor
  • 头部节点head,
  • 起始位移from,起始位置$from
  • 结束位移to,结束位置$to

此外,选段的empty()返回其内容是否为空,而content()返回选段包含的内容。replacereplaceWith用来替换选段内容。

选段的getBookmark()方法返回一个SelectionBookmark对象。这是一种与文档无关的选段表示方式,可以用于历史记录中。

选段的map()方法可以更新选段的范围,可以在文档被修改后保持选段范围的有效性。

选段可以有不同的类型,对于文本的话可以有文本选段(TextSelection);对于单个节点的话可以有节点选段(NodeSelection);对于整个文档的话有全选段(AllSelection),因为前面两种选段类型不一定能够用来表示对整个文档的选择。每一种选段类型,都对应着一种Bookmark类型。

事务(transaction)

pm-state还包含事务(transaction)。事务其实是从ProseMirror-Transform中的Transform类派生出来的。Transform以steps的方式管理对编辑器状态的改动,一个Transform就是若干个step的集合。

那pm-state为什么要从Transform类派生出一个Transaction呢?因为Transform看到的是文档的Model,也就是由pm-model中定义的Model,而不是具体的状态。所以pm-state要从派生出一个Transaction,在Transform的基础上,同时管理当前的编辑状态。

Tansaction管理的状态的例子包括选段(Selection)、当前激活的标记集(storedMarks)等等。

EditorStatetr()方法可以创建一个针对当前状态的事务。EditorStateapply()方法可以提交一个事务。因为EditorState其实是插件的状态的集合,所以apply()其实是让每个插件返回一个更新状态,然后构成一个新的EditorState

(本篇完)