折り畳み


■ 概要

 「折り畳み」とは、簡単に言えば、編集テキストの一部を隠す(あるいは、 見えなくする)機能のことです。現実の世界でも、文字が書かれた紙の一部分を折り畳んで、 そこを見えなくすることができますが、それに相当します。 本エディタの「折り畳み」は、以下のようになります。 例えば、次のような文章があったとします。

  「折り畳み」を使えば、いろいろと便利なことができます。
  例えば、見出しや表題だけを残して、その詳細部分を隠せば、
  全体の構成を容易に見渡せるようになります。また、変更の
  無い行だけを折り畳めば、変更の有る行が浮き彫りになります。
  また、C言語系やHTMLのテキストでは、その構文構造に
  基づいて折り畳むと、効率的な編集作業が行なえます。
  そのほかにも、折り畳みの用途はいろいろとあります。
ここで、2〜6行目を折り畳むと次のようになります。
  「折り畳み」を使えば、いろいろと便利なことができます。
[+] 例えば、見出しや表題だけを残して、その詳細部分を隠せば、[...]
  そのほかにも、折り畳みの用途はいろいろとあります。

 折り畳んだ部分は、すべて見えなくなるのではなく(もしそうしてしまうと、そこに 何があったか分からなくなるので)、その最初の表示行だけが見えて、 残りの部分が隠れます。この表示行を「折り畳み行」と呼びます。 この行は、一般の行と区別できるように、その左端余白部に [+] マークが表示され、 背景が特別な色になって、行末に [...] のマークが表示されます。 なお、実際の表示は上例とは見た目が若干異なります。

 折り畳んだ部分は勿論、いつでも任意に「開く」(あるいは「展開する」)ことが できます。例えば、先ほど折り畳んだ部分を「開く」と次のようになります。

  「折り畳み」を使えば、いろいろと便利なことができます。
 例えば、見出しや表題だけを残して、その詳細部分を隠せば、
 全体の構成を容易に見渡せるようになります。また、変更の
 無い行だけを折り畳めば、変更の有る行が浮き彫りになります。
 また、C言語系やHTMLのテキストでは、その構文構造に
 基づいて折り畳むと、効率的な編集作業が行なえます。
  そのほかにも、折り畳みの用途はいろいろとあります。
 折り畳み部を開いた時には、「折り目」が付いた状態になっています。 その折り目は、上例のように左端の余白部に表示されます(但し、これは実際の表示とは若干異なります)。 この部分をマウスでクリックすると、再度「閉じる」(前回と同様に折り畳む)ことが できます。ちなみに、「折り畳み行」の左端の余白部( [+] マーク上)をクリックすると、 その折り畳み部を「開く」ことができます。

 本エディタでは、折り畳まないで、「折り目を付ける」こともできます。 その場合、必要な時に、その折り目に沿って「閉じる(折り畳みなおす)」ことができます。

 折り畳みは「解除」することもできます。その場合、折り目が消えます。 この状態になると「閉じる」ことはできません。

● 折り畳み形態

 本エディタでは、折り畳みは、論理行単位に なります。つまり、表示行の途中の桁や折り返し行を境目にした折り畳みはできません。 また、折り畳みは、その対象範囲に表示行が2行以上ないと行なわれません。

 「折り畳み行」の表示形態は前述の通りですが、これらは、環境設定の補助表示記号表示である程度カスタマイズできます。

 折り畳みは、何重にも可能です。つまり、「折り畳み行」を含む行範囲を、さらに 折り畳むこともできます。これによって、折り畳みの階層が形成されます。 折り畳み部は、基本的に「入れ子」構造になります。

 折り畳みの「深さ」とは、その折り畳みの階層の深さのことです。 この「深さ」は「レベル」という場合もあります。 ちなみに、この深さには限度があります(現状最大 255 まで)。

● 折り畳みの展開状態(折り目)表示

 折り畳みを展開すると、前述のように、その折り目を示す線が表示されます。 この縦線の太さは、展開された折り畳みの深さに比例します。 また、短い横線は、その境目を示します。以下にその例を示します(但し、実際の表示は これとは若干異なります)。

  XMLのタグ構造に基づいて折り畳む:
  ≪ 概説 ≫
  ・折り畳みは、XML宣言、処理命令、文書型宣言、開始タグ〜終了タグ、
     空要素タグ、コメント、CDATAセクション、の各範囲が対象
    但し、その対象範囲が1行内に収まっている場合は、折り畳まない
  ・開始タグと終了タグはペアで、入れ子になっているのが前提
  ・本処理は、構文解析のみで、意味解析は行なわない
この展開状態線を見ると、1〜6行目の折り目の中に、3〜5行目の折り目があることが 分かります。

 この折り目を示す展開状態線は、編集テキストの左端の余白部に表示されます。 その縦線の太さは、この余白部の幅以上にはなりません。 この線を表示するか否か、線色、余白部の幅は、環境設定で 調整できます。

● 本折り畳み機能の特徴

 折り畳み機能を持つテキストエディタは、いろいろありますが、今のところ、 フリーで日本製のものは、本エディタ以外にはないようです。また、有料であっても、 スクリプトで折り畳みを自由に操作できるものは少ないようです。

 折り畳み機能は、アウトライン機能と一体になっている場合が多いようですが、 本エディタでは、この両機能は、独立しています。 そのため、アウトラインの構成に沿った折り畳みだけでなく、 さまざまな基準で折り畳みができます。

 例えば、C言語系のプログラムのテキストを編集する場合、その構文構成に基づいた 折り畳みだけでなく、変更のある行やマーク行を抽出する折り畳み方や、任意範囲の 折り畳み等もできるようになっています。

 そのほかにもいろいろな折り畳み方が、予め用意されています。 それらはすべてスクリプトで実現されています。 本エディタのスクリプトは、折り畳みをかなり自由に操作できます。 そのため、希望の折り畳み方が、予め用意された中になくても、 新たにスクリプトを作成(あるいは、既存のスクリプトを変更)すれば実現できる 場合も少なくありません。

 但し、本エディタの折り畳み機能にも弱点はあります。 現状、「元に戻す(Undo)」と「やり直り(Redo)」の 対象に、折り畳み操作が含まれていません。また、折り畳み状態は、そのテキストの ファイル自身にも、セッションにも、保存されません。 また、テキストを変更した時、その関連部分の折り畳みが、所定の折り畳み方(アウトライン 構成に基づいた折り畳み方など)に従って、自動的に補正される訳でもありません。 例えば、「階層付きテキスト」を、その構造に応じて折り畳んでいても、 そのテキストを変更すると、期待通りの折り畳みにならないところがでてきます。 それを補正するには、その折り畳み方にするスクリプトを再度実行する必要があります。

 ところで、仮にこの自動補正をエディタ内部の機能として組み込んだ場合には、 それなりのオーバーヘッドが発生します。つまり、キー操作が若干重くなります。 また、その折り畳み方しかできなくなります。 本エディタでは、この負荷が無いので、折り畳みのあるテキストを編集する時も、 キー操作は軽快です。また、この制約がないので、いろいろな折り畳み方ができます。

■ 折り畳みの基本操作

 折り畳み関連の操作には、いろいろありますが、基本的なものは、以下のどれかの 形態で実行します。

 折り畳み関連の基本操作は、本エディタの編集コマンドとして組み込まれています。 これらは、上記のコンテキストメニューから実行できるので、 初期環境では、メインメニューやキー機能には、割り当てられていません。 しかし、これらは勿論、環境設定で、任意に割り当てできます。

 以下に、編集コマンドとして組み込まれている折り畳み関連の機能を説明します。 なお、下記の機能名は、環境設定でのデフォールトの機能名で、コンテキストメニューに 表示される項目名と異なる場合があります。但し、その対応は容易に推測できます。

●選択行を折り畳む

選択範囲を含む最小の論理行範囲を折り畳みます。 但し、その対象範囲内の表示行数が1行以下の場合は、何もしません。

●選択行に折目を付ける

選択範囲を含む最小の論理行範囲に折り目を付けます。 但し、その対象範囲内の表示行数が1行以下の場合は、何もしません。

●現折り畳みを1段開く

選択範囲が無い時は、現カーソル行が折り畳み行なら、その最外段を開きます。 選択範囲が有る時は、その行範囲内に含まれる各折り畳み行の最外段を開きます。

●現折り畳みを全段開く

選択範囲が無い時は、現カーソル行が折り畳み行なら、その行の全段を開きます。 選択範囲が有る時は、その行範囲内に含まれる各折り畳み行の全段を開きます。

●全折り畳みを全段開く

現編集テキスト内の全折り畳みを全て開きます。

●現折り目を1段閉じる

選択範囲が無い時は、現カーソル行が折り畳み展開部上なら、 その行を含む最内段を閉じます。
選択範囲が有る時は、その行範囲内に完全に含まれている各折り畳み展開部の最内段を閉じます。 ちなみに、その行範囲内に1部分しか含まれていない折り畳み展開部は、閉じる対象にはなりません。

●現折り目を全段閉じる

選択範囲が無い時は、現カーソル行が折り畳み展開部上なら、 その行を含む全段を閉じます。
選択範囲が有る時は、その行範囲内に完全に含まれている各折り畳み展開部の全段を閉じます。 ちなみに、その行範囲内に1部分しか含まれていない折り畳み展開部は、閉じる対象にはなりません。

●全折り目を全段閉じる

現編集テキスト内の全折り畳み展開部を全て閉じます。

●現折り畳みを1段解除

選択範囲が無い時は、現カーソル行が折り畳み行なら、その最外段の折り畳みを解除し、 さもなくて、現カーソル行が折り畳み展開部上なら、その行を含む最内段の折り目を消します。
選択範囲が有る時は、その行範囲内に含まれる各折り畳み行の最外段の折り畳みを解除し、 また、その行範囲内に完全に含まれている各折り畳み展開部の最内段の折り目を消します。 ちなみに、その行範囲内に1部分しか含まれていない折り畳み展開部は、解除の対象にはなりません。

●現折り畳みを全段解除

選択範囲が無い時は、現カーソル行が折り畳み行なら、その全段の折り畳みを解除し、 さもなくて、現カーソル行が折り畳み展開部上なら、その行を含む全段の折り目を消します。
選択範囲が有る時は、その行範囲内に含まれる各折り畳み行の全段の折り畳みを解除し、 また、その行範囲内に完全に含まれている各折り畳み展開部の全段の折り目と折り畳みを解除します。 ちなみに、その行範囲内に1部分しか含まれていない折り畳み展開部は、解除の対象にはなりません。

●全折り畳みを全段解除

現編集テキスト内の全折り畳みと全折り目を全段解除します。

●現折り畳みを1段反転

選択範囲が無い時は、現カーソル行が折り畳みの開閉部上にあれば、 その状態を1段だけ反転します。
選択範囲が有る時は、その行範囲内に完全に含まれている各折り畳みの開閉部の 状態を1段だけ反転します。ちなみに、その行範囲内に1部分しか含まれていない 折り畳み展開部は、反転の対象にはなりません。

●現折り畳みを全段反転

選択範囲が無い時は、現カーソル行が折り畳みの開閉部上にあれば、 その全段の状態を反転します。
選択範囲が有る時は、その行範囲内に完全に含まれている各折り畳みの開閉部の 全段の状態を反転します。ちなみに、その行範囲内に1部分しか含まれていない 折り畳み展開部は、反転の対象にはなりません。

●折り畳み展開部を選択

現カーソル行を含む折り畳み展開部の最内の行範囲を選択します。

●次の折り目へ

カーソルを、現在位置から、次の折り畳み行、または、折り畳み展開部の 先頭行の行頭へ移動します。

●前の折り目へ

カーソルを、現在位置から、前の折り畳み行、または、折り畳み展開部の 先頭行の行頭へ移動します。

■ スクリプトによる折り畳み操作

 本エディタでは、折り畳みを扱うためのスクリプト関数が充実しています (例えば、.MakeFold() 等)。 これらを使って、いろいろな折り畳み方を行なうスクリプトを作成することができます。 以下は、標準で搭載されている折り畳み関連のスクリプトです。

機能名ファイル名機能説明
変更行抽出 FoldUnchanged.mc 変更の無い行を折り畳んで、変更の有る行を抽出する
表題行抽出 FoldByTitle.mc 特定の正規表現に一致する表題行を抽出する
マーク行抽出 FoldByMarkLn.mc マーク行を表題にして折り畳む
C言語系構造抽出 FoldByCSyntax.mc C 言語系の構文構造に基づいて折り畳む
HTML構造抽出 FoldByHtmlTag.mc HTML のタグ構造に基づいて折り畳む
XML構造抽出 FoldByXmlTag.mc XML のタグ構造に基づいて折り畳む
階層文書構造抽出 FoldByDotLv.mc 階層付きテキストをその構造に基づいて折り畳む
字下げ階層抽出 FoldByIndent.mc 各論理行頭の字下げの深さが示す構造に応じて折り畳む
空行まとめ FoldByBlankLn.mc 空行の連続区間と非空行の連続区間をそれぞれまとめて折り畳む

 これらの折り畳み操作は、初期環境では、メインメニューの「編集」下の「折り畳み」内から 実行できます。

 なお、これらのスクリプトは、ソースプログラムで提供されています。 どれも、かなり詳しいコメントが入っているので、 プログラムの詳細な機能を確認したり、別途変更して使う際には、非常に参考になります。

● 外見行と実質行

 スクリプトで折り畳みを扱う場合、大して難しいことではありませんが、「外見行」と「実質行」と いう概念を認識しておく必要があります。

 「外見行」とは、編集ウィンドウに表示される外見上の行(つまり、隠れていない行)のことです。 「実質行」とは、編集テキスト内にある実際の行のことです。 折り畳みがなければ、どの実質行も、外見行になりますが、 折り畳みがあれば、その部分の実質行は最初の表示行を除いて、外見行になりません。

 編集行には、「表示行」と「論理行」の区別がありますが、 スクリプトで「表示行」を指定する場合(例えば、'VLi 等)、 それは、実質行の表示行になります。 また、スクリプトで「論理行」を指定する場合(例えば、'LLi 等)、 それは、実質行の論理行になります。 一方、スクリプトで外見行を指定する場合(例えば、'FLi 等)、 これはあくまで外見上の行なので、 表示上の行がその単位になります。

● スクリプト例

 最後に、折り畳みを行なう簡単なスクリプトの例を示します。 このスクリプトは、編集テキスト内に、表題行(ここでは、■◆● 等の記号で始まる行)が 幾つかある場合、各表題行で区切られる行範囲を折り畳みます。つまり、 このスクリプトを実行すると、表題行だけが残り、他の行は隠れます。

///  特定の正規表現に一致する行を表題にして折り畳む  ///

    // 正規表現の指定(任意に変更可)
    // 以下は、行頭に(空白を除いて)四角や丸の記号がある行を表題にする例
    Regex = $"^[ \t ]*[■□◆◇●○◎・\.]";

    'AddScope( ::Apsaly );
    SetWindowCond( 4, 0 );          // 折り畳み中、一時的に画面更新禁止
    CurVLi = 'VLi;                  // 現カーソル行の表示行を記憶
    CurSLn = 'SLn;                  // 現カーソル行の画面上の相対行を記憶
    MoveTo( 'TextTop );             // カーソルをテキストの先頭へ移動
    ResetFold( MaxLineNo(2) );      // 全折り畳みを解除
    MaxFLn = MaxLineNo(2);          // 全外見行数(全折り畳み解除時)

    SetFindCond( Regex, "RGE" );    // 検索する正規表現を設定
    CurFLi = 0;                     // 折り目を付ける範囲の先頭の外見行
    while( FindFwd() >= 0 )      // 順方向に検索を繰り返す
    {
        // 正規表現に一致した行の直前までの行範囲に折り目を付ける
        NewFLi = EvalTxpos( -GetSelSize()'DCn, 0'FLn );
        FLn = NewFLi - CurFLi;
        if( FLn >= 2 )
        {
            PresetFold( CurFLi'FLi, FLn );
            MoveTo( NewFLi'FLi, 'LineEnd );
        }
        CurFLi = NewFLi;    // 次に折り目を付ける範囲の先頭行
    }

    // 最後に残った範囲に折り目を付ける
    FLn = MaxFLn - CurFLi;
    if( FLn >= 2 )
        PresetFold( CurFLi'FLi, FLn );

    // 全箇所の折り目を閉じる
    CloseFold( 0'FLi, MaxFLn );

    // カーソルを元の位置に対応する外見行へ移動
    MoveTo( CurVLi'VLi, 0'FLn, 0'Column, CurSLn'SLn );

    // 折り畳み処理結果の画面に表示更新
    //SetWindowCond( 4, 1 );
    //( これは実行していなくても、スクリプト終了時に自動で実行される )

■ 注意事項