カーソルが行頭になくてもTabでインデントするUserScript
実装 ~2024-04-17
バグ修正版
お試し
$ import("/api/code/villagepump/カーソルが行頭になくてもTabでインデントするUserScript/script.js").then(({ setup })=>setup())
type check
code:script.js
// @ts-check
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/** カーソルが行頭になくてもTabでインデントするUserScriptを起動する
*
* @return {() => void} scriptを解除する関数
*/
export const setup = () => {
registListener();
// @ts-ignore
scrapbox.on("layout:changed", registListener);
// @ts-ignore
return () => scrapbox.off("layout:changed", registListener);
};
/** #text-inputがあるときのみlistenerを登録する
*
* @return {void}
*/
const registListener = () => {
const textInput = document.getElementById("text-input");
if (!textInput) return;
textInput.addEventListener("keydown", operateIndent);
};
/** Tabでindent, Shift+Tabでoutdent
*
* 重複登録を防止するため、不変なobjectとして定義している
*
* @param {KeyboardEvent} e
* @return {void}
*/
const operateIndent = (e) => {
if (e.key !== "Tab") return;
const cursorLine = document.getElementsByClassName("cursor-line")0; if((cursorLine?.getElementsByClassName?.("code-block")?.length ?? 0) !== 0) return;
if((cursorLine?.getElementsByClassName?.("table-block")?.length ?? 0) !== 0) return;
if(document.getElementsByClassName("popup-menu").length !== 0) return;
const textInput = document.getElementById("text-input");
if (!textInput) return;
if (!(textInput instanceof HTMLTextAreaElement)) return;
e.preventDefault();
e.stopPropagation();
press(e.shiftKey ? 37 : 39, textInput, { ctrlKey: true });
};
code:script.js
/** @typedef {object} PressOptions - the options for press()
* @property {boolean=} shiftKey
* @property {boolean=} ctrlKey
* @property {boolean=} altKey
* @property {boolean=} metaKey
* @property {boolean=} noModifiedKeys
*/
/**
* @param {number} keyCode
* @param {HTMLTextAreaElement} textInput
* @param {PressOptions=} pressOptions
* @return {void}
*/
const press = (keyCode, textInput, pressOptions) => {
const { noModifiedKeys = false, ...rest } = pressOptions ?? {};
const options = {
bubbles: true,
cancelable: true,
keyCode,
...(noModifiedKeys ? {} : { ...rest }),
};
textInput.dispatchEvent(new KeyboardEvent("keydown", options));
textInput.dispatchEvent(new KeyboardEvent("keyup", options));
};