дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
chore: Format the codebase with oxfmt and check formatting in CI (#135)
Run oxfmt once over the whole repository (formatting only, no logic changes) and add `pnpm format:check` to the frontend CI job so unformatted code no longer builds up. `.oxfmtrc.json` now skips the generated theme docs, test fixtures and code blocks inside Markdown. Co-authored-by: jaw52 <[email protected]>
Цей коміт міститься в:
380 files changed
+6409
-1634
No files matched your search
@@ -136,7 +136,9 @@ function Filters() {
|
||||
<Select
|
||||
value={query.state}
|
||||
options={STATES.map((value) => ({ value, label: t(`github.prState.${value}`) }))}
|
||||
onChange={(state) => setQuery({ state })} aria-label="State" />
|
||||
onChange={(state) => setQuery({ state })}
|
||||
aria-label="State"
|
||||
/>
|
||||
<Checkbox checked={query.authorMe} onChange={(authorMe) => setQuery({ authorMe })} label="Mine" />
|
||||
<Checkbox
|
||||
checked={query.reviewRequested}
|
||||
|
||||
@@ -2,7 +2,13 @@ import { describe, expect, it } from "vitest";
|
||||
import type { PrCheck, PrReviewComment, PullRequestDetails } from "@/platform/ipc";
|
||||
import { checksSummary, checksText, diffNotes, fileDiffData, relativeTime, reviewThreads, stateLabel } from "./model";
|
||||
|
||||
const comment = (id: number, path: string, line: number | null, inReplyTo: number | null = null, side = "RIGHT"): PrReviewComment => ({
|
||||
const comment = (
|
||||
id: number,
|
||||
path: string,
|
||||
line: number | null,
|
||||
inReplyTo: number | null = null,
|
||||
side = "RIGHT",
|
||||
): PrReviewComment => ({
|
||||
id,
|
||||
inReplyTo,
|
||||
path,
|
||||
@@ -57,7 +63,12 @@ describe("pull request model", () => {
|
||||
});
|
||||
|
||||
it("threads replies under their root, ordered by file and line", () => {
|
||||
const threads = reviewThreads([comment(1, "b.ts", 9), comment(2, "a.ts", 5), comment(3, "b.ts", 9, 1), comment(4, "b.ts", 9, 3)]);
|
||||
const threads = reviewThreads([
|
||||
comment(1, "b.ts", 9),
|
||||
comment(2, "a.ts", 5),
|
||||
comment(3, "b.ts", 9, 1),
|
||||
comment(4, "b.ts", 9, 3),
|
||||
]);
|
||||
expect(threads.map((t) => [t.root.id, t.replies.map((r) => r.id)])).toEqual([
|
||||
[2, []],
|
||||
[1, [3, 4]],
|
||||
@@ -65,7 +76,12 @@ describe("pull request model", () => {
|
||||
});
|
||||
|
||||
it("turns current comments into diff notes", () => {
|
||||
const notes = diffNotes("a.ts", [comment(1, "a.ts", 5), comment(2, "a.ts", null), comment(3, "a.ts", 2, null, "LEFT"), comment(4, "b.ts", 1)]);
|
||||
const notes = diffNotes("a.ts", [
|
||||
comment(1, "a.ts", 5),
|
||||
comment(2, "a.ts", null),
|
||||
comment(3, "a.ts", 2, null, "LEFT"),
|
||||
comment(4, "b.ts", 1),
|
||||
]);
|
||||
expect(notes.map((n) => [n.side, n.line])).toEqual([
|
||||
["right", 4],
|
||||
["left", 1],
|
||||
@@ -80,7 +96,12 @@ describe("pull request model", () => {
|
||||
root: "/repo",
|
||||
files: [{ path: "src/new.ts", kind: "renamed" as const, oldPath: "src/old.ts" }],
|
||||
};
|
||||
const renamed = fileDiffData(pr, { path: "src/new.ts", additions: 1, deletions: 0, changeType: "RENAMED" }, revs, []);
|
||||
const renamed = fileDiffData(
|
||||
pr,
|
||||
{ path: "src/new.ts", additions: 1, deletions: 0, changeType: "RENAMED" },
|
||||
revs,
|
||||
[],
|
||||
);
|
||||
expect(renamed.path).toBe("/repo/src/new.ts");
|
||||
expect(renamed.left).toMatchObject({ type: "rev", rev: revs.base, path: "/repo/src/old.ts" });
|
||||
expect(renamed.right).toMatchObject({ type: "rev", rev: revs.head });
|
||||
|
||||
@@ -63,7 +63,14 @@ export function activate(): () => void {
|
||||
when: WHEN,
|
||||
run: () => openInBrowser(),
|
||||
},
|
||||
{ id: "github.refresh", title: "Refresh Pull Requests", category: "GitHub", icon: "refresh", when: WHEN, run: () => refreshAll() },
|
||||
{
|
||||
id: "github.refresh",
|
||||
title: "Refresh Pull Requests",
|
||||
category: "GitHub",
|
||||
icon: "refresh",
|
||||
when: WHEN,
|
||||
run: () => refreshAll(),
|
||||
},
|
||||
]),
|
||||
useProject.subscribe((s, prev) => {
|
||||
if (s.project?.root !== prev.project?.root) resetGithub();
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
/** Pure helpers of the Pull Requests view (labels, check summaries, review threads, diff inputs). */
|
||||
import type { ChangedFile, PrCheck, PrDiffRevisions, PrFile, PrReviewComment, PullRequestDetails, PullRequestSummary } from "@/platform/ipc";
|
||||
import type {
|
||||
ChangedFile,
|
||||
PrCheck,
|
||||
PrDiffRevisions,
|
||||
PrFile,
|
||||
PrReviewComment,
|
||||
PullRequestDetails,
|
||||
PullRequestSummary,
|
||||
} from "@/platform/ipc";
|
||||
import { t, tx } from "@/platform/i18n";
|
||||
|
||||
/** Keyed, not source text: "Open" as a state reads differently from the "Open" action in some locales. */
|
||||
@@ -107,7 +115,9 @@ export function reviewThreads(comments: readonly PrReviewComment[]): ReviewThrea
|
||||
}
|
||||
}
|
||||
return threads.sort(
|
||||
(a, b) => a.root.path.localeCompare(b.root.path) || (a.root.line ?? a.root.originalLine ?? 0) - (b.root.line ?? b.root.originalLine ?? 0),
|
||||
(a, b) =>
|
||||
a.root.path.localeCompare(b.root.path) ||
|
||||
(a.root.line ?? a.root.originalLine ?? 0) - (b.root.line ?? b.root.originalLine ?? 0),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,15 +134,24 @@ export interface DiffNote {
|
||||
export function diffNotes(path: string, comments: readonly PrReviewComment[]): DiffNote[] {
|
||||
return comments
|
||||
.filter((c) => c.path === path && c.line != null)
|
||||
.map((c) => ({ side: c.side === "LEFT" ? "left" : "right", line: c.line! - 1, author: c.author, body: c.body, date: c.createdAt }));
|
||||
.map((c) => ({
|
||||
side: c.side === "LEFT" ? "left" : "right",
|
||||
line: c.line! - 1,
|
||||
author: c.author,
|
||||
body: c.body,
|
||||
date: c.createdAt,
|
||||
}));
|
||||
}
|
||||
|
||||
type Side =
|
||||
| { type: "rev"; rev: string; path?: string; label?: string }
|
||||
| { type: "text"; text: string; label: string };
|
||||
type Side = { type: "rev"; rev: string; path?: string; label?: string } | { type: "text"; text: string; label: string };
|
||||
|
||||
/** `DiffData` for one changed file of a pull request (merge base ↔ head), with its review comments. */
|
||||
export function fileDiffData(pr: PullRequestDetails, file: PrFile, revs: PrDiffRevisions, comments: readonly PrReviewComment[]) {
|
||||
export function fileDiffData(
|
||||
pr: PullRequestDetails,
|
||||
file: PrFile,
|
||||
revs: PrDiffRevisions,
|
||||
comments: readonly PrReviewComment[],
|
||||
) {
|
||||
const root = revs.root;
|
||||
const changed: ChangedFile | undefined = revs.files.find((f) => f.path === file.path);
|
||||
const added = file.changeType === "ADDED" || changed?.kind === "added";
|
||||
@@ -146,7 +165,15 @@ export function fileDiffData(pr: PullRequestDetails, file: PrFile, revs: PrDiffR
|
||||
path: changed?.oldPath ? `${root}/${changed.oldPath}` : undefined,
|
||||
label: `${pr.summary.baseRef} (${revs.base.slice(0, 8)})`,
|
||||
};
|
||||
const right: Side = deleted ? { type: "text", text: "", label: "(deleted)" } : { type: "rev", rev: revs.head, label: `#${n} ${pr.summary.headRef}` };
|
||||
const right: Side = deleted
|
||||
? { type: "text", text: "", label: "(deleted)" }
|
||||
: { type: "rev", rev: revs.head, label: `#${n} ${pr.summary.headRef}` };
|
||||
const name = file.path.slice(file.path.lastIndexOf("/") + 1);
|
||||
return { path: `${root}/${file.path}`, left, right, title: `#${n}: ${name}`, annotations: diffNotes(file.path, comments) };
|
||||
return {
|
||||
path: `${root}/${file.path}`,
|
||||
left,
|
||||
right,
|
||||
title: `#${n}: ${name}`,
|
||||
annotations: diffNotes(file.path, comments),
|
||||
};
|
||||
}
|
||||
@@ -5,7 +5,16 @@ import { call, commands } from "@/platform/ipc";
|
||||
import { notifications, toolWindowService } from "@/workbench/contributions";
|
||||
import { createPullRequestDialog, numberDialog, reviewDialog } from "./dialogs";
|
||||
import { fileDiffData } from "./model";
|
||||
import { diffRevisions, errorMessage, loadDetails, loadList, readyRepo, refreshAll, useGithub, withBusy } from "./store";
|
||||
import {
|
||||
diffRevisions,
|
||||
errorMessage,
|
||||
loadDetails,
|
||||
loadList,
|
||||
readyRepo,
|
||||
refreshAll,
|
||||
useGithub,
|
||||
withBusy,
|
||||
} from "./store";
|
||||
|
||||
export const WINDOW = "pullRequests";
|
||||
|
||||
@@ -34,14 +43,20 @@ function notReady(): boolean {
|
||||
export async function checkoutPullRequest(number: number) {
|
||||
const done = await withBusy(`Checking out #${number}…`, (r, out) => call(commands.ghPrCheckout(r, number, out)));
|
||||
if (done !== null)
|
||||
notifications().notify({ group: "vcs", level: "info", title: `Checked out pull request #${number}`, key: "github-checkout" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "info",
|
||||
title: `Checked out pull request #${number}`,
|
||||
key: "github-checkout",
|
||||
});
|
||||
}
|
||||
|
||||
/** Git › GitHub › Checkout Pull Request…: the selected one, else asks for a number. */
|
||||
export async function checkoutAction(arg: unknown) {
|
||||
if (useGithub.getState().availability?.kind !== "ready") await refreshAll();
|
||||
if (notReady()) return;
|
||||
const n = typeof arg === "number" ? arg : (useGithub.getState().selected ?? (await numberDialog("Checkout Pull Request")));
|
||||
const n =
|
||||
typeof arg === "number" ? arg : (useGithub.getState().selected ?? (await numberDialog("Checkout Pull Request")));
|
||||
if (n) await checkoutPullRequest(n);
|
||||
}
|
||||
|
||||
@@ -62,10 +77,21 @@ export async function submitReview(number: number, initial: ReviewEvent) {
|
||||
if (!r) return;
|
||||
try {
|
||||
await call(commands.ghPrReview(number, r.event, r.body));
|
||||
notifications().notify({ group: "vcs", level: "info", title: `Review submitted on #${number}`, key: "github-review" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "info",
|
||||
title: `Review submitted on #${number}`,
|
||||
key: "github-review",
|
||||
});
|
||||
await loadDetails(number);
|
||||
} catch (e) {
|
||||
notifications().notify({ group: "vcs", level: "error", title: "Could not submit the review", body: errorMessage(e), key: "github-review" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "error",
|
||||
title: "Could not submit the review",
|
||||
body: errorMessage(e),
|
||||
key: "github-review",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +101,13 @@ export async function addComment(number: number, body: string): Promise<boolean>
|
||||
await loadDetails(number);
|
||||
return true;
|
||||
} catch (e) {
|
||||
notifications().notify({ group: "vcs", level: "error", title: "Could not add the comment", body: errorMessage(e), key: "github-comment" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "error",
|
||||
title: "Could not add the comment",
|
||||
body: errorMessage(e),
|
||||
key: "github-comment",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -92,12 +124,21 @@ export async function createPullRequest() {
|
||||
const info = await call(commands.vcsBranchInfo(null));
|
||||
head = info.head;
|
||||
needsPush = !info.upstream || info.ahead > 0;
|
||||
if (head) title = head.replace(/^[^/]+\//, "").replace(/[-_]+/g, " ").replace(/^\w/, (c) => c.toUpperCase());
|
||||
if (head)
|
||||
title = head
|
||||
.replace(/^[^/]+\//, "")
|
||||
.replace(/[-_]+/g, " ")
|
||||
.replace(/^\w/, (c) => c.toUpperCase());
|
||||
} catch {
|
||||
// Not a git repository: gh will explain.
|
||||
}
|
||||
if (!head) {
|
||||
notifications().notify({ group: "vcs", level: "warning", title: "Check out a branch to create a pull request from", key: "github-create" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "warning",
|
||||
title: "Check out a branch to create a pull request from",
|
||||
key: "github-create",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (head === repo?.defaultBranch) {
|
||||
|
||||
@@ -113,7 +113,12 @@ let detailSeq = 0;
|
||||
export async function loadDetails(number: number): Promise<void> {
|
||||
const seq = ++detailSeq;
|
||||
const same = useGithub.getState().details?.summary.number === number;
|
||||
useGithub.setState({ selected: number, detailsLoading: true, detailsError: null, ...(same ? {} : { details: null, reviewComments: [] }) });
|
||||
useGithub.setState({
|
||||
selected: number,
|
||||
detailsLoading: true,
|
||||
detailsError: null,
|
||||
...(same ? {} : { details: null, reviewComments: [] }),
|
||||
});
|
||||
try {
|
||||
const [details, comments] = await Promise.all([
|
||||
call(commands.ghPrView(number)),
|
||||
@@ -136,9 +141,17 @@ export function select(number: number | null) {
|
||||
}
|
||||
|
||||
/** Runs a cancellable, streaming gh operation shown in the view's banner. */
|
||||
export async function withBusy<T>(title: string, run: (request: RequestId, out: Channel<string>) => Promise<T>): Promise<T | null> {
|
||||
export async function withBusy<T>(
|
||||
title: string,
|
||||
run: (request: RequestId, out: Channel<string>) => Promise<T>,
|
||||
): Promise<T | null> {
|
||||
if (useGithub.getState().busy) {
|
||||
notifications().notify({ group: "vcs", level: "info", title: "Another GitHub operation is still running", key: "github-busy" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "info",
|
||||
title: "Another GitHub operation is still running",
|
||||
key: "github-busy",
|
||||
});
|
||||
return null;
|
||||
}
|
||||
const request = nextRequestId();
|
||||
@@ -149,7 +162,13 @@ export async function withBusy<T>(title: string, run: (request: RequestId, out:
|
||||
return await run(request, out);
|
||||
} catch (e) {
|
||||
if (!isCancelled(e))
|
||||
notifications().notify({ group: "vcs", level: "error", title: `${title.replace(/…$/, "")} failed`, body: errorMessage(e), key: "github-op" });
|
||||
notifications().notify({
|
||||
group: "vcs",
|
||||
level: "error",
|
||||
title: `${title.replace(/…$/, "")} failed`,
|
||||
body: errorMessage(e),
|
||||
key: "github-op",
|
||||
});
|
||||
return null;
|
||||
} finally {
|
||||
useGithub.setState({ busy: null });
|
||||
@@ -166,7 +185,14 @@ const diffCache = new Map<string, PrDiffRevisions>();
|
||||
/** Another project: start over. */
|
||||
export function resetGithub() {
|
||||
diffCache.clear();
|
||||
useGithub.setState({ availability: null, list: [], selected: null, details: null, reviewComments: [], query: DEFAULT_QUERY });
|
||||
useGithub.setState({
|
||||
availability: null,
|
||||
list: [],
|
||||
selected: null,
|
||||
details: null,
|
||||
reviewComments: [],
|
||||
query: DEFAULT_QUERY,
|
||||
});
|
||||
}
|
||||
|
||||
/** Fetches what the local repository lacks to diff `pr`, once per head commit. */
|
||||
@@ -176,7 +202,17 @@ export async function diffRevisions(pr: PullRequestDetails): Promise<PrDiffRevis
|
||||
if (hit) return hit;
|
||||
const repo = readyRepo();
|
||||
const revs = await withBusy(`Fetching #${pr.summary.number}…`, (r, out) =>
|
||||
call(commands.ghPrPrepareDiff(r, pr.summary.number, pr.headOid, pr.baseOid, pr.summary.baseRef, repo?.nameWithOwner ?? "", out)),
|
||||
call(
|
||||
commands.ghPrPrepareDiff(
|
||||
r,
|
||||
pr.summary.number,
|
||||
pr.headOid,
|
||||
pr.baseOid,
|
||||
pr.summary.baseRef,
|
||||
repo?.nameWithOwner ?? "",
|
||||
out,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (revs) diffCache.set(key, revs);
|
||||
return revs;
|
||||
|
||||
Reference in new issue
Заблокувати користувача