From 78fb4f8f4b2cfc8c4d79ef3e8037d0b772116fc8 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 21 Sep 2025 15:08:55 -0400 Subject: [PATCH] feat(cache): cache clearing, updating and resetting --- README.md | 4 --- lua/cp/cache.lua | 20 ++++++++++++ lua/cp/config.lua | 2 +- lua/cp/constants.lua | 2 +- lua/cp/init.lua | 53 ++++++++++++++++++++++++++++-- lua/cp/runner/execute.lua | 4 +-- lua/cp/runner/run.lua | 2 +- scrapers/atcoder.py | 68 +-------------------------------------- scrapers/codeforces.py | 36 +-------------------- spec/cache_spec.lua | 34 ++++++++++++++++++++ 10 files changed, 111 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 0766ca4..b31febd 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,3 @@ See [my config](https://github.com/barrett-ruth/dots/blob/main/nvim/lua/plugins/ - [competitest.nvim](https://github.com/xeluxee/competitest.nvim) - [assistant.nvim](https://github.com/A7Lavinraj/assistant.nvim) - -## TODO - -- Windows support diff --git a/lua/cp/cache.lua b/lua/cp/cache.lua index 04d7dbd..c59fc61 100644 --- a/lua/cp/cache.lua +++ b/lua/cp/cache.lua @@ -342,4 +342,24 @@ function M.clear_contest_list(platform) end end +function M.clear_all() + cache_data = {} + M.save() +end + +---@param platform string +function M.clear_platform(platform) + vim.validate({ + platform = { platform, 'string' }, + }) + + if cache_data[platform] then + cache_data[platform] = nil + end + if cache_data.contest_lists and cache_data.contest_lists[platform] then + cache_data.contest_lists[platform] = nil + end + M.save() +end + return M diff --git a/lua/cp/config.lua b/lua/cp/config.lua index 0207968..7e3dcf6 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -101,7 +101,7 @@ M.defaults = { filename = nil, run_panel = { ansi = true, - diff_mode = 'vim', + diff_mode = 'git', next_test_key = '', prev_test_key = '', toggle_diff_key = 't', diff --git a/lua/cp/constants.lua b/lua/cp/constants.lua index c14569b..7544435 100644 --- a/lua/cp/constants.lua +++ b/lua/cp/constants.lua @@ -1,7 +1,7 @@ local M = {} M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' } -M.ACTIONS = { 'run', 'next', 'prev', 'pick' } +M.ACTIONS = { 'run', 'next', 'prev', 'pick', 'cache' } M.PLATFORM_DISPLAY_NAMES = { atcoder = 'AtCoder', diff --git a/lua/cp/init.lua b/lua/cp/init.lua index d933f42..9bd8597 100644 --- a/lua/cp/init.lua +++ b/lua/cp/init.lua @@ -294,6 +294,7 @@ local function toggle_run_panel(is_debug) vim.api.nvim_set_current_win(parent_win) vim.cmd.split() + vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35)) local actual_win = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(actual_win, actual_buf) @@ -341,6 +342,7 @@ local function toggle_run_panel(is_debug) vim.api.nvim_set_current_win(parent_win) vim.cmd.split() + vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35)) local diff_win = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(diff_win, diff_buf) @@ -375,6 +377,7 @@ local function toggle_run_panel(is_debug) vim.api.nvim_set_current_win(parent_win) vim.cmd.split() + vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35)) local win = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(win, buf) vim.api.nvim_set_option_value('filetype', 'cptest', { buf = buf }) @@ -600,7 +603,7 @@ local function toggle_run_panel(is_debug) state.test_buffers = test_buffers state.test_windows = test_windows local test_state = run.get_run_panel_state() - logger.log(string.format('test panel opened (%d test cases)', #test_state.test_cases)) + logger.log(string.format('test panel opened (%d test cases)', #test_state.test_cases), vim.log.levels.INFO) end ---@param contest_id string @@ -751,6 +754,26 @@ local function handle_pick_action() end end +local function handle_cache_command(cmd) + if cmd.subcommand == 'clear' then + cache.load() + if cmd.platform then + if vim.tbl_contains(platforms, cmd.platform) then + cache.clear_platform(cmd.platform) + logger.log(('cleared cache for %s'):format(cmd.platform), vim.log.levels.INFO, true) + else + logger.log( + ('unknown platform: %s. Available: %s'):format(cmd.platform, table.concat(platforms, ', ')), + vim.log.levels.ERROR + ) + end + else + cache.clear_all() + logger.log('cleared all cache', vim.log.levels.INFO, true) + end + end +end + local function restore_from_current_file() local current_file = vim.fn.expand('%:p') if current_file == '' then @@ -820,7 +843,24 @@ local function parse_command(args) local first = filtered_args[1] if vim.tbl_contains(actions, first) then - return { type = 'action', action = first, language = language, debug = debug } + if first == 'cache' then + local subcommand = filtered_args[2] + if not subcommand then + return { type = 'error', message = 'cache command requires subcommand: clear' } + end + if subcommand == 'clear' then + local platform = filtered_args[3] + return { + type = 'cache', + subcommand = 'clear', + platform = platform + } + else + return { type = 'error', message = 'unknown cache subcommand: ' .. subcommand } + end + else + return { type = 'action', action = first, language = language, debug = debug } + end end if vim.tbl_contains(platforms, first) then @@ -896,6 +936,11 @@ function M.handle_command(opts) return end + if cmd.type == 'cache' then + handle_cache_command(cmd) + return + end + if cmd.type == 'platform_only' then set_platform(cmd.platform) return @@ -929,7 +974,9 @@ function M.handle_command(opts) #metadata_result.problems, cmd.platform, cmd.contest - ) + ), + vim.log.levels.INFO, + true ) problem_ids = vim.tbl_map(function(prob) return prob.id diff --git a/lua/cp/runner/execute.lua b/lua/cp/runner/execute.lua index 109f365..f53d26a 100644 --- a/lua/cp/runner/execute.lua +++ b/lua/cp/runner/execute.lua @@ -94,7 +94,7 @@ function M.compile_generic(language_config, substitutions) result.stderr = ansi.bytes_to_string(result.stderr or '') if result.code == 0 then - logger.log(('compilation successful (%.1fms)'):format(compile_time)) + logger.log(('compilation successful (%.1fms)'):format(compile_time), vim.log.levels.INFO) else logger.log(('compilation failed (%.1fms)'):format(compile_time)) end @@ -235,7 +235,7 @@ function M.compile_problem(ctx, contest_config, is_debug) if compile_result.code ~= 0 then return { success = false, output = compile_result.stdout or 'unknown error' } end - logger.log(('compilation successful (%s)'):format(is_debug and 'debug mode' or 'test mode')) + logger.log(('compilation successful (%s)'):format(is_debug and 'debug mode' or 'test mode'), vim.log.levels.INFO) end return { success = true, output = nil } diff --git a/lua/cp/runner/run.lua b/lua/cp/runner/run.lua index eef5901..f996a60 100644 --- a/lua/cp/runner/run.lua +++ b/lua/cp/runner/run.lua @@ -315,7 +315,7 @@ function M.load_test_cases(ctx, state) run_panel_state.constraints.memory_mb ) or '' - logger.log(('loaded %d test case(s)%s'):format(#test_cases, constraint_info)) + logger.log(('loaded %d test case(s)%s'):format(#test_cases, constraint_info), vim.log.levels.INFO) return #test_cases > 0 end diff --git a/scrapers/atcoder.py b/scrapers/atcoder.py index 83e1cc1..02afc83 100644 --- a/scrapers/atcoder.py +++ b/scrapers/atcoder.py @@ -272,74 +272,8 @@ def scrape_contests() -> list[ContestSummary]: r"[\uff01-\uff5e]", lambda m: chr(ord(m.group()) - 0xFEE0), name ) - def generate_display_name_from_id(contest_id: str) -> str: - parts = contest_id.replace("-", " ").replace("_", " ") - - parts = re.sub( - r"\b(jsc|JSC)\b", - "Japanese Student Championship", - parts, - flags=re.IGNORECASE, - ) - parts = re.sub( - r"\b(wtf|WTF)\b", - "World Tour Finals", - parts, - flags=re.IGNORECASE, - ) - parts = re.sub( - r"\b(ahc)(\d+)\b", - r"Heuristic Contest \2 (AHC)", - parts, - flags=re.IGNORECASE, - ) - parts = re.sub( - r"\b(arc)(\d+)\b", - r"Regular Contest \2 (ARC)", - parts, - flags=re.IGNORECASE, - ) - parts = re.sub( - r"\b(abc)(\d+)\b", - r"Beginner Contest \2 (ABC)", - parts, - flags=re.IGNORECASE, - ) - parts = re.sub( - r"\b(agc)(\d+)\b", - r"Grand Contest \2 (AGC)", - parts, - flags=re.IGNORECASE, - ) - - return parts.title() - - english_chars = sum(1 for c in name if c.isascii() and c.isalpha()) - total_chars = len(re.sub(r"\s+", "", name)) - - if total_chars > 0 and english_chars / total_chars < 0.3: - display_name = generate_display_name_from_id(contest_id) - else: - display_name = name - if "AtCoder Beginner Contest" in name: - match = re.search(r"AtCoder Beginner Contest (\d+)", name) - if match: - display_name = f"Beginner Contest {match.group(1)} (ABC)" - elif "AtCoder Regular Contest" in name: - match = re.search(r"AtCoder Regular Contest (\d+)", name) - if match: - display_name = f"Regular Contest {match.group(1)} (ARC)" - elif "AtCoder Grand Contest" in name: - match = re.search(r"AtCoder Grand Contest (\d+)", name) - if match: - display_name = f"Grand Contest {match.group(1)} (AGC)" - elif "AtCoder Heuristic Contest" in name: - match = re.search(r"AtCoder Heuristic Contest (\d+)", name) - if match: - display_name = f"Heuristic Contest {match.group(1)} (AHC)" - contests.append( - ContestSummary(id=contest_id, name=name, display_name=display_name) + ContestSummary(id=contest_id, name=name, display_name=name) ) return contests diff --git a/scrapers/codeforces.py b/scrapers/codeforces.py index ffc0837..334b782 100644 --- a/scrapers/codeforces.py +++ b/scrapers/codeforces.py @@ -237,42 +237,8 @@ def scrape_contests() -> list[ContestSummary]: contest_id = str(contest["id"]) name = contest["name"] - display_name = name - if "Educational Codeforces Round" in name: - match = re.search(r"Educational Codeforces Round (\d+)", name) - if match: - display_name = f"Educational Round {match.group(1)}" - elif "Codeforces Global Round" in name: - match = re.search(r"Codeforces Global Round (\d+)", name) - if match: - display_name = f"Global Round {match.group(1)}" - elif "Codeforces Round" in name: - div_match = re.search(r"Codeforces Round (\d+) \(Div\. (\d+)\)", name) - if div_match: - display_name = ( - f"Round {div_match.group(1)} (Div. {div_match.group(2)})" - ) - else: - combined_match = re.search( - r"Codeforces Round (\d+) \(Div\. 1 \+ Div\. 2\)", name - ) - if combined_match: - display_name = ( - f"Round {combined_match.group(1)} (Div. 1 + Div. 2)" - ) - else: - single_div_match = re.search( - r"Codeforces Round (\d+) \(Div\. 1\)", name - ) - if single_div_match: - display_name = f"Round {single_div_match.group(1)} (Div. 1)" - else: - round_match = re.search(r"Codeforces Round (\d+)", name) - if round_match: - display_name = f"Round {round_match.group(1)}" - contests.append( - ContestSummary(id=contest_id, name=name, display_name=display_name) + ContestSummary(id=contest_id, name=name, display_name=name) ) return contests diff --git a/spec/cache_spec.lua b/spec/cache_spec.lua index 5b06911..a72946a 100644 --- a/spec/cache_spec.lua +++ b/spec/cache_spec.lua @@ -156,4 +156,38 @@ describe('cp.cache', function() assert.equals('python', result.language) end) end) + + describe('cache management', function() + it('clears all cache data', function() + cache.set_contest_data('atcoder', 'test_contest', { { id = 'A' } }) + cache.set_contest_data('codeforces', 'test_contest', { { id = 'B' } }) + cache.set_file_state('/tmp/test.cpp', 'atcoder', 'abc123', 'a', 'cpp') + + cache.clear_all() + + assert.is_nil(cache.get_contest_data('atcoder', 'test_contest')) + assert.is_nil(cache.get_contest_data('codeforces', 'test_contest')) + assert.is_nil(cache.get_file_state('/tmp/test.cpp')) + end) + + it('clears cache for specific platform', function() + cache.set_contest_data('atcoder', 'test_contest', { { id = 'A' } }) + cache.set_contest_data('codeforces', 'test_contest', { { id = 'B' } }) + cache.set_contest_list('atcoder', { { id = '123', name = 'Test' } }) + cache.set_contest_list('codeforces', { { id = '456', name = 'Test' } }) + + cache.clear_platform('atcoder') + + assert.is_nil(cache.get_contest_data('atcoder', 'test_contest')) + assert.is_nil(cache.get_contest_list('atcoder')) + assert.is_not_nil(cache.get_contest_data('codeforces', 'test_contest')) + assert.is_not_nil(cache.get_contest_list('codeforces')) + end) + + it('handles clear platform for non-existent platform', function() + assert.has_no_errors(function() + cache.clear_platform('nonexistent') + end) + end) + end) end)