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>
 
+![overview](https://raw.githubusercontent.com/blueedgetechno/codeblue/master/img/face.png)
 
 ### 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
 
-![overview](https://raw.githubusercontent.com/blueedgetechno/codeblue/master/img/face.png)
+##### Any feedback will be highly appreciable
+
+<br>  
 
 ![work](https://raw.githubusercontent.com/blueedgetechno/codeblue/master/img/work.png)
 <hr>
@@ -76,4 +83,3 @@ oj l https://codeforces.com/
 
 # MLE - Its a thing too
 ![TLE](https://raw.githubusercontent.com/blueedgetechno/codeblue/master/img/mle.png)
-<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);