Commit graph

66 commits

Author SHA1 Message Date
Barrett Ruth
da4e2ebeba
feat: git credential backend for credential storage (#371)
## Problem

Credentials were stored as plaintext JSON in
`stdpath('data')/cp-nvim.json`, with no integration with system
credential managers.

## Solution

Replace file-based credential storage with `git credential
fill/approve/reject`, delegating to whatever credential helper the user
has configured (`cache`, `store`, `libsecret`, macOS Keychain, etc.).

- New `lua/cp/git_credential.lua` module wrapping the git credential
protocol
- All credential consumers (`credentials.lua`, `submit.lua`,
`scraper.lua`) use `git_credential` directly — `cache.lua` no longer
handles credentials
- CSES API token packed into the password field (`password<TAB>token`)
so it works with helpers that ignore the `path` field
- `has_helper()` guard on `:CP login`, `:CP logout`, and `:CP submit`
with an error message if no helper is configured
- Healthcheck split into `[required]`/`[optional]` sections; git version
and credential helper status shown
- `git` checked at startup in `check_required_runtime()`
- Cache version system (`CACHE_VERSION`, v1→v2 migration) removed — the
cache file is now a plain JSON blob
- `:CP` command gets `bar = true`
2026-03-07 20:15:06 -05:00
Barrett Ruth
b53c8ca44e
fix(security): harden credential storage and transmission (#369)
Some checks are pending
luarocks / ci (push) Waiting to run
luarocks / publish (push) Blocked by required conditions
## Problem

Credential and cookie files were world-readable (0644), passwords
transited via `CP_CREDENTIALS` env var (visible in `/proc/PID/environ`),
and Kattis/USACO echoed passwords back through stdout unnecessarily.

## Solution

Set 0600 permissions on `cp-nvim.json` and `cookies.json` after every
write, pass credentials via stdin pipe instead of env var, and stop
emitting passwords in ndjson from Kattis/USACO `LoginResult` (CSES token
emission unchanged).
2026-03-07 18:14:34 -05:00
Barrett Ruth
3c11d609f5
feat(codechef): implement full CodeChef support (#354)
## Problem

CodeChef had no working login, submit, or contest list. The browser
selectors were wrong, the contest list was missing present/past
contests,
and problem/contest URLs were unset.

## Solution

Fix login and submit selectors for the Drupal-based site. Paginate
`/api/list/contests/past` to collect all 228 Starters, then expand each
parent contest into individual division entries (e.g. `START228 (Div.
4)`).
Add language IDs, correct `url`/`contest_url`/`standings_url` in
metadata,
and make `:CP <platform>` open the contest picker directly.
2026-03-06 23:10:44 -05:00
Barrett Ruth
1ac521a126
fix: language version IDs, cache crash, and stress session restore (#352)
## Problem

Codeforces language version IDs were wrong (pointed at old compiler
versions), `LANGUAGE_VERSIONS` was incomplete for most platforms, the
contest picker crashed when `supports_countdown` was stored as a
non-table entry, and `:CP stress` restored a broken IO view on exit.

## Solution

Correct CF `programTypeId` values and default to C++20. Add
`LANGUAGE_VERSIONS` entries for all six platforms. Guard
`get_contest_summaries` against non-table cache entries. Call
`ensure_io_view()` after stress session restore. Shorten the stress
terminal buffer name to a readable `term://stress.py` format.
2026-03-06 20:22:28 -05:00
Barrett Ruth
58b6840815
feat: race countdown support and language version selection (#346)
## Problem

Two gaps in the plugin: (1) contest pickers have no way to know whether
a contest supports countdown (race), so the UI can't surface that
affordance; (2) `:CP submit` hardcodes a single language ID per platform
with no way to choose C++ standard or override the platform ID.

## Solution

**Race countdown** (`4e709c8`): Add `supports_countdown` boolean to
`ContestListResult` and wire it through CSES/USACO scrapers, cache, race
module, and pickers.

**Language version selection** (`b90ac67`): Add `LANGUAGE_VERSIONS` and
`DEFAULT_VERSIONS` tables in `constants.lua`. Config gains `version` and
`submit_id` fields validated at setup time. `submit.lua` resolves the
effective config to a platform ID (priority: `submit_id` > `version` >
default). Helpdocs add `*cp-submit-language*` section. Tests cover
`LANGUAGE_IDS` completeness.
2026-03-06 18:18:21 -05:00
Barrett Ruth
ba5ae8df69
fix(kattis): fix nil display_name in contest picker (#332)
## Problem

`ContestSummary.display_name` defaults to `None`, which serializes to
JSON `null` → Lua `vim.NIL`. The contest picker displayed "vim.NIL" for
every entry. Additionally, `start_time` was always stored even when
null, because `vim.NIL` is truthy in Lua.

## Solution

Pass `display_name=name` explicitly in `_parse_contests_page` so JSON
never emits `null`. In `cache.lua` `set_contest_summaries`, coerce
`display_name` via a `~= vim.NIL` guard and apply the same guard before
storing `start_time`.
2026-03-06 13:25:17 -05:00
Barrett Ruth
7bf4cf2538
feat(commands): implement :CP open [problem|contest|standings] (#319)
## Problem

There was no way to open a problem, contest, or standings page in the
browser from within the plugin.

## Solution

Add `contest_url` and `standings_url` to `MetadataResult` and persist
them in the cache. Add `cache.get_open_urls` to resolve all three URLs.
Wire up `:CP open [problem|contest|standings]` in `commands/init.lua`
to call `vim.ui.open`, warning when a URL is unavailable (e.g. CSES
has no standings). Closes #315.
2026-03-05 19:16:05 -05:00
Barrett Ruth
1bc0aa41b6
refactor(cache): nest credentials under platform namespace (#293)
## Problem

Credentials lived in a top-level `_credentials` namespace, requiring
special
preservation logic in `clear_all()` and a separate key hierarchy from
the
platform data they belong to.

## Solution

Move credentials from `_credentials.<platform>` to
`<platform>._credentials`.
Migrate v1 caches on load, skip underscore-prefixed keys when
enumerating
contest IDs and summaries, and simplify `clear_all()` now that no
special
preservation is needed.

Stacked on #292.
2026-03-04 13:37:22 -05:00
a04702d87c refactor: replace :CP login with :CP credentials subcommand
Problem: :CP login was a poor API — no way to clear credentials without
raw Lua, and the single command didn't scale to multiple operations.

Solution: replace login with a :CP credentials subcommand following the
same pattern as :CP cache. :CP credentials set [platform] prompts and
saves; :CP credentials clear [platform] removes one or all platforms.
Add cache.clear_credentials(), rename login.lua to credentials.lua,
update parse/dispatch/tab-complete, and rewrite vimdoc accordingly.
2026-03-03 16:46:07 -05:00
a08d1f0c5e refactor(submit): consolidate credentials into main cache file
Problem: credentials were stored in a separate file,
cp-nvim-credentials.json, alongside the main cp-nvim.json cache.
Two files for one plugin's persistent state was unnecessary.

Solution: add get_credentials/set_credentials to cache.lua, storing
credentials under _credentials[platform] in the shared cache. Update
clear_all() to preserve _credentials across cache wipes. Remove the
separate file, load_credentials, and save_credentials from submit.lua.
2026-03-03 16:24:12 -05:00
de5a20c567 fix: resolve typecheck errors in cache, atcoder, cses, and usaco
Problem: lua typecheck flagged missing start_time field on ContestSummary;
ty flagged BeautifulSoup Tag/NavigableString union on csrf_input.get(),
a 3-tuple unpack where _extract_problem_info now returns 4 values in
cses.py, and an untyped list assignment in usaco.py.

Solution: add start_time? to ContestSummary LuaDoc, guard csrf_input
with hasattr check and type: ignore, unpack precision from
_extract_problem_info in cses.py callers, and use cast() in usaco.py.
2026-03-03 15:09:41 -05:00
90bd13580b feat(scraper): add precision extraction, start_time, and submit support
Problem: problem pages contain floating-point precision requirements and
contest start timestamps that were not being extracted or stored. The
submit workflow also needed a foundation in the scraper layer.

Solution: add extract_precision() to base.py and propagate through all
scrapers into cache. Add start_time to ContestSummary and extract it
from AtCoder and Codeforces. Add SubmitResult model, abstract submit()
method, submit CLI case with get_language_id() resolution, stdin/env_extra
support in run_scraper, and a full AtCoder submit implementation; stub
the remaining platforms.
2026-03-03 15:09:41 -05:00
e685a8089f feat: add epsilon tolerance for floating-point output comparison
Problem: output comparison used exact string equality after whitespace
normalisation, causing correct solutions to fail on problems where
floating-point answers are accepted within a tolerance (e.g. 1e-6).

Solution: add an optional ui.panel.epsilon config value. When set,
actual and expected output are compared token-by-token: numeric tokens
are compared with math.abs(a - b) <= epsilon, non-numeric tokens fall
back to exact string equality. Per-problem epsilon can also be stored
in the cache and takes precedence over the global default.
2026-02-26 23:00:35 -05:00
d274e0c117 fix(cache): replace stale M._cache field with get_raw_cache accessor
Problem: M._cache = cache_data captured the initial empty table reference
at module load time. After M.load() reassigns cache_data to the decoded
JSON, M._cache is permanently stale and returns the wrong table.

Solution: remove the field assignment and expose get_raw_cache() which
closes over cache_data and always returns the current table.
2026-02-26 22:46:06 -05:00
d3ac300ea0 fix(cache): remove unused logger import
Problem: the logger import became unused after replacing the error log
with a silent cache wipe.

Solution: drop the require.
2026-02-22 22:19:51 -05:00
db5bd791f9 fix(cache): invalidate stale cache on version mismatch
Problem: after an install or update, the on-disk cache may contain data
written by an older version of the plugin whose format no longer matches
what the current code expects.

Solution: embed a CACHE_VERSION in every saved cache file. On load, if
the stored version is missing or differs from the current one, wipe the
cache and rewrite it. Corrupt (non-decodable) cache files are handled
the same way instead of only logging an error.
2026-02-22 22:19:51 -05:00
Barrett Ruth
96c01bf796 cleanup 2025-11-04 23:47:06 -05:00
Barrett Ruth
aab211902e feat: multi-test case view 2025-11-04 21:32:40 -05:00
Barrett Ruth
fef73887e4 feat(io): multi-test case view 2025-11-04 08:15:08 -05:00
Barrett Ruth
9d848eba22 feat: improve autocomplete 2025-10-24 01:44:06 -04:00
c0e175d84b feat(config): open url option 2025-10-12 16:19:02 -04:00
cedcd82367 fix: write interaction into cache 2025-10-05 13:50:14 -04:00
d2bde9bad8 fix(config): better file org 2025-10-04 19:54:53 -04:00
a76d228e3f feat(doc): update for new config 2025-10-04 19:04:49 -04:00
17b5e0a52b make cache resilient 2025-10-04 16:15:26 -04:00
3fbbfa9423 normalize scraper behavior 2025-10-04 16:13:04 -04:00
18dbcd43d2 fix(cache): contest override 2025-10-04 12:48:57 -04:00
1520939d4b some refactors 2025-10-03 14:34:49 -04:00
d9537e72ba many fixes 2025-10-02 22:35:30 -04:00
1974addbd2 fix(lua): bunch of typing 2025-10-02 14:18:26 -04:00
057b0890c2 fix: remove unused function 2025-10-02 13:56:38 -04:00
91e6fbe455 fix caching 2025-10-02 10:18:29 -04:00
6b8a1e2087 more docs 2025-10-01 21:36:53 -04:00
7eb314b02c fix caching 2025-10-01 20:21:11 -04:00
e6c09a4897 fix some cachign 2025-10-01 17:08:36 -04:00
b406c0ce4e fix: synchronous problem fetch 2025-10-01 12:25:07 -04:00
7761c7c759 fix(cache): file state 2025-09-30 21:21:13 -04:00
46cd509747 fix docs and superfluous vim.validate calls 2025-09-30 20:55:29 -04:00
ae2f8b94cf feat: interactive problem finer-tuning 2025-09-27 10:05:58 -04:00
170021af8e no more ttl 2025-09-24 20:46:43 -04:00
bcbcc4365f remove ttl 2025-09-24 20:16:33 -04:00
f3666a30be fix(ci): lint 2025-09-23 12:28:53 -04:00
8df8c16a72 fix(ci): selene lint 2025-09-23 12:25:53 -04:00
8a9bc7434f fix: remove comments 2025-09-23 10:22:02 -04:00
e171017ab0 fixup 2025-09-23 09:42:45 -04:00
ba81df2266 fix(cache): expiry 2025-09-22 16:50:14 -04:00
039fad1614 fix(cache): cache contest data indefinitely 2025-09-22 16:32:52 -04:00
78fb4f8f4b feat(cache): cache clearing, updating and resetting 2025-09-21 15:08:55 -04:00
1b8365265d fix(ci): unused variables 2025-09-21 11:36:06 -04:00
a3dd6f4e1e fix(run): foldcolumn 2025-09-19 23:11:12 -04:00