Smalltalk ついでに VisualWorks のテキストエディタについても調べてみた。調べる前にポイントを整理。
- 文字列を挿入する時のイベントの流れ
- 選択の表現
- 編集対象テキストの表現
- テキスト表示
- テキスト表示の最適化
まず大まかな構成から。 Workspace open でワークスペースを開き、Debug の Process Monitor でワークスペースのウインドウを Debug する。そこから
- WindowsManager
- windows: ApplicationWindow
- model: Workspace
- textPage: WorkspacePage
- textModel: PluggableAdaptor
- dependents: TextEditorView (近道をしてしまったが、本当は ApplicationWindow から深く辿っていっても TextEditorView にたどり着ける)
- displayContents: ComposedText
- text: Text
のように主要なオブジェクトを辿って行ける(ここでの疑問点。なぜ PluggableAdaptor の dependents にはコレクションでは無く TextEditorView が一つだけ入っているのか?)。ここでの主要なオブジェクトは
- WorkspacePage: textModel にファイルに保存されたワークスペースの内容を保持。
- ComposedText: text に編集中の内容を保持。レイアウト情報を保持
- TextEditorView: 選択位置を保持
MVC 的には WorkspacePage がモデルだが、ワークスペースの機能としては ComposedText が重要だ。また、ComposedText と TextEditorView は Squeak の MultiNewParagraph を分担して受け持っている。という事は、結局 VisualWorks もテキストは双方リンクでは無く固定長文字列で持っている。
ただ、編集のたびに毎回文字列を作り直す Squeak と違って、VisualWorks の String >> changeFrom:to:with: は内部的に 1024 文字以上の場合 GapString という特別な String を使う。これは文字の挿入や削除の際にメモリを確保し直すのではなく、出来るだけ同じメモリを使い能率を上げるという仕組みになっている。
文字列を挿入する時のイベントの流れ
WindowManager >> processNextEvent に
(event isKindOf: KeyboardEvent) ifTrue: [self halt].
のようなやつを差し込んでコードの流れを追った。イベントの流れは大変難しいが、TextEditorController >> processKeyboardEvent: からがエディタの処理。dispachTable を元にキー操作に応じた処理を選ぶが、普通の文字の場合は #normalCharacterKey: が選ばれる。
選択の表現
Squeak と同じく TextEditorView 中で CharacterBlock が保持している。CharacterBlock は Rectangle のサブクラスで分表示文字列を座標などを含めて持つオブジェクト。
編集対象テキストの表現
ComposedText 内の text が保持する。テキストは長さに応じて String (変更不能バッファ) か GapString (変更可能バッファ)が持つ。
テキスト表示
意外な事に、イベント処理 (TextEditorController>>replaceFrom:to:with:)
の中で表示も行う。
テキスト表示の最適化
エディタに関係するクラスを抽象 -> 具象順に並べると
- Text : 編集中データを持つ
- ComposedText : テキストの座標を持つ
- TextEditorView : 選択位置を持つ
最適化に関して重要そうな ComposedText の機能を調べてみた。インスタンス変数
- textStyle : ?
- text : 表示対象テキスト
- compositionWidth : 表示幅
- compositionHeight : 表示高さ
- wordWrap : テキストを幅に合わせて改行するか
- fontPolicy : ?
- lineTable : ? OptimizedLineInformationTable
- fitWidth : 幅に合わせてテキストを表示するか
さらに lineTable : OptimizedLineInformationTable のインスタンス変数
- lines : 物理行の始まる位置
- lastLine : 最後の行
- adjustmentStartLine : 微調整の始まる行(先頭位置が2?)
- adjustment : 微調整数
lineTable は文字列中で物理行のインデックス位置のリストを保持していて、どの文字が何行目なのかを知るのに役に立つ。文字列の実体である Text と OptimizedLineInformationTable を駆使して、行単位リストと同じような効率を得るためにあるみたい。