diff --git a/doc/fold.jax b/doc/fold.jax index c56e0bf5b..189b8a228 100644 --- a/doc/fold.jax +++ b/doc/fold.jax @@ -1,4 +1,4 @@ -*fold.txt* For Vim バージョン 9.1. Last change: 2023 Mar 24 +*fold.txt* For Vim バージョン 9.1. Last change: 2024 Dec 17 VIMリファレンスマニュアル by Bram Moolenaar @@ -77,18 +77,20 @@ 同じ事(「段落」を折り畳みに)をする別の表現: > :set foldexpr=getline(v:lnum-1)=~'^\\s*$'&&getline(v:lnum)=~'\\S'?'>1':1 -バックスラッシュ(日本では \ 記号)が ":set" の流儀で、通常とは異なるキャラクタ -(空白文字、バックスラッシュ、ダブルクォート、その他、詳細は|option-backslash| -参照)をエスケープしていることに注意。 +Note バックスラッシュ (日本では \ 記号) が ":set" の流儀で、通常とは異なるキャ +ラクタ (空白文字、バックスラッシュ、ダブルクォート、その他、詳細は +|option-backslash| 参照) をエスケープしていることに注意。 最も効果的なのはコンパイル済み関数を引数なしで呼ぶ: > :set foldexpr=MyFoldLevel() 関数は v:lnum を使用しなくてはいけない。|expr-option-function| を参照。 式が評価される際の前提条件は以下の通り: + - その行について現在のバッファとウィンドウが常に存在している。 - 変数 "v:lnum" には評価対象となる行番号が設定されている。 -- 式の結果(戻り値)は以下の形式で折り畳みレベルを示す: + +foldexpr の結果によって、以下のように折り畳みレベルが決定される: 値 意味 ~ 0 対象行は折り畳みに含まれない 1, 2, .. 対象行はこのレベルの折り畳みに含まれる @@ -102,6 +104,8 @@ "<1", "<2", .. 指定したレベルの折り畳みを対象行で終了する ">1", ">2", .. 指定したレベルの折り畳みを対象行から開始する +結果の値 "="、"s" および "a" はより高価である。|fold-expr-slow| を参照。 + 折り畳みは直前の行の折り畳みレベルより高い(低い)行から開始(終了)されるので、折 り畳みの開始 (終了)マーク ">1" ("<1") は明示的に指定する必要は無い。 @@ -113,13 +117,6 @@ 折り畳みレベルは0に設定される。'debug' オプションに "msg" を設定すれば、エラー メッセージが表示されるようになるので、デバッグに利用できる。 -NOTE: 各行について式評価が実行されるので、この折り畳み方式は非常に動作が遅くな -る可能性がある! - -"=", "a", そして "s" は極力避けるようにする。なぜならVimはそれらが使われると、 -折り畳みレベルが定義された行が見つかるまで戻って、幾度も検索を行わなければなら -ないからだ。これは動作が遅くなることがある。 - 'foldexpr' の式が s: か || で始まる場合、スクリプトID(|local-function|) に置き換えられる。例: > set foldexpr=s:MyFoldExpr() @@ -144,6 +141,37 @@ NOTE: 各行について式評価が実行されるので、この折り畳み 折り畳みが適切に更新されない場合がある。その場合は |zx| か |zX| を使って強制的 に更新すること。 +☆計算コストの最小化 *fold-expr-slow* + +この折り畳み方式は計算コストが高いため、特にすべての行の折り畳みレベルを最初に +計算する必要がある場合、Vim が応答しなくなる可能性がある。 +その後、各変更の後に Vim は折り畳みレベルの計算を折り畳みレベルが影響を受けた +行に制限する (他のすべての行の既知の折り畳みレベルを再利用する)。 + +したがって、折り畳み式は特定の行の計算に必要な依存行の数を最小限に抑えるように +努める必要がある。例えば、独立した折り畳みレベルが見つかるまで前の行の折り畳み +レベルの評価が必要になるため、"="、"a" および "s" の戻り値を避けるようにするこ +と。 + +これが難しい場合は、次善策として |b:changedtick| でのみ更新されるバッファロー +カル変数 (b:foldlevels) にすべての折り畳みレベルをキャッシュすることが考えられ +る: +>vim + vim9script + def MyFoldFunc(): number + if b:lasttick == b:changedtick + return b:foldlevels[v:lnum - 1] + endif + b:lasttick = b:changedtick + b:foldlevels = [] + # compute foldlevels ... + return b:foldlevels[v:lnum - 1] + enddef + set foldexpr=s:MyFoldFunc() +< +上記の例では、プリコンパイルされた引数なしの Vim9 script 関数を使用することで、 +さらに高速化された (それでも v:lnum を使用する必要がある)。 +|expr-option-function| を参照。 ☆構文 *fold-syntax* @@ -446,7 +474,7 @@ zk カーソルより上方の折り畳みへ移動する。閉じられた折 折り畳みに対してコマンドを実行する ~ -:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen* +:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen* 閉じた折り畳みの中以外の全ての行に対して{cmd}を実行する。 [range]が与えられた時は、その範囲だけが対象となる。 コマンドが各行に対して実行される時にはカーソルはその対象となる diff --git a/en/fold.txt b/en/fold.txt index b29049205..b6cb2a440 100644 --- a/en/fold.txt +++ b/en/fold.txt @@ -1,4 +1,4 @@ -*fold.txt* For Vim version 9.1. Last change: 2023 Mar 24 +*fold.txt* For Vim version 9.1. Last change: 2024 Dec 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -87,9 +87,11 @@ The most efficient is to call a compiled function without arguments: > The function must use v:lnum. See |expr-option-function|. These are the conditions with which the expression is evaluated: + - The current buffer and window are set for the line. - The variable "v:lnum" is set to the line number. -- The result is used for the fold level in this way: + +The result of foldexpr then determines the fold level as follows: value meaning ~ 0 the line is not in a fold 1, 2, .. the line is in a fold with this level @@ -104,6 +106,9 @@ These are the conditions with which the expression is evaluated: "<1", "<2", .. a fold with this level ends at this line ">1", ">2", .. a fold with this level starts at this line +The result values "=", "s" and "a" are more expensive, please see +|fold-expr-slow|. + It is not required to mark the start (end) of a fold with ">1" ("<1"), a fold will also start (end) when the fold level is higher (lower) than the fold level of the previous line. @@ -117,14 +122,8 @@ recognized, there is no error message and the fold level will be zero. For debugging the 'debug' option can be set to "msg", the error messages will be visible then. -Note: Since the expression has to be evaluated for every line, this fold -method can be very slow! - -Try to avoid the "=", "a" and "s" return values, since Vim often has to search -backwards for a line for which the fold level is defined. This can be slow. - If the 'foldexpr' expression starts with s: or ||, then it is replaced -with the script ID (|local-function|). Examples: > +with the script ID (|local-function|). Examples: > set foldexpr=s:MyFoldExpr() set foldexpr=SomeFoldExpr() < @@ -148,6 +147,39 @@ end in that line. It may happen that folds are not updated properly. You can use |zx| or |zX| to force updating folds. +MINIMIZING COMPUTATIONAL COST *fold-expr-slow* + +Due to its computational cost, this fold method can make Vim unresponsive, +especially when the fold level of all lines have to be initially computed. +Afterwards, after each change, Vim restricts the computation of foldlevels +to those lines whose fold level was affected by it (and reuses the known +foldlevels of all the others). + +The fold expression should therefore strive to minimize the number of +dependent lines needed for the computation of a given line: For example, try +to avoid the "=", "a" and "s" return values, because these will require the +evaluation of the fold levels on previous lines until an independent fold +level is found. + +If this proves difficult, the next best thing could be to cache all fold +levels in a buffer-local variable (b:foldlevels) that is only updated on +|b:changedtick|: +>vim + vim9script + def MyFoldFunc(): number + if b:lasttick == b:changedtick + return b:foldlevels[v:lnum - 1] + endif + b:lasttick = b:changedtick + b:foldlevels = [] + # compute foldlevels ... + return b:foldlevels[v:lnum - 1] + enddef + set foldexpr=s:MyFoldFunc() +< +In above example further speedup was gained by using a precompiled Vim9 script +function without arguments (that must still use v:lnum). See +|expr-option-function|. SYNTAX *fold-syntax* @@ -384,8 +416,8 @@ zX Undo manually opened and closed folds: re-apply 'foldlevel'. Also forces recomputing folds, like |zx|. *zm* -zm Fold more: Subtract |v:count1| from 'foldlevel'. If 'foldlevel' was - already zero nothing happens. +zm Fold more: Subtract |v:count1| from 'foldlevel'. If + 'foldlevel' was already zero nothing happens. 'foldenable' will be set. *zM* @@ -449,7 +481,7 @@ zk Move upwards to the end of the previous fold. A closed fold EXECUTING COMMANDS ON FOLDS ~ -:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen* +:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen* Execute {cmd} on all lines that are not in a closed fold. When [range] is given, only these lines are used. Each time {cmd} is executed the cursor is positioned on the @@ -539,7 +571,7 @@ When there is room after the text, it is filled with the character specified by 'fillchars'. If the 'foldtext' expression starts with s: or ||, then it is replaced -with the script ID (|local-function|). Examples: > +with the script ID (|local-function|). Examples: > set foldtext=s:MyFoldText() set foldtext=SomeFoldText() <