チュートリアル1 PythonMarkdownのExtensionを記述する

《 初回公開:2022/03/26 , 最終更新:未 》

原文は

旧バージョンの記事は

【 目次 】

イントロダクション

Python-Markdownは、多数の組み込み拡張機能を提供するだけでなく、アプリケーションプログラミングインターフェイス(API)を提供します。これにより、誰でも独自の拡張機能を記述して、既存の動作を変更したり、新しい動作を追加したりできます。
APIドキュメントは、開始時に(情報の多さに)少し圧倒される可能性があるため、次のチュートリアルでは、単純なインラインプロセッサ拡張機能を働かせてから、さらに機能を追加するプロセスを順を追って説明します。
APIのさまざまな部分を示すために、さまざまな手順がさまざまな方法で繰り返されます。

まず、実装する構文を確立する必要があります。
既存のMarkdown構文を再実装するのではなく、Markdownに典型的ではないいくつかの異なる構文を作成しましょう。
実際、txt2tagsマークアップ言語で使用されるインライン構文のサブセットを実装します。
構文は次のようになります。

  • strikeの2つのハイフン: --del-- => <del>del</del> => del
  • 下線のための2つのアンダースコア: __ins__ => <ins>ins</ins> => ins
  • boldのための2つのアスタリスク: **strong** => <strong>strong</strong> => strong
  • イタリックのための2つのスラッシュ: //emphasis// => <em>emphasis</em> => emphasis

ボイラープレートコード

愚鈍人の独り言

ボイラープレートコードとは

  • ボイラープレートコード - Wikipedia

    殆ど、または全く変化することなく、複数の場所で繰り返される定型コードのセクションのこと。冗長な言語を使用する場合、プログラマーはコードを少しだけ書くだけでも多くのコードを作成する必要がある。
    このような定型コードはボイラープレートと呼ばれる。

最初のステップは、Python-Markdown拡張機能で必要となるお決まりのコードを作成することです。
警告:このチュートリアルは非常に一般的であり、開発環境についての仮定はありません。
以下のコマンドの一部は、正しい権限を持つユーザーによって実行されない限り、一部の(すべてではない)システムでエラーを生成する可能性があります。
これらのタイプの問題を回避するには、プライマリシステムから分離された環境での開発にvirtualenvを使用することをお勧めします。 そうすることが確実に必要ではありませんが。
適切な開発環境のセットアップはすべてのPythonの開発に適用されるため(Markdown拡張機能を開発しても追加の要件はありません)、この事はこのチュートリアルの範囲を超えています。
Python開発の基本的な理解が必要になります。

まず、拡張子ファイルを保存する新しいディレクトリを作成します。 コマンドラインから次の手順を実行します。

mkdir myextension
cd myextension

作成した「myextension」ディレクトリ内のすべてのファイルを必ず保存してください。
拡張機能に「myextension」という名前を付けていることに注意してください。
別の名前を使用することもできますが、常に一貫して選択した名前を使用してください。
最初のPythonファイルを作成し、myextension.pyという名前を付けて、次のお決まりのコードを追加します。

from markdown.extensions import Extension

class MyExtension(Extension):
   def extendMarkdown(self, md):
       # Insert code here to change markdown's behavior.
       pass

そのファイルを保存した後、2番目のPythonファイルを作成し、setup.pyという名前を付けて、次のコードを追加します。

from setuptools import setup
setup(
    name='myextension',
    version='1.0',
    py_modules=['myextension'],
    install_requires = ['markdown>=3.0'],
)

最後に、コマンドラインから次のコマンドを実行して、Pythonに新しい拡張機能について通知します。

python setup.py develop

installサブコマンドではなくdevelopサブコマンドが実行されたことに注意してください。
プラグインはまだ完成していないため、この特別な開発モードでは、Pythonのsite-packagesディレクトリではなくソースファイルからプラグインを実行するためのパスを設定します。
そうすれば、ファイルに加えられた変更は、拡張機能を再インストールする必要なしにすぐに有効になります。

また、セットアップスクリプトは、setuptoolsがインストールされていることを前提としていることに注意してください。
setuptoolsは必要ありませんが(代わりにdistutils.core import setupから実行するだけです)、setuptoolsを使用する場合にのみdevelopサブコマンドを取得します。
pipvirtualenv(両方を推奨)がインストールされているシステムには、setuptoolsもインストールされています。

すべてが正しく機能していることを確認するには、拡張機能をMarkdownに渡してみてください。
Pythonインタープリターを開き、次のことを試してください。

>>> import markdown
>>> from myextension import MyExtension
>>> markdown.markdown('foo bar', extensions=[MyExtension()])
'<p>foo bar</p>'

明らかに、拡張機能は何の役にも立ちませんが、エラーなしで配置されたので、実際に新しい構文の実装を開始できます。

一般的なパターンの使用

まず、Markdownの標準構文と重複しない構文の一部を実装しましょう。; --del--構文。テキストを<del>タグでラップします。
最初のステップは、del構文に一致する正規表現を作成することです。

DEL_RE = r'(--)(.*?)--'

ハイフンの最初のセット((--))は括弧内にグループ化されており、コンテンツが2番目のグループになっていることに注意してください。
これは、Python-Markdownが提供する汎用パターンクラスを使用するためです。
具体的には、SimpleTextPatternは、パターンを変更して別のグループを付加し、テキストコンテンツが新しい正規表現のgroup(3)にあることを期待します。

また、コンテンツは欲張りでない一致 (.*?)を使用して一致することに注意してください。
そうしないと、最初の出現から最後の出現までのすべてが1つの<del>タグ内に配置されますが、これは望ましくありません。
それでは、正規表現をMarkdownに組み込みましょう。

from markdown.extensions import Extension
from markdown.inlinepatterns import SimpleTagPattern

DEL_RE = r'(--)(.*?)--'

class MyExtension(Extension):
    def extendMarkdown(self, md):
        # Create the del pattern
        del_tag = SimpleTagPattern(DEL_RE, 'del')
        # Insert del pattern into markdown parser
        md.inlinePatterns.add('del', del_tag, '>not_strong')

お気づきの方もいらっしゃると思いますが、2行追加しました。
最初の行は、markdown.inlinePatterns.SimpleTagPatternのインスタンスを作成します。
この汎用のパターンクラスは2つの引数を取ります。 照合する正規表現(この場合はDEL_RE)、およびgroup(3)のテキストを( 'del')に挿入するタグの名前。

2行目は、新しいパターンをMarkdownパーサーに追加します。
明らかでない場合は、任意のmarkdown.ExtensionクラスのextendMarkdownメソッドに、変更するMarkdownクラスのインスタンスである「md」が渡されます。
この場合、「not_strong」という名前のパターンの後にパターンインスタンスdel_tagを使用して、'del'という名前の新しいインラインパターンを挿入しています(それゆえ'>not_strong'です)。

今回は、非推奨ですが、addメソッドを使用しました。
将来のバージョンでは、最初に実際の優先順位番号を決定する必要があります。inline_patterns.pyのbuild_inlinepatterns()を調べ、それから「not_strong」の少し前として75を選択、md.inlinePatterns.register(del_tag, 'del', 75)を使います。

それでは、新しい拡張機能をテストしてみましょう。 Pythonインタープリターを開き、次のことを試してください。

>>> import markdown
>>> from myextension import MyExtension
>>> markdown.markdown('foo --deleted-- bar', extensions=[MyExtension()])
'<p>foo <del>deleted</del> bar</p>'

'myextension'モジュールからMyExtensionクラスをインポートしたことに注意してください。
次に、そのクラスのインスタンスをmarkdown.markdownextensionsキーワードに渡しました。
返されたHTMLも確認できます。これは、ブラウザに次のように表示されます。

foo deleted bar

<ins>タグを使用する__ins__の構文を追加しましょう。

DEL_RE = r'(--)(.*?)--'
INS_RE = r'(__)(.*?)__'

class MyExtension(Extension):
    def extendMarkdown(self, md):
        del_tag = SimpleTagPattern(DEL_RE, 'del')
        md.inlinePatterns.add('del', del_tag, '>not_strong')
        ins_tag = SimpleTagPattern(INS_RE, 'ins')
        md.inlinePatterns.add('ins', ins_tag, '>del')

それは自明のはずです。
'ins'構文に一致する新しいパターンを作成し、 'del'パターンの後に追加しただけです。
2つの下線で囲まれたテキストに対して2つの可能な結果が定義されている事を除けば、'ins'構文を使用しておこなう事ができます。
Markdownの既存の太字構文(__bold__)はまだパーサーで定義されていることを思い出してください。
ただし、新しい挿入構文が太字パターンの前にinlinePatternsに挿入されたため、太字パターンがそれを見つける前に、挿入パターンが最初に実行され、二重アンダースコアマークアップが取り込まれてしまいます。
それでも、既存の太字のパターンは依然としてテキストに対して実行されており、パーサーの速度が不必要に遅くなっています。
したがって、不要になったパーツはすべて取り外すことをお勧めします。
ただし、独自の新しい太字構文を定義するため、実際には古いパターンをオーバーライドするか、新しいパターンに置き換えることができます。
同じことが私たちのemphasisパターンにも当てはまります。
まず、新しい正規表現を定義する必要があります。
前回と同じ式を少し変更して使用できます。

STRONG_RE = r'(\*\*)(.*?)\*\*'
EMPH_RE = r'(\/\/)(.*?)\/\/'

次に、これらをmarkdownパーサーに挿入する必要があります。
ただし、挿入および削除とは異なり、既存のインラインパターンをオーバーライドする必要があります。
Markdownの強力で強調された構文は、現在2つのインラインパターンで実装されています。; 'em_strong'(アスタリスクによる)および 'em_strong2'(アンダースコアによる)。

最初に「em_strong」をオーバーライドしてみましょう。

class MyExtension(Extension):
    def extendMarkdown(self, md):
        ...
        # Create new strong pattern
        strong_tag = SimpleTagPattern(STRONG_RE, 'strong')
        # Override existing strong pattern
        md.inlinePatterns['em_strong'] = strong_tag

既存のパターンの前後に新しいパターンを「追加」するのではなく、'em_strong'という名前のパターンの値を単純に再割り当てしたことに注意してください。
これは、'strong'という名前の古いパターンがすでに存在しており、パーサー内でその位置を変更する必要がないためです。
したがって、新しいパターンインスタンスを割り当てるだけです。
add()と同様に、このメソッドは非推奨であるため、後でmd.inlinePatterns.register(strong_tag, 'em_strong', 60)が必要になる場合があります。

'emphasis'も割り当てることで設定できます。
デフォルトの優先度は非常に低くなります。

class MyExtension(Extension):
    def extendMarkdown(self, md):
        ...
        emph_tag = SimpleTagPattern(EMPH_RE, 'em')
        md.inlinePatterns['emphasis'] = emph_tag

これで、古いパターン'em_strong2'が1つ残っています。
'em_strong2'パターンは、under_scored_wordsが強調されないという特殊なケースを含め、アンダースコアを処理しましたが、新しい構文では二重アンダースコアが必要なため、これ以上必要ありません。
したがって、削除することができます。
Markdown構文では、strongとemphasis の両方が同じ文字を使用しているため、ネストされた2つを一致させるために特別なケースが必要でした(つまり、 ___like this___または___like_this__)。
繰り返しますが、これは新しい構文には必要ありません。
登録を解除することで削除できます。

class MyExtension(markdown.Extension):
    def extendMarkdown(self, md):
        ...
        md.inlinePatterns.deregister('em_strong2')

これにより、新しい構文がすべて実装されます。
完全を期すために、拡張機能全体は次のようになります。

from markdown.extensions import Extension
from markdown.inlinepatterns import SimpleTagPattern

DEL_RE = r'(--)(.*?)--'
INS_RE = r'(__)(.*?)__'
STRONG_RE = r'(\*\*)(.*?)\*\*'
EMPH_RE = r'(\/\/)(.*?)\/\/'

class MyExtension(Extension):
    def extendMarkdown(self, md):
        del_tag = SimpleTagPattern(DEL_RE, 'del')
        md.inlinePatterns.add('del', del_tag, '>not_strong')
        ins_tag = SimpleTagPattern(INS_RE, 'ins')
        md.inlinePatterns.add('ins', ins_tag, '>del')
        strong_tag = SimpleTagPattern(STRONG_RE, 'strong')
        md.inlinePatterns['em_strong'] = strong_tag
        emph_tag = SimpleTagPattern(EMPH_RE, 'em')
        md.inlinePatterns['emphasis'] = emph_tag
        md.inlinePatterns.deregister('em_strong2')

また、正しく機能していることを確認するには、Pythonインタープリターから次のコマンドを実行します。

>>> import markdown
>>> from myextension import MyExtension
>>> txt = """
... Some __underline__
... Some --strike--
... Some **bold**
... Some //italics//
... """
... 
>>> markdown.markdown(txt, extensions=[MyExtension()])
"<p>Some <ins>underline</ins>\nSome <del>strike</del>\nSome <strong>bold</strong>\nSome <em>italics</em>"

独自のパターンクラスを作成する

ただし、そのコードには多くの繰り返しがあることに気付くかもしれません。
実際、4つの新しい正規表現はすべて、1つの正規表現に簡単にまとめることができます。
また、実行するパターンが1つしかない場合は、4つよりもパフォーマンスが高くなります。
4つの正規表現を1つの新しい式にリファクタリングしてみましょう。

MULTI_RE = r'([*/_-]{2})(.*?)\2'

正規表現は最初に1つのグループをキャプチャするように変更されるため、これは「グループ2として2つの一致する句読点を取得し、グループ3としてタグ付けされたテキストを取得し、次に句読点の別のコピーを取得する」と読み取ることができます。
その正規表現を使用できる汎用パターンクラスは存在しないため、独自の正規表現を定義する必要があります。
すべてのパターンクラスは、markdown.inlinepatterns.Pattern基本クラスから継承する必要があります。
少なくとも、サブクラスは、正規表現のMatchObjectを受け入れ、ElementTreeElementを返すhandleMatchメソッドを定義する必要があります。

from markdown.inlinepatterns import Pattern
from markdown.extensions import Extension
import xml.etree.ElementTree as etree

class MultiPattern(Pattern):
    def handleMatch(self, m):
        if m.group(2) == '**':
            # Bold
            tag = 'strong'
        elif m.group(2) == '//':
            # Italics
            tag = 'em'
        elif m.group(2) == '__':
            # Underline
            tag = 'ins'
        else:   # must be m.group(2) == '--':
            # Strike
            tag = 'del'
        # Create the Element
        el = etree.Element(tag)
        el.text = m.group(3)
        return el

次に、Markdownに新しいパターンについて通知し、不要になった既存のパターンを削除する必要があります。

class MultiExtension(Extension):
    def extendMarkdown(self, md):
        # Delete the old patterns
        md.inlinePatterns.deregister('em_strong')
        md.inlinePatterns.deregister('em_strong2')
        md.inlinePatterns.deregister('not_strong')

        # Add our new MultiPattern
        multi = MultiPattern(MULTI_RE)
        md.inlinePatterns['multi'] = multi

完全を期すために、新しく追加されたコードは次のようになります。

from markdown.inlinepatterns import Pattern
from markdown.extensions import Extension
import xml.etree.ElementTree as etree

MULTI_RE = r'([*/_-]{2})(.*?)\2'

class MultiPattern(Pattern):
    def handleMatch(self, m):
        if m.group(2) == '**':
            # Bold
            tag = 'strong'
        elif m.group(2) == '//':
            # Italics
            tag = 'em'
        elif m.group(2) == '__':
            # Underline
            tag = 'ins'        
        else:   # must be m.group(2) == '--':
            # Strike
            tag = 'del'
        # Create the Element
        el = etree.Element(tag)
        el.text = m.group(3)
        return el

class MultiExtension(Extension):
    def extendMarkdown(self, md):
        # Delete the old patterns
        md.inlinePatterns.deregister('em_strong')
        md.inlinePatterns.deregister('em_strong2')
        md.inlinePatterns.deregister('not_strong')

        # Add our new MultiPattern
        multi = MultiPattern(MULTI_RE)
        md.inlinePatterns['multi'] = multi

そのコードをmyextension.pyファイルに追加した後、Pythonインタープリターを開きます。

>>> import markdown
>>> from myextension import MultiExtension
>>> txt = """
... Some __underline__
... Some --strike--
... Some **bold**
... Some //italics//
... """
... 
>>> markdown.markdown(txt, extensions=[MultiExtension()])
"<p>Some <ins>underline</ins>\nSome <del>strike</del>\nSome <strong>bold</strong>\nSome <em>italics</em>"

構成オプションの追加

ここで、拡張機能にいくつかの構成オプションを提供するとします。
おそらく、ユーザーがオンとオフを切り替えることができるオプションとして、挿入と削除の構文のみを提供したいと考えています。

まず、正規表現を2つに分割しましょう。

STRONG_EM_RE = r'([*/]{2})(.*?)\2'
INS_DEL_RE = r'([_-]{2})(.*?)\2'

次に、新しく名前を変更したExtensionサブクラスで構成オプションを定義する必要があります。

class ConfigExtension(Extension):
    def __init__(self, **kwargs):
        # Define config options and defaults
        self.config = {
            'ins_del': [False, 'Enable Insert and Delete syntax.']
        }
        # Call the parent class's __init__ method to configure options
        super().__init__(**kwargs)

構成オプションをdict、self.configとして定義し、キーはオプションの名前です。
各値は2項目のリストであり、オプションのデフォルト値とその説明です。
Extensionクラスではconfigを変更可能にする必要があるため、タプルの代わりにリストを使用します。

最後に、extendMarkdownメソッドをリファクタリングして、構成オプションを考慮します。

    def extendMarkdown(self, md):
        ...
        # Add STRONG_EM pattern
        strong_em = MultiPattern(STRONG_EM_RE)
        md.inlinePatterns['strong_em'] = strong_em
        # Add INS_DEL pattern if active
        if self.getConfig('ins_del'):
            ins_del = MultiPattern(INS_DEL_RE)
            md.inlinePatterns['ins_del'] = ins_del

ただ単にstrongとemphasisのためMultiPatternクラスのインスタンスを1つ作成して、さらにins_del構成オプションがTrueなら、MultiPatternクラスの2番目のインスタンスを作成します。

完全を期すために、新しく追加されたすべてのコードは次のようになります。

STRONG_EM_RE = r'([*/]{2})(.*?)\2'
INS_DEL_RE = r'([_-]{2})(.*?)\2'

class ConfigExtension(Extension):
    def __init__(self, **kwargs):
        # Define config options and defaults
        self.config = {
            'ins_del': [False, 'Enable Insert and Delete syntax.']
        }
        # Call the parent class's __init__ method to configure options
        super().__init__(**kwargs)

    def extendMarkdown(self, md):
        # Delete the old patterns
        md.inlinePatterns.deregister('em_strong')
        md.inlinePatterns.deregister('em_strong2')
        md.inlinePatterns.deregister('not_strong')

        # Add STRONG_EM pattern
        strong_em = MultiPattern(STRONG_EM_RE)
        md.inlinePatterns['strong_em'] = strong_em
        # Add INS_DEL pattern if active
        if self.getConfig('ins_del'):
            ins_del = MultiPattern(INS_DEL_RE)
            md.inlinePatterns['ins_del'] = ins_del

変更を保存した後、Pythonインタープリターを開きます。

>>> import markdown
>>> from myextension import ConfigExtension
>>> txt = """
... Some __underline__
... Some --strike--
... Some **bold**
... Some //italics//
... """
... 
>>> # First try it with ins_del set to True
>>> markdown.markdown(txt, extensions=[ConfigExtension(ins_del=True)])
"<p>Some <ins>underline</ins>\nSome <del>strike</del>\nSome <strong>bold</strong>\nSome <em>italics</em>"
>>> # Now try it with ins_del defaulting to False
>>> markdown.markdown(txt, extensions=[ConfigExtension()])
"<p>Some __underline__\nSome --strike--\nSome <strong>bold</strong>\nSome <em>italics</em>"

(文字列としての)Extension名のサポート

拡張機能をテストするたびに、extensionをインポートして、Extensionサブクラスのインスタンスを渡す必要があることに気付いたかもしれません。
これは拡張機能を呼び出すための推奨される方法ですが、ユーザーがコマンドラインまたはテンプレートシステムからMarkdownを呼び出す必要があり、文字列を渡すことしかできない場合があります。

この機能は既に組み込まれて自由に使う事ができます。
ただし、ユーザーは、定義したExtensionクラスのインポートパス(Pythonドット表記)を知って使用する必要があります。
たとえば、上記で定義した3つのクラスは、それぞれ次のように呼び出されます。

>>> markdown.markdown(txt, extensions=['myextension:MyExtension'])
>>> markdown.markdown(txt, extensions=['myextension:MultiExtension'])
>>> markdown.markdown(txt, extensions=['myextension:ConfigExtension'])

パスとクラスの間にはコロン (:)を使用する必要があることに注意してください。 一方、パスの残りの部分にはドット (.)を使用する必要があります。 これは、import部分の"from" importステートメントをコロンに置き換えるものと考えてください。
たとえば、ファイルsomepackage/extensions/foo.pyで定義されたextensionクラスFooExtensionがあるなら、importステートメントはfrom somepackage.extensions.foo import FooExtensionとなり、文字列ベースの名前は'somepackage.extensions.foo:FooExtension'になるでしょう。

実際、前のステップをリファクタリングするのではなく、上記の各ステップで新しいクラスを作成した場合、3つの拡張機能すべてが同じモジュール内に存在し、すべて別々に呼び出される可能性があります。
これは、内部でのみ使用される大規模なプロジェクト(CMS、静的ブログジェネレーターなど)の一部として多数の拡張機能を構築した場合に最適です。

ただし、他の人がプロジェクトに組み込むためのスタンドアロンモジュールとして拡張機能を配布する場合は、より短い名前のサポートを有効にすることをお勧めします。
間違いなく、'myextension''myextension:MyExtension'よりもユーザーが入力(および文書化)するのが簡単です。
また、Python-Markdownに付属するすべての組み込み拡張機能がこのように機能するため、ユーザーは同じことを期待する可能性があります。
この機能を有効にするには、拡張機能の下部に以下を追加します。

def makeExtension(**kwargs):
    return ConfigExtension(**kwargs)

このモジュールレベルの関数は、Extensionサブクラスのインスタンスを返すだけであることに注意してください。
Markdownに文字列が提供されている場合、その文字列は、モジュールのimport可能なパスを指すPythonのドット表記を使用する必要があります。
次に、文字列にコロンが見つからない場合は、そのモジュールにあるmakeExtension関数を呼び出します。

Pythonインタープリターをもう一度開いて、拡張機能をテストしてみましょう。

>>> import markdown
>>> txt = """
... Some __underline__
... Some --strike--
... Some **bold**
... Some //italics//
... """
... 
>>> markdown.markdown(txt, extensions=['myextension'])
"<p>Some __underline__\nSome --strike--\nSome <strong>bold</strong>\nSome <em>italics</em>"

上記のConfigExtensionを使用したので、いくつかの構成オプションを拡張機能に渡します。

>>> markdown.markdown(
...     txt, 
...     extensions=['myextension'],
...     extension_configs = {
...         'myextension': {'ins_del': True}
...     }
... )
"<p>Some <ins>underline</ins>\nSome <del>strike</del>\nSome <strong>bold</strong>\nSome <em>italics</em>"

追加の作業なしでextension_configsキーワードがサポートされていることに注意してください。
extension_configsキーワードの完全な説明については、ドキュメントを参照してください。

配布の準備

setup.pyスクリプトがすでに作成されているため、配布用の拡張機能を準備する上で最も重要な部分が完了しています。
ただし、セットアップスクリプトはかなり基本的なものでした。
もう少しメタデータ、特に開発者の名前、電子メールアドレス、プロジェクトのURLを含めることをお勧めします(例については、Pythonドキュメントのセットアップスクリプトの記述のセクションを参照してください)。
また、少なくともREADMEファイルとLICENSEファイルをディレクトリに含めることをお勧めします。

この時点で、コードをバージョン管理システム(Git、Mercurial、Subversion、Bazaarなど)にコミットし、選択したシステムをサポートするホストにアップロードできます。
その後、ユーザーはpipコマンドを使用して、拡張機能を簡単にダウンロードしてインストールできます。
または、さらに簡単なコマンドとして、プロジェクトをPython PackageIndexにアップロードすることもできます。
または、setup.pyスクリプトで使用可能ないくつかのサブコマンド(sdistなど)を使用して、ユーザーがダウンロードできるようにするファイル(zipファイルやtarファイルなど)を作成することもできます。
詳細はこのチュートリアルの範囲を超えていますが、Pythonモジュールの配布に関するPythonのドキュメントと、パッケージの構築と配布に関するSetuptoolsのドキュメントの両方で、使用可能なオプションの説明が提供されています。

結論

このチュートリアルではインラインプロセッサの使用のみを示しましたが、拡張APIには、プリプロセッサブロックプロセッサツリープロセッサ、およびポストプロセッサのサポートも含まれています。
各タイプのプロセッサは異なる目的を果たしますが(解析プロセスの異なる段階で実行されます)、同じ基本原則が各タイプのプロセッサに適用されます。
実際、1つの拡張機能で、複数の異なるタイプのプロセッサを変更できます。

APIドキュメントとさまざまな組み込み拡張機能のソースコードを確認すると、独自の優れた拡張機能を構築するための十分な情報が得られるはずです。
もちろん、サポートが必要な場合は、メーリングリストでお気軽にサポートを依頼してください。
そして、他の人がそれらを見つけることができるように、ウィキにあなたの拡張機能をリストすることを忘れないでください。

ページのトップへ戻る