fix: systematic context guards and full panel lifecycle (#317)
## Problem The `CONTEST_ACTIONS` list in `handle_command` was a manually maintained parallel of `ACTIONS` that had already drifted (`pick` was incorrectly included, breaking `:CP pick` cold). `navigate_problem` only cancelled the `'run'` panel on `next`/`prev`, leaving interactive and stress terminals alive and in-flight `run_io_view` callbacks unguarded. `pick` and `cache` also appeared twice in tab-completion when a contest was active. ## Solution Replace `CONTEST_ACTIONS` with a `requires_context` field on `ParsedCommand`, set at each parse-site in `parse_command` so the semantics live with the action definition. Expand `navigate_problem` to call `cancel_io_view()` and dispatch `cancel_interactive`/`stress.cancel` for all panel types, mirroring the contest-switch logic. Filter already-present `pick` and `cache` from the context-conditional completion candidates.
This commit is contained in:
parent
7e7e135681
commit
6c036a7b2e
3 changed files with 40 additions and 11 deletions
|
|
@ -11,6 +11,7 @@ local actions = constants.ACTIONS
|
||||||
---@field type string
|
---@field type string
|
||||||
---@field error string?
|
---@field error string?
|
||||||
---@field action? string
|
---@field action? string
|
||||||
|
---@field requires_context? boolean
|
||||||
---@field message? string
|
---@field message? string
|
||||||
---@field contest? string
|
---@field contest? string
|
||||||
---@field platform? string
|
---@field platform? string
|
||||||
|
|
@ -71,7 +72,7 @@ local function parse_command(args)
|
||||||
end
|
end
|
||||||
elseif first == 'race' then
|
elseif first == 'race' then
|
||||||
if args[2] == 'stop' then
|
if args[2] == 'stop' then
|
||||||
return { type = 'action', action = 'race_stop' }
|
return { type = 'action', action = 'race_stop', requires_context = false }
|
||||||
end
|
end
|
||||||
if not args[2] or not args[3] then
|
if not args[2] or not args[3] then
|
||||||
return {
|
return {
|
||||||
|
|
@ -86,6 +87,7 @@ local function parse_command(args)
|
||||||
return {
|
return {
|
||||||
type = 'action',
|
type = 'action',
|
||||||
action = 'race',
|
action = 'race',
|
||||||
|
requires_context = false,
|
||||||
platform = args[2],
|
platform = args[2],
|
||||||
contest = args[3],
|
contest = args[3],
|
||||||
language = language,
|
language = language,
|
||||||
|
|
@ -93,14 +95,20 @@ local function parse_command(args)
|
||||||
elseif first == 'interact' then
|
elseif first == 'interact' then
|
||||||
local inter = args[2]
|
local inter = args[2]
|
||||||
if inter and inter ~= '' then
|
if inter and inter ~= '' then
|
||||||
return { type = 'action', action = 'interact', interactor_cmd = inter }
|
return {
|
||||||
|
type = 'action',
|
||||||
|
action = 'interact',
|
||||||
|
requires_context = true,
|
||||||
|
interactor_cmd = inter,
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return { type = 'action', action = 'interact' }
|
return { type = 'action', action = 'interact', requires_context = true }
|
||||||
end
|
end
|
||||||
elseif first == 'stress' then
|
elseif first == 'stress' then
|
||||||
return {
|
return {
|
||||||
type = 'action',
|
type = 'action',
|
||||||
action = 'stress',
|
action = 'stress',
|
||||||
|
requires_context = true,
|
||||||
generator_cmd = args[2],
|
generator_cmd = args[2],
|
||||||
brute_cmd = args[3],
|
brute_cmd = args[3],
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +127,7 @@ local function parse_command(args)
|
||||||
end
|
end
|
||||||
test_index = idx
|
test_index = idx
|
||||||
end
|
end
|
||||||
return { type = 'action', action = 'edit', test_index = test_index }
|
return { type = 'action', action = 'edit', requires_context = true, test_index = test_index }
|
||||||
elseif first == 'run' or first == 'panel' then
|
elseif first == 'run' or first == 'panel' then
|
||||||
local debug = false
|
local debug = false
|
||||||
local test_indices = nil
|
local test_indices = nil
|
||||||
|
|
@ -232,10 +240,22 @@ local function parse_command(args)
|
||||||
return {
|
return {
|
||||||
type = 'action',
|
type = 'action',
|
||||||
action = first,
|
action = first,
|
||||||
|
requires_context = true,
|
||||||
test_indices = test_indices,
|
test_indices = test_indices,
|
||||||
debug = debug,
|
debug = debug,
|
||||||
mode = mode,
|
mode = mode,
|
||||||
}
|
}
|
||||||
|
elseif first == 'pick' then
|
||||||
|
local language = nil
|
||||||
|
if #args >= 3 and args[2] == '--lang' then
|
||||||
|
language = args[3]
|
||||||
|
elseif #args >= 2 and args[2] ~= nil and args[2]:sub(1, 2) ~= '--' then
|
||||||
|
return {
|
||||||
|
type = 'error',
|
||||||
|
message = ("Unknown argument '%s' for action '%s'"):format(args[2], first),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return { type = 'action', action = 'pick', requires_context = false, language = language }
|
||||||
else
|
else
|
||||||
local language = nil
|
local language = nil
|
||||||
if #args >= 3 and args[2] == '--lang' then
|
if #args >= 3 and args[2] == '--lang' then
|
||||||
|
|
@ -246,7 +266,7 @@ local function parse_command(args)
|
||||||
message = ("Unknown argument '%s' for action '%s'"):format(args[2], first),
|
message = ("Unknown argument '%s' for action '%s'"):format(args[2], first),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
return { type = 'action', action = first, language = language }
|
return { type = 'action', action = first, requires_context = true, language = language }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -258,7 +278,7 @@ local function parse_command(args)
|
||||||
}
|
}
|
||||||
elseif #args == 2 then
|
elseif #args == 2 then
|
||||||
if args[2] == 'login' or args[2] == 'logout' then
|
if args[2] == 'login' or args[2] == 'logout' then
|
||||||
return { type = 'action', action = args[2], platform = first }
|
return { type = 'action', action = args[2], requires_context = false, platform = first }
|
||||||
end
|
end
|
||||||
local contest = args[2]
|
local contest = args[2]
|
||||||
if first == 'codeforces' then
|
if first == 'codeforces' then
|
||||||
|
|
@ -318,9 +338,7 @@ function M.handle_command(opts)
|
||||||
local restore = require('cp.restore')
|
local restore = require('cp.restore')
|
||||||
restore.restore_from_current_file()
|
restore.restore_from_current_file()
|
||||||
elseif cmd.type == 'action' then
|
elseif cmd.type == 'action' then
|
||||||
local CONTEST_ACTIONS =
|
if cmd.requires_context and not state.get_platform() then
|
||||||
{ 'run', 'panel', 'edit', 'interact', 'stress', 'submit', 'next', 'prev', 'pick' }
|
|
||||||
if vim.tbl_contains(CONTEST_ACTIONS, cmd.action) and not state.get_platform() then
|
|
||||||
local restore = require('cp.restore')
|
local restore = require('cp.restore')
|
||||||
if not restore.restore_from_current_file() then
|
if not restore.restore_from_current_file() then
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -477,9 +477,15 @@ function M.navigate_problem(direction, language)
|
||||||
|
|
||||||
logger.log(('navigate_problem: %s -> %s'):format(current_problem_id, problems[new_index].id))
|
logger.log(('navigate_problem: %s -> %s'):format(current_problem_id, problems[new_index].id))
|
||||||
|
|
||||||
|
local views = require('cp.ui.views')
|
||||||
|
views.cancel_io_view()
|
||||||
local active_panel = state.get_active_panel()
|
local active_panel = state.get_active_panel()
|
||||||
if active_panel == 'run' then
|
if active_panel == 'run' then
|
||||||
require('cp.ui.views').disable()
|
views.disable()
|
||||||
|
elseif active_panel == 'interactive' then
|
||||||
|
views.cancel_interactive()
|
||||||
|
elseif active_panel == 'stress' then
|
||||||
|
require('cp.stress').cancel()
|
||||||
end
|
end
|
||||||
|
|
||||||
local lang = nil
|
local lang = nil
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,12 @@ end, {
|
||||||
table.insert(candidates, 'cache')
|
table.insert(candidates, 'cache')
|
||||||
table.insert(candidates, 'pick')
|
table.insert(candidates, 'pick')
|
||||||
if platform and contest_id then
|
if platform and contest_id then
|
||||||
vim.list_extend(candidates, actions)
|
vim.list_extend(
|
||||||
|
candidates,
|
||||||
|
vim.tbl_filter(function(a)
|
||||||
|
return a ~= 'pick' and a ~= 'cache'
|
||||||
|
end, actions)
|
||||||
|
)
|
||||||
local cache = require('cp.cache')
|
local cache = require('cp.cache')
|
||||||
cache.load()
|
cache.load()
|
||||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue