add json worker
This commit is contained in:
parent
edaf0d0ce0
commit
099f1541fd
7 changed files with 586 additions and 12 deletions
|
|
@ -232,7 +232,7 @@ function buildAce(aceProject, options) {
|
|||
"tomorrow_night_blue", "tomorrow_night_bright", "tomorrow_night_eighties",
|
||||
"twilight", "vibrant_ink"
|
||||
],
|
||||
workers: ["javascript", "coffee", "css"],
|
||||
workers: ["javascript", "coffee", "css", "json"],
|
||||
keybindings: ["vim", "emacs"]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var Mirror = require("../worker/mirror").Mirror;
|
||||
var coffee = require("../mode/coffee/coffee-script");
|
||||
|
|
@ -53,29 +53,29 @@ var Worker = exports.Worker = function(sender) {
|
|||
oop.inherits(Worker, Mirror);
|
||||
|
||||
(function() {
|
||||
|
||||
|
||||
this.onUpdate = function() {
|
||||
var value = this.doc.getValue();
|
||||
|
||||
|
||||
try {
|
||||
coffee.parse(value);
|
||||
} catch(e) {
|
||||
var m = e.message.match(/Parse error on line (\d+): (.*)/);
|
||||
if (m) {
|
||||
this.sender.emit("error", {
|
||||
row: parseInt(m[1]) - 1,
|
||||
row: parseInt(m[1], 10) - 1,
|
||||
column: null,
|
||||
text: m[2],
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (e instanceof SyntaxError) {
|
||||
var m = e.message.match(/ on line (\d+)/);
|
||||
if (m) {
|
||||
if (m) {
|
||||
this.sender.emit("error", {
|
||||
row: parseInt(m[1]) - 1,
|
||||
row: parseInt(m[1], 10) - 1,
|
||||
column: null,
|
||||
text: e.message.replace(m[0], ""),
|
||||
type: "error"
|
||||
|
|
@ -86,7 +86,7 @@ oop.inherits(Worker, Mirror);
|
|||
}
|
||||
this.sender.emit("ok");
|
||||
};
|
||||
|
||||
|
||||
}).call(Worker.prototype);
|
||||
|
||||
});
|
||||
|
|
@ -45,6 +45,7 @@ var HighlightRules = require("./json_highlight_rules").JsonHighlightRules;
|
|||
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
|
||||
var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
|
||||
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
|
||||
var WorkerClient = require("../worker/worker_client").WorkerClient;
|
||||
|
||||
var Mode = function() {
|
||||
this.$tokenizer = new Tokenizer(new HighlightRules().getRules());
|
||||
|
|
@ -76,7 +77,23 @@ oop.inherits(Mode, TextMode);
|
|||
this.autoOutdent = function(state, doc, row) {
|
||||
this.$outdent.autoOutdent(doc, row);
|
||||
};
|
||||
|
||||
|
||||
this.createWorker = function(session) {
|
||||
var worker = new WorkerClient(["ace"], "worker-json.js", "ace/mode/json_worker", "JsonWorker");
|
||||
worker.attachToDocument(session.getDocument());
|
||||
|
||||
worker.on("error", function(e) {
|
||||
session.setAnnotations([e.data]);
|
||||
});
|
||||
|
||||
worker.on("ok", function() {
|
||||
session.clearAnnotations();
|
||||
});
|
||||
|
||||
return worker;
|
||||
};
|
||||
|
||||
|
||||
}).call(Mode.prototype);
|
||||
|
||||
exports.Mode = Mode;
|
||||
|
|
|
|||
346
lib/ace/mode/json/json_parse.js
Normal file
346
lib/ace/mode/json/json_parse.js
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
http://www.JSON.org/json_parse.js
|
||||
2008-09-18
|
||||
|
||||
Public Domain.
|
||||
|
||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
|
||||
This file creates a json_parse function.
|
||||
|
||||
json_parse(text, reviver)
|
||||
This method parses a JSON text to produce an object or array.
|
||||
It can throw a SyntaxError exception.
|
||||
|
||||
The optional reviver parameter is a function that can filter and
|
||||
transform the results. It receives each of the keys and values,
|
||||
and its return value is used instead of the original value.
|
||||
If it returns what it received, then the structure is not modified.
|
||||
If it returns undefined then the member is deleted.
|
||||
|
||||
Example:
|
||||
|
||||
// Parse the text. Values that look like ISO date strings will
|
||||
// be converted to Date objects.
|
||||
|
||||
myData = json_parse(text, function (key, value) {
|
||||
var a;
|
||||
if (typeof value === 'string') {
|
||||
a =
|
||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
||||
if (a) {
|
||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
||||
+a[5], +a[6]));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
This is a reference implementation. You are free to copy, modify, or
|
||||
redistribute.
|
||||
|
||||
This code should be minified before deployment.
|
||||
See http://javascript.crockford.com/jsmin.html
|
||||
|
||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
||||
NOT CONTROL.
|
||||
*/
|
||||
|
||||
/*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode,
|
||||
hasOwnProperty, message, n, name, push, r, t, text
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
// This is a function that can parse a JSON text, producing a JavaScript
|
||||
// data structure. It is a simple, recursive descent parser. It does not use
|
||||
// eval or regular expressions, so it can be used as a model for implementing
|
||||
// a JSON parser in other languages.
|
||||
|
||||
// We are defining the function inside of another function to avoid creating
|
||||
// global variables.
|
||||
|
||||
var at, // The index of the current character
|
||||
ch, // The current character
|
||||
escapee = {
|
||||
'"': '"',
|
||||
'\\': '\\',
|
||||
'/': '/',
|
||||
b: '\b',
|
||||
f: '\f',
|
||||
n: '\n',
|
||||
r: '\r',
|
||||
t: '\t'
|
||||
},
|
||||
text,
|
||||
|
||||
error = function (m) {
|
||||
|
||||
// Call error when something is wrong.
|
||||
|
||||
throw {
|
||||
name: 'SyntaxError',
|
||||
message: m,
|
||||
at: at,
|
||||
text: text
|
||||
};
|
||||
},
|
||||
|
||||
next = function (c) {
|
||||
|
||||
// If a c parameter is provided, verify that it matches the current character.
|
||||
|
||||
if (c && c !== ch) {
|
||||
error("Expected '" + c + "' instead of '" + ch + "'");
|
||||
}
|
||||
|
||||
// Get the next character. When there are no more characters,
|
||||
// return the empty string.
|
||||
|
||||
ch = text.charAt(at);
|
||||
at += 1;
|
||||
return ch;
|
||||
},
|
||||
|
||||
number = function () {
|
||||
|
||||
// Parse a number value.
|
||||
|
||||
var number,
|
||||
string = '';
|
||||
|
||||
if (ch === '-') {
|
||||
string = '-';
|
||||
next('-');
|
||||
}
|
||||
while (ch >= '0' && ch <= '9') {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
if (ch === '.') {
|
||||
string += '.';
|
||||
while (next() && ch >= '0' && ch <= '9') {
|
||||
string += ch;
|
||||
}
|
||||
}
|
||||
if (ch === 'e' || ch === 'E') {
|
||||
string += ch;
|
||||
next();
|
||||
if (ch === '-' || ch === '+') {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
while (ch >= '0' && ch <= '9') {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
}
|
||||
number = +string;
|
||||
if (isNaN(number)) {
|
||||
error("Bad number");
|
||||
} else {
|
||||
return number;
|
||||
}
|
||||
},
|
||||
|
||||
string = function () {
|
||||
|
||||
// Parse a string value.
|
||||
|
||||
var hex,
|
||||
i,
|
||||
string = '',
|
||||
uffff;
|
||||
|
||||
// When parsing for string values, we must look for " and \ characters.
|
||||
|
||||
if (ch === '"') {
|
||||
while (next()) {
|
||||
if (ch === '"') {
|
||||
next();
|
||||
return string;
|
||||
} else if (ch === '\\') {
|
||||
next();
|
||||
if (ch === 'u') {
|
||||
uffff = 0;
|
||||
for (i = 0; i < 4; i += 1) {
|
||||
hex = parseInt(next(), 16);
|
||||
if (!isFinite(hex)) {
|
||||
break;
|
||||
}
|
||||
uffff = uffff * 16 + hex;
|
||||
}
|
||||
string += String.fromCharCode(uffff);
|
||||
} else if (typeof escapee[ch] === 'string') {
|
||||
string += escapee[ch];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
string += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
error("Bad string");
|
||||
},
|
||||
|
||||
white = function () {
|
||||
|
||||
// Skip whitespace.
|
||||
|
||||
while (ch && ch <= ' ') {
|
||||
next();
|
||||
}
|
||||
},
|
||||
|
||||
word = function () {
|
||||
|
||||
// true, false, or null.
|
||||
|
||||
switch (ch) {
|
||||
case 't':
|
||||
next('t');
|
||||
next('r');
|
||||
next('u');
|
||||
next('e');
|
||||
return true;
|
||||
case 'f':
|
||||
next('f');
|
||||
next('a');
|
||||
next('l');
|
||||
next('s');
|
||||
next('e');
|
||||
return false;
|
||||
case 'n':
|
||||
next('n');
|
||||
next('u');
|
||||
next('l');
|
||||
next('l');
|
||||
return null;
|
||||
}
|
||||
error("Unexpected '" + ch + "'");
|
||||
},
|
||||
|
||||
value, // Place holder for the value function.
|
||||
|
||||
array = function () {
|
||||
|
||||
// Parse an array value.
|
||||
|
||||
var array = [];
|
||||
|
||||
if (ch === '[') {
|
||||
next('[');
|
||||
white();
|
||||
if (ch === ']') {
|
||||
next(']');
|
||||
return array; // empty array
|
||||
}
|
||||
while (ch) {
|
||||
array.push(value());
|
||||
white();
|
||||
if (ch === ']') {
|
||||
next(']');
|
||||
return array;
|
||||
}
|
||||
next(',');
|
||||
white();
|
||||
}
|
||||
}
|
||||
error("Bad array");
|
||||
},
|
||||
|
||||
object = function () {
|
||||
|
||||
// Parse an object value.
|
||||
|
||||
var key,
|
||||
object = {};
|
||||
|
||||
if (ch === '{') {
|
||||
next('{');
|
||||
white();
|
||||
if (ch === '}') {
|
||||
next('}');
|
||||
return object; // empty object
|
||||
}
|
||||
while (ch) {
|
||||
key = string();
|
||||
white();
|
||||
next(':');
|
||||
if (Object.hasOwnProperty.call(object, key)) {
|
||||
error('Duplicate key "' + key + '"');
|
||||
}
|
||||
object[key] = value();
|
||||
white();
|
||||
if (ch === '}') {
|
||||
next('}');
|
||||
return object;
|
||||
}
|
||||
next(',');
|
||||
white();
|
||||
}
|
||||
}
|
||||
error("Bad object");
|
||||
};
|
||||
|
||||
value = function () {
|
||||
|
||||
// Parse a JSON value. It could be an object, an array, a string, a number,
|
||||
// or a word.
|
||||
|
||||
white();
|
||||
switch (ch) {
|
||||
case '{':
|
||||
return object();
|
||||
case '[':
|
||||
return array();
|
||||
case '"':
|
||||
return string();
|
||||
case '-':
|
||||
return number();
|
||||
default:
|
||||
return ch >= '0' && ch <= '9' ? number() : word();
|
||||
}
|
||||
};
|
||||
|
||||
// Return the json_parse function. It will have access to all of the above
|
||||
// functions and variables.
|
||||
|
||||
return function (source, reviver) {
|
||||
var result;
|
||||
|
||||
text = source;
|
||||
at = 0;
|
||||
ch = ' ';
|
||||
result = value();
|
||||
white();
|
||||
if (ch) {
|
||||
error("Syntax error");
|
||||
}
|
||||
|
||||
// If there is a reviver function, we recursively walk the new structure,
|
||||
// passing each name/value pair to the reviver function for possible
|
||||
// transformation, starting with a temporary root object that holds the result
|
||||
// in an empty key. If there is not a reviver function, we simply return the
|
||||
// result.
|
||||
|
||||
return typeof reviver === 'function' ? function walk(holder, key) {
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}({'': result}, '') : result;
|
||||
};
|
||||
});
|
||||
103
lib/ace/mode/json_worker.js
Normal file
103
lib/ace/mode/json_worker.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Ajax.org Code Editor (ACE).
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Ajax.org B.V.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Fabian Jakobs <fabian AT ajax DOT org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var Mirror = require("../worker/mirror").Mirror;
|
||||
var parse = require("./json/json_parse");
|
||||
|
||||
var JsonWorker = exports.JsonWorker = function(sender) {
|
||||
Mirror.call(this, sender);
|
||||
this.setTimeout(200);
|
||||
};
|
||||
|
||||
oop.inherits(JsonWorker, Mirror);
|
||||
|
||||
(function() {
|
||||
|
||||
this.onUpdate = function() {
|
||||
var value = this.doc.getValue();
|
||||
|
||||
try {
|
||||
var result = parse(value);
|
||||
} catch (e) {
|
||||
var pos = this.charToDocumentPosition(e.at-1);
|
||||
this.sender.emit("error", {
|
||||
row: pos.row,
|
||||
column: pos.column,
|
||||
text: e.message,
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.sender.emit("ok");
|
||||
};
|
||||
|
||||
this.charToDocumentPosition = function(charPos) {
|
||||
var i = 0;
|
||||
var len = this.doc.getLength();
|
||||
var nl = this.doc.getNewLineCharacter().length;
|
||||
|
||||
if (!len) {
|
||||
return { row: 0, column: 0};
|
||||
}
|
||||
|
||||
var lineStart = 0;
|
||||
while (i < len) {
|
||||
var line = this.doc.getLine(i);
|
||||
var lineLength = line.length + nl;
|
||||
if (lineStart + lineLength > charPos)
|
||||
return {
|
||||
row: i,
|
||||
column: charPos - lineStart
|
||||
};
|
||||
|
||||
lineStart += lineLength;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return {
|
||||
row: i-1,
|
||||
column: line.length
|
||||
};
|
||||
};
|
||||
|
||||
}).call(JsonWorker.prototype);
|
||||
|
||||
});
|
||||
108
lib/ace/mode/json_worker_test.js
Normal file
108
lib/ace/mode/json_worker_test.js
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Ajax.org Code Editor (ACE).
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Ajax.org B.V.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Fabian Jakobs <fabian AT ajax DOT org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
if (typeof process !== "undefined") {
|
||||
require("amd-loader");
|
||||
}
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var assert = require("../test/assertions");
|
||||
var Worker = require("./json_worker").JsonWorker;
|
||||
|
||||
|
||||
module.exports = {
|
||||
setUp : function() {
|
||||
this.sender = {
|
||||
on: function() {},
|
||||
callback: function(data, id) {
|
||||
this.data = data;
|
||||
},
|
||||
events: [],
|
||||
emit: function(type, e) {
|
||||
this.events.push([type, e]);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
"test check valid json": function() {
|
||||
var worker = new Worker(this.sender);
|
||||
worker.setValue("{}");
|
||||
worker.deferredUpdate.call();
|
||||
|
||||
assert.equal(this.sender.events[0][0], "ok");
|
||||
},
|
||||
|
||||
"test check for syntax error": function() {
|
||||
var worker = new Worker(this.sender);
|
||||
worker.setValue([
|
||||
"{",
|
||||
"juhu: 12",
|
||||
"}"
|
||||
].join("\n"));
|
||||
worker.deferredUpdate.call();
|
||||
|
||||
var event = this.sender.events[0];
|
||||
assert.equal(event[0], "error");
|
||||
assert.equal(event[1].type, "error");
|
||||
assert.equal(event[1].text, "Bad string");
|
||||
assert.equal(event[1].row, 1);
|
||||
assert.equal(event[1].column, 0);
|
||||
|
||||
},
|
||||
|
||||
"test check for syntax error at first char": function() {
|
||||
var worker = new Worker(this.sender);
|
||||
worker.setValue("x");
|
||||
worker.deferredUpdate.call();
|
||||
|
||||
var event = this.sender.events[0];
|
||||
assert.equal(event[0], "error");
|
||||
assert.equal(event[1].type, "error");
|
||||
assert.equal(event[1].text, "Unexpected 'x'");
|
||||
assert.equal(event[1].row, 0);
|
||||
assert.equal(event[1].column, 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec();
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ var WorkerClient = function(topLevelNamespaces, packagedJs, mod, classname) {
|
|||
oop.implement(this, EventEmitter);
|
||||
|
||||
this.$normalizePath = function(path) {
|
||||
path = path.replace(/^[a-z]+:\/\/[^\/]+\//, ""); // Remove domain name and rebuild it
|
||||
path = path.replace(/^[a-z]+:\/\/[^\/]+/, ""); // Remove domain name and rebuild it
|
||||
path = location.protocol + "//" + location.host
|
||||
// paths starting with a slash are relative to the root (host)
|
||||
+ (path.charAt(0) == "/" ? "" : location.pathname.replace(/\/[^\/]*$/, ""))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue