自动格式化

通过短代码和类Markdown快捷方式实现文本自动格式化。

Files
components/demo.tsx
'use client';

import * as React from 'react';

import { Plate, usePlateEditor } from 'platejs/react';

import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';

import { DEMO_VALUES } from './values/demo-values';

export default function Demo({ id }: { id: string }) {
  const editor = usePlateEditor({
    plugins: EditorKit,
    value: DEMO_VALUES[id],
  });

  return (
    <Plate editor={editor}>
      <EditorContainer variant="demo">
        <Editor />
      </EditorContainer>
    </Plate>
  );
}
Apply formatting automatically using shortcodes.
autoformat-demo
autoformat-demo

功能特性

  • 块级元素的Markdown风格快捷方式(如#转换为H1,>转换为引用块)
  • 行内标记格式化(如**粗体***斜体*~~删除线~~
  • 智能标点转换(如--转为...转为
  • 数学符号和分数
  • 法律符号和箭头
  • 支持通过删除操作撤销自动格式化

套件使用

安装

最快捷的添加自动格式化方式是使用AutoformatKit,它包含了全面的块级、标记级和文本替换的格式化规则。

'use client';

import type { AutoformatRule } from '@platejs/autoformat';

import {
  autoformatArrow,
  autoformatLegal,
  autoformatLegalHtml,
  autoformatMath,
  AutoformatPlugin,
  autoformatPunctuation,
  autoformatSmartQuotes,
} from '@platejs/autoformat';
import { insertEmptyCodeBlock } from '@platejs/code-block';
import { toggleList } from '@platejs/list';
import { KEYS } from 'platejs';

const autoformatMarks: AutoformatRule[] = [
  {
    match: '***',
    mode: 'mark',
    type: [KEYS.bold, KEYS.italic],
  },
  {
    match: '__*',
    mode: 'mark',
    type: [KEYS.underline, KEYS.italic],
  },
  {
    match: '__**',
    mode: 'mark',
    type: [KEYS.underline, KEYS.bold],
  },
  {
    match: '___***',
    mode: 'mark',
    type: [KEYS.underline, KEYS.bold, KEYS.italic],
  },
  {
    match: '**',
    mode: 'mark',
    type: KEYS.bold,
  },
  {
    match: '__',
    mode: 'mark',
    type: KEYS.underline,
  },
  {
    match: '*',
    mode: 'mark',
    type: KEYS.italic,
  },
  {
    match: '_',
    mode: 'mark',
    type: KEYS.italic,
  },
  {
    match: '~~',
    mode: 'mark',
    type: KEYS.strikethrough,
  },
  {
    match: '^',
    mode: 'mark',
    type: KEYS.sup,
  },
  {
    match: '~',
    mode: 'mark',
    type: KEYS.sub,
  },
  {
    match: '==',
    mode: 'mark',
    type: KEYS.highlight,
  },
  {
    match: '≡',
    mode: 'mark',
    type: KEYS.highlight,
  },
  {
    match: '`',
    mode: 'mark',
    type: KEYS.code,
  },
];

const autoformatBlocks: AutoformatRule[] = [
  {
    match: '# ',
    mode: 'block',
    type: KEYS.h1,
  },
  {
    match: '## ',
    mode: 'block',
    type: KEYS.h2,
  },
  {
    match: '### ',
    mode: 'block',
    type: KEYS.h3,
  },
  {
    match: '#### ',
    mode: 'block',
    type: KEYS.h4,
  },
  {
    match: '##### ',
    mode: 'block',
    type: KEYS.h5,
  },
  {
    match: '###### ',
    mode: 'block',
    type: KEYS.h6,
  },
  {
    match: '> ',
    mode: 'block',
    type: KEYS.blockquote,
  },
  {
    match: '```',
    mode: 'block',
    type: KEYS.codeBlock,
    format: (editor) => {
      insertEmptyCodeBlock(editor, {
        defaultType: KEYS.p,
        insertNodesOptions: { select: true },
      });
    },
  },
  // {
  //   match: '+ ',
  //   mode: 'block',
  //   preFormat: openNextToggles,
  //   type: KEYS.toggle,
  // },
  {
    match: ['---', '—-', '___ '],
    mode: 'block',
    type: KEYS.hr,
    format: (editor) => {
      editor.tf.setNodes({ type: KEYS.hr });
      editor.tf.insertNodes({
        children: [{ text: '' }],
        type: KEYS.p,
      });
    },
  },
];

const autoformatLists: AutoformatRule[] = [
  {
    match: ['* ', '- '],
    mode: 'block',
    type: 'list',
    format: (editor) => {
      toggleList(editor, {
        listStyleType: KEYS.ul,
      });
    },
  },
  {
    match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
    matchByRegex: true,
    mode: 'block',
    type: 'list',
    format: (editor, { matchString }) => {
      toggleList(editor, {
        listRestartPolite: Number(matchString) || 1,
        listStyleType: KEYS.ol,
      });
    },
  },
  {
    match: ['[] '],
    mode: 'block',
    type: 'list',
    format: (editor) => {
      toggleList(editor, {
        listStyleType: KEYS.listTodo,
      });
      editor.tf.setNodes({
        checked: false,
        listStyleType: KEYS.listTodo,
      });
    },
  },
  {
    match: ['[x] '],
    mode: 'block',
    type: 'list',
    format: (editor) => {
      toggleList(editor, {
        listStyleType: KEYS.listTodo,
      });
      editor.tf.setNodes({
        checked: true,
        listStyleType: KEYS.listTodo,
      });
    },
  },
];

export const AutoformatKit = [
  AutoformatPlugin.configure({
    options: {
      enableUndoOnDelete: true,
      rules: [
        ...autoformatBlocks,
        ...autoformatMarks,
        ...autoformatSmartQuotes,
        ...autoformatPunctuation,
        ...autoformatLegal,
        ...autoformatLegalHtml,
        ...autoformatArrow,
        ...autoformatMath,
        ...autoformatLists,
      ].map(
        (rule): AutoformatRule => ({
          ...rule,
          query: (editor) =>
            !editor.api.some({
              match: { type: editor.getType(KEYS.codeBlock) },
            }),
        })
      ),
    },
  }),
];

添加套件

import { createPlateEditor } from 'platejs/react';
import { AutoformatKit } from '@/components/editor/plugins/autoformat-kit';
 
const editor = createPlateEditor({
  plugins: [
    // ...其他插件,
    ...AutoformatKit,
  ],
});

手动配置

安装

pnpm add @platejs/autoformat

添加插件

import { AutoformatPlugin } from '@platejs/autoformat';
import { createPlateEditor } from 'platejs/react';
 
const editor = createPlateEditor({
  plugins: [
    // ...其他插件,
    AutoformatPlugin,
  ],
});

配置插件

自定义自动格式化规则:

import { AutoformatPlugin } from '@platejs/autoformat';
 
AutoformatPlugin.configure({
  options: {
    rules: [
      // 块级规则
      {
        match: '# ',
        mode: 'block',
        type: 'h1',
      },
      {
        match: '> ',
        mode: 'block',
        type: 'blockquote',
      },
      // 标记规则
      {
        match: '**',
        mode: 'mark',
        type: 'bold',
      },
      {
        match: '*',
        mode: 'mark',
        type: 'italic',
      },
    ],
    enableUndoOnDelete: true,
  },
});

高级配置

导入预定义的规则集实现全面自动格式化:

import { AutoformatPlugin } from '@platejs/autoformat';
import {
  autoformatArrow,
  autoformatLegal,
  autoformatMath,
  autoformatPunctuation,
  autoformatSmartQuotes,
} from '@platejs/autoformat';
 
AutoformatPlugin.configure({
  options: {
    enableUndoOnDelete: true,
    rules: [
      // 自定义块级规则
      {
        match: '# ',
        mode: 'block',
        type: 'h1',
      },
      // 预定义规则集
      ...autoformatSmartQuotes,
      ...autoformatPunctuation,
      ...autoformatArrow,
      ...autoformatLegal,
      ...autoformatMath,
    ].map((rule) => ({
      ...rule,
      // 在代码块中禁用自动格式化
      query: (editor) =>
        !editor.api.some({
          match: { type: 'code_block' },
        }),
    })),
  },
});
  • rules: 定义触发条件和格式化操作的规则数组
  • enableUndoOnDelete: 允许通过退格键撤销自动格式化
  • query: 根据上下文条件启用/禁用规则的函数

使用正则表达式

对于更复杂的匹配模式,可以使用正则表达式:

import { AutoformatPlugin } from '@platejs/autoformat';
import { toggleList } from '@platejs/list';
 
AutoformatPlugin.configure({
  options: {
    rules: [
      {
        match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
        matchByRegex: true,
        mode: 'block',
        type: 'list',
        format: (editor, { matchString }) => {
          const number = Number(matchString.match(/\d+/)?.[0]) || 1;
          toggleList(editor, {
            listRestartPolite: number,
            listStyleType: 'ol',
          });
        },
      },
    ],
  },
});
  • matchByRegex: 启用正则模式匹配(替代字符串完全匹配)
  • 注意:正则模式仅适用于mode: 'block'且只在块起始位置生效(triggerAtBlockStart: true)

插件

AutoformatPlugin

基于输入模式实现文本自动格式化的插件。

Options

Collapse all

    触发规则列表。可以是以下类型之一:AutoformatBlockRuleAutoformatMarkRuleAutoformatTextRule。均继承自AutoformatCommonRule

    • 默认值: []

    启用删除时撤销自动格式化功能。

    • 默认值: false

预定义规则集

可导入以下预定义规则集:

名称描述
autoformatSmartQuotes转换"文本""文本"
转换'文本''文本'
autoformatPunctuation转换--
转换...
转换>>»
转换<<«
autoformatArrow转换->
转换<-
转换=>
转换<=≤=
autoformatLegal转换(tm)(TM)
转换(r)(R)®
转换(c)(C)©
autoformatLegalHtml转换&trade;
转换&reg;®
转换&copy;©
转换&sect;§
autoformatComparison转换!>
转换!<
转换>=
转换<=
转换!>=
转换!<=
autoformatEquality转换!=
转换==
转换!==≠=
转换~=
转换!~=
autoformatFraction转换1/2½
转换1/3
...
转换7/8
autoformatDivision转换//÷
autoformatOperation转换+-±
转换%%
转换%%%‰%
包含autoformatDivision规则
autoformatSubscriptNumbers转换~0
转换~1
...
转换~9
autoformatSubscriptSymbols转换~+
转换~-
autoformatSuperscriptNumbers转换^0
转换^1¹
...
转换^9
autoformatSuperscriptSymbols转换^+
转换^-
autoformatMath包含autoformatComparison规则
autoformatEquality规则
autoformatOperation规则
autoformatFraction规则
autoformatSubscriptNumbers规则
autoformatSubscriptSymbols规则
autoformatSuperscriptNumbers规则
autoformatSuperscriptSymbols规则

类型定义

AutoformatCommonRule

自动格式化规则的通用接口结构,与模式无关。

Attributes

Collapse all

    当触发字符和光标前的文本匹配时应用规则。

    • 对于mode: 'block': 在光标前查找结束匹配项
    • 对于mode: 'text': 在光标前查找结束匹配项。如果format是数组,还需查找起始匹配项
    • 对于mode: 'mark': 查找起始和结束匹配项
    • 注意:'_*'['_*']{ start: '_*', end: '*_' }等效
    • MatchRange:

    触发自动格式化的字符

    为true时,在自动格式化后插入触发字符

    • 默认值: false

    允许自动格式化的查询函数

    OptionsAutoformatQueryOptions

    Collapse all

      insertText文本内容

AutoformatBlockRule

块级模式自动格式化规则接口。

Attributes

Collapse all

    块级模式:设置块类型或自定义格式

    自动格式化规则的匹配模式

    对于mode: 'block':设置块类型。如果定义了format,则忽略此字段

    是否仅在块起始位置触发

    • 默认值: true

    是否允许上方存在相同块类型时触发

    • 默认值: false

    format前调用的函数。用于重置选中块

    自定义格式化函数

AutoformatMarkRule

标记模式自动格式化规则接口。

Attributes

Collapse all

    标记模式:在匹配项之间插入标记

    要添加的标记(可多个)

    字符串可修剪时是否仍进行格式化

AutoformatTextRule

文本模式自动格式化规则接口。

Parameters

Collapse all

    文本模式:插入文本

    自动格式化规则的匹配模式

    文本替换内容或格式化函数