2020-06-02

Emacsで部分的な水平スクロールを実現する

Emacsでは toggle-truncate-lines を使えばバッファ全体の水平スクロールが可能です。行の折り返しをやめて左右端で切り詰めて表示できるので、幅の長い整形済みテキストを折り返さずに見たい場合には有効です。

しかしこのモードを使うと当然ながら文書全体で折り返しが無くなってしまいます。文書内の通常の文章は折り返して表示し、整形済みの部分だけ水平スクロールを適用する、そんな器用な機能は見当たりませんでした。

一方Emacsにはテキストプロパティオーバーレイという機能でテキストの一部を非表示にできます。となると、行の先頭部分を非表示にすることで擬似的に部分的な水平スクロールを実現できないでしょうか。

というわけで実装したのがこちら:

misohena/phscroll: Enable partial horizontal scroll in Emacs

使い方は

  1. elispをロードする 例:(require 'phscroll)
  2. スクロールさせたいリージョンで囲む
  3. M-x phscroll-region

これで選択した部分だけ水平スクロール出来るようになります。

ただし、truncate-lines が t (折り返さず表示するモード)のときは加工せずそのまま表示します。Emacs標準の水平スクロール機能を使用してください。 M-x toggle-truncate-lines で切り替えると、それを検出して表示を更新します。

既知の問題:

  • 遅い : すでに適用されているテキストプロパティやオーバーレイを調べて各行の幅を計算する部分やオーバーレイを適用して一部を非表示にする部分がかなり遅いみたいです。頑張って必要な部分だけ(windowで表示する範囲内や編集した部分だけ)更新するようにしてみましたが、それでもまだ遅いみたいです。
  • 複数ウィンドウ対応 : 複数のウィンドウで水平スクロール部分を見たときに表示が乱れることがあります。特にフレームを左右に分割したときに左右の幅が異なる場合は選択している方のウィンドウ幅でレイアウト計算するので、他のウィンドウではウィンドウの幅を超えてしまうことがあります。
  • display, invisibleプロパティの対応形式: 既にdisplayプロパティinvisibleプロパティで加工されているテキストも水平スクロールの対象にできますが、正しく幅を計算できる形式は限られています。単純に文字列に置き換えている場合やただ消している場合、それとrelative-spaceは対応しましたがそれ以外は未対応です。
phscroll.el使用例
図1: phscroll.el使用例

これだけだといちいちスクロールさせるエリアを手動で設定しなければならず面倒なので、次回は自動的にエリアを設定する方法について書こうと思います。

ふと思ったのですが、これとは逆に通常部分のみ折り返すというのは可能なのでしょうか。truncate-lines tで通常部分だけオーバーレイで折り返す。オーバーレイで改行ってできるのかな。でも変更があるたびに改行位置を維持するのは大変そうですね。