Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50650 views
1
// Copyright (c) IPython Development Team.
2
// Distributed under the terms of the Modified BSD License.
3
4
define([
5
'jquery',
6
'base/js/utils',
7
'codemirror/lib/codemirror',
8
'codemirror/mode/meta',
9
'codemirror/addon/comment/comment',
10
'codemirror/addon/dialog/dialog',
11
'codemirror/addon/edit/closebrackets',
12
'codemirror/addon/edit/matchbrackets',
13
'codemirror/addon/search/searchcursor',
14
'codemirror/addon/search/search',
15
'codemirror/keymap/emacs',
16
'codemirror/keymap/sublime',
17
'codemirror/keymap/vim',
18
],
19
function($,
20
utils,
21
CodeMirror
22
) {
23
"use strict";
24
25
var Editor = function(selector, options) {
26
var that = this;
27
this.selector = selector;
28
this.clean = false;
29
this.contents = options.contents;
30
this.events = options.events;
31
this.base_url = options.base_url;
32
this.file_path = options.file_path;
33
this.config = options.config;
34
this.codemirror = new CodeMirror($(this.selector)[0]);
35
this.codemirror.on('changes', function(cm, changes){
36
that._clean_state();
37
});
38
this.generation = -1;
39
40
// It appears we have to set commands on the CodeMirror class, not the
41
// instance. I'd like to be wrong, but since there should only be one CM
42
// instance on the page, this is good enough for now.
43
CodeMirror.commands.save = $.proxy(this.save, this);
44
45
this.save_enabled = false;
46
47
this.config.loaded.then(function () {
48
// load codemirror config
49
var cfg = that.config.data.Editor || {};
50
var cmopts = $.extend(true, {}, // true = recursive copy
51
Editor.default_codemirror_options,
52
cfg.codemirror_options || {}
53
);
54
that._set_codemirror_options(cmopts);
55
that.events.trigger('config_changed.Editor', {config: that.config});
56
that._clean_state();
57
});
58
this.clean_sel = $('<div/>');
59
$('.last_modified').before(this.clean_sel);
60
this.clean_sel.addClass('dirty-indicator-dirty');
61
};
62
63
// default CodeMirror options
64
Editor.default_codemirror_options = {
65
extraKeys: {
66
"Tab" : "indentMore",
67
},
68
indentUnit: 4,
69
theme: "ipython",
70
lineNumbers: true,
71
lineWrapping: true,
72
};
73
74
Editor.prototype.load = function() {
75
/** load the file */
76
var that = this;
77
var cm = this.codemirror;
78
return this.contents.get(this.file_path, {type: 'file', format: 'text'})
79
.then(function(model) {
80
cm.setValue(model.content);
81
82
// Setting the file's initial value creates a history entry,
83
// which we don't want.
84
cm.clearHistory();
85
that._set_mode_for_model(model);
86
that.save_enabled = true;
87
that.generation = cm.changeGeneration();
88
that.events.trigger("file_loaded.Editor", model);
89
that._clean_state();
90
}).catch(
91
function(error) {
92
that.events.trigger("file_load_failed.Editor", error);
93
if (((error.xhr||{}).responseJSON||{}).reason === 'bad format') {
94
window.location = utils.url_path_join(
95
that.base_url,
96
'files',
97
that.file_path
98
);
99
} else {
100
console.warn('Error while loading: the error was:')
101
console.warn(error)
102
}
103
cm.setValue("Error! " + error.message +
104
"\nSaving disabled.\nSee Console for more details.");
105
cm.setOption('readOnly','nocursor')
106
that.save_enabled = false;
107
}
108
);
109
};
110
111
Editor.prototype._set_mode_for_model = function (model) {
112
/** Set the CodeMirror mode based on the file model */
113
114
// Find and load the highlighting mode,
115
// first by mime-type, then by file extension
116
117
var modeinfo;
118
// mimetype is unset on file rename
119
if (model.mimetype) {
120
modeinfo = CodeMirror.findModeByMIME(model.mimetype);
121
}
122
if (!modeinfo || modeinfo.mode === "null") {
123
// find by mime failed, use find by ext
124
var ext_idx = model.name.lastIndexOf('.');
125
126
if (ext_idx > 0) {
127
// CodeMirror.findModeByExtension wants extension without '.'
128
modeinfo = CodeMirror.findModeByExtension(model.name.slice(ext_idx + 1));
129
}
130
}
131
if (modeinfo) {
132
this.set_codemirror_mode(modeinfo);
133
}
134
};
135
136
Editor.prototype.set_codemirror_mode = function (modeinfo) {
137
/** set the codemirror mode from a modeinfo struct */
138
var that = this;
139
utils.requireCodeMirrorMode(modeinfo, function () {
140
that.codemirror.setOption('mode', modeinfo.mode);
141
that.events.trigger("mode_changed.Editor", modeinfo);
142
});
143
};
144
145
Editor.prototype.get_filename = function () {
146
return utils.url_path_split(this.file_path)[1];
147
};
148
149
Editor.prototype.rename = function (new_name) {
150
/** rename the file */
151
var that = this;
152
var parent = utils.url_path_split(this.file_path)[0];
153
var new_path = utils.url_path_join(parent, new_name);
154
return this.contents.rename(this.file_path, new_path).then(
155
function (model) {
156
that.file_path = model.path;
157
that.events.trigger('file_renamed.Editor', model);
158
that._set_mode_for_model(model);
159
that._clean_state();
160
}
161
);
162
};
163
164
Editor.prototype.save = function () {
165
/** save the file */
166
if (!this.save_enabled) {
167
console.log("Not saving, save disabled");
168
return;
169
}
170
var model = {
171
path: this.file_path,
172
type: 'file',
173
format: 'text',
174
content: this.codemirror.getValue(),
175
};
176
var that = this;
177
// record change generation for isClean
178
this.generation = this.codemirror.changeGeneration();
179
that.events.trigger("file_saving.Editor");
180
return this.contents.save(this.file_path, model).then(function(data) {
181
that.events.trigger("file_saved.Editor", data);
182
that._clean_state();
183
});
184
};
185
186
Editor.prototype._clean_state = function(){
187
var clean = this.codemirror.isClean(this.generation);
188
if (clean === this.clean){
189
return
190
} else {
191
this.clean = clean;
192
}
193
if(clean){
194
this.events.trigger("save_status_clean.Editor");
195
this.clean_sel.attr('class','dirty-indicator-clean').attr('title','No changes to save');
196
} else {
197
this.events.trigger("save_status_dirty.Editor");
198
this.clean_sel.attr('class','dirty-indicator-dirty').attr('title','Unsaved changes');
199
}
200
};
201
202
Editor.prototype._set_codemirror_options = function (options) {
203
// update codemirror options from a dict
204
var codemirror = this.codemirror;
205
$.map(options, function (value, opt) {
206
if (value === null) {
207
value = CodeMirror.defaults[opt];
208
}
209
codemirror.setOption(opt, value);
210
});
211
var that = this;
212
};
213
214
Editor.prototype.update_codemirror_options = function (options) {
215
/** update codemirror options locally and save changes in config */
216
var that = this;
217
this._set_codemirror_options(options);
218
return this.config.update({
219
Editor: {
220
codemirror_options: options
221
}
222
}).then(
223
that.events.trigger('config_changed.Editor', {config: that.config})
224
);
225
};
226
227
return {Editor: Editor};
228
});
229
230