////////////////////////////////////////////////////////////////////////////////
// html 入力支援
// の直後にを自動で補完します。
//
// インストール方法
// キー SHIFT + . にこのマクロを割り当ててください。
Main();
function Main()
{
// SHIFT + . で呼び出されるので、とりあえず > を出力する
Editor.Char(0x3e); // 0x3e = '>'
if(Editor.IsCurTypeExt("html")){
CompleteTagHtml(false);
}
}
// タグ補間
// bForce == true のとき
// カーソル位置に入るべき終了タグを探して挿入し
// カーソル位置をその直後に移す
// bForce == false のとき
// カーソル位置の直前に開始タグがある場合のみ
// それに対応する終了タグを挿入し
// カーソル位置を開始・終了タグの間に置く
// いずれの場合も
// 終了タグを挿入しなくてもいい場合は挿入しない
// 間違った終了タグが存在する場合は、
// まずそれを置き換えることを考えてみる
function CompleteTagHtml(bForce)
{
// 現在位置と全文
var xCur = Number(Editor.ExpandParameter("$x")) - 1;
var yCur = Number(Editor.ExpandParameter("$y")) - 1;
var asText = [];
var height = Editor.GetLineCount(0);
for(var y = 1; y <= height; ++y){
asText.push(Editor.GetLineStr(y));
}
// カーソルより左の全テキスト
var as = asText.slice(0, yCur+1);
as[yCur] = as[yCur].substring(0, xCur);
var sBack = as.join('');
// カーソルより左にある部分から、タグを抜き出す
var atTagBack = ExtractTags(sBack);
if(atTagBack.length <= 0) return;
// 強制補完を指定されなかったときは、なにもしないで終わる条件がいくつかある
if(! bForce){
var tTagLast = atTagBack[atTagBack.length - 1];
// 今しがた入力された '>' が タグの一部でなければなにもしない
if(tTagLast.posEnd != sBack.length) return;
// 今入力されたタグが終了タグなら、なにもしない
if(tTagLast.name.substring(0,2) == "") return;
}
// カーソルより右の全テキスト
as = asText.slice(yCur);
as[0] = as[0].substring(xCur);
var sFore = as.join('');
// カーソルより右にある部分から、タグを抜き出す
var atTagFore = ExtractTags(sFore);
// もし何も補完しなくてもよいならば、なにもしない
var err = {};
if(IsConsistent(atTagBack.concat(atTagFore), err)){
return;
}
var ixErr = err["ixTagEnd"];
// 補完すべきタグ
var sEndTag;
if(bForce){
// 最内でまだ開いたままのタグを選択
IsConsistent(atTagBack, err);
ix = err["ixTagBeg"];
if(ix != null){
sEndTag = "" + atTagBack[ix].name.substring(1);
}
else{
return;
}
}
else{
// 最後の一個
sEndTag = "" + atTagBack[atTagBack.length - 1].name.substring(1);
}
// 不整合な""がある場合、それを消して代わりに終了タグをいれてみる
if( ixErr != null
&& ixErr >= atTagBack.length
){ var ixErrFore = ixErr - atTagBack.length;
if( atTagFore[ixErrFore].name != sEndTag
){
var t = atTagFore[ixErrFore];
atTagFore[ixErrFore] = { "name": sEndTag };
// 整合性がとれるなら
if(IsConsistent(atTagBack.concat(atTagFore), {})){
// 置き換え
xyTag = PositionToXY(sBack.length + t.posBeg, asText);
lenTag = t.posEnd - t.posBeg;
GotoXY(xyTag.x, xyTag.y);
Editor.BeginSelect();
for(var i = 0; i < lenTag; ++i) Editor.Right();
Editor.InsText(sEndTag);
if(! bForce){
// カーソルを挿入前のところに戻す
GotoXY(xCur, yCur);
}
return;
}
atTagFore[ixErrFore] = t; // 元に戻す
}}
// 上でうまくいかなかったときは、そのまま挿入
Editor.InsText(sEndTag);
if(! bForce){
// カーソルを挿入前のところに戻す
GotoXY(xCur, yCur);
}
}
// タグを抜き出す -> atTags = array of { name, beg, end }
// name は もしくは の形式
function ExtractTags(s)
{
var atTags = [];
var re = /