"use strict"; /*global ace*/ angular.module("mp-ide.components.editor", [ "ui.ace", ]) .service("parser", //ideally this should probably be an ACE plugin but this was easier for now ["$window", "$q", function($window, $q) { var worker = null, working = {}, worked = 0; if (Boolean($window.Worker)) { worker = new Worker("assets/mp2-parser-worker.js"); worker.onmessage = function onParserWorkerMessage(msg) { var data = msg.data, work = working[data.id]; delete working[data.id]; work.resolve(data.parsed); }; } return { parse: function(input) { var id = worked++, work = $q.defer(); if (worker !== null) { worker.postMessage({ id: id, input: input, }); working[id] = work; } else { $q.all($window.mp2 ? [] : [$.getScript("assets/mp2-parser.js")]) .then(function() { var parsed; try { parsed = $window.mp2.parse(input); } catch (err) { parsed = { errors: [err], }; } work.resolve(parsed); }); } return work.promise; } }; }, ]) .directive("editor", ["$timeout", "parser", function($timeout, parser) { return { restrict: "E", scope: { model: "=editorModel", }, controller: "EditorCtrl", controllerAs: "ctrl", templateUrl: "components/editor/editor.html", link: { pre: function(scope, element, attrs, ctrl) { scope.model.options.onLoad = function onEditorLoaded(editor) { editor.$blockScrolling = Infinity; //fix warning //var session = editor.getSession(); //TODO: there is a flicker on load... AAAAAAHAHHAHAHAHAH! //TODO: disable this syntax debugging code var ttExt = ace.require("ace/token_tooltip"); new ttExt.TokenTooltip(editor); //jshint ignore:line //TODO: grab list of commands from Kyle's MongoDB IDE //TODO: integrate whitespace: use debounced `onchange` handler to call `wsExt.trimTrailingSpace(session);` //var wsExt = ace.require("ace/ext/whitespace"); // TODO: fork statusbar: row/col should start at 1 not 0 var sbExt = ace.require("ace/ext/statusbar"), sb = new sbExt.StatusBar(editor, editor.container); //TODO: fork keybinding_menu: bindKey, styling, could have more explanatory text, modal, maybe a Ctrl-P feature? // var kmExt = ace.require("ace/ext/keybinding_menu"); // kmExt.init(editor); // editor.commands.addCommands([ // { // name: "showKeyboardShortcuts", // bindKey: {win: "Ctrl-Alt-/", mac: "Command-Alt-/"}, // exec: function(editor, line) { // editor.showKeyboardShortcuts(); // }, // }, // ]); //TODO: redirect all window keys w/ modifiers to the most recently selected editor (to avoid editor find vs browser find) //TODO: use themed inline line number dialog, map to Ctrl+G as well please var pendingParse; scope.$watch("model.data", function onDataChanged(data) { $timeout.cancel(pendingParse); pendingParse = $timeout(parser.parse, 100, true, data) .then(function(parsed) { var annotations = parsed.errors.map(function(err) { return { row: err.line - 1, column: err.column - 1, text: err.name + ": " + err.message, type: "error", }; }); editor.getSession().setAnnotations(annotations); }); }); }; } }, }; }, ]) .controller("EditorCtrl", ["$scope", function($scope) { var defaults = { mode: "mp", theme: "tomorrow_night", require: ["ace/ext/language_tools"], useSoftTabs: false, advanced: { enableSnippets: true, enableBasicAutocompletion: true, enableLiveAutocompletion: true, }, rendererOptions: { fontSize: "13px", fontFamily: "Inconsolata, Monaco, Consolas, Courier New, Courier", fadeFoldWidgets: true, scrollPastEnd: true, }, }; if (!$scope.model) $scope.model = {}; $scope.model.options = Object.keys(defaults).reduce(function(opts, optName) { if (!(optName in opts)) opts[optName] = defaults[optName]; return opts; }, $scope.model.options || {}); if (!$scope.model.data) $scope.model.data = ""; }, ]);