サクラエディタで文書内の数値を増減させるマクロ

サクラエディタ内のカーソル位置にある数字を増減操作します。

ネタ的にはありふれたものですが、ドキュメントクッキーにデータ保持して色々やってます。

増加用と減少用の2つセットでの使用前提です。

<details><summary>**マクロコード**</summary><div>
**増加用** (numCtrl.js)

~~~~javascript:numCtrl.js
/**** 数値操作マクロ **************************************
カーソル位置の数値を操作するマクロ。
増加用 & 減少用の2つセットで使用。

負数(0未満)にはならない。数値の種類で挙動変化。

* 10進数 (整数/実数)
	常に最少桁の値を操作。
	連続操作(割り当てキー押しっぱ)で増減量が増加。
	実数の場合、少数点以下の桁数を維持。
	+ 0埋め10進数値
		数値の桁数は維持。最大値を超えると0に、0を下回ると最大値へと循環。

* 2進数:  0b01010101
* 8進数:  0o01234567
* 16進数: 0x1234ABCD・#1188FF・&#x60; 等
	カーソル左側の数値を操作、連続操作でも増減量は一定。
	数値の桁数は維持。最大値を超えると0に、0を下回ると最大値へと循環。

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

//基本増減量
var NUM_STEP = 1;

//10進数操作時の増減加速[a,b,c]: c㍉秒内の間隔でb回同じ操作をすると増減値*a倍化
var NUM_ACCEL = [2,20,80];

//操作後の待機時間[ms]
var INTERVAL = 0;

//16進数アルファベット大文字化の既定値
var UPPER_HEX = 0;

//10進数値をステータスバーに表示: [ 初期値 ]%N進数 => 10進数値 (2以上ならN桁区切り)
var SHOW_DECIMAL = 1;

//全角数字も扱う?
var USE_MBN = 0;
//漢数字も扱う?
var USE_CJK = 0;

//数値以外の場所での既定動作
var DEF_FUNC = function(){return NUM_STEP>0?WheelLeft():WheelRight()}

var nre = {//数値判定用の正規表現
	'1' :[/()(\d*\x01\d*\.\d+\b|\d*\.\d*\x01\d+\b|\d*\.\d+\x01)()/],//実数
	'2' :[/\b(0b)([01]*\x01[01]+\b|[01]+\x01)()/i],
	'8' :[/\b(0o)([0-7]*\x01[0-7]+\b|[0-7]+\x01)()/i],
	'16':[/(\b0x|[\\\%][ux]|\&?\#x|[\#\$]|\&h?)([\da-f]*\x01[\da-f]+\b|[\da-f]+\x01)()/i, /\b()([\da-f]*\x01[\da-f]+|[\da-f]+\x01)(h\b)/i],
	'64':[/()(\d*\x01\d+|\d+\x01)()/],//10進整数
	'513':[/()([\d０-９]*\x01[\d０-９]*[\.．][\d０-９]+\b|[\d０-９]*[\.．][\d０-９]*\x01[\d０-９]+\b|[\d０-９]*[\.．][\d０-９]+\x01)()/i],
	'576':[/()([\d０-９]*\x01[\d０-９]+|[\d０-９]+\x01)()/],
	'65536':[/()([〇一二三四五六七八九十百千万億兆京垓零壱弐参肆伍陸漆捌玖拾佰仟萬○丸陌阡]*\x01[〇一二三四五六七八九十百千万億兆京垓零壱弐参肆伍陸漆捌玖拾佰仟萬○丸陌阡]+|[〇一二三四五六七八九十百千万億兆京垓零壱弐参肆伍陸漆捌玖拾佰仟萬○丸陌阡]+\x01)()/i]
};

//function put(msg){if(typeof(outp)=='undefined'){TraceOut('\r\n▼-- $t @['+ExpandParameter('$M').replace(/^.*?([^\\\/]+)$/m,'$1')+'] > $f --',1),outp=1} TraceOut((''+msg).replace(/^([\S])/gm,'\t$1'),0)}

//== マクロメイン =========================================
function numControl(){
	var prm=[0,1], cx=ExpandParameter('$x')-1, cy=ExpandParameter('$y')-0, val=0, vmx=0, ret='', c, t, tf=0, iv='';
	if(c=GetCookie('document','_NuMcTrL_aT_')||''){//呼び出し用の仕込み
		NUM_STEP=c-0; DeleteCookie('document','_NuMcTrL_aT_'); if(!NUM_STEP){return null}
	}

	t=(t=GetLineStr(0).replace(/\r|\n/g,'')).slice(0,cx)+'\x01'+t.slice(cx);
	if(c=GetCookie('document','_NuMcTrL_')||''){c=c.split(',');//連続操作中
		for(var i=0; i<c.length && i<10; i++){c[i]=c[i]-0}
		if(c[0]==cy && c[1]==GetLineStr(0).length && c[4]<=cx && c[5]>=cx){
			prm=c.slice(2,8); UPPER_HEX=c[8]; t=t.slice(c[4],c[5]+1); iv=c[10];
			if(prm[5] && (prm[4]<0)==(0>NUM_STEP) && (new Date()<(c[9]-0))){
				prm[5]-=1; if(!prm[5]){prm[4]=(prm[4]-0)*NUM_ACCEL[0], prm[5]=NUM_ACCEL[1]}//10進数加速
			}else{prm[4]=NUM_STEP, prm[5]=prm[0]&30?0:NUM_ACCEL[1]}
		}
	}

	if(!prm[0]){//初回なら数値の種類判定
		t=t.replace(/\&\#(\d+)(\;?)/g,'\x02\x02$1$2');//HTML文字参照用の補正
		for(var k in nre){
			if(!USE_MBN && (k-0)&512){continue}
			if(!USE_CJK && k==65536){continue}
			for(var i=0; i<nre[k].length; i++){
				if(t.match(nre[k][i])){
					prm[0]=k-0; t=RegExp.$2; prm[2]=RegExp.index+RegExp.$1.length;
					prm[3]=RegExp.lastIndex-RegExp.$3.length-1;
					iv=(RegExp.$1+RegExp.$2+RegExp.$3).replace(/\x01/g,''); break;
				}
			}
			if(prm[0]){break}
		}
		if(!prm[0]){return false}//数値じゃなさそ
		prm[4]=NUM_STEP, prm[5]=prm[0]&62?0:NUM_ACCEL[1];
		if(prm[0]&65 && t.match(/^[0０][\d０-９]+/m)){prm[0]|=1024}//0埋め10進数
	}
	if(prm[0]&16 && !(UPPER_HEX&8)){if(t.match(/([a-f])/i)){UPPER_HEX='.ABCDEF'.indexOf(RegExp.$1)>0?1:0} UPPER_HEX|=8}

	if(prm[0]&65){//現在値取得:10進数
		if(prm[0]&512){t=t.replace(/[０-９．]/g,function(a){return '0123456789.'.charAt('０１２３４５６７８９．'.indexOf(a))})}
		val=('0'+(t=t.replace(/\x01/g,'')))-0; var l=t.replace(/^.*?\.(\d+)$/m,'$1').length;
		if(prm[0]&1){prm[1]=1; while(l--){prm[1]*=10; val*=10; tf++}}
		l=t.length-(prm[0]&1?1:0); if(prm[0]&1024){vmx=1; while(l--){vmx*=10}}
		if(vmx){var m=vmx>>3,n=prm[4]<0?prm[4]*(-1):prm[4]; while(m<n){n/=2; prm[4]/=2}}//加速補正
	}else if(prm[0]&62){//2,8,16進数
		var l=t=t.charAt(0)=='\x01'?t.charAt(1):t.replace(/^(.*?)\x01.*?$/m,'$1');
		while(l){val=val*prm[0]+(('0x'+l.charAt(0))-0); l=l.slice(1)}
		l=t.length; vmx=1; while(l--){vmx*=prm[0]}
	}else if(prm[0]&65536){//漢数
		var pd,s,l; iv=iv.replace(/○/g,'〇');
		if(!(UPPER_HEX&8)){prm[1]=0; iv=iv.replace(/^(.*?)([万億兆京垓萬])$/m,function(a,v,d){prm[1]=d=='萬'?1:'_万億兆京垓'.indexOf(d); prm[3]--; return t=v})}//大元の単位分は端折る
		t=t.replace(/\x01/g,'').replace(/([零壱弐参肆伍陸漆捌玖拾佰仟萬○丸陌阡])/g,function(a,k){return '〇一二三四五六七八九十百千万〇〇百千'.charAt('零壱弐参肆伍陸漆捌玖拾佰仟萬○丸陌阡'.indexOf(k))});//大字等変換
		if(!(UPPER_HEX&8)){//表記フラグ
			UPPER_HEX=8|(iv.match(/[零壱弐参肆伍陸漆捌玖]/)?1:0)|(iv.match(/[佰仟]/)?130:iv.match(/[拾陌阡]/)?2:0);
			UPPER_HEX|=(t.match(/一[十百千]/)?0:4)|(t.match(/〇/)?16:0)|(iv.match(/丸/)?32:0);
			UPPER_HEX|=(t.length>1&&!t.match(/[十百千]/)?64:0)|(iv.match(/萬/)?256:0);
		}
		if(prm[0]&1024 || t.match(/^〇./m)){prm[0]|=1024; vmx=1; l=t.length; while(l--){vmx*=10}} pd=0, s=t, l=1;//0埋め?
		while(s && s.match(/^(.*?)([万億兆京垓]|[〇一二三四五六七八九]?[十百千]?)$/)){//桁を増やしつつ数値解析
			s=RegExp.$1; var ss=RegExp.$2, j;
			if((j='_万億兆京垓'.indexOf(ss)-prm[1])>0){l=1; while(j--){l*=10000} continue}
			j=1; ss=ss.replace(/[十百千]/g,function(a){var h='_十百千'.indexOf(a); j=1; while(h--){j*=10}; return ''});
			j*=l; if(j<=pd){j=pd*10} pd=j; if(ss){j*='〇一二三四五六七八九'.indexOf(ss)} val+=j;
		}
	}

	//数値操作 & 表記化
	val+=(prm[4]>>0); if(vmx){val=(val+vmx)%vmx}else{val=val<0?0:val}
	if(prm[0]&65){//10進数
		ret=(val/prm[1]).toFixed(tf); while(prm[0]&1024 && ret.length<t.length){ret='0'+ret}
		if(prm[0]&512){ret=ret.replace(/[\d\.]/g,function(a){return '０１２３４５６７８９．'.charAt('0123456789.'.indexOf(a))})}
	}else if(prm[0]&62){//N進数表記化
		var l=val; while(l){ret=((l%(prm[0]&62)).toString(prm[0]&62))+ret; l=(l/(prm[0]&62))>>>0}
		if(UPPER_HEX&1){ret=ret.toUpperCase()}
		while(ret.length<t.length){ret='0'+ret}
	}else{//漢数 (まず公倍数で作って表記形式に約分するようなイメージ)
		function _kj(n,k){var cjk=['〇一二三四五六七八九','_十百千','_万億兆京垓']; n=n||0, k=k||0; return cjk[k].charAt(n)}
		var l=val, f=UPPER_HEX, d=0;
		while(l){if(d && !(d%4)){ret=_kj(d/4+prm[1],2)+ret} var j=l%10; l=(l/10)>>>0; ret=_kj(j)+_kj(d%4,1)+ret; d++}
		if(!ret){ret='〇'} ret=ret.replace(/\_/g,'').replace(/(〇)[十百千]/g,'$1');
		if(f&64){ret=ret.replace(/[十百千]/g,'')} if(f&4){ret=ret.replace(/一([十百千])/g,'$1')}
		ret=ret.replace(/(〇)/g,f&32?'丸':f&80||ret.length<2?'$1':'');
		while(vmx && ret.length<t.length){ret=(f&32?'丸':'〇')+ret}
		if(f&1){ret=ret.replace(/[〇一二三四五六七八九]/g,function(a){return '零壱弐参肆伍陸漆捌玖'.charAt('〇一二三四五六七八九'.indexOf(a))})}
		if(f&2){ret=ret.replace(/[十百千]/g,function(a){return (f&128?'拾佰仟':'拾陌阡').charAt('十百千'.indexOf(a))})}
		if(f&256){ret=ret.replace(/万/g,'萬')}
	}
	if(ret==t){return null} prm[3]=(prm[3]-0)+ret.length-t.length;

	SetDrawSwitch(0); AddRefUndoBuffer();//結果反映
	MoveCursor(cy,prm[2]+1,0); MoveCursor(cy,prm[2]+t.length+1,1);
	InsText(ret); AppendUndoBufferCursor(); SetUndoBuffer();
	SetDrawSwitch(1); ReDraw();

	//連続操作用にクッキーへデータ保持
	ret=[ExpandParameter('$y'),GetLineStr(0).length].concat(prm);
	ret=ret.concat([UPPER_HEX,(new Date()-0)+NUM_ACCEL[2],iv]);
	SetCookie('document','_NuMcTrL_',ret.join(','));

	if(SHOW_DECIMAL){//ステータスバー表示
		function _dgt(v,d,s){v=''+(v||0); d=d||3; s=s||','; var r=''; while(v){if(r){r=s+r} r=v.slice(-d)+r; v=v.substr(0,v.length-d)} return r}
		if(prm[0]&62){val=0; t=GetLineStr(0).slice(prm[2],prm[3]); while(t){val=val*(prm[0]&62)+(('0x'+t.charAt(0))-0); t=t.slice(1)}}
		StatusMsg('[ '+iv+' ]%'+(prm[0]&62?prm[0]&62:10)+' => '+(SHOW_DECIMAL>1?_dgt(val,SHOW_DECIMAL):val),0);
	}

	if(INTERVAL && INTERVAL>0){Sleep(INTERVAL)}

	return true;
}

if(Editor){if(numControl()===false){DEF_FUNC()}}
~~~~

**減少用** (numCtrl_alt.js)

~~~~javascript:numCtrl_alt.js
var NUM_STEP = -1;

//== マクロメイン =========================================
if(Editor){//内容はほぼ同じなのでクッキー経由で引数与えて呼び出しだけ
	SetCookie('document','_NuMcTrL_aT_',NUM_STEP);
	ExecExternalMacro(ExpandParameter('$M').replace(/[^\\\/]+$/m,'')+'numCtrl.js');
}
~~~~

</div></details>


## 仕様
負数(0未満)にはなりません。というか符号を見てません。
数値の種類で若干挙動が異なります。

- **10進数** (整数・実数)<br />
  最少桁の値から操作します。<br />
  連続で操作[^1]すると徐々に変化量が増えます。<br />
  実数の場合、少数点以下の桁数は維持します。<br /><br />
- **0埋め10進数**<br />
  桁数を維持して最小値 ～ 最大値の数値で循環します[^2]。<br /><br />
- **&nbsp;2進数:** `0b01010101`
- **&nbsp;8進数:** `0o01234567`
- **16進数:** `0x1234ABCD`・`#1188DD`・`&h89AB`等<br />
  **カーソルの左側の数値**を操作します。<br />
  桁数を維持して最小値 ～ 最大値の数値で循環します[^2]。<br /><br />
- **全角数字** `２３４．５６`
- **漢数字** `三十六`・`一〇八`・`壱阡萬`等<br />
  `USE_MBN = 1`にすると全角数字を、`USE_CJK = 1`にすると漢数字を、10進数同様に扱います。<br />
  ほぼネタです。:cheese:

----
0埋め数値だったか・同じ数値を操作中か、などの判定はクッキーに保持したデータを参照してます。

このデータは、別の数値を操作する・その行の文字数が変わる・文書を開き直すとリセットされます。


[^1]: マクロに割り当てたキーを押しっぱなし。等
[^2]: 最大値を超えると0(最小値)に、0を下回るとその桁数での最大値へと変化します。