From 79c3e2966a159c57434d29fca4c5893dda4e2a49 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 11 Mar 2013 16:57:50 -0700 Subject: [PATCH 01/64] Fixes issue #258 --- client/scripts/plugins/esprima/types.js | 3 ++- .../esprima/esprimaJsContentAssistTests.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 217df9f4..b28f7b60 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1793,7 +1793,8 @@ SVGFEFuncAElement var expr = this.convertJsDocType(jsdocType.expression, env); if (expr === "Array" && jsdocType.applications && jsdocType.applications.length > 0) { // only parameterize arrays not handling objects yet - return "Array.<" + this.convertJsDocType(jsdocType.applications[0], env) + ">"; + var typeParam = this.convertJsDocType(jsdocType.applications[0], env) || "Object"; + return "Array.<" + typeParam + ">"; } return expr; case 'ParameterType': diff --git a/tests/client/esprima/esprimaJsContentAssistTests.js b/tests/client/esprima/esprimaJsContentAssistTests.js index a82c4192..03bce6e1 100644 --- a/tests/client/esprima/esprimaJsContentAssistTests.js +++ b/tests/client/esprima/esprimaJsContentAssistTests.js @@ -4139,5 +4139,21 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ]); }; + + + + + + // See https://github.com/scripted-editor/scripted/issues/258 + tests['test invalid array type param in jsdoc'] = function() { + var results = computeContentAssist( + "/** @type Array. */\n" + + "var graph = {};\n" + + "graph[''].k.proto", "proto"); + testProposals(results, [ + ["prototype", "prototype : Object"] + ]); + }; + return tests; }); From aa87311ded16aaa8b42453479774b9bc476d3990 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 11 Mar 2013 20:26:53 -0700 Subject: [PATCH 02/64] Fix for issue #257 Incremental find should disable autoactivation of content assist. --- client/scripts/orion/editor/contentAssist.js | 2 +- client/scripts/scripted/editor/scriptedEditor.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/client/scripts/orion/editor/contentAssist.js b/client/scripts/orion/editor/contentAssist.js index ccb75e85..cbfdb58f 100644 --- a/client/scripts/orion/editor/contentAssist.js +++ b/client/scripts/orion/editor/contentAssist.js @@ -201,7 +201,7 @@ define("orion/editor/contentAssist", ['i18n!orion/editor/nls/messages', 'orion/t * provides auto-activation for content assist on '.' */ autoActivate : function(e) { - if (e.text === '.' && !this.activationRequest) { + if (e.text === '.' && !this.activationRequest && !this.textView.isIncrementalFindActive()) { // simple check to see if we are in a valid location // for content assist. // if not, prevent auto-activation diff --git a/client/scripts/scripted/editor/scriptedEditor.js b/client/scripts/scripted/editor/scriptedEditor.js index a719c137..1f9a7517 100644 --- a/client/scripts/scripted/editor/scriptedEditor.js +++ b/client/scripts/scripted/editor/scriptedEditor.js @@ -961,6 +961,18 @@ define([ themeManager.applyCurrentTheme(editor); + // must add this function to textView and not editor + // since it must be available in the content assistant + editor.getTextView().isIncrementalFindActive = function() { + var keyModes = editor._keyModes; + for (var i = 0; i < keyModes.length; i++) { + if (keyModes[i].toggleIncrementalFind) { + return keyModes[i].isActive(); + } + } + return false; + }; + return editor; }; From 1805c728460dd5b918b3aff7e67302ead0339956 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 12 Mar 2013 12:12:04 -0700 Subject: [PATCH 03/64] Add some experimental code using a mongodb on cf.com. --- manifest.yml | 13 +- package.json | 3 +- server/mongodb-config.js | 85 +++++ .../plugable-fs/github-fs/github-repo-fs.V2 | 317 ------------------ server/routes/debug-routes.js | 68 ++++ server/start-cloudfoundry.js | 2 - 6 files changed, 164 insertions(+), 324 deletions(-) create mode 100644 server/mongodb-config.js delete mode 100644 server/plugable-fs/github-fs/github-repo-fs.V2 diff --git a/manifest.yml b/manifest.yml index a5ab2ed8..09bdc092 100644 --- a/manifest.yml +++ b/manifest.yml @@ -1,9 +1,14 @@ --- applications: -- instances: 1 - memory: 1G - name: scr +- services: + scr-mongodb: + vendor: mongodb + version: "2.0" + tier: free runtime: node08 - path: . + instances: 1 url: scrptd.${target-base} + memory: 1G framework: node + name: scr + path: . diff --git a/package.json b/package.json index 5bfe08b6..1f865e3a 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "express": "3.0.6", "bower": "0.8.5", "mime": "1.2.9", - "priorityqueuejs": "0.1.0" + "priorityqueuejs": "0.1.0", + "mongodb": "1.2.13" }, "devDependencies": { "fake-fs": "https://github.com/eldargab/node-fake-fs/archive/50f54e9551d9ece5578213564268021d4976db26.tar.gz", diff --git a/server/mongodb-config.js b/server/mongodb-config.js new file mode 100644 index 00000000..1090b5fa --- /dev/null +++ b/server/mongodb-config.js @@ -0,0 +1,85 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Kris De Volder + ******************************************************************************/ + +var mongodb = require('mongodb'); +var when = require('when'); + +if(process.env.VCAP_SERVICES){ + var env = JSON.parse(process.env.VCAP_SERVICES); + console.log('VCAP_SERVICES = '+JSON.stringify(env, null, ' ')); + var mongo = env['mongodb-2.0'][0].credentials; +} else{ + var mongo = { + "hostname":"localhost", + "port":27017, + "username":"", + "password":"", + "name":"", + "db":"db" + }; +} + +var generate_mongo_url = function(obj){ + obj.hostname = (obj.hostname || 'localhost'); + obj.port = (obj.port || 27017); + obj.db = (obj.db || 'test'); + + if(obj.username && obj.password){ + return "mongodb://" + obj.username + ":" + obj.password + "@" + obj.hostname + ":" + obj.port + "/" + obj.db; + } + else{ + return "mongodb://" + obj.hostname + ":" + obj.port + "/" + obj.db; + } +}; + +var mongourl = generate_mongo_url(mongo); + +console.log(mongourl); + +function connect() { + var d = when.defer(); + mongodb.connect(mongourl, function (err, conn) { + if (err) { + d.reject(err); + } else { + d.resolve(conn); + } + }); + return d.promise; +} + +/** + * Connect to mongodb with either local or cloudfoundry url depending on + * where we are running. + */ +exports.connect = connect; +/** + * Get a mongodb collection + */ +exports.collection = function (name) { + var d = when.defer(); + connect().then(function (conn) { + console.log('fetch collection connection = '+conn); + conn.collection(name, function (err, coll) { + console.log('fetch collection err = '+err); + console.log('fetch collection coll = '+coll); + console.log('Connection = '+conn); + if (err) { + return d.reject(err); + } else { + return d.resolve(coll); + } + }); + }); + return d.promise; +}; \ No newline at end of file diff --git a/server/plugable-fs/github-fs/github-repo-fs.V2 b/server/plugable-fs/github-fs/github-repo-fs.V2 deleted file mode 100644 index b3f11774..00000000 --- a/server/plugable-fs/github-fs/github-repo-fs.V2 +++ /dev/null @@ -1,317 +0,0 @@ -/******************************************************************************* - * @license - * Copyright (c) 2013 VMware, Inc. All Rights Reserved. - * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE - * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE - * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. - * You can obtain a current copy of the Eclipse Public License from - * http://www.opensource.org/licenses/eclipse-1.0.php - * - * Contributors: - * Kris De Volder - ******************************************************************************/ - -var nodeCallback = require('../../utils/promises').nodeCallback; -var fsErrors = require('../fs-errors'); -var readonlyFs = require('../read-only-fs'); - -var pathJoin = require('../../jsdepend/utils').pathJoin; -var when = require('when'); - -var LOGGING = false; - -// -// Quick implementation of a 'github readonly fs'. -// -// Issues: -// - rest client doesn't follow 'redirects' according to github api docs, it should. -// - doesn't handle paginated results. So for large files or dirs with many subdirs -// results may be incomplete. -// - builds in-memory tree but doesn't update when tree structure changes -// in the repo. -// - in memory only grows. How to reclaim memory when not used for a long time? - -function configure(options) { - - //console.log('repoFs options = '+ JSON.stringify(options, null, ' ')); - - if (!options.token) { - throw new Error('github-repo-fs needs to be configured with a "token" for OAuth'); - } - if (!options.owner) { - throw new Error('github-repo-fs needs to be configured with a "owner"'); - } - if (!options.repo) { - throw new Error('github-repo-fs needs to be configured with a "repo"'); - } - - //The URL that should be used to fetch the 'root node' data. - var API_ROOT = 'https://api.github.com/repos/'+options.owner+'/'+options.repo+'/contents'; - - var store = {}; - var cache = { - get: function (url) { - var entry = store[url]; - if (entry) { - return entry; - } - }, - put: function (url, entry) { -// console.log('put '+url + " : "+entry); - store[url] = entry; - } - }; - - function getNode(url) { -// console.log('>> getNode '+url); - var cached = cache.get(url); -// console.log('>> getNode from cache = '+cached); - if (!cached) { - cached = new Node(url); -// console.log('>> getNode created = '+cached); - cache.put(url, cached); - } -// console.log('>> getNode returning : '+cached); - return cached; - } - - var rest = require('./github-rest-client').configure({ - token: options.token - }); - - var INTERESTING = [ - //All nodes: - 'size', 'name', 'type', 'sha', - //File nodes: - 'content', 'encoding' - ]; - function copyInterestingData(source, dest) { - INTERESTING.forEach(function (name) { - if (source.hasOwnProperty(name)) { - dest[name] = source[name]; - } else { - delete dest[name]; - } - }); - } - - function Node(url) { - this.url = url; - } - /** - * Inserts data just fetched from rest api into a node - */ - Node.prototype.setData = function (data) { - if (Array.isArray(data)) { //Directory node - //Directories are kind-a funny. When we fetch their contents we - //actually get summary info about their children as an array. - //So this is the time to create the child nodes with that data - //already in it. - var children = this.children = {}; //Will point to the urls of our children. - data.forEach(function (childData) { - if (childData.name && childData.url) { - var url = childData.url; - children[childData.name] = childData.url; - var child = getNode(url); - child.setData(childData); - } else { - console.log('Ignoring unexpected child data:'+JSON.stringify(childData, null, ' ')); - } - }); - if (!this.isDirectory()) { - //Provide minima 'dirEntry' data that makes us look like a dir - // because it seems we are a dir now, regardless of what we were before. - this.dirEntry = { - type: 'dir' - }; - } - } else { - this.dirEntry = {}; - if (!this.isDirectory()) { - //In that case shouldn't have any children! - delete this.children; - } - copyInterestingData(data, this.dirEntry); - } - }; - /** - * Fetch this node's contents from its url and store it in the node - */ - Node.prototype.fetch = function () { - //TODO: don't fetch if not needed. Right now we always fetch! - return rest({ path: this.url }).then(function (data) { - //console.log('data received for '+this.url); - //console.dir(data); - this.setData(data); - }.bind(this)); - }; - Node.prototype.getChildren = function () { - return this.fetch().then(function () { - if (!this.isDirectory()) { - return when.reject(fsErrors.isNotDirError('getChildren', this.url)); - } - if (!this.children) { - return when.reject('Internal Error: type is dir but no children'); - } - return when.resolve(this.children); - }.bind(this)); - }; - Node.prototype.getChild = function (name) { - var that = this; - return this.getChildren().then(function (children) { - var childUrl = children[name]; - return (childUrl && getNode(children[name])) || when.reject( - fsErrors.noExistError('getChild', pathJoin(that.url, name)) - ); - }); - }; - Node.prototype.navigate = function (segments) { - if (typeof(segments)==='string') { - segments = segments.split('/'); - } - if (segments.length===0) { - return this; - } else { - var segment = segments.shift(); - if (!segment) { - return this.navigate(segments); - } else { - return this.getChild(segment).then(function (child) { -// console.log('Got child: '); -// console.dir(child); - return child.navigate(segments); - }); - } - } - }; - Node.prototype.isDirectory = function () { - //Only the root node is created without a dirEntry read from the parent node. - //The root node is assumed to always be a directory. - return !this.dirEntry || this.dirEntry.type === 'dir'; - }; - Node.prototype.isFile = function () { - return this.dirEntry && this.dirEntry.type === 'file'; - }; - Node.prototype.stat = function () { - return this; - }; - Node.prototype.toString = function () { - return this.url + ' type: '+ (this.dirEntry && this.dirEntry.type); - }; - Node.prototype.readdir = function () { - return this.getChildren().then(function (children) { - return Object.keys(children); - }); - }; - Node.prototype.readFile = function (encoding) { - if (!this.isFile()) { - //Avoid expensive fetch if this looks like its not even a file - return when.reject(fsErrors.isDirError('readFile', this.url)); - } - return this.fetch().then(function () { - if (!this.isFile()) { - return when.reject(fsErrors.isDirError('readFile', this.url)); - } - var apiData = this.dirEntry; - var contents = apiData.content; - if (encoding===apiData.encoding) { - return contents; - } - //Encodings mismatch... must convert - - var buf = new Buffer(contents, apiData.encoding); - if (!encoding) { - return buf; // return the data in raw form if no encoding is specified - } else { - return buf.toString(encoding); - } - }.bind(this)); - }; - - var rootNode = new Node(API_ROOT); - - function readdir(path, callback) { - nodeCallback( - when(rootNode.navigate(path), function (node) { - console.log('node :'); - console.dir(rootNode); - return node.readdir(); - }), - callback - ); - } - - function stat(path, callback) { - nodeCallback( - when(rootNode.navigate(path), function (node) { - return node.stat(); - }), - callback - ); - } - - function notImplemented(name) { - function fun() { - var callback = arguments[arguments.length-1]; - if (typeof(callback)==='function') { - callback(new Error('Not implemented yet: github-fs function '+name)); - } - } - fun.name = name; - return fun; - } - - function logged(f) { - if (!LOGGING) { - return f; - } - var loggedF = function (handle, callback) { - console.log('>>> '+f.name + ' ' + handle); - return f(handle, function (err, result) { - if (err) { - console.log('<<< '+f.name + ' ' + handle + ' => ERROR'); - console.log(err); - } else { - console.log('<<< '+f.name + ' ' + handle + ' => ' +result.toString()); - } - return callback(err, result); - }); - }; - loggedF.name = f.name; - return loggedF; - } - - function readFile(path, encoding, callback) { - //encoding is optional! - if (!callback) { - callback = encoding; - encoding = null; - } - nodeCallback( - when(rootNode.navigate(path), function (node) { - return node.readFile(encoding); - }), - callback - ); - } - - function fetch(path) { - return rest({ - path: pathJoin(API_ROOT, path) - }); - } - - return readonlyFs({ - forTesting: { - rootNode: rootNode, - fetch: fetch, - rest: rest - }, - stat: logged(stat), - readFile: readFile, - readdir: logged(readdir) - }); - -} - -exports.configure = configure; \ No newline at end of file diff --git a/server/routes/debug-routes.js b/server/routes/debug-routes.js index 92a009e6..93688280 100644 --- a/server/routes/debug-routes.js +++ b/server/routes/debug-routes.js @@ -17,6 +17,22 @@ //var memwatch = require('memwatch'); var memwatch = null; +var mongodb = require('../mongodb-config'); + +function errorFun(res) { + function error(err) { + console.error(err); + res.status(500); + res.header('Content-Type', 'text/plain'); + res.write(""+err); + if (err.stack) { + res.write(""+err.stack); + } + res.end(); + } + return error; +} + exports.install = function install(app) { var lastGc = null; @@ -43,4 +59,56 @@ exports.install = function install(app) { res.write(JSON.stringify(process.memoryUsage(), null, ' ')); res.end(); }); + + /** + * Retrieve a mongodb collection as json text + */ + app.get('/debug/mongodb/:collection', function (req, res) { + var name = req.params.collection; + console.log('mongo fetch collection : '+name ); + mongodb.collection(name).then(function (coll) { + console.log('collection = '+coll); + coll.find(function (err, entries) { + console.log('find coll err = '+err); + console.log('find coll entries = '+entries); + if (err) { + return errorFun(res)(err); + } + res.header('Content-Type', 'application/json'); + entries.toArray(function (err, array) { + if (err) { + errorFun(res)(err); + } + res.write(JSON.stringify(array, null, ' ')); + res.end(); + }); + }); + }).otherwise(errorFun(res)); + }); + + app.get('/debug/test-mongodb', function (req, res) { + console.log('test mongo request'); + var entry = { + date: new Date(), + ip: req.connection.remoteAddress + }; + mongodb.connect().then(function (connection) { + connection.collection('testlog', function (err, collection) { + if (err) { + return errorFun(res)(err); + } else { + collection.insert(entry, {safe: true}, function (err) { + if (err) { + return errorFun(res)(err); + } + //Woohoo! no errors so far! + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write(JSON.stringify(entry)); + res.end('\n'); + }); + } + }); + }).otherwise(errorFun(res)); + + }); }; diff --git a/server/start-cloudfoundry.js b/server/start-cloudfoundry.js index 7f63d104..a1159b1a 100644 --- a/server/start-cloudfoundry.js +++ b/server/start-cloudfoundry.js @@ -56,8 +56,6 @@ // optional // - github-fs -// - needs some cache management cleanup so it can avoid running out of memory -// - cache contents of files as well // - login mechanis to obtain oauth token for individual user (optional for 'demo') // - upload zip? From 84ed0bfe3b44bc67356ceb4514b70b4e3ed06fd6 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 12 Mar 2013 12:14:40 -0700 Subject: [PATCH 04/64] Add some 'search excludes' sample code in .scripted. --- .scripted | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.scripted b/.scripted index 85c6b5bb..fc5a1249 100644 --- a/.scripted +++ b/.scripted @@ -96,6 +96,12 @@ // "unescape_strings": false // } // }, - + +// search: { +// exclude: [ +// '**/node_modules', +// '**/client/components' +// ] +// } } From 0fce86e39332a170ca4d97e936dae05e1acdde98 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 12 Mar 2013 12:58:08 -0700 Subject: [PATCH 05/64] Move 'user.home' to 'home' and make the 'home' directory 'unlistable'. --- .../.scriptedrc/scripted.json | 0 server/plugable-fs/unlistable-fs.js | 50 +++++++++++++++++++ server/start-cloudfoundry.js | 8 ++- 3 files changed, 56 insertions(+), 2 deletions(-) rename sandbox/{user.home => home}/.scriptedrc/scripted.json (100%) create mode 100644 server/plugable-fs/unlistable-fs.js diff --git a/sandbox/user.home/.scriptedrc/scripted.json b/sandbox/home/.scriptedrc/scripted.json similarity index 100% rename from sandbox/user.home/.scriptedrc/scripted.json rename to sandbox/home/.scriptedrc/scripted.json diff --git a/server/plugable-fs/unlistable-fs.js b/server/plugable-fs/unlistable-fs.js new file mode 100644 index 00000000..e086f93f --- /dev/null +++ b/server/plugable-fs/unlistable-fs.js @@ -0,0 +1,50 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Kris De Volder + ******************************************************************************/ + +// +// A wrapper that makes a specific path on a filesystem 'unlistable'. +// This means that when trying to readdir that path the result will +// be just an empty list. +// +// The idea is to use this to make the subdirectories 'semi-private'. +// It will be hard to guess/discover the names of the subdirectories, but if +// you know the name of *your* directory can still access or share it +// with others. +// + +var fsErrors = require('./fs-errors'); +var extend = require('../jsdepend/utils').extend; + +function create(fs, path) { + + path = path || '/'; + + /** + * Replacement for 'readdir' so that readdir('/') => [] and other paths + * are just delegated to retain their orginal behavior. + */ + function readdir(handle, k) { + if (handle===path) { + return k(null, []); + } else { + return fs.readdir(handle, k); + } + } + + return extend(fs, { + readdir: readdir + }); + +} + +module.exports = create; \ No newline at end of file diff --git a/server/start-cloudfoundry.js b/server/start-cloudfoundry.js index a1159b1a..979689c4 100644 --- a/server/start-cloudfoundry.js +++ b/server/start-cloudfoundry.js @@ -67,13 +67,17 @@ var scriptedFs = require('../server/plugable-fs/scripted-fs'); var githubFs = require('../server/plugable-fs/github-fs/github-fs'); var compose = require('../server/plugable-fs/composite-fs').compose; var readOnly = require('../server/plugable-fs/read-only-fs'); +var unlistable = require('../server/plugable-fs/unlistable-fs'); var withBaseDir = mappedFs.withBaseDir; var withPrefix = mappedFs.withPrefix; var scriptedHomeLocation = path.resolve(__dirname, '..'); -var sandbox = mappedFs.withBaseDir(path.resolve(__dirname, '../sandbox')); +var sandbox = unlistable( + mappedFs.withBaseDir(path.resolve(__dirname, '../sandbox')), + '/home' +); var cache = require('./plugable-fs/github-fs/rest-node-manager').configure({ limit: 2500 // Limits number of in-memory cached nodes. @@ -105,7 +109,7 @@ var corefs = compose( //Now wrap that to create our 'fat' API that scripted uses throughout its codebase. var filesystem = scriptedFs.configure(corefs, { - userHome: '/user.home', + userHome: '/home', scriptedHome: '/scripted.home' }); From 469784a460cd0dc876b2ca372c6c74485be0dc5c Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 12 Mar 2013 15:48:00 -0700 Subject: [PATCH 06/64] Add a methodCaller utility ease calling node-style methods with callbacks and return promises. --- server/utils/promises.js | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/server/utils/promises.js b/server/utils/promises.js index b48dea09..c283a0f6 100644 --- a/server/utils/promises.js +++ b/server/utils/promises.js @@ -242,7 +242,51 @@ return d.promise; } + var slice = Array.prototype.slice; + var nodeApply = require('when/node/function').apply; + + /** + * Creates a promisified methodCaller function for a method with a given + * name. Usage example: + * + * Say you have a collection object that has a node-style callbacky method + * called 'find' that you would call as follows: + * + * collection.find(query, function (err, result) { + * if (err) { + * ...handle error... + * } else { + * ...process result... + * } + * }); + * + * Then you can do the following instead + * + * var find = methodCaller('find'); + * + * find(collection, query).then( + * function (result) { ... }, + * function (err) { ... } + * ); + */ + function methodCaller(name) { + //TODO: more sensible error messages when calling non-existent methods. + var caller = function (self /*, args...*/) { + var args = slice.call(arguments, 1); + return when(self, function (self) { + //console.log('calling '+name+' on '+self); + //console.log('args = '+args); + var f = self[name]; + //console.log('self.'+name+' = '+f); + return nodeApply(f.bind(self), args); + }); + }; + caller.name = name+'MethodCaller'; + return caller; + } + return { + methodCaller: methodCaller, whileLoop: whileLoop, not: not, until: until, From 697ed72357188e551f31bf221d32e935828390e0 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 12 Mar 2013 15:59:07 -0700 Subject: [PATCH 07/64] Simply small subset of mongo apis using promises. --- server/{mongodb-config.js => mongo.js} | 57 ++++++++++++++++++++------ server/routes/cloudfoundry-routes.js | 10 +++-- server/routes/debug-routes.js | 36 ++++------------ 3 files changed, 60 insertions(+), 43 deletions(-) rename server/{mongodb-config.js => mongo.js} (60%) diff --git a/server/mongodb-config.js b/server/mongo.js similarity index 60% rename from server/mongodb-config.js rename to server/mongo.js index 1090b5fa..807da8fe 100644 --- a/server/mongodb-config.js +++ b/server/mongo.js @@ -11,8 +11,18 @@ * Kris De Volder ******************************************************************************/ +// +// Provides some convenience api to access a mongodb database. +// +// The database is automatically configure to work on a local +// testing mongodb server or on a database provided by +// cloudfoundry as a service. + var mongodb = require('mongodb'); var when = require('when'); +var whenFunction = require('when/node/function'); +var methodCaller = require('./utils/promises').methodCaller; +//var createCallback = whenFunction.createCallback; if(process.env.VCAP_SERVICES){ var env = JSON.parse(process.env.VCAP_SERVICES); @@ -46,23 +56,46 @@ var mongourl = generate_mongo_url(mongo); console.log(mongourl); +var _connect = methodCaller('connect'); function connect() { - var d = when.defer(); - mongodb.connect(mongourl, function (err, conn) { - if (err) { - d.reject(err); - } else { - d.resolve(conn); - } - }); - return d.promise; + return _connect(mongodb, mongourl); } +//function apply(f, self/*optional*/, args) { +// if (!args) { +// //Actually, self are the args in that case! +// return _apply(f, self); +// } else { +// return when(self, function (self) { +// return _apply(f.bind(self), args); +// }); +// } +//} + +var _insert = methodCaller('insert'); + +/** + * Insert an object into a mongodb collection. + * + * @return {Promise} that resolves when object was inserted or rejects if there was + * an error. + */ +exports.insert = function (collection, data, options) { + return _insert(collection, data, options || { safe: true }); +}; + + /** - * Connect to mongodb with either local or cloudfoundry url depending on - * where we are running. + * Call 'find' on a collection. This just passes through arguments to + * mongo as is, but returns a promise instead of accepting a callback. */ -exports.connect = connect; +exports.find = methodCaller('find'); + +/** + * Call 'toArray' on a Cursor... . + */ +exports.toArray = methodCaller('toArray'); + /** * Get a mongodb collection */ diff --git a/server/routes/cloudfoundry-routes.js b/server/routes/cloudfoundry-routes.js index 3357525f..a7fee94a 100644 --- a/server/routes/cloudfoundry-routes.js +++ b/server/routes/cloudfoundry-routes.js @@ -23,9 +23,11 @@ var pathJoin = require('../jsdepend/utils').pathJoin; var when = require('when'); -exports.install = function (app, filesystem) { +//TODO: this cooky stuff should really be done as a connect +// middleware on every access not just on access to the 'root' path +// of the webserver. - var welcomeText = 'Welcome to Scripted!'; +exports.install = function (app, filesystem) { var getUserHome = filesystem.getUserHome; var isDirectory = filesystem.isDirectory; @@ -50,7 +52,7 @@ exports.install = function (app, filesystem) { } function randomString(len) { - var chars = "abcdefghijklmnopqrstuvwxyz01234567890"; + var chars = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var str = ""; for (var i = 0; i < len; i++) { var code = Math.floor(Math.random()*chars.length); @@ -61,7 +63,7 @@ exports.install = function (app, filesystem) { function generateUserID(tries) { tries = tries || 0; - var id = randomString(8); + var id = randomString(10); return exists(getUserPath(id)).then(function (exists) { if (exists) { //already exists... try to find another one diff --git a/server/routes/debug-routes.js b/server/routes/debug-routes.js index 93688280..7586d8c2 100644 --- a/server/routes/debug-routes.js +++ b/server/routes/debug-routes.js @@ -17,7 +17,7 @@ //var memwatch = require('memwatch'); var memwatch = null; -var mongodb = require('../mongodb-config'); +var mongodb = require('../mongo'); function errorFun(res) { function error(err) { @@ -67,18 +67,10 @@ exports.install = function install(app) { var name = req.params.collection; console.log('mongo fetch collection : '+name ); mongodb.collection(name).then(function (coll) { - console.log('collection = '+coll); - coll.find(function (err, entries) { - console.log('find coll err = '+err); + return mongodb.find(coll).then(function (entries) { console.log('find coll entries = '+entries); - if (err) { - return errorFun(res)(err); - } res.header('Content-Type', 'application/json'); - entries.toArray(function (err, array) { - if (err) { - errorFun(res)(err); - } + return mongodb.toArray(entries).then(function (array) { res.write(JSON.stringify(array, null, ' ')); res.end(); }); @@ -92,23 +84,13 @@ exports.install = function install(app) { date: new Date(), ip: req.connection.remoteAddress }; - mongodb.connect().then(function (connection) { - connection.collection('testlog', function (err, collection) { - if (err) { - return errorFun(res)(err); - } else { - collection.insert(entry, {safe: true}, function (err) { - if (err) { - return errorFun(res)(err); - } - //Woohoo! no errors so far! - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write(JSON.stringify(entry)); - res.end('\n'); - }); - } + mongodb.collection('testlog').then(function (collection) { + return mongodb.insert(collection, entry).then(function () { + //Woohoo! no errors so far! + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write(JSON.stringify(entry)); + res.end('\n'); }); }).otherwise(errorFun(res)); - }); }; From 050bf650292ab5c3ed8ca2aa8d5e60a580ceaa6a Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 12 Mar 2013 18:24:00 -0700 Subject: [PATCH 08/64] Add something to track visitors to the root redirect page. --- server/mongo.js | 19 +++-- server/routes/cloudfoundry-routes.js | 117 ++++++++++++++++++++------- server/routes/debug.js | 45 +++++++++++ 3 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 server/routes/debug.js diff --git a/server/mongo.js b/server/mongo.js index 807da8fe..e3fb4650 100644 --- a/server/mongo.js +++ b/server/mongo.js @@ -57,8 +57,12 @@ var mongourl = generate_mongo_url(mongo); console.log(mongourl); var _connect = methodCaller('connect'); +var connection = null; function connect() { - return _connect(mongodb, mongourl); + if (!connection) { + connection = _connect(mongodb, mongourl); + } + return connection; } //function apply(f, self/*optional*/, args) { @@ -100,13 +104,14 @@ exports.toArray = methodCaller('toArray'); * Get a mongodb collection */ exports.collection = function (name) { + //TODO: use methodCaller to implement this in much less code. var d = when.defer(); connect().then(function (conn) { - console.log('fetch collection connection = '+conn); + //console.log('fetch collection connection = '+conn); conn.collection(name, function (err, coll) { - console.log('fetch collection err = '+err); - console.log('fetch collection coll = '+coll); - console.log('Connection = '+conn); +// console.log('fetch collection err = '+err); +// console.log('fetch collection coll = '+coll); +// console.log('Connection = '+conn); if (err) { return d.reject(err); } else { @@ -115,4 +120,6 @@ exports.collection = function (name) { }); }); return d.promise; -}; \ No newline at end of file +}; + +exports.findAndModify = methodCaller('findAndModify'); \ No newline at end of file diff --git a/server/routes/cloudfoundry-routes.js b/server/routes/cloudfoundry-routes.js index a7fee94a..8523df3a 100644 --- a/server/routes/cloudfoundry-routes.js +++ b/server/routes/cloudfoundry-routes.js @@ -22,6 +22,26 @@ var pathJoin = require('../jsdepend/utils').pathJoin; var when = require('when'); +var mongo = require('../mongo'); + +function errorFun(res) { + function error(err) { + console.error(err); + if (err.stack) { + console.error(err.stack); + } + res.status(500); + res.header('Content-Type', 'text/plain'); + res.write(""+err); + if (err.stack) { + res.write(""+err.stack); + } + res.end(); + } + return error; +} + +var users = mongo.collection('users'); //TODO: this cooky stuff should really be done as a connect // middleware on every access not just on access to the 'root' path @@ -39,17 +59,19 @@ exports.install = function (app, filesystem) { return pathJoin(getUserHome(), userID); } - function createUserArea(userID) { - var path = getUserPath(userID); - return isDirectory(path).then(function (isDir) { - if (isDir) { - //already got a user-area - return; - } - //must create user area - return copyDir('/sample', path); - }); - } +// Discontinued: will only show a read-only sample now. +// +// function createUserArea(userID) { +// var path = getUserPath(userID); +// return isDirectory(path).then(function (isDir) { +// if (isDir) { +// //already got a user-area +// return; +// } +// //must create user area +// return copyDir('/sample', path); +// }); +// } function randomString(len) { var chars = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -64,36 +86,77 @@ exports.install = function (app, filesystem) { function generateUserID(tries) { tries = tries || 0; var id = randomString(10); - return exists(getUserPath(id)).then(function (exists) { - if (exists) { - //already exists... try to find another one - if (++tries < 10) { - return generateUserID(tries); - } else { - //Can't find a free 'private' user id. Use the 'shared' ID. - return 'shared'; - } - } + return createUser(id).then(function () { + console.log('User created: '+id); return id; //Yeah! Found one that's still available. + }, function (err) { + console.log('Faile to create User: '+id); + console.err(err); + if (++tries < 10) { + return generateUserID(tries); + } else { + //Can't find a free 'private' user id. Use the 'shared' ID. + return 'shared'; + } }); } - app.get('/', function (req, res) { - console.log("Should be redirecting to user-specific area"); + function recordVisitor(id) { + console.log('Recording visit: '+id); + //Ignoring any errors about creating duplicate ids + return users.then(function (users) { + return mongo.findAndModify(users, + //Criteria: + {_id: id}, + //Sort: + [['_id','asc']], + //Update: + { + '$inc' : { visits: 1 }, + '$set' : { lastVisit: new Date()} + }, + //Options: + { upsert: true, 'new': true } + ).then(function (user) { + console.log('Visit recorded: '+JSON.stringify(user, null, ' ')); + return user; + }); + }); + } + + function createUser(id) { + return users.then(function (users) { + //The insert call below will reject when the user id already exists. + return mongo.insert(users, { + _id: id, + created: new Date() + }).then(function () {return id;}); + }); + } + app.get('/', function (req, res) { var userID = req.cookies.userID; console.log('cookie userID = '+userID); if (!userID) { userID = generateUserID(); } + //Yes... doing next sep asyncly (i.e. we aren't waiting for success to proceed). + when(userID, recordVisitor).otherwise(function (err) { + console.error('Problems recording a visit?'); + console.error(err); + if (err.stack) { + console.error(err.stack); + } + }); when(userID, function (userID) { res.cookie('userID', userID, { maxAge: 365 * 24 * 3600 * 1000 /*expires in one year*/ }); - createUserArea(userID).then(function () { - res.redirect('/editor'+getUserHome()+'/'+userID); - }); - }); + res.redirect('/editor/sample'); +// createUserArea(userID).then(function () { +// res.redirect('/editor'+getUserHome()+'/'+userID); +// }); + }).otherwise(errorFun(res)); }); }; \ No newline at end of file diff --git a/server/routes/debug.js b/server/routes/debug.js new file mode 100644 index 00000000..44848964 --- /dev/null +++ b/server/routes/debug.js @@ -0,0 +1,45 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Kris De Volder + ******************************************************************************/ + +var when = require('when'); +var mongo = require('../mongo'); +var insert = mongo.insert; + +mongo.collection('foo').then(function(foo) { + insert(foo, { date: new Date() }).then( + function () { + console.log('Inserted ok'); + }, + function (err) { + console.log('Problem inserting'); + console.error(err); + } + ).always(process.exit); +}); + +//// when(undefined).then(function () { +//// console.log('About to insert into '+foo); +//// return insert(foo, {_id: 1}).then(function () { +//// console.log('Inserted first entry'); +//// }); +//// }).then(function () { +//// return insert(foo, {_id: 1}).then(function () { +//// console.log('Inserted second entry'); +//// }); +//// }); +//}).otherwise(function (err) { +// console.error(err); +//}).always(function () { +// console.log('Done'); +// process.exit(); +//}); \ No newline at end of file From 4358546fe1009751e2597654efae1e9068de3c92 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 13 Mar 2013 12:11:35 -0700 Subject: [PATCH 09/64] Also track users on '/editor' paths. Usertracking is now a 'middleware'. --- package.json | 2 +- sandbox/{ => home}/sample/.scripted | 0 sandbox/{ => home}/sample/foo.js | 0 .../cloudfoundry-server-customizations.js} | 97 +++++++++++-------- .../{ => cloudfoundry}/start-cloudfoundry.js | 26 +++-- server/server.js | 9 +- server/utils/path-glob.js | 24 +++-- tests/server/path-glob-test.js | 41 +++++--- 8 files changed, 116 insertions(+), 83 deletions(-) rename sandbox/{ => home}/sample/.scripted (100%) rename sandbox/{ => home}/sample/foo.js (100%) rename server/{routes/cloudfoundry-routes.js => cloudfoundry/cloudfoundry-server-customizations.js} (63%) rename server/{ => cloudfoundry}/start-cloudfoundry.js (83%) diff --git a/package.json b/package.json index 1f865e3a..e4f3ca00 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "scripted": "./bin/scripted" }, "scripts": { - "start" : "node server/start-cloudfoundry.js", + "start" : "node server/cloudfoundry/start-cloudfoundry.js", "test": "node tests/server/run-all.js", "postinstall": "cd client && bower install" }, diff --git a/sandbox/sample/.scripted b/sandbox/home/sample/.scripted similarity index 100% rename from sandbox/sample/.scripted rename to sandbox/home/sample/.scripted diff --git a/sandbox/sample/foo.js b/sandbox/home/sample/foo.js similarity index 100% rename from sandbox/sample/foo.js rename to sandbox/home/sample/foo.js diff --git a/server/routes/cloudfoundry-routes.js b/server/cloudfoundry/cloudfoundry-server-customizations.js similarity index 63% rename from server/routes/cloudfoundry-routes.js rename to server/cloudfoundry/cloudfoundry-server-customizations.js index 8523df3a..ea106265 100644 --- a/server/routes/cloudfoundry-routes.js +++ b/server/cloudfoundry/cloudfoundry-server-customizations.js @@ -12,37 +12,42 @@ ******************************************************************************/ // -// On cloudfoundry this module replaces the handling of the '/' url. +// On cloudfoundry this module +// 1) replaces the handling of the '/' url. +// 2) adds some 'user tracking' middleware // -// It provides some logic to use a cooky to assign a unique identifier -// to a user. -// -// Instead of redirecting to user.home we then setup a sample project -// area for this user and redirect them to that area. var pathJoin = require('../jsdepend/utils').pathJoin; var when = require('when'); -var mongo = require('../mongo'); +var mongo = require('../mongo'); //TODO: inject rather than import database instance +var glob = require('../utils/path-glob'); + +var users = mongo.collection('users'); +/** + * Creates an error function in the context of a given http response object. + * The error fun may attache error info to the response. + */ function errorFun(res) { function error(err) { console.error(err); if (err.stack) { console.error(err.stack); } - res.status(500); - res.header('Content-Type', 'text/plain'); - res.write(""+err); - if (err.stack) { - res.write(""+err.stack); - } - res.end(); + //Only logging errors now, don't reject requests just because + //there's probably a bug or resource limit problem with user tracking. + +// res.status(500); +// res.header('Content-Type', 'text/plain'); +// res.write(""+err); +// if (err.stack) { +// res.write(""+err.stack); +// } +// res.end(); } return error; } -var users = mongo.collection('users'); - //TODO: this cooky stuff should really be done as a connect // middleware on every access not just on access to the 'root' path // of the webserver. @@ -59,7 +64,7 @@ exports.install = function (app, filesystem) { return pathJoin(getUserHome(), userID); } -// Discontinued: will only show a read-only sample now. +// Discontinued for now. We will only show a read-only sample now. // // function createUserArea(userID) { // var path = getUserPath(userID); @@ -134,29 +139,43 @@ exports.install = function (app, filesystem) { }); } - app.get('/', function (req, res) { - var userID = req.cookies.userID; - console.log('cookie userID = '+userID); - if (!userID) { - userID = generateUserID(); - } - //Yes... doing next sep asyncly (i.e. we aren't waiting for success to proceed). - when(userID, recordVisitor).otherwise(function (err) { - console.error('Problems recording a visit?'); - console.error(err); - if (err.stack) { - console.error(err.stack); + /** + * When one of these paths gets hit we create user tracking cookies. + */ + var trackedPaths = glob.fromJson([ + '/', '/editor/**' + ]); + + function trackUsers(req, res, next) { + var shouldTrack = trackedPaths.test(req.path); + console.log('trackUsers ['+shouldTrack+'] '+req.path); + if (shouldTrack) { + var userID = req.cookies.userID; + console.log('cookie userID = '+userID); + if (!userID) { + userID = generateUserID(); } - }); - when(userID, function (userID) { - res.cookie('userID', userID, { - maxAge: 365 * 24 * 3600 * 1000 /*expires in one year*/ - }); - res.redirect('/editor/sample'); -// createUserArea(userID).then(function () { -// res.redirect('/editor'+getUserHome()+'/'+userID); -// }); - }).otherwise(errorFun(res)); + //Yes... its deliberate we are doing next sep asyncly + // (i.e. we aren't waiting for success to proceed). + when(userID, recordVisitor).otherwise(errorFun(res)); + when(userID, function (userID) { + res.cookie('userID', userID, { + maxAge: 365 * 24 * 3600 * 1000 /*expires in one year*/ + }); + // createUserArea(userID).then(function () { + // res.redirect('/editor'+getUserHome()+'/'+userID); + // }); + }).otherwise(errorFun(res)); + } + next(); + } + + app.use(trackUsers); + + // Change the redirect target for the '/' path to point to the + // sample project. + app.get('/', function (req, res) { + res.redirect('/editor/home/sample'); }); }; \ No newline at end of file diff --git a/server/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js similarity index 83% rename from server/start-cloudfoundry.js rename to server/cloudfoundry/start-cloudfoundry.js index 979689c4..242db3b7 100644 --- a/server/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -62,28 +62,26 @@ var path = require('path'); var nodefs = require('fs'); -var mappedFs = require('../server/plugable-fs/mapped-fs'); -var scriptedFs = require('../server/plugable-fs/scripted-fs'); -var githubFs = require('../server/plugable-fs/github-fs/github-fs'); -var compose = require('../server/plugable-fs/composite-fs').compose; -var readOnly = require('../server/plugable-fs/read-only-fs'); -var unlistable = require('../server/plugable-fs/unlistable-fs'); +var mappedFs = require('../plugable-fs/mapped-fs'); +var scriptedFs = require('../plugable-fs/scripted-fs'); +var githubFs = require('../plugable-fs/github-fs/github-fs'); +var compose = require('../plugable-fs/composite-fs').compose; +var readOnly = require('../plugable-fs/read-only-fs'); +//var unlistable = require('../plugable-fs/unlistable-fs'); var withBaseDir = mappedFs.withBaseDir; var withPrefix = mappedFs.withPrefix; -var scriptedHomeLocation = path.resolve(__dirname, '..'); +var scriptedHomeLocation = path.resolve(__dirname, '../..'); +console.log('scripted.home = '+scriptedHomeLocation); -var sandbox = unlistable( - mappedFs.withBaseDir(path.resolve(__dirname, '../sandbox')), - '/home' -); +var sandbox = readOnly(mappedFs.withBaseDir(path.resolve(scriptedHomeLocation, 'sandbox'))); -var cache = require('./plugable-fs/github-fs/rest-node-manager').configure({ +var cache = require('../plugable-fs/github-fs/rest-node-manager').configure({ limit: 2500 // Limits number of in-memory cached nodes. }); var github = withPrefix('/github', githubFs.configure({ - token: require('./plugable-fs/github-fs/secret').token, + token: require('../plugable-fs/github-fs/secret').token, cache: cache })); @@ -113,7 +111,7 @@ var filesystem = scriptedFs.configure(corefs, { scriptedHome: '/scripted.home' }); -var server=require('../server/scriptedServer.js').start(filesystem, { +var server=require('../scriptedServer.js').start(filesystem, { port: 8123, cloudfoundry: true //Enables some customization for the cf deployed scripted 'showroom' app. }); diff --git a/server/server.js b/server/server.js index 6d23d21b..24a3091c 100644 --- a/server/server.js +++ b/server/server.js @@ -50,6 +50,11 @@ function configure(filesystem, options) { app.configure(function() { app.use(express.json()); app.use(express.cookieParser()); + + if (isCloudfoundry) { + require('./cloudfoundry/cloudfoundry-server-customizations').install(app, filesystem); + } + app.use(app.router); app.use(onRequest); // bridge to 'servlets', we should remove over time app.use(express['static'](pathResolve(__dirname, '../client'), { maxAge: 6e5 })); @@ -61,10 +66,6 @@ function configure(filesystem, options) { require('./servlets/status').install(app); - - if (isCloudfoundry) { - require('./routes/cloudfoundry-routes.js').install(app, filesystem); - } require('./routes/editor-routes').install(app, filesystem); require('./routes/test-routes').install(app); require('./routes/config-routes').install(app, filesystem); diff --git a/server/utils/path-glob.js b/server/utils/path-glob.js index 2dd799dc..e2527126 100644 --- a/server/utils/path-glob.js +++ b/server/utils/path-glob.js @@ -18,7 +18,7 @@ // against ant-like path patterns using '**' and '*'. // -// IMPORTANT: +// IMPORTANT: // It is assumed that patterns are always being matched against absolute // path strings. Any 'relative' patterns will be interpreted as matching // from the file system root. @@ -26,7 +26,9 @@ // TODO: It could make sense to make the 'root dir' to which relative paths // should be made relative a configuration option or parameter. -var pathResolve = require('../jsdepend/utils').pathResolve; +var utils = require('../jsdepend/utils'); +var pathResolve = utils.pathResolve; +var endsWith = utils.endsWith; function configure(isWindows) { @@ -42,12 +44,12 @@ function configure(isWindows) { } return false; } - + /** * Regexp snippet to match a '/' depending on the platform. */ var SLASH = isWindows ? '[\\\\/]' : '/'; - + /** * Regexp snippet to match a 'non slash' depending on the platform. */ @@ -63,7 +65,7 @@ function configure(isWindows) { function pat2regexp(pat) { //TODO: something like this also exists in client-side code to // support 'afterSave' exec action triggering. - + //TODO: is all this string concatenation slow? var re = "^"; if (isWindows) { @@ -82,22 +84,26 @@ function configure(isWindows) { re = re + '[a-zA-Z]:'; } } - + //At this point there should be no more differences between win or nowin since //we already dealt with the device portion of the pattern. - + //Ensure patterns always start with a '/' if (pat[0]!=='/') { pat = '/' + pat; } - + var i = 0; while (i Date: Wed, 13 Mar 2013 12:19:08 -0700 Subject: [PATCH 10/64] Cleanup: logging --- server/cloudfoundry/cloudfoundry-server-customizations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/cloudfoundry/cloudfoundry-server-customizations.js b/server/cloudfoundry/cloudfoundry-server-customizations.js index ea106265..d9db1f87 100644 --- a/server/cloudfoundry/cloudfoundry-server-customizations.js +++ b/server/cloudfoundry/cloudfoundry-server-customizations.js @@ -123,7 +123,7 @@ exports.install = function (app, filesystem) { //Options: { upsert: true, 'new': true } ).then(function (user) { - console.log('Visit recorded: '+JSON.stringify(user, null, ' ')); + console.log('Visit recorded: '+JSON.stringify(user[0])); return user; }); }); @@ -148,7 +148,7 @@ exports.install = function (app, filesystem) { function trackUsers(req, res, next) { var shouldTrack = trackedPaths.test(req.path); - console.log('trackUsers ['+shouldTrack+'] '+req.path); +// console.log('trackUsers ['+shouldTrack+'] '+req.path); if (shouldTrack) { var userID = req.cookies.userID; console.log('cookie userID = '+userID); From f647c7395647d1bc5b1c36164a7d5fdf9730e56c Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 13 Mar 2013 13:26:26 -0700 Subject: [PATCH 11/64] Bugfix: user tracking failed to actually set cooky. --- server/cloudfoundry/cloudfoundry-routes.js | 21 +++++++++++ ...rver-customizations.js => user-tracker.js} | 35 +++++++++---------- server/server.js | 10 +++++- 3 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 server/cloudfoundry/cloudfoundry-routes.js rename server/cloudfoundry/{cloudfoundry-server-customizations.js => user-tracker.js} (87%) diff --git a/server/cloudfoundry/cloudfoundry-routes.js b/server/cloudfoundry/cloudfoundry-routes.js new file mode 100644 index 00000000..2dbfde4a --- /dev/null +++ b/server/cloudfoundry/cloudfoundry-routes.js @@ -0,0 +1,21 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Kris De Volder + ******************************************************************************/ + +exports.install = function (app, filesystem) { + + app.get('/', function (req, res) { + console.log('Redirecting to sample area'); + res.redirect('/editor/home/sample'); + }); + +}; \ No newline at end of file diff --git a/server/cloudfoundry/cloudfoundry-server-customizations.js b/server/cloudfoundry/user-tracker.js similarity index 87% rename from server/cloudfoundry/cloudfoundry-server-customizations.js rename to server/cloudfoundry/user-tracker.js index d9db1f87..baa0ac97 100644 --- a/server/cloudfoundry/cloudfoundry-server-customizations.js +++ b/server/cloudfoundry/user-tracker.js @@ -30,20 +30,22 @@ var users = mongo.collection('users'); */ function errorFun(res) { function error(err) { + //Always log errors to console. console.error(err); if (err.stack) { console.error(err.stack); } - //Only logging errors now, don't reject requests just because - //there's probably a bug or resource limit problem with user tracking. - -// res.status(500); -// res.header('Content-Type', 'text/plain'); -// res.write(""+err); -// if (err.stack) { -// res.write(""+err.stack); -// } -// res.end(); + + //If res object is available also show error to the client. + if (res) { + res.status(500); + res.header('Content-Type', 'text/plain'); + res.write(""+err); + if (err.stack) { + res.write(""+err.stack); + } + res.end(); + } } return error; } @@ -149,25 +151,22 @@ exports.install = function (app, filesystem) { function trackUsers(req, res, next) { var shouldTrack = trackedPaths.test(req.path); // console.log('trackUsers ['+shouldTrack+'] '+req.path); - if (shouldTrack) { + if (!shouldTrack) { + return next(); + } else { var userID = req.cookies.userID; console.log('cookie userID = '+userID); if (!userID) { userID = generateUserID(); } - //Yes... its deliberate we are doing next sep asyncly - // (i.e. we aren't waiting for success to proceed). - when(userID, recordVisitor).otherwise(errorFun(res)); + when(userID, recordVisitor).otherwise(errorFun(null)); when(userID, function (userID) { res.cookie('userID', userID, { maxAge: 365 * 24 * 3600 * 1000 /*expires in one year*/ }); - // createUserArea(userID).then(function () { - // res.redirect('/editor'+getUserHome()+'/'+userID); - // }); + next(); }).otherwise(errorFun(res)); } - next(); } app.use(trackUsers); diff --git a/server/server.js b/server/server.js index 24a3091c..a69b5ce8 100644 --- a/server/server.js +++ b/server/server.js @@ -52,7 +52,9 @@ function configure(filesystem, options) { app.use(express.cookieParser()); if (isCloudfoundry) { - require('./cloudfoundry/cloudfoundry-server-customizations').install(app, filesystem); + //Add cf specific middleware + console.log('Add cf middleware'); + require('./cloudfoundry/user-tracker').install(app, filesystem); } app.use(app.router); @@ -64,6 +66,12 @@ function configure(filesystem, options) { })); }); + if (isCloudfoundry) { + console.log('Add cf routes'); + + //Add cf specific 'routes' + require('./cloudfoundry/cloudfoundry-routes').install(app, filesystem); + } require('./servlets/status').install(app); require('./routes/editor-routes').install(app, filesystem); From 74529e1382f8fe470345c2c905ec831d20f69e70 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 13 Mar 2013 15:58:42 -0700 Subject: [PATCH 12/64] Add todo about keybindings on CF app. --- server/cloudfoundry/start-cloudfoundry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index 242db3b7..80a52361 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -26,6 +26,8 @@ // - Disable shutdown hook // - auto restart after crash // - prefetch sensitive to rate limit remaining +// - keybindings: now that filesystem is all read-only, they can't be saved. +// //- (must do) accessible usage stats? We may need the numbers as //ammunition going forward, we need to know how many users try this out. From 933e5ba55557d3170b24ca013da6b18adb906bf0 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 13 Mar 2013 17:27:37 -0700 Subject: [PATCH 13/64] Make it possible to disable 'applicationManager' feature. Use to disable on CF instance. --- client/scripts/layoutManager.js | 11 ++++++++--- .../scripts/scripted/utils/server-options.js | 19 +++++++++++++++++++ server/cloudfoundry/start-cloudfoundry.js | 3 ++- server/scriptedServer.js | 7 +++++++ server/server.js | 15 ++++++++++++--- server/servlets/application-servlet.js | 7 ++++--- 6 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 client/scripts/scripted/utils/server-options.js diff --git a/client/scripts/layoutManager.js b/client/scripts/layoutManager.js index e093765e..e4c45c57 100644 --- a/client/scripts/layoutManager.js +++ b/client/scripts/layoutManager.js @@ -12,8 +12,11 @@ * Brian Cavalier ******************************************************************************/ define(['require', "jquery", "jquery_ui", "scripted/utils/navHistory", "scripted/utils/pageState", - "scripted/utils/editorUtils", "scripted/utils/storage", "scripted/exec/exec-on-load"], -function(require, jQuery, jqueryUi, mNavHistory, mPageState, editorUtils, storage, execOnLoad) { + "scripted/utils/editorUtils", "scripted/utils/storage", "scripted/exec/exec-on-load", + "scripted/utils/server-options" +], +function(require, jQuery, jqueryUi, mNavHistory, mPageState, editorUtils, storage, execOnLoad, +options) { // Initialize navigator state var navigatorVisible = false; @@ -142,7 +145,9 @@ function(require, jQuery, jqueryUi, mNavHistory, mPageState, editorUtils, storag }); // add live reloading support - require(['scripted/application-manager']); + if (options.applicationManager) { + require(['scripted/application-manager']); + } require(['scripted/editor/themeManager']); diff --git a/client/scripts/scripted/utils/server-options.js b/client/scripts/scripted/utils/server-options.js new file mode 100644 index 00000000..34377486 --- /dev/null +++ b/client/scripts/scripted/utils/server-options.js @@ -0,0 +1,19 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Kris De Volder + ******************************************************************************/ +define(function (require) { + //This module provides access to a bunch of options defined on the server-side. + // The options are configured in the .js file that starts the server. e.g. + // start-cloudfoundry.js + + return JSON.parse(require('text!/options')); +}); \ No newline at end of file diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index 80a52361..e3abd8dc 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -115,6 +115,7 @@ var filesystem = scriptedFs.configure(corefs, { var server=require('../scriptedServer.js').start(filesystem, { port: 8123, - cloudfoundry: true //Enables some customization for the cf deployed scripted 'showroom' app. + cloudfoundry: true, //Enables some customization for the cf deployed scripted 'showroom' app. + applicationManager: false //Disable the application manager. }); diff --git a/server/scriptedServer.js b/server/scriptedServer.js index 2b73213e..16c5af98 100644 --- a/server/scriptedServer.js +++ b/server/scriptedServer.js @@ -15,12 +15,19 @@ ******************************************************************************/ /*jslint node:true */ +var jsonMerge = require('./jsdepend/json-merge'); + // Entry point for the node app // Basic construction. Some requestHandlers are used for // specific actions. Other URLs are assumed to be static content. function start(filesystem, options) { +var defaultOptions = { + applicationManager: true +}; +options = jsonMerge(defaultOptions, options); + var isCloudfoundry = (options && options.cloudfoundry); var server = require("./server").configure(filesystem, options); diff --git a/server/server.js b/server/server.js index a69b5ce8..0f2b83f7 100644 --- a/server/server.js +++ b/server/server.js @@ -51,7 +51,7 @@ function configure(filesystem, options) { app.use(express.json()); app.use(express.cookieParser()); - if (isCloudfoundry) { + if (options.cloudfoundry) { //Add cf specific middleware console.log('Add cf middleware'); require('./cloudfoundry/user-tracker').install(app, filesystem); @@ -65,8 +65,15 @@ function configure(filesystem, options) { showStack: true })); }); + //Make the options available to client code. + app.get('/options', function (req, res) { + res.status(200); + res.header('Content-Type', 'application/json'); + res.write(JSON.stringify(options)); + res.end(); + }); - if (isCloudfoundry) { + if (options.cloudfoundry) { console.log('Add cf routes'); //Add cf specific 'routes' @@ -83,7 +90,9 @@ function configure(filesystem, options) { require('./servlets/incremental-search-servlet').install(app, filesystem); require('./servlets/incremental-file-search-servlet').install(app, filesystem); - require('./servlets/application-servlet').install(app); + if (options.applicationManager) { + require('./servlets/application-servlet').install(app); + } console.log('Server port = ' + port); app.server.listen(port, "127.0.0.1" /* only accepting connections from localhost */); diff --git a/server/servlets/application-servlet.js b/server/servlets/application-servlet.js index 80d3535f..79b6956d 100644 --- a/server/servlets/application-servlet.js +++ b/server/servlets/application-servlet.js @@ -38,11 +38,11 @@ exports.install = function (app) { */ app.get("/application/status", function(request, response) { // Return status of the server. - + // There is no path through 'serv' right now so we communicate // direct with the server it starts (see URL) var mimeclient = mime(); - + mimeclient({ path:url+"/serv/status", method:"GET" }).then(function(status_response) { response.send({"status": "running", "path": status_response.entity.path},200); },function(error) { @@ -52,6 +52,7 @@ exports.install = function (app) { /** Start the server */ app.put("/application/status", function(request, response) { + console.log('Starting app'); servStart.exec({cwd:request.param("path"),suppressOpen:request.param("suppressOpen"),logdir:request.param("path")}); response.end(); }); @@ -61,7 +62,7 @@ exports.install = function (app) { servStop.exec({}); response.end(); }); - + /** Reload a file */ app.post("/application/reload", function(request, response) { console.log("Reloading "+request.param("path")); From 6667074d08a8831674f52fc3809be2e027e6c5f1 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 10:00:52 -0700 Subject: [PATCH 14/64] Pickup inifnite retry bugfix in the cloned 'serv' utility. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e4f3ca00..db6341ad 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "node-static": "0.5.9", "optimist": "0.3.5", "rest": "0.8.4", - "serv": "https://github.com/aclement/serv/archive/master.tar.gz", + "serv": "https://github.com/kdvolder/serv/archive/master.tar.gz", "sockjs": "0.3.1", "websocket-multiplex": "https://github.com/kdvolder/websocket-multiplex/archive/master.tar.gz", "when": "https://github.com/cujojs/when/archive/noisy-deferred-then.tar.gz", From 018c91f8c9f035960888c57591bab8d91549e752 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 13:20:42 -0700 Subject: [PATCH 15/64] Add some stuff to play-area to test live-reload feature. --- play-area/css/main.css | 3 +++ play-area/index.html | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 play-area/css/main.css diff --git a/play-area/css/main.css b/play-area/css/main.css new file mode 100644 index 00000000..1953c56b --- /dev/null +++ b/play-area/css/main.css @@ -0,0 +1,3 @@ +h1 { + color: blue +} \ No newline at end of file diff --git a/play-area/index.html b/play-area/index.html index 85b534cd..fc1f1140 100644 --- a/play-area/index.html +++ b/play-area/index.html @@ -4,10 +4,10 @@ Iearch experiment - + +

Hello

- -fff \ No newline at end of file + \ No newline at end of file From fc61cd2105714b013bdb6ef9d26eebe75c2dc1b3 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 14:07:36 -0700 Subject: [PATCH 16/64] Move 'status.js' servlet to its proper place. Disable shutdown behavior on CF. --- play-area/kill-scripted.js | 16 ++++++++++++ server/cloudfoundry/start-cloudfoundry.js | 3 ++- .../status.js => routes/status-routes.js} | 25 ++++++++++++------- server/scriptedServer.js | 6 ++++- server/server.js | 2 +- 5 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 play-area/kill-scripted.js rename server/{servlets/status.js => routes/status-routes.js} (64%) diff --git a/play-area/kill-scripted.js b/play-area/kill-scripted.js new file mode 100644 index 00000000..3de16373 --- /dev/null +++ b/play-area/kill-scripted.js @@ -0,0 +1,16 @@ + +var killUrl = 'http://localhost:8123/status'; +//var killUrl = 'http://scrptd.cloudfoundry.com/status + +var rest = require('rest'); + +console.log('Making kill request'); +rest({ + path: killUrl, + method: 'DELETE' +}).then(function (resp) { + console.log('status = ' + resp.status.code); + console.log('entity = ' + resp.entity); +}).otherwise(function (err) { + console.error(err); +}); \ No newline at end of file diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index e3abd8dc..6ef7ab1e 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -116,6 +116,7 @@ var filesystem = scriptedFs.configure(corefs, { var server=require('../scriptedServer.js').start(filesystem, { port: 8123, cloudfoundry: true, //Enables some customization for the cf deployed scripted 'showroom' app. - applicationManager: false //Disable the application manager. + applicationManager: false, //Disable the application manager. + shutdownHook: false //Disable the 'shutdown hook' used by 'scr -k' and 'scr -r' commands. }); diff --git a/server/servlets/status.js b/server/routes/status-routes.js similarity index 64% rename from server/servlets/status.js rename to server/routes/status-routes.js index 71e56947..50edef0f 100644 --- a/server/servlets/status.js +++ b/server/routes/status-routes.js @@ -12,7 +12,7 @@ * Andy Clement - overhaul ******************************************************************************/ -exports.install = function (app) { +exports.install = function (app, options) { app.get("/status",function(request, response) { response.writeHead(200, { @@ -22,14 +22,21 @@ exports.install = function (app) { response.write("\n"); response.end(); }); - + app.del("/status",function(request, response) { - response.writeHead(200, {"Content-Type": "text/plain"}); - response.write("Server will stop shortly"); - response.write("\n"); - response.end(); - console.log("Scripted is exiting..."); - process.exit(); - // this might be better: app.close(); + if (options.shutdownHook) { + response.writeHead(200, {"Content-Type": "text/plain"}); + response.write("Server will stop shortly"); + response.write("\n"); + response.end(); + console.log("Scripted is exiting..."); + process.exit(); + // this might be better: app.close(); + } else { + response.status(403); + response.header('Content-Type', 'text/plain'); + response.write('Scripted server shutdown hook is disabled'); + response.end(); + } }); }; \ No newline at end of file diff --git a/server/scriptedServer.js b/server/scriptedServer.js index 16c5af98..a1f09f60 100644 --- a/server/scriptedServer.js +++ b/server/scriptedServer.js @@ -23,8 +23,12 @@ var jsonMerge = require('./jsdepend/json-merge'); function start(filesystem, options) { +//Default options for the 'localhost', commandline version of scripted. +// Note: any options that need to be false by default don't need to be +// added here since not setting them will already make them 'falsy'. var defaultOptions = { - applicationManager: true + applicationManager: true, + shutdownHook: true }; options = jsonMerge(defaultOptions, options); diff --git a/server/server.js b/server/server.js index 0f2b83f7..1981bc12 100644 --- a/server/server.js +++ b/server/server.js @@ -79,7 +79,7 @@ function configure(filesystem, options) { //Add cf specific 'routes' require('./cloudfoundry/cloudfoundry-routes').install(app, filesystem); } - require('./servlets/status').install(app); + require('./routes/status-routes').install(app, options); require('./routes/editor-routes').install(app, filesystem); require('./routes/test-routes').install(app); From 53543046ad8e6005db1440f3e0622f5244f156aa Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 15:02:57 -0700 Subject: [PATCH 17/64] Disabled exec now show more sensible error message, also not disabled by 'cloudfoundry' option but by its own optional switch. --- client/scripts/scripted/exec/exec-shared.js | 5 +- client/scripts/servlets/stub-maker.js | 8 ++-- play-area/kill-scripted.js | 6 +-- server/cloudfoundry/start-cloudfoundry.js | 35 +++++++------- server/scriptedServer.js | 8 ++-- server/servlets/exec-servlet.js | 51 +++++++++++++-------- 6 files changed, 64 insertions(+), 49 deletions(-) diff --git a/client/scripts/scripted/exec/exec-shared.js b/client/scripts/scripted/exec/exec-shared.js index e2cdfafa..31d16a8f 100644 --- a/client/scripts/scripted/exec/exec-shared.js +++ b/client/scripts/scripted/exec/exec-shared.js @@ -18,11 +18,8 @@ // // Common functionality shared between exec-keys and exec-after-save // -// TODO: actually, exec-keys isn't using this yet. The code was merely copied -// from it. ///////////////////////////// -define(['require', 'servlets/exec-client', 'scripted/exec/exec-console'], -function (require) { +define(function (require) { var exec = require('servlets/exec-client').exec; var execConsole = require('scripted/exec/exec-console'); diff --git a/client/scripts/servlets/stub-maker.js b/client/scripts/servlets/stub-maker.js index 9de5cffc..902f3565 100644 --- a/client/scripts/servlets/stub-maker.js +++ b/client/scripts/servlets/stub-maker.js @@ -146,8 +146,8 @@ define(['when'], function(when) { if(xhrobj.readyState === 4) { // 4 means content has finished loading if (xhrobj.status===200) { callback.apply(null, JSON.parse(xhrobj.responseText)); - } else if (xhrobj.status===500) { - callback("Error: xhr request status = "+xhrobj.responseText); + } else { + callback("Error: status ["+xhrobj.status+"] "+xhrobj.responseText); } } }; @@ -169,8 +169,8 @@ define(['when'], function(when) { if(xhrobj.readyState === 4) { // 4 means content has finished loading if (xhrobj.status===200) { callback.apply(null, JSON.parse(xhrobj.responseText)); - } else if (xhrobj.status===500) { - callback("Error: xhr request status = "+xhrobj.responseText); + } else { + callback("Error: status ["+xhrobj.status+"] "+xhrobj.responseText); } } }; diff --git a/play-area/kill-scripted.js b/play-area/kill-scripted.js index 3de16373..32ce2e8a 100644 --- a/play-area/kill-scripted.js +++ b/play-area/kill-scripted.js @@ -1,6 +1,6 @@ -var killUrl = 'http://localhost:8123/status'; -//var killUrl = 'http://scrptd.cloudfoundry.com/status +//var killUrl = 'http://localhost:8123/status'; +var killUrl = 'http://scrptd.cloudfoundry.com/status'; var rest = require('rest'); @@ -13,4 +13,4 @@ rest({ console.log('entity = ' + resp.entity); }).otherwise(function (err) { console.error(err); -}); \ No newline at end of file +}); diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index 6ef7ab1e..f089671d 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -17,16 +17,12 @@ // TODO: Before this can go 'public' // -// - disable 'exec' related features. Don't really want people to run exec commands -// on cf hosts. -// - remove or fix the 'Play' button. // - Customized readme shown when opening on a folder. // - A reasonable piece of sample code to pre-populate first-time visitor space. // - Ask Scott to vacate domain name 'scripted.cloudfoundry.com' so we can use that. -// - Disable shutdown hook -// - auto restart after crash -// - prefetch sensitive to rate limit remaining // - keybindings: now that filesystem is all read-only, they can't be saved. +// fix: should make 'keybindings store' injectable on client-side +// and then use localstorage. // //- (must do) accessible usage stats? We may need the numbers as @@ -36,18 +32,9 @@ // addresses of visitors (do we?) - but just a count of users creating //projects would be useful. // -//- (must do) decide how to handle these things: -//(a) how do we stop -//people putting up stuff they shouldn't? Either copyrighted or offensive -//material. Do we have to care about that? Feels like we might. Do we need -// some kind of disclaimer - like the jsfiddle one. -//(b) how do we check -// the space isn't filled up? Handle rogue users filling it up? Can we -//easily see all the material that is up there? // //- (must do) improve the landing page getting started text or even offer alternative text when deployed in this way. // -//- (must do) decide on exec keys, do we need to shut it off? We can't expose a server to running arbitrary commands. // //- // (must do) links to download pages for scripted, maybe to @@ -59,8 +46,23 @@ // - github-fs // - login mechanis to obtain oauth token for individual user (optional for 'demo') +// - prefetch sensitive to rate limit remaining // - upload zip? +// DONE +//- disable shutdown hook +//- disable play / stop button +//- (must do) decide on exec keys, do we need to shut it off? We can't expose a server to running arbitrary commands. +//- (must do) decide how to handle these things: +//(a) how do we stop +//people putting up stuff they shouldn't? Either copyrighted or offensive +//material. Do we have to care about that? Feels like we might. Do we need +// some kind of disclaimer - like the jsfiddle one. +//(b) how do we check +// the space isn't filled up? Handle rogue users filling it up? Can we +//easily see all the material that is up there? + + var path = require('path'); var nodefs = require('fs'); @@ -117,6 +119,7 @@ var server=require('../scriptedServer.js').start(filesystem, { port: 8123, cloudfoundry: true, //Enables some customization for the cf deployed scripted 'showroom' app. applicationManager: false, //Disable the application manager. - shutdownHook: false //Disable the 'shutdown hook' used by 'scr -k' and 'scr -r' commands. + shutdownHook: false, //Disable the 'shutdown hook' used by 'scr -k' and 'scr -r' commands. + exec: false //Disable 'exec' related features. }); diff --git a/server/scriptedServer.js b/server/scriptedServer.js index a1f09f60..6a7d7b26 100644 --- a/server/scriptedServer.js +++ b/server/scriptedServer.js @@ -28,7 +28,8 @@ function start(filesystem, options) { // added here since not setting them will already make them 'falsy'. var defaultOptions = { applicationManager: true, - shutdownHook: true + shutdownHook: true, + exec: true }; options = jsonMerge(defaultOptions, options); @@ -43,9 +44,8 @@ var requestHandlers = require("./requestHandlers").configure(filesystem); //require("./servlets/hello"); //require("./servlets/listFiles"); //Dead? require("./servlets/jsdepend-servlet").install(filesystem); -if (!isCloudfoundry) { - require("./servlets/exec-servlet"); -} +require("./servlets/exec-servlet").install(options); + //require("./servlets/config-servlet"); //these two wired up in server. //require("./servlets/kill"); diff --git a/server/servlets/exec-servlet.js b/server/servlets/exec-servlet.js index 3868d628..cefd1751 100644 --- a/server/servlets/exec-servlet.js +++ b/server/servlets/exec-servlet.js @@ -18,24 +18,39 @@ var makeRequestHandler = require('./servlet-utils').makeRequestHandler; var cpExec = require('child_process').exec; -function exec(cmd, callback, errback) { - - //cmd looks like this: - // {cmd: "ls -la", ...other options to pass to the nodejs exec function...} - - var options = cmd; - cmd = cmd.cmd; - - // TODO use mixin, if scipted had one... - // options.env = mixin({}, options.env, process.env); - options.env = options.env || {}; - for (var env in process.env) { - if (!(env in options.env)) { - options.env[env] = process.env[env]; - } +function install(options) { + + var exec; + + if (options.exec) { + exec = function (cmd, callback) { + + //cmd looks like this: + // {cmd: "ls -la", ...other options to pass to the nodejs exec function...} + + var options = cmd; + cmd = cmd.cmd; + + // TODO use mixin, if scipted had one... + // options.env = mixin({}, options.env, process.env); + options.env = options.env || {}; + for (var env in process.env) { + if (!(env in options.env)) { + options.env[env] = process.env[env]; + } + } + + /*var process = */cpExec(cmd, options, callback); + }; + } else { + exec = function (cmd, callback) { + callback('ERROR: exec is disabled'); + }; } - /*var process = */cpExec(cmd, options, callback); + exec.remoteType = ['JSON', 'callback']; + servlets.register('/exec', makeRequestHandler(exec)); + } -exec.remoteType = ['JSON', 'callback']; -servlets.register('/exec', makeRequestHandler(exec)); \ No newline at end of file + +exports.install = install; \ No newline at end of file From b72e6497410797263fa1cea2eff6371bfca35216 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 15:04:19 -0700 Subject: [PATCH 18/64] Update CF TODO comments. --- server/cloudfoundry/start-cloudfoundry.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index f089671d..71d5d9a2 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -25,12 +25,6 @@ // and then use localstorage. // -//- (must do) accessible usage stats? We may need the numbers as -//ammunition going forward, we need to know how many users try this out. -// At least number of visitors who try it out - if this is captured in the -// server log, can we access that file? I don't think we want to track IP -// addresses of visitors (do we?) - but just a count of users creating -//projects would be useful. // // //- (must do) improve the landing page getting started text or even offer alternative text when deployed in this way. @@ -61,6 +55,12 @@ //(b) how do we check // the space isn't filled up? Handle rogue users filling it up? Can we //easily see all the material that is up there? +//- (must do) accessible usage stats? We may need the numbers as +//ammunition going forward, we need to know how many users try this out. +// At least number of visitors who try it out - if this is captured in the +// server log, can we access that file? I don't think we want to track IP +// addresses of visitors (do we?) - but just a count of users creating +//projects would be useful. var path = require('path'); From 97038b554a7b3f88acac4749e81fd0d1ef60562c Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 16:43:41 -0700 Subject: [PATCH 19/64] Make help text customizable, customize for cf. --- server/cloudfoundry/start-cloudfoundry.js | 51 ++++++++++++++++++++++- server/scriptedServer.js | 5 +++ server/server.js | 12 ++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index 71d5d9a2..bc51da36 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -120,6 +120,55 @@ var server=require('../scriptedServer.js').start(filesystem, { cloudfoundry: true, //Enables some customization for the cf deployed scripted 'showroom' app. applicationManager: false, //Disable the application manager. shutdownHook: false, //Disable the 'shutdown hook' used by 'scr -k' and 'scr -r' commands. - exec: false //Disable 'exec' related features. + exec: false, //Disable 'exec' related features. + help_text: [ +" _ _ _ _ ", +" | || || | | | _ ", +" | || || |_____| | ____ ___ ____ _____ _| |_ ___ ", +" | || || | ___ | |/ ___) _ \\| \\| ___ | (_ _) _ \\ ", +" | || || | ____| ( (__| |_| | | | | ____| | || |_| |", +" \\_____/|_____)\\_)____)___/|_|_|_|_____) \\__)___/ ", +"", +" ______ _ _ ", +" / _____) (_) _ | |", +" ( (____ ____ ____ _ ____ _| |_ _____ __| |", +" \\____ \\ / ___)/ ___) | _ (_ _) ___ |/ _ |", +" _____) | (___| | | | |_| || |_| ____( (_| |", +" (______/ \\____)_| |_| __/ \\__)_____)\\____|", +" |_| ", +"", +" This is a DEMO of the Scripted Editor running on 'cloudfoundry.com'.", +" Here you can quickly try out Scripted without any hassles such as", +" installing, signing-up, etc.", +"", +" Unfortunately, due to practical and legal limitations we", +" could only make this 'hassle free' publically hosted DEMO with a", +" read-only file system.", +"", +" We hope this demo will help you decide if you want to give Scripted", +" a 'real' try and install it for a more thorough try-out.", +"", +" To find out more visit our GitHub homepage:", +"", +" 'http://github.com/scripted-editor/scripted", +"", +" Some basic instructions for getting started with Scripted:", +"", +" Use the navigator on the left to select a file for editing.", +"", +" Help on all supported key bindings is available by clicking the", +" '?' icon in the top right, or simply pressing 'F1'", +"", +" To search your project for a file to open by name, press 'Cmd/Ctrl+Shift+F'", +" to show the 'Open File' dialog.", +"", +" To search for a file based simply on a string within it, press ", +" 'Cmd/Ctrl+Shift+L' to open the 'Look in files' dialog.", +"", +" The 'bars' icon next to the help icon opens the side panel which can", +" host a second editor, pressing 'Shift' when opening any link or navigable", +" JavaScript reference in Scripted will open the target in the side panel.", +" The side panel can also be opened/closed with 'Cmd/Ctrl+Shift+E'." + ] }); diff --git a/server/scriptedServer.js b/server/scriptedServer.js index 6a7d7b26..f3a08b04 100644 --- a/server/scriptedServer.js +++ b/server/scriptedServer.js @@ -32,6 +32,11 @@ var defaultOptions = { exec: true }; options = jsonMerge(defaultOptions, options); +if (options.help_text) { + if (Array.isArray(options.help_text)) { + options.help_text = options.help_text.join('\n'); + } +} var isCloudfoundry = (options && options.cloudfoundry); diff --git a/server/server.js b/server/server.js index 1981bc12..8e854b5d 100644 --- a/server/server.js +++ b/server/server.js @@ -65,6 +65,7 @@ function configure(filesystem, options) { showStack: true })); }); + //Make the options available to client code. app.get('/options', function (req, res) { res.status(200); @@ -73,6 +74,17 @@ function configure(filesystem, options) { res.end(); }); + //Serve alternate help.txt + if (options.help_text) { + app.get('/scripts/scripted/help.txt', function (req, res) { + console.log('Request for help.txt intercepted'); + res.status(200); + res.header('Content-Type', 'text/plain'); + res.write(options.help_text); + res.end(); + }); + } + if (options.cloudfoundry) { console.log('Add cf routes'); From e397bb94e5f5c14c8fed930e64189c986abfc1c1 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Thu, 14 Mar 2013 16:55:43 -0700 Subject: [PATCH 20/64] basic textual replace refactoring --- .../editor/InlineRenameRefactoring.js | 235 ++++++++++++++++++ .../scripts/scripted/editor/scriptedEditor.js | 4 + 2 files changed, 239 insertions(+) create mode 100644 client/scripts/scripted/editor/InlineRenameRefactoring.js diff --git a/client/scripts/scripted/editor/InlineRenameRefactoring.js b/client/scripts/scripted/editor/InlineRenameRefactoring.js new file mode 100644 index 00000000..abf3e9b2 --- /dev/null +++ b/client/scripts/scripted/editor/InlineRenameRefactoring.js @@ -0,0 +1,235 @@ +define(['orion/textview/keyBinding','scripted/markoccurrences'], +function(keyBinding,markoccurrences) { + + var State = { INACTIVE: 1, ACTIVE: 2 }; + + function InlineRenameRefactoring(editor,undoStack,linkedMode) { + this._undoStack = undoStack; + this._linkedMode = linkedMode; + this._editor = editor; + this._textView = editor.getTextView(); + this._inlineRenameRefactoringMode = new InlineRenameRefactoringMode(this,undoStack); + this._state = State.INACTIVE; + var self = this; + + this._editor.pushKeyMode(this._inlineRenameRefactoringMode); + this._textView.setKeyBinding(new keyBinding.KeyBinding('r', false, true, false, true), "rename"); + this._textView.setAction("rename", function() { + self.activate(); + return true; + }, {name: "rename"}); + } + + InlineRenameRefactoring.prototype = { + activate: function() { + var selection = this._editor.getSelection(); + if (selection.start===selection.end) { + console.log("rename only activates on selection"); + return; + } + if (this._state === State.INACTIVE) { + console.log("activated"); + this._textView.addKeyPressHandler(this._inlineRenameRefactoringMode); + this._setState(State.ACTIVE); + + // Locate the place where the selected text exists in the file + var model = this._editor.getModel(); + var selectedText=model.getText(selection.start,selection.end); + console.log("Selection is >"+selectedText+"<"); + + this._undoStack.startCompoundChange(); + var matcher = new markoccurrences.SelectionMatcher(); + var results = matcher.findMatches(selection.start,selection.end,this._editor.getText()); + if (results.matches) { + console.log("word >"+results.word+"< matchcount=#"+results.matches.length); + for (var m=0;m"+key+"<"); + + var replacementLength = word.length; + if (typed.length>0) { + replacementLength = typed.length; + } + typed = typed+ch; + this._inlineRenameRefactoring.typed = typed; + var offset = 0; + for (var m=0;m0) { +// var linetext = model.getLine(lineNum,false); +// var options = textview.getOptions("tabSize", "expandTab"); //$NON-NLS-1$ //$NON-NLS-0$ +// var tabtext = options.expandTab ? new Array(options.tabSize + 1).join(" ") : "\t"; //$NON-NLS-1$ //$NON-NLS-0$ +// if (this._isAllWhitespace(linetext)) { +// if (this._endsWith(linetext,tabtext)) { +// // check the previous line +// var unindent = false; +// var previouslinetext=model.getLine(lineNum-1,false); +// var previouslinelength = previouslinetext.length; +// // If the line before starts with the same amount of whitespace, probably worth unindenting: +// if (previouslinelength>linetext.length && previouslinetext.indexOf(linetext)===0 && +// !this._isWhitespace(previouslinetext.charAt(linetext.length)) && +// previouslinetext.charAt(previouslinetext.length-1)!=='{') { +// unindent = true; +// } +// // if the line before ends with a '{' unindent +// if (previouslinelength>0 && previouslinetext.charAt(previouslinetext.length-1)==='{' && +// linetext.length>this._posOfFirstNonwhitespace(previouslinetext)) { +// unindent = true; +// // TODO unindent to same level? +// } +// +// if (unindent) { +// // replace the 'tab' with the '}' and be done. +// textview._modifyContent({text: '}', start: selection.start-tabtext.length, end: selection.end, _ignoreDOMSelection: true}, true); +// return true; +// } +// } +// } +// } + }, +// lineUp: function() { +// var newSelected = (this.selectedIndex === 0) ? this.proposals.length - 1 : this.selectedIndex - 1; +// while (this.proposals[newSelected].unselectable && newSelected > 0) { +// newSelected--; +// } +// this.selectedIndex = newSelected; +// if (this.widget) { +// this.widget.setSelectedIndex(this.selectedIndex); +// } +// return true; +// }, +// lineDown: function() { +// var newSelected = (this.selectedIndex === this.proposals.length - 1) ? 0 : this.selectedIndex + 1; +// while (this.proposals[newSelected].unselectable && newSelected < this.proposals.length-1) { +// newSelected++; +// } +// this.selectedIndex = newSelected; +// if (this.widget) { +// this.widget.setSelectedIndex(this.selectedIndex); +// } +// return true; +// }, + enter: function() { + this._undoStack.endCompoundChange(); + this._inlineRenameRefactoring.deactivate(); + var locations = this._inlineRenameRefactoring.matches; + var matchNumber = this._inlineRenameRefactoring.matchNumber; + var word = this._inlineRenameRefactoring.word; + var caret = locations[matchNumber] - matchNumber*(word.length-1)+ matchNumber*(this._inlineRenameRefactoring.typed.length-1); + var tv = this._inlineRenameRefactoring._textView; + tv.setCaretOffset(caret); + return true; + }, + tab: function() { + this._undoStack.endCompoundChange(); + this._inlineRenameRefactoring.deactivate(); + return true; + } + }; + + return { + InlineRenameRefactoring:InlineRenameRefactoring + }; + +}); diff --git a/client/scripts/scripted/editor/scriptedEditor.js b/client/scripts/scripted/editor/scriptedEditor.js index 1f9a7517..815fcc58 100644 --- a/client/scripts/scripted/editor/scriptedEditor.js +++ b/client/scripts/scripted/editor/scriptedEditor.js @@ -18,6 +18,7 @@ define([ "require", + "scripted/editor/InlineRenameRefactoring", "scripted/api/editor-wrapper", "scripted/utils/deref", "scripted/editor/save-hooks", "when", "scripted/fileapi", "orion/textview/textView", "orion/textview/keyBinding", "orion/editor/editor", @@ -31,6 +32,7 @@ define([ "scripted/exec/exec-keys", "scripted/exec/exec-after-save", "jshint", "jquery" ], function ( require, + inlineRenameRefactoring, EditorProxy, deref, mSaveHooks, when, fileapi, mTextView, mKeyBinding, mEditor, mKeystroke, @@ -573,6 +575,8 @@ define([ return true; }); + new inlineRenameRefactoring.InlineRenameRefactoring(editor,undoStack,linkedMode); + // save binding editor.getTextView().setKeyBinding(new mKeyBinding.KeyBinding("s", true), "Save"); editor.getTextView().setAction("Save", function() { From 25e2affd3eb7a6fff82af923c430b501908def14 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Thu, 14 Mar 2013 17:17:42 -0700 Subject: [PATCH 21/64] Disable keybindings editor in CF version. --- .../scripts/scripted/keybindings/keyhelp.js | 36 ++++++++++--------- server/cloudfoundry/start-cloudfoundry.js | 17 ++++----- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/client/scripts/scripted/keybindings/keyhelp.js b/client/scripts/scripted/keybindings/keyhelp.js index 29022046..1fc2ff72 100644 --- a/client/scripts/scripted/keybindings/keyhelp.js +++ b/client/scripts/scripted/keybindings/keyhelp.js @@ -21,10 +21,12 @@ // from the main app. define(['jsrender', 'jquery', './keybinder', './keystroke', './keyedit', - "scripted/utils/editorUtils",'text!./_keybinding.tmpl.html', './action-info'], -function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, commandTemplate, mActionInfo) { + "scripted/utils/editorUtils",'text!./_keybinding.tmpl.html', './action-info', + "scripted/utils/server-options"], +function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, commandTemplate, mActionInfo, +options) { - var attachKeyEditor = mKeyedit.attachKeyEditor; + var attachKeyEditor = options.keyedit ? mKeyedit.attachKeyEditor : function () {}; var getActionDescription = mActionInfo.getActionDescription; var getCategory = mActionInfo.getCategory; @@ -32,7 +34,7 @@ function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, comm // use a copy so we can sort var editor = editorUtil.getMainEditor(); var keyBindings = editor.getTextView()._keyBindings.filter(function (kb) { return kb.actionID; }); - + // not perfect since not all names are correct here, but pretty close keyBindings.sort(function(l,r) { var lname = getActionDescription(l.actionID); @@ -54,23 +56,23 @@ function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, comm return keyBindings; } - + /** * Render or re-render the current keybindings to the help side panel. */ function renderKeyHelp() { var editor = editorUtil.getMainEditor(); - + $.views.converters({ toKeystroke: mKeystroke.fromKeyBinding, toShortcutName: getActionDescription }); var keyBindings = getSortedKeybindings(); - + var importantKeyBindings = []; var otherKeyBindings = []; - + keyBindings.forEach(function (kb) { switch(getCategory(kb.actionID)) { case 'hidden': @@ -82,9 +84,9 @@ function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, comm importantKeyBindings.push(kb); } }); - + var tmpl = $.templates(commandTemplate); - + function render(it, into) { if (Array.isArray(it)) { for (var i = 0; i < it.length; i++) { @@ -96,9 +98,9 @@ function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, comm into.append(element); } } - + var cl = $('#command_list'); - + cl.empty(); cl.append("Click any key binding value to configure it. Scroll down to see unbound actions."); cl.append('

  • '); @@ -119,10 +121,10 @@ function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, comm editor.getTextView()._update(); } - + /*Command help panel*/ var help_close, help_open; - + var isOpen = false; help_open = function (){ @@ -140,13 +142,13 @@ function (mJsRender, mJquery, mKeybinder, mKeystroke, mKeyedit, editorUtil, comm $('#help_open').off('click'); $('#help_open').on('click', help_open); }; - + $('#help_open').on('click', help_open); $('#help_panel').on('refresh', function () { if (isOpen) { //Don't bother to render if the panel is not visible. renderKeyHelp(); } }); - + }); - + diff --git a/server/cloudfoundry/start-cloudfoundry.js b/server/cloudfoundry/start-cloudfoundry.js index bc51da36..e09566ec 100644 --- a/server/cloudfoundry/start-cloudfoundry.js +++ b/server/cloudfoundry/start-cloudfoundry.js @@ -17,17 +17,11 @@ // TODO: Before this can go 'public' // -// - Customized readme shown when opening on a folder. +// - Customized readme shown when opening on a folder (done), but contents +// is still a bit iffy. + // - A reasonable piece of sample code to pre-populate first-time visitor space. // - Ask Scott to vacate domain name 'scripted.cloudfoundry.com' so we can use that. -// - keybindings: now that filesystem is all read-only, they can't be saved. -// fix: should make 'keybindings store' injectable on client-side -// and then use localstorage. -// - -// -// -//- (must do) improve the landing page getting started text or even offer alternative text when deployed in this way. // // //- @@ -61,11 +55,12 @@ // server log, can we access that file? I don't think we want to track IP // addresses of visitors (do we?) - but just a count of users creating //projects would be useful. +// - keybindings: now that filesystem is all read-only, they can't be saved. +// 'fix' disable the keyeditor in CF version var path = require('path'); -var nodefs = require('fs'); var mappedFs = require('../plugable-fs/mapped-fs'); var scriptedFs = require('../plugable-fs/scripted-fs'); var githubFs = require('../plugable-fs/github-fs/github-fs'); @@ -121,6 +116,8 @@ var server=require('../scriptedServer.js').start(filesystem, { applicationManager: false, //Disable the application manager. shutdownHook: false, //Disable the 'shutdown hook' used by 'scr -k' and 'scr -r' commands. exec: false, //Disable 'exec' related features. + keyedit: false, //Disable help side-panel's keybdingins editor as it doesn't work well with + // a shared fs, and won't work at all with a read-only fs. help_text: [ " _ _ _ _ ", " | || || | | | _ ", From e69f86620d4340f65b01f83495f141e9898a1833 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Fri, 15 Mar 2013 08:19:58 -0700 Subject: [PATCH 22/64] moving key press handler to further through down default key handling code --- client/scripts/orion/textview/textView.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/scripts/orion/textview/textView.js b/client/scripts/orion/textview/textView.js index d1bbfb18..317875f1 100644 --- a/client/scripts/orion/textview/textView.js +++ b/client/scripts/orion/textview/textView.js @@ -2781,15 +2781,6 @@ define("orion/textview/textView", ['orion/textview/textModel', 'orion/textview/k } }, _handleKeyPress: function (e) { - if (this._keyPressHandlers) { - for (var kph=0; kph 31) { this._doContent(String.fromCharCode (key)); From 1b6a404d23cf56e34693978b39e9ac6e8120c72f Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 15 Mar 2013 16:27:57 -0700 Subject: [PATCH 23/64] Implement find references for variables --- .../plugins/esprima/refactoringSupport.js | 167 ++++++++++++++++++ client/scripts/scripted/markoccurrences.js | 36 ++-- 2 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 client/scripts/plugins/esprima/refactoringSupport.js diff --git a/client/scripts/plugins/esprima/refactoringSupport.js b/client/scripts/plugins/esprima/refactoringSupport.js new file mode 100644 index 00000000..078ebaa7 --- /dev/null +++ b/client/scripts/plugins/esprima/refactoringSupport.js @@ -0,0 +1,167 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Andrew Eisenberg (VMware) - initial API and implementation + ******************************************************************************/ + +/** + * This module provides tools to help wth refactoring + */ + +/*global define */ +define(function (require) { + + var visitor = require('plugins/esprima/esprimaVisitor'); + var findSelectedWord = require('scripted/markOccurrences').findSelectedWord; + + + function inRange(range, offset) { + return range[0] <= offset && range[1] >= offset; + } + + /** + * finds all references for the given variable under the + * selection + * @param {{start:Number,end:Number}} selection the currently selected element + * @param String buffer the entire contents of the editor + * @returns [{start:Number,end:Number}] an array of elemnnts corresponding to references + * or null if invalid location + */ + return { + findVarReferences : function (buffer, selection) { + var expanded = findSelectedWord(selection.start, selection.end, buffer); + if (!expanded) { + return null; + } + var i; + var root = visitor.parse(buffer); + var parentStack = []; + var refNodes = []; + var declScopes = []; + + // is this node a valid reference??? + function isRef(node) { + var parent = parentStack[parentStack.length-1]; + if (parent.type === 'Property') { + return node === parent.value; + } + if (parent.type === 'MemberExpression') { + return node === parent.object; + } + return parent.type === 'CallExpression' || + parent.type === 'ArrayExpression' || + parent.type === 'AssignmentExpression' || + parent.type === 'VariableDeclaration' || + parent.type === 'ExpressionStatement' || + parent.type === 'FunctionExpression' || + parent.type === 'NewExpression' || + parent.type === 'VariableDeclarator' || + parent.type === 'IfStatement' || + parent.type === 'WhileStatement' || + parent.type === 'DoWhileStatement' || + parent.type === 'FunctionExpression' || + parent.type === 'FunctionDeclaration' || + parent.type === 'ForInStatement' || + parent.type === 'ForStatement' || + parent.type === 'CatchClause' || + parent.type === 'TryStatement' || + parent.type === 'BlockStatement' || + parent.type === 'SwitchStatement' || + parent.type === 'SwitchCase' || + parent.type === 'Program' || + parent.type === 'ReturnStatement'; + } + + visitor.visit(root, null, function(node, context) { + if (node.type === 'Identifier' && node.name === expanded.word) { + var parent = parentStack[parentStack.length-1]; + if (isRef(node)) { + + refNodes.push(node); + + if (parent.type === 'VariableDeclarator' || parent.type === 'FunctionDeclaration') { + // look for enclosing scope. travel up parent stack and find enclosing + // function decl, or just use all + + // if this is a function decl, then the name is put into the scope of the enclosing. + // So, look two into the stack + var startStack = (parent.type === 'FunctionDeclaration' && node === parent.id) + ? parentStack.length-2 : parentStack.length-1; + for (i = startStack; i >= 0 ; i--) { + if (parentStack[i].type === 'FunctionExpression' || + parentStack[i].type === 'FunctionDeclaration' || + parentStack[i].type === 'Program') { + + declScopes.push(parentStack[i].range); + break; + } + } + } + } + } + parentStack.push(node); + return true; + }, function () { parentStack.pop(); }); + + // ensure that we have found the target node (ie- it is a renamable thing + var found; + for (i = 0; i < refNodes.length; i++) { + var node = refNodes[i]; + if (node.range[0] === expanded.start && node.range[1] === expanded.end) { + found = true; + break; + } + } + if (!found) { + return null; + } + + // also include global scope in case a var is not explicitly declarared + declScopes.push(root.range); + + // next, order declScopes from smallest to biggest + declScopes.sort(function(l, r) { + var lSize = l.end - l.start; + var rSize = r.end - r.start; + return rSize - lSize; // TODO reverse??? + }); + + var targetScope; + for (i = 0; i < declScopes.length; i++) { + if (inRange(declScopes[i], selection.start)) { + targetScope = declScopes[i]; + break; + } + } + + var foundRefs = []; + refNodes.forEach(function(node) { + // ensure not in range of any smaller scope, but in range of the target scope + for (i = 0; i < declScopes.length; i++) { + if (declScopes[i] === targetScope) { + // must be in this scope + if (inRange(targetScope, node.range[0])) { + foundRefs.push({start: node.range[0], end:node.range[1]}); + } + // no need to try any larget scopes + break; + } else { + // must not be in this scope + if (inRange(declScopes[i], node.range[0])) { + break; + } + } + } + }); + + return foundRefs; + } + }; +}); \ No newline at end of file diff --git a/client/scripts/scripted/markoccurrences.js b/client/scripts/scripted/markoccurrences.js index 6ab6e5f6..c25b6ec0 100644 --- a/client/scripts/scripted/markoccurrences.js +++ b/client/scripts/scripted/markoccurrences.js @@ -10,7 +10,7 @@ * Contributors: * Andrew Eisenberg- initial API and implementation ******************************************************************************/ - + /*global require define scripted*/ /*jslint browser:true */ @@ -28,7 +28,7 @@ define(['orion/textview/annotations'], function(mAnnotations) { (char >= '0' && char <= '9') || char === '_' || char === '$'; } - + function isWord(selstart, selend, buffer) { if (selstart < 0 || selend > buffer.length || selstart > selend || !isWordChar(buffer[selstart])) { return false; @@ -42,11 +42,11 @@ define(['orion/textview/annotations'], function(mAnnotations) { } return true; } - + function isDelineatedWord(selstart, selend, buffer) { return isWord(selstart, selend, buffer) && !isWordChar(buffer[selstart-1]) && !isWordChar(buffer[selend]); } - + /** * Find word from selection * A word is a contiguous block of alphanumerics or $ or _ @@ -59,7 +59,7 @@ define(['orion/textview/annotations'], function(mAnnotations) { if (!isWord(selstart, selend, buffer)) { return null; } - + // at this point, we know there is a word selected, must find the start and the end var start, end; var i = selstart; @@ -70,11 +70,11 @@ define(['orion/textview/annotations'], function(mAnnotations) { } i--; } - + if (!start && i === 0) { start = 0; } - + i = selend; while (i <= buffer.length) { if (!isWordChar(buffer[i])) { @@ -83,18 +83,18 @@ define(['orion/textview/annotations'], function(mAnnotations) { } i++; } - + if (!end && i === buffer.length) { end = buffer.length; } - + return { word: buffer.substring(start, end), start: start, end: end }; } - + /** * @param {String} buffer * @param {String} toFind @@ -111,10 +111,10 @@ define(['orion/textview/annotations'], function(mAnnotations) { } return matches; } - - + + var currentRequest; - + function SelectionMatcher() { // config options this.interval = 500; // inteval between caret changes and mark occurrence changes @@ -163,7 +163,7 @@ define(['orion/textview/annotations'], function(mAnnotations) { self.markOccurrences(evt.newValue.start, evt.newValue.end); }, this.interval); }, - + findMatches : function(selstart, selend, buffer) { var toFind = findSelectedWord(selstart, selend, buffer); var matches; @@ -174,11 +174,11 @@ define(['orion/textview/annotations'], function(mAnnotations) { return { matches : null, word : null }; } }, - + markOccurrences : function(selstart, selend) { /** @type AnnotationModel*/ var annotationModel = this.editor.getAnnotationModel(); - + // find new matches var buffer = this.editor.getText(); var result = this.findMatches(selstart, selend, buffer); @@ -202,7 +202,7 @@ define(['orion/textview/annotations'], function(mAnnotations) { } } }; - + // configure: persist when no selection. disable completely, settimeout - return { SelectionMatcher : SelectionMatcher }; + return { SelectionMatcher : SelectionMatcher, findSelectedWord : findSelectedWord}; }); \ No newline at end of file From 8d5a6586c777f8fde8fb454c6f492a152f93b153 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Mon, 18 Mar 2013 12:40:34 -0700 Subject: [PATCH 24/64] annotation kind added, mark occurrences deactivatable. --- client/css/main.css | 28 +++ .../editor/InlineRenameRefactoring.js | 198 ++++++++---------- .../scripts/scripted/editor/scriptedEditor.js | 4 +- client/scripts/scripted/markoccurrences.js | 23 +- 4 files changed, 137 insertions(+), 116 deletions(-) diff --git a/client/css/main.css b/client/css/main.css index 96ea7d6c..72484266 100644 --- a/client/css/main.css +++ b/client/css/main.css @@ -1176,3 +1176,31 @@ a.keybinding_button { } +.annotationRange.renameRefactoring { + /* + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); + background-color: #ffffff; + */ + outline: 1px Cyan ridge; +} +.annotationOverview.renameRefactoring { + background-color: Blue; + border: 1px solid #7f7f7f; + /* + background-color: Lavender; + border: 1px solid black; + */ + +} +/* +.annotationHTML.renameRefactoring { + /* warning triangle */ + /* + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); + */ + /* flashlight */ + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAALClrLu1ubOpsKqdp6eapKufqMTAw7attLSrsrGnr62jq8C7v765vaebpb22vLmyuMbCxsnGycfEx8G+wcrIysTBxUltof//yf///v70jergpPvws+nWc/npqvrpqvrpq/raffffnvXVkfTVkvXUkd+9f+SiOemvV+uyXa2OX7mYZqeIXKuNX/ClO7KQYqiIXJ59Vp19VpFvTo9uTZBvTpNyUJNyUf///////wAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADgALAAAAAAQABAAAAZ4QJxwSCwajS2aS1U6DlunzcagcuKgG4sn5HJiLZ2QiHbEbj6hEapVTKVYr3OItG5TIhVGLF0npigUEAsPAjV9Q24pEhMBCAoybEUmGRcrDgcAAzNGkxcYNzAJBQSbRJ0YqBc2DaVEHJ6pGTStRBqfGBcZILRWvThBADs="); +} +*/ + + diff --git a/client/scripts/scripted/editor/InlineRenameRefactoring.js b/client/scripts/scripted/editor/InlineRenameRefactoring.js index abf3e9b2..46b871a9 100644 --- a/client/scripts/scripted/editor/InlineRenameRefactoring.js +++ b/client/scripts/scripted/editor/InlineRenameRefactoring.js @@ -1,8 +1,10 @@ -define(['orion/textview/keyBinding','scripted/markoccurrences'], -function(keyBinding,markoccurrences) { +define(['orion/textview/keyBinding','scripted/markoccurrences','plugins/esprima/refactoringSupport','scripted/editor/annotationManager','orion/textview/annotations'], +function(keyBinding, markoccurrences, refactoringSupport, annotationManager, orionAnnotationModule) { var State = { INACTIVE: 1, ACTIVE: 2 }; + annotationManager.registerAnnotationType('scripted.renameRefactoring',false); + function InlineRenameRefactoring(editor,undoStack,linkedMode) { this._undoStack = undoStack; this._linkedMode = linkedMode; @@ -28,38 +30,71 @@ function(keyBinding,markoccurrences) { return; } if (this._state === State.INACTIVE) { - console.log("activated"); - this._textView.addKeyPressHandler(this._inlineRenameRefactoringMode); - this._setState(State.ACTIVE); + console.log("rename refactoring activated"); // Locate the place where the selected text exists in the file var model = this._editor.getModel(); var selectedText=model.getText(selection.start,selection.end); console.log("Selection is >"+selectedText+"<"); - this._undoStack.startCompoundChange(); - var matcher = new markoccurrences.SelectionMatcher(); - var results = matcher.findMatches(selection.start,selection.end,this._editor.getText()); - if (results.matches) { - console.log("word >"+results.word+"< matchcount=#"+results.matches.length); - for (var m=0;m"+results.word+"< matchcount=#"+results.matches.length); + for (var m=0;m"+key+"<"); +// console.log("key was >"+key+"<"); var replacementLength = word.length; if (typed.length>0) { @@ -131,6 +148,7 @@ function(keyBinding,markoccurrences) { } typed = typed+ch; this._inlineRenameRefactoring.typed = typed; + var newAnnos = []; var offset = 0; for (var m=0;m0) { -// var linetext = model.getLine(lineNum,false); -// var options = textview.getOptions("tabSize", "expandTab"); //$NON-NLS-1$ //$NON-NLS-0$ -// var tabtext = options.expandTab ? new Array(options.tabSize + 1).join(" ") : "\t"; //$NON-NLS-1$ //$NON-NLS-0$ -// if (this._isAllWhitespace(linetext)) { -// if (this._endsWith(linetext,tabtext)) { -// // check the previous line -// var unindent = false; -// var previouslinetext=model.getLine(lineNum-1,false); -// var previouslinelength = previouslinetext.length; -// // If the line before starts with the same amount of whitespace, probably worth unindenting: -// if (previouslinelength>linetext.length && previouslinetext.indexOf(linetext)===0 && -// !this._isWhitespace(previouslinetext.charAt(linetext.length)) && -// previouslinetext.charAt(previouslinetext.length-1)!=='{') { -// unindent = true; -// } -// // if the line before ends with a '{' unindent -// if (previouslinelength>0 && previouslinetext.charAt(previouslinetext.length-1)==='{' && -// linetext.length>this._posOfFirstNonwhitespace(previouslinetext)) { -// unindent = true; -// // TODO unindent to same level? -// } -// -// if (unindent) { -// // replace the 'tab' with the '}' and be done. -// textview._modifyContent({text: '}', start: selection.start-tabtext.length, end: selection.end, _ignoreDOMSelection: true}, true); -// return true; -// } -// } -// } -// } }, -// lineUp: function() { -// var newSelected = (this.selectedIndex === 0) ? this.proposals.length - 1 : this.selectedIndex - 1; -// while (this.proposals[newSelected].unselectable && newSelected > 0) { -// newSelected--; -// } -// this.selectedIndex = newSelected; -// if (this.widget) { -// this.widget.setSelectedIndex(this.selectedIndex); -// } -// return true; -// }, -// lineDown: function() { -// var newSelected = (this.selectedIndex === this.proposals.length - 1) ? 0 : this.selectedIndex + 1; -// while (this.proposals[newSelected].unselectable && newSelected < this.proposals.length-1) { -// newSelected++; -// } -// this.selectedIndex = newSelected; -// if (this.widget) { -// this.widget.setSelectedIndex(this.selectedIndex); -// } -// return true; -// }, - enter: function() { + lineUp: function() { + this.cancel(); + return false; + }, + lineDown: function() { + this.cancel(); + return false; + }, + _complete: function() { this._undoStack.endCompoundChange(); this._inlineRenameRefactoring.deactivate(); var locations = this._inlineRenameRefactoring.matches; @@ -219,11 +189,13 @@ function(keyBinding,markoccurrences) { var caret = locations[matchNumber] - matchNumber*(word.length-1)+ matchNumber*(this._inlineRenameRefactoring.typed.length-1); var tv = this._inlineRenameRefactoring._textView; tv.setCaretOffset(caret); + }, + enter: function() { + this._complete(); return true; }, tab: function() { - this._undoStack.endCompoundChange(); - this._inlineRenameRefactoring.deactivate(); + this._complete(); return true; } }; diff --git a/client/scripts/scripted/editor/scriptedEditor.js b/client/scripts/scripted/editor/scriptedEditor.js index 815fcc58..5f46eb9a 100644 --- a/client/scripts/scripted/editor/scriptedEditor.js +++ b/client/scripts/scripted/editor/scriptedEditor.js @@ -956,8 +956,8 @@ define([ } } - // TODO should we persist the instance of mark occurrences? - new mMarkoccurrences.SelectionMatcher().install(editor); + editor.selectionMatcher = new mMarkoccurrences.SelectionMatcher(); + editor.selectionMatcher.install(editor); editor.type = editorType; diff --git a/client/scripts/scripted/markoccurrences.js b/client/scripts/scripted/markoccurrences.js index c25b6ec0..a6ec60b3 100644 --- a/client/scripts/scripted/markoccurrences.js +++ b/client/scripts/scripted/markoccurrences.js @@ -120,12 +120,13 @@ define(['orion/textview/annotations'], function(mAnnotations) { this.interval = 500; // inteval between caret changes and mark occurrence changes this.disable = false; // set to true if mark occurrences should be disabled this.retain = false; // set to true if marks should be retained after caret moves away - + this.isActive = true; this.initOptions(); } SelectionMatcher.prototype = { install : function(editor) { this.editor = editor; + this.isActive = true; editor.getTextView().addEventListener("Selection", this); }, initOptions : function() { @@ -143,6 +144,23 @@ define(['orion/textview/annotations'], function(mAnnotations) { } }, + deactivate: function() { + this.isActive=false; + var annotationModel = this.editor.getAnnotationModel(); + annotationModel.removeAnnotations(ANNOTATION_TYPE); + }, + + activate: function() { + this.isActive=true; + var self = this; + currentRequest = setTimeout(function() { + var sel = self.editor.getSelection(); + if (sel) { + self.markOccurrences(sel.start,sel.end); + } + }, this.interval); + }, + // TODO not used uninstall : function() { if (this.editor) { @@ -176,6 +194,9 @@ define(['orion/textview/annotations'], function(mAnnotations) { }, markOccurrences : function(selstart, selend) { + if (!this.isActive) { + return; + } /** @type AnnotationModel*/ var annotationModel = this.editor.getAnnotationModel(); From 874e1851ad4ab9d2db080acda97de7592d72f111 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Mon, 18 Mar 2013 13:03:50 -0700 Subject: [PATCH 25/64] corrected case of markoccurrences reference --- client/scripts/plugins/esprima/refactoringSupport.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/scripts/plugins/esprima/refactoringSupport.js b/client/scripts/plugins/esprima/refactoringSupport.js index 078ebaa7..8687f383 100644 --- a/client/scripts/plugins/esprima/refactoringSupport.js +++ b/client/scripts/plugins/esprima/refactoringSupport.js @@ -19,7 +19,7 @@ define(function (require) { var visitor = require('plugins/esprima/esprimaVisitor'); - var findSelectedWord = require('scripted/markOccurrences').findSelectedWord; + var findSelectedWord = require('scripted/markoccurrences').findSelectedWord; function inRange(range, offset) { @@ -164,4 +164,4 @@ define(function (require) { return foundRefs; } }; -}); \ No newline at end of file +}); From b046745ebc93f79b070e4c5c1856000a717e35fe Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Mon, 18 Mar 2013 13:19:34 -0700 Subject: [PATCH 26/64] keybinding change to alt+shift+r --- client/scripts/scripted/editor/InlineRenameRefactoring.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/scripts/scripted/editor/InlineRenameRefactoring.js b/client/scripts/scripted/editor/InlineRenameRefactoring.js index 46b871a9..f06fd676 100644 --- a/client/scripts/scripted/editor/InlineRenameRefactoring.js +++ b/client/scripts/scripted/editor/InlineRenameRefactoring.js @@ -15,7 +15,9 @@ function(keyBinding, markoccurrences, refactoringSupport, annotationManager, ori var self = this; this._editor.pushKeyMode(this._inlineRenameRefactoringMode); - this._textView.setKeyBinding(new keyBinding.KeyBinding('r', false, true, false, true), "rename"); + // was: + // this._textView.setKeyBinding(new keyBinding.KeyBinding('r', false, true, false, true), "rename"); + this._textView.setKeyBinding(new keyBinding.KeyBinding('r', false, true, true, false), "rename"); // Shift+Alt+R this._textView.setAction("rename", function() { self.activate(); return true; From 8d1fa5a33f477ce5e1368e40fc94d11e6bc8182e Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 14 Mar 2013 17:01:17 -0700 Subject: [PATCH 27/64] Checkpoint. Most of the standard content assist tests passing --- client/component.json | 2 +- client/scripts/main.js | 2 +- .../plugins/esprima/esprimaJsContentAssist.js | 523 +++--- .../scripts/plugins/esprima/indexerService.js | 52 +- client/scripts/plugins/esprima/types.js | 1574 ++++++++--------- play-area/app.js | 8 +- play-area/foo.js | 8 +- .../esprima/esprimaJsContentAssistTests.js | 395 +++-- 8 files changed, 1243 insertions(+), 1321 deletions(-) diff --git a/client/component.json b/client/component.json index 4200f89d..c0870d25 100644 --- a/client/component.json +++ b/client/component.json @@ -11,6 +11,6 @@ "qunit": "1.10.0", "json5": "0.1.0", "probes": "0.1.0", - "doctrine": "" + "doctrine": "git://github.com/aeisenberg/doctrine.git" } } diff --git a/client/scripts/main.js b/client/scripts/main.js index d5742b9e..c1aac17d 100644 --- a/client/scripts/main.js +++ b/client/scripts/main.js @@ -74,7 +74,7 @@ define({ }, plugins : [ - //{ module : 'wire/debug' }, + { module : 'wire/debug' }, { module : 'wire/jquery/dom' } ] }); diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 844a5205..b3c7d007 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -16,7 +16,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/esprima/proposalUtils", "scriptedLogger", "esprima/esprima"], function(mVisitor, mTypes, proposalUtils, scriptedLogger) { - /** @type {function(obj):Boolean} a safe way of checking for arrays */ + /** + * TODO move this to a central location + * @type {function(obj):Boolean} a safe way of checking for arrays + */ var isArray = Array.isArray; if (!isArray) { isArray = function isArray(ary) { @@ -103,37 +106,42 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * Convert an array of parameters into a string and also compute linked editing positions * @param {String} name name of the function - * @param {String} type the type of the function using the following structure '?Type:arg1,arg2,...' + * @param {{}} typeObj the type object of the function * @param {Number} offset offset - * @return {{ completion:String, positions:Array. }} + * @return {{ completion:String, positions:[Number] }} */ - function calculateFunctionProposal(name, type, offset) { - var paramsOffset = mTypes.findReturnTypeEnd(type), paramsStr, params; - paramsStr = paramsOffset > 0 ? type.substring(paramsOffset+1) : ""; - params = paramsStr.split(","); - if (!paramsStr || params.length === 0) { - return {completion: name + "()", positions:null}; - } + function calculateFunctionProposal(name, typeObj, offset) { + var params = typeObj.params || []; + var ret = typeObj.result; + var positions = []; var completion = name + '('; var plen = params.length; for (var p = 0; p < plen; p++) { + if (params[p].name === 'new' || params[p].name === 'this') { + continue; + } if (p > 0) { completion += ', '; } - var argName; - if (typeof params[p] === "string") { - // need this because jslintworker.js augments the String prototype with a name() function - // don't want confusion - argName = params[p]; - var slashIndex = argName.indexOf('/'); - if (slashIndex > 0) { - argName = argName.substring(0, slashIndex); - } - } else if (params[p].name) { - argName = params[p].name(); - } else { - argName = params[p]; + var param = params[p]; + var optional, rest; + if (param.type === 'OptionalType') { + param = param.expression; + optional=true; + } + + if (param.type === 'RestType') { + param = param.expression; + rest = true; + } + + var argName = param.name || 'arg' + p; + if (rest) { + argName += '...'; + } + if (optional) { + argName = '[' + argName + ']'; } positions.push({offset:offset+completion.length+1, length: argName.length}); completion += argName; @@ -397,28 +405,27 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * updates a function type to include a new return type. - * function types are specified like this: ?returnType:[arg-n...] - * return type is the name of the return type, arg-n is the name of - * the nth argument. + * makes a copy of the functiontype + * TODO this is not a deep copy...should it be? + * TOD move to mTypes */ - function updateReturnType(originalFunctionType, newReturnType) { - if (! originalFunctionType) { - // not a valid function type - return newReturnType; - } - - var firstChar = originalFunctionType.charAt(0); - if (firstChar !== "?" && firstChar !== "*") { - // not a valid function type - return newReturnType; - } - - var end = mTypes.findReturnTypeEnd(originalFunctionType); - if (end < 0) { - // not a valid function type - return newReturnType; + function updateReturnType(originalFunctionTypeObj, newReturnTypeObj) { + if (! originalFunctionTypeObj || originalFunctionTypeObj.type !== "FunctionType") { + return newReturnTypeObj; + } else { + var newFunctionTypeObj = { + type: originalFunctionTypeObj.type, + params: originalFunctionTypeObj.params, + result: newReturnTypeObj + }; + if (originalFunctionTypeObj['this']) { + newFunctionTypeObj['this'] = originalFunctionTypeObj['this']; + } + if (originalFunctionTypeObj['new']) { + newFunctionTypeObj['new'] = originalFunctionTypeObj['new']; + } + return newFunctionTypeObj; } - return firstChar + newReturnType + originalFunctionType.substring(end); } /** * checks to see if this file looks like an AMD module @@ -558,7 +565,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr paramTypes.push(env.newFleetingObject()); } } else { - paramTypes.push("Object"); + paramTypes.push(mTypes.OBJECT_TYPE); } } } @@ -629,7 +636,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * @param env the context for the visitor. See computeProposals below for full description of contents */ function inferencer(node, env) { - var type = node.type, name, i, property, params, newTypeName, jsdocResult, jsdocType; + var type = node.type, name, i, property, params, newTypeObj, jsdocResult, jsdocType, docComment; // extras prop is where we stuff everything that we have added if (!node.extras) { @@ -646,7 +653,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } break; case "BlockStatement": - node.extras.inferredType = env.newScope(); + node.extras.inferredTypeObj = env.newScopeObj(); if (node.extras.stop) { // this BlockStatement inferencing is deferred until after the rest of the file is inferred inferencerPostOp(node, env); @@ -657,7 +664,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr case "Literal": break; case "ArrayExpression": - node.extras.inferredType = "Array"; + node.extras.inferredTypeObj = mTypes.ARRAY_TYPE; break; case "ObjectExpression": if (node.extras.fname) { @@ -668,8 +675,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // for object literals, create a new object type so that we can stuff new properties into it. // we might be able to do better by walking into the object and inferring each RHS of a // key-value pair - newTypeName = env.newObject(null, node.range); - node.extras.inferredType = newTypeName; + newTypeObj = env.newObject(null, node.range); + node.extras.inferredTypeObj = newTypeObj; for (i = 0; i < node.properties.length; i++) { property = node.properties[i]; // only remember if the property is an identifier @@ -677,10 +684,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // first just add as an object property (or use jsdoc if exists). // after finishing the ObjectExpression, go and update // all of the variables to reflect their final inferred type - var docComment = findAssociatedCommentBlock(property.key, env.comments); + docComment = findAssociatedCommentBlock(property.key, env.comments); jsdocResult = mTypes.parseJSDocComment(docComment); - jsdocType = mTypes.convertJsDocType(jsdocResult.type, env); - var keyType = jsdocType ? jsdocType : "Object"; + jsdocType = jsdocResult.type && mTypes.convertJsDocType(jsdocResult.type, env); + var keyType = jsdocType ? jsdocType : mTypes.OBJECT_TYPE; env.addVariable(property.key.name, node, keyType, property.key.range, docComment.range); if (!property.key.extras) { property.key.extras = {}; @@ -734,72 +741,70 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // assume that function name that starts with capital is // a constructor - var isConstuctor; + var isConstructor; if (name && node.body && isUpperCaseChar(name)) { if (node.extras.cname) { // RHS of assignment name = node.extras.cname; } // create new object so that there is a custom "this" - newTypeName = env.newObject(name, node.range); - isConstuctor = true; + newTypeObj = env.newObject(name, node.range); + isConstructor = true; } else { - var jsdocReturn = mTypes.convertJsDocType(jsdocResult.rturn, env); - if (jsdocReturn) { + if (jsdocResult.rturn) { + var jsdocReturn = mTypes.convertJsDocType(jsdocResult.rturn, env); // keep track of the return type for the way out node.extras.jsdocReturn = jsdocReturn; - newTypeName = jsdocReturn; - node.extras.inferredType = jsdocReturn; + newTypeObj = jsdocReturn; + node.extras.inferredTypeObj = jsdocReturn; } else { // temporarily use "undefined" as type, but this may change once we // walk through to get to a return statement - newTypeName = "undefined"; + newTypeObj = mTypes.UNDEFINED_TYPE; } - isConstuctor = false; + isConstructor = false; } if (!node.body.extras) { node.body.extras = {}; } - node.body.extras.isConstructor = isConstuctor; + node.body.extras.isConstructor = isConstructor; // add parameters to the current scope - var paramTypeSigs = [], paramSigs = []; + var paramTypeObjs = []; if (params.length > 0) { var moduleDefs = findModuleDefinitions(node, env); for (i = 0; i < params.length; i++) { // choose jsdoc tags over module definitions var jsDocParam = jsdocResult.params[params[i]]; - var typeName = null; + var paramTypeObj = null; if (jsDocParam) { - typeName = mTypes.convertJsDocType(jsDocParam, env); + paramTypeObj = mTypes.convertJsDocType(jsDocParam, env); } - if (!typeName) { - typeName = moduleDefs[i]; + if (!paramTypeObj) { + paramTypeObj = moduleDefs[i]; } - paramTypeSigs.push(typeName); - paramSigs.push(params[i] + "/" + typeName); + paramTypeObjs.push(mTypes.createParamType(params[i], paramTypeObj)); } } - - var functionTypeName = (isConstuctor ? "*" : "?") + newTypeName + ":" + paramSigs.join(","); - if (isConstuctor) { - env.createConstructor(functionTypeName, newTypeName); + var functionTypeObj = mTypes.createFunctionType(paramTypeObjs, newTypeObj, isConstructor); + if (isConstructor) { + env.createConstructor(name); // assume that constructor will be available from global scope using qualified name // this is not correct in all cases - env.addOrSetGlobalVariable(name, functionTypeName, nameRange, docComment.range); + env.addOrSetGlobalVariable(name, functionTypeObj, nameRange, docComment.range); } - node.extras.inferredType = functionTypeName; + node.extras.inferredTypeObj = functionTypeObj; if (name && !isBefore(env.offset, node.range)) { // if we have a name, then add it to the scope - env.addVariable(name, node.extras.target, functionTypeName, nameRange, docComment.range); + env.addVariable(name, node.extras.target, functionTypeObj, nameRange, docComment.range); } // now add the scope for inside the function env.newScope(); - env.addVariable("arguments", node.extras.target, "Arguments", node.range); + env.addVariable("arguments", node.extras.target, mTypes.createNameType("Arguments"), node.range); // now determine if we need to add 'this'. If this function has an appliesTo, the we know it is being assigned as a property onto something else @@ -812,16 +817,16 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var ownerTypeName = env.scope(appliesToOwner); // for the special case of adding to the prototype, we want to make sure that we also add to the 'this' of // the instantiated types - if (mTypes.isPrototype(ownerTypeName)) { + if (mTypes.isPrototypeName(ownerTypeName)) { ownerTypeName = mTypes.extractReturnType(ownerTypeName); } - env.addVariable("this", node.extras.target, ownerTypeName, nameRange, docComment.range); + env.addVariable("this", node.extras.target, mTypes.createNameType(ownerTypeName), nameRange, docComment.range); } } // add variables for all parameters for (i = 0; i < params.length; i++) { - env.addVariable(params[i], node.extras.target, paramTypeSigs[i], node.params[i].range); + env.addVariable(params[i], node.extras.target, paramTypeObjs[i].expression, node.params[i].range); } break; case "VariableDeclarator": @@ -842,12 +847,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr node.init.extras.fnameRange = node.id.range; } else { // not the RHS of a function, check for jsdoc comments - var docComment = findAssociatedCommentBlock(node, env.comments); + docComment = findAssociatedCommentBlock(node, env.comments); jsdocResult = mTypes.parseJSDocComment(docComment); - jsdocType = mTypes.convertJsDocType(jsdocResult.type, env); + jsdocType = jsdocResult.type && mTypes.convertJsDocType(jsdocResult.type, env); node.extras.docRange = docComment.range; if (jsdocType) { - node.extras.inferredType = jsdocType; + node.extras.inferredTypeObj = jsdocType; node.extras.jsdocType = jsdocType; env.addVariable(node.id.name, node.extras.target, jsdocType, node.id.range, docComment.range); } @@ -876,12 +881,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr node.left.extras = {}; } } - var docComment = findAssociatedCommentBlock(node, env.comments); + docComment = findAssociatedCommentBlock(node, env.comments); jsdocResult = mTypes.parseJSDocComment(docComment); - jsdocType = mTypes.convertJsDocType(jsdocResult.type, env); + jsdocType = jsdocResult.type && mTypes.convertJsDocType(jsdocResult.type, env); node.extras.docRange = docComment.range; if (jsdocType) { - node.extras.inferredType = jsdocType; + node.extras.inferredTypeObj = jsdocType; node.extras.jsdocType = jsdocType; env.addVariable(rightMost.name, node.extras.target, jsdocType, rightMost.range, docComment.range); } @@ -890,13 +895,14 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr break; case "CatchClause": // create a new scope for the catch parameter - node.extras.inferredType = env.newScope(); + node.extras.inferredTypeObj = env.newScope(); if (node.param) { if (!node.param.extras) { node.param.extras = {}; } - node.param.extras.inferredType = "Error"; - env.addVariable(node.param.name, node.extras.target, "Error", node.param.range); + var inferredTypeObj = mTypes.createNameType("Error"); + node.param.extras.inferredTypeObj = inferredTypeObj; + env.addVariable(node.param.name, node.extras.target, inferredTypeObj, node.param.range); } break; case "MemberExpression": @@ -936,7 +942,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // defer the inferencing of the function's children containing the offset. if (node === env.defer) { node.extras.associatedComment = findAssociatedCommentBlock(node, env.comments); - node.extras.inferredType = node.extras.inferredType || "Object"; // will be filled in later + node.extras.inferredTypeObj = node.extras.inferredTypeObj || mTypes.OBJECT_TYPE; // will be filled in later // need to remember the scope to place this function in for later node.extras.scope = env.scope(node.extras.target); @@ -952,7 +958,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * Finishes off the inferencing and adds all proposals */ function inferencerPostOp(node, env) { - var type = node.type, name, inferredType, newTypeName, rightMost, kvps, i; + var type = node.type, name, inferredTypeObj, newTypeObj, rightMost, kvps, i; switch(type) { case "Program": @@ -964,7 +970,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var defer = env.defer; env.defer = null; - env.targetType = null; + env.targetTypeName = null; env.pushScope(defer.extras.scope); mVisitor.visit(defer.body, env, inferencer, inferencerPostOp); env.popScope(); @@ -974,7 +980,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr env.storeTarget(); // TODO FIXADE for historical reasons we end visit by throwing exception. Should chamge - throw env.targetType; + throw env.targetTypeName; case "BlockStatement": case "CatchClause": if (inRange(env.offset, node.range)) { @@ -993,35 +999,37 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // for arrays, inferred type is the dereferncing of the array type // for non-arrays inferred type is the type of the property expression - if (mTypes.isArrayType(node.object.extras.inferredType) && node.computed) { + if (mTypes.isArrayType(node.object.extras.inferredTypeObj) && node.computed) { // inferred type of expression is the type of the dereferenced array - node.extras.inferredType = mTypes.extractArrayParameterType(node.object.extras.inferredType); + node.extras.inferredTypeObj = mTypes.extractArrayParameterType(node.object.extras.inferredTypeObj); } else if (node.computed && node.property && node.property.type !== "Literal") { // we don't infer parameterized objects, but we have something like this: 'foo[at]' just assume type is object - node.extras.inferredType = "Object"; + node.extras.inferredTypeObj = mTypes.OBJECT_TYPE; } else { // a regular member expression: foo.bar or foo['bar'] // node.propery will be null for mal-formed asts - node.extras.inferredType = node.property ? node.property.extras.inferredType : node.object.extras.inferredType; + node.extras.inferredTypeObj = node.property ? + node.property.extras.inferredTypeObj : + node.object.extras.inferredTypeObj; } break; case "CallExpression": // first check to see if this is a require call - var fnType = extractRequireModule(node, env); + var fnTypeObj = extractRequireModule(node, env); // otherwise, apply the function - if (!fnType) { - fnType = node.callee.extras.inferredType; - fnType = mTypes.extractReturnType(fnType); + if (!fnTypeObj) { + fnTypeObj = node.callee.extras.inferredTypeObj; + fnTypeObj = mTypes.extractReturnType(fnTypeObj); } - node.extras.inferredType = fnType; + node.extras.inferredTypeObj = fnTypeObj; break; case "NewExpression": // FIXADE we have a problem here. // constructors that are called like this: new foo.Bar() should have an inferred type of foo.Bar, // This ensures that another constructor new baz.Bar() doesn't conflict. However, // we are only taking the final prefix and assuming that it is unique. - node.extras.inferredType = mTypes.extractReturnType(node.callee.extras.inferredType); + node.extras.inferredTypeObj = mTypes.extractReturnType(node.callee.extras.inferredTypeObj); break; case "ObjectExpression": // now that we know all the types of the values, use that to populate the types of the keys @@ -1039,8 +1047,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // and the key and value names match. var range = null; if (name === kvps[i].value.name) { - var def = env.lookupName(kvps[i].value.name, null, false, true); - if (def && def.range && (mTypes.isFunctionOrConstructor(def.typeName))) { + var def = env.lookupTypeObj(kvps[i].value.name, null, true); + if (def && def.range && (mTypes.isFunctionOrConstructor(def.typeObj))) { range = def.range; } } @@ -1048,9 +1056,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr range = kvps[i].key.range; } - inferredType = kvps[i].value.extras.inferredType; + inferredTypeObj = kvps[i].value.extras.inferredTypeObj; var docComment = kvps[i].key.extras.associatedComment; - env.addVariable(name, node, inferredType, range, docComment.range); + env.addVariable(name, node, inferredTypeObj, range, docComment.range); if (inRange(env.offset-1, kvps[i].key.range)) { // We found it! rmember for later, but continue to the end of file anyway env.storeTarget(env.scope(node)); @@ -1069,12 +1077,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr switch (node.operator) { case '+': // special case: if either side is a string, then result is a string - if (node.left.extras.inferredType === "String" || - node.right.extras.inferredType === "String") { + if (node.left.extras.inferredTypeObj.name === "String" || + node.right.extras.inferredTypeObj.name === "String") { - node.extras.inferredType = "String"; + node.extras.inferredTypeObj = mTypes.STRING_TYPE; } else { - node.extras.inferredType = "Number"; + node.extras.inferredTypeObj = mTypes.NUMBER_TYPE; } break; case '-': @@ -1088,13 +1096,13 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr case '>>': case '>>>': // Numeric and bitwise operations always return a number - node.extras.inferredType = "Number"; + node.extras.inferredTypeObj = mTypes.NUMBER_TYPE; break; case '&&': case '||': // will be the type of the left OR the right // for now arbitrarily choose the left - node.extras.inferredType = node.left.extras.inferredType; + node.extras.inferredTypeObj = node.left.extras.inferredTypeObj; break; case '!==': @@ -1105,22 +1113,22 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr case '<=': case '>': case '>=': - node.extras.inferredType = "Boolean"; + node.extras.inferredTypeObj = mTypes.BOOLEAN_TYPE; break; default: - node.extras.inferredType = "Object"; + node.extras.inferredTypeObj = mTypes.OBJECT_TYPE; } break; case "UpdateExpression": case "UnaryExpression": if (node.operator === '!') { - node.extras.inferredType = "Boolean"; + node.extras.inferredTypeObj = mTypes.BOOLEAN_TYPE; } else { // includes all unary operations and update operations // ++ -- - and ~ - node.extras.inferredType = "Number"; + node.extras.inferredTypeObj = mTypes.NUMBER_TYPE; } break; case "FunctionDeclaration": @@ -1139,19 +1147,19 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr env.popScope(); // now add a reference to the constructor - env.addOrSetVariable(mTypes.extractReturnType(node.extras.inferredType), node.extras.target, node.extras.inferredType, fnameRange); + env.addOrSetVariable(mTypes.extractReturnType(node.extras.inferredTypeObj), node.extras.target, node.extras.inferredTypeObj, fnameRange); } else { // a regular function. if we don't already know the jsdoc return, // try updating to a more explicit return type if (!node.extras.jsdocReturn) { var returnStatement = findReturn(node.body); var returnType; - if (returnStatement && returnStatement.extras && returnStatement.extras.inferredType) { - returnType = returnStatement.extras.inferredType; + if (returnStatement && returnStatement.extras && returnStatement.extras.inferredTypeObj) { + returnType = returnStatement.extras.inferredTypeObj; } else { - returnType = "undefined"; + returnType = mTypes.UNDEFINED_TYPE; } - node.extras.inferredType = updateReturnType(node.extras.inferredType, returnType); + node.extras.inferredTypeObj = updateReturnType(node.extras.inferredTypeObj, returnType); } // if there is a name, then update that as well var fname; @@ -1165,21 +1173,21 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr fnameRange = node.extras.fnameRange; } if (fname) { - env.addOrSetVariable(fname, node.extras.target, node.extras.inferredType, fnameRange); + env.addOrSetVariable(fname, node.extras.target, node.extras.inferredTypeObj, fnameRange); } } } break; case "VariableDeclarator": if (node.init) { - inferredType = node.init.extras.inferredType; + inferredTypeObj = node.init.extras.inferredTypeObj; } else { - inferredType = env.newFleetingObject(); + inferredTypeObj = env.newFleetingObject(); } - node.id.extras.inferredType = inferredType; + node.id.extras.inferredTypeObj = inferredTypeObj; if (!node.extras.jsdocType) { - node.extras.inferredType = inferredType; - env.addVariable(node.id.name, node.extras.target, inferredType, node.id.range, node.extras.docRange); + node.extras.inferredTypeObj = inferredTypeObj; + env.addVariable(node.id.name, node.extras.target, inferredTypeObj, node.id.range, node.extras.docRange); } if (inRange(env.offset-1, node.id.range)) { // We found it! rmember for later, but continue to the end of file anyway @@ -1189,32 +1197,32 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr break; case "Property": - node.extras.inferredType = node.key.extras.inferredType = node.value.extras.inferredType; + node.extras.inferredTypeObj = node.key.extras.inferredTypeObj = node.value.extras.inferredTypeObj; break; case "AssignmentExpression": if (node.extras.jsdocType) { // use jsdoc instead of whatever we have inferred - inferredType = node.extras.jsdocType; + inferredTypeObj = node.extras.jsdocType; } else if (node.operator === '=') { // standard assignment - inferredType = node.right.extras.inferredType; + inferredTypeObj = node.right.extras.inferredTypeObj; } else { // +=, -=, *=, /=, >>=, <<=, >>>=, &=, |=, or ^=. - if (node.operator === '+=' && node.left.extras.inferredType === 'String') { - inferredType = "String"; + if (node.operator === '+=' && node.left.extras.inferredTypeObj.name === 'String') { + inferredTypeObj = mTypes.STRING_TYPE; } else { - inferredType = "Number"; + inferredTypeObj = mTypes.NUMBER_TYPE; } } - node.extras.inferredType = inferredType; + node.extras.inferredTypeObj = inferredTypeObj; // when we have 'this.that.theOther.f' need to find the right-most identifier rightMost = findRightMost(node.left); if (rightMost && (rightMost.type === "Identifier" || rightMost.type === "Literal")) { name = rightMost.name ? rightMost.name : rightMost.value; - rightMost.extras.inferredType = inferredType; - env.addOrSetVariable(name, rightMost.extras.target, inferredType, rightMost.range, node.extras.docRange); + rightMost.extras.inferredTypeObj = inferredTypeObj; + env.addOrSetVariable(name, rightMost.extras.target, inferredTypeObj, rightMost.range, node.extras.docRange); if (inRange(env.offset-1, rightMost.range)) { // We found it! remember for later, but continue to the end of file anyway env.storeTarget(env.scope(rightMost.extras.target)); @@ -1227,9 +1235,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (rightMost && !(rightMost.type === 'Identifier' && rightMost.name === 'prototype')) { // yep...now go and update the type of the array // (also don't turn refs to prototype into an array. this breaks things) - var arrayType = mTypes.parameterizeArray(inferredType); - node.left.extras.inferredType = inferredType; - node.left.object.extras.inferredType = arrayType; + var arrayType = mTypes.parameterizeArray(inferredTypeObj); + node.left.extras.inferredTypeObj = inferredTypeObj; + node.left.object.extras.inferredTypeObj = arrayType; env.addOrSetVariable(rightMost.name, rightMost.extras.target, arrayType, rightMost.range, node.extras.docRange); } } @@ -1238,10 +1246,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr break; case 'Identifier': name = node.name; - newTypeName = env.lookupName(name, node.extras.target); - if (newTypeName && !node.extras.isDecl) { + newTypeObj = env.lookupTypeObj(name, node.extras.target); + if (newTypeObj && !node.extras.isDecl) { // name already exists but we are redeclaring it and so not being overridden - node.extras.inferredType = newTypeName; + node.extras.inferredTypeObj = newTypeObj; if (inRange(env.offset, node.range, true)) { // We found it! rmember for later, but continue to the end of file anyway env.storeTarget(env.scope(node.extras.target)); @@ -1255,7 +1263,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr env.addVariable(name, node.extras.target, env.newFleetingObject(), node.range); } else { // add as a global variable - node.extras.inferredType = env.addOrSetGlobalVariable(name, null, node.range).typeName; + node.extras.inferredTypeObj = env.addOrSetGlobalVariable(name, null, node.range).typeObj; } } else { // We found it! rmember for later, but continue to the end of file anyway @@ -1268,7 +1276,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } break; case "ThisExpression": - node.extras.inferredType = env.lookupName("this"); + node.extras.inferredTypeObj = env.lookupTypeObj("this"); if (inRange(env.offset-1, node.range)) { // We found it! rmember for later, but continue to the end of file anyway env.storeTarget(env.scope()); @@ -1276,7 +1284,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr break; case "ReturnStatement": if (node.argument) { - node.extras.inferredType = node.argument.extras.inferredType; + node.extras.inferredTypeObj = node.argument.extras.inferredTypeObj; } break; @@ -1285,21 +1293,21 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // we are inside a computed member expression. // find the type of the property referred to if exists name = node.value; - newTypeName = env.lookupName(name, node.extras.target); - node.extras.inferredType = newTypeName; + newTypeObj = env.lookupTypeObj(name, node.extras.target); + node.extras.inferredTypeObj = newTypeObj; } else if (node.extras.target && typeof node.value === "number") { // inside of an array access - node.extras.inferredType = "Number"; + node.extras.inferredTypeObj = mTypes.NUMBER_TYPE; } else { var oftype = (typeof node.value); - node.extras.inferredType = oftype[0].toUpperCase() + oftype.substring(1, oftype.length); + node.extras.inferredTypeObj = mTypes.createNameType(oftype[0].toUpperCase() + oftype.substring(1, oftype.length)); } break; case "ConditionalExpression": var target = node.consequent ? node.consequent : node.alternate; if (target) { - node.extras.inferredType = target.extras.inferredType; + node.extras.inferredTypeObj = target.extras.inferredTypeObj; } break; @@ -1308,14 +1316,14 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (node.elements) { for (i = 0; i < node.elements.length; i++) { if (node.elements[i]) { - node.extras.inferredType = mTypes.parameterizeArray(node.elements[i].extras.inferredType); + node.extras.inferredTypeObj = mTypes.parameterizeArray(node.elements[i].extras.inferredTypeObj); } } } } - if (!node.extras.inferredType) { - node.extras.inferredType = "Object"; + if (!node.extras.inferredTypeObj) { + node.extras.inferredTypeObj = mTypes.OBJECT_TYPE; } } @@ -1328,7 +1336,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (lintOptions && isArray(lintOptions.global)) { for (i = 0; i < lintOptions.global.length; i++) { globName = lintOptions.global[i]; - if (!env.lookupName(globName)) { + if (!env.lookupTypeObj(globName)) { env.addOrSetVariable(globName); } } @@ -1349,7 +1357,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } else { globName = splits[j].trim(); } - if (!env.lookupName(globName)) { + if (!env.lookupTypeObj(globName)) { env.addOrSetVariable(globName, null, null, range); } } @@ -1394,17 +1402,22 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * added to it. Additionally, the type specified in the $$proto property is * either empty or is Object * - * @param String leftTypeName - * @param String rightTypeName + * @param String leftTypeObj + * @param String leftTypeObj * @param {{getAllTypes:function():Object}} env * * @return Boolean */ - function leftTypeIsMoreGeneral(leftTypeName, rightTypeName, env) { + function leftTypeIsMoreGeneral(leftTypeObj, rightTypeObj, env) { + var leftTypeName = leftTypeObj.name, rightTypeName = rightTypeObj; + function isEmpty(generatedTypeName) { - if (generatedTypeName === "Object" || generatedTypeName === "undefined") { + if (typeof generatedTypeName !== 'string') { + // original type was not a name expression + return false; + } else if (generatedTypeName === "Object" || generatedTypeName === "undefined") { return true; - } else if (leftTypeName.substring(0, mTypes.GEN_NAME.length) !== mTypes.GEN_NAME) { + } else if (generatedTypeName.substring(0, mTypes.GEN_NAME.length) !== mTypes.GEN_NAME) { return false; } @@ -1423,7 +1436,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (popCount === 1) { // we have an empty object literal, must check parent // must traverse prototype hierarchy to make sure empty - return isEmpty(type.$$proto.typeName); + return isEmpty(type.$$proto.typeObj); } return false; } @@ -1476,6 +1489,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * Creates the environment object that stores type information * Called differently depending on what job this content assistant is being called to do. + * TODO move to own module? */ function createEnvironment(options) { var buffer = options.buffer, uid = options.uid, offset = options.offset, indexer = options.indexer, globalObjName = options.globalObjName; @@ -1527,6 +1541,14 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr newName: function() { return namePrefix + this._typeCount++; }, + + /** + * @return {boolean} true iff this is an internally generated name + */ + isSyntheticName: function(name) { + return name.substr(0, namePrefix.length) === namePrefix; + }, + /** * Creates a new empty scope and returns the name of the scope * must call this.popScope() when finished with this scope @@ -1542,6 +1564,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr return newScopeName; }, + newScopeObj : function(range) { + return mTypes.createNameType(this.newScope(range)); + }, + pushScope : function(scopeName) { this._scopeStack.push(scopeName); }, @@ -1562,8 +1588,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * Creates a new empty object scope and returns the name of this object * must call this.popScope() when finished + * @return {{type:String,name:String}} type object that was just created */ - newObject: function(newObjectName, range) { + newObject : function(newObjectName, range) { // object needs its own scope this.newScope(); // if no name passed in, create a new one @@ -1573,9 +1600,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr this._allTypes[newObjectName] = { $$proto : new mTypes.Definition("Object", range, this.uid) }; - this.addVariable("this", null, newObjectName, range); + var typeObj = mTypes.createNameType(newObjectName); + this.addVariable("this", null, typeObj, range); - return newObjectName; + return typeObj; }, /** @@ -1583,6 +1611,19 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * object created has not scope added to the scope stack */ newFleetingObject : function(name, range) { + var newObjectName = name ? name : this.newName(); + this._allTypes[newObjectName] = { + $$proto : new mTypes.Definition("Object", range, this.uid) + }; + return mTypes.createNameType(newObjectName); + }, + + /** + * like a call to this.newObject(), but the + * object created has not scope added to the scope stack + * @return String the constructor name generated + */ + con: function(name, range) { var newObjectName = name ? name : this.newName(); this._allTypes[newObjectName] = { $$proto : new mTypes.Definition("Object", range, this.uid) @@ -1599,31 +1640,23 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * @param {ASTNode|String} target - * returns the type name for the current scope + * @return {{}} the type object for the current scope * if a target is passed in (optional), then use the * inferred type of the target instead (if it exists) */ scope : function(target) { if (typeof target === "string") { return target; - } - - if (target && target.extras.inferredType) { - // check for function literal - var inferredType = target.extras.inferredType; - // hmmmm... will be a problem here if there are nested ~protos - if (mTypes.isFunctionOrConstructor(inferredType) && !mTypes.isPrototype(inferredType)) { - var noArgsType = mTypes.removeParameters(inferredType); - if (this._allTypes[noArgsType]) { - return noArgsType; - } else { - return "Function"; - } - } else if (mTypes.isArrayType(inferredType)) { - // TODO FIXADE we are losing parameterization here - return "Array"; - } else { - return inferredType; + } else if (target && target.extras.inferredTypeObj) { + var inferredTypeObj = target.extras.inferredTypeObj; + // TODO what happens if not a NameExpression or FunctionType??? + if (inferredTypeObj.type === 'NameExpression') { + return inferredTypeObj.name; + } else if (inferredTypeObj.type === 'FunctionType') { + if (inferredTypeObj['new'] && inferredTypeObj['new'].name) { + return inferredTypeObj['new'].name; + } + return "Function"; } } else { // grab topmost scope @@ -1648,12 +1681,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * Will not add to a built in type * * @param {String} name - * @param {String} typeName + * @param {{}} typeObj * @param {Object} target * @param {Array.} range * @param {Array.} docRange */ - addVariable : function(name, target, typeName, range, docRange) { + addVariable : function(name, target, typeObj, range, docRange) { if (this._allTypes.Object["$_$" + name]) { // this is a built in property of object. do not redefine return; @@ -1662,24 +1695,24 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // do not allow augmenting built in types if (!type.$$isBuiltin) { // if new type name is not more general than old type, do not replace - if (typeContainsProperty(type, name) && leftTypeIsMoreGeneral(typeName, type[name].typeName, this)) { + if (typeContainsProperty(type, name) && leftTypeIsMoreGeneral(typeObj, type[name].typeObj, this)) { // do nuthin } else { - type[name] = new mTypes.Definition(typeName ? typeName : "Object", range, this.uid); + type[name] = new mTypes.Definition(typeObj ? typeObj : mTypes.OBJECT_TYPE, range, this.uid); type[name].docRange = docRange; return type[name]; } } }, - addOrSetGlobalVariable : function(name, typeName, range, docRange) { + addOrSetGlobalVariable : function(name, typeObj, range, docRange) { if (this._allTypes.Object["$_$" + name]) { // this is a built in property of object. do not redefine return; } return this.addOrSetVariable(name, // mock an ast node with a global type - { extras : { inferredType : this.globalTypeName() } }, typeName, range, docRange); + this.globalTypeName(), typeObj, range, docRange); }, /** @@ -1688,7 +1721,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * * Will not override an existing variable if the new typeName is "Object" or "undefined" */ - addOrSetVariable : function(name, target, typeName, range, docRange) { + addOrSetVariable : function(name, target, typeObj, range, docRange) { if (name === 'prototype') { name = '$$proto'; } else if (this._allTypes.Object["$_$" + name]) { @@ -1696,10 +1729,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr return; } - var targetType = this.scope(target); - var current = this._allTypes[targetType], found = false; + var targetTypeName = this.scope(target); + var current = this._allTypes[targetTypeName], found = false; // if no type provided, create a new type - typeName = typeName ? typeName : this.newFleetingObject(); + typeObj = typeObj ? typeObj : this.newFleetingObject(); var defn; while (current) { if (typeContainsProperty(current, name)) { @@ -1711,10 +1744,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // 2. builtin types cannot be redefined // 3. new type name is more general than old type if (!current.$$isBuiltin && current.hasOwnProperty(name) && - !leftTypeIsMoreGeneral(typeName, defn.typeName, this)) { + !leftTypeIsMoreGeneral(typeObj, defn.typeObj, this)) { // since we are just overwriting the type we do not want to change // the path or the range - defn.typeName = typeName; + defn.typeObj = typeObj; if (docRange) { defn.docRange = docRange; } @@ -1722,7 +1755,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr found = true; break; } else if (current.$$proto) { - current = this._allTypes[current.$$proto.typeName]; + var tname = current.$$proto.typeObj.name; + current = this._allTypes[tname || "Function"]; } else { current = null; } @@ -1731,9 +1765,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (!found) { // not found, so just add to current scope // do not allow overwriting of built in types - var type = this._allTypes[targetType]; + var type = this._allTypes[targetTypeName]; if (!type.$$isBuiltin) { - defn = new mTypes.Definition(typeName, range, this.uid); + defn = new mTypes.Definition(typeObj, range, this.uid); defn.docRange = docRange; type[name] = defn; } @@ -1741,8 +1775,11 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr return defn; }, - /** looks up the name in the hierarchy */ - lookupName : function(name, target, applyFunction, includeDefinition) { + /** + * looks up the name in the hierarchy + * @return {{}} type objec for the current name or null if doesn't exist + */ + lookupTypeObj : function(name, target, includeDefinition) { // translate function names on object into safe names var swapper = function(name) { @@ -1766,9 +1803,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var proto = type.$$proto; if (res) { - return includeDefinition ? res : res.typeName; + return includeDefinition ? res : res.typeObj; } else if (proto) { - return innerLookup(name, allTypes[proto.typeName], allTypes); + return innerLookup(name, allTypes[proto.typeObj.name], allTypes); } else { return null; } @@ -1797,6 +1834,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * adds a file summary to this module + * @param {{types, provided}} summary + * @param String targetTypeName */ mergeSummary : function(summary, targetTypeName) { @@ -1811,6 +1850,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // but only if a composite type is exported var targetType = this._allTypes[targetTypeName]; if (typeof summary.provided !== 'string') { + // TODO summary.provided mightbe a RecordType for (var providedProperty in summary.provided) { if (summary.provided.hasOwnProperty(providedProperty)) { // the targetType may already have the providedProperty defined @@ -1826,32 +1866,16 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * We need to ensure that ConstructorName.prototype = { ... } does the * thing that we expect. This is why we set the $$proto property of the types */ - createConstructor : function(constructorName, rawTypeName) { + createConstructor : function(rawTypeName) { // don't include the parameter names since we don't want them confusing things when exported - constructorName = mTypes.removeParameters(constructorName); - this.newFleetingObject(constructorName); - var flobj = this.newFleetingObject(constructorName + "~proto"); - this._allTypes[constructorName].$$proto = new mTypes.Definition(flobj, null, this.uidj); - this._allTypes[rawTypeName].$$proto = new mTypes.Definition(constructorName, null, this.uid); + this.newFleetingObject(rawTypeName); + var flobj = this.newFleetingObject(rawTypeName + "~proto"); + this._allTypes[rawTypeName].$$proto = new mTypes.Definition(flobj.name, null, this.uid); }, - findType : function(typeName) { - if (mTypes.isArrayType(typeName)) { - // TODO is there anything we need to do here? - // parameterized array - typeName = "Array"; - } - - // trim arguments if a constructor, careful to avoid a constructor prototype - if (typeName.charAt(0) === "?") { - typeName = mTypes.removeParameters(typeName); - - if (!this._allTypes[typeName]) { - // function type has not been explicitly added to list - // just return function instead - return this._allTypes.Function; - } - } + /** @returns {{}} entry in types array */ + findType : function(typeObj) { + var typeName = mTypes.convertToSimpleTypeName(typeObj); return this._allTypes[typeName]; }, @@ -1863,12 +1887,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * This function stores the target type * so it can be used as the result of this inferencing operation */ - storeTarget : function(targetType) { - if (!this.targetType) { - if (!targetType) { - targetType = this.scope(); + storeTarget : function(targetTypeName) { + if (!this.targetTypeName) { + if (!targetTypeName) { + targetTypeName = this.scope(); } - this.targetType = targetType; + this.targetTypeName = targetTypeName; this.targetFound = true; } } @@ -1880,13 +1904,13 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } function createInferredProposals(targetTypeName, env, completionKind, prefix, replaceStart, proposals, relevance) { - var prop, propName, propType, res, type = env.findType(targetTypeName), proto = type.$$proto; + var prop, propTypeObj, propName, res, type = env.getAllTypes()[targetTypeName], proto = type.$$proto; if (!relevance) { relevance = 100; } // start at the top of the prototype hierarchy so that duplicates can be removed if (proto) { - createInferredProposals(proto.typeName, env, completionKind, prefix, replaceStart, proposals, relevance - 10); + createInferredProposals(proto.typeObj.name, env, completionKind, prefix, replaceStart, proposals, relevance - 10); } // add a separator proposal @@ -1920,18 +1944,16 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // (eg- this.this is wrong) continue; } - if (!type[prop].typeName) { + if (!type[prop].typeObj) { // minified files sometimes have invalid property names (eg- numbers). Ignore them) continue; } if (proposalUtils.looselyMatches(prefix, propName)) { - propType = type[prop].typeName; - var first = propType.charAt(0); - if (first === "?" || first === "*") { - // we have a function + propTypeObj = type[prop].typeObj; + if (propTypeObj.type === 'FunctionType') { res = calculateFunctionProposal(propName, - propType, replaceStart - 1); - var funcDesc = res.completion + " : " + mTypes.createReadableType(propType, env); + propTypeObj, replaceStart - 1); + var funcDesc = res.completion + " : " + mTypes.createReadableType(propTypeObj, env); proposals["$"+propName] = { proposal: res.completion, description: funcDesc, @@ -1946,7 +1968,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr proposals["$"+propName] = { proposal: propName, relevance: relevance, - description: createProposalDescription(propName, propType, env), + description: createProposalDescription(propName, propTypeObj, env), style: 'emphasis', overwrite: true }; @@ -1969,7 +1991,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr function forType(type) { for (var prop in type) { if (isInterestingProperty(type, prop)) { - var propType = type[prop].typeName; + var propType = type[prop].typeObj; var first = propType.charAt(0); if (first === "?" || first === "*") { var res = calculateFunctionProposal(prop, propType, replaceStart - 1); @@ -2014,6 +2036,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } + // TODO this is incorrect!!!!! function findUnreachable(currentTypeName, allTypes, alreadySeen) { if (currentTypeName.charAt(0) === '*') { // constructors are not stored with their arguments so need to remove them in order to find them @@ -2023,7 +2046,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (currentType) { for(var prop in currentType) { if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { - var propType = currentType[prop].typeName; + var propType = currentType[prop].typeObj; while (mTypes.isFunctionOrConstructor(propType) || mTypes.isArrayType(propType)) { if (!alreadySeen[propType]) { alreadySeen[propType] = true; @@ -2091,7 +2114,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr reachable[retType] = true; } - findUnreachable(moduleTypeName, allTypes, reachable); +// findUnreachable(moduleTypeName, allTypes, reachable); for (var prop in allTypes) { if (allTypes.hasOwnProperty(prop) && !reachable[prop]) { delete allTypes[prop]; @@ -2347,9 +2370,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var target = this._doVisit(root, environment); var lookupName = toLookFor.type === "Identifier" ? toLookFor.name : 'this'; - var maybeType = environment.lookupName(lookupName, toLookFor.extras.target || target, false, true); + var maybeType = environment.lookupTypeObj(lookupName, toLookFor.extras.target || target, true); if (maybeType) { - var hover = mTypes.styleAsProperty(lookupName, findName) + " : " + mTypes.createReadableType(maybeType.typeName, environment, true, 0, findName); + var hover = mTypes.styleAsProperty(lookupName, findName) + " : " + mTypes.createReadableType(maybeType.typeObj, environment, true, 0, findName); maybeType.hoverText = hover; return maybeType; } else { @@ -2385,7 +2408,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr this._doVisit(root, environment); } catch (e) { if (typeof scriptedLogger !== "undefined") { - scriptedLogger.error("Problem with: " + fileName, "CONTENT_ASSIST"); + scriptedLogger.error("Problem inferring in: " + fileName, "CONTENT_ASSIST"); scriptedLogger.error(e.message, "CONTENT_ASSIST"); scriptedLogger.error(e.stack, "CONTENT_ASSIST"); } @@ -2399,7 +2422,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // the exports is the return value of the final argument var args = environment.amdModule["arguments"]; if (args && args.length > 0) { - modType = mTypes.extractReturnType(args[args.length-1].extras.inferredType); + modType = mTypes.extractReturnType(args[args.length-1].extras.inferredTypeObj); } else { modType = "Object"; } @@ -2408,7 +2431,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // a wrapped commonjs module // we have already checked the correctness of this function var exportsParam = environment.commonjsModule["arguments"][0].params[1]; - modType = exportsParam.extras.inferredType; + modType = exportsParam.extras.inferredTypeObj; provided = provided = environment.findType(modType); } else { @@ -2418,7 +2441,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (provided.exports) { // actually, commonjs kind = "commonjs"; - modType = provided.exports.typeName; + modType = provided.exports.typeObj; } else { kind = "global"; modType = environment.globalTypeName(); diff --git a/client/scripts/plugins/esprima/indexerService.js b/client/scripts/plugins/esprima/indexerService.js index 170c2cae..4e0bfb9f 100644 --- a/client/scripts/plugins/esprima/indexerService.js +++ b/client/scripts/plugins/esprima/indexerService.js @@ -24,7 +24,7 @@ */ define(["plugins/esprima/esprimaJsContentAssist", "servlets/jsdepend-client", "scripted/utils/storage", "when", "scriptedLogger"], function(mEsprimaContentAssist, jsdepend, storage, when, scriptedLogger) { - + /** * Promise aware array iterator. Loops over elements of an array from left to right * applying the function to each element in the array. The function gets passed @@ -39,7 +39,7 @@ function each(array, fun) { null ); } - + // webworkers exist var worker; if ((this.window && this.window.Worker) && !this.window.isTest) { @@ -67,7 +67,7 @@ function each(array, fun) { } } - + // for each file, there are 4 things put in local storage: // -deps : direct dependency list for file // -deps-ts : timestamp for dependency list (not sure if this is necessary) @@ -75,7 +75,7 @@ function each(array, fun) { // -summary-ts : timestamp for summary // See https://issuetracker.springsource.com/browse/SCRIPTED-160 for a full description of // what the dependencies look like - + // The dependencies is an associative array where each key is a path to a transitive dependency // the values contain the module kind, and an associative array of references. // Each reference has a kind, name, and path @@ -122,7 +122,7 @@ function each(array, fun) { var textStructure = JSON.stringify(structure); var ts = generateTimeStamp(); statusFn("Persisting summary of " + file); - + persistFn(file + "-summary", textStructure); persistFn(file + "-summary-ts", ts); indexer.setTargetFile(oldFile); @@ -142,7 +142,7 @@ function each(array, fun) { } return deferred.promise; } - + /** * caches the dependencies for current file and its transitive dependencies */ @@ -154,7 +154,7 @@ function each(array, fun) { } } } - + /** * checks the dependency list to see which summaries need updating. * TODO FIXADE : this is currently a stub method and always assumes that everything needs updating @@ -176,9 +176,9 @@ function each(array, fun) { } return needsUpdating; } - - - + + + // anything over 2 days old is considered stale var twoDays = 1000 * 60 * 60 * 24 * 2; function isStale(val, currentTime) { @@ -201,7 +201,7 @@ function each(array, fun) { * @param statusFn is a function that accepts status messages */ function Indexer(persistFn, retrieveFn, statusFn) { - + if (!persistFn) { persistFn = function(key, value) { storage.safeStore(key, value); }; } @@ -212,7 +212,7 @@ function each(array, fun) { statusFn = function(msg) { scriptedLogger.debug(msg, "INDEXER"); }; } - + // private instance variable var indexTargetFile; @@ -228,7 +228,7 @@ function each(array, fun) { } return JSON.parse(deps); } - + /** * retrieves the summaries for all dependencies in the global scope */ @@ -242,7 +242,7 @@ function each(array, fun) { return { }; } deps = JSON.parse(deps); - + // for each dependency that is global, extract the summary var summaries = [ ]; for (var prop in deps.refs) { @@ -263,7 +263,7 @@ function each(array, fun) { } return summaries; }; - + /** * retrieves the summary with the given name if it exists, or null if it doesn't */ @@ -277,7 +277,7 @@ function each(array, fun) { return null; } deps = JSON.parse(deps); - + var ref = deps.refs[name]; if (ref) { var summary = retrieveFn(ref.path + "-summary"); @@ -294,15 +294,15 @@ function each(array, fun) { } return null; }; - + this.setTargetFile = function(targetFile){ indexTargetFile = targetFile; }; - + this.getTargetFile = function(){ return indexTargetFile; }; - + this.hasProblem = function(name) { var deps = getDeps(name); if (deps) { @@ -311,7 +311,7 @@ function each(array, fun) { } return true; }; - + /** * looks for a dependency with the given module name * returns the path to that dependency @@ -322,7 +322,7 @@ function each(array, fun) { return deps.refs[name].path; } }; - + /** * Two kinds of objects are worked with here: * dependency = { path : { path to file }, name { module name }, kind : { global, AMD }, timestamp : long } @@ -361,11 +361,11 @@ function each(array, fun) { that._internalPerformIndex(fileName, callback); }, 100); } - + // since this function is being used as a syntax checker, must return an empty array return []; }; - + /** * Does the actual indexing. Will be performed by a webworker if the current browser supports them. * So, therefore must abstract away from localStorage and from calling the console @@ -377,10 +377,10 @@ function each(array, fun) { getDependencies(fileName, statusFn, function(deps) { // cache these dependencies cacheDeps(fileName, deps, persistFn); - + // for each dependency, check local storage to see if still valid var needsUpdating = checkCache(deps, retrieveFn); - + each(needsUpdating, function(element) { return createSummary(element, indexer, persistFn, statusFn); }).then(function() { @@ -391,7 +391,7 @@ function each(array, fun) { }); }; } - + return { Indexer : Indexer }; diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index b28f7b60..7cc144cf 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -14,27 +14,39 @@ /* This module defines the built in types for the scripted JS inferencer. It also contains functions for manipulating internal type signatures. +*/ -Here is the BNF for internal type signature: - - ::= | | | | - - ::= js_identifier // simple types are just js identifiers - - ::= "Array.[" "]" // a bit verbose for array types, but following syntax of closure compiler - - ::= "~proto" // specifies that this type is a prototype of the base type - - ::= "?" ":" ( ",")* // function types have one or more paramteters +/*jslint es5:true browser:true*/ +/*global define doctrine console */ +define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], +function(proposalUtils, scriptedLogger/*, doctrine*/) { - ::= "*" ":" ( ",")* // same as function type, but with different start + /** + * Doctrine closure compiler style type objects + */ + function ensureTypeObject(signature) { + if (signature.type) { + return signature; + } + try { + return doctrine.parseParamType(signature); + } catch(e) { + console.error("doctrine failure to parse: " + signature); + return {}; + } + } - ::= js_identifier ("/" )? // a parameter type consists of a name and an optional type, separated by a / -*/ + function createNameType(name) { + if (typeof name !== 'string') { + throw new Error('Expected string, but found: ' + JSON.parse(name)); + } + return { type: 'NameExpression', name: name }; + } -/*global define doctrine */ -define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], function(proposalUtils, scriptedLogger) { + var THE_UNKNOWN_TYPE = createNameType("Object"); + + var JUST_DOTS = '$$__JUST_DOTS__$$'; /** * The Definition class refers to the declaration of an identifier. @@ -46,45 +58,59 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], * If the document is undefined, then the definition is in the current document. * * @param String typeName - * @param {Array.} range + * @param {[Number]} range * @param String path */ - var Definition = function(typeName, range, path) { - this.typeName = typeName; + var Definition = function(typeObj, range, path) { + this._typeObj = ensureTypeObject(typeObj); this.range = range; this.path = path; }; + Definition.prototype = { + set typeObj(val) { + var maybeObj = val; + if (typeof maybeObj === 'string') { + maybeObj = ensureTypeObject(maybeObj); + } + this._typeObj = maybeObj; + }, + + get typeObj() { + return this._typeObj; + } + }; + // From ecma script manual 262 section 15 // the global object when not in browser or node var Global = function() {}; Global.prototype = { $$proto : new Definition("Object"), - decodeURI : new Definition("?String:uri"), - encodeURI : new Definition("?String:uri"), - 'eval' : new Definition("?Object:toEval"), - parseInt : new Definition("?Number:str,[radix]"), - parseFloat : new Definition("?Number:str,[radix]"), - "this": new Definition("Global"), + decodeURI : new Definition("function(uri:String):String"), + encodeURI : new Definition("function(uri:String):String"), + 'eval' : new Definition("function(toEval:String):Object"), + parseInt : new Definition("function(str:String,radix:Number=):Number"), + parseFloat : new Definition("function(str:String,radix:Number=):Number"), Math: new Definition("Math"), JSON: new Definition("JSON"), - Object: new Definition("*Object:[val]"), - Function: new Definition("*Function:"), - Array: new Definition("*Array:[val]"), - Boolean: new Definition("*Boolean:[val]"), - Number: new Definition("*Number:[val]"), - Date: new Definition("*Date:[val]"), - RegExp: new Definition("*RegExp:[val]"), - Error: new Definition("*Error:[err]"), + Object: new Definition("function(new:Object,val:Object=):Object"), + Function: new Definition("function(new:Function):Function"), + Array: new Definition("function(new:Array,val:Array=):Array"), + Boolean: new Definition("function(new:Boolean,val:Boolean=):Boolean"), + Number: new Definition("function(new:Number,val:Number=):Number"), + Date: new Definition("function(new:Date,val:Date=):Date"), + RegExp: new Definition("function(new:RegExp,val:RegExp=):RegExp"), + Error: new Definition("function(new:Error,err:Error=):Error"), 'undefined' : new Definition("undefined"), - isNaN : new Definition("?Boolean:num"), - isFinite : new Definition("?Boolean:num"), + isNaN : new Definition("function(num:Number):Boolean"), + isFinite : new Definition("function(num:Number):Boolean"), "NaN" : new Definition("Number"), "Infinity" : new Definition("Number"), - decodeURIComponent : new Definition("?String:encodedURIString"), - encodeURIComponent : new Definition("?String:decodedURIString") + decodeURIComponent : new Definition("function(encodedURIString:String):String"), + encodeURIComponent : new Definition("function(decodedURIString:String):String"), + "this": new Definition("Global") // not included since not meant to be referenced directly // EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError }; @@ -93,43 +119,42 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], var Module = function() {}; Module.prototype = { - // From Window - decodeURI : new Definition("?String:uri"), - encodeURI : new Definition("?String:uri"), - 'eval' : new Definition("?Object:toEval"), - parseInt : new Definition("?Number:str,[radix]"), - parseFloat : new Definition("?Number:str,[radix]"), - "this": new Definition("Module"), + // From Global + decodeURI : new Definition("function(uri:String):String"), + encodeURI : new Definition("function(uri:String):String"), + 'eval' : new Definition("function(toEval:String):Object"), + parseInt : new Definition("function(str:String,radix:Number=):Number"), + parseFloat : new Definition("function(str:String,radix:Number=):Number"), Math: new Definition("Math"), JSON: new Definition("JSON"), - Object: new Definition("*Object:[val]"), - Function: new Definition("*Function:"), - Array: new Definition("*Array:[val]"), - Boolean: new Definition("*Boolean:[val]"), - Number: new Definition("*Number:[val]"), - Date: new Definition("*Date:[val]"), - RegExp: new Definition("*RegExp:[val]"), - Error: new Definition("*Error:[err]"), + Object: new Definition("function(new:Object,val:Object=):Object"), + Function: new Definition("function(new:Function):Function"), + Array: new Definition("function(new:Array,val:Array=):Array"), + Boolean: new Definition("function(new:Boolean,val:Boolean=):Boolean"), + Number: new Definition("function(new:Number,val:Number=):Number"), + Date: new Definition("function(new:Date,val:Date=):Date"), + RegExp: new Definition("function(new:RegExp,val:RegExp=):RegExp"), + Error: new Definition("function(new:Error,err:Error=):Error"), 'undefined' : new Definition("undefined"), - isNaN : new Definition("?Boolean:num"), - isFinite : new Definition("?Boolean:num"), + isNaN : new Definition("function(num:Number):Boolean"), + isFinite : new Definition("function(num:Number):Boolean"), "NaN" : new Definition("Number"), "Infinity" : new Definition("Number"), - decodeURIComponent : new Definition("?String:encodedURIString"), - encodeURIComponent : new Definition("?String:decodedURIString"), - + decodeURIComponent : new Definition("function(encodedURIString:String):String"), + encodeURIComponent : new Definition("function(decodedURIString:String):String"), + "this": new Definition("Module"), Buffer: new Definition("Object"), console: new Definition("Object"), module: new Definition("Module"), process: new Definition("Process"), - require: new Definition("?Object:module"), + require: new Definition("function(module:String):Object"), // exports: new Definition("Object"), - clearInterval: new Definition("?undefined:t"), - clearTimeout: new Definition("?undefined:t"), - setInterval: new Definition("?Number:callback,ms"), - setTimeout : new Definition("?Number:callback,ms"), + clearInterval: new Definition("function(t:Number)"), + clearTimeout: new Definition("function(t:Number)"), + setInterval: new Definition("function(callback:Function,ms:Number):Number"), + setTimeout : new Definition("function(callback:Function,ms:Number):Number"), global: new Definition("Module"), querystring: new Definition("String"), __filename: new Definition("String"), @@ -141,30 +166,30 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], // copied from Global $$proto : new Definition("Object"), - decodeURI : new Definition("?String:uri"), - encodeURI : new Definition("?String:uri"), - 'eval' : new Definition("?Object:toEval"), - parseInt : new Definition("?Number:str,[radix]"), - parseFloat : new Definition("?Number:str,[radix]"), - "this": new Definition("Window"), + decodeURI : new Definition("function(uri:String):String"), + encodeURI : new Definition("function(uri:String):String"), + 'eval' : new Definition("function(toEval:String):Object"), + parseInt : new Definition("function(str:String,radix:Number=):Number"), + parseFloat : new Definition("function(str:String,radix:Number=):Number"), Math: new Definition("Math"), JSON: new Definition("JSON"), - Object: new Definition("*Object:[val]"), - Function: new Definition("*Function:"), - Array: new Definition("*Array:[val]"), - Boolean: new Definition("*Boolean:[val]"), - Number: new Definition("*Number:[val]"), - Date: new Definition("*Date:[val]"), - RegExp: new Definition("*RegExp:[val]"), - Error: new Definition("*Error:[err]"), + Object: new Definition("function(new:Object,val:Object=):Object"), + Function: new Definition("function(new:Function):Function"), + Array: new Definition("function(new:Array,val:Array=):Array"), + Boolean: new Definition("function(new:Boolean,val:Boolean=):Boolean"), + Number: new Definition("function(new:Number,val:Number=):Number"), + Date: new Definition("function(new:Date,val:Date=):Date"), + RegExp: new Definition("function(new:RegExp,val:RegExp=):RegExp"), + Error: new Definition("function(new:Error,err:Error=):Error"), 'undefined' : new Definition("undefined"), - isNaN : new Definition("?Boolean:num"), - isFinite : new Definition("?Boolean:num"), + isNaN : new Definition("function(num:Number):Boolean"), + isFinite : new Definition("function(num:Number):Boolean"), "NaN" : new Definition("Number"), "Infinity" : new Definition("Number"), - decodeURIComponent : new Definition("?String:encodedURIString"), - encodeURIComponent : new Definition("?String:decodedURIString"), + decodeURIComponent : new Definition("function(encodedURIString:String):String"), + encodeURIComponent : new Definition("function(decodedURIString:String):String"), + "this": new Definition("Window"), // see https://developer.mozilla.org/en/DOM/window // Properties applicationCache : new Definition("DOMApplicationCache"), @@ -211,97 +236,92 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], // Methods // commented methods are mozilla-specific - addEventListener : new Definition("?undefined:"), - alert : new Definition("?undefined:String"), - atob : new Definition("?String:val"), - back : new Definition("?undefined:"), - blur : new Definition("?undefined:"), - btoa : new Definition("?String:val"), - clearInterval : new Definition("?undefined:interval"), - clearTimeout : new Definition("?undefined:timeout"), - close : new Definition("?undefined:"), - confirm : new Definition("?Boolean:msg"), - //disableExternalCapture : new Definition("???"), - dispatchEvent : new Definition("?undefined:domnode"), - dump : new Definition("?undefined:message"), - //enableExternalCapture : new Definition("???"), - escape : new Definition("?String:str"), - find : new Definition("?Boolean:text"), - focus : new Definition("?undefined:"), - forward : new Definition("?undefined:"), - getAttention : new Definition("?undefined:"), - getComputedStyle : new Definition("?CSSStyleDeclaration:dombode"), - getSelection : new Definition("?Selection:"), - home : new Definition("?undefined:"), - matchMedia : new Definition("?MediaQueryList:query"), - //maximize : new Definition("???"), - //minimize : new Definition("???"), - moveBy : new Definition("?undefined:deltaX,deltaY"), - moveTo : new Definition("?undefined:x,y"), - open : new Definition("?Window:strUrl,strWindowName,[strWindowFeatures]"), - openDialog : new Definition("?Window:strUrl,strWindowName,strWindowFeatures,[args]"), - postMessage : new Definition("?undefined:message,targetOrigin"), - print : new Definition("?undefined:"), - prompt : new Definition("?String:message"), - removeEventListener : new Definition("?undefined:type,listener,[useCapture]"), - resizeBy : new Definition("?undefined:deltaX,deltaY"), - resizeTo : new Definition("?undefined:x,y"), - scroll : new Definition("?undefined:x,y"), - scrollBy : new Definition("?undefined:deltaX,deltaY"), - scrollByLines : new Definition("?undefined:lines"), - scrollByPages : new Definition("?undefined:pages"), - scrollTo : new Definition("?undefined:x,y"), - setCursor : new Definition("?undefined:cursor"), - setInterval : new Definition("?Number:func,interval"), - //setResizable : new Definition("???"), - setTimeout : new Definition("?Number:func,timeout"), - sizeToContent : new Definition("?undefined:"), - stop : new Definition("?undefined:"), - unescape : new Definition("?String:str"), - updateCommands : new Definition("?undefined:cmdName"), + addEventListener : new Definition("function()"), + alert : new Definition("function(msg:String)"), + atob : new Definition("function(val:Object):String"), + back : new Definition("function()"), + blur : new Definition("function()"), + btoa : new Definition("function(val:Object):String"), + clearInterval: new Definition("function(t:Number)"), + clearTimeout: new Definition("function(t:Number)"), + close : new Definition("function()"), + confirm : new Definition("function(msg:String):Boolean"), + dispatchEvent : new Definition("function(domnode:Node)"), + dump : new Definition("function(msg:String)"), + escape : new Definition("function(str:String):String"), + find : new Definition("function(str:String):Boolean"), + focus : new Definition("function()"), + forward : new Definition("function()"), + getAttention : new Definition("function()"), + getComputedStyle : new Definition("function(domnode:Node):CSSStyleDeclaration"), + getSelection : new Definition("function():Selection"), + home : new Definition("function()"), + matchMedia : new Definition("function(query:Object):MediaQueryList"), + moveBy : new Definition("function(deltaX:Number,deltaY:Number)"), + moveTo : new Definition("function(x:Number,y:Number)"), + open : new Definition("function(strUrl:String,strWindowName:String,strWindowFeatures:String=):Window"), + openDialog : new Definition("function(strUrl:String,strWindowName:String,strWindowFeatures:String,args:String=):Window"), + postMessage : new Definition("function(message:String,targetOrigin:String)"), + print : new Definition("function()"), + prompt : new Definition("function(message:String):String"), + removeEventListener : new Definition("function(type:String,listener:Object,useCapture:Boolean=)"), + resizeBy : new Definition("function(deltaX:Number,deltaY:Number)"), + resizeTo : new Definition("function(x:Number,y:Number)"), + scroll : new Definition("function(x:Number,y:Number)"), + scrollBy : new Definition("function(deltaX:Number,deltaY:Number)"), + scrollByLines : new Definition("function(lines:Number)"), + scrollByPages : new Definition("function(pages:Number)"), + scrollTo : new Definition("function(x:Number,y:Number)"), + setCursor : new Definition("function(cursor)"), + setInterval: new Definition("function(callback:Function,ms:Number):Number"), + setTimeout : new Definition("function(callback:Function,ms:Number):Number"), + sizeToContent : new Definition("function()"), + stop : new Definition("function()"), + unescape : new Definition("function(str:String):String"), + updateCommands : new Definition("function(cmdName:String)"), // Events - onabort : new Definition("?undefined:event"), - onbeforeunload : new Definition("?undefined:event"), - onblur : new Definition("?undefined:event"), - onchange : new Definition("?undefined:event"), - onclick : new Definition("?undefined:event"), - onclose : new Definition("?undefined:event"), - oncontextmenu : new Definition("?undefined:event"), - ondevicemotion : new Definition("?undefined:event"), - ondeviceorientation : new Definition("?undefined:event"), - ondragdrop : new Definition("?undefined:event"), - onerror : new Definition("?undefined:event"), - onfocus : new Definition("?undefined:event"), - onhashchange : new Definition("?undefined:event"), - onkeydown : new Definition("?undefined:event"), - onkeypress : new Definition("?undefined:event"), - onkeyup : new Definition("?undefined:event"), - onload : new Definition("?undefined:event"), - onmousedown : new Definition("?undefined:event"), - onmousemove : new Definition("?undefined:event"), - onmouseout : new Definition("?undefined:event"), - onmouseover : new Definition("?undefined:event"), - onmouseup : new Definition("?undefined:event"), - onpaint : new Definition("?undefined:event"), - onpopstate : new Definition("?undefined:event"), - onreset : new Definition("?undefined:event"), - onresize : new Definition("?undefined:event"), - onscroll : new Definition("?undefined:event"), - onselect : new Definition("?undefined:event"), - onsubmit : new Definition("?undefined:event"), - onunload : new Definition("?undefined:event"), - onpageshow : new Definition("?undefined:event"), - onpagehide : new Definition("?undefined:event"), + onabort : new Definition("function(event:Event)"), + onbeforeunload : new Definition("function(event:Event)"), + onblur : new Definition("function(event:Event)"), + onchange : new Definition("function(event:Event)"), + onclick : new Definition("function(event:Event)"), + onclose : new Definition("function(event:Event)"), + oncontextmenu : new Definition("function(event:Event)"), + ondevicemotion : new Definition("function(event:Event)"), + ondeviceorientation : new Definition("function(event:Event)"), + ondragdrop : new Definition("function(event:Event)"), + onerror : new Definition("function(event:Event)"), + onfocus : new Definition("function(event:Event)"), + onhashchange : new Definition("function(event:Event)"), + onkeydown : new Definition("function(event:Event)"), + onkeypress : new Definition("function(event:Event)"), + onkeyup : new Definition("function(event:Event)"), + onload : new Definition("function(event:Event)"), + onmousedown : new Definition("function(event:Event)"), + onmousemove : new Definition("function(event:Event)"), + onmouseout : new Definition("function(event:Event)"), + onmouseover : new Definition("function(event:Event)"), + onmouseup : new Definition("function(event:Event)"), + onpaint : new Definition("function(event:Event)"), + onpopstate : new Definition("function(event:Event)"), + onreset : new Definition("function(event:Event)"), + onresize : new Definition("function(event:Event)"), + onscroll : new Definition("function(event:Event)"), + onselect : new Definition("function(event:Event)"), + onsubmit : new Definition("function(event:Event)"), + onunload : new Definition("function(event:Event)"), + onpageshow : new Definition("function(event:Event)"), + onpagehide : new Definition("function(event:Event)"), // Constructors - Image : new Definition("*HTMLImageElement:[width],[height]"), - Option : new Definition("*HTMLOptionElement:[text].[value],[defaultSelected],[selected]"), - Worker : new Definition("*Worker:url"), - XMLHttpRequest : new Definition("*XMLHttpRequest:"), - WebSocket : new Definition("*WebSocket:url,protocols"), - Event : new Definition("*Event:type"), - Node : new Definition("*Node:") + Image : new Definition("function(new:HTMLImageElement,width:Number=,height:Number=):HTMLImageElement"), + Option : new Definition("function(new:HTMLOptionElement,text:String=,value:Object=,defaultSelected:Boolean=,selected:Boolean=):HTMLOptionElement"), + Worker : new Definition("function(new:Worker,url:String):Worker"), + XMLHttpRequest : new Definition("function(new:XMLHttpRequest):XMLHttpRequest"), + WebSocket : new Definition("function(new:WebSocket,url,protocols):WebSocket"), + Event : new Definition("function(new:Event,type:String):Event"), + Node : new Definition("function(new:Node):Node") }; var initialGlobalProperties = []; @@ -359,12 +379,12 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, // Can't use the real propoerty name here because would override the real methods of that name $_$prototype : new Definition("Object"), - $_$toString: new Definition("?String:"), - $_$toLocaleString : new Definition("?String:"), - $_$valueOf: new Definition("?Object:"), - $_$hasOwnProperty: new Definition("?Boolean:property"), - $_$isPrototypeOf: new Definition("?Boolean:object"), - $_$propertyIsEnumerable: new Definition("?Boolean:property") + $_$toString: new Definition("function():String"), + $_$toLocaleString : new Definition("function():String"), + $_$valueOf: new Definition("function():Object"), + $_$hasOwnProperty: new Definition("function(property:String):Boolean"), + $_$isPrototypeOf: new Definition("function(object:Object):Boolean"), + $_$propertyIsEnumerable: new Definition("function(property:String):Boolean") }, /** @@ -372,10 +392,10 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], */ Function : { $$isBuiltin: true, - apply : new Definition("?Object:func,[argArray]"), + apply : new Definition("function(func:function(),argArray:Array=):Object"), "arguments" : new Definition("Arguments"), - bind : new Definition("?Object:func,[args...]"), - call : new Definition("?Object:func,[args...]"), + bind : new Definition("function(func:function(),args:Object...):Object"), + call : new Definition("function(func:function(),args:Object...):Object"), caller : new Definition("Function"), length : new Definition("Number"), name : new Definition("String"), @@ -388,26 +408,26 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], Array : { $$isBuiltin: true, - concat : new Definition("?Array:first,[rest...]"), - join : new Definition("?String:separator"), + concat : new Definition("function(first:Array,rest:Array...):Array"), + join : new Definition("function(separator:Object):String"), length : new Definition("Number"), - pop : new Definition("?Object:"), - push : new Definition("?Object:[vals...]"), - reverse : new Definition("?Array:"), - shift : new Definition("?Object:"), - slice : new Definition("?Array:start,deleteCount,[items...]"), - splice : new Definition("?Array:start,end"), - sort : new Definition("?Array:[sorter]"), - unshift : new Definition("?Number:[items...]"), - indexOf : new Definition("?Number:searchElement,[fromIndex]"), - lastIndexOf : new Definition("?Number:searchElement,[fromIndex]"), - every : new Definition("?Boolean:callbackFn,[thisArg]"), - some : new Definition("?Boolean:callbackFn,[thisArg]"), - forEach : new Definition("?Object:callbackFn,[thisArg]"), // should return - map : new Definition("?Array:callbackFn,[thisArg]"), - filter : new Definition("?Array:callbackFn,[thisArg]"), - reduce : new Definition("?Array:callbackFn,[initialValue]"), - reduceRight : new Definition("?Array:callbackFn,[initialValue]"), + pop : new Definition("function():Object"), + push : new Definition("function(vals:Object...):Object"), + reverse : new Definition("function():Array"), + shift : new Definition("function():Object"), + slice : new Definition("function(start:Number,deleteCount:Number,items:Object...):Array"), + splice : new Definition("function(start:Number,end:Number):Array"), + sort : new Definition("function(sorter:Object=):Array"), + unshift : new Definition("function(items:Object...):Number"), + indexOf : new Definition("function(searchElement,fromIndex=):Number"), + lastIndexOf : new Definition("function(searchElement,fromIndex=):Number"), + every : new Definition("function(callbackFn:function(elt:Object),thisArg:Object=):Boolean"), + some : new Definition("function(callbackFn:function(elt:Object),thisArg:Object=):Boolean"), + forEach : new Definition("function(callbackFn:function(elt:Object),thisArg:Object=):Object"), + map : new Definition("function(callbackFn:function(elt:Object):Object,thisArg:Object=):Array"), + filter : new Definition("function(callbackFn:function(elt:Object):Boolean,thisArg:Object=):Array"), + reduce : new Definition("function(callbackFn:function(elt:Object):Object,initialValue:Object=):Array"), + reduceRight : new Definition("function(callbackFn:function(elt:Object):Object,initialValue:Object=):Array"), $$proto : new Definition("Object") }, @@ -416,24 +436,24 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], */ String : { $$isBuiltin: true, - charAt : new Definition("?String:index"), - charCodeAt : new Definition("?Number:index"), - concat : new Definition("?String:array"), - indexOf : new Definition("?Number:searchString,[start]"), - lastIndexOf : new Definition("?Number:searchString,[start]"), + charAt : new Definition("function(index:Number):String"), + charCodeAt : new Definition("function(index:Number):Number"), + concat : new Definition("function(str:String):String"), + indexOf : new Definition("function(searchString:String,start:Number=):Number"), + lastIndexOf : new Definition("function(searchString:String,start:Number=):Number"), length : new Definition("Number"), - localeCompare : new Definition("?Number:Object"), - match : new Definition("?Boolean:regexp"), - replace : new Definition("?String:searchValue,replaceValue"), - search : new Definition("?String:regexp"), - slice : new Definition("?String:start,end"), - split : new Definition("?Array:separator,[limit]"), // Array of string - substring : new Definition("?String:start,end"), - toLocaleUpperCase : new Definition("?String:"), - toLowerCase : new Definition("?String:"), - toLocaleLowerCase : new Definition("?String:"), - toUpperCase : new Definition("?String:"), - trim : new Definition("?String:"), + localeCompare : new Definition("function(str:String):Number"), + match : new Definition("function(regexp:(String|RegExp)):Boolean"), + replace : new Definition("function(searchValue:(String|RegExp),replaceValue:String):String"), + search : new Definition("function(regexp:(String|RegExp)):String"), + slice : new Definition("function(start:Number,end:Number):String"), + split : new Definition("function(separator:String,limit:Number=):[String]"), // Array of string + substring : new Definition("function(start:Number,end:Number=):String"), + toLocaleUpperCase : new Definition("function():String"), + toLowerCase : new Definition("function():String"), + toLocaleLowerCase : new Definition("function():String"), + toUpperCase : new Definition("function():String"), + trim : new Definition("function():String"), $$proto : new Definition("Object") }, @@ -451,9 +471,9 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], */ Number : { $$isBuiltin: true, - toExponential : new Definition("?Number:digits"), - toFixed : new Definition("?Number:digits"), - toPrecision : new Definition("?Number:digits"), + toExponential : new Definition("function(digits:Number):String"), + toFixed : new Definition("function(digits:Number):String"), + toPrecision : new Definition("function(digits:Number):String"), // do we want to include NaN, MAX_VALUE, etc? $$proto : new Definition("Object") @@ -477,24 +497,24 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], SQRT2 : new Definition("Number"), // Methods - abs : new Definition("?Number:val"), - acos : new Definition("?Number:val"), - asin : new Definition("?Number:val"), - atan : new Definition("?Number:val"), - atan2 : new Definition("?Number:val1,val2"), - ceil : new Definition("?Number:val"), - cos : new Definition("?Number:val"), - exp : new Definition("?Number:val"), - floor : new Definition("?Number:val"), - log : new Definition("?Number:val"), - max : new Definition("?Number:val1,val2"), - min : new Definition("?Number:val1,val2"), - pow : new Definition("?Number:x,y"), - random : new Definition("?Number:"), - round : new Definition("?Number:val"), - sin : new Definition("?Number:val"), - sqrt : new Definition("?Number:val"), - tan : new Definition("?Number:val"), + abs : new Definition("function(val:Number):Number"), + acos : new Definition("function(val:Number):Number"), + asin : new Definition("function(val:Number):Number"), + atan : new Definition("function(val:Number):Number"), + atan2 : new Definition("function(val1:Number,val2:Number):Number1"), + ceil : new Definition("function(val:Number):Number"), + cos : new Definition("function(val:Number):Number"), + exp : new Definition("function(val:Number):Number"), + floor : new Definition("function(val:Number):Number"), + log : new Definition("function(val:Number):Number"), + max : new Definition("function(val1:Number,val2:Number):Number"), + min : new Definition("function(val1:Number,val2:Number):Number"), + pow : new Definition("function(x:Number,y:Number):Number"), + random : new Definition("function():Number"), + round : new Definition("function(val:Number):Number"), + sin : new Definition("function(val:Number):Number"), + sqrt : new Definition("function(val:Number):Number"), + tan : new Definition("function(val:Number):Number"), $$proto : new Definition("Object") }, @@ -504,53 +524,53 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], */ Date : { $$isBuiltin: true, - toDateString : new Definition("?String:"), - toTimeString : new Definition("?String:"), - toUTCString : new Definition("?String:"), - toISOString : new Definition("?String:"), - toJSON : new Definition("?Object:key"), - toLocaleDateString : new Definition("?String:"), - toLocaleTimeString : new Definition("?String:"), - - getTime : new Definition("?Number:"), - getTimezoneOffset : new Definition("?Number:"), - - getDay : new Definition("?Number:"), - getUTCDay : new Definition("?Number:"), - getFullYear : new Definition("?Number:"), - getUTCFullYear : new Definition("?Number:"), - getHours : new Definition("?Number:"), - getUTCHours : new Definition("?Number:"), - getMinutes : new Definition("?Number:"), - getUTCMinutes : new Definition("?Number:"), - getSeconds : new Definition("?Number:"), - getUTCSeconds : new Definition("?Number:"), - getMilliseconds : new Definition("?Number:"), - getUTCMilliseconds : new Definition("?Number:"), - getMonth : new Definition("?Number:"), - getUTCMonth : new Definition("?Number:"), - getDate : new Definition("?Number:"), - getUTCDate : new Definition("?Number:"), - - setTime : new Definition("?Number:"), - setTimezoneOffset : new Definition("?Number:"), - - setDay : new Definition("?Number:dayOfWeek"), - setUTCDay : new Definition("?Number:dayOfWeek"), - setFullYear : new Definition("?Number:year,[month],[date]"), - setUTCFullYear : new Definition("?Number:year,[month],[date]"), - setHours : new Definition("?Number:hour,[min],[sec],[ms]"), - setUTCHours : new Definition("?Number:hour,[min],[sec],[ms]"), - setMinutes : new Definition("?Number:min,[sec],[ms]"), - setUTCMinutes : new Definition("?Number:min,[sec],[ms]"), - setSeconds : new Definition("?Number:sec,[ms]"), - setUTCSeconds : new Definition("?Number:sec,[ms]"), - setMilliseconds : new Definition("?Number:ms"), - setUTCMilliseconds : new Definition("?Number:ms"), - setMonth : new Definition("?Number:month,[date]"), - setUTCMonth : new Definition("?Number:month,[date]"), - setDate : new Definition("?Number:date"), - setUTCDate : new Definition("?Number:gate"), + toDateString : new Definition("function():String"), + toTimeString : new Definition("function():String"), + toUTCString : new Definition("function():String"), + toISOString : new Definition("function():String"), + toJSON : new Definition("function(key:String):Object"), + toLocaleDateString : new Definition("function():String"), + toLocaleTimeString : new Definition("function():String"), + + getTime : new Definition("function():Number"), + getTimezoneOffset : new Definition("function():Number"), + + getDay : new Definition("function():Number"), + getUTCDay : new Definition("function():Number"), + getFullYear : new Definition("function():Number"), + getUTCFullYear : new Definition("function():Number"), + getHours : new Definition("function():Number"), + getUTCHours : new Definition("function():Number"), + getMinutes : new Definition("function():Number"), + getUTCMinutes : new Definition("function():Number"), + getSeconds : new Definition("function():Number"), + getUTCSeconds : new Definition("function():Number"), + getMilliseconds : new Definition("function():Number"), + getUTCMilliseconds : new Definition("function():Number"), + getMonth : new Definition("function():Number"), + getUTCMonth : new Definition("function():Number"), + getDate : new Definition("function():Number"), + getUTCDate : new Definition("function():Number"), + + setTime : new Definition("function():Number"), + setTimezoneOffset : new Definition("function():Number"), + + setDay : new Definition("function(dayOfWeek:Number):Number"), + setUTCDay : new Definition("function(dayOfWeek:Number):Number"), + setFullYear : new Definition("function(year:Number,month:Number=,date:Number=):Number"), + setUTCFullYear : new Definition("function(year:Number,month:Number=,date:Number=):Number"), + setHours : new Definition("function(hour:Number,min:Number=,sec:Number=,ms:Number=):Number"), + setUTCHours : new Definition("function(hour:Number,min:Number=,sec:Number=,ms:Number=):Number"), + setMinutes : new Definition("function(min:Number,sec:Number=,ms:Number=):Number"), + setUTCMinutes : new Definition("function(min:Number,sec:Number=,ms:Number=):Number"), + setSeconds : new Definition("function(sec:Number,ms:Number=):Number"), + setUTCSeconds : new Definition("function(sec:Number,ms:Number=):Number"), + setMilliseconds : new Definition("function(ms:Number):Number"), + setUTCMilliseconds : new Definition("function(ms:Number):Number"), + setMonth : new Definition("function(month:Number,date:Number=):Number"), + setUTCMonth : new Definition("function(month:Number,date:Number=):Number"), + setDate : new Definition("function(date:Number):Number"), + setUTCDate : new Definition("function(date:Number):Number"), $$proto : new Definition("Object") }, @@ -570,13 +590,13 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], multiline : new Definition("Boolean"), lastIndex : new Definition("Boolean"), - exec : new Definition("?Array:str"), - test : new Definition("?Boolean:str"), + exec : new Definition("function(str:String):[String]"), + test : new Definition("function(str:String):Boolean"), $$proto : new Definition("Object") }, - "?RegExp:" : { + "function(new:RegExp):RegExp" : { $$isBuiltin: true, $$proto : new Definition("Function"), @@ -625,8 +645,8 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], JSON : { $$isBuiltin: true, - parse : new Definition("?Object:str"), - stringify : new Definition("?String:obj"), + parse : new Definition("function(str:String):Object"), + stringify : new Definition("function(json:Object):String"), $$proto : new Definition("Object") }, @@ -643,34 +663,34 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, $$proto : new Definition("Object"), - on: new Definition("?undefined:kind,callback"), + on: new Definition("function(kind:String,callback:function())"), - abort: new Definition("?undefined:"), + abort: new Definition("function()"), stdout: new Definition("Stream"), stderr: new Definition("Stream"), stdin: new Definition("Stream"), argv: new Definition("Array"), // Array. execPath: new Definition("String"), - chdir: new Definition("?undefined:directory"), - cwd: new Definition("?String:"), + chdir: new Definition("function(directory:String)"), + cwd: new Definition("function():String"), env: new Definition("Object"), - getgid: new Definition("?Number:"), - setgid: new Definition("?undefined:id"), - getuid: new Definition("?Number:"), - setuid: new Definition("?undefined:id"), + getgid: new Definition("function():Number"), + setgid: new Definition("function(id:Number)"), + getuid: new Definition("function():Number"), + setuid: new Definition("function(id:Number)"), version: new Definition("String"), versions: new Definition("Object"), // TODO create a versions object? config: new Definition("Object"), - kill: new Definition("?undefined:pid,[signal]"), + kill: new Definition("function(pid:Number,signal:Number=)"), pid: new Definition("Number"), title: new Definition("String"), arch: new Definition("String"), platform: new Definition("String"), - memoryUsage: new Definition("?Object:"), - nextTick: new Definition("?undefined:callback"), - umask: new Definition("?undefined:[mask]"), - uptime: new Definition("?Number:"), - hrtime: new Definition("?Array:") // Array. + memoryUsage: new Definition("function():Object"), + nextTick: new Definition("function(callback:function())"), + umask: new Definition("function(mask:Number=)"), + uptime: new Definition("function():Number"), + hrtime: new Definition("function():Array") // Array. }, // See http://nodejs.org/api/stream.html @@ -684,26 +704,26 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], // readable // events - data: new Definition("?undefined:data"), - error: new Definition("?undefined:exception"), - close: new Definition("?undefined:"), + data: new Definition("function(data:Object)"), + error: new Definition("function(exception:Object)"), + close: new Definition("function()"), readable: new Definition("Boolean"), - setEncoding: new Definition("?undefined:[encoding]"), - pause: new Definition("?undefined:"), - resume: new Definition("?undefined:"), - pipe: new Definition("?undefined:destingation,[options]"), + setEncoding: new Definition("function(encoding:String=)"), + pause: new Definition("function()"), + resume: new Definition("function()"), + pipe: new Definition("function(destination:Object,options:Object=)"), // writable - drain: new Definition("?undefined:"), + drain: new Definition("function()"), writable: new Definition("Boolean"), - write: new Definition("?undefined:[nuffer]"), - end: new Definition("?undefined:[string],[encoding]"), - destroy: new Definition("?undefined:"), - destroySoon: new Definition("?undefined:") + write: new Definition("function(buffer:Object=)"), + end: new Definition("function(string:String=,encoding:String=)"), + destroy: new Definition("function()"), + destroySoon: new Definition("function()") }, /////////////////////////////////////////////////// @@ -762,17 +782,17 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], userAgent : new Definition("String"), // methods - javaEnabled : new Definition("?Boolean:"), - registerContentHandler : new Definition("?undefined:mimType,url,title"), - registerProtocolHandler : new Definition("?undefined:protocol,url,title") + javaEnabled : new Definition("function():Boolean"), + registerContentHandler : new Definition("function(mimType:String,url:String,title:String)"), + registerProtocolHandler : new Definition("function(protocol:String,url:String,title:String)") }, // (not in MDN) http://www.coursevector.com/dommanual/dom/objects/MimeTypeArray.html MimeTypeArray : { $$isBuiltin: true, length : new Definition("Number"), - item : new Definition("?MimeType:index"), - namedItem : new Definition("?MimeType:name") + item : new Definition("function(index:Number):MimeType"), + namedItem : new Definition("function(name:String):MimeType") }, // (not in MDN) http://www.coursevector.com/dommanual/dom/objects/MimeType.html @@ -791,8 +811,8 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], fileName : new Definition("String"), length : new Definition("Number"), name : new Definition("String"), - item : new Definition("?MimeType:index"), - namedItem : new Definition("?MimeType:name") + item : new Definition("function(index:Number):MimeType"), + namedItem : new Definition("function(name:String):MimeType") }, // http://dvcs.w3.org/hg/dap/raw-file/tip/network-api/Overview.html#the-connection-interface @@ -811,11 +831,11 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], length : new Definition("Number"), - key : new Definition("?String:idx"), - getItem : new Definition("?String:key"), - setItem : new Definition("?undefined:key,value"), - removeItem : new Definition("?undefined:key"), - clear : new Definition("?undefined:") + key : new Definition("function(idx:Number):String"), + getItem : new Definition("function(key:String):String"), + setItem : new Definition("function(key:String,value:String)"), + removeItem : new Definition("function(key:String)"), + clear : new Definition("function()") }, // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#interface-xmlhttprequest @@ -826,17 +846,17 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], onreadystatechange : new Definition("EventHandler"), // request - open : new Definition("?undefined:method,url,[async],[user],[password]"), - setRequestHeader : new Definition("?undefined:header,value"), + open : new Definition("function(method:String,url:String,async:Boolean=,user:String=,password:String=)"), + setRequestHeader : new Definition("function(header,value)"), timeout : new Definition("Number"), withCredentials : new Definition("Boolean"), upload : new Definition("Object"), // not right - send : new Definition("?undefined:[data]"), - abort : new Definition("?undefined:"), + send : new Definition("function(data:String=)"), + abort : new Definition("function()"), // response - getResponseHeader : new Definition("?String:header"), - getAllResponseHeaders : new Definition("?String:"), + getResponseHeader : new Definition("function(header:String):String"), + getAllResponseHeaders : new Definition("function():String"), overrideMimType : new Definition("Object"), responseType : new Definition("Object"), // not right readyState : new Definition("Number"), @@ -852,9 +872,9 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, $$proto : new Definition("Object"), - terminate : new Definition("?undefined:"), - postMessage : new Definition("?undefined:message,[transfer]"), - onmessage : new Definition("?undefined:") + terminate : new Definition("function()"), + postMessage : new Definition("function(message:String,transfer:Object=)"), + onmessage : new Definition("function()") }, // http://www.w3.org/TR/workers/#messageport @@ -877,25 +897,25 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], extensions : new Definition("String"), protocol : new Definition("String"), - close : new Definition("?undefined:[reason]"), - send : new Definition("?undefined:data") + close : new Definition("function(reason:Object=)"), + send : new Definition("function(data)") }, // https://developer.mozilla.org/en/DOM/Console Console : { $$isBuiltin: true, - debug : new Definition("?undefined:msg"), - dir : new Definition("?undefined:obj"), - error : new Definition("?undefined:msg"), - group : new Definition("?undefined:"), - groupCollapsed : new Definition("?undefined:"), - groupEnd : new Definition("?undefined:"), - info : new Definition("?undefined:msg"), - log : new Definition("?undefined:msg"), - time : new Definition("?undefined:timerName"), - timeEnd : new Definition("?undefined:timerName"), - trace : new Definition("?undefined:"), - warn : new Definition("?undefined:msg") + debug : new Definition("function(msg:String)"), + dir : new Definition("function(obj)"), + error : new Definition("function(msg:String)"), + group : new Definition("function()"), + groupCollapsed : new Definition("function()"), + groupEnd : new Definition("function()"), + info : new Definition("function(msg:String)"), + log : new Definition("function(msg:String)"), + time : new Definition("function(timerName:String)"), + timeEnd : new Definition("function(timerName:String)"), + trace : new Definition("function()"), + warn : new Definition("function(msg:String)") }, // TODO FIXADE remove ??? @@ -923,13 +943,13 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], isTrusted : new Definition("Boolean"), // methods - initEvent : new Definition("?undefined:type,bubbles,cancelable"), - preventDefault : new Definition("?undefined:"), - stopImmediatePropagation : new Definition("?undefined:"), - stopPropagation : new Definition("?undefined:") + initEvent : new Definition("function(type:String,bubbles:Boolean,cancelable:Boolean)"), + preventDefault : new Definition("function()"), + stopImmediatePropagation : new Definition("function()"), + stopPropagation : new Definition("function()") }, - "?Event:" : { + "function(new:Event):Event" : { $$isBuiltin: true, $$proto : new Definition("Function"), @@ -947,10 +967,10 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], publicId : new Definition("String"), systemId : new Definition("String"), - before : new Definition("?undefined:nodeOrString"), - after : new Definition("?undefined:nodeOrString"), - replace : new Definition("?undefined:nodeOrString"), - remove : new Definition("?undefined:") + before : new Definition("function(nodeOrString:(Node|String))"), + after : new Definition("function(nodeOrString:(Node|String))"), + replace : new Definition("function(nodeOrString:(Node|String))"), + remove : new Definition("function()") }, // see http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#the-history-interface @@ -961,11 +981,11 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], length : new Definition("Number"), state : new Definition("Object"), - go : new Definition("?undefined:delta"), - back : new Definition("?undefined:"), - forward : new Definition("?undefined:"), - pushState : new Definition("?undefined:data,title,url"), - replaceState : new Definition("?undefined:data,title,url") + go : new Definition("function(delta:Number)"), + back : new Definition("function()"), + forward : new Definition("function()"), + pushState : new Definition("function(data:Object,title:String,url:String)"), + replaceState : new Definition("function(data:Object,title:String,url:String)") }, // see http://www.w3.org/TR/dom/#document (complete) @@ -984,26 +1004,23 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], doctype : new Definition("DocumentType"), documentElement : new Definition("Element"), - getElementsByTagName : new Definition("?HTMLCollection:localName"), - getElementsByTagNameNS : new Definition("?HTMLCollection:namespace,localName"), - getElementsByClassName : new Definition("?HTMLCollection:classNames"), - getElementById : new Definition("?Element:elementId"), - createElement : new Definition("?Element:elementId"), - createElementNS : new Definition("?Element:namespace,qualifiedName"), - createDocumentFragment : new Definition("?DocumentFragment:"), - createTextNode : new Definition("?Text:data"), - createComment : new Definition("?Comment:data"), - createProcessingInstruction : new Definition("?ProcessingInstruction:target,data"), - importNode : new Definition("?Node:node,[deep]"), - adoptNode : new Definition("?Node:node"), - createEvent : new Definition("?Event:eventInterfaceName"), - createRange : new Definition("?Range:"), - - createNodeIterator : new Definition("?NodeIterator:root,[whatToShow],[filter]"), - createTreeWalker : new Definition("?TreeWalker:root,[whatToShow],[filter]"), - - prepend : new Definition("?undefined:[nodes]"), - append : new Definition("?undefined:[nodes]") + getElementsByTagName : new Definition("function(localName:String):HTMLCollection"), + getElementsByTagNameNS : new Definition("function(namespace,localName:String):HTMLCollection"), + getElementsByClassName : new Definition("function(classNames:String):HTMLCollection"), + getElementById : new Definition("function(elementId:String):Element"), + createElement : new Definition("function(elementId:String):Element"), + createElementNS : new Definition("function(namespace,qualifiedName:String):Element"), + createDocumentFragment : new Definition("function():DocumentFragment"), + createTextNode : new Definition("function(data):Text"), + createComment : new Definition("function(data):Comment"), + createProcessingInstruction : new Definition("function(target,data):ProcessingInstruction"), + importNode : new Definition("function(node:Node,deep:Boolean=):Node"), + adoptNode : new Definition("function(node:Node):Node"), + createEvent : new Definition("function(eventInterfaceName:String):Event"), + createRange : new Definition("function():Range"), + + createNodeIterator : new Definition("function(root:Node,whatToShow:Object=,filter:Object=):NodeIterator"), + createTreeWalker : new Definition("function(root:Node,whatToShow:Object=,filter:Object=):TreeWalker") }, // see http://www.w3.org/TR/dom/#domimplementation @@ -1011,10 +1028,10 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, $$proto : new Definition("Object"), - createDocumentType : new Definition("?DocumentType:qualifiedName,publicId,systemId"), - createDocument : new Definition("?Document:namespace,qualifiedName,doctype"), - createHTMLDocument : new Definition("?Document:title"), - hasFeature : new Definition("?Boolean:feature") + createDocumentType : new Definition("function(qualifiedName:String,publicId:String,systemId:String):DocumentType"), + createDocument : new Definition("function(namespace:String,qualifiedName:String,doctype:String):Document"), + createHTMLDocument : new Definition("function(title:String):Document"), + hasFeature : new Definition("function(feature:String):Boolean") }, // see http://www.w3.org/TR/dom/#node @@ -1036,23 +1053,23 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], nodeValue : new Definition("String"), textContent : new Definition("String"), - hasChildNodes : new Definition("?Boolean:"), - compareDocumentPosition : new Definition("?Number:other"), - contains : new Definition("?Boolean:other"), - insertBefore : new Definition("?Node:node,child"), - appendChild : new Definition("?Node:node"), - replaceChild : new Definition("?Node:node,child"), - removeChild : new Definition("?Node:node,child"), - normalize : new Definition("?undefined:"), - cloneNode : new Definition("?Node:[deep]"), - isEqualNode : new Definition("?Boolean:node"), - lookupPrefix : new Definition("?String:namespace"), - lookupNamespaceURI : new Definition("?String:prefix"), - isDefaultNamespace : new Definition("?Boolean:namespace") + hasChildNodes : new Definition("function():Boolean"), + compareDocumentPosition : new Definition("function(other:Node):Number"), + contains : new Definition("function(other:Node):Boolean"), + insertBefore : new Definition("function(child:Node):Node"), + appendChild : new Definition("function(node:Node):Node"), + replaceChild : new Definition("function(child:Node):Node"), + removeChild : new Definition("function(child:Node):Node"), + normalize : new Definition("function()"), + cloneNode : new Definition("function(deep:Boolean=):Node"), + isEqualNode : new Definition("function(node:Node):Boolean"), + lookupPrefix : new Definition("function(namespace:String):String"), + lookupNamespaceURI : new Definition("function(prefix:String):String"), + isDefaultNamespace : new Definition("function(namespace:String):Boolean") }, // Constants declared on Node - "?Node:" : { + "function(new:Node):Node" : { $$isBuiltin: true, $$proto : new Definition("Function"), ELEMENT_NODE : new Definition("Number"), @@ -1101,25 +1118,25 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], previousElementSibling : new Definition("Element"), nextElementSibling : new Definition("Element"), - getAttribute : new Definition("?String:name"), - getAttributeNS : new Definition("?String:namespace,localname"), - setAttribute : new Definition("?undefined:name,value"), - setAttributeNS : new Definition("?undefined:namespace,name,value"), - removeAttribute : new Definition("?undefined:name"), - removeAttributeNS : new Definition("?undefined:namespace,localname"), - hasAttribute : new Definition("?Boolean:name"), - hasAttributeNS : new Definition("?Boolean:namespace,localname"), + getAttribute : new Definition("function(name:String):String"), + getAttributeNS : new Definition("function(namespace:String,localname:String):String"), + setAttribute : new Definition("function(name:String,value:Object)"), + setAttributeNS : new Definition("function(namespace:String,name:String,value:Object)"), + removeAttribute : new Definition("function(name:String)"), + removeAttributeNS : new Definition("function(namespace:String,localname:String)"), + hasAttribute : new Definition("function(name:String):Boolean"), + hasAttributeNS : new Definition("function(namespace:String,localname:String):Boolean"), - getElementsByTagName : new Definition("?HTMLCollection:localName"), - getElementsByTagNameNS : new Definition("?HTMLCollection:namespace,localName"), - getElementsByClassName : new Definition("?HTMLCollection:classname"), + getElementsByTagName : new Definition("function(localName:String):HTMLCollection"), + getElementsByTagNameNS : new Definition("function(namespace:String,localName:String):HTMLCollection"), + getElementsByClassName : new Definition("function(classname:String):HTMLCollection"), - prepend : new Definition("?undefined:[nodes]"), - append : new Definition("?undefined:[nodes]"), - before : new Definition("?undefined:[nodes]"), - after : new Definition("?undefined:[nodes]"), - replace : new Definition("?undefined:[nodes]"), - remove : new Definition("?undefined:") + prepend : new Definition("function(nodes:Node...)"), + append : new Definition("function(nodes:Node...)"), + before : new Definition("function(nodes:Node...)"), + after : new Definition("function(nodes:Node...)"), + replace : new Definition("function(nodes:Node...)"), + remove : new Definition("function()") }, // see http://www.w3.org/TR/dom/#attr @@ -1165,9 +1182,9 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, $$proto : new Definition("Object"), - assign : new Definition("?undefined:url"), - replace : new Definition("?undefined:url"), - reload : new Definition("?undefined:"), + assign : new Definition("function(url:String)"), + replace : new Definition("function(url:String)"), + reload : new Definition("function()"), href : new Definition("String"), protocol : new Definition("String"), @@ -1193,18 +1210,18 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], isCollapsed : new Definition("Boolean"), - collapse : new Definition("?undefined:node,offset"), - collapseToStart : new Definition("?undefined:"), - collapseToEnd : new Definition("?undefined:"), + collapse : new Definition("function(node:Node,offset:Number)"), + collapseToStart : new Definition("function()"), + collapseToEnd : new Definition("function()"), - extend : new Definition("?undefined:node,offset"), + extend : new Definition("function(node:Node,offset:Number)"), - selectAllChildren : new Definition("?undefined:node"), - deleteFromDocument : new Definition("?undefined:"), - getRangeAt : new Definition("?Range:index"), - addRange : new Definition("?undefined:range"), - removeRange : new Definition("?undefined:range"), - removeAllRanges : new Definition("?undefined:") + selectAllChildren : new Definition("function(node:Node)"), + deleteFromDocument : new Definition("function()"), + getRangeAt : new Definition("function(index:Number):Range"), + addRange : new Definition("function(range:Range)"), + removeRange : new Definition("function(range:Range)"), + removeAllRanges : new Definition("function()") }, // see http://www.w3.org/TR/html5/the-html-element.html#the-html-element @@ -1238,8 +1255,8 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, $$proto : new Definition("Object"), length : new Definition("Number"), - item : new Definition("?Element:index"), - namedItem : new Definition("?Element:name") + item : new Definition("function(index:Number):Element"), + namedItem : new Definition("function(name:String):Element") }, // incomplete @@ -1259,8 +1276,8 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], $$isBuiltin: true, $$proto : new Definition("Node"), - prepend : new Definition("?undefined:[nodes]"), - append : new Definition("?undefined:[nodes]") + prepend : new Definition("function(nodes:Node...)"), + append : new Definition("function(nodes:Node...)") }, // incomplete @@ -1293,35 +1310,35 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], collapsed : new Definition("Boolean"), commonAncestorContainer : new Definition("Node"), - setStart : new Definition("?undefined:refNode,offset"), - setEnd : new Definition("?undefined:refNode,offset"), - setStartBefore : new Definition("?undefined:refNode"), - setStartAfter : new Definition("?undefined:refNode"), - setEndBefore : new Definition("?undefined:refNode"), - setEndAfter : new Definition("?undefined:refNode"), - collapse : new Definition("?undefined:toStart"), - selectNode : new Definition("?undefined:refNode"), - selectNodeContents : new Definition("?undefined:refNode"), + setStart : new Definition("function(refNode:Node,offset:Number)"), + setEnd : new Definition("function(refNode:Node,offset:Number)"), + setStartBefore : new Definition("function(refNode:Node)"), + setStartAfter : new Definition("function(refNode:Node)"), + setEndBefore : new Definition("function(refNode:Node)"), + setEndAfter : new Definition("function(refNode:Node)"), + collapse : new Definition("function(toStart:Node)"), + selectNode : new Definition("function(refNode:Node)"), + selectNodeContents : new Definition("function(refNode:Node)"), - compareBoundaryPoints : new Definition("?Number:how,sourceRange"), + compareBoundaryPoints : new Definition("function(how:Object,sourceRange:Object):Number"), - deleteContents : new Definition("?undefined:"), - extractContents : new Definition("?DocumentFragment:"), - cloneContents : new Definition("?DocumentFragment:"), - insertNode : new Definition("?undefined:node"), - surroundContents : new Definition("?undefined:nodeParent"), + deleteContents : new Definition("function()"), + extractContents : new Definition("function():DocumentFragment"), + cloneContents : new Definition("function():DocumentFragment"), + insertNode : new Definition("function(node:Node)"), + surroundContents : new Definition("function(nodeParent:Node)"), - cloneRange : new Definition("?Range:"), - detach : new Definition("?undefined:"), + cloneRange : new Definition("function():Range"), + detach : new Definition("function()"), - isPointInRange : new Definition("?Boolean:node,offset"), - comparePoint : new Definition("?Number:node,offset"), + isPointInRange : new Definition("function(node:Node,offset:Number):Boolean"), + comparePoint : new Definition("function(node:Node,offset:Number):Number"), - intersectsNode : new Definition("?Boolean:node") + intersectsNode : new Definition("function(node:Node):Boolean") }, - "?Range:" : { + "funciton():Range" : { $$isBuiltin: true, START_TO_START : new Definition("Number"), START_TO_END : new Definition("Number"), @@ -1337,232 +1354,12 @@ define(["plugins/esprima/proposalUtils", "scriptedLogger", "doctrine/doctrine"], length : new Definition("Number"), - item : new Definition("?String:index"), - contains : new Definition("?Boolean:token"), - add : new Definition("?undefined:token"), - remove : new Definition("?undefined:token"), - toggle : new Definition("?Boolean:token") + item : new Definition("function(index:Number):String"), + contains : new Definition("function(token:String):Boolean"), + add : new Definition("function(token:String)"), + remove : new Definition("function(token:String)"), + toggle : new Definition("function(token:String):Boolean") } - -// HTML constructors -// http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-33759296 -/* -HTMLVideoElement -HTMLAppletElement -HTMLCollection -HTMLOutputElement -HTMLQuoteElement -HTMLFrameElement -HTMLTableSectionElement -HTMLModElement -HTMLTableCaptionElement -HTMLCanvasElement -HTMLOptGroupElement -HTMLLinkElement -HTMLImageElement -HTMLBRElement -HTMLProgressElement -HTMLParagraphElement -HTMLScriptElement -HTMLOListElement -HTMLTableCellElement -HTMLTextAreaElement -HTMLUListElement -HTMLMarqueeElement -HTMLFieldSetElement -HTMLLIElement -HTMLTableElement -HTMLButtonElement -HTMLAnchorElement -HTMLAllCollection -HTMLMetaElement -HTMLLabelElement -HTMLMenuElement -HTMLMapElement -HTMLParamElement -HTMLTableColElement -HTMLTableRowElement -HTMLDocument -HTMLSpanElement -HTMLBaseFontElement -HTMLEmbedElement -HTMLDivElement -HTMLBaseElement -HTMLHeadElement -HTMLTitleElement -HTMLDirectoryElement -HTMLUnknownElement -HTMLHtmlElement -HTMLHRElement -HTMLInputElement -HTMLDataListElement -HTMLStyleElement -HTMLSourceElement -HTMLOptionElement -HTMLFontElement -HTMLElement -HTMLBodyElement -HTMLFormElement -HTMLHeadingElement -HTMLSelectElement -HTMLPreElement -HTMLIFrameElement -HTMLMediaElement -HTMLLegendElement -HTMLObjectElement -HTMLDListElement -HTMLAudioElement -HTMLAreaElement -HTMLFrameSetElement -HTMLMeterElement -HTMLKeygenElement -*/ -// SVG constructors -// http://www.w3.org/TR/SVG11/struct.html#NewDocument -/* -SVGScriptElement -SVGCircleElement -SVGTitleElement -SVGFEDistantLightElement -SVGGElement -SVGAnimatedString -SVGFEConvolveMatrixElement -SVGTransform -SVGAltGlyphDefElement -SVGAnimatedLengthList -SVGCursorElement -SVGAnimateColorElement -SVGPathSegCurvetoQuadraticSmoothAbs -SVGDefsElement -SVGAnimateElement -SVGPathSegLinetoVerticalAbs -SVGAnimatedBoolean -SVGVKernElement -SVGElement -SVGEllipseElement -SVGForeignObjectElement -SVGColor -SVGFEPointLightElement -SVGMissingGlyphElement -SVGPathSegCurvetoCubicRel -SVGPathSegMovetoRel -SVGFEDisplacementMapElement -SVGPathSegArcRel -SVGAElement -SVGFETurbulenceElement -SVGMetadataElement -SVGTextElement -SVGElementInstanceList -SVGFEBlendElement -SVGTSpanElement -SVGFESpecularLightingElement -SVGPathSegArcAbs -SVGZoomEvent -SVGSVGElement -SVGPathSegLinetoHorizontalRel -SVGFEOffsetElement -SVGAltGlyphItemElement -SVGPaint -SVGException -SVGLengthList -SVGFontFaceUriElement -SVGPathSegLinetoAbs -SVGMarkerElement -SVGStyleElement -SVGAnimatedRect -SVGFilterElement -SVGFEFuncGElement -SVGAnimatedNumberList -SVGPathSegLinetoHorizontalAbs -SVGZoomAndPan -SVGFEImageElement -SVGAnimatedPreserveAspectRatio -SVGPathSegLinetoVerticalRel -SVGAltGlyphElement -SVGSetElement -SVGPathSegCurvetoCubicAbs -SVGRect -SVGPathSegClosePath -SVGFEGaussianBlurElement -SVGAngle -SVGViewElement -SVGMatrix -SVGPreserveAspectRatio -SVGTextPathElement -SVGRenderingIntent -SVGFEFloodElement -SVGAnimateTransformElement -SVGFEMergeNodeElement -SVGPoint -SVGTRefElement -SVGFESpotLightElement -SVGLinearGradientElement -SVGPathSegList -SVGTextContentElement -SVGPointList -SVGSwitchElement -SVGPathSegCurvetoQuadraticSmoothRel -SVGFontFaceElement -SVGLineElement -SVGLength -SVGFECompositeElement -SVGDocument -SVGGlyphElement -SVGFontFaceNameElement -SVGFEMergeElement -SVGPathSegCurvetoCubicSmoothRel -SVGAnimatedInteger -SVGAnimatedNumber -SVGAnimateMotionElement -SVGStopElement -SVGUseElement -SVGFontElement -SVGGradientElement -SVGPathSegLinetoRel -SVGPathSegCurvetoQuadraticAbs -SVGAnimatedEnumeration -SVGNumber -SVGTextPositioningElement -SVGComponentTransferFunctionElement -SVGFEDiffuseLightingElement -SVGStringList -SVGRadialGradientElement -SVGPathElement -SVGMaskElement -SVGFEFuncBElement -SVGPolygonElement -SVGGlyphRefElement -SVGFEColorMatrixElement -SVGElementInstance -SVGFontFaceSrcElement -SVGAnimatedAngle -SVGFontFaceFormatElement -SVGHKernElement -SVGPolylineElement -SVGAnimatedTransformList -SVGFEFuncRElement -SVGDescElement -SVGAnimatedLength -SVGSymbolElement -SVGNumberList -SVGViewSpec -SVGPathSegCurvetoCubicSmoothAbs -SVGMPathElement -SVGPatternElement -SVGPathSegCurvetoQuadraticRel -SVGFEComponentTransferElement -SVGRectElement -SVGTransformList -SVGFETileElement -SVGFEDropShadowElement -SVGUnitTypes -SVGPathSegMovetoAbs -SVGClipPathElement -SVGFEMorphologyElement -SVGImageElement -SVGPathSeg -SVGFEFuncAElement -*/ }; var protoLength = "~proto".length; @@ -1577,92 +1374,73 @@ SVGFEFuncAElement // type parsing - isArrayType : function(typeName) { - return typeName.substr(0, "Array.<".length) === "Array.<"; + isArrayType : function(typeObj) { + return typeObj.type === 'ArrayType' || typeObj.type === 'TypeApplication'; }, - isFunctionOrConstructor : function(typeName) { - return typeName.charAt(0) === "?" || typeName.charAt(0) === "*"; + isFunctionOrConstructor : function(typeObj) { + return typeObj.type === 'FunctionType'; }, - isPrototype : function(typeName) { - return typeName.charAt(0) === "*" && typeName.substr( - protoLength, protoLength) === "~proto"; - }, - - findReturnTypeEnd : function(fnType) { - if (this.isFunctionOrConstructor(fnType)) { - // walk the string and for every ? or *, find the corresponding :, until we reach the - // : for the first ? or * - var depth = 1; - var index = 1; - var len = fnType.length; - - while (index < len) { - if (this.isFunctionOrConstructor(fnType.charAt(index))) { - depth++; - } else if (fnType.charAt(index) === ":") { - depth--; - } - - if (depth === 0) { - // found it - return index; - } - - index++; - } - } - return -1; - }, - - removeParameters : function(fnType) { - var index = this.findReturnTypeEnd(fnType); - if (index >= 0) { - return fnType.substring(0,index+1); - } - // didn't find a matching ":" (ie- invalid type) - // or just not a function type - return fnType; + isPrototypeName : function(typeName) { + return typeName.substr( - protoLength, protoLength) === "~proto"; }, /** - * if the type passed in is a function type, extracts the return type - * otherwise returns as is + * returns a parameterized array type with the given type parameter */ - extractReturnType : function(fnType) { - var index = this.findReturnTypeEnd(fnType); - if (index >= 0) { - return fnType.substring(1,index); + parameterizeArray : function(parameterTypeObj) { + return { + type: 'ArrayType', + elements: [parameterTypeObj] + }; + }, + + createFunctionType : function(params, result, isConstructor) { + var functionTypeObj = { + type: 'FunctionType', + params: params, + result: result + }; + if (isConstructor) { + // TODO should we also do 'this'? + functionTypeObj['new'] = result; } - // didn't find a matching ":" (ie- invalid type) - // or just not a function type - return fnType; - }, - /** - * returns a parameterized array type with the given type parameter - */ - parameterizeArray : function(parameterType) { - return "Array.<" + parameterType + ">"; + return functionTypeObj; }, /** * If this is a parameterized array type, then extracts the type, * Otherwise object */ - extractArrayParameterType : function (arrayType) { - if (arrayType.substr(0, "Array.<".length) === "Array.<" && arrayType.substr(-1, 1) === ">") { - return arrayType.substring("Array.<".length, arrayType.length -1); + extractArrayParameterType : function(arrayObj) { + if ((arrayObj.type === 'ArrayType' || arrayObj.type === 'TypeApplication')) { + if (arrayObj.elements.length > 0) { + return arrayObj.elements[0]; + } else { + return THE_UNKNOWN_TYPE; + } } else { - return "Object"; + // not an array type + return arrayObj; } + + }, + + extractReturnType : function(fnType) { + return fnType.result || fnType; }, + // TODO should we just return a typeObj here??? parseJSDocComment : function(docComment) { var result = { }; result.params = {}; if (docComment) { var commentText = docComment.value; + if (!commentText) { + return result; + } try { var rawresult = doctrine.parse("/*" + commentText + "*/", {unwrap : true, tags : ['param', 'type', 'return']}); // transform result into something more manageable @@ -1692,149 +1470,236 @@ SVGFEFuncAElement } catch (e) { scriptedLogger.error(e.message, "CONTENT_ASSIST"); scriptedLogger.error(e.stack, "CONTENT_ASSIST"); + scriptedLogger.error("Error parsing doc comment:\n" + (docComment && docComment.value), + "CONTENT_ASSIST"); } } return result; }, + /** - * Best effort to recursively convert from a jsdoc type specification to a scripted type name. - * - * See here: https://developers.google.com/closure/compiler/docs/js-for-compiler - * should handle: - NullableLiteral - AllLiteral - NullLiteral - UndefinedLiteral - VoidLiteral - UnionType - ArrayType - RecordType - FieldType - FunctionType - ParameterType - RestType - NonNullableType - OptionalType - NullableType - NameExpression - TypeApplication - * @return {String} if the type is found, then return string, otherwise null + * takes this jsdoc type and recursively splits out all record types into their own type + * also converts unknown name types into Objects + * @see https://developers.google.com/closure/compiler/docs/js-for-compiler */ - convertJsDocType : function(jsdocType, env) { - var allTypes = env.getAllTypes(); + convertJsDocType : function(jsdocType, env, doCombine, depth) { + if (typeof depth !== 'number') { + depth = 0; + } if (!jsdocType) { - return null; + return THE_UNKNOWN_TYPE; } - var i; + var self = this; + var name = jsdocType.name; + var allTypes = env.getAllTypes(); switch (jsdocType.type) { case 'NullableLiteral': case 'AllLiteral': case 'NullLiteral': - return "Object"; - case 'UndefinedLiteral': case 'VoidLiteral': - return "undefined"; + return { + type: jsdocType.type + }; case 'UnionType': - // TODO no direct handling of union types - // for now, just return the first of the union - if (jsdocType.elements && jsdocType.elements.length > 0) { - return this.convertJsDocType(jsdocType.elements[0], env); - } - return "Object"; + return { + type: jsdocType.type, + elements: jsdocType.elements.map(function(elt) { + return self.convertJsDocType(elt, env, doCombine, depth+1); + }) + }; case 'RestType': - return "Array.<" + this.convertJsDocType(jsdocType.expression, env) + ">"; + return { + type: jsdocType.type, + expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1) + }; + case 'ArrayType': - if (jsdocType.elements && jsdocType.elements.length > 0) { - // assume array type is type of first element, not correct, but close enough - return "Array.<" + this.convertJsDocType(jsdocType.elements[0], env) + ">"; - } - return "Array"; + return { + type: jsdocType.type, + elements: jsdocType.elements.map(function(elt) { + return self.convertJsDocType(elt, env, doCombine, depth+1); + }) + }; case 'FunctionType': - var ret = this.convertJsDocType(jsdocType.result, env); - if (!ret) { - ret = "Object"; - } - var params = []; - if (jsdocType.params) { - for (i = 0; i < jsdocType.params.length; i++) { - // this means that if no name is used, then the type name is used (if a simple type) - var param = jsdocType.params[i].name; - if (!param) { - param = 'arg'+i; - } - var paramType = ""; - if (jsdocType.params[i].expression) { - paramType = "/" + this.convertJsDocType(jsdocType.params[i].expression, env); - } - params.push(param + paramType); - } + var fnType = { + type: jsdocType.type, + result: self.convertJsDocType(jsdocType.result, env, doCombine, depth+1), + params: jsdocType.params.map(function(elt) { + return self.convertJsDocType(elt, env, doCombine, depth+1); + }) + }; + + if (jsdocType['new']) { + fnType['new'] = self.convertJsDocType(jsdocType['new'], env, doCombine, depth+1); } - // TODO FIXADE must also handle @constructor - var funcConstr; - if (jsdocType['new'] && jsdocType['this']) { - // this is actually a constructor - var maybeRet = this.convertJsDocType(jsdocType['this'], env); - if (maybeRet) { - ret = maybeRet; - } - funcConstr = "*"; - } else { - funcConstr = "?"; + + if (jsdocType['this']) { + fnType['this'] = self.convertJsDocType(jsdocType['this'], env, doCombine, depth+1); } - return funcConstr + ret + ":" + params.join(','); + + return fnType; case 'TypeApplication': - var expr = this.convertJsDocType(jsdocType.expression, env); - if (expr === "Array" && jsdocType.applications && jsdocType.applications.length > 0) { - // only parameterize arrays not handling objects yet - var typeParam = this.convertJsDocType(jsdocType.applications[0], env) || "Object"; - return "Array.<" + typeParam + ">"; + var typeApp = { + type: jsdocType.type, + expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1), + + }; + if (jsdocType.applications) { + typeApp.applications = jsdocType.applications.map(function(elt) { + return self.convertJsDocType(elt, env, doCombine, depth+1); + }); } - return expr; + return typeApp; + case 'ParameterType': + return { + type: jsdocType.type, + name: name, + expression: jsdocType.expression ? + self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1) : + null + }; + case 'NonNullableType': case 'OptionalType': case 'NullableType': - return this.convertJsDocType(jsdocType.expression, env); + return { + type: jsdocType.type, + expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1) + }; case 'NameExpression': - var name = jsdocType.name; - name = name.trim(); - if (allTypes[name]) { - return name; + if (doCombine && env.isSyntheticName(name)) { + // Must mush together all properties for this synthetic type + var origFields = allTypes[name]; + // must combine a record type + var newFields = []; + Object.keys(origFields).forEach(function(key) { + if (key === '$$proto') { + // maybe should traverse the prototype + return; + } + var prop = origFields[key]; + var fieldType = depth > 0 && (prop.typeObj.type === 'NameExpression' && env.isSyntheticName(prop.typeObj.name)) ? + { type : 'NameExpression', name : JUST_DOTS } : + self.convertJsDocType(prop.typeObj, env, doCombine, depth+1); + newFields.push({ + type: 'FieldType', + key: key, + value: fieldType + }); + }); + + + return { + type: 'RecordType', + fields: newFields + }; } else { - var capType = name[0].toUpperCase() + name.substring(1); - if (allTypes[capType]) { - return capType; + if (allTypes[name]) { + return { type: 'NameExpression', name: name }; + } else { + var capType = name[0].toUpperCase() + name.substring(1); + if (allTypes[capType]) { + return { type: 'NameExpression', name: capType }; + } } } - return null; + return THE_UNKNOWN_TYPE; + + case 'FieldType': + return { + type: jsdocType.type, + key: jsdocType.key, + value: self.convertJsDocType(jsdocType.value, env, doCombine, depth+1) + }; + case 'RecordType': - var fields = { }; - for (i = 0; i < jsdocType.fields.length; i++) { - var field = jsdocType.fields[i]; - var fieldType = this.convertJsDocType(field, env); - fields[field.key] = fieldType ? fieldType : "Object"; - } - // create a new type to store the record - var obj = env.newFleetingObject(); - for (var prop in fields) { - if (fields.hasOwnProperty(prop)) { - // add the variable to the new object, which happens to be the top-level scope - env.addVariable(prop, obj, fields[prop]); + if (doCombine) { + // when we are combining, do not do anything special for record types + return { + type: jsdocType.type, + params: jsdocType.fields.map(function(elt) { + return self.convertJsDocType(elt, env, doCombine, depth+1); + }) + }; + } else { + // here's where it gets interesting + // create a synthetic type in the env and then + // create a property in the env type for each record property + var fields = { }; + for (var i = 0; i < jsdocType.fields.length; i++) { + var field = jsdocType.fields[i]; + var convertedField = self.convertJsDocType(field, env, doCombine, depth+1); + fields[convertedField.key] = convertedField.value; } + // create a new type to store the record + var obj = env.newFleetingObject(); + for (var prop in fields) { + if (fields.hasOwnProperty(prop)) { + // add the variable to the new object, which happens to be the top-level scope + env.addVariable(prop, obj.name, fields[prop]); + } + } + return obj; } - return obj; + } + return THE_UNKNOWN_TYPE; + }, + + createNameType : createNameType, + + createParamType : function(name, typeObj) { + return { + type: 'ParameterType', + name: name, + expression: typeObj + }; + }, + + convertToSimpleTypeName : function(typeObj) { + switch (typeObj.type) { + case 'NullableLiteral': + case 'AllLiteral': + case 'NullLiteral': + return "Object"; + + case 'UndefinedLiteral': + case 'VoidLiteral': + return "undefined"; + + case 'NameExpression': + return typeObj.name; + + case 'TypeApplication': + case 'ArrayExpressopm': + return "Array"; + + case 'FunctionType': + return "Function"; + + case 'UnionType': + return typeObj.expressions[0]; + + case 'RecordType': + return "Object"; + case 'FieldType': - return this.convertJsDocType(jsdocType.value, env); + return this.convertToSimpleTypeName(typeObj.value); + + case 'NonNullableType': + case 'OptionalType': + case 'NullableType': + case 'ParameterType': + return this.convertToSimpleTypeName(typeObj.expression); } - return null; }, // type styling @@ -1848,10 +1713,25 @@ SVGFEFuncAElement return useHtml ? "" + text + "": text; }, + + /** + * creates a human readable type name from the name given + */ + createReadableType : function(typeObj, env, useFunctionSig, depth, useHtml) { + if (useFunctionSig) { + typeObj = this.convertJsDocType(typeObj, env, true); + var res = doctrine.stringify(typeObj); + res = res.replace(JUST_DOTS, "{...}", 'g'); + return res; + } else { + typeObj = this.extractReturnType(typeObj); + return this.createReadableType(typeObj, env, true, depth, useHtml); + } + }, /** * creates a human readable type name from the name given */ - createReadableType : function(typeName, env, useFunctionSig, depth, useHtml) { + createReadableTypeOLD : function(typeName, env, useFunctionSig, depth, useHtml) { depth = depth || 0; var first = typeName.charAt(0); if (first === "?" || first === "*") { @@ -1908,7 +1788,7 @@ SVGFEFuncAElement if (type.hasOwnProperty(val) && val !== "$$proto") { var name; // don't show inner objects - name = this.createReadableType(type[val].typeName, env, true, depth + 1, useHtml); + name = this.createReadableType(type[val].typeObj, env, true, depth + 1, useHtml); props.push((useHtml ? "
    " + proposalUtils.repeatChar("    ", depth+1) : "" ) + this.styleAsProperty(val, useHtml) + ":" + name); } @@ -1926,6 +1806,14 @@ SVGFEFuncAElement } else { return this.styleAsType(typeName, useHtml); } - } + }, + + OBJECT_TYPE: THE_UNKNOWN_TYPE, + UNDEFINED_TYPE: createNameType("undefined"), + NUMBER_TYPE: createNameType("Number"), + BOOLEAN_TYPE: createNameType("Boolean"), + STRING_TYPE: createNameType("String"), + ARRAY_TYPE: createNameType("Array"), + FUNCTION_TYPE: createNameType("Function") }; }); \ No newline at end of file diff --git a/play-area/app.js b/play-area/app.js index f8393856..3c1b8ae5 100644 --- a/play-area/app.js +++ b/play-area/app.js @@ -2,8 +2,8 @@ require(['car'], function(Car) { var c = new Car('ford'); c.show(); - + var x = new Car(''); - - -}); \ No newline at end of file + + console.log(); +}); \ No newline at end of file diff --git a/play-area/foo.js b/play-area/foo.js index 14c9a773..36331996 100644 --- a/play-area/foo.js +++ b/play-area/foo.js @@ -1,8 +1,6 @@ /** - * @param {String} path + * @param {String} path * @return String */ -function parseFile(path) { } -var x = parseFile(''); - -1 \ No newline at end of file +function parseFile(path) { } +var x = parseFile(''); \ No newline at end of file diff --git a/tests/client/esprima/esprimaJsContentAssistTests.js b/tests/client/esprima/esprimaJsContentAssistTests.js index 03bce6e1..466e264e 100644 --- a/tests/client/esprima/esprimaJsContentAssistTests.js +++ b/tests/client/esprima/esprimaJsContentAssistTests.js @@ -236,7 +236,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["NaN", "NaN : Number"], ["this", "this : Global"], ["undefined", "undefined : undefined"], - ["zzz", "zzz : { }"], + ["zzz", "zzz : {}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -273,9 +273,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["NaN", "NaN : Number"], ["this", "this : Global"], ["undefined", "undefined : undefined"], - ["xxx", "xxx : { }"], - ["yyy", "yyy : { }"], - ["zzz", "zzz : { }"], + ["xxx", "xxx : {}"], + ["yyy", "yyy : {}"], + ["zzz", "zzz : {}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -289,8 +289,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test multi var content assist 2"] = function() { var results = computeContentAssist("var zzz;\nvar zxxx, xxx, yyy;\nz","z"); testProposals(results, [ - ["zxxx", "zxxx : { }"], - ["zzz", "zzz : { }"] + ["zxxx", "zxxx : {}"], + ["zzz", "zzz : {}"] ]); }; tests["test single function content assist"] = function() { @@ -373,7 +373,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "var coo = 9; var other = function(coo) { c/**/ }", "c"); testProposals(results, [ - ["coo", "coo : { }"] + ["coo", "coo : {}"] ]); }; @@ -406,7 +406,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "var coo;\nfunction other(a, b, c) {\nfunction inner() { var coo2; }\nco/**/}", "co"); testProposals(results, [ - ["coo", "coo : { }"] + ["coo", "coo : {}"] ]); }; tests["test scopes 2"] = function() { @@ -414,7 +414,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist("var foo;\n" + "var foo = 1;\nfunction other(a, b, c) {\nfunction inner() { foo2 = \"\"; }\nfoo.toF/**/}", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test multi function content assist 2"] = function() { @@ -426,10 +426,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test in function 1"] = function() { var results = computeContentAssist("function fun(a, b, c) {}\nfunction other(a, b, c) {/**/}", ""); testProposals(results, [ - ["a", "a : { }"], + ["a", "a : {}"], ["arguments", "arguments : Arguments"], - ["b", "b : { }"], - ["c", "c : { }"], + ["b", "b : {}"], + ["c", "c : {}"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"], ["Boolean([val])", "Boolean([val]) : Boolean"], @@ -469,10 +469,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test in function 2"] = function() { var results = computeContentAssist("function fun(a, b, c) {}\nfunction other(a, b, c) {\n/**/nuthin}", ""); testProposals(results, [ - ["a", "a : { }"], + ["a", "a : {}"], ["arguments", "arguments : Arguments"], - ["b", "b : { }"], - ["c", "c : { }"], + ["b", "b : {}"], + ["c", "c : {}"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"], ["Boolean([val])", "Boolean([val]) : Boolean"], @@ -497,7 +497,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["JSON", "JSON : JSON"], ["Math", "Math : Math"], ["NaN", "NaN : Number"], - ["nuthin", "nuthin : { }"], + ["nuthin", "nuthin : {}"], ["this", "this : Global"], ["undefined", "undefined : undefined"], ["", "---------------------------------"], @@ -520,8 +520,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test in function 4"] = function() { var results = computeContentAssist("function fun(a, b, c) {}\nfunction other(aa, ab, c) {a/**/}", "a"); testProposals(results, [ - ["aa", "aa : { }"], - ["ab", "ab : { }"], + ["aa", "aa : {}"], + ["ab", "ab : {}"], ["arguments", "arguments : Arguments"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"] @@ -530,11 +530,11 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test in function 5"] = function() { var results = computeContentAssist("function fun(a, b, c) {}\nfunction other(aa, ab, c) {var abb;\na/**/\nvar aaa}", "a"); testProposals(results, [ - ["aaa", "aaa : { }"], - ["abb", "abb : { }"], + ["aaa", "aaa : {}"], + ["abb", "abb : {}"], ["", "---------------------------------"], - ["aa", "aa : { }"], - ["ab", "ab : { }"], + ["aa", "aa : {}"], + ["ab", "ab : {}"], ["arguments", "arguments : Arguments"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"] @@ -546,14 +546,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "function other(aa, ab, c) {\n"+ "var abb;\na/**/\nvar aaa\n}\n}", "a"); testProposals(results, [ - ["aaa", "aaa : { }"], - ["abb", "abb : { }"], + ["aaa", "aaa : {}"], + ["abb", "abb : {}"], ["", "---------------------------------"], - ["aa", "aa : { }"], - ["ab", "ab : { }"], + ["aa", "aa : {}"], + ["ab", "ab : {}"], ["arguments", "arguments : Arguments"], ["", "---------------------------------"], - ["a", "a : { }"], + ["a", "a : {}"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"] ]); @@ -565,10 +565,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "function other(aa, ab, ac) {\n"+ "var abb;\na\nvar aaa\n}\n}"); testProposals(results, [ - ["a", "a : { }"], + ["a", "a : {}"], ["arguments", "arguments : Arguments"], - ["b", "b : { }"], - ["c", "c : { }"], + ["b", "b : {}"], + ["c", "c : {}"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"], ["Boolean([val])", "Boolean([val]) : Boolean"], @@ -614,10 +614,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri testProposals(results, [ ["other(aa, ab, ac)", "other(aa, ab, ac) : undefined"], ["", "---------------------------------"], - ["a", "a : { }"], + ["a", "a : {}"], ["arguments", "arguments : Arguments"], - ["b", "b : { }"], - ["c", "c : { }"], + ["b", "b : {}"], + ["c", "c : {}"], ["", "---------------------------------"], ["Array([val])", "Array([val]) : Array"], ["Boolean([val])", "Boolean([val]) : Boolean"], @@ -688,9 +688,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test Number inferencing with Variable"] = function() { var results = computeContentAssist("var t = 1\nt.to", "to"); testProposals(results, [ - ["toExponential(digits)", "toExponential(digits) : Number"], - ["toFixed(digits)", "toFixed(digits) : Number"], - ["toPrecision(digits)", "toPrecision(digits) : Number"], + ["toExponential(digits)", "toExponential(digits) : String"], + ["toFixed(digits)", "toFixed(digits) : String"], + ["toPrecision(digits)", "toPrecision(digits) : String"], ["", "---------------------------------"], ["toLocaleString()", "toLocaleString() : String"], ["toString()", "toString() : String"] @@ -709,25 +709,25 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test Data flow inferencing 1"] = function() { var results = computeContentAssist("var ttt = 9\nttt.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test Data flow inferencing 2"] = function() { var results = computeContentAssist("ttt = 9\nttt.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test Data flow inferencing 3"] = function() { var results = computeContentAssist("var ttt = \"\"\nttt = 9\nttt.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test Data flow inferencing 4"] = function() { var results = computeContentAssist("var name = toString(property.key.value);\nname.co", "co"); testProposals(results, [ - ["concat(array)", "concat(array) : String"] + ["concat(str)", "concat(str) : String"] ]); }; @@ -776,13 +776,13 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test this reference 1"] = function() { var results = computeContentAssist("var xxxx;\nthis.x", "x"); testProposals(results, [ - ["xxxx", "xxxx : { }"] + ["xxxx", "xxxx : {}"] ]); }; tests["test binary expression 1"] = function() { var results = computeContentAssist("(1+3).toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -823,19 +823,19 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test while loop 1"] = function() { var results = computeContentAssist("var iii;\nwhile(ii/**/ === null) {\n}", "ii"); testProposals(results, [ - ["iii", "iii : { }"] + ["iii", "iii : {}"] ]); }; tests["test while loop 2"] = function() { var results = computeContentAssist("var iii;\nwhile(this.ii/**/ === null) {\n}", "ii"); testProposals(results, [ - ["iii", "iii : { }"] + ["iii", "iii : {}"] ]); }; tests["test while loop 3"] = function() { var results = computeContentAssist("var iii;\nwhile(iii === null) {this.ii/**/\n}", "ii"); testProposals(results, [ - ["iii", "iii : { }"] + ["iii", "iii : {}"] ]); }; tests["test catch clause 1"] = function() { @@ -862,7 +862,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // should infer that we are referring to the globally defined xxx, not the param var results = computeContentAssist("var xxx = 9;\nfunction fff(xxx) { this.xxx.toF/**/}", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -900,7 +900,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test JSON 4"] = function() { var results = computeContentAssist("this.JSON.st", "st"); testProposals(results, [ - ["stringify(obj)", "stringify(obj) : String"] + ["stringify(json)", "stringify(json) : String"] ]); }; tests["test multi-dot inferencing 1"] = function() { @@ -914,7 +914,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "var zz = {};\nzz.zz = zz;\nzz.zz.zz.z", "z"); testProposals(results, [ - ["zz", "zz : { zz:{ zz:{...} } }"] + ["zz", "zz : {zz:{zz:{...}}}"] ]); }; tests["test multi-dot inferencing 3"] = function() { @@ -928,7 +928,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "var x = { yy : { } };\nx.yy.zz = 1;\nx.yy.zz.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test constructor 1"] = function() { @@ -953,7 +953,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new Fun();\n" + "y.xxx.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test constructor 3"] = function() { @@ -962,7 +962,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new Fun();\n" + "y.uuu.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -972,7 +972,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new Fun();\n" + "y.uuu.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -982,7 +982,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new x.Fun();\n" + "y.uuu.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -991,7 +991,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = { Fun : function () { this.xxx = 9; this.uuu = this.xxx; } }\n" + "var y = new x.Fun().uuu.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1002,7 +1002,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new x.Fun();\n" + "y.uuu.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1013,7 +1013,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new x.Fun();\n" + "y.uuu.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1064,9 +1064,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["Math", "Math : Math"], ["NaN", "NaN : Number"], ["this", "this : Global"], - ["ttt", "ttt : { }"], + ["ttt", "ttt : {}"], ["undefined", "undefined : undefined"], - ["uuu", "uuu : { }"], + ["uuu", "uuu : {}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -1103,9 +1103,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["Math", "Math : Math"], ["NaN", "NaN : Number"], ["this", "this : Global"], - ["ttt", "ttt : { }"], + ["ttt", "ttt : {}"], ["undefined", "undefined : undefined"], - ["uuu", "uuu : { }"], + ["uuu", "uuu : {}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -1142,9 +1142,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["Math", "Math : Math"], ["NaN", "NaN : Number"], ["this", "this : Global"], - ["ttt", "ttt : { }"], + ["ttt", "ttt : {}"], ["undefined", "undefined : undefined"], - ["uuu", "uuu : { }"], + ["uuu", "uuu : {}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -1181,7 +1181,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // "var y = new obj.Fun();\n" + // "y.uuu.toF", "toF"); // testProposals(results, [ -// ["toFixed(digits)", "toFixed(digits) : Number"] +// ["toFixed(digits)", "toFixed(digits) : String"] // ]); // }; tests["test constructor 6"] = function() { @@ -1191,7 +1191,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var y = new Fun();\n" + "y.uuu.toF/**/}", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1201,7 +1201,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var ttt = { xxx : { yyy : { zzz : 1} } };\n" + "ttt.xxx.y", "y"); testProposals(results, [ - ["yyy", "yyy : { zzz:Number }"] + ["yyy", "yyy : {zzz:Number}"] ]); }; tests["test nested object expressions 2"] = function() { @@ -1217,7 +1217,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var ttt = { xxx : { yyy : { zzz : 1} } };\n" + "ttt.xxx.yyy.zzz.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1274,14 +1274,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test globals 1"] = function() { var results = computeContentAssist("/*global faaa */\nfa", "fa"); testProposals(results, [ - ["faaa", "faaa : { }"] + ["faaa", "faaa : {}"] ]); }; tests["test globals 2"] = function() { var results = computeContentAssist("/*global \t\n faaa \t\t\n faaa2 */\nfa", "fa"); testProposals(results, [ - ["faaa", "faaa : { }"], - ["faaa2", "faaa2 : { }"] + ["faaa", "faaa : {}"], + ["faaa2", "faaa2 : {}"] ]); }; tests["test globals 3"] = function() { @@ -1293,16 +1293,16 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test globals 4"] = function() { var results = computeContentAssist("/*global \t\n faaa:true \t\t\n faaa2:false */\nfa", "fa"); testProposals(results, [ - ["faaa", "faaa : { }"], - ["faaa2", "faaa2 : { }"] + ["faaa", "faaa : {}"], + ["faaa2", "faaa2 : {}"] ]); }; tests["test globals 5"] = function() { var results = computeContentAssist("/*global \t\n faaa:true, \t\t\n faaa2:false, */\nfa", "fa"); testProposals(results, [ - ["faaa", "faaa : { }"], - ["faaa2", "faaa2 : { }"] + ["faaa", "faaa : {}"], + ["faaa2", "faaa2 : {}"] ]); }; @@ -1328,19 +1328,19 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test complex name 3"] = function() { var results = computeContentAssist("var ttt = { };\ntt", "tt"); testProposals(results, [ - ["ttt", "ttt : { }"] + ["ttt", "ttt : {}"] ]); }; tests["test complex name 4"] = function() { var results = computeContentAssist("var ttt = { aa: 1, bb: 2 };\ntt", "tt"); testProposals(results, [ - ["ttt", "ttt : { aa:Number, bb:Number }"] + ["ttt", "ttt : {aa:Number,bb:Number}"] ]); }; tests["test complex name 5"] = function() { var results = computeContentAssist("var ttt = { aa: 1, bb: 2 };\nttt.cc = 9;\ntt", "tt"); testProposals(results, [ - ["ttt", "ttt : { aa:Number, bb:Number, cc:Number }"] + ["ttt", "ttt : {aa:Number,bb:Number,cc:Number}"] ]); }; @@ -1380,7 +1380,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test broken after dot 3"] = function() { var results = computeContentAssist("var ttt = { ooo:this.};", "", "var ttt = { ooo:this.".length); testProposals(results, [ - ["ooo", "ooo : { ooo:{ ooo:{...} } }"], + ["ooo", "ooo : {ooo:{ooo:{...}}}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -1395,7 +1395,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test broken after dot 3a"] = function() { var results = computeContentAssist("var ttt = { ooo:this./**/};", ""); testProposals(results, [ - ["ooo", "ooo : { ooo:{ ooo:{...} } }"], + ["ooo", "ooo : {ooo:{ooo:{...}}}"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -1487,7 +1487,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "var first = function() { return 9; };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1504,7 +1504,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { return 9; };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1512,7 +1512,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "var obj = { first : function () { return 9; } };\nobj.first().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1520,7 +1520,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { return { ff : 9 }; };\nfirst().ff.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1528,7 +1528,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { return function() { return 9; }; };\nvar ff = first();\nff().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1536,7 +1536,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { return function() { return 9; }; };\nfirst()().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1545,7 +1545,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { if(true) { return 8; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1554,7 +1554,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { if(true) { return ''; } else { return 8; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1563,7 +1563,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { while(true) { return 1; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1572,7 +1572,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { do { return 1; } while(true); };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1581,7 +1581,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { for (var i; i < 10; i++) { return 1; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1590,7 +1590,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { for (var i in k) { return 1; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1599,7 +1599,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { try { return 1; } catch(e) { } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1608,7 +1608,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { try { return 1; } catch(e) { } finally { } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1617,7 +1617,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { try { return ''; } catch(e) { return 9; } finally { } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1626,7 +1626,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { try { return ''; } catch(e) { return ''; } finally { return 9; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1635,7 +1635,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { switch (v) { case a: return 9; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1644,7 +1644,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { switch (v) { case b: return ''; case a: return 1; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1653,7 +1653,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { switch (v) { case b: return ''; default: return 1; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1662,7 +1662,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { while(true) { a;\nb\n;return 9; } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -1671,14 +1671,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "function first() { while(true) { while(false) { \n;return 9; } } };\nfirst().toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test function return type obj literal 1"] = function() { var results = computeContentAssist( "function first() { return { a : 9, b : '' }; };\nfir", "fir"); testProposals(results, [ - ["first()", "first() : { a:Number, b:String }"] + ["first()", "first() : {a:Number,b:String}"] ]); }; @@ -1691,7 +1691,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri " }\n" + "}\nfir", "fir"); testProposals(results, [ - ["first()", "first() : () ⇒ { a:Number, b:String }"] + ["first()", "first() : function():{a:Number,b:String}"] ]); }; tests["test function return type obj literal 3"] = function() { @@ -1728,7 +1728,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "xxx;\nxx", "xx"); testProposals(results, [ - ["xxx", "xxx : { }"] + ["xxx", "xxx : {}"] ]); }; @@ -1779,7 +1779,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "xx/**/\nxxx", "xx"); testProposals(results, [ - ["xxx", "xxx : { }"] + ["xxx", "xxx : {}"] ]); }; @@ -1808,7 +1808,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "(1 + 2).toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr2"] = function() { @@ -1839,49 +1839,49 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "(hucairz + hucairz).toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr6"] = function() { var results = computeContentAssist( "(hucairz - hucairz).toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr7"] = function() { var results = computeContentAssist( "('' - '').toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr8"] = function() { var results = computeContentAssist( "('' & '').toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr9"] = function() { var results = computeContentAssist( "({ a : 9 } && '').a.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr10"] = function() { var results = computeContentAssist( "({ a : 9 } || '').a.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test binary expr11"] = function() { var results = computeContentAssist( "var aaa = function() { return hucairz || hucairz; }\naa", "aa"); testProposals(results, [ - ["aaa()", "aaa() : { }"] + ["aaa()", "aaa() : {}"] ]); }; tests["test binary expr12"] = function() { @@ -1903,14 +1903,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "(x += y).toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test unary expr2"] = function() { var results = computeContentAssist( "(x += 1).toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test unary expr3"] = function() { @@ -1985,7 +1985,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri testProposals(results, [ ["obj.Fun()", "obj.Fun() : obj.Fun"], ["Object([val])", "Object([val]) : Object"], - ["obj", "obj : { Fun:new() ⇒ obj.Fun, fun:() ⇒ undefined, fun2:Number }"] + ["obj", "obj : {Fun:function(new:obj.Fun):obj.Fun,fun:function():undefined,fun2:Number}"] ]); }; @@ -2003,7 +2003,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri testProposals(results, [ ["obj.Fun()", "obj.Fun() : obj.Fun"], ["Object([val])", "Object([val]) : Object"], - ["obj", "obj : { Fun:new() ⇒ obj.Fun }"] + ["obj", "obj : {Fun:function(new:obj.Fun):obj.Fun}"] ]); }; @@ -2013,27 +2013,28 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri testProposals(results, [ ["obj.inner.Fun()", "obj.inner.Fun() : obj.inner.Fun"], ["Object([val])", "Object([val]) : Object"], - ["obj", "obj : { inner:{ Fun:new() ⇒ obj.inner.Fun } }"] + ["obj", "obj : {inner:{Fun:function(new:obj.inner.Fun):obj.inner.Fun}}"] ]); }; tests["test dotted constructor5"] = function() { var results = computeContentAssist( - "var obj = { inner : { } }\nobj.inner.Fun = function() { }\nnew obj", "obj"); + "var obj = { inner : {} }\nobj.inner.Fun = function() { }\nnew obj", "obj"); testProposals(results, [ ["obj.inner.Fun()", "obj.inner.Fun() : obj.inner.Fun"], ["Object([val])", "Object([val]) : Object"], - ["obj", "obj : { inner:{ Fun:new() ⇒ obj.inner.Fun } }"] + ["obj", "obj : {inner:{Fun:function(new:obj.inner.Fun):obj.inner.Fun}}"] + ]); }; tests["test dotted constructor6"] = function() { var results = computeContentAssist( - "var obj = { inner : { } }\nobj.inner.inner2 = { Fun : function() { } }\nnew obj", "obj"); + "var obj = { inner : {} }\nobj.inner.inner2 = { Fun : function() { } }\nnew obj", "obj"); testProposals(results, [ ["obj.inner.inner2.Fun()", "obj.inner.inner2.Fun() : obj.inner.inner2.Fun"], ["Object([val])", "Object([val]) : Object"], - ["obj", "obj : { inner:{ inner2:{...} } }"] + ["obj", "obj : {inner:{inner2:{...}}}"] ]); }; @@ -2044,7 +2045,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var other = obj\n" + "new other.inner", "inner"); testProposals(results, [ - ["inner", "inner : { Fun:new() ⇒ obj.inner.Fun }"] + ["inner", "inner : {Fun:function(new:obj.inner.Fun):obj.inner.Fun}"] ]); }; @@ -2055,7 +2056,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var other = obj.inner\n" + "new other", "other"); testProposals(results, [ - ["other", "other : { Fun:new() ⇒ obj.inner.Fun }"] + ["other", "other : {Fun:function(new:obj.inner.Fun):obj.inner.Fun}"] ]); }; @@ -2122,7 +2123,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "this.JSON = {};\n" + "JSON.st", "st"); testProposals(results, [ - ["stringify(obj)", "stringify(obj) : String"] + ["stringify(json)", "stringify(json) : String"] ]); }; // should not be able to redefine or add to global types @@ -2131,7 +2132,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "this.JSON.stFOO;\n" + "JSON.st", "st"); testProposals(results, [ - ["stringify(obj)", "stringify(obj) : String"] + ["stringify(json)", "stringify(json) : String"] ]); }; @@ -2162,7 +2163,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "/*jslint browser:true*/\n" + "JSON.st", "st"); testProposals(results, [ - ["stringify(obj)", "stringify(obj) : String"] + ["stringify(json)", "stringify(json) : String"] ]); }; @@ -2555,7 +2556,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xx = function(ss) { s/**/ };", "s" ); testProposals(results, [ - ["ss", "ss : { }"] + ["ss", "ss : {}"] ]); }; @@ -2577,7 +2578,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xx = function(ss) { s/**/ };", "s" ); testProposals(results, [ - ["ss", "ss : { }"] + ["ss", "ss : {}"] ]); }; @@ -2601,7 +2602,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ); // for union types, we arbitrarily choose the first type testProposals(results, [ - ["xx", "xx : String"] + ["xx", "xx : (String|Number)"] ]); }; @@ -2611,7 +2612,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nx", "x" ); testProposals(results, [ - ["xx", "xx : String"] + ["xx", "xx : ?String"] ]); }; @@ -2621,7 +2622,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nx", "x" ); testProposals(results, [ - ["xx", "xx : String"] + ["xx", "xx : !String"] ]); }; @@ -2631,7 +2632,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nx", "x" ); testProposals(results, [ - ["xx", "xx : Array"] + ["xx", "xx : []"] ]); }; @@ -2642,7 +2643,17 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ); // currently, we just ignore parameterization testProposals(results, [ - ["xx", "xx : String[]"] + ["xx", "xx : Array."] + ]); + }; + tests["test type parameterized jsdoc1a"] = function() { + var results = computeContentAssist( + "/** @type {[String]}*/\n" + + "var xx;\nx", "x" + ); + // currently, we just ignore parameterization + testProposals(results, [ + ["xx", "xx : [String]"] ]); }; @@ -2734,7 +2745,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nxx.foo.foo", "foo" ); testProposals(results, [ - ["foo(a, b)", "foo(a, b) : { len:Number }"] + ["foo(a, b)", "foo(a, b) : {len:Number}"] ]); }; @@ -2765,7 +2776,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nxx.fo", "fo" ); testProposals(results, [ - ["foo()", "foo() : Flart"] + // should we be returning Flart here??? +// ["foo()", "foo() : Flart"] + ["foo()", "foo() : Number"] ]); }; @@ -2799,7 +2812,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xx"); testProposals(results, [ ["xx2(String, Number)", "xx2(String, Number) : Number"], - ["xx1", "xx1 : { }"] + ["xx1", "xx1 : {}"] ]); }; @@ -2810,7 +2823,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xx"); testProposals(results, [ ["xx2(a, Number)", "xx2(a, Number) : Number"], - ["xx1", "xx1 : { }"] + ["xx1", "xx1 : {}"] ]); }; @@ -2821,7 +2834,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xx"); testProposals(results, [ ["xx2(a, arg1)", "xx2(a, arg1) : Number"], - ["xx1", "xx1 : { }"] + ["xx1", "xx1 : {}"] ]); }; @@ -2940,7 +2953,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "/** @type [Number] */ var xxx;\nxxx[0].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type2"] = function() { @@ -2948,7 +2961,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type [Number,String] */ var xxx;\nxxx[0].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type3"] = function() { @@ -2956,7 +2969,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type Array. */ var xxx;\nxxx[0].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type4"] = function() { @@ -2964,7 +2977,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type Array.<{foo:Number}> */ var xxx;\nxxx[0].foo.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type5"] = function() { @@ -2972,7 +2985,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type Array.> */ var xxx;\nxxx[0][0].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type6"] = function() { @@ -2980,7 +2993,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type Array.>> */ var xxx;\nxxx[0][0][bar].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type7"] = function() { @@ -2988,7 +3001,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type {...Number} */ var xxx;\nxxx[0].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array type8"] = function() { @@ -2996,7 +3009,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri // ignoring the second type "/** @type {...Array.} */ var xxx;\nxxx[0][0].toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -3005,7 +3018,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var jjj = {};/** @type {Number} */\n" + "jjj.x = '';x.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test jsdoc on property decl2"] = function() { @@ -3013,7 +3026,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var jjj = {x:false};/** @type {Number} */\n" + "jjj.x = '';x.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; } @@ -3067,7 +3080,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { };\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { }"] + ["a", "a : {}"] ]); }; tests["test reassignment undef->{a}"] = function() { @@ -3076,7 +3089,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { a: 9 };\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment undef->any"] = function() { @@ -3105,7 +3118,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { };\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { }"] + ["a", "a : {}"] ]); }; tests["test reassignment obj->{a}"] = function() { @@ -3114,7 +3127,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { a: 9 };\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment obj->any"] = function() { @@ -3134,7 +3147,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = undefined;\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { }"] + ["a", "a : {}"] ]); }; tests["test reassignment { }->obj"] = function() { @@ -3143,7 +3156,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = new Object();\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { }"] + ["a", "a : {}"] ]); }; tests["test reassignment { }->{a}"] = function() { @@ -3152,7 +3165,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { a: 9 };\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment { }->any"] = function() { @@ -3172,7 +3185,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = undefined;\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment {a}->obj"] = function() { @@ -3181,7 +3194,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = new Object();\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment {a}->{ }"] = function() { @@ -3190,7 +3203,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { };\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment {a}->{a}"] = function() { @@ -3199,7 +3212,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = {b:9};\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { b:Number }"] + ["a", "a : {b:Number}"] ]); }; tests["test reassignment {a}->any"] = function() { @@ -3246,7 +3259,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "v.a = { a: 9};\n" + "v.a", "a"); testProposals(results, [ - ["a", "a : { a:Number }"] + ["a", "a : {a:Number}"] ]); }; tests["test reassignment any->any"] = function() { @@ -3302,7 +3315,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "aa", "aa", null, jsOptions1Global); testProposals(results, [ - ["aaa", "aaa : { }"] + ["aaa", "aaa : {}"] ]); }; @@ -3310,8 +3323,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "aa", "aa", null, jsOptions2Globals); testProposals(results, [ - ["aaa", "aaa : { }"], - ["aab", "aab : { }"] + ["aaa", "aaa : {}"], + ["aab", "aab : {}"] ]); }; @@ -3319,9 +3332,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "/*global aac */\naa", "aa", null, jsOptions2Globals); testProposals(results, [ - ["aaa", "aaa : { }"], - ["aab", "aab : { }"], - ["aac", "aac : { }"] + ["aaa", "aaa : {}"], + ["aab", "aab : {}"], + ["aac", "aac : {}"] ]); }; @@ -3329,8 +3342,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri var results = computeContentAssist( "aa", "aa", null, jsOptionsAndGlobals); testProposals(results, [ - ["aaa", "aaa : { }"], - ["aab", "aab : { }"] + ["aaa", "aaa : {}"], + ["aab", "aab : {}"] ]); }; @@ -3424,7 +3437,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var foo = { at: { bar: 0} };\n" + "foo['at'].bar.toF", "toF"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -3449,9 +3462,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var foo = [];\n" + "foo[x./**/]"); testProposals(results, [ - ["toExponential(digits)", "toExponential(digits) : Number"], - ["toFixed(digits)", "toFixed(digits) : Number"], - ["toPrecision(digits)", "toPrecision(digits) : Number"], + ["toExponential(digits)", "toExponential(digits) : String"], + ["toFixed(digits)", "toFixed(digits) : String"], + ["toPrecision(digits)", "toPrecision(digits) : String"], ["", "---------------------------------"], ["hasOwnProperty(property)", "hasOwnProperty(property) : Boolean"], ["isPrototypeOf(object)", "isPrototypeOf(object) : Boolean"], @@ -3502,7 +3515,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test full file inferecing 5"] = function() { var results = computeContentAssist( "function a() { var y = x.fff; y/**/}\n" + - "var x = { };\n" + + "var x = {};\n" + "x.fff = 8;", "y"); testProposals(results, [ ["y", "y : Number"] @@ -3511,7 +3524,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri tests["test full file inferecing 6"] = function() { var results = computeContentAssist( "function a() { x.fff = ''; var y = x.fff; y/**/}\n" + - "var x = { };\n" + + "var x = {};\n" + "x.fff = 8;", "y"); testProposals(results, [ ["y", "y : String"] @@ -3812,7 +3825,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "function x() { return ''; }", "f"); testProposals(results, [ ["Function()", "Function() : Function"], - ["fff", "fff : { }"] + ["fff", "fff : {}"] ]); }; @@ -3854,7 +3867,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = function() { return ''; }", "f"); testProposals(results, [ ["Function()", "Function() : Function"], - ["fff", "fff : { }"] + ["fff", "fff : {}"] ]); }; @@ -3911,7 +3924,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xxx.lll++;\n" + "xxx.ll", "ll"); testProposals(results, [ - ["lll", "lll : { }"] + ["lll", "lll : {}"] ]); }; @@ -3921,7 +3934,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "xxx.ll/**/;\n" + "xxx.lll++;", "ll"); testProposals(results, [ - ["lll", "lll : { }"] + ["lll", "lll : {}"] ]); }; @@ -3930,7 +3943,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "lll++;\n" + "ll", "ll"); testProposals(results, [ - ["lll", "lll : { }"] + ["lll", "lll : {}"] ]); }; @@ -3939,7 +3952,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "ll/**/;\n" + "lll++;", "ll"); testProposals(results, [ - ["lll", "lll : { }"] + ["lll", "lll : {}"] ]); }; @@ -3951,7 +3964,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [1];\n" + "x[foo].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization2"] = function() { @@ -3959,7 +3972,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [1];\n" + "x[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization3"] = function() { @@ -3967,14 +3980,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [1];\n" + "x['foo'].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization4"] = function() { var results = computeContentAssist( "([1, 0])[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization5"] = function() { @@ -3982,7 +3995,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [[1]];\n" + "x[0][0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization6"] = function() { @@ -3990,7 +4003,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [{}];x[0].a = 8;\n" + "x[0].a.toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization7"] = function() { @@ -3999,7 +4012,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "x[0].a.toFi", "toFi"); // may not work because a string testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization8"] = function() { @@ -4007,7 +4020,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [[1]];\n" + "x = x[0];\nx[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization9"] = function() { @@ -4015,7 +4028,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [];\n" + "x[9] = 0;\nx[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization10"] = function() { @@ -4023,7 +4036,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = [];\n" + "x[9] = '';\nx[9] = 0;\nx[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization11"] = function() { @@ -4031,7 +4044,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = (function() { return [0]; })();\n" + "x[9] = 0;\nx[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests["test array parameterization10"] = function() { @@ -4039,7 +4052,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var x = ['','',''];\n" + "x[9] = 0;\nx[0].toFi", "toFi"); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; From 375f03fc3665994e06b4b084d154618555a40369 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 14 Mar 2013 21:29:34 -0700 Subject: [PATCH 28/64] All single file content assist tests passing --- .../plugins/esprima/esprimaJsContentAssist.js | 33 ++++++++++++++----- client/scripts/plugins/esprima/types.js | 24 +++++++++----- .../esprima/esprimaJsContentAssistTests.js | 10 +++++- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index b3c7d007..fb973719 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -687,11 +687,16 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr docComment = findAssociatedCommentBlock(property.key, env.comments); jsdocResult = mTypes.parseJSDocComment(docComment); jsdocType = jsdocResult.type && mTypes.convertJsDocType(jsdocResult.type, env); - var keyType = jsdocType ? jsdocType : mTypes.OBJECT_TYPE; - env.addVariable(property.key.name, node, keyType, property.key.range, docComment.range); if (!property.key.extras) { property.key.extras = {}; } + var keyType; + if (jsdocType) { + property.key.extras.inferredType = property.key.extras.jsdocType = keyType = jsdocType; + } else { + keyType = mTypes.OBJECT_TYPE; + } + env.addVariable(property.key.name, node, keyType, property.key.range, docComment.range); property.key.extras.associatedComment = docComment; // remember that this is the LHS so that we don't add the identifier to global scope property.key.extras.isLHS = property.key.extras.isDecl = true; @@ -1056,7 +1061,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr range = kvps[i].key.range; } - inferredTypeObj = kvps[i].value.extras.inferredTypeObj; + inferredTypeObj = kvps[i].key.extras.jsdocType || kvps[i].value.extras.inferredTypeObj; var docComment = kvps[i].key.extras.associatedComment; env.addVariable(name, node, inferredTypeObj, range, docComment.range); if (inRange(env.offset-1, kvps[i].key.range)) { @@ -1197,7 +1202,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr break; case "Property": - node.extras.inferredTypeObj = node.key.extras.inferredTypeObj = node.value.extras.inferredTypeObj; + if (node.key.extras.jsdocType) { + // use jsdoc instead of whatever we have inferred + node.extras.inferredTypeObj = node.key.extras.jsdocType; + } else { + node.extras.inferredTypeObj = node.key.extras.inferredTypeObj = node.value.extras.inferredTypeObj; + } break; case "AssignmentExpression": @@ -1402,15 +1412,20 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * added to it. Additionally, the type specified in the $$proto property is * either empty or is Object * - * @param String leftTypeObj - * @param String leftTypeObj + * @param {{}} leftTypeObj + * @param {{}} rightTypeObj * @param {{getAllTypes:function():Object}} env * * @return Boolean */ function leftTypeIsMoreGeneral(leftTypeObj, rightTypeObj, env) { - var leftTypeName = leftTypeObj.name, rightTypeName = rightTypeObj; + var leftTypeName = leftTypeObj.name, rightTypeName = rightTypeObj.name; + if (!leftTypeName) { + if (leftTypeObj.type === 'NullLiteral' || leftTypeObj.type === 'UndefinedLiteral' || leftTypeObj.type === 'VoidLiteral') { + leftTypeName = 'undefined'; + } + } function isEmpty(generatedTypeName) { if (typeof generatedTypeName !== 'string') { // original type was not a name expression @@ -1418,10 +1433,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } else if (generatedTypeName === "Object" || generatedTypeName === "undefined") { return true; } else if (generatedTypeName.substring(0, mTypes.GEN_NAME.length) !== mTypes.GEN_NAME) { + // not a synthetic type, so not empty return false; } + // now check to see if there are any non-default fields in this type var type = env.getAllTypes()[generatedTypeName]; var popCount = 0; // type should have a $$proto only and nothing else if it is empty @@ -1436,7 +1453,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (popCount === 1) { // we have an empty object literal, must check parent // must traverse prototype hierarchy to make sure empty - return isEmpty(type.$$proto.typeObj); + return isEmpty(type.$$proto.typeObj.name); } return false; } diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 7cc144cf..69b33840 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -45,7 +45,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { } var THE_UNKNOWN_TYPE = createNameType("Object"); - + var JUST_DOTS = '$$__JUST_DOTS__$$'; /** @@ -1415,17 +1415,25 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { * Otherwise object */ extractArrayParameterType : function(arrayObj) { - if ((arrayObj.type === 'ArrayType' || arrayObj.type === 'TypeApplication')) { - if (arrayObj.elements.length > 0) { - return arrayObj.elements[0]; + var elts; + if (arrayObj.type === 'TypeApplication') { + if (arrayObj.expression.name === 'Array') { + elts = arrayObj.applications; } else { - return THE_UNKNOWN_TYPE; + return arrayObj.expression; } + } else if (arrayObj.type === 'ArrayType') { + elts = arrayObj.elements; } else { // not an array type return arrayObj; } + if (elts.length > 0) { + return elts[0]; + } else { + return THE_UNKNOWN_TYPE; + } }, extractReturnType : function(fnType) { @@ -1549,12 +1557,12 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { var typeApp = { type: jsdocType.type, expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1), - + }; if (jsdocType.applications) { typeApp.applications = jsdocType.applications.map(function(elt) { return self.convertJsDocType(elt, env, doCombine, depth+1); - }); + }); } return typeApp; @@ -1587,7 +1595,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return; } var prop = origFields[key]; - var fieldType = depth > 0 && (prop.typeObj.type === 'NameExpression' && env.isSyntheticName(prop.typeObj.name)) ? + var fieldType = depth > 0 && (prop.typeObj.type === 'NameExpression' && env.isSyntheticName(prop.typeObj.name)) ? { type : 'NameExpression', name : JUST_DOTS } : self.convertJsDocType(prop.typeObj, env, doCombine, depth+1); newFields.push({ diff --git a/tests/client/esprima/esprimaJsContentAssistTests.js b/tests/client/esprima/esprimaJsContentAssistTests.js index 466e264e..1e7caf8f 100644 --- a/tests/client/esprima/esprimaJsContentAssistTests.js +++ b/tests/client/esprima/esprimaJsContentAssistTests.js @@ -2846,7 +2846,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "flar"); // hmmmm... functions returning functions not really showing up testProposals(results, [ - ["flart(xx1, xx2)", "flart(xx1, xx2) : (a:String, arg1:Number) ⇒ Number"] + ["flart(xx1, xx2)", "flart(xx1, xx2) : function(a:String,?Number):Number"] ]); }; @@ -2972,6 +2972,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ["toFixed(digits)", "toFixed(digits) : String"] ]); }; + tests["test array type3a"] = function() { + var results = computeContentAssist( + // ignoring the second type + "/** @type [Number] */ var xxx;\nxxx[0].toF", "toF"); + testProposals(results, [ + ["toFixed(digits)", "toFixed(digits) : String"] + ]); + }; tests["test array type4"] = function() { var results = computeContentAssist( // ignoring the second type From 2717ef25cde2d8497cf4a7e0acd5c0476372c03b Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 15 Mar 2013 13:03:57 -0700 Subject: [PATCH 29/64] Partway through summary building tests --- .../plugins/esprima/esprimaJsContentAssist.js | 182 +++++++++++++----- tests/client/esprima/navigationTests.js | 31 +-- tests/client/esprima/summaryBuildingTests.js | 86 ++++----- tests/client/scriptedClientTests.html | 4 +- 4 files changed, 192 insertions(+), 111 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index fb973719..bc548e4b 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -8,11 +8,11 @@ * http://www.opensource.org/licenses/eclipse-1.0.php * * Contributors: - * Andy Clement (VMware) - initial API and implementation - * Andrew Eisenberg (VMware) - implemented visitor pattern + * Andy Clement (VMware) - initial API and implementation + * Andrew Eisenberg (VMware) - implemented visitor pattern ******************************************************************************/ -/*global define require eclipse esprima window */ +/*global define esprima doctrine */ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/esprima/proposalUtils", "scriptedLogger", "esprima/esprima"], function(mVisitor, mTypes, proposalUtils, scriptedLogger) { @@ -22,9 +22,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr */ var isArray = Array.isArray; if (!isArray) { - isArray = function isArray(ary) { - return Object.prototype.toString.call(ary) === '[object Array]'; - }; + isArray = function isArray(ary) { + return Object.prototype.toString.call(ary) === '[object Array]'; + }; } var RESERVED_WORDS = { @@ -156,8 +156,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * the first char and exclusive of the last char, must * use a +1 at the end. * eg- (^ is the line start) - * ^x ---> range[0,0] - * ^ xx ---> range[2,3] + * ^x ---> range[0,0] + * ^ xx ---> range[2,3] */ function inRange(offset, range, includeEdge) { return range[0] <= offset && (includeEdge ? range[1] >= offset : range[1] > offset); @@ -1670,9 +1670,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (inferredTypeObj.type === 'NameExpression') { return inferredTypeObj.name; } else if (inferredTypeObj.type === 'FunctionType') { - if (inferredTypeObj['new'] && inferredTypeObj['new'].name) { - return inferredTypeObj['new'].name; - } + if (inferredTypeObj['new'] && inferredTypeObj['new'].name) { + return inferredTypeObj['new'].name; + } return "Function"; } } else { @@ -1772,7 +1772,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr found = true; break; } else if (current.$$proto) { - var tname = current.$$proto.typeObj.name; + var tname = current.$$proto.typeObj.name; current = this._allTypes[tname || "Function"]; } else { current = null; @@ -2053,32 +2053,80 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } - // TODO this is incorrect!!!!! function findUnreachable(currentTypeName, allTypes, alreadySeen) { - if (currentTypeName.charAt(0) === '*') { - // constructors are not stored with their arguments so need to remove them in order to find them - currentTypeName = mTypes.removeParameters(currentTypeName); + + function visitTypeStructure(typeObj) { + if (typeof typeObj !== 'object') { + return; + } + switch (typeObj.type) { + case 'NullableLiteral': + case 'AllLiteral': + case 'NullLiteral': + case 'UndefinedLiteral': + case 'VoidLiteral': + // leaf nodes + return; + + case 'NameExpression': + alreadySeen[typeObj.name] = true; + return; + + case 'ArrayType': + visitTypeStructure(typeObj.expression); + // fall-through + case 'UnionType': + if (typeObj.elements) { + typeObj.elements.forEach(visitTypeStructure); + } + return; + + case 'RecordType': + if (typeObj.fields) { + typeObj.fields.forEach(visitTypeStructure); + } + return; + + case 'FieldType': + visitTypeStructure(typeObj.expression); + return; + + case 'FunctionType': + // do we need to check for serialized functions??? + visitTypeStructure(typeObj.result); + if (typeObj.params) { + typeObj.params.forEach(visitTypeStructure); + } + return; + + case 'ParameterType': + visitTypeStructure(typeObj.expression); + return; + + case 'TypeApplication': + if (typeObj.applications) { + typeObj.applications.forEach(visitTypeStructure); + } + + // fall-throudh + case 'RestType': + case 'NonNullableType': + case 'OptionalType': + case 'NullableType': + visitTypeStructure(typeObj.expression); + return; + + + } } + var currentType = allTypes[currentTypeName]; if (currentType) { for(var prop in currentType) { if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { var propType = currentType[prop].typeObj; - while (mTypes.isFunctionOrConstructor(propType) || mTypes.isArrayType(propType)) { - if (!alreadySeen[propType]) { - alreadySeen[propType] = true; - findUnreachable(propType, allTypes, alreadySeen); - } - if (mTypes.isFunctionOrConstructor(propType)) { - propType = mTypes.extractReturnType(propType); - } else if (mTypes.isArrayType(propType)) { - propType = mTypes.extractArrayParameterType(propType); - } - } - if (!alreadySeen[propType]) { - alreadySeen[propType] = true; - findUnreachable(propType, allTypes, alreadySeen); - } + // must visit the type strucutre + visitTypeStructure(propType); } } } @@ -2087,13 +2135,13 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * filters types from the environment that should not be exported */ - function filterTypes(environment, kind, moduleTypeName) { + function filterTypes(environment, kind, moduleTypeName, provided) { var allTypes = environment.getAllTypes(); if (kind === "global") { // for global dependencies must keep the global scope, but remove all builtin global variables allTypes.clearDefaultGlobal(); } else { - delete allTypes.Global; + delete allTypes[environment.globalTypeName()]; } // recursively walk the type tree to find unreachable types and delete them, too @@ -2131,12 +2179,37 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr reachable[retType] = true; } -// findUnreachable(moduleTypeName, allTypes, reachable); + findUnreachable(moduleTypeName, allTypes, reachable); for (var prop in allTypes) { if (allTypes.hasOwnProperty(prop) && !reachable[prop]) { delete allTypes[prop]; } } + + // now reformat the types so that they are combined and serialized + Object.keys(allTypes).forEach(function(typeName) { + var type = allTypes[typeName]; + for (var defName in type) { + if (type.hasOwnProperty(defName)) { + var def = type[defName]; + def.typeSig = doctrine.stringify(def.typeObj); + delete def._typeObj; + } + } + }); + + if (typeof provided === 'object') { + for (var defName in provided) { + if (provided.hasOwnProperty(defName)) { + var def = provided[defName]; + if (def.typeObj) { + def.typeSig = doctrine.stringify(def.typeObj); + delete def._typeObj; + } + } + } + } + } var browserRegExp = /browser\s*:\s*true/; @@ -2334,7 +2407,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // This is fixed in trunk of esprima. // after next upgrade of esprima if the following has correct slocs, then // can remove the second part of the && - // mUsers.getUser().name + // mUsers.getUser().name if (node.range[0] > offset && (node.type === "ExpressionStatement" || node.type === "ReturnStatement" || @@ -2431,57 +2504,62 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } throw (e); } - var provided; + var providedType; var kind; - var modType; + var modTypeName; + var modTypeObj; if (environment.amdModule) { // provide the exports of the AMD module // the exports is the return value of the final argument var args = environment.amdModule["arguments"]; if (args && args.length > 0) { - modType = mTypes.extractReturnType(args[args.length-1].extras.inferredTypeObj); + modTypeObj = mTypes.extractReturnType(args[args.length-1].extras.inferredTypeObj); + modTypeName = doctrine.stringify(modTypeObj); } else { - modType = "Object"; + modTypeObj = mTypes.OBJECT_TYPE; + modTypeName = modTypeObj.name; } kind = "AMD"; } else if (environment.commonjsModule) { // a wrapped commonjs module // we have already checked the correctness of this function var exportsParam = environment.commonjsModule["arguments"][0].params[1]; - modType = exportsParam.extras.inferredTypeObj; - provided = provided = environment.findType(modType); + modTypeObj = exportsParam.extras.inferredTypeObj; + modTypeName = doctrine.stringify(modTypeObj); + providedType = environment.findType(modTypeObj); } else { // assume a non-module - provided = environment.globalScope(); + providedType = environment.globalScope(); - if (provided.exports) { + if (providedType.exports) { // actually, commonjs kind = "commonjs"; - modType = provided.exports.typeObj; + modTypeObj = providedType.exports.typeObj; + modTypeName = doctrine.stringify(modTypeObj); } else { kind = "global"; - modType = environment.globalTypeName(); + modTypeName = environment.globalTypeName(); + modTypeObj = providedType['this'].typeObj; } } // simplify the exported type - if (mTypes.isFunctionOrConstructor(modType) || environment.findType(modType).$$isBuiltin) { + if (mTypes.isFunctionOrConstructor(modTypeObj) || environment.findType(modTypeObj).$$isBuiltin) { // this module provides a built in type or a function - provided = modType; + providedType = modTypeName; } else { // this module provides a composite type - provided = environment.findType(modType); + providedType = environment.findType(modTypeObj); } + var allTypes = environment.getAllTypes(); // now filter the builtins since they are always available - filterTypes(environment, kind, modType); - - var allTypes = environment.getAllTypes(); + filterTypes(environment, kind, modTypeName, providedType); return { - provided : provided, + provided : providedType, types : allTypes, kind : kind }; diff --git a/tests/client/esprima/navigationTests.js b/tests/client/esprima/navigationTests.js index f6435359..42872c8f 100644 --- a/tests/client/esprima/navigationTests.js +++ b/tests/client/esprima/navigationTests.js @@ -12,7 +12,7 @@ ******************************************************************************/ // tests for javascript navigation, both in-file and out -/*global define esprima console setTimeout esprimaContentAssistant*/ +/*global define esprima console setTimeout esprimaContentAssistant doctrine*/ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsprimaPlugin, assert) { ////////////////////////////////////////////////////////// @@ -46,7 +46,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp if (!actual) { assert.fail("No definition found for:\n" + expected.hoverText ); } - assert.equal(actual.typeName, expected.typeName, "Invalid type name in definition"); + assert.equal(doctrine.stringify(actual.typeObj), expected.typeSig, "Invalid type name in definition"); assert.equal(actual.path, expected.path, "Invalid path in definition"); if (!expected.range) { assert.ok(!actual.range, "Should not have found a range object: " + actual.range); @@ -81,7 +81,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp } else { expected.range = null; } - expected.typeName = typeName; + expected.typeSig = typeName; expected.hoverText = hover; expected.path = path; expected.buffer = buffer; @@ -130,22 +130,25 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp doSameFileTest("var aaa = 9\naaa", 'aaa', 'Number'); }; tests.testVar2 = function() { - doSameFileTest("var aaa = function(a,b,c) { return 9; }\naaa", 'aaa', "?Number:a/gen~local~0,b/gen~local~1,c/gen~local~2", 'aaa : (a, b, c) ⇒ Number'); + doSameFileTest("var aaa = function(a,b,c) { return 9; }\naaa", 'aaa', + "function(a:gen~local~0,b:gen~local~1,c:gen~local~2):Number", 'aaa : function(a:{},b:{},c:{}):Number'); + }; tests.testVar3 = function() { doSameFileTest("var aaa = function(a,b,c) { return function(a) { return 9; }; }\naaa", - 'aaa', "??Number:a/gen~local~5:a/gen~local~0,b/gen~local~1,c/gen~local~2", 'aaa : (a, b, c) ⇒ (a:{...}) ⇒ Number'); + 'aaa', "function(a:gen~local~0,b:gen~local~1,c:gen~local~2):function(a:gen~local~5):Number", + 'aaa : function(a:{},b:{},c:{}):function(a:{}):Number'); }; tests.testParam1 = function() { doSameFileTest("var bbb = function(a,b,d) { d }", - 'd', "gen~local~2", 'd : { }'); + 'd', "gen~local~2", 'd : {}'); }; tests.testParam2 = function() { doSameFileTest("var d = 9;var bbb = function(a,b,d) { d }", - 'd', "gen~local~2", 'd : { }', 2); + 'd', "gen~local~2", 'd : {}', 2); }; tests.testParam3 = function() { - doSameFileTest("var d = 9;var bbb = function(a,b,d) { }\nd", + doSameFileTest("var d = 9;var bbb = function(a,b,d) {}\nd", 'd', "Number", 'd : Number', 1); }; tests.testObjLiteral1 = function() { @@ -188,7 +191,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testThis4 = function() { doSameFileTest("var Foo = function() {};\nFoo.prototype = { a : this }", - 'this', "gen~local~4", 'this : { a:{ a:{...} } }', '{ a : this }'); + 'this', "gen~local~4", 'this : {a:{a:{...}}}', '{ a : this }'); }; ////////////////////////////////////////////////////////// @@ -236,7 +239,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testAMD2 = function() { doMultiFileTest( "file1", "define({ val1 : function() { return 9; } });", "define(['file1'], function(f1) { f1.val1; });", - 'val1', "?Number:", 'val1 : () ⇒ Number'); + 'val1', "function():Number", 'val1 : function():Number'); }; ////////////////////////////////////////////////////////// @@ -344,7 +347,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testFullFile2 = function() { doSameFileTest("var x = y;\n" + "x;\n" + - "var y = { fff : 0 };", "x", "gen~local~0", "x : { }", 1); + "var y = { fff : 0 };", "x", "gen~local~0", "x : {}", 1); }; // arrrrgh need to fix this one so that the test chooses the first fff, not the last @@ -443,7 +446,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " */\n" + "var x;"; doSameFileTest( - contents, "x", "gen~local~0", "x : { }", 1, + contents, "x", "gen~local~0", "x : {}", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; tests.testJSDoc0a = function() { @@ -451,7 +454,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "/***/\n" + "var x;"; doSameFileTest( - contents, "x", "gen~local~0", "x : { }", 1, + contents, "x", "gen~local~0", "x : {}", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; tests.testJSDoc0b = function() { @@ -460,7 +463,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "// TODO this is strannnnnnnnge.\n" + "var x;"; doSameFileTest( - contents, "x", "gen~local~0", "x : { }", 1, + contents, "x", "gen~local~0", "x : {}", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; diff --git a/tests/client/esprima/summaryBuildingTests.js b/tests/client/esprima/summaryBuildingTests.js index 4613e93d..5f7a9e30 100644 --- a/tests/client/esprima/summaryBuildingTests.js +++ b/tests/client/esprima/summaryBuildingTests.js @@ -17,10 +17,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp // helpers ////////////////////////////////////////////////////////// var esprimaContentAssistant = new mEsprimaPlugin.EsprimaJavaScriptContentAssistProvider(); - + function filterSummary(summary) { - if (summary && summary.typeName) { - summary = summary.typeName; + if (summary && summary.typeSig) { + summary = summary.typeSig; } else if (typeof summary === "object") { for (var prop in summary) { if (summary.hasOwnProperty(prop)) { @@ -30,7 +30,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp } return summary; } - + function computeSummary(fileName, buffer) { return esprimaContentAssistant.computeSummary(buffer, fileName); } @@ -39,12 +39,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp var filteredSummary =filterSummary(actualSummary); assert.equal(JSON.stringify(filteredSummary), expectedSummaryText); } - + function assertCreateSummary(expectedSummaryText, buffer, fileName) { assertSameSummary(expectedSummaryText, computeSummary(fileName, buffer)); } - - + + ////////////////////////////////////////////////////////// // tests ////////////////////////////////////////////////////////// @@ -56,52 +56,52 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testEmptyGlobalStructure = function() { assertCreateSummary('{"provided":{},"types":{},"kind":"global"}', "", "a"); }; - + tests.testOneVarGlobalStructure1 = function() { assertCreateSummary('{"provided":{"x":"gen~a~0"},"types":{"gen~a~0":{"$$proto":"Object"}},"kind":"global"}', "var x;", "a"); }; - + tests.testOneVarGlobalStructure2 = function() { assertCreateSummary('{"provided":{"x":"Number"},"types":{},"kind":"global"}', "var x=0;", "a"); }; - + tests.testOneVarGlobalStructure3 = function() { assertCreateSummary('{"provided":{"x":"String"},"types":{},"kind":"global"}', "var x='';", "a"); }; - + tests.testOneVarGlobalStructure4 = function() { assertCreateSummary('{"provided":{"x":"gen~a~1"},"types":{"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', "var x={};", "a"); }; - + tests.testOneVarGlobalStructure5 = function() { assertCreateSummary('{"provided":{"x":"gen~a~1"},"types":{"gen~a~1":{"$$proto":"Object","f":"Number","g":"String"}},"kind":"global"}', "var x={f:9, g:''};", "a"); }; - + tests.testOneVarGlobalStructure6 = function() { - assertCreateSummary('{"provided":{"x":"?undefined:"},"types":{},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function():undefined"},"types":{},"kind":"global"}', "var x=function() {};", "a"); }; - + tests.testOneVarGlobalStructure7 = function() { - assertCreateSummary('{"provided":{"x":"?undefined:a/gen~a~0,b/gen~a~1"},"types":{},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function(a:gen~a~0,b:gen~a~1):undefined"},"types":{"gen~a~0":{"$$proto":"Object"},"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', "var x=function(a,b) {};", "a"); }; - + tests.testOneVarGlobalStructure8 = function() { - assertCreateSummary('{"provided":{"x":"?Number:a/gen~a~0,b/gen~a~1"},"types":{},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function(a:gen~a~0,b:gen~a~1):Number"},"types":{"gen~a~0":{"$$proto":"Object"},"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', "var x=function(a,b) {return 7; };", "a"); }; - + tests.testOneVarGlobalStructure9 = function() { - assertCreateSummary('{"provided":{"x":"?Number:a/gen~a~0,b/gen~a~1"},"types":{},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function(a:gen~a~0,b:gen~a~1):Number"},"types":{"gen~a~0":{"$$proto":"Object"},"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', "function x(a,b) {return 7; }", "a"); }; - + ////////////////////////////////////////////////////////// // AMD dependencies name value pairs (NVP) // See http://requirejs.org/docs/api.html#defsimple @@ -112,17 +112,17 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testNVP2 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"?Number:"},"types":{},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():Number"},"types":{},"kind":"AMD"}', "define({a : 1, b: function() { return 8; }});", "a"); }; tests.testNVP3 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"??Fun::"},"types":{"Fun":{"$$proto":"*Fun:","ff":"Number"},"*Fun:":{"$$proto":"*Fun:~proto"},"*Fun:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function():Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"}},"kind":"AMD"}', "define({a : 1, b: function() { function Fun(a) { this.ff = 8; }; return function() { return new Fun(); }}});", "a"); }; tests.testNVP4 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"?*Fun:a/gen~a~6:"},"types":{"Fun":{"$$proto":"*Fun:","ff":"Number"},"*Fun:":{"$$proto":"*Fun:~proto"},"*Fun:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function(a:gen~a~6,new:Fun):Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"},"gen~a~6":{"$$proto":"Object"}},"kind":"AMD"}', "define({a : 1, b: function() { function Fun(a) { this.ff = 8; }; return Fun; }});", "a"); }; tests.testNVP5 = function() { @@ -181,8 +181,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return new Exported(); });", "a"); }; - - + + ////////////////////////////////////////////////////////// // AMD futzing with prototypes of exported constructors ////////////////////////////////////////////////////////// @@ -195,18 +195,18 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype = { foo : 9, bar : '' };\nreturn new Exported(); });", "a"); }; tests.testAMDProto3 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.open = function() { return 9; };\nreturn new Exported(); });", "a"); }; tests.testAMDProto4 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nreturn new Exported(); });", "a"); }; tests.testAMDProto5 = function() { - assertCreateSummary('{"provided":"*Exported:a/gen~a~4,b/gen~a~5","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"*Exported:a/gen~a~4,b/gen~a~5","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nreturn Exported; });", "a"); }; - + // https://github.com/scripted-editor/scripted/issues/96 tests["test constructor export with changed prototype"] = function() { assertCreateSummary('{"provided":"*Car:model/gen~a~4","types":{"Car":{"$$proto":"*Car:","model":"gen~a~4"},"*Car:":{"$$proto":"gen~a~9"},"*Car:~proto":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object","show":"?undefined:","model":"gen~a~14"},"gen~a~14":{"$$proto":"Object"}},"kind":"AMD"}', @@ -223,7 +223,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});", "a"); }; - + ////////////////////////////////////////////////////////// // common js modules are modules that have an exports variable in the global scope ////////////////////////////////////////////////////////// @@ -231,12 +231,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp assertCreateSummary('{"provided":{"$$proto":"Object","foo":"Number"},"types":{},"kind":"commonjs"}', "/*global exports*/\nexports.foo = 9", "a"); }; - + tests.testCommonJS2 = function() { assertCreateSummary('{"provided":{"$$proto":"Object","foo":"Number"},"types":{},"kind":"commonjs"}', "exports.foo = 9", "a"); }; - + tests.testCommonJS3 = function() { assertCreateSummary('{"provided":"Number","types":{},"kind":"commonjs"}', "exports = 9", "a"); @@ -253,7 +253,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~3"},"types":{"gen~a~3":{"$$proto":"Object","a":"gen~a~5"},"gen~a~5":{"$$proto":"Object","a":"gen~a~7"},"gen~a~7":{"$$proto":"Object"}},"kind":"commonjs"}', "var a = { a : { a : { a : { } } } }\n exports = a;", "a"); }; - + // not sure if this is right...an explicitly declared exports variable is the // same as an implicit one tests.testCommonJS7 = function() { @@ -279,7 +279,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " exports.a = { flart: function(a,b) { return ''; } }\n" + "});", "a"); }; - + ////////////////////////////////////////////////////////// // Common JS futzing with prototypes of exported constructors ////////////////////////////////////////////////////////// @@ -292,22 +292,22 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype = { foo : 9, bar : '' };\nexports.Exported = new Exported();", "a"); }; tests.testCommonjsProto3 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.open = function() { return 9; };\nexports.Exported = new Exported();", "a"); }; tests.testCommonjsProto4 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports = new Exported(); });", "a"); }; tests.testCommonjsProto5 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"*Exported:a/gen~a~1,b/gen~a~2"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"*Exported:a/gen~a~1,b/gen~a~2"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports.Exported = Exported; });", "a"); }; tests.testCommonjsProto6 = function() { - assertCreateSummary('{"provided":"*Exported:a/gen~a~1,b/gen~a~2","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"?Number:"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"*Exported:a/gen~a~1,b/gen~a~2","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports = Exported; });", "a"); }; - + ////////////////////////////////////////////////////////// // Browser awareness ////////////////////////////////////////////////////////// @@ -328,7 +328,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "/*jslint browser:true*/\n" + "define([], function () { return { foo : { loc : location, scr : screen } }; });", "a"); }; - + ////////////////////////////////////////////////////////// // Dotted constructors ////////////////////////////////////////////////////////// @@ -379,8 +379,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function foo() {}\n" + "exports = foo;", "a"); }; - - + + tests["test array export1"] = function() { assertCreateSummary('{"provided":"Array.","types":{},"kind":"commonjs"}', "exports = [1];", "a"); diff --git a/tests/client/scriptedClientTests.html b/tests/client/scriptedClientTests.html index 5136b704..6e313893 100644 --- a/tests/client/scriptedClientTests.html +++ b/tests/client/scriptedClientTests.html @@ -50,10 +50,10 @@ testShim(resolverTests); module("Esprima JS Content Assist tests"); testShim(contentAssistTests); - module("Navigation tests"); - testShim(navTests); module("Summary tests"); testShim(summaryTests); + module("Navigation tests"); + testShim(navTests); module("Dependency tests"); testShim(dependencyTests); module("Proposal Utils tests"); From 365ca738dfc2d498e3d323cf256aaa9e472cee5f Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sat, 16 Mar 2013 22:34:50 -0700 Subject: [PATCH 30/64] all summary building tests passing --- .../plugins/esprima/esprimaJsContentAssist.js | 167 ++++++++---------- tests/client/esprima/summaryBuildingTests.js | 72 ++++---- 2 files changed, 112 insertions(+), 127 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index bc548e4b..17764d31 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -2053,80 +2053,94 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } - function findUnreachable(currentTypeName, allTypes, alreadySeen) { - - function visitTypeStructure(typeObj) { - if (typeof typeObj !== 'object') { + function visitTypeStructure(typeObj, allTypes, alreadySeen) { + if (typeof typeObj !== 'object') { + return; + } + switch (typeObj.type) { + case 'NullableLiteral': + case 'AllLiteral': + case 'NullLiteral': + case 'UndefinedLiteral': + case 'VoidLiteral': + // leaf nodes return; - } - switch (typeObj.type) { - case 'NullableLiteral': - case 'AllLiteral': - case 'NullLiteral': - case 'UndefinedLiteral': - case 'VoidLiteral': - // leaf nodes - return; - case 'NameExpression': - alreadySeen[typeObj.name] = true; + case 'NameExpression': + if (alreadySeen[typeObj.name]) { + // prevent infinite recursion for circular refs return; + } + alreadySeen[typeObj.name] = true; + findUnreachable(typeObj.name, allTypes, alreadySeen); + return; - case 'ArrayType': - visitTypeStructure(typeObj.expression); - // fall-through - case 'UnionType': - if (typeObj.elements) { - typeObj.elements.forEach(visitTypeStructure); - } - return; + case 'ArrayType': + visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + // fall-through + case 'UnionType': + if (typeObj.elements) { + typeObj.elements.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + } + return; - case 'RecordType': - if (typeObj.fields) { - typeObj.fields.forEach(visitTypeStructure); - } - return; + case 'RecordType': + if (typeObj.fields) { + typeObj.fields.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + } + return; - case 'FieldType': - visitTypeStructure(typeObj.expression); - return; + case 'FieldType': + visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + return; - case 'FunctionType': - // do we need to check for serialized functions??? - visitTypeStructure(typeObj.result); - if (typeObj.params) { - typeObj.params.forEach(visitTypeStructure); - } - return; + case 'FunctionType': + // do we need to check for serialized functions??? + if (typeObj.params) { + typeObj.params.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + } + if (typeObj.result) { + visitTypeStructure(typeObj.result, allTypes, alreadySeen); + } + if (typeObj['this']) { + visitTypeStructure(typeObj['this'], allTypes, alreadySeen); + } + if (typeObj['new']) { + visitTypeStructure(typeObj['new'], allTypes, alreadySeen); + } + return; - case 'ParameterType': - visitTypeStructure(typeObj.expression); - return; + case 'ParameterType': + visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + return; - case 'TypeApplication': - if (typeObj.applications) { - typeObj.applications.forEach(visitTypeStructure); - } + case 'TypeApplication': + if (typeObj.applications) { + typeObj.applications.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + } - // fall-throudh - case 'RestType': - case 'NonNullableType': - case 'OptionalType': - case 'NullableType': - visitTypeStructure(typeObj.expression); - return; + // fall-throudh + case 'RestType': + case 'NonNullableType': + case 'OptionalType': + case 'NullableType': + visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + return; - } } + } + // finds unreachable types from the given type name + // and marks them as already seen + function findUnreachable(currentTypeName, allTypes, alreadySeen) { var currentType = allTypes[currentTypeName]; if (currentType) { for(var prop in currentType) { if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { var propType = currentType[prop].typeObj; // must visit the type strucutre - visitTypeStructure(propType); + visitTypeStructure(propType, allTypes, alreadySeen); } } } @@ -2135,48 +2149,19 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * filters types from the environment that should not be exported */ - function filterTypes(environment, kind, moduleTypeName, provided) { + function filterTypes(environment, kind, moduleTypeName, moduleTypeObj, provided) { var allTypes = environment.getAllTypes(); if (kind === "global") { // for global dependencies must keep the global scope, but remove all builtin global variables - allTypes.clearDefaultGlobal(); } else { - delete allTypes[environment.globalTypeName()]; } + allTypes.clearDefaultGlobal(); +// delete allTypes[environment.globalTypeName()]; // recursively walk the type tree to find unreachable types and delete them, too var reachable = { }; - // if we have a function, then the function return type and its prototype are reachable - // also do same if parameterized array type - // in the module, so add them - if (mTypes.isFunctionOrConstructor(moduleTypeName) || mTypes.isArrayType(moduleTypeName)) { - var retType = moduleTypeName; - while (mTypes.isFunctionOrConstructor(retType) || mTypes.isArrayType(retType)) { - if (mTypes.isFunctionOrConstructor(retType)) { - retType = mTypes.removeParameters(retType); - reachable[retType] = true; - var constrType; - if (retType.charAt(0) === "?") { - // this is a function, not a constructor, but we also - // need to expose the constructor if one exists. - constrType = "*" + retType.substring(1); - reachable[constrType] = true; - } else { - constrType = retType; - } - // don't strictly need this if the protoype of the object has been changed, but OK to keep - reachable[constrType + "~proto"] = true; - retType = mTypes.extractReturnType(retType); - } else if (mTypes.isArrayType(retType)) { - retType = mTypes.extractArrayParameterType(retType); - if (retType) { - reachable[retType] = true; - } else { - retType = "Object"; - } - } - } - reachable[retType] = true; + if (moduleTypeObj.type !== "NameExpression") { + visitTypeStructure(moduleTypeObj, allTypes, reachable); } findUnreachable(moduleTypeName, allTypes, reachable); @@ -2185,7 +2170,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr delete allTypes[prop]; } } - + // now reformat the types so that they are combined and serialized Object.keys(allTypes).forEach(function(typeName) { var type = allTypes[typeName]; @@ -2197,7 +2182,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } }); - + if (typeof provided === 'object') { for (var defName in provided) { if (provided.hasOwnProperty(defName)) { @@ -2556,7 +2541,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var allTypes = environment.getAllTypes(); // now filter the builtins since they are always available - filterTypes(environment, kind, modTypeName, providedType); + filterTypes(environment, kind, modTypeName, modTypeObj, providedType); return { provided : providedType, diff --git a/tests/client/esprima/summaryBuildingTests.js b/tests/client/esprima/summaryBuildingTests.js index 5f7a9e30..3bdd5eb6 100644 --- a/tests/client/esprima/summaryBuildingTests.js +++ b/tests/client/esprima/summaryBuildingTests.js @@ -117,16 +117,16 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testNVP3 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function():Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"}},"kind":"AMD"}', + assertCreateSummary('{\"provided\":{\"$$proto\":\"Object\",\"a\":\"Number\",\"b\":\"function():function():Fun\"},\"types\":{\"Fun\":{\"$$proto\":\"Fun~proto\",\"ff\":\"Number\"},\"Fun~proto\":{\"$$proto\":\"Object\"}},\"kind\":\"AMD\"}', "define({a : 1, b: function() { function Fun(a) { this.ff = 8; }; return function() { return new Fun(); }}});", "a"); }; tests.testNVP4 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function(a:gen~a~6,new:Fun):Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"},"gen~a~6":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function(a:gen~a~6,new:Fun):Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"},"gen~a~6":{"$$proto":"Object"},"Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define({a : 1, b: function() { function Fun(a) { this.ff = 8; }; return Fun; }});", "a"); }; tests.testNVP5 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"?Fun:"},"types":{"Fun":{"$$proto":"*Fun:","ff":"Number"},"*Fun:":{"$$proto":"*Fun:~proto"},"*Fun:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"},"Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define({a : 1, b: function() { function Fun(a) { this.ff = 8; }; return new Fun(); }});", "a"); }; @@ -154,31 +154,31 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "define('afg', [], function() { var a = 9;\n return { first: a }; });", "a"); }; tests.testAMD6 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","first":"?String:"},"types":{},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","first":"function():String"},"types":{},"kind":"AMD"}', "define('afg', [], function() { var a = function() { return ''; };\n return { first: a }; });", "a"); }; tests.testAMD7 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","first":"?String:","second":"Number"},"types":{},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","first":"function():String","second":"Number"},"types":{},"kind":"AMD"}', "define('afg', [], function() { var a = function() { return ''; };\n return { first: a, second: 8 }; });", "a"); }; tests.testAMD8 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"*Exported:","second":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"function(new:Exported):Exported","second":"Number"},"types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define('afg', [], function() { var Exported = function() { this.a = 9; };\n return { Exported: Exported, second: 8 }; });", "a"); }; tests.testAMD9 = function() { - assertCreateSummary('{"provided":"*Exported:a/gen~a~4,b/gen~a~5","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(a:gen~a~4,b:gen~a~5,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~4":{"$$proto":"Object"},"gen~a~5":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return Exported; });", "a"); }; tests.testAMD10 = function() { - assertCreateSummary('{"provided":"?*Exported:a/gen~a~4,b/gen~a~5:c/gen~a~9,d/gen~a~10","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(c:gen~a~9,d:gen~a~10):function(a:gen~a~4,b:gen~a~5,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~4":{"$$proto":"Object"},"gen~a~5":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object"},"gen~a~10":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return function(c,d) { return Exported; }; });", "a"); }; tests.testAMD11 = function() { - assertCreateSummary('{"provided":"?Exported:c/gen~a~9,d/gen~a~10","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(c:gen~a~9,d:gen~a~10):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object"},"gen~a~10":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return function(c,d) { return new Exported(c,d); }; });", "a"); }; tests.testAMD12 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Exported~proto","a":"Number"},"types":{"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return new Exported(); });", "a"); }; @@ -187,29 +187,29 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp // AMD futzing with prototypes of exported constructors ////////////////////////////////////////////////////////// tests.testAMDProto1 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","foo":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Exported~proto","a":"Number"},"types":{"Exported~proto":{"$$proto":"Object","foo":"Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.foo = 9;\nreturn new Exported(); });", "a"); }; tests.testAMDProto2 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"gen~a~10"},"gen~a~10":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"gen~a~10","a":"Number"},"types":{"gen~a~10":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype = { foo : 9, bar : '' };\nreturn new Exported(); });", "a"); }; tests.testAMDProto3 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Exported~proto","a":"Number"},"types":{"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.open = function() { return 9; };\nreturn new Exported(); });", "a"); }; tests.testAMDProto4 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Exported~proto","a":"Number"},"types":{"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nreturn new Exported(); });", "a"); }; tests.testAMDProto5 = function() { - assertCreateSummary('{"provided":"*Exported:a/gen~a~4,b/gen~a~5","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(a:gen~a~4,b:gen~a~5,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~4":{"$$proto":"Object"},"gen~a~5":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nreturn Exported; });", "a"); }; // https://github.com/scripted-editor/scripted/issues/96 tests["test constructor export with changed prototype"] = function() { - assertCreateSummary('{"provided":"*Car:model/gen~a~4","types":{"Car":{"$$proto":"*Car:","model":"gen~a~4"},"*Car:":{"$$proto":"gen~a~9"},"*Car:~proto":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object","show":"?undefined:","model":"gen~a~14"},"gen~a~14":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(model:gen~a~4,new:Car):Car","types":{"Car":{"$$proto":"gen~a~9","model":"gen~a~4"},"gen~a~4":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object","show":"function():undefined","model":"gen~a~14"},"gen~a~14":{"$$proto":"Object"}},"kind":"AMD"}', "define(function() {\n" + " function Car(model) {\n" + " this.model = model;\n" + @@ -274,7 +274,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " exports.a = 7; });", "a"); }; tests.testWrappedCommonJS3 = function() { - assertCreateSummary('{\"provided\":{\"$$proto\":\"Object\",\"a\":\"gen~a~8\"},\"types\":{\"gen~a~8\":{\"$$proto\":\"Object\",\"flart\":\"?String:a/gen~a~9,b/gen~a~10\"}}}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~8"},"types":{"gen~a~8":{"$$proto":"Object","flart":"function(a:gen~a~9,b:gen~a~10):String"},"gen~a~9":{"$$proto":"Object"},"gen~a~10":{"$$proto":"Object"}}}', "define(function(require, exports, module) {\n" + " exports.a = { flart: function(a,b) { return ''; } }\n" + "});", "a"); @@ -284,27 +284,27 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp // Common JS futzing with prototypes of exported constructors ////////////////////////////////////////////////////////// tests.testCommonjsProto1 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","foo":"Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object","foo":"Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.foo = 9; exports.Exported = new Exported();", "a"); }; tests.testCommonjsProto2 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"gen~a~7"},"gen~a~7":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"gen~a~7","a":"Number"},"gen~a~7":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype = { foo : 9, bar : '' };\nexports.Exported = new Exported();", "a"); }; tests.testCommonjsProto3 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.open = function() { return 9; };\nexports.Exported = new Exported();", "a"); }; tests.testCommonjsProto4 = function() { - assertCreateSummary('{"provided":{"$$proto":"*Exported:","a":"Number"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Exported~proto","a":"Number"},"types":{"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports = new Exported(); });", "a"); }; tests.testCommonjsProto5 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"*Exported:a/gen~a~1,b/gen~a~2"},"types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"function(a:gen~a~1,b:gen~a~2,new:Exported):Exported"},"types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~1":{"$$proto":"Object"},"gen~a~2":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports.Exported = Exported; });", "a"); }; tests.testCommonjsProto6 = function() { - assertCreateSummary('{"provided":"*Exported:a/gen~a~1,b/gen~a~2","types":{"Exported":{"$$proto":"*Exported:","a":"Number"},"*Exported:":{"$$proto":"*Exported:~proto"},"*Exported:~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"function(a:gen~a~1,b:gen~a~2,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~1":{"$$proto":"Object"},"gen~a~2":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports = Exported; });", "a"); }; @@ -333,12 +333,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp // Dotted constructors ////////////////////////////////////////////////////////// tests["test dotted constructor1"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~a~6"},"types":{"gen~a~6":{"$$proto":"Object","Fun":"*obj.Fun:"},"obj.Fun":{"$$proto":"*obj.Fun:"},"*obj.Fun:":{"$$proto":"*obj.Fun:~proto"},"*obj.Fun:~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~a~6"},"types":{"gen~a~6":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function () { return { obj : { Fun: function() { } } }; });", "a"); }; tests["test dotted constructor2"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~a~4"},"types":{"gen~a~4":{"$$proto":"Object","Fun":"*obj.Fun:"},"obj.Fun":{"$$proto":"*obj.Fun:"},"*obj.Fun:":{"$$proto":"*obj.Fun:~proto"},"*obj.Fun:~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~a~4"},"types":{"gen~a~4":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', "define([], function () {\n" + " var obj = { Fun: function() { } };\n" + " obj.Fun.prototype.larf = 9;\n" + @@ -347,7 +347,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests["test dotted constructor3"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Fun":"*obj.Fun:"},"types":{"obj.Fun":{"$$proto":"*obj.Fun:"},"*obj.Fun:":{"$$proto":"*obj.Fun:~proto"},"*obj.Fun:~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"types":{"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', "define([], function () {\n" + " var obj = { Fun: function() { } };\n" + " obj.Fun.prototype.larf = 9;\n" + @@ -356,7 +356,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests["test dotted constructor4"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","obj":"*obj.Fun:"},"types":{"obj.Fun":{"$$proto":"*obj.Fun:"},"*obj.Fun:":{"$$proto":"*obj.Fun:~proto"},"*obj.Fun:~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","obj":"function(new:obj.Fun):obj.Fun"},"types":{"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', "define([], function () {\n" + " var obj = { Fun: function() { } };\n" + " obj.Fun.prototype.larf = 9;\n" + @@ -365,7 +365,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests["test dotted constructor5"] = function() { - assertCreateSummary('{"provided":{"$$proto":"*obj.Fun:"},"types":{"obj.Fun":{"$$proto":"*obj.Fun:"},"*obj.Fun:":{"$$proto":"*obj.Fun:~proto"},"*obj.Fun:~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"obj.Fun~proto"},"types":{"obj.Fun~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', "define([], function () {\n" + " var obj = { Fun: function() { } };\n" + " obj.Fun.prototype.larf = 9;\n" + @@ -374,7 +374,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests["test jslint settings"] = function() { - assertCreateSummary('{"provided":"?undefined:","types":{},"kind":"commonjs"}', + assertCreateSummary('{"provided":"function():undefined","types":{},"kind":"commonjs"}', "/*jslint node:true */\n" + "function foo() {}\n" + "exports = foo;", "a"); @@ -382,7 +382,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests["test array export1"] = function() { - assertCreateSummary('{"provided":"Array.","types":{},"kind":"commonjs"}', + assertCreateSummary('{"provided":"[Number]","types":{},"kind":"commonjs"}', "exports = [1];", "a"); }; tests["test array export2"] = function() { @@ -390,30 +390,30 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = ([1])[0];", "a"); }; tests["test array export3"] = function() { - assertCreateSummary('{"provided":"Array.>","types":{},"kind":"commonjs"}', + assertCreateSummary('{"provided":"[[Number]]","types":{},"kind":"commonjs"}', "exports = [[1]];", "a"); }; tests["test array export4"] = function() { - assertCreateSummary('{"provided":"Array.","types":{"gen~a~2":{"$$proto":"Object","a":"Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"[gen~a~2]","types":{"gen~a~2":{"$$proto":"Object","a":"Number"}},"kind":"commonjs"}', "exports = [{a:1}];", "a"); }; tests["test array export5"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Array."},"types":{},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"[Number]"},"types":{},"kind":"commonjs"}', "exports = {a:[1]};", "a"); }; tests["test array export6"] = function() { - assertCreateSummary('{"provided":"Array.","types":{"gen~a~2":{"$$proto":"Object","a":"Array."}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"[gen~a~2]","types":{"gen~a~2":{"$$proto":"Object","a":"[Number]"}},"kind":"commonjs"}', "exports = [{a:[1]}];", "a"); }; tests["test array export amd1"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Array."},"types":{},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"[Number]"},"types":{},"kind":"AMD"}', "/*global define */\n" + "define([], function() {\n" + " return { a : [9]};\n" + "});", "a"); }; tests["test array export amd2"] = function() { - assertCreateSummary('{"provided":"Array.","types":{"gen~a~4":{"$$proto":"Object","a":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"[gen~a~4]","types":{"gen~a~4":{"$$proto":"Object","a":"Number"}},"kind":"AMD"}', "/*global define */\n" + "define([], function() {\n" + " return [{ a : 9}];\n" + From f2e11b71645405d44da3391f4d06562a4c4a14c8 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 18 Mar 2013 16:00:52 -0700 Subject: [PATCH 31/64] all tests passing ready for merge --- .../plugins/esprima/esprimaJsContentAssist.js | 37 +++-- client/scripts/plugins/esprima/types.js | 49 +++++-- .../esprima/contentAssistDependencyTests.js | 134 +++++++++--------- tests/client/esprima/navigationTests.js | 35 ++--- tests/client/indexer/indexerTests.js | 48 +++---- 5 files changed, 168 insertions(+), 135 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 17764d31..ff889f91 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -138,7 +138,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var argName = param.name || 'arg' + p; if (rest) { - argName += '...'; + argName = '...' + argName; } if (optional) { argName = '[' + argName + ']'; @@ -512,7 +512,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr env.popScope(); } env.mergeSummary(summary, mergeTypeName); - return typeName; + return mTypes.ensureTypeObject(typeName); } } } @@ -1563,7 +1563,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * @return {boolean} true iff this is an internally generated name */ isSyntheticName: function(name) { - return name.substr(0, namePrefix.length) === namePrefix; + return name.substr(0, mTypes.GEN_NAME.length) === mTypes.GEN_NAME; }, /** @@ -1674,6 +1674,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr return inferredTypeObj['new'].name; } return "Function"; + } else if (inferredTypeObj.type === 'ArrayType') { + return "Array"; } } else { // grab topmost scope @@ -1855,11 +1857,24 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * @param String targetTypeName */ mergeSummary : function(summary, targetTypeName) { - + var defn, existingDefn, property; // add the extra types that don't already exists - for (var type in summary.types) { - if (summary.types.hasOwnProperty(type) && !this._allTypes[type]) { - this._allTypes[type] = summary.types[type]; + for (var typeName in summary.types) { + if (summary.types.hasOwnProperty(typeName)) { + // create type if doesn't already exist, othewise merge + var type = this._allTypes[typeName]; + var existingType = summary.types[typeName]; + if (!type) { + type = this._allTypes[typeName] = {}; + } + + // for each property defined in the type from the sumamry, + // also add it to the current module's type + for (var typeProp in existingType) { + if (!type[typeProp]) { + type[typeProp] = mTypes.Definition.revive(existingType[typeProp]); + } + } } } @@ -1870,9 +1885,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // TODO summary.provided mightbe a RecordType for (var providedProperty in summary.provided) { if (summary.provided.hasOwnProperty(providedProperty)) { + // copy over the summary into the type den // the targetType may already have the providedProperty defined // but should override - targetType[providedProperty] = summary.provided[providedProperty]; + targetType[providedProperty] = mTypes.Definition.revive(summary.provided[providedProperty]); } } } @@ -2151,12 +2167,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr */ function filterTypes(environment, kind, moduleTypeName, moduleTypeObj, provided) { var allTypes = environment.getAllTypes(); - if (kind === "global") { - // for global dependencies must keep the global scope, but remove all builtin global variables - } else { - } allTypes.clearDefaultGlobal(); -// delete allTypes[environment.globalTypeName()]; // recursively walk the type tree to find unreachable types and delete them, too var reachable = { }; diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 69b33840..f46510ec 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -25,6 +25,9 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { * Doctrine closure compiler style type objects */ function ensureTypeObject(signature) { + if (!signature) { + return signature; + } if (signature.type) { return signature; } @@ -81,6 +84,23 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { } }; + /** + * Revivies a Definition object from a regular object + */ + Definition.revive = function(obj) { + var defn = new Definition(); + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + if (prop === 'typeSig') { + defn.typeObj = obj[prop]; + } else { + defn[prop] = obj[prop]; + } + } + } + return defn; + }; + // From ecma script manual 262 section 15 // the global object when not in browser or node var Global = function() {}; @@ -394,8 +414,8 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { $$isBuiltin: true, apply : new Definition("function(func:function(),argArray:Array=):Object"), "arguments" : new Definition("Arguments"), - bind : new Definition("function(func:function(),args:Object...):Object"), - call : new Definition("function(func:function(),args:Object...):Object"), + bind : new Definition("function(func:function(),...args:Object):Object"), + call : new Definition("function(func:function(),...args:Object):Object"), caller : new Definition("Function"), length : new Definition("Number"), name : new Definition("String"), @@ -408,17 +428,17 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { Array : { $$isBuiltin: true, - concat : new Definition("function(first:Array,rest:Array...):Array"), + concat : new Definition("function(first:Array,...rest:Array):Array"), join : new Definition("function(separator:Object):String"), length : new Definition("Number"), pop : new Definition("function():Object"), - push : new Definition("function(vals:Object...):Object"), + push : new Definition("function(...vals:Object):Object"), reverse : new Definition("function():Array"), shift : new Definition("function():Object"), - slice : new Definition("function(start:Number,deleteCount:Number,items:Object...):Array"), + slice : new Definition("function(start:Number,deleteCount:Number,...items:Object):Array"), splice : new Definition("function(start:Number,end:Number):Array"), sort : new Definition("function(sorter:Object=):Array"), - unshift : new Definition("function(items:Object...):Number"), + unshift : new Definition("function(...items:Object):Number"), indexOf : new Definition("function(searchElement,fromIndex=):Number"), lastIndexOf : new Definition("function(searchElement,fromIndex=):Number"), every : new Definition("function(callbackFn:function(elt:Object),thisArg:Object=):Boolean"), @@ -1131,11 +1151,11 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { getElementsByTagNameNS : new Definition("function(namespace:String,localName:String):HTMLCollection"), getElementsByClassName : new Definition("function(classname:String):HTMLCollection"), - prepend : new Definition("function(nodes:Node...)"), - append : new Definition("function(nodes:Node...)"), - before : new Definition("function(nodes:Node...)"), - after : new Definition("function(nodes:Node...)"), - replace : new Definition("function(nodes:Node...)"), + prepend : new Definition("function(...nodes:Node)"), + append : new Definition("function(...nodes:Node)"), + before : new Definition("function(...nodes:Node)"), + after : new Definition("function(...nodes:Node)"), + replace : new Definition("function(...nodes:Node)"), remove : new Definition("function()") }, @@ -1276,8 +1296,8 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { $$isBuiltin: true, $$proto : new Definition("Node"), - prepend : new Definition("function(nodes:Node...)"), - append : new Definition("function(nodes:Node...)") + prepend : new Definition("function(...nodes:Node)"), + append : new Definition("function(...nodes:Node)") }, // incomplete @@ -1729,6 +1749,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { if (useFunctionSig) { typeObj = this.convertJsDocType(typeObj, env, true); var res = doctrine.stringify(typeObj); + // TODO g flag doesn't work on chrome and webkit res = res.replace(JUST_DOTS, "{...}", 'g'); return res; } else { @@ -1815,7 +1836,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return this.styleAsType(typeName, useHtml); } }, - + ensureTypeObject: ensureTypeObject, OBJECT_TYPE: THE_UNKNOWN_TYPE, UNDEFINED_TYPE: createNameType("undefined"), NUMBER_TYPE: createNameType("Number"), diff --git a/tests/client/esprima/contentAssistDependencyTests.js b/tests/client/esprima/contentAssistDependencyTests.js index c771962a..217db7f3 100644 --- a/tests/client/esprima/contentAssistDependencyTests.js +++ b/tests/client/esprima/contentAssistDependencyTests.js @@ -14,33 +14,33 @@ // tests for javascript content assist where dependencies are provided /*global define esprima console setTimeout esprimaContentAssistant*/ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsprimaPlugin, assert) { - + ////////////////////////////////////////////////////////// // helpers ////////////////////////////////////////////////////////// - + function MockIndexer(globalDeps, amdDeps) { function createSummary(buffer, name) { var esprimaContentAssistant = new mEsprimaPlugin.EsprimaJavaScriptContentAssistProvider(); return esprimaContentAssistant.computeSummary(buffer, name); } - + var processedGlobalDeps = []; for (var name in globalDeps) { if (globalDeps.hasOwnProperty(name)) { processedGlobalDeps.push(createSummary(globalDeps[name], name)); } } - + this.retrieveGlobalSummaries = function() { return processedGlobalDeps; }; - + this.retrieveSummary = function(name) { return amdDeps ? createSummary(amdDeps[name], name) : null; }; } - + function computeContentAssist(buffer, prefix, indexer) { var esprimaContentAssistant = new mEsprimaPlugin.EsprimaJavaScriptContentAssistProvider(indexer); if (!prefix) { @@ -52,14 +52,14 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp } return esprimaContentAssistant.computeProposals(buffer, offset, {prefix : prefix, inferredOnly : true}); } - + function testProposal(proposal, text, description) { assert.equal(proposal.proposal, text, "Invalid proposal text"); if (description) { assert.equal(proposal.description, description, "Invalid proposal description"); } } - + function stringifyExpected(expectedProposals) { var text = ""; for (var i = 0; i < expectedProposals.length; i++) { @@ -67,7 +67,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp } return text; } - + function stringifyActual(actualProposals) { var text = ""; for (var i = 0; i < actualProposals.length; i++) { @@ -75,12 +75,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp } return text; } - + function testProposals(actualProposals, expectedProposals) { - + assert.equal(actualProposals.length, expectedProposals.length, "Wrong number of proposals. Expected:\n" + stringifyExpected(expectedProposals) +"\nActual:\n" + stringifyActual(actualProposals)); - + for (var i = 0; i < actualProposals.length; i++) { testProposal(actualProposals[i], expectedProposals[i][0], expectedProposals[i][1]); } @@ -179,7 +179,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp ["ff2", "ff2 : Number"] ]); }; - + ////////////////////////////////////////////////////////// // tests of amd dependencies ////////////////////////////////////////////////////////// @@ -210,7 +210,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define('first', [], function() { return { aaa : 9 } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testAMD4 = function() { @@ -221,13 +221,13 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp second: "define('second', [], function() { return { aaa : 9 } });" })); testProposals(results, [ - ["fa", "fa : { aaa:Number }"], - ["fb", "fb : { aaa:Number }"], + ["fa", "fa : {aaa:Number}"], + ["fb", "fb : {aaa:Number}"], ["", "---------------------------------"], ["Function()", "Function() : Function"] ]); }; - + // returns an anonymous function tests.testAMD5a = function() { var results = computeContentAssist( @@ -248,11 +248,11 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define('first', [], function() { return function(a,b) { return 9; } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - - + + // returns a named function tests.testAMD6a = function() { var results = computeContentAssist( @@ -271,7 +271,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define('first', [], function() { return { fun : function(a,b) { return 9; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -293,10 +293,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define('first', [], function() { return { Fun : function(a,b) { this.ff = 9; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + ////////////////////////////////////////////////////////// // tests for name-value pair (NVP) style modules @@ -308,7 +308,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define({ fun : 8 });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testNVP2 = function() { @@ -318,7 +318,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define({ fun : function() { return 8; }});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testNVP3 = function() { @@ -328,10 +328,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define({ Fun : function() { this.ff = 8; }});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + ////////////////////////////////////////////////////////// // tests for async require function // note that async require calls are typically either in @@ -344,7 +344,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define('first', [], function() { return { Fun : function(a,b) { this.ff = 9; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -356,7 +356,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp second: "define('second', [], function() { return { Fun2 : function(a,b) { this.ff = 9; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -368,10 +368,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp second: "define('second', [], function() { return { Fun2 : function(a,b) { this.ff = 9; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + tests.testAMDRequire4NestedInDefineWithShadowing = function() { var results = computeContentAssist( "define(['second'], function(ff) { require(['first'], function(ff) { new ff.Fun().ff.toF/**/ }); });", "toF", new MockIndexer( @@ -380,7 +380,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp second: "define('second', [], function() { return { Fun2 : function(a,b) { this.ff = ''; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -392,10 +392,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp second: "define('second', [], function() { return { Fun2 : function(a,b) { this.ff = ''; } } });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + ////////////////////////////////////////////////////////// // AMD with prototype manipulation ////////////////////////////////////////////////////////// @@ -460,7 +460,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "exports = 9;" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonJS2 = function() { @@ -471,7 +471,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "exports = 9;" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonJS3 = function() { @@ -482,7 +482,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "exports = { first : 9 };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonJS4 = function() { @@ -493,7 +493,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "var first = 9;\nexports = { first : first };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonJS5 = function() { @@ -505,7 +505,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { first : first };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonJS6 = function() { @@ -517,10 +517,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { First : First };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + // I don't know if this one is valid syntax since jslint flags this with an error, // but we'll keep this test since esprima parses it properly and the result is correct tests.testCommonJS7 = function() { @@ -532,10 +532,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { First : First };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + tests.testCommonJS8 = function() { var results = computeContentAssist( "var foo = require('first');\n" + @@ -545,7 +545,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { First : First };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonJS9 = function() { @@ -557,10 +557,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { First : First };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + tests.testCommonJS10 = function() { var results = computeContentAssist( "var a = require('first').a;\n" + @@ -570,7 +570,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { a : { First : Foo } };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; @@ -584,11 +584,11 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = { a : { b : b } };" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - - + + ////////////////////////////////////////////////////////// // tests for prototype manipulation in commonjs modules ////////////////////////////////////////////////////////// @@ -620,7 +620,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp ["ff1", "ff1 : Number"], ["", "---------------------------------"], ["ff2", "ff2 : Number"], - ["ff3", "ff3 : { a:Number, b:Number }"] + ["ff3", "ff3 : {a:Number,b:Number}"] ]); }; tests.testCommonJSproto2 = function() { @@ -636,7 +636,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp ["ff1", "ff1 : Number"], ["", "---------------------------------"], ["ff2", "ff2 : Number"], - ["ff3", "ff3 : { a:Number, b:Number }"] + ["ff3", "ff3 : {a:Number,b:Number}"] ]); }; tests.testCommonJSproto3 = function() { @@ -688,7 +688,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testAMDSyncRequire2 = function() { @@ -703,7 +703,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testAMDSyncRequire3 = function() { @@ -717,10 +717,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + ////////////////////////////////////////////////////////// // tests for wrapped commonjs modules ////////////////////////////////////////////////////////// @@ -735,7 +735,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonjsWrapped2 = function() { @@ -747,7 +747,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testCommonjsWrapped3 = function() { @@ -759,11 +759,11 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: " exports.a = { flart: function(a,b) { return 1; } }\n" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - - + + ////////////////////////////////////////////////////////// // Browser awareness ////////////////////////////////////////////////////////// @@ -815,7 +815,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp ["prototype", "prototype : Object"] ]); }; - + tests.testArray1 = function() { var results = computeContentAssist( "define(['first'], function(mFirst) {\n"+ @@ -824,10 +824,10 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define([], function() { return [1]; });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; - + tests.testArray2 = function() { var results = computeContentAssist( "define(['first'], function(mFirst) {\n"+ @@ -836,7 +836,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define([], function() { return {a : [1]}; });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testArray3 = function() { @@ -847,7 +847,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define([], function() { return [{a: 1}]; });" })); testProposals(results, [ - ["toFixed(digits)", "toFixed(digits) : Number"] + ["toFixed(digits)", "toFixed(digits) : String"] ]); }; tests.testArray4 = function() { @@ -858,7 +858,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp first: "define([], function() { return [{a: 1}]; });" })); testProposals(results, [ - ["concat(first, [rest...])", "concat(first, [rest...]) : Array"] + ["concat(first, ...rest)", "concat(first, ...rest) : Array"] ]); }; return tests; diff --git a/tests/client/esprima/navigationTests.js b/tests/client/esprima/navigationTests.js index 42872c8f..88d740a6 100644 --- a/tests/client/esprima/navigationTests.js +++ b/tests/client/esprima/navigationTests.js @@ -281,7 +281,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " return Car;\n" + "});", "define(['car'], function(Car) { var c = new Car('ford'); c.show(); });", - 'show', "?undefined:", 'show : () ⇒ undefined'); + 'show', "function():undefined", 'show : function():undefined'); }; ////////////////////////////////////////////////////////// @@ -368,7 +368,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " function a() {\n" + " aa();\n" + " }\n" + - "}", "aa", "?undefined:", "aa : () ⇒ undefined", 1); + "}", "aa", "function():undefined", "aa : function():undefined", 1); }; tests.testFullFile7 = function() { @@ -377,7 +377,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " aa();\n" + " }\n" + " function aa() { }\n" + - "}", "aa", "?undefined:", "aa : () ⇒ undefined", 2); + "}", "aa", "function():undefined", "aa : function():undefined", 2); }; tests.testFullFile8 = function() { @@ -408,17 +408,17 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testArray2 = function() { doSameFileTest( "var other = [1]\n;" + - "other", "other", "Array.", "other : Number[]", 1); + "other", "other", "[Number]", "other : [Number]", 1); }; tests.testArray3 = function() { doSameFileTest( "var other = [[1]]\n;" + - "other", "other", "Array.>", "other : Number[][]", 1); + "other", "other", "[[Number]]", "other : [[Number]]", 1); }; tests.testArray4 = function() { doSameFileTest( "var first = [[1]]\n;" + - "var other = first[0]", "other", "Array.", "other : Number[]", 1); + "var other = first[0]", "other", "[Number]", "other : [Number]", 1); }; tests.testArray5 = function() { doSameFileTest( @@ -473,7 +473,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports.x = function() {};"; doMultiFileTest( "a", otherContents, "require('a').x", "x", - "?undefined:", "x : () ⇒ undefined", 2, false, + "function():undefined", "x : function():undefined", 2, false, [otherContents.indexOf("/**"), otherContents.indexOf("*/")+2]); }; @@ -495,7 +495,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function parseFile(path) { }\n" + "var x;"; doSameFileTest( - contents, "parseFile", "?String:path/String", "parseFile : (path:String) ⇒ String", 1, + contents, "parseFile", "function(path:String):String", "parseFile : function(path:String):String", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -507,7 +507,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function parseFile(path) { path.sumpin = ''; }\n" + "var x;"; doSameFileTest( - contents, "parseFile", "?String:path/gen~local~1", "parseFile : (path:{ foo:Number, bar:String, sumpin:String }) ⇒ String", 1, + contents, "parseFile", "function(path:gen~local~1):String", "parseFile : function(path:{foo:Number,bar:String,sumpin:String}):String", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -518,7 +518,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function ParseFile(path) { path.sumpin = ''; }\n" + "var x;"; doSameFileTest( - contents, "ParseFile", "*ParseFile:path/gen~local~2", "ParseFile : new(path:{ foo:Number, bar:String, sumpin:String }) ⇒ ParseFile", 1, + contents, "ParseFile", "function(path:gen~local~2,new:ParseFile):ParseFile", "ParseFile : function(path:{foo:Number,bar:String,sumpin:String},new:ParseFile):ParseFile", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -529,7 +529,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function parseFile(path) { path.sumpin = ''; }\n" + "var x;"; doSameFileTest( - contents, "parseFile", "?undefined:path/gen~local~1", "parseFile : (path:{ foo:Number, bar:(a, b) ⇒ String, sumpin:String }) ⇒ undefined", 1, + contents, "parseFile", "function(path:gen~local~1):undefined", "parseFile : function(path:{foo:Number,bar:function(Object,Object):String,sumpin:String}):undefined", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -539,8 +539,9 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp doSameFileTest( contents, "addSaveTransform", - "?undefined:transformFun/?String:text/String,path/String,configuration/?Object:foo/String", - "addSaveTransform : (transformFun:(text:String) ⇒ String, path:String, configuration:(foo:String) ⇒ Object) ⇒ undefined", 1, + + "function(transformFun:function(text:String,path:String,configuration:function(foo:String):Object):?String):undefined", + "addSaveTransform : function(transformFun:function(text:String,path:String,configuration:function(foo:String):Object):?String):undefined", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -558,7 +559,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function a() {}"; doSameFileTest( contents, "getContent", - "?String:","getContent : () ⇒ String", 0, + "function():String","getContent : function():String", 0, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -577,7 +578,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function a() {}"; doSameFileTest( contents, "getContent", - "?String:","getContent : () ⇒ String", 0, + "function():String","getContent : function():String", 0, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -606,7 +607,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function a() {}"; doSameFileTest( contents, "getContent", - "?String:","getContent : () ⇒ String", 0, + "function():String","getContent : function():String", 0, [contents.indexOf("/**", contents.indexOf("before")), contents.indexOf("*/", contents.indexOf("before"))+2]); }; tests.testFullFileJSDoc4 = function() { @@ -630,7 +631,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function a() {}"; doSameFileTest( contents, "getContent", - "?String:","getContent : () ⇒ String", 0, + "function():String","getContent : function():String", 0, [contents.indexOf("/**", contents.indexOf("before")), contents.indexOf("*/", contents.indexOf("before"))+2]); }; return tests; diff --git a/tests/client/indexer/indexerTests.js b/tests/client/indexer/indexerTests.js index 9611b384..2ed89e22 100644 --- a/tests/client/indexer/indexerTests.js +++ b/tests/client/indexer/indexerTests.js @@ -10,14 +10,14 @@ * Contributors: * Andrew Eisenberg (VMware) - initial API and implementation ******************************************************************************/ - + // Tests that the indexing service properly adds and retrieves indexed files. // must use a stubbed out server /*global define window console setTimeout */ -define(["plugins/esprima/indexerService", "servlets/jsdepend-client", "orion/assert"], +define(["plugins/esprima/indexerService", "servlets/jsdepend-client", "orion/assert"], function(mIndexer, jsdependsStub, assert) { - var sampleData1 = + var sampleData1 = { "utils.js": { "kind": "commonjs", "path": "utils.js", @@ -51,14 +51,14 @@ function(mIndexer, jsdependsStub, assert) { } } }; - - + + var sampleContents1 = { "utils.js" : "exports.foo = 9;", "sub/other.js" : "exports.bar = 9; exports.foo2 = require('../utils');", "main.js" : "require('./utils').foo++; require('sub/other').foo2.foo++;" }; - + var sampleSummaries1 = { "utils.js": { "kind": "commonjs", @@ -66,14 +66,14 @@ function(mIndexer, jsdependsStub, assert) { "provided": { "$$proto": { "path": "utils.js", - "typeName": "Object" + "typeSig": "Object" }, "foo": { "path": "utils.js", "range": [ 8, 11], - "typeName": "Number" + "typeSig": "Number" } }, "types": {} @@ -85,35 +85,35 @@ function(mIndexer, jsdependsStub, assert) { "provided": { "$$proto": { "path": "sub/other.js", - "typeName": "Object" + "typeSig": "Object" }, "bar": { "path": "sub/other.js", "range": [ 8, 11], - "typeName": "Number" + "typeSig": "Number" }, "foo2": { "path": "sub/other.js", "range": [ 25, 29], - "typeName": "gen~sub/other.js~4" + "typeSig": "gen~sub/other.js~4" } }, "types": { "gen~sub/other.js~4": { "$$proto": { "path": "utils.js", - "typeName": "Object" + "typeSig": "Object" }, "foo": { "path": "utils.js", "range": [ 8, 11], - "typeName": "Number" + "typeSig": "Number" } } } @@ -123,13 +123,13 @@ function(mIndexer, jsdependsStub, assert) { function toCompareString(obj) { return JSON.stringify(obj, null, ' '); } - + var persistedData = {}; var persistFn = function(key, value) { persistedData[key] = value; }; var retrieveFn = function(key) { return persistedData[key]; }; var statusFn = function(msg) { console.log(msg); }; var indexer = new mIndexer.Indexer(persistFn, retrieveFn, statusFn); - // TODO FIXADE not tested yet: retrieveGlobalSummaries + // TODO FIXADE not tested yet: retrieveGlobalSummaries var setUp = function() { persistedData = {}; @@ -139,29 +139,29 @@ function(mIndexer, jsdependsStub, assert) { }; var tests = {}; - + tests.asyncTestPerformIndex1 = function() { setUp(); indexer.performIndex("main.js", function() { // check that the proper things are persisted for (var file in sampleContents1) { if (sampleContents1.hasOwnProperty(file)) { - assert.ok(persistedData[file + "-summary"], + assert.ok(persistedData[file + "-summary"], "Expected a summary of " + file + ", instead found:\n" + toCompareString(persistedData)); - assert.ok(persistedData[file + "-summary-ts"], + assert.ok(persistedData[file + "-summary-ts"], "Expected a timestamp for summary of " + file + ", instead found:\n" + toCompareString(persistedData)); - assert.ok(persistedData[file + "-deps"], + assert.ok(persistedData[file + "-deps"], "Expected a dependency list for " + file + ", instead found:\n" + toCompareString(persistedData)); - assert.deepEqual(JSON.parse(persistedData[file + "-deps"]), sampleData1[file], + assert.deepEqual(sampleData1[file], JSON.parse(persistedData[file + "-deps"]), "Persisted dependency doesn't match provided"); - assert.ok(persistedData[file + "-deps-ts"], + assert.ok(persistedData[file + "-deps-ts"], "Expected a timestamp for the dependency list of " + file + ", instead found:\n" + toCompareString(persistedData)); } } assert.start(); }); }; - + tests.asyncTestHasDependency1 = function() { setUp(); indexer.performIndex("main.js", function() { @@ -180,7 +180,7 @@ function(mIndexer, jsdependsStub, assert) { assert.start(); }); }; - + tests.asyncTestRetrieveSummary1 = function() { setUp(); indexer.performIndex("main.js", function() { @@ -202,6 +202,6 @@ function(mIndexer, jsdependsStub, assert) { assert.start(); }); }; - + return tests; }); \ No newline at end of file From 654e8f135002eb818749d1a68cce256d09302d33 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 18 Mar 2013 16:28:23 -0700 Subject: [PATCH 32/64] make refactoring support better --- .../plugins/esprima/refactoringSupport.js | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/client/scripts/plugins/esprima/refactoringSupport.js b/client/scripts/plugins/esprima/refactoringSupport.js index 8687f383..ada92136 100644 --- a/client/scripts/plugins/esprima/refactoringSupport.js +++ b/client/scripts/plugins/esprima/refactoringSupport.js @@ -55,28 +55,46 @@ define(function (require) { if (parent.type === 'MemberExpression') { return node === parent.object; } - return parent.type === 'CallExpression' || - parent.type === 'ArrayExpression' || - parent.type === 'AssignmentExpression' || - parent.type === 'VariableDeclaration' || - parent.type === 'ExpressionStatement' || - parent.type === 'FunctionExpression' || - parent.type === 'NewExpression' || - parent.type === 'VariableDeclarator' || - parent.type === 'IfStatement' || - parent.type === 'WhileStatement' || - parent.type === 'DoWhileStatement' || - parent.type === 'FunctionExpression' || - parent.type === 'FunctionDeclaration' || - parent.type === 'ForInStatement' || - parent.type === 'ForStatement' || - parent.type === 'CatchClause' || - parent.type === 'TryStatement' || - parent.type === 'BlockStatement' || - parent.type === 'SwitchStatement' || - parent.type === 'SwitchCase' || - parent.type === 'Program' || - parent.type === 'ReturnStatement'; + + switch(parent.type) { + case 'ArrayExpression': + case 'AssignmentExpression': + case 'BinaryExpression': + case 'BlockStatement': + case 'CallExpression': + case 'CatchClause': + case 'ConditionalExpression': + case 'DoWhileStatement': + case 'ExpressionStatement': + case 'ForInStatement': + case 'ForStatement': + case 'FunctionDeclaration': + case 'FunctionExpression': + case 'IfStatement': + case 'LogicalExpression': + case 'NewExpression': + case 'Program': + case 'ReturnStatement': + case 'SwitchCase': + case 'SwitchStatement': + case 'ThrowStatemen': + case 'TryStatement': + case 'UnaryExpression': + case 'UpdateExpression': + case 'VariableDeclaration': + case 'VariableDeclarator': + case 'WhileStatement': + case 'WithStatement': + return true; + + case 'BreakStatement': + case 'Identifier': + case 'LabeledStatement': + case 'Literal': + return false; + default: // in case there's anythign I missed + console.warning("Unhandled expression type in refactoring: " + parent.type); + } } visitor.visit(root, null, function(node, context) { From 24afb611a48c5ba0cd33c9bfdd6c82af9a689057 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 18 Mar 2013 16:54:05 -0700 Subject: [PATCH 33/64] fix TypeError when there are __defineGetter__ or other synthetic references in code --- client/scripts/plugins/esprima/esprimaJsContentAssist.js | 5 +++++ client/scripts/plugins/esprima/indexerService.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index ff889f91..a8497bdc 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1500,6 +1500,11 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // check that here return !type.hasOwnProperty(prop); } + if (Object.prototype.hasOwnProperty(prop)) { + // this is one of the synthetic properties like __defineGetter__ + // should be ignored, unless already exists in property + return type.hasOwnProperty(prop); + } return true; } diff --git a/client/scripts/plugins/esprima/indexerService.js b/client/scripts/plugins/esprima/indexerService.js index 4e0bfb9f..4feae0da 100644 --- a/client/scripts/plugins/esprima/indexerService.js +++ b/client/scripts/plugins/esprima/indexerService.js @@ -45,7 +45,7 @@ function each(array, fun) { if ((this.window && this.window.Worker) && !this.window.isTest) { try { // comment this line out if you want to run w/o webworkers - worker = new Worker('/scripts/plugins/esprima/indexerWorker.js'); +// worker = new Worker('/scripts/plugins/esprima/indexerWorker.js'); } catch (e) { if (this.console) { // TODO temporarily add the popup for debugging on firefox From 4026925db1c965aff0bc4862bccd9b4634b54f95 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 21 Mar 2013 14:01:43 -0700 Subject: [PATCH 34/64] Handle the __proto__ property better. --- client/scripts/plugins/esprima/esprimaJsContentAssist.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index a8497bdc..7bd2108a 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1711,7 +1711,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * @param {Array.} docRange */ addVariable : function(name, target, typeObj, range, docRange) { - if (this._allTypes.Object["$_$" + name]) { + if (name === 'prototype' || name === '__proto__') { + name = '$$proto'; + } else if (this._allTypes.Object["$_$" + name]) { // this is a built in property of object. do not redefine return; } @@ -1746,7 +1748,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * Will not override an existing variable if the new typeName is "Object" or "undefined" */ addOrSetVariable : function(name, target, typeObj, range, docRange) { - if (name === 'prototype') { + if (name === 'prototype' || name === '__proto__') { name = '$$proto'; } else if (this._allTypes.Object["$_$" + name]) { // this is a built in property of object. do not redefine @@ -1809,6 +1811,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var swapper = function(name) { switch (name) { case "prototype": + case "__proto__": return "$$proto"; case "toString": case "hasOwnProperty": From c56d5c3da9c9e984529f20a575cab41ac4136dc5 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 21 Mar 2013 15:09:05 -0700 Subject: [PATCH 35/64] Some reasonable formatting of hovers. --- client/scripts/plugins/esprima/types.js | 208 +++++++++++++++--------- 1 file changed, 131 insertions(+), 77 deletions(-) diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index f46510ec..5aa18392 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -50,6 +50,9 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { var THE_UNKNOWN_TYPE = createNameType("Object"); var JUST_DOTS = '$$__JUST_DOTS__$$'; + var JUST_DOTS_REGEX = /\$\$__JUST_DOTS__\$\$/g; + var UNDEFINED_OR_EMPTY_OBJ = /:undefined|:\{\}/g; + /** * The Definition class refers to the declaration of an identifier. @@ -1732,13 +1735,13 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { // type styling styleAsProperty : function(prop, useHtml) { - return useHtml ? "" + prop + "": prop; + return useHtml ? '' + prop + '': prop; }, styleAsType : function(type, useHtml) { - return useHtml ? "" + type + "": type; + return useHtml ? '' + type + '': type; }, styleAsOther : function(text, useHtml) { - return useHtml ? "" + text + "": text; + return useHtml ? '' + text + '': text; }, @@ -1748,93 +1751,144 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { createReadableType : function(typeObj, env, useFunctionSig, depth, useHtml) { if (useFunctionSig) { typeObj = this.convertJsDocType(typeObj, env, true); + if (useHtml) { + return this.convertToHtml(typeObj, env, 0); + } var res = doctrine.stringify(typeObj); - // TODO g flag doesn't work on chrome and webkit - res = res.replace(JUST_DOTS, "{...}", 'g'); + res = res.replace(JUST_DOTS_REGEX, "{...}"); + res = res.replace(UNDEFINED_OR_EMPTY_OBJ, ""); return res; } else { typeObj = this.extractReturnType(typeObj); return this.createReadableType(typeObj, env, true, depth, useHtml); } }, - /** - * creates a human readable type name from the name given - */ - createReadableTypeOLD : function(typeName, env, useFunctionSig, depth, useHtml) { - depth = depth || 0; - var first = typeName.charAt(0); - if (first === "?" || first === "*") { - // a function - var returnEnd = this.findReturnTypeEnd(typeName); - if (returnEnd === -1) { - returnEnd = typeName.length; - } - var funType = typeName.substring(1, returnEnd); - if (useFunctionSig) { - // convert into a function signature - var prefix = first === "?" ? "" : "new"; - var args = typeName.substring(returnEnd+1, typeName.length); - var argsSigs = []; - var self = this; - args.split(",").forEach(function(arg) { - var typeSplit = arg.indexOf("/"); - var argName = typeSplit > 0 ? arg.substring(0, typeSplit) : arg; - argName = self.styleAsProperty(argName, useHtml); - var argSig = typeSplit > 0 ? arg.substring(typeSplit + 1) : ""; - - if (argSig) { - var sig = self.createReadableType(argSig, env, true, depth+1, useHtml); - if (sig === "{ }") { - argsSigs.push(argName); + convertToHtml : function(typeObj) { + // typeObj must already be converted to avoid infinite loops +// typeObj = this.convertJsDocType(typeObj, env, true); + var self = this; + var res; + var parts = []; + + switch(typeObj.type) { + case 'NullableLiteral': + return this.styleAsType("?", true); + case 'AllLiteral': + return this.styleAsType("*", true); + case 'NullLiteral': + return this.styleAsType("null", true); + case 'UndefinedLiteral': + return this.styleAsType("undefined", true); + case 'VoidLiteral': + return this.styleAsType("void", true); + + case 'NameExpression': + var name = typeObj.name === JUST_DOTS ? "{...}" : typeObj.name; + return this.styleAsType(name, true); + + case 'UnionType': + parts = []; + if (typeObj.expressions) { + typeObj.expressions.forEach(function(elt) { + parts.push(self.convertToHtml(elt)); + }); + } + return "( " + parts.join(", ") + " )"; + + + + case 'TypeApplication': + if (typeObj.applications) { + typeObj.applications.forEach(function(elt) { + parts.push(self.convertToHtml(elt)); + }); + } + var isArray = typeObj.expression.name === 'Array'; + if (!isArray) { + res = this.convertToHtml(typeObj.expression) + ".<"; + } + res += parts.join(","); + if (isArray) { + res += '[]'; + } else { + res += ">"; + } + return res; + case 'ArrayExpressopm': + if (typeObj.applications) { + typeObj.applications.forEach(function(elt) { + parts.push(self.convertToHtml(elt)); + }); + } + return parts.join(", ") + '[]'; + + case 'NonNullableType': + return "!" + this.convertToHtml(typeObj.expression); + case 'OptionalType': + return this.convertToHtml(typeObj.expression) + "="; + case 'NullableType': + return "?" + this.convertToHtml(typeObj.expression); + case 'RestType': + return "..." + this.convertToHtml(typeObj.expression); + + case 'ParameterType': + return this.styleAsProperty(typeObj.name, true) + + (typeObj.expression.name === JUST_DOTS ? "" : (":" + this.convertToHtml(typeObj.expression))); + + case 'FunctionType': + var isCons = false; + var resType; + if (typeObj.params) { + typeObj.params.forEach(function(elt) { + if (elt.name === 'this') { + isCons = true; + resType = elt.expression; + } else if (elt.name === 'new') { + isCons = true; + resType = elt.expression; } else { - argsSigs.push(argName + ":" + sig); + parts.push(self.convertToHtml(elt)); } - } else { - argsSigs.push(argName); - } - }); + }); + } - // note the use of the ⇒ ⇒ char here. Must use the char directly since js_render will format it otherwise - return prefix + "(" + argsSigs.join(", ") + - (useHtml ? ")
    " + proposalUtils.repeatChar("  ", depth+1) + "⇒ " : ") ⇒ ") + - this.createReadableType(funType, env, true, depth + 1, useHtml); - } else { - // use the return type - return this.createReadableType(funType, env, true, depth, useHtml); - } - } else if (typeName.indexOf(this.GEN_NAME) === 0) { - // a generated object - if (depth > 1) { - // don't show inner types - return this.styleAsOther("{...}", useHtml); - } + if (!resType && typeObj.result) { + resType = typeObj.result; + } - // create a summary - var type = env.findType(typeName); - var res = "{ "; - var props = []; - for (var val in type) { - if (type.hasOwnProperty(val) && val !== "$$proto") { - var name; - // don't show inner objects - name = this.createReadableType(type[val].typeObj, env, true, depth + 1, useHtml); - props.push((useHtml ? "
    " + proposalUtils.repeatChar("    ", depth+1) : "" ) + - this.styleAsProperty(val, useHtml) + ":" + name); + var resText; + if (resType && resType.type !== 'UndefinedLiteral' && resType.name !== 'undefined') { + resText = this.convertToHtml(resType); + } else { + resText = ''; } - } - res += props.join(", "); - return res + " }"; - } else if (this.isArrayType(typeName)) { - var typeParameter = this.extractArrayParameterType(typeName); - if (typeParameter !== "Object") { - typeName = this.createReadableType(typeParameter, env, true, depth+1, useHtml) + "[]"; - } else { - typeName = "[]"; - } - return typeName; - } else { - return this.styleAsType(typeName, useHtml); + res = this.styleAsOther(isCons ? 'new ' : 'function', true); + if (isCons) { + res += resText; + } + res += '(' + parts.join(",") + ')'; + if (!isCons && resText) { + res += '→' + resText; + } + + return res; + + case 'RecordType': + if (typeObj.fields && typeObj.fields.length > 0) { + typeObj.fields.forEach(function(elt) { + parts.push('  ' + self.convertToHtml(elt)); + }); + return '{
    ' + parts.join(',
    ') + '
    }'; + } else { + return '{ }'; + } + break; + + case 'FieldType': + return this.styleAsProperty(typeObj.key, true) + + ":" + this.convertToHtml(typeObj.value); } + }, ensureTypeObject: ensureTypeObject, OBJECT_TYPE: THE_UNKNOWN_TYPE, From a82517744df07f99e28e3b98facef75ecd247afa Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 21 Mar 2013 22:29:02 -0700 Subject: [PATCH 36/64] Cut out too much from the summaries, but at least now localStorage doesn't overflow. Tests are failing. --- client/scripts/main.js | 2 +- .../plugins/esprima/esprimaJsContentAssist.js | 201 +++++++++++++----- client/scripts/plugins/esprima/types.js | 52 ++--- client/scripts/scripted/utils/storage.js | 2 +- 4 files changed, 173 insertions(+), 84 deletions(-) diff --git a/client/scripts/main.js b/client/scripts/main.js index c1aac17d..aec8b884 100644 --- a/client/scripts/main.js +++ b/client/scripts/main.js @@ -74,7 +74,7 @@ define({ }, plugins : [ - { module : 'wire/debug' }, +// { module : 'wire/debug' }, { module : 'wire/jquery/dom' } ] }); diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 7bd2108a..bcdb53cc 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1403,6 +1403,39 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr return string.substring(prefix.length); } + function isEmpty(generatedTypeName, allTypes) { + if (typeof generatedTypeName !== 'string') { + // original type was not a name expression + return false; + } else if (generatedTypeName === "Object" || generatedTypeName === "undefined") { + return true; + } else if (generatedTypeName.substring(0, mTypes.GEN_NAME.length) !== mTypes.GEN_NAME) { + // not a synthetic type, so not empty + return false; + } + + + // now check to see if there are any non-default fields in this type + var type = allTypes[generatedTypeName]; + var popCount = 0; + // type should have a $$proto only and nothing else if it is empty + for (var property in type) { + if (type.hasOwnProperty(property)) { + popCount++; + if (popCount > 1) { + break; + } + } + } + if (popCount === 1) { + // we have an empty object literal, must check parent + // must traverse prototype hierarchy to make sure empty + return isEmpty(type.$$proto.typeObj.name, allTypes); + } + return false; + } + + /** * Determines if the left type name is more general than the right type name. * Generality (>) is defined as follows: @@ -1426,44 +1459,13 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr leftTypeName = 'undefined'; } } - function isEmpty(generatedTypeName) { - if (typeof generatedTypeName !== 'string') { - // original type was not a name expression - return false; - } else if (generatedTypeName === "Object" || generatedTypeName === "undefined") { - return true; - } else if (generatedTypeName.substring(0, mTypes.GEN_NAME.length) !== mTypes.GEN_NAME) { - // not a synthetic type, so not empty - return false; - } - - - // now check to see if there are any non-default fields in this type - var type = env.getAllTypes()[generatedTypeName]; - var popCount = 0; - // type should have a $$proto only and nothing else if it is empty - for (var property in type) { - if (type.hasOwnProperty(property)) { - popCount++; - if (popCount > 1) { - break; - } - } - } - if (popCount === 1) { - // we have an empty object literal, must check parent - // must traverse prototype hierarchy to make sure empty - return isEmpty(type.$$proto.typeObj.name); - } - return false; - } function convertToNumber(typeName) { if (typeName === "undefined") { return 0; } else if (typeName === "Object") { return 1; - } else if (isEmpty(typeName)) { + } else if (isEmpty(typeName, env.getAllTypes())) { return 2; } else { return 3; @@ -1481,7 +1483,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } else if (leftNum === 1) { return rightTypeName !== "undefined" && rightTypeName !== "Object"; } else if (leftNum === 2) { - return rightTypeName !== "undefined" && rightTypeName !== "Object" && !isEmpty(rightTypeName); + return rightTypeName !== "undefined" && rightTypeName !== "Object" && !isEmpty(rightTypeName, env.getAllTypes()); } else { return false; } @@ -1527,10 +1529,27 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } + + // create a hash of the path using a hashcode calculation similar to java's String.hashCode() method + var hashCode = function(str) { + var hash = 0, c, i; + if (str.length === 0) { + return hash; + } + for (i = 0; i < str.length; i++) { + c = str.charCodeAt(i); + hash = ((hash << 5) - hash) + c; + hash = hash & hash; // Convert to 32bit integer + } + return hash; + }; + // prefix for generating local types // need to add a unique id for each file so that types defined in dependencies don't clash with types // defined locally - var namePrefix = mTypes.GEN_NAME + uid + "~"; + var namePrefix = mTypes.GEN_NAME + hashCode(uid) + "~"; + // uncomment to show names +// var namePrefix = mTypes.GEN_NAME + (uid) + "~"; return { /** Each element is the type of the current scope, which is a key into the types array */ @@ -2077,7 +2096,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } - function visitTypeStructure(typeObj, allTypes, alreadySeen) { + function visitTypeStructure(typeObj, operation) { if (typeof typeObj !== 'object') { return; } @@ -2091,56 +2110,51 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr return; case 'NameExpression': - if (alreadySeen[typeObj.name]) { - // prevent infinite recursion for circular refs - return; - } - alreadySeen[typeObj.name] = true; - findUnreachable(typeObj.name, allTypes, alreadySeen); + operation(typeObj, operation); return; case 'ArrayType': - visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + visitTypeStructure(typeObj.expression, operation); // fall-through case 'UnionType': if (typeObj.elements) { - typeObj.elements.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + typeObj.elements.forEach(function(elt) { visitTypeStructure(elt, operation); }); } return; case 'RecordType': if (typeObj.fields) { - typeObj.fields.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + typeObj.fields.forEach(function(elt) { visitTypeStructure(elt, operation); }); } return; case 'FieldType': - visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + visitTypeStructure(typeObj.expression, operation); return; case 'FunctionType': // do we need to check for serialized functions??? if (typeObj.params) { - typeObj.params.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + typeObj.params.forEach(function(elt) { visitTypeStructure(elt, operation); }); } if (typeObj.result) { - visitTypeStructure(typeObj.result, allTypes, alreadySeen); + visitTypeStructure(typeObj.result, operation); } if (typeObj['this']) { - visitTypeStructure(typeObj['this'], allTypes, alreadySeen); + visitTypeStructure(typeObj['this'], operation); } if (typeObj['new']) { - visitTypeStructure(typeObj['new'], allTypes, alreadySeen); + visitTypeStructure(typeObj['new'], operation); } return; case 'ParameterType': - visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + visitTypeStructure(typeObj.expression, operation); return; case 'TypeApplication': if (typeObj.applications) { - typeObj.applications.forEach(function(elt) { visitTypeStructure(elt, allTypes, alreadySeen); }); + typeObj.applications.forEach(function(elt) { visitTypeStructure(elt, operation); }); } // fall-throudh @@ -2148,7 +2162,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr case 'NonNullableType': case 'OptionalType': case 'NullableType': - visitTypeStructure(typeObj.expression, allTypes, alreadySeen); + visitTypeStructure(typeObj.expression, operation); return; @@ -2159,17 +2173,55 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // and marks them as already seen function findUnreachable(currentTypeName, allTypes, alreadySeen) { var currentType = allTypes[currentTypeName]; + var operation = function(typeObj, operation) { + if (alreadySeen[typeObj.name]) { + // prevent infinite recursion for circular refs + return; + } + + alreadySeen[typeObj.name] = true; + findUnreachable(typeObj.name, allTypes, alreadySeen); + }; + if (currentType) { for(var prop in currentType) { if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { var propType = currentType[prop].typeObj; // must visit the type strucutre - visitTypeStructure(propType, allTypes, alreadySeen); + visitTypeStructure(propType, operation); } } } } + function fixMissingPointers(currentTypeName, allTypes, empties, alreadySeen) { + alreadySeen = alreadySeen || {}; + var currentType = allTypes[currentTypeName]; + var operation = function(typeObj, operation) { + if (alreadySeen[typeObj.name]) { + return; + } + while (empties[typeObj.name]) { + // change this to the first non-epty prototype of the empty type + typeObj.name = allTypes[typeObj.name].$$proto.name; + if (!typeObj.name) { + typeObj.name = 'Object'; + } + } + alreadySeen[typeObj.name] = true; + fixMissingPointers(typeObj.name, allTypes, empties, alreadySeen); + }; + + if (currentType) { + for(var prop in currentType) { + if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { + var propType = currentType[prop].typeObj; + // must visit the type strucutre + visitTypeStructure(propType, operation); + } + } + } + } /** * filters types from the environment that should not be exported */ @@ -2179,16 +2231,49 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // recursively walk the type tree to find unreachable types and delete them, too var reachable = { }; + var wasReachable = true; if (moduleTypeObj.type !== "NameExpression") { - visitTypeStructure(moduleTypeObj, allTypes, reachable); + // TODO FIXADE duplicated code + visitTypeStructure(moduleTypeObj, function(typeObj, operation) { + if (reachable[typeObj.name]) { + // prevent infinite recursion for circular refs + return; + } + reachable[typeObj.name] = true; + findUnreachable(typeObj.name, allTypes, reachable); + }); + } else { + // first remove any types that are unreachable + findUnreachable(moduleTypeName, allTypes, reachable); + if (!reachable[moduleTypeName]) { + // not really reachable, but need to keep it for now to track empties + reachable[moduleTypeName] = true; + wasReachable = false; + } + for (var prop in allTypes) { + if (allTypes.hasOwnProperty(prop) && !reachable[prop]) { + delete allTypes[prop]; + } + } } - findUnreachable(moduleTypeName, allTypes, reachable); - for (var prop in allTypes) { - if (allTypes.hasOwnProperty(prop) && !reachable[prop]) { - delete allTypes[prop]; + // now find empty types + var empties = {}; + Object.keys(allTypes).forEach(function(key) { + if (isEmpty(key, allTypes)) { + empties[key] = true; } + }); + // now fix up pointers to empties + fixMissingPointers(moduleTypeName, allTypes, empties); + + if (!wasReachable) { + delete allTypes[moduleTypeName]; } + // don't need the empty types any more + Object.keys(empties).forEach(function(key) { + delete allTypes[key]; + }); // now reformat the types so that they are combined and serialized Object.keys(allTypes).forEach(function(typeName) { diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 5aa18392..4f3b8cf9 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -347,42 +347,38 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { Node : new Definition("function(new:Node):Node") }; - var initialGlobalProperties = []; - for (var prop in Global) { - if (Global.hasOwnProperty(prop)) { - initialGlobalProperties.push(prop); - } - } + var initialGlobalProperties = {}; + Object.keys(Global.prototype).forEach(function(key) { + initialGlobalProperties[key] = true; + }); + Object.keys(Window.prototype).forEach(function(key) { + initialGlobalProperties[key] = true; + }); + Object.keys(Module.prototype).forEach(function(key) { + initialGlobalProperties[key] = true; + }); - for (prop in Window) { - if (Window.hasOwnProperty(prop)) { - initialGlobalProperties.push(prop); - } - } /** * A prototype that contains the common built-in types */ var Types = function(globalObjName) { - + var globObj; // this object can be touched by clients // and so must not be in the prototype // the global 'this' if (globalObjName === 'Window') { - this.Window = new Window(); + globObj = this.Window = new Window(); } else if (globalObjName === 'Module') { - this.Module = new Module(); + globObj = this.Module = new Module(); } else { - this.Global = new Global(); + globObj = this.Global = new Global(); } - // TODO FIXADE should be declared on prototype this.clearDefaultGlobal = function() { - for (var i = 0; i < initialGlobalProperties.length; i++) { - if (this.Global[initialGlobalProperties[i]]) { - delete this.Global[initialGlobalProperties[i]]; - } - } + Object.keys(initialGlobalProperties).forEach(function(key) { + delete globObj[key]; + }); }; }; @@ -1560,18 +1556,26 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'FunctionType': var fnType = { type: jsdocType.type, - result: self.convertJsDocType(jsdocType.result, env, doCombine, depth+1), params: jsdocType.params.map(function(elt) { return self.convertJsDocType(elt, env, doCombine, depth+1); }) }; + if (jsdocType.result) { + // prevent recursion on functions that return themselves + fnType.result = depth < 2 ? self.convertJsDocType(jsdocType.result, env, doCombine, depth+1) : + { type : 'NameExpression', name : JUST_DOTS }; + } if (jsdocType['new']) { - fnType['new'] = self.convertJsDocType(jsdocType['new'], env, doCombine, depth+1); + // prevent recursion on functions that return themselves + fnType['new'] = depth < 2 ? self.convertJsDocType(jsdocType['new'], env, doCombine, depth+1) : + { type : 'NameExpression', name : JUST_DOTS }; } if (jsdocType['this']) { - fnType['this'] = self.convertJsDocType(jsdocType['this'], env, doCombine, depth+1); + // prevent recursion on functions that return themselves + fnType['this'] = depth < 2 ? self.convertJsDocType(jsdocType['this'], env, doCombine, depth+1) : + { type : 'NameExpression', name : JUST_DOTS }; } return fnType; diff --git a/client/scripts/scripted/utils/storage.js b/client/scripts/scripted/utils/storage.js index c64fba4c..2c989cf7 100644 --- a/client/scripts/scripted/utils/storage.js +++ b/client/scripts/scripted/utils/storage.js @@ -43,7 +43,7 @@ define(["scriptedLogger"], function(scriptedLogger) { if (depth < thresholds.length) { localStorage.setItem(key, value); } else { - scriptedLogger.warn("Tried to add to local storage: " + key + " : " + value, "STORAGE"); + scriptedLogger.warn("Tried to add to local storage: " + key, "STORAGE"); scriptedLogger.warn("Tried too many times. Ignoring request.", "STORAGE"); } } catch (e) { From 5675d323be6a685368c9d35c3a0371773c025bb1 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 21 Mar 2013 22:29:23 -0700 Subject: [PATCH 37/64] return to background indexing --- client/scripts/plugins/esprima/indexerService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/scripts/plugins/esprima/indexerService.js b/client/scripts/plugins/esprima/indexerService.js index 4feae0da..4e0bfb9f 100644 --- a/client/scripts/plugins/esprima/indexerService.js +++ b/client/scripts/plugins/esprima/indexerService.js @@ -45,7 +45,7 @@ function each(array, fun) { if ((this.window && this.window.Worker) && !this.window.isTest) { try { // comment this line out if you want to run w/o webworkers -// worker = new Worker('/scripts/plugins/esprima/indexerWorker.js'); + worker = new Worker('/scripts/plugins/esprima/indexerWorker.js'); } catch (e) { if (this.console) { // TODO temporarily add the popup for debugging on firefox From 8340271bd0b3b77dff84b8e16edcc0545362c64d Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 22 Mar 2013 14:00:38 -0700 Subject: [PATCH 38/64] Final fixes for crossfile indexing with new type structure still too large --- .../plugins/esprima/esprimaJsContentAssist.js | 63 +++++++++++-------- client/scripts/scripted/utils/storage.js | 2 +- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index bcdb53cc..90571dce 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1574,7 +1574,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr offset : offset, /** the entire contents being completed on */ contents : buffer, - uid : uid === 'local' ? null : uid, + uid : uid === 'local' ? null : uid, // make the uid shorter /** List of comments in the AST*/ comments : comments, @@ -1891,7 +1891,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // create type if doesn't already exist, othewise merge var type = this._allTypes[typeName]; var existingType = summary.types[typeName]; - if (!type) { + // if doesn't exist yet create it + // if type is built-in, then we must overwrite it with ours + if (!type || type.$$isBuiltin) { type = this._allTypes[typeName] = {}; } @@ -2052,8 +2054,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr for (var prop in type) { if (isInterestingProperty(type, prop)) { var propType = type[prop].typeObj; - var first = propType.charAt(0); - if (first === "?" || first === "*") { + if (propType.type === 'FunctionType') { var res = calculateFunctionProposal(prop, propType, replaceStart - 1); proposals[prop] = { proposal: removePrefix(prefix, res.completion), @@ -2140,15 +2141,18 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (typeObj.result) { visitTypeStructure(typeObj.result, operation); } - if (typeObj['this']) { - visitTypeStructure(typeObj['this'], operation); - } - if (typeObj['new']) { - visitTypeStructure(typeObj['new'], operation); - } + // not correct. this and new are folded into result +// if (typeObj['this']) { +// visitTypeStructure(typeObj['this'], operation); +// } +// if (typeObj['new']) { +// visitTypeStructure(typeObj['new'], operation); +// } return; case 'ParameterType': + // TODO FIXADE this will make the size of summaries smaller + typeObj.expression = { name: 'Object', type: 'NameExpression' }; visitTypeStructure(typeObj.expression, operation); return; @@ -2194,9 +2198,13 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } - function fixMissingPointers(currentTypeName, allTypes, empties, alreadySeen) { + /** + * Before we can remove empty objects from the type graph, we need to update + * the properties currently pointing to those types. Make them point to the + * closest non-empty type in their prototype hierarchy (most likely, this is Object). + */ + function fixMissingPointers(currentTypeObj, allTypes, empties, alreadySeen) { alreadySeen = alreadySeen || {}; - var currentType = allTypes[currentTypeName]; var operation = function(typeObj, operation) { if (alreadySeen[typeObj.name]) { return; @@ -2209,18 +2217,19 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } } alreadySeen[typeObj.name] = true; - fixMissingPointers(typeObj.name, allTypes, empties, alreadySeen); - }; - - if (currentType) { - for(var prop in currentType) { - if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { - var propType = currentType[prop].typeObj; - // must visit the type strucutre - visitTypeStructure(propType, operation); + var currentType = allTypes[typeObj.name]; + if (currentType) { + for(var prop in currentType) { + if (currentType.hasOwnProperty(prop) && prop !== '$$isBuiltin' ) { + var propType = currentType[prop].typeObj; + // must visit the type strucutre + visitTypeStructure(propType, operation); + } } } - } + }; + + visitTypeStructure(currentTypeObj, operation); } /** * filters types from the environment that should not be exported @@ -2250,10 +2259,10 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr reachable[moduleTypeName] = true; wasReachable = false; } - for (var prop in allTypes) { - if (allTypes.hasOwnProperty(prop) && !reachable[prop]) { - delete allTypes[prop]; - } + } + for (var prop in allTypes) { + if (allTypes.hasOwnProperty(prop) && !reachable[prop]) { + delete allTypes[prop]; } } @@ -2265,7 +2274,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } }); // now fix up pointers to empties - fixMissingPointers(moduleTypeName, allTypes, empties); + fixMissingPointers(moduleTypeObj, allTypes, empties); if (!wasReachable) { delete allTypes[moduleTypeName]; diff --git a/client/scripts/scripted/utils/storage.js b/client/scripts/scripted/utils/storage.js index 2c989cf7..4a8b462b 100644 --- a/client/scripts/scripted/utils/storage.js +++ b/client/scripts/scripted/utils/storage.js @@ -48,7 +48,7 @@ define(["scriptedLogger"], function(scriptedLogger) { } } catch (e) { if (e.name.indexOf('QUOTA')>=0 || e.name === "QuotaExceededError") { // Chrome: "QUOTA_EXCEEDED_ERR" or "QuotaExceededError", FireFox: "NS_ERROR_DOM_QUOTA_REACHED", other browsers - scriptedLogger.warn("Tried to add to local storage: " + key + " : " + value, "STORAGE"); + scriptedLogger.warn("Tried to add to local storage: " + key + " : " + value.toString().length+ " bytes", "STORAGE"); scriptedLogger.warn("Local storage quota exceeded. Purging parts of local storage and trying again", "STORAGE"); if (thresholds[depth]) { var threshold = thresholds[depth]; From 9745bf6f5a01f21ce06811e8a04df41e3974320d Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 22 Mar 2013 14:00:59 -0700 Subject: [PATCH 39/64] Better formatting of type hovers. Indenting object literals. --- client/scripts/plugins/esprima/types.js | 33 +++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 4f3b8cf9..c89cc167 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1756,7 +1756,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { if (useFunctionSig) { typeObj = this.convertJsDocType(typeObj, env, true); if (useHtml) { - return this.convertToHtml(typeObj, env, 0); + return this.convertToHtml(typeObj, 0); } var res = doctrine.stringify(typeObj); res = res.replace(JUST_DOTS_REGEX, "{...}"); @@ -1767,12 +1767,13 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return this.createReadableType(typeObj, env, true, depth, useHtml); } }, - convertToHtml : function(typeObj) { + convertToHtml : function(typeObj, depth) { // typeObj must already be converted to avoid infinite loops // typeObj = this.convertJsDocType(typeObj, env, true); var self = this; var res; var parts = []; + depth = depth || 0; switch(typeObj.type) { case 'NullableLiteral': @@ -1794,7 +1795,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { parts = []; if (typeObj.expressions) { typeObj.expressions.forEach(function(elt) { - parts.push(self.convertToHtml(elt)); + parts.push(self.convertToHtml(elt, depth+1)); }); } return "( " + parts.join(", ") + " )"; @@ -1804,12 +1805,12 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'TypeApplication': if (typeObj.applications) { typeObj.applications.forEach(function(elt) { - parts.push(self.convertToHtml(elt)); + parts.push(self.convertToHtml(elt, depth)); }); } var isArray = typeObj.expression.name === 'Array'; if (!isArray) { - res = this.convertToHtml(typeObj.expression) + ".<"; + res = this.convertToHtml(typeObj.expression, depth) + ".<"; } res += parts.join(","); if (isArray) { @@ -1821,23 +1822,23 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'ArrayExpressopm': if (typeObj.applications) { typeObj.applications.forEach(function(elt) { - parts.push(self.convertToHtml(elt)); + parts.push(self.convertToHtml(elt, depth+1)); }); } return parts.join(", ") + '[]'; case 'NonNullableType': - return "!" + this.convertToHtml(typeObj.expression); + return "!" + this.convertToHtml(typeObj.expression, depth); case 'OptionalType': - return this.convertToHtml(typeObj.expression) + "="; + return this.convertToHtml(typeObj.expression, depth) + "="; case 'NullableType': - return "?" + this.convertToHtml(typeObj.expression); + return "?" + this.convertToHtml(typeObj.expression, depth); case 'RestType': - return "..." + this.convertToHtml(typeObj.expression); + return "..." + this.convertToHtml(typeObj.expression, depth); case 'ParameterType': return this.styleAsProperty(typeObj.name, true) + - (typeObj.expression.name === JUST_DOTS ? "" : (":" + this.convertToHtml(typeObj.expression))); + (typeObj.expression.name === JUST_DOTS ? "" : (":" + this.convertToHtml(typeObj.expression, depth))); case 'FunctionType': var isCons = false; @@ -1851,7 +1852,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { isCons = true; resType = elt.expression; } else { - parts.push(self.convertToHtml(elt)); + parts.push(self.convertToHtml(elt, depth+1)); } }); } @@ -1862,7 +1863,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { var resText; if (resType && resType.type !== 'UndefinedLiteral' && resType.name !== 'undefined') { - resText = this.convertToHtml(resType); + resText = this.convertToHtml(resType, depth+1); } else { resText = ''; } @@ -1880,9 +1881,9 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'RecordType': if (typeObj.fields && typeObj.fields.length > 0) { typeObj.fields.forEach(function(elt) { - parts.push('  ' + self.convertToHtml(elt)); + parts.push(proposalUtils.repeatChar('  ', depth+1) + self.convertToHtml(elt, depth+1)); }); - return '{
    ' + parts.join(',
    ') + '
    }'; + return '{
    ' + parts.join(',
    ') + '
    ' + proposalUtils.repeatChar('  ', depth) + '}'; } else { return '{ }'; } @@ -1890,7 +1891,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'FieldType': return this.styleAsProperty(typeObj.key, true) + - ":" + this.convertToHtml(typeObj.value); + ":" + this.convertToHtml(typeObj.value, depth); } }, From 1a05f331dd3ef03156e11dc43cd1fb772f0a4d0f Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 22 Mar 2013 15:35:55 -0700 Subject: [PATCH 40/64] Summary size much smaller now. Don't try to merge type from summary into an existing type. This bloats things too much. --- .../plugins/esprima/esprimaJsContentAssist.js | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 90571dce..4079497e 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1895,15 +1895,15 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // if type is built-in, then we must overwrite it with ours if (!type || type.$$isBuiltin) { type = this._allTypes[typeName] = {}; - } - - // for each property defined in the type from the sumamry, - // also add it to the current module's type - for (var typeProp in existingType) { - if (!type[typeProp]) { - type[typeProp] = mTypes.Definition.revive(existingType[typeProp]); + // for each property defined in the type from the sumamry, + // also add it to the current module's type + for (var typeProp in existingType) { + if (!type[typeProp]) { + type[typeProp] = mTypes.Definition.revive(existingType[typeProp]); + } } } + } } @@ -2630,14 +2630,19 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // assume a non-module providedType = environment.globalScope(); - if (providedType.exports) { + // if there is an exports global or a module.exports global, then assume commonjs + var maybeExports = providedType.exports || + (providedType.module && environment.getAllTypes()[providedType.module.typeObj.name] && + environment.getAllTypes()[providedType.module.typeObj.name].exports); + + if (maybeExports) { // actually, commonjs kind = "commonjs"; - modTypeObj = providedType.exports.typeObj; - modTypeName = doctrine.stringify(modTypeObj); + modTypeObj = maybeExports.typeObj; + modTypeName = doctrine.stringify(modTypeObj); } else { kind = "global"; - modTypeName = environment.globalTypeName(); + modTypeName = environment.globalTypeName(); modTypeObj = providedType['this'].typeObj; } } From a0f79d956d7c9a8564744ce7c80127099404aa42 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sun, 24 Mar 2013 09:09:21 -0700 Subject: [PATCH 41/64] Very simple inferencing for callback functions parameters --- .../plugins/esprima/esprimaJsContentAssist.js | 61 +++++++++++++++++-- client/scripts/plugins/esprima/types.js | 30 +++++---- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 4079497e..1e3b80b1 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -769,6 +769,18 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } isConstructor = false; } + + var callArgs = new Array(params.length); + if (node.extras.paramTypeObj && node.extras.paramTypeObj.type === 'FunctionType') { + // this function is an anonymous function being passed as an argument + // to another function and we have a hint of what the function type is + // shunt the argument types to this function's arguments + var paramTypes = node.extras.paramTypeObj.params; + var len = Math.min(params.length || 0, paramTypes.length || 0); + for (var i = 0; i < len; i++) { + callArgs[i] = paramTypes[i]; + } + } if (!node.body.extras) { node.body.extras = {}; } @@ -779,15 +791,20 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (params.length > 0) { var moduleDefs = findModuleDefinitions(node, env); for (i = 0; i < params.length; i++) { - // choose jsdoc tags over module definitions + // choose jsdoc tags over module definitions and both of those over call args var jsDocParam = jsdocResult.params[params[i]]; var paramTypeObj = null; if (jsDocParam) { paramTypeObj = mTypes.convertJsDocType(jsDocParam, env); - } - if (!paramTypeObj) { + } else { paramTypeObj = moduleDefs[i]; } + if (callArgs[i] && leftTypeIsMoreGeneral(paramTypeObj, callArgs[i], env)) { + // unwrap parameter type since name is probably wrong + // TODO if param is wrapped in a RestType, then problem + paramTypeObj = callArgs[i].type === 'ParameterType' ? callArgs[i].expression : callArgs[i]; + } + paramTypeObjs.push(mTypes.createParamType(params[i], paramTypeObj)); } } @@ -912,8 +929,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr break; case "MemberExpression": if (node.property) { - if (!node.computed || // like this: foo.at - (node.computed && node.property.type === "Literal" && typeof node.property.value === "string")) { // like this: foo['at'] + if (!node.computed || // like this: foo.prop + (node.computed && node.property.type === "Literal" && typeof node.property.value === "string")) { // like this: foo['prop'] // keep track of the target of the property expression // so that its type can be used as the seed for finding properties @@ -921,7 +938,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr node.property.extras = {}; } node.property.extras.target = node.object; - } else { // like this: foo[at] or foo[0] + } else { // like this: foo[prop] or foo[0] // do nothing } } @@ -940,6 +957,14 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } args[args.length-1].extras.amdDefn = node; } + } else { + // the type of the function call may help infer the types of the parameters + // keep track of that here + rightMost = findRightMost(node.callee); + if (rightMost.type === "Identifier") { + rightMost.extras = rightMost.extras || {}; + rightMost.extras.callArgs = node["arguments"]; + } } break; } @@ -1264,6 +1289,30 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // We found it! rmember for later, but continue to the end of file anyway env.storeTarget(env.scope(node.extras.target)); } + + // if this identifier refers to a function call, and we know the argument types, then push on to the arg nodes + if (node.extras.callArgs) { + if (newTypeObj.type === 'FunctionType') { + // match param types with args + var paramTypes = newTypeObj.params; + var args = node.extras.callArgs; + var len = Math.min(args.length || 0, paramTypes.length || 0); + for (var i = 0; i < len; i++) { + args[i].extras = args[i].extras || {}; + args[i].extras.paramTypeObj = paramTypes[i].type === 'ParameterType' ? paramTypes[i].expression : paramTypes[i]; + } + } + } + + if (node.extras.paramTypeObj) { + // this identifier is an argument of a function call whose type we know + if (leftTypeIsMoreGeneral(newTypeObj, node.extras.paramTypeObj, env)) { + // the param type is more specific, use that one instead of the otherwise inferred type + node.extras.inferredTypeObj = newTypeObj = node.extras.paramTypeObj; + env.addOrSetVariable(name, node.extras.target /* should be null */, newTypeObj); + } + } + } else if (!node.extras.isLHS) { if (!inRange(env.offset, node.range, true) && !isReserverdWord(name)) { // we have encountered a read of a variable/property that we have never seen before diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index c89cc167..b758a06f 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1557,26 +1557,30 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { var fnType = { type: jsdocType.type, params: jsdocType.params.map(function(elt) { - return self.convertJsDocType(elt, env, doCombine, depth+1); + return self.convertJsDocType(elt, env, doCombine, depth); }) }; if (jsdocType.result) { // prevent recursion on functions that return themselves - fnType.result = depth < 2 ? self.convertJsDocType(jsdocType.result, env, doCombine, depth+1) : - { type : 'NameExpression', name : JUST_DOTS }; - } - - if (jsdocType['new']) { - // prevent recursion on functions that return themselves - fnType['new'] = depth < 2 ? self.convertJsDocType(jsdocType['new'], env, doCombine, depth+1) : + fnType.result = depth < 2 && jsdocType.result.type === 'FunctionType' ? + self.convertJsDocType(jsdocType.result, env, doCombine, depth) : { type : 'NameExpression', name : JUST_DOTS }; } - if (jsdocType['this']) { - // prevent recursion on functions that return themselves - fnType['this'] = depth < 2 ? self.convertJsDocType(jsdocType['this'], env, doCombine, depth+1) : - { type : 'NameExpression', name : JUST_DOTS }; - } + // TODO should remove? new and this are folded into params +// if (jsdocType['new']) { +// // prevent recursion on functions that return themselves +// fnType['new'] = depth < 2 && jsdocType['new'].type === 'FunctionType' ? +// self.convertJsDocType(jsdocType['new'], env, doCombine, depth) : +// { type : 'NameExpression', name : JUST_DOTS }; +// } +// +// if (jsdocType['this']) { +// // prevent recursion on functions that return themselves +// fnType['this'] = depth < 2 && jsdocType['this'].type === 'FunctionType' ? +// self.convertJsDocType(jsdocType['this'], env, doCombine, depth) : +// { type : 'NameExpression', name : JUST_DOTS }; +// } return fnType; From 4dc414be4b25ea7a2bf6c31b28bbfc7c86832c10 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sun, 24 Mar 2013 20:03:24 -0700 Subject: [PATCH 42/64] Fix NPE for some kinds of args. --- client/scripts/plugins/esprima/esprimaJsContentAssist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 1e3b80b1..b44f650c 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -961,7 +961,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // the type of the function call may help infer the types of the parameters // keep track of that here rightMost = findRightMost(node.callee); - if (rightMost.type === "Identifier") { + if (rightMost && rightMost.type === "Identifier") { rightMost.extras = rightMost.extras || {}; rightMost.extras.callArgs = node["arguments"]; } From 135e548f35bbae66424c6ce729176af9dfa0f7f7 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 25 Mar 2013 14:56:14 -0700 Subject: [PATCH 43/64] All tests passing again Added new tests for summary type compression --- .../plugins/esprima/esprimaJsContentAssist.js | 56 ++-- client/scripts/plugins/esprima/types.js | 31 ++- .../esprima/esprimaJsContentAssistTests.js | 6 +- tests/client/esprima/navigationTests.js | 37 ++- tests/client/esprima/summaryBuildingTests.js | 247 +++++++++++++++--- tests/client/indexer/indexerTests.js | 4 +- 6 files changed, 287 insertions(+), 94 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index b44f650c..4bd8ca1d 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1743,8 +1743,15 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (inferredTypeObj.type === 'NameExpression') { return inferredTypeObj.name; } else if (inferredTypeObj.type === 'FunctionType') { - if (inferredTypeObj['new'] && inferredTypeObj['new'].name) { - return inferredTypeObj['new'].name; + if (inferredTypeObj.params) { + for (var i = 0; i < inferredTypeObj.params.length; i++) { + if ((inferredTypeObj.params[i].name === 'new' || + inferredTypeObj.params[i].name === 'this') && + inferredTypeObj.params[i].expression.name) { + + return inferredTypeObj.params[i].expression.name; + } + } } return "Function"; } else if (inferredTypeObj.type === 'ArrayType') { @@ -2190,18 +2197,12 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (typeObj.result) { visitTypeStructure(typeObj.result, operation); } - // not correct. this and new are folded into result -// if (typeObj['this']) { -// visitTypeStructure(typeObj['this'], operation); -// } -// if (typeObj['new']) { -// visitTypeStructure(typeObj['new'], operation); -// } return; case 'ParameterType': - // TODO FIXADE this will make the size of summaries smaller - typeObj.expression = { name: 'Object', type: 'NameExpression' }; + // TODO FIXADE uncomment to make the size of summaries smaller + // by not including parameter types in summary. +// typeObj.expression = { name: 'Object', type: 'NameExpression' }; visitTypeStructure(typeObj.expression, operation); return; @@ -2255,16 +2256,16 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr function fixMissingPointers(currentTypeObj, allTypes, empties, alreadySeen) { alreadySeen = alreadySeen || {}; var operation = function(typeObj, operation) { - if (alreadySeen[typeObj.name]) { - return; - } while (empties[typeObj.name]) { // change this to the first non-epty prototype of the empty type - typeObj.name = allTypes[typeObj.name].$$proto.name; + typeObj.name = allTypes[typeObj.name].$$proto.typeObj.name; if (!typeObj.name) { typeObj.name = 'Object'; } } + if (alreadySeen[typeObj.name]) { + return; + } alreadySeen[typeObj.name] = true; var currentType = allTypes[typeObj.name]; if (currentType) { @@ -2283,7 +2284,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr /** * filters types from the environment that should not be exported */ - function filterTypes(environment, kind, moduleTypeName, moduleTypeObj, provided) { + function filterTypes(environment, kind, moduleTypeObj, provided) { + var moduleTypeName = doctrine.stringify(moduleTypeObj); var allTypes = environment.getAllTypes(); allTypes.clearDefaultGlobal(); @@ -2653,7 +2655,6 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } var providedType; var kind; - var modTypeName; var modTypeObj; if (environment.amdModule) { // provide the exports of the AMD module @@ -2661,10 +2662,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var args = environment.amdModule["arguments"]; if (args && args.length > 0) { modTypeObj = mTypes.extractReturnType(args[args.length-1].extras.inferredTypeObj); - modTypeName = doctrine.stringify(modTypeObj); } else { modTypeObj = mTypes.OBJECT_TYPE; - modTypeName = modTypeObj.name; } kind = "AMD"; } else if (environment.commonjsModule) { @@ -2672,7 +2671,6 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // we have already checked the correctness of this function var exportsParam = environment.commonjsModule["arguments"][0].params[1]; modTypeObj = exportsParam.extras.inferredTypeObj; - modTypeName = doctrine.stringify(modTypeObj); providedType = environment.findType(modTypeObj); } else { @@ -2688,19 +2686,16 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // actually, commonjs kind = "commonjs"; modTypeObj = maybeExports.typeObj; - modTypeName = doctrine.stringify(modTypeObj); } else { kind = "global"; - modTypeName = environment.globalTypeName(); modTypeObj = providedType['this'].typeObj; } } // simplify the exported type - if (mTypes.isFunctionOrConstructor(modTypeObj) || environment.findType(modTypeObj).$$isBuiltin) { - // this module provides a built in type or a function - providedType = modTypeName; - } else { + if (!mTypes.isFunctionOrConstructor(modTypeObj) && + !environment.findType(modTypeObj).$$isBuiltin) { + // this module provides a composite type providedType = environment.findType(modTypeObj); } @@ -2708,7 +2703,14 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var allTypes = environment.getAllTypes(); // now filter the builtins since they are always available - filterTypes(environment, kind, modTypeName, modTypeObj, providedType); + filterTypes(environment, kind, modTypeObj, providedType); + + // Cases when provided type is not a record type. store as a string + // warning...not all cases handled here...eg- union types + if (mTypes.isFunctionOrConstructor(modTypeObj) || + (environment.findType(modTypeObj) && environment.findType(modTypeObj).$$isBuiltin)) { + providedType = doctrine.stringify(modTypeObj); + } return { provided : providedType, diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index b758a06f..88fb3ee8 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1422,8 +1422,13 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { result: result }; if (isConstructor) { + functionTypeObj.params = functionTypeObj.params || []; // TODO should we also do 'this'? - functionTypeObj['new'] = result; + functionTypeObj.params.push({ + type: 'ParameterType', + name: 'new', + expression: result + }); } return functionTypeObj; @@ -1456,7 +1461,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { }, extractReturnType : function(fnType) { - return fnType.result || fnType; + return fnType.result || (fnType.type === 'FunctionType' ? this.UNDEFINED_TYPE: fnType); }, // TODO should we just return a typeObj here??? @@ -1535,21 +1540,21 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return { type: jsdocType.type, elements: jsdocType.elements.map(function(elt) { - return self.convertJsDocType(elt, env, doCombine, depth+1); + return self.convertJsDocType(elt, env, doCombine, depth); }) }; case 'RestType': return { type: jsdocType.type, - expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1) + expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth) }; case 'ArrayType': return { type: jsdocType.type, elements: jsdocType.elements.map(function(elt) { - return self.convertJsDocType(elt, env, doCombine, depth+1); + return self.convertJsDocType(elt, env, doCombine, depth); }) }; @@ -1562,9 +1567,9 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { }; if (jsdocType.result) { // prevent recursion on functions that return themselves - fnType.result = depth < 2 && jsdocType.result.type === 'FunctionType' ? - self.convertJsDocType(jsdocType.result, env, doCombine, depth) : - { type : 'NameExpression', name : JUST_DOTS }; + fnType.result = depth > 1 && jsdocType.result.type === 'FunctionType' ? + { type : 'NameExpression', name : JUST_DOTS } : + self.convertJsDocType(jsdocType.result, env, doCombine, depth); } // TODO should remove? new and this are folded into params @@ -1587,12 +1592,12 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'TypeApplication': var typeApp = { type: jsdocType.type, - expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1), + expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth), }; if (jsdocType.applications) { typeApp.applications = jsdocType.applications.map(function(elt) { - return self.convertJsDocType(elt, env, doCombine, depth+1); + return self.convertJsDocType(elt, env, doCombine, depth); }); } return typeApp; @@ -1602,7 +1607,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { type: jsdocType.type, name: name, expression: jsdocType.expression ? - self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1) : + self.convertJsDocType(jsdocType.expression, env, doCombine, depth) : null }; @@ -1611,7 +1616,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'NullableType': return { type: jsdocType.type, - expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth+1) + expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth) }; case 'NameExpression': @@ -1657,7 +1662,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return { type: jsdocType.type, key: jsdocType.key, - value: self.convertJsDocType(jsdocType.value, env, doCombine, depth+1) + value: self.convertJsDocType(jsdocType.value, env, doCombine, depth) }; case 'RecordType': diff --git a/tests/client/esprima/esprimaJsContentAssistTests.js b/tests/client/esprima/esprimaJsContentAssistTests.js index 1e7caf8f..5f5a272f 100644 --- a/tests/client/esprima/esprimaJsContentAssistTests.js +++ b/tests/client/esprima/esprimaJsContentAssistTests.js @@ -1985,7 +1985,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri testProposals(results, [ ["obj.Fun()", "obj.Fun() : obj.Fun"], ["Object([val])", "Object([val]) : Object"], - ["obj", "obj : {Fun:function(new:obj.Fun):obj.Fun,fun:function():undefined,fun2:Number}"] + ["obj", "obj : {Fun:function(new:obj.Fun):obj.Fun,fun:function(),fun2:Number}"] ]); }; @@ -2715,7 +2715,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nxx.foo.foo", "foo" ); testProposals(results, [ - ["foo()", "foo() : Object"] + ["foo()", "foo() : undefined"] ]); }; @@ -2725,7 +2725,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri "var xx;\nxx.foo.foo", "foo" ); testProposals(results, [ - ["foo(a, b)", "foo(a, b) : Object"] + ["foo(a, b)", "foo(a, b) : undefined"] ]); }; diff --git a/tests/client/esprima/navigationTests.js b/tests/client/esprima/navigationTests.js index 88d740a6..85d48cdb 100644 --- a/tests/client/esprima/navigationTests.js +++ b/tests/client/esprima/navigationTests.js @@ -131,21 +131,21 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testVar2 = function() { doSameFileTest("var aaa = function(a,b,c) { return 9; }\naaa", 'aaa', - "function(a:gen~local~0,b:gen~local~1,c:gen~local~2):Number", 'aaa : function(a:{},b:{},c:{}):Number'); + "function(a:gen~103145323~0,b:gen~103145323~1,c:gen~103145323~2):Number", 'aaa : function(a,b,c):Number'); }; tests.testVar3 = function() { doSameFileTest("var aaa = function(a,b,c) { return function(a) { return 9; }; }\naaa", - 'aaa', "function(a:gen~local~0,b:gen~local~1,c:gen~local~2):function(a:gen~local~5):Number", - 'aaa : function(a:{},b:{},c:{}):function(a:{}):Number'); + 'aaa', "function(a:gen~103145323~0,b:gen~103145323~1,c:gen~103145323~2):function(a:gen~103145323~5):Number", + 'aaa : function(a,b,c):function(a):Number'); }; tests.testParam1 = function() { doSameFileTest("var bbb = function(a,b,d) { d }", - 'd', "gen~local~2", 'd : {}'); + 'd', "gen~103145323~2", 'd : {}'); }; tests.testParam2 = function() { doSameFileTest("var d = 9;var bbb = function(a,b,d) { d }", - 'd', "gen~local~2", 'd : {}', 2); + 'd', "gen~103145323~2", 'd : {}', 2); }; tests.testParam3 = function() { doSameFileTest("var d = 9;var bbb = function(a,b,d) {}\nd", @@ -191,7 +191,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testThis4 = function() { doSameFileTest("var Foo = function() {};\nFoo.prototype = { a : this }", - 'this', "gen~local~4", 'this : {a:{a:{...}}}', '{ a : this }'); + 'this', "gen~103145323~4", 'this : {a:{a:{...}}}', '{ a : this }'); }; ////////////////////////////////////////////////////////// @@ -281,7 +281,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " return Car;\n" + "});", "define(['car'], function(Car) { var c = new Car('ford'); c.show(); });", - 'show', "function():undefined", 'show : function():undefined'); + 'show', "function():undefined", 'show : function()'); }; ////////////////////////////////////////////////////////// @@ -347,7 +347,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp tests.testFullFile2 = function() { doSameFileTest("var x = y;\n" + "x;\n" + - "var y = { fff : 0 };", "x", "gen~local~0", "x : {}", 1); + "var y = { fff : 0 };", "x", "gen~103145323~0", "x : {}", 1); }; // arrrrgh need to fix this one so that the test chooses the first fff, not the last @@ -368,7 +368,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " function a() {\n" + " aa();\n" + " }\n" + - "}", "aa", "function():undefined", "aa : function():undefined", 1); + "}", "aa", "function():undefined", "aa : function()", 1); }; tests.testFullFile7 = function() { @@ -377,7 +377,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " aa();\n" + " }\n" + " function aa() { }\n" + - "}", "aa", "function():undefined", "aa : function():undefined", 2); + "}", "aa", "function():undefined", "aa : function()", 2); }; tests.testFullFile8 = function() { @@ -446,7 +446,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " */\n" + "var x;"; doSameFileTest( - contents, "x", "gen~local~0", "x : {}", 1, + contents, "x", "gen~103145323~0", "x : {}", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; tests.testJSDoc0a = function() { @@ -454,7 +454,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "/***/\n" + "var x;"; doSameFileTest( - contents, "x", "gen~local~0", "x : {}", 1, + contents, "x", "gen~103145323~0", "x : {}", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; tests.testJSDoc0b = function() { @@ -463,7 +463,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "// TODO this is strannnnnnnnge.\n" + "var x;"; doSameFileTest( - contents, "x", "gen~local~0", "x : {}", 1, + contents, "x", "gen~103145323~0", "x : {}", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -473,7 +473,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports.x = function() {};"; doMultiFileTest( "a", otherContents, "require('a').x", "x", - "function():undefined", "x : function():undefined", 2, false, + "function():undefined", "x : function()", 2, false, [otherContents.indexOf("/**"), otherContents.indexOf("*/")+2]); }; @@ -507,7 +507,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function parseFile(path) { path.sumpin = ''; }\n" + "var x;"; doSameFileTest( - contents, "parseFile", "function(path:gen~local~1):String", "parseFile : function(path:{foo:Number,bar:String,sumpin:String}):String", 1, + contents, "parseFile", "function(path:gen~103145323~1):String", "parseFile : function(path:{foo:Number,bar:String,sumpin:String}):String", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -518,7 +518,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function ParseFile(path) { path.sumpin = ''; }\n" + "var x;"; doSameFileTest( - contents, "ParseFile", "function(path:gen~local~2,new:ParseFile):ParseFile", "ParseFile : function(path:{foo:Number,bar:String,sumpin:String},new:ParseFile):ParseFile", 1, + contents, "ParseFile", "function(path:gen~103145323~2,new:ParseFile):ParseFile", "ParseFile : function(path:{foo:Number,bar:String,sumpin:String},new:ParseFile):ParseFile", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -529,7 +529,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "function parseFile(path) { path.sumpin = ''; }\n" + "var x;"; doSameFileTest( - contents, "parseFile", "function(path:gen~local~1):undefined", "parseFile : function(path:{foo:Number,bar:function(Object,Object):String,sumpin:String}):undefined", 1, + contents, "parseFile", "function(path:gen~103145323~1):undefined", "parseFile : function(path:{foo:Number,bar:function(Object,Object):String,sumpin:String})", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; @@ -539,9 +539,8 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp doSameFileTest( contents, "addSaveTransform", - "function(transformFun:function(text:String,path:String,configuration:function(foo:String):Object):?String):undefined", - "addSaveTransform : function(transformFun:function(text:String,path:String,configuration:function(foo:String):Object):?String):undefined", 1, + "addSaveTransform : function(transformFun:function(text:String,path:String,configuration:function(foo:String):Object):?String)", 1, [contents.indexOf("/**"), contents.indexOf("*/")+2]); }; diff --git a/tests/client/esprima/summaryBuildingTests.js b/tests/client/esprima/summaryBuildingTests.js index 3bdd5eb6..60029a95 100644 --- a/tests/client/esprima/summaryBuildingTests.js +++ b/tests/client/esprima/summaryBuildingTests.js @@ -58,7 +58,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testOneVarGlobalStructure1 = function() { - assertCreateSummary('{"provided":{"x":"gen~a~0"},"types":{"gen~a~0":{"$$proto":"Object"}},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"Object"},"types":{},"kind":"global"}', "var x;", "a"); }; @@ -73,12 +73,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testOneVarGlobalStructure4 = function() { - assertCreateSummary('{"provided":{"x":"gen~a~1"},"types":{"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"Object"},"types":{},"kind":"global"}', "var x={};", "a"); }; tests.testOneVarGlobalStructure5 = function() { - assertCreateSummary('{"provided":{"x":"gen~a~1"},"types":{"gen~a~1":{"$$proto":"Object","f":"Number","g":"String"}},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"gen~97~1"},"types":{"gen~97~1":{"$$proto":"Object","f":"Number","g":"String"}},"kind":"global"}', "var x={f:9, g:''};", "a"); }; @@ -88,17 +88,17 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testOneVarGlobalStructure7 = function() { - assertCreateSummary('{"provided":{"x":"function(a:gen~a~0,b:gen~a~1):undefined"},"types":{"gen~a~0":{"$$proto":"Object"},"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function(a:Object,b:Object):undefined"},"types":{},"kind":"global"}', "var x=function(a,b) {};", "a"); }; tests.testOneVarGlobalStructure8 = function() { - assertCreateSummary('{"provided":{"x":"function(a:gen~a~0,b:gen~a~1):Number"},"types":{"gen~a~0":{"$$proto":"Object"},"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function(a:Object,b:Object):Number"},"types":{},"kind":"global"}', "var x=function(a,b) {return 7; };", "a"); }; tests.testOneVarGlobalStructure9 = function() { - assertCreateSummary('{"provided":{"x":"function(a:gen~a~0,b:gen~a~1):Number"},"types":{"gen~a~0":{"$$proto":"Object"},"gen~a~1":{"$$proto":"Object"}},"kind":"global"}', + assertCreateSummary('{"provided":{"x":"function(a:Object,b:Object):Number"},"types":{},"kind":"global"}', "function x(a,b) {return 7; }", "a"); }; @@ -122,7 +122,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests.testNVP4 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function(a:gen~a~6,new:Fun):Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"},"gen~a~6":{"$$proto":"Object"},"Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"Number","b":"function():function(a:Object,new:Fun):Fun"},"types":{"Fun":{"$$proto":"Fun~proto","ff":"Number"},"Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define({a : 1, b: function() { function Fun(a) { this.ff = 8; }; return Fun; }});", "a"); }; tests.testNVP5 = function() { @@ -142,7 +142,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "define('afg', [], function() { return 8; });", "a"); }; tests.testAMD3 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object"},"types":{},"kind":"AMD"}', + assertCreateSummary('{"provided":"Object","types":{},"kind":"AMD"}', "define('afg', [], function() { return { }; });", "a"); }; tests.testAMD4 = function() { @@ -166,15 +166,15 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "define('afg', [], function() { var Exported = function() { this.a = 9; };\n return { Exported: Exported, second: 8 }; });", "a"); }; tests.testAMD9 = function() { - assertCreateSummary('{"provided":"function(a:gen~a~4,b:gen~a~5,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~4":{"$$proto":"Object"},"gen~a~5":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(a:Object,b:Object,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return Exported; });", "a"); }; tests.testAMD10 = function() { - assertCreateSummary('{"provided":"function(c:gen~a~9,d:gen~a~10):function(a:gen~a~4,b:gen~a~5,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~4":{"$$proto":"Object"},"gen~a~5":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object"},"gen~a~10":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(c:Object,d:Object):function(a:Object,b:Object,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return function(c,d) { return Exported; }; });", "a"); }; tests.testAMD11 = function() { - assertCreateSummary('{"provided":"function(c:gen~a~9,d:gen~a~10):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object"},"gen~a~10":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(c:Object,d:Object):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n return function(c,d) { return new Exported(c,d); }; });", "a"); }; tests.testAMD12 = function() { @@ -191,7 +191,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.foo = 9;\nreturn new Exported(); });", "a"); }; tests.testAMDProto2 = function() { - assertCreateSummary('{"provided":{"$$proto":"gen~a~10","a":"Number"},"types":{"gen~a~10":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"gen~97~10","a":"Number"},"types":{"gen~97~10":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n Exported.prototype = { foo : 9, bar : '' };\nreturn new Exported(); });", "a"); }; tests.testAMDProto3 = function() { @@ -203,13 +203,13 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "define([], function() { var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nreturn new Exported(); });", "a"); }; tests.testAMDProto5 = function() { - assertCreateSummary('{"provided":"function(a:gen~a~4,b:gen~a~5,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~4":{"$$proto":"Object"},"gen~a~5":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(a:Object,b:Object,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"AMD"}', "define([], function() { var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nreturn Exported; });", "a"); }; // https://github.com/scripted-editor/scripted/issues/96 tests["test constructor export with changed prototype"] = function() { - assertCreateSummary('{"provided":"function(model:gen~a~4,new:Car):Car","types":{"Car":{"$$proto":"gen~a~9","model":"gen~a~4"},"gen~a~4":{"$$proto":"Object"},"gen~a~9":{"$$proto":"Object","show":"function():undefined","model":"gen~a~14"},"gen~a~14":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"function(model:Object,new:Car):Car","types":{"Car":{"$$proto":"gen~97~9","model":"Object"},"gen~97~9":{"$$proto":"Object","show":"function():undefined","model":"Object"}},"kind":"AMD"}', "define(function() {\n" + " function Car(model) {\n" + " this.model = model;\n" + @@ -242,28 +242,28 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = 9", "a"); }; tests.testCommonJS4 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object"},"types":{},"kind":"commonjs"}', + assertCreateSummary('{"provided":"Object","types":{},"kind":"commonjs"}', "exports = { }", "a"); }; tests.testCommonJS5 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~4"},"types":{"gen~a~4":{"$$proto":"Object","a":"gen~a~6"},"gen~a~6":{"$$proto":"Object","a":"gen~a~8"},"gen~a~8":{"$$proto":"Object"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~97~4"},"types":{"gen~97~4":{"$$proto":"Object","a":"gen~97~6"},"gen~97~6":{"$$proto":"Object","a":"Object"}},"kind":"commonjs"}', "exports = { a : { a : { a : { } } } }", "a"); }; tests.testCommonJS6 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~3"},"types":{"gen~a~3":{"$$proto":"Object","a":"gen~a~5"},"gen~a~5":{"$$proto":"Object","a":"gen~a~7"},"gen~a~7":{"$$proto":"Object"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~97~3"},"types":{"gen~97~3":{"$$proto":"Object","a":"gen~97~5"},"gen~97~5":{"$$proto":"Object","a":"Object"}},"kind":"commonjs"}', "var a = { a : { a : { a : { } } } }\n exports = a;", "a"); }; // not sure if this is right...an explicitly declared exports variable is the // same as an implicit one tests.testCommonJS7 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~3"},"types":{"gen~a~3":{"$$proto":"Object","a":"gen~a~5"},"gen~a~5":{"$$proto":"Object","a":"gen~a~7"},"gen~a~7":{"$$proto":"Object"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~97~3"},"types":{"gen~97~3":{"$$proto":"Object","a":"gen~97~5"},"gen~97~5":{"$$proto":"Object","a":"Object"}},"kind":"commonjs"}', "var a = { a : { a : { a : { } } } }\n var exports = a;", "a"); }; tests.testWrappedCommonJS1 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~7"},"types":{"gen~a~7":{"$$proto":"Object","a":"gen~a~9"},"gen~a~9":{"$$proto":"Object","a":"gen~a~11"},"gen~a~11":{"$$proto":"Object","a":"gen~a~13"},"gen~a~13":{"$$proto":"Object"}}}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~97~7"},"types":{"gen~97~7":{"$$proto":"Object","a":"gen~97~9"},"gen~97~9":{"$$proto":"Object","a":"gen~97~11"},"gen~97~11":{"$$proto":"Object","a":"Object"}}}', "define(function(require, exports, module) {\n" + " var a = { a : { a : { a : { } } } };\n" + " exports.a = a; });", "a"); @@ -274,7 +274,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp " exports.a = 7; });", "a"); }; tests.testWrappedCommonJS3 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~a~8"},"types":{"gen~a~8":{"$$proto":"Object","flart":"function(a:gen~a~9,b:gen~a~10):String"},"gen~a~9":{"$$proto":"Object"},"gen~a~10":{"$$proto":"Object"}}}', + assertCreateSummary('{"provided":{"$$proto":"Object","a":"gen~97~8"},"types":{"gen~97~8":{"$$proto":"Object","flart":"function(a:Object,b:Object):String"}}}', "define(function(require, exports, module) {\n" + " exports.a = { flart: function(a,b) { return ''; } }\n" + "});", "a"); @@ -288,7 +288,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype.foo = 9; exports.Exported = new Exported();", "a"); }; tests.testCommonjsProto2 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"gen~a~7","a":"Number"},"gen~a~7":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"Exported"},"types":{"Exported":{"$$proto":"gen~97~7","a":"Number"},"gen~97~7":{"$$proto":"Object","foo":"Number","bar":"String"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n Exported.prototype = { foo : 9, bar : '' };\nexports.Exported = new Exported();", "a"); }; tests.testCommonjsProto3 = function() { @@ -300,11 +300,11 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports = new Exported(); });", "a"); }; tests.testCommonjsProto5 = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"function(a:gen~a~1,b:gen~a~2,new:Exported):Exported"},"types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~1":{"$$proto":"Object"},"gen~a~2":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":{"$$proto":"Object","Exported":"function(a:Object,b:Object,new:Exported):Exported"},"types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports.Exported = Exported; });", "a"); }; tests.testCommonjsProto6 = function() { - assertCreateSummary('{"provided":"function(a:gen~a~1,b:gen~a~2,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"gen~a~1":{"$$proto":"Object"},"gen~a~2":{"$$proto":"Object"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"function(a:Object,b:Object,new:Exported):Exported","types":{"Exported":{"$$proto":"Exported~proto","a":"Number"},"Exported~proto":{"$$proto":"Object","open":"function():Number"}},"kind":"commonjs"}', "var Exported = function(a,b) { this.a = 9; };\n var func = function() { return 9; };\n Exported.prototype.open = func;\nexports = Exported; });", "a"); }; @@ -318,13 +318,13 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp }; tests["test browser2"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","loc":"gen~a~3"},"types":{"gen~a~3":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","loc":"Object"},"types":{},"kind":"AMD"}', "/*jslint browser:false*/\n" + "define({ loc : location });", "a"); }; tests["test browser3"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","foo":"gen~a~6"},"types":{"gen~a~6":{"$$proto":"Object","loc":"Location","scr":"Screen"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","foo":"gen~97~6"},"types":{"gen~97~6":{"$$proto":"Object","loc":"Location","scr":"Screen"}},"kind":"AMD"}', "/*jslint browser:true*/\n" + "define([], function () { return { foo : { loc : location, scr : screen } }; });", "a"); }; @@ -333,12 +333,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp // Dotted constructors ////////////////////////////////////////////////////////// tests["test dotted constructor1"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~a~6"},"types":{"gen~a~6":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~97~6"},"types":{"gen~97~6":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object"}},"kind":"AMD"}', "define([], function () { return { obj : { Fun: function() { } } }; });", "a"); }; tests["test dotted constructor2"] = function() { - assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~a~4"},"types":{"gen~a~4":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":{"$$proto":"Object","obj":"gen~97~4"},"types":{"gen~97~4":{"$$proto":"Object","Fun":"function(new:obj.Fun):obj.Fun"},"obj.Fun":{"$$proto":"obj.Fun~proto"},"obj.Fun~proto":{"$$proto":"Object","larf":"Number"}},"kind":"AMD"}', "define([], function () {\n" + " var obj = { Fun: function() { } };\n" + " obj.Fun.prototype.larf = 9;\n" + @@ -394,7 +394,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = [[1]];", "a"); }; tests["test array export4"] = function() { - assertCreateSummary('{"provided":"[gen~a~2]","types":{"gen~a~2":{"$$proto":"Object","a":"Number"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"[gen~97~2]","types":{"gen~97~2":{"$$proto":"Object","a":"Number"}},"kind":"commonjs"}', "exports = [{a:1}];", "a"); }; tests["test array export5"] = function() { @@ -402,7 +402,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "exports = {a:[1]};", "a"); }; tests["test array export6"] = function() { - assertCreateSummary('{"provided":"[gen~a~2]","types":{"gen~a~2":{"$$proto":"Object","a":"[Number]"}},"kind":"commonjs"}', + assertCreateSummary('{"provided":"[gen~97~2]","types":{"gen~97~2":{"$$proto":"Object","a":"[Number]"}},"kind":"commonjs"}', "exports = [{a:[1]}];", "a"); }; tests["test array export amd1"] = function() { @@ -413,11 +413,198 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "});", "a"); }; tests["test array export amd2"] = function() { - assertCreateSummary('{"provided":"[gen~a~4]","types":{"gen~a~4":{"$$proto":"Object","a":"Number"}},"kind":"AMD"}', + assertCreateSummary('{"provided":"[gen~97~4]","types":{"gen~97~4":{"$$proto":"Object","a":"Number"}},"kind":"AMD"}', "/*global define */\n" + "define([], function() {\n" + " return [{ a : 9}];\n" + "});", "a"); }; + + // some tests about type compression + // empty types should be converted to object + tests["test type compression array1"] = function() { + assertCreateSummary('{"provided":{"$$proto":"Object","foo":"Number"},"types":{},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " y.foo = 9;\n" + + " return x;\n" + + "});", "a"); + }; + + tests["test type compression array2"] = function() { + assertCreateSummary('{"provided":"[gen~97~5]","types":{"gen~97~5":{"$$proto":"Object","foo":"Number"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " y.foo = 9;\n" + + " return [x];\n" + + "});", "a"); + }; + + tests["test type compression array3"] = function() { + assertCreateSummary('{"provided":"[gen~97~5]","types":{"gen~97~5":{"$$proto":"Object","foo":"Object"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " y.foo = {};\n" + + " return [x];\n" + + "});", "a"); + }; + tests["test type compression array4"] = function() { + assertCreateSummary('{"provided":"[gen~97~5]","types":{"gen~97~5":{"$$proto":"Object","foo":"gen~97~5"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " y.foo = y;\n" + + " return [x];\n" + + "});", "a"); + }; + tests["test type compression array5"] = function() { + assertCreateSummary('{"provided":"[Object]","types":{},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " return [x];\n" + + "});", "a"); + }; + tests["test type compression array6"] = function() { + assertCreateSummary('{"provided":"[[Object]]","types":{},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " return [[x]];\n" + + "});", "a"); + }; + tests["test type compression array7"] = function() { + assertCreateSummary('{"provided":"[[gen~97~5]]","types":{"gen~97~5":{"$$proto":"Object","foo":"Object"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " y.foo = {};\n" + + " return [[x]];\n" + + "});", "a"); + }; + tests["test type compression array8"] = function() { + assertCreateSummary('{"provided":"[[gen~97~5]]","types":{"gen~97~5":{"$$proto":"Object","foo":"[Object]"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = y = { };\n" + + " y.foo = [{}];\n" + + " return [[x]];\n" + + "});", "a"); + }; + tests["test type compression constructor 1"] = function() { + assertCreateSummary('{"provided":"function(new:X):X","types":{"X":{"$$proto":"X~proto"},"X~proto":{"$$proto":"Object"}},"kind":"AMD"}', + "define([], function() {\n" + + " var X = function() { };\n" + + " return X" + + "});", "a"); + }; + tests["test type compression constructor 2"] = function() { + assertCreateSummary('{"provided":{"$$proto":"Object","X":"function(new:X):X"},"types":{"X":{"$$proto":"X~proto"},"X~proto":{"$$proto":"Object"}},"kind":"AMD"}', + "define([], function() {\n" + + " var X = function() { };\n" + + " X.prototype = { }\n" + + " return {X:X}" + + "});", "a"); + }; + tests["test type compression constructor 3"] = function() { + assertCreateSummary('{\"provided\":\"function(new:X):X\",\"types\":{\"X\":{\"$$proto\":\"function(a:Object,b:Object):Number\"}},\"kind\":\"AMD\"}', + "define([], function() {\n" + + " var X = function() { };\n" + + " X.prototype = function(a, b) { return 9; }\n" + + " return X" + + "});", "a"); + }; + tests["test type compression constructor 4"] = function() { + assertCreateSummary('{"provided":{"$$proto":"Object","X":"function(new:X):X"},"types":{"X":{"$$proto":"gen~97~7"},"gen~97~7":{"$$proto":"Object","foo":"Number"}},"kind":"AMD"}', + "define([], function() {\n" + + " var X = function() { };\n" + + " X.prototype = { foo: 0 }\n" + + " return {X:X}" + + "});", "a"); + }; + + tests["test type compression function 1"] = function() { + assertCreateSummary('{"provided":"function(a:gen~97~3,b:gen~97~4):gen~97~10","types":{"gen~97~3":{"$$proto":"Object","foo":"Number"},"gen~97~4":{"$$proto":"Object","foo":"String"},"gen~97~10":{"$$proto":"Object","a":"gen~97~3","b":"gen~97~4"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = function(a, b) { a.foo = 9; b.foo = ''; return { a:a, b:b }; };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 2"] = function() { + assertCreateSummary('{"provided":"function(a:gen~97~3,b:gen~97~4):gen~97~10","types":{"gen~97~3":{"$$proto":"Object","foo":"Number"},"gen~97~4":{"$$proto":"Object","foo":"String"},"gen~97~10":{"$$proto":"Object","a":"Object","b":"Object"}},"kind":"AMD"}', + "define([], function() {\n" + + " var x = function(a, b) { a.foo = 9; b.foo = ''; return { a:{}, b:{} }; };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 3"] = function() { + assertCreateSummary('', + "define([], function() {\n" + + " /**\n" + + " * @param {String} a\n" + + " * @param {String} b\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 3"] = function() { + assertCreateSummary('{"provided":"function(a:String,b:function(c:Object):String):undefined","types":{},"kind":"AMD"}', + "define([], function() {\n" + + " /**\n" + + " * @param {String} a\n" + + " * @param {function(c:{}):String} b\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 4"] = function() { + assertCreateSummary('{"provided":"function(a:String,b:function(c:gen~97~5):String):undefined","types":{"gen~97~5":{"$$proto":"Object","foo":"Number"}},"kind":"AMD"}', + "define([], function() {\n" + + " /**\n" + + " * @param {String} a\n" + + " * @param {function(c:{foo:Number}):String} b\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 5"] = function() { + assertCreateSummary('{"provided":"function(a:function(c:gen~97~5):String,b:Object):undefined","types":{"gen~97~5":{"$$proto":"Object","foo":"function()"}},"kind":"AMD"}', + "define([], function() {\n" + + " /**\n" + + " * @param {function(c:{foo:function()}):String} a\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 6"] = function() { + assertCreateSummary('{"provided":"function(a:Object,b:Object):undefined","types":{},"kind":"AMD"}', + "define([], function() {\n" + + " /**\n" + + " * @param {{}} a\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 7"] = function() { + assertCreateSummary('{"provided":"function(a:gen~97~5,b:Object):undefined","types":{"gen~97~5":{"$$proto":"Object","b":"Object"}},"kind":"AMD"}', + "define([], function() {\n" + + " /**\n" + + " * @param {{b}} a\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + tests["test type compression function 8"] = function() { + assertCreateSummary('{"provided":"function(a:gen~97~5,b:Object):undefined","types":{"gen~97~5":{"$$proto":"Object","b":"Number"}},"kind":"AMD"}', + "define([], function() {\n" + + " /**\n" + + " * @param {{b:Number}} a\n" + + " */\n" + + " var x = function(a, b) { };\n" + + " return x;" + + "});", "a"); + }; + return tests; }); \ No newline at end of file diff --git a/tests/client/indexer/indexerTests.js b/tests/client/indexer/indexerTests.js index 2ed89e22..d2c08bd9 100644 --- a/tests/client/indexer/indexerTests.js +++ b/tests/client/indexer/indexerTests.js @@ -99,11 +99,11 @@ function(mIndexer, jsdependsStub, assert) { "range": [ 25, 29], - "typeSig": "gen~sub/other.js~4" + "typeSig": "gen~-130954282~4" } }, "types": { - "gen~sub/other.js~4": { + "gen~-130954282~4": { "$$proto": { "path": "utils.js", "typeSig": "Object" From 0a70bf829f4dd985262542efbf95eed13137b48a Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 26 Mar 2013 20:00:43 +0900 Subject: [PATCH 44/64] Add tests for rename refactoring Add tests for call args --- .../plugins/esprima/esprimaJsContentAssist.js | 3 +- .../plugins/esprima/refactoringSupport.js | 2 +- client/scripts/scripted/markoccurrences.js | 2 +- .../esprima/esprimaJsContentAssistTests.js | 126 +++++++++++++++++- .../client/esprima/refactoringSupportTests.js | 100 ++++++++++++++ tests/client/scriptedClientTests.html | 5 +- 6 files changed, 229 insertions(+), 9 deletions(-) create mode 100644 tests/client/esprima/refactoringSupportTests.js diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 4bd8ca1d..b0e94f43 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1501,7 +1501,8 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * @return Boolean */ function leftTypeIsMoreGeneral(leftTypeObj, rightTypeObj, env) { - var leftTypeName = leftTypeObj.name, rightTypeName = rightTypeObj.name; + var leftTypeName = leftTypeObj.name || mTypes.convertToSimpleTypeName(leftTypeObj), + rightTypeName = rightTypeObj.name || mTypes.convertToSimpleTypeName(rightTypeObj); if (!leftTypeName) { if (leftTypeObj.type === 'NullLiteral' || leftTypeObj.type === 'UndefinedLiteral' || leftTypeObj.type === 'VoidLiteral') { diff --git a/client/scripts/plugins/esprima/refactoringSupport.js b/client/scripts/plugins/esprima/refactoringSupport.js index ada92136..11994bb2 100644 --- a/client/scripts/plugins/esprima/refactoringSupport.js +++ b/client/scripts/plugins/esprima/refactoringSupport.js @@ -77,7 +77,7 @@ define(function (require) { case 'ReturnStatement': case 'SwitchCase': case 'SwitchStatement': - case 'ThrowStatemen': + case 'ThrowStatement': case 'TryStatement': case 'UnaryExpression': case 'UpdateExpression': diff --git a/client/scripts/scripted/markoccurrences.js b/client/scripts/scripted/markoccurrences.js index a6ec60b3..bcdedc86 100644 --- a/client/scripts/scripted/markoccurrences.js +++ b/client/scripts/scripted/markoccurrences.js @@ -71,7 +71,7 @@ define(['orion/textview/annotations'], function(mAnnotations) { i--; } - if (!start && i === 0) { + if (!start && i === -1) { start = 0; } diff --git a/tests/client/esprima/esprimaJsContentAssistTests.js b/tests/client/esprima/esprimaJsContentAssistTests.js index 5f5a272f..ef383931 100644 --- a/tests/client/esprima/esprimaJsContentAssistTests.js +++ b/tests/client/esprima/esprimaJsContentAssistTests.js @@ -4160,11 +4160,6 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ]); }; - - - - - // See https://github.com/scripted-editor/scripted/issues/258 tests['test invalid array type param in jsdoc'] = function() { var results = computeContentAssist( @@ -4176,5 +4171,126 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert", "esprima/espri ]); }; + // call args inferencing + tests['test call args 1'] = function() { + var results = computeContentAssist( + "function parseFile(path) {\n" + + " path.xxx = 9;\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x.xx", "xx"); + testProposals(results, [ + ["xxx", "xxx : Number"] + ]); + }; + tests['test call args 2'] = function() { + var results = computeContentAssist( + "function parseFile(path) {\n" + + " path.xxx = 9;\n" + + "}\n" + + "var x = 0;\n" + + "parseFile(x);\n" + + "x.xx", "xx"); + // should keep the Number type + testProposals(results, [ + ]); + }; + tests['test call args 3'] = function() { + var results = computeContentAssist( + "var parseFile = function(path) {\n" + + " path.xxx = 9;\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x.xx", "xx"); + testProposals(results, [ + ["xxx", "xxx : Number"] + ]); + }; + tests['test call args 4'] = function() { + var results = computeContentAssist( + "var parseFile = function(path) {\n" + + " path.xxx = 9;\n" + + // this one is ignored + " path = 9;\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x.xx", "xx"); + testProposals(results, [ + ["xxx", "xxx : Number"] + ]); + }; + tests['test call args 5'] = function() { + var results = computeContentAssist( + "var parseFile = function(path) {\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x", "x"); + testProposals(results, [ + ["x", "x : {}"] + ]); + }; + tests['test call args 6'] = function() { + var results = computeContentAssist( + "/** @param {{xxx:Number}} path */\n" + + "var parseFile = function(path) {\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x.xx", "xx"); + testProposals(results, [ + ["xxx", "xxx : Number"] + ]); + }; + tests['test call args 7'] = function() { + var results = computeContentAssist( + "/** @param {function()} path */\n" + + "var parseFile = function(path) {\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x", "x"); + testProposals(results, [ + ["x()", "x() : undefined"] + ]); + }; + tests['test call args 8'] = function() { + var results = computeContentAssist( + "/** @param {function(String,Number)} path */\n" + + "var parseFile = function(path) {\n" + + "}\n" + + "var x;\n" + + "parseFile(x);\n" + + "x", "x"); + testProposals(results, [ + ["x(String, Number)", "x(String, Number) : undefined"] + ]); + }; + tests['test call args 9'] = function() { + var results = computeContentAssist( + "/** @param {function(a1:String,b1:Number)} path */\n" + + "var parseFile = function(path) {\n" + + "}\n" + + "parseFile(function(a,b) { b.toF/**/ } );\n", + "toF"); + testProposals(results, [ + ["toFixed(digits)", "toFixed(digits) : String"] + ]); + }; + tests['test call args 10'] = function() { + var results = computeContentAssist( + "/** @param {function(String,Number)} path */\n" + + "var parseFile = function(path) {\n" + + "}\n" + + "parseFile(function(a,b) { b.toF/**/ } );\n", + "toF"); + testProposals(results, [ + ["toFixed(digits)", "toFixed(digits) : String"] + ]); + }; + return tests; }); diff --git a/tests/client/esprima/refactoringSupportTests.js b/tests/client/esprima/refactoringSupportTests.js new file mode 100644 index 00000000..a6811fe6 --- /dev/null +++ b/tests/client/esprima/refactoringSupportTests.js @@ -0,0 +1,100 @@ +/******************************************************************************* + * @license + * Copyright (c) 2013 VMware, Inc. All Rights Reserved. + * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE + * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE + * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT. + * You can obtain a current copy of the Eclipse Public License from + * http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Andrew Eisenberg (VMware) - initial API and implementation + ******************************************************************************/ +/*global define */ + +// Tests for finding references for inline rename refactoring support +define(['plugins/esprima/refactoringSupport', "orion/assert"], function(refactoringSupport, assert) { + + function findOccurrences(contents, toFind, exceptions) { + var res = [], next = 0; + while ((next = contents.indexOf(toFind, next)) > -1) { + res.push({ start:next, end: next+toFind.length }); + next = next+toFind.length; + } + + if (exceptions) { + var newRes = []; + for (var i = exceptions.length-1; i >= 0; i--) { + res.splice(exceptions[i], 1); + } + } + return res; + } + + function findIndexOf(contents, toFind, selectionCount) { + var next = 0, iter = 0; + while ((next = contents.indexOf(toFind, next)) > -1) { + if (iter === selectionCount) { + return { start: next, end: next + toFind.length }; + } + iter++; + } + return null; + } + + function doTest(contents, toFind, selectionCount, exceptions) { + var selection = findIndexOf(contents, toFind, selectionCount); + var realResults = refactoringSupport.findVarReferences(contents, selection); + var expectedResults = findOccurrences(contents, toFind, exceptions); + assert.deepEqual(realResults, expectedResults); + } + + var tests = {}; + + tests['test simple'] = function() { + doTest("xxx", "xxx", 0); + doTest("var xxx", "xxx", 0); + doTest("var xxx;\nxxx", "xxx", 0); + doTest("var xxx;\nxxx", "xxx", 1); + doTest("var xxx;\nxxx\nxxxx", "xxx", 1, [2]); + doTest("var xxx;\nxxx\nxxxx.xxx", "xxx", 1, [2,3]); + }; + + tests['test statement kinds'] = function() { + doTest("var xxx; if (xxx) { xxx; } else { xxx; }", "xxx", 1); + doTest("var xxx; try { xxx; } catch(e) { xxx } finally { xxx }", "xxx", 1); + doTest("var xxx; xxx: xxx", "xxx", 0, [1]); + doTest("var xxx; switch (xxx) { case xxx: xxx; }", "xxx", 0); + doTest("var xxx;\nfunction foo() { return xxx; }", "xxx", 0); + doTest("var xxx;\nvar foo = function() { xxx; }", "xxx", 0); + doTest("for (var xxx; xxx; xxx) { xxx; }", "xxx", 0); + doTest("for (var xxx in xxx) { xxx; }", "xxx", 0); + doTest("var xxx; while (xxx) { xxx; }", "xxx", 0); + doTest("var xxx; throw xxx;", "xxx", 0); + doTest("var xxx; do { xxx; } while (xxx); ", "xxx", 0); + }; + tests['test expression kinds'] = function() { + doTest("var xxx; [xxx,xxx];", "xxx", 1); + doTest("var xxx; ['xxx','xxx'];", "xxx", 0, [1,2]); + doTest("var xxx; xxx = xxx;", "xxx", 1); + doTest("var xxx; xxx.xxx;", "xxx", 1, [2]); + doTest("var xxx; xxx(xxx,xxx);", "xxx", 1); + doTest("var xxx; xxx++; xxx--; xxx+= xxx;", "xxx", 1); + doTest("var yyy, xxx; xxx++; xxx--; xxx+= xxx;", "xxx", 1); + }; + + tests['test function arguments'] = function() { + doTest('function f(xxx) { xxx; }', "xxx", 1); + doTest('function f(yyy,xxx) { xxx; }', "xxx", 1); + doTest('var xxx; function f(xxx) { var xxx; xxx; } xxx;', "xxx", 2, [0,1,4]); + }; + + tests['_test functions still failing'] = function() { + doTest('function f(yyy,xxx) { xxx; function f2(xxx) { xxx; } xxx; } xxx;', "xxx", 1, [2,3,5]); + doTest('function f(yyy,xxx) { xxx; } var xxx;', "xxx", 2, [0,1]); + doTest('var xxx; function f(yyy,xxx) { xxx; } xxx;', "xxx", 0, [1,2]); + }; + + + return tests; +}); \ No newline at end of file diff --git a/tests/client/scriptedClientTests.html b/tests/client/scriptedClientTests.html index 6e313893..73975fbe 100644 --- a/tests/client/scriptedClientTests.html +++ b/tests/client/scriptedClientTests.html @@ -22,12 +22,13 @@ "tests/client/esprima/summaryBuildingTests", "tests/client/esprima/contentAssistDependencyTests", "tests/client/esprima/proposalUtilsTests", + "tests/client/esprima/refactoringSupportTests", "tests/client/markoccurrences/markOccurrencesTests", "tests/client/indexer/indexerTests", "tests/client/keybindings/keystroke-tests", "tests/client/templates/param-resolverTests"], function(nodeunitShim, pageState, contentAssistTests, navTests, summaryTests, dependencyTests, - proposalUtilsTests, markOccurrencesTests, indexerTests, keystroke, resolverTests) { + proposalUtilsTests, refactoringSupportTests, markOccurrencesTests, indexerTests, keystroke, resolverTests) { pageState._setEditorPrefix(window.location.pathname + '?'); function testShim(tests) { if (tests.module) { @@ -58,6 +59,8 @@ testShim(dependencyTests); module("Proposal Utils tests"); testShim(proposalUtilsTests); + module("Refactoring Support tests"); + testShim(refactoringSupportTests); module("Indexer tests"); testShim(indexerTests); module("Mark occurrences tests"); From be309987220f3fe982596f4c311a161ed89d1faa Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 26 Mar 2013 20:01:31 +0900 Subject: [PATCH 45/64] some refactoring tests still failing. disable for now. --- tests/client/esprima/refactoringSupportTests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/client/esprima/refactoringSupportTests.js b/tests/client/esprima/refactoringSupportTests.js index a6811fe6..5fdf5847 100644 --- a/tests/client/esprima/refactoringSupportTests.js +++ b/tests/client/esprima/refactoringSupportTests.js @@ -86,13 +86,13 @@ define(['plugins/esprima/refactoringSupport', "orion/assert"], function(refactor tests['test function arguments'] = function() { doTest('function f(xxx) { xxx; }', "xxx", 1); doTest('function f(yyy,xxx) { xxx; }', "xxx", 1); - doTest('var xxx; function f(xxx) { var xxx; xxx; } xxx;', "xxx", 2, [0,1,4]); }; tests['_test functions still failing'] = function() { doTest('function f(yyy,xxx) { xxx; function f2(xxx) { xxx; } xxx; } xxx;', "xxx", 1, [2,3,5]); doTest('function f(yyy,xxx) { xxx; } var xxx;', "xxx", 2, [0,1]); doTest('var xxx; function f(yyy,xxx) { xxx; } xxx;', "xxx", 0, [1,2]); + doTest('var xxx; function f(xxx) { var xxx; xxx; } xxx;', "xxx", 2, [0,1,4]); }; From 5a793ee9a73c1921bc2ba441bff806f396e3965b Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 26 Mar 2013 09:10:07 -0700 Subject: [PATCH 46/64] Enable keyedit feature by default. --- server/scriptedServer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/scriptedServer.js b/server/scriptedServer.js index f3a08b04..2ad950f1 100644 --- a/server/scriptedServer.js +++ b/server/scriptedServer.js @@ -29,7 +29,8 @@ function start(filesystem, options) { var defaultOptions = { applicationManager: true, shutdownHook: true, - exec: true + exec: true, + keyedit: true }; options = jsonMerge(defaultOptions, options); if (options.help_text) { From 10a3992b7210c19c609531e83d2e8e1c53454b93 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 27 Mar 2013 16:44:27 -0700 Subject: [PATCH 47/64] Fix the nefarious bug that broke Andy's demo --- server/jsdepend/amd-reference-finder.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/server/jsdepend/amd-reference-finder.js b/server/jsdepend/amd-reference-finder.js index 59703a87..8160cf82 100644 --- a/server/jsdepend/amd-reference-finder.js +++ b/server/jsdepend/amd-reference-finder.js @@ -10,7 +10,7 @@ * Contributors: * Kris De Volder - initial API and implementation ******************************************************************************/ - + /*global require define console module*/ if (typeof define !== 'function') { var define = require('amdefine')(module); @@ -83,17 +83,22 @@ var requirePat = objectPat({ var stringVar = variablePat('string'); var starVar = variablePat(); +var stringLitPat = objectPat({ + type: 'Literal', + value: stringVar +}); + var arrayExp = objectPat({ "type": "ArrayExpression", "elements": starVar }); -// Given a parse-tree, find references to other modules in that module. +// Given a parse-tree, find references to other modules in that module. function findReferences(tree, callback) { var foundList = []; var foundSet = {}; - + function addFound(name) { if (typeof(name)==='string') { if (!foundSet.hasOwnProperty(name)) { @@ -102,23 +107,23 @@ function findReferences(tree, callback) { } } } - + function addArrayElements(args) { for (var i = 0; i < args.length; i++) { var arg = args[i]; - if (arg.type === 'Literal' && typeof(arg.value)==='string') { + if (matches(stringLitPat, arg)) { addFound(arg.value); } } } - + walk(tree, function (node) { //dumpTree(node); if (matches(definePat, node)) { addArrayElements(defineVar.value); } else if (matches(requirePat, node)) { var arg = requireParam.value; - if (arg.type === 'Literal' && typeof(arg.value)==='string') { + if (matches(stringLitPat, arg)) { addFound(arg.value); } else if (matches(arrayExp, arg)) { addArrayElements(starVar.value); From 13965bc3a5f13fa1b73e01254cad59f747d232ca Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 28 Mar 2013 08:48:36 +0900 Subject: [PATCH 48/64] Let computed-style property accesses be renamed: eg- rename bar here: foo[bar] --- client/scripts/plugins/esprima/refactoringSupport.js | 2 +- tests/client/esprima/refactoringSupportTests.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/scripts/plugins/esprima/refactoringSupport.js b/client/scripts/plugins/esprima/refactoringSupport.js index 11994bb2..368ec072 100644 --- a/client/scripts/plugins/esprima/refactoringSupport.js +++ b/client/scripts/plugins/esprima/refactoringSupport.js @@ -53,7 +53,7 @@ define(function (require) { return node === parent.value; } if (parent.type === 'MemberExpression') { - return node === parent.object; + return node === parent.object || (node === parent.property && parent.computed); } switch(parent.type) { diff --git a/tests/client/esprima/refactoringSupportTests.js b/tests/client/esprima/refactoringSupportTests.js index 5fdf5847..fb7c90f3 100644 --- a/tests/client/esprima/refactoringSupportTests.js +++ b/tests/client/esprima/refactoringSupportTests.js @@ -75,6 +75,7 @@ define(['plugins/esprima/refactoringSupport', "orion/assert"], function(refactor }; tests['test expression kinds'] = function() { doTest("var xxx; [xxx,xxx];", "xxx", 1); + doTest("var xxx; xxx[xxx];", "xxx", 1); doTest("var xxx; ['xxx','xxx'];", "xxx", 0, [1,2]); doTest("var xxx; xxx = xxx;", "xxx", 1); doTest("var xxx; xxx.xxx;", "xxx", 1, [2]); From a041d7521aba1e2be6d950c55d1bc532396619ba Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 28 Mar 2013 08:49:05 +0900 Subject: [PATCH 49/64] Fix type in types so that array hovers will look nice. --- client/scripts/plugins/esprima/types.js | 8 ++++---- tests/client/esprima/navigationTests.js | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 88fb3ee8..d7c7ebd1 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1723,7 +1723,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return typeObj.name; case 'TypeApplication': - case 'ArrayExpressopm': + case 'ArrayType': return "Array"; case 'FunctionType': @@ -1828,9 +1828,9 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { res += ">"; } return res; - case 'ArrayExpressopm': - if (typeObj.applications) { - typeObj.applications.forEach(function(elt) { + case 'ArrayType': + if (typeObj.elements) { + typeObj.elements.forEach(function(elt) { parts.push(self.convertToHtml(elt, depth+1)); }); } diff --git a/tests/client/esprima/navigationTests.js b/tests/client/esprima/navigationTests.js index 85d48cdb..d676c849 100644 --- a/tests/client/esprima/navigationTests.js +++ b/tests/client/esprima/navigationTests.js @@ -335,6 +335,11 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "foo[x]", "x", "Number", "x : Number", 1); }; + tests.testInsideArrayAccess1 = function() { + doSameFileTest("var x = 0;\n" + + "var foo = [];\n" + + "foo[x]", "x", "Number", "x : Number", 1); + }; ////////////////////////////////////////////////////////// // full file inferencing @@ -438,6 +443,12 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp "var other = first[0][0]", "other", "Number", "other : Number", 1); }; + tests.testArray8 = function() { + doSameFileTest( + "var first = ['Andrew', 'Eisenberg'];\n" + + "first", "first", "[String]", "first : [String]", 1); + }; + // jsdoc testing tests.testJSDoc0 = function() { From ad4da781763ec4dbd783a43ff6b4a85dac6bba74 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Thu, 28 Mar 2013 09:34:55 +0900 Subject: [PATCH 50/64] Fix type error for empty union types. --- client/scripts/plugins/esprima/types.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index d7c7ebd1..1eb40821 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1730,7 +1730,9 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { return "Function"; case 'UnionType': - return typeObj.expressions[0]; + return typeObj.expressions && typeObj.expressions.length > 0 ? + this.convertToSimpleTypeName(typeObj.expressions[0]) : + "Object"; case 'RecordType': return "Object"; From 06d55f611422a5513b1ba747776d63d05d1b7841 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 29 Mar 2013 07:53:30 +0900 Subject: [PATCH 51/64] Fix for issue #265 ensure that editor switching does not produce dirty editors. --- client/scripts/scripted/utils/navHistory.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/scripts/scripted/utils/navHistory.js b/client/scripts/scripted/utils/navHistory.js index 52e232aa..55c3ef39 100644 --- a/client/scripts/scripted/utils/navHistory.js +++ b/client/scripts/scripted/utils/navHistory.js @@ -109,6 +109,9 @@ function(mSidePanelManager, mPaneFactory, mPageState, mOsUtils, editorUtils, scr var mainEditor = editorUtils.getMainEditor(); var subEditor = editorUtils.getSubEditor(); + var mainWasDirty = mainEditor.isDirty(); + var subWasDirty = subEditor.isDirty(); + var mainPath = mainEditor.getFilePath(); var mainScrollpos = mainEditor.getScroll(); var mainSel = mainEditor.getTextView().getSelection(); @@ -149,6 +152,13 @@ function(mSidePanelManager, mPaneFactory, mPageState, mOsUtils, editorUtils, scr } else { mainEditor.getTextView().focus(); } + + if (!mainWasDirty) { + subEditor.setDirty(false); + } + if (!subWasDirty) { + mainEditor.setDirty(false); + } }); }); }; From a9598e529edd3bbbc08fcbe5ed9495b5878319fd Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Mon, 1 Apr 2013 09:32:38 -0700 Subject: [PATCH 52/64] Issue 271 fixed --- .../scripts/scripted/processConfiguration.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/client/scripts/scripted/processConfiguration.js b/client/scripts/scripted/processConfiguration.js index 66f723df..e79f2116 100644 --- a/client/scripts/scripted/processConfiguration.js +++ b/client/scripts/scripted/processConfiguration.js @@ -26,10 +26,10 @@ define(function() { // editor.expandtab (boolean) // editor.tabsize (number) // rule: if possible (compatible), copy one config to the other - var editor_expandtab_set = dotScripted.editor && dotScripted.editor.expandtab !== null; - var editor_tabsize_set = dotScripted.editor && dotScripted.editor.tabsize !== null; - var formatter_js_indent_size_set = dotScripted.formatter && dotScripted.formatter.js && dotScripted.formatter.js.indent_size !== null; - var formatter_js_indent_char_set = dotScripted.formatter && dotScripted.formatter.js && dotScripted.formatter.js.indent_char !== null; + var editor_expandtab_set = dotScripted.editor && (typeof dotScripted.editor.expandtab !== 'undefined'); + var editor_tabsize_set = dotScripted.editor && (typeof dotScripted.editor.tabsize !== 'undefined'); + var formatter_js_indent_size_set = dotScripted.formatter && dotScripted.formatter.js && (typeof dotScripted.formatter.js.indent_size !== 'undefined'); + var formatter_js_indent_char_set = dotScripted.formatter && dotScripted.formatter.js && (typeof dotScripted.formatter.js.indent_char !== 'undefined'); // Just do the common cases for now: if (editor_expandtab_set || editor_tabsize_set) { @@ -80,16 +80,16 @@ define(function() { } else { dotScripted.editor.expandtab = true; } - if (formatter_js_indent_size_set) { - // Set the tabsize to match the indent size - var indentsize = dotScripted.formatter.js.indent_size; - if (!dotScripted.editor) { - dotScripted.editor = { - "tabsize": indentsize - }; - } else { - dotScripted.editor.tabsize = indentsize; - } + } + if (formatter_js_indent_size_set) { + // Set the tabsize to match the indent size + var indentsize = dotScripted.formatter.js.indent_size; + if (!dotScripted.editor) { + dotScripted.editor = { + "tabsize": indentsize + }; + } else { + dotScripted.editor.tabsize = indentsize; } } } From c6da6e873256c07df55d0e95e08cefb6ee288495 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 29 Mar 2013 13:38:03 +0900 Subject: [PATCH 53/64] Some fixes for refactoring support around function parameters variables. --- .../plugins/esprima/refactoringSupport.js | 28 +++++++++++++++---- .../client/esprima/refactoringSupportTests.js | 7 ++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/client/scripts/plugins/esprima/refactoringSupport.js b/client/scripts/plugins/esprima/refactoringSupport.js index 368ec072..3597ea27 100644 --- a/client/scripts/plugins/esprima/refactoringSupport.js +++ b/client/scripts/plugins/esprima/refactoringSupport.js @@ -114,8 +114,26 @@ define(function (require) { ? parentStack.length-2 : parentStack.length-1; for (i = startStack; i >= 0 ; i--) { if (parentStack[i].type === 'FunctionExpression' || - parentStack[i].type === 'FunctionDeclaration' || - parentStack[i].type === 'Program') { + parentStack[i].type === 'FunctionDeclaration') { + + // if the node is a parameter, then add the function as the scope + // if the node is an identifier in the function body, then add the body. + // this allows for a distinction between function parameters and vars declared in them + var found; + if (parentStack[i].params && parentStack[i].params.length > 0) { + for (var j = 0; j < parentStack[i].params.length; j++) { + if (parentStack[i].params[j] === node) { + declScopes.push(parentStack[i].range); + found = true; + break; + } + } + } + + if (!found) { + declScopes.push(parentStack[i].body.range); + } + } else if (parentStack[i].type === 'Program') { declScopes.push(parentStack[i].range); break; @@ -146,9 +164,9 @@ define(function (require) { // next, order declScopes from smallest to biggest declScopes.sort(function(l, r) { - var lSize = l.end - l.start; - var rSize = r.end - r.start; - return rSize - lSize; // TODO reverse??? + var lSize = l[1] - l[0]; + var rSize = r[1] - r[0]; + return lSize - rSize; }); var targetScope; diff --git a/tests/client/esprima/refactoringSupportTests.js b/tests/client/esprima/refactoringSupportTests.js index fb7c90f3..0fc9ef0d 100644 --- a/tests/client/esprima/refactoringSupportTests.js +++ b/tests/client/esprima/refactoringSupportTests.js @@ -37,6 +37,7 @@ define(['plugins/esprima/refactoringSupport', "orion/assert"], function(refactor if (iter === selectionCount) { return { start: next, end: next + toFind.length }; } + next = next + toFind.length; iter++; } return null; @@ -87,15 +88,11 @@ define(['plugins/esprima/refactoringSupport', "orion/assert"], function(refactor tests['test function arguments'] = function() { doTest('function f(xxx) { xxx; }', "xxx", 1); doTest('function f(yyy,xxx) { xxx; }', "xxx", 1); - }; - - tests['_test functions still failing'] = function() { doTest('function f(yyy,xxx) { xxx; function f2(xxx) { xxx; } xxx; } xxx;', "xxx", 1, [2,3,5]); - doTest('function f(yyy,xxx) { xxx; } var xxx;', "xxx", 2, [0,1]); doTest('var xxx; function f(yyy,xxx) { xxx; } xxx;', "xxx", 0, [1,2]); + doTest('function f(yyy,xxx) { xxx; } var xxx;', "xxx", 2, [0,1]); doTest('var xxx; function f(xxx) { var xxx; xxx; } xxx;', "xxx", 2, [0,1,4]); }; - return tests; }); \ No newline at end of file From fd3707c1d3a582cf28fc676e1ee7d0433a6de721 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 31 May 2013 10:09:25 -0700 Subject: [PATCH 54/64] Upgrade to doctrine 0.0.4 1. ensure all tests still passing. 2. ensure calls to doctrine's stringify are invoked correctly. --- client/component.json | 2 +- client/scripts/plugins/esprima/esprimaJsContentAssist.js | 8 ++++---- client/scripts/plugins/esprima/types.js | 3 ++- tests/client/esprima/navigationTests.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/component.json b/client/component.json index c0870d25..6becbf21 100644 --- a/client/component.json +++ b/client/component.json @@ -11,6 +11,6 @@ "qunit": "1.10.0", "json5": "0.1.0", "probes": "0.1.0", - "doctrine": "git://github.com/aeisenberg/doctrine.git" + "doctrine": "0.0.4" } } diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index b0e94f43..4b8cd738 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -2286,7 +2286,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr * filters types from the environment that should not be exported */ function filterTypes(environment, kind, moduleTypeObj, provided) { - var moduleTypeName = doctrine.stringify(moduleTypeObj); + var moduleTypeName = doctrine.type.stringify(moduleTypeObj, {compact: true}); var allTypes = environment.getAllTypes(); allTypes.clearDefaultGlobal(); @@ -2342,7 +2342,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr for (var defName in type) { if (type.hasOwnProperty(defName)) { var def = type[defName]; - def.typeSig = doctrine.stringify(def.typeObj); + def.typeSig = doctrine.type.stringify(def.typeObj, {compact: true}); delete def._typeObj; } } @@ -2353,7 +2353,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr if (provided.hasOwnProperty(defName)) { var def = provided[defName]; if (def.typeObj) { - def.typeSig = doctrine.stringify(def.typeObj); + def.typeSig = doctrine.type.stringify(def.typeObj, {compact: true}); delete def._typeObj; } } @@ -2710,7 +2710,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr // warning...not all cases handled here...eg- union types if (mTypes.isFunctionOrConstructor(modTypeObj) || (environment.findType(modTypeObj) && environment.findType(modTypeObj).$$isBuiltin)) { - providedType = doctrine.stringify(modTypeObj); + providedType = doctrine.type.stringify(modTypeObj, {compact: true}); } return { diff --git a/client/scripts/plugins/esprima/types.js b/client/scripts/plugins/esprima/types.js index 1eb40821..c1bdc84b 100644 --- a/client/scripts/plugins/esprima/types.js +++ b/client/scripts/plugins/esprima/types.js @@ -1615,6 +1615,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { case 'OptionalType': case 'NullableType': return { + prefix: true, type: jsdocType.type, expression: self.convertJsDocType(jsdocType.expression, env, doCombine, depth) }; @@ -1769,7 +1770,7 @@ function(proposalUtils, scriptedLogger/*, doctrine*/) { if (useHtml) { return this.convertToHtml(typeObj, 0); } - var res = doctrine.stringify(typeObj); + var res = doctrine.type.stringify(typeObj, {compact: true}); res = res.replace(JUST_DOTS_REGEX, "{...}"); res = res.replace(UNDEFINED_OR_EMPTY_OBJ, ""); return res; diff --git a/tests/client/esprima/navigationTests.js b/tests/client/esprima/navigationTests.js index d676c849..aea539f9 100644 --- a/tests/client/esprima/navigationTests.js +++ b/tests/client/esprima/navigationTests.js @@ -46,7 +46,7 @@ define(["plugins/esprima/esprimaJsContentAssist", "orion/assert"], function(mEsp if (!actual) { assert.fail("No definition found for:\n" + expected.hoverText ); } - assert.equal(doctrine.stringify(actual.typeObj), expected.typeSig, "Invalid type name in definition"); + assert.equal(doctrine.type.stringify(actual.typeObj, {compact: true}), expected.typeSig, "Invalid type name in definition"); assert.equal(actual.path, expected.path, "Invalid path in definition"); if (!expected.range) { assert.ok(!actual.range, "Should not have found a range object: " + actual.range); From 8ce91bfe02a60a6eb98e75204f37cfdc10fe57ed Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 3 Feb 2014 15:35:07 -0800 Subject: [PATCH 55/64] Add user agent as required by github api --- server/plugable-fs/github-fs/github-rest-client.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/plugable-fs/github-fs/github-rest-client.js b/server/plugable-fs/github-fs/github-rest-client.js index a0b201bc..aa7cc859 100644 --- a/server/plugable-fs/github-fs/github-rest-client.js +++ b/server/plugable-fs/github-fs/github-rest-client.js @@ -35,6 +35,7 @@ function configure(options) { request: function (request, config) { var headers = request.headers || (request.headers = {}); headers.Authorization = "token " + token; + headers["User-Agent"] = "Scripted"; return request; } }); From 44a2811b758fd9ff5635f4bd12557f88a5fd7da9 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 3 Feb 2014 15:35:35 -0800 Subject: [PATCH 56/64] Add instructions for creating secret token for github use. --- server/plugable-fs/github-fs/README-secret.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 server/plugable-fs/github-fs/README-secret.txt diff --git a/server/plugable-fs/github-fs/README-secret.txt b/server/plugable-fs/github-fs/README-secret.txt new file mode 100644 index 00000000..1e3010c5 --- /dev/null +++ b/server/plugable-fs/github-fs/README-secret.txt @@ -0,0 +1,12 @@ +The github-fs needs a file called +'secret.json' which contains a secret token for accessing github rest api. +The file should look like this (excluding the ----) +---------------------- +{ + "token" : "???????" +} +--------------------- + +In place of the "????????????" a secret token must be placed that was created via: + +https://github.com/settings/tokens/new From 4b36172a72129ec66df18cd48191dbcd849acdc8 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 3 Feb 2014 15:39:38 -0800 Subject: [PATCH 57/64] Fix some NPEs --- .../scripts/plugins/esprima/esprimaJsContentAssist.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/scripts/plugins/esprima/esprimaJsContentAssist.js b/client/scripts/plugins/esprima/esprimaJsContentAssist.js index 4b8cd738..ec837e2a 100644 --- a/client/scripts/plugins/esprima/esprimaJsContentAssist.js +++ b/client/scripts/plugins/esprima/esprimaJsContentAssist.js @@ -1795,7 +1795,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr } var type = this._allTypes[this.scope(target)]; // do not allow augmenting built in types - if (!type.$$isBuiltin) { + if (type && !type.$$isBuiltin) { // if new type name is not more general than old type, do not replace if (typeContainsProperty(type, name) && leftTypeIsMoreGeneral(typeObj, type[name].typeObj, this)) { // do nuthin @@ -1902,7 +1902,7 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr }; var innerLookup = function(name, type, allTypes) { - var res = type[name]; + var res = type && type[name]; var proto = type.$$proto; if (res) { @@ -1916,9 +1916,9 @@ define(["plugins/esprima/esprimaVisitor", "plugins/esprima/types", "plugins/espr var targetType = this._allTypes[this.scope(target)]; // uncomment this if we want to hide errors where there is an unknown type being placed on the scope stack -// if (!targetType) { -// targetType = this.globalScope() -// } + if (!targetType) { + targetType = this.globalScope(); + } var res = innerLookup(swapper(name), targetType, this._allTypes); return res; }, From 21ece2fad866fc5a67857ea907f00d3dff7158bc Mon Sep 17 00:00:00 2001 From: Alain Kalker Date: Thu, 6 Feb 2014 19:46:26 +0100 Subject: [PATCH 58/64] Fix QUnit paths --- tests/client/scriptedClientServerTests.html | 6 +++--- tests/client/scriptedClientTests.html | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/client/scriptedClientServerTests.html b/tests/client/scriptedClientServerTests.html index 141448da..364d6af0 100644 --- a/tests/client/scriptedClientServerTests.html +++ b/tests/client/scriptedClientServerTests.html @@ -4,8 +4,8 @@ Client tests that require a server - - + + + +