Ace to Monaco for AtCoder

AtCoderのエディタをMonacoに差し替えます

スクリプトをインストール?
作者が勧める他のスクリプト

AtCoder Beautiful Code Viewも気に入るかもしれません

スクリプトをインストール
このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
  1. // ==UserScript==
  2. // @name Ace to Monaco for AtCoder
  3. // @namespace https://yahoo.co.jp
  4. // @version 1.0.0
  5. // @description AtCoderのエディタをMonacoに差し替えます
  6. // @author 茶色コーダー
  7. // @license MIT
  8. // @match https://atcoder.jp/contests/*/custom_test
  9. // @exclude
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. // 要素が出現するまで待つ関数(出てこなかったら永遠ループ)
  14. const waitQuerySelector = async function (selector, node = document) {
  15. let obj = null;
  16.  
  17. while (!obj) {
  18. obj = await new Promise((resolve) =>
  19. setTimeout(() => resolve(node.querySelector(selector), 100))
  20. );
  21. }
  22.  
  23. return obj;
  24. };
  25.  
  26. // 指定秒数待つ関数
  27. const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  28.  
  29. const func1 = async function () {
  30. // headを取得する
  31. const head = document.head;
  32. // headの末尾にMonaco Editor用CSS(+α)を追加する
  33. head.insertAdjacentHTML(
  34. "beforeEnd",
  35. `<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/editor/editor.main.min.css" />
  36. <style>#editor {max-height: 600px;}</style>`
  37. );
  38.  
  39. const script = document.createElement("script");
  40. script.src = "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js";
  41. document.head.prepend(script);
  42.  
  43. // ACEエディタの中身はいらないので消す
  44. const editor = document.getElementById("editor");
  45. editor.innerHTML = "";
  46.  
  47. // loaderを読み込み終わったら処理開始
  48. script.onload = async function () {
  49. // 画面読み込み時の初期コードの取得
  50. const initCode = document.querySelector("#plain-textarea").innerText;
  51.  
  52. // プログラミング言語を選択する要素の取得(読み込み対策で0.1秒遅延)
  53. await wait(100);
  54. const langSelector = document.querySelector('select[name="data.LanguageId"]');
  55. const langOptions = Array.from(document.querySelectorAll("option"));
  56.  
  57. // 選択した要素のIDがどの言語なのか判別
  58. const langText = langOptions.find((item) => {
  59. return item.getAttribute("value") === langSelector.value;
  60. });
  61. const langMode = langText ? langText.getAttribute("data-ace-mode") : "text";
  62. // console.log("最初のテキストは", langMode);
  63.  
  64. // Monaco EditorのCDNのパスを設定
  65. require.config({
  66. paths: { vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs" },
  67. });
  68.  
  69. // Monaco Editor周りの処理
  70. require(["vs/editor/editor.main"], async function () {
  71. // Monaco Editorを降臨させる
  72. const localEditor = monaco.editor.create(editor, {
  73. value: initCode,
  74. language: langMode,
  75. theme: "vs-dark",
  76. lineHeight: 21,
  77. });
  78.  
  79. // 言語変更時の監視対象のエレメントの取得
  80. const langElement = await waitQuerySelector(".select2-selection__rendered");
  81.  
  82. //MutationObserver(インスタンス)の作成
  83. var mo = new MutationObserver(function (record, observer) {
  84. const lang = langOptions.find((item) => {
  85. return item.getAttribute("value") === langSelector.value;
  86. });
  87.  
  88. // console.info(
  89. // "innerText: ",
  90. // langElement.innerText,
  91. // "\n ace-mode: ",
  92. // lang.getAttribute("data-ace-mode")
  93. // );
  94.  
  95. // Editorに新たな言語を設定
  96. monaco.editor.setModelLanguage(
  97. localEditor.getModel(),
  98. lang.getAttribute("data-ace-mode")
  99. );
  100. });
  101.  
  102. // 監視する「もの」の指定(必ず1つ以上trueにする)
  103. var config = {
  104. childList: true, //「子ノード(テキストノードも含む)」の変化
  105. attributes: false, //「属性」の変化
  106. characterData: false, //「テキストノード」の変化
  107. };
  108.  
  109. // 監視の開始
  110. mo.observe(langElement, config);
  111.  
  112. // コードテストのform実行時にAce Editorから値を持ってきてしまうので、
  113. // その上からMonaco Editorの値で書き換える
  114. const formRef = $(".form-code-submit");
  115. formRef.on("submit", function () {
  116. $("#plain-textarea").val(localEditor.getValue());
  117. });
  118. });
  119. };
  120. };
  121.  
  122. async function execWorkflow() {
  123. await func1();
  124. }
  125.  
  126. // メイン処理の実行タイミングが、windowのロード時となるように登録する
  127. window.addEventListener("load", async function () {
  128. await execWorkflow();
  129. });