MikoScript 言語仕様( Epsaly関連 )


 MikoScript は、簡単なツールの作成はもちろんのこと、本格的なオブジェクト指向 プログラミングも手軽にできるスクリプト言語です。

 本エディタでは、この MikoScript の汎用部に、本エディタ専用のオブジェクトと関数を 組み込んで特化しています。この汎用部に関しては、 MikoScript のホームページをご覧ください。ここでは、特化部について説明します。 特化部には、
  ・Epsaly オブジェクト
  ・各種リレー型関数
があります。 Epsaly オブジェクトは、スクリプトから本エディタにアクセスする時に使います。 また、各種リレー型関数は、主に、編集テキスト内の位置を規定する時に使います。 これらの詳細は、後述します。 「リレー型関数」自身の仕様に関しては、 MikoScript のホームページの関連箇所をご覧ください。
 本エディタ用の MikoScript では、print 文の出力先が、 本エディタの「スクリプトコンソールウィンドウ」に なっています。そのため、例えば、
  print "Hello, world.";
を実行すると、そのウィンドウに「Hello, world.」の1行がプリントされます。

 本スクリプトのグローバルスコープは、一度生成されると、 本エディタが終了するまで、存続します。(「補足説明」参照 ) そのため、例えば、あるスクリプトで、
  ::GlobalVar = 123;
を実行すると、このスクリプトの終了後も、この GlobalVar 変数は、 存続しているので、別のスクリプトの実行時に、この変数にアクセスできます。

 テキスト編集に関連する ::Clipboard オブジェクトや各種文字列操作関数等は、 本エディタ用の特化部ではなく、MikoScript の汎用部に実装されています。 そのため、本ヘルプには、その説明はありません。

■ Epsaly オブジェクト

 Epsaly オブジェクトは、MikoScript から、本エディタにアクセスするための 各種のメンバー関数が装備された組み込みオブジェクトです。 このオブジェクトは、クラスとして使用するのではなく、 そのオブジェクト単体で使用するようになっています。 そのため、そのインスタンスやコピーを生成する必要はありません。

 Epsaly オブジェクトは、MikoScript のグローバルスコープに登録されています。 このメンバー関数をコールするには、

  ::Epsaly.関数名( 引数列 )

という書式になります。この他、状況に応じて、いろいろと簡便な書式も利用できます。 これについては後述します。

 Epsaly オブジェクトのメンバー関数は、次のように、3グループに分類して説明しています。
(1)現編集ウィンドウを対象とする関数
(2)全編集ウィンドウ共通の関数
(3)プロジェクト関連の関数

 ActiveX スクリプトにも、Epsaly オブジェクトが組み込まれますが、それと、 この MikoScript 用の Epsaly オブジェクトとの互換性はありません。 それぞれ、独自の仕様になっています。


■編集テキスト内の位置を規定するリレー型関数

 編集テキスト内の位置を規定するパラメータには、論理行番号、表示行番号、桁番号、 文字数、文字コードインデックス、単語数、X座標、などがあります。 これらのパラメータの値は、テキストの先頭からの場合や、ある行の先頭から、あるいは、 現在位置からの場合等があります。また、これらには、いろいろな組み合わせが 想定されます。

 本スクリプトでは、このようなパラメータを、各種の関数(カーソル移動、範囲選択、削除、 部分テキストの取得等)で、共通した仕様で柔軟に使えるようにするために、 専用のリレー型関数群を装備しています。

 このリレー型関数の主な機能は、その対象に、編集テキスト内の位置を規定するパラメータの 種別を示す属性を設定することです。例えば、'LineNo というリレー型関数は、 「行番号」の属性を、その対象に設定します。これを使って、
  100'LineNo
とすれば、「行番号」の属性を、整数値 100 に設定します。 これで、100 行目の位置を表わす値になります。 また、「桁番号」の属性を設定する 'Column というリレー型関数を使って、
  20'Column
とすれば、20 桁目の位置を表わす値になります。これらを組み合せて、
  100'LineNo, 20'Column
とすれば、100 行 20 桁の位置を表わすことができます。

 このリレー型関数のなかには、その対象が不要なものや、省略可能なものもあります。 例えば、
  'TextTop
は、「テキストの先頭」を表わします。また、
  'LineNo
は、現カーソル行の「行番号」を表わします。
 次に、その他のいろいろな例を示します。

現位置から 5 文字先:
  5'DKn
次の表示行の行頭:
  +1'VLn, 'LineTop
前の論理行の行末から 3 単語前:
  -1'LLn, 'LineEnd, -3'DWn
テキスト先頭からの文字コード数が 1200 の位置:
  1200'Ci
論理行インデックスが line という変数の値で、 その行頭からのX座標が x という変数の値になる位置:
  line'LLi, x'Xc

 このような表記で規定する位置は、Epsaly オブジェクトのメンバー関数の引数に使います。 例えば、カーソル移動では、
  ::Epsaly.MoveTo( 100'LineNo, 20'Column );
のようになります。これは、100 行 20 桁目の位置を、カーソルの移動先として 指定した例です。
 また、文字範囲の選択で、例えば、2 行前の表示行の先頭を、その始点に指定する場合は、
  ::Epsaly.SelectChars( -2'VLn, 'LineTop );
のようになります。
 この形態での引数の個数は、0〜8 個の範囲で任意です。

 当メンバー関数の引数で、このように、リレー型関数を使って、編集テキスト内の位置を 指定する際には、「現位置」というものを意識しておく必要があります。 この「現位置」は、最初、現カーソル位置になり、以降、各引数のリレー型関数の実行で、 順次更新され、最終の「現位置」が、当メンバー関数の目標位置になります。 例えば、
  +1'LLn, 'LineEnd, -2'DKn
の場合、「現位置」は、最初、現カーソル位置で、以降、次のように変わっていきます。
 (1) +1'LLn で、現カーソル行の次の論理行
 (2) 'LineEnd で、その行の行末
 (3) -2'DKn で、その行末から 2 字前
この最後の時点での「現位置」が、目標位置になります。

 この「現位置」の変遷時に、行位置が変わらなければ、「現位置」の行は、 現カーソル行のままです。また、桁位置が変わらなければ、 「現位置」の桁は、現カーソル桁のままです。 メンバー関数によっては、その結果を特別に扱う場合もあります。

 また、この変遷時に、「現位置」が、移動できない桁やX座標に指定された場合、 「現位置」は、その時点の行内で移動可能な最寄の桁位置で(一時的に)代替されます。 なお、「現位置」は、フリーカーソル時には、改行コード以降の桁にも移動できます。

 「現位置」には、論理行か表示行かの「行種別」も含まれます。 これは、最初、環境設定の行番号欄で選ばれている方になり、以降、 リレー型関数で更新されれば、その方に変わります。この最終結果は、 それに関係するメンバー関数( ::Epsaly.GetLineText() 等 )で参照されます。

 リレー型関数で属性が設定された引数を必要とする関数が、属性の無い引数を 受け取ると、通常の処理は行なわずに、null を返します。例えば、
  ::Epsaly.MoveTo( 20, 8'Column );
では、最初の引数の 20 の値が何を意味するのか不明なので、null が返ります。

 また、属性の無い通常の引数と、リレー型関数を使う引数の両方が必要な関数もあります。例えば、
  ::Epsaly.GetLineCond( 2, 100'LineNo );
では、最初の引数は、テキスト内の位置を規定するのではなく、取得する状態の種類を指定しています。

 リレー型関数で設定された属性は、通常の演算には全く影響しません。 例えば、あまり意味はありませんが、
  120'LineNo + 40'Column
とすることもできます。ちなみに、この結果は、160 になります。また、
  print 'LineNo, 'Column;
を実行すると、スクリプトコンソールウィンドウに、 現カーソル位置の行番号と桁番号がプリントされます。

 各リレー型関数については、以下で詳しく説明します。



■プログラム例

 次に、MikoScript でのプログラム例を示します。

(例1)引用符付き貼り付け

 クリップボードのテキストの各行頭に「 > 」を付けた文字列を 現カーソル位置に挿入する。

    text = ::Clipboard.GetText();
    if( text )
        ::Epsaly.Insert( text'subst( "@(.*\n|.+$)", "> @1" ) );

(例2)選択範囲内での全置換

 選択されているテキスト(矩形範囲も可)内で、 正規表現の「検索文字列」に一致する全ての箇所を「置換文字列」に置き換える。

    if( ::Epsaly.GetSelType() <= 0 )  // 範囲選択なし?
        return;
    if(( FindString = ::Input( "検索文字列" )) == null )
        return;
    if(( ReplString = ::Input( "置換文字列" )) == null )
        return;
    ::Epsaly.SetSelText( 
        ::Epsaly.GetSelText()'subst( FindString, ReplString ) );

(例3)現編集ファイルの一覧表示

 現在開いている編集ファイルの一覧を、フルパス名で、 スクリプトコンソールウィンドウにプリントする。

    for( i = 0 ; ( id = ::Epsaly.GetWindowId( i )) != 0 ; i++ )
        print id's(2), ::Epsaly.GetFileName( id );

● Epsaly オブジェクトの簡易表記方法

 スクリプトを記述する際、Epsaly オブジェクトを使う箇所が多くなる場合があります。 例えば、次のスクリプトは、現編集テキスト内の全ての空白行を除去するプログラムですが、 ここでは、::Epsaly を 6 箇所で使っています。

    ::Epsaly.UndoBlockBegin();
    ::Epsaly.MoveTo( 'TextTop );
    ::Epsaly.SetFindCond( $"(^[ \t]*\n)+", "RGE" );
    while( ::Epsaly.FindFwd() >= 0 )
        ::Epsaly.Delete();
    ::Epsaly.UndoBlockEnd();

このように、逐一、::Epsaly と表記するのは、ちょっと面倒なので、 MikoScript では、いろいろな対応ができるようになっています。 そのうちの主なものを3通り、次に示します。

(1)'AddScope を使う場合

 'AddScope で、現関数のローカルスコープ内に、::Epsaly オブジェクト内のスコープを 追加します。そうすれば、::Epsaly のメンバー関数は、あたかも、現ローカルスコープ内に あるかのようにアクセスできます。この場合、上記のスクリプトは、次のように書けます。

    'AddScope( ::Epsaly );
    UndoBlockBegin();
    MoveTo( 'TextTop );
    SetFindCond( $"(^[ \t]*\n)+", "RGE" );
    while( FindFwd() >= 0 )
        Delete();
    UndoBlockEnd();
(2)scope 構文を使う場合

 scope 構文で、現メンバースコープを一時的に、::Epsaly オブジェクトのスコープに 変えます。そうすれば、::Epsaly のメンバー関数は、あたかも、現メンバースコープ内に あるかのようにアクセスできます。この場合、上記のスクリプトは、次のように書けます。

    scope ::Epsaly
    {
        .UndoBlockBegin();
        .MoveTo( 'TextTop );
        .SetFindCond( $"(^[ \t]*\n)+", "RGE" );
        while( .FindFwd() >= 0 )
            .Delete();
        .UndoBlockEnd();
    }
(3)::Epsaly への参照を使う場合

 ::Epsaly への参照を適当な変数に代入して、それで、::Epsaly オブジェクトに アクセスします。この場合、上記のスクリプトは、次のように書けます。

    e := ::Epsaly;    // ※
    e.UndoBlockBegin();
    e.MoveTo( 'TextTop );
    e.SetFindCond( $"(^[ \t]*\n)+", "RGE" );
    while( e.FindFwd() >= 0 )
        e.Delete();
    e.UndoBlockEnd();

≪注意≫ ※ 印の行を、
  e = ::Epsaly;
としてしまうと、::Epsaly の複製が代入されるので、 処理時間とメモリーの浪費になります。


 以上を踏まえて、もう少し、プログラム例を示します。
(例4)空白除去

 選択テキスト(矩形範囲も可)内の半角空白と TAB を除去する。

    'AddScope( ::Epsaly );
    if( GetSelType() > 0 )
        SetSelText( GetSelText()'subst( $"[ \t]+", "" ) );

(例5)連続する空行の統合

 現編集テキスト内で、連続する複数の空行を1つの空行にまとめる。

    'AddScope( ::Epsaly );
    UndoBlockBegin();
    MoveTo( 'TextTop );
    SetFindCond( $"(^[ \t]*\n)+", "RGE" );
    while( FindFwd() >= 0 )
        Insert( "\r\n" );
    UndoBlockEnd();

(例5)連番付加

 選択テキスト(矩形範囲も可)内の各行の選択部の先頭に、括弧付きの連番を付加する。

    'AddScope( ::Epsaly );
    switch( GetSelType() )
    {
      case 1:   
      case 2:   eol = "";       break;
      case 3:   eol = "\r\n";   break;
      default:  return;
    }
    text = "";
    ( MinLi, MaxLi ) = GetSelRange( 0 );
    i = 1;
    for( Li = MinLi ; Li <= MaxLi ; Li++ )
        text += "(%d)"'fmt( i++ ) + GetSelText( Li ) + eol;
    SetSelText( text );


■補足説明

 Epsaly 用の MikoScript は、DLL(ダイナミック・リンク・ライブラリ)として、 実装されています。これは最初、Epsaly エディタ本体には、リンクされていません。 MikoScript を始めて起動した時に、動的にリンクされます。そのため、 最初の MikoScript の起動には、若干時間がかかります。しかし、2回目以降は、 高速に起動されます。もし、MikoScript を最初から全く使わなければ、 このDLLはリンクされないので、その分のメモリー等のリソースが、節約されます。

 本エディタでは、ファイルの拡張子が、.mc の場合、MikoScript のソースファイル として扱うということは、既に述べましたが、これは、 Windows に登録されている拡張子の関連付けとは無関係です。 つまり、拡張子の関連付けがどのようになっていても、 本エディタでは、.mc を、MikoScript のソースとして扱います。 この扱いが、他のアプリケーションに影響することはありません。