Rhinocs_仕様検討
Rhinocs_仕様検討
Rhinocsの仕様の検討を残しておく。
floating-list 2026-05-27 (水)
switch-bufferはタブ補完みたいなのよりはVSCodeのCmd+pみたいなのにしたい。フィルタリングとかはJS側で書くとする。 するとエディタ側で用意すべきものは何なのかを考えたい。
とりあえず以下があれば実現は出来るか?
- 文字列のリストをセットするとそれを下から上のリストとして描画し、一番下が選択される
- 選択を一つ上、一つ下に移動するコマンド
- 現在の選択を返す関数
そもそもfloatingListインスタンスが取れればあとはJS側からこれを操ればいいか?
let floating = get_floating_list();
floating.items = ["abc", "def", "ghi"];
floating.moveUp();
floating.moveDown();
let res = floating.selectedIndex
switch-buffer 2026-05-27 (水)
メモリ上にあるバッファを探すというのはAndroid的ではない気もする。 リサイクルで全部無くなっちゃうので。
現状の手抜きfile_historyではuriとファイル名を保存しているが、 むしろこのデータから探す方がAndroid的ではないか? そしてメモリ上にあればそれを使ってなければファイルからロードする、 という風にメモリの方をキャッシュ的に使えば、メモリにある間はemacsっぽくも振る舞う。
ようするにget-buffer-createにurlでとってくるものがあればいいようにも思う。
バッファ名 > ヒストリ > uri > switch-buffer-by-uri
となればメモリに無くても動く気がするな。 あ、これだと保存してないバッファが引っかからないな。 もう一捻り必要そうだが、路線は悪くない気がする。 先にメモリ上を探して、なければヒストリを探すのがいいか。
こうヒストリをコアな機能に使うと、 ヒストリが現状js側で勝手に実装している独自拡張なのがどうなんだ、という気もするが。
まぁこの辺はエディタはJS側で実装するのに必要な最小限の機能を実装するだけにしてJS側でいろいろやるようにしておくかな。 第三者が試すのが難しくなるが、しばらくは自分のことだけ考えるのが今風だろう。
ファイルの新規作成の仕様検討
SAFではファイルを開くのと新規作成ではリクエストが別なので、 find_fileで無かったら作成というemacs的な挙動とは異なる。 無理にemacsっぽくしようと思えば実現は出来るのだが、 それよりは、よりSAFに自然な仕様にしておきたい。
考えているのは、新規作成のようなコマンドを作り、 するとVSCodeとかのuntitledみたいな扱いのバッファが出来る感じにして、 それをC-x C-sするとSAF的には新規作成と扱う、という挙動。 これならsave-asと同じコードに出来るし、SAF的にも自然だし、 VSCodeなどの昨今のエディタに慣れている人からすれば自然に思う。
emacs的では無いが、ファイル回りはSAFに寄せるほうがいい気もしている。
当初はfind_fileももっとemacsっぽく実装しようと思っていたが、そのためにはディレクトリのpermissionをもらう必要がある。 それは結構ややこしい仕様だよなぁ、という気はしてた。
その辺が面倒なので、現状はとりあえずSAFでファイルを単体で開いていて、 選ぶファイルの選択はシステムのファイルマネージャーを使っていて、 あとで書き直そうと思っていたが、使っていると、今のこの挙動の方がAndroid的で良い気はしてきている。
システムのファイルマネージャーは使いづらいけど、 ブックマークとかヒストリを充実させたり、 あとでファイラーっぽいものをJSレイヤーで書いたりする方がいいような気がしてきている。
emacsと変えると学習コストとかドキュメントとかの問題が出てくるけれど、 中途半端に似せようとして分かりにくいよりはAndroid的に自然にしておく方が将来困ったことにもなりにくい気がする。
C-x C-nでUntitiled-1とかのバッファが出来て、それをC-x C-sで保存する時に名前を指定する、 という感じにしたい。
ミニバッファの仕様検討
ミニバッファはxyzzyやemacsやlemでは、割とモーダルっぽく内部でループを回す感じの実装になっている。 そちらの方がトラブルが無かったのだろうが、SKKとかとは相性が悪そうだしJS的でもない。
という事でもう少し低レベルのAPIを提供してJavaScriptのレイヤーでプロミスで実装する感じを目指したい。
低レベルAPI
以下の2つを提供する。
function enter_minibuffer(prompt: string): void;
ミニバッファとミニバッファWindowを用意してそれをアクティブにする。promptはミニバッファの先頭に表示されるラベル。
function leave_minibuffer(): string;
ミニバッファとミニバッファWindowを破棄し、その前にアクティブだったWindowとバッファにフォーカスを戻す。 ミニバッファに入力されている文字列を返す。
これらを使ってJS側で通常のミニバッファ系列の関数を実装する。
JS側での実装
まずはemacs lispのread-string相当を実装する。
GNU Emacs Lispリファレンスマニュアル: Minibuffers
function read_string(prompt: string) : Promise<string>;
promptでミニバッファを表示して入力を受け取り、結果のPromiseを返す。
これはminibuffer keymapをpushしてenter_minibuffer()して、 Promiseを返し、このキーマップでReturnだったらこのPromiseをresolve、C-gだったらrejectするような感じの実装になる。
デバイスごとの保存 2026-05-21 (木)
Rhinocsはなるべく見えない所(data下)にものを保存しない、をコンセプトにしているが、これだとファイルのヒストリのような、デバイスごとに保存したいものをjsから保存する時にsyncthingで共有されてしまって困る。
デバイスごとのIDは簡単にはとれなさそうなので、手動で設定する事にしたい。
APIとしてはSharedPreferenceに文字列を読み書き出来ればいいだろう。それを使ってデバイスごとに一回手動で set_device_id("BOOX") とかをevalする感じにすればいいかな、と思っている。
すると、 /storage/per_device/BOOX/file_history/history.json とかに保存する感じになればいいだろうか。
get_per_device_storage_dir()とかで取れればいいかな。storageって名前がいまいちだな。なんかいい名前あるだろうか?でもstoreではplay storeと被るしなぁ。 storageでいいか。
結局、以下を実装した。
- join_path
- put_pref_string
- get_pref_string
- set_device_id
- get_device_id
- get_per_device_storage_dir
モード行の仕様検討
emacsやxyzzyはmode-line-format変数に設定しておくと毎回それをformatして表示するが、RhinoだといちいちContextをenterして値を取り出すのでなんか無駄な気はする。
それよりは以下みたいな関数で設定してWindowに持たせるかな。
set_mode_line_format(fmt: string)get_mode_line_format() : string
mode-line-formatの%の略語は今どき覚える気も起こらないので、冗長にドル始まりの変数名っぽいのでいいだろう。 JavaScriptのテンプレート文字列っぽい以下の表記でどうか?
${column}${lineNum}${bufferName}${modified}
とりあえずこの4つくらいサポートする事から始めたい。modifiedは変更されてると**でそれ以外は--とする。read onlyとかはしばらく考えない。
例えば以下みたいな感じで指定する。
set_mode_line_format("--${modified}- ${bufferName} Ln: ${lineNum} Col: ${column} --");
get_mode_line_formatでは置換される前のfmt文字列がそのまま取れる。
モード行はアプリで一つとする
モード行はemacsなどはバッファローカルでそれはモードがバッファローカルだからなのだが、するとSKKとかもモードとして実装する方が自然になる。 だがそれが望ましいかというと割と微妙。 そもそもモードがいるのか?というのはちょっと微妙なので、必要になるまではやらなくてもいいかな?という気もする。
結局拡張していけるプラットフォーム的なものを作りたいのか、あくまでエディタだけに特化したいのか、という問いで、 今必要なのはエディタなので、まずはエディタに特化していきたい。
モード行に相当するものはVSCodeではアプリで一つでアクティブなバッファ(ウィンドウ)の情報が表示されている。 こっちの方がスペースも食わないしいいような気もする。
モードが無いならそれはモード行ではないのでは?という気もするが、とりあえず関数などの名前はemacsに寄せておく。
マーカー仕様検討 2026-05-20 (水)
eval-regionやコピペのためにマーカーが欲しくなる。既存実装を調査>エディタ調査
elipsの関数名を参考に考える。
GNU Emacs Lispリファレンスマニュアル: Markers
マーク的なものは最小限にして基本的にはマーカーのみを揃える感じで。しばらくmark-ringとかはやらない。 以下を用意するか。
- mark_marker
- set_marker
- marker_position
mark関連の唯一の関数がmark_marker。 make_markerはしばらく提供せず、mark_markerで唯一のmarkerを取得する感じにする。 markなどはmark-markerとmarker-positionなどを使ってjsレイヤーで実装する。
この3つを実装するには以下をすればいいか。
- Bufferにmarkerのリストをぶら下げる
- insertとかdeleteとか必要な所でadjustの実装を追加
- js側に見せるメソッドを追加
リサイクルとenv
Androidはプロセスが殺されるので、インタープリタをずっと活かしておく、という事が出来ない。 だから殺されて再生成された時にまぁまぁの速度で動く必要がある。
emacsのようにすべてをメモリ上にずっと置いておく、という前提は難しいので、恐らく現在のバッファに対して読み込まなくてはいけないものをある程度は限定する必要はありそう。
また、再生されてから動き出すまでと、その裏でいろいろやるのは分離したい。SKKの初期化などはいかにも分離したいよなぁ。
Viewについての雑記
下の方に書いたように初期はスクロールを真面目にやろうと考えていたが、それはドッグフードまでが遠すぎてやる気が出ない。 もっと単純にしよう。
Viewはバッファのある範囲を書くが、コンソールっぽく単純化したい。 Viewのうち文字を書く領域をPaneと呼ぶ事にする。
Paneは縦横の文字のグリッドで、文字は幅が1か2のどちらかとする。フォントは等幅フォント固定。 文字の描画は文字ごとに書く。くっついたりする言語とかはサポートしないし、文字の幅を足したものが文字列の幅になるようにする。
バッファに対してある行の範囲を指定すると、その範囲でのGrid情報を計算して、rowとcolを指定すると文字が取り出せる(または前のcolが2幅なら無しが取り出せる)。 この情報はGridと呼ぶ事にするか。
PaneはGridの通りに文字を描くだけとする。スクロールとかもとりあえずは考えずに全部redrawとする。
GUIスレッドとRhinoのスレッド
とりあえずGUIスレッドでRhinoも動かす。これでは駄目なのは少し考えればわかるが、Bufferの更新とUIのレンダリングの同期がちょっと面倒なので、まずは同一スレッドで始める。
最終的にはRhinoは専用の一つのスレッドでいつも動かし、GUI側にはイベントで変更を通知し、Gridを生成する時にはBufferをロックする感じにしたい。
パッケージについて
最初にホームディレクトリを指定させる。これはSAFで。とりあえずそこにjsのスクリプト置いて、それをロードする感じでいってみたい。 本当にそれでエディタになるのかは少し自信が無い所もあるが、 カスタマイズしたい訳では無いので、既存のパッケージの関数の上書きとかはまずは出来なくてもいいだろう。 どちらかといえば途中からは実装をRhino側でやるようにしたいなぁ、という気持ちが強い。
ただActivityのリサイクルがあるので、あまり大きな初期化を最初にやる、というモデルは厳しいかもしれない。
バッファについて
バッファは行のリストで。ギャップは入れないで単純に毎回コピーする。 バッファ周りはAPIは割と真面目に作る。 StringBuilderのArrayListくらいから始めるか。