diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2e897f..f411b31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Install ruff run: uv tool install ruff - name: Check Python formatting with ruff - run: ruff format --check templates/scrapers/ + run: ruff format --check scrapers/ python-lint: name: Python Linting @@ -51,4 +51,4 @@ jobs: - name: Install ruff run: uv tool install ruff - name: Lint Python files with ruff - run: ruff check templates/scrapers/ + run: ruff check scrapers/ diff --git a/lua/cp/config.lua b/lua/cp/config.lua index 1bbda23..0c59be5 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -14,9 +14,15 @@ M.defaults = { codeforces = { cpp_version = 23, }, - cses = {}, + cses = { + cpp_version = 20, + }, }, snippets = {}, + hooks = { + before_run = nil, + before_debug = nil, + }, } local function extend_contest_config(base_config, contest_config) @@ -30,6 +36,25 @@ local function extend_contest_config(base_config, contest_config) end function M.setup(user_config) + vim.validate({ + user_config = { user_config, { "table", "nil" }, true }, + }) + + if user_config then + vim.validate({ + contests = { user_config.contests, { "table", "nil" }, true }, + snippets = { user_config.snippets, { "table", "nil" }, true }, + hooks = { user_config.hooks, { "table", "nil" }, true }, + }) + + if user_config.hooks then + vim.validate({ + before_run = { user_config.hooks.before_run, { "function", "nil" }, true }, + before_debug = { user_config.hooks.before_debug, { "function", "nil" }, true }, + }) + end + end + local config = vim.tbl_deep_extend("force", M.defaults, user_config or {}) local default_contest = config.contests.default diff --git a/lua/cp/init.lua b/lua/cp/init.lua index d3d1aa3..75b8250 100644 --- a/lua/cp/init.lua +++ b/lua/cp/init.lua @@ -161,9 +161,8 @@ local function run_problem() return end - local has_lsp, lsp = pcall(require, "lsp") - if has_lsp and lsp.lsp_format then - lsp.lsp_format({ async = true }) + if config.hooks and config.hooks.before_run then + config.hooks.before_run(problem_id) end if not vim.g.cp_contest then @@ -185,9 +184,8 @@ local function debug_problem() return end - local has_lsp, lsp = pcall(require, "lsp") - if has_lsp and lsp.lsp_format then - lsp.lsp_format({ async = true }) + if config.hooks and config.hooks.before_debug then + config.hooks.before_debug(problem_id) end if not vim.g.cp_contest then @@ -246,9 +244,6 @@ function M.setup(user_config) config = config_module.setup(user_config) - local plugin_path = get_plugin_path() - config.snippets.path = plugin_path .. "/templates/snippets" - snippets.setup(config) if initialized then diff --git a/lua/cp/scrape.lua b/lua/cp/scrape.lua index dd0bb79..1d54faf 100644 --- a/lua/cp/scrape.lua +++ b/lua/cp/scrape.lua @@ -13,7 +13,7 @@ function M.scrape_problem(contest, problem_id, problem_letter) ensure_io_directory() local plugin_path = get_plugin_path() - local scraper_path = plugin_path .. "/templates/scrapers/" .. contest .. ".py" + local scraper_path = plugin_path .. "/scrapers/" .. contest .. ".py" local args if contest == "cses" then diff --git a/templates/scrapers/atcoder.py b/scrapers/atcoder.py similarity index 100% rename from templates/scrapers/atcoder.py rename to scrapers/atcoder.py diff --git a/templates/scrapers/codeforces.py b/scrapers/codeforces.py similarity index 100% rename from templates/scrapers/codeforces.py rename to scrapers/codeforces.py diff --git a/templates/scrapers/cses.py b/scrapers/cses.py similarity index 100% rename from templates/scrapers/cses.py rename to scrapers/cses.py diff --git a/templates/.clang-format b/templates/.clang-format deleted file mode 100644 index e7350c4..0000000 --- a/templates/.clang-format +++ /dev/null @@ -1,9 +0,0 @@ -BasedOnStyle: Google -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortCompoundRequirementOnASingleLine: false -AllowShortEnumsOnASingleLine: false -AllowShortFunctionsOnASingleLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLambdasOnASingleLine: false -AllowShortLoopsOnASingleLine: false diff --git a/templates/makefile b/templates/makefile deleted file mode 100644 index 0ae8c41..0000000 --- a/templates/makefile +++ /dev/null @@ -1,30 +0,0 @@ -.PHONY: run debug clean setup init scrape - -VERSION ?= 20 - -SRC = $(word 2,$(MAKECMDGOALS)) - -.SILENT: - -run: - sh scripts/run.sh $(SRC) - -debug: - sh scripts/debug.sh $(SRC) - -clean: - rm -rf build/* - -setup: - test -d build || mkdir -p build - test -d io || mkdir -p io - test -f compile_flags.txt && echo -std=c++$(VERSION) >>compile_flags.txt - -init: - make setup - -scrape: - sh scripts/scrape.sh $(word 2,$(MAKECMDGOALS)) $(word 3,$(MAKECMDGOALS)) $(word 4,$(MAKECMDGOALS)) - -%: - @: diff --git a/templates/scripts/debug.sh b/templates/scripts/debug.sh deleted file mode 100644 index 1e63f37..0000000 --- a/templates/scripts/debug.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -. ./scripts/utils.sh - -SRC="$1" -BASE=$(basename "$SRC" .cc) -INPUT="${BASE}.in" -OUTPUT="${BASE}.out" -DBG_BIN="${BASE}.debug" - -test -d build || mkdir -p build -test -d io || mkdir -p io - -test -f "$INPUT" && test ! -f "io/$INPUT" && mv "$INPUT" "io/" -test -f "$OUTPUT" && test ! -f "io/$OUTPUT" && mv "$OUTPUT" "io/" - -test -f "io/$INPUT" || touch "io/$INPUT" -test -f "io/$OUTPUT" || touch "io/$OUTPUT" - -INPUT="io/$INPUT" -OUTPUT="io/$OUTPUT" -DBG_BIN="build/$DBG_BIN" - -compile_source "$SRC" "$DBG_BIN" "$OUTPUT" @debug_flags.txt -CODE=$? -test $CODE -gt 0 && exit $CODE - -execute_binary "$DBG_BIN" "$INPUT" "$OUTPUT" true -exit $? diff --git a/templates/scripts/run.sh b/templates/scripts/run.sh deleted file mode 100644 index ab9aa7d..0000000 --- a/templates/scripts/run.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -. ./scripts/utils.sh - -SRC="$1" -BASE=$(basename "$SRC" .cc) -INPUT="${BASE}.in" -OUTPUT="${BASE}.out" -RUN_BIN="${BASE}.run" - -test -d build || mkdir -p build -test -d io || mkdir -p io - -test -f "$INPUT" && test ! -f "io/$INPUT" && mv "$INPUT" "io/" -test -f "$OUTPUT" && test ! -f "io/$OUTPUT" && mv "$OUTPUT" "io/" - -test -f "io/$INPUT" || touch "io/$INPUT" -test -f "io/$OUTPUT" || touch "io/$OUTPUT" - -INPUT="io/$INPUT" -OUTPUT="io/$OUTPUT" -RUN_BIN="build/$RUN_BIN" - -compile_source "$SRC" "$RUN_BIN" "$OUTPUT" "" -CODE=$? -test $CODE -gt 0 && exit $CODE - -execute_binary "$RUN_BIN" "$INPUT" "$OUTPUT" -exit $? diff --git a/templates/scripts/scrape.sh b/templates/scripts/scrape.sh deleted file mode 100755 index 973ef7b..0000000 --- a/templates/scripts/scrape.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/sh - -CONTEST="$1" -PROBLEM="$2" -PROBLEM_LETTER="$3" - -if [ -z "$CONTEST" ] || [ -z "$PROBLEM" ]; then - echo "Usage: make scrape [problem_letter]" - echo "Available contests: cses, atcoder, codeforces" - echo "Examples:" - echo " make scrape cses 1068" - echo " make scrape atcoder abc042 a" - echo " make scrape codeforces 1234 A" - exit -fi - -test -d io && true || mkdir -p io -TMPFILE=$(mktemp) -ORIGDIR=$(pwd) - -case "$CONTEST" in - cses) - cd "$(dirname "$0")/../.." && uv run scrapers/cses.py "$PROBLEM" > "$TMPFILE" - if [ $? -eq 0 ]; then - cd "$ORIGDIR" - awk '/^---INPUT---$/ {getline; while ($0 != "---OUTPUT---") {print; getline}} END {}' "$TMPFILE" > "io/$PROBLEM.in" - awk '/^---OUTPUT---$/ {getline; while ($0 != "---END---") {print; getline}} END {}' "$TMPFILE" > "io/$PROBLEM.expected" - echo "Scraped problem $PROBLEM to io/$PROBLEM.in and io/$PROBLEM.expected" - else - echo "Failed to scrape problem $PROBLEM" - cat "$TMPFILE" - rm "$TMPFILE" - exit - fi - ;; - atcoder) - if [ -z "$PROBLEM_LETTER" ]; then - echo "AtCoder requires problem letter (e.g., make scrape atcoder abc042 a)" - rm "$TMPFILE" - exit - fi - FULL_PROBLEM_ID="${PROBLEM}${PROBLEM_LETTER}" - cd "$(dirname "$0")/../.." && uv run scrapers/atcoder.py "$PROBLEM" "$PROBLEM_LETTER" > "$TMPFILE" - if [ $? -eq 0 ]; then - cd "$ORIGDIR" - awk '/^---INPUT---$/ {getline; while ($0 != "---OUTPUT---") {print; getline}} END {}' "$TMPFILE" > "io/$FULL_PROBLEM_ID.in" - awk '/^---OUTPUT---$/ {getline; while ($0 != "---END---") {print; getline}} END {}' "$TMPFILE" > "io/$FULL_PROBLEM_ID.expected" - echo "Scraped problem $FULL_PROBLEM_ID to io/$FULL_PROBLEM_ID.in and io/$FULL_PROBLEM_ID.expected" - else - echo "Failed to scrape problem $FULL_PROBLEM_ID" - cat "$TMPFILE" - rm "$TMPFILE" - exit - fi - ;; - codeforces) - if [ -z "$PROBLEM_LETTER" ]; then - echo "Codeforces requires problem letter (e.g., make scrape codeforces 1234 A)" - rm "$TMPFILE" - exit - fi - FULL_PROBLEM_ID="${PROBLEM}${PROBLEM_LETTER}" - cd "$(dirname "$0")/../.." && uv run scrapers/codeforces.py "$PROBLEM" "$PROBLEM_LETTER" > "$TMPFILE" - if [ $? -eq 0 ]; then - cd "$ORIGDIR" - awk '/^---INPUT---$/ {getline; while ($0 != "---OUTPUT---") {print; getline}} END {}' "$TMPFILE" > "io/$FULL_PROBLEM_ID.in" - awk '/^---OUTPUT---$/ {getline; while ($0 != "---END---") {print; getline}} END {}' "$TMPFILE" > "io/$FULL_PROBLEM_ID.expected" - echo "Scraped problem $FULL_PROBLEM_ID to io/$FULL_PROBLEM_ID.in and io/$FULL_PROBLEM_ID.expected" - else - echo "Failed to scrape problem $FULL_PROBLEM_ID" - echo "You can manually add test cases to io/$FULL_PROBLEM_ID.in and io/$FULL_PROBLEM_ID.expected" - cat "$TMPFILE" - rm "$TMPFILE" - exit - fi - ;; - *) - echo "Unknown contest type: $CONTEST" - echo "Available contests: cses, atcoder, codeforces" - rm "$TMPFILE" - exit - ;; -esac - -rm "$TMPFILE" diff --git a/templates/scripts/utils.sh b/templates/scripts/utils.sh deleted file mode 100644 index b804089..0000000 --- a/templates/scripts/utils.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -execute_binary() { - binary="$1" - input="$2" - output="$3" - is_debug="$4" - - start=$(date '+%s.%N') - if [ -n "$is_debug" ]; then - asan="$(ldconfig -p | grep libasan.so | head -n1 | awk '{print $4}')" - LD_PRELOAD="$asan" timeout 2s ./"$binary" <"$input" >"$output" 2>&1 - else - timeout 2s ./"$binary" <"$input" >"$output" 2>&1 - fi - CODE=$? - end=$(date '+%s.%N') - truncate -s "$(head -n 1000 "$output" | wc -c)" "$output" - - if [ $CODE -ge 124 ]; then - MSG='' - case $CODE in - 124) MSG='TIMEOUT' ;; - 128) MSG='SIGILL' ;; - 130) MSG='SIGABRT' ;; - 131) MSG='SIGBUS' ;; - 136) MSG='SIGFPE' ;; - 135) MSG='SIGSEGV' ;; - 137) MSG='SIGPIPE' ;; - 139) MSG='SIGTERM' ;; - esac - [ $CODE -ne 124 ] && sed -i '$d' "$output" - test -n "$MSG" && printf '\n[code]: %s (%s)' "$CODE" "$MSG" >>"$output" - else - printf '\n[code]: %s' "$CODE" >>"$output" - fi - - printf '\n[time]: %s ms' "$(awk "BEGIN {print ($end - $start) * 1000}")" >>$output - test -n "$is_debug" && is_debug_string=true || is_debug_string=false - printf '\n[debug]: %s' "$is_debug_string" >>$output - - expected_file="${output%.out}.expected" - if [ -f "$expected_file" ] && [ $CODE -eq 0 ]; then - awk '/^\[[^]]*\]:/ {exit} {print}' "$output" > /tmp/program_output - if cmp -s /tmp/program_output "$expected_file"; then - printf '\n[matches]: true' >>"$output" - else - printf '\n[matches]: false' >>"$output" - fi - rm -f /tmp/program_output - fi - - return $CODE -} - -compile_source() { - src="$1" - bin="$2" - output="$3" - flags="$4" - - test -f "$bin" && rm "$bin" || true - g++ @compile_flags.txt $flags "$src" -o "$bin" 2>"$output" - CODE=$? - - if [ $CODE -gt 0 ]; then - printf '\n[code]: %s' "$CODE" >>"$output" - return $CODE - else - echo '' >"$output" - return 0 - fi -}