Azukiの内部構造がグチャグチャ感があるので、整理したい。しかしAzukiの開発はクラスの数を増やさないというアプローチで進めていたため今になってモジュールがこんがらがっており、おいそれと試行錯誤できない状態になってしまった。そこで、細かくクラス分けしてあるAvalonEditのソースを読むことで、より良い構造を勉強することにした。特に興味があるのは選択状態の保持方法、および折り返し表示モードにおける折り返し位置の管理方法あたり。WPFは素人同然なので苦労したけれど、選択範囲の保持・管理の方法については理解できたのでメモがてらに記録しておく。
以下、理解できた範囲でオレオレ擬似コードで「どのようなモジュールが、どのような情報・機能を管理しているか」を表現したものを記す。
AvalonEdit
TextEditor {
TextArea
}
TextArea {
TextView (実質、編集機能の無いテキストビューア)
Selection
Document
Options (編集動作に関するユーザ向け設定)
Graphical style (フォント、配色など)
Indentation strategy (自動インデント)
Overstrike mode (上書きモードのこと?)
}
TextView {
Graphics rendering
Highlighter management (依存性注入のターゲットになっているだけかな?)
}
HighlightingColorizer {
Lastly highlighted position (実際にはDocumentLineオブジェクト、つまり行単位で記録)
}
FoldingManager {
}
Document {
List of characters
Undo stack
}
何点か気付いた点を記すと:
- 実質のコアであるTextAreaと、テキストデータであるDocumentで構成されいてる印象
- Documentは純粋なテキストデータの集合に更新差分を管理するUndoStackを持つ程度の機能性しか無い
- 「UndoStackを持つ」と言っても、UndoはDocumentの機能ではなくUndoStackの機能になっている
- Undo実行後、選択状態は以下の経路で復元される [*1]
- UndoStack.Undo()
- → Document.Replace()
- → Changed event
- → (WeakEventManager経由で) TextArea.OnDocumentChanged()
- → Selection.UpdateOnDocumentChange()
- TextViewは描画処理に徹している
- TextAreaが、DocumentとTextViewを持っており、一対一対応している
- TextArea.Documentを差し替えると選択状態をクリアするなど、一つのViewで複数のDocumentを扱わない設計
- 編集動作に関する機能はTextAreaが管理している
- SyntaxHighlighterとFoldingManagerはServiceContainerクラスを使用して依存性注入する作り
- ユーザ独自に新しい文法をサポートする場合、それぞれの実装を注入できる
[*1] これはAzukiの設計で一番後悔している「Documentが選択範囲情報を管理する」構造の改善に向けて参考になる
Azuki v1
UserInterface {
View (グラフィック描画とスクリーン行の管理)
Document
AutoIndentHook (自動インデント)
}
View {
Graphical style (フォント、配色など)
Scroll position
}
Document {
List of characters
Style bits for each character (syntax highlightingの結果)
Edit history (Undo stack)
Selection
Syntax highlighter
Selection mode (normal, line, or rectangular)
Overwrite mode
Word processing (単語単位でのキャレット移動や折り返し表示で使う単語区切り検出処理)
}
Azukiは、ユーザによる操作を司るUserInteface、表示を司るView、データを司るDocumentで構成している。が、テキストエディタはこの3要素以外にも様々な要素があるので、それらをどうプログラム的に配置するのかをもう少しマジメに考え直した方が良さそうだ。とりあえず、Documentが持っているSyntax highlighterはViewに、SelectionとSelection modeとOverwrite modeはUserInterfaceに移管すべきなんだろうなとは思っている。
雑感
とりあえず選択範囲という情報をDocumentから切り離すにあたり、AvalonEditの設計は参考になった。現在のAzukiは選択という概念を抽象化していないため(範囲両端のindex値を直接Document等のロジックが扱う)、まずは選択の抽象化を行い、その後で表示系のViewに移動すべきなんだろうな。ただし、単純にViewへ選択情報を移管するだけではUndoやDocument差し替え時に選択範囲が元に戻らなくなるので、そこは何らかのケアが必要そう。