【授業メモ】閲覧専用のエディタに挿入機能をつける

情報工学実験という科目で、kilovという意図的に機能が削られたエディタに機能を追加する演習をやっている。
kilovはkiloというC言語で書かれたエディタをいじったものなので、完全なソースコードGitHubで見ることもできる。
このブログに載せているコードが正しいかどうかはわからない。

github.com

やる

kilovは、editorConfigという構造体にerowという行を表す構造体の配列を持たせているため、こいつを編集して画面を更新してやるという流れになる。
この演習でメインループをいじる必要は無い。

ざっとコードを眺めると、キー入力を受け取って各種処理につなげているeditorProcessKeypressという関数が見つかる。
こいつの中にあるswitch文が、特殊な制御の入力以外はdefault節からそのままの値を返していることがわかる。
このままでは何も起こらずメインループが次に進むので、このdefault節から文字を挿入する関数を呼んであげればいい。
命名は既存の関数に倣って、editorInsertCharacterとかにしておく。

まずは現在のカーソル位置から行のデータを取る。

erow row = E.row[E.rowoff + E.cy];

rowoffは行のオフセット、cyはカーソルが何行にあるかを表している。
最初はrowoffを足し忘れて、普通に動いているように見えるがカーソルが下に行って画面が動くと挙動がおかしくなる不具合が出た。そいういうことに気付けるので、デバッグ時は大きめのファイルを開いてみることをおすすめする。僕はいつもkilov.cを開いてる。

行が取れたので、こいつが持っているcharsをいじっていく。
方針としては、charsreallocで拡張し、必要に応じて文字列を配列の後方に1文字ぶん移動させ、入力文字を格納してやる。
文字列の移動は、挿入箇所が行の末尾である場合のみ不要になる。

適当に書くとこんな感じ。

int filecol = E.coloff + E.cx;
int filerow = E.rowoff + E.cy;

row.chars = realloc(row.chars, sizeof(char)*(row.size+2));

if (filecol == row.size) {
  row.chars[row.size+1] = '\0';
} else {
  memmove(row.chars+filecol+1, row.chars+filecol, row.size-filecol+1);
}

row.chars[filecol] = c;
editorUpdateRow(&row);
E.dirty++;

元のサイズ+2でreallocしているのはヌル終端を入れるため。
行を変更したあとはeditorUpdateRowを呼び出している。こいつがcharsを元にrenderという表示用の文字列を更新してくれる。
また、E.dirty++とすることで未保存の変更があることを教えている。

これでだいたい動くはず。

Enterキーで改行する機能は実装したので、適当に削除も実装してレポート書きます。

おわり