feat: bindings and --debug flag
This commit is contained in:
parent
038fcd36f8
commit
6a6cf2c594
8 changed files with 142 additions and 46 deletions
|
|
@ -39,22 +39,25 @@ COMMANDS *cp-commands*
|
||||||
:CP atcoder abc324
|
:CP atcoder abc324
|
||||||
<
|
<
|
||||||
View Commands ~
|
View Commands ~
|
||||||
:CP run [n] Run tests in I/O view (see |cp-io-view|).
|
:CP run [--debug] [n]
|
||||||
|
Run tests in I/O view (see |cp-io-view|).
|
||||||
Lightweight split showing test verdicts.
|
Lightweight split showing test verdicts.
|
||||||
Without [n]: runs all tests, shows verdict summary
|
Without [n]: runs all tests, shows verdict summary
|
||||||
With [n]: runs test n, shows detailed output
|
With [n]: runs test n, shows detailed output
|
||||||
|
--debug: Use debug build (builds to build/<name>.dbg)
|
||||||
Examples: >
|
Examples: >
|
||||||
:CP run " All tests, verdict list
|
:CP run " All tests
|
||||||
:CP run 3 " Test 3 detail
|
:CP run --debug 2 " Test 2, debug build
|
||||||
<
|
<
|
||||||
:CP panel [n] Open full-screen test panel (see |cp-panel|).
|
:CP panel [--debug] [n]
|
||||||
|
Open full-screen test panel (see |cp-panel|).
|
||||||
Aggregate table with diff modes for detailed analysis.
|
Aggregate table with diff modes for detailed analysis.
|
||||||
Optional [n] focuses on specific test.
|
Optional [n] focuses on specific test.
|
||||||
Example: >
|
--debug: Use debug build (with sanitizers, etc.)
|
||||||
:CP panel " All tests with diffs
|
Examples: >
|
||||||
:CP panel 2 " Focus on test 2
|
:CP panel " All tests
|
||||||
|
:CP panel --debug 3 " Test 3, debug build
|
||||||
<
|
<
|
||||||
:CP debug [n] Same as :CP panel but uses debug build configuration.
|
|
||||||
|
|
||||||
:CP pick Launch configured picker for interactive
|
:CP pick Launch configured picker for interactive
|
||||||
platform/contest selection.
|
platform/contest selection.
|
||||||
|
|
@ -97,13 +100,39 @@ Template Variables ~
|
||||||
Command templates support variable substitution using {variable} syntax:
|
Command templates support variable substitution using {variable} syntax:
|
||||||
|
|
||||||
• {source} Source file path (e.g. "abc324a.cpp")
|
• {source} Source file path (e.g. "abc324a.cpp")
|
||||||
• {binary} Output binary path (e.g. "build/abc324a.run")
|
• {binary} Output binary path (e.g. "build/abc324a.run" or
|
||||||
|
"build/abc324a.dbg" for debug builds)
|
||||||
|
|
||||||
Example template: >
|
Example template: >
|
||||||
build = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
build = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
||||||
< Would expand to: >
|
< Would expand to: >
|
||||||
g++ abc324a.cpp -o build/abc324a.run -std=c++17
|
g++ abc324a.cpp -o build/abc324a.run -std=c++17
|
||||||
<
|
<
|
||||||
|
Debug Builds ~
|
||||||
|
*cp-debug-builds*
|
||||||
|
The --debug flag uses the debug command configuration instead of build:
|
||||||
|
|
||||||
|
• Normal build: commands.build → outputs to build/<name>.run
|
||||||
|
• Debug build: commands.debug → outputs to build/<name>.dbg
|
||||||
|
|
||||||
|
Debug builds typically include sanitizers (address, undefined behavior) to
|
||||||
|
catch memory errors, buffer overflows, and other issues. Both binaries
|
||||||
|
coexist, so you can switch between normal and debug mode without
|
||||||
|
recompiling.
|
||||||
|
|
||||||
|
Example debug configuration: >
|
||||||
|
languages = {
|
||||||
|
cpp = {
|
||||||
|
extension = 'cc',
|
||||||
|
commands = {
|
||||||
|
build = { 'g++', '-std=c++17', '{source}', '-o', '{binary}' },
|
||||||
|
run = { '{binary}' },
|
||||||
|
debug = { 'g++', '-std=c++17', '-fsanitize=address,undefined',
|
||||||
|
'{source}', '-o', '{binary}' },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
CONFIGURATION *cp-config*
|
CONFIGURATION *cp-config*
|
||||||
|
|
@ -446,7 +475,7 @@ The panel provides full-screen test analysis with diff modes for detailed
|
||||||
debugging. Problem time/memory limit constraints are in columns Time/Mem
|
debugging. Problem time/memory limit constraints are in columns Time/Mem
|
||||||
respectively. Used time/memory are in columns Runtime/RSS respectively.
|
respectively. Used time/memory are in columns Runtime/RSS respectively.
|
||||||
|
|
||||||
Access with :CP panel or :CP debug (uses debug build configuration).
|
Access with :CP panel or :CP panel --debug (uses debug build configuration).
|
||||||
|
|
||||||
Interface ~
|
Interface ~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,23 +53,30 @@ local function parse_command(args)
|
||||||
else
|
else
|
||||||
return { type = 'action', action = 'interact' }
|
return { type = 'action', action = 'interact' }
|
||||||
end
|
end
|
||||||
elseif first == 'run' then
|
elseif first == 'run' or first == 'panel' then
|
||||||
local test_arg = args[2]
|
local debug = false
|
||||||
if test_arg then
|
local test_index = nil
|
||||||
local test_index = tonumber(test_arg)
|
|
||||||
if not test_index then
|
for i = 2, #args do
|
||||||
return {
|
local arg = args[i]
|
||||||
type = 'error',
|
if arg == '--debug' then
|
||||||
message = ("Test index '%s' is not a number"):format(test_index),
|
debug = true
|
||||||
}
|
else
|
||||||
|
local idx = tonumber(arg)
|
||||||
|
if not idx then
|
||||||
|
return {
|
||||||
|
type = 'error',
|
||||||
|
message = ("Invalid argument '%s': expected test number or --debug"):format(arg),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if idx < 1 or idx ~= math.floor(idx) then
|
||||||
|
return { type = 'error', message = ("'%s' is not a valid test index"):format(idx) }
|
||||||
|
end
|
||||||
|
test_index = idx
|
||||||
end
|
end
|
||||||
if test_index < 1 or test_index ~= math.floor(test_index) then
|
|
||||||
return { type = 'error', message = ("'%s' is not a valid test index"):format(test_index) }
|
|
||||||
end
|
|
||||||
return { type = 'action', action = 'run', test_index = test_index }
|
|
||||||
else
|
|
||||||
return { type = 'action', action = 'run' }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return { type = 'action', action = first, test_index = test_index, debug = debug }
|
||||||
else
|
else
|
||||||
return { type = 'action', action = first }
|
return { type = 'action', action = first }
|
||||||
end
|
end
|
||||||
|
|
@ -127,11 +134,9 @@ function M.handle_command(opts)
|
||||||
if cmd.action == 'interact' then
|
if cmd.action == 'interact' then
|
||||||
ui.toggle_interactive(cmd.interactor_cmd)
|
ui.toggle_interactive(cmd.interactor_cmd)
|
||||||
elseif cmd.action == 'run' then
|
elseif cmd.action == 'run' then
|
||||||
ui.run_io_view(cmd.test_index)
|
ui.run_io_view(cmd.test_index, cmd.debug)
|
||||||
elseif cmd.action == 'panel' then
|
elseif cmd.action == 'panel' then
|
||||||
ui.toggle_panel()
|
ui.toggle_panel({ debug = cmd.debug, test_index = cmd.test_index })
|
||||||
elseif cmd.action == 'debug' then
|
|
||||||
ui.toggle_panel({ debug = true })
|
|
||||||
elseif cmd.action == 'next' then
|
elseif cmd.action == 'next' then
|
||||||
setup.navigate_problem(1)
|
setup.navigate_problem(1)
|
||||||
elseif cmd.action == 'prev' then
|
elseif cmd.action == 'prev' then
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,12 @@
|
||||||
---@field setup_io_input? fun(bufnr: integer, state: cp.State)
|
---@field setup_io_input? fun(bufnr: integer, state: cp.State)
|
||||||
---@field setup_io_output? fun(bufnr: integer, state: cp.State)
|
---@field setup_io_output? fun(bufnr: integer, state: cp.State)
|
||||||
|
|
||||||
|
---@class RunConfig
|
||||||
|
---@field width number
|
||||||
|
|
||||||
---@class CpUI
|
---@class CpUI
|
||||||
---@field ansi boolean
|
---@field ansi boolean
|
||||||
|
---@field run RunConfig
|
||||||
---@field panel PanelConfig
|
---@field panel PanelConfig
|
||||||
---@field diff DiffConfig
|
---@field diff DiffConfig
|
||||||
---@field picker string|nil
|
---@field picker string|nil
|
||||||
|
|
@ -116,6 +120,7 @@ M.defaults = {
|
||||||
filename = nil,
|
filename = nil,
|
||||||
ui = {
|
ui = {
|
||||||
ansi = true,
|
ansi = true,
|
||||||
|
run = { width = 0.3 },
|
||||||
panel = { diff_mode = 'none', max_output_lines = 50 },
|
panel = { diff_mode = 'none', max_output_lines = 50 },
|
||||||
diff = {
|
diff = {
|
||||||
git = {
|
git = {
|
||||||
|
|
@ -157,6 +162,11 @@ local function validate_language(id, lang)
|
||||||
extension = { lang.extension, 'string' },
|
extension = { lang.extension, 'string' },
|
||||||
commands = { lang.commands, { 'table' } },
|
commands = { lang.commands, { 'table' } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if not lang.commands.run then
|
||||||
|
error(('[cp.nvim] languages.%s.commands.run is required'):format(id))
|
||||||
|
end
|
||||||
|
|
||||||
if lang.commands.build ~= nil then
|
if lang.commands.build ~= nil then
|
||||||
vim.validate({ build = { lang.commands.build, { 'table' } } })
|
vim.validate({ build = { lang.commands.build, { 'table' } } })
|
||||||
if not has_tokens(lang.commands.build, { '{source}', '{binary}' }) then
|
if not has_tokens(lang.commands.build, { '{source}', '{binary}' }) then
|
||||||
|
|
@ -232,6 +242,14 @@ function M.setup(user_config)
|
||||||
vim.validate({ user_config = { user_config, { 'table', 'nil' }, true } })
|
vim.validate({ user_config = { user_config, { 'table', 'nil' }, true } })
|
||||||
local cfg = vim.tbl_deep_extend('force', vim.deepcopy(M.defaults), user_config or {})
|
local cfg = vim.tbl_deep_extend('force', vim.deepcopy(M.defaults), user_config or {})
|
||||||
|
|
||||||
|
if not next(cfg.languages) then
|
||||||
|
error('[cp.nvim] At least one language must be configured')
|
||||||
|
end
|
||||||
|
|
||||||
|
if not next(cfg.platforms) then
|
||||||
|
error('[cp.nvim] At least one platform must be configured')
|
||||||
|
end
|
||||||
|
|
||||||
vim.validate({
|
vim.validate({
|
||||||
hooks = { cfg.hooks, { 'table' } },
|
hooks = { cfg.hooks, { 'table' } },
|
||||||
ui = { cfg.ui, { 'table' } },
|
ui = { cfg.ui, { 'table' } },
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' }
|
M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' }
|
||||||
M.ACTIONS = { 'run', 'panel', 'debug', 'next', 'prev', 'pick', 'cache', 'interact' }
|
M.ACTIONS = { 'run', 'panel', 'next', 'prev', 'pick', 'cache', 'interact' }
|
||||||
|
|
||||||
M.PLATFORM_DISPLAY_NAMES = {
|
M.PLATFORM_DISPLAY_NAMES = {
|
||||||
atcoder = 'AtCoder',
|
atcoder = 'AtCoder',
|
||||||
|
|
|
||||||
|
|
@ -160,19 +160,21 @@ function M.run(cmd, stdin, timeout_ms, memory_mb)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.compile_problem()
|
function M.compile_problem(debug)
|
||||||
local state = require('cp.state')
|
local state = require('cp.state')
|
||||||
local config = require('cp.config').get_config()
|
local config = require('cp.config').get_config()
|
||||||
local platform = state.get_platform() or ''
|
local platform = state.get_platform()
|
||||||
local language = config.platforms[platform].default_language
|
local language = config.platforms[platform].default_language
|
||||||
local eff = config.runtime.effective[platform][language]
|
local eff = config.runtime.effective[platform][language]
|
||||||
local compile_config = eff and eff.commands and eff.commands.build
|
|
||||||
|
local compile_config = (debug and eff.commands.debug) or eff.commands.build
|
||||||
|
|
||||||
if not compile_config then
|
if not compile_config then
|
||||||
return { success = true, output = nil }
|
return { success = true, output = nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
local substitutions = { source = state.get_source_file(), binary = state.get_binary_file() }
|
local binary = debug and state.get_debug_file() or state.get_binary_file()
|
||||||
|
local substitutions = { source = state.get_source_file(), binary = binary }
|
||||||
local r = M.compile(compile_config, substitutions)
|
local r = M.compile(compile_config, substitutions)
|
||||||
|
|
||||||
if r.code ~= 0 then
|
if r.code ~= 0 then
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,12 @@ local function build_command(cmd, substitutions)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param test_case RanTestCase
|
---@param test_case RanTestCase
|
||||||
|
---@param debug boolean?
|
||||||
---@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 }
|
---@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(test_case)
|
local function run_single_test_case(test_case, debug)
|
||||||
local source_file = state.get_source_file()
|
local source_file = state.get_source_file()
|
||||||
|
|
||||||
local binary_file = state.get_binary_file()
|
local binary_file = debug and state.get_debug_file() or state.get_binary_file()
|
||||||
local substitutions = { source = source_file, binary = binary_file }
|
local substitutions = { source = source_file, binary = binary_file }
|
||||||
|
|
||||||
local platform_config = config.platforms[state.get_platform() or '']
|
local platform_config = config.platforms[state.get_platform() or '']
|
||||||
|
|
@ -198,15 +199,16 @@ function M.load_test_cases()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param index number
|
---@param index number
|
||||||
|
---@param debug boolean?
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function M.run_test_case(index)
|
function M.run_test_case(index, debug)
|
||||||
local tc = panel_state.test_cases[index]
|
local tc = panel_state.test_cases[index]
|
||||||
if not tc then
|
if not tc then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
tc.status = 'running'
|
tc.status = 'running'
|
||||||
local r = run_single_test_case(tc)
|
local r = run_single_test_case(tc, debug)
|
||||||
|
|
||||||
tc.status = r.status
|
tc.status = r.status
|
||||||
tc.actual = r.actual
|
tc.actual = r.actual
|
||||||
|
|
@ -225,8 +227,9 @@ function M.run_test_case(index)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param indices? integer[]
|
---@param indices? integer[]
|
||||||
|
---@param debug boolean?
|
||||||
---@return RanTestCase[]
|
---@return RanTestCase[]
|
||||||
function M.run_all_test_cases(indices)
|
function M.run_all_test_cases(indices, debug)
|
||||||
local to_run = indices
|
local to_run = indices
|
||||||
if not to_run then
|
if not to_run then
|
||||||
to_run = {}
|
to_run = {}
|
||||||
|
|
@ -236,7 +239,7 @@ function M.run_all_test_cases(indices)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, i in ipairs(to_run) do
|
for _, i in ipairs(to_run) do
|
||||||
M.run_test_case(i)
|
M.run_test_case(i, debug)
|
||||||
end
|
end
|
||||||
|
|
||||||
return panel_state.test_cases
|
return panel_state.test_cases
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
---@field input_buf integer
|
---@field input_buf integer
|
||||||
---@field output_win integer
|
---@field output_win integer
|
||||||
---@field input_win integer
|
---@field input_win integer
|
||||||
|
---@field current_test_index integer?
|
||||||
|
|
||||||
---@class cp.State
|
---@class cp.State
|
||||||
---@field get_platform fun(): string?
|
---@field get_platform fun(): string?
|
||||||
|
|
@ -127,6 +128,12 @@ function M.get_binary_file()
|
||||||
return base_name and ('build/%s.run'):format(base_name) or nil
|
return base_name and ('build/%s.run'):format(base_name) or nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function M.get_debug_file()
|
||||||
|
local base_name = M.get_base_name()
|
||||||
|
return base_name and ('build/%s.dbg'):format(base_name) or nil
|
||||||
|
end
|
||||||
|
|
||||||
---@return string?
|
---@return string?
|
||||||
function M.get_input_file()
|
function M.get_input_file()
|
||||||
local base_name = M.get_base_name()
|
local base_name = M.get_base_name()
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,8 @@ function M.ensure_io_view()
|
||||||
|
|
||||||
vim.cmd.vsplit()
|
vim.cmd.vsplit()
|
||||||
output_win = vim.api.nvim_get_current_win()
|
output_win = vim.api.nvim_get_current_win()
|
||||||
local width = math.floor(vim.o.columns * 0.3)
|
local config = config_module.get_config()
|
||||||
|
local width = math.floor(vim.o.columns * (config.ui.run.width or 0.3))
|
||||||
vim.api.nvim_win_set_width(output_win, width)
|
vim.api.nvim_win_set_width(output_win, width)
|
||||||
output_buf = utils.create_buffer_with_options()
|
output_buf = utils.create_buffer_with_options()
|
||||||
vim.api.nvim_win_set_buf(output_win, output_buf)
|
vim.api.nvim_win_set_buf(output_win, output_buf)
|
||||||
|
|
@ -243,6 +244,7 @@ function M.ensure_io_view()
|
||||||
input_buf = input_buf,
|
input_buf = input_buf,
|
||||||
output_win = output_win,
|
output_win = output_win,
|
||||||
input_win = input_win,
|
input_win = input_win,
|
||||||
|
current_test_index = 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
local config = config_module.get_config()
|
local config = config_module.get_config()
|
||||||
|
|
@ -253,6 +255,36 @@ function M.ensure_io_view()
|
||||||
if config.hooks and config.hooks.setup_io_input then
|
if config.hooks and config.hooks.setup_io_input then
|
||||||
pcall(config.hooks.setup_io_input, input_buf, state)
|
pcall(config.hooks.setup_io_input, input_buf, state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function navigate_test(delta)
|
||||||
|
local io_state = state.get_io_view_state()
|
||||||
|
if not io_state then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local test_cases = cache.get_test_cases(platform, contest_id, problem_id)
|
||||||
|
if not test_cases or #test_cases == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local new_index = (io_state.current_test_index or 1) + delta
|
||||||
|
if new_index < 1 or new_index > #test_cases then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
io_state.current_test_index = new_index
|
||||||
|
M.run_io_view(new_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.keymap.set('n', '<c-n>', function()
|
||||||
|
navigate_test(1)
|
||||||
|
end, { buffer = output_buf, silent = true, desc = 'Next test' })
|
||||||
|
vim.keymap.set('n', '<c-p>', function()
|
||||||
|
navigate_test(-1)
|
||||||
|
end, { buffer = output_buf, silent = true, desc = 'Previous test' })
|
||||||
|
vim.keymap.set('n', '<c-n>', function()
|
||||||
|
navigate_test(1)
|
||||||
|
end, { buffer = input_buf, silent = true, desc = 'Next test' })
|
||||||
|
vim.keymap.set('n', '<c-p>', function()
|
||||||
|
navigate_test(-1)
|
||||||
|
end, { buffer = input_buf, silent = true, desc = 'Previous test' })
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.update_buffer_content(input_buf, {})
|
utils.update_buffer_content(input_buf, {})
|
||||||
|
|
@ -272,7 +304,7 @@ function M.ensure_io_view()
|
||||||
vim.api.nvim_set_current_win(solution_win)
|
vim.api.nvim_set_current_win(solution_win)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.run_io_view(test_index)
|
function M.run_io_view(test_index, debug)
|
||||||
local platform, contest_id, problem_id =
|
local platform, contest_id, problem_id =
|
||||||
state.get_platform(), state.get_contest_id(), state.get_problem_id()
|
state.get_platform(), state.get_contest_id(), state.get_problem_id()
|
||||||
if not platform or not contest_id or not problem_id then
|
if not platform or not contest_id or not problem_id then
|
||||||
|
|
@ -332,7 +364,7 @@ function M.run_io_view(test_index)
|
||||||
end
|
end
|
||||||
|
|
||||||
local execute = require('cp.runner.execute')
|
local execute = require('cp.runner.execute')
|
||||||
local compile_result = execute.compile_problem()
|
local compile_result = execute.compile_problem(debug)
|
||||||
if not compile_result.success then
|
if not compile_result.success then
|
||||||
local ansi = require('cp.ui.ansi')
|
local ansi = require('cp.ui.ansi')
|
||||||
local output = compile_result.output or ''
|
local output = compile_result.output or ''
|
||||||
|
|
@ -352,7 +384,7 @@ function M.run_io_view(test_index)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
run.run_all_test_cases(test_indices)
|
run.run_all_test_cases(test_indices, debug)
|
||||||
|
|
||||||
local run_render = require('cp.runner.run_render')
|
local run_render = require('cp.runner.run_render')
|
||||||
run_render.setup_highlights()
|
run_render.setup_highlights()
|
||||||
|
|
@ -629,9 +661,9 @@ function M.toggle_panel(panel_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
local execute = require('cp.runner.execute')
|
local execute = require('cp.runner.execute')
|
||||||
local compile_result = execute.compile_problem()
|
local compile_result = execute.compile_problem(panel_opts and panel_opts.debug)
|
||||||
if compile_result.success then
|
if compile_result.success then
|
||||||
run.run_all_test_cases()
|
run.run_all_test_cases(nil, panel_opts and panel_opts.debug)
|
||||||
else
|
else
|
||||||
run.handle_compilation_failure(compile_result.output)
|
run.handle_compilation_failure(compile_result.output)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue