editor.js
196 lines
| 6.6 KiB
| application/javascript
|
JavascriptLexer
|
r19013 | // Copyright (c) IPython Development Team. | ||
// Distributed under the terms of the Modified BSD License. | ||||
define([ | ||||
'jquery', | ||||
'base/js/utils', | ||||
'codemirror/lib/codemirror', | ||||
'codemirror/mode/meta', | ||||
|
r19303 | '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', | ||||
|
r19013 | ], | ||
function($, | ||||
utils, | ||||
CodeMirror | ||||
) { | ||||
|
r19303 | "use strict"; | ||
|
r19319 | |||
|
r19013 | var Editor = function(selector, options) { | ||
|
r19303 | var that = this; | ||
|
r19013 | this.selector = selector; | ||
this.contents = options.contents; | ||||
this.events = options.events; | ||||
this.base_url = options.base_url; | ||||
this.file_path = options.file_path; | ||||
|
r19303 | this.config = options.config; | ||
this.codemirror = new CodeMirror($(this.selector)[0]); | ||||
|
r19304 | this.generation = -1; | ||
|
r19013 | |||
|
r19020 | // 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 | ||||
// instance on the page, this is good enough for now. | ||||
CodeMirror.commands.save = $.proxy(this.save, this); | ||||
|
r19015 | this.save_enabled = false; | ||
|
r19303 | |||
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 || {} | ||||
); | ||||
|
r19309 | that._set_codemirror_options(cmopts); | ||
|
r19303 | 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, | ||||
|
r19341 | lineWrapping: true, | ||
|
r19013 | }; | ||
Editor.prototype.load = function() { | ||||
|
r19303 | /** load the file */ | ||
|
r19015 | var that = this; | ||
|
r19013 | var cm = this.codemirror; | ||
|
r19303 | return this.contents.get(this.file_path, {type: 'file', format: 'text'}) | ||
|
r19015 | .then(function(model) { | ||
cm.setValue(model.content); | ||||
|
r19089 | |||
// Setting the file's initial value creates a history entry, | ||||
// which we don't want. | ||||
cm.clearHistory(); | ||||
|
r19321 | that._set_mode_for_model(model); | ||
|
r19015 | that.save_enabled = true; | ||
|
r19304 | that.generation = cm.changeGeneration(); | ||
|
r19316 | that.events.trigger("file_loaded.Editor", model); | ||
|
r19015 | }, | ||
function(error) { | ||||
|
r19337 | that.events.trigger("file_load_failed.Editor", error); | ||
if (error.xhr.responseJSON.reason === 'bad format') { | ||||
window.location = utils.url_path_join( | ||||
that.base_url, | ||||
'files', | ||||
that.file_path | ||||
); | ||||
} | ||||
|
r19015 | cm.setValue("Error! " + error.message + | ||
"\nSaving disabled."); | ||||
that.save_enabled = false; | ||||
|
r19013 | } | ||
|
r19015 | ); | ||
|
r19013 | }; | ||
|
r19321 | |||
Editor.prototype._set_mode_for_model = function (model) { | ||||
/** Set the CodeMirror mode based on the file model */ | ||||
|
r19787 | |||
|
r19321 | // Find and load the highlighting mode, | ||
// first by mime-type, then by file extension | ||||
|
r19788 | |||
|
r19787 | var modeinfo; | ||
|
r19788 | // mimetype is unset on file rename | ||
|
r19787 | if (model.mimetype) { | ||
modeinfo = CodeMirror.findModeByMIME(model.mimetype); | ||||
} | ||||
if (!modeinfo || modeinfo.mode === "null") { | ||||
|
r19321 | // find by mime failed, use find by ext | ||
var ext_idx = model.name.lastIndexOf('.'); | ||||
|
r19787 | |||
|
r19321 | if (ext_idx > 0) { | ||
// CodeMirror.findModeByExtension wants extension without '.' | ||||
modeinfo = CodeMirror.findModeByExtension(model.name.slice(ext_idx + 1)); | ||||
} | ||||
} | ||||
if (modeinfo) { | ||||
this.set_codemirror_mode(modeinfo); | ||||
} | ||||
}; | ||||
|
r19787 | |||
|
r19319 | Editor.prototype.set_codemirror_mode = function (modeinfo) { | ||
/** set the codemirror mode from a modeinfo struct */ | ||||
var that = this; | ||||
utils.requireCodeMirrorMode(modeinfo, function () { | ||||
that.codemirror.setOption('mode', modeinfo.mode); | ||||
that.events.trigger("mode_changed.Editor", modeinfo); | ||||
}); | ||||
}; | ||||
|
r19316 | Editor.prototype.get_filename = function () { | ||
return utils.url_path_split(this.file_path)[1]; | ||||
|
r19319 | }; | ||
|
r19013 | |||
|
r19316 | Editor.prototype.rename = function (new_name) { | ||
/** rename the file */ | ||||
var that = this; | ||||
var parent = utils.url_path_split(this.file_path)[0]; | ||||
var new_path = utils.url_path_join(parent, new_name); | ||||
return this.contents.rename(this.file_path, new_path).then( | ||||
|
r19321 | function (model) { | ||
that.file_path = model.path; | ||||
that.events.trigger('file_renamed.Editor', model); | ||||
that._set_mode_for_model(model); | ||||
|
r19316 | } | ||
); | ||||
}; | ||||
Editor.prototype.save = function () { | ||||
|
r19303 | /** save the file */ | ||
|
r19015 | if (!this.save_enabled) { | ||
console.log("Not saving, save disabled"); | ||||
return; | ||||
} | ||||
|
r19013 | var model = { | ||
|
r19015 | path: this.file_path, | ||
|
r19013 | type: 'file', | ||
format: 'text', | ||||
content: this.codemirror.getValue(), | ||||
}; | ||||
var that = this; | ||||
|
r19304 | // record change generation for isClean | ||
this.generation = this.codemirror.changeGeneration(); | ||||
|
r20149 | that.events.trigger("file_saving.Editor"); | ||
|
r19316 | return this.contents.save(this.file_path, model).then(function(data) { | ||
that.events.trigger("file_saved.Editor", data); | ||||
|
r19013 | }); | ||
}; | ||||
|
r19303 | |||
Editor.prototype._set_codemirror_options = function (options) { | ||||
// update codemirror options from a dict | ||||
|
r19334 | var codemirror = this.codemirror; | ||
$.map(options, function (value, opt) { | ||||
|
r19311 | if (value === null) { | ||
value = CodeMirror.defaults[opt]; | ||||
} | ||||
|
r19334 | codemirror.setOption(opt, value); | ||
}); | ||||
|
r19303 | }; | ||
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}) | ||||
); | ||||
}; | ||||
|
r19013 | |||
return {Editor: Editor}; | ||||
}); | ||||