feat: add Go, Rust, Java, PHP, TypeScript, and C/C++ docs shortcuts

The rest of the languages from #25, each probed for the cheapest shape
that works:

- go: pkg.go.dev renders search on the server, so 'search' is a plain
  URL and 'pkg <path>' opens a package page.
- php: manual-lookup.php sends an exact function name straight to its
  manual page, so 'fn array_map' lands on the function doc; fuzzy
  search goes through DuckDuckGo since the suggestions render behind
  the manual's own TOC.
- cpp: cppreference's new static site has clean /cpp/ and /c/ paths;
  its native search page is bare links oc refuses as unreadable, so
  'search' goes through DuckDuckGo.
- rust: std and doc page opens; rustdoc's search-index.js format is
  version-fragile, so 'search' goes through DuckDuckGo.
- java: Javadoc opens pinned to javase/26, the current GA (27
  redirects there); the typeSearchIndex file lacks the module names a
  URL needs, so 'search' goes through DuckDuckGo.
- ts: handbook page opens; the site searches via Algolia client-side,
  so DuckDuckGo again.
- learn gets a 'dotnet <api>' verb, since .NET API docs already live
  there.

README table and prose and the skill notes cover the new sites.
This commit is contained in:
only-cli
2026-08-24 12:27:17 -04:00
parent fa41abbb2e
commit 378d566765
10 changed files with 92 additions and 5 deletions
+35 -1
View File
@@ -56,7 +56,7 @@ test('every shipped definition is reachable and every url template is filled', (
// A search verb resolves to a site root or endpoint to ask, not a
// URL: an API endpoint keeps {query} until search time, so it is
// filled here the way apiSearch fills it before the template check.
const url = resolved.url ?? resolved.sphinx ?? resolved.nodedoc
const url = resolved.url ?? resolved.sphinx ?? resolved.nodedoc ?? resolved.rdoc
?? (def.args ?? []).reduce((u, a) => u.replaceAll(`{${a}}`, `test-${a}`), resolved.api?.api ?? '');
assert.doesNotMatch(url, /[{}]/, `oc ${name} ${verb} left a template var in ${url}`);
assert.equal(new URL(url).protocol, 'https:', `oc ${name} ${verb} is not https`);
@@ -99,3 +99,37 @@ test('language docs shortcuts resolve, and a doc path keeps its slashes', () =>
assert.equal(mdn.api.api, 'https://developer.mozilla.org/api/v1/search?q={query}&locale=en-US');
assert.equal(mdn.query, 'array map');
});
test('the second wave of language docs resolves the same way', () => {
assert.equal(resolveSite('go', ['pkg', 'net/http']).url, 'https://pkg.go.dev/net/http');
assert.equal(
resolveSite('go', ['search', 'json decode']).url,
'https://pkg.go.dev/search?q=json%20decode');
assert.equal(
resolveSite('php', ['fn', 'array_map']).url,
'https://www.php.net/manual-lookup.php?pattern=array_map');
assert.equal(
resolveSite('cpp', ['cpp', 'container/vector']).url,
'https://en.cppreference.com/cpp/container/vector');
assert.equal(
resolveSite('cppreference', ['search', 'push_back']).url,
'https://html.duckduckgo.com/html/?q=site%3Aen.cppreference.com+push_back');
assert.equal(
resolveSite('rust', ['std', 'vec/struct.Vec']).url,
'https://doc.rust-lang.org/std/vec/struct.Vec.html');
assert.equal(
resolveSite('java', ['api', 'java.base/java/util/HashMap']).url,
'https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/HashMap.html');
assert.equal(
resolveSite('ts', ['handbook', '2/everyday-types']).url,
'https://www.typescriptlang.org/docs/handbook/2/everyday-types.html');
assert.equal(
resolveSite('learn', ['dotnet', 'system.string']).url,
'https://learn.microsoft.com/en-us/dotnet/api/system.string');
assert.equal(
resolveSite('ruby', ['class', 'Array']).url,
'https://docs.ruby-lang.org/en/3.4/Array.html');
const ruby = resolveSite('docs.ruby-lang.org', ['search', 'each_slice']);
assert.equal(ruby.rdoc, 'https://docs.ruby-lang.org/en/3.4/');
assert.equal(ruby.query, 'each_slice');
});