From c5378b54e19a1d92a23f0ee13f79543b6a0c54c5 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 8 Jul 2013 13:58:57 +0400 Subject: [PATCH 1/4] fix #1508: jshint is broken in build --- Makefile.dryice.js | 4 +- demo/kitchen-sink/require.js | 24 +- lib/ace/mode/javascript/jshint.js | 63 +++-- lib/ace/mode/lua/luaparse.js | 384 ++++++++++++++++++++---------- lib/ace/worker/worker.js | 3 +- tool/update_deps.js | 27 ++- 6 files changed, 334 insertions(+), 171 deletions(-) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index 487bd2ce..660c8943 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -603,7 +603,9 @@ function namespace(ns) { text = text .toString() .replace('var ACE_NAMESPACE = "";', 'var ACE_NAMESPACE = "' + ns +'";') - .replace(/\bdefine\(/g, ns + ".define("); + .replace(/(.define)|\bdefine\(/g, function(_, a) { + return a || ns + ".define(" + }); return text; }; diff --git a/demo/kitchen-sink/require.js b/demo/kitchen-sink/require.js index 2109a251..3a061fdb 100644 --- a/demo/kitchen-sink/require.js +++ b/demo/kitchen-sink/require.js @@ -1,5 +1,5 @@ /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.7 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -12,7 +12,7 @@ var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.1.6', + version = '2.1.7', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, @@ -1794,6 +1794,19 @@ var requirejs, require, define; */ req.onError = defaultOnError; + /** + * Creates the node for the load command. Only used in browser envs. + */ + req.createNode = function (config, moduleName, url) { + var node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + node.async = true; + return node; + }; + /** * Does the request to load a module for the browser case. * Make this a separate function to allow other environments @@ -1808,12 +1821,7 @@ var requirejs, require, define; node; if (isBrowser) { //In the browser so use a script tag - node = config.xhtml ? - document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : - document.createElement('script'); - node.type = config.scriptType || 'text/javascript'; - node.charset = 'utf-8'; - node.async = true; + node = req.createNode(config, moduleName, url); node.setAttribute('data-requirecontext', context.contextName); node.setAttribute('data-requiremodule', moduleName); diff --git a/lib/ace/mode/javascript/jshint.js b/lib/ace/mode/javascript/jshint.js index 45091639..7c02574d 100644 --- a/lib/ace/mode/javascript/jshint.js +++ b/lib/ace/mode/javascript/jshint.js @@ -197,7 +197,7 @@ require.alias = function (from, to) { })(); -require.define("path",function(require,module,exports,__dirname,__filename,process,global) { +require.def("path",function(req,module,exports,__dirname,__filename,process,global) { function filter (xs, fn) { var res = []; for (var i = 0; i < xs.length; i++) { @@ -336,7 +336,7 @@ exports.extname = function(path) { //@ sourceURL=path }); -require.define("__browserify_process",function(require,module,exports,__dirname,__filename,process,global) { +require.def("__browserify_process",function(req,module,exports,__dirname,__filename,process,global) { var process = module.exports = {}; process.nextTick = (function () { @@ -379,7 +379,7 @@ process.env = {}; process.argv = []; process.binding = function (name) { - if (name === 'evals') return (require)('vm') + if (name === 'evals') return (req)('vm') else throw new Error('No such module. (Possibly not yet loaded)') }; @@ -388,7 +388,7 @@ process.binding = function (name) { var path; process.cwd = function () { return cwd }; process.chdir = function (dir) { - if (!path) path = require('path'); + if (!path) path = req('path'); cwd = path.resolve(dir, cwd); }; })(); @@ -396,12 +396,12 @@ process.binding = function (name) { //@ sourceURL=__browserify_process }); -require.define("/node_modules/underscore/package.json",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/node_modules/underscore/package.json",function(req,module,exports,__dirname,__filename,process,global) { module.exports = {"main":"underscore.js"} //@ sourceURL=/node_modules/underscore/package.json }); -require.define("/node_modules/underscore/underscore.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/node_modules/underscore/underscore.js",function(req,module,exports,__dirname,__filename,process,global) { // Underscore.js 1.4.0 // http://underscorejs.org // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. @@ -1592,7 +1592,7 @@ require.define("/node_modules/underscore/underscore.js",function(require,module, //@ sourceURL=/node_modules/underscore/underscore.js }); -require.define("events",function(require,module,exports,__dirname,__filename,process,global) { +require.def("events",function(req,module,exports,__dirname,__filename,process,global) { if (!process.EventEmitter) process.EventEmitter = function () {}; var EventEmitter = exports.EventEmitter = process.EventEmitter; @@ -1609,7 +1609,7 @@ var isArray = typeof Array.isArray === 'function' // // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. -var defaultMaxListeners = 10; +var defaultMaxListeners = 200; EventEmitter.prototype.setMaxListeners = function(n) { if (!this._events) this._events = {}; this._events.maxListeners = n; @@ -1768,7 +1768,7 @@ EventEmitter.prototype.listeners = function(type) { //@ sourceURL=events }); -require.define("/src/shared/vars.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/shared/vars.js",function(req,module,exports,__dirname,__filename,process,global) { "use strict"; // Identifiers provided by the ECMAScript standard. @@ -2160,10 +2160,10 @@ exports.yui = { //@ sourceURL=/src/shared/vars.js }); -require.define("/src/shared/messages.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/shared/messages.js",function(req,module,exports,__dirname,__filename,process,global) { "use strict"; -var _ = require("underscore"); +var _ = req("underscore"); var errors = { // JSHint options @@ -2371,17 +2371,17 @@ _.each(info, function (desc, code) { //@ sourceURL=/src/shared/messages.js }); -require.define("/src/stable/lex.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/stable/lex.js",function(req,module,exports,__dirname,__filename,process,global) { /* * Lexical analysis and token construction. */ "use strict"; -var _ = require("underscore"); -var events = require("events"); -var reg = require("./reg.js"); -var state = require("./state.js").state; +var _ = req("underscore"); +var events = req("events"); +var reg = req("./reg.js"); +var state = req("./state.js").state; // Some of these token types are from JavaScript Parser API // while others are specific to JSHint parser. @@ -3987,7 +3987,7 @@ exports.Lexer = Lexer; //@ sourceURL=/src/stable/lex.js }); -require.define("/src/stable/reg.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/stable/reg.js",function(req,module,exports,__dirname,__filename,process,global) { /* * Regular expressions. Some of these are stupidly long. */ @@ -4025,7 +4025,7 @@ exports.fallsThrough = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; //@ sourceURL=/src/stable/reg.js }); -require.define("/src/stable/state.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/stable/state.js",function(req,module,exports,__dirname,__filename,process,global) { "use strict"; var state = { @@ -4051,7 +4051,7 @@ exports.state = state; //@ sourceURL=/src/stable/state.js }); -require.define("/src/stable/style.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/stable/style.js",function(req,module,exports,__dirname,__filename,process,global) { "use strict"; exports.register = function (linter) { @@ -4226,7 +4226,7 @@ exports.register = function (linter) { //@ sourceURL=/src/stable/style.js }); -require.define("/src/stable/jshint.js",function(require,module,exports,__dirname,__filename,process,global) { +require.def("/src/stable/jshint.js",function(req,module,exports,__dirname,__filename,process,global) { /*! * JSHint, by JSHint Community. * @@ -4259,14 +4259,14 @@ require.define("/src/stable/jshint.js",function(require,module,exports,__dirname /*jshint quotmark:double */ -var _ = require("underscore"); -var events = require("events"); -var vars = require("../shared/vars.js"); -var messages = require("../shared/messages.js"); -var Lexer = require("./lex.js").Lexer; -var reg = require("./reg.js"); -var state = require("./state.js").state; -var style = require("./style.js"); +var _ = req("underscore"); +var events = req("events"); +var vars = req("../shared/vars.js"); +var messages = req("../shared/messages.js"); +var Lexer = req("./lex.js").Lexer; +var reg = req("./reg.js"); +var state = req("./state.js").state; +var style = req("./style.js"); // We build the application inside a function so that we produce only a single // global variable. That function will be invoked immediately, and its return @@ -7888,8 +7888,7 @@ if (typeof exports === "object" && exports) { //@ sourceURL=/src/stable/jshint.js }); -require("/src/stable/jshint.js"); +req("/src/stable/jshint.js"); -var jsHint = require("/src/stable/jshint.js"); -return function(require, exports, module) {module.exports = jsHint;} -}()) \ No newline at end of file +function req() {return require.apply(this, arguments)}module.exports = req("/src/stable/jshint.js"); +}) \ No newline at end of file diff --git a/lib/ace/mode/lua/luaparse.js b/lib/ace/mode/lua/luaparse.js index be4d5af4..75efb153 100644 --- a/lib/ace/mode/lua/luaparse.js +++ b/lib/ace/mode/lua/luaparse.js @@ -2,41 +2,11 @@ define(function(require, exports, module) { /*global exports:true module:true require:true define:true global:true */ (function (root, name, factory) { - 'use strict'; - - var freeExports = typeof exports === 'object' && exports - // While CommonJS defines `module` as an object, component define it as a - // function - , freeModule = (typeof module === 'object' || typeof module === 'function') && - module && module.exports === freeExports && module; - - // Detect free variable `global`, from Node.js or Browserified code, and use - // it as `root` - var freeGlobal = typeof global === 'object' && global; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) - root = freeGlobal; - - // Some AMD build optimizers, like r.js, check for specific condition - // patterns like the following: - if (typeof define === 'function' && define.amd) { - define(['exports'], factory); - } - // check for `exports` after `define` in case a build optimizer adds an - // `exports` object - else if (freeExports && !freeExports.nodeType) { - // in Node.js or RingoJS v0.8.0+ - if (freeModule) factory(freeModule.exports); - // in Narwhal or RingoJS v0.7.0- - else factory(freeExports); - } - // in a browser or Rhino - else { - factory((root[name] = {})); - } + factory(exports) }(this, 'luaparse', function (exports) { 'use strict'; - exports.version = '0.0.14'; + exports.version = '0.1.4'; var input, options, length; @@ -50,6 +20,12 @@ define(function(require, exports, module) { // Track identifier scopes by adding an isLocal attribute to each // identifier-node. , scope: false + // Store location information on each syntax node as + // `loc: { start: { line, column }, end: { line, column } }`. + , locations: false + // Store the start and end character locations on each syntax node as + // `range: [start, end]`. + , ranges: false }; // The available tokens expressed as enum flags so they can be checked with @@ -59,6 +35,12 @@ define(function(require, exports, module) { , NumericLiteral = 16, Punctuator = 32, BooleanLiteral = 64 , NilLiteral = 128, VarargLiteral = 256; + exports.tokenTypes = { EOF: EOF, StringLiteral: StringLiteral + , Keyword: Keyword, Identifier: Identifier, NumericLiteral: NumericLiteral + , Punctuator: Punctuator, BooleanLiteral: BooleanLiteral + , NilLiteral: NilLiteral, VarargLiteral: VarargLiteral + }; + // As this parser is a bit different from luas own, the error messages // will be different in some situations. @@ -321,8 +303,31 @@ define(function(require, exports, module) { , argument: argument }; } + + , comment: function(value, raw) { + return { + type: 'Comment' + , value: value + , raw: raw + }; + } }; + // Wrap up the node object. + + function finishNode(node) { + // Pop a `Marker` off the location-array and attach its location data. + if (trackLocations) { + var location = locations.pop(); + location.complete(); + if (options.locations) node.loc = location.loc; + if (options.ranges) node.range = location.range; + } + return node; + } + + + // Helpers // ------- @@ -335,6 +340,16 @@ define(function(require, exports, module) { return -1; }; + // Iterate through an array of objects and return the index of an object + // with a matching property. + + function indexOfObject(array, property, element) { + for (var i = 0, length = array.length; i < length; i++) { + if (array[i][property] === element) return i; + } + return -1; + } + // A sprintf implementation using %index (beginning at 1) to input // arguments in the format string. // @@ -363,7 +378,7 @@ define(function(require, exports, module) { , dest = {} , src, prop; - for (var i = 0, l = args.length; i < l; i++) { + for (var i = 0, length = args.length; i < length; i++) { src = args[i]; for (prop in src) if (src.hasOwnProperty(prop)) { dest[prop] = src[prop]; @@ -471,6 +486,7 @@ define(function(require, exports, module) { var index , token + , previousToken , lookahead , comments , tokenStart @@ -496,14 +512,14 @@ define(function(require, exports, module) { , range: [index, index] }; - var character = input.charCodeAt(index) + var charCode = input.charCodeAt(index) , next = input.charCodeAt(index + 1); // Memorize the range index where the token begins. tokenStart = index; - if (isIdentifierStart(character)) return scanIdentifierOrKeyword(); + if (isIdentifierStart(charCode)) return scanIdentifierOrKeyword(); - switch (character) { + switch (charCode) { case 39: case 34: // '" return scanStringLiteral(); @@ -561,10 +577,10 @@ define(function(require, exports, module) { function skipWhiteSpace() { while (index < length) { - var character = input.charCodeAt(index); - if (isWhiteSpace(character)) { + var charCode = input.charCodeAt(index); + if (isWhiteSpace(charCode)) { index++; - } else if (isLineTerminator(character)) { + } else if (isLineTerminator(charCode)) { line++; lineStart = ++index; } else { @@ -640,20 +656,20 @@ define(function(require, exports, module) { var delimiter = input.charCodeAt(index++) , stringStart = index , string = '' - , character; + , charCode; while (index < length) { - character = input.charCodeAt(index++); - if (delimiter === character) break; - if (92 === character) { // \ + charCode = input.charCodeAt(index++); + if (delimiter === charCode) break; + if (92 === charCode) { // \ string += input.slice(stringStart, index - 1) + readEscapeSequence(); stringStart = index; } // EOF or `\n` terminates a string literal. If we haven't found the // ending delimiter by now, raise an exception. - else if (index >= length || isLineTerminator(character)) { + else if (index >= length || isLineTerminator(charCode)) { string += input.slice(stringStart, index - 1); - raise({}, errors.unfinishedString, string + String.fromCharCode(character)); + raise({}, errors.unfinishedString, string + String.fromCharCode(charCode)); } } string += input.slice(stringStart, index - 1); @@ -847,7 +863,9 @@ define(function(require, exports, module) { var character = input.charAt(index) , content = '' , isLong = false - , commentStart = index; + , commentStart = index + , lineStartComment = lineStart + , lineComment = line; if ('[' === character) { content = readLongString(); @@ -861,15 +879,24 @@ define(function(require, exports, module) { if (isLineTerminator(input.charCodeAt(index))) break; index++; } - content = input.slice(commentStart, index); + if (options.comments) content = input.slice(commentStart, index); } if (options.comments) { - comments.push({ - type: 'Comment' - , value: content - , raw: input.slice(tokenStart, index) - }); + var node = ast.comment(content, input.slice(tokenStart, index)); + + // `Marker`s depend on tokens available in the parser and as comments are + // intercepted in the lexer all location data is set manually. + if (options.locations) { + node.loc = { + start: { line: lineComment, column: tokenStart - lineStartComment } + , end: { line: line, column: index - lineStart } + }; + } + if (options.ranges) { + node.range = [tokenStart, index]; + } + comments.push(node); } } @@ -935,6 +962,7 @@ define(function(require, exports, module) { // reading in the new lookahead token. function next() { + previousToken = token; token = lookahead; lookahead = lex(); } @@ -959,31 +987,31 @@ define(function(require, exports, module) { // ### Validation functions - function isWhiteSpace(character) { - return 9 === character || 32 === character || 0xB === character || 0xC === character; + function isWhiteSpace(charCode) { + return 9 === charCode || 32 === charCode || 0xB === charCode || 0xC === charCode; } - function isLineTerminator(character) { - return 10 === character || 13 === character; + function isLineTerminator(charCode) { + return 10 === charCode || 13 === charCode; } - function isDecDigit(character) { - return character >= 48 && character <= 57; + function isDecDigit(charCode) { + return charCode >= 48 && charCode <= 57; } - function isHexDigit(character) { - return (character >= 48 && character <= 57) || (character >= 97 && character <= 102) || (character >= 65 && character <= 70); + function isHexDigit(charCode) { + return (charCode >= 48 && charCode <= 57) || (charCode >= 97 && charCode <= 102) || (charCode >= 65 && charCode <= 70); } // From [Lua 5.2](http://www.lua.org/manual/5.2/manual.html#8.1) onwards // identifiers cannot use locale-dependet letters. - function isIdentifierStart(character) { - return (character >= 65 && character <= 90) || (character >= 97 && character <= 122) || 95 === character; + function isIdentifierStart(charCode) { + return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode; } - function isIdentifierPart(character) { - return (character >= 65 && character <= 90) || (character >= 97 && character <= 122) || 95 === character || (character >= 48 && character <= 57); + function isIdentifierPart(charCode) { + return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode || (charCode >= 48 && charCode <= 57); } // [3.1 Lexical Conventions](http://www.lua.org/manual/5.2/manual.html#3.1) @@ -1048,11 +1076,7 @@ define(function(require, exports, module) { // The current scope index , scopeDepth // A list of all global identifier nodes. - , globals - // A list of all global identifiers names used for faster lookup. - // @TODO benchmark, with exposing the globals this entire implementation - // should probably change. - , globalNames; + , globals; // Create a new scope inheriting all declarations from the previous scope. function createScope() { @@ -1080,10 +1104,8 @@ define(function(require, exports, module) { // Attach scope information to node. If the node is global, store it in the // globals array so we can return the information to the user. function attachScope(node, isLocal) { - if (!isLocal && -1 === indexOf(globalNames, node.name)) { - globalNames.push(node.name); + if (!isLocal && -1 === indexOfObject(globals, 'name', node.name)) globals.push(node); - } node.isLocal = isLocal; } @@ -1093,6 +1115,57 @@ define(function(require, exports, module) { return (-1 !== indexOf(scopes[scopeDepth], name)); } + // Location tracking + // ----------------- + // + // Locations are stored in FILO-array as a `Marker` object consisting of both + // `loc` and `range` data. Once a `Marker` is popped off the list an end + // location is added and the data is attached to a syntax node. + + var locations = [] + , trackLocations; + + function createLocationMarker() { + return new Marker(token); + } + + function Marker(token) { + if (options.locations) { + this.loc = { + start: { + line: token.line + , column: token.range[0] - token.lineStart + } + , end: { + line: 0 + , column: 0 + } + }; + } + if (options.ranges) this.range = [token.range[0], 0]; + } + + // Complete the location data stored in the `Marker` by adding the location + // of the *previous token* as an end location. + Marker.prototype.complete = function() { + if (options.locations) { + this.loc.end.line = previousToken.line; + this.loc.end.column = previousToken.range[1] - previousToken.lineStart; + } + if (options.ranges) { + this.range[1] = previousToken.range[1]; + } + }; + + // Create a new `Marker` and add it to the FILO-array. + function markLocation() { + if (trackLocations) locations.push(createLocationMarker()); + } + + // Push an arbitrary `Marker` object onto the FILO-array. + function pushLocation(marker) { + if (trackLocations) locations.push(marker); + } // Parse functions // --------------- @@ -1103,9 +1176,12 @@ define(function(require, exports, module) { function parseChunk() { next(); + markLocation(); var body = parseBlock(); if (EOF !== token.type) unexpected(token); - return ast.chunk(body); + // If the body is empty no previousToken exists when finishNode runs. + if (trackLocations && !body.length) previousToken = token; + return finishNode(ast.chunk(body)); } // A block contains a list of statements with an optional return statement @@ -1144,6 +1220,7 @@ define(function(require, exports, module) { // | functioncall | ';' function parseStatement() { + markLocation(); if (Keyword === token.type) { switch (token.value) { case 'local': next(); return parseLocalStatement(); @@ -1164,6 +1241,9 @@ define(function(require, exports, module) { if (Punctuator === token.type) { if (consume('::')) return parseLabelStatement(); } + // Assignments memorizes the location and pushes it manually for wrapper + // nodes. Additionally empty `;` statements should not mark a location. + if (trackLocations) locations.pop(); // When a `;` is encounted, simply eat it without storing it. if (consume(';')) return; @@ -1185,13 +1265,13 @@ define(function(require, exports, module) { } expect('::'); - return ast.labelStatement(label); + return finishNode(ast.labelStatement(label)); } // break ::= 'break' function parseBreakStatement() { - return ast.breakStatement(); + return finishNode(ast.breakStatement()); } // goto ::= 'goto' Name @@ -1201,7 +1281,7 @@ define(function(require, exports, module) { , label = parseIdentifier(); if (options.scope) label.isLabel = scopeHasName('::' + name + '::'); - return ast.gotoStatement(label); + return finishNode(ast.gotoStatement(label)); } // do ::= 'do' block 'end' @@ -1209,7 +1289,7 @@ define(function(require, exports, module) { function parseDoStatement() { var body = parseBlock(); expect('end'); - return ast.doStatement(body); + return finishNode(ast.doStatement(body)); } // while ::= 'while' exp 'do' block 'end' @@ -1219,7 +1299,7 @@ define(function(require, exports, module) { expect('do'); var body = parseBlock(); expect('end'); - return ast.whileStatement(condition, body); + return finishNode(ast.whileStatement(condition, body)); } // repeat ::= 'repeat' block 'until' exp @@ -1228,7 +1308,7 @@ define(function(require, exports, module) { var body = parseBlock(); expect('until'); var condition = parseExpectedExpression(); - return ast.repeatStatement(condition, body); + return finishNode(ast.repeatStatement(condition, body)); } // retstat ::= 'return' [exp {',' exp}] [';'] @@ -1245,7 +1325,7 @@ define(function(require, exports, module) { } consume(';'); // grammar tells us ; is optional here. } - return ast.returnStatement(expressions); + return finishNode(ast.returnStatement(expressions)); } // if ::= 'if' exp 'then' block {elif} ['else' block] 'end' @@ -1254,27 +1334,42 @@ define(function(require, exports, module) { function parseIfStatement() { var clauses = [] , condition - , body; + , body + , marker; + // IfClauses begin at the same location as the parent IfStatement. + // It ends at the start of `end`, `else`, or `elseif`. + if (trackLocations) { + marker = locations[locations.length - 1]; + locations.push(marker); + } condition = parseExpectedExpression(); expect('then'); body = parseBlock(); - clauses.push(ast.ifClause(condition, body)); + clauses.push(finishNode(ast.ifClause(condition, body))); + if (trackLocations) marker = createLocationMarker(); while (consume('elseif')) { + pushLocation(marker); condition = parseExpectedExpression(); expect('then'); body = parseBlock(); - clauses.push(ast.elseifClause(condition, body)); + clauses.push(finishNode(ast.elseifClause(condition, body))); + if (trackLocations) marker = createLocationMarker(); } if (consume('else')) { + // Include the `else` in the location of ElseClause. + if (trackLocations) { + marker = new Marker(previousToken); + locations.push(marker); + } body = parseBlock(); - clauses.push(ast.elseClause(body)); + clauses.push(finishNode(ast.elseClause(body))); } expect('end'); - return ast.ifStatement(clauses); + return finishNode(ast.ifStatement(clauses)); } // There are two types of for statements, generic and numeric. @@ -1306,7 +1401,7 @@ define(function(require, exports, module) { body = parseBlock(); expect('end'); - return ast.forNumericStatement(variable, start, end, step, body); + return finishNode(ast.forNumericStatement(variable, start, end, step, body)); } // If not, it's a Generic For Statement else { @@ -1331,7 +1426,7 @@ define(function(require, exports, module) { body = parseBlock(); expect('end'); - return ast.forGenericStatement(variables, iterators, body); + return finishNode(ast.forGenericStatement(variables, iterators, body)); } } @@ -1374,7 +1469,7 @@ define(function(require, exports, module) { } } - return ast.localStatement(variables, init); + return finishNode(ast.localStatement(variables, init)); } if (consume('function')) { name = parseIdentifier(); @@ -1398,7 +1493,10 @@ define(function(require, exports, module) { // Keep a reference to the previous token for better error messages in case // of invalid statement var previous = token - , expression = parsePrefixExpression(); + , expression, marker; + + if (trackLocations) marker = createLocationMarker(); + expression = parsePrefixExpression(); if (null == expression) return unexpected(token); if (',='.indexOf(token.value) >= 0) { @@ -1416,10 +1514,13 @@ define(function(require, exports, module) { exp = parseExpectedExpression(); init.push(exp); } while (consume(',')); - return ast.assignmentStatement(variables, init); + + pushLocation(marker); + return finishNode(ast.assignmentStatement(variables, init)); } if (isCallExpression(expression)) { - return ast.callStatement(expression); + pushLocation(marker); + return finishNode(ast.callStatement(expression)); } // The prefix expression was neither part of an assignment or a // callstatement, however as it was valid it's been consumed, so raise @@ -1434,10 +1535,11 @@ define(function(require, exports, module) { // Identifier ::= Name function parseIdentifier() { + markLocation(); var identifier = token.value; if (Identifier !== token.type) raiseUnexpectedToken('', token); next(); - return ast.identifier(identifier); + return finishNode(ast.identifier(identifier)); } // Parse the functions parameters and body block. The name should already @@ -1484,7 +1586,7 @@ define(function(require, exports, module) { expect('end'); isLocal = isLocal || false; - return ast.functionStatement(name, parameters, isLocal, body); + return finishNode(ast.functionStatement(name, parameters, isLocal, body)); } // Parse the function name as identifiers and member expressions. @@ -1492,20 +1594,25 @@ define(function(require, exports, module) { // Name {'.' Name} [':' Name] function parseFunctionName() { - var base = parseIdentifier() - , name; + var base, name, marker; + + if (trackLocations) marker = createLocationMarker(); + base = parseIdentifier(); + if (options.scope) attachScope(base, false); while (consume('.')) { + pushLocation(marker); name = parseIdentifier(); if (options.scope) attachScope(name, false); - base = ast.memberExpression(base, '.', name); + base = finishNode(ast.memberExpression(base, '.', name)); } if (consume(':')) { + pushLocation(marker); name = parseIdentifier(); if (options.scope) attachScope(name, false); - base = ast.memberExpression(base, ':', name); + base = finishNode(ast.memberExpression(base, ':', name)); } return base; @@ -1522,23 +1629,27 @@ define(function(require, exports, module) { , key, value; while (true) { + markLocation(); if (Punctuator === token.type && consume('[')) { key = parseExpectedExpression(); expect(']'); expect('='); value = parseExpectedExpression(); - fields.push(ast.tableKey(key, value)); + fields.push(finishNode(ast.tableKey(key, value))); } else if (Identifier === token.type) { key = parseExpectedExpression(); if (consume('=')) { value = parseExpectedExpression(); - fields.push(ast.tableKeyString(key, value)); + fields.push(finishNode(ast.tableKeyString(key, value))); } else { - fields.push(ast.tableValue(key)); + fields.push(finishNode(ast.tableValue(key))); } } else { - if (null == (value = parseExpression())) break; - fields.push(ast.tableValue(value)); + if (null == (value = parseExpression())) { + locations.pop(); + break; + } + fields.push(finishNode(ast.tableValue(value))); } if (',;'.indexOf(token.value) >= 0) { next(); @@ -1547,7 +1658,7 @@ define(function(require, exports, module) { if ('}' === token.value) break; } expect('}'); - return ast.tableConstructorExpression(fields); + return finishNode(ast.tableConstructorExpression(fields)); } // Expression parser @@ -1588,23 +1699,23 @@ define(function(require, exports, module) { // the expensive CompareICStub which took ~8% of the parse time. function binaryPrecedence(operator) { - var character = operator.charCodeAt(0) + var charCode = operator.charCodeAt(0) , length = operator.length; if (1 === length) { - switch (character) { + switch (charCode) { case 94: return 10; // ^ case 42: case 47: case 37: return 7; // * / % case 43: case 45: return 6; // + - case 60: case 62: return 3; // < > } } else if (2 === length) { - switch (character) { + switch (charCode) { case 46: return 5; // .. case 60: case 62: case 61: case 126: return 3; // <= >= == ~= case 111: return 1; // or } - } else if (97 === character && 'and' === operator) return 2; + } else if (97 === charCode && 'and' === operator) return 2; return 0; } @@ -1618,16 +1729,19 @@ define(function(require, exports, module) { // exp ::= (unop exp | primary | prefixexp ) { binop exp } function parseSubExpression(minPrecedence) { - var operator = token.value; + var operator = token.value // The left-hand side in binary operations. - var expression; + , expression, marker; + + if (trackLocations) marker = createLocationMarker(); // UnaryExpression if (isUnary(token)) { + markLocation(); next(); var argument = parseSubExpression(8); if (argument == null) raiseUnexpectedToken('', token); - expression = ast.unaryExpression(operator, argument); + expression = finishNode(ast.unaryExpression(operator, argument)); } if (null == expression) { // PrimaryExpression @@ -1654,7 +1768,10 @@ define(function(require, exports, module) { next(); var right = parseSubExpression(precedence); if (null == right) raiseUnexpectedToken('', token); - expression = ast.binaryExpression(operator, expression, right); + // Push in the marker created before the loop to wrap its entirety. + if (trackLocations) locations.push(marker); + expression = finishNode(ast.binaryExpression(operator, expression, right)); + } return expression; } @@ -1666,10 +1783,12 @@ define(function(require, exports, module) { // args ::= '(' [explist] ')' | tableconstructor | String function parsePrefixExpression() { - var base, name + var base, name, marker // Keep track of the scope, if a parent is local so are the children. , isLocal; + if (trackLocations) marker = createLocationMarker(); + // The prefix if (Identifier === token.type) { name = token.value; @@ -1690,34 +1809,40 @@ define(function(require, exports, module) { if (Punctuator === token.type) { switch (token.value) { case '[': + pushLocation(marker); next(); expression = parseExpectedExpression(); - base = ast.indexExpression(base, expression); + base = finishNode(ast.indexExpression(base, expression)); expect(']'); break; case '.': + pushLocation(marker); next(); identifier = parseIdentifier(); // Inherit the scope if (options.scope) attachScope(identifier, isLocal); - base = ast.memberExpression(base, '.', identifier); + base = finishNode(ast.memberExpression(base, '.', identifier)); break; case ':': + pushLocation(marker); next(); identifier = parseIdentifier(); if (options.scope) attachScope(identifier, isLocal); - base = ast.memberExpression(base, ':', identifier); - // Once a : is found, this has to be a callexpression, otherwise + base = finishNode(ast.memberExpression(base, ':', identifier)); + // Once a : is found, this has to be a CallExpression, otherwise // throw an error. + pushLocation(marker); base = parseCallExpression(base); break; case '(': case '{': // args + pushLocation(marker); base = parseCallExpression(base); break; default: return base; } } else if (StringLiteral === token.type) { + pushLocation(marker); base = parseCallExpression(base); } else { break; @@ -1745,15 +1870,16 @@ define(function(require, exports, module) { } expect(')'); - return ast.callExpression(base, expressions); + return finishNode(ast.callExpression(base, expressions)); case '{': + markLocation(); next(); var table = parseTableConstructor(); - return ast.tableCallExpression(base, table); + return finishNode(ast.tableCallExpression(base, table)); } } else if (StringLiteral === token.type) { - return ast.stringCallExpression(base, parsePrimaryExpression()); + return finishNode(ast.stringCallExpression(base, parsePrimaryExpression())); } raiseUnexpectedToken('function arguments', token); @@ -1765,17 +1891,24 @@ define(function(require, exports, module) { function parsePrimaryExpression() { var literals = StringLiteral | NumericLiteral | BooleanLiteral | NilLiteral | VarargLiteral , value = token.value - , type = token.type; + , type = token.type + , marker; + + if (trackLocations) marker = createLocationMarker(); if (type & literals) { + pushLocation(marker); var raw = input.slice(token.range[0], token.range[1]); next(); - return ast.literal(type, value, raw); + return finishNode(ast.literal(type, value, raw)); } else if (Keyword === type && 'function' === value) { + pushLocation(marker); next(); return parseFunctionDeclaration(null); - } else if (consume('{')) + } else if (consume('{')) { + pushLocation(marker); return parseTableConstructor(); + } } // Parser @@ -1813,7 +1946,7 @@ define(function(require, exports, module) { scopes = [[]]; scopeDepth = 0; globals = []; - globalNames = []; + locations = []; if (options.comments) comments = []; if (!options.wait) return end(); @@ -1836,12 +1969,17 @@ define(function(require, exports, module) { if ('undefined' !== typeof _input) write(_input); length = input.length; + trackLocations = options.locations || options.ranges; // Initialize with a lookahead token. lookahead = lex(); var chunk = parseChunk(); if (options.comments) chunk.comments = comments; if (options.scope) chunk.globals = globals; + + if (locations.length > 0) + throw new Error('Location tracking failed. This is most likely a bug in luaparse'); + return chunk; } diff --git a/lib/ace/worker/worker.js b/lib/ace/worker/worker.js index 24e1a83b..e530b5dd 100644 --- a/lib/ace/worker/worker.js +++ b/lib/ace/worker/worker.js @@ -12,7 +12,8 @@ window.console = { error: function() { var msgs = Array.prototype.slice.call(arguments, 0); postMessage({type: "log", data: msgs}); - } + }, + trace: function() {} }; window.window = window; window.ace = window; diff --git a/tool/update_deps.js b/tool/update_deps.js index b036d5b4..4adc8bdb 100644 --- a/tool/update_deps.js +++ b/tool/update_deps.js @@ -16,7 +16,13 @@ var deps = [{ }, { path: "mode/lua/luaparse.js", url: "https://raw.github.com/oxyc/luaparse/master/luaparse.js", - needsFixup: true + needsFixup: true, + postProcess: function(src) { + return src.replace( + /\(function\s*\(root,\s*name,\s*factory\)\s*{[\s\S]*?}\(this,\s*'luaparse',/, + "(function (root, name, factory) {\n factory(exports)\n}(this, 'luaparse'," + ) + } }]; var download = function(href, callback) { @@ -39,12 +45,12 @@ var download = function(href, callback) { var getDep = function(dep) { download(dep.url, function(data) { + if (dep.postProcess) + data = dep.postProcess(data); if (dep.needsFixup) data = "define(function(require, exports, module) {\n" + data + "\n});"; - if (dep.postProcess) - data = dep.postProcess(data); fs.writeFile(rootDir + dep.path, data, "utf-8", function(err){ if (err) throw err; @@ -173,10 +179,19 @@ run("npm install jshint", function() { /"Expected a conditional expression and instead saw an assignment."/g, '"Assignment in conditional expression"' ); + + jshintDist = jshintDist.replace(/\brequire\(["']|\(require,|\(require\)/g, function(r){ + return r.replace("require", "req"); + }).replace(/\brequire.define\(/g, function(d){ + return d.replace("define", "def"); + }); + + jshintDist = jshintDist.replace(/var defaultMaxListeners = 10;/, function(a) {return a.replace("10", "200")}); + jshintDist = 'define(function() {\n' + jshintDist + '\n' - + 'var jsHint = require("/src/stable/jshint.js");\n' - + 'return function(require, exports, module) {module.exports = jsHint;}\n' - +'}())'; + + 'function req() {return require.apply(this, arguments)}' + + 'module.exports = req("/src/stable/jshint.js");\n' + +'})'; fs.writeFileSync(rootDir + "mode/javascript/jshint.js", jshintDist); }); \ No newline at end of file From 38dc22b421094aaa308f74041d08979244765d6f Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 8 Jul 2013 14:10:54 +0400 Subject: [PATCH 2/4] fix tests --- lib/ace/mode/javascript/jshint.js | 9 +++++---- tool/update_deps.js | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/ace/mode/javascript/jshint.js b/lib/ace/mode/javascript/jshint.js index 7c02574d..e486084a 100644 --- a/lib/ace/mode/javascript/jshint.js +++ b/lib/ace/mode/javascript/jshint.js @@ -1,4 +1,4 @@ -define(function() { +define(function(require, exports, module) { var require = function (file, cwd) { var resolved = require.resolve(file, cwd || '/'); var mod = require.modules[resolved]; @@ -143,7 +143,7 @@ require.alias = function (from, to) { var global = typeof window !== 'undefined' ? window : {}; var definedProcess = false; - require.define = function (filename, fn) { + require.def = function (filename, fn) { if (!definedProcess && require.modules.__browserify_process) { process = require.modules.__browserify_process(); definedProcess = true; @@ -7890,5 +7890,6 @@ if (typeof exports === "object" && exports) { }); req("/src/stable/jshint.js"); -function req() {return require.apply(this, arguments)}module.exports = req("/src/stable/jshint.js"); -}) \ No newline at end of file +function req() {return require.apply(this, arguments)} +module.exports = req("/src/stable/jshint.js"); +}); \ No newline at end of file diff --git a/tool/update_deps.js b/tool/update_deps.js index 4adc8bdb..f5a9e22d 100644 --- a/tool/update_deps.js +++ b/tool/update_deps.js @@ -182,16 +182,16 @@ run("npm install jshint", function() { jshintDist = jshintDist.replace(/\brequire\(["']|\(require,|\(require\)/g, function(r){ return r.replace("require", "req"); - }).replace(/\brequire.define\(/g, function(d){ + }).replace(/\brequire.define(\(|\s*=)/g, function(d){ return d.replace("define", "def"); }); jshintDist = jshintDist.replace(/var defaultMaxListeners = 10;/, function(a) {return a.replace("10", "200")}); - jshintDist = 'define(function() {\n' + jshintDist = 'define(function(require, exports, module) {\n' + jshintDist + '\n' - + 'function req() {return require.apply(this, arguments)}' + + 'function req() {return require.apply(this, arguments)}\n' + 'module.exports = req("/src/stable/jshint.js");\n' - +'})'; + +'});'; fs.writeFileSync(rootDir + "mode/javascript/jshint.js", jshintDist); }); \ No newline at end of file From 49967c139a73b941adb2842dbba7c10d6558de72 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 9 Jul 2013 18:22:18 +0400 Subject: [PATCH 3/4] fix typo in noworker detection --- demo/kitchen-sink/demo.js | 2 +- lib/ace/mode/javascript/jshint.js | 4 ++-- lib/ace/mode/javascript_worker_test.js | 9 +++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 88d5fa0e..193da9bb 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -71,7 +71,7 @@ var IncrementalSearch = require("ace/incremental_search").IncrementalSearch; var workerModule = require("ace/worker/worker_client"); -if (location.href.indexOf("noworker" !== -1)) { +if (location.href.indexOf("noworker") !== -1) { workerModule.WorkerClient = workerModule.UIWorkerClient; } diff --git a/lib/ace/mode/javascript/jshint.js b/lib/ace/mode/javascript/jshint.js index e486084a..8de6386e 100644 --- a/lib/ace/mode/javascript/jshint.js +++ b/lib/ace/mode/javascript/jshint.js @@ -5308,7 +5308,7 @@ var JSHINT = (function () { nobreaknonadjacent(state.tokens.prev, state.tokens.curr); nonadjacent(state.tokens.curr, state.tokens.next); } - if (s === "in" && left.id === "!") { + if (s === "in" && left && left.id === "!") { warning("W018", left, "!"); } if (typeof f === "function") { @@ -7892,4 +7892,4 @@ req("/src/stable/jshint.js"); function req() {return require.apply(this, arguments)} module.exports = req("/src/stable/jshint.js"); -}); \ No newline at end of file +}); diff --git a/lib/ace/mode/javascript_worker_test.js b/lib/ace/mode/javascript_worker_test.js index b8b43cba..bf6b1bfc 100644 --- a/lib/ace/mode/javascript_worker_test.js +++ b/lib/ace/mode/javascript_worker_test.js @@ -87,6 +87,15 @@ module.exports = { assert.equal(error.type, "error"); assert.equal(error.row, 0); assert.equal(error.column, 4); + }, + + "test for each": function() { + var worker = new JavaScriptWorker(this.sender); + worker.setValue("for each(var i in x)'"); + worker.deferredUpdate.call(); + + var error = this.sender.events[0][1][0]; + assert.equal(error.text, "Expected '(' and instead saw 'each'."); } }; From a41a06ff33a99373c8a9746ec4914665bfc91ab6 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 9 Jul 2013 19:20:53 +0400 Subject: [PATCH 4/4] escape . in regexp --- Makefile.dryice.js | 2 +- tool/update_deps.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index 660c8943..09e4eca9 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -603,7 +603,7 @@ function namespace(ns) { text = text .toString() .replace('var ACE_NAMESPACE = "";', 'var ACE_NAMESPACE = "' + ns +'";') - .replace(/(.define)|\bdefine\(/g, function(_, a) { + .replace(/(\.define)|\bdefine\(/g, function(_, a) { return a || ns + ".define(" }); diff --git a/tool/update_deps.js b/tool/update_deps.js index f5a9e22d..0e0dbcd9 100644 --- a/tool/update_deps.js +++ b/tool/update_deps.js @@ -182,7 +182,7 @@ run("npm install jshint", function() { jshintDist = jshintDist.replace(/\brequire\(["']|\(require,|\(require\)/g, function(r){ return r.replace("require", "req"); - }).replace(/\brequire.define(\(|\s*=)/g, function(d){ + }).replace(/\brequire\.define(\(|\s*=)/g, function(d){ return d.replace("define", "def"); });