テキストエディタのミニマップについて

 

遅刻すると思ったか??
 
 
この記事は NITKC Prolab Advent Calendar 2019 の14日目の記事として書かれています。
13日目はKP、15日目はキョウ君が担当です。

 

前置き

うぃんじーです。もう現役生ではなくなってしまったので軽く自己紹介しておくと僕はプロラボ部のOB(ロウガイと読む)です。
現5年生KP君の同級生でした。
現5年生KP君の、同級生でした。
この世界に時の狭間が発生して以来KP君とは同級生ではなくなってしまいました。
 
 
 
… ←時の狭間
 
 
 
ところで僕は現在就活中(就活しているとは言っていない)です。
とりあえずプログラミング関係の仕事に就いていい感じに年収5000万円を得たいところなのですが、二つ問題があります。
一つは、自撮りが苦手なので履歴書とかに載せる写真を撮るのが嫌だということです。知らんがな
 
もう一つは、最近全く実践的なプログラミングをしていないことです。
このままでは、面接でプログラミングできます!と言っても
 
 
 
「お前は、プログラミングをできるような顔には、見えません…」
 
 
 
と言われて
 
 
 
「嘘をつくような顔に見えますか?」「でも嘘じゃなかったら成果物くらいあるでしょう」「御社が第一志望です!」「面接はこれにて終了とさせていただきます」「御社が第一志望です!御社が第一志望です!御社が第一志望です!…
 
 
 
という感じで就活に失敗して路頭に迷うに違いありません。
 
 
そういう浅はかな考えから最近テキストエディタを作っています。
なんでテキストエディタかというと、昔やたらプログラミングに対する意識が高まって作りかけていたテキストエディタが残っていたからです。
そのまま放っておくと意識が高かった時の自分がかわいそうですよね。
 
テキストエディタと言ってもただ車輪の再発明をしていたら面白くないので、ざっくりとした方向性を持ってやっています。
 
 
 
  1. 自然言語の文章、特にクリエイティブな文章を書くことに特化させる
  2. 「とりあえず開くnotepad」を代替できる(シンプルに)
  3. なんか触ってて楽しくなれる(インタラクティブ
  4. 人生はいつでもやり直せる
 
 
 
色々な機能を実装しているところですが、今回はそれらの機能のうち個人的に面白いかもしれないと思っている「ミニマップ機能」について取り上げます。

ミニマップとは

ミニマップというのは、ゲームで
 
「クソ!早く人を殺したいぜ!」
 
ってなって
 
「でも敵がどこにもいねえ!一体どこにいやがるんだ」
 
って思ったときに見る右上とか左上とかにあるマップのことです。
「ミニ」とつけることでちょっとかわいいけどシンプルでモダンな感じを醸し出しているあいつのことです。
 
テキストエディタにおけるミニマップも同じです。
説明が面倒なのでVisual Studio Codeのミニマップを見せよう。これです。
 

f:id:winjii:20191214220845j:plain

VS Codeのミニマップ(画面右)
 
どうです、ちょっとかわいいけどシンプルでモダンな感じですよね。
これを実装したらちょっとかわいいけどシンプルでモダンな感じのテキストエディタになるんだな!
 
…と早まる前に、まずミニマップが何のためにあるのか考えます。
ここでただ「chotto kawaiiけど(略)」を達成するためだけにミニマップを実装するのは浅はかだからです。
 
 
 
 
個人的な答えですが、これは多分「リッチなスクロールバー」です。
 
スクロールとは?
スクロールとは、アナログと違ってドキュメントの表示領域が限られているデジタル表示媒体において読む場所を移動するためのものです。
スクロールバーとは、「今この辺を読んでいますよ」というのと「スクロールするにはここを動かしてくださいね」というのを伝えるためにあります。
従来のスクロールバーでは今読んでいる場所を「全体をこの長さとするとこのくらいです」としか表現できなかったのに対して、ミニマップを使うことで実際に読んでいるものの縮小表示を見せながら「この辺です」って言えるよね、ということだと思います。
ただchotto kawaiiだけのやつじゃなかった。
 
しかし僕はVisual Studio Codeについているようなミニマップを使いません。
恐らく一つの理由としては、Visual Studio Codeで書くドキュメントというのは「ソースコード」だからです。
ソースコードでは文字が華やかに飾られていて、内容自体が構造化されているのでそもそもミニマップを見なくても普通の表示領域を見れば十分に位置が把握できる。(正確な位置ではなく、脳内地図があって大雑把に分類できるということ)
 
というか、ソースコードを読むときは構造を把握するのが前提でソースコードの内容にも視覚的/論理的に分かりやすい構造が求められるので脳内地図が作られやすい状態にある。
 
ミニマップの助けを借りないと全体が把握できないようなソースコードを書いている人はリファクタリングしてください。(はい、ごめんなさい)
 
 
 
 
普通に自然言語の文章を書くときは話が違います。自然言語の文章は必ずしもシンプルな分かりやすい構造があるわけではないので、脳内地図が作りづらく、位置を移動するということには困難が伴います。
まあそれで困るほどひと続きの長い文章を書く人も稀なのですが、それは一旦置いておきましょう。
 
人類みな芥川龍之介です。
 
芥川龍之介がミニマップを見たらどう思うでしょうか?
長い文章を書いて、ふう、ちょっと前のところ読み返そうかな、どこだっけ…って思ったときにミニマップを見ます。
それがVisual Studio Codeのミニマップだったら?
 
実はVisual Studio Codeのミニマップはリッチなスクロールバーと言い切れないところがあります。
長過ぎるドキュメントだと、ミニマップが表示領域をはみ出して、ミニマップ自体を上下にスクロールしなければならないのです。(他のミニマップは知りませんが、多分ネット上で探して見つかるミニマップの実装は全部そうでしょう)
上から下にズラッと並んだだけのドキュメントを縮小表示して全体像を分かりやすくする、それがミニマップのすごいところです。ところが今度はながいながいミニマップをスクロールしなければならなくなって、やっぱり全体像が把握できなくなる。
一番右のスクロールバーはこのためについていたのです。
ちなみにこの現象は僕の環境では500行くらいから始まります。
 
それから自然言語の文章では改行が頻繁に起こるとは限りません。
我々はどういうわけかブログを書くときはこうやって改行だらけの文章を書くと見やすくなることを知っていますが、いつも文章がこうであるとは限りませんし、この調子で改行していても普通に折り返し(右端に到達した文章が次の行に表示されるやつ)は発生しています。
一方でVisual Studio Codeのミニマップには文字の折り返しが反映されません。
 
(簡単なテキストエディタを実装している人なりに意見すると、この辺の処理は手間としても実行時間としても重いので、ソースコードのエディタとしては実装しないのが正解だろうと思いますが…)
 
つまり、改行の少ない文章ではミニマップと通常表示とで文章の見た目がかなり変わってしまい、ミニマップが表示領域の縮小版であるという立場を失います。これはもしミニマップを手がかりに脳内地図を作ってスクロールする場合は障害になるかもしれません。
 
 
 
 
 
Visual Studio Codeに対するヘイトスピーチみたいになってしまいましたが、僕が好きなエディタはVisual StudioVisual Studio Codeであることは強調しておきます。
 
しかし、僕はこのままでは芥川龍之介に顔向けできないと思うのです。
普通に原稿用紙に書けばスクロール量が分かりやすくていいし、ワタシ原稿用紙で書きますねん、と言われてしまう。
 
――原稿用紙はスクロール量が分かりやすいんですか?
 
――そりゃ原稿用紙はぴったり400文字入るってわかってて、原稿用紙の枚数をざっと見れば文章量が把握できるからな。書いた量も今の位置も分かりやすいねん。
 
――なるほど、原稿用紙という構造を与えることで大量の文章がより把握しやすくなっているんですね!

ミニマップ・改

つまり既存のミニマップを脳内地図の手がかりとして使う場合に問題となることは、ミニマップ表示によって新たな構造が生まれるわけではない、ということです。
ミニマップがあろうとなかろうと、ドキュメントの構造は上から下にずっと流れていくものだし長すぎる場合には上下が切り取られるので、今見ている場所と全体との関係が分かりづらくなっていきます。
スクロールバーが与える「今読んでいる位置」の情報も、「全体の長さのうちの割合」でしかないため、ドキュメントの長さによって感覚が変わってしまうし脳内地図の手がかりにはなりにくい。
 
そこで僕はミニマップ・改を実装しました。
 
というわけで、満を持して、僕のテキストエディタのミニマップ機能を紹介します。
これです。
 

 

 
じゃ~ん!
ん?なんか縦書きですね…
実はこのテキストエディタ、最初は日本語縦書き用として作り始めまして、まだ縦書きしか対応していないのです。そこは無視してください。
 
ミニマップというよりもはやただのマップです。
アピールしたいのは、元の表示の縮小版を忠実に、そして画面いっぱいに(処理落ちせず)表示できていることです。
更に、長すぎるミニマップを一列に並べるのではなく、折り返して二次元的に表示することで長い文章に新たな構造を与えています。
ぶっちゃけこれでもはみ出されると結局スクロールしなければならない(そしてミニマップスクロールは未実装…)のですが、大量のスクロールをさせられて位置が分からなくならないような実用的な範囲に収まっていると思います。
 
それに、ミニマップのフォントサイズはもっと小さくすることもできます。
このテキストエディタを自分でもあまり使っていないので、実際どのくらいまで小さくしてもいいのかわかりませんが…。(公開始めたら誰か使ってみてほしい)
 

f:id:winjii:20191214221433j:plain

 

 
このくらい小さくすると「吾輩は猫である」全文が一画面に表示できます!
これ何文字あると思いますか?
37万文字ですよ、37万文字!
一体ゼロいくつやねんと思う人のために書いておくと、3.7 \times 10^5文字です。
新書や文庫本だと十数万文字(10^5文字強)とかが普通なので、このフォントサイズでよければ本一冊余裕で表示できるということです。
天国の芥川龍之介もにっこりですね。
 
 
ところで、実は芥川龍之介の作品をあまり読んだことがありません。「我輩は猫である」すら読んだことないんですよね。読んだほうがいいかも。
 
追記(2022/1/8):「吾輩は猫である」って実は夏目漱石やねん。最近読んでみて結構面白かったけど猫が突然無意味に死んだ理由がずっと分かりません。なるべく無意味に殺したかったのか?
 
「歯車」というやつは読んだ覚えがあります。確か死にそうな芥川龍之介が「かしこま!」って言いながらホテルのベルを連打するみたいな話だったと思います。
 
 
芥川龍之介さんごめんなさい。
それでは。
 
 
 
最後に一応GitHubリンクを貼っておきます。
まだ公開していませんが、最低限の機能が実装できたら公開すると思うのでぜひ遊んでみてください。
ミニマップ以外にも面白い機能を実装しています。(機能過多には気をつけよう)
ちなみに実装にはC++とSiv3Dを使っています。
 UnnamedEditorって中学生が考えたテキストエディタみたいな.......

github.com

追記

2019/12/15: アイカツを見てください

余談

面白くない苦労話を書かずにはいられなかったので書いておきます。
 
テキストエディタでは、内容を編集されるので文字を一文字ずつ動的に表示しなければなりません。
ある場所が編集されると後ろの文字の位置全てに変更が及びます。
またインタラクティブテキストエディタを目指しているので、文字の位置がアニメーションで変化したりしていて更に面倒なことになっています。
それを毎回再計算していたら大変なことになります。
そのため文章を改行で区切ったりして、必要なブロックだけ再計算するようにしています。(ある位置が編集されたときに文字が複雑にずれるのは次の改行までで、次の改行から先はスライドするだけ)
 
そこまではいいです。
 
問題は、まさに今回紹介したミニマップの実装です。
このミニマップは元の表示とは別に折返しがあって話がややこしくなります。
それに、プログラマの皆さんはなんとなく分かると思いますが、位置の再計算とか以前にそもそも37万文字の描画はやばいです。
37万文字を一つずつ描画していたら一生は終わらないにしてもできたてのラーメンがしこたま伸びるかもしれません。それも言いすぎですが1フレームや数フレームで計算が終わらないのは確実です。
そのためバケット法的な感じである程度まとめて描画物=文字をキャッシュしていて、更にテキストが編集されたときは関係するバケットだけを再計算するようにしています。
キャッシュ単位がでかぎると今度は再計算コストがアホみたいになるのでうまいところを狙っていきます。(平方分割)
要素が追加/削除されるバケット法って初めて考えましたが、隣り合うバケット同士のサイズ合計が常に一定値を超えるように保つ感じで再計算していて、多分実用的には√nで抑えられていると思います。
バケットの区切りをどこに持ってくるか(改行位置で区切ってる)とかの話でややこしくなるので細かい説明は書きませんが…
極端な話改行がまったくない37万文字の文章だと完全に計算量が崩壊したりはします。(というかそもそも折り返し処理の計算量が改行のない文章の長さに比例する)
 
バケット法を使ったことで、元の表示の忠実な縮小版というのも達成できました。つまり、大きいフォントで描画したものをちょっと処理して縮小しています。普通のミニマップだと最初から小さいフォントで描画したり等幅フォントに限定したりすることで手間を削減していて、このために文字の配置が元の表示と変わったりします。VS Codeみたいに折り返しがないミニマップならあまり関係ないのですが折り返しがあると配置のずれが積み重なっていって、文字自体は読めないのでいいですが文章全体の形/余白の形が変わってしまいます。
まあ細かすぎてあまり関係ない気もしますが…
 
あとアニメーション管理がマジで面倒。
インタラクティブなアニメーションってものによっては結構破壊的な変更なので、アニメーションで使いそうな描画関係の情報を律儀にカプセル化しているとアニメーションをいじるときに死にそうになる。アニメーション自体の処理もある程度はきれいな設計を妥協したほうがいい。
それから単純にフォントの情報持ってきて処理するのも面倒。Siv3Dだけだと細かいフォント処理ができないからと思って自分でFreeTypeを叩いているんだよね。まだちょっとしかやってないけど。
 
 
面倒だけど、自分としてはすごい量のコードを書いている気がします。