◆ textarea (input でも) にスニペット風な入力でコマンド実行
◆ 単純にスニペット展開もできる
◆ ユーザスクリプトとして拡張機能から使いたいページに挿入する

textarea でコマンドを実行する拡張機能です
拡張機能というよりユーザスクリプトで 拡張機能に登録するものです
イメージはスニペット入力ツールで JavaScript 処理もできるようになったもの

@alert;

で alert を表示したり

@fn;



function(){

}

と入力したり ができます

もちろんなにするかは自分で定義するわけですけど


↓がサンプル兼テンプレート
start_marker や end_marker で開始と終了の文字を変更できます
textarea 以外でも許可する要素を指定できます
map にコマンドと処理を書きます
好きに JavaScript で処理できるので 直前の文字を見て連番作ったりもできます (expand)
関数じゃなくて文字列を指定するとその文字列に置き換えます

const snip_start_marker = "@"
const snip_end_marker = ";"
const accept_sources = [
"textarea", "input[type=text]"
]
const snimap = {
"test": "てすと",
"run"(elem){
console.log(elem)
},
"tabindent"(elem){
const pre = elem.value
elem.value = pre.replace(/^ {4,}/g, e =>
"\t".repeat(~~(e.length / 4)) + " ".repeat(e.length % 4))
if(elem.value !== pre){
elem.selectionStart = elem.selectionEnd = elem.value.length
}
},
"doctype"(elem){
const text = `<!doctype html>\n<meta charset="utf-8">\n`
elem.value = strinsert(elem.value, elem.selectionStart, text)
setCaret(elem, elem.selectionStart + text.length)
},
"expand"(elem){
let start
{
let i = elem.selectionStart
while(true){
const char = elem.value.charAt(--i)
if(char === "\n" || char === ""){
start = i + 1
break
}
}
}
const snivar = elem.value.slice(start, elem.selectionStart)
const matched = snivar.trim().match(/^(-?\d+) *\: *(-?\d+)$/)
if(!matched) return

const rstart = ~~matched[1]
const rend = ~~matched[2]
if(rstart > rend) return

const text = Array.from(Array(rend - rstart + 1), (e, i) => rstart + i).join("\n")
replaceElementText(elem, text, start, elem.selectionStart)
}
}

function strcut(str, start, end){
return str.substr(0, start) + str.substr(end)
}

function strinsert(str, index, text){
return str.substr(0, index) + text + str.substr(index)
}

function setCaret(elem, start, end = start){
elem.selectionStart = start
elem.selectionEnd = end
}

function replaceElementText(elem, text, start, end = start){
const inipos = elem.selectionStart

elem.value = strinsert(strcut(elem.value, start, end), start, text)

if(inipos >= start){
const newpos = start + text.length
setCaret(elem, newpos)
}
}

function checkSnip(elem){
if(!(elem.selectionStart === elem.selectionEnd && elem.selectionStart)){
return false
}
const end = elem.selectionStart
let start
{
let i = elem.selectionStart
while(true){
const char = elem.value.charAt(--i)
if(char === "\n" || char === ""){
return false
}
if(char === snip_start_marker){
start = i
break
}
}
}
const snip = elem.value.slice(start + 1, end)
if(!(snip in snimap)){
return false
}

return {start, end, key: snip}
}

window.addEventListener("keydown", eve => {
const elem = eve.target
const do_handle = accept_sources.some(e => eve.target.matches(e))
&& eve.key === snip_end_marker
if(!do_handle) return

const snip = checkSnip(elem)
if(!snip) return

const action = snimap[snip.key]

elem.value = strcut(elem.value, snip.start, snip.end)
setCaret(elem, snip.start)

if(typeof action === "string"){
elem.value = strinsert(elem.value, snip.start, action)
setCaret(elem, snip.start + action.length)
}else{
action(elem)
}

eve.preventDefault()
eve.stopPropagation()
}, true)

Gist