Browse Source

Few updates and add TODOs

* Users can create empty contest to be filled manually. Rename `personal` to `empty`.
* New configuration to set solutions folder. Important to set it before using the tool.
* Create contest with better names.
Marcelo Fornet 6 years ago
parent
commit
44a3af75ec
10 changed files with 149 additions and 87 deletions
  1. 6 0
      CHANGELOG.md
  2. 10 11
      README.md
  3. 25 19
      package.json
  4. 37 3
      src/conn.ts
  5. 16 12
      src/core.ts
  6. 19 33
      src/extension.ts
  7. 4 1
      src/parsers/codeforces.ts
  8. 5 3
      src/test/extension.test.ts
  9. 4 2
      src/types.ts
  10. 23 3
      todo.md

+ 6 - 0
CHANGELOG.md

@@ -2,6 +2,12 @@
 
 
 All notable updates and fixes to the "acmX" extension will be documented in this file.
 All notable updates and fixes to the "acmX" extension will be documented in this file.
 
 
+## 0.1.5
+
+* Users can create empty contest to be filled manually. Rename `personal` to `empty`.
+* New configuration to set solutions folder. Important to set it before using the tool.
+* Create contest with better names.
+
 ## 0.1.4
 ## 0.1.4
 
 
 * Fix problem in Codeforces parser
 * Fix problem in Codeforces parser

+ 10 - 11
README.md

@@ -24,30 +24,29 @@
 
 
 * Add more testcases than provided in statement using `Add Test Case`, or modify and see existing testcases by calling `Open Test Case`.
 * Add more testcases than provided in statement using `Add Test Case`, or modify and see existing testcases by calling `Open Test Case`.
 
 
-* If your solution keep failing you can stress it using a generator and a brute solution. Call `Upgrage` to create both generator (`attic/gen.py`) and correct (`brute.cpp`) programs. Right now generator must be written in python, and correct program must be written in C++. After both codes are ready just call `Stress` and your original code will be tested on random test cases from generator against correct solution.
+* If your solution keep failing you can stress it using a generator and a brute solution. Call `Upgrage` to create both generator (`gen.py`) and correct (`brute.cpp`) programs. Right now generator must be written in python, and correct program must be written in C++. After both codes are ready just call `Stress` and your original code will be tested on random test cases from generator against correct solution.
 
 
 The environment structure is the following:
 The environment structure is the following:
 
 
 ```file
 ```file
-    contest/
-        problemA/
+    round-525/
+        A/
             sol.cpp
             sol.cpp
             brute.cpp
             brute.cpp
-            attic/
-                gen.py
-                ...
+            gen.py
+            attic/...
             testcases/
             testcases/
                 1.in
                 1.in
                 1.out
                 1.out
                 1.real
                 1.real
                 ...
                 ...
-        problemB/...
-        problemC/...
-        problemD/...
-        problemE/...
+        B/...
+        C/...
+        D/...
+        E/...
 ```
 ```
 
 
-Certainly **acmX** can be (and hopefully will be) extended so that it fits everyones pipeline. If **acmX** almost fit yours, feel free to improve it and make a PR! I'll be happy to hear from you and give you support.
+Certainly **acmX** can be (and hopefully will be) extended so that it fits everyones pipeline. If **acmX** almost fit yours, feel free to improve it and make a PR! I'll be happy to hear from you and give you support. If you find any issue report it at [github issue tracker](https://github.com/mfornet/acmx/issues).
 
 
 ## Default template is awful, how can I change it
 ## Default template is awful, how can I change it
 
 

+ 25 - 19
package.json

@@ -16,16 +16,16 @@
         "Other"
         "Other"
     ],
     ],
     "activationEvents": [
     "activationEvents": [
-        "onCommand:extension.addProblem",
-        "onCommand:extension.addContest",
-        "onCommand:extension.runSolution",
-        "onCommand:extension.openTestcase",
-        "onCommand:extension.addTestcase",
-        "onCommand:extension.coding",
-        "onCommand:extension.stress",
-        "onCommand:extension.upgrade",
-        "onCommand:extension.compile",
-        "onCommand:extension.debugTest"
+        "onCommand:acmx.addProblem",
+        "onCommand:acmx.addContest",
+        "onCommand:acmx.runSolution",
+        "onCommand:acmx.openTestcase",
+        "onCommand:acmx.addTestcase",
+        "onCommand:acmx.coding",
+        "onCommand:acmx.stress",
+        "onCommand:acmx.upgrade",
+        "onCommand:acmx.compile",
+        "onCommand:acmx.debugTest"
     ],
     ],
     "main": "./out/extension",
     "main": "./out/extension",
     "contributes": {
     "contributes": {
@@ -58,45 +58,51 @@
 						"default": "",
 						"default": "",
 						"description": "Path to template file. Leave empty to use default template.",
 						"description": "Path to template file. Leave empty to use default template.",
 						"scope": "resource"
 						"scope": "resource"
+					},
+					"acmx.configuration.solutionPath": {
+                        "type": "string",
+						"default": ".",
+						"description": "Path to folder where contest will be created and stored. To set active workspace use `.`",
+						"scope": "resource"
 					}
 					}
 				}
 				}
             }
             }
         ],
         ],
         "commands": [
         "commands": [
             {
             {
-                "command": "extension.addProblem",
+                "command": "acmx.addProblem",
                 "title": "ACMX: New Problem"
                 "title": "ACMX: New Problem"
             },
             },
             {
             {
-                "command": "extension.addContest",
+                "command": "acmx.addContest",
                 "title": "ACMX: New Contest"
                 "title": "ACMX: New Contest"
             },
             },
             {
             {
-                "command": "extension.runSolution",
+                "command": "acmx.runSolution",
                 "title": "ACMX: Run"
                 "title": "ACMX: Run"
             },
             },
             {
             {
-                "command": "extension.openTestcase",
+                "command": "acmx.openTestcase",
                 "title": "ACMX: Open Test Case"
                 "title": "ACMX: Open Test Case"
             },
             },
             {
             {
-                "command": "extension.addTestcase",
+                "command": "acmx.addTestcase",
                 "title": "ACMX: Add Test Case"
                 "title": "ACMX: Add Test Case"
             },
             },
             {
             {
-                "command": "extension.coding",
+                "command": "acmx.coding",
                 "title": "ACMX: View: Code"
                 "title": "ACMX: View: Code"
             },
             },
             {
             {
-                "command": "extension.stress",
+                "command": "acmx.stress",
                 "title": "ACMX: Stress"
                 "title": "ACMX: Stress"
             },
             },
             {
             {
-                "command": "extension.upgrade",
+                "command": "acmx.upgrade",
                 "title": "ACMX: Upgrade"
                 "title": "ACMX: Upgrade"
             },
             },
             {
             {
-                "command": "extension.compile",
+                "command": "acmx.compile",
                 "title": "ACMX: Compile"
                 "title": "ACMX: Compile"
             }
             }
         ]
         ]

+ 37 - 3
src/conn.ts

@@ -6,7 +6,7 @@ import { CODEFORCES } from "./parsers/codeforces";
  *
  *
  * Util to create personal problems and debug this tool.
  * Util to create personal problems and debug this tool.
  */
  */
-const PERSONAL = new SiteDescription(
+export const PERSONAL = new SiteDescription(
     "personal",
     "personal",
     "Not a site. Custom problems and contest.",
     "Not a site. Custom problems and contest.",
     "Contest name",
     "Contest name",
@@ -20,18 +20,52 @@ const PERSONAL = new SiteDescription(
             problems.push(new Problem(`P${i+1}`, `P${i+1}`, ["0\n", "2\n", "9\n"], ["2\n", "4\n", "11\n"]));
             problems.push(new Problem(`P${i+1}`, `P${i+1}`, ["0\n", "2\n", "9\n"], ["2\n", "4\n", "11\n"]));
         }
         }
 
 
-        return new Contest(problems);
+        return new Contest("personal", problems);
     },
     },
     async problemId => {
     async problemId => {
         return new Problem(problemId, problemId, ["0\n", "2\n", "9\n"], ["2\n", "4\n", "11\n"]);
         return new Problem(problemId, problemId, ["0\n", "2\n", "9\n"], ["2\n", "4\n", "11\n"]);
     }
     }
 );
 );
 
 
+/**
+ * Not a real site.
+ *
+ * Create an empty contest that will be filled by user manually.
+ */
+const EMPTY = new SiteDescription(
+    "empty",
+    "Not a site. Create empty problems",
+    "Contest name",
+    "Problem name",
+    async problemId => {
+        // Parse problemId. It is of the form problem-name-10
+        // Where `problem-name` is current name and `10` is number of problems
+        let args = problemId.split('-');
+
+        let numProblems =  args[args.length - 1];
+        let total = Number.parseInt(numProblems);
+
+        args.pop();
+        let name = args.join('-');
+
+        let problems = [];
+
+        for (let i = 0; i < total; i++) {
+            problems.push(new Problem(`P${i+1}`, `P${i+1}`, [], []));
+        }
+
+        return new Contest(name, problems);
+    },
+    async problemId => {
+        return new Problem(problemId, problemId, [], []);
+    }
+);
+
 /**
 /**
  * Register a new site creating an entry in this dictionary.
  * Register a new site creating an entry in this dictionary.
  */
  */
 export const SITES: SiteDescription[] = [
 export const SITES: SiteDescription[] = [
-    PERSONAL,
+    EMPTY,
     CODEFORCES,
     CODEFORCES,
 ];
 ];
 
 

+ 16 - 12
src/core.ts

@@ -11,7 +11,7 @@ export const ATTIC = 'attic';
 const SRC = dirname(__filename);
 const SRC = dirname(__filename);
 
 
 export function getTimeout(){
 export function getTimeout(){
-    let timeout: number|undefined = vscode.workspace.getConfiguration('acmx.run').get('timeLimit');
+    let timeout: number|undefined = vscode.workspace.getConfiguration('acmx.run', null).get('timeLimit');
     timeout = timeout! * 1000;
     timeout = timeout! * 1000;
     return timeout;
     return timeout;
 }
 }
@@ -109,7 +109,7 @@ export function newArena(path: string){
     let attic = join(path, ATTIC);
     let attic = join(path, ATTIC);
     createFolder(attic);
     createFolder(attic);
 
 
-    let templatePath: string | undefined = vscode.workspace.getConfiguration('acmx.configuration').get('templatePath');
+    let templatePath: string | undefined = vscode.workspace.getConfiguration('acmx.configuration', null).get('templatePath');
 
 
     if (templatePath! === ""){
     if (templatePath! === ""){
         templatePath = join(SRC, 'static', 'sol.cpp');
         templatePath = join(SRC, 'static', 'sol.cpp');
@@ -164,7 +164,7 @@ export function upgradeArena(path: string) {
         copyFileSync(join(SRC, 'static', 'sol.cpp'), brute);
         copyFileSync(join(SRC, 'static', 'sol.cpp'), brute);
     }
     }
 
 
-    let generator = join(path, ATTIC, 'gen.py');
+    let generator = join(path, 'gen.py');
 
 
     if (!existsSync(generator)){
     if (!existsSync(generator)){
         // Create generator
         // Create generator
@@ -215,12 +215,16 @@ function newContest(path: string, contest: Contest){
 /**
 /**
  * Create a contest
  * Create a contest
  *
  *
- * @param contestId Can be a number if the site is `personal` and this number denote number of problems
+ * @param contestId Id of the contest that user want to retrieve.
  */
  */
 export async function newContestFromId(path: string, site: SiteDescription, contestId: string){
 export async function newContestFromId(path: string, site: SiteDescription, contestId: string){
-    createFolder(path);
     let contest = await site.contestParser(contestId);
     let contest = await site.contestParser(contestId);
-    newContest(path, contest);
+    let contestPath = join(path, site.name, contest.name);
+
+    createFolder(contestPath);
+
+    newContest(contestPath, contest);
+    return contestPath;
 }
 }
 
 
 /**
 /**
@@ -250,9 +254,11 @@ export function timedRun(path: string, tcName: string, timeout: number){
         killSignal: "SIGTERM"
         killSignal: "SIGTERM"
     });
     });
 
 
+    let spanTime = new Date().getTime() - startTime;
+
     // Check if an error happened
     // Check if an error happened
     if (xresult.status !== 0){
     if (xresult.status !== 0){
-        if (xresult.error === undefined){
+        if (spanTime < timeout){
             return new TestcaseResult(Veredict.RTE);
             return new TestcaseResult(Veredict.RTE);
         }
         }
         else{
         else{
@@ -260,8 +266,6 @@ export function timedRun(path: string, tcName: string, timeout: number){
         }
         }
     }
     }
 
 
-    let spanTime = new Date().getTime() - startTime;
-
     // Check output is ok
     // Check output is ok
     let currentFd = openSync(tcCurrent, 'w');
     let currentFd = openSync(tcCurrent, 'w');
     writeSync(currentFd, xresult.stdout);
     writeSync(currentFd, xresult.stdout);
@@ -278,7 +282,7 @@ export function timedRun(path: string, tcName: string, timeout: number){
 }
 }
 
 
 export function compileCode(pathCode: string, pathOutput: string){
 export function compileCode(pathCode: string, pathOutput: string){
-    let instruction: string | undefined = vscode.workspace.getConfiguration('acmx.execution').get('compileCpp');
+    let instruction: string | undefined = vscode.workspace.getConfiguration('acmx.execution', null).get('compileCpp');
     let splitedInstruction = instruction!.split(' ');
     let splitedInstruction = instruction!.split(' ');
 
 
     for (let i = 0; i < splitedInstruction.length; ++i){
     for (let i = 0; i < splitedInstruction.length; ++i){
@@ -351,8 +355,8 @@ export function testSolution(path: string){
 }
 }
 
 
 function generateTestcase(path: string){
 function generateTestcase(path: string){
-    let python: string | undefined = vscode.workspace.getConfiguration('acmx.execution').get('pythonPath');
-    let genResult = child_process.spawnSync(python!, [join(path, ATTIC, 'gen.py')]);
+    let python: string | undefined = vscode.workspace.getConfiguration('acmx.execution', null).get('pythonPath');
+    let genResult = child_process.spawnSync(python!, [join(path, 'gen.py')]);
 
 
     let currentFd = openSync(join(path, TESTCASES, 'gen.in'), 'w');
     let currentFd = openSync(join(path, TESTCASES, 'gen.in'), 'w');
     writeSync(currentFd, genResult.stdout);
     writeSync(currentFd, genResult.stdout);

+ 19 - 33
src/extension.ts

@@ -26,13 +26,6 @@ function quickPickSites() {
 
 
 // Create a new problem
 // Create a new problem
 async function addProblem() {
 async function addProblem() {
-    if (vscode.workspace.workspaceFolders === undefined) {
-        vscode.window.showErrorMessage("Open the folder that will contain the problem.");
-        return;
-    }
-
-    let path = vscode.workspace.workspaceFolders[0].uri.path;
-
     let site_info = await vscode.window.showQuickPick(quickPickSites(), { placeHolder: 'Select contest site' });
     let site_info = await vscode.window.showQuickPick(quickPickSites(), { placeHolder: 'Select contest site' });
 
 
     if (site_info === undefined){
     if (site_info === undefined){
@@ -49,6 +42,9 @@ async function addProblem() {
         return;
         return;
     }
     }
 
 
+    let path: string | undefined = vscode.workspace.getConfiguration('acmx.configuration', null).get('solutionPath');
+    path = join(path!, site.name, 'single');
+
     let problemPath = await newProblemFromId(path, site, id);
     let problemPath = await newProblemFromId(path, site, id);
 
 
     await vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(problemPath));
     await vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(problemPath));
@@ -59,13 +55,7 @@ async function addProblem() {
 }
 }
 
 
 async function addContest() {
 async function addContest() {
-    if (vscode.workspace.workspaceFolders === undefined) {
-        vscode.window.showErrorMessage("Open the folder that will contain the contest.");
-        return;
-    }
-
-    let path = vscode.workspace.workspaceFolders[0].uri.path;
-
+    let path: string | undefined = vscode.workspace.getConfiguration('acmx.configuration', null).get('solutionPath');
     let site_info = await vscode.window.showQuickPick(quickPickSites(), { placeHolder: 'Select contest site' });
     let site_info = await vscode.window.showQuickPick(quickPickSites(), { placeHolder: 'Select contest site' });
 
 
     if (site_info === undefined){
     if (site_info === undefined){
@@ -76,7 +66,7 @@ async function addContest() {
     let site = getSite(site_info.target);
     let site = getSite(site_info.target);
     let id = undefined;
     let id = undefined;
 
 
-    if (site.name === "personal"){
+    if (site.name === "empty"){
         let name= await vscode.window.showInputBox({placeHolder: site.contestIdPlaceholder});
         let name= await vscode.window.showInputBox({placeHolder: site.contestIdPlaceholder});
 
 
         if (name === undefined){
         if (name === undefined){
@@ -84,8 +74,6 @@ async function addContest() {
             return;
             return;
         }
         }
 
 
-        path = join(path, name);
-
         let probCountStr = await vscode.window.showInputBox({placeHolder: "Number of problems"});
         let probCountStr = await vscode.window.showInputBox({placeHolder: "Number of problems"});
 
 
         if (name === undefined){
         if (name === undefined){
@@ -93,7 +81,7 @@ async function addContest() {
             return;
             return;
         }
         }
 
 
-        id = probCountStr!;
+        id = name + '-' + probCountStr!;
     }
     }
     else{
     else{
         id = await vscode.window.showInputBox({placeHolder: site.contestIdPlaceholder});
         id = await vscode.window.showInputBox({placeHolder: site.contestIdPlaceholder});
@@ -102,13 +90,11 @@ async function addContest() {
             vscode.window.showErrorMessage("Contest ID not provided.");
             vscode.window.showErrorMessage("Contest ID not provided.");
             return;
             return;
         }
         }
-
-        path = join(path, `${id}`);
     }
     }
 
 
-    await newContestFromId(path, site, id);
+    let contestPath = await newContestFromId(path!, site, id);
 
 
-    vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(path));
+    vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(contestPath));
 }
 }
 
 
 async function debugTestcase(path: string, tcId: string){
 async function debugTestcase(path: string, tcId: string){
@@ -286,17 +272,17 @@ async function debugTest(){
 // this method is called when your extension is activated
 // this method is called when your extension is activated
 // your extension is activated the very first time the command is executed
 // your extension is activated the very first time the command is executed
 export function activate(context: vscode.ExtensionContext) {
 export function activate(context: vscode.ExtensionContext) {
-    let addProblemCommand = vscode.commands.registerCommand('extension.addProblem', addProblem);
-    let addContestCommand = vscode.commands.registerCommand('extension.addContest', addContest);
-    let runSolutionCommand = vscode.commands.registerCommand('extension.runSolution', runSolution);
-    let openTestcaseCommand = vscode.commands.registerCommand('extension.openTestcase', openTestcase);
-    let addTestcaseCommand = vscode.commands.registerCommand('extension.addTestcase', addTestcase);
-    let codingCommand = vscode.commands.registerCommand('extension.coding', coding);
-    let stressCommand = vscode.commands.registerCommand('extension.stress', stress);
-    let upgradeCommand = vscode.commands.registerCommand('extension.upgrade', upgrade);
-    let compileCommand = vscode.commands.registerCommand('extension.compile', compile);
-
-    let debugTestCommand = vscode.commands.registerCommand('extension.debugTest', debugTest);
+    let addProblemCommand = vscode.commands.registerCommand('acmx.addProblem', addProblem);
+    let addContestCommand = vscode.commands.registerCommand('acmx.addContest', addContest);
+    let runSolutionCommand = vscode.commands.registerCommand('acmx.runSolution', runSolution);
+    let openTestcaseCommand = vscode.commands.registerCommand('acmx.openTestcase', openTestcase);
+    let addTestcaseCommand = vscode.commands.registerCommand('acmx.addTestcase', addTestcase);
+    let codingCommand = vscode.commands.registerCommand('acmx.coding', coding);
+    let stressCommand = vscode.commands.registerCommand('acmx.stress', stress);
+    let upgradeCommand = vscode.commands.registerCommand('acmx.upgrade', upgrade);
+    let compileCommand = vscode.commands.registerCommand('acmx.compile', compile);
+
+    let debugTestCommand = vscode.commands.registerCommand('acmx.debugTest', debugTest);
 
 
     context.subscriptions.push(addProblemCommand);
     context.subscriptions.push(addProblemCommand);
     context.subscriptions.push(addContestCommand);
     context.subscriptions.push(addContestCommand);

+ 4 - 1
src/parsers/codeforces.ts

@@ -37,7 +37,10 @@ export async function parseContest(contestId: string) {
         problems.push(prob);
         problems.push(prob);
     }
     }
 
 
-    return new Contest(problems);
+    let name: string = soup.find("div", "sidebar").find("a").text;
+    name = name.toLowerCase().replace(' ', '-');
+
+    return new Contest(name, problems);
 }
 }
 
 
 /**
 /**

+ 5 - 3
src/test/extension.test.ts

@@ -9,11 +9,13 @@ import { dirname, join } from 'path';
 import { timedRun, testcasesName, testSolution, newArena, ATTIC, TESTCASES, upgradeArena, stressSolution, newProblemFromId, newContestFromId, getTimeout } from '../core';
 import { timedRun, testcasesName, testSolution, newArena, ATTIC, TESTCASES, upgradeArena, stressSolution, newProblemFromId, newContestFromId, getTimeout } from '../core';
 import { TestcaseResult, Veredict } from '../types';
 import { TestcaseResult, Veredict } from '../types';
 import { rmdirSync, existsSync, readdirSync, unlinkSync, openSync, writeSync, closeSync } from 'fs';
 import { rmdirSync, existsSync, readdirSync, unlinkSync, openSync, writeSync, closeSync } from 'fs';
-import { getSite } from '../conn';
+import { getSite, SITES, PERSONAL } from '../conn';
 
 
 const SRC = join(dirname(dirname(dirname(__filename))), 'src', 'test');
 const SRC = join(dirname(dirname(dirname(__filename))), 'src', 'test');
 const ARENA = join(SRC, 'arena');
 const ARENA = join(SRC, 'arena');
 
 
+SITES.push(PERSONAL);
+
 suite("Extension Tests", function () {
 suite("Extension Tests", function () {
     /**
     /**
      * Recursive remove
      * Recursive remove
@@ -78,7 +80,7 @@ suite("Extension Tests", function () {
         newArena(path);
         newArena(path);
         upgradeArena(path);
         upgradeArena(path);
 
 
-        assert.equal(existsSync(join(path, ATTIC, 'gen.py')), true);
+        assert.equal(existsSync(join(path, 'gen.py')), true);
         assert.equal(existsSync(join(path, 'brute.cpp')), true);
         assert.equal(existsSync(join(path, 'brute.cpp')), true);
 
 
         recRmdir(path);
         recRmdir(path);
@@ -237,7 +239,7 @@ suite("Extension Tests", function () {
         );
         );
 
 
         // populate gen.py
         // populate gen.py
-        writeFile(join(path, ATTIC, 'gen.py'),
+        writeFile(join(path, 'gen.py'),
         `import random\n` +
         `import random\n` +
         `print(random.randint(0, 99))\n`
         `print(random.randint(0, 99))\n`
         );
         );

+ 4 - 2
src/types.ts

@@ -44,9 +44,11 @@ export class Problem{
 }
 }
 
 
 export class Contest{
 export class Contest{
-    problems?: Problem[];
+    name: string;
+    problems: Problem[];
 
 
-    constructor(problems?: Problem[]){
+    constructor(name: string, problems: Problem[]){
+        this.name = name;
         this.problems = problems;
         this.problems = problems;
     }
     }
 }
 }

+ 23 - 3
todo.md

@@ -2,12 +2,28 @@
 
 
 * **WOW** Use this tool: [caide-cpp-inliner](https://github.com/slycelote/caide-cpp-inliner). Suggestion from jcg
 * **WOW** Use this tool: [caide-cpp-inliner](https://github.com/slycelote/caide-cpp-inliner). Suggestion from jcg
 * When a new view is activated (after run or view:code) close all open tabs. (also (maybe) collapse everything not related to the problem)
 * When a new view is activated (after run or view:code) close all open tabs. (also (maybe) collapse everything not related to the problem)
-* Allow programming in other languages than c++
+* Allow programming in other languages than c++ (easy now)
+* Implement parser for codeforces-gym/codechef/atcoder/matcomgrader/coj (which are most popular online judges currently)
+* TODO: Test add problem/add contest with codeforces problems (mostly folder and names where are created)
 
 
 * [005](/src/core.ts): Restrict brute in time, and capture errors
 * [005](/src/core.ts): Restrict brute in time, and capture errors
   * Allow stopping a running program (such as sol.cpp/brute.cpp/gen.py/etc...)
   * Allow stopping a running program (such as sol.cpp/brute.cpp/gen.py/etc...)
 * [007](/src/extension.ts): How can I have access to new proccess created using `openFolder`?
 * [007](/src/extension.ts): How can I have access to new proccess created using `openFolder`?
 
 
+## QUICK TODO
+
+
+* Update README to support multiple languages (only that need to be properly setted is compilation line. Even python is accepted) User need to provide line that take $PROGRAM file and makes and executable at $OUTPUT (this can be anything). Make an example of how to do that for python and maybe other languages. This would be good as a separate minitutorial linked here
+* Move minitutorials to doc folder and write a minitutorial on what is contest-id problem-id etc on each platform to avoid doubts
+
+* On README First steps:
+  * Folder to store contests
+  * Compilation line (in sevaral languages linking minitutorial)
+  * Path to template
+
+* Copy to clipboard (smart copy in the future with tool suggested by jcg) (Find in examples, create shortcut)
+* Create shortcut to Run/Stress/Compile/etc...
+
 ## Settings
 ## Settings
 
 
 Global settings
 Global settings
@@ -17,10 +33,14 @@ Global settings
 * [X] Line to execute C++ (Upgrade this line, by increasing stack and making optimizations by default)
 * [X] Line to execute C++ (Upgrade this line, by increasing stack and making optimizations by default)
 * [X] Line to execute Python
 * [X] Line to execute Python
 
 
-Particular settings (per problem)
+Particular settings (per problem) configuration on current workspace.
+This can be done creating such configurations globally and udpating them per workspace (only problem here is that in one workspace might coexist several programs so best answer is probably creating a config file inside each problem and access them through cool UI settings provided by VSCode. This can be done since GitLens already do that.) Store also problem name on this config file, maybe URL.
+
+* Make a new command to open particular settings of a problem
 
 
 * [ ] Checker
 * [ ] Checker
   * [ ] Allow custom checker implemented with testlib.
   * [ ] Allow custom checker implemented with testlib.
   * [ ] Try to figure out correct checker.
   * [ ] Try to figure out correct checker.
+  * [ ] Create few cool checkers such as only first token or accept everything.
 * [ ] Allow multiple solutions. (Don't check on this case. Try to figure out if this is the case)
 * [ ] Allow multiple solutions. (Don't check on this case. Try to figure out if this is the case)
-* [ ] Is interactive (Don't check on this case. Try to figure out if this is the case)
+* [ ] Is interactive (Don't check on this case. Try to figure out if this is the case)