No-parse JSON prettify (#80)

* Multiple recursive rendering

* Added custom JSON prettify that doesn't require parsing to JS

* Fixed spacing issues
This commit is contained in:
Gregory Schier 2017-02-07 09:13:59 -08:00 committed by GitHub
parent 4865807822
commit 61a27b5c5f
4 changed files with 145 additions and 12 deletions

View File

@ -71,7 +71,34 @@ describe('buildRenderContext()', () => {
const context = renderUtils.buildRenderContext(ancestors); const context = renderUtils.buildRenderContext(ancestors);
expect(context).toEqual({recursive: '{{ recursive }}/hello/hello'}); // This is longer than 3 because it multiplies every time (1 -> 2 -> 4 -> 8)
expect(context).toEqual({
recursive: '{{ recursive }}/hello/hello/hello/hello/hello/hello/hello/hello'
});
});
it('render up to 3 recursion levels', () => {
const ancestors = [{
// Sub Environment
type: models.requestGroup.type,
environment: {
d: '/d',
c: '/c{{ d }}',
b: '/b{{ c }}',
a: '/a{{ b }}',
test: 'http://insomnia.rest{{ a }}'
}
}];
const context = renderUtils.buildRenderContext(ancestors);
expect(context).toEqual({
d: '/d',
c: '/c/d',
b: '/b/c/d',
a: '/a/b/c/d',
test: 'http://insomnia.rest/a/b/c/d',
});
}); });
it('rendered sibling environment variables', () => { it('rendered sibling environment variables', () => {

97
app/common/prettify.js Normal file
View File

@ -0,0 +1,97 @@
/**
* Format a JSON string without parsing it as JavaScript.
*
* Code taken from jsonlint (http://zaa.ch/jsonlint/)
*
* @param json
* @param indentChars
* @returns {string}
*/
export function prettifyJson (json, indentChars) {
let i = 0;
let il = json.length;
let tab = (typeof indentChars !== 'undefined') ? indentChars : ' ';
let newJson = '';
let indentLevel = 0;
let inString = false;
let currentChar = null;
let previousChar = null;
let nextChar = null;
for (;i < il; i += 1) {
currentChar = json.charAt(i);
previousChar = json.charAt(i - 1);
nextChar = json.charAt(i + 1);
switch (currentChar) {
case '{':
if (!inString && nextChar !== '}') {
newJson += currentChar + '\n' + _repeatString(tab, indentLevel + 1);
indentLevel += 1;
} else {
newJson += currentChar;
}
break;
case '[':
if (!inString && nextChar !== ']') {
newJson += currentChar + '\n' + _repeatString(tab, indentLevel + 1);
indentLevel += 1;
} else {
newJson += currentChar;
}
break;
case '}':
if (!inString && previousChar !== '{') {
indentLevel -= 1;
newJson += '\n' + _repeatString(tab, indentLevel) + currentChar;
} else {
newJson += currentChar;
}
break;
case ']':
if (!inString && previousChar !== '[') {
indentLevel -= 1;
newJson += '\n' + _repeatString(tab, indentLevel) + currentChar;
} else {
newJson += currentChar;
}
break;
case ',':
if (!inString) {
newJson += ',\n' + _repeatString(tab, indentLevel);
} else {
newJson += currentChar;
}
break;
case ':':
if (!inString) {
newJson += ': ';
} else {
newJson += currentChar;
}
break;
case ' ':
case '\n':
case '\t':
if (inString) {
newJson += currentChar;
}
break;
case '"':
if (i > 0 && previousChar !== '\\') {
inString = !inString;
}
newJson += currentChar;
break;
default:
newJson += currentChar;
break;
}
}
return newJson;
}
function _repeatString(s, count) {
return new Array(count + 1).join(s);
}

View File

@ -92,7 +92,14 @@ export function buildRenderContext (ancestors, rootEnvironment, subEnvironment)
} }
// Render the context with itself to fill in the rest. // Render the context with itself to fill in the rest.
return recursiveRender(renderContext, renderContext); let finalRenderContext = renderContext;
// Render up to 5 levels of recursive references.
for (let i = 0; i < 3; i++) {
finalRenderContext = recursiveRender(finalRenderContext, finalRenderContext);
}
return finalRenderContext;
} }
export function recursiveRender (obj, context) { export function recursiveRender (obj, context) {
@ -178,7 +185,7 @@ function _objectDeepAssignRender (base, obj) {
* *
* A regular Object.assign would yield { base_url: '{{ base_url }}/foo' } and the * A regular Object.assign would yield { base_url: '{{ base_url }}/foo' } and the
* original base_url of google.com would be lost. * original base_url of google.com would be lost.
*/ */
if (typeof base[key] === 'string') { if (typeof base[key] === 'string') {
base[key] = render(obj[key], base); base[key] = render(obj[key], base);
} else { } else {

View File

@ -49,6 +49,7 @@ import * as misc from '../../../common/misc';
import {trackEvent} from '../../../analytics/index'; import {trackEvent} from '../../../analytics/index';
// Make jsonlint available to the jsonlint plugin // Make jsonlint available to the jsonlint plugin
import {parser as jsonlint} from 'jsonlint'; import {parser as jsonlint} from 'jsonlint';
import {prettifyJson} from '../../../common/prettify';
global.jsonlint = jsonlint; global.jsonlint = jsonlint;
@ -170,36 +171,37 @@ class Editor extends Component {
this._prettify(this.codeMirror.getValue()); this._prettify(this.codeMirror.getValue());
} }
async _prettify (code) { _prettify (code) {
if (this._isXML(this.props.mode)) { if (this._isXML(this.props.mode)) {
code = this._formatXML(code); code = this._prettifyXML(code);
} else { } else {
code = this._formatJSON(code); code = this._prettifyJSON(code);
} }
this.codeMirror.setValue(code); this.codeMirror.setValue(code);
} }
_formatJSON (code) { _prettifyJSON (code) {
try { try {
let obj = JSON.parse(code); let jsonString = code;
if (this.props.updateFilter && this.state.filter) { if (this.props.updateFilter && this.state.filter) {
let obj = JSON.parse(code);
try { try {
obj = jq.query(obj, this.state.filter); jsonString = JSON.stringify(jq.query(obj, this.state.filter));
} catch (err) { } catch (err) {
obj = '[]'; jsonString = '[]';
} }
} }
return vkBeautify.json(obj, '\t'); return prettifyJson(jsonString, '\t');
} catch (e) { } catch (e) {
// That's Ok, just leave it // That's Ok, just leave it
return code; return code;
} }
} }
_formatXML (code) { _prettifyXML (code) {
if (this.props.updateFilter && this.state.filter) { if (this.props.updateFilter && this.state.filter) {
try { try {
const dom = new DOMParser().parseFromString(code); const dom = new DOMParser().parseFromString(code);