diff --git a/IPython/html/static/edit/js/editor.js b/IPython/html/static/edit/js/editor.js index 53320b663..9026ab5e2 100644 --- a/IPython/html/static/edit/js/editor.js +++ b/IPython/html/static/edit/js/editor.js @@ -6,20 +6,31 @@ define([ 'base/js/utils', 'codemirror/lib/codemirror', 'codemirror/mode/meta', - 'codemirror/addon/search/search' + 'codemirror/addon/comment/comment', + 'codemirror/addon/dialog/dialog', + 'codemirror/addon/edit/closebrackets', + 'codemirror/addon/edit/matchbrackets', + 'codemirror/addon/search/searchcursor', + 'codemirror/addon/search/search', + 'codemirror/keymap/emacs', + 'codemirror/keymap/sublime', + 'codemirror/keymap/vim', ], function($, utils, CodeMirror ) { + "use strict"; + var Editor = function(selector, options) { + var that = this; this.selector = selector; this.contents = options.contents; this.events = options.events; this.base_url = options.base_url; this.file_path = options.file_path; - - this.codemirror = CodeMirror($(this.selector)[0]); + this.config = options.config; + this.codemirror = new CodeMirror($(this.selector)[0]); // It appears we have to set commands on the CodeMirror class, not the // instance. I'd like to be wrong, but since there should only be one CM @@ -27,12 +38,34 @@ function($, CodeMirror.commands.save = $.proxy(this.save, this); this.save_enabled = false; + + this.config.loaded.then(function () { + // load codemirror config + var cfg = that.config.data.Editor || {}; + var cmopts = $.extend(true, {}, // true = recursive copy + Editor.default_codemirror_options, + cfg.codemirror_options || {} + ); + that.set_codemirror_options(cmopts, false); + that.events.trigger('config_changed.Editor', {config: that.config}); + }); + }; + + // default CodeMirror options + Editor.default_codemirror_options = { + extraKeys: { + "Tab" : "indentMore", + }, + indentUnit: 4, + theme: "ipython", + lineNumbers: true, }; Editor.prototype.load = function() { + /** load the file */ var that = this; var cm = this.codemirror; - this.contents.get(this.file_path, {type: 'file', format: 'text'}) + return this.contents.get(this.file_path, {type: 'file', format: 'text'}) .then(function(model) { cm.setValue(model.content); @@ -56,6 +89,7 @@ function($, }; Editor.prototype.save = function() { + /** save the file */ if (!this.save_enabled) { console.log("Not saving, save disabled"); return; @@ -67,10 +101,30 @@ function($, content: this.codemirror.getValue(), }; var that = this; - this.contents.save(this.file_path, model).then(function() { + return this.contents.save(this.file_path, model).then(function() { that.events.trigger("save_succeeded.TextEditor"); }); }; + + Editor.prototype._set_codemirror_options = function (options) { + // update codemirror options from a dict + for (var opt in options) { + this.codemirror.setOption(opt, options[opt]); + } + }; + + Editor.prototype.update_codemirror_options = function (options) { + /** update codemirror options locally and save changes in config */ + var that = this; + this._set_codemirror_options(options); + return this.config.update({ + Editor: { + codemirror_options: options + } + }).then( + that.events.trigger('config_changed.Editor', {config: that.config}) + ); + }; return {Editor: Editor}; }); diff --git a/IPython/html/static/edit/js/main.js b/IPython/html/static/edit/js/main.js index ec787d4e7..e172d5f4f 100644 --- a/IPython/html/static/edit/js/main.js +++ b/IPython/html/static/edit/js/main.js @@ -36,6 +36,7 @@ require([ events: events, contents: contents, file_path: file_path, + config: config, }); // Make it available for debugging @@ -44,6 +45,7 @@ require([ var menus = new menubar.MenuBar('#menubar', { base_url: base_url, editor: editor, + events: events, }); var notification_area = new notificationarea.EditorNotificationArea( diff --git a/IPython/html/static/edit/js/menubar.js b/IPython/html/static/edit/js/menubar.js index 15b1b4e26..3073e79c1 100644 --- a/IPython/html/static/edit/js/menubar.js +++ b/IPython/html/static/edit/js/menubar.js @@ -29,6 +29,7 @@ define([ this.base_url = options.base_url || utils.get_body_data("baseUrl"); this.selector = selector; this.editor = options.editor; + this.events = options.events; if (this.selector !== undefined) { this.element = $(selector); @@ -41,8 +42,31 @@ define([ * File */ var that = this; - this.element.find('#save_file').click(function () { - that.editor.save(); + var editor = that.editor; + this.element.find('#save-file').click(function () { + editor.save(); + }); + + // Edit + this.element.find('#menu-find').click(function () { + editor.codemirror.execCommand("find"); + }); + this.element.find('#menu-replace').click(function () { + editor.codemirror.execCommand("replace"); + }); + + // View + this.element.find('#menu-line-numbers').click(function () { + var current = editor.codemirror.getOption('lineNumbers'); + var value = Boolean(1-current); + editor.update_codemirror_options({lineNumbers: value}); + }); + + this.events.on("config_changed.Editor", function () { + var lineNumbers = editor.codemirror.getOption('lineNumbers'); + var text = lineNumbers ? "Hide" : "Show"; + text = text + " Line Numbers"; + that.element.find('#menu-line-numbers').find("a").text(text); }); }; diff --git a/IPython/html/templates/edit.html b/IPython/html/templates/edit.html index 7841bd438..661592f24 100644 --- a/IPython/html/templates/edit.html +++ b/IPython/html/templates/edit.html @@ -36,28 +36,40 @@ data-file-path="{{file_path}}" {% block site %}