情報工学実験という科目で、kilovという意図的に機能が削られたエディタに機能を追加する演習をやっている。
kilovはkiloというC言語で書かれたエディタをいじったものなので、完全なソースコードをGitHubで見ることもできる。
このブログに載せているコードが正しいかどうかはわからない。
やる
kilovは、editorConfig
という構造体にerow
という行を表す構造体の配列を持たせているため、こいつを編集して画面を更新してやるという流れになる。
この演習でメインループをいじる必要は無い。
ざっとコードを眺めると、キー入力を受け取って各種処理につなげているeditorProcessKeypress
という関数が見つかる。
こいつの中にあるswitch文が、特殊な制御の入力以外はdefault節からそのままの値を返していることがわかる。
このままでは何も起こらずメインループが次に進むので、このdefault節から文字を挿入する関数を呼んであげればいい。
命名は既存の関数に倣って、editorInsertCharacter
とかにしておく。
まずは現在のカーソル位置から行のデータを取る。
erow row = E.row[E.rowoff + E.cy];
rowoff
は行のオフセット、cy
はカーソルが何行にあるかを表している。
最初はrowoff
を足し忘れて、普通に動いているように見えるがカーソルが下に行って画面が動くと挙動がおかしくなる不具合が出た。そいういうことに気付けるので、デバッグ時は大きめのファイルを開いてみることをおすすめする。僕はいつもkilov.c
を開いてる。
行が取れたので、こいつが持っているchars
をいじっていく。
方針としては、chars
をrealloc
で拡張し、必要に応じて文字列を配列の後方に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キーで改行する機能は実装したので、適当に削除も実装してレポート書きます。
おわり