処理の完了に時間がかかる場合は、進捗状況をユーザーに知らせるべきです。そうすることで、ユーザーは残り時間を見積もることができ、Emacsがハングしているのではなく、処理中であることが明確に分かります。これを実現する便利な方法は、プログレスレポーターを使用することです。
特に役に立つことはないものの、動作する例を以下に示します:
(let ((progress-reporter
(make-progress-reporter "Collecting mana for Emacs..."
0 500)))
(dotimes (k 500)
(sit-for 0.01)
(progress-reporter-update progress-reporter k))
(progress-reporter-done progress-reporter))
この関数は進捗報告用オブジェクトを生成して返します。このオブジェクトを、以下に挙げる他の関数の引数として使用します。その狙いは、可能な限り多くのデータを事前に計算しておくことで、進捗報告を非常に高速に行うことにあります。
この進捗報告関数をその後使用すると、エコー領域に message
が表示され、続いて進捗率が表示されます。message
は単なる文字列として扱われます。例えば、ファイル名に依存させる必要がある場合は、この関数を呼び出す前に format-message を使用してください。
引数 min-value と max-value
は、操作の開始状態と終了状態を表す数値である必要があります。例えば、バッファをスキャンする操作では、これらの値をそれぞれ point-min と point-max
の結果に設定する必要があります。max-value は min-value より大きい値である必要があります。
あるいは、min-value と max-value を
nil
に設定することもできます。その場合、プログレスレポーターは処理の進捗率を報告せず、代わりに、プログレスレポーターを更新するたびにノッチが回転する「スピナー」を表示します。
min-value と max-value が数値の場合、引数 current-value に初期進捗を指定する数値を与えることができます。省略した場合は min-value がデフォルトとなります。
引数 min-change と min-time は、エコー領域の更新頻度を制御します。プログレスレポーターは、次のメッセージを出力する前に、操作が少なくともさらに min-change パーセント完了するまで待機します。デフォルトは 1 パーセントです。min-time は、連続する出力間の最小間隔を秒単位で指定します。デフォルトは 0.2 秒です。(一部のオペレーティングシステムでは、プログレスレポーターが秒未満の時間を異なる精度で処理する場合があります)。
context がシンボル async
である場合、更新が非同期に行われることが示されます。バックエンドはこの情報を利用して、進捗更新が他のデータに干渉するのを防ぐことができます。例えば、エコーエリアに進捗を表示するバックエンドは、エコーエリアが使用中のときには、それらの非同期更新を表示しません。
この関数はprogress-reporter-updateを呼び出すため、最初のメッセージがすぐに出力されます。
この関数は、処理の進捗状況を報告するという主要な役割を担います。reporter のメッセージを表示し、続いて value で決定された進捗率を表示します。進捗率が 0 であるか、あるいは引数 min-change および min-time に基づき十分に近い値である場合は、出力から省略されます。
reporter は make-progress-reporter の呼び出し結果である必要があります。value は操作の現在状態を指定し、make-progress-reporter に渡される際に min-value 以上 max-value
以下(両端を含む)である必要があります。例えば、バッファをスキャンする場合、value は point の呼び出し結果である必要があります。
オプション引数 update-text は、reporter
のメインメッセージおよび進捗テキストの後に表示される文字列です。典型的な用途の一つとして、長時間実行されるプロセスの「ステップ」として使用し、ユーザーに現在の状況を知らせることが挙げられます。reporter が数値以外のリポーターである場合、value には
nil または update-text
の代わりに使用する文字列を指定する必要があります。
この関数は、make-progress-reporter に渡される min-change および min-time
をそのまま扱うため、呼び出されるたびに新しいメッセージを出力することはありません。そのため非常に高速であり、通常はこの関数の呼び出し回数を減らそうとすべきではありません。結果として生じるオーバーヘッドが、そうした試みの効果を打ち消してしまう可能性が高いためです。
この関数は progress-reporter-update
と同様ですが、無条件にエコー領域にメッセージを出力する点が異なります。
reporter、value、およびupdate-textは、progress-reporter-updateと同様の意味を持ちます。オプションのnew-messageを使用すると、reporterのメッセージを変更できます。この機能は常にエコー領域を更新するため、このような変更は直ちにユーザーに提示されます。
この関数は、処理が完了した際に呼び出す必要があります。エコー領域に reporter というメッセージに続き、単語 ‘done’ を出力します。
常にこの関数を呼び出すべきであり、progress-reporter-update が
‘100%’
を出力することを期待すべきではありません。まず、出力されない可能性があり、そうならない理由は数多くあります。次に、‘done’ の方がより明示的です。
これは dotimes
と同様に動作しつつ、前述の関数を用いてループの進捗を報告する便利なマクロです。これにより、記述量を削減できます。引数 reporter-or-message には、文字列または進捗報告オブジェクトのいずれかを指定できます。
このサブセクションの冒頭にある例を、次のようにこのマクロを使って書き換えることができます。
(dotimes-with-progress-reporter
(k 500)
"Collecting some mana for Emacs..."
(sit-for 0.01))
レポーターオブジェクトをreporter-or-messageの引数として使用すると、make-progress-reporterでオプション引数を指定したい場合に便利です。例えば、前述の例は次のように記述できます。
(dotimes-with-progress-reporter
(k 500)
(make-progress-reporter "Collecting some mana for Emacs..." 0 500 0 1 1.5)
(sit-for 0.01))
これも dolist
と同様に動作する便利なマクロですが、前述の関数を用いてループの進捗を報告します。dotimes-with-progress-reporter と同様に、reporter-or-message
には進捗報告関数または文字列を指定できます。前述の例は、このマクロを使って次のように書き換えることができます。
(dolist-with-progress-reporter
(k (number-sequence 0 500))
"Collecting some mana for Emacs..."
(sit-for 0.01))
処理の実行に時間がかかるかどうかが不明な場合や、進捗報告機能を実装するのが不便な場合があります。そのような状況で、このマクロを利用できます。
(with-delayed-message (2 (format "Gathering data for %s" entry)) (setq data (gather-data entry)))
この例では、本体の実行に2秒以上かかった場合、メッセージが表示されます。それより短い時間で済んだ場合は、メッセージは表示されません。いずれの場合も、本体は通常通り評価され、本体の最後の要素の戻り値がマクロの戻り値となります。
message 要素は body よりも先に評価され、メッセージの表示有無にかかわらず、常に評価されます。