From 74ec1e43d621dd89b97dd490fdcdedf4f2419e6a Mon Sep 17 00:00:00 2001 From: blueedgetechno <blueedgetechno@gmail.com> Date: Sun, 23 Aug 2020 23:22:30 +0530 Subject: [PATCH] Now you also test and run c and c++ files --- README.md | 16 ++-- lib/view/actions.js | 35 ++++++--- lib/view/index.js | 1 - lib/view/judge.js | 47 +++++++++--- lib/view/play.js | 4 +- lib/view/problems.js | 133 ++++++++++++++++++++++++++++++---- lib/view/recentsubmissions.js | 2 +- package.json | 1 - styles/codeblue.less | 70 +++++++++++++----- 9 files changed, 246 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index d162227..b535719 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ ## An Atom package for all your competitive coding needs <br> + ### Installation Step 1. @@ -32,25 +33,31 @@ oj l https://codeforces.com/ - Automatically create an environment for contest including working files 💻 - Run code with official sample cases of any particular problem 🔭 - Compare your output with expected output in a elegant manner 📺 -- Get your code verdicts like OK ✅, Wrong answer ❌, TLE 🕒, MLE 🛢, Runtime error ⚠, Queue ⏳, Hacked ☠️ +- Get your code verdicts like OK ✅, Wrong answer ❌, TLE 🕒, MLE 🛢, Runtime error or Compilation error ⚠, Queue ⏳, Hacked ☠️ - Submit 🏳🌈 your code from your favorite text editor - Get your official standing of the live contest 👥 - Play minigames while you wait for the contest 🎮 +## Updates +- Now supports the feature to test and run C++(g++ compiler) and C (gcc compiler) language codes 🎉🎊 +- Option to run code for case independent problems 📡 + ## Guide - Set your preferences in settings of the package, in advance +- Delete the folders and files of working directory before contest - Click on Refresh button to refresh the standing - Click on your standing to minimize problems view - Click on your profile picture to refresh problems - Click on an example number to copy its input data ## Limitations -- Currently, test and run code option is only available for python users - I will try to remove the dependency of online-judge-tools for non-python users in future updates -<br> +- While running C or C++ files, it sometimes run the previously compiled file and shows the same verdict. Incase of that delete the previously compiled file and run the code again - +##### Any feedback will be highly appreciable + +<br>  <hr> @@ -76,4 +83,3 @@ oj l https://codeforces.com/ # MLE - Its a thing too  -<hr> diff --git a/lib/view/actions.js b/lib/view/actions.js index d245662..b092a42 100644 --- a/lib/view/actions.js +++ b/lib/view/actions.js @@ -12,7 +12,8 @@ export default class Actions extends React.PureComponent { outputs: props.outputs, tests: props.tests, runexamples: props.runexamples, - submitsolution: props.submitsolution + submitsolution: props.submitsolution, + changeignorevalue: props.changeignorevalue } } @@ -23,19 +24,20 @@ export default class Actions extends React.PureComponent { outputs: nextProps.outputs, tests: nextProps.tests, runexamples: nextProps.runexamples, - submitsolution: nextProps.submitsolution + submitsolution: nextProps.submitsolution, + changeignorevalue: nextProps.changeignorevalue }); } - change(ele){ - if(ele==null) return - ele.target.classList.toggle("icon-chevron-right") - ele.target.classList.toggle("icon-chevron-down") - var val = ele.target.parentElement.nextElementSibling.style.display + change(){ + var ele = this.refs.chevronbutton + ele.classList.toggle("icon-chevron-right") + ele.classList.toggle("icon-chevron-down") + var val = this.refs.down.style.display if(val=="flex"){ - ele.target.parentElement.nextElementSibling.style.display = "none"; + this.refs.down.style.display = "none"; }else{ - ele.target.parentElement.nextElementSibling.style.display = "flex"; + this.refs.down.style.display = "flex"; } } @@ -88,13 +90,22 @@ export default class Actions extends React.PureComponent { return ( <div className="actions"> <h2 className="title titlename">{this.state.prob.index} - {this.state.prob.name}</h2> + <div className="addoption"> + <div className="igcasediv"> + <input ref="igcase" type="checkbox" name="igcase" checked={this.state.prob.ignorecase} onChange={this.state.changeignorevalue}/> + <span>Ignore case</span> + </div> + </div> <div className="tasks"> <div className="task"> <div className="up"> - <span onClick={this.change.bind(this)} className="icon icon-chevron-right">Examples</span> - <button className="actionbutton" onClick={this.state.runexamples}>Run all</button> + <span ref="chevronbutton" onClick={this.change.bind(this)} className="icon icon-chevron-right">Examples</span> + <button className="actionbutton" onClick={()=>{ + this.state.runexamples() + this.refs.down.style.display = "flex" + }}>Run all</button> </div> - <div className="down" style={{display: "none"}}> + <div ref="down" className="down" style={{display: "none"}}> {this.state.tests && this.state.tests.map(test=>{ if(test.error){ if(test.stdout.length>0 && test.stderr.length>0) moredetails = <div className="errordetails"> <pre>{test.stdout}<br/>{test.stderr}</pre> </div> diff --git a/lib/view/index.js b/lib/view/index.js index 5ceebb4..aaf87b3 100644 --- a/lib/view/index.js +++ b/lib/view/index.js @@ -5,7 +5,6 @@ import Problems from './problems'; import config from '../config' import cheerio from 'cheerio' import Play from './play' -// import CodeforcesScraper from './codeforces-scraper'; function couple(x) { if(x<10){ diff --git a/lib/view/judge.js b/lib/view/judge.js index c034e39..9094c60 100644 --- a/lib/view/judge.js +++ b/lib/view/judge.js @@ -1,11 +1,11 @@ 'use babel'; class Judge { - constructor(){ + constructor() { this.ignorewhitespace = true; } - clearstring(s){ + clearstring(s) { s = s.trim() s = s.split("\n") for (var i = 0; i < s.length; i++) { @@ -14,19 +14,23 @@ class Judge { return s.join("\n") } - verify(exp, out){ + verify(exp, out, ig) { exp = this.clearstring(exp) out = this.clearstring(out) + if(ig){ + exp = exp.toUpperCase() + out = out.toUpperCase() + } return exp == out } - beautify(err){ + beautify(err) { err = err.trim() err = err.split("\n") var tmp = [] for (var i = 1; i < err.length; i++) { - if(err[i].length){ - if(err[i].includes("File")){ + if (err[i].length) { + if (err[i].includes("File")) { err[i] = err[i].split(",")[1] } tmp.push(err[i].trim()) @@ -35,22 +39,45 @@ class Judge { var sol = [] for (var i = 0; i < tmp.length; i++) { - if(i&1^1){ + if (i & 1 ^ 1) { sol.push(tmp[i]) - }else{ - sol[sol.length-1]+=" : " + tmp[i] + } else { + sol[sol.length - 1] += " : " + tmp[i] } } var toreturn = [] - for (var i = sol.length-1; i >=0 ; i--) { + for (var i = sol.length - 1; i >= 0; i--) { toreturn.push(sol[i]) } return toreturn.join("\n"); } + beautifycpp(err,fname){ + const path = require('path'); + err = err.trim() + err = err.split("\n") + var tmp = [] + for (var i = 1; i < err.length; i++) { + if(err[i].length && err[i].includes("In function")==false){ + err[i] = err[i].trim() + var idx = err[i].indexOf(fname) + if(idx==0){ + err[i] = err[i].replace(fname,path.basename(fname)) + var errarray = err[i].split(":") + if(errarray[2].trim()!="note"){ + errarray[1]="Line "+errarray[1] + tmp.push(errarray.slice(1,).join(":").trim()) + } + } + } + } + + return tmp.join("\n") + } + } export default new Judge(); diff --git a/lib/view/play.js b/lib/view/play.js index 4914cb0..dd084b4 100644 --- a/lib/view/play.js +++ b/lib/view/play.js @@ -61,8 +61,10 @@ export default class Play extends React.PureComponent { fetchimage(){ fetch("https://source.unsplash.com/random/280x280").then(res=>{ this.setState({image: res.url}) + this.refs.nextbutton.style.display = "initial"; }).catch(err=> { this.setState({image: "https://images.unsplash.com/photo-1595496710086-d69bff2ccb19?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=280&ixlib=rb-1.2.1&q=80&w=280"}) + this.refs.nextbutton.style.display = "initial"; }) } @@ -175,7 +177,7 @@ export default class Play extends React.PureComponent { }) })} </div> - <button className="nextbutton" onClick={this.reset.bind(this)}>Next</button> + <button ref="nextbutton" className="nextbutton" style={{display: "none"}} onClick={this.reset.bind(this)}>Next</button> </div> </div> ) diff --git a/lib/view/problems.js b/lib/view/problems.js index 941e32f..726eddc 100644 --- a/lib/view/problems.js +++ b/lib/view/problems.js @@ -40,24 +40,29 @@ export default class Problems extends React.PureComponent { } } - runtest (i){ + changeignorevalue(){ + var probs = this.state.probs + probs[this.state.curr].ignorecase^=1 + this.setState({probs: probs}) + this.setState({temp: this.state.temp^1}) + } + + runtestpy (i){ + console.log("Running python"); const path = require('path'); var prob = this.state.probs[this.state.curr] var langcode = atom.config.get("codeblue.programmingLanguage") - var ext = "" - if(langcode==43) ext=".c" - else if(langcode==54) ext=".cpp" - else ext=".py" + var ext = ".py" var wd = atom.config.get("codeblue.workingDirectory") var towhere = path.join(wd,prob.index) var torun = path.join(towhere,prob.index+ext) var inputfile = path.join(towhere,"examples/input"+i+".in") var allverdicts = this.state.allverdicts + const { exec } = require('child_process'); cmd = "python "+ torun +" < " + inputfile - const { exec } = require('child_process'); exec(cmd,{ timeout: 2500, maxBuffer: 1024*32, @@ -78,11 +83,11 @@ export default class Problems extends React.PureComponent { res.icon = "alert" } }else{ - if(Judge.verify(res.stdout,this.state.alloutputs[prob.index][i-1])){ + if(Judge.verify(res.stdout,this.state.alloutputs[prob.index][i-1],prob.ignorecase)){ res.verdict="OK" res.icon="check" }else{ - res.verdict=="WRONG_ANSWER" + res.verdict="WRONG_ANSWER" res.icon="x" } } @@ -93,11 +98,110 @@ export default class Problems extends React.PureComponent { }) } + runtestcpp (i){ + console.log("Running cpp or c"); + const path = require('path'); + var prob = this.state.probs[this.state.curr] + var langcode = atom.config.get("codeblue.programmingLanguage") + var ext = "" + if(langcode==43) ext=".c" + else if(langcode==54) ext=".cpp" + + if(ext=="") return + + var wd = atom.config.get("codeblue.workingDirectory") + var towhere = path.join(wd,prob.index) + const fname = prob.index+ext + var torun = path.join(towhere,fname) + var tout = path.join(towhere,prob.index) + var inputfile = path.join(towhere,"examples/input"+i+".in") + + var allverdicts = this.state.allverdicts + + const { exec } = require('child_process'); + + var soft="" + if(langcode==54){ + soft="g++ " + }else if (langcode==43) { + soft="gcc " + } + + if(soft=="") return + var cmd = soft+torun+" -o "+tout+" -fno-show-column -fno-diagnostics-show-caret" + var cmd2 = tout+" < "+inputfile + + exec(cmd,{ + timeout: 2500, + maxBuffer: 1024*32 + },(error, stdout, stderr)=>{ + // console.log("What"); + if(error !== null){ + var res = {n: i-1, error: true, verdict: "COMPILATION_ERROR", icon: "alert"} + stderr = Judge.beautifycpp(stderr,torun) + res.stderr = stderr + res.stdout = stdout.trim() + allverdicts[prob.index][i-1] = res + this.setState({allverdicts: allverdicts}) + this.setState({temp: this.state.temp^1}) + }else{ + exec(cmd2,{ + timeout: 5000, + maxBuffer: 1024*32 + },(error, stdout, stderr)=>{ + // console.log("The hell"); + var res = {n: i-1, error: false, verdict: "none", icon: "none"} + res.stdout = stdout.trim() + if(error !== null){ + res.error = true; + if(error.killed){ + // console.log("tle"); + res.verdict = "TIME_LIMIT_EXCEEDED" + res.icon = "clock" + }else if(error.code=="ERR_CHILD_PROCESS_STDIO_MAXBUFFER"){ + // console.log("mle"); + res.verdict = "MEMORY_LIMIT_EXCEEDED" + res.icon = "database" + }else{ + // console.log("runtime"); + res.verdict = "RUNTIME_ERROR" + res.icon = "alert" + stderr = "Runtime error\nexit code : "+error.code + } + }else{ + if(Judge.verify(res.stdout,this.state.alloutputs[prob.index][i-1],prob.ignorecase)){ + res.verdict="OK" + res.icon="check" + }else{ + res.verdict="WRONG_ANSWER" + res.icon="x" + } + } + res.stderr = stderr + allverdicts[prob.index][i-1] = res + this.setState({allverdicts: allverdicts}) + this.setState({temp: this.state.temp^1}) + }) + } + }) + + } + runexamples(){ console.log("Running tests"); - for (var i = 0; i < this.state.noftests[this.state.curr]; i++) { - this.runtest(i+1) + var langcode = atom.config.get("codeblue.programmingLanguage") + if(langcode==31 || langcode==41){ + for (var i = 0; i < this.state.noftests[this.state.curr]; i++) { + this.runtestpy(i+1) + } } + + if(langcode==43 || langcode==54){ + for (var i = 0; i < this.state.noftests[this.state.curr]; i++) { + this.runtestcpp(i+1) + } + } + } submitsolution(){ @@ -114,10 +218,7 @@ export default class Problems extends React.PureComponent { var tosub = path.join(towhere,prob.index+ext) var proburl = "https://codeforces.com/contest/"+ this.props.contest.id +"/problem/" + prob.index - cmd = "oj s " + proburl + " " + tosub + " --wait=0 --yes --no-open" - if(langcode==41){ - cmd+= " --guess-python-interpreter pypy" - } + cmd = "oj s " + proburl + " " + tosub + " --wait=0 --yes --no-open -l "+langcode const { exec } = require('child_process'); exec(cmd,(error, stdout, stderr)=>{ @@ -290,6 +391,7 @@ export default class Problems extends React.PureComponent { verdict: "NONE", testset: "NONE", errtest: 0, + ignorecase: 0, sub: 0}) allinputs[problem.index] = [] alloutputs[problem.index] = [] @@ -468,7 +570,8 @@ export default class Problems extends React.PureComponent { inputs={this.state.allinputs[this.state.probs[this.state.curr].index]} outputs={this.state.alloutputs[this.state.probs[this.state.curr].index]} tests={this.state.allverdicts[this.state.probs[this.state.curr].index]} - runexamples ={this.runexamples.bind(this)} + runexamples={this.runexamples.bind(this)} + changeignorevalue={this.changeignorevalue.bind(this)} submitsolution={this.submitsolution.bind(this)} /> : null } {this.state.probs.length ? <RecentSubmissions actions={this.state.actions}/> : null } diff --git a/lib/view/recentsubmissions.js b/lib/view/recentsubmissions.js index 80accfe..7b894e8 100644 --- a/lib/view/recentsubmissions.js +++ b/lib/view/recentsubmissions.js @@ -27,7 +27,7 @@ const RecentSubmissions = ({actions})=>{ <span>{action.index}</span> <i className={"icon-"+action.icon}> {action.errtest} </i> <span>{action.time}ms</span> - <span>{action.memory}kb</span> + {action.memory<1000?<span>{action.memory}kb</span>:<span>{Math.floor(action.memory/1000)}mb</span>} </div> ) })} diff --git a/package.json b/package.json index a98b8d4..ff24ab7 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "copy-to-clipboard": "^3.3.1", "fs": "0.0.1-security", "path": "^0.12.7", - "pretty-error": "^2.1.1", "react": "^16.13.1", "react-dom": "^16.13.1", "request": "^2.88.2" diff --git a/styles/codeblue.less b/styles/codeblue.less index 05a336c..188d3a9 100644 --- a/styles/codeblue.less +++ b/styles/codeblue.less @@ -3,6 +3,8 @@ .codeblue { width: 100%; padding: 10px; + height: 92vh; + overflow-y: scroll; } .profile { @@ -43,7 +45,7 @@ text-align: center; } - button{ + button { border: none; padding: 0 10px; font-size: 1.2em; @@ -52,7 +54,6 @@ font-weight: bold; color: white; } - } .fillup { @@ -68,14 +69,23 @@ } input[type=checkbox] { - display: inline-block; - width: 16pt; + -webkit-appearance: none; + background: none; + width: 12pt; + height: 12pt; padding: 2px 0 0 3px; - margin: 20px 10px 20px 40px; + margin: 20px 10px 25px 60px; vertical-align: middle; + border: 1.5px solid @text-color; + border-radius: 50%; + outline: none; cursor: pointer; } + input[type=checkbox]:checked{ + background: #0e45c1; + } + button[id=submit] { border: none; padding: 10px; @@ -89,15 +99,6 @@ color: white; } } -// #check{ -// border: none; -// background: #888; -// border-radius: 10px; -// } -// -// .checkthis{ -// font-size: 1.5em; -// } .timer { display: flex; @@ -142,7 +143,7 @@ } } -.refreshproblems{ +.refreshproblems { position: absolute; border: none; background: @background-color-selected; //#383f4b; @@ -342,6 +343,43 @@ flex-direction: column; } +.addoption { + width: 100%; + display: flex; + justify-content: space-between; +} + +.igcasediv { + width: 85px; + margin: 5px 5px 10px; + display: flex; + justify-content: space-between; + align-items: center; + + input[type=checkbox] { + -webkit-appearance: none; + display: block; + margin: 2px 0 0; + background: none; + border: 1.5px solid @text-color; + width: 13px; + height: 13px; + border-radius: 50%; + outline: none; + cursor: pointer; + } + + input[type=checkbox]:checked{ + background: #1351de; + } + + + span { + font-weight: bold; + color: #178eeb; + } +} + .tasks { display: flex; flex-direction: column; @@ -611,9 +649,7 @@ .copied { animation: pop 0.3s linear; transition: 0.3s; - } - @keyframes pop { 0% { transform: scale(1) translateZ(0);