「利用者:Masme」の版間の差分

提供: MeryWiki
ナビゲーションに移動 検索に移動
Masme (トーク | 投稿記録)
<code>を除去、<samp>に置換。
Masme (トーク | 投稿記録)
マクロ「テキスト整形」の仕様変更について
1行目: 1行目:
=マクロ「テキスト整形」の仕様変更について (2019/05/01)=
マクロ「テキスト整形」の処理の流れを「前処理」「本処理」「後処理」の3段階に分けて考えたとき、モード (F/R/RS) 別に異なっていた流れを統合し、モードを廃止しようと考えています。
{|class="wikitable"
!colspan="4"| 旧仕様
|-
! モード !! 前処理 !! 本処理 !! 後処理
|-
! F
| なし
| 引数に Selection を渡して処理
| なし
|-
! R
| なし
|rowspan="2"| 引数に Selection.Text を渡して処理
|rowspan="2"| 戻り値で選択範囲を置換、再選択
|-
! RS
| 選択範囲を行全体に拡張
|-
!colspan="4"| 新仕様
|-
|
| オプションで指定
| 第1引数に Selection.Text を、<br>第2引数に Selection を渡して処理
| 戻り値で置換・再選択を制御
|}
==後処理:戻り値による選択範囲の置換・再選択の制御==
Mery 2.7.0 にて「マクロで Chakra (jscript9.dll) における [[マクロリファレンス:Window_インターフェイス#Quit_メソッド|Quit メソッド]]をサポート外にした」([https://www.haijin-boys.com/software/mery/mery-2-7-0 公式記事])ことを受け、戻り値により後処理(選択範囲の置換・再選択)を制御できる仕組みを導入します。
String (文字列) か Array (配列) を return すると、選択範囲を置換してカーソルを移動します。それ以外 (undefined など) の場合は、選択範囲に対し何もしません。<br>
<code>Quit();</code> (処理を中断) の代替策として <code>return;</code> (undefined を返す) などとすることで処理を終了させることができます。
<source lang="javascript">
f.push(["例1","",function(){
  return "abcd" }]);
f.push(["例2","",function(){
  return ["ab","cd"] }]);
f.push(["例3","",function(){
  return ["ab","cd",""] }]);
f.push(["例4","",function(){
  return; }]);
</source>
{|class="wikitable"
! 例 !! 動作
|-
| <code>"abcd"</code> || abcd に置換後、abcd を選択 = <code>["","abcd",""]</code>
|-
| <code>["ab","cd"]</code> || abcd に置換後、ab と cd の間に移動
|-
| <code>["ab","cd",""]</code> || abcd に置換後、cd を選択
|-
| String / Array 以外 || 選択範囲に対し何もしない
|}
==本処理:第2引数を復活==
マクロ「[[テキスト変換]]」の第2引数を復活し、第1引数に Document.Selection.Text を、第2引数に Document.Selection を渡すようにします。
「テキスト整形」で第2引数を削除したのは、「テキスト変換」で Selection を利用したコマンドが無かったこと、戻り値で選択範囲が置換されるため Selection を利用しづらいと感じたことが理由でした。
「テキスト整形」でモード F を設け分離したのも、そうした理由によりますが、戻り値で選択範囲を置換するかしないか制御できるようになったため状況が変わりました。
==前処理:モードを廃止、オプションに変更==
「モード」を「オプション」に変更し、機能を追加します。
{|class="wikitable"
| <code>s</code> || 実行に範囲選択を要求 (require selection)<br>非選択時はメニュー項目がグレーになり実行不可
|-
| <code>c</code> || 実行にクリップボードを要求 (require clipboard)<br>貼り付けが行えない状況では同上
|-
| <code>L</code> || 行選択してから処理 (select Line)<br>モード RS はオプション L に変更
|-
| <code>O</code> || 非選択時はすべて選択してから処理 (select OR all)<br>※実験的機能(変更・廃止の可能性あり)
|}
* 複数指定する場合、空白で区切ります。順序は問いません。
* モードは必須でしたが、オプションは任意です。
* 上記以外の文字(オプション)を指定してもエラーにはなりません。<br>モード F/R/RS はオプション F/R/RS と認識されるが効果は無い、ということになります。
==旧仕様から新仕様への移行方法==
移行を補助するコマンドを用意しました。
※旧仕様の基本コード用。新仕様で使用する場合は、モード "RS" をオプション "L" に書き換えてください。
<pre style="word-wrap:break-word;">
f.push(["拡張コードを旧→新仕様に変換","RS",function(_){ //2019/05/01
  return _.replace(/^(\s*(?:\/\/[\/\s]*)?)f\.push\(\[("[^\\"]*(?:\\.[^\\"]*)*"|'[^\\']*(?:\\.[^\\']*)*'),"(RS?|F)",function\(([$0-9A-Za-z_]*)\)/gm,function(_,cmnt,name,mode,arg){return cmnt+'f.push(['+name+',"'+(mode==="RS"?"L":"")+'",function('+(mode==="F"&&arg?"_,":"")+arg+')'}) }]);
</pre>
旧仕様のコマンドに対し、上記のコマンドを実行すると、以下のように置換され、新仕様の基本コードで問題なく動作するようになります。
;モード F
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">F</span>",function(a) 以下略</samp></p>
:↓
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","",function(<span style="background:#fcc;">_,</span>a) 以下略</samp></p>
;モード R
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">R</span>",function(a) 以下略</samp></p>
:↓
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","",function(a) 以下略</samp></p>
;モード RS
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">RS</span>",function(a) 以下略</samp></p>
:↓
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">L</span>",function(a) 以下略</samp></p>
==変更点まとめ==
{|class="wikitable"
! 機能 !! 旧仕様 !! 新仕様 !! 備考
|-
! モード F
|style="background:#cfc;"| 利用可 ||style="background:#fc6;"| 動作不良
| 引数を利用しているものは動作しなくなります
|-
! モード R
|style="background:#cfc;"| 利用可 ||style="background:#fc6;"| 動作可
| 動作しますが、モード名の R は不要です(削除推奨)
|-
! モード RS
|style="background:#cfc;"| 利用可 ||style="background:#fc6;"| 動作不良
| 動作しますが、行選択はされなくなります
|-
! 戻り値制御
|style="background:#fc6;"| 動作不良 ||style="background:#cfc;"| 利用可
| 旧仕様では文字列以外に非対応、モード F では利用不可
|-
! 第2引数
|style="background:#fcc;"| 利用不可 ||style="background:#cfc;"| 利用可
| モード F は第2引数利用に移行
|-
! オプション
|style="background:#fcc;"| 利用不可 ||style="background:#cfc;"| 利用可
| モード RS はオプション L に移行
|}
==基本コード(新仕様)==
<b style="color:#c00;">※正式版では変更となる場合があります。</b>
<source lang="javascript">
//■テキスト整形
// 2013/09/15-2019/05/01
//■拡張コード:リスト
var f = [];
// ----------------- ↓ここより下に拡張コードを追加
// ----------------- ↑ここより上に拡張コードを追加
/*■拡張コード:書式
//▼コマンド
f.push(["コマンド名","オプション",function(引数){ 処理 }]);
//▽オプション
  s : 実行に範囲選択を要求 (require selection)
  c : 実行にクリップボードを要求 (require clipboard)
  L : 行選択してから処理 (select Line)
  O : 非選択時はすべて選択してから処理 (select OR all) ※実験的
//▽引数(arguments)
  第1引数 = Document.Selection.Text
  第2引数 = Document.Selection
//▽処理の戻り値(return value)
  String/Array以外 : 選択範囲に対し何もしない
  "abcd"          : abcd に置換後、abcd を選択
  ["ab","cd"]      : abcd に置換後、ab と cd の間に移動
  ["ab","cd",""]  : abcd に置換後、cd を選択
//▼サブメニュー
f.push(["メニュー名",function(f){ コマンドなど }]);
//▼セパレーター
f.push(["--------"]);
// ----------------- */
//■メソッド:連続置換
//"string".replaceBatch([ [検索1,置換1],[検索2,置換2],… ]);
String.prototype.replaceBatch = function(a){ //▲引数の型はチェックしてない
  var _this=this, i=0, n=a.length;
  if (!a[0]) i++;
  if (!a[n-1]) n--;
  for (; i<n; i++) {
    if (typeof a[i][0]==="string") //◆特殊文字をエスケープし、"g"フラグを付加
      a[i][0] = new RegExp(a[i][0].replace(/[$()*+.?\[\\\]^{|}]/g,"\\$&"),"g");
    _this = _this.replace(a[i][0],a[i][1]);
  }
  return _this;
};
(function(){
//■実行コード
var Sel = Document.Selection;
var isSelEmpty = Sel.IsEmpty;
var isClipEmpty = !ClipboardData.GetData();
var cmd = [], cid = 0; //コマンド, ID
//リストからメニュー作成
var Menu = CreatePopupMenu();
(function ListToMenu(List, Menu) {
  for (var i=0,_L; _L=List[i]; i++) {
    if (typeOf(_L[0]) !== "String") continue;
    switch (_L.length) {
    case 3: //◆コマンド      _L = ["コマンド名","オプション",function]
      if (typeOf(_L[1]) !== "String") break;
      if (typeOf(_L[2]) !== "Function") break;
      ++cid;
      var isGray = (/\bs\b/.test(_L[1]) && isSelEmpty)
                || (/\bc\b/.test(_L[1]) && isClipEmpty) ;
      Menu.Add(_L[0]+"\t"+_L[1], cid, isGray? meMenuGrayed: 0);
      if (isGray) break;
      cmd[cid] = { func: _L[2]
      , selLine:  /\bL\b/.test(_L[1])
      , selOrAll: /\bO\b/.test(_L[1])
      };
    break;
    case 2:  //◆サブメニュー  _L = ["メニュー名",function]
      if (typeOf(_L[1]) !== "Function") break;
      var sub_f = [], sub_Menu = CreatePopupMenu();
      _L[1](sub_f);
      Menu.AddPopup(_L[0], sub_Menu);
      ListToMenu(sub_f, sub_Menu); //再帰:サブメニュー作成
    break;
    case 1:  //◆セパレーター  _L = ["--------"]
      if (!/^-+$/.test(_L[0])) break;
      Menu.Add("", 0, meMenuSeparator);
    break;
    }
  }
}(f, Menu));
//ポップアップメニューを表示して処理
main: if (cid = Menu.Track(mePosMouse)) { //※キャンセル(Esc)時は 0 で終了
  var invertPos = Sel.GetActivePos() < Sel.GetAnchorPos();
  if (cmd[cid].selLine) selectLine(Sel);
  if (cmd[cid].selOrAll && isSelEmpty) Sel.SelectAll();
  var res = cmd[cid].func(Sel.Text, Sel);
  if (typeOf(res) === "String") res = ["",res,""];
  if (typeOf(res) !== "Array") break main;
 
  var head = res.shift(), tail = res.pop() || "", body = res.join("");
  Sel.Text = head + body + tail;
  var rear = Sel.GetActivePos() - tail.replace(/\r\n/g,"\n").length;
  var fore = rear              - body.replace(/\r\n/g,"\n").length;
    //※CR+LFを含むクリップボードが返された場合、1字と数えなければズレる
  Sel.SetActivePos(invertPos? fore: rear);
  Sel.SetAnchorPos(invertPos? rear: fore);
}
//■型判別
function typeOf(obj) {
  return (obj===null)? "Null": (obj==null)? "Undefined":
    Object.prototype.toString.call(obj).slice(8,-1); //"[object Type]".slice
};
//■論理行選択
function selectLine(Sel) {
  var ty = Sel.GetTopPointY(mePosLogical);
  var by = Sel.GetBottomPointY(mePosLogical);
  var bx = Sel.GetBottomPointX(mePosLogical);
  var nn =(Sel.Text.match(/\n/g)||[]).length;
  if (bx===1 && nn<=by-ty && nn) by--; //末尾改行は除く
    //下端が論理行頭 && 矩形選択でない && 2行以上選択
  Sel.SetActivePoint(mePosLogical, 1, by);
  Sel.EndOfLine(false, mePosLogical);
  Sel.SetAnchorPoint(mePosLogical, 1, ty);
};
}());
</source>
==拡張コード テンプレート(新仕様)==
<b style="color:#c00;">※正式版では変更となる場合があります。</b>
<pre style="word-wrap:break-word;">
f.push(["拡張コード テンプレート",function(f){ //2019/05/01
f.push(["コマンド","",function(_,$){
  $.StartOfLine(); return ['f.push(["コマンド","",function(_,$){\n  ' , ' }]);\n'] }]);
f.push(["コマンド s (選択必須)","",function(_,$){
  $.StartOfLine(); return ['f.push(["コマンド","s",function(_,$){\n  return _.replace(' , ') }]);\n'] }]);
f.push(["コマンド L (行選択)","",function(_,$){
  $.StartOfLine(); return ['f.push(["コマンド","L",function(_,$){\n  return _.replace(' , ') }]);\n'] }]);
f.push(["サブメニュー","L",function(_){
  return ['f.push(["' , 'メニュー' , '",function(f){\n'+ _ +'\n}]);'] }]);
f.push(["セパレーター","",function(_,$){
  $.StartOfLine(); return ['f.push(["--------"]);\n' , ''] }]);
}]);
</pre>
==記事ソースから拡張コードを抽出==
「テキスト整形」の記事ソースから拡張コードを抽出するコマンドを用意しました。
※旧仕様・新仕様兼用。文書全体を対象に置換が行われます。
<pre style="word-wrap:break-word;">
f.push(["記事ソースから拡張コードを抽出","F",function(){ //2019/05/01
  var $=Document.Selection; $.SelectAll(); var src=$.Text.match(/(?:^|\n)=拡張コード(?::コマンド)?=\n[\s\S]*?(\n==[\s\S]*?)(?:\n=拡張コード:書式=\n|$)/); if(src)$.Text=src[1].replace(/\n==([^=]+)==\n([\s\S]*?)(?=\n==[^=]|$)/g,function(_,ctg,src){var cmd=[], res, re=new RegExp(/^<pre(?: [^>]*)?>\s*(f\.push\(\[[\s\S]*?\]\);)\s*<\/pre>/gm); while(res=re.exec(src)){cmd.push(res[1])} return 'f.push(["'+ctg+'",function(f){\n\n'+ cmd.join("\n\n") +'\n\n}]);\n\n'}).slice(0,-1) }]);
</pre>
<b>使用手順</b>
# 「[[テキスト整形]]」の記事上部にある [編集] タブをクリックする。<br>または、記事の「[[テキスト整形#拡張コード|拡張コード]]」見出しの [編集] リンクをクリックする。
# 記事編集ページに移動するので、テキストボックス内の文章をすべて選択し、コピーする。<br>※テキストボックス内を右クリックしてメニューから実行するのが一番わかりやすいと思われます。
#* コピーした後は戻る(ブラウザバック)か閉じるかして記事編集ページから離れてください。
# Mery で文書を新規作成し、コピーした文章を貼り付ける。
# 「テキスト整形」マクロを起動し、「記事ソースから拡張コードを抽出」コマンドを実行する。
=マクロ「テキスト整形」の仕様変更について (2015/02/14)=
=マクロ「テキスト整形」の仕様変更について (2015/02/14)=


11行目: 298行目:
* コマンド実行中にカーソル位置を移動させる処理がある場合には対応できません。
* コマンド実行中にカーソル位置を移動させる処理がある場合には対応できません。


==モード RM の廃止==
==モード RM を廃止==
モード RM (一行ごとに処理) は、マクロ「[[テキスト変換]]」の関数タイプ replaceL に相当し、モード RS で実現可能な処理をより簡素に記述するために設けられた機能です。<br>
モード RM (一行ごとに処理) は、マクロ「[[テキスト変換]]」の関数タイプ replaceL に相当し、モード RS で実現可能な処理をより簡素に記述するために設けられた機能です。<br>
しかし、以下のような難点が見受けられることから、廃止を考えるに至りました。
しかし、以下のような難点が見受けられることから、廃止を考えるに至りました。
46行目: 333行目:
</source>
</source>


==モード CR の廃止、String.replaceBatch メソッドの追加==
==モード CR を廃止、replaceBatch メソッドを追加==
モード CR (連続置換) は、<code>replace</code> を連鎖させる記法(メソッドチェーン)をより簡素に記述するために設けられた機能です。
モード CR (連続置換) は、<code>replace</code> を連鎖させる記法(メソッドチェーン)をより簡素に記述するために設けられた機能です。
<source lang="javascript">
<source lang="javascript">
75行目: 362行目:


===モード CR → R (replaceBatch) への移行方法===
===モード CR → R (replaceBatch) への移行方法===
基本的にモード CR を継承した機能であるため、手直しすることで移行が可能です。
以下のように書き換えることで移行が可能です。
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">CR</span>",function(a){<br>&nbsp;&nbsp;return <span style="background:#fcc;">[</span> [検索1,置換1],[検索2,置換2],[検索3,置換3]… <span style="background:#fcc;">]</span>}]);</samp></p>
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">CR</span>",function(a){<br>&nbsp;&nbsp;return <span style="background:#fcc;">[</span> [検索1,置換1],[検索2,置換2] 中略 <span style="background:#fcc;">]</span>}]);</samp></p>
:↓
:↓
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">R</span>",function(a){<br>&nbsp;&nbsp;return <span style="background:#fcc;">a.replaceBatch([</span> [検索1,置換1],[検索2,置換2],[検索3,置換3]… <span style="background:#fcc;">]) </span>}]);</samp></p>
<p style="background:#f9f9f9; border:1px dashed #999; padding:6px;"><samp>f.push(["コマンド名","<span style="background:#fcc;">R</span>",function(a){<br>&nbsp;&nbsp;return <span style="background:#fcc;">a.replaceBatch([</span> [検索1,置換1],[検索2,置換2] 中略 <span style="background:#fcc;">]) </span>}]);</samp></p>


==変更点まとめ==
==変更点まとめ==
{| class="wikitable"
{|class="wikitable"
! 機能 !! 旧仕様 !! 新仕様 !! 備考
! 機能 !! 旧仕様 !! 新仕様 !! 備考
|-
|-
98行目: 385行目:
! モード RM
! モード RM
|style="background:#cfc;"| 利用可 ||style="background:#fcc;"| 廃止
|style="background:#cfc;"| 利用可 ||style="background:#fcc;"| 廃止
| モード RS に作り替え
| モード RS で作り直し
|-
|-
! モード CR
! モード CR
106行目: 393行目:
! replaceBatch
! replaceBatch
|style="background:#fcc;"| エラー ||style="background:#cfc;"| 利用可
|style="background:#fcc;"| エラー ||style="background:#cfc;"| 利用可
| ※旧仕様ではコマンド実行時にエラーが発生する
| 旧仕様ではコマンド実行時にエラーが発生する
|}
|}

2019年5月1日 (水) 15:39時点における版

マクロ「テキスト整形」の仕様変更について (2019/05/01)

マクロ「テキスト整形」の処理の流れを「前処理」「本処理」「後処理」の3段階に分けて考えたとき、モード (F/R/RS) 別に異なっていた流れを統合し、モードを廃止しようと考えています。

旧仕様
モード 前処理 本処理 後処理
F なし 引数に Selection を渡して処理 なし
R なし 引数に Selection.Text を渡して処理 戻り値で選択範囲を置換、再選択
RS 選択範囲を行全体に拡張
新仕様
オプションで指定 第1引数に Selection.Text を、
第2引数に Selection を渡して処理
戻り値で置換・再選択を制御

後処理:戻り値による選択範囲の置換・再選択の制御

Mery 2.7.0 にて「マクロで Chakra (jscript9.dll) における Quit メソッドをサポート外にした」(公式記事)ことを受け、戻り値により後処理(選択範囲の置換・再選択)を制御できる仕組みを導入します。

String (文字列) か Array (配列) を return すると、選択範囲を置換してカーソルを移動します。それ以外 (undefined など) の場合は、選択範囲に対し何もしません。
Quit(); (処理を中断) の代替策として return; (undefined を返す) などとすることで処理を終了させることができます。

f.push(["例1","",function(){
  return "abcd" }]);
f.push(["例2","",function(){
  return ["ab","cd"] }]);
f.push(["例3","",function(){
  return ["ab","cd",""] }]);
f.push(["例4","",function(){
  return; }]);
動作
"abcd" abcd に置換後、abcd を選択 = ["","abcd",""]
["ab","cd"] abcd に置換後、ab と cd の間に移動
["ab","cd",""] abcd に置換後、cd を選択
String / Array 以外 選択範囲に対し何もしない

本処理:第2引数を復活

マクロ「テキスト変換」の第2引数を復活し、第1引数に Document.Selection.Text を、第2引数に Document.Selection を渡すようにします。

「テキスト整形」で第2引数を削除したのは、「テキスト変換」で Selection を利用したコマンドが無かったこと、戻り値で選択範囲が置換されるため Selection を利用しづらいと感じたことが理由でした。

「テキスト整形」でモード F を設け分離したのも、そうした理由によりますが、戻り値で選択範囲を置換するかしないか制御できるようになったため状況が変わりました。

前処理:モードを廃止、オプションに変更

「モード」を「オプション」に変更し、機能を追加します。

s 実行に範囲選択を要求 (require selection)
非選択時はメニュー項目がグレーになり実行不可
c 実行にクリップボードを要求 (require clipboard)
貼り付けが行えない状況では同上
L 行選択してから処理 (select Line)
モード RS はオプション L に変更
O 非選択時はすべて選択してから処理 (select OR all)
※実験的機能(変更・廃止の可能性あり)
  • 複数指定する場合、空白で区切ります。順序は問いません。
  • モードは必須でしたが、オプションは任意です。
  • 上記以外の文字(オプション)を指定してもエラーにはなりません。
    モード F/R/RS はオプション F/R/RS と認識されるが効果は無い、ということになります。

旧仕様から新仕様への移行方法

移行を補助するコマンドを用意しました。

※旧仕様の基本コード用。新仕様で使用する場合は、モード "RS" をオプション "L" に書き換えてください。

f.push(["拡張コードを旧→新仕様に変換","RS",function(_){ //2019/05/01
  return _.replace(/^(\s*(?:\/\/[\/\s]*)?)f\.push\(\[("[^\\"]*(?:\\.[^\\"]*)*"|'[^\\']*(?:\\.[^\\']*)*'),"(RS?|F)",function\(([$0-9A-Za-z_]*)\)/gm,function(_,cmnt,name,mode,arg){return cmnt+'f.push(['+name+',"'+(mode==="RS"?"L":"")+'",function('+(mode==="F"&&arg?"_,":"")+arg+')'}) }]);

旧仕様のコマンドに対し、上記のコマンドを実行すると、以下のように置換され、新仕様の基本コードで問題なく動作するようになります。

モード F

f.push(["コマンド名","F",function(a) 以下略

f.push(["コマンド名","",function(_,a) 以下略

モード R

f.push(["コマンド名","R",function(a) 以下略

f.push(["コマンド名","",function(a) 以下略

モード RS

f.push(["コマンド名","RS",function(a) 以下略

f.push(["コマンド名","L",function(a) 以下略

変更点まとめ

機能 旧仕様 新仕様 備考
モード F 利用可 動作不良 引数を利用しているものは動作しなくなります
モード R 利用可 動作可 動作しますが、モード名の R は不要です(削除推奨)
モード RS 利用可 動作不良 動作しますが、行選択はされなくなります
戻り値制御 動作不良 利用可 旧仕様では文字列以外に非対応、モード F では利用不可
第2引数 利用不可 利用可 モード F は第2引数利用に移行
オプション 利用不可 利用可 モード RS はオプション L に移行

基本コード(新仕様)

※正式版では変更となる場合があります。

//■テキスト整形
// 2013/09/15-2019/05/01

//■拡張コード:リスト
var f = [];
// ----------------- ↓ここより下に拡張コードを追加

// ----------------- ↑ここより上に拡張コードを追加
/*■拡張コード:書式
//▼コマンド
f.push(["コマンド名","オプション",function(引数){ 処理 }]);
//▽オプション
  s : 実行に範囲選択を要求 (require selection)
  c : 実行にクリップボードを要求 (require clipboard)
  L : 行選択してから処理 (select Line)
  O : 非選択時はすべて選択してから処理 (select OR all) ※実験的
//▽引数(arguments)
  第1引数 = Document.Selection.Text
  第2引数 = Document.Selection
//▽処理の戻り値(return value)
  String/Array以外 : 選択範囲に対し何もしない
  "abcd"           : abcd に置換後、abcd を選択
  ["ab","cd"]      : abcd に置換後、ab と cd の間に移動
  ["ab","cd",""]   : abcd に置換後、cd を選択
//▼サブメニュー
f.push(["メニュー名",function(f){ コマンドなど }]);
//▼セパレーター
f.push(["--------"]);
// ----------------- */

//■メソッド:連続置換
//"string".replaceBatch([ [検索1,置換1],[検索2,置換2],… ]);
String.prototype.replaceBatch = function(a){ //▲引数の型はチェックしてない
  var _this=this, i=0, n=a.length;
  if (!a[0]) i++;
  if (!a[n-1]) n--;
  for (; i<n; i++) {
    if (typeof a[i][0]==="string") //◆特殊文字をエスケープし、"g"フラグを付加
      a[i][0] = new RegExp(a[i][0].replace(/[$()*+.?\[\\\]^{|}]/g,"\\$&"),"g");
    _this = _this.replace(a[i][0],a[i][1]);
  }
  return _this;
};

(function(){
//■実行コード
var Sel = Document.Selection;
var isSelEmpty = Sel.IsEmpty;
var isClipEmpty = !ClipboardData.GetData();
var cmd = [], cid = 0; //コマンド, ID
//リストからメニュー作成
var Menu = CreatePopupMenu();
(function ListToMenu(List, Menu) {
  for (var i=0,_L; _L=List[i]; i++) {
    if (typeOf(_L[0]) !== "String") continue;
    switch (_L.length) {
    case 3: //◆コマンド      _L = ["コマンド名","オプション",function]
      if (typeOf(_L[1]) !== "String") break;
      if (typeOf(_L[2]) !== "Function") break;
      ++cid;
      var isGray = (/\bs\b/.test(_L[1]) && isSelEmpty)
                || (/\bc\b/.test(_L[1]) && isClipEmpty) ;
      Menu.Add(_L[0]+"\t"+_L[1], cid, isGray? meMenuGrayed: 0);
      if (isGray) break;
      cmd[cid] = { func: _L[2]
      , selLine:  /\bL\b/.test(_L[1])
      , selOrAll: /\bO\b/.test(_L[1])
      };
    break;
    case 2:  //◆サブメニュー  _L = ["メニュー名",function]
      if (typeOf(_L[1]) !== "Function") break;
      var sub_f = [], sub_Menu = CreatePopupMenu();
      _L[1](sub_f);
      Menu.AddPopup(_L[0], sub_Menu);
      ListToMenu(sub_f, sub_Menu); //再帰:サブメニュー作成
    break;
    case 1:  //◆セパレーター  _L = ["--------"]
      if (!/^-+$/.test(_L[0])) break;
      Menu.Add("", 0, meMenuSeparator);
    break;
    }
  }
}(f, Menu));
//ポップアップメニューを表示して処理
main: if (cid = Menu.Track(mePosMouse)) { //※キャンセル(Esc)時は 0 で終了
  var invertPos = Sel.GetActivePos() < Sel.GetAnchorPos();
  if (cmd[cid].selLine) selectLine(Sel);
  if (cmd[cid].selOrAll && isSelEmpty) Sel.SelectAll();
  var res = cmd[cid].func(Sel.Text, Sel);
  if (typeOf(res) === "String") res = ["",res,""];
  if (typeOf(res) !== "Array") break main;
  
  var head = res.shift(), tail = res.pop() || "", body = res.join("");
  Sel.Text = head + body + tail;
  var rear = Sel.GetActivePos() - tail.replace(/\r\n/g,"\n").length;
  var fore = rear               - body.replace(/\r\n/g,"\n").length;
    //※CR+LFを含むクリップボードが返された場合、1字と数えなければズレる
  Sel.SetActivePos(invertPos? fore: rear);
  Sel.SetAnchorPos(invertPos? rear: fore);
}

//■型判別
function typeOf(obj) {
  return (obj===null)? "Null": (obj==null)? "Undefined":
    Object.prototype.toString.call(obj).slice(8,-1); //"[object Type]".slice
};
//■論理行選択
function selectLine(Sel) {
  var ty = Sel.GetTopPointY(mePosLogical);
  var by = Sel.GetBottomPointY(mePosLogical);
  var bx = Sel.GetBottomPointX(mePosLogical);
  var nn =(Sel.Text.match(/\n/g)||[]).length;
  if (bx===1 && nn<=by-ty && nn) by--; //末尾改行は除く
    //下端が論理行頭 && 矩形選択でない && 2行以上選択
  Sel.SetActivePoint(mePosLogical, 1, by);
  Sel.EndOfLine(false, mePosLogical);
  Sel.SetAnchorPoint(mePosLogical, 1, ty);
};

}());

拡張コード テンプレート(新仕様)

※正式版では変更となる場合があります。

f.push(["拡張コード テンプレート",function(f){ //2019/05/01
f.push(["コマンド","",function(_,$){
  $.StartOfLine(); return ['f.push(["コマンド","",function(_,$){\n  ' , ' }]);\n'] }]);
f.push(["コマンド s (選択必須)","",function(_,$){
  $.StartOfLine(); return ['f.push(["コマンド","s",function(_,$){\n  return _.replace(' , ') }]);\n'] }]);
f.push(["コマンド L (行選択)","",function(_,$){
  $.StartOfLine(); return ['f.push(["コマンド","L",function(_,$){\n  return _.replace(' , ') }]);\n'] }]);
f.push(["サブメニュー","L",function(_){
  return ['f.push(["' , 'メニュー' , '",function(f){\n'+ _ +'\n}]);'] }]);
f.push(["セパレーター","",function(_,$){
  $.StartOfLine(); return ['f.push(["--------"]);\n' , ''] }]);
}]);

記事ソースから拡張コードを抽出

「テキスト整形」の記事ソースから拡張コードを抽出するコマンドを用意しました。

※旧仕様・新仕様兼用。文書全体を対象に置換が行われます。

f.push(["記事ソースから拡張コードを抽出","F",function(){ //2019/05/01
  var $=Document.Selection; $.SelectAll(); var src=$.Text.match(/(?:^|\n)=拡張コード(?::コマンド)?=\n[\s\S]*?(\n==[\s\S]*?)(?:\n=拡張コード:書式=\n|$)/); if(src)$.Text=src[1].replace(/\n==([^=]+)==\n([\s\S]*?)(?=\n==[^=]|$)/g,function(_,ctg,src){var cmd=[], res, re=new RegExp(/^<pre(?: [^>]*)?>\s*(f\.push\(\[[\s\S]*?\]\);)\s*<\/pre>/gm); while(res=re.exec(src)){cmd.push(res[1])} return 'f.push(["'+ctg+'",function(f){\n\n'+ cmd.join("\n\n") +'\n\n}]);\n\n'}).slice(0,-1) }]);

使用手順

  1. テキスト整形」の記事上部にある [編集] タブをクリックする。
    または、記事の「拡張コード」見出しの [編集] リンクをクリックする。
  2. 記事編集ページに移動するので、テキストボックス内の文章をすべて選択し、コピーする。
    ※テキストボックス内を右クリックしてメニューから実行するのが一番わかりやすいと思われます。
    • コピーした後は戻る(ブラウザバック)か閉じるかして記事編集ページから離れてください。
  3. Mery で文書を新規作成し、コピーした文章を貼り付ける。
  4. 「テキスト整形」マクロを起動し、「記事ソースから拡張コードを抽出」コマンドを実行する。

マクロ「テキスト整形」の仕様変更について (2015/02/14)

モード R/RS 実行後、処理した範囲を選択し直すように

旧仕様では、コマンド実行後は範囲選択が 解 除 されました。↵
新仕様では、コマンド実行前に選択範囲を 記 憶 し、実行後に 復 元 します。↵
処理の結果、字数が 増 減 した場合は、選択範囲が 調 整 
されます。↵

↓ コマンド「空白文字を除去」実行

旧仕様では、コマンド実行後は範囲選択が解除されました。↵
新仕様では、コマンド実行前に選択範囲を記憶し、実行後に復元します。↵
処理の結果、字数が増減した場合は、選択範囲が調整
されます。↵

この変更により、コマンドの効果範囲が目視できるようになり、また、選択し直す手間なく別の処理に移ることができるようになります。

  • モード R では、選択範囲の始点終点の位置関係が維持されます。
  • モード RS では、選択範囲を行頭行末まで拡張した状態が記憶されます。始点は上端行頭、終点は下端行末となります。
  • コマンド実行中にカーソル位置を移動させる処理がある場合には対応できません。

モード RM を廃止

モード RM (一行ごとに処理) は、マクロ「テキスト変換」の関数タイプ replaceL に相当し、モード RS で実現可能な処理をより簡素に記述するために設けられた機能です。
しかし、以下のような難点が見受けられることから、廃止を考えるに至りました。

  • モード RS より簡素に記述できるとは必ずしも限らない。
  • モード RS で実現可能=代替可能であり、RM ならではの利点が無い。
  • モード R/RS に比べ処理が異質で、コマンドを作る際はそれを考慮しなければならない。
    ※異質=R/RS が「選択範囲の文字列」を対象に処理を行うのに対し、RM は「選択範囲の各行の文字列」を対象に処理を行う。
//※モード RM では「クロージャ」という仕組みを使用する必要があり難解
f.push(["各行行頭に文字列付加...","RM",(function(a){
  var p=null; return function(a){ if(!p){ if(!(p=Prompt("各行行頭に付加する文字列:",""))){Quit()} } return p+a } })()]);
//※モード RS で replace(正規表現) を使用するほうが簡素に記述できる
f.push(["各行行頭に文字列付加...","RS",function(a){
  var p=Prompt("各行行頭に付加する文字列:",""); if(!p)Quit(); return a.replace(/^/gm,p) }]);
//※モード RM でも、
f.push(["空白行を削除","RM",function(a){
  return a.match(/^[  \t]*$/)? null:a }]);
//※モード RS でも、必要な知識や字数がほぼ変わらない時がある
f.push(["空白行を削除","RS",function(a){
  return a.replace(/^[  \t]*$\n?/gm,"") }]);
//※モード RM なら正規表現に頼らず簡素に記述できるコマンドもあるが、
f.push(["インデント(半角空白2個)","RM",function(a){
  return "  "+a }]);
f.push(["インデント(半角空白2個)","RS",function(a){
  return a.replace(/^/gm,"  ") }]);
//※正規表現を利用したコマンドも数多くあり、利点になりづらい
f.push(["行頭 \/\/ を付加/削除","RM",function(a){
  return a.match(/^\/\/.*$/)? a.slice(2): "\/\/"+a }]);
f.push(["行頭 \/\/ を付加/削除","RS",function(a){
  return a.replace(/^(\/\/)?/gm,function(s){return s? "": "\/\/"}) }]);

モード CR を廃止、replaceBatch メソッドを追加

モード CR (連続置換) は、replace を連鎖させる記法(メソッドチェーン)をより簡素に記述するために設けられた機能です。

f.push(["コマンド名","R",function(a){
  return a.replace(/ABC/g,"DEF").replace(/\(1\)/g,"①").replace(/GHI/gi,"JKL") }]);
//↓簡素化
f.push(["コマンド名","CR",function(a){
  return [ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]}]);

しかし、モードの一種としたがゆえに、他のモードでは利用できない事、replace のような使い方ができない事が難点としてありました。
replaceBatch メソッドは、この難点を解消するために設けられる機能です。

//※モード R で利用する(基本形)
f.push(["コマンド名","R",function(a){
  return a.replaceBatch([ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]) }]);
//※モード RS で利用する(選択範囲を拡張)
f.push(["コマンド名","RS",function(a){
  return a.replaceBatch([ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]) }]);
//※モード F で利用する(▲非推奨)
f.push(["コマンド名","F",function(a){
  a.Text = a.Text.replaceBatch([ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]) }]);
//※replace のようにメソッドチェーンが可能
f.push(["コマンド名","R",function(a){
  return a.replace(省略).replaceBatch([ 省略 ]).replace(省略) }]);

モード CR → R (replaceBatch) への移行方法

以下のように書き換えることで移行が可能です。

f.push(["コマンド名","CR",function(a){
  return [ [検索1,置換1],[検索2,置換2] 中略 ]}]);

f.push(["コマンド名","R",function(a){
  return a.replaceBatch([ [検索1,置換1],[検索2,置換2] 中略 ]) }]);

変更点まとめ

機能 旧仕様 新仕様 備考
モード F 利用可 利用可 変更なし
モード R 利用可 利用可 選択範囲を維持・調整するように
モード RS 利用可 利用可 選択範囲を維持・調整するように
モード RM 利用可 廃止 モード RS で作り直し
モード CR 利用可 廃止 replaceBatch メソッドに移行
replaceBatch エラー 利用可 旧仕様ではコマンド実行時にエラーが発生する
スポンサーリンク