diff --git a/bin/jcf b/bin/jcf
new file mode 100755
index 0000000..f92094b
Binary files /dev/null and b/bin/jcf differ
diff --git a/bin/jcf.xml b/bin/jcf.xml
new file mode 100644
index 0000000..f942140
--- /dev/null
+++ b/bin/jcf.xml
@@ -0,0 +1,180 @@
+
+
+ 2.44
+ 39387.7118126273
+ JEDI Code Format Settings
+
+ False
+ 1
+ True
+ True
+ True
+ True
+
+
+ 0
+ True
+ True
+ Sender
+ dpr, pas, pp
+
+
+ 2
+ 2
+ False
+ True
+ True
+ False
+ True
+ True
+ True
+ True
+ False
+ 2
+ False
+ True
+
+
+ True
+ False
+ 2
+ 2
+ True
+ False
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ True
+ 0
+ False
+ False
+ False
+ False
+ False
+
+
+ 2
+ 90
+ 1
+ True
+ True
+ False
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ 1
+ 1
+
+ 1
+ 1
+ 1
+ 0
+ 0
+ 1
+ 1
+ 0
+ 1
+ 0
+ 1
+ 1
+ 0
+ 1
+ 0
+ True
+ 4
+ 1
+ 1
+
+
+ True
+ True
+
+
+ True
+ 1
+ 1
+ 1
+ 1
+ 1
+
+
+ True
+
+
+
+ True
+ ActivePage, AnsiCompareStr, AnsiCompareText, AnsiUpperCase, AsBoolean, AsDateTime, AsFloat, AsInteger, Assign, AsString, AsVariant, BeginDrag, Buttons, Caption, Checked, Classes, ClassName, Clear, Close, Components, Controls, Count, Create, Data, Dec, Delete, Destroy, Dialogs, Enabled, EndDrag, EOF, Exception, Execute, False, FieldByName, First, Forms, Free, FreeAndNil, GetFirstChild, Graphics, Height, idAbort, idCancel, idIgnore, IDispatch, idNo, idOk, idRetry, idYes, Inc, Initialize, IntToStr, ItemIndex, IUnknown, Lines, Math, MaxValue, mbAbort, mbAll, mbCancel, mbHelp, mbIgnore, mbNo, mbOK, mbRetry, mbYes, mbYesToAll, Messages, MinValue, mnNoToAll, mrAbort, mrAll, mrCancel, mrIgnore, mrNo, mrNone, mrNoToAll, mrOk, mrRetry, mrYes, mrYesToAll, mtConfirmation, mtCustom, mtError, mtInformation, mtWarning, Name, Next, Open, Ord, ParamStr, PChar, Perform, ProcessMessages, Read, ReadOnly, RecordCount, Register, Release, Result, Sender, SetFocus, Show, ShowMessage, Source, StdCtrls, StrToInt, SysUtils, TAutoObject, TButton, TComponent, TDataModule, Text, TForm, TFrame, TList, TNotifyEvent, TObject, TObjectList, TPageControl, TPersistent, True, TStringList, TStrings, TTabSheet, Unassigned, Value, Visible, WideString, Width, Windows, Write
+
+
+ True
+ False, Name, nil, PChar, Read, ReadOnly, True, WideString, Write
+
+
+ True
+ ActnColorMaps, ActnCtrls, ActnList, ActnMan, ActnMenus, ActnPopup, ActnRes, ADOConst, ADODB, ADOInt, AppEvnts, AxCtrls, BandActn, bdeconst, bdemts, Buttons, CheckLst, Classes, Clipbrd.pas, CmAdmCtl, ComCtrls, ComStrs, Consts, Controls, CtlConsts, CtlPanel, CustomizeDlg, DataBkr, DB, DBActns, dbcgrids, DBClient, DBClientActnRes, DBClientActns, DBCommon, DBConnAdmin, DBConsts, DBCtrls, DbExcept, DBGrids, DBLocal, DBLocalI, DBLogDlg, dblookup, DBOleCtl, DBPWDlg, DBTables, DBXpress, DdeMan, Dialogs, DrTable, DSIntf, ExtActns, ExtCtrls, ExtDlgs, FileCtrl, FMTBcd, Forms, Graphics, GraphUtil, Grids, HTTPIntr, IB, IBBlob, IBCustomDataSet, IBDatabase, IBDatabaseInfo, IBDCLConst, IBErrorCodes, IBEvents, IBExternals, IBExtract, IBGeneratorEditor, IBHeader, IBIntf, IBQuery, IBRestoreEditor, IBSecurityEditor, IBServiceEditor, IBSQL, IBSQLMonitor, IBStoredProc, IBTable, IBUpdateSQL, IBUtils, IBXConst, ImgList, Jcl8087, JclAbstractContainers, JclAlgorithms, JclAnsiStrings, JclAppInst, JclArrayLists, JclArraySets, JclBase, JclBinaryTrees, JclBorlandTools, JclCIL, JclCLR, JclCOM, JclComplex, JclCompression, JclConsole, JclContainerIntf, JclCounter, JclDateTime, JclDebug, JclDotNet, JclEDI, JclEDI_ANSIX12, JclEDI_ANSIX12_Ext, JclEDI_UNEDIFACT, JclEDI_UNEDIFACT_Ext, JclEDISEF, JclEDITranslators, JclEDIXML, JclExprEval, JclFileUtils, JclFont, JclGraphics, JclGraphUtils, JclHashMaps, JclHashSets, JclHookExcept, JclIniFiles, JclLANMan, JclLinkedLists, JclLocales, JclLogic, JclMapi, JclMath, JclMetadata, JclMIDI, JclMime, JclMiscel, JclMsdosSys, JclMultimedia, JclNTFS, JclPCRE, JclPeImage, JclPrint, JclQGraphics, JclQGraphUtils, JclQueues, JclRegistry, JclResources, JclRTTI, JclSchedule, JclSecurity, JclShell, JclSimpleXml, JclStacks, JclStatistics, JclStreams, JclStrHashMap, JclStringLists, JclStrings, JclStructStorage, JclSvcCtrl, JclSynch, JclSysInfo, JclSysUtils, JclTask, JclTD32, JclUnicode, JclUnitConv, JclUnitVersioning, JclUnitVersioningProviders, JclValidation, JclVectors, JclWideFormat, JclWideStrings, JclWin32, JclWin32Ex, JclWinMIDI, ListActns, Mask, MConnect, Menus, Midas, MidasCon, MidConst, MPlayer, MtsRdm, Mxconsts, ObjBrkr, OleAuto, OleConst, OleCtnrs, OleCtrls, OleDB, OleServer, Outline, Printers, Provider, recerror, ScktCnst, ScktComp, ScktMain, SConnect, ShadowWnd, SimpleDS, SMINTF, SqlConst, SqlExpr, SqlTimSt, StdActnMenus, StdActns, StdCtrls, StdStyleActnCtrls, SvcMgr, SysUtils, TabNotBk, Tabs, TConnect, Themes, ToolWin, ValEdit, VDBConsts, WinHelpViewer, XPActnCtrls, XPMan, XPStyleActnCtrls
+
+
+ 0
+ 0
+ False
+ False
+ 0
+ False
+ 0
+
+
+ True
+ DELPHI5_UP, MSWINDOWS
+
+
+
+ True
+ True
+ True
+ True
+ True
+ True
+ False
+ 2
+ 60
+ 1
+ 10
+ 0
+
+
+ False
+
+
+
+ False
+ False
+ False
+ False
+
+
+
+
+
+
+
+ 1
+ True
+ False
+ False
+ False
+ False
+ True
+ 0
+ False
+
+
diff --git a/language-config.json b/language-config.json
index ae84b79..1ab1396 100644
--- a/language-config.json
+++ b/language-config.json
@@ -3,55 +3,64 @@
"show": "C++",
"highlight": "cpp",
"version": "GCC 8.2.0",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"cpp11": {
"show": "C++ 11",
"highlight": "cpp",
"version": "GCC 8.2.0",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"cpp17": {
"show": "C++ 17",
"highlight": "cpp",
"version": "GCC 8.2.0",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"cpp-noilinux": {
"show": "C++ (NOI)",
"highlight": "cpp",
"version": "GCC 4.8.4 (NOILinux 1.4.1)",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"cpp11-noilinux": {
"show": "C++ 11 (NOI)",
"highlight": "cpp",
"version": "GCC 4.8.4 (NOILinux 1.4.1)",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"cpp11-clang": {
"show": "C++ 11 (Clang)",
"highlight": "cpp",
"version": "Clang 7.0.1",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"cpp17-clang": {
"show": "C++ 17 (Clang)",
"highlight": "cpp",
"version": "Clang 7.0.1",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "cpp"
},
"c": {
"show": "C",
"highlight": "c",
"version": "Clang 7.0.1",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "c"
},
"c-noilinux": {
"show": "C (NOI)",
"highlight": "c",
"version": "GCC 4.8.4 (NOILinux 1.4.1)",
- "editor": "c_cpp"
+ "editor": "c_cpp",
+ "format": "c"
},
"csharp": {
"show": "C#",
@@ -69,13 +78,15 @@
"show": "Java",
"highlight": "java",
"version": "OpenJDK 10.0.2",
- "editor": "java"
+ "editor": "java",
+ "format": "java"
},
"pascal": {
"show": "Pascal",
"highlight": "pascal",
"version": "Free Pascal 3.0.4",
- "editor": "pascal"
+ "editor": "pascal",
+ "format": "pas"
},
"lua": {
"show": "Lua",
@@ -105,7 +116,8 @@
"show": "Node.js",
"highlight": "js",
"version": "10.14.0",
- "editor": "javascript"
+ "editor": "javascript",
+ "format": "js"
},
"ruby": {
"show": "Ruby",
diff --git a/libs/code_formatter.js b/libs/code_formatter.js
new file mode 100644
index 0000000..3cea19b
--- /dev/null
+++ b/libs/code_formatter.js
@@ -0,0 +1,54 @@
+let child_process = require('child_process');
+let streamToString = require('stream-to-string');
+let tempfile = require('tempfile');
+let fs = require('fs-extra');
+
+module.exports = async (code, lang) => {
+ let timer, result, tempFile;
+ try {
+ let process, pascalAddProgram;
+ if (lang === 'pas') {
+ tempFile = tempfile('.pas');
+
+ if (code.indexOf('program') === -1) {
+ code = 'program format\n' + code;
+ pascalAddProgram = true;
+ }
+
+ await fs.writeFile(tempFile, code);
+ process = child_process.spawn(`${__dirname}/../bin/jcf`, [tempFile, '-inplace']);
+ } else {
+ process = child_process.spawn(`clang-format -style="{BasedOnStyle: Google, IndentWidth: 4, AccessModifierOffset: -4, SortIncludes: false, AllowShortIfStatementsOnASingleLine: true, ColumnLimit: 110, Cpp11BracedListStyle: false }" -assume-filename="a.${lang}"`, { shell: true });
+ process.stdin.setEncoding('utf-8');
+ process.stdin.write(code);
+ process.stdin.end();
+ }
+
+ timer = setTimeout(() => process.kill(), 5000);
+
+ if (lang === 'pas') {
+ await streamToString(process.stdout);
+ result = await fs.readFile(tempFile, 'utf-8');
+ if (pascalAddProgram) result = result.replace('program format\n', '');
+ } else {
+ result = await streamToString(process.stdout);
+ }
+
+ await new Promise((resolve, reject) => {
+ let exit = code => {
+ if (code === 0) resolve();
+ else reject(code);
+ }
+
+ if (process.exitCode !== null) exit(process.exitCode);
+ else process.on('close', exit);
+ });
+ } catch (e) {
+ console.log(e.stack);
+ result = null;
+ }
+
+ clearTimeout(timer);
+ try { if (tempFile) await fs.delete(tempFile); } catch (e) {}
+ return result;
+}
diff --git a/models/formatted_code.js b/models/formatted_code.js
new file mode 100644
index 0000000..4ff3239
--- /dev/null
+++ b/models/formatted_code.js
@@ -0,0 +1,31 @@
+let Sequelize = require('sequelize');
+let db = syzoj.db;
+
+let model = db.define('formatted_code', {
+ key: { type: Sequelize.STRING(50), primaryKey: true },
+ code: { type: Sequelize.TEXT('medium') }
+}, {
+ timestamps: false,
+ tableName: 'formatted_code',
+ indexes: [
+ {
+ fields: ['key']
+ }
+ ]
+});
+
+let Model = require('./common');
+class FormattedCode extends Model {
+ static async create(val) {
+ return FormattedCode.fromRecord(FormattedCode.model.build(Object.assign({
+ key: "",
+ code: ""
+ }, val)));
+ }
+
+ getModel() { return model; }
+}
+
+FormattedCode.model = model;
+
+module.exports = FormattedCode;
diff --git a/models/user.js b/models/user.js
index 0fd9b21..7782154 100644
--- a/models/user.js
+++ b/models/user.js
@@ -17,6 +17,7 @@ let model = db.define('user', {
is_admin: { type: Sequelize.BOOLEAN },
is_show: { type: Sequelize.BOOLEAN },
public_email: { type: Sequelize.BOOLEAN },
+ prefer_formatted_code: { type: Sequelize.BOOLEAN },
sex: { type: Sequelize.INTEGER },
rating: { type: Sequelize.INTEGER },
@@ -65,7 +66,7 @@ class User extends Model {
}
}));
}
-
+
static async fromName(name) {
return User.fromRecord(User.model.findOne({
where: {
diff --git a/modules/problem.js b/modules/problem.js
index 8b46c72..afa41dc 100644
--- a/modules/problem.js
+++ b/modules/problem.js
@@ -1,5 +1,6 @@
let Problem = syzoj.model('problem');
let JudgeState = syzoj.model('judge_state');
+let FormattedCode = syzoj.model('formatted_code');
let CustomTest = syzoj.model('custom_test');
let WaitingJudge = syzoj.model('waiting_judge');
let Contest = syzoj.model('contest');
@@ -9,6 +10,7 @@ let Article = syzoj.model('article');
const Sequelize = require('sequelize');
let Judger = syzoj.lib('judger');
+let CodeFormatter = syzoj.lib('code_formatter');
app.get('/problems', async (req, res) => {
try {
@@ -665,6 +667,27 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1
}
await judge_state.updateRelatedInfo(true);
+ if (problem.type !== 'submit-answer' && syzoj.languages[req.body.language].format) {
+ let key = syzoj.utils.getFormattedCodeKey(judge_state.code, req.body.language);
+ let formattedCode = await FormattedCode.findOne({
+ where: {
+ key: key
+ }
+ });
+
+ if (!formattedCode) {
+ let formatted = await CodeFormatter(judge_state.code, syzoj.languages[req.body.language].format);
+ if (formatted) {
+ formattedCode = await FormattedCode.create({
+ key: key,
+ code: formatted
+ });
+
+ await formattedCode.save();
+ }
+ }
+ }
+
try {
await Judger.judge(judge_state, problem, contest_id ? 3 : 2);
judge_state.pending = true;
diff --git a/modules/submission.js b/modules/submission.js
index c602d42..499e085 100644
--- a/modules/submission.js
+++ b/modules/submission.js
@@ -1,4 +1,5 @@
let JudgeState = syzoj.model('judge_state');
+let FormattedCode = syzoj.model('formatted_code');
let User = syzoj.model('user');
let Contest = syzoj.model('contest');
let Problem = syzoj.model('problem');
@@ -147,6 +148,19 @@ app.get('/submission/:id', async (req, res) => {
if (judge.problem.type !== 'submit-answer') {
judge.codeLength = judge.code.length;
+
+ let key = syzoj.utils.getFormattedCodeKey(judge.code, judge.language);
+ if (key) {
+ let formattedCode = await FormattedCode.findOne({
+ where: {
+ key: key
+ }
+ });
+
+ if (formattedCode) {
+ judge.formattedCode = await syzoj.utils.highlight(formattedCode.code, syzoj.languages[judge.language].highlight);
+ }
+ }
judge.code = await syzoj.utils.highlight(judge.code, syzoj.languages[judge.language].highlight);
}
@@ -155,6 +169,8 @@ app.get('/submission/:id', async (req, res) => {
info: getSubmissionInfo(judge, displayConfig),
roughResult: getRoughResult(judge, displayConfig),
code: (judge.problem.type !== 'submit-answer') ? judge.code.toString("utf8") : '',
+ formattedCode: judge.formattedCode ? judge.formattedCode.toString("utf8") : null,
+ preferFormattedCode: res.locals.user ? res.locals.user.prefer_formatted_code : false,
detailResult: processOverallResult(judge.result, displayConfig),
socketToken: (judge.pending && judge.task_id != null) ? jwt.sign({
taskId: judge.task_id,
diff --git a/modules/user.js b/modules/user.js
index 65985c9..39731cb 100644
--- a/modules/user.js
+++ b/modules/user.js
@@ -187,6 +187,8 @@ app.post('/user/:id/edit', async (req, res) => {
user.information = req.body.information;
user.sex = req.body.sex;
user.public_email = (req.body.public_email === 'on');
+ console.log(req.body);
+ user.prefer_formatted_code = (req.body.prefer_formatted_code === 'on');
await user.save();
diff --git a/package.json b/package.json
index 91484c4..afd270b 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,9 @@
"sequelize": "^4.41.2",
"session-file-store": "^1.0.0",
"socket.io": "^2.2.0",
+ "stream-to-string": "^1.1.0",
"syzoj-divine": "^1.0.2",
+ "tempfile": "^2.0.0",
"tmp-promise": "^1.0.3",
"waliyun": "^3.1.1",
"winston": "^3.1.0",
diff --git a/utility.js b/utility.js
index 36c3b69..a5c7b1d 100644
--- a/utility.js
+++ b/utility.js
@@ -98,6 +98,12 @@ module.exports = {
else res.suffix = res.suffix.replace('iB', '');
return res.fixed + ' ' + res.suffix;
},
+ getFormattedCodeKey(code, lang) {
+ if (syzoj.languages[lang].format) {
+ return syzoj.languages[lang].format + '\n' + syzoj.utils.md5(code);
+ }
+ return null;
+ },
judgeServer(suffix) {
return JSON.stringify(url.resolve(syzoj.config.judge_server_addr, suffix));
},
diff --git a/views/submission.ejs b/views/submission.ejs
index 6c98188..04d472e 100644
--- a/views/submission.ejs
+++ b/views/submission.ejs
@@ -51,7 +51,18 @@
-
+
+ <% if (formattedCode !== null) { %>
+
+
+ 显示原始代码
+
+
+ 格式化代码
+
+
+ <% } %>
+
@@ -125,6 +136,7 @@
@@ -134,7 +146,7 @@