disable scraper disabling
This commit is contained in:
parent
0a320945a0
commit
aae98a5796
9 changed files with 88 additions and 166 deletions
|
|
@ -1,14 +1,12 @@
|
|||
---@class LanguageConfig
|
||||
---@class PlatformLanguageConfig
|
||||
---@field compile? string[] Compile command template
|
||||
---@field test string[] Test execution command template
|
||||
---@field debug? string[] Debug command template
|
||||
---@field executable? string Executable name
|
||||
---@field version? number Language version
|
||||
---@field extension? string File extension
|
||||
|
||||
---@class ContestConfig
|
||||
---@field cpp LanguageConfig
|
||||
---@field python LanguageConfig
|
||||
---@class PlatformConfig
|
||||
---@field [string] PlatformLanguageConfig
|
||||
---@field default_language? string
|
||||
|
||||
---@class Hooks
|
||||
|
|
@ -28,22 +26,20 @@
|
|||
---@field git DiffGitConfig
|
||||
|
||||
---@class cp.Config
|
||||
---@field platforms table<string, ContestConfig>
|
||||
---@field platforms table<string, PlatformConfig>
|
||||
---@field snippets any[]
|
||||
---@field hooks Hooks
|
||||
---@field debug boolean
|
||||
---@field scrapers string[]
|
||||
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
|
||||
---@field run_panel RunPanelConfig
|
||||
---@field diff DiffConfig
|
||||
---@field picker string|nil
|
||||
|
||||
---@class cp.PartialConfig
|
||||
---@field platforms? table<string, ContestConfig>
|
||||
---@field platforms? table<string, PlatformConfig>
|
||||
---@field snippets? any[]
|
||||
---@field hooks? Hooks
|
||||
---@field debug? boolean
|
||||
---@field scrapers? string[]
|
||||
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
|
||||
---@field run_panel? RunPanelConfig
|
||||
---@field diff? DiffConfig
|
||||
|
|
@ -64,7 +60,6 @@ local default_contest_config = {
|
|||
test = { '{source}' },
|
||||
debug = { '{source}' },
|
||||
executable = 'python',
|
||||
extension = 'py',
|
||||
},
|
||||
default_language = 'cpp',
|
||||
}
|
||||
|
|
@ -111,7 +106,6 @@ function M.setup(user_config)
|
|||
snippets = { user_config.snippets, { 'table', 'nil' }, true },
|
||||
hooks = { user_config.hooks, { 'table', 'nil' }, true },
|
||||
debug = { user_config.debug, { 'boolean', 'nil' }, true },
|
||||
scrapers = { user_config.scrapers, { 'table', 'nil' }, true },
|
||||
filename = { user_config.filename, { 'function', 'nil' }, true },
|
||||
run_panel = { user_config.run_panel, { 'table', 'nil' }, true },
|
||||
diff = { user_config.diff, { 'table', 'nil' }, true },
|
||||
|
|
@ -136,22 +130,6 @@ function M.setup(user_config)
|
|||
end
|
||||
end
|
||||
|
||||
if user_config.scrapers then
|
||||
for _, platform_name in ipairs(user_config.scrapers) do
|
||||
if type(platform_name) ~= 'string' then
|
||||
error(('Invalid scraper value type. Expected string, got %s'):format(type(platform_name)))
|
||||
end
|
||||
if not vim.tbl_contains(constants.PLATFORMS, platform_name) then
|
||||
error(
|
||||
("Invalid platform '%s' in scrapers config. Valid platforms: %s"):format(
|
||||
platform_name,
|
||||
table.concat(constants.PLATFORMS, ', ')
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if user_config.picker then
|
||||
if not vim.tbl_contains({ 'telescope', 'fzf-lua' }, user_config.picker) then
|
||||
error(("Invalid picker '%s'. Must be 'telescope' or 'fzf-lua'"):format(user_config.picker))
|
||||
|
|
@ -206,34 +184,9 @@ function M.setup(user_config)
|
|||
})
|
||||
|
||||
for _, platform_config in pairs(config.platforms) do
|
||||
for lang_name, lang_config in pairs(platform_config) do
|
||||
if type(lang_config) == 'table' and not lang_config.extension then
|
||||
if lang_name == 'cpp' then
|
||||
lang_config.extension = 'cpp'
|
||||
elseif lang_name == 'python' then
|
||||
lang_config.extension = 'py'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vim.print(platform_config)
|
||||
if not platform_config.default_language then
|
||||
local available_langs = {}
|
||||
for lang_name, lang_config in pairs(platform_config) do
|
||||
if type(lang_config) == 'table' and lang_name ~= 'default_language' then
|
||||
table.insert(available_langs, lang_name)
|
||||
end
|
||||
end
|
||||
|
||||
if vim.tbl_isemtpy(available_langs) then
|
||||
error('No language configurations found')
|
||||
end
|
||||
|
||||
vim.print('sorting langs')
|
||||
--- arbitrarily break ties
|
||||
table.sort(available_langs)
|
||||
vim.print(available_langs)
|
||||
platform_config.default_language = available_langs[1]
|
||||
-- arbitrarily choose a language
|
||||
platform_config.default_language = vim.tbl_keys(platform_config)[1]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ M.canonical_filetypes = {
|
|||
[M.PYTHON] = 'python',
|
||||
}
|
||||
|
||||
---@type table<string, string>
|
||||
M.canonical_filetype_to_extension = {
|
||||
[M.CPP] = 'cc',
|
||||
[M.PYTHON] = 'py',
|
||||
}
|
||||
|
||||
---@type table<number, string>
|
||||
M.signal_codes = {
|
||||
[128] = 'SIGILL',
|
||||
|
|
|
|||
|
|
@ -5,15 +5,11 @@ local logger = require('cp.log')
|
|||
local state = require('cp.state')
|
||||
|
||||
function M.restore_from_current_file()
|
||||
local current_file = vim.fn.expand('%:p')
|
||||
if current_file == '' then
|
||||
logger.log('No file is currently open.', vim.log.levels.ERROR)
|
||||
return false
|
||||
end
|
||||
|
||||
cache.load()
|
||||
|
||||
local current_file = vim.fn.expand('%:p')
|
||||
local file_state = cache.get_file_state(current_file)
|
||||
if not file_state then
|
||||
if current_file or not file_state then
|
||||
logger.log('No cached state found for current file.', vim.log.levels.ERROR)
|
||||
return false
|
||||
end
|
||||
|
|
@ -27,13 +23,9 @@ function M.restore_from_current_file()
|
|||
)
|
||||
|
||||
local setup = require('cp.setup')
|
||||
if not setup.set_platform(file_state.platform) then
|
||||
return false
|
||||
end
|
||||
|
||||
local _ = setup.set_platform(file_state.platform)
|
||||
state.set_contest_id(file_state.contest_id)
|
||||
state.set_problem_id(file_state.problem_id)
|
||||
|
||||
setup.setup_contest(file_state.platform, file_state.contest_id, file_state.problem_id)
|
||||
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -7,26 +7,28 @@
|
|||
---@field peak_mb number
|
||||
---@field signal string|nil
|
||||
|
||||
---@class SubstitutableCommand
|
||||
---@field source string substituted via '{source}'
|
||||
---@field binary string substitued via '{binary}'
|
||||
|
||||
local M = {}
|
||||
local constants = require('cp.constants')
|
||||
local logger = require('cp.log')
|
||||
local utils = require('cp.utils')
|
||||
|
||||
local filetype_to_language = constants.filetype_to_language
|
||||
|
||||
local function get_language_from_file(source_file, contest_config)
|
||||
local ext = vim.fn.fnamemodify(source_file, ':e')
|
||||
return filetype_to_language[ext] or contest_config.default_language
|
||||
end
|
||||
|
||||
---@param cmd_template string[]
|
||||
---@param substitutions SubstitutableCommand
|
||||
---@return string[] string normalized with substitutions
|
||||
local function substitute_template(cmd_template, substitutions)
|
||||
local out = {}
|
||||
for _, a in ipairs(cmd_template) do
|
||||
local s = a
|
||||
for k, v in pairs(substitutions) do
|
||||
s = s:gsub('{' .. k .. '}', v)
|
||||
for _, arg in ipairs(cmd_template) do
|
||||
if arg == '{source}' and substitutions.source then
|
||||
table.insert(out, substitutions.source)
|
||||
elseif arg == '{binary}' and substitutions.binary then
|
||||
table.insert(out, substitutions.binary)
|
||||
else
|
||||
table.insert(out, arg)
|
||||
end
|
||||
table.insert(out, s)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
|
@ -39,12 +41,10 @@ function M.build_command(cmd_template, executable, substitutions)
|
|||
return cmd
|
||||
end
|
||||
|
||||
function M.compile(language_config, substitutions)
|
||||
if not language_config.compile then
|
||||
return { code = 0, stdout = '' }
|
||||
end
|
||||
|
||||
local cmd = substitute_template(language_config.compile, substitutions)
|
||||
---@param compile_cmd string[]
|
||||
---@param substitutions SubstitutableCommand
|
||||
function M.compile(compile_cmd, substitutions)
|
||||
local cmd = substitute_template(compile_cmd, substitutions)
|
||||
local sh = table.concat(cmd, ' ') .. ' 2>&1'
|
||||
|
||||
local t0 = vim.uv.hrtime()
|
||||
|
|
@ -164,32 +164,19 @@ function M.run(cmd, stdin, timeout_ms, memory_mb)
|
|||
}
|
||||
end
|
||||
|
||||
function M.compile_problem(contest_config, is_debug)
|
||||
function M.compile_problem()
|
||||
local state = require('cp.state')
|
||||
local source_file = state.get_source_file()
|
||||
if not source_file then
|
||||
return { success = false, output = 'No source file found.' }
|
||||
end
|
||||
|
||||
local language = get_language_from_file(source_file, contest_config)
|
||||
local language_config = contest_config[language]
|
||||
if not language_config then
|
||||
return { success = false, output = ('No configuration for language %s.'):format(language) }
|
||||
end
|
||||
|
||||
local binary_file = state.get_binary_file()
|
||||
local substitutions = { source = source_file, binary = binary_file }
|
||||
|
||||
local chosen = (is_debug and language_config.debug) and language_config.debug
|
||||
or language_config.compile
|
||||
if not chosen then
|
||||
local config = require('cp.config').get_config()
|
||||
local platform = state.get_platform() or ''
|
||||
local language = config.platforms[platform].default_language
|
||||
local compile_config = config.platforms[platform][language].compile
|
||||
if not compile_config then
|
||||
return { success = true, output = nil }
|
||||
end
|
||||
|
||||
local saved = language_config.compile
|
||||
language_config.compile = chosen
|
||||
local r = M.compile(language_config, substitutions)
|
||||
language_config.compile = saved
|
||||
local substitutions = { source = state.get_source_file(), binary = state.get_binary_file() }
|
||||
local r = M.compile(compile_config, substitutions)
|
||||
|
||||
if r.code ~= 0 then
|
||||
return { success = false, output = r.stdout or 'unknown error' }
|
||||
|
|
|
|||
|
|
@ -31,8 +31,11 @@
|
|||
|
||||
local M = {}
|
||||
local cache = require('cp.cache')
|
||||
local config = require('cp.config').get_config()
|
||||
local constants = require('cp.constants')
|
||||
local execute = require('cp.runner.execute')
|
||||
local logger = require('cp.log')
|
||||
local state = require('cp.state')
|
||||
|
||||
---@type RunPanelState
|
||||
local run_panel_state = {
|
||||
|
|
@ -90,42 +93,35 @@ local function create_sentinal_panel_data(test_cases)
|
|||
return out
|
||||
end
|
||||
|
||||
---@param language_config LanguageConfig
|
||||
---@param substitutions table<string, string>
|
||||
---@param cmd string[]
|
||||
---@param executable string
|
||||
---@return string[]
|
||||
local function build_command(language_config, substitutions)
|
||||
local execute = require('cp.runner.execute')
|
||||
return execute.build_command(language_config.test, language_config.executable, substitutions)
|
||||
local function build_command(cmd, executable, substitutions)
|
||||
return execute.build_command(cmd, executable, substitutions)
|
||||
end
|
||||
|
||||
---@param contest_config ContestConfig
|
||||
---@param cp_config cp.Config
|
||||
---@param test_case RanTestCase
|
||||
---@return { status: "pass"|"fail"|"tle"|"mle", actual: string, actual_highlights: Highlight[], error: string, stderr: string, time_ms: number, code: integer, ok: boolean, signal: string, tled: boolean, mled: boolean, rss_mb: number }
|
||||
local function run_single_test_case(contest_config, cp_config, test_case)
|
||||
local state = require('cp.state')
|
||||
local exec = require('cp.runner.execute')
|
||||
|
||||
local function run_single_test_case(test_case)
|
||||
local source_file = state.get_source_file()
|
||||
local ext = vim.fn.fnamemodify(source_file or '', ':e')
|
||||
local lang_name = constants.filetype_to_language[ext] or contest_config.default_language
|
||||
local language_config = contest_config[lang_name]
|
||||
|
||||
local binary_file = state.get_binary_file()
|
||||
local substitutions = { source = source_file, binary = binary_file }
|
||||
|
||||
local cmd = build_command(language_config, substitutions)
|
||||
local platform_config = config.platforms[state.get_platform() or '']
|
||||
local language_config = platform_config[platform_config.default_language]
|
||||
local cmd = build_command(language_config.test, language_config.executable, substitutions)
|
||||
local stdin_content = (test_case.input or '') .. '\n'
|
||||
local timeout_ms = (run_panel_state.constraints and run_panel_state.constraints.timeout_ms) or 0
|
||||
local memory_mb = run_panel_state.constraints and run_panel_state.constraints.memory_mb or 0
|
||||
|
||||
local r = exec.run(cmd, stdin_content, timeout_ms, memory_mb)
|
||||
local r = execute.run(cmd, stdin_content, timeout_ms, memory_mb)
|
||||
|
||||
local ansi = require('cp.ui.ansi')
|
||||
local out = r.stdout or ''
|
||||
local highlights = {}
|
||||
if out ~= '' then
|
||||
if cp_config.run_panel.ansi then
|
||||
if config.run_panel.ansi then
|
||||
local parsed = ansi.parse_ansi_text(out)
|
||||
out = table.concat(parsed.lines, '\n')
|
||||
highlights = parsed.highlights
|
||||
|
|
@ -134,7 +130,7 @@ local function run_single_test_case(contest_config, cp_config, test_case)
|
|||
end
|
||||
end
|
||||
|
||||
local max_lines = cp_config.run_panel.max_output_lines
|
||||
local max_lines = config.run_panel.max_output_lines
|
||||
local lines = vim.split(out, '\n')
|
||||
if #lines > max_lines then
|
||||
local trimmed = {}
|
||||
|
|
@ -180,9 +176,8 @@ local function run_single_test_case(contest_config, cp_config, test_case)
|
|||
}
|
||||
end
|
||||
|
||||
---@param state table
|
||||
---@return boolean
|
||||
function M.load_test_cases(state)
|
||||
function M.load_test_cases()
|
||||
local tcs = cache.get_test_cases(
|
||||
state.get_platform() or '',
|
||||
state.get_contest_id() or '',
|
||||
|
|
@ -201,18 +196,16 @@ function M.load_test_cases(state)
|
|||
return #tcs > 0
|
||||
end
|
||||
|
||||
---@param contest_config ContestConfig
|
||||
---@param cp_config cp.Config
|
||||
---@param index number
|
||||
---@return boolean
|
||||
function M.run_test_case(contest_config, cp_config, index)
|
||||
function M.run_test_case(index)
|
||||
local tc = run_panel_state.test_cases[index]
|
||||
if not tc then
|
||||
return false
|
||||
end
|
||||
|
||||
tc.status = 'running'
|
||||
local r = run_single_test_case(contest_config, cp_config, tc)
|
||||
local r = run_single_test_case(tc)
|
||||
|
||||
tc.status = r.status
|
||||
tc.actual = r.actual
|
||||
|
|
@ -230,13 +223,11 @@ function M.run_test_case(contest_config, cp_config, index)
|
|||
return true
|
||||
end
|
||||
|
||||
---@param contest_config ContestConfig
|
||||
---@param cp_config cp.Config
|
||||
---@return RanTestCase[]
|
||||
function M.run_all_test_cases(contest_config, cp_config)
|
||||
function M.run_all_test_cases()
|
||||
local results = {}
|
||||
for i = 1, #run_panel_state.test_cases do
|
||||
M.run_test_case(contest_config, cp_config, i)
|
||||
M.run_test_case(i)
|
||||
results[i] = run_panel_state.test_cases[i]
|
||||
end
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ local utils = require('cp.utils')
|
|||
|
||||
local function syshandle(result)
|
||||
if result.code ~= 0 then
|
||||
vim.print(('<%s>'):format(result.stderr))
|
||||
local msg = 'Scraper failed: ' .. (result.stderr or 'Unknown error')
|
||||
return { success = false, error = msg }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -42,19 +42,13 @@ end
|
|||
---@param contest_id string
|
||||
---@param problem_id string|nil
|
||||
function M.setup_contest(platform, contest_id, problem_id)
|
||||
local config = config_module.get_config()
|
||||
if not vim.tbl_contains(config.scrapers, platform) then
|
||||
logger.log(('Scraping disabled for %s.'):format(platform), vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
state.set_contest_id(contest_id)
|
||||
cache.load()
|
||||
|
||||
local function proceed(contest_data)
|
||||
local problems = contest_data.problems
|
||||
local pid = problems[(problem_id and contest_data.index_map[problem_id] or 1)].id
|
||||
M.setup_problem(pid, language)
|
||||
M.setup_problem(pid)
|
||||
|
||||
local cached_len = #vim.tbl_filter(function(p)
|
||||
return not vim.tbl_isempty(cache.get_test_cases(platform, contest_id, p.id))
|
||||
|
|
@ -96,8 +90,7 @@ function M.setup_contest(platform, contest_id, problem_id)
|
|||
end
|
||||
|
||||
---@param problem_id string
|
||||
---@param language? string
|
||||
function M.setup_problem(problem_id, language)
|
||||
function M.setup_problem(problem_id)
|
||||
local platform = state.get_platform()
|
||||
if not platform then
|
||||
logger.log('No platform set.', vim.log.levels.ERROR)
|
||||
|
|
@ -111,10 +104,8 @@ function M.setup_problem(problem_id, language)
|
|||
vim.schedule(function()
|
||||
vim.cmd.only({ mods = { silent = true } })
|
||||
|
||||
local language = config.platforms[platform].default_language
|
||||
local source_file = state.get_source_file(language)
|
||||
if not source_file then
|
||||
return
|
||||
end
|
||||
vim.cmd.e(source_file)
|
||||
local source_buf = vim.api.nvim_get_current_buf()
|
||||
|
||||
|
|
|
|||
|
|
@ -90,10 +90,8 @@ function M.toggle_interactive()
|
|||
vim.cmd(('mksession! %s'):format(state.saved_interactive_session))
|
||||
vim.cmd('silent only')
|
||||
|
||||
local config = config_module.get_config()
|
||||
local contest_config = config.platforms[state.get_platform() or '']
|
||||
local execute = require('cp.runner.execute')
|
||||
local compile_result = execute.compile_problem(contest_config, false)
|
||||
local compile_result = execute.compile_problem()
|
||||
if not compile_result.success then
|
||||
require('cp.runner.run').handle_compilation_failure(compile_result.output)
|
||||
return
|
||||
|
|
@ -120,7 +118,8 @@ function M.toggle_interactive()
|
|||
state.set_active_panel('interactive')
|
||||
end
|
||||
|
||||
function M.toggle_run_panel(is_debug)
|
||||
---@param debug? boolean
|
||||
function M.toggle_run_panel(debug)
|
||||
if state.get_active_panel() == 'run' then
|
||||
if current_diff_layout then
|
||||
current_diff_layout.cleanup()
|
||||
|
|
@ -191,7 +190,7 @@ function M.toggle_run_panel(is_debug)
|
|||
if config.hooks and config.hooks.before_run then
|
||||
config.hooks.before_run(state)
|
||||
end
|
||||
if is_debug and config.hooks and config.hooks.before_debug then
|
||||
if debug and config.hooks and config.hooks.before_debug then
|
||||
config.hooks.before_debug(state)
|
||||
end
|
||||
|
||||
|
|
@ -199,7 +198,7 @@ function M.toggle_run_panel(is_debug)
|
|||
local input_file = state.get_input_file()
|
||||
logger.log(('run panel: checking test cases for %s'):format(input_file or 'none'))
|
||||
|
||||
if not run.load_test_cases(state) then
|
||||
if not run.load_test_cases() then
|
||||
logger.log('no test cases found', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
|
@ -284,10 +283,9 @@ function M.toggle_run_panel(is_debug)
|
|||
setup_keybindings_for_buffer(test_buffers.tab_buf)
|
||||
|
||||
local execute = require('cp.runner.execute')
|
||||
local contest_config = config.platforms[state.get_platform() or '']
|
||||
local compile_result = execute.compile_problem(contest_config, is_debug)
|
||||
local compile_result = execute.compile_problem()
|
||||
if compile_result.success then
|
||||
run.run_all_test_cases(contest_config, config)
|
||||
run.run_all_test_cases()
|
||||
else
|
||||
run.handle_compilation_failure(compile_result.output)
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue