[スレッド全体]

2012/2/4 (土) 11:21:02  
[546] 直前の閉じていない HTML/XML タグを閉じる

コツコツバージョンアップし続けていたものをリリースします。
今も需要あるかなあ?

/**********************************************************
**  Close Last Tag 3.0                                   **
***********************************************************
**  A "Sakura-Editor Macro" using WSH 5.6                **
**  For sakura.exe ver.1.6.2.0 and over                  **
**                               zlib/libpng ライセンス  **
**                      Copyright (C) 2004-2012 gis_dur  **
***********************************************************
**【動作】                                               **
**  カーソル位置より前にある HTML/XML タグを             **
**  ファイル先頭に向かって検索し、                       **
**  1. 終了タグのない開始タグ                            **
**  2. 閉じていないHTMLコメント        <!--      -->     **
**  3. 閉じていないCDATAブロック       <![CDATA[ ]]>     **
**  4. 閉じていないJSPスクリプトレット <%        %>      **
**  5. 閉じていないJSPコメント         <%--      --%>    **
**  がある場合、対応する終了タグを挿入します。           **
**  コメントタグ内は無視します。                         **
**********************************************************/

// サクラエディタ unicode 版の場合は true にする
var UNICODE_VER = true;
// xml モード(大文字・小文字を区別する)
var XML_MODE = false;
// 挿入した閉じタグの後ろにカーソルを移動するかどうか
var MOVE_CURSOR = true;
// 閉じタグを省略可能な要素名
var NO_CLOSE_TAG = "," +
[
  "area", "base", "basefont", "bgsound", "br",
  "col", "embed", "frame", "hr", "img",
  "input", "isindex", "keygen", "link", "meta",
  "nextid", "param", "spacer", "wbr"
].join(",") + ",";

/*********************************************************/

// シェル
if (typeof(Shell) == "undefined") {
  Shell = new ActiveXObject("WScript.Shell");
}

// 文字列拡張
if (typeof(String.prototype.is_wide) == "undefined") {
  String.prototype.is_wide = function() {
    if (UNICODE_VER) return false;
    if (this.length == 0) return false;
    var c = (this.length == 1) ? this : this.charAt(0);
    return (!c.match(/[ア-ンァィゥェォッャュョ゛゜ー、。「」・]/) && escape(c).length >= 4);
  };
}

(function() {
  if (!XML_MODE) {
    NO_CLOSE_TAG = NO_CLOSE_TAG.toUpperCase();
  }
  // タグを表す正規表現
  var TAG_CHARS = "s!\"#$%&\'()=~|^\\`{+*}<>?@[;],/";
  (function(){
    var tmp = "";
    for (var i=0; i<TAG_CHARS.length; i++) {
      tmp += "\\"+TAG_CHARS.charAt(i);
    }
    TAG_CHARS = "[^"+tmp+"]+";
  })();
  var TAGS_EXPRESSION = new RegExp();
  TAGS_EXPRESSION.compile("<!--|-->|<!\\[CDATA\\[|\\]\\]>|<%--|--%>|<%|%>|<"+TAG_CHARS+"([^>]*/>)?|<\\/"+TAG_CHARS+"", "g");
 
  // データ
  var stack = new Array();
  var ins_text = "";
  var err_text = "";
 
  // ステート
  var is_comment = false;
  var is_cdata = false;
  var is_jsp_comment = false;
  var is_jsp = false;
  var is_error = false;
 
  // テキストをすべて取得
  Editor.CancelMode(0);
  var cursorX = Number(Editor.ExpandParameter('$x')) - 1;
  var cursorY = Number(Editor.ExpandParameter('$y')) - 1;
  Editor.SelectAll(0);
  var all_text = Editor.GetSelectedString(0);
  Editor.CancelMode(0);
 
  // 改行を統一
  all_text = all_text.replace(/\r\n|\r|\n/g, "\n");
  var all_lines = all_text.split("\n");
  var num_lines = all_lines.length;
 
  // カーソル以前のテキストをすべて取得
  var tmp_text = all_lines[cursorY];
  if (tmp_text == null) {
    tmp_text = "";
  }
  if (num_lines-(cursorY+1) > 0) {
    all_lines.splice(cursorY+1, num_lines-(cursorY+1));
  }
  for (var i=0; i<cursorX; i++) {
    if (tmp_text.charAt(i).is_wide()) {
      cursorX--;
    }
  }
  all_lines[cursorY] = tmp_text.substring(0, cursorX);
 
  // タグを取得
  var all_tags = all_lines.join(" ").match(TAGS_EXPRESSION);
  var num_tags = (all_tags == null)? 0: all_tags.length;
 
  // 直前の開始タグを検索
  for (var i=num_tags-1; i>=0; i--) {
    // タグ文字列取得
    var now_text = all_tags[i];
    if (!XML_MODE) {
      now_text = now_text.toUpperCase();
    }
 
    // 特殊なステートにある場合
    if (is_comment) {
      if (now_text == "<!--") {
        is_comment = false;
      }
      continue;
    }
    else if (is_cdata) {
      if (now_text == "<![CDATA[") {
        is_cdata = false;
      }
      continue;
    }
    else if (is_jsp_comment) {
      if (now_text == "<%--") {
        is_jsp_comment = false;
      }
      continue;
    }
    else if (is_jsp) {
      if (now_text == "<%") {
        is_jsp = false;
      }
      continue;
    }
 
    // 特殊なステートに遷移する場合
    if (now_text == "-->") {
      is_comment = true;
      continue;
    }
    else if (now_text == "<!--") {
      ins_text = "-->";
      break;
    }
    else if (now_text == "]]>") {
      is_cdata = true;
      continue;
    }
    else if (now_text == "<![CDATA[") {
      ins_text = "]]>";
      break;
    }
    else if (now_text == "--%>") {
      is_jsp_comment = true;
      continue;
    }
    else if (now_text == "<%--") {
      ins_text = "--%>";
      break;
    }
    else if (now_text == "%>") {
      is_jsp = true;
      continue;
    }
    else if (now_text == "<%") {
      ins_text = "%>";
      break;
    }
    // 閉じタグ不要
    else if (now_text.indexOf("/>") != -1) {
      continue;
    }
    // 閉じタグのスタックに追加
    else if (now_text.indexOf("</") == 0) {
      now_text = now_text.substring(2);
      stack.push(now_text);
      continue;
    }
 
    // 要素名取得
    now_text = now_text.substring(1);
 
    // 省略可能なタグの場合
    if (NO_CLOSE_TAG.indexOf(","+now_text+",") != -1) {
      if (stack.length == 0) {
        continue;
      }
      else {
        tmp_text = stack.pop();
        if (now_text != tmp_text) {
          stack.push(tmp_text);
        }
        continue;
      }
    }
    // 通常のタグの場合
    else {
      if (stack.length == 0) {
        ins_text = "</" + all_tags[i].substring(1) + ">";
        break;
      }
      else {
        tmp_text = stack.pop();
        if (now_text != tmp_text) {
          is_error = true;
          err_text += "タグの包含関係が不正です。\r\n";
          err_text += "<"+now_text+"> ... ... </"+tmp_text+">\r\n";
          stack = new Array();
          break;
        }
      }
    }
  }
 
  // コメント中
  if (is_comment) {
    is_error = true;
    err_text += "コメントの開閉関係が不正です。\r\n";
  }
  // CDATA 中
  else if (is_cdata) {
    is_error = true;
    err_text += "CDATA ブロックの開閉関係が不正です。\r\n";
  }
  // JSP コメント中
  else if (is_jsp_comment) {
    is_error = true;
    err_text += "JSP コメントの開閉関係が不正です。\r\n";
  }
  // JSP 中
  else if (is_jsp) {
    is_error = true;
    err_text += "JSP スクリプトレットの開閉関係が不正です。\r\n";
  }
  // スタックに終了タグあり
  else if (stack.length > 0) {
    is_error = true;
    err_text += "開始タグのない終了タグが見つかりました。\r\n";
    for (var i=0; i<stack.length; i++) {
      err_text += "<"+stack[i]+">\r\n";
    }
  }
 
  // エラーダイアログ表示
  if (is_error) {
    Shell.Popup(err_text, 0, "文法エラー", 0);
    return;
  }
 
  // 終了タグの挿入
  Editor.InsText(ins_text);
 
  // カーソルを動かさない場合は、元の位置に戻す
  if (!MOVE_CURSOR) {
    for (var i=0; i<ins_text.length; i++) {
      Editor.Left(0);
    }
  }
})();
gis_dur
Mozilla/5.0 (Windows NT 5.1; rv:10.0) Gecko/20100101 Firefox/10.0

[▼次のスレッド]
INCM/CMT
Cyclamen v3.81