web404

HTMLの終了タグを補完するマクロ

カーソル位置で閉じられていないHTML要素の終了タグを挿入します。

マクロで定義されている終了タグを補完しない要素以外の要素について、カーソル位置からファイル先頭に向かって開始タグと終了タグの数を比較し、閉じられていない要素の終了タグを挿入します。

HTMLの文法に厳密ではなく、積極的に終了タグを補完します。
要素名は編集中のファイルから取得しているので、未知の要素でも終了タグを補完します。
開始タグさえ入力すれば、終了タグの入力はすべてこのマクロで行えます。


ダウンロード

endtag.zip


更新履歴

Version 1.10 (2025/2/17)
やり直し操作をまとめた
範囲選択時の動作の修正
不要な処理の削除
ソースコードの整理
過去の履歴
Version 1.00 (2023/12/14)
公開

ソースコード

//設定
//終了タグを補完しない要素を「|」で区切って指定
$ve = "area|base|br|col|embed|hr|img|input|keygen|link|param|source|track|meta|wbr";	//空要素

setcompatiblemode 0x20200;
#x0 = x; #y0 = y; #x = x; #y = y;
#sel = selecting;
disabledraw;
begingroupundo;

if (#sel) {	//範囲選択時の処理
	#x = seltopx; #y = seltopy; #xe = selendx; #ye = selendy;
	escape;
	if (x == #x && y == #y) moveto #xe, #ye;
	call CheckTagArea;
	moveto #x, #y;
}

call CheckTagArea;

while (1) {
	searchup "<(?!" + $ve + ")[a-z][a-z0-9]*( [^>]+)?>", regular, nocasesense;
	if (!result) {
		if (x != #x0 || y != #y0) moveto #x0, #y0;
		beep;
		goto End;
	}
	$str = gettext(x, y, #x, #y);	//開始タグからカーソル位置までの文字列
	$stag = gettext(x, y, selendx, selendy);	//開始タグ
	#len = strstr($stag, " ") - 1;	//要素名の長さ
	if (#len < 0) #len = strlen($stag) - 2;
	$en = midstr($stag, 1, #len);	//要素名
	call CheckElement $str, $en;
	if (##return) break;
}

moveto #x, #y;
if (#sel) {
	beginsel;
	moveto #xe, #ye;
}
insert "</" + $en + ">";

goto End;

CheckTagArea:	//カーソル位置がタグ内なら警告音で終了
	##x = x; ##y = y;
	searchup "<[a-zA-Z/]", regular;
	if (!result) return;
	searchdown "[^ <=>?-]>", regular;
	if (result) {
		moveto selendx, selendy;
		if (##y < y || (##x < x && ##y == y)) {
			moveto #x0, #y0;
			beep;
			goto End;
		}
	}
	moveto ##x, ##y;
	return;

CheckElement:	//指定文字列における指定要素のタグの対応を調べる
	call Count $$1, "<" + $$2 + " ";
	##stag = ##return;	//開始タグの数
	call Count $$1, "<" + $$2 + ">";
	##stag = ##stag + ##return;
	call Count $$1, "</" + $$2 + ">";
	##etag = ##return;	//終了タグの数
	return ##stag > ##etag;

Count:	//文字列の出現回数を調べる
	##n = strstr($$1, $$2);	//文字列の出現位置
	##len = strlen($$2);	//文字列長
	##i = 0;
	while (##n > -1) {
		##i = ##i + 1;
		$$1 = midstr($$1, ##n + ##len);	//未検索文字列
		##n = strstr($$1, $$2);
	}
	return ##i;

End:	//終了処理
	endgroupundo;
	enabledraw;
	endmacro;

終了タグの省略や文法の正しさは考慮していません

個人的にも、終了タグは省略しない派です。
終了タグを省略すると、見づらくなったり、不具合の原因になりやすいからです。
もし特定の終了タグを省略する場合は、マクロファイルの設定項目に終了タグを補完しない要素を追加してください。

文法の検証はきりがないので、一切していません。
結局、文法は本人がわかっていないとマズいと思います。
編集の過程で、一時的に文法が正しくない状態になることもありますし。
逆に、おかしな終了タグが挿入されたら、そこまでの文法に誤りがあるとわかります。