generated at
normal modeをJavascript Objectで表現してみる
ScrapVimで扱うために、Mormal ModeをJavascriptのObjectで表現してみる

利点
vim commandsをparseしたobjectとして扱える
command実行に必要な情報を全て詰め込める

とりあえずVim normal command一覧にあるやつを全部表現してみる。
文字列を解析すると同時に、実行可能なcommandを構築していく感じ
出来たObject model
ts
interface VimCommand { command: (params: object) => void: params?: { register?: Register; count?: number; char?: string; range?: { command: (props: object) => Selections; params?: { count?: number; } } }; }
Selections は選択範囲を表すobject
scrapbox-selectionの返り値
params command に渡すparameterを全部詰め込む
どんなコマンドでも command(params) で実行できるようになる
Register string の部分型
使用可能なレジスタの名前を羅列しただけ
range はoperatorの処理範囲を決めるやつ
command には VimCommand.command の一部を流用できる
motionとか
{visual: true}を渡すと、scrapbox上に選択範囲を出して、その情報を返却する
選択範囲を常に返すようにしておく
選択はしない
Visual modeで使えるようにするために選択範囲を出すつもりだったのだ
しかしよく思い出すと、Visual modeの選択範囲の挙動は普通のテキストエディタとは違う。
選択範囲の情報だけ渡してもらって、Visual modeで挙動を調節するとかしたほうが良さそうだ
Visual ModeはVisual Modeで別途commandを用意した方が良い気がしてきた
選択範囲の縮小/拡張が結構独特
選択範囲の状況に応じて考えないといけないことが結構ある
text objectも定義しておく
aw / iw など
cursor移動はせず、単に選択範囲を返す関数として定義する
コマンドの解析状態を表す変数も用意したほうがいい
ts
interface ParseState { state: 'operator' | 'count' | 'register' | ... : }
解析する時、投げられた文字がどのcommandに当たるのかを調べる必要があるのか
例えば何も入力していない状態で " が入ってきたら、 register modeに移行する
以後、 {'a','b',..,'unnamed','*'} に前方一致するcommandかどうかを判定する
matchしなかったら不正なcommandとしてその場で破棄 ( <Esc> 相当)
matchした候補が複数あったらそのまま待機
候補が一つに絞られたら VimCommand.params.register に格納し、状態を operator に移動する
一つ一つのstateに必要なのはこれらかな
コマンドが不正かどうか判定する条件式
不正の場合は <ESC> を発動し EntryState に戻る
コマンドが確定したかどうか判定する条件式
コマンド確定後に移動するstate
未処理のコマンド文字列
次のstateに渡す
これはState管理クラスが持っていても良いかも
presentState.parse((newState, restSequence)=>this.changeState(newState,restSequence),newChar);

必要な ParseState の種類
EntryState
Normal Modeで何も入力されていない状態のときのstate
CountState , OperatorState , MotionState など、取りうる状態を全てScrapVim-lite-3/Modeで配列として持たせれば解決する
数字が入力されたら CountState に移行する
入力された数字を渡す必要があるのかtakker
未処理の文字として渡せばいいか。
どのstateに移行するか確定できない文字の場合は待機
ユーザー定義コマンドもここで解析する
設定に応じてどの ParseState のcommand listに登録されるかは変化する
OperatorState
operatorの入力を受け付ける
operatorをuserが作れるようにすると面白いかな
確定後 MotionOrCountState に移行する
MotionOrCountState
OperatorState から CountState / MotionState へ移行するつなぎ
数字が入力されたら CountState に、Motionに前方一致する文字列が入力されたら MotionState に移行する
CountState
繰り返し回数の入力を受け付ける
OperatorState から移行した場合は、 VimCommand.range.params.count に格納する
移行元の情報を持たせるべきか。
constructor から文字列で注入するとか
このstateは不正な値かどうかの検証をしない
数字以外が投げたれたら、 EntryState に移行する
RegisterState
Register入力を受け付ける
確定後 OperatorState に移行する
MotionState
Motion入力を受け付ける
確定したらcommandを実行する
OperatorState 経由の場合は、 VimCommand.range.command に格納する
どのstateに移るのか決めるためのstateが必要なんだな
MotionOrCountState
CharState
文字入力を一つだけ受け付ける
確定したらcommandを実行する
試行錯誤
1文字cursor移動/単語移動/スクロール/インデント/undo/redo
{command: moveLeft, params: {count: 1}}
それぞれ個別に関数を作っておく
実行するときは moveLeft(params) とする
行内移動
{command: jumpLF}
実行方法: jumpLF({})
count は無視する
f / t
{command: motion_f, params: {count: 2, char: 'a'}}
jump先の文字を指定する必要がある
行移動
gg / G / H / M / L count 無視
それ以外は count 付きで
Insert Mode突入
iaIAoO
どれも {command: enterInsertModeBefore} などとしておく
mode変更はどうやって通知しようか?
更新関数を渡す
CustomEventを発火する
置換
~ は…… count いらないか
r : {command: replace, param: {char: 'あ'}}
切り取り/yank/paste
{command: delete, params: {register: 'unnamed', count: 2, range: {command: moveRight, params: {count: 1}}}}
x dl とみなして一つのコマンドで賄う
{command: yank, params: {register: 'unnamed', count: 2, range: {command: moveRight, params: {count: 1}}}}
macro
{command: record, params: {register: 'a', char: 'ggVGd'}}
char に複数文字を入れられるようにしておく

#2020-12-17 06:25:37
#2020-12-13 07:32:12
#2020-12-12 05:05:47