Switch from CodeMirror to Ace editor

This commit is contained in:
Arik Fraimovich
2016-06-30 18:28:37 +03:00
parent dfb92dbb4e
commit 2e1dce5961
7 changed files with 94 additions and 91 deletions

View File

@@ -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",

View File

@@ -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">

View File

@@ -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 -->

View File

@@ -14,6 +14,7 @@ angular.module('redash', [
'ngResource',
'ngRoute',
'ui.select',
'ui.ace',
'naif.base64',
'ui.bootstrap.showErrors',
'angularResizable',

View File

@@ -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);
}
}
};

View File

@@ -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 {

View File

@@ -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 -->