@rhseung/ps-cli 1.11.1 → 1.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -4
- package/dist/{chunk-3DTUNRPH.js → chunk-C3IOK7V4.js} +1 -1
- package/dist/{chunk-4VPUJY7G.js → chunk-CKSAX7KT.js} +19 -7
- package/dist/{chunk-EZLFR55N.js → chunk-EQDQI3RC.js} +2 -2
- package/dist/{chunk-ERJEYECY.js → chunk-EQTRV4DN.js} +52 -4
- package/dist/{chunk-COPOWZM5.js → chunk-K5KMUKVD.js} +87 -16
- package/dist/{chunk-CGL3LQEB.js → chunk-KUV37YXC.js} +2 -2
- package/dist/{chunk-C25Y3KFE.js → chunk-LGSE3IEE.js} +72 -8
- package/dist/{chunk-XNZBOLC2.js → chunk-RHWSWBUG.js} +1 -1
- package/dist/{chunk-U6KQM2P4.js → chunk-RPBTJIZG.js} +4 -3
- package/dist/{chunk-AHE4QHJD.js → chunk-UDZLWCHF.js} +75 -6
- package/dist/{chunk-LCHSAHUP.js → chunk-V27D7TPW.js} +1 -1
- package/dist/{chunk-G272SKVM.js → chunk-WJQ622GN.js} +2 -2
- package/dist/{chunk-QT2FQELR.js → chunk-ZZ76HNFP.js} +1 -1
- package/dist/commands/archive.js +3 -3
- package/dist/commands/config.js +2 -2
- package/dist/commands/fetch.js +4 -4
- package/dist/commands/init.js +2 -2
- package/dist/commands/open.js +2 -2
- package/dist/commands/run.js +74 -11
- package/dist/commands/search.js +14 -14
- package/dist/commands/stats.js +3 -3
- package/dist/commands/submit.js +2 -2
- package/dist/commands/test.js +9 -9
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,7 +82,8 @@ ps fetch 1000 -l cpp
|
|
|
82
82
|
|
|
83
83
|
- Solved.ac API와 BOJ 크롤링을 통해 문제 정보 수집
|
|
84
84
|
- 문제 설명, 입출력 형식, 예제 입출력 파일 자동 생성
|
|
85
|
-
- 선택한 언어의 솔루션 템플릿 파일 생성
|
|
85
|
+
- 선택한 언어의 솔루션 템플릿 파일 생성 (`solution-1.{확장자}` 형식)
|
|
86
|
+
- 같은 언어로 다시 fetch하면 자동으로 다음 인덱스 파일 생성 (`solution-2.{확장자}`, `solution-3.{확장자}` 등)
|
|
86
87
|
- README.md에 문제 정보, 통계, 태그 등 포함
|
|
87
88
|
|
|
88
89
|
---
|
|
@@ -104,6 +105,8 @@ ps test [문제번호] [옵션]
|
|
|
104
105
|
- 지원 언어: python, javascript, typescript, cpp
|
|
105
106
|
- `--watch`, `-w`: watch 모드 (파일 변경 시 자동 재테스트)
|
|
106
107
|
- solution._, testcases/\*\*/_.txt 파일 변경 감지
|
|
108
|
+
- `--file`, `-f`: 특정 solution 파일 지정 (예: `solution-2.py`)
|
|
109
|
+
- `--index`: 인덱스로 solution 파일 지정 (예: `2`)
|
|
107
110
|
|
|
108
111
|
**예제:**
|
|
109
112
|
|
|
@@ -113,12 +116,16 @@ ps test 1000 # 1000번 문제 테스트
|
|
|
113
116
|
ps test --watch # watch 모드로 테스트
|
|
114
117
|
ps test 1000 --watch # 1000번 문제를 watch 모드로 테스트
|
|
115
118
|
ps test --language python # Python으로 테스트
|
|
119
|
+
ps test --file solution-2.py # 특정 파일로 테스트
|
|
120
|
+
ps test --index 2 # 인덱스 2의 파일로 테스트
|
|
116
121
|
```
|
|
117
122
|
|
|
118
123
|
**설명:**
|
|
119
124
|
|
|
120
125
|
- 현재 디렉토리 또는 지정한 문제 번호의 테스트 실행
|
|
121
|
-
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
126
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지 (기본적으로 가장 최근 수정된 파일 사용)
|
|
127
|
+
- 여러 답안 파일이 있는 경우 `--file` 또는 `--index` 옵션으로 특정 파일 지정 가능
|
|
128
|
+
- 파일 네이밍: `solution-1.py`, `solution-2.py` 형식으로 여러 답안 관리 가능
|
|
122
129
|
- testcases/{번호}/input.txt와 testcases/{번호}/output.txt 파일을 기반으로 테스트
|
|
123
130
|
- 문제의 시간 제한을 자동으로 적용
|
|
124
131
|
|
|
@@ -142,6 +149,8 @@ ps run [문제번호] [옵션]
|
|
|
142
149
|
- `--input`, `-i`: 입력 파일 지정
|
|
143
150
|
- 숫자만 입력 시 testcases/{숫자}/input.txt로 자동 변환 (예: `--input 1`)
|
|
144
151
|
- 전체 경로도 지원 (예: testcases/1/input.txt)
|
|
152
|
+
- `--file`, `-f`: 특정 solution 파일 지정 (예: `solution-2.py`)
|
|
153
|
+
- `--index`: 인덱스로 solution 파일 지정 (예: `2`)
|
|
145
154
|
|
|
146
155
|
**예제:**
|
|
147
156
|
|
|
@@ -151,12 +160,16 @@ ps run 1000 # 1000번 문제 표준 입력으로 실행
|
|
|
151
160
|
ps run --language python # Python으로 표준 입력으로 실행
|
|
152
161
|
ps run --input 1 # 테스트 케이스 1번 사용
|
|
153
162
|
ps run --input testcases/1/input.txt # 전체 경로로 입력 파일 지정
|
|
163
|
+
ps run --file solution-2.py # 특정 파일로 실행
|
|
164
|
+
ps run --index 2 # 인덱스 2의 파일로 실행
|
|
154
165
|
```
|
|
155
166
|
|
|
156
167
|
**설명:**
|
|
157
168
|
|
|
158
169
|
- 현재 디렉토리 또는 지정한 문제 번호의 코드 실행
|
|
159
|
-
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
170
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지 (기본적으로 가장 최근 수정된 파일 사용)
|
|
171
|
+
- 여러 답안 파일이 있는 경우 `--file` 또는 `--index` 옵션으로 특정 파일 지정 가능
|
|
172
|
+
- 파일 네이밍: `solution-1.py`, `solution-2.py` 형식으로 여러 답안 관리 가능
|
|
160
173
|
- --input 옵션으로 입력 파일 지정 가능 (예: `--input 1` 또는 `--input testcases/1/input.txt`)
|
|
161
174
|
- 옵션 없이 실행 시 표준 입력으로 입력 받기
|
|
162
175
|
- 테스트 케이스 검증 없이 단순 실행
|
|
@@ -178,6 +191,8 @@ ps submit [문제번호] [옵션]
|
|
|
178
191
|
- `--help`, `-h`: 도움말 표시
|
|
179
192
|
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
180
193
|
- 지원 언어: python, javascript, typescript, cpp
|
|
194
|
+
- `--file`, `-f`: 특정 solution 파일 지정 (예: `solution-2.py`)
|
|
195
|
+
- `--index`, `-i`: 인덱스로 solution 파일 지정 (예: `2`)
|
|
181
196
|
|
|
182
197
|
**예제:**
|
|
183
198
|
|
|
@@ -185,12 +200,16 @@ ps submit [문제번호] [옵션]
|
|
|
185
200
|
ps submit # 현재 디렉토리에서 제출
|
|
186
201
|
ps submit 1000 # 1000번 문제 제출
|
|
187
202
|
ps submit --language python # Python으로 제출
|
|
203
|
+
ps submit --file solution-2.py # 특정 파일로 제출
|
|
204
|
+
ps submit --index 2 # 인덱스 2의 파일로 제출
|
|
188
205
|
```
|
|
189
206
|
|
|
190
207
|
**설명:**
|
|
191
208
|
|
|
192
209
|
- 문제 번호를 인자로 전달하거나 문제 디렉토리에서 실행하면 자동으로 문제 번호를 추론
|
|
193
|
-
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
210
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지 (기본적으로 가장 최근 수정된 파일 사용)
|
|
211
|
+
- 여러 답안 파일이 있는 경우 `--file` 또는 `--index` 옵션으로 특정 파일 지정 가능
|
|
212
|
+
- 파일 네이밍: `solution-1.py`, `solution-2.py` 형식으로 여러 답안 관리 가능
|
|
194
213
|
- 소스 코드를 클립보드에 자동 복사
|
|
195
214
|
- 제출 페이지를 브라우저로 자동 열기
|
|
196
215
|
|
|
@@ -426,6 +445,42 @@ ps config clear # .ps-cli 폴더 및 모든 설정 삭제
|
|
|
426
445
|
|
|
427
446
|
**참고:** solving 디렉토리는 항상 평면적으로 나열됩니다.
|
|
428
447
|
|
|
448
|
+
## 여러 답안 파일 지원
|
|
449
|
+
|
|
450
|
+
한 문제에 대해 여러 답안 파일을 관리할 수 있습니다.
|
|
451
|
+
|
|
452
|
+
### 파일 네이밍
|
|
453
|
+
|
|
454
|
+
- 형식: `solution-{인덱스}.{확장자}` (예: `solution-1.py`, `solution-2.py`, `solution-1.cpp`)
|
|
455
|
+
- 첫 번째 파일은 `solution-1.{확장자}`로 시작
|
|
456
|
+
- 같은 언어로 `ps fetch`를 다시 실행하면 자동으로 다음 인덱스 파일 생성
|
|
457
|
+
|
|
458
|
+
### 파일 선택
|
|
459
|
+
|
|
460
|
+
- **기본 동작**: 가장 최근 수정된 파일을 자동으로 선택
|
|
461
|
+
- **직접 지정**: `--file` 또는 `--index` 옵션으로 특정 파일 지정
|
|
462
|
+
|
|
463
|
+
**예제:**
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
# 가장 최근 파일로 테스트 (기본값)
|
|
467
|
+
ps test
|
|
468
|
+
|
|
469
|
+
# 특정 파일로 테스트
|
|
470
|
+
ps test --file solution-2.py
|
|
471
|
+
ps test --index 2
|
|
472
|
+
|
|
473
|
+
# 다른 명령어에서도 동일하게 사용 가능
|
|
474
|
+
ps run --index 2
|
|
475
|
+
ps submit --file solution-3.py
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 사용 사례
|
|
479
|
+
|
|
480
|
+
- 같은 문제를 여러 알고리즘으로 해결한 경우
|
|
481
|
+
- 같은 언어로 여러 버전의 코드를 작성한 경우
|
|
482
|
+
- 다른 언어로 같은 문제를 해결한 경우
|
|
483
|
+
|
|
429
484
|
## 워크플로우
|
|
430
485
|
|
|
431
486
|
1. **초기화**: `ps init`으로 프로젝트 설정
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
findSolutionFile,
|
|
3
4
|
getLanguageConfig,
|
|
4
5
|
getProblemTimeLimitMs
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UDZLWCHF.js";
|
|
6
7
|
|
|
7
8
|
// src/hooks/use-run-solution.ts
|
|
8
9
|
import { useEffect, useState } from "react";
|
|
@@ -31,11 +32,20 @@ async function runSolution({
|
|
|
31
32
|
problemDir,
|
|
32
33
|
language,
|
|
33
34
|
inputPath,
|
|
34
|
-
timeoutMs = 5e3
|
|
35
|
+
timeoutMs = 5e3,
|
|
36
|
+
solutionPath: providedSolutionPath
|
|
35
37
|
}) {
|
|
38
|
+
let solutionPath;
|
|
39
|
+
if (providedSolutionPath) {
|
|
40
|
+
if (providedSolutionPath.startsWith("/") || providedSolutionPath.match(/^[A-Z]:/)) {
|
|
41
|
+
solutionPath = providedSolutionPath;
|
|
42
|
+
} else {
|
|
43
|
+
solutionPath = join(problemDir, providedSolutionPath);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
solutionPath = await findSolutionFile(problemDir, language);
|
|
47
|
+
}
|
|
36
48
|
const langConfig = getLanguageConfig(language);
|
|
37
|
-
const solutionFile = `solution.${langConfig.extension}`;
|
|
38
|
-
const solutionPath = join(problemDir, solutionFile);
|
|
39
49
|
let input;
|
|
40
50
|
let capturedInput;
|
|
41
51
|
if (inputPath) {
|
|
@@ -98,7 +108,8 @@ function useRunSolution({
|
|
|
98
108
|
problemDir,
|
|
99
109
|
language,
|
|
100
110
|
inputFile,
|
|
101
|
-
onComplete
|
|
111
|
+
onComplete,
|
|
112
|
+
solutionPath
|
|
102
113
|
}) {
|
|
103
114
|
const [status, setStatus] = useState(
|
|
104
115
|
"loading"
|
|
@@ -114,7 +125,8 @@ function useRunSolution({
|
|
|
114
125
|
problemDir,
|
|
115
126
|
language,
|
|
116
127
|
inputPath: inputFile,
|
|
117
|
-
timeoutMs: effectiveTimeout
|
|
128
|
+
timeoutMs: effectiveTimeout,
|
|
129
|
+
solutionPath
|
|
118
130
|
});
|
|
119
131
|
setResult(runResult);
|
|
120
132
|
setStatus("ready");
|
|
@@ -130,7 +142,7 @@ function useRunSolution({
|
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
void run();
|
|
133
|
-
}, [problemDir, language, inputFile, onComplete]);
|
|
145
|
+
}, [problemDir, language, inputFile, onComplete, solutionPath]);
|
|
134
146
|
return {
|
|
135
147
|
status,
|
|
136
148
|
result,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useArchive
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-V27D7TPW.js";
|
|
5
5
|
import {
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
__decorateClass,
|
|
10
10
|
logger,
|
|
11
11
|
resolveProblemContext
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-UDZLWCHF.js";
|
|
13
13
|
|
|
14
14
|
// src/commands/archive.tsx
|
|
15
15
|
import { StatusMessage, Alert, Spinner } from "@inkjs/ui";
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getProblem,
|
|
4
4
|
scrapeProblem
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RPBTJIZG.js";
|
|
6
6
|
import {
|
|
7
|
+
detectLanguageFromFile,
|
|
7
8
|
findProjectRoot,
|
|
8
9
|
getAutoOpenEditor,
|
|
9
10
|
getEditor,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
getTierImageUrl,
|
|
14
15
|
getTierName,
|
|
15
16
|
parseTimeLimitToMs
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UDZLWCHF.js";
|
|
17
18
|
|
|
18
19
|
// src/hooks/use-fetch-problem.ts
|
|
19
20
|
import { execaCommand } from "execa";
|
|
@@ -21,7 +22,7 @@ import { useEffect, useState } from "react";
|
|
|
21
22
|
|
|
22
23
|
// src/services/file-generator.ts
|
|
23
24
|
import { existsSync } from "fs";
|
|
24
|
-
import { mkdir, writeFile, readFile } from "fs/promises";
|
|
25
|
+
import { mkdir, writeFile, readFile, readdir } from "fs/promises";
|
|
25
26
|
import { join, dirname } from "path";
|
|
26
27
|
import { fileURLToPath } from "url";
|
|
27
28
|
function ensureTrailingNewline(content) {
|
|
@@ -61,13 +62,60 @@ function getProjectRoot() {
|
|
|
61
62
|
}
|
|
62
63
|
return join(__dirname, "../..");
|
|
63
64
|
}
|
|
65
|
+
function parseSolutionIndex(filename) {
|
|
66
|
+
const match = filename.match(/^solution-(\d+)\./);
|
|
67
|
+
if (match) {
|
|
68
|
+
const index = parseInt(match[1], 10);
|
|
69
|
+
if (!isNaN(index) && index > 0) {
|
|
70
|
+
return index;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (filename.match(/^solution\./)) {
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
async function getNextSolutionIndex(problemDir, language) {
|
|
79
|
+
try {
|
|
80
|
+
if (!existsSync(problemDir)) {
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
const files = await readdir(problemDir);
|
|
84
|
+
const langConfig = getLanguageConfig(language);
|
|
85
|
+
const extension = langConfig.extension;
|
|
86
|
+
let maxIndex = 0;
|
|
87
|
+
for (const file of files) {
|
|
88
|
+
if (!file.endsWith(`.${extension}`)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!file.startsWith("solution")) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const detectedLang = detectLanguageFromFile(file);
|
|
95
|
+
if (detectedLang !== language) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const index = parseSolutionIndex(file);
|
|
99
|
+
if (index !== null && index > maxIndex) {
|
|
100
|
+
maxIndex = index;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return maxIndex + 1;
|
|
104
|
+
} catch {
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
64
108
|
async function generateProblemFiles(problem, language = "python") {
|
|
65
109
|
const problemDir = getSolvingDirPath(problem.id, process.cwd(), problem);
|
|
66
110
|
await mkdir(problemDir, { recursive: true });
|
|
67
111
|
const langConfig = getLanguageConfig(language);
|
|
68
112
|
const projectRoot = getProjectRoot();
|
|
69
113
|
const userProjectRoot = findProjectRoot(process.cwd());
|
|
70
|
-
const
|
|
114
|
+
const nextIndex = await getNextSolutionIndex(problemDir, language);
|
|
115
|
+
const solutionPath = join(
|
|
116
|
+
problemDir,
|
|
117
|
+
`solution-${nextIndex}.${langConfig.extension}`
|
|
118
|
+
);
|
|
71
119
|
let templateContent = "";
|
|
72
120
|
let templateFound = false;
|
|
73
121
|
if (userProjectRoot) {
|
|
@@ -7,21 +7,24 @@ import {
|
|
|
7
7
|
} from "./chunk-QGMWUOJ3.js";
|
|
8
8
|
import {
|
|
9
9
|
runSolution
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CKSAX7KT.js";
|
|
11
11
|
import {
|
|
12
12
|
Command,
|
|
13
13
|
CommandBuilder,
|
|
14
14
|
CommandDef,
|
|
15
15
|
__decorateClass,
|
|
16
16
|
defineFlags,
|
|
17
|
+
detectLanguageFromFile,
|
|
17
18
|
detectProblemIdFromPath,
|
|
18
19
|
findSolutionFile,
|
|
20
|
+
findSolutionFileByIndex,
|
|
21
|
+
findSolutionFiles,
|
|
19
22
|
getProblemTimeLimitMs,
|
|
20
23
|
getSupportedLanguagesString,
|
|
21
24
|
icons,
|
|
22
25
|
resolveLanguage,
|
|
23
26
|
resolveProblemContext
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-UDZLWCHF.js";
|
|
25
28
|
|
|
26
29
|
// src/commands/test.tsx
|
|
27
30
|
import { Alert, Badge as Badge2, Spinner, StatusMessage as StatusMessage2 } from "@inkjs/ui";
|
|
@@ -134,7 +137,8 @@ function buildSummary(results) {
|
|
|
134
137
|
async function runAllTests({
|
|
135
138
|
problemDir,
|
|
136
139
|
language,
|
|
137
|
-
timeoutMs
|
|
140
|
+
timeoutMs,
|
|
141
|
+
solutionPath
|
|
138
142
|
}) {
|
|
139
143
|
const testcasesDir = join(problemDir, "testcases");
|
|
140
144
|
let caseDirs = [];
|
|
@@ -172,7 +176,8 @@ async function runAllTests({
|
|
|
172
176
|
problemDir,
|
|
173
177
|
language,
|
|
174
178
|
inputPath,
|
|
175
|
-
timeoutMs: effectiveTimeout
|
|
179
|
+
timeoutMs: effectiveTimeout,
|
|
180
|
+
solutionPath
|
|
176
181
|
});
|
|
177
182
|
if (runResult.exitCode !== 0 || runResult.timedOut) {
|
|
178
183
|
results.push({
|
|
@@ -204,7 +209,8 @@ function useTestRunner({
|
|
|
204
209
|
problemDir,
|
|
205
210
|
language,
|
|
206
211
|
watch,
|
|
207
|
-
timeoutMs
|
|
212
|
+
timeoutMs,
|
|
213
|
+
solutionPath
|
|
208
214
|
}) {
|
|
209
215
|
const [status, setStatus] = useState("loading");
|
|
210
216
|
const [results, setResults] = useState([]);
|
|
@@ -224,7 +230,8 @@ function useTestRunner({
|
|
|
224
230
|
void runAllTests({
|
|
225
231
|
problemDir,
|
|
226
232
|
language,
|
|
227
|
-
timeoutMs
|
|
233
|
+
timeoutMs,
|
|
234
|
+
solutionPath
|
|
228
235
|
}).then(({ results: results2, summary: summary2 }) => {
|
|
229
236
|
setResults(results2);
|
|
230
237
|
setSummary(summary2);
|
|
@@ -234,7 +241,7 @@ function useTestRunner({
|
|
|
234
241
|
setStatus("error");
|
|
235
242
|
});
|
|
236
243
|
},
|
|
237
|
-
[problemDir, language, timeoutMs, watch]
|
|
244
|
+
[problemDir, language, timeoutMs, watch, solutionPath]
|
|
238
245
|
);
|
|
239
246
|
useEffect(() => {
|
|
240
247
|
const timer = setTimeout(() => {
|
|
@@ -344,6 +351,16 @@ var testFlagsSchema = {
|
|
|
344
351
|
testcaseAc: {
|
|
345
352
|
type: "boolean",
|
|
346
353
|
description: "\uB85C\uCEEC \uD14C\uC2A4\uD2B8 \uC2E4\uD589 \uC5C6\uC774 testcase.ac \uBB38\uC81C \uD398\uC774\uC9C0\uB97C \uC5F4\uACE0 \uC18C\uC2A4\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD569\uB2C8\uB2E4"
|
|
354
|
+
},
|
|
355
|
+
file: {
|
|
356
|
+
type: "string",
|
|
357
|
+
shortFlag: "f",
|
|
358
|
+
description: "\uD2B9\uC815 solution \uD30C\uC77C \uC9C0\uC815 (\uC608: solution-2.py)"
|
|
359
|
+
},
|
|
360
|
+
index: {
|
|
361
|
+
type: "string",
|
|
362
|
+
shortFlag: "i",
|
|
363
|
+
description: "\uC778\uB371\uC2A4\uB85C solution \uD30C\uC77C \uC9C0\uC815 (\uC608: 2)"
|
|
347
364
|
}
|
|
348
365
|
};
|
|
349
366
|
function TestcaseAcPanel({
|
|
@@ -408,13 +425,15 @@ function TestView({
|
|
|
408
425
|
timeoutMs,
|
|
409
426
|
onComplete,
|
|
410
427
|
problemId,
|
|
411
|
-
sourcePath
|
|
428
|
+
sourcePath,
|
|
429
|
+
solutionPath
|
|
412
430
|
}) {
|
|
413
431
|
const { status, results, summary, error } = useTestRunner({
|
|
414
432
|
problemDir,
|
|
415
433
|
language,
|
|
416
434
|
watch,
|
|
417
435
|
timeoutMs,
|
|
436
|
+
solutionPath,
|
|
418
437
|
onComplete: () => {
|
|
419
438
|
}
|
|
420
439
|
});
|
|
@@ -526,7 +545,58 @@ function TestView({
|
|
|
526
545
|
var TestCommand = class extends Command {
|
|
527
546
|
async execute(args, flags) {
|
|
528
547
|
const context = await resolveProblemContext(args);
|
|
529
|
-
|
|
548
|
+
let sourcePath;
|
|
549
|
+
let language;
|
|
550
|
+
if (flags.file) {
|
|
551
|
+
const filePath = flags.file;
|
|
552
|
+
if (filePath.startsWith("/") || filePath.match(/^[A-Z]:/)) {
|
|
553
|
+
sourcePath = filePath;
|
|
554
|
+
} else {
|
|
555
|
+
const { join: join3 } = await import("path");
|
|
556
|
+
sourcePath = join3(context.archiveDir, filePath);
|
|
557
|
+
}
|
|
558
|
+
const fileName = sourcePath.split(/[/\\]/).pop() || "";
|
|
559
|
+
const fileLang = detectLanguageFromFile(fileName);
|
|
560
|
+
if (flags.language) {
|
|
561
|
+
language = flags.language;
|
|
562
|
+
} else if (fileLang) {
|
|
563
|
+
language = fileLang;
|
|
564
|
+
} else {
|
|
565
|
+
language = await resolveLanguage(
|
|
566
|
+
context.archiveDir,
|
|
567
|
+
flags.language
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
} else if (flags.index) {
|
|
571
|
+
const index = parseInt(flags.index, 10);
|
|
572
|
+
if (isNaN(index) || index < 1) {
|
|
573
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC778\uB371\uC2A4\uC785\uB2C8\uB2E4: ${flags.index}`);
|
|
574
|
+
}
|
|
575
|
+
if (flags.language) {
|
|
576
|
+
language = flags.language;
|
|
577
|
+
sourcePath = await findSolutionFileByIndex(
|
|
578
|
+
context.archiveDir,
|
|
579
|
+
index,
|
|
580
|
+
language
|
|
581
|
+
);
|
|
582
|
+
} else {
|
|
583
|
+
const files = await findSolutionFiles(context.archiveDir);
|
|
584
|
+
const targetFile = files.find((f) => f.index === index);
|
|
585
|
+
if (!targetFile) {
|
|
586
|
+
throw new Error(
|
|
587
|
+
`\uC778\uB371\uC2A4 ${index}\uC758 solution \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
sourcePath = targetFile.path;
|
|
591
|
+
language = targetFile.language;
|
|
592
|
+
}
|
|
593
|
+
} else {
|
|
594
|
+
language = await resolveLanguage(
|
|
595
|
+
context.archiveDir,
|
|
596
|
+
flags.language
|
|
597
|
+
);
|
|
598
|
+
sourcePath = await findSolutionFile(context.archiveDir, language);
|
|
599
|
+
}
|
|
530
600
|
let finalProblemId = context.problemId;
|
|
531
601
|
if (finalProblemId === null) {
|
|
532
602
|
finalProblemId = detectProblemIdFromPath(context.archiveDir);
|
|
@@ -543,17 +613,14 @@ var TestCommand = class extends Command {
|
|
|
543
613
|
});
|
|
544
614
|
return;
|
|
545
615
|
}
|
|
546
|
-
const language = await resolveLanguage(
|
|
547
|
-
context.archiveDir,
|
|
548
|
-
flags.language
|
|
549
|
-
);
|
|
550
616
|
await this.renderView(TestView, {
|
|
551
617
|
problemDir: context.archiveDir,
|
|
552
618
|
language,
|
|
553
619
|
watch: Boolean(flags.watch),
|
|
554
620
|
timeoutMs: flags.timeoutMs,
|
|
555
621
|
problemId: finalProblemId,
|
|
556
|
-
sourcePath
|
|
622
|
+
sourcePath,
|
|
623
|
+
solutionPath: sourcePath
|
|
557
624
|
});
|
|
558
625
|
}
|
|
559
626
|
};
|
|
@@ -562,7 +629,9 @@ TestCommand = __decorateClass([
|
|
|
562
629
|
name: "test",
|
|
563
630
|
description: `\uC608\uC81C \uC785\uCD9C\uB825 \uAE30\uBC18\uC73C\uB85C \uB85C\uCEEC \uD14C\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4.
|
|
564
631
|
- \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uD14C\uC2A4\uD2B8 \uC2E4\uD589
|
|
565
|
-
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
632
|
+
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0 (\uAE30\uBCF8\uC801\uC73C\uB85C \uAC00\uC7A5 \uCD5C\uADFC \uC218\uC815\uB41C \uD30C\uC77C \uC0AC\uC6A9)
|
|
633
|
+
- \uC5EC\uB7EC \uB2F5\uC548 \uD30C\uC77C \uC9C0\uC6D0: solution-1.py, solution-2.py \uD615\uC2DD\uC73C\uB85C \uC5EC\uB7EC \uB2F5\uC548 \uAD00\uB9AC \uAC00\uB2A5
|
|
634
|
+
- --file \uB610\uB294 --index \uC635\uC158\uC73C\uB85C \uD2B9\uC815 \uD30C\uC77C \uC9C0\uC815 \uAC00\uB2A5
|
|
566
635
|
- testcases/{\uBC88\uD638}/input.txt\uC640 testcases/{\uBC88\uD638}/output.txt \uD30C\uC77C\uC744 \uAE30\uBC18\uC73C\uB85C \uD14C\uC2A4\uD2B8
|
|
567
636
|
- \uBB38\uC81C\uC758 \uC2DC\uAC04 \uC81C\uD55C\uC744 \uC790\uB3D9\uC73C\uB85C \uC801\uC6A9
|
|
568
637
|
- --watch \uC635\uC158\uC73C\uB85C \uD30C\uC77C \uBCC0\uACBD \uC2DC \uC790\uB3D9 \uC7AC\uD14C\uC2A4\uD2B8
|
|
@@ -575,7 +644,9 @@ TestCommand = __decorateClass([
|
|
|
575
644
|
"test 1000 # 1000\uBC88 \uBB38\uC81C \uD14C\uC2A4\uD2B8",
|
|
576
645
|
"test --watch # watch \uBAA8\uB4DC\uB85C \uD14C\uC2A4\uD2B8",
|
|
577
646
|
"test 1000 --watch # 1000\uBC88 \uBB38\uC81C\uB97C watch \uBAA8\uB4DC\uB85C \uD14C\uC2A4\uD2B8",
|
|
578
|
-
"test --language python # Python\uC73C\uB85C \uD14C\uC2A4\uD2B8"
|
|
647
|
+
"test --language python # Python\uC73C\uB85C \uD14C\uC2A4\uD2B8",
|
|
648
|
+
"test --file solution-2.py # \uD2B9\uC815 \uD30C\uC77C\uB85C \uD14C\uC2A4\uD2B8",
|
|
649
|
+
"test --index 2 # \uC778\uB371\uC2A4 2\uC758 \uD30C\uC77C\uB85C \uD14C\uC2A4\uD2B8"
|
|
579
650
|
]
|
|
580
651
|
})
|
|
581
652
|
], TestCommand);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useFetchProblem
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EQTRV4DN.js";
|
|
5
5
|
import {
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getTierName,
|
|
15
15
|
logger,
|
|
16
16
|
resolveProblemContext
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UDZLWCHF.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/fetch.tsx
|
|
20
20
|
import { StatusMessage, Alert, Spinner } from "@inkjs/ui";
|
|
@@ -8,12 +8,15 @@ import {
|
|
|
8
8
|
CommandDef,
|
|
9
9
|
__decorateClass,
|
|
10
10
|
defineFlags,
|
|
11
|
+
detectLanguageFromFile,
|
|
11
12
|
detectProblemIdFromPath,
|
|
12
13
|
findSolutionFile,
|
|
14
|
+
findSolutionFileByIndex,
|
|
15
|
+
findSolutionFiles,
|
|
13
16
|
getSupportedLanguagesString,
|
|
14
17
|
resolveLanguage,
|
|
15
18
|
resolveProblemContext
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-UDZLWCHF.js";
|
|
17
20
|
|
|
18
21
|
// src/commands/submit.tsx
|
|
19
22
|
import { Badge, StatusMessage, Alert, Spinner } from "@inkjs/ui";
|
|
@@ -25,6 +28,16 @@ var submitFlagsSchema = {
|
|
|
25
28
|
shortFlag: "l",
|
|
26
29
|
description: `\uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
|
|
27
30
|
\uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}`
|
|
31
|
+
},
|
|
32
|
+
file: {
|
|
33
|
+
type: "string",
|
|
34
|
+
shortFlag: "f",
|
|
35
|
+
description: "\uD2B9\uC815 solution \uD30C\uC77C \uC9C0\uC815 (\uC608: solution-2.py)"
|
|
36
|
+
},
|
|
37
|
+
index: {
|
|
38
|
+
type: "string",
|
|
39
|
+
shortFlag: "i",
|
|
40
|
+
description: "\uC778\uB371\uC2A4\uB85C solution \uD30C\uC77C \uC9C0\uC815 (\uC608: 2)"
|
|
28
41
|
}
|
|
29
42
|
};
|
|
30
43
|
function SubmitView({
|
|
@@ -105,11 +118,58 @@ function SubmitView({
|
|
|
105
118
|
var SubmitCommand = class extends Command {
|
|
106
119
|
async execute(args, flags) {
|
|
107
120
|
const context = await resolveProblemContext(args, { requireId: true });
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
flags.
|
|
112
|
-
|
|
121
|
+
let sourcePath;
|
|
122
|
+
let detectedLanguage;
|
|
123
|
+
if (flags.file) {
|
|
124
|
+
const filePath = flags.file;
|
|
125
|
+
if (filePath.startsWith("/") || filePath.match(/^[A-Z]:/)) {
|
|
126
|
+
sourcePath = filePath;
|
|
127
|
+
} else {
|
|
128
|
+
const { join } = await import("path");
|
|
129
|
+
sourcePath = join(context.archiveDir, filePath);
|
|
130
|
+
}
|
|
131
|
+
const fileName = sourcePath.split(/[/\\]/).pop() || "";
|
|
132
|
+
const fileLang = detectLanguageFromFile(fileName);
|
|
133
|
+
if (flags.language) {
|
|
134
|
+
detectedLanguage = flags.language;
|
|
135
|
+
} else if (fileLang) {
|
|
136
|
+
detectedLanguage = fileLang;
|
|
137
|
+
} else {
|
|
138
|
+
detectedLanguage = await resolveLanguage(
|
|
139
|
+
context.archiveDir,
|
|
140
|
+
flags.language
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
} else if (flags.index) {
|
|
144
|
+
const index = parseInt(flags.index, 10);
|
|
145
|
+
if (isNaN(index) || index < 1) {
|
|
146
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC778\uB371\uC2A4\uC785\uB2C8\uB2E4: ${flags.index}`);
|
|
147
|
+
}
|
|
148
|
+
if (flags.language) {
|
|
149
|
+
detectedLanguage = flags.language;
|
|
150
|
+
sourcePath = await findSolutionFileByIndex(
|
|
151
|
+
context.archiveDir,
|
|
152
|
+
index,
|
|
153
|
+
detectedLanguage
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
const files = await findSolutionFiles(context.archiveDir);
|
|
157
|
+
const targetFile = files.find((f) => f.index === index);
|
|
158
|
+
if (!targetFile) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`\uC778\uB371\uC2A4 ${index}\uC758 solution \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
sourcePath = targetFile.path;
|
|
164
|
+
detectedLanguage = targetFile.language;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
detectedLanguage = await resolveLanguage(
|
|
168
|
+
context.archiveDir,
|
|
169
|
+
flags.language
|
|
170
|
+
);
|
|
171
|
+
sourcePath = await findSolutionFile(context.archiveDir, detectedLanguage);
|
|
172
|
+
}
|
|
113
173
|
let finalProblemId = context.problemId;
|
|
114
174
|
if (finalProblemId === null) {
|
|
115
175
|
finalProblemId = detectProblemIdFromPath(context.archiveDir);
|
|
@@ -131,7 +191,9 @@ SubmitCommand = __decorateClass([
|
|
|
131
191
|
name: "submit",
|
|
132
192
|
description: `\uBC31\uC900 \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5F4\uACE0 \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD569\uB2C8\uB2E4.
|
|
133
193
|
- \uBB38\uC81C \uBC88\uD638\uB97C \uC778\uC790\uB85C \uC804\uB2EC\uD558\uAC70\uB098 \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBB38\uC81C \uBC88\uD638\uB97C \uCD94\uB860
|
|
134
|
-
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
194
|
+
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0 (\uAE30\uBCF8\uC801\uC73C\uB85C \uAC00\uC7A5 \uCD5C\uADFC \uC218\uC815\uB41C \uD30C\uC77C \uC0AC\uC6A9)
|
|
195
|
+
- \uC5EC\uB7EC \uB2F5\uC548 \uD30C\uC77C \uC9C0\uC6D0: solution-1.py, solution-2.py \uD615\uC2DD\uC73C\uB85C \uC5EC\uB7EC \uB2F5\uC548 \uAD00\uB9AC \uAC00\uB2A5
|
|
196
|
+
- --file \uB610\uB294 --index \uC635\uC158\uC73C\uB85C \uD2B9\uC815 \uD30C\uC77C \uC9C0\uC815 \uAC00\uB2A5
|
|
135
197
|
- \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uC790\uB3D9 \uBCF5\uC0AC
|
|
136
198
|
- \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC790\uB3D9 \uC5F4\uAE30
|
|
137
199
|
- \uAE30\uBCF8 \uC5B8\uC5B4 \uB4F1\uC740 ps config\uC5D0\uC11C \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.`,
|
|
@@ -142,7 +204,9 @@ SubmitCommand = __decorateClass([
|
|
|
142
204
|
examples: [
|
|
143
205
|
"submit # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC81C\uCD9C",
|
|
144
206
|
"submit 1000 # 1000\uBC88 \uBB38\uC81C \uC81C\uCD9C",
|
|
145
|
-
"submit --language python # Python\uC73C\uB85C \uC81C\uCD9C"
|
|
207
|
+
"submit --language python # Python\uC73C\uB85C \uC81C\uCD9C",
|
|
208
|
+
"submit --file solution-2.py # \uD2B9\uC815 \uD30C\uC77C\uB85C \uC81C\uCD9C",
|
|
209
|
+
"submit --index 2 # \uC778\uB371\uC2A4 2\uC758 \uD30C\uC77C\uB85C \uC81C\uCD9C"
|
|
146
210
|
]
|
|
147
211
|
})
|
|
148
212
|
], SubmitCommand);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
logger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UDZLWCHF.js";
|
|
5
5
|
|
|
6
6
|
// src/services/scraper.ts
|
|
7
7
|
import * as cheerio from "cheerio";
|
|
@@ -242,8 +242,8 @@ ${preContent}
|
|
|
242
242
|
break;
|
|
243
243
|
}
|
|
244
244
|
case "ul":
|
|
245
|
-
case "ol":
|
|
246
|
-
$node.
|
|
245
|
+
case "ol": {
|
|
246
|
+
$node.children("li").each((i, li) => {
|
|
247
247
|
const liContent = htmlToMarkdown($, $(li));
|
|
248
248
|
if (liContent) {
|
|
249
249
|
result += `- ${liContent}
|
|
@@ -252,6 +252,7 @@ ${preContent}
|
|
|
252
252
|
});
|
|
253
253
|
result += "\n";
|
|
254
254
|
break;
|
|
255
|
+
}
|
|
255
256
|
case "li":
|
|
256
257
|
result += htmlToMarkdown($, $node);
|
|
257
258
|
break;
|
|
@@ -10642,7 +10642,7 @@ function getSolvingDirPath(problemId, cwd = process.cwd(), _) {
|
|
|
10642
10642
|
}
|
|
10643
10643
|
|
|
10644
10644
|
// src/core/execution-context.ts
|
|
10645
|
-
import { access } from "fs/promises";
|
|
10645
|
+
import { access, stat } from "fs/promises";
|
|
10646
10646
|
import { readdir } from "fs/promises";
|
|
10647
10647
|
import { join as join3 } from "path";
|
|
10648
10648
|
async function directoryExists(dirPath) {
|
|
@@ -10708,13 +10708,79 @@ async function resolveLanguage(problemDir, override) {
|
|
|
10708
10708
|
}
|
|
10709
10709
|
return detectedLanguage;
|
|
10710
10710
|
}
|
|
10711
|
-
|
|
10712
|
-
const
|
|
10713
|
-
|
|
10714
|
-
|
|
10711
|
+
function parseSolutionIndex(filename) {
|
|
10712
|
+
const match = filename.match(/^solution-(\d+)\./);
|
|
10713
|
+
if (match) {
|
|
10714
|
+
const index = parseInt(match[1], 10);
|
|
10715
|
+
if (!isNaN(index) && index > 0) {
|
|
10716
|
+
return index;
|
|
10717
|
+
}
|
|
10718
|
+
}
|
|
10719
|
+
if (filename.match(/^solution\./)) {
|
|
10720
|
+
return 1;
|
|
10721
|
+
}
|
|
10722
|
+
return null;
|
|
10723
|
+
}
|
|
10724
|
+
async function findSolutionFiles(problemDir, language) {
|
|
10725
|
+
try {
|
|
10726
|
+
const files = await readdir(problemDir);
|
|
10727
|
+
const solutionFiles = [];
|
|
10728
|
+
for (const file of files) {
|
|
10729
|
+
if (!file.startsWith("solution")) {
|
|
10730
|
+
continue;
|
|
10731
|
+
}
|
|
10732
|
+
const detectedLang = detectLanguageFromFile(file);
|
|
10733
|
+
if (!detectedLang) {
|
|
10734
|
+
continue;
|
|
10735
|
+
}
|
|
10736
|
+
if (language && detectedLang !== language) {
|
|
10737
|
+
continue;
|
|
10738
|
+
}
|
|
10739
|
+
const index = parseSolutionIndex(file);
|
|
10740
|
+
if (index === null) {
|
|
10741
|
+
continue;
|
|
10742
|
+
}
|
|
10743
|
+
const filePath = join3(problemDir, file);
|
|
10744
|
+
try {
|
|
10745
|
+
const stats = await stat(filePath);
|
|
10746
|
+
solutionFiles.push({
|
|
10747
|
+
path: filePath,
|
|
10748
|
+
filename: file,
|
|
10749
|
+
index,
|
|
10750
|
+
language: detectedLang,
|
|
10751
|
+
mtime: stats.mtime
|
|
10752
|
+
});
|
|
10753
|
+
} catch {
|
|
10754
|
+
}
|
|
10755
|
+
}
|
|
10756
|
+
return solutionFiles;
|
|
10757
|
+
} catch {
|
|
10758
|
+
return [];
|
|
10759
|
+
}
|
|
10760
|
+
}
|
|
10761
|
+
async function findLatestSolutionFile(problemDir, language) {
|
|
10762
|
+
const files = await findSolutionFiles(problemDir, language);
|
|
10763
|
+
if (files.length === 0) {
|
|
10715
10764
|
throw new Error("solution.* \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
10716
10765
|
}
|
|
10717
|
-
|
|
10766
|
+
const latest = files.reduce((prev, current) => {
|
|
10767
|
+
return current.mtime > prev.mtime ? current : prev;
|
|
10768
|
+
});
|
|
10769
|
+
return latest.path;
|
|
10770
|
+
}
|
|
10771
|
+
async function findSolutionFileByIndex(problemDir, index, language) {
|
|
10772
|
+
const files = await findSolutionFiles(problemDir, language);
|
|
10773
|
+
const targetFile = files.find((f) => f.index === index);
|
|
10774
|
+
if (!targetFile) {
|
|
10775
|
+
const langText = language ? ` (\uC5B8\uC5B4: ${language})` : "";
|
|
10776
|
+
throw new Error(
|
|
10777
|
+
`\uC778\uB371\uC2A4 ${index}\uC758 solution \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.${langText}`
|
|
10778
|
+
);
|
|
10779
|
+
}
|
|
10780
|
+
return targetFile.path;
|
|
10781
|
+
}
|
|
10782
|
+
async function findSolutionFile(problemDir, language) {
|
|
10783
|
+
return findLatestSolutionFile(problemDir, language);
|
|
10718
10784
|
}
|
|
10719
10785
|
|
|
10720
10786
|
// src/core/base-command.tsx
|
|
@@ -11556,6 +11622,7 @@ export {
|
|
|
11556
11622
|
getSupportedLanguages,
|
|
11557
11623
|
getSupportedLanguagesString,
|
|
11558
11624
|
getLanguageConfig,
|
|
11625
|
+
detectLanguageFromFile,
|
|
11559
11626
|
TIER_COLORS,
|
|
11560
11627
|
getNextTierMinRating,
|
|
11561
11628
|
calculateTierProgress,
|
|
@@ -11570,6 +11637,8 @@ export {
|
|
|
11570
11637
|
getSolvingDirPath,
|
|
11571
11638
|
resolveProblemContext,
|
|
11572
11639
|
resolveLanguage,
|
|
11640
|
+
findSolutionFiles,
|
|
11641
|
+
findSolutionFileByIndex,
|
|
11573
11642
|
findSolutionFile,
|
|
11574
11643
|
Command,
|
|
11575
11644
|
source_default,
|
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
getUserTagRatings,
|
|
6
6
|
getUserTop100,
|
|
7
7
|
scrapeUserStats
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RPBTJIZG.js";
|
|
9
9
|
import {
|
|
10
10
|
findProjectRoot,
|
|
11
11
|
getArchiveDir,
|
|
12
12
|
getSolvingDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UDZLWCHF.js";
|
|
14
14
|
|
|
15
15
|
// src/hooks/use-user-stats.ts
|
|
16
16
|
import { existsSync } from "fs";
|
package/dist/commands/archive.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
ArchiveCommand,
|
|
4
4
|
ArchiveView,
|
|
5
5
|
archive_default
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-EQDQI3RC.js";
|
|
7
|
+
import "../chunk-V27D7TPW.js";
|
|
8
|
+
import "../chunk-UDZLWCHF.js";
|
|
9
9
|
export {
|
|
10
10
|
ArchiveCommand,
|
|
11
11
|
ArchiveView,
|
package/dist/commands/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useConfig
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-RHWSWBUG.js";
|
|
5
5
|
import {
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
getConfigMetadata,
|
|
11
11
|
icons,
|
|
12
12
|
logger
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-UDZLWCHF.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/config.tsx
|
|
16
16
|
import { StatusMessage, Select, TextInput, Alert, Badge } from "@inkjs/ui";
|
package/dist/commands/fetch.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
FetchCommand,
|
|
4
4
|
FetchView,
|
|
5
5
|
fetch_default
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-KUV37YXC.js";
|
|
7
|
+
import "../chunk-EQTRV4DN.js";
|
|
8
|
+
import "../chunk-RPBTJIZG.js";
|
|
9
|
+
import "../chunk-UDZLWCHF.js";
|
|
10
10
|
export {
|
|
11
11
|
FetchCommand,
|
|
12
12
|
FetchView,
|
package/dist/commands/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useInit
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-ZZ76HNFP.js";
|
|
5
5
|
import {
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
getSupportedLanguages,
|
|
11
11
|
getVersion,
|
|
12
12
|
icons
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-UDZLWCHF.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/init.tsx
|
|
16
16
|
import {
|
package/dist/commands/open.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
OpenCommand,
|
|
4
4
|
OpenView,
|
|
5
5
|
open_default
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-C3IOK7V4.js";
|
|
7
7
|
import "../chunk-3LR2NGRC.js";
|
|
8
8
|
import "../chunk-QGMWUOJ3.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-UDZLWCHF.js";
|
|
10
10
|
export {
|
|
11
11
|
OpenCommand,
|
|
12
12
|
OpenView,
|
package/dist/commands/run.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useRunSolution
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-CKSAX7KT.js";
|
|
5
5
|
import {
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
8
8
|
CommandDef,
|
|
9
9
|
__decorateClass,
|
|
10
10
|
defineFlags,
|
|
11
|
+
detectLanguageFromFile,
|
|
12
|
+
findSolutionFileByIndex,
|
|
13
|
+
findSolutionFiles,
|
|
11
14
|
getSupportedLanguagesString,
|
|
12
15
|
icons,
|
|
13
16
|
resolveLanguage,
|
|
14
17
|
resolveProblemContext
|
|
15
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-UDZLWCHF.js";
|
|
16
19
|
|
|
17
20
|
// src/commands/run.tsx
|
|
18
21
|
import { join } from "path";
|
|
@@ -31,19 +34,30 @@ var runFlagsSchema = {
|
|
|
31
34
|
type: "string",
|
|
32
35
|
shortFlag: "i",
|
|
33
36
|
description: "\uC785\uB825 \uD30C\uC77C \uC9C0\uC815 (\uC608: 1 \uB610\uB294 testcases/1/input.txt)"
|
|
37
|
+
},
|
|
38
|
+
file: {
|
|
39
|
+
type: "string",
|
|
40
|
+
shortFlag: "f",
|
|
41
|
+
description: "\uD2B9\uC815 solution \uD30C\uC77C \uC9C0\uC815 (\uC608: solution-2.py)"
|
|
42
|
+
},
|
|
43
|
+
index: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "\uC778\uB371\uC2A4\uB85C solution \uD30C\uC77C \uC9C0\uC815 (\uC608: 2)"
|
|
34
46
|
}
|
|
35
47
|
};
|
|
36
48
|
function RunView({
|
|
37
49
|
problemDir,
|
|
38
50
|
language,
|
|
39
51
|
inputFile,
|
|
40
|
-
onComplete
|
|
52
|
+
onComplete,
|
|
53
|
+
solutionPath
|
|
41
54
|
}) {
|
|
42
55
|
const { status, result, error } = useRunSolution({
|
|
43
56
|
problemDir,
|
|
44
57
|
language,
|
|
45
58
|
inputFile,
|
|
46
|
-
onComplete
|
|
59
|
+
onComplete,
|
|
60
|
+
solutionPath
|
|
47
61
|
});
|
|
48
62
|
if (status === "loading") {
|
|
49
63
|
if (!inputFile) {
|
|
@@ -115,6 +129,54 @@ function RunView({
|
|
|
115
129
|
var RunCommand = class extends Command {
|
|
116
130
|
async execute(args, flags) {
|
|
117
131
|
const context = await resolveProblemContext(args);
|
|
132
|
+
let solutionPath;
|
|
133
|
+
let detectedLanguage;
|
|
134
|
+
if (flags.file) {
|
|
135
|
+
const filePath = flags.file;
|
|
136
|
+
if (filePath.startsWith("/") || filePath.match(/^[A-Z]:/)) {
|
|
137
|
+
solutionPath = filePath;
|
|
138
|
+
} else {
|
|
139
|
+
solutionPath = join(context.archiveDir, filePath);
|
|
140
|
+
}
|
|
141
|
+
const fileName = solutionPath.split(/[/\\]/).pop() || "";
|
|
142
|
+
const fileLang = detectLanguageFromFile(fileName);
|
|
143
|
+
if (flags.language) {
|
|
144
|
+
detectedLanguage = flags.language;
|
|
145
|
+
} else if (fileLang) {
|
|
146
|
+
detectedLanguage = fileLang;
|
|
147
|
+
} else {
|
|
148
|
+
detectedLanguage = await resolveLanguage(
|
|
149
|
+
context.archiveDir,
|
|
150
|
+
flags.language
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
} else if (flags.index) {
|
|
154
|
+
const index = parseInt(flags.index, 10);
|
|
155
|
+
if (isNaN(index) || index < 1) {
|
|
156
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC778\uB371\uC2A4\uC785\uB2C8\uB2E4: ${flags.index}`);
|
|
157
|
+
}
|
|
158
|
+
if (flags.language) {
|
|
159
|
+
detectedLanguage = flags.language;
|
|
160
|
+
solutionPath = await findSolutionFileByIndex(
|
|
161
|
+
context.archiveDir,
|
|
162
|
+
index,
|
|
163
|
+
detectedLanguage
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
const files = await findSolutionFiles(context.archiveDir);
|
|
167
|
+
const targetFile = files.find((f) => f.index === index);
|
|
168
|
+
if (!targetFile) {
|
|
169
|
+
throw new Error(`\uC778\uB371\uC2A4 ${index}\uC758 solution \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`);
|
|
170
|
+
}
|
|
171
|
+
solutionPath = targetFile.path;
|
|
172
|
+
detectedLanguage = targetFile.language;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
detectedLanguage = await resolveLanguage(
|
|
176
|
+
context.archiveDir,
|
|
177
|
+
flags.language
|
|
178
|
+
);
|
|
179
|
+
}
|
|
118
180
|
let inputPath;
|
|
119
181
|
if (flags.input) {
|
|
120
182
|
const inputValue = flags.input;
|
|
@@ -129,14 +191,11 @@ var RunCommand = class extends Command {
|
|
|
129
191
|
inputPath = join(context.archiveDir, inputValue);
|
|
130
192
|
}
|
|
131
193
|
}
|
|
132
|
-
const detectedLanguage = await resolveLanguage(
|
|
133
|
-
context.archiveDir,
|
|
134
|
-
flags.language
|
|
135
|
-
);
|
|
136
194
|
await this.renderView(RunView, {
|
|
137
195
|
problemDir: context.archiveDir,
|
|
138
196
|
language: detectedLanguage,
|
|
139
|
-
inputFile: inputPath
|
|
197
|
+
inputFile: inputPath,
|
|
198
|
+
solutionPath
|
|
140
199
|
});
|
|
141
200
|
}
|
|
142
201
|
};
|
|
@@ -145,7 +204,9 @@ RunCommand = __decorateClass([
|
|
|
145
204
|
name: "run",
|
|
146
205
|
description: `\uCF54\uB4DC\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4 (\uD14C\uC2A4\uD2B8 \uC5C6\uC774).
|
|
147
206
|
- \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uCF54\uB4DC \uC2E4\uD589
|
|
148
|
-
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
207
|
+
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0 (\uAE30\uBCF8\uC801\uC73C\uB85C \uAC00\uC7A5 \uCD5C\uADFC \uC218\uC815\uB41C \uD30C\uC77C \uC0AC\uC6A9)
|
|
208
|
+
- \uC5EC\uB7EC \uB2F5\uC548 \uD30C\uC77C \uC9C0\uC6D0: solution-1.py, solution-2.py \uD615\uC2DD\uC73C\uB85C \uC5EC\uB7EC \uB2F5\uC548 \uAD00\uB9AC \uAC00\uB2A5
|
|
209
|
+
- --file \uB610\uB294 --index \uC635\uC158\uC73C\uB85C \uD2B9\uC815 \uD30C\uC77C \uC9C0\uC815 \uAC00\uB2A5
|
|
149
210
|
- --input \uC635\uC158\uC73C\uB85C \uC785\uB825 \uD30C\uC77C \uC9C0\uC815 \uAC00\uB2A5 (\uC608: testcases/1/input.txt)
|
|
150
211
|
- \uC635\uC158 \uC5C6\uC774 \uC2E4\uD589 \uC2DC \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC785\uB825 \uBC1B\uAE30
|
|
151
212
|
- \uD14C\uC2A4\uD2B8 \uCF00\uC774\uC2A4 \uAC80\uC99D \uC5C6\uC774 \uB2E8\uC21C \uC2E4\uD589
|
|
@@ -158,7 +219,9 @@ RunCommand = __decorateClass([
|
|
|
158
219
|
"run 1000 # 1000\uBC88 \uBB38\uC81C \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC2E4\uD589",
|
|
159
220
|
"run --language python # Python\uC73C\uB85C \uD45C\uC900 \uC785\uB825\uC73C\uB85C \uC2E4\uD589",
|
|
160
221
|
"run --input 1 # \uD14C\uC2A4\uD2B8 \uCF00\uC774\uC2A4 1\uBC88 \uC0AC\uC6A9",
|
|
161
|
-
"run --input testcases/1/input.txt # \uC804\uCCB4 \uACBD\uB85C\uB85C \uC785\uB825 \uD30C\uC77C \uC9C0\uC815"
|
|
222
|
+
"run --input testcases/1/input.txt # \uC804\uCCB4 \uACBD\uB85C\uB85C \uC785\uB825 \uD30C\uC77C \uC9C0\uC815",
|
|
223
|
+
"run --file solution-2.py # \uD2B9\uC815 \uD30C\uC77C\uB85C \uC2E4\uD589",
|
|
224
|
+
"run --index 2 # \uC778\uB371\uC2A4 2\uC758 \uD30C\uC77C\uB85C \uC2E4\uD589"
|
|
162
225
|
]
|
|
163
226
|
})
|
|
164
227
|
], RunCommand);
|
package/dist/commands/search.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
SubmitView
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LGSE3IEE.js";
|
|
5
5
|
import {
|
|
6
6
|
TestView
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-K5KMUKVD.js";
|
|
8
|
+
import "../chunk-WJQ622GN.js";
|
|
9
9
|
import "../chunk-MZUR7SER.js";
|
|
10
10
|
import {
|
|
11
11
|
ArchiveView
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-EQDQI3RC.js";
|
|
13
|
+
import "../chunk-V27D7TPW.js";
|
|
14
|
+
import "../chunk-RHWSWBUG.js";
|
|
15
15
|
import {
|
|
16
16
|
FetchView
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-KUV37YXC.js";
|
|
18
|
+
import "../chunk-EQTRV4DN.js";
|
|
19
19
|
import {
|
|
20
20
|
fetchWithRetry,
|
|
21
21
|
getProblem,
|
|
22
22
|
searchProblems
|
|
23
|
-
} from "../chunk-
|
|
24
|
-
import "../chunk-
|
|
23
|
+
} from "../chunk-RPBTJIZG.js";
|
|
24
|
+
import "../chunk-ZZ76HNFP.js";
|
|
25
25
|
import {
|
|
26
26
|
OpenView
|
|
27
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-C3IOK7V4.js";
|
|
28
28
|
import "../chunk-3LR2NGRC.js";
|
|
29
29
|
import "../chunk-QGMWUOJ3.js";
|
|
30
|
-
import "../chunk-
|
|
30
|
+
import "../chunk-CKSAX7KT.js";
|
|
31
31
|
import {
|
|
32
32
|
Command,
|
|
33
33
|
CommandBuilder,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
logger,
|
|
45
45
|
resolveLanguage,
|
|
46
46
|
source_default
|
|
47
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-UDZLWCHF.js";
|
|
48
48
|
|
|
49
49
|
// src/commands/search.tsx
|
|
50
50
|
import { existsSync } from "fs";
|
|
@@ -282,7 +282,7 @@ function ProblemActionView({
|
|
|
282
282
|
OpenView,
|
|
283
283
|
{
|
|
284
284
|
problemId,
|
|
285
|
-
problemDir:
|
|
285
|
+
problemDir: problemDir || void 0,
|
|
286
286
|
mode: "editor",
|
|
287
287
|
onComplete
|
|
288
288
|
}
|
package/dist/commands/stats.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useUserStats
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-WJQ622GN.js";
|
|
5
|
+
import "../chunk-RPBTJIZG.js";
|
|
6
6
|
import {
|
|
7
7
|
Command,
|
|
8
8
|
CommandBuilder,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
icons,
|
|
20
20
|
logger,
|
|
21
21
|
source_default
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-UDZLWCHF.js";
|
|
23
23
|
|
|
24
24
|
// src/commands/stats.tsx
|
|
25
25
|
import { Alert, Spinner } from "@inkjs/ui";
|
package/dist/commands/submit.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
SubmitCommand,
|
|
4
4
|
SubmitView,
|
|
5
5
|
submit_default
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-LGSE3IEE.js";
|
|
7
7
|
import "../chunk-MZUR7SER.js";
|
|
8
8
|
import "../chunk-QGMWUOJ3.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-UDZLWCHF.js";
|
|
10
10
|
export {
|
|
11
11
|
SubmitCommand,
|
|
12
12
|
SubmitView,
|
package/dist/commands/test.js
CHANGED
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
TestCommand,
|
|
4
4
|
TestView,
|
|
5
5
|
test_default
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-K5KMUKVD.js";
|
|
7
|
+
import "../chunk-WJQ622GN.js";
|
|
8
8
|
import "../chunk-MZUR7SER.js";
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-V27D7TPW.js";
|
|
10
|
+
import "../chunk-RHWSWBUG.js";
|
|
11
|
+
import "../chunk-EQTRV4DN.js";
|
|
12
|
+
import "../chunk-RPBTJIZG.js";
|
|
13
|
+
import "../chunk-ZZ76HNFP.js";
|
|
14
14
|
import "../chunk-3LR2NGRC.js";
|
|
15
15
|
import "../chunk-QGMWUOJ3.js";
|
|
16
|
-
import "../chunk-
|
|
17
|
-
import "../chunk-
|
|
16
|
+
import "../chunk-CKSAX7KT.js";
|
|
17
|
+
import "../chunk-UDZLWCHF.js";
|
|
18
18
|
export {
|
|
19
19
|
TestCommand,
|
|
20
20
|
TestView,
|
package/dist/index.js
CHANGED