Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ Median cold full-file check across <!--g:benchCount-->26<!--/g:benchCount--> sin



## Install

The CLI is on [PyPI as `basilisk-python`](https://pypi.org/project/basilisk-python/) — install it as a standalone tool; the installed command is `basilisk`:

```sh
uv tool install basilisk-python # or: pipx install basilisk-python
```

Also available via Homebrew (`brew tap Nimblesite/tap && brew install basilisk`), Scoop, and
[GitHub Releases](https://github.com/Nimblesite/Basilisk/releases) — every channel ships the same
single Rust binary. Full options: [install guide](https://www.basilisk-python.dev/docs/install-cli/).

## Try it

The `examples/` folder has ready-to-go Python files:
Expand Down
12 changes: 12 additions & 0 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ Basilisk 是**唯一**在官方

在 <!--g:benchMachine-->Apple M4 Max<!--/g:benchMachine--> 上,跨 <!--g:benchCount-->26<!--/g:benchCount--> 个单一类型构造压力测试样本的冷启动全文件检查中位数 —— 数值越低越好。Basilisk 的热重检查可降至约 <!--g:benchWarm-->4<!--/g:benchWarm--> ms。每个数字均由 [`hyperfine`](https://github.com/sharkdp/hyperfine) 生成并按机器提交,没有一个是手写的。**克隆仓库,在你自己的硬件上运行 `make bench`,并把 CSV 发给我们 —— 欢迎社区独立复核。** [完整基准测试与方法论(英文)&rarr;](https://www.basilisk-python.dev/docs/benchmarks/)

## 安装

CLI 已发布到 [PyPI,包名为 `basilisk-python`](https://pypi.org/project/basilisk-python/)——请将其作为独立工具安装,安装后的命令是 `basilisk`:

```sh
uv tool install basilisk-python # 或:pipx install basilisk-python
```

也可通过 Homebrew(`brew tap Nimblesite/tap && brew install basilisk`)、Scoop 以及
[GitHub Releases](https://github.com/Nimblesite/Basilisk/releases) 安装——所有渠道分发的都是同一个
Rust 二进制文件。完整选项参见[安装指南](https://www.basilisk-python.dev/docs/install-cli/)。

## 试用

`examples/` 文件夹中提供了可直接运行的 Python 文件:
Expand Down
9 changes: 7 additions & 2 deletions docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,13 @@ from [STUBRES-TYPESHED-WARN](CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-TYPESHED-WA
| verification waived | persistent `UNVERIFIED` |
| custom | persistent `USER-MANAGED SOURCE` |

Rows may coexist, never poll, and carry no command; fixes remain in the editable
section. They describe Basilisk transport around pinned typing step 3, not extra
Rows may coexist, never poll, and never mutate; fixes remain in the editable
section. A warning row's message names its fix (e.g. `UNPINNED`'s **Pin
current**), so the row carries exactly one navigation-only command that opens
the configuration editor — attached only while the server advertises the
editor capability, because the open command is capability-gated and a
shown-but-dead command is forbidden. Other rows carry no command. They
describe Basilisk transport around pinned typing step 3, not extra
typing diagnostics
([`python/typing@6ef9f77`](https://github.com/python/typing/blob/6ef9f7719ecfff09dad8724ef42b621fd994fb5e/docs/spec/distributing.rst)).

Expand Down
39 changes: 33 additions & 6 deletions vscode-extension/src/info-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { effect } from "@preact/signals-core";
import type { LanguageClient } from "vscode-languageclient/node";
import { type Store } from "./store";
import type { TypeshedStatusState } from "./configuration-editor-model";
import { CONFIGURATION_EDITOR_COMMAND, supportsConfigurationEditor } from "./configuration-editor";

// ── Tree node types ──────────────────────────────────────────────────────

Expand Down Expand Up @@ -264,8 +265,38 @@ function typeshedSourceItem(
return item;
}

// Implements the navigation affordance of [LSPCFGED-TYPESHED-SERVICE-INFO]:
// warning rows never mutate (fixes stay in the editable section), but their
// messages name actions that live in the configuration editor (e.g. the
// UNPINNED row's "Pin current"), so a warning row navigates there on click.
// The command is attached only while the server advertises the editor —
// basilisk.openConfigurationEditor is capability-gated
// (configuration-editor-registration.ts), and a shown-but-dead command is
// exactly issue #103 defect 1.
function typeshedWarningItem(
prefix: string,
warning: TypeshedStatusState["warnings"][number],
editorSupported: boolean,
): InfoTextItem {
const item = new InfoTextItem(
`${prefix} ${warning.code}`,
warning.message,
statusKind(warning.severity) === "High" ? "warning" : "info",
);
if (editorSupported) {
item.contextValue = "typeshed-warning";
item.tooltip = "Click to open the Configuration Editor, where the typeshed fixes live";
item.command = {
command: CONFIGURATION_EDITOR_COMMAND,
title: "Open Configuration Editor",
};
}
return item;
}

function typeshedInfoItems(
statuses: ReadonlyMap<string, TypeshedStatusState>,
editorSupported: boolean,
): InfoTextItem[] {
const entries = [...statuses.entries()].sort(([left], [right]) => left.localeCompare(right));
return entries.flatMap(([rootUri, status]) => {
Expand All @@ -280,11 +311,7 @@ function typeshedInfoItems(
...(status.blockedReason === undefined
? []
: [new InfoTextItem(`${prefix} Blocked`, status.blockedReason, "error")]),
...status.warnings.map((warning) => new InfoTextItem(
`${prefix} ${warning.code}`,
warning.message,
statusKind(warning.severity) === "High" ? "warning" : "info",
)),
...status.warnings.map((warning) => typeshedWarningItem(prefix, warning, editorSupported)),
];
return rows;
});
Expand Down Expand Up @@ -377,7 +404,7 @@ export class InfoPanelProvider implements vscode.TreeDataProvider<InfoItem>, vsc
...(binary !== undefined && binary !== null
? [new InfoTextItem("Binary", formatResolvedTool(binary), "file-binary")]
: []),
...typeshedInfoItems(this.store.typeshedStatuses.value),
...typeshedInfoItems(this.store.typeshedStatuses.value, supportsConfigurationEditor(client)),
];

return new SectionItem("Server Info", items);
Expand Down
94 changes: 94 additions & 0 deletions vscode-extension/src/test/suite/info-panel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,90 @@ function verifyAcquiringTypeshedSpinner(): void {
}
}

/** A store whose single root reports the UNPINNED typeshed warning. */
function storeWithUnpinnedWarning(): ReturnType<typeof createStore> {
const store = createStore();
const writable = store.typeshedStatuses as unknown as {
value: ReadonlyMap<string, TypeshedStatusState>;
};
writable.value = new Map([[
"file:///workspace",
{
lifecycle: { kind: "Ready" }, activeSource: { kind: "Latest" },
blockedReason: undefined,
commitIdentity: "6fb14c98ee340a07eea807a4c804e20a849eb92b",
treeIdentity: undefined,
transport: { kind: "Codeload" }, licenseStatus: { kind: "Approved" },
licenseReference: "typeshed://license/6fb14c9", provenance: { kind: "GithubTlsAttested" },
signedRelease: false,
warnings: [{
code: "UNPINNED", message: "Pin current to make this reproducible",
severity: { kind: "Advisory" },
}],
},
]]);
return store;
}

// Tests [LSPCFGED-TYPESHED-SERVICE-INFO] navigation + [EXTACT-INFO-AFFORDANCE]:
// the UNPINNED row's own message tells the user to "Pin current", and the Pin
// current action lives in the configuration editor — so the row must navigate
// there when the editor capability is live (info-panel.ts typeshedInfoItems).
function verifyUnpinnedWarningRowOpensConfigurationEditor(): void {
const store = storeWithUnpinnedWarning();
const writableClient = store.client as unknown as { value: unknown };
writableClient.value = {
initializeResult: {
capabilities: { experimental: { basilisk: { configurationEditor: true } } },
},
};
const typeshedProvider = new InfoPanelProvider(store);
try {
const section = typeshedProvider.getChildren().find((row) => labelOf(row) === "Server Info");
assert.ok(section, "Server Info section should exist");
const unpinned = typeshedProvider
.getChildren(section)
.find((row) => labelOf(row) === "Typeshed UNPINNED");
assert.ok(unpinned, "the UNPINNED warning row should exist");
assert.strictEqual(
unpinned.command?.command,
"basilisk.openConfigurationEditor",
"the UNPINNED row advertises Pin current, so clicking it must open the configuration editor where Pin current lives",
);
const tip = tooltipOf(unpinned).trim();
assert.ok(
tip.length > 0,
"an actionable row must carry an imperative tooltip describing its effect",
);
} finally {
typeshedProvider.dispose();
}
}

// Regression guard for issue #103 defect 1: basilisk.openConfigurationEditor
// is capability-gated (configuration-editor-registration.ts), so a warning row
// must NOT carry it while no server advertises the editor — a shown-but-dead
// command raises "command not found".
function verifyUnpinnedWarningRowStaysInertWithoutEditorCapability(): void {
const store = storeWithUnpinnedWarning();
const typeshedProvider = new InfoPanelProvider(store);
try {
const section = typeshedProvider.getChildren().find((row) => labelOf(row) === "Server Info");
assert.ok(section, "Server Info section should exist");
const unpinned = typeshedProvider
.getChildren(section)
.find((row) => labelOf(row) === "Typeshed UNPINNED");
assert.ok(unpinned, "the UNPINNED warning row should exist");
assert.strictEqual(
unpinned.command,
undefined,
"without the configuration-editor capability the row must not carry a dead command",
);
} finally {
typeshedProvider.dispose();
}
}

suite("Basilisk Info Panel Contents (slimmed, issue #103)", () => {
let provider: InfoPanelProvider;

Expand Down Expand Up @@ -219,6 +303,16 @@ suite("Basilisk Info Panel Contents (slimmed, issue #103)", () => {

test("Server Info shows an acquiring Typeshed spinner", verifyAcquiringTypeshedSpinner);

test(
"the UNPINNED warning row opens the configuration editor where Pin current lives",
verifyUnpinnedWarningRowOpensConfigurationEditor,
);

test(
"the UNPINNED warning row stays inert while no server advertises the configuration editor",
verifyUnpinnedWarningRowStaysInertWithoutEditorCapability,
);

// Tests [EXTACT-INFO-SERVER-INFO]: one uv row, sub-settings in the tooltip.
test("uv sub-settings are folded into the uv row tooltip, not separate rows", () => {
const rows = serverInfoRows();
Expand Down
Loading
Loading