feat(commands): implement :CP open [problem|contest|standings]

Problem: There was no way to open a problem or contest page in the
browser from within the plugin; users had to manually navigate to
the platform URL.

Solution: Add `contest_url` and `standings_url` to `MetadataResult`
and persist them via `cache.set_contest_data`. Add `cache.get_open_urls`
to assemble all three URLs from cache. Wire up `:CP open` in
`commands/init.lua` to call `vim.ui.open` on the resolved URL,
warning when none is available (e.g. CSES standings).
This commit is contained in:
Barrett Ruth 2026-03-05 19:13:06 -05:00
parent cc48c901c0
commit cc279166cb
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
8 changed files with 65 additions and 2 deletions

View file

@ -606,6 +606,8 @@ class AtcoderScraper(BaseScraper):
contest_id=contest_id,
problems=problems,
url=f"https://atcoder.jp/contests/{contest_id}/tasks/{contest_id}_%s",
contest_url=f"https://atcoder.jp/contests/{contest_id}",
standings_url=f"https://atcoder.jp/contests/{contest_id}/standings",
)
except Exception as e:
return self._metadata_error(str(e))

View file

@ -223,6 +223,8 @@ class CodeforcesScraper(BaseScraper):
contest_id=contest_id,
problems=problems,
url=f"https://codeforces.com/contest/{contest_id}/problem/%s",
contest_url=f"https://codeforces.com/contest/{contest_id}",
standings_url=f"https://codeforces.com/contest/{contest_id}/standings",
)
except Exception as e:
return self._metadata_error(str(e))

View file

@ -218,6 +218,8 @@ class CSESScraper(BaseScraper):
contest_id=contest_id,
problems=problems,
url="https://cses.fi/problemset/task/%s",
contest_url="https://cses.fi/problemset",
standings_url="",
)
async def scrape_contest_list(self) -> ContestListResult:

View file

@ -42,6 +42,8 @@ class MetadataResult(ScrapingResult):
contest_id: str = ""
problems: list[ProblemSummary] = Field(default_factory=list)
url: str
contest_url: str = ""
standings_url: str = ""
model_config = ConfigDict(extra="forbid")