Masme (トーク | 投稿記録)
マクロ「テキスト整形」の仕様変更について
MSY-07 (トーク | 投稿記録)
非推奨のタグを修正
 
(他の1人の利用者による、間の2版が非表示)
34行目: 34行目:
<code>Quit();</code> (処理を中断) の代替策として <code>return;</code> (undefined を返す) などとすることで処理を終了させることができます。
<code>Quit();</code> (処理を中断) の代替策として <code>return;</code> (undefined を返す) などとすることで処理を終了させることができます。


<source lang="javascript">
<syntaxhighlight lang="javascript">
f.push(["例1","",function(){
f.push(["例1","",function(){
   return "abcd" }]);
   return "abcd" }]);
43行目: 43行目:
f.push(["例4","",function(){
f.push(["例4","",function(){
   return; }]);
   return; }]);
</source>
</syntaxhighlight>
{|class="wikitable"
{|class="wikitable"
! 例 !! 動作
! 例 !! 動作
81行目: 81行目:
移行を補助するコマンドを用意しました。
移行を補助するコマンドを用意しました。


※旧仕様の基本コード用。新仕様で使用する場合は、モード "RS" をオプション "L" に書き換えてください。
※新仕様の基本コード用。旧仕様で使用する場合は、オプション "L" をモード "RS" に書き換えてください。
<pre style="word-wrap:break-word;">
<pre>
f.push(["拡張コードを旧→新仕様に変換","RS",function(_){ //2019/05/01
f.push(["拡張コードを旧→新仕様に変換","L",function(_){ //2019/06/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+')'}) }]);
   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>
</pre>
128行目: 128行目:
| モード RS はオプション L に移行
| モード 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;">
<pre>
f.push(["記事ソースから拡張コードを抽出","F",function(){ //2019/05/01
f.push(["記事ソースから拡張コードを抽出","",function(_,$){ //2019/12/21
   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) }]);
   $.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].replace(/&amp;amp;/g,"&"))} return 'f.push(["'+ctg+'",function(f){\n\n'+ cmd.join("\n\n") +'\n\n}]);\n\n'}).slice(0,-1) }]);
</pre>
</pre>
<b>使用手順</b>
<b>使用手順</b>
304行目: 162行目:
* モード RS で実現可能=代替可能であり、RM ならではの利点が無い。
* モード RS で実現可能=代替可能であり、RM ならではの利点が無い。
* モード R/RS に比べ処理が異質で、コマンドを作る際はそれを考慮しなければならない。<br>※異質=R/RS が「選択範囲の文字列」を対象に処理を行うのに対し、RM は「選択範囲の各行の文字列」を対象に処理を行う。
* モード R/RS に比べ処理が異質で、コマンドを作る際はそれを考慮しなければならない。<br>※異質=R/RS が「選択範囲の文字列」を対象に処理を行うのに対し、RM は「選択範囲の各行の文字列」を対象に処理を行う。
<source lang="javascript" style="word-wrap:break-word;">
<syntaxhighlight lang="javascript">
//※モード RM では「クロージャ」という仕組みを使用する必要があり難解
//※モード RM では「クロージャ」という仕組みを使用する必要があり難解
f.push(["各行行頭に文字列付加...","RM",(function(a){
f.push(["各行行頭に文字列付加...","RM",(function(a){
311行目: 169行目:
f.push(["各行行頭に文字列付加...","RS",function(a){
f.push(["各行行頭に文字列付加...","RS",function(a){
   var p=Prompt("各行行頭に付加する文字列:",""); if(!p)Quit(); return a.replace(/^/gm,p) }]);
   var p=Prompt("各行行頭に付加する文字列:",""); if(!p)Quit(); return a.replace(/^/gm,p) }]);
</source>
</syntaxhighlight>
<source lang="javascript" style="word-wrap:break-word;">
<syntaxhighlight lang="javascript">
//※モード RM でも、
//※モード RM でも、
f.push(["空白行を削除","RM",function(a){
f.push(["空白行を削除","RM",function(a){
319行目: 177行目:
f.push(["空白行を削除","RS",function(a){
f.push(["空白行を削除","RS",function(a){
   return a.replace(/^[  \t]*$\n?/gm,"") }]);
   return a.replace(/^[  \t]*$\n?/gm,"") }]);
</source>
</syntaxhighlight>
<source lang="javascript" style="word-wrap:break-word;">
<syntaxhighlight lang="javascript">
//※モード RM なら正規表現に頼らず簡素に記述できるコマンドもあるが、
//※モード RM なら正規表現に頼らず簡素に記述できるコマンドもあるが、
f.push(["インデント(半角空白2個)","RM",function(a){
f.push(["インデント(半角空白2個)","RM",function(a){
331行目: 189行目:
f.push(["行頭 \/\/ を付加/削除","RS",function(a){
f.push(["行頭 \/\/ を付加/削除","RS",function(a){
   return a.replace(/^(\/\/)?/gm,function(s){return s? "": "\/\/"}) }]);
   return a.replace(/^(\/\/)?/gm,function(s){return s? "": "\/\/"}) }]);
</source>
</syntaxhighlight>


==モード CR を廃止、replaceBatch メソッドを追加==
==モード CR を廃止、replaceBatch メソッドを追加==
モード CR (連続置換) は、<code>replace</code> を連鎖させる記法(メソッドチェーン)をより簡素に記述するために設けられた機能です。
モード CR (連続置換) は、<code>replace</code> を連鎖させる記法(メソッドチェーン)をより簡素に記述するために設けられた機能です。
<source lang="javascript">
<syntaxhighlight lang="javascript">
f.push(["コマンド名","R",function(a){
f.push(["コマンド名","R",function(a){
   return a.replace(/ABC/g,"DEF").replace(/\(1\)/g,"①").replace(/GHI/gi,"JKL") }]);
   return a.replace(/ABC/g,"DEF").replace(/\(1\)/g,"①").replace(/GHI/gi,"JKL") }]);
341行目: 199行目:
f.push(["コマンド名","CR",function(a){
f.push(["コマンド名","CR",function(a){
   return [ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]}]);
   return [ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]}]);
</source>
</syntaxhighlight>
しかし、モードの一種としたがゆえに、他のモードでは利用できない事、<code>replace</code> のような使い方ができない事が難点としてありました。<br>
しかし、モードの一種としたがゆえに、他のモードでは利用できない事、<code>replace</code> のような使い方ができない事が難点としてありました。<br>
<code>replaceBatch</code> メソッドは、この難点を解消するために設けられる機能です。
<code>replaceBatch</code> メソッドは、この難点を解消するために設けられる機能です。
<source lang="javascript">
<syntaxhighlight lang="javascript">
//※モード R で利用する(基本形)
//※モード R で利用する(基本形)
f.push(["コマンド名","R",function(a){
f.push(["コマンド名","R",function(a){
354行目: 212行目:
f.push(["コマンド名","F",function(a){
f.push(["コマンド名","F",function(a){
   a.Text = a.Text.replaceBatch([ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]) }]);
   a.Text = a.Text.replaceBatch([ ["ABC","DEF"],["(1)","①"],[/GHI/gi,"JKL"] ]) }]);
</source>
</syntaxhighlight>
<source lang="javascript">
<syntaxhighlight lang="javascript">
//※replace のようにメソッドチェーンが可能
//※replace のようにメソッドチェーンが可能
f.push(["コマンド名","R",function(a){
f.push(["コマンド名","R",function(a){
   return a.replace(省略).replaceBatch([ 省略 ]).replace(省略) }]);
   return a.replace(省略).replaceBatch([ 省略 ]).replace(省略) }]);
</source>
</syntaxhighlight>


===モード CR → R (replaceBatch) への移行方法===
===モード CR → R (replaceBatch) への移行方法===
スポンサーリンク