Skip to content
Closed
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
38 changes: 38 additions & 0 deletions apps/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CfError, ProxyError, LoginRequiredError, setSessionCookie } from "./too
import { fetchContest, signedUpContest } from "./tools/SignUpContest";
import { translateTextRaw } from "./tools/deepl";
import { runCommand } from "./tools/command";
import { fetchSubmitPage, submitCodeWithRedirect } from "./tools/submit";
import { IncomingMessage } from "./tools/types";

const log = {
Expand Down Expand Up @@ -183,6 +184,43 @@ export async function handleRegistration(contest: string, rated: boolean | undef
}
}

export async function handleFetchSubmitPage(contest: string, send: (payload: Record<string, unknown>) => void) {
send({ type: "loading", text: `正在获取 ${contest} 提交页面信息...` });
try {
const pageData = await fetchSubmitPage(contest);
send({ type: "submitPage", submitTasks: pageData.tasks, languages: pageData.languages, csrfToken: pageData.csrfToken });
send({ type: "update", text: "已获取提交页面信息" });
} catch (error) {
if (!handleErrorWithCfAndLogin(error, send)) {
send({ type: "error", text: error instanceof Error ? error.message : "获取提交页面失败" });
}
}
}
Comment on lines +187 to +198

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.


export async function handleSubmitCode(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`handleSubmitCode` has a cyclomatic complexity of 8 with "medium" risk


A function with high cyclomatic complexity can be hard to understand and
maintain. Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A higher cyclomatic complexity indicates
that the function has more decision points and is more complex.

contest: string,
taskScreenName: string | undefined,
languageId: string | undefined,
sourceCode: string | undefined,
send: (payload: Record<string, unknown>) => void,
) {
if (!taskScreenName || !languageId || !sourceCode) {
send({ type: "submitResult", submitResult: { success: false, message: "提交参数不完整" } });
return;
}
send({ type: "loading", text: "正在提交代码..." });
try {
const result = await submitCodeWithRedirect(contest, taskScreenName, languageId, sourceCode);
send({ type: "submitResult", submitResult: result });
if (result.success) send({ type: "update", text: "代码提交成功" });
else send({ type: "error", text: result.message });
} catch (error) {
if (!handleErrorWithCfAndLogin(error, send)) {
send({ type: "submitResult", submitResult: { success: false, message: error instanceof Error ? error.message : "提交失败" } });
}
}
}
Comment on lines +200 to +222

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.


function registerSetDeeplApiKey(context: vscode.ExtensionContext) {
return vscode.commands.registerCommand("extension.setDeeplApiKey", async () => {
const key = await vscode.window.showInputBox({
Expand Down
37 changes: 31 additions & 6 deletions apps/vscode-extension/src/tools/command.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import * as vscode from "vscode";
import { copyMarkdown } from "./copy";
import { IncomingMessage } from "./types";
import { handleContestLoad, handleProblemLoad } from "../extension";
import { handleTranslate, handleGetCookie, handleSetCookie, handleRegistration } from "../extension";
import {
handleContestLoad,
handleProblemLoad,
handleTranslate,
handleGetCookie,
handleSetCookie,
handleRegistration,
handleFetchSubmitPage,
handleSubmitCode,
} from "../extension";

const loadCommands = new Set(["loadContest", "loadProblem", "openBrowser"]);
const deeplCommands = new Set(["translate", "setApiKey"]);
const cookie = new Set(["getCookie", "setCookie"]);
const problem = new Set(["registerContest", "copyMarkdown", "alert"]);
const cookieCommands = new Set(["getCookie", "setCookie"]);
const problemCommands = new Set(["registerContest", "copyMarkdown", "alert"]);
const submitCommands = new Set(["fetchSubmitPage", "submitCode"]);

export async function runCommand(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`runCommand` has a cyclomatic complexity of 6 with "medium" risk


A function with high cyclomatic complexity can be hard to understand and
maintain. Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A higher cyclomatic complexity indicates
that the function has more decision points and is more complex.

message: IncomingMessage,
Expand All @@ -16,11 +25,27 @@ export async function runCommand(
) {
if (loadCommands.has(message.command!)) await runLoadCommand(message, sendToWebview);
else if (deeplCommands.has(message.command!)) await runDeepL(message, context, sendToWebview);
else if (cookie.has(message.command!)) await runCookie(message, context, sendToWebview);
else if (problem.has(message.command!)) await runProblem(message, context, sendToWebview);
else if (cookieCommands.has(message.command!)) await runCookie(message, context, sendToWebview);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidden non-null assertion


Using non-null assertions cancels out the benefits of strict null-checking, and introduces the possibility of runtime errors. Avoid non-null assertions unless absolutely necessary. If you still need to use one, write a skipcq comment to explain why it is safe.

else if (problemCommands.has(message.command!)) await runProblem(message, context, sendToWebview);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidden non-null assertion


Using non-null assertions cancels out the benefits of strict null-checking, and introduces the possibility of runtime errors. Avoid non-null assertions unless absolutely necessary. If you still need to use one, write a skipcq comment to explain why it is safe.

else if (submitCommands.has(message.command!)) await runSubmit(message, context, sendToWebview);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidden non-null assertion


Using non-null assertions cancels out the benefits of strict null-checking, and introduces the possibility of runtime errors. Avoid non-null assertions unless absolutely necessary. If you still need to use one, write a skipcq comment to explain why it is safe.

else throw new Error("unknown command");
}

async function runSubmit(command: IncomingMessage, context: vscode.ExtensionContext, sendToWebview: (payload: Record<string, unknown>) => void,): Promise<boolean> {
switch (command.command) {
case "fetchSubmitPage":
if (!command.contest) return false;
await handleFetchSubmitPage(command.contest, sendToWebview);
return true;
case "submitCode":
if (!command.contest) return false;
await handleSubmitCode(command.contest, command.taskScreenName, command.languageId, command.sourceCode, sendToWebview);
return true;
default:
return false;
}
}
Comment on lines +34 to +47

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.


async function runProblem(
command: IncomingMessage,
context: vscode.ExtensionContext,
Expand Down
90 changes: 90 additions & 0 deletions apps/vscode-extension/src/tools/submit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { fetchText, fetchTextPost } from "./fetch";

export interface LanguageOption {
id: string;
label: string;
}

export interface SubmitPage {
contest: string;
csrfToken: string;
tasks: Array<{ value: string; label: string }>;
languages: LanguageOption[];
}

export interface SubmitStatus {
success: boolean;
message: string;
url?: string;
}

export async function fetchSubmitPage(contest: string): Promise<SubmitPage> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`fetchSubmitPage` has a cyclomatic complexity of 6 with "medium" risk


A function with high cyclomatic complexity can be hard to understand and
maintain. Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A higher cyclomatic complexity indicates
that the function has more decision points and is more complex.

const url = `https://atcoder.jp/contests/${contest}/submit`;
const html = await fetchText(url);

const csrfMatch = html.match(/name="csrf_token"[^>]*value="([^"]*)"/i);
const csrfToken = csrfMatch ? csrfMatch[1] : "";

const tasks: Array<{ value: string; label: string }> = [];
const taskRegex = /<option[^>]*value="([^"]*)"[^>]*>([^<]*)<\/option>/gi;
let taskMatch: RegExpExecArray | null;
for (;(taskMatch = taskRegex.exec(html)) !== null;) {
const value = taskMatch[1];
if (value) tasks.push({ value, label: taskMatch[2].trim() });
}

const languages: LanguageOption[] = [];
const langRegex = /<option[^>]*value="(\d+)"[^>]*>([^<]*)<\/option>/gi;
let langMatch: RegExpExecArray | null;
for (;(langMatch = langRegex.exec(html)) !== null;) {
const value = langMatch[1];
if (value) languages.push({ id: value, label: langMatch[2].trim() });
}

return { contest, csrfToken, tasks, languages };
}
Comment on lines +21 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.


export async function submitCode(contest: string, taskScreenName: string, languageId: string, sourceCode: string, csrfToken: string): Promise<SubmitStatus> {
const url = `https://atcoder.jp/contests/${contest}/submit`;
const body =
`csrf_token=${encodeURIComponent(csrfToken)}` +
`&data.TaskScreenName=${encodeURIComponent(taskScreenName)}` +
`&data.LanguageId=${encodeURIComponent(languageId)}` +
`&sourceCode=${encodeURIComponent(sourceCode)}`;

const responseHtml = await fetchTextPost(url, body);

const errMatch = responseHtml.match(
/<div[^>]*class="[^"]*(?:alert-danger|alert-warning|alert-error)[^"]*"[^>]*>([\s\S]*?)<\/div>/i
);
if (errMatch) {
const errMsg = errMatch[1].replace(/<[^>]+>/g, "").trim();
return { success: false, message: errMsg };
}

const successed = responseHtml.match(
/<div[^>]*class="[^"]*alert-success[^"]*"[^>]*>([\s\S]*?)<\/div>/i
);
if (successed) {
const msg = successed[1].replace(/<[^>]+>/g, "").trim();
return { success: true, message: msg, url: `https://atcoder.jp/contests/${contest}/submissions/me` };
}

if (responseHtml.includes("/submissions/me") || responseHtml.includes("Submission")) {
return {
success: true,
message: "代码提交成功",
url: `https://atcoder.jp/contests/${contest}/submissions/me`,
};
}

return { success: false, message: "提交失败,请检查 Cookie 是否有效" };
}
Comment on lines +47 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.


export async function submitCodeWithRedirect(contest: string, taskScreenName: string, languageId: string, sourceCode: string): Promise<SubmitStatus> {
const pageData = await fetchSubmitPage(contest);
if (!pageData.csrfToken) {
return { success: false, message: "无法获取 CSRF Token,请检查 Cookie 是否有效" };
}
return await submitCode(contest, taskScreenName, languageId, sourceCode, pageData.csrfToken);
}
Comment on lines +84 to +90

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

3 changes: 3 additions & 0 deletions apps/vscode-extension/src/tools/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export interface IncomingMessage {
command?: string;
contest?: string;
task?: string;
taskScreenName?: string;
languageId?: string;
sourceCode?: string;
url?: string;
payload?: Record<string, string>;
targetLang?: string;
Expand Down
Loading