競プロとゲームをしていません

VSCodeのショートカットでonline-judge-toolsを実行する

まずはこの動画を見てください。

www.youtube.com

この動画の通り、私は競技プログラミングをするとき、VSCodeのショートカットから次の操作をしています。

  • アクティブなファイルのテスト
  • アクティブなファイルの提出
  • テンプレート生成とサンプルのダウンロード

この裏では、VSCodeのショートカットはonline-judge-tools(以下oj)を呼び出してテスト実施等々を行っています。ojは競プロ支援ツールです。

競技プログラミング中に、ショートカットから問題のテスト、提出ができると非常に便利です。今回はこの設定方法を紹介したいと思います。

VSCodeのショートカットからojを走らせたい人、またはVSCodeのショートカットから他のコマンドを走らせたい人は参考にしてください。

環境構築、タスク作成、ショートカット作成の3つに分けて解説します。

前提条件

次のOS、言語を想定します。

パスに登場するユーザー名はhotaruとします。

環境構築

次のソフトを事前にインストールしておいてください。

なおこの記事ではoj, template-generatorの解説は行いません。必要なら公式のチュートリアルを読んでください。
oj/README.ja.md at master · online-judge-tools/oj
template-generator/README.ja.md at master · online-judge-tools/template-generator

oj-prepareの設定変更

コンテストのテンプレート生成とサンプルのダウンロードは、oj-prepareで行います。 テンプレートファイルの生成パスは(デフォルトの設定では)問題のURLを指定すると.、コンテストのURLを指定すると./{service_domain}/{contest_id}/{problem_id}です。

ショートカットでテンプレート生成とサンプルのダウンロードをするにあたり、問題のURLを指定したときもコンテストのときと同じパスにテンプレートファイルを設定して欲しいです。 なので設定を変更します。

oj-prepareの設定は(Linuxの場合)~/.config/online-judge-tools/prepare.config.toml、(Windows 11の私の環境の場合)C:\Users\hotaru\AppData\Local\online-judge-tools\online-judge-tools\prepare.config.tomlで行えます。以下のように書いてください。

contest_directory = ""
problem_directory = "./{service_domain}/{contest_id}/{problem_id}"

[templates]
"main.cpp" = "main.cpp"
"main.py" = "main.py"
"generate.py" = "generate.py"

templatesはお好みで設定してください。

タスク作成

タスクはVSCodeの機能です。 任意のコマンドをタスクとして登録しておいて、VSCode上から実行できます。 よくある使い方としてはビルドタスク・テストタスクが挙げられます。

ojを実行するタスクを作成します。 .vscode/tasks.jsonを以下のように書き換えてください。 既に設定がある場合は、以下の設定を追加してください。

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "cppbuild",
            "label": "oj: C++ アクティブなファイルのビルド",
            "command": "/usr/bin/g++",
            "args": [
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}.out"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build",
            "detail": "コンパイラ: /usr/bin/g++"
        },
        {
            "label": "oj: C++ アクティブなファイルのテスト",
            "type": "shell",
            "command": "oj",
            "args": [
                "test",
                "-c",
                "${fileDirname}/${fileBasenameNoExtension}.out"
            ],
            "dependsOn": [
                "oj: C++ アクティブなファイルのビルド"
            ],
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "options": {
                "cwd": "${fileDirname}"
            }
        },
        {
            "label": "oj: Python3 アクティブなファイルのテスト",
            "type": "shell",
            "command": "oj",
            "args": [
                "test",
                "-c",
                "python3 ${file}"
            ],
            "group": "test",
            "options": {
                "cwd": "${fileDirname}"
            }
        },
        {
            "label": "oj: アクティブなファイルの提出",
            "type": "shell",
            "command": "oj",
            "args": [
                "submit",
                "${file}"
            ],
            "presentation": {
                "focus": true
            },
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": []
        },
        {
            "label": "oj-prepare: テンプレート生成とサンプルのダウンロード",
            "type": "shell",
            "command": "oj-prepare",
            "args": [
                "${input:oj-prepare URL}"
            ],
            "problemMatcher": []
        }
    ],
    "inputs": [
        {
            "id": "oj-prepare URL",
            "type": "promptString",
            "description": "問題またはコンテストのURL"
        }
    ]
}

ビルドの方法はお込みで設定してください。

タスク作成 Python3の場合の変更点

"label": "oj: Python3 アクティブなファイルのテスト"のオブジェクトの、"group"{"kind": "test","isDefault": true}に変更してください。

"label": "oj: C++ アクティブなファイルのテスト"のオブジェクトの、"group""test"に変更してください。


これで次のタスクを作成できました。 試しにShift + Ctrl + PTasks: Run Taskと入力して選択→作成したタスクを選択してタスクを実行してみましょう。

  • アクティブなファイルのテスト
  • アクティブなファイルの提出
  • テンプレート生成とサンプルのダウンロード

ショートカット作成

ojを実行するショートカットを作成します。

ファイル(F)→ユーザー設定→キーボード ショートカット→右上のキーボードショートカットを開く(JSON)ボタンと操作してショートカットファイルを開きます。 ショートカットファイルを以下のように書き換えてください。 既に設定がある場合は、以下の設定を追加してください。

[
    {
        "key": "alt+t",
        "command": "workbench.action.tasks.test",
    },
    {
        "key": "alt+s",
        "command": "workbench.action.tasks.runTask",
        "args": "oj: アクティブなファイルの提出",
        "when": "editorLangId != 'markdown'"
    },
    {
        "key": "alt+d",
        "command": "workbench.action.tasks.runTask",
        "args": "oj-prepare: テンプレート生成とサンプルのダウンロード"
    },
]

これで次のショートカットを作成できました。

  • Alt + T:アクティブなファイルのテスト
  • Alt + S:アクティブなファイルの提出
  • Alt + D:テンプレート生成とサンプルのダウンロード

おまけ

oj-prepareで指定した問題をWebブラウザで開き、生成したテンプレートをエディタで開くシェルスクリプトoj-prepare-open.shを開発中です。

こちらもいつか記事にします。

作成中のものです。fish-shellスクリプトです。 my_vscode_settings_for_procon/ojopen.sh at main · hotarunx/my_vscode_settings_for_procon

テンプレート生成とサンプルのダウンロードのタスクと同時に実行するとよいでしょう。

FIXME

  • テストに通ったらコードを提出する
  • ojの他の機能を活用する
    • ランダムテスト
    • 解が複数ある問題対応
    • リアクティブ問題対応
  • ac-libraryのライブラリを展開する(expander.pyを実行する)
  • ショートカットがアクティブな場合の条件が広すぎるため、whenを適切に設定して狭める