第2章 小物マクロ詳説

 理論ばかり追いかけていると、書くほうも読むほうも飽きてきます。即戦力になる小物マクロを通して、理解を深めてください。実戦では、

 を照して処理を分岐することが多いので、最初の3つのマクロは読み飛ばさないでください。

ページングモード切替えをスキップ


======== paging.def by y.mikome ========
                (modified by I.KAMIMURA)
【ページングモード切替えをスキップ】

* M
80 ^@   "Paging Mode"

        ?.
        (cm-30 && mp%2)? #@             ; 不連続実行かつ[C]なら余分に切り替え
        #@                              ; ページングモード切替え(cm=30)
*
======== End of paging.def ========

機能

 ページングモードの切替えを状況におうじてスキップします。

解説

 VZにはページングモードという概念があって、同じスクロールキーを押してもモードによって動作がことなります。現在どのモードにいるかは、ステータスライン(画面最上行)の左端に示され、マクロではシステム変数mpを参照して知ることができます。

ページングモードステータスラインmp
表示ページングモード[P]0
論理ページングモード[C]1
文字列検索モード[S]2

 モード切替えの順序は固定しているので、不要なモードを通過しなければなりません。そのわずか1打鍵をわずらわしく感じる人が多いのではないでしょうか。このマクロは、[C]モードで一度でもほかの操作(スクロールも含む)をしたあと[CTRL]+[@]を押すと、[S]をスキップして[P]に戻ります。

 連続してマクロキーを押しているかどうかは、cm(直前に実行したエデットコマンド番号)で判断します。「#@」あるいは「#30」を実行した直後はcm=30になります。「cm-30」が0でない(真)ならば、連続実行でないとわかります。「cm-30」は「cm!=30」でも同じ結果を得られますが、短く記述できるほうを選びました。

 現在のモードが[C]かどうかは、素直に「mp==1」としてもかまいませんが、mpが奇数であることに着目して、「mp%2」(mpを2で割った余り)とすればこれまた短く記述できます。

  不連続実行かつ[C]ならば、「#@」を1回よぶんに実行して、[P]まで戻ってしまうわけです。

 [C]は、空行の直後かつ行頭にある「*」「/」「;」「^L(制御コード)」を区切りとしてスクロールする(タイトルサーチ)少々特殊なモードですが、ふだんこのモードでVZを使っている人もいるようです。[C]に直接戻るマクロも紹介しておきます。

【論理ページングモード愛好者向け】


80 ^@   "Paging Mode"
        ?.
        (cm-30 && mp>1)? #@          ; 不連続実行かつ[S]なら余分に切り替え
        #@                              ; ページングモード切替え(cm=30)

 「mp==2」でなく「mp>1」がうまい書き方です。

 さて、筆者自身はというと、ふだんは[P]で使っており、[C]は滅多に使いません。絶対に使わないというのであれば、

Et+                       ;タイトルサーチ

 を「Et-」にすればよいのですが……。

 そこで、切り替え順序を[P]→[S]→[C]→[P]に変更して、かつ不連続実行の場合には[C]をスキップするマクロを作ってみました。

【論理ページングモード迫害者向け】


80 ^@   "Paging Mode"
        ?.
        (mp)?? { #F #e #m #@ }          ; [P]なら検索文字列を設定
        (cm-30 && mp>1) #@ ?. #@     ; 不連続実行かつ[S]なら中断

 あまりスマートではありません。

 VZの仕様で、起動後一度も検索機能を使っていない場合、または【検索文字列】の1行ウィンドウをキャンセルしたあとは、何度[CTRL]+[@]を押しても[S]はなりません。そこで、強制的に検索文字列を再設定するようにしました。

 モードの変化を追ってみましょう。

([P]の場合)
                     #F #e #m                 #@ 3回
        [P] (0)        →      [S] (2)        →      [S] (2)
([C][S]の場合)
                        #@                      #@
        [C] (1)        →      [S] (2)        →      [P] (0)
        [S] (2)        →      [P] (0)        →      [C] (1)

 これで結果的に[P]→[S]→[C]→[P]が実現するわけです。

画面分割モード切替えをスキップ


======== split.def by y.mikome ========
               (modified by I.KAMIMURA)
【画面分割モード切替えをスキップ】

* M
80 ^KS  "Split Window"
        ?.
        (cm-64 && wt)? wt4,             ; 不連続実行かつ画面分割時なら縦分割
        #Y                              ; 分割モード切替え (cm=64)
*
======== End of split.def ========

機能

 画面分割モードの切替えを状況におうじてスキップします。

解説

 前述の「ページングモード切替えをスキップ」と同じ発想です。

 画面分割モードはシステム変数wtで知ることができます。

画面分割モードwt
全画面0
横分割の上2
横分割の下3
縦分割の左4
縦分割の右5

 「wt4,」は「wt=4,」から「=」を取り払っただけです。変数と数値のあいだの「=」は省略しても支障ありません。「wt4,」で画面分割モードを強制的に縦分割にします。……と言いたいところですが、すぐには画面に反映されません。[ESC]@「マクロ文の実行」で「wt4」を入力してみればわかりますが、縦分割になったような、ならないような乱れた画面になります。最後に「#Y」を実行するので、それにともなって画面の書き換えがおこなわれます。mpとちがって、画面分割モードの切替えでは画面全体がちらつくので、こうした手法を使っていますが、本来wtは直接いじってはいけないシステム変数です。

 さて、「ページングモード」と同様、切り替え順序を「全画面」→「縦分割」→「横分割」→「全画面」に変更して、かつ不連続実行の場合には「横分割」をスキップするマクロを紹介しましょう。


80 ^KS  "Split Window"
        ?.
        (wt&2)? wt4, ? { (cm-64 && wt)?? wt!, } #Y

 wtの値は複雑です。wtが2か3(横分割)の場合にだけ「wt4,」に分岐させなくてはなりません。素直に書くと「(wt>0 && wt<4)」(0より大きい、かつ4より小さい)ですが、ビット演算を使えば「(wt&2)」と短く記述できます。

 「wt!,」は苦心の策です。wtが0なら1になり、0以外なら0になります。

 モードの変化を追ってみます。

                        wt!,                    #Y
        全画面 (0)      →      ?画面 (1)      →      縦分割(4)
        縦分割 (4,5)    →      全画面 (0)      →      横分割(2)

 これで結果的に「全画面」→「縦分割」→「横分割」→「全画面」が実現するわけです。wtが1になる危ない場面(該当するモードがない)もありますが、最後の「#Y」で正常化されます。

インデント削除


======== delind.def by I.KAMIMURA ========
【インデント削除】

* M
80 ^G   "Delete Indent"
        (ct>1) #g ?.                 ; #g…1文字削除。改行以外なら終了
        (ct==2)? #t                     ; タブ・スペースならさらに削除

*
======== End of delind.def ========

機能

解説

 文書を整形するマクロはいろいろありますが、完璧に思い通りになるとは限らないので、手作業で整形することがあります。いったん桁折りした文書の改行を削除して、行を連結するときなど、このマクロは威力を発揮します。

 改行コードの文字タイプ(ct)は1なので、改行以外かどうかを判定するには「(ct>1)」という条件式を使います。「(ct>1) #g ?.」は裏技的な書き方です。基本通り「(式)」と「?」を隣り合わせで使うと、次のように長くなります。


46 ^G   "Delete Indent"
        (ct>1)? { #g. }                      ; 改行以外なら1文字削除して終了
        #g                              ; 改行を1文字削除
        (ct==2)? #t                     ; タブ・スペースならさらに削除

 改行であってもなくても、最初に#g(1文字削除)を実行するのですから、削除してから分岐すれば、短く記述できます。あいだにあるのが#gだけなので、(ct>1)を評価した結果(内部レジスタの値)は変わりませんが、あまり複雑な処理をはさむと、思い通りに分岐しないので気をつけてください。

ブロック再現


======== reblock.def by I.KAMIMURA ========
【ブロック再現】

* M
80 ^B   "reblock"
        ?.
        (ks&1)?? { #b. }            ; 通常実行なら #b のみ(基本機能)
        &s                          ; 画面表示の停止
        #B #B mb+,                      ; シフト実行の場合
        &d                          ; 全画面を再表示

*
======== End of reblock.def ========


機能

 直前のブロックを再現します。たとえば、ブロックをペーストした直後には、そのブロックの範囲を再現するので、ひきつづきその範囲で何か処理をおこないたいときに便利です。

解説

 ksはシフトキーフラグを示すシステム変数です。ビット演算については別に章をもうけて解説しますので、いまは「ks&1」で[SHIFT]の押し下げ状態を判断できると丸暗記してください。

 システム変数mbには現在のブロックモードをあらわす数値がはいっており、[ESC]@「マクロ文の実行」で「mb」を入力して確認できます。いろいろな状態で試してみてください。

ブロックモードmb
非ブロックモード0
行境界のブロックモード1
文字境界のブロックモード2
矩形ブロックモード4

 mbに数値を代入すると、ブロックモードを強制変更できます。非ブロックモードで数値を代入すると、潜在しているブロックの範囲を顕在化します。行境界のブロックを^Bでキャンセルしたあと、「マクロ文の実行」で「mb=1」と入力してみてください。

 ただし、文字境界のブロックをmb+,(mb=1,)で再現しようとすると、範囲がおかしくなります。mbを調べると、当然ながら1(行境界)になっています。そのまま^Y,[F08]を押すと、ちゃんと文字境界で削除されるものの、行境界のブロックとしてテキストスタックに保存されるので、ペーストで奇妙な動作をします(行の途中にカーソルがあっても、行頭にペーストされる)。このマクロでは、#B(ブロックの先頭/最後)を2度実行して、ブロックの範囲を1往復することによって、範囲を明示しています。mbが1になるのは変わりませんが、テキストスタックに保存した時点で2(文字境界)に正常化されます。

 万全を期すなら、次のようになります。


80 ^B   "reblock"
        ?.
        (ks&1)?? { #b. }            ; 通常実行なら #b のみ(基本機能)
        &s                          ; 画面表示の停止
        #B n=ln,                        ; ブロックの反対側の行番号を n に代入
        #B (ln==n) mb+ ? mb=2,          ; 同じ行なら文字境界のブロックにする
        &d                          ; 全画面を再表示

 ちなみに、非ブロックモードでないと誤動作するマクロでは、最初に「(mb)? #b」(ブロックモードなら解除)を実行しておく必要がありますが、代わりに「mb-,」を使えば短く記述できます。

拡張・1語大/小文字変換


======== wordcase.def by I.KAMIMURA ========
【拡張・1語大/小文字変換】

* M
80 ^QU  "Word Case"

        (ct-6)? #a                      ; 英数字でなければ1語左へ
        (ks&1)? { (cd>='a')? #23 #d }    ; シフト実行なら先頭1字を大文字に
        #23 #f                          ; 1語大/小文字変換して1語右へ

*
======== End of wordcase.def ========

機能

解説

 VZの基本機能^QU「1語大/小文字変換」では、カーソル位置は動かないので、欧文中の単語を連続して変換したいときに不便です。変換したあと1語右へ移動するだけなら「#23 #f」でよいのですが、ついでに再変換(後戻り変換)と頭文字変換の機能を付加しました。

 まず、ct(カーソル位置の文字タイプ)が英数字かどうか調べます。「ct-6」はctが6と等しくなければ真(非ゼロ)で、「ct!=6」と同じ意味になります。わずか1バイトですが、短く記述できます。カーソル位置が英数字でなければ、「#a」で1語左へ移動します。

 「(cd>='a')?」は「(cd>=$61)?」と同じ意味です。一重引用符でくくられた文字は、VZの内部処理では文字コード(数値)として扱われるのです。「$」を前置すると16進数で表記したことになります。10進数表記なら「(cd>=97)」です(巻末の文字コード表を参照してください)。'亜'のように全角文字を書くこともできます。マクロの可読性から言うと、文字そのものを書く方がよいでしょう。

 さて、「(cd>='a')?」で何を判断しているのでしょうか。cdが'a'以上、すなわち'a'~'z'の小文字であるならば「#23」で大文字に変換します。そして、「#d」でカーソルを1字進めて、「#23」で2番目以降を小文字に変換します。

        editfile

 #23 で大文字に変換すると、

        EDITFILE

 カーソルを1字進めて、#23 で2番目以降を小文字に変換すると、

        Editfile

 で首尾よく頭文字変換が実現します。

テキスト破棄



======== deltxt.def by I.KAMIMURA ========
【テキスト破棄】

* M
80 [ESC]C "Delete Text"
        ?.
        (ks&1)?? { #C. }            ; 通常実行なら #C…ファイルのクローズ
        #E "del " #P #m             ; #E…[ESC]E  #P…ファイル名の複写
        #[ mr-, #C                      ; #[…[ESC]  mr-,…強制的に未修正にする
        &m(px)                          ; px…コマンドラインヒストリーバッファ

*
======== End of deltxt.def ========

機能

解説

 ファイラーを眺めていて、内容が不明のファイルがあったので、オープンしてみる。不要なファイルだったので、削除したい。あなたなら、どうしますか。ファイルをクローズして、ファイラーを起動して、削除したいファイル名をスペースで反転させて、「Exec」「D削除」でコマンドラインに落として、リターン……。なんと手間のかかることでしょう。

 このマクロを使えば、簡単にファイルを破棄することができます。ファイラーを経由するやり方では、目的のファイルを特定するのは困難なので、^QN「ファイル名の複写」を利用しています。「#E」でコマンドラインにおりて、「del」と書き込み、「#P」でファイル名を複写。「#m」(リターン)で実行。「#[」(エスケープ)で編集画面にもどり、「#C」でクローズします。最後の「&m(px)」はなくてもかまいませんが、メッセージの内容をポインタ変数で指定する手法が参考になるでしょう。

 コマンドラインで^QN「ファイル名の複写」が有効なのが意外かもしれません。^QI「文字列の複写」も有効なので、編集テキストから文字列を引用したい場合には、文字境界のブロックモードで範囲指定して、「#G」で検索文字列に取得しておくとよいでしょう。

カーソル移動やブロックの削除・挿入を和文化


======== wabun.def by I.KAMIMURA ========
【カーソル移動やブロックの削除・挿入を和文化】

* M

32 ^QS "Top of Sentence"              ;(センテンス先頭)左側の句点へ
        ?. &s
        #s                              ; [←]
        c-, &01 &d #]                   ; 左側の句点の次へ

33 ^QD "End of Sentence"              ;(センテンス後尾)右側の句点へ
        ?. &s
        c+, &01 &d #]                   ; 右側の句点の次へ

34 ^A   "Left Knot"                   ;(1文節左)左側の句読点へ
        ?.
        (ct==6)? { #a. }                ; 英数字なら通常の1語左
        &s #s c=#s, &02 &d #]               ; 左側の句読点の次へ

35 ^F   "Right Knot"                  ;(1文節右)右側の句読点へ
        ?.
        (ct==6)? { #f. }                ; 英数字なら通常の1語右
        &s c=#d, &02 &d #]          ; 右側の句読点の次へ

36 ^Y   "Cut Sentence"                        ;(センテンス削除)
        ?.
        (ks&1)?? { #y. }            ; 通常の行・ブロック削除
        &s                          ; 以下、シフト実行の場合
        c-, &01                             ; 左側の句点の次へ
        #b
        c+, &01                             ; 右側の句点の次へ
        #y &d #]                    ; #y…行・ブロック削除

37 ^J   "Insert Sentence"             ;(センテンス挿入)
        ?.
        (ks&1)?? { #i. }            ; 通常の行・ブロック挿入
        &s                          ; 以下、シフト実行の場合
        c-, &01                             ; 左側の句点の次へ
        #i &d #]                    ; #i…行・ブロック挿入

1:      ;---句点の次へ---
        &o(#s+c)                    ; [←]または[→]
        (r||ct<2)?.                  ; 行き止まり・改行か?
        &f(cd,"。. ")(r<0)? >^    ; 句点・全角スペースまで繰り返し
        #d                              ; [→]

2:      ;---句読点の次へ---
        &o(c)                               ; [←]または[→]
        (r||ct<2)?.                  ; 行き止まり・改行か?
        &f(cd,"。、., ")(r<0)? >^        ; 句読点・全角スペースまで繰り返し
        #d                              ; [→]

*
======== End of wabun.def ========


解説

 シフト実行すると、センテンスを削除します。カーソルはセンテンスのどこにあってもかまいません。シフト実行でなければ、通常の行・ブロック削除をおこないます。

 シフト実行すると、現在カーソルのあるセンテンスの先頭にインサートします。「Cut Sentence」と対で使います。シフト実行でなければ、通常の行・ブロック挿入をおこないます。

 ※ 「Cut Sentence」「Insert Sentence」は範囲指定や挿入位置を自動化しているだけですので、通常の^J,[F09]で挿入してもかまいません。

 ※ 「Right Knot」「Left Knot」を英数字の上で実行すると、通常の「1語右」「1語左」になります。

解説

 VZを日本語ワープロとして使うとき最初に気になるのが、カーソルの移動ではないでしょうか。画面の右端にカーソルを移動するつもりで[CTRL]+[→]を押すと、論理行末に移動してしまいます。そのためVZ.DEFには^Q[「表示行頭」、^Q]「表示行末」といったマクロが用意されていますが、そもそも表示行頭や表示行末に移動するのは何のためでしょうか。たいていは、そこからさらに[]や[]で編集したい箇所へ移動するはずです。それならば、最初から一発で目的の箇所へ移動できたほうがいい。句読点を区切りにカーソルを移動したり文字列を削除したりするのがこのマクロの狙いです。

 動作原理としては、1文字ずつカーソルを移動しながら、cd(カーソル位置の文字コード)が句読点であるかどうか調べています。素直に書くと、


        (cd=='。'||cd=='、'||cd=='.'||cd==','||cd=' ')?

 という長い条件式になりますが、便利なシステム関数が用意されています。


        &f(cd,"。、., ")(r>=0)?

 cdと「。、., 」を比較して一致するものがあれば、r(返り値)に何番目に見つかったが入ります。「。」なら0、「,」なら3、一致するものがなければ-1です。

 まず「Left Knot」「Right Knot」を解説します。

 「c=#s,」「c=#d,」という書き方に驚かれたかもしれません。ローカルマクロ「2:」の「&o(c)」にわたして「#s」または「#d」を実行させることによって、同じような処理を重複して記述せずに済みます。ちなみに、「#s」(#07),「#d」(#08)などのコマンドを数値で表現すると、機能番号+256で263,264になります。したがって「c=263,」「c=264,」と書き換えても同じ動作をします。この256という数値はいったいどこからきているのかと言うと、0~256は半角の英数字・カタカナなどに割り当てられているのです(巻末文字コード表を参照)。

 「(r||ct<2)?」は正確には「(r==-1||ct<2)?」と書きます。「#s」または「#d」を実行して、進むことができた場合には0、行き止まりだった場合(ファイルの先頭や最後)には-1がr(返り値)に入ります。0と-1の2通りしかないので、(r||ct<2)?と略記できるのです。「ct<2」はct(文字タイプ)が0(EOF)か1(改行)かを判定しています。

 「Top of Sentence」「End of Sentence」も動作原理は同じですが、「#s」と「#d」の実行方法を変えてみました。ローカルマクロ「1:」の「&o(#s+c)」に「c-,」(c=0,)あるいは「c+,」(c=1,)を渡します。「&o(263+c)」と書くこともできます。

 「Cut Sentence」は、まずセンテンスの先頭に移動して、そこからブロックモードで次のセンテンスの先頭まで移動して、文字列を削除しています。「Insert Sentence」は、現在カーソルのあるセンテンスの先頭に移動して文字列を挿入しているだけです。

 画面表示の再開には完璧を期しました。Du+,(カーソル行アンダーラインON)の環境では、「&s」(Silent)のあと「&d」(Display)で再表示するだけでは、アンダーラインがあらわれません(カーソルを動かすとあらわれる)。そこで「#]」でカーソル行に活をいれてやるのが常套手段です。

参照画面スクロール


======== refwin.def by I.KAMIMURA ========
【参照画面スクロール】

* M
41 ^R   "Page Up"
        c=#r, >01                    ; #r…ページアップ

42 ^C   "Page Down"
        c=#c, >01                    ; #c…ページダウン

43 ^W   "Roll Up"
        c=#w, >01                    ; #w…ロールアップ

44 ^Z   "Roll Down"

        c=#z, >01                    ; #z…ロールダウン

1:      (wt)?? { &o(c). }           ; 全画面モード(wt=0)なら通常実行
        &s #W &o(c) #W &d           ; 参照画面をスクロール
        (ks&1)? &o(c)                   ; シフト実行なら編集画面もスクロール

*
======== End of refwin.def ========

機能

解説

 画面分割しているとき、参照画面だけをスクロールしたいことが結構あります。^KW,[F02]でウィンドウを切り替えて、スクロールして、編集画面に戻るといった作業を自動化するのが、このマクロです。

 動作原理としては、「#W」でウィンドウを切り替えて、スクロールして、再度ウィンドウを切り替えています。「c=#r,」「c=#c,」「c=#w,」「c=#z,」をローカルマクロ「1:」の「&o(c)」に渡すことによって、同じような処理を重複して記述せずに済みます。

 シフトキーを押しているかどうかは、「ks&1」で判定します。編集画面側でも「&o(c)」を実行すると、両画面がスクロールしたことになります。

 全画面モードの場合、wt(画面分割モード)は0なので、「&o(c)」を実行してすぐに終了します。

拡張・テキスト切替え


======== chgtxt.def by I.KAMIMURA ========
【拡張・テキスト切替え】

* M
80 [F03] "Change Text"
        ?.
        n=wn,                           ; 元テキスト番号を n に取得
        #T                              ;【編集テキスト】メニュー
:L      (s)? { &c >L }                   ; メニューにいるあいだキーを受け付ける
        &s                          ; 画面出力を停止
        (wn-n) #W ? &#T(n)          ; 参照画面(裏)に元テキスト n を配置
        (wt)? &#Y(wt) ?     #W              ; 画面分割ならテキスト配置を逆にする
        &d #]                               ; 全画面を再表示
*
======== End of chgtxt.def ========

機能

 最初に、編集中テキストの一覧が出ます。(これは基本機能)

【カーソルまたは英数字で選択】
  • 全画面モード ……選択したテキストを表に、元のテキストを裏に配置します。
  • 画面分割モード……選択したテキストを参照ウィンドウに配置します。
【元のテキストを選択、あるいは[ESC]でキャンセル】
  • 全画面モード ……何も起こりません。
  • 画面分割モード……テキストの配置を逆にします。

解説

 VZのテキスト切り替えには癖があります。あるテキストを編集している最中に、他のテキストを参照したくなって、【編集テキスト】のメニューで切り替えると、元のテキストに戻るために再度【編集テキスト】で切り替えなくてはなりません。それを避けるために、画面分割して他のテキストを参照している場合、参照テキストを切り替えたくなったら、^KW,[F02]「ウィンドウ切替え」で往復しなくてはなりませんが、これも面倒です。

 これらの問題点を改善するマクロを作ってみました。全画面モードでは、選択したテキストの裏側に元テキストを配置するので、^KW,[F02]ですぐに戻ることができます。画面分割モードでは、参照画面側を切り替えます。

 「n=wn,」で元テキスト番号を変数nに取得して、「#T」で【編集テキスト】のメニューをオープンします。「:L (s)? { &c >L }」は正確に書くと「:L (s==4)? { &c >L }」です。メニュー(s=4)にいるあいだ、「&c」でキー入力を受け付けて、ラベル「:L」に戻ります。テキストを選択するか、エスケープでキャンセルするかして、編集モード(s=0)に戻ると、「&s」以下の処理をおこないます。

 「(wn-n) #W ? &#T(n)」は素直に書くと、次のようになります。


        (wn-n)? { #W &#T(n) } ? #W  ; 参照画面(裏)に元テキスト n を配置

 参照画面(裏)に元テキストを配置するだけなら、「#W &#T(n)」でよいのですが、元テキストを選択したり、メニューをキャンセルした場合には、テキスト番号が同じ(wn-n=0)になるので、とりあえず#W(ウィンドウ切替え)のみ実行します。

 「(wt)? &#Y(wt)? #W」が曲者です。全画面モード(wt=0)なら「#W」を実行するのは簡単にわかるでしょうが、画面分割モード(wt!=0)での「&#Y(wt)」は少々複雑な動作をします。

画面分割モードwt
全画面0
横分割の上2
横分割の下3
縦分割の左4
縦分割の右5
&#Y(n)実行結果
&#Y(0)フル画面にする
&#Y(2)横分割にする
&#Y(4)縦分割にする

 wtには現在の画面分割の状態をあらわす数値がはいっており、[ESC]@「マクロ文の実行」で「wt」を入力して確認できます。いろいろな状態で試してみてください。「&#Y(n)」はnに数値(引数)をあたえて、画面分割の状態を変更する命令(コマンド関数)です。さて、このマクロの「&#Y(wt)」では、wtは3か5です。これは本来、不正な引数ですが、テキストの配置を逆にする効果があるのです。

テキスト連続クローズなど


======== closetxt.def by I.KAMIMURA ========
【テキスト連続クローズなど】

* M
51 ^KT  "Close Text"
        ?.
        n=wn,                           ; 元テキスト番号を n に取得
        (k=ks&1)? >B                     ; シフト実行か?/ k に取得
        (wt)?? { #T. }                  ; 全画面モードなら【編集テキスト】
        #W                              ; 画面分割モードならウィンドウ切替え
:A      (wn==n)?.                       ; 元テキストなら終了
        (wn<n)? n--,                 ; 元テキスト番号を調整
        #C (s==4)? &c                       ; ファイルのクローズ、問い合わせ
        &#T(n) &#Y(0)                   ; 元テキストに戻り、全画面モード
        (k)??.                          ; 通常実行なら終了
:B      #T (-1){ &c (s)?? >A }           ; メニューにいるあいだキーを受け付ける

*
======== End of closetxt.def ========


機能

【通常実行】

【シフト実行】(マクロ実行キーをシフトキーといっしょに押す)

解説

 全てのテキストをセーブの確認をしながらクローズするには、VZ標準の[ESC]X「全ファイルのクローズ」を使えばよいのですが、不要なテキストだけを連続してクローズしたい場合にはどうすればよいでしょうか。ひとつずつ【編集テキスト】のメニューから選択して、[ESC]Cでクローズするのは面倒です。そこで、連続してクローズするマクロを作ってみました。

 「n=wn,」で元テキスト番号を変数nに取得。「(k=ks&1)?」でシフトキーの押し下げ状態を判断して分岐します。ここでは「式の評価」と「変数kへの代入」を同時におこなう手法を使って、短く記述しています。素直に書くと、次のようになります。


        k=ks&1,
        (k)? >B


 「(wt)?? { #T. }」は「(wt==0)? { #T. }」と同じ意味です。全画面モード(wt=0)なら、【編集テキスト】メニューをだすだけで終了します。

 「:B #T(-1){&c(s)?? >A}」は【編集テキスト】のメニューからテキストを選択するか、エスケープでキャンセルするまで、キー入力を受け付けます。(-1){<statement>}は無限ループと呼ばれ、<statement>を無限に繰り返します。このマクロでは、編集モード(s=0)に戻ると、「(s)?? >A」でラベル「A:」へ脱出する仕組みになっています。参考のために無限ループを使いましたが、素直に書くと次のようになります。


:B      #T                              ;【編集テキスト】のメニュー(s=4)
:C      (s)? { &c >C } >A             ; メニューにいるあいだキーを受け付ける

 選択したテキスト番号(wn)が元テキスト番号(n)と同じなら、「(wn==n)?.」で終了します。これからクローズするテキスト番号が元テキスト番号より小さいなら、テキスト番号がずれるので、「(wn<n)? n--,」で、変数nに代入されている数値をひとつ減らします。「#C」(ファイルのクローズ)で、もし修正が加えられたテキストなら「~を出力しますか? (Y/N)」と問い合わせてきます。'Y'か'N'を選択するメニューモード(s=4)になるので、「(s==4)? &c」でキー入力(Y/N)を一回待ちます。未修正なら、問い合わせなしにクローズするので、「(s==4)」は偽となり、「&c」はスキップされます。ファイルをクローズして、セーブするかどうか問い合わせる場合には「#C (s==4)? &c」が定石なので、憶えておいてください。

 「&#T(n)」で元テキスト(n)に戻り、「&#Y(0)」で全画面モードにします。通常実行(k=0)なら「(k)??.」で終了。シフト実行なら、再び「#T」で【編集テキスト】のメニューをだして、選択をうながします。

ワンタッチセーブと別名セーブ


======== esc_s.def by I.KAMIMURA ========
【ワンタッチセーブと別名セーブ】

* M
80 [ESC]S @S "Save"
        ?.
        (ks&1)? { #J #P #m m=mr, }  ; シフト実行ならファイル名を pw へ
        (mb) #S ?.                      ; ブロックモードなら終了
        (ks&1)? {                   ; シフト実行なら別名でセーブ
                &b(3)                       ; ビープ音
                &l (r)?.            ; [CR]/[ESC] を待つ
                #82 #x &?(pw) #m    ; 元のファイル名にリネーム
                mr=m, #].               ; 修正フラグを元に戻す
        }
        #m                              ; 通常実行ならワンタッチセーブ
*
======== End of esc_s.def ========

機能

解説

 VZの基本機能「ファイルのセーブ」は次のような仕様になっています。

  1. 【出力ファイル】の1行ウィンドウに現在のファイル名があらわれる。そのままリターンを押すと、修正内容をセーブ。(修正フラグは消える)
  2. 【出力ファイル】の1行ウィンドウに現在のファイル名があらわれる。ファイル名を変更してリターンを押すと、そのファイル名でセーブして、編集中のテキストはリネームされる。(修正フラグは消える)
  3. ブロックモードの場合、【出力ファイル】の1行ウィンドウがあらわれて、入力したファイル名でブロックの範囲をセーブ。(修正フラグは変わらない)

 ちょっと奇妙なのが(2)で、[ESC]Pでファイル名を変更してセーブしたのと同じ結果になってしまいます。「テキストに変更を加える前の内容を念のため別名でセーブしておく」という使い方をする場合、元のファイル名に再度リネームする手間がかかります。テキスト全体をブロックで範囲指定して(3)を実行すればよいのでしょうが、元のカーソル位置に戻る操作も必要なので面倒です。

 そこで、「別名セーブ」したあと元のファイル名にもどるマクロを作ってみました。(1)で、いちいちリターンを押さなくてもいい「ワンタッチセーブ」機能も付加しました。

 まず、元のファイル名をどこかに保存しておかなくてなりません。このマクロでは、【行番号】の1行ウィンドウがpw(ワーク用ヒストリーバッファ)を利用することに着目しました。「#J」(^QJ)で【行番号】の1行ウィンドウをひらき、「#P」(^QN)で現在のファイル名を複写して、#m(リターン)でpwに渡すことができます。入力した文字列の先頭に数値があると、本当に行番号ジャンプするので気をつけてください。ファイル名ならその心配はありません。

 「(mb) #S ?.」は短縮した記述です。素直に書くと次のようになります。


        (mb)? { #S. }
        #S

 【出力ファイル】の1行ウィンドウにいるあいだ、システム関数「&l」で、リターンまたはエスケープが押されるまでキー入力を受け付けます。「&l」自体に返り値(r)はありませんが、エスケープで編集モードに戻ると、#Sが異常終了したものとして-1が返るので、「(r)?.」で中断できます。

 「#82」でリネーム用の1行ウィンドウをひらき、現在のファイル名を「#x」(「」)で消し、「&?(pw)」で元のファイル名を書き込みます。変数mに保存しておいた修正フラグを「mr=m,」で引き継ぎます。最後に「#]」を実行して、スタータスラインに表示されている修正フラグを更新すると同時に、「~はすでに存在します」というメッセージを消します。

行/ページ番号ジャンプ


======== pagejump.def by I.KAMIMURA ========
【行/ページ番号ジャンプ】

* M
45 ^QJ  "Jump to Line/Page"
        ?.
        (ks&1)? {                   ; ◆シフト実行
;       (Dn && Dl)? {                   ; ◇表示行モードかつ行番号表示ON
        &g("【ページ番号】")              ; 1行ウィンドウで数値入力をまつ
        (r<1)?.                              ; 正の数値以外なら終了
        &#J((r-1)*PG+1).            ; コマンド関数による指定行ジャンプ
        }
        #J                              ; #J(#45)…指定行番号ジャンプ

*
======== End of pagejump.def ========


機能

解説

 VZには最低限ながらページ番号を管理する機能が用意されています。表示行モード(Dn+)かつ行番号表示ON(Dl+)にすると、画面の左側に「ページ」と「ページごとの行番号」が表示されます。論理行モード(Dn-)でも行番号を表示することはできますが、テキスト全体の通し行番号のみとなります。

 オプション変数PG「表示行番号のページ数」はVZの標準設定では0になっており、そのままではページ数は表示されません。1ページあたりの行数を適当に設定しておいてください。PGは「カーソル行のページ番号」ではないので、気をつけてください。「表示行番号のページ数」という名称は誤解を招きそうです。

 ここで欲しくなるのが【ページ番号】によるジャンプです。^QJ「指定行ジャンプ」をシフト実行で使い分けて、キーアサインを節約しましょう。

 システム関数 &g("<title>") を利用して、数値の入力をうながします。

        ┌ 【ページ番号】 ┐
        │                │
        └────────┘

 どんな文字列が入力されたか、r(返り値)で判別できます。

数字数値
文字列0(ワーク用ヒストリーバッファに格納)
[Esc]でキャンセル-1
リターンのみ入力-2

 このマクロでは数値を求めているので、文字列や負の数値が入力されたら、「(r<1)?.」で終了させます。

 「&#J(n)」は行番号nへジャンプするコマンド関数です。「(r-1)*PG+1」は「(r-1)×PG+1」の意味で、入力された数値(r)を行番号に換算しています。

 表示行モードかつ行番号表示ONのとき自動的に【ページ番号】に切り替える方法を◇行に示しました。もう一歩すすめて、「シフト実行」または「表示行モードかつ行番号表示ON」で切り替える方法を次に示します。


48 ^QJ  "Jump to Line/Page"
        ?.
        (ks&1 || Dn && Dl)? {
        &g("【ページ番号】")              ; 1行ウィンドウで数値入力をまつ
        (r<1)?.                              ; 正の数値以外なら終了
        &#J((r-1)*PG+1).            ; コマンド関数による指定行ジャンプ
        }
        #J                              ; #J(#45)…指定行番号ジャンプ

 さて、「&#J(n)」と「#J」はどう使い分けるのでしょうか。たとえば、30行目にジャンプするには、それぞれ次のようになります。

        &#J(30)

        #J "30" #m

 後者は、「#J」でオープンした【行番号】の1行ウィンドウに「30」を入力してリターン(#m)でジャンプします。「30」のような定数ならこれでもよいのですが、演算をともなうと厄介です。



        #J &?("%d",(r-1)*PG+1) #m

 システム関数「&?」を利用して、数値を文字列に変換して出力しなくてはなりません。マクロ中では一般に「&#J(n)」を使うと憶えてください。「#J」は本来の行番号ジャンプよりも、1行ウィンドウの中で文字列を編集したり、ワーク用ヒストリーバッファ(pw)に文字列を渡したりする、裏技的な使い方をされることが多いようです。

拡張・編集のやり直し


======== esc_u.def by I.KAMIMURA ========
【拡張・編集のやり直し】

* M
7  [ESC]U @U "Undo Edit"
        ?. #]                           ; ステータスラインを再表示
        (mr==1)? { &a(37) (r<1)?. }      ; 修正フラグが立っていたら問い合わせ
        &s ky[, (Dn)? ld[, ? ln[,   ; 表示行番号/論理行番号
        mr-, #L #P #m                   ; 同じファイル名でロードし直す
        &#J(r]) ky], &d #]              ; 破棄前の行番号へジャンプ

*
======== End of esc_u.def ========


機能

解説

 VZの基本コマンドに^QL「行の回復」(機能番号#54)がありますが、このマクロはテキスト全体を編集前の状態に復元します。動作原理としては、現在編集中のテキストを破棄して、ディスクの元ファイルをあらためてオープンします。

 mr(修正フラグ)を調べる前に「#]」(ステータスラインを再表示)を実行しているのは何故でしょうか。未修正テキストで文字入力してみるとわかりますが、文字入力の直後には修正フラグは立ちません。他の行にカーソルを移動したり、編集テキストを切り替えたりすると、はじめて修正フラグが立ちます。「#]」を実行すると、それと同じ効果が得られるのです。

 テキストを破棄する前と同じ行に戻るには、行番号ジャンプを使いますが、論理行モードではln(論理行番号)を、表示行モードではld(表示行番号)を渡すようにしないと、ズレが生じます。カーソル位置を記憶するために行番号を使う場合は気をつけてください。通常はカーソル位置マーク(#1#4)を使えば問題ないのですが、このマクロではテキストを入れ替えてしまうので使えません。

 「mr-,」で修正フラグを強制的に0(未修正)にします。オリジナルでは修正フラグが立っていた場合、「~を出力しますか?(Y/N)」という問い合わせに'n'と答えて破棄しています。

(オリジナル)

7  [ESC]U @U "U 編集のやり直し"

        ?. #] (mr==1)?{ &a(37) (r<=0)?. }
        &s #L (s==4)?'n' #P #m &d

 あらかじめ「mr-,」で未修正にしておけば、「(s==4)? 'n'」を省略して短く記述できます。

 「#L」(クローズ・オープン)で【入力ファイル】の1行ウィンドウがあらわれます。いま破棄しつつあるファイルの名前を「#P」(ファイル名の複写)で書き込むという、いささかトリッキーな方法で、入れ替わりにディスクの元ファイルをオープンします。

 ファイルの先頭から行番号ジャンプすると、Evオプション(ジャンプ後カーソルを画面の中央へ)の設定によって、カーソルのY位置は最上行か中央かどちらかになります。これもできれば破棄前の状態に戻したいので、ky(Y位置)をいじります。このマクロではユーザー変数に数値を代入して受け渡す方法ではなく、マクロスタックに数値をプッシュ・ポップして受け渡す方法を使っています。若干短く記述できますし、ユーザー変数も汚さずに済みます。マクロスタックにプッシュした逆の順序で正確にポップしないと誤動作するので、慣れないうちはユーザー変数を使った方がよいでしょう。

(ユーザー変数を使った例)

7  [ESC]U @U "Undo Edit"
        ?. #]                           ; ステータスラインを再表示
        (mr==1)? { &a(37) (r<1)?. }      ; 修正フラグが立っていたら問い合わせ
        &s y=ky, (Dn)? n=ld, ? n=ln,        ; 表示行番号/論理行番号
        mr-, #L #P #m                   ; 同じファイル名でロードし直す
        &#J(n) ky=y, &d #]              ; 破棄前の行番号へジャンプ

 kyをいじったあとは「#]」を実行してやらないと、見かけのY位置と実際のY位置がずれて、一時的に画面がおかしくなります。

拡張・テキストの比較


======== kx.def by I.KAMIMURA ========
【拡張・テキストの比較】

* M
80 ^KX  "Compare Texts"
        ?.
        c=cm-69,                        ; c=0, なら連続実行
        k=ks&1,                             ; [SHIFT]押し下げ状態をkに保存
        (c)? { (k)?? { #69. } }         ; ◆通常実行(cm=69, になる)
        (c)? #< ? #>                      ; 最初…行頭(#<) 連続…行末(#>)
        (Dn)? n=ld, ? n=ln,             ; 表示行/論理行番号を n に代入
        #W                              ; 裏テキストへ
        (k)? &#J(n)                 ; シフト実行なら第 n 行へジャンプ
        (c)? #< ? #>                      ; 最初…行頭(#<) 連続…行末(#>)
        #W                              ; 表テキストへ
        #69                             ; テキストの比較(cm=69, になる)

*
======== End of kx.def ========


機能

解説

 基本機能の^KX「テキストの比較」では、表テキストと裏テキストのカーソル位置からただちに比較を開始するので、位置合わせをしておかなくてはなりません。それを自動化するのが、このマクロです。連続実行すると、次行に移動してから比較を開始するので、相違のある箇所を次々と確認することができます。

 連続実行かどうかを調べるにはcm(直前に実行したエデットコマンド番号)を参照します。「テキストの比較」(#69)を実行した直後にはcm=69,になります。cmは次々と変わっていくので、最初に「c=cm-69,」で変数cに保存しておきます。cが0なら連続実行で、cが0以外なら最初の実行です。なお、マクロの構造上、最後に「#69」がこない場合には、代わりに「cm=69,」と記述して、強引にcmを69に変更できます。

 最初の実行なら論理行頭へ(#<)、連続実行なら論理行末へ(#>)カーソルを移動します。表示行モード(Dn+)なら表示行番号(ld)を、論理行モード(Dn-)なら論理行番号(ln)を変数nに代入します。「#W」でウィンドウ切替え。シフト実行なら第n行にジャンプ(&#J(n))して位置合わせをします。表テキストと同様に論理行頭か論理行末にカーソルを移動します。表テキストに戻り、最後に「#69」(テキストの比較)を実行します。

数字にカンマを挿入


======== comma.def by I.KAMIMURA ========
【数字にカンマを挿入】

* M
80 ^KQ  "Insert Comma"
        ?.
        #] x=cp,                        ; カーソル位置(cp)を x に代入
        &s
        i-,                             ; カウンタ初期化
        mi[, mi-,                       ; 挿入モード
:A      #s                              ; [←]
        (r)? >C                              ; ファイル先頭で行き止まりか?
        (cd>='0' && cd<='9' || cd>='0' && cd<='9')?? >B        ; 数字か?
        i++, (i>3)?? >A                   ; 数字が3桁以上あるか?
        #d                              ; [→]
        (ck)                            ; カーソル位置の文字種(全角:1 半角:0)
        ? { ',' x+=2, }                ; 「,」を挿入して、x を 2 増やす
        ? { ',' x++, }                  ; 「,」を挿入して、x を 1 増やす
        #s i-, >A                    ; [←] カウンタ初期化
:B      #d                              ; [→]
:C      mi], cp=x, #?                   ; 元の位置に戻り、カーソル行を再表示

*
======== End of comma.def ========


機能

 全角/半角の数字を入力した直後(数字の並びの終端)にカーソルをおいて実行すると、3桁ごとにカンマを挿入します。日本語FEPの「句読点変換」との相性で、数字入力とカンマ入力の切り替えが面倒な人におすすめです。

-----------------------------------------------------------------------
        1234567890■
-----------------------------------------------------------------------
        1,234,567,890■
-----------------------------------------------------------------------

解説

 cp(カーソル位置のオフセット)とは、大ざっぱに言えば、ファイルの先頭からカーソル位置までのバイト数です。文字入力直後にはラインバッファと呼ばれる作業領域内でのオフセットになっているので、「#]」を実行して編集テキスト側に呼び戻しておかなくてはなりません。cpxに代入しておき、カンマを挿入したぶんxの値を増やして、元の位置に戻ります。全角数字の場合は全角カンマを挿入して2増やし、半角数字の場合は半角カンマを挿入して1増やします。「x+=2,」と「x=x+2,」、「x++,」と「x=x+1,」は同じ意味です。このように、数値を増やしていくことをインクリメントと呼びます。

 このマクロではもうひとつインクリメントしている変数があります。数字の上をひとつ左に移動するごとにiをインクリメントして、3を超えたらカンマを挿入します。「i-,」で0に戻して、繰り返します。こういう使い方をする変数のことをカウンタと呼びます。

手軽にソート


======== sort.def by I.KAMIMURA ========
【手軽にソート】

* M
80 ^\   "Sort"

        ?.
        &s                          ; 画面出力を停止
        k=ks&1,                     ; [SHIFT]の押し下げ状態を k に代入
        x=lx+1,                         ; ソート対象の桁位置を x に代入
        #M y=ky,                        ; 元のカーソル位置を記憶
        (mb)?? { #^ #b #U #_  }         ; 非ブロックモードなら全体を範囲指定
        #S "SORT.SRC" #m (s)? 'Y'     ; 「SORT.SRC」というファイル名でセーブ
        mb-,                            ; 非ブロックモードにする
        #U ky=y,                        ; 元のカーソル位置へ戻る
        #E "SORT"                     ; コマンドラインに「SORT」と書き込む
        (k)? " /r"                    ; シフト実行なら「 /r」
        &?(" /+%d <SORT.SRC >SORT.DST",x)   ; 「%d」←「x」ソート対象桁位置
        #m                              ; コマンド実行
        "DEL SORT.SRC" #m             ; ソート前のファイルを削除
        #[                              ; [ESC]で編集画面に戻る
        #R "SORT.DST" #m              ; ソート後のファイルをRead onlyオープン
        #67                             ; Read only解除
        &d #]                               ; 全画面を再表示

*
======== End of sort.def ========


機能

(ソート前)
-----------------------------------------------------------------------
COMMAND  COM    28292  90-06-18  18:58
VZ       COM    47970  91-12-21   1:57
CONFIG   SYS       43  92-11-08  19:07
FORMAT   COM    10221  90-06-27  10:03
FROLL    COM     1383  89-12-10   1:00
AUTOEXEC BAT      238  92-11-08  22:06
SORT     EXE     1680  86-08-27   3:10
VMAP     COM     1958  90-01-10   1:04
-----------------------------------------------------------------------
(第1桁でソート:ファイル名順に並びます)
-----------------------------------------------------------------------
AUTOEXEC BAT      238  92-11-08  22:06
COMMAND  COM    28292  90-06-18  18:58
CONFIG   SYS       43  92-11-08  19:07
FORMAT   COM    10221  90-06-27  10:03
FROLL    COM     1383  89-12-10   1:00
SORT     EXE     1680  86-08-27   3:10
VMAP     COM     1958  90-01-10   1:04
VZ       COM    47970  91-12-21   1:57
-----------------------------------------------------------------------
(第24桁でソート:日付順に並びます)
-----------------------------------------------------------------------
SORT     EXE     1680  86-08-27   3:10
FROLL    COM     1383  89-12-10   1:00
VMAP     COM     1958  90-01-10   1:04
COMMAND  COM    28292  90-06-18  18:58
FORMAT   COM    10221  90-06-27  10:03
VZ       COM    47970  91-12-21   1:57
CONFIG   SYS       43  92-11-08  19:07
AUTOEXEC BAT      238  92-11-08  22:06
-----------------------------------------------------------------------

解説

 マクロだけでは荷が重い複雑な処理は、子プロセスで他のツールを起動して、リダイレクトしたファイルを利用します。

 最初に、ソートしたいファイル全体あるいはブロックの範囲を「SORT.DST」というファイル名でセーブします。非ブロックモードの場合「#^ #b #U #_」としていますが、どうして「#U」(直前のカーソル位置)が必要なのでしょうか。これがないと、そのあとの「#U」でファイルの先頭へ戻ってしまうからです。ジャンプ系のコマンドを使うと、「直前のカーソル位置」は変わるので気をつけてください。最初のカーソル位置を経由すれば、ちゃんと戻ることができます。「&#M(4)」(カーソル位置マーク #4)など固定したマークを使ってもよいでしょう。

 「#E」でコマンドラインにおりて、

-----------------------------------------------------------------------
C:\>SORT /r /+1 <SORT.SRC >SORT.DST
-----------------------------------------------------------------------

 と書き込みます。「/r」はシフト実行(逆順ソート)の場合だけ書き込むオプション(条件指定)です。「/+1」はソート対象の桁位置を指定するオプションで、「x=lx+1,」の値が書き込まれます。VZのlx(表示カーソルのカラム位置)は見た目と1ずれているので補正する必要があります。

 ソート後のファイルを「#R」でRead onlyオープンして、「#67」でRead onlyを解除する二度手間をかけているのは何故でしょうか。「#O」でふつうにオープンすればよさそうなものです。これは「SORT.DST」がすでにオープンされていた場合(いろいろな条件のソート結果を比較している場合)にそなえています。「#O」だとオープン済みのテキストに切り替わってしまいますが、「#R」だとファイル名が重複していても新たにオープンします。(「テキストの二重化」もこれを利用)

左寄せ/センタリング/右寄せ


======== clr.def by I.KAMIMURA ========
【左寄せ/センタリング/右寄せ】

* M
80 ^\   "clr"
        ?.
        (mr==$80)? { &m(36). }              ; Viewモードなら中止
        eu[, eu+,                       ; [TAB]でスペースコードを入力OFF
:A      p"tab", (eu)? p"spc",               ; メニューに表示する文字列
        !01                             ; 1 番のメニュー
        (r==3)? { eu!, >A }          ; [E]なら eu を反転
        (r<0)? >C                 ; [ESC]なら終了
        m=r,                            ; メニュー返り値を m に代入
        n=ln,                           ; カーソル行番号を n に代入
        e72,                            ; ★ e72…右端設定値
        (ks&1)? e=we,                       ; ◆シフト実行なら画面幅を e に代入
;       (ks&1)?? e=we,                      ; ◇通常実行なら画面幅を e に代入
        &s                          ; 画面出力を停止
        (mb)? {                         ; ブロックモードか?
                #b #B                   ; ブロックの反対側へ
                (ln>n)? { n=ln, #B } ; ブロックの最後の行番号を n に代入
                #b                      ; あらためてブロックモード
        }
:B      #< (ct==2)? { #t #J #u #[ }  ; 行頭のタブ・スペースを削除
        #> ' '#16                    ; 行末のタブ・スペースを削除
        #J #u #[                        ; 削除したタブ・スペースを捨てる
        x=(e-lx-1)/2,                   ; 挿入する桁数を計算
        #< (ct>1 && x>0)? &#68(x*m)        ; センターリングまたは右寄せ
        #> #d                                ; 次の行頭へ
        (ln<n)? >B                        ; ブロックの最後まで繰り返し
:C      eu], mb-,                       ; euオプション復帰、非ブロックモード
        &d #]                               ; 全画面を再表示

* P
1       "",7,4
        "Left"

        "Center"
        "Right"
        "E $p"

*
======== End of clr.def ========

機能

 最初に次のメニューがあらわれます。

        ┌ clr ┐
        │Left  │ 左寄せ
        │Center│ センタリング
        │Right │ 右寄せ
        │E spc │ 挿入するのは「spc」か「tab」か切り替える
        └───┘

解説

 コマンド関数「&#68(n)」を利用して、行頭にタブかスペースを挿入します。

 メニューの4番目の表示を「E spc」と「E tab」で切り替える手法が参考になるでしょう。euオプション([TAB]でスペースコードを入力)の値(+/-)によって、メニューに表示する文字列を切り替えます。

-----------------------------------------------------------------------
        p"tab", (eu)? p"spc",
-----------------------------------------------------------------------

 メニューの「$p」を「tab」または「spc」と置き換えて表示します。「p"tab",」は「p="tab",」の「=」を省略しています。このように文字列(のアドレス)を変数に代入して使うこともできるのです。

 「センタリング/右寄せ」では普通スペースを挿入する(eu+)でしょうから、「eu[, eu+, ... eu],」として、退避と復帰をおこないます。

 右端の初期設定「e72,」も「e=72,」の「=」を省略しています。「e=we,」のほうは「=」を省略すると意味不明になります。

 処理手順としては、まず何がなんでも左寄せ(行頭のタブ・スペースを削除)します。

-----------------------------------------------------------------------
        ひょっこりひょうたん島
-----------------------------------------------------------------------

 左寄せすると、

-----------------------------------------------------------------------
ひょっこりひょうたん島
-----------------------------------------------------------------------
                      lx                                              (e:72)
                      ↑                                                ↑
                      └─────────(e-lx-1)───────────┘
                      ↑                      ↑
                      └── x=(e-lx-1)/2 ──┘
                           (基本挿入桁数)

 「x=(e-lx-1)/2,」で、挿入する桁数の基本的な値をもとめます。左寄せならxを0回、センタリングならxを1回、右寄せならxを2回挿入すればよいわけです。メニューの返り値を代入しておいたmを利用して、「&#68(x*m)」でx×m桁のタブあるいはスペースを挿入します。したがって、メニューアイテムの順序を変更すると誤動作します。

(センタリング)
-----------------------------------------------------------------------
                        ひょっこりひょうたん島
-----------------------------------------------------------------------
↑                      ↑
└──── x  ─────┘
(右寄せ)
-----------------------------------------------------------------------
                                                ひょっこりひょうたん島
-----------------------------------------------------------------------
↑                      ↑                      ↑
└──── x  ─────┴─── x  ──────┘

 空行はスキップするべきです。また、複数行にまたがる論理行ではxが負数になり得るので、「(ct>1 && x>0)」でチェックしています。

タブ・スペース変換


======= tabspace.def by I.KAMIMURA ========
【タブ・スペース変換】

* M
51 ^KM  "タブ→スペース変換"
        r=9, >01                     ; タブ…10進数:9/16進数:$9

52 ^KN  "スペース→タブ変換"
        r=32, >01                    ; 半角スペース…10進数:32/16進数:$20

1:      &s
        mp[, #F &o(r) #m            ; タブか半角スペースを検索する
        mi[, mi-,                       ; 挿入モード
        Eu[,                            ; [TAB]でタブ/スペース
        (Dn)? n=ld, ? n=ln,             ; 表示/論理行番号を n に代入
        x=kx, y=ky,                     ; X位置、Y位置
        i-,                             ; 置換カウンタ初期化
        (mb)?? { #_ #> >A }               ; 非ブロックモードならファイルの最後へ
        l=ln, #B (ln<l)? { l=ln, #B }        ; ブロック先頭の行番号を l に代入
        #< #b #b                     ; ブロックの最後からブロックモード
:A      #r (r)? >D                   ; 後方検索。不発見なら終了
        &m("%d",i)                        ; 現在の置換数をメッセージ
        (mb)? { (ln<l)? >D }              ; ブロックモードなら行番号をチェック
        (cd==9)                         ; カーソル位置の文字はタブか?
        ? Eu+,                          ; タブなら Eu+,
        ? {
                Eu-,                    ; スペースなら Eu-,
                ((lx+1)%ht)? >B              ; ひとつ右(lx+1)はタブ位置でないか?
                #s (r)? >D           ; ◆この2行を消すと、スペース1個も
                (cd!=32) #d ? >B     ; ◆タブに変換する
        }
        i++,                            ; 置換数をカウントアップ
        #d t=lx, #16 &#68(t)                ; カラム t までタブかスペースを入力
:B      #a                              ; 1語左。スペースの連続をスキップ
:C      #J #u #[ >A                  ; 削除した文字を廃棄
:D      (mb)? { &#J(l) (n>l)? #B }       ; ブロックの先頭行へ
        Eu], mi], mp],                  ; オプション復帰
        &#J(n) ky=y, mb-, &d &x(x)  ; 元の位置へ
        &m("%d",i)                        ; 置換数をメッセージ

*
======== End of tabspace.def ========


機能

解説

 まずタブとは何か、おさらいします。

(ht:8)
-----------------------------------------------------------------------
January         一月    むつき
May             五月    さつき
-----------------------------------------------------------------------
1       9       17      25      (ステータスラインの表示)
0       8       16      24      (システム変数 lx)
-----------------------------------------------------------------------

 このように、項目のあいだを空けてカラム位置を揃えたいとき、たとえば「January」を入力し終わったあと[TAB]を押せば、適当な位置までカーソルが飛ぶので、そこから「一月」と入力します。「January」と「May」では長さがちがいますが、タブは伸縮自在なので吸収してくれます。カーソルの飛ぶカラム位置はシステム変数htにはいっており、標準設定では8の倍数です。ステータスラインに表示される数字と、システム変数lx(表示カーソルのカラム位置)の数値は1ずれているので、気をつけてください。タブを視認したい場合は、Dt+,(TABの表示ON)にしてください。(\[F01]「モードメニュー」で変更できます)

 ht=8,の環境で作った文書をht=4,の環境で見ると、項目がずれて表示されることがあります。(^KI「タブの切替え」で8と4を切り替えることができます)

(ht:4)
-----------------------------------------------------------------------
January     一月    むつき
May     五月    さつき
-----------------------------------------------------------------------

 また、パソコン通信で文書を送受信する場合、ホストによってはタブが無効になります。

(タブがとんだ状態)
---------------------------------------
January一月むつき
May五月さつき
---------------------------------------

 文書をプリントアウトする場合にも、タブの扱いはなにかと厄介です。

 とくに不特定多数の受け手を想定した文書では、タブは半角スペースに変換しておいたほうがよいでしょう。

 さて、どちらに変換する場合も、ファイル全体を検索する処理は同じなので、ローカルマクロ「1:」にまとめました。「r=9,」「r=32,」でタブと半角スペースの文字コードをわたします。「#F」で【検索文字列】の1行ウィンドウをひらき、「&o(r)」で文字を書き込みます。一度きりの受け渡しなので、rを使いました。ほかの箇所でも文字コードを使う場合は、たいてい途中でrが壊れるので、cなどを使ってください。

 n,x,yに実行時のカーソル位置を保存して、ファイルの最後(EOF)から先頭に向かってタブか半角スペースを検索します。先頭から最後に向かうやり方だと、ファイルの先頭にある検索語は、燈台もと暗しで検索できません。ブロックで範囲指定した場合は、ブロックの先頭の行番号をl(エル)に代入して、ブロックの最後へ移動します。これはブロックの範囲内を処理する場合の定石なので、おぼえてください。ブロックの先頭から最後に向かって処理する場合は、次のように書きます。


        l=ln, #B (ln>l)? { l=ln, #B }

 「#r」で発見できなくなるまで後方検索を繰り返します。ブロックモードでは、発見位置が範囲外(行番号lnlより小さい)なら終了します。

 まず、「タブ→スペース変換」を説明します。

 単純な置換処理ではないので、タブ1個を半角スペース8個に置換するといった手抜きはできません。カラム位置に応じた個数の半角スペースを挿入しなくてはなりませんが、「&#68(n)」という便利なコマンド関数(nカラムまでタブまたは半角スペースで埋める)を利用すれば、複雑な計算は必要ありません。タブを発見したら、「#d」で右の語に移動して、lx(カラム位置)をtに記憶します。「#h」(パックスペース)ではなく「#16」(語の先頭まで削除)でタブを削除します。これは連続したタブをいっぺんに削除して、処理を速くするためです。そして、「&#68(t)」でtカラムまで半角スペースで埋めます。「&#68(n)」と「#21」(タブ)は、Euオプションによって動作がことなります。

Eu-,
[TAB]でスペースコードを入力 OFF(タブを入力)
Eu+,
[TAB]でスペースコードを入力 ON (スペースを入力)

 「スペース→タブ変換」も同じ手順ですが、半角スペース1個の場合は変換しないように、◆の2行を加えました。全角と半角が混在している文書では、半角スペースをいれて体裁を整えていることがあります。

-----------------------------------------------------------------------
 マクロの名前は tabspace.def です。
-----------------------------------------------------------------------

 半角スペースの右の語がちょうどタブ位置(htの倍数)にあった場合、必要もないのに変換されては困ります。マクロのリストでも、コマンドを半角スペース1個で区切るのがふつうですが、これが変換されると不都合です。

 また、半角スペースがタブ位置にない場合は、


                ((lx+1)%ht)? >B              ; ひとつ右(lx+1)はタブ位置でないか?


 で、スキップします。htで割り切れないならタブ位置ではありません。

 実を言うと、この行はなくてもかまいません。&#68(t)tがタブ位置でない場合、必要なだけタブを入力したあと、余りは半角スペースで埋めるからです。少しでも速度を向上させるための措置です。

 ブロックの範囲を再現できるように、変換を開始する前に「#< #b #b」として、ブロックの最後からブロックの先頭に向かって、あらためて範囲指定しています。

拡張・新規ファイルオープン


======== newfile.def by I.KAMIMURA ========
【拡張・新規ファイルオープン】

* M
80 [ESC]N "newfile"
        ?.
        (mb)?? { #N. }                  ; 非ブロックモードでは基本機能のまま
        (wc==tc)? { &m(6). }                ; テキスト数チェック
        &s
        o=wn,                           ; 元テキスト番号を o に代入
        (wn) #N ?? >B                        ;【新規ファイル】の1行ウィンドウ
        #P                              ;「ファイル名の複写」
:A      #s (cd!='\')? >A             ; 「\」に突き当たるまで左へ
        #d #18 #x #u                    ; 主ファイル名クリア(パスのみ残す)
:B      ax=$2A00, &i($21)           ; ◆ MS-DOS function call
        &?("%02d%02d",dx>>8,dx&$FF)     ; ◆「月日」を書き込む
        ax=$2C00, &i($21)           ; ◆ MS-DOS function call
        &?("%02d%02d.NEW",cx>>8,cx&$FF) ; ◆「時分.NEW」を書き込む
        #m t=wn,                        ; 新規テキスト番号を t に代入
:C      &#T(o)                              ; 元テキストに戻る
        (mb)? {                         ; ブロックモード?(巨大ブロック対応)
                #k &#T(t)           ; #k…行・ブロック記憶
                #i (Ek)?? #B            ; #i…行・ブロック挿入
                >C
        }
        &d &#T(t)                       ; 全画面の再表示。新規テキストへ
        #82 (12){#s} #18 #x #u          ; ★ファイル名変更(パスのみ残す)
*
======== End of newfile.def ========


機能


        ┌─【ファイル名】───────────┐
        │C:\VZ\                              │
        └───────────────────┘

 このディクトリ名も不要なら、[↓]のワンタッチで消すことができます。仮のファイル名のままでかまわなければ、[ESC]でキャンセルしてください。★の行を削除すれば、ファイル名変更の1行ウィンドウがでなくなります。

解説

 新規ファイルをオープンする前に、テキスト数に余裕があるかどうかしらべます。wc(編集中のテキストの数)とtc(オープン可能なテキスト数)が等しかったら、「(wc==tc)? { &m(6). }」で終了します。「&m(6)」は「オープンできません」というメッセージを出します。

 新規ファイル名は「newfile.tmp」といった固定した名前でもよいのですが、すこし凝ってみました。ディレクトリ名は複写元のファイル名と同じ。主ファイル名は作成日時にします。【新規ファイル】の1行ウィンドウに複写元のファイル名を書き込み(#P)、「\」までカーソルを左に移動(#s)します。カーソルをひとつ右へ戻して(#d)、行末まで削除(#l,#19)すれば、ディレクトリ名のみ残すことができますが、削除文字列バッファにゴミ(ファイル名)が残ります。このマクロでは逆にディレクトリ名を行頭まで削除(#18)して、1行ウィンドウに残ったファイル名を[↓]#x)でクリアして、削除文字列の復活(#u)でディレクトリ名を復活することによって、ゴミを残さないようにしました。なお、consoleで実行するとファイル名に「\」がないので、これらの処理はスキップするようにしてあります。

 ファイル名に「月日時分」を与えるには、ここでは詳しく述べませんが、MS- DOSのファンクションコールを使います。◆の4行を「"newfile.tmp"」と置き換えれば、固定した名前になります。

 元のテキストに戻り(&#T(o))、ブロックを記憶し(#k)、新規ファイルに切り替えて(&#T(t))、ブロックを挿入します(#i)。VZは一度に64キロバイトまでのブロックしか記憶できないので、巨大ブロックは何回かに分けて複写しなくてはなりません。巨大ブロックで^KK,[F09]「行・ブロック記憶」を実行するとわかりますが、「ブロックが大きすぎます」とメッセージがでて、記憶できなかった部分はブロックモードのまま残ります。元のテキストに戻って、ブロックモードだったら、複写を繰り返せばよいわけです。先頭の側から順に記憶されるので、複写先でブロックペースト後のカーソル位置がブロックの最後にないと、逆順に複写されてしまいます。ブロックペースト後のカーソル位置はEkオプションに依存します。

Ek-,
ブロックの先頭
Ek+,
ブロックの最後

 「(Ek)?? #B」で、カーソル位置がブロックの先頭(Ek-)ならブロックの最後へ移動(#B)します。

 最後にファイル名変更の1行ウィンドウをオープンして(#82)、カーソルを12字分左へ移動します((12){#s})。ディレクトリ名のみ残す手順は、前述した通りです。

 細かいことですが、「&d &#T(t)」は「&#T(t)&d」のほうがよいと思った人がいるかもしれません。画面のちらつきをなくすために&d(全画面の再表示)はできるだけ後ろに記述したほうがよいのですが、&dだけではカーソル行アンダーラインが表示されないので、完璧を期すには#]を付け足さなくてはなりません。「&d &#T(t)」なら、ちゃんと表示されます。いずれにせよテキストが切り替わって画面がちらつくので、&dを先に実行する弊害は気にしなくてよいでしょう。

巨大ブロック対応・自動コピー


======== bigblock.def by I.KAMIMURA ========
【巨大ブロック対応・自動コピー】

* M
37 ^Y   "Big Block Cut"
        ?. c=#y, >01                 ; #y…行・ブロック削除

38 ^KK  "Big Block Copy"
        ?. c=#k, >01                 ; #k…行・ブロック記憶

1:      (mb)?? {                        ; 非ブロックモードか?
                #W (mb)?? {             ; 参照画面は非ブロックモードか?
                #W &o(c).           ; 通常実行
                }
        }
        (wt)?? {                        ; ◆全画面モードか?
                &#Y(4)                      ; ◆縦分割
                &a(" OK ")                ; ◆「OK か? (Y/N)」
                (r<1)?.                      ; ◆'N' [ESC] なら終了
        }                               ; ◆
        &s b=mb,                    ; ブロックモードを b に代入
        #W #M #W                        ; #M…ブロック挿入位置をマーク
:A      &m(" Copy ...%d",ln)              ; 処理経過をメッセージ
        (mb)? {
                &o(c) #W #i         ; ブロックを複写(巨大ブロック対応)
                (ek)?? #B               ; ブロックの最後へ
                #W >A
        }
        #W mb=0, #b #U (ek)? #B #b      ; ブロックの範囲を明示
        &m("") &d &#Y(0)          ; 全画面モード
        cm=48, r=b,                     ; 直前のコマンド・返り値を調整

*
======== End of bigblock.def ========


機能

解説

 ほかのエディタからVZに乗り換えて、カット&ペーストの仕様に戸惑う人が多いようです。VZは行・ブロックを削除あるいは記憶すると、テキストスタックと呼ばれる領域に保存します。この領域には最大で64キロバイトしか保存できません。64キロバイトをこえる巨大なブロックを削除・記憶しようとすると、「ブロックが大きすぎます」とメッセージが出ます。保存しきれなかったブロックは反転表示のまま残ります。それをさらに削除・記憶すると、前回保存したブロックはテキストスタックから追い出されて消えてしまうので、下手をすると、大切な文書の一部を電子の藻屑にしかねません。

 テキストA内の巨大ブロック(160キロバイト)をテキストBに手作業で移動する例を図に示します。

    (テキストA)                                 (テキストB)
┌─────────┐                          ┌─────────┐
├─────────┤   (テキストスタック)   │                  │
│                  │         ┌─┐           │                  │
│160 KB            │         │?│           │  ---挿入位置---  │
│                  │         └─┘           │                  │
│        ┌────┤                          └─────────┘
│        │        │
├────┘        │
└─────────┘

    (テキストA)で [CTRL]+[Y]                    (テキストB)
┌─────────┐                          ┌─────────┐
├─────────┤   (テキストスタック)   │                  │
│96 KB             │  ┌─────────┐  │                  │
│        ┌────┤→│64 KB             │  │  ---挿入位置---  │
│        │        │  └─────────┘  │                  │
├────┘        │                          └─────────┘
└─────────┘

    (テキストA)                                 (テキストB)で [CTRL]+[J]
┌─────────┐                          ┌─────────┐
├─────────┤   (テキストスタック)   │                  │
│96 KB             │         ┌─┐           │                  │
│        ┌────┤         │空│     →    ├─────────┤
│        │        │         └─┘           │64 KB             │
├────┘        │                          ├─────────┤
└─────────┘                          │                  │
                                                └─────────┘

    (テキストA)で [CTRL]+[Y]                    (テキストB)
┌─────────┐                          ┌─────────┐
├────┐        │   (テキストスタック)   │                  │
│32 KB   │        │  ┌─────────┐  │                  │
├────┘        │→│64 KB             │  ├─────────┤
└─────────┘  └─────────┘  │64 KB             │
                                                ├─────────┤
                                                │                  │
                                                └─────────┘

    (テキストA)                                 (テキストB)で [CTRL]+[J]
┌─────────┐                          ┌─────────┐
├────┐        │   (テキストスタック)   │                  │
│32 KB   │        │         ┌─┐           │                  │
├────┘        │         │空│     →    ├─────────┤
└─────────┘         └─┘           │64 KB             │
                                                ├─────────┤
                                                │64 KB             │
                                                ├─────────┤
                                                │                  │
                                                └─────────┘

    (テキストA)で [CTRL]+[Y]                    (テキストB)
┌─────────┐                          ┌─────────┐
│                  │   (テキストスタック)   │                  │
│                  │       ┌───┐         │                  │
└─────────┘ →   │32 KB │         ├─────────┤
                             └───┘         │64 KB             │
                                                ├─────────┤
                                                │64 KB             │
                                                ├─────────┤
                                                │                  │
                                                └─────────┘

    (テキストA)                                 (テキストB)で [CTRL]+[J]
┌─────────┐                          ┌─────────┐
│                  │   (テキストスタック)   │                  │
│                  │         ┌─┐           │                  │
└─────────┘         │空│     →    ├─────────┤
                               └─┘           │64 KB             │
                                                ├─────────┤
                                                │64 KB             │
                                                ├────┬────┤
                                                │32 KB   │        │
                                                ├────┘        │
                                                └─────────┘

 テキストスタックをカゴだと考えてください。テキストAからカゴいっぱいに積んだブロックを、テキストBにあけてやると、カゴが空になり、次のブロックを運ぶことができます。カゴがいっぱいなのに、さらに積もうとすると、VZはカゴの中身でいちばん古いブロックを捨てて、新しいブロックを載せる余裕を作ります。

 このように、ブロックの反転表示が消えるまで、AとBを何度か往復してカット&ペーストする作業を自動化したのが、このbigblock.defです。巨大ブロックに対応した複写の手法はnewfile.defと同じです。

 巨大ブロックを小分けして移動・複写した場合にも、ひとつのブロックに見せかけるために、


        #W mb=0, #b #U (ek)? #B #b      ; ブロックの範囲を明示

 という処理をおこないます。#bでブロックモードにして、#Uで挿入開始位置にジャンプすることによって、ブロック全体をあらためて範囲指定しています。ek+(ブロックペースト後カーソル位置更新する)ならば、#Bでブロックの最後に移動します。再び#bでブロックモードを解除します。

 完璧を期して、「直前のコマンド」と「返り値」を調整します。


        cm=48, r=b,                     ; 直前のコマンド・返り値を調整

 こうしておけば、^KU「ペーストのアンドゥ」やreblock.defなど、直前のブロック操作と関連の深いコマンド・マクロと整合性をたもつことができます。

カーソル位置へ挿入


======== esc_i.def by I.KAMIMURA ========
【カーソル位置へ挿入】

* M
8  [ESC]I @I "Insert File"
        ?. (mr==$80)? { &m(36). }   ; View モードなら「変更できません」
        #< #M                                ; 挿入開始位置をマーク
        &m("File will be insert.")
        n=wn, c=wc,                     ; テキスト番号、テキスト数
        #R                              ;【入力ファイル】(リードオンリー)
        #m                              ; リターン空打ちでファイラー起動
:A      (s)? { &c >A }                   ; 編集モードに戻るまでキーを受けつける
        (wc==c)? >Z                  ; テキスト数が同じなら終了
        mr=-1,                          ; Read onlyモードにする
        &s                          ; 画面出力を停止
        #W &#T(n)                   ; 元テキスト
:B      #W &#T(c+1)                 ; 挿入するテキスト
        #b #_ (ct)? { #> #m #x }     ; ファイル全体を範囲指定
:C      (mb)? {
                #y #W #i                ; ブロック複写(巨大ブロック対応)
                (ek)?? #B               ; ブロックの最後へ
                &m(" Copy ...%d",ln)      ; 処理経過をメッセージ
                #W >C
        }
        #C &#T(n)                   ; 挿入したテキストをクローズ
        (wc>c)? >B                        ; 複数テキスト挿入なら繰り返し
        mb=0, #b #U (ek)? #B #b         ; ブロックの範囲を明示
:Z      &m("") &d #]                  ; メッセージを消し、全画面を再表示
        cm=48, r=1,                     ; 直前のコマンド・返り値を調整

*
======== End of esc_i.def ========


機能

解説

 VZ添付の「カーソル位置へ挿入」では、最初に【入力ファイル】の1行ウィンドウがあらわれますが、たいていはファイラーから選択するためにリータンを空打ちするのではないでしょうか。また、ブロックが正常に挿入されたかどうか、ブロックの先頭を確認したくなるのが人情ですが、^QB「ブロックの先頭/最後」を押しても、挿入開始位置にジャンプできないことがあります。巨大なファイルを挿入する場合にはいくつかのブロックに分割して複写するので、最後に挿入したブロックの先頭にジャンプしてしまうのです。これらを改善するのが上のマクロです。

 「#R #m」でファイラーが起動します。「:A (s)? { &c >A }」はchgtxt.def「拡張・編集テキスト切替え」でも出てきましたが、編集モード(s=0)に戻るまでキー操作を受け付ける書き方です。ひとつもファイルをオープンせずに[ESC]で戻ることもありえるので、「(wc==c)?」でチェックします。「#b #_」でファイル全体をブロックで範囲指定しますが、最終行の尻にEOFがくっついていると、その行はブロックに含まれません。そこで「(ct)? { #> #m #x }」(カーソル位置がEOFでなければ改行)でEOFを追い出します。カーソルがEOF上にある場合は、挿入/上書きモードにかかわらず、「#m」は改行として働きます。

 ブロックを複写する手順はbigblock.defと同じです。

 複数のファイルをオープンした場合には、1ファイルずつ複写します。

              wn(テキスト番号)
            ~~~~~~~~~~~~~~~
            │ 4│無関係のテキスト      │
            ├─┼───────────┤
            │ 5│現在編集中のテキスト  │(n)
            ├─┼───────────┤
            │ 6│無関係のテキスト      │(c)オープン前のテキスト数
            ├─┼───────────┤
            │ 7│挿入するテキスト      │&#T(c+1)
          ↑├─┼───────────┤
        く││ 8│挿入するテキスト      │
        り│├─┼───────────┤
        あ││ 9│挿入するテキスト      │
        が│├─┼───────────┤
        る││ A│挿入するテキスト      │
            └─┴───────────┘

テキスト番号c+1からテキスト番号nへ複写し終わったら、「#C」でクローズします。後ろ側のテキストがくりあがってくるので、また「&#T(c+1)」で切り替えることできます。現在のテキスト数がオープン前のテキスト数より大きいあいだ(wc>c)くりかえします。 ekオプションの設定によって、挿入開始位置に戻るかどうか指定できます。

拡張・テキストの二重化


======== esc_d.def by I.KAMIMURA ========
【拡張・テキストの二重化】

* M
6  [ESC]D @D "Duplicate"
        ?.
        (wn)??.                         ; console での実行を禁じる
        (wt)? >D                     ; 画面分割か?
        (wt==tc)? { &m(6). }                ; テキスト数チェック
        &s                          ; 画面出力の停止
        ro[, ro-,                       ; Viewモード保存
        &#Y(0) n=ln, y=ky,          ; 全画面モード。行番号、表示位置を記憶
        (ks&1)? {                   ; ◆ シフト実行か?
;       (ks&1)?? {                  ; ◇ シフト実行でないか?(反転)
                #R #P #m                ; ディスクの元ファイルをオープン
                (s)? { 'Y' >A }              ; 存在しないなら新規オープン
                >C
        }
        #N #P #m                        ; 同名ファイルを新規オープン
:A      #W #^ mb=0, #b #_               ; ファイル全体を範囲指定
        (ct)? { #> #m #x }           ; EOF 行対応
:B      &m("Line:%d",ln)          ; 処理経過をメッセージ
        (mb)? {
                #k #W #i                ; ブロックを複写(巨大ブロック対応)
                (ek)?? #B               ; ブロックの最後へ
                #W >B
        }
        &#J(n) ky=y, #W                     ; 元の位置へジャンプ
:C      mr=$80,                         ; Viewモードにする
        &#J(n) ky=y, #W                     ; 対応する位置へジャンプ
        ro],                            ; 変数・Viewモード復帰
        &m("") &d #Y .                        ; 画面分割

;--- 画面分割の場合 ---

:D      (mr<=1)? >E                       ; 編集画面側か?
        &s &m(" wait ...")            ; 参照画面側ならテキスト入れ替え
        mp[, #M
        t=wn, ky[, #W o=wn, ky[,
        &01 &#T(t) &#J(n)           ; 対応する行にジャンプ
        #> #r (r)? #c #U &01 #U ky],     ; 対応する文字列を検索
        #W &#T(o) &#J(n)                ; 編集画面側でも同じ処理
        #> #r (r)? #c ky],
        mp], &m("") &d #] .

:E      #W (mr<=1)? { #W . }         ; 参照画面は Viewモードか?
        #C (wt)? { #W &#Y(0) }

1:      n=ln,
        #> mb=0, #b #< #G         ; 現在行を検索文字列に設定
        (mb)? { #s (r)?. >^ }                ; 空行なら前の行へ

*
======== End of esc_d.def ========


機能

【全画面モード】
  • 通常実行……現在編集中のテキストを複写して二重化
  • シフト実行……ディスクの元テキストを呼び出して二重化

 いずれも View モードでオープンします。

 シフトキーの意味を逆転させたい場合は、◆行と◇行を差し替えてください。

【二重化したテキストを画面分割している場合】
  • カーソルが編集テキスト側にある場合、参照テキストをクローズします。
  • カーソルが参照テキスト側にある場合、テキストを入れ替えて、それぞれ該当する行へジャンプします。見た目には、ステータスバーの色が入れ替わり、それまでViewモードだった側が編集できるようになります。

解説

 VZには同一テキストの別の箇所を分割表示したり編集したりする機能はありません。VZ標準の「テキストの二重化」では、ディスクの元ファイルを呼び出して分割表示しています。変更前と変更後のテキストを比較するには便利ですが、同一テキストの別の箇所を参照したい場合、大幅に修正が加えられたテキストでは役に立ちません。また、参照テキスト側に修正したい箇所が見つかったとき、編集テキスト側をわざわざ同じ箇所までスクロールするのは面倒です。それを改善するのがこのマクロです。

 どんな処理をしているかというと、まず編集テキストのカーソル行の行番号と文字列を取得します(ローカルマクロ「1:」)。参照テキストでその行番号にジャンプして、さらに文字列を検索します。これで参照テキストのカーソル位置が編集テキストと同じ位置に移動しました。編集テキストでも同じような処理をおこない、テキストの配置(上下/左右)を逆にしているのです。

 二重化についてはとりたてて変わったことはしていません。「#N #P #m」で同名のファイルを新規オープンして、ファイル全体を複写しているだけです。