summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorLakshmi Shanmugam2013-07-23 15:37:00 (EDT)
committer Bogdan Gheorghe2013-08-22 17:17:15 (EDT)
commit498df76156bad51710db42b7f21e22f914e6de65 (patch)
tree15b812899dd812f8bac7b12effa7976c47f50dce
parent721b903d390ce417e88ac73306ef48e0d631a044 (diff)
downloadorg.eclipse.orion.client-498df76156bad51710db42b7f21e22f914e6de65.zip
org.eclipse.orion.client-498df76156bad51710db42b7f21e22f914e6de65.tar.gz
org.eclipse.orion.client-498df76156bad51710db42b7f21e22f914e6de65.tar.bz2
initial work
Signed-off-by: Lakshmi Shanmugam <lshanmug@in.ibm.com>
-rw-r--r--bundles/org.eclipse.orion.client.ui/web/edit/setup.js8
-rw-r--r--bundles/org.eclipse.orion.client.ui/web/orion/occurrenceFinder.js94
-rw-r--r--bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.html18
-rw-r--r--bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.js362
4 files changed, 481 insertions, 1 deletions
diff --git a/bundles/org.eclipse.orion.client.ui/web/edit/setup.js b/bundles/org.eclipse.orion.client.ui/web/edit/setup.js
index 848a247..276259f 100644
--- a/bundles/org.eclipse.orion.client.ui/web/edit/setup.js
+++ b/bundles/org.eclipse.orion.client.ui/web/edit/setup.js
@@ -34,6 +34,7 @@ define([
'orion/editorCommands',
'orion/editor/editorFeatures',
'orion/editor/editor',
+ 'orion/occurrenceFinder',
'orion/syntaxchecker',
'orion/editor/textView',
'orion/editor/textModel',
@@ -56,7 +57,7 @@ define([
'orion/widgets/input/DropDownMenu'
], function(messages, require, EventTarget, lib, mSelection, mStatus, mProgress, mDialogs, mCommandRegistry, mExtensionCommands,
mFileClient, mOperationsClient, mSearchClient, mGlobalCommands, mOutliner, mProblems, mBlameAnnotation, mContentAssist, mEditorCommands, mEditorFeatures, mEditor,
- mSyntaxchecker, mTextView, mTextModel, mProjectionTextModel, mKeyBinding, mEmacs, mVI, mSearcher,
+ mOccurrenceFinder, mSyntaxchecker, mTextView, mTextModel, mProjectionTextModel, mKeyBinding, mEmacs, mVI, mSearcher,
mContentTypes, PageUtil, mInputManager, i18nUtil, mThemePreferences, mThemeData, EditorSettings, mEditorPreferences, URITemplate, Sidebar,
mTooltip, DropDownMenu) {
@@ -358,6 +359,11 @@ exports.setUpEditor = function(serviceRegistry, preferences, isReadOnly){
serviceRegistry.getService("orion.core.blame").addEventListener("blameChanged", function(event) { //$NON-NLS-1$ //$NON-NLS-0$
editor.showBlame(event.blameInfo);
});
+ // Occurrence service
+ var occurrenceFinder = new mOccurrenceFinder.OccurrenceFinder(serviceRegistry, editor);
+ editor.addEventListener("TextViewInstalled", function(event) { //$NON-NLS-0$
+ occurrenceFinder.findOccurrences(inputManager, event.textView);
+ });
var syntaxChecker = new mSyntaxchecker.SyntaxChecker(serviceRegistry, editor);
editor.addEventListener("InputChanged", function(evt) { //$NON-NLS-0$
diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/occurrenceFinder.js b/bundles/org.eclipse.orion.client.ui/web/orion/occurrenceFinder.js
new file mode 100644
index 0000000..086b91f
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.ui/web/orion/occurrenceFinder.js
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+/*global define window */
+
+define(['orion/Deferred'], function(Deferred) {
+
+var OccurrenceFinder = (function () {
+ function OccurrenceFinder(serviceRegistry, editor) {
+ this.registry = serviceRegistry;
+ this.editor = editor;
+ }
+ OccurrenceFinder.prototype = {
+ /* Looks up applicable references of occurrence service, calls references, calls the editor to show the occurrences. */
+ findOccurrences: function(inputManager, textView) {
+ function getServiceRefs(registry, contentType, title) {
+ var contentTypeService = registry.getService("orion.core.contenttypes"); //$NON-NLS-0$
+ function getFilteredServiceRef(registry, sReference, contentType) {
+ var contentTypeIds = sReference.getProperty("contentType"); //$NON-NLS-0$
+ return contentTypeService.isSomeExtensionOf(contentType, contentTypeIds).then(function(result) {
+ return result ? sReference : null;
+ });
+ }
+ var serviceRefs = registry.getServiceReferences("orion.edit.occurrences"); //$NON-NLS-0$
+ var filteredServiceRefs = [];
+ for (var i=0; i < serviceRefs.length; i++) {
+ var serviceRef = serviceRefs[i];
+ var pattern = serviceRef.getProperty("pattern"); // backwards compatibility //$NON-NLS-0$
+ if (serviceRef.getProperty("contentType")) { //$NON-NLS-0$
+ filteredServiceRefs.push(getFilteredServiceRef(registry, serviceRef, contentType));
+ } else if (pattern && new RegExp(pattern).test(title)) {
+ var d = new Deferred();
+ d.resolve(serviceRef);
+ filteredServiceRefs.push(d);
+ }
+ }
+
+ // Return a promise that gives the service references that aren't null
+ return Deferred.all(filteredServiceRefs, function(error) {return {_error: error}; }).then(
+ function(serviceRefs) {
+ var capableServiceRefs = [];
+ for (var i=0; i < serviceRefs.length; i++) {
+ var serviceRef = serviceRefs[i];
+ if (serviceRef && !serviceRef._error) {
+ capableServiceRefs.push(serviceRef);
+ }
+ }
+ return capableServiceRefs;
+ });
+ }
+
+ var occurrenceTimer;
+ var self = this;
+ var occurrencesService = self.registry.getService("orion.edit.occurrences"); //$NON-NLS-0$
+ var selectionListener = function(e) {
+ if (occurrenceTimer) {
+ window.clearTimeout(occurrenceTimer);
+ }
+ occurrenceTimer = window.setTimeout(function() {
+ occurrenceTimer = null;
+ var sel = self.editor.getSelection();
+ var word = self.editor.getText(sel.start, sel.end);
+ occurrencesService.findOccurrences(self.editor.getText(), word, sel).then(function (occurrences) {
+ self.editor.showOccurrences(occurrences);
+ });
+ }, 500);
+ };
+
+ inputManager.addEventListener("ContentTypeChanged", function(event) {//$NON-NLS-0$
+ textView.removeEventListener("Selection", selectionListener); //$NON-NLS-0$
+ getServiceRefs(self.registry, event.contentType, self.editor.getTitle()).then(function(serviceRefs) {
+ if (!serviceRefs || serviceRefs.length === 0) {
+ if (occurrenceTimer) {
+ window.clearTimeout(occurrenceTimer);
+ }
+ } else {
+ textView.addEventListener("Selection", selectionListener); //$NON-NLS-0$
+ }
+ });
+ });
+ }
+ };
+ return OccurrenceFinder;
+}());
+return {OccurrenceFinder: OccurrenceFinder};
+});
diff --git a/bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.html b/bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.html
new file mode 100644
index 0000000..6b799cd
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Occurrences Plugin</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <script src="../../requirejs/require.js"></script>
+ <script>
+ /*global require*/
+ require({
+ baseUrl: '../../'
+ });
+ require(['occurrences.js']);
+ </script>
+</head>
+<body>
+<h1>Esprima based Occurrences plugin</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.js b/bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.js
new file mode 100644
index 0000000..0c5a3e3
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.ui/web/plugins/occurrences/occurrences.js
@@ -0,0 +1,362 @@
+/*jslint browser:true*/
+/*global define orion console esprima */
+define(['orion/plugin', 'esprima/esprima'], function(PluginProvider) {
+
+ function createAST(context) {
+ var ast = esprima.parse(context.text, {
+ range: true,
+ loc: true,
+ tolerant: true
+ });
+ // if (ast !== null) {console.log(stringify(ast));}
+ return ast;
+ }
+
+ function isOccurrenceInSelScope(oScope, wScope) {
+ if (oScope.global && wScope.global) {
+ return true;
+ }
+ if (!oScope.global && !wScope.global && (oScope.name === wScope.name) && (oScope.loc.start.line === wScope.loc.start.line) &&
+ (oScope.loc.start.column === wScope.loc.start.column)) {
+ return true;
+ }
+ return false;
+ }
+
+ function filterOccurrences(occurrences, context) {
+ if (!context.scope) {
+ return null;
+ }
+ var matches = [];
+ for (var i = 0; i < occurrences.length; i++) {
+ if (isOccurrenceInSelScope(occurrences[i].scope, context.scope)) {
+ matches.push({
+ readAccess: occurrences[i].readAccess,
+ line: occurrences[i].node.loc.start.line,
+ start: occurrences[i].node.loc.start.column + 1,
+ end: occurrences[i].node.loc.end.column,
+ description: (occurrences[i].readAccess ? "occurrence of " : "write occurrence of") + context.word //$NON-NLS-0$ //$NON-NLS-1$
+ });
+ }
+ }
+ return matches;
+ }
+
+ function updateScope(node, scope) {
+ if (!node || !node.type) {
+ return;
+ }
+ switch (node.type) {
+ case 'FunctionDeclaration': //$NON-NLS-0$
+ scope.pop();
+ break;
+ case 'FunctionExpression': //$NON-NLS-0$
+ scope.pop();
+ break;
+ }
+ }
+
+ function traverse(node, context, func, occurrences, scope) {
+ func(node, context, occurrences, scope);
+ for (var key in node) {
+ if (node.hasOwnProperty(key)) {
+ var child = node[key];
+ if (typeof child === 'object' && child !== null) { //$NON-NLS-0$
+ if (Array.isArray(child)) {
+ child.forEach(function (node) {
+ traverse(node, context, func, occurrences, scope);
+ });
+ } else {
+ traverse(child, context, func, occurrences, scope);
+ }
+ }
+ }
+ }
+ updateScope(node, scope);
+ }
+
+ /* convert ast array to String */
+
+ function stringify(parsedProgram) {
+ var body = parsedProgram.body;
+ if (body.length === 1) {
+ body = body[0];
+ }
+ var replacer = function (key, value) {
+ if (key === 'computed') { //$NON-NLS-0$
+ return;
+ }
+ return value;
+ };
+ return JSON.stringify(body, replacer).replace(/"/g, '');
+ }
+
+ function checkIdentifier(node, context) {
+ if (node && node.type === 'Identifier') { //$NON-NLS-0$
+ if (node.name === context.word) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function findMatchingDeclarationScope(scope) {
+ for (var i = scope.length - 1; i >= 0; i--) {
+ if (scope[i].decl) {
+ return scope[i];
+ }
+ }
+ return null;
+ }
+
+ function addOccurrence(scope, node, context, occurrences, readAccess) {
+ if (node) {
+ if (readAccess === undefined) {
+ readAccess = true;
+ }
+
+ var mScope = findMatchingDeclarationScope(scope);
+ if (!mScope) {
+ return;
+ }
+
+ if ((node.range[0] === context.start) && (node.range[1] === context.end)) {
+ // if ((node.loc.start.line === context.line) && (node.loc.start.column === context.column.start)) {
+ if (mScope) {
+ context.scope = mScope;
+ } else {
+ console.error("matching declaration scope for selected type not found " + context.word); //$NON-NLS-0$
+ }
+ }
+
+ occurrences.push({
+ readAccess: readAccess,
+ node: node,
+ scope: mScope
+ });
+ }
+ }
+
+ function findOccurrence(node, context, occurrences, scope) {
+ if (!node || !node.type) {
+ return;
+ }
+ var readAccess = true;
+ switch (node.type) {
+ case 'Program': //$NON-NLS-0$
+ var curScope = {
+ global: true,
+ name: null,
+ decl: false
+ };
+ scope.push(curScope);
+ break;
+ case 'VariableDeclarator': //$NON-NLS-0$
+ if (checkIdentifier(node.id, context)) {
+ var varScope = scope.pop();
+ varScope.decl = true;
+ scope.push(varScope);
+ addOccurrence(scope, node.id, context, occurrences, false);
+ }
+ if (node.init) {
+ if (checkIdentifier(node.init, context)) {
+ addOccurrence(scope, node.init, context, occurrences);
+ break;
+ }
+ if (node.init.type === 'ObjectExpression') { //$NON-NLS-0$
+ var properties = node.init.properties;
+ for (var i = 0; i < properties.length; i++) {
+ // if (checkIdentifier (properties[i].key, context)) {
+ // var varScope = scope.pop();
+ // varScope.decl = true;
+ // scope.push(varScope);
+ // addOccurrence (scope, properties[i].key, context, occurrences, false);
+ // }
+ if (checkIdentifier(properties[i].value, context)) {
+ addOccurrence(scope, properties[i].value, context, occurrences);
+ }
+ }
+ }
+ }
+ break;
+ case 'ArrayExpression': //$NON-NLS-0$
+ if (node.elements) {
+ for (var i = 0; i < node.elements.length; i++) {
+ if (checkIdentifier(node.elements[i], context)) {
+ addOccurrence(scope, node.elements[i], context, occurrences);
+ }
+ }
+ }
+ break;
+ case 'AssignmentExpression': //$NON-NLS-0$
+ var leftNode = node.left;
+ if (checkIdentifier(leftNode, context)) {
+ addOccurrence(scope, leftNode, context, occurrences, false);
+ }
+ if (leftNode.type === 'MemberExpression') { //$NON-NLS-0$
+ if (checkIdentifier(leftNode.object, context)) {
+ addOccurrence(scope, leftNode.object, context, occurrences, false);
+ }
+ }
+ var rightNode = node.right;
+ if (checkIdentifier(rightNode, context)) {
+ addOccurrence(scope, rightNode, context, occurrences);
+ }
+ break;
+ case 'MemberExpression': //$NON-NLS-0$
+ if (checkIdentifier(node.object, context)) {
+ addOccurrence(scope, node.object, context, occurrences);
+ }
+ if (node.computed) { //computed = true for [], false for . notation
+ if (checkIdentifier(node.property, context)) {
+ addOccurrence(scope, node.property, context, occurrences);
+ }
+ }
+ break;
+ case 'BinaryExpression': //$NON-NLS-0$
+ if (checkIdentifier(node.left, context)) {
+ addOccurrence(scope, node.left, context, occurrences);
+ }
+ if (checkIdentifier(node.right, context)) {
+ addOccurrence(scope, node.right, context, occurrences);
+ }
+ break;
+ case 'UnaryExpression': //$NON-NLS-0$
+ if (checkIdentifier(node.argument, context)) {
+ addOccurrence(scope, node.argument, context, occurrences, node.operator === 'delete' ? false : true); //$NON-NLS-0$
+ }
+ break;
+ case 'IfStatement': //$NON-NLS-0$
+ if (checkIdentifier(node.test, context)) {
+ addOccurrence(scope, node.test, context, occurrences);
+ }
+ break;
+ case 'SwitchStatement': //$NON-NLS-0$
+ if (checkIdentifier(node.discriminant, context)) {
+ addOccurrence(scope, node.discriminant, context, occurrences, false);
+ }
+ break;
+ case 'UpdateExpression': //$NON-NLS-0$
+ if (checkIdentifier(node.argument, context)) {
+ addOccurrence(scope, node.argument, context, occurrences, false);
+ }
+ break;
+ case 'ConditionalExpression': //$NON-NLS-0$
+ if (checkIdentifier(node.test, context)) {
+ addOccurrence(scope, node.test, context, occurrences);
+ }
+ if (checkIdentifier(node.consequent, context)) {
+ addOccurrence(scope, node.consequent, context, occurrences);
+ }
+ if (checkIdentifier(node.alternate, context)) {
+ addOccurrence(scope, node.alternate, context, occurrences);
+ }
+ break;
+ case 'FunctionDeclaration': //$NON-NLS-0$
+ var curScope = {
+ global: false,
+ name: node.id.name,
+ loc: node.loc,
+ decl: false
+ };
+ scope.push(curScope);
+ if (node.params) {
+ for (var i = 0; i < node.params.length; i++) {
+ if (checkIdentifier(node.params[i], context)) {
+ var varScope = scope.pop();
+ varScope.decl = true;
+ scope.push(varScope);
+ addOccurrence(scope, node.params[i], context, occurrences, false);
+ }
+ }
+ }
+ break;
+ case 'FunctionExpression': //$NON-NLS-0$
+ var curScope = {
+ global: false,
+ name: null,
+ loc: node.loc,
+ decl: false
+ };
+ scope.push(curScope);
+ if (!node.params) break;
+ for (var i = 0; i < node.params.length; i++) {
+ if (checkIdentifier(node.params[i], context)) {
+ var varScope = scope.pop();
+ varScope.decl = true;
+ scope.push(varScope);
+ addOccurrence(scope, node.params[i], context, occurrences, false);
+ }
+ }
+ break;
+ case 'CallExpression': //$NON-NLS-0$
+ if (!node.arguments) {
+ break;
+ }
+ for (var j = 0; j < node.arguments.length; j++) {
+ if (checkIdentifier(node.arguments[j], context)) {
+ addOccurrence(scope, node.arguments[j], context, occurrences);
+ }
+ }
+ break;
+ case 'ReturnStatement': //$NON-NLS-0$
+ if (checkIdentifier(node.argument, context)) {
+ addOccurrence(scope, node.argument, context, occurrences);
+ }
+ }
+ }
+
+ function getOccurrences(context) {
+ var ast = createAST(context);
+ if (ast) {
+ var occurrences = [],
+ scope = [];
+ traverse(ast, context, findOccurrence, occurrences, scope);
+ occurrences = filterOccurrences(occurrences, context);
+ if (!occurrences) {
+ console.error("no matching occurrences found"); //$NON-NLS-0$
+ }
+ return occurrences;
+ }
+ console.error("ast is null"); //$NON-NLS-0$
+ return null;
+ }
+
+////
+ var headers = {
+ name: "Occurrence Plugin", //$NON-NLS-0$
+ version: "0.1", //$NON-NLS-0$
+ description: "Esprima-based mark occurrences plugin." //$NON-NLS-0$
+ };
+ var provider = new PluginProvider(headers);
+
+ // Create the service implementation for getting selected text
+ var serviceImpl = {
+ findOccurrences: function(text, word, selection) {
+ var context = {
+ word: word,
+ text: text,
+ start : selection.start,
+ end: selection.end,
+ scope: null
+ };
+
+ if (!context || !context.word) {
+ return null;
+ }
+ return getOccurrences (context);
+ }
+ };
+
+ var serviceProperties = {
+ name: 'Mark Occurrences', //$NON-NLS-0$
+ id: 'markoccurrences.editor', //$NON-NLS-0$
+ tooltip: 'Mark Occurrences', //$NON-NLS-0$
+ key: ['M', true, true], // Ctrl+Shift+M //$NON-NLS-0$
+ contentType: ['application/javascript'] //$NON-NLS-0$
+ };
+
+ provider.registerService("orion.edit.occurrences", serviceImpl, serviceProperties); //$NON-NLS-0$
+ provider.connect();
+});
+ \ No newline at end of file