ESM: Ported case converters, generic beautifier and syntax highlighting
This commit is contained in:
parent
176e83a79f
commit
905bc6699e
31
src/core/lib/Code.mjs
Normal file
31
src/core/lib/Code.mjs
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Code functions.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This tries to rename variable names in a code snippet according to a function.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {function} replacer - this function will be fed the token which should be renamed.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function replaceVariableNames(input, replacer) {
|
||||
const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
|
||||
|
||||
return input.replace(tokenRegex, (...args) => {
|
||||
const match = args[0],
|
||||
quotes = args[1];
|
||||
|
||||
if (!quotes) {
|
||||
return match;
|
||||
} else {
|
||||
return replacer(match);
|
||||
}
|
||||
});
|
||||
}
|
161
src/core/operations/GenericCodeBeautify.mjs
Normal file
161
src/core/operations/GenericCodeBeautify.mjs
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
|
||||
/**
|
||||
* Generic Code Beautify operation
|
||||
*/
|
||||
class GenericCodeBeautify extends Operation {
|
||||
|
||||
/**
|
||||
* GenericCodeBeautify constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Generic Code Beautify";
|
||||
this.module = "Code";
|
||||
this.description = "Attempts to pretty print C-style languages such as C, C++, C#, Java, PHP, JavaScript etc.<br><br>This will not do a perfect job, and the resulting code may not work any more. This operation is designed purely to make obfuscated or minified code more easy to read and understand.<br><br>Things which will not work properly:<ul><li>For loop formatting</li><li>Do-While loop formatting</li><li>Switch/Case indentation</li><li>Certain bit shift operators</li></ul>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const preservedTokens = [];
|
||||
let code = input,
|
||||
t = 0,
|
||||
m;
|
||||
|
||||
// Remove strings
|
||||
const sstrings = /'([^'\\]|\\.)*'/g;
|
||||
while ((m = sstrings.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
sstrings.lastIndex = m.index;
|
||||
}
|
||||
|
||||
const dstrings = /"([^"\\]|\\.)*"/g;
|
||||
while ((m = dstrings.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
dstrings.lastIndex = m.index;
|
||||
}
|
||||
|
||||
// Remove comments
|
||||
const scomments = /\/\/[^\n\r]*/g;
|
||||
while ((m = scomments.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
scomments.lastIndex = m.index;
|
||||
}
|
||||
|
||||
const mcomments = /\/\*[\s\S]*?\*\//gm;
|
||||
while ((m = mcomments.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
mcomments.lastIndex = m.index;
|
||||
}
|
||||
|
||||
const hcomments = /(^|\n)#[^\n\r#]+/g;
|
||||
while ((m = hcomments.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
hcomments.lastIndex = m.index;
|
||||
}
|
||||
|
||||
// Remove regexes
|
||||
const regexes = /\/.*?[^\\]\/[gim]{0,3}/gi;
|
||||
while ((m = regexes.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
regexes.lastIndex = m.index;
|
||||
}
|
||||
|
||||
code = code
|
||||
// Create newlines after ;
|
||||
.replace(/;/g, ";\n")
|
||||
// Create newlines after { and around }
|
||||
.replace(/{/g, "{\n")
|
||||
.replace(/}/g, "\n}\n")
|
||||
// Remove carriage returns
|
||||
.replace(/\r/g, "")
|
||||
// Remove all indentation
|
||||
.replace(/^\s+/g, "")
|
||||
.replace(/\n\s+/g, "\n")
|
||||
// Remove trailing spaces
|
||||
.replace(/\s*$/g, "")
|
||||
.replace(/\n{/g, "{");
|
||||
|
||||
// Indent
|
||||
let i = 0,
|
||||
level = 0,
|
||||
indent;
|
||||
while (i < code.length) {
|
||||
switch (code[i]) {
|
||||
case "{":
|
||||
level++;
|
||||
break;
|
||||
case "\n":
|
||||
if (i+1 >= code.length) break;
|
||||
|
||||
if (code[i+1] === "}") level--;
|
||||
indent = (level >= 0) ? Array(level*4+1).join(" ") : "";
|
||||
|
||||
code = code.substring(0, i+1) + indent + code.substring(i+1);
|
||||
if (level > 0) i += level*4;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
code = code
|
||||
// Add strategic spaces
|
||||
.replace(/\s*([!<>=+-/*]?)=\s*/g, " $1= ")
|
||||
.replace(/\s*<([=]?)\s*/g, " <$1 ")
|
||||
.replace(/\s*>([=]?)\s*/g, " >$1 ")
|
||||
.replace(/([^+])\+([^+=])/g, "$1 + $2")
|
||||
.replace(/([^-])-([^-=])/g, "$1 - $2")
|
||||
.replace(/([^*])\*([^*=])/g, "$1 * $2")
|
||||
.replace(/([^/])\/([^/=])/g, "$1 / $2")
|
||||
.replace(/\s*,\s*/g, ", ")
|
||||
.replace(/\s*{/g, " {")
|
||||
.replace(/}\n/g, "}\n\n")
|
||||
// Hacky horribleness
|
||||
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)\s*\n([^{])/gim, "$1 ($2)\n $3")
|
||||
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)([^{])/gim, "$1 ($2) $3")
|
||||
.replace(/else\s*\n([^{])/gim, "else\n $1")
|
||||
.replace(/else\s+([^{])/gim, "else $1")
|
||||
// Remove strategic spaces
|
||||
.replace(/\s+;/g, ";")
|
||||
.replace(/\{\s+\}/g, "{}")
|
||||
.replace(/\[\s+\]/g, "[]")
|
||||
.replace(/}\s*(else|catch|except|finally|elif|elseif|else if)/gi, "} $1");
|
||||
|
||||
// Replace preserved tokens
|
||||
const ptokens = /###preservedToken(\d+)###/g;
|
||||
while ((m = ptokens.exec(code))) {
|
||||
const ti = parseInt(m[1], 10);
|
||||
code = code.substring(0, m.index) + preservedTokens[ti] + code.substring(m.index + m[0].length);
|
||||
ptokens.lastIndex = m.index;
|
||||
}
|
||||
|
||||
return code;
|
||||
|
||||
/**
|
||||
* Replaces a matched token with a placeholder value.
|
||||
*/
|
||||
function preserveToken(str, match, t) {
|
||||
preservedTokens[t] = match[0];
|
||||
return str.substring(0, match.index) +
|
||||
"###preservedToken" + t + "###" +
|
||||
str.substring(match.index + match[0].length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GenericCodeBeautify;
|
78
src/core/operations/SyntaxHighlighter.mjs
Normal file
78
src/core/operations/SyntaxHighlighter.mjs
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import hljs from "highlight.js";
|
||||
|
||||
/**
|
||||
* Syntax highlighter operation
|
||||
*/
|
||||
class SyntaxHighlighter extends Operation {
|
||||
|
||||
/**
|
||||
* SyntaxHighlighter constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Syntax highlighter";
|
||||
this.module = "Code";
|
||||
this.description = "Adds syntax highlighting to a range of source code languages. Note that this will not indent the code. Use one of the 'Beautify' operations for that.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "html";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Language",
|
||||
"type": "option",
|
||||
"value": ["auto detect"].concat(hljs.listLanguages())
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
run(input, args) {
|
||||
const language = args[0];
|
||||
|
||||
if (language === "auto detect") {
|
||||
return hljs.highlightAuto(input).value;
|
||||
}
|
||||
|
||||
return hljs.highlight(language, input, true).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight Syntax highlighter
|
||||
*
|
||||
* @param {Object[]} pos
|
||||
* @param {number} pos[].start
|
||||
* @param {number} pos[].end
|
||||
* @param {Object[]} args
|
||||
* @returns {Object[]} pos
|
||||
*/
|
||||
highlight(pos, args) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight Syntax highlighter in reverse
|
||||
*
|
||||
* @param {Object[]} pos
|
||||
* @param {number} pos[].start
|
||||
* @param {number} pos[].end
|
||||
* @param {Object[]} args
|
||||
* @returns {Object[]} pos
|
||||
*/
|
||||
highlightReverse(pos, args) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default SyntaxHighlighter;
|
53
src/core/operations/ToCamelCase.mjs
Normal file
53
src/core/operations/ToCamelCase.mjs
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { camelCase } from "lodash";
|
||||
import Operation from "../Operation";
|
||||
import { replaceVariableNames } from "../lib/Code";
|
||||
|
||||
/**
|
||||
* To Camel case operation
|
||||
*/
|
||||
class ToCamelCase extends Operation {
|
||||
|
||||
/**
|
||||
* ToCamelCase constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Camel case";
|
||||
this.module = "Code";
|
||||
this.description = "Converts the input string to camel case.\n<br><br>\nCamel case is all lower case except letters after word boundaries which are uppercase.\n<br><br>\ne.g. thisIsCamelCase\n<br><br>\n'Attempt to be context aware' will make the operation attempt to nicely transform variable and function names.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Attempt to be context aware",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return replaceVariableNames(input, camelCase);
|
||||
} else {
|
||||
return camelCase(input);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToCamelCase;
|
53
src/core/operations/ToKebabCase.mjs
Normal file
53
src/core/operations/ToKebabCase.mjs
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { kebabCase } from "lodash";
|
||||
import Operation from "../Operation";
|
||||
import { replaceVariableNames } from "../lib/Code";
|
||||
|
||||
/**
|
||||
* To Kebab case operation
|
||||
*/
|
||||
class ToKebabCase extends Operation {
|
||||
|
||||
/**
|
||||
* ToKebabCase constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Kebab case";
|
||||
this.module = "Code";
|
||||
this.description = "Converts the input string to kebab case.\n<br><br>\nKebab case is all lower case with dashes as word boundaries.\n<br><br>\ne.g. this-is-kebab-case\n<br><br>\n'Attempt to be context aware' will make the operation attempt to nicely transform variable and function names.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Attempt to be context aware",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return replaceVariableNames(input, kebabCase);
|
||||
} else {
|
||||
return kebabCase(input);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToKebabCase;
|
52
src/core/operations/ToSnakeCase.mjs
Normal file
52
src/core/operations/ToSnakeCase.mjs
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { snakeCase } from "lodash";
|
||||
import Operation from "../Operation";
|
||||
import { replaceVariableNames } from "../lib/Code";
|
||||
|
||||
/**
|
||||
* To Snake case operation
|
||||
*/
|
||||
class ToSnakeCase extends Operation {
|
||||
|
||||
/**
|
||||
* ToSnakeCase constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Snake case";
|
||||
this.module = "Code";
|
||||
this.description = "Converts the input string to snake case.\n<br><br>\nSnake case is all lower case with underscores as word boundaries.\n<br><br>\ne.g. this_is_snake_case\n<br><br>\n'Attempt to be context aware' will make the operation attempt to nicely transform variable and function names.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Attempt to be context aware",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return replaceVariableNames(input, snakeCase);
|
||||
} else {
|
||||
return snakeCase(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ToSnakeCase;
|
Loading…
x
Reference in New Issue
Block a user