generated at
カーソルが行頭になくてもTabでインデントするUserScript
行内でどこにカーソルがあってもtabインデントできるUserScript

実装 ~2024-04-17

バグ修正版
お試し
$ import("/api/code/villagepump/カーソルが行頭になくてもTabでインデントするUserScript/script.js").then(({ setup })=>setup())
type check
$ deno check --remote -r https://scrapbox.io/api/code/villagepump/カーソルが行頭になくてもTabでインデントするUserScript/script.js
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 }); };

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)); };