CodeMirror
CodeMirror
目录
接口剖析
State包
import {
// Extension, // 这个比较特殊,很关键,但好像看不到类声明
// 比较常用的
StateField, // 状态字段
Facet,
EditorState, // 编辑器状态
StateEffect,
StateEffectType,
SelectionRange,
// 没见过
Text,
Line,
ChangeDesc,
ChangeSet,
EditorSelection,
Compartment,
Annotation,
AnnotationType,
Transaction,
RangeValut,
Range,
RangeSet,
RangeSetBuilder
} from '@codemirror/state';
Extensions(扩展)
CM6 也有自己的插件,称之为 扩展(extensions)
尽管 CM6 支持多种扩展,但其中两个最常见的分别是 View plugins 以及 State fields。
讲真,这个东西好难弄懂,一开始完全不理解
例如下面这个,大概意思我懂,可以传View Plugins、State fields、Facet Providers等等,但就是不懂他是什么声明出来的
/**
在创建状态以附加各种配置和行为信息时,可以[提供]扩展值。
它们可以是内置的提供扩展的对象,如[state fields]或[facet providers],也可以是在其' extension '属性中带有扩展的对象。
扩展可以嵌套在任意深度的数组中——在处理时将被平铺。
*/
declare type Extension = {
extension: Extension;
} | readonly Extension[];
OB的Extensions
OB文档说Ob的Extensions和CM的一同一回事,这里看一下OB的使用方法:
基本用法
onload() {
this.registerEditorExtension([examplePlugin, exampleField]);
}
使用举例(Extension x ViewPlugin x EditorView)
不得不说这真的太麻烦了
// 原型:(method) Plugin_2.registerEditorExtension(extension: Extension): void
this.registerEditorExtension(this.editorExtension(this));
// 原型:(method) AnyBlockPlugin.editorExtension(plugin_this: AnyBlockPlugin): ViewPlugin<(Anonymous class)>
// 这里返回的是ViewPlugin类型给Extension接口,是吻合的
// ViewPlugin类,是一个泛型类
editorExtension(plugin_this: AnyBlockPlugin) {
// 原型:(method) ViewPlugin<V extends PluginValue>.fromClass<(Anonymous class)>(cls: new (view: EditorView) => (Anonymous class), spec?: PluginSpec<(Anonymous class)> | undefined): ViewPlugin<...>
// static fromClass<V extends PluginValue>(
// cls: {new (view: EditorView): V;},
// spec?: PluginSpec<V>
// ): ViewPlugin<V>;
return ViewPlugin.fromClass(
class {/*...*/}, // 这是一个EditorView类
{decorations: (v) => v.decorations},
)
}
StateField
StateField<T>
/**
字段可以在**编辑器状态下存储其他信息**,并使其与状态的其余部分保持同步。
*/
declare class StateField<Value> {
private createF;
private updateF;
private compareF;
private constructor();
/**
定义一个 state field.
*/
static define<Value>(config: StateFieldSpec<Value>): StateField<Value>;
private create;
/**
Returns an extension that enables this field and overrides the way it is initialized. Can be useful when you need to provide a non-default starting value for the field.
*/
init(create: (state: EditorState) => Value): Extension;
/**
状态字段实例可以作为[Extension][^1]值来启用给定状态下的字段
[^1]:(https://codemirror.net/6/docs/ref/#state.Extension)
*/
get extension(): Extension;
}
StateFieldSpec<T>
declare type StateFieldSpec<Value> = {
/**
在创建状态时为字段创建初始值。
*/
create: (state: EditorState) => Value;
/**
从字段的前一个值和[transaction][^1]计算一个新值
[^1]:(https://codemirror.net/6/docs/ref/#state.Transaction)
*/
update: (value: Value, transaction: Transaction) => Value;
/**
比较字段的两个值,当它们相同时返回'true'。
这用于避免在字段值未改变时重新计算依赖于该字段的facet。默认使用'==='
*/
compare?: (a: Value, b: Value) => boolean;
/**
提供基于该字段的扩展。给定的函数将被初始化字段调用一次。
它通常会调用一些facet的 ['from'][^1] 方法来从该字段创建facet输入,但也可以返回当该字段出现在配置中时应该启用的其他扩展。
[^1]: (https://codemirror.net/6/docs/ref/#state.Facet.from)
*/
provide?: (field: StateField<Value>) => Extension;
/**
用于将该字段的内容序列化为JSON的函数。只有当该字段包含在 ['EditorState.toJSON'][^1] 的参数中时才有必要
[^1]:(https://codemirror.net/6/docs/ref/#state.EditorState.toJSON)
*/
toJSON?: (value: Value, state: EditorState) => any;
/**
反序列化此字段内容的JSON表示的函数。
*/
fromJSON?: (json: any, state: EditorState) => Value;
};
StateEffect<T>
/**
状态效应可用于表示与[transaction][^1]相关的附加效应
当这些更改不隐含在文档或选择更改中时,它们通常用于对自定义[state fields][^2]的更改建模
[^1]:(https://codemirror.net/6/docs/ref/#state.Transaction.effects)
[^2]:(https://codemirror.net/6/docs/ref/#state.StateField)
*/
declare class StateEffect<Value> {
/**
The value of this effect.
*/
readonly value: Value;
/**
Map this effect through a position mapping. Will return
`undefined` when that ends up deleting the effect.
*/
map(mapping: ChangeDesc): StateEffect<Value> | undefined;
/**
Tells you whether this effect object is of a given
[type](https://codemirror.net/6/docs/ref/#state.StateEffectType).
*/
is<T>(type: StateEffectType<T>): this is StateEffect<T>;
/**
定义一个新的effect类型。type参数表示其效果所持有的值的类型
*/
static define<Value = null>(spec?: StateEffectSpec<Value>): StateEffectType<Value>;
/**
Map an array of effects through a change set.
*/
static mapEffects(effects: readonly StateEffect<any>[], mapping: ChangeDesc): readonly StateEffect<any>[];
/**
This effect can be used to reconfigure the root extensions of
the editor. Doing this will discard any extensions
[appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset
the content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure)
compartments.
*/
static reconfigure: StateEffectType<Extension>;
/**
将扩展附加到编辑器的顶级配置
*/
static appendConfig: StateEffectType<Extension>;
}
StateEffectType<T>
StateEffect.define()
用的
/**
一种StateEffect的表现形式。使用 StateEffect.define[^1] 定义
[^1]:(https://codemirror.net/6/docs/ref/#state.StateEffect^define)
*/
declare class StateEffectType<Value> {
/**
@internal
*/
readonly map: (value: any, mapping: ChangeDesc) => any | undefined;
/**
Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this type.
*/
of(value: Value): StateEffect<Value>;
}
StateField 和 StateEffect 的联系
// 实例
StateEffect.appendConfig.of(
[underlineField, underlineTheme]
))
---------------
// 剖析
declare class StateEffect<Value> {
static appendConfig: StateEffectType<Extension>;
}
declare class StateEffectType<Value> {
of(value: Value): StateEffect<Value>;
}
EditorState
尝试打印输出editorState
const underlineField = StateField.define<DecorationSet>({
create(editorState) {
return Decoration.none
},
update(decorationSet, tr) {
// 基本变量
const view: View|null = global_plugin_this.app.workspace.getActiveViewOfType(MarkdownView);
if (!view) return decorationSet
// @ts-ignore 这里会说View没有editor属性
const editor: Editor = view.editor
// @ts-ignore 这里会说Editor没有cm属性
const editorView: EditorView = editor.cm
const editorState: EditorState = editorView.state
console.log("update - effectsState", editorState)
},
provide: f => EditorView.decorations.from(f)
})
打印结构
{
computeSlot: null,
config: e/*{}*/,
doc: {
children: Array(3), // 感觉是每32行为一个元素,没什么其他规律
length: 551,
lines: 81,
},
selection: e/*{...}*/,
status: Array(16), // 好像没什么特别的
values: Array(16),
// get
lineBreak: "\n",
readOnly: false,
tabSize: 4,
tree: e/*{...}*/
}
和EditorState的关系
关系:有四个方法会需要StateField参数。主要是 field()
方法
这个在@codemirror/view里
/**
编辑器状态类是一个持久(不可变)的数据结构。
要更新状态,您[创建][^1]一个[transaction][^2],它生成一个_new_ state实例,而不修改原始对象。
因此,永远不要直接改变状态的属性。这只会破坏事情。
[^1]:(https://codemirror.net/6/docs/ref/#state.EditorState.update)
[^2]:(https://codemirror.net/6/docs/ref/#state.Transaction)
*/
declare class EditorState {
/**
当前文档.
*/
readonly doc: Text;
/**
当前选择.
*/
readonly selection: EditorSelection;
private constructor();
/**
检索 状态字段[^1] 的值.
当状态没有该字段时抛出错误,除非你传递' false '作为第二个参数
[^1]:(https://codemirror.net/6/docs/ref/#state.StateField)
*/
field<T>(field: StateField<T>): T;
field<T>(field: StateField<T>, require: false): T | undefined;
/**
Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this
state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec)
can be passed. Unless
[`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the
[changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec
are assumed to start in the _current_ document (not the document
produced by previous specs), and its
[selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and
[effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer
to the document created by its _own_ changes. The resulting
transaction contains the combined effect of all the different
specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later
specs take precedence over earlier ones.
*/
update(...specs: readonly TransactionSpec[]): Transaction;
/**
创建一个[事务规范 transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec),
用给定的内容替换每个选择范围。
*/
replaceSelection(text: string | Text): TransactionSpec;
/**
Create a set of changes and a new selection by running the given
function for each range in the active selection. The function
can return an optional set of changes (in the coordinate space
of the start document), plus an updated range (in the coordinate
space of the document produced by the call's own changes). This
method will merge all the changes and ranges into a single
changeset and selection, and return it as a [transaction
spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to
[`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update).
*/
changeByRange(f: (range: SelectionRange) => {
range: SelectionRange;
changes?: ChangeSpec;
effects?: StateEffect<any> | readonly StateEffect<any>[];
}): {
changes: ChangeSet;
selection: EditorSelection;
effects: readonly StateEffect<any>[];
};
/**
根据给定的变更描述创建一个[变更集 change set](https://codemirror.net/6/docs/ref/#state.ChangeSet),
将状态的文档长度和行分隔符考虑在内。
*/
changes(spec?: ChangeSpec): ChangeSet;
/**
Using the state's [line
separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a
[`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string.
*/
toText(string: string): Text;
/**
Return the given range of the document as a string.
*/
sliceDoc(from?: number, to?: number): string;
/**
Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet).
*/
facet<Output>(facet: Facet<any, Output>): Output;
/**
Convert this state to a JSON-serializable object. When custom
fields should be serialized, you can pass them in as an object
mapping property names (in the resulting object, which should
not use `doc` or `selection`) to fields.
*/
toJSON(fields?: {
[prop: string]: StateField<any>;
}): any;
/**
Deserialize a state from its JSON representation. When custom
fields should be deserialized, pass the same object you passed
to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as
third argument.
*/
static fromJSON(json: any, config?: EditorStateConfig, fields?: {
[prop: string]: StateField<any>;
}): EditorState;
/**
Create a new state. You'll usually only need this when
initializing an editor—updated states are created by applying
transactions.
*/
static create(config?: EditorStateConfig): EditorState;
/**
A facet that, when enabled, causes the editor to allow multiple
ranges to be selected. Be careful though, because by default the
editor relies on the native DOM selection, which cannot handle
multiple selections. An extension like
[`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make
secondary selections visible to the user.
*/
static allowMultipleSelections: Facet<boolean, boolean>;
/**
Configures the tab size to use in this state. The first
(highest-precedence) value of the facet is used. If no value is
given, this defaults to 4.
*/
static tabSize: Facet<number, number>;
/**
The size (in columns) of a tab in the document, determined by
the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet.
*/
get tabSize(): number;
/**
The line separator to use. By default, any of `"\n"`, `"\r\n"`
and `"\r"` is treated as a separator when splitting lines, and
lines are joined with `"\n"`.
When you configure a value here, only that precise separator
will be used, allowing you to round-trip documents through the
editor without normalizing line separators.
*/
static lineSeparator: Facet<string, string | undefined>;
/**
Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator)
string for this state.
*/
get lineBreak(): string;
/**
This facet controls the value of the
[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) getter, which is
consulted by commands and extensions that implement editing
functionality to determine whether they should apply. It
defaults to false, but when its highest-precedence value is
`true`, such functionality disables itself.
Not to be confused with
[`EditorView.editable`](https://codemirror.net/6/docs/ref/#view.EditorView^editable), which
controls whether the editor's DOM is set to be editable (and
thus focusable).
*/
static readOnly: Facet<boolean, boolean>;
/**
Returns true when the editor is
[configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only.
*/
get readOnly(): boolean;
/**
Registers translation phrases. The
[`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through
all objects registered with this facet to find translations for
its argument.
*/
static phrases: Facet<{
[key: string]: string;
}, readonly {
[key: string]: string;
}[]>;
/**
Look up a translation for the given phrase (via the
[`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the
original string if no translation is found.
If additional arguments are passed, they will be inserted in
place of markers like `$1` (for the first value) and `$2`, etc.
A single `$` is equivalent to `$1`, and `$$` will produce a
literal dollar sign.
*/
phrase(phrase: string, ...insert: any[]): string;
/**
A facet used to register [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers.
*/
static languageData: Facet<(state: EditorState, pos: number, side: 0 | 1 | -1) => readonly {
[name: string]: any;
}[], readonly ((state: EditorState, pos: number, side: 0 | 1 | -1) => readonly {
[name: string]: any;
}[])[]>;
/**
Find the values for a given language data field, provided by the
the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet.
*/
languageDataAt<T>(name: string, pos: number, side?: -1 | 0 | 1): readonly T[];
/**
Return a function that can categorize strings (expected to
represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak))
into one of:
- Word (contains an alphanumeric character or a character
explicitly listed in the local language's `"wordChars"`
language data, which should be a string)
- Space (contains only whitespace)
- Other (anything else)
*/
charCategorizer(at: number): (char: string) => CharCategory;
/**
Find the word at the given position, meaning the range
containing all [word](https://codemirror.net/6/docs/ref/#state.CharCategory.Word) characters
around it. If no word characters are adjacent to the position,
this returns null.
*/
wordAt(pos: number): SelectionRange | null;
/**
Facet used to register change filters, which are called for each
transaction (unless explicitly
[disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress
part of the transaction's changes.
Such a function can return `true` to indicate that it doesn't
want to do anything, `false` to completely stop the changes in
the transaction, or a set of ranges in which changes should be
suppressed. Such ranges are represented as an array of numbers,
with each pair of two numbers indicating the start and end of a
range. So for example `[10, 20, 100, 110]` suppresses changes
between 10 and 20, and between 100 and 110.
*/
static changeFilter: Facet<(tr: Transaction) => boolean | readonly number[], readonly ((tr: Transaction) => boolean | readonly number[])[]>;
/**
Facet used to register a hook that gets a chance to update or
replace transaction specs before they are applied. This will
only be applied for transactions that don't have
[`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You
can either return a single transaction spec (possibly the input
transaction), or an array of specs (which will be combined in
the same way as the arguments to
[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)).
When possible, it is recommended to avoid accessing
[`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter,
since it will force creation of a state that will then be
discarded again, if the transaction is actually filtered.
(This functionality should be used with care. Indiscriminately
modifying transaction is likely to break something or degrade
the user experience.)
*/
static transactionFilter: Facet<(tr: Transaction) => TransactionSpec | readonly TransactionSpec[], readonly ((tr: Transaction) => TransactionSpec | readonly TransactionSpec[])[]>;
/**
This is a more limited form of
[`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter),
which can only add
[annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and
[effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type
of filter runs even if the transaction has disabled regular
[filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable
for effects that don't need to touch the changes or selection,
but do want to process every transaction.
Extenders run _after_ filters, when both are present.
*/
static transactionExtender: Facet<(tr: Transaction) => Pick<TransactionSpec, "effects" | "annotations"> | null, readonly ((tr: Transaction) => Pick<TransactionSpec, "effects" | "annotations"> | null)[]>;
}
Range
RangeValue
// 没什么好说的,用来被RangeSet包含
declare abstract class RangeValue;
// 主要看一下他的一些派生类
declare abstract class Decoration extends RangeValue; // 这个在@codemirror/view里
RangeSet<T>
// 用来包含RangeValue的一种特殊可迭代类
declare class RangeSet<T extends RangeValue>
完整原型声明
/**
* 范围集存储[range]的集合,使它们能够有效地进行[map]和[update]。
* 这是一个不可变的数据结构。
* _
* 注意“不可变”,修改要类似这样:decorationSet = decorationSet.map(tr.changes)
*/
declare class RangeSet<T extends RangeValue> {
private constructor();
/**
The number of ranges in the set.
*/
get size(): number;
/**
更新范围集,可选地添加新的范围或过滤现有的范围。
(注意: 当`X`是`Y`的子类型时,类型参数只是作为解决TypeScript方差问题的一个拼凑,阻止 `RangeSet<Y>` 成为 `RangeSet<Y>` 的子类型。)
*/
update<U extends T>(updateSpec: RangeSetUpdate<U>): RangeSet<T>;
/**
通过一组更改映射这个范围集,并返回新的集合。
*/
map(changes: ChangeDesc): RangeSet<T>;
/**
遍历触及区域 `from` 到 `to` 的范围,对每个区域调用 `f`。不能保证这些范围将以任何特定的顺序进行报告。当回调返回 `false` 时,迭代停止。
*/
between(from: number, to: number, f: (from: number, to: number, value: T) => void | false): void;
/**
按顺序遍历此集合中的范围,包括以 `from` 结束或在 `from` 之后结束的所有范围。
*/
iter(from?: number): RangeCursor<T>;
/**
从 `from` 开始,按顺序遍历集合中的范围。
*/
static iter<T extends RangeValue>(sets: readonly RangeSet<T>[], from?: number): RangeCursor<T>;
/**
比较。
遍历两组集合,调用comparator上的方法,通知它可能存在的差异。
*/
static compare<T extends RangeValue>(oldSets: readonly RangeSet<T>[], newSets: readonly RangeSet<T>[],
/**
This indicates how the underlying data changed between these
ranges, and is needed to synchronize the iteration. `from` and
`to` are coordinates in the _new_ space, after these changes.
*/
textDiff: ChangeDesc, comparator: RangeComparator<T>,
/**
Can be used to ignore all non-point ranges, and points below
the given size. When -1, all ranges are compared.
*/
minPointSize?: number): void;
/**
比较两组范围集的内容,如果它们在给定范围内相等,则返回true。
*/
static eq<T extends RangeValue>(oldSets: readonly RangeSet<T>[], newSets: readonly RangeSet<T>[], from?: number, to?: number): boolean;
/**
同时遍历一组范围集,通知迭代器覆盖每个给定内容的范围。
在迭代结束时返回打开计数 (参见[`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span))。
*/
static spans<T extends RangeValue>(sets: readonly RangeSet<T>[], from: number, to: number, iterator: SpanIterator<T>,
/**
当给定值且大于-1时,只考虑至少这个大小的点。
*/
minPointSize?: number): number;
/**
为给定的范围或范围数组创建一个范围集。
默认情况下,这期望范围是 _sorted_ (根据开始位置,如果两个开始位置相同,则 `value.startSide`)。
你可以传递 `true` 作为第二个参数,让方法对它们进行排序。
*/
static of<T extends RangeValue>(ranges: readonly Range<T>[] | Range<T>, sort?: boolean): RangeSet<T>;
/**
范围的空集合。
*/
static empty: RangeSet<any>;
}
RangeSetBuilder<T extends RangeValue>
// 主要定义出来构造RangeSet<T>
// 相较于直接使用RangeSet,这里可以add填充列表,再生成RangeValue,比较方便
declare class RangeSetBuilder<T extends RangeValue> {
constructor();
add(from: number, to: number, value: T): void; // 这里传入泛型
finish(): RangeSet<T>; // 返回RangeSet<T>
}
看Range如何影响Decoration的设计
这个在@codemirror/view里
DecorationSet
// DecorationSet(装饰集):表示Decoration (装饰) Range (范围)的集合,为高效的访问和映射而组织
// 完全等同于 RangeSet<Decoration>
declare type DecorationSet = RangeSet<Decoration>;
RangeSetBuilder<Decoration>()
RangeSetBuilder<Decoration>()
Transaction
比较重要的两个参数的输出:
- tr.changes
- tr.effects
tr.changes
// tr.changes,修改前
{
inserted: [],
sections: [482, -1],
// get
desc: e,
empty: true, // 无修改内容
invertedDesc: e,
length: 482,
newLength: 482,
}
// tr.changes,修改后(倒数第2个字符处插入1个字符"1")
{
inserted: [
{
length: 0,
text: [''],
children: null,
lines: 1
},
{
length: 1,
text: ['1'], // 插入的字符
children: null,
lines: 1
}
],
sections: [480, -1, 0, 1, 2, -1],
// get
desc: e,
empty: false, // 有修改内容
invertedDesc: e,
length: 482, // 原文档482个字符
newLength: 483, // 修改后483个字符
}
重点说一下这个inserted和sections列表
// 有一个插入位置长度是2,有2个插入位置长度就是4,其中单数位置上似乎没存东西,双数位置是增加的内容东西
inserted: Array(2)
// 含义详见后面
sections: [480, -1, 0, 1, 2, -1]
// 这里举例来说明一下
// 其中-1相当于是一种识别符,用来间隔的
// [光标, -1] 这种表示没有变化
// “到下一修改处光标移动的数量”,如果没有下一处,则为到末尾的光标移动数量
[
光标, -1,
减少的字符数量, 增加的自负数量, 到下一修改处光标移动的数量, -1
]
[480, -1, 0, 1, 2, -1] // 加一
[480, -1, 1, 0, 2, -1] // 减一
[480, -1, 0, 2, 2, -1] // 加二
[480, -1, 2, 0, 2, -1] // 减二
// 来复杂点的。这个我用多光标在两个位置分别插入了1个字符
// 其中第一处和第二处间隔8个字符,第二处和结尾间隔3个字符
[
光标, -1,
减少, 增加, 到下一修改处光标移动的数量, -1,
减少, 增加, 到下一修改处光标移动的数量, -1
]
[477, -1, 0, 1, 8, -1, 0, 1, 3, -1]
tr.effects
// tr.effects
[
{
type: "e {map: f}",
value: underfined
}
]
原型
/**
编辑器**状态的更改**被分组到transactions(事务)中。
通常,用户操作创建单个事务,该事务可以包含任意数量的文档更改,可以更改选择,或者具有其他影响。
通过调用EditorState[^1]创建事务。通过调用EditorView.dispatch[^2]来调度一个。
[^1]:(https://codemirror.net/6/docs/ref/#state.EditorState.update)
[^2]:(https://codemirror.net/6/docs/ref/#view.EditorView.dispatch)
*/
declare class Transaction {
/**
The state from which the transaction starts.
*/
readonly startState: EditorState;
/**
本次Transaction所做的文件变更
*/
readonly changes: ChangeSet;
/**
The selection set by this transaction, or undefined if it
doesn't explicitly set a selection.
*/
readonly selection: EditorSelection | undefined;
/**
The effects added to the transaction.
*/
readonly effects: readonly StateEffect<any>[];
/**
Whether the selection should be scrolled into view after this
transaction is dispatched.
*/
readonly scrollIntoView: boolean;
private constructor();
/**
The new document produced by the transaction. Contrary to
[`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't
force the entire new state to be computed right away, so it is
recommended that [transaction
filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter
when they need to look at the new document.
*/
get newDoc(): Text;
/**
The new selection produced by the transaction. If
[`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined,
this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's
current selection through the changes made by the transaction.
*/
get newSelection(): EditorSelection;
/**
Transaction创建的新状态。按需计算(但为后续访问保留),
因此建议尽可能不要在Transaction 过滤器[^1] 中访问它。
[^1]:(https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter)
*/
get state(): EditorState;
/**
Get the value of the given annotation type, if any.
*/
annotation<T>(type: AnnotationType<T>): T | undefined;
/**
指示事务是否更改了文档。
*/
get docChanged(): boolean;
/**
Indicates whether this transaction reconfigures the state
(through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or
with a top-level configuration
[effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure).
*/
get reconfigured(): boolean;
/**
Returns true if the transaction has a [user
event](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent) annotation that is equal to
or more specific than `event`. For example, if the transaction
has `"select.pointer"` as user event, `"select"` and
`"select.pointer"` will match it.
*/
isUserEvent(event: string): boolean;
/**
Annotation used to store transaction timestamps. Automatically
added to every transaction, holding `Date.now()`.
*/
static time: AnnotationType<number>;
/**
Annotation used to associate a transaction with a user interface
event. Holds a string identifying the event, using a
dot-separated format to support attaching more specific
information. The events used by the core libraries are:
- `"input"` when content is entered
- `"input.type"` for typed input
- `"input.type.compose"` for composition
- `"input.paste"` for pasted input
- `"input.drop"` when adding content with drag-and-drop
- `"input.complete"` when autocompleting
- `"delete"` when the user deletes content
- `"delete.selection"` when deleting the selection
- `"delete.forward"` when deleting forward from the selection
- `"delete.backward"` when deleting backward from the selection
- `"delete.cut"` when cutting to the clipboard
- `"move"` when content is moved
- `"move.drop"` when content is moved within the editor through drag-and-drop
- `"select"` when explicitly changing the selection
- `"select.pointer"` when selecting with a mouse or other pointing device
- `"undo"` and `"redo"` for history actions
Use [`isUserEvent`](https://codemirror.net/6/docs/ref/#state.Transaction.isUserEvent) to check
whether the annotation matches a given event.
*/
static userEvent: AnnotationType<string>;
/**
Annotation indicating whether a transaction should be added to
the undo history or not.
*/
static addToHistory: AnnotationType<boolean>;
/**
Annotation indicating (when present and true) that a transaction
represents a change made by some other actor, not the user. This
is used, for example, to tag other people's changes in
collaborative editing.
*/
static remote: AnnotationType<boolean>;
}
View包
import {
// 比较常用的
EditorView, // 重要参数 - 文档和视图相关
ViewPlugin, // 视图插件
WidgetType, // 绘制小部件
Decoration, // 装饰
PluginValue, // @interface 传入ViewPlugin的一个重要参数的接口规范
// PluginSpec, // @interface 传入ViewPlugin的一个重要参数的接口规范,一般不需要import
// 没见过
ViewUpdate, // 文档变更时的更新内容
BlockInfo,
BidiSpan,
MatchDecorator,
GutterMarker
} from '@codemirror/view';
EditorView(view: EditorView)
view
// 打印属性
// update(...){console.log("view", view)}
// 上下滚动会触发打印
{
viewState
// 这几个都是返回div元素
dom: div.cm-editor.ͼ1.ͼ2.ͼ3a.ͼq
announceDOM: div
contentDOM: div.cm-content
scrollDOM: div.cm-scroller
// get类 方法
viewport // 和visibleRanges类似,返回 {from, to} (没有列表)
visibleRanges // 获取当前文档被渲染的部分,返回 readonly {form, to}[]
state // view.state.sliceDoc(from, to); 这个方法能返回渲染部分的正文内容
}
view.visibleRanges
// 打印属性
// update(...){console.log("visibleRanges", view.visibleRanges)},这是一个getter属性
// 上下滚动会触发打印
[{from:0, to:653}] // 现在开始往下滚动
[{from:0, to:920}]
[{from:0, to:992}]
[{from:539, to:1175}]
[{from:775, to:2013}]
[{from:921, to:2771}]
[{from:1101, to:3456}]
[{from:1390, to:4249}]
[{from:2156, to:4262}] // 现在开始往上滚动。全文一共4262个字符,符合
[{from:1192, to:4067}]
[{from:889, to:2379}]
[{from:785, to:1855}]
[{from:588, to:1226}]
// 有时这个数组长度是大于1的,并且会发现可能会缺失一部分
// 发现缺失的部分是ad和callout语法或与之相似的情况
[
{from:0, to:557},
{from:783, to:830},
{from:856, to:859},
{from:884, to:919},
]
// 一般使用该属性的写法:
for (const { from, to } of visibleRanges) {}
原型
/**
An editor view represents the editor's user interface. It holds
the editable DOM surface, and possibly other elements such as the
line number gutter. It handles events and dispatches state
transactions for editing actions.
*/
declare class EditorView {
/**
The current editor state.
*/
get state(): EditorState;
/**
To be able to display large documents without consuming too much
memory or overloading the browser, CodeMirror only draws the
code that is visible (plus a margin around it) to the DOM. This
property tells you the extent of the current drawn viewport, in
document positions.
*/
get viewport(): {
from: number;
to: number;
};
/**
When there are, for example, large collapsed ranges in the
viewport, its size can be a lot bigger than the actual visible
content. Thus, if you are doing something like styling the
content in the viewport, it is preferable to only do so for
these ranges, which are the subset of the viewport that is
actually drawn.
*/
get visibleRanges(): readonly {
from: number;
to: number;
}[];
/**
Returns false when the editor is entirely scrolled out of view
or otherwise hidden.
*/
get inView(): boolean;
/**
Indicates whether the user is currently composing text via
[IME](https://en.wikipedia.org/wiki/Input_method), and at least
one change has been made in the current composition.
*/
get composing(): boolean;
/**
Indicates whether the user is currently in composing state. Note
that on some platforms, like Android, this will be the case a
lot, since just putting the cursor on a word starts a
composition there.
*/
get compositionStarted(): boolean;
private _dispatch;
private _root;
/**
The document or shadow root that the view lives in.
*/
get root(): DocumentOrShadowRoot;
/**
The DOM element that wraps the entire editor view.
*/
readonly dom: HTMLElement;
/**
The DOM element that can be styled to scroll. (Note that it may
not have been, so you can't assume this is scrollable.)
*/
readonly scrollDOM: HTMLElement;
/**
The editable DOM element holding the editor content. You should
not, usually, interact with this content directly though the
DOM, since the editor will immediately undo most of the changes
you make. Instead, [dispatch](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch)
[transactions](https://codemirror.net/6/docs/ref/#state.Transaction) to modify content, and
[decorations](https://codemirror.net/6/docs/ref/#view.Decoration) to style it.
*/
readonly contentDOM: HTMLElement;
private announceDOM;
private plugins;
private pluginMap;
private editorAttrs;
private contentAttrs;
private styleModules;
private bidiCache;
private destroyed;
/**
构造一个新视图。你可能想要提供一个 `parent` 选项,或者put `view.dom` 。
在创建视图后,将Dom添加到文档中,以便用户可以看到编辑器。
*/
constructor(config?: EditorViewConfig);
/**
所有常规的编辑器状态更新都应经过此步骤。它接受一个事务或事务规范,并更新视图以显示该事务产生的新状态。
它的实现可以被一个[option](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.dispatch)覆盖。
此函数绑定到视图实例,因此不必作为方法调用。
*/
dispatch(tr: Transaction): void;
dispatch(...specs: TransactionSpec[]): void;
/**
更新给定事务数组的视图。这将更新可见的文档和选择,以匹配事务产生的状态,并将更改通知视图插件。
您通常应该调用[`dispatch`][^1],它使用this作为原型
[^1]:(https://codemirror.net/6/docs/ref/#view.EditorView.dispatch)
*/
update(transactions: readonly Transaction[]): void;
/**
将视图重置为给定的状态。
(这将导致整个文档被重绘,所有的视图插件都被重新初始化,所以你应该只在新状态不是从旧状态派生出来的时候使用它。
否则,使用[`dispatch`][^1]代替。)
[^1]:(https://codemirror.net/6/docs/ref/#view.EditorView.dispatch)
*/
setState(newState: EditorState): void;
private updatePlugins;
/**
Get the CSS classes for the currently active editor themes.
*/
get themeClasses(): string;
private updateAttrs;
private showAnnouncements;
private mountStyles;
private readMeasured;
/**
Schedule a layout measurement, optionally providing callbacks to
do custom DOM measuring followed by a DOM write phase. Using
this is preferable reading DOM layout directly from, for
example, an event handler, because it'll make sure measuring and
drawing done by other components is synchronized, avoiding
unnecessary DOM layout computations.
*/
requestMeasure<T>(request?: MeasureRequest<T>): void;
/**
Get the value of a specific plugin, if present. Note that
plugins that crash can be dropped from a view, so even when you
know you registered a given plugin, it is recommended to check
the return value of this method.
*/
plugin<T extends PluginValue>(plugin: ViewPlugin<T>): T | null;
/**
The top position of the document, in screen coordinates. This
may be negative when the editor is scrolled down. Points
directly to the top of the first line, not above the padding.
*/
get documentTop(): number;
/**
Reports the padding above and below the document.
*/
get documentPadding(): {
top: number;
bottom: number;
};
/**
Find the text line or block widget at the given vertical
position (which is interpreted as relative to the [top of the
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
*/
elementAtHeight(height: number): BlockInfo;
/**
Find the line block (see
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
height, again interpreted relative to the [top of the
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop).
*/
lineBlockAtHeight(height: number): BlockInfo;
/**
Get the extent and vertical position of all [line
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
are relative to the [top of the
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
*/
get viewportLineBlocks(): BlockInfo[];
/**
Find the line block around the given document position. A line
block is a range delimited on both sides by either a
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, or the
start/end of the document. It will usually just hold a line of
text, but may be broken into multiple textblocks by block
widgets.
*/
lineBlockAt(pos: number): BlockInfo;
/**
The editor's total content height.
*/
get contentHeight(): number;
/**
Move a cursor position by [grapheme
cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether
the motion is away from the line start, or towards it. In
bidirectional text, the line is traversed in visual order, using
the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
When the start position was the last one on the line, the
returned position will be across the line break. If there is no
further line, the original position is returned.
By default, this method moves over a single cluster. The
optional `by` argument can be used to move across more. It will
be called with the first cluster as argument, and should return
a predicate that determines, for each subsequent cluster,
whether it should also be moved over.
*/
moveByChar(start: SelectionRange, forward: boolean, by?: (initial: string) => (next: string) => boolean): SelectionRange;
/**
Move a cursor position across the next group of either
[letters](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) or non-letter
non-whitespace characters.
*/
moveByGroup(start: SelectionRange, forward: boolean): SelectionRange;
/**
Move to the next line boundary in the given direction. If
`includeWrap` is true, line wrapping is on, and there is a
further wrap point on the current line, the wrap point will be
returned. Otherwise this function will return the start or end
of the line.
*/
moveToLineBoundary(start: SelectionRange, forward: boolean, includeWrap?: boolean): SelectionRange;
/**
Move a cursor position vertically. When `distance` isn't given,
it defaults to moving to the next line (including wrapped
lines). Otherwise, `distance` should provide a positive distance
in pixels.
When `start` has a
[`goalColumn`](https://codemirror.net/6/docs/ref/#state.SelectionRange.goalColumn), the vertical
motion will use that as a target horizontal position. Otherwise,
the cursor's own horizontal position is used. The returned
cursor will have its goal column set to whichever column was
used.
*/
moveVertically(start: SelectionRange, forward: boolean, distance?: number): SelectionRange;
/**
Find the DOM parent node and offset (child offset if `node` is
an element, character offset when it is a text node) at the
given document position.
Note that for positions that aren't currently in
`visibleRanges`, the resulting DOM position isn't necessarily
meaningful (it may just point before or after a placeholder
element).
*/
domAtPos(pos: number): {
node: Node;
offset: number;
};
/**
Find the document position at the given DOM node. Can be useful
for associating positions with DOM events. Will raise an error
when `node` isn't part of the editor content.
*/
posAtDOM(node: Node, offset?: number): number;
/**
Get the document position at the given screen coordinates. For
positions not covered by the visible viewport's DOM structure,
this will return null, unless `false` is passed as second
argument, in which case it'll return an estimated position that
would be near the coordinates if it were rendered.
*/
posAtCoords(coords: {
x: number;
y: number;
}, precise: false): number;
posAtCoords(coords: {
x: number;
y: number;
}): number | null;
/**
Get the screen coordinates at the given document position.
`side` determines whether the coordinates are based on the
element before (-1) or after (1) the position (if no element is
available on the given side, the method will transparently use
another strategy to get reasonable coordinates).
*/
coordsAtPos(pos: number, side?: -1 | 1): Rect | null;
/**
The default width of a character in the editor. May not
accurately reflect the width of all characters (given variable
width fonts or styling of invididual ranges).
*/
get defaultCharacterWidth(): number;
/**
The default height of a line in the editor. May not be accurate
for all lines.
*/
get defaultLineHeight(): number;
/**
The text direction
([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
CSS property) of the editor's content element.
*/
get textDirection(): Direction;
/**
Find the text direction of the block at the given position, as
assigned by CSS. If
[`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection)
isn't enabled, or the given position is outside of the viewport,
this will always return the same as
[`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that
this may trigger a DOM layout.
*/
textDirectionAt(pos: number): Direction;
/**
Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
(as determined by the
[`white-space`](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space)
CSS property of its content element).
*/
get lineWrapping(): boolean;
/**
Returns the bidirectional text structure of the given line
(which should be in the current document) as an array of span
objects. The order of these spans matches the [text
direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection)—if that is
left-to-right, the leftmost spans come first, otherwise the
rightmost spans come first.
*/
bidiSpans(line: Line): readonly BidiSpan[];
/**
Check whether the editor has focus.
*/
get hasFocus(): boolean;
/**
Put focus on the editor.
*/
focus(): void;
/**
Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only
necessary when moving the editor's existing DOM to a new window or shadow root.
*/
setRoot(root: Document | ShadowRoot): void;
/**
Clean up this editor view, removing its element from the
document, unregistering event handlers, and notifying
plugins. The view instance can no longer be used after
calling this.
*/
destroy(): void;
/**
Returns an effect that can be
[added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
cause it to scroll the given position or range into view.
*/
static scrollIntoView(pos: number | SelectionRange, options?: {
/**
By default (`"nearest"`) the position will be vertically
scrolled only the minimal amount required to move the given
position into view. You can set this to `"start"` to move it
to the top of the view, `"end"` to move it to the bottom, or
`"center"` to move it to the center.
*/
y?: ScrollStrategy;
/**
Effect similar to
[`y`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView^options.y), but for the
horizontal scroll position.
*/
x?: ScrollStrategy;
/**
Extra vertical distance to add when moving something into
view. Not used with the `"center"` strategy. Defaults to 5.
*/
yMargin?: number;
/**
Extra horizontal distance to add. Not used with the `"center"`
strategy. Defaults to 5.
*/
xMargin?: number;
}): StateEffect<unknown>;
/**
Facet to add a [style
module](https://github.com/marijnh/style-mod#documentation) to
an editor view. The view will ensure that the module is
mounted in its [document
root](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.root).
*/
static styleModule: Facet<StyleModule, readonly StyleModule[]>;
/**
Returns an extension that can be used to add DOM event handlers.
The value should be an object mapping event names to handler
functions. For any given event, such functions are ordered by
extension precedence, and the first handler to return true will
be assumed to have handled that event, and no other handlers or
built-in behavior will be activated for it. These are registered
on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
for `scroll` handlers, which will be called any time the
editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
its parent nodes is scrolled.
*/
static domEventHandlers(handlers: DOMEventHandlers<any>): Extension;
/**
An input handler can override the way changes to the editable
DOM content are handled. Handlers are passed the document
positions between which the change was found, and the new
content. When one returns true, no further input handlers are
called and the default behavior is prevented.
*/
static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string) => boolean, readonly ((view: EditorView, from: number, to: number, text: string) => boolean)[]>;
/**
By default, the editor assumes all its content has the same
[text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
value to make it read the text direction of every (rendered)
line separately.
*/
static perLineTextDirection: Facet<boolean, boolean>;
/**
Allows you to provide a function that should be called when the
library catches an exception from an extension (mostly from view
plugins, but may be used by other extensions to route exceptions
from user-code-provided callbacks). This is mostly useful for
debugging and logging. See [`logException`](https://codemirror.net/6/docs/ref/#view.logException).
*/
static exceptionSink: Facet<(exception: any) => void, readonly ((exception: any) => void)[]>;
/**
A facet that can be used to register a function to be called
every time the view updates.
*/
static updateListener: Facet<(update: ViewUpdate) => void, readonly ((update: ViewUpdate) => void)[]>;
/**
Facet that controls whether the editor content DOM is editable.
When its highest-precedence value is `false`, the element will
not have its `contenteditable` attribute set. (Note that this
doesn't affect API calls that change the editor content, even
when those are bound to keys or buttons. See the
[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
*/
static editable: Facet<boolean, boolean>;
/**
Allows you to influence the way mouse selection happens. The
functions in this facet will be called for a `mousedown` event
on the editor, and can return an object that overrides the way a
selection is computed from that mouse click or drag.
*/
static mouseSelectionStyle: Facet<MakeSelectionStyle, readonly MakeSelectionStyle[]>;
/**
Facet used to configure whether a given selection drag event
should move or copy the selection. The given predicate will be
called with the `mousedown` event, and can return `true` when
the drag should move the content.
*/
static dragMovesSelection: Facet<(event: MouseEvent) => boolean, readonly ((event: MouseEvent) => boolean)[]>;
/**
Facet used to configure whether a given selecting click adds a
new range to the existing selection or replaces it entirely. The
default behavior is to check `event.metaKey` on macOS, and
`event.ctrlKey` elsewhere.
*/
static clickAddsSelectionRange: Facet<(event: MouseEvent) => boolean, readonly ((event: MouseEvent) => boolean)[]>;
/**
A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration)
are shown in the view. Decorations can be provided in two
ways—directly, or via a function that takes an editor view.
Only decoration sets provided directly are allowed to influence
the editor's vertical layout structure. The ones provided as
functions are called _after_ the new viewport has been computed,
and thus **must not** introduce block widgets or replacing
decorations that cover line breaks.
If you want decorated ranges to behave like atomic units for
cursor motion and deletion purposes, also provide the range set
containing the decorations to
[`EditorView.atomicRanges`](https://codemirror.net/6/docs/ref/#view.EditorView^atomicRanges).
*/
static decorations: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Used to provide ranges that should be treated as atoms as far as
cursor motion is concerned. This causes methods like
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
[`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
commands built on top of them) to skip across such regions when
a selection endpoint would enter them. This does _not_ prevent
direct programmatic [selection
updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
regions.
*/
static atomicRanges: Facet<(view: EditorView) => _codemirror_state.RangeSet<any>, readonly ((view: EditorView) => _codemirror_state.RangeSet<any>)[]>;
/**
Facet that allows extensions to provide additional scroll
margins (space around the sides of the scrolling element that
should be considered invisible). This can be useful when the
plugin introduces elements that cover part of that element (for
example a horizontally fixed gutter).
*/
static scrollMargins: Facet<(view: EditorView) => Partial<Rect> | null, readonly ((view: EditorView) => Partial<Rect> | null)[]>;
/**
Create a theme extension. The first argument can be a
[`style-mod`](https://github.com/marijnh/style-mod#documentation)
style spec providing the styles for the theme. These will be
prefixed with a generated class for the style.
Because the selectors will be prefixed with a scope class, rule
that directly match the editor's [wrapper
element](https://codemirror.net/6/docs/ref/#view.EditorView.dom)—to which the scope class will be
added—need to be explicitly differentiated by adding an `&` to
the selector for that element—for example
`&.cm-focused`.
When `dark` is set to true, the theme will be marked as dark,
which will cause the `&dark` rules from [base
themes](https://codemirror.net/6/docs/ref/#view.EditorView^baseTheme) to be used (as opposed to
`&light` when a light theme is active).
*/
static theme(spec: {
[selector: string]: StyleSpec;
}, options?: {
dark?: boolean;
}): Extension;
/**
This facet records whether a dark theme is active. The extension
returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically
includes an instance of this when the `dark` option is set to
true.
*/
static darkTheme: Facet<boolean, boolean>;
/**
Create an extension that adds styles to the base theme. Like
with [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme), use `&` to indicate the
place of the editor wrapper element when directly targeting
that. You can also use `&dark` or `&light` instead to only
target editors with a dark or light theme.
*/
static baseTheme(spec: {
[selector: string]: StyleSpec;
}): Extension;
/**
Facet that provides additional DOM attributes for the editor's
editable DOM element.
*/
static contentAttributes: Facet<AttrSource, readonly AttrSource[]>;
/**
Facet that provides DOM attributes for the editor's outer
element.
*/
static editorAttributes: Facet<AttrSource, readonly AttrSource[]>;
/**
An extension that enables line wrapping in the editor (by
setting CSS `white-space` to `pre-wrap` in the content).
*/
static lineWrapping: Extension;
/**
State effect used to include screen reader announcements in a
transaction. These will be added to the DOM in a visually hidden
element with `aria-live="polite"` set, and should be used to
describe effects that are visually obvious but may not be
noticed by screen reader users (such as moving to the next
search match).
*/
static announce: _codemirror_state.StateEffectType<string>;
/**
Retrieve an editor view instance from the view's DOM
representation.
*/
static findFromDOM(dom: HTMLElement): EditorView | null;
}
ViewPlugin
内容不多,主要是fromClass方法比较奇特。需要传入一个 PluginValue
declare class ViewPlugin<V extends PluginValue> {
/**
该类的实例充当扩展
*/
extension: Extension;
private constructor();
/**
在给定的编辑器视图下,从构造函数定义插件,该函数创建插件的值。
*/
static define<V extends PluginValue>(create: (view: EditorView) => V, spec?: PluginSpec<V>): ViewPlugin<V>;
/**
创建一个类的插件,其构造函数以单个编辑器视图作为参数。
*/
static fromClass<V extends PluginValue>(cls: {
new (view: EditorView): V;
}, spec?: PluginSpec<V>): ViewPlugin<V>;
}
PluginValue(interface)
(注意区分 PluginValue 和 ViewPlugin,他们长得有点像)
要想创建一个视图插件,需要创建一个继承自 PluginValue 的类,并将它传给 ViewPlugin.fromClass() 方法。
import {
ViewUpdate,
PluginValue,
EditorView,
ViewPlugin,
} from "@codemirror/view";
class ExamplePlugin implements PluginValue {
constructor(view: EditorView) {/*...*/}
update(update: ViewUpdate) {/*...*/}
destroy() {/*...*/} // 可以不实现
}
export const examplePlugin = ViewPlugin.fromClass(ExamplePlugin);
以下三个视图插件的方法控制它的生命周期:
constructor()
方法用于插件的初始化。update()
方法在发生改变时更新您的插件,比如在用户输入或者选择一些文本时。destroy()
方法在插件卸载后进行清理操作。
PluginSpec(interface)
一般只用写一个 decorations: (v) => v.decorations,
方法就行了
interface PluginSpec<V extends PluginValue> {
/**
为插件注册给定的[事件处理程序](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers)。
当调用时,这些函数的“this”将绑定到插件值。
*/
eventHandlers?: DOMEventHandlers<V>;
/**
指定插件在添加到编辑器配置时提供额外的扩展。
*/
provide?: (plugin: ViewPlugin<V>) => Extension;
/**
允许插件提供装饰。
当给出时,这应该是一个接受插件值并返回[decoration set](https://codemirror.net/6/docs/ref/#view.DecorationSet)的函数。
另见关于[改变布局的装饰](https://codemirror.net/6/docs/ref/#view.EditorView^decorations)依赖于视图的警告。
一般直接写这句是就好了:decorations: (v) => v.decorations,
*/
decorations?: (value: V) => DecorationSet;
}
Decoration
详见 装饰专题 和 Range 这两章笔记
链接到当前文件 0
没有文件链接到当前文件