mirror of
https://github.com/getredash/redash.git
synced 2026-05-13 16:01:20 -04:00
Switch from CodeMirror to Ace editor
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
"es5-shim": "2.0.8",
|
||||
"angular-moment": "0.10.3",
|
||||
"moment": "~2.8.0",
|
||||
"codemirror": "4.8.0",
|
||||
"underscore": "1.5.1",
|
||||
"pivottable": "2.0.2",
|
||||
"cornelius": "https://github.com/restorando/cornelius.git",
|
||||
@@ -35,7 +34,8 @@
|
||||
"angular-ui-sortable": "~0.13.4",
|
||||
"angular-resizable": "^1.2.0",
|
||||
"material-design-iconic-font": "^2.2.0",
|
||||
"plotly.js": "^1.9.0"
|
||||
"plotly.js": "^1.9.0",
|
||||
"angular-ui-ace": "bower"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "1.2.18",
|
||||
|
||||
@@ -12,14 +12,12 @@
|
||||
<!-- build:css /styles/main.css -->
|
||||
<link rel="stylesheet" href="/styles/superflat_redash.css">
|
||||
<link rel="stylesheet" href="/bower_components/material-design-iconic-font/dist/css/material-design-iconic-font.css">
|
||||
<link rel="stylesheet" href="/bower_components/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="/bower_components/gridster/dist/jquery.gridster.css">
|
||||
<link rel="stylesheet" href="/bower_components/pivottable/dist/pivot.css">
|
||||
<link rel="stylesheet" href="/bower_components/cornelius/src/cornelius.css">
|
||||
<link rel="stylesheet" href="/bower_components/angular-ui-select/dist/select.css">
|
||||
<link rel="stylesheet" href="/bower_components/pace/themes/pace-theme-minimal.css">
|
||||
<link rel="stylesheet" href="/bower_components/font-awesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="/bower_components/codemirror/addon/hint/show-hint.css">
|
||||
<link rel="stylesheet" href="/bower_components/leaflet/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="/bower_components/angular-resizable/src/angular-resizable.css">
|
||||
<link rel="stylesheet" href="/styles/redash.css">
|
||||
|
||||
@@ -11,14 +11,12 @@
|
||||
|
||||
<!-- build:css /styles/embed.css -->
|
||||
<link rel="stylesheet" href="/styles/superflat_redash.css">
|
||||
<link rel="stylesheet" href="/bower_components/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="/bower_components/gridster/dist/jquery.gridster.css">
|
||||
<link rel="stylesheet" href="/bower_components/pivottable/dist/pivot.css">
|
||||
<link rel="stylesheet" href="/bower_components/cornelius/src/cornelius.css">
|
||||
<link rel="stylesheet" href="/bower_components/angular-ui-select/dist/select.css">
|
||||
<link rel="stylesheet" href="/bower_components/pace/themes/pace-theme-minimal.css">
|
||||
<link rel="stylesheet" href="/bower_components/font-awesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="/bower_components/codemirror/addon/hint/show-hint.css">
|
||||
<link rel="stylesheet" href="/bower_components/leaflet/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="/styles/redash.css">
|
||||
<!-- endbuild -->
|
||||
|
||||
@@ -14,6 +14,7 @@ angular.module('redash', [
|
||||
'ngResource',
|
||||
'ngRoute',
|
||||
'ui.select',
|
||||
'ui.ace',
|
||||
'naif.base64',
|
||||
'ui.bootstrap.showErrors',
|
||||
'angularResizable',
|
||||
|
||||
@@ -60,6 +60,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
// By default Ace will try to load snippet files for the different modes and fail. We don't need them, so we use these
|
||||
// placeholders until we define our own.
|
||||
function defineDummySnippets(mode) {
|
||||
ace.define("ace/snippets/" + mode, ["require", "exports", "module"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
exports.snippetText = "";
|
||||
exports.scope = mode;
|
||||
});
|
||||
};
|
||||
|
||||
defineDummySnippets("python");
|
||||
defineDummySnippets("sql");
|
||||
defineDummySnippets("json");
|
||||
|
||||
function queryEditor() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -69,88 +84,85 @@
|
||||
'schema': '=',
|
||||
'syntax': '='
|
||||
},
|
||||
template: '<textarea></textarea>',
|
||||
template: '<div ui-ace="editorOptions" ng-model="query.query"></div>',
|
||||
link: {
|
||||
pre: function ($scope, element) {
|
||||
$scope.syntax = $scope.syntax || 'sql';
|
||||
|
||||
var modes = {
|
||||
'sql': 'text/x-sql',
|
||||
'python': 'text/x-python',
|
||||
'json': 'application/json'
|
||||
};
|
||||
$scope.editorOptions = {
|
||||
mode: 'json',
|
||||
require: ['ace/ext/language_tools'],
|
||||
advanced: {
|
||||
behavioursEnabled: true,
|
||||
enableSnippets: true,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
autoScrollEditorIntoView: true,
|
||||
},
|
||||
onLoad: function(editor) {
|
||||
// Test for snippet manager
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
editor.setShowPrintMargin(false);
|
||||
|
||||
var textarea = element.children()[0];
|
||||
var editorOptions = {
|
||||
mode: modes[$scope.syntax],
|
||||
lineWrapping: true,
|
||||
lineNumbers: true,
|
||||
readOnly: false,
|
||||
matchBrackets: true,
|
||||
autoCloseBrackets: true,
|
||||
extraKeys: {"Ctrl-Space": "autocomplete"}
|
||||
};
|
||||
|
||||
var additionalHints = [];
|
||||
|
||||
CodeMirror.commands.autocomplete = function(cm) {
|
||||
var hinter = function(editor, options) {
|
||||
var hints = CodeMirror.hint.anyword(editor, options);
|
||||
var cur = editor.getCursor(), token = editor.getTokenAt(cur).string;
|
||||
|
||||
hints.list = _.union(hints.list, _.filter(additionalHints, function (h) {
|
||||
return h.search(token) === 0;
|
||||
}));
|
||||
|
||||
return hints;
|
||||
};
|
||||
|
||||
// CodeMirror.showHint(cm, CodeMirror.hint.anyword);
|
||||
CodeMirror.showHint(cm, hinter);
|
||||
};
|
||||
|
||||
var codemirror = CodeMirror.fromTextArea(textarea, editorOptions);
|
||||
|
||||
codemirror.on('change', function(instance) {
|
||||
var newValue = instance.getValue();
|
||||
|
||||
if (newValue !== $scope.query.query) {
|
||||
$scope.$evalAsync(function() {
|
||||
$scope.query.query = newValue;
|
||||
$scope.$watch('syntax', function(syntax) {
|
||||
var newMode = 'ace/mode/' + syntax;
|
||||
editor.getSession().setMode(newMode);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('query.query', function () {
|
||||
if ($scope.query.query !== codemirror.getValue()) {
|
||||
codemirror.setValue($scope.query.query);
|
||||
}
|
||||
});
|
||||
$scope.$watch('schema', function(newSchema, oldSchema) {
|
||||
if (newSchema !== oldSchema) {
|
||||
var tokensCount = _.reduce(newSchema, function(totalLength, table) { return totalLength + table.columns.length }, 0);
|
||||
// If there are too many tokens we disable live autocomplete, as it makes typing slower.
|
||||
if (tokensCount > 5000) {
|
||||
editor.setOption('enableLiveAutocompletion', false);
|
||||
} else {
|
||||
editor.setOption('enableLiveAutocompletion', true);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch('schema', function (schema) {
|
||||
if (schema) {
|
||||
var keywords = [];
|
||||
_.each(schema, function (table) {
|
||||
keywords.push(table.name);
|
||||
_.each(table.columns, function (c) {
|
||||
keywords.push(c);
|
||||
});
|
||||
|
||||
$scope.$parent.$on("angular-resizable.resizing", function (event, args) {
|
||||
editor.resize();
|
||||
});
|
||||
|
||||
editor.focus();
|
||||
}
|
||||
};
|
||||
|
||||
var langTools = ace.require("ace/ext/language_tools");
|
||||
|
||||
var schemaCompleter = {
|
||||
getCompletions: function(state, session, pos, prefix, callback) {
|
||||
if (prefix.length === 0) { callback(null, []); return }
|
||||
|
||||
if (!$scope.schema.keywords) {
|
||||
var keywords = {};
|
||||
|
||||
_.each($scope.schema, function (table) {
|
||||
keywords[table.name] = 'Table';
|
||||
|
||||
_.each(table.columns, function (c) {
|
||||
keywords[c] = 'Column';
|
||||
keywords[table.name + "." + c] = 'Column';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
additionalHints = _.unique(keywords);
|
||||
$scope.schema.keywords = _.map(keywords, function(v, k) {
|
||||
return {
|
||||
name: k,
|
||||
value: k,
|
||||
score: 0,
|
||||
meta: v
|
||||
};
|
||||
});
|
||||
}
|
||||
callback(null, $scope.schema.keywords);
|
||||
}
|
||||
};
|
||||
|
||||
codemirror.refresh();
|
||||
});
|
||||
|
||||
$scope.$watch('syntax', function(syntax) {
|
||||
codemirror.setOption('mode', modes[syntax]);
|
||||
});
|
||||
|
||||
$scope.$watch('lock', function (locked) {
|
||||
var readOnly = locked ? 'nocursor' : false;
|
||||
codemirror.setOption('readOnly', readOnly);
|
||||
});
|
||||
langTools.addCompleter(schemaCompleter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -188,18 +188,14 @@ li.widget:hover {
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
/* CodeMirror */
|
||||
.CodeMirror {
|
||||
/* Editor */
|
||||
|
||||
.ace_editor {
|
||||
border: 1px solid #eee;
|
||||
height: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* Support for Font-Awesome in btn-xs */
|
||||
|
||||
.btn-xs > .fa {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<!-- build:js /scripts/plugins.js -->
|
||||
<script src="/bower_components/jquery/jquery.js"></script>
|
||||
<script src="/bower_components/ace-builds/src-min-noconflict/ace.js"></script>
|
||||
<script src="/bower_components/ace-builds/src-min-noconflict/mode-sql.js"></script>
|
||||
<script src="/bower_components/ace-builds/src-min-noconflict/mode-json.js"></script>
|
||||
<script src="/bower_components/ace-builds/src-min-noconflict/mode-python.js"></script>
|
||||
<script src="/bower_components/ace-builds/src-min-noconflict/ext-language_tools.js"></script>
|
||||
<script src="/bower_components/angular/angular.js"></script>
|
||||
<script src="/bower_components/angular-sanitize/angular-sanitize.js"></script>
|
||||
<script src="/bower_components/angular-resizable/src/angular-resizable.js"></script>
|
||||
@@ -11,14 +16,6 @@
|
||||
<script src="/bower_components/underscore/underscore.js"></script>
|
||||
<script src="/bower_components/moment/moment.js"></script>
|
||||
<script src="/bower_components/angular-moment/angular-moment.js"></script>
|
||||
<script src="/bower_components/codemirror/lib/codemirror.js"></script>
|
||||
<script src="/bower_components/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="/bower_components/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="/bower_components/codemirror/addon/hint/show-hint.js"></script>
|
||||
<script src="/bower_components/codemirror/addon/hint/anyword-hint.js"></script>
|
||||
<script src="/bower_components/codemirror/mode/sql/sql.js"></script>
|
||||
<script src="/bower_components/codemirror/mode/python/python.js"></script>
|
||||
<script src="/bower_components/codemirror/mode/javascript/javascript.js"></script>
|
||||
<script src="/bower_components/gridster/dist/jquery.gridster.js"></script>
|
||||
<script src="/bower_components/angular-growl/build/angular-growl.js"></script>
|
||||
<script src="/bower_components/pivottable/dist/pivot.js"></script>
|
||||
@@ -42,4 +39,5 @@
|
||||
<script src="/bower_components/angular-bootstrap-show-errors/src/showErrors.js"></script>
|
||||
<script src="/bower_components/d3/d3.js"></script>
|
||||
<script src="/bower_components/angular-ui-sortable/sortable.js"></script>
|
||||
<script src="/bower_components/angular-ui-ace/ui-ace.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
Reference in New Issue
Block a user