浏览代码

[core] Introduce Filebrowser and Metastore links on Ace Editor

Enrico Berti 10 年之前
父节点
当前提交
616cf17

+ 9 - 0
apps/spark/src/spark/static/spark/js/ace.autocomplete.js

@@ -36,6 +36,7 @@ var hasExpired = function (timestamp) {
 function Autocomplete(options) {
 function Autocomplete(options) {
   this.options = options;
   this.options = options;
   this.currentDB = "";
   this.currentDB = "";
+  this.currentTables = [];
 }
 }
 
 
 Autocomplete.prototype.jsonCalls = function (options) {
 Autocomplete.prototype.jsonCalls = function (options) {
@@ -265,3 +266,11 @@ Autocomplete.prototype.setDatabase = function (db) {
 Autocomplete.prototype.getDatabase = function () {
 Autocomplete.prototype.getDatabase = function () {
   return this.currentDB;
   return this.currentDB;
 }
 }
+
+Autocomplete.prototype.setCurrentTables = function (tables) {
+  this.currentTables = tables;
+}
+
+Autocomplete.prototype.getCurrentTables = function () {
+  return this.currentTables;
+}

+ 4 - 0
apps/spark/src/spark/templates/editor.mako

@@ -63,6 +63,10 @@ ${ editorComponents.commonJS() }
     aceAutocomplete.setDatabase(db);
     aceAutocomplete.setDatabase(db);
   });
   });
 
 
+  huePubSub.subscribe('assist.firstLevelChange', function (tables) {
+    aceAutocomplete.setCurrentTables(tables);
+  });
+
   var options = ${ options_json | n,unicode };
   var options = ${ options_json | n,unicode };
 
 
   viewModel = new EditorViewModel(${ notebooks_json | n,unicode }, options);
   viewModel = new EditorViewModel(${ notebooks_json | n,unicode }, options);

+ 1 - 0
apps/spark/src/spark/templates/editor_components.mako

@@ -464,6 +464,7 @@ from django.utils.translation import ugettext as _
           extraCompleters: completers,
           extraCompleters: completers,
           errors: errors,
           errors: errors,
           autocompleter: aceAutocomplete,
           autocompleter: aceAutocomplete,
+          openIt: '${ _("Double click to open it") }',
           placeholder: $root.snippetPlaceholders[type()] }"></div>
           placeholder: $root.snippetPlaceholders[type()] }"></div>
       </div>
       </div>
   </div>
   </div>

+ 4 - 0
apps/spark/src/spark/templates/notebook.mako

@@ -71,6 +71,10 @@ ${ editorComponents.commonJS() }
     aceAutocomplete.setDatabase(db);
     aceAutocomplete.setDatabase(db);
   });
   });
 
 
+  huePubSub.subscribe('assist.firstLevelChange', function (tables) {
+    aceAutocomplete.setCurrentTables(tables);
+  });
+
   var options = ${ options_json | n,unicode };
   var options = ${ options_json | n,unicode };
 
 
   viewModel = new EditorViewModel(${ notebooks_json | n,unicode }, options);
   viewModel = new EditorViewModel(${ notebooks_json | n,unicode }, options);

+ 153 - 2
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -1689,16 +1689,18 @@ ko.bindingHandlers.aceEditor = {
       editor.setValue(options.placeholder);
       editor.setValue(options.placeholder);
     }
     }
 
 
-    var _extraOptions = $.totalStorage("hue.ace.options") || {};
-    $.extend(editorOptions, options.editorOptions || _extraOptions);
+    var userOptions = $.totalStorage("hue.ace.options") || {};
+    $.extend(editorOptions, options.editorOptions || userOptions);
 
 
     editor.setOptions(editorOptions);
     editor.setOptions(editorOptions);
+
     editor.on("focus", function () {
     editor.on("focus", function () {
       if (options.placeholder && editor.getValue() == options.placeholder) {
       if (options.placeholder && editor.getValue() == options.placeholder) {
         editor.setValue("");
         editor.setValue("");
       }
       }
       onFocus(editor);
       onFocus(editor);
     });
     });
+
     editor.on("blur", function () {
     editor.on("blur", function () {
       options.value(editor.getValue());
       options.value(editor.getValue());
       if (editor.getValue() == "" && options.placeholder) {
       if (editor.getValue() == "" && options.placeholder) {
@@ -1706,13 +1708,162 @@ ko.bindingHandlers.aceEditor = {
       }
       }
       onBlur(editor);
       onBlur(editor);
     });
     });
+
     editor.on("copy", function () {
     editor.on("copy", function () {
       onCopy(editor);
       onCopy(editor);
     });
     });
+
     editor.on("paste", function () {
     editor.on("paste", function () {
       onPaste(editor);
       onPaste(editor);
     });
     });
 
 
+    ace.define("huelink", [], function (require, exports, module) {
+      "use strict";
+
+      var Oop = ace.require("ace/lib/oop");
+      var Event = ace.require("ace/lib/event");
+      var Range = ace.require("ace/range").Range;
+      var EventEmitter = ace.require("ace/lib/event_emitter").EventEmitter;
+      var Tooltip = ace.require("ace/tooltip").Tooltip;
+
+      var HueLink = function (editor) {
+        if (editor.hueLink)
+          return;
+        editor.hueLink = this;
+        this.editor = editor;
+        Tooltip.call(this, editor.container);
+
+        this.update = this.update.bind(this);
+        this.onMouseMove = this.onMouseMove.bind(this);
+        this.onMouseOut = this.onMouseOut.bind(this);
+        this.onClick = this.onClick.bind(this);
+        Event.addListener(editor.renderer.scroller, "mousemove", this.onMouseMove);
+        Event.addListener(editor.renderer.content, "mouseout", this.onMouseOut);
+        Event.addListener(editor.renderer.content, "dblclick", this.onClick);
+      };
+
+      Oop.inherits(HueLink, Tooltip);
+
+      (function () {
+        Oop.implement(this, EventEmitter);
+
+        this.token = {};
+        this.marker = null;
+
+        this.update = function () {
+          this.$timer = null;
+          var editor = this.editor;
+          var renderer = editor.renderer;
+
+          var canvasPos = renderer.scroller.getBoundingClientRect();
+          var offset = (this.x + renderer.scrollLeft - canvasPos.left - renderer.$padding) / renderer.characterWidth;
+          var row = Math.floor((this.y + renderer.scrollTop - canvasPos.top) / renderer.lineHeight);
+          var col = Math.round(offset);
+
+          var screenPos = {row: row, column: col, side: offset - col > 0 ? 1 : -1};
+          var session = editor.session;
+          var docPos = session.screenToDocumentPosition(screenPos.row, screenPos.column);
+
+          var selectionRange = editor.selection.getRange();
+          if (!selectionRange.isEmpty()) {
+            if (selectionRange.start.row <= row && selectionRange.end.row >= row)
+              return this.clear();
+          }
+
+          var line = editor.session.getLine(docPos.row);
+          if (docPos.column == line.length) {
+            var clippedPos = editor.session.documentToScreenPosition(docPos.row, docPos.column);
+            if (clippedPos.column != screenPos.column) {
+              return this.clear();
+            }
+          }
+
+          var token = editor.session.getTokenAt(docPos.row, docPos.column);
+
+          var isMetastoreLink = Object.keys(valueAccessor().autocompleter.getCurrentTables()).indexOf(token.value) > -1;
+
+          if (token.value.indexOf("'/") == 0 && token.value.lastIndexOf("'") == token.value.length - 1 ||
+              token.value.indexOf("\"/") == 0 && token.value.lastIndexOf("\"") == token.value.length - 1 ||
+              isMetastoreLink) {
+            // add highlight for the clicked token
+            var range = new AceRange(docPos.row, token.start, docPos.row, token.start + token.value.length);
+            editor.session.removeMarker(this.marker);
+            this.marker = editor.session.addMarker(range, 'ace_bracket red');
+            editor.renderer.setCursorStyle("pointer");
+            this.setText(options.openIt);
+            if ($.totalStorage("hue.ace.showLinkTooltips") == null || $.totalStorage("hue.ace.showLinkTooltips")) {
+              $(".ace_tooltip").show();
+              this.show(null, this.x + 10, this.y + 10);
+            }
+            this.link = token;
+            this.isClearable = true
+          }
+          else {
+            this.clear();
+          }
+        };
+
+        this.clear = function () {
+          if (this.isClearable) {
+            $(".ace_tooltip").hide();
+            this.editor.session.removeMarker(this.marker);
+            this.editor.renderer.setCursorStyle("");
+            this.isClearable = false;
+            this.link = null;
+          }
+        };
+
+        this.onClick = function (e) {
+          if (this.link) {
+            this.link.editor = this.editor;
+            this._signal("open", this.link);
+            this.clear()
+          }
+        };
+
+        this.onMouseMove = function (e) {
+          if (this.editor.$mouseHandler.isMousePressed) {
+            if (!this.editor.selection.isEmpty())
+              this.clear();
+            return;
+          }
+          this.x = e.clientX;
+          this.y = e.clientY;
+          this.update();
+        };
+
+        this.onMouseOut = function (e) {
+          Tooltip.prototype.hide();
+          this.clear();
+        };
+
+        this.destroy = function () {
+          this.onMouseOut();
+          Event.removeListener(this.editor.renderer.scroller, "mousemove", this.onMouseMove);
+          Event.removeListener(this.editor.renderer.content, "mouseout", this.onMouseOut);
+          delete this.editor.hueLink;
+        };
+
+      }).call(HueLink.prototype);
+
+      exports.HueLink = HueLink;
+
+    });
+
+    HueLink = ace.require("huelink").HueLink;
+    editor.hueLink = new HueLink(editor);
+    editor.hueLink.on("open", function (token) {
+      if (token.value.indexOf("'/") == 0 && token.value.lastIndexOf("'") == token.value.length - 1) {
+        window.open("/filebrowser/#" + token.value.replace(/'/gi, ""));
+      }
+      else if (token.value.indexOf("\"/") == 0 && token.value.lastIndexOf("\"") == token.value.length - 1) {
+        window.open("/filebrowser/#" + token.value.replace(/\"/gi, ""));
+      }
+      else {
+        window.open("/metastore/table/" + valueAccessor().autocompleter.getDatabase() + "/" + token.value);
+      }
+    });
+
     function newCompleter(items) {
     function newCompleter(items) {
       return {
       return {
         getCompletions: function (editor, session, pos, prefix, callback) {
         getCompletions: function (editor, session, pos, prefix, callback) {

+ 4 - 0
desktop/core/src/desktop/templates/ko_components.mako

@@ -297,6 +297,10 @@ from django.utils.translation import ugettext as _
             self.assist.isLoading(false);
             self.assist.isLoading(false);
           };
           };
           self.assist.getData(self.assist.selectedMainObject(), force);
           self.assist.getData(self.assist.selectedMainObject(), force);
+
+          self.assist.firstLevelObjects.subscribe(function(value){
+            huePubSub.publish('assist.firstLevelChange', value);
+          });
         };
         };
 
 
         self.loadAssistSecondLevel = function(first) {
         self.loadAssistSecondLevel = function(first) {