extension.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. 'use strict';
  2. // The module 'vscode' contains the VS Code extensibility API
  3. // Import the module and reference it with the alias vscode in your code below
  4. import * as vscode from 'vscode';
  5. import { existsSync, writeFileSync, readdirSync } from 'fs';
  6. import { join, dirname, extname } from 'path';
  7. import { SITES } from './conn';
  8. import { newContestFromId, testSolution, veredictName, stressSolution, upgradeArena, newProblemFromId, removeExtension } from './core';
  9. import { Veredict } from './types';
  10. /**
  11. * TODO: Allow custom checker easily
  12. * TODO: Add several checkers and try to infer which is the correct! [*]
  13. * TODO: Smart ID detection while parsing ContestId & ProblemId [*]
  14. * TODO: Smart generator [*]
  15. * TODO: Find great name/slogan!!! other than acmhelper Competitive Programming made simple
  16. * TODO: Implement parser for codeforces to test on real cases
  17. * TODO: Learn how to move static files from `src` to `out`.
  18. * TODO: Allow programming in other languages than c++
  19. *
  20. * [*] Machine Learning?
  21. */
  22. const TESTCASES = 'testcases';
  23. function isProblemFolder(path: string) {
  24. return existsSync(join(path, 'sol.cpp')) &&
  25. existsSync(join(path, 'attic'));
  26. }
  27. function currentProblem() {
  28. // Try to find the problem using current open file
  29. if (vscode.window.activeTextEditor){
  30. let path = vscode.window.activeTextEditor.document.uri.path;
  31. const MAX_DEPTH = 3;
  32. for (let i = 0; i < MAX_DEPTH && !isProblemFolder(path); i++) {
  33. path = dirname(path);
  34. }
  35. if (isProblemFolder(path)){
  36. return path;
  37. }
  38. }
  39. // Try to find the problem using the current open workspace folder
  40. if (vscode.workspace.workspaceFolders !== undefined){
  41. let path = vscode.workspace.workspaceFolders[0].uri.path;
  42. const MAX_DEPTH = 1;
  43. for (let i = 0; i < MAX_DEPTH && !isProblemFolder(path); i++) {
  44. path = dirname(path);
  45. }
  46. if (isProblemFolder(path)){
  47. return path;
  48. }
  49. }
  50. // Problem not found
  51. return undefined;
  52. }
  53. function quickPickSites() {
  54. let sites: any[] = [];
  55. SITES.forEach(value => {
  56. sites.push({
  57. "label" : value.name,
  58. "target" : value.name,
  59. "description" : value.description,
  60. });
  61. });
  62. return sites;
  63. }
  64. // Create a new problem
  65. async function addProblem() {
  66. if (vscode.workspace.workspaceFolders === undefined) {
  67. vscode.window.showErrorMessage("Open the folder that will contain the problem.");
  68. return;
  69. }
  70. let path = vscode.workspace.workspaceFolders[0].uri.path;
  71. let site_info = await vscode.window.showQuickPick(quickPickSites(), { placeHolder: 'Select contest site' });
  72. if (site_info === undefined){
  73. vscode.window.showErrorMessage("Site not provided.");
  74. return;
  75. }
  76. let site = site_info.target;
  77. // TODO: IMPORTANT: Provide custom problem id example in placeholder per different site
  78. let id = await vscode.window.showInputBox({placeHolder: "Problem ID"});
  79. if (id === undefined){
  80. vscode.window.showErrorMessage("Problem ID not provided.");
  81. return;
  82. }
  83. path = join(path, `${id}`);
  84. newProblemFromId(path, site, id);
  85. await vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(path));
  86. // TODO: How can I have access to new proccess created using `openFolder`?
  87. // Just want to run two commands below
  88. // await vscode.commands.executeCommand("vscode.open", vscode.Uri.file("sol.cpp"));
  89. // vscode.window.showInformationMessage(`Add problem ${site}/${id} at ${path}`);
  90. }
  91. async function addContest() {
  92. if (vscode.workspace.workspaceFolders === undefined) {
  93. vscode.window.showErrorMessage("Open the folder that will contain the contest.");
  94. return;
  95. }
  96. let path = vscode.workspace.workspaceFolders[0].uri.path;
  97. let site_info = await vscode.window.showQuickPick(quickPickSites(), { placeHolder: 'Select contest site' });
  98. if (site_info === undefined){
  99. vscode.window.showErrorMessage("Site not provided.");
  100. return;
  101. }
  102. let site = site_info.target;
  103. let id = undefined;
  104. if (site === "personal"){
  105. let name= await vscode.window.showInputBox({placeHolder: "Contest Name"});
  106. if (name === undefined){
  107. vscode.window.showErrorMessage("Name not provided.");
  108. return;
  109. }
  110. path = join(path, name);
  111. let probCountStr = await vscode.window.showInputBox({placeHolder: "Number of problems"});
  112. if (name === undefined){
  113. vscode.window.showErrorMessage("Number of problems not provided.");
  114. return;
  115. }
  116. id = Number.parseInt(probCountStr!);
  117. }
  118. else{
  119. // TODO: IMPORTANT: Provide custom contest id example in placeholder per different site
  120. id = await vscode.window.showInputBox({placeHolder: "Contest ID"});
  121. if (id === undefined){
  122. vscode.window.showErrorMessage("Contest ID not provided.");
  123. return;
  124. }
  125. path = join(path, `${id}`);
  126. }
  127. newContestFromId(path, site, id);
  128. vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(path));
  129. }
  130. async function debugTestcase(path: string, tcId: string){
  131. // Change editor layout to show failing test
  132. await vscode.commands.executeCommand("vscode.setEditorLayout", { orientation: 0, groups: [{ groups: [{}], size: 0.5 }, { groups: [{}, {}, {}], size: 0.5 }] });
  133. let sol = join(path, `sol.cpp`);
  134. let inp = join(path, TESTCASES, `${tcId}.in`);
  135. let out = join(path, TESTCASES, `${tcId}.out`);
  136. let cur = join(path, TESTCASES, `${tcId}.cur`);
  137. // TODO: How to clear opened tabs?
  138. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(sol), vscode.ViewColumn.One);
  139. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(inp), vscode.ViewColumn.Two);
  140. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(out), vscode.ViewColumn.Three);
  141. // This file might not exist!
  142. if (existsSync(cur)){
  143. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(cur), vscode.ViewColumn.Four);
  144. }
  145. }
  146. async function runSolution(){
  147. let path = currentProblem();
  148. if (path === undefined){
  149. vscode.window.showErrorMessage("No active problem");
  150. return;
  151. }
  152. let result = testSolution(path);
  153. if (result.status === Veredict.OK){
  154. vscode.window.showInformationMessage("OK");
  155. }
  156. else{
  157. vscode.window.showErrorMessage(`${veredictName(result.status)} on test ${result.failTcId}`);
  158. debugTestcase(path, result.failTcId!);
  159. }
  160. }
  161. async function openTestcase() {
  162. let path = currentProblem();
  163. if (path === undefined){
  164. vscode.window.showErrorMessage("No active problem");
  165. return;
  166. }
  167. let tcs: any[] = [];
  168. // Read testcases
  169. readdirSync(join(path, TESTCASES)).
  170. filter( function (tcpath) {
  171. return extname(tcpath) === '.in';}).
  172. map( function(tcpath) {
  173. let name = removeExtension(tcpath);
  174. tcs.push({
  175. 'label' : name,
  176. 'target' : name,
  177. });
  178. });
  179. let tc = await vscode.window.showQuickPick(tcs, { placeHolder: 'Select testcase' });
  180. if (tc !== undefined){
  181. let inp = join(path, TESTCASES, `${tc.target}.in`);
  182. let out = join(path, TESTCASES, `${tc.target}.out`);
  183. await vscode.commands.executeCommand("vscode.setEditorLayout", { orientation: 0, groups: [{}, {}]});
  184. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(inp), vscode.ViewColumn.One);
  185. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(out), vscode.ViewColumn.Two);
  186. }
  187. }
  188. async function addTestcase() {
  189. let path = currentProblem();
  190. if (path === undefined){
  191. vscode.window.showErrorMessage("No active problem");
  192. return;
  193. }
  194. let index = 0;
  195. while (existsSync(join(path, TESTCASES, `${index}.hand.in`))){
  196. index += 1;
  197. }
  198. let inp = join(path, TESTCASES, `${index}.hand.in`);
  199. let out = join(path, TESTCASES, `${index}.hand.out`);
  200. writeFileSync(inp, "");
  201. writeFileSync(out, "");
  202. await vscode.commands.executeCommand("vscode.setEditorLayout", { orientation: 0, groups: [{}, {}]});
  203. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(inp), vscode.ViewColumn.One);
  204. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(out), vscode.ViewColumn.Two);
  205. }
  206. async function coding() {
  207. let path = currentProblem();
  208. if (path === undefined){
  209. vscode.window.showErrorMessage("No active problem");
  210. return;
  211. }
  212. await vscode.commands.executeCommand("vscode.setEditorLayout", { groups: [{}]});
  213. let sol = join(path, `sol.cpp`);
  214. await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(sol), vscode.ViewColumn.One);
  215. }
  216. // TODO: Show time that the program took when it's ok
  217. async function stress(){
  218. let path = currentProblem();
  219. if (path === undefined){
  220. vscode.window.showErrorMessage("No active problem");
  221. return;
  222. }
  223. let result = stressSolution(path);
  224. if (result.status === Veredict.OK){
  225. vscode.window.showInformationMessage("OK");
  226. }
  227. else{
  228. vscode.window.showErrorMessage(`${veredictName(result.status)} on test ${result.failTcId}`);
  229. debugTestcase(path, result.failTcId!);
  230. }
  231. }
  232. async function upgrade(){
  233. let path = currentProblem();
  234. if (path === undefined){
  235. vscode.window.showErrorMessage("No active problem");
  236. return;
  237. }
  238. upgradeArena(path);
  239. }
  240. // this method is called when your extension is activated
  241. // your extension is activated the very first time the command is executed
  242. export function activate(context: vscode.ExtensionContext) {
  243. console.log('Congratulations, your extension "acmhelper-vscode" is now active!');
  244. let addProblemCommand = vscode.commands.registerCommand('extension.addProblem', addProblem);
  245. let addContestCommand = vscode.commands.registerCommand('extension.addContest', addContest);
  246. let runSolutionCommand = vscode.commands.registerCommand('extension.runSolution', runSolution);
  247. let openTestcaseCommand = vscode.commands.registerCommand('extension.openTestcase', openTestcase);
  248. let addTestcaseCommand = vscode.commands.registerCommand('extension.addTestcase', addTestcase);
  249. let codingCommand = vscode.commands.registerCommand('extension.coding', coding);
  250. let stressCommand = vscode.commands.registerCommand('extension.stress', stress);
  251. let upgradeCommand = vscode.commands.registerCommand('extension.upgrade', upgrade);
  252. context.subscriptions.push(addProblemCommand);
  253. context.subscriptions.push(addContestCommand);
  254. context.subscriptions.push(runSolutionCommand);
  255. context.subscriptions.push(openTestcaseCommand);
  256. context.subscriptions.push(addTestcaseCommand);
  257. context.subscriptions.push(codingCommand);
  258. context.subscriptions.push(stressCommand);
  259. context.subscriptions.push(upgradeCommand);
  260. }
  261. // this method is called when your extension is deactivated
  262. export function deactivate() {
  263. }