mirror of
https://github.com/harivansh-afk/nvim.git
synced 2026-04-15 05:02:10 +00:00
cp.nvim
This commit is contained in:
parent
deee1c3835
commit
ca5d1145dd
4 changed files with 303 additions and 6 deletions
8
init.lua
8
init.lua
|
|
@ -1,6 +1,14 @@
|
||||||
vim.g.mapleader = ' '
|
vim.g.mapleader = ' '
|
||||||
vim.g.maplocalleader = ','
|
vim.g.maplocalleader = ','
|
||||||
|
|
||||||
|
local home = os.getenv('HOME') or ''
|
||||||
|
local local_bin = home .. '/.local/bin'
|
||||||
|
if not (os.getenv('PATH') or ''):find(local_bin, 1, true) then
|
||||||
|
local new_path = local_bin .. ':' .. (os.getenv('PATH') or '')
|
||||||
|
vim.env.PATH = new_path
|
||||||
|
vim.uv.os_setenv('PATH', new_path)
|
||||||
|
end
|
||||||
|
|
||||||
function _G.map(mode, lhs, rhs, opts)
|
function _G.map(mode, lhs, rhs, opts)
|
||||||
vim.keymap.set(mode, lhs, rhs, vim.tbl_extend('keep', opts or {}, { silent = true }))
|
vim.keymap.set(mode, lhs, rhs, vim.tbl_extend('keep', opts or {}, { silent = true }))
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
{
|
{
|
||||||
"diffs.nvim": { "branch": "main", "commit": "b1abfe4f4a164ad776148ca36f852df4f1e4014e" },
|
"cp.nvim": { "branch": "main", "commit": "ff5ba39a592d079780819bb8226dcb35741349a4" },
|
||||||
|
"diffs.nvim": { "branch": "main", "commit": "dfebc68a1fc3e93dae5b6d49133243ec1886cb19" },
|
||||||
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
|
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
|
||||||
"fzf-lua": { "branch": "main", "commit": "b2c0603216adb92c6bba81053bc996d7ae95b77a" },
|
"fzf-lua": { "branch": "main", "commit": "9004cbb4c065a32b690e909c49903967b45301eb" },
|
||||||
"gitsigns.nvim": { "branch": "main", "commit": "9f3c6dd7868bcc116e9c1c1929ce063b978fa519" },
|
"gitsigns.nvim": { "branch": "main", "commit": "9f3c6dd7868bcc116e9c1c1929ce063b978fa519" },
|
||||||
"gruvbox.nvim": { "branch": "main", "commit": "561126520034a1dac2f78ab063db025d12555998" },
|
"gruvbox.nvim": { "branch": "main", "commit": "561126520034a1dac2f78ab063db025d12555998" },
|
||||||
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
|
||||||
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
|
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
|
||||||
"nonicons.nvim": { "branch": "main", "commit": "62549ecb9906e4216398c44af96719ca4cc670ef" },
|
"nonicons.nvim": { "branch": "main", "commit": "5426ec037f2a295ae687fa9d4def290fb044e3e8" },
|
||||||
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
|
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
|
||||||
"nvim-lspconfig": { "branch": "master", "commit": "44acfe887d4056f704ccc4f17513ed41c9e2b2e6" },
|
"nvim-lspconfig": { "branch": "master", "commit": "5a855bcfec7973767a1a472335684bbd71d2fa2b" },
|
||||||
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" },
|
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" },
|
||||||
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
|
"nvim-treesitter": { "branch": "main", "commit": "dc42c209f3820bdfaae0956f15de29689aa6b451" },
|
||||||
"nvim-treesitter-textobjects": { "branch": "main", "commit": "a0e182ae21fda68c59d1f36c9ed45600aef50311" },
|
"nvim-treesitter-textobjects": { "branch": "main", "commit": "a0e182ae21fda68c59d1f36c9ed45600aef50311" },
|
||||||
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
|
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
|
||||||
"nvim-web-devicons": { "branch": "master", "commit": "746ffbb17975ebd6c40142362eee1b0249969c5c" },
|
"nvim-web-devicons": { "branch": "master", "commit": "746ffbb17975ebd6c40142362eee1b0249969c5c" },
|
||||||
|
|
|
||||||
238
lua/cp/scraper.lua
Normal file
238
lua/cp/scraper.lua
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local constants = require('cp.constants')
|
||||||
|
local logger = require('cp.log')
|
||||||
|
local utils = require('cp.utils')
|
||||||
|
|
||||||
|
local function syshandle(result)
|
||||||
|
if result.code ~= 0 then
|
||||||
|
local msg = 'Scraper failed: ' .. (result.stderr or 'Unknown error')
|
||||||
|
return { success = false, error = msg }
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, data = pcall(vim.json.decode, result.stdout)
|
||||||
|
if not ok then
|
||||||
|
local msg = 'Failed to parse scraper output: ' .. tostring(data)
|
||||||
|
logger.log(msg, vim.log.levels.ERROR)
|
||||||
|
return { success = false, error = msg }
|
||||||
|
end
|
||||||
|
|
||||||
|
return { success = true, data = data }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function spawn_env_list(env_map)
|
||||||
|
local out = {}
|
||||||
|
for key, value in pairs(env_map) do
|
||||||
|
out[#out + 1] = tostring(key) .. '=' .. tostring(value)
|
||||||
|
end
|
||||||
|
table.sort(out)
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param platform string
|
||||||
|
---@param subcommand string
|
||||||
|
---@param args string[]
|
||||||
|
---@param opts { sync?: boolean, ndjson?: boolean, on_event?: fun(ev: table), on_exit?: fun(result: table) }
|
||||||
|
local function run_scraper(platform, subcommand, args, opts)
|
||||||
|
if not utils.setup_python_env() then
|
||||||
|
local msg = 'no Python environment available (install uv or nix)'
|
||||||
|
logger.log(msg, vim.log.levels.ERROR)
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit({ success = false, error = msg })
|
||||||
|
end
|
||||||
|
return { success = false, error = msg }
|
||||||
|
end
|
||||||
|
|
||||||
|
local plugin_path = utils.get_plugin_path()
|
||||||
|
local cmd = utils.get_python_cmd(platform, plugin_path)
|
||||||
|
vim.list_extend(cmd, { subcommand })
|
||||||
|
vim.list_extend(cmd, args)
|
||||||
|
|
||||||
|
logger.log('scraper cmd: ' .. table.concat(cmd, ' '))
|
||||||
|
|
||||||
|
local env = vim.fn.environ()
|
||||||
|
env.VIRTUAL_ENV = ''
|
||||||
|
env.PYTHONPATH = ''
|
||||||
|
env.CONDA_PREFIX = ''
|
||||||
|
|
||||||
|
if opts and opts.ndjson then
|
||||||
|
local uv = vim.loop
|
||||||
|
local stdout = uv.new_pipe(false)
|
||||||
|
local stderr = uv.new_pipe(false)
|
||||||
|
local buf = ''
|
||||||
|
|
||||||
|
local handle
|
||||||
|
handle = uv.spawn(cmd[1], {
|
||||||
|
args = vim.list_slice(cmd, 2),
|
||||||
|
stdio = { nil, stdout, stderr },
|
||||||
|
env = spawn_env_list(env),
|
||||||
|
cwd = plugin_path,
|
||||||
|
}, function(code, signal)
|
||||||
|
if buf ~= '' and opts.on_event then
|
||||||
|
local ok_tail, ev_tail = pcall(vim.json.decode, buf)
|
||||||
|
if ok_tail then
|
||||||
|
opts.on_event(ev_tail)
|
||||||
|
end
|
||||||
|
buf = ''
|
||||||
|
end
|
||||||
|
if opts.on_exit then
|
||||||
|
opts.on_exit({ success = (code == 0), code = code, signal = signal })
|
||||||
|
end
|
||||||
|
if not stdout:is_closing() then
|
||||||
|
stdout:close()
|
||||||
|
end
|
||||||
|
if not stderr:is_closing() then
|
||||||
|
stderr:close()
|
||||||
|
end
|
||||||
|
if handle and not handle:is_closing() then
|
||||||
|
handle:close()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not handle then
|
||||||
|
logger.log('Failed to start scraper process', vim.log.levels.ERROR)
|
||||||
|
return { success = false, error = 'spawn failed' }
|
||||||
|
end
|
||||||
|
|
||||||
|
uv.read_start(stdout, function(_, data)
|
||||||
|
if data == nil then
|
||||||
|
if buf ~= '' and opts.on_event then
|
||||||
|
local ok_tail, ev_tail = pcall(vim.json.decode, buf)
|
||||||
|
if ok_tail then
|
||||||
|
opts.on_event(ev_tail)
|
||||||
|
end
|
||||||
|
buf = ''
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
buf = buf .. data
|
||||||
|
while true do
|
||||||
|
local s, e = buf:find('\n', 1, true)
|
||||||
|
if not s then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local line = buf:sub(1, s - 1)
|
||||||
|
buf = buf:sub(e + 1)
|
||||||
|
local ok, ev = pcall(vim.json.decode, line)
|
||||||
|
if ok and opts.on_event then
|
||||||
|
opts.on_event(ev)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
uv.read_start(stderr, function(_, _) end)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local sysopts = { text = true, timeout = 30000, env = env, cwd = plugin_path }
|
||||||
|
if opts and opts.sync then
|
||||||
|
local result = vim.system(cmd, sysopts):wait()
|
||||||
|
return syshandle(result)
|
||||||
|
else
|
||||||
|
vim.system(cmd, sysopts, function(result)
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
return opts.on_exit(syshandle(result))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.scrape_contest_metadata(platform, contest_id, callback)
|
||||||
|
run_scraper(platform, 'metadata', { contest_id }, {
|
||||||
|
on_exit = function(result)
|
||||||
|
if not result or not result.success then
|
||||||
|
logger.log(
|
||||||
|
("Failed to scrape metadata for %s contest '%s'."):format(
|
||||||
|
constants.PLATFORM_DISPLAY_NAMES[platform],
|
||||||
|
contest_id
|
||||||
|
),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local data = result.data or {}
|
||||||
|
if not data.problems or #data.problems == 0 then
|
||||||
|
logger.log(
|
||||||
|
("No problems returned for %s contest '%s'."):format(
|
||||||
|
constants.PLATFORM_DISPLAY_NAMES[platform],
|
||||||
|
contest_id
|
||||||
|
),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if type(callback) == 'function' then
|
||||||
|
callback(data)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.scrape_contest_list(platform)
|
||||||
|
local result = run_scraper(platform, 'contests', {}, { sync = true })
|
||||||
|
if not result or not result.success or not (result.data and result.data.contests) then
|
||||||
|
logger.log(
|
||||||
|
('Could not scrape contests list for platform %s: %s'):format(
|
||||||
|
platform,
|
||||||
|
(result and result.error) or 'unknown'
|
||||||
|
),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
return result.data.contests
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param platform string
|
||||||
|
---@param contest_id string
|
||||||
|
---@param callback fun(data: table)|nil
|
||||||
|
function M.scrape_all_tests(platform, contest_id, callback)
|
||||||
|
run_scraper(platform, 'tests', { contest_id }, {
|
||||||
|
ndjson = true,
|
||||||
|
on_event = function(ev)
|
||||||
|
if ev.done then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if ev.error and ev.problem_id then
|
||||||
|
logger.log(
|
||||||
|
("Failed to load tests for problem '%s' in contest '%s': %s"):format(
|
||||||
|
ev.problem_id,
|
||||||
|
contest_id,
|
||||||
|
ev.error
|
||||||
|
),
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not ev.problem_id or not ev.tests then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.schedule(function()
|
||||||
|
require('cp.utils').ensure_dirs()
|
||||||
|
local config = require('cp.config')
|
||||||
|
local base_name = config.default_filename(contest_id, ev.problem_id)
|
||||||
|
for i, t in ipairs(ev.tests) do
|
||||||
|
local input_file = 'io/' .. base_name .. '.' .. i .. '.cpin'
|
||||||
|
local expected_file = 'io/' .. base_name .. '.' .. i .. '.cpout'
|
||||||
|
local input_content = t.input:gsub('\r', '')
|
||||||
|
local expected_content = t.expected:gsub('\r', '')
|
||||||
|
vim.fn.writefile(vim.split(input_content, '\n'), input_file)
|
||||||
|
vim.fn.writefile(vim.split(expected_content, '\n'), expected_file)
|
||||||
|
end
|
||||||
|
if type(callback) == 'function' then
|
||||||
|
callback({
|
||||||
|
combined = ev.combined,
|
||||||
|
tests = ev.tests,
|
||||||
|
timeout_ms = ev.timeout_ms or 0,
|
||||||
|
memory_mb = ev.memory_mb or 0,
|
||||||
|
interactive = ev.interactive or false,
|
||||||
|
multi_test = ev.multi_test or false,
|
||||||
|
problem_id = ev.problem_id,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
@ -35,6 +35,56 @@ return {
|
||||||
map('n', 'zM', require('ufo').closeAllFolds)
|
map('n', 'zM', require('ufo').closeAllFolds)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'barrettruth/cp.nvim',
|
||||||
|
dependencies = { 'ibhagwan/fzf-lua' },
|
||||||
|
init = function()
|
||||||
|
-- Keep uv cache in-project so cp.nvim scraping works in restricted environments.
|
||||||
|
if vim.env.UV_CACHE_DIR == nil or vim.env.UV_CACHE_DIR == '' then
|
||||||
|
local uv_cache_dir = vim.fn.getcwd() .. '/.uv-cache'
|
||||||
|
vim.fn.mkdir(uv_cache_dir, 'p')
|
||||||
|
vim.env.UV_CACHE_DIR = uv_cache_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.g.cp = {
|
||||||
|
languages = {
|
||||||
|
python = {
|
||||||
|
extension = 'py',
|
||||||
|
commands = {
|
||||||
|
run = { 'python3', '{source}' },
|
||||||
|
debug = { 'python3', '{source}' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
platforms = {
|
||||||
|
codeforces = {
|
||||||
|
enabled_languages = { 'python' },
|
||||||
|
default_language = 'python',
|
||||||
|
},
|
||||||
|
atcoder = {
|
||||||
|
enabled_languages = { 'python' },
|
||||||
|
default_language = 'python',
|
||||||
|
},
|
||||||
|
cses = {
|
||||||
|
enabled_languages = { 'python' },
|
||||||
|
default_language = 'python',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ui = {
|
||||||
|
picker = 'fzf-lua',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
config = function()
|
||||||
|
map('n', '<leader>cr', '<cmd>CP run<cr>', { desc = 'CP run' })
|
||||||
|
map('n', '<leader>cp', '<cmd>CP panel<cr>', { desc = 'CP panel' })
|
||||||
|
map('n', '<leader>ce', '<cmd>CP edit<cr>', { desc = 'CP edit tests' })
|
||||||
|
map('n', '<leader>cn', '<cmd>CP next<cr>', { desc = 'CP next problem' })
|
||||||
|
map('n', '<leader>cN', '<cmd>CP prev<cr>', { desc = 'CP previous problem' })
|
||||||
|
map('n', '<leader>cc', '<cmd>CP pick<cr>', { desc = 'CP contest picker' })
|
||||||
|
map('n', '<leader>ci', '<cmd>CP interact<cr>', { desc = 'CP interact' })
|
||||||
|
end,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'supermaven-inc/supermaven-nvim',
|
'supermaven-inc/supermaven-nvim',
|
||||||
opts = {
|
opts = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue