Merge branch 'main' into feat/epsilon-float-comparison
This commit is contained in:
commit
e4a7e7c434
9 changed files with 201 additions and 42 deletions
20
.luarc.json
20
.luarc.json
|
|
@ -1,8 +1,16 @@
|
||||||
{
|
{
|
||||||
"runtime.version": "LuaJIT",
|
"runtime": {
|
||||||
"runtime.path": ["lua/?.lua", "lua/?/init.lua"],
|
"version": "LuaJIT",
|
||||||
"diagnostics.globals": ["vim"],
|
"path": ["lua/?.lua", "lua/?/init.lua"]
|
||||||
"workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"],
|
},
|
||||||
"workspace.checkThirdParty": false,
|
"diagnostics": {
|
||||||
"completion.callSnippet": "Replace"
|
"globals": ["vim"]
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"library": ["$VIMRUNTIME/lua", "${3rd}/luv/library", "${3rd}/busted/library"],
|
||||||
|
"checkThirdParty": false
|
||||||
|
},
|
||||||
|
"completion": {
|
||||||
|
"callSnippet": "Replace"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
110
doc/cp.nvim.txt
110
doc/cp.nvim.txt
|
|
@ -888,6 +888,116 @@ Functions ~
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{bufnr} (integer) Buffer handle
|
{bufnr} (integer) Buffer handle
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
STATUSLINE INTEGRATION *cp-statusline*
|
||||||
|
|
||||||
|
cp.nvim exposes its runtime state through a public module that can be queried
|
||||||
|
from any statusline plugin. Import it with: >lua
|
||||||
|
local state = require('cp.state')
|
||||||
|
<
|
||||||
|
All getters return nil when no problem is active, so guard every value before
|
||||||
|
use. Calling any getter outside a CP context is safe and has no side effects.
|
||||||
|
|
||||||
|
State API ~
|
||||||
|
*cp.State*
|
||||||
|
The following getters are available for statusline use:
|
||||||
|
|
||||||
|
get_platform() (string?) Platform id. e.g. "codeforces", "atcoder"
|
||||||
|
get_contest_id() (string?) Contest id. e.g. "1933", "abc324"
|
||||||
|
get_problem_id() (string?) Problem id. e.g. "A", "B"
|
||||||
|
get_language() (string?) Language id. e.g. "cpp", "python"
|
||||||
|
get_base_name() (string?) Derived filename stem. e.g. "1933a"
|
||||||
|
get_source_file() (string?) Full source filename. e.g. "1933a.cc"
|
||||||
|
get_active_panel() (string?) Non-nil when the test panel is open.
|
||||||
|
|
||||||
|
Recipe: vanilla statusline ~
|
||||||
|
|
||||||
|
Set vim.o.statusline from an autocommand so it is recalculated on every
|
||||||
|
BufEnter: >lua
|
||||||
|
local function cp_component()
|
||||||
|
local state = require('cp.state')
|
||||||
|
local platform = state.get_platform()
|
||||||
|
if not platform then
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
local parts = {
|
||||||
|
platform,
|
||||||
|
state.get_contest_id(),
|
||||||
|
state.get_problem_id(),
|
||||||
|
state.get_language(),
|
||||||
|
}
|
||||||
|
local filtered = {}
|
||||||
|
for _, v in ipairs(parts) do
|
||||||
|
if v then filtered[#filtered + 1] = v end
|
||||||
|
end
|
||||||
|
return '[' .. table.concat(filtered, ' · ') .. ']'
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd({ 'BufEnter', 'User' }, {
|
||||||
|
callback = function()
|
||||||
|
vim.o.statusline = cp_component() .. ' %f %=%l:%c'
|
||||||
|
end
|
||||||
|
})
|
||||||
|
<
|
||||||
|
|
||||||
|
Recipe: lualine ~
|
||||||
|
|
||||||
|
Add a custom component to any lualine section. The cond field hides the
|
||||||
|
component entirely when no problem is active: >lua
|
||||||
|
local function cp_lualine()
|
||||||
|
local state = require('cp.state')
|
||||||
|
local parts = {
|
||||||
|
state.get_platform(),
|
||||||
|
state.get_contest_id(),
|
||||||
|
state.get_problem_id(),
|
||||||
|
state.get_language(),
|
||||||
|
}
|
||||||
|
local filtered = {}
|
||||||
|
for _, v in ipairs(parts) do
|
||||||
|
if v then filtered[#filtered + 1] = v end
|
||||||
|
end
|
||||||
|
return table.concat(filtered, ' · ')
|
||||||
|
end
|
||||||
|
|
||||||
|
require('lualine').setup({
|
||||||
|
sections = {
|
||||||
|
lualine_c = {
|
||||||
|
{
|
||||||
|
cp_lualine,
|
||||||
|
cond = function()
|
||||||
|
return require('cp.state').get_platform() ~= nil
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
<
|
||||||
|
|
||||||
|
Recipe: heirline ~
|
||||||
|
|
||||||
|
Build a heirline component using a provider and condition: >lua
|
||||||
|
local CpComponent = {
|
||||||
|
condition = function()
|
||||||
|
return require('cp.state').get_platform() ~= nil
|
||||||
|
end,
|
||||||
|
provider = function()
|
||||||
|
local state = require('cp.state')
|
||||||
|
local parts = {
|
||||||
|
state.get_platform(),
|
||||||
|
state.get_contest_id(),
|
||||||
|
state.get_problem_id(),
|
||||||
|
state.get_language(),
|
||||||
|
}
|
||||||
|
local filtered = {}
|
||||||
|
for _, v in ipairs(parts) do
|
||||||
|
if v then filtered[#filtered + 1] = v end
|
||||||
|
end
|
||||||
|
return '[' .. table.concat(filtered, ' · ') .. ']'
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
<
|
||||||
|
Include CpComponent in your heirline StatusLine spec wherever desired.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
PANEL KEYMAPS *cp-panel-keys*
|
PANEL KEYMAPS *cp-panel-keys*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,8 @@ function M.get_data_pretty()
|
||||||
return vim.inspect(cache_data)
|
return vim.inspect(cache_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
M._cache = cache_data
|
function M.get_raw_cache()
|
||||||
|
return cache_data
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
---@class CpLanguage
|
---@class CpLanguage
|
||||||
---@field extension string
|
---@field extension string
|
||||||
---@field commands CpLangCommands
|
---@field commands CpLangCommands
|
||||||
|
---@field template? string
|
||||||
|
|
||||||
---@class CpPlatformOverrides
|
---@class CpPlatformOverrides
|
||||||
---@field extension? string
|
---@field extension? string
|
||||||
|
|
@ -216,6 +217,10 @@ local function validate_language(id, lang)
|
||||||
commands = { lang.commands, { 'table' } },
|
commands = { lang.commands, { 'table' } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if lang.template ~= nil then
|
||||||
|
vim.validate({ template = { lang.template, 'string' } })
|
||||||
|
end
|
||||||
|
|
||||||
if not lang.commands.run then
|
if not lang.commands.run then
|
||||||
error(('[cp.nvim] languages.%s.commands.run is required'):format(id))
|
error(('[cp.nvim] languages.%s.commands.run is required'):format(id))
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ local function check()
|
||||||
vim.health.error('cp.nvim requires Neovim 0.10.0+')
|
vim.health.error('cp.nvim requires Neovim 0.10.0+')
|
||||||
end
|
end
|
||||||
|
|
||||||
local uname = vim.loop.os_uname()
|
local uname = vim.uv.os_uname()
|
||||||
if uname.sysname == 'Windows_NT' then
|
if uname.sysname == 'Windows_NT' then
|
||||||
vim.health.error('Windows is not supported')
|
vim.health.error('Windows is not supported')
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -323,26 +323,35 @@ function M.run_all_test_cases(indices, debug, on_each, on_done)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function run_next(pos)
|
if #to_run == 0 then
|
||||||
if pos > #to_run then
|
logger.log(
|
||||||
logger.log(
|
('Finished %s %d test cases.'):format(debug and 'debugging' or 'running', 0),
|
||||||
('Finished %s %d test cases.'):format(debug and 'debugging' or 'running', #to_run),
|
vim.log.levels.INFO,
|
||||||
vim.log.levels.INFO,
|
true
|
||||||
true
|
)
|
||||||
)
|
on_done(panel_state.test_cases)
|
||||||
on_done(panel_state.test_cases)
|
return
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
M.run_test_case(to_run[pos], debug, function()
|
|
||||||
if on_each then
|
|
||||||
on_each(pos, #to_run)
|
|
||||||
end
|
|
||||||
run_next(pos + 1)
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
run_next(1)
|
local total = #to_run
|
||||||
|
local remaining = total
|
||||||
|
|
||||||
|
for _, idx in ipairs(to_run) do
|
||||||
|
M.run_test_case(idx, debug, function()
|
||||||
|
if on_each then
|
||||||
|
on_each(idx, total)
|
||||||
|
end
|
||||||
|
remaining = remaining - 1
|
||||||
|
if remaining == 0 then
|
||||||
|
logger.log(
|
||||||
|
('Finished %s %d test cases.'):format(debug and 'debugging' or 'running', total),
|
||||||
|
vim.log.levels.INFO,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
on_done(panel_state.test_cases)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return PanelState
|
---@return PanelState
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ local function run_scraper(platform, subcommand, args, opts)
|
||||||
env.CONDA_PREFIX = ''
|
env.CONDA_PREFIX = ''
|
||||||
|
|
||||||
if opts and opts.ndjson then
|
if opts and opts.ndjson then
|
||||||
local uv = vim.loop
|
local uv = vim.uv
|
||||||
local stdout = uv.new_pipe(false)
|
local stdout = uv.new_pipe(false)
|
||||||
local stderr = uv.new_pipe(false)
|
local stderr = uv.new_pipe(false)
|
||||||
local buf = ''
|
local buf = ''
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,21 @@ local logger = require('cp.log')
|
||||||
local scraper = require('cp.scraper')
|
local scraper = require('cp.scraper')
|
||||||
local state = require('cp.state')
|
local state = require('cp.state')
|
||||||
|
|
||||||
|
local function apply_template(bufnr, lang_id, platform)
|
||||||
|
local config = config_module.get_config()
|
||||||
|
local eff = config.runtime.effective[platform] and config.runtime.effective[platform][lang_id]
|
||||||
|
if not eff or not eff.template then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local path = vim.fn.expand(eff.template)
|
||||||
|
if vim.fn.filereadable(path) ~= 1 then
|
||||||
|
logger.log(('[cp.nvim] template not readable: %s'):format(path), vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local lines = vim.fn.readfile(path)
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
||||||
|
end
|
||||||
|
|
||||||
---Get the language of the current file from cache
|
---Get the language of the current file from cache
|
||||||
---@return string?
|
---@return string?
|
||||||
local function get_current_file_language()
|
local function get_current_file_language()
|
||||||
|
|
@ -179,7 +194,7 @@ function M.setup_contest(platform, contest_id, problem_id, language)
|
||||||
contest_id = contest_id,
|
contest_id = contest_id,
|
||||||
language = lang,
|
language = lang,
|
||||||
requested_problem_id = problem_id,
|
requested_problem_id = problem_id,
|
||||||
token = vim.loop.hrtime(),
|
token = vim.uv.hrtime(),
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.log('Fetching contests problems...', vim.log.levels.INFO, true)
|
logger.log('Fetching contests problems...', vim.log.levels.INFO, true)
|
||||||
|
|
@ -270,14 +285,17 @@ function M.setup_problem(problem_id, language)
|
||||||
mods = { silent = true, noautocmd = true, keepalt = true },
|
mods = { silent = true, noautocmd = true, keepalt = true },
|
||||||
})
|
})
|
||||||
state.set_solution_win(vim.api.nvim_get_current_win())
|
state.set_solution_win(vim.api.nvim_get_current_win())
|
||||||
if config.hooks and config.hooks.setup_code and not vim.b[prov.bufnr].cp_setup_done then
|
if not vim.b[prov.bufnr].cp_setup_done then
|
||||||
local ok = pcall(config.hooks.setup_code, state)
|
apply_template(prov.bufnr, lang, platform)
|
||||||
if ok then
|
if config.hooks and config.hooks.setup_code then
|
||||||
|
local ok = pcall(config.hooks.setup_code, state)
|
||||||
|
if ok then
|
||||||
|
vim.b[prov.bufnr].cp_setup_done = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
helpers.clearcol(prov.bufnr)
|
||||||
vim.b[prov.bufnr].cp_setup_done = true
|
vim.b[prov.bufnr].cp_setup_done = true
|
||||||
end
|
end
|
||||||
elseif not vim.b[prov.bufnr].cp_setup_done then
|
|
||||||
helpers.clearcol(prov.bufnr)
|
|
||||||
vim.b[prov.bufnr].cp_setup_done = true
|
|
||||||
end
|
end
|
||||||
cache.set_file_state(
|
cache.set_file_state(
|
||||||
vim.fn.fnamemodify(source_file, ':p'),
|
vim.fn.fnamemodify(source_file, ':p'),
|
||||||
|
|
@ -300,14 +318,21 @@ function M.setup_problem(problem_id, language)
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
state.set_solution_win(vim.api.nvim_get_current_win())
|
state.set_solution_win(vim.api.nvim_get_current_win())
|
||||||
require('cp.ui.views').ensure_io_view()
|
require('cp.ui.views').ensure_io_view()
|
||||||
if config.hooks and config.hooks.setup_code and not vim.b[bufnr].cp_setup_done then
|
if not vim.b[bufnr].cp_setup_done then
|
||||||
local ok = pcall(config.hooks.setup_code, state)
|
local is_new = vim.api.nvim_buf_line_count(bufnr) == 1
|
||||||
if ok then
|
and vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] == ''
|
||||||
|
if is_new then
|
||||||
|
apply_template(bufnr, lang, platform)
|
||||||
|
end
|
||||||
|
if config.hooks and config.hooks.setup_code then
|
||||||
|
local ok = pcall(config.hooks.setup_code, state)
|
||||||
|
if ok then
|
||||||
|
vim.b[bufnr].cp_setup_done = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
helpers.clearcol(bufnr)
|
||||||
vim.b[bufnr].cp_setup_done = true
|
vim.b[bufnr].cp_setup_done = true
|
||||||
end
|
end
|
||||||
elseif not vim.b[bufnr].cp_setup_done then
|
|
||||||
helpers.clearcol(bufnr)
|
|
||||||
vim.b[bufnr].cp_setup_done = true
|
|
||||||
end
|
end
|
||||||
cache.set_file_state(
|
cache.set_file_state(
|
||||||
vim.fn.expand('%:p'),
|
vim.fn.expand('%:p'),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ local logger = require('cp.log')
|
||||||
local _nix_python = nil
|
local _nix_python = nil
|
||||||
local _nix_discovered = false
|
local _nix_discovered = false
|
||||||
|
|
||||||
local uname = vim.loop.os_uname()
|
local uname = vim.uv.os_uname()
|
||||||
|
|
||||||
local _time_cached = false
|
local _time_cached = false
|
||||||
local _time_path = nil
|
local _time_path = nil
|
||||||
|
|
@ -336,7 +336,7 @@ function M.timeout_capability()
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.cwd_executables()
|
function M.cwd_executables()
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv
|
||||||
local req = uv.fs_scandir('.')
|
local req = uv.fs_scandir('.')
|
||||||
if not req then
|
if not req then
|
||||||
return {}
|
return {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue