1 |
|
2 |
|
3 | import { Dialog, SessionContext, sessionContextDialogs, showDialog, showErrorMessage } from '@jupyterlab/apputils';
|
4 | import { PathExt } from '@jupyterlab/coreutils';
|
5 | import { ProviderMock } from '@jupyterlab/docprovider';
|
6 | import { RenderMimeRegistry } from '@jupyterlab/rendermime';
|
7 | import { nullTranslator } from '@jupyterlab/translation';
|
8 | import { PromiseDelegate } from '@lumino/coreutils';
|
9 | import { DisposableDelegate } from '@lumino/disposable';
|
10 | import { Signal } from '@lumino/signaling';
|
11 | import { Widget } from '@lumino/widgets';
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | export class Context {
|
18 | |
19 |
|
20 |
|
21 | constructor(options) {
|
22 | this._path = '';
|
23 | this._lineEnding = null;
|
24 | this._contentsModel = null;
|
25 | this._populatedPromise = new PromiseDelegate();
|
26 | this._isPopulated = false;
|
27 | this._isReady = false;
|
28 | this._isDisposed = false;
|
29 | this._pathChanged = new Signal(this);
|
30 | this._fileChanged = new Signal(this);
|
31 | this._saveState = new Signal(this);
|
32 | this._disposed = new Signal(this);
|
33 | this._lastModifiedCheckMargin = 500;
|
34 | this._timeConflictModalIsOpen = false;
|
35 | const manager = (this._manager = options.manager);
|
36 | this.translator = options.translator || nullTranslator;
|
37 | this._trans = this.translator.load('jupyterlab');
|
38 | this._factory = options.factory;
|
39 | this._dialogs = options.sessionDialogs || sessionContextDialogs;
|
40 | this._opener = options.opener || Private.noOp;
|
41 | this._path = this._manager.contents.normalize(options.path);
|
42 | this._lastModifiedCheckMargin = options.lastModifiedCheckMargin || 500;
|
43 | const localPath = this._manager.contents.localPath(this._path);
|
44 | const lang = this._factory.preferredLanguage(PathExt.basename(localPath));
|
45 | const dbFactory = options.modelDBFactory;
|
46 | if (dbFactory) {
|
47 | const localPath = manager.contents.localPath(this._path);
|
48 | this._modelDB = dbFactory.createNew(localPath);
|
49 | this._model = this._factory.createNew(lang, this._modelDB, false);
|
50 | }
|
51 | else {
|
52 | this._model = this._factory.createNew(lang, undefined, false);
|
53 | }
|
54 | const ymodel = this._model.sharedModel;
|
55 | const ydoc = ymodel.ydoc;
|
56 | this._ydoc = ydoc;
|
57 | this._ycontext = ydoc.getMap('context');
|
58 | const docProviderFactory = options.docProviderFactory;
|
59 | this._provider = docProviderFactory
|
60 | ? docProviderFactory({
|
61 | path: this._path,
|
62 | contentType: this._factory.contentType,
|
63 | ymodel
|
64 | })
|
65 | : new ProviderMock();
|
66 | this._readyPromise = manager.ready.then(() => {
|
67 | return this._populatedPromise.promise;
|
68 | });
|
69 | const ext = PathExt.extname(this._path);
|
70 | this.sessionContext = new SessionContext({
|
71 | sessionManager: manager.sessions,
|
72 | specsManager: manager.kernelspecs,
|
73 | path: this._path,
|
74 | type: ext === '.ipynb' ? 'notebook' : 'file',
|
75 | name: PathExt.basename(localPath),
|
76 | kernelPreference: options.kernelPreference || { shouldStart: false },
|
77 | setBusy: options.setBusy
|
78 | });
|
79 | this.sessionContext.propertyChanged.connect(this._onSessionChanged, this);
|
80 | manager.contents.fileChanged.connect(this._onFileChanged, this);
|
81 | const urlResolver = (this.urlResolver = new RenderMimeRegistry.UrlResolver({
|
82 | path: this._path,
|
83 | contents: manager.contents
|
84 | }));
|
85 | this._ycontext.set('path', this._path);
|
86 | this._ycontext.observe(event => {
|
87 | var _a;
|
88 | const pathChanged = event.changes.keys.get('path');
|
89 | if (pathChanged) {
|
90 | const newPath = this._ycontext.get('path');
|
91 | if (newPath && newPath !== pathChanged.oldValue) {
|
92 | urlResolver.path = newPath;
|
93 | this._path = newPath;
|
94 | this._provider.setPath(newPath);
|
95 | this._pathChanged.emit(this.path);
|
96 | (_a = this.sessionContext.session) === null || _a === void 0 ? void 0 : _a.setPath(newPath);
|
97 | }
|
98 | }
|
99 | });
|
100 | }
|
101 | |
102 |
|
103 |
|
104 | get pathChanged() {
|
105 | return this._pathChanged;
|
106 | }
|
107 | |
108 |
|
109 |
|
110 | get fileChanged() {
|
111 | return this._fileChanged;
|
112 | }
|
113 | |
114 |
|
115 |
|
116 | get saveState() {
|
117 | return this._saveState;
|
118 | }
|
119 | |
120 |
|
121 |
|
122 | get disposed() {
|
123 | return this._disposed;
|
124 | }
|
125 | |
126 |
|
127 |
|
128 | get lastModifiedCheckMargin() {
|
129 | return this._lastModifiedCheckMargin;
|
130 | }
|
131 | set lastModifiedCheckMargin(value) {
|
132 | this._lastModifiedCheckMargin = value;
|
133 | }
|
134 | |
135 |
|
136 |
|
137 | get model() {
|
138 | return this._model;
|
139 | }
|
140 | |
141 |
|
142 |
|
143 | get path() {
|
144 | return this._path;
|
145 | }
|
146 | |
147 |
|
148 |
|
149 |
|
150 |
|
151 | get localPath() {
|
152 | return this._manager.contents.localPath(this._path);
|
153 | }
|
154 | |
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | get contentsModel() {
|
162 | return this._contentsModel;
|
163 | }
|
164 | |
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | get factoryName() {
|
171 | return this.isDisposed ? '' : this._factory.name;
|
172 | }
|
173 | |
174 |
|
175 |
|
176 | get isDisposed() {
|
177 | return this._isDisposed;
|
178 | }
|
179 | |
180 |
|
181 |
|
182 | dispose() {
|
183 | if (this.isDisposed) {
|
184 | return;
|
185 | }
|
186 | this._isDisposed = true;
|
187 | this.sessionContext.dispose();
|
188 | if (this._modelDB) {
|
189 | this._modelDB.dispose();
|
190 | }
|
191 | this._model.dispose();
|
192 | this._provider.destroy();
|
193 | this._model.sharedModel.dispose();
|
194 | this._ydoc.destroy();
|
195 | this._disposed.emit(void 0);
|
196 | Signal.clearData(this);
|
197 | }
|
198 | |
199 |
|
200 |
|
201 | get isReady() {
|
202 | return this._isReady;
|
203 | }
|
204 | |
205 |
|
206 |
|
207 | get ready() {
|
208 | return this._readyPromise;
|
209 | }
|
210 | |
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 | async initialize(isNew) {
|
218 | const lock = await this._provider.acquireLock();
|
219 | const contentIsInitialized = await this._provider.requestInitialContent();
|
220 | let promise;
|
221 | if (isNew || contentIsInitialized) {
|
222 | promise = this._save();
|
223 | }
|
224 | else {
|
225 | promise = this._revert();
|
226 | }
|
227 |
|
228 | const finally_ = () => {
|
229 | this._provider.releaseLock(lock);
|
230 | };
|
231 |
|
232 | promise
|
233 | .then(() => {
|
234 | this._provider.putInitializedState();
|
235 | this._model.initialize();
|
236 | })
|
237 | .then(finally_, finally_);
|
238 | return promise;
|
239 | }
|
240 | |
241 |
|
242 |
|
243 |
|
244 |
|
245 | rename(newName) {
|
246 | return this.ready.then(() => {
|
247 | return this._manager.ready.then(() => {
|
248 | return this._rename(newName);
|
249 | });
|
250 | });
|
251 | }
|
252 | |
253 |
|
254 |
|
255 | async save() {
|
256 | const [lock] = await Promise.all([
|
257 | this._provider.acquireLock(),
|
258 | this.ready
|
259 | ]);
|
260 | let promise;
|
261 | promise = this._save();
|
262 |
|
263 | promise = promise.then(() => {
|
264 | this._provider.putInitializedState();
|
265 | });
|
266 | const finally_ = () => {
|
267 | this._provider.releaseLock(lock);
|
268 | };
|
269 | promise.then(finally_, finally_);
|
270 | return await promise;
|
271 | }
|
272 | |
273 |
|
274 |
|
275 | saveAs() {
|
276 | return this.ready
|
277 | .then(() => {
|
278 | return Private.getSavePath(this._path);
|
279 | })
|
280 | .then(newPath => {
|
281 | if (this.isDisposed || !newPath) {
|
282 | return;
|
283 | }
|
284 | if (newPath === this._path) {
|
285 | return this.save();
|
286 | }
|
287 |
|
288 | return this._manager.ready
|
289 | .then(() => {
|
290 | return this._manager.contents.get(newPath);
|
291 | })
|
292 | .then(() => {
|
293 | return this._maybeOverWrite(newPath);
|
294 | })
|
295 | .catch(err => {
|
296 | if (!err.response || err.response.status !== 404) {
|
297 | throw err;
|
298 | }
|
299 | return this._finishSaveAs(newPath);
|
300 | });
|
301 | });
|
302 | }
|
303 | |
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 | async download() {
|
312 | const url = await this._manager.contents.getDownloadUrl(this._path);
|
313 | const element = document.createElement('a');
|
314 | element.href = url;
|
315 | element.download = '';
|
316 | document.body.appendChild(element);
|
317 | element.click();
|
318 | document.body.removeChild(element);
|
319 | return void 0;
|
320 | }
|
321 | |
322 |
|
323 |
|
324 | async revert() {
|
325 | const [lock] = await Promise.all([
|
326 | this._provider.acquireLock(),
|
327 | this.ready
|
328 | ]);
|
329 | const promise = this._revert();
|
330 | const finally_ = () => {
|
331 | this._provider.releaseLock(lock);
|
332 | };
|
333 | promise.then(finally_, finally_);
|
334 | return await promise;
|
335 | }
|
336 | |
337 |
|
338 |
|
339 | createCheckpoint() {
|
340 | const contents = this._manager.contents;
|
341 | return this._manager.ready.then(() => {
|
342 | return contents.createCheckpoint(this._path);
|
343 | });
|
344 | }
|
345 | |
346 |
|
347 |
|
348 | deleteCheckpoint(checkpointId) {
|
349 | const contents = this._manager.contents;
|
350 | return this._manager.ready.then(() => {
|
351 | return contents.deleteCheckpoint(this._path, checkpointId);
|
352 | });
|
353 | }
|
354 | |
355 |
|
356 |
|
357 | restoreCheckpoint(checkpointId) {
|
358 | const contents = this._manager.contents;
|
359 | const path = this._path;
|
360 | return this._manager.ready.then(() => {
|
361 | if (checkpointId) {
|
362 | return contents.restoreCheckpoint(path, checkpointId);
|
363 | }
|
364 | return this.listCheckpoints().then(checkpoints => {
|
365 | if (this.isDisposed || !checkpoints.length) {
|
366 | return;
|
367 | }
|
368 | checkpointId = checkpoints[checkpoints.length - 1].id;
|
369 | return contents.restoreCheckpoint(path, checkpointId);
|
370 | });
|
371 | });
|
372 | }
|
373 | |
374 |
|
375 |
|
376 | listCheckpoints() {
|
377 | const contents = this._manager.contents;
|
378 | return this._manager.ready.then(() => {
|
379 | return contents.listCheckpoints(this._path);
|
380 | });
|
381 | }
|
382 | |
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 | addSibling(widget, options = {}) {
|
396 | const opener = this._opener;
|
397 | if (opener) {
|
398 | opener(widget, options);
|
399 | }
|
400 | return new DisposableDelegate(() => {
|
401 | widget.close();
|
402 | });
|
403 | }
|
404 | |
405 |
|
406 |
|
407 | _onFileChanged(sender, change) {
|
408 | var _a, _b, _c;
|
409 | if (change.type !== 'rename') {
|
410 | return;
|
411 | }
|
412 | let oldPath = change.oldValue && change.oldValue.path;
|
413 | let newPath = change.newValue && change.newValue.path;
|
414 | if (newPath && this._path.indexOf(oldPath || '') === 0) {
|
415 | let changeModel = change.newValue;
|
416 |
|
417 |
|
418 | if (oldPath !== this._path) {
|
419 | newPath = this._path.replace(new RegExp(`^${oldPath}/`), `${newPath}/`);
|
420 | oldPath = this._path;
|
421 |
|
422 | changeModel = {
|
423 | last_modified: (_a = change.newValue) === null || _a === void 0 ? void 0 : _a.created,
|
424 | path: newPath
|
425 | };
|
426 | }
|
427 | this._path = newPath;
|
428 | void ((_b = this.sessionContext.session) === null || _b === void 0 ? void 0 : _b.setPath(newPath));
|
429 | const updateModel = Object.assign(Object.assign({}, this._contentsModel), changeModel);
|
430 | const localPath = this._manager.contents.localPath(newPath);
|
431 | void ((_c = this.sessionContext.session) === null || _c === void 0 ? void 0 : _c.setName(PathExt.basename(localPath)));
|
432 | this._updateContentsModel(updateModel);
|
433 | this._ycontext.set('path', this._path);
|
434 | }
|
435 | }
|
436 | |
437 |
|
438 |
|
439 | _onSessionChanged(sender, type) {
|
440 | if (type !== 'path') {
|
441 | return;
|
442 | }
|
443 | const path = this.sessionContext.session.path;
|
444 | if (path !== this._path) {
|
445 | this._path = path;
|
446 | this._ycontext.set('path', this._path);
|
447 | }
|
448 | }
|
449 | |
450 |
|
451 |
|
452 | _updateContentsModel(model) {
|
453 | const newModel = {
|
454 | path: model.path,
|
455 | name: model.name,
|
456 | type: model.type,
|
457 | content: undefined,
|
458 | writable: model.writable,
|
459 | created: model.created,
|
460 | last_modified: model.last_modified,
|
461 | mimetype: model.mimetype,
|
462 | format: model.format
|
463 | };
|
464 | const mod = this._contentsModel ? this._contentsModel.last_modified : null;
|
465 | this._contentsModel = newModel;
|
466 | this._ycontext.set('last_modified', newModel.last_modified);
|
467 | if (!mod || newModel.last_modified !== mod) {
|
468 | this._fileChanged.emit(newModel);
|
469 | }
|
470 | }
|
471 | |
472 |
|
473 |
|
474 | _populate() {
|
475 | this._isPopulated = true;
|
476 | this._isReady = true;
|
477 | this._populatedPromise.resolve(void 0);
|
478 |
|
479 | return this._maybeCheckpoint(false).then(() => {
|
480 | if (this.isDisposed) {
|
481 | return;
|
482 | }
|
483 |
|
484 | const name = this._model.defaultKernelName ||
|
485 | this.sessionContext.kernelPreference.name;
|
486 | this.sessionContext.kernelPreference = Object.assign(Object.assign({}, this.sessionContext.kernelPreference), { name, language: this._model.defaultKernelLanguage });
|
487 |
|
488 |
|
489 |
|
490 | void this.sessionContext.initialize().then(shouldSelect => {
|
491 | if (shouldSelect) {
|
492 | void this._dialogs.selectKernel(this.sessionContext, this.translator);
|
493 | }
|
494 | });
|
495 | });
|
496 | }
|
497 | |
498 |
|
499 |
|
500 |
|
501 |
|
502 | async _rename(newName) {
|
503 | var _a, _b;
|
504 | const splitPath = this.path.split('/');
|
505 | splitPath[splitPath.length - 1] = newName;
|
506 | const newPath = splitPath.join('/');
|
507 | await this._manager.contents.rename(this.path, newPath);
|
508 | await ((_a = this.sessionContext.session) === null || _a === void 0 ? void 0 : _a.setPath(newPath));
|
509 | await ((_b = this.sessionContext.session) === null || _b === void 0 ? void 0 : _b.setName(newName));
|
510 | this._path = newPath;
|
511 | this._ycontext.set('path', this._path);
|
512 | }
|
513 | |
514 |
|
515 |
|
516 | async _save() {
|
517 | this._saveState.emit('started');
|
518 | const model = this._model;
|
519 | let content;
|
520 | if (this._factory.fileFormat === 'json') {
|
521 | content = model.toJSON();
|
522 | }
|
523 | else {
|
524 | content = model.toString();
|
525 | if (this._lineEnding) {
|
526 | content = content.replace(/\n/g, this._lineEnding);
|
527 | }
|
528 | }
|
529 | const options = {
|
530 | type: this._factory.contentType,
|
531 | format: this._factory.fileFormat,
|
532 | content
|
533 | };
|
534 | try {
|
535 | let value;
|
536 | await this._manager.ready;
|
537 | if (!model.modelDB.isCollaborative) {
|
538 | value = await this._maybeSave(options);
|
539 | }
|
540 | else {
|
541 | value = await this._manager.contents.save(this._path, options);
|
542 | }
|
543 | if (this.isDisposed) {
|
544 | return;
|
545 | }
|
546 | model.dirty = false;
|
547 | this._updateContentsModel(value);
|
548 | if (!this._isPopulated) {
|
549 | await this._populate();
|
550 | }
|
551 |
|
552 | this._saveState.emit('completed');
|
553 | }
|
554 | catch (err) {
|
555 |
|
556 |
|
557 |
|
558 | if (err.message === 'Cancel' ||
|
559 | err.message === 'Modal is already displayed') {
|
560 | throw err;
|
561 | }
|
562 |
|
563 | const localPath = this._manager.contents.localPath(this._path);
|
564 | const name = PathExt.basename(localPath);
|
565 | void this._handleError(err, this._trans.__('File Save Error for %1', name));
|
566 |
|
567 | this._saveState.emit('failed');
|
568 | throw err;
|
569 | }
|
570 | }
|
571 | |
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 | _revert(initializeModel = false) {
|
578 | const opts = Object.assign({ type: this._factory.contentType, content: this._factory.fileFormat !== null }, (this._factory.fileFormat !== null
|
579 | ? { format: this._factory.fileFormat }
|
580 | : {}));
|
581 | const path = this._path;
|
582 | const model = this._model;
|
583 | return this._manager.ready
|
584 | .then(() => {
|
585 | return this._manager.contents.get(path, opts);
|
586 | })
|
587 | .then(contents => {
|
588 | if (this.isDisposed) {
|
589 | return;
|
590 | }
|
591 | if (contents.format === 'json') {
|
592 | model.fromJSON(contents.content);
|
593 | if (initializeModel) {
|
594 | model.initialize();
|
595 | }
|
596 | }
|
597 | else {
|
598 | let content = contents.content;
|
599 |
|
600 |
|
601 | if (content.indexOf('\r\n') !== -1) {
|
602 | this._lineEnding = '\r\n';
|
603 | content = content.replace(/\r\n/g, '\n');
|
604 | }
|
605 | else if (content.indexOf('\r') !== -1) {
|
606 | this._lineEnding = '\r';
|
607 | content = content.replace(/\r/g, '\n');
|
608 | }
|
609 | else {
|
610 | this._lineEnding = null;
|
611 | }
|
612 | model.fromString(content);
|
613 | if (initializeModel) {
|
614 | model.initialize();
|
615 | }
|
616 | }
|
617 | this._updateContentsModel(contents);
|
618 | model.dirty = false;
|
619 | if (!this._isPopulated) {
|
620 | return this._populate();
|
621 | }
|
622 | })
|
623 | .catch(async (err) => {
|
624 | const localPath = this._manager.contents.localPath(this._path);
|
625 | const name = PathExt.basename(localPath);
|
626 | void this._handleError(err, this._trans.__('File Load Error for %1', name));
|
627 | throw err;
|
628 | });
|
629 | }
|
630 | |
631 |
|
632 |
|
633 | _maybeSave(options) {
|
634 | const path = this._path;
|
635 |
|
636 | const promise = this._manager.contents.get(path, { content: false });
|
637 | return promise.then(model => {
|
638 | var _a;
|
639 | if (this.isDisposed) {
|
640 | return Promise.reject(new Error('Disposed'));
|
641 | }
|
642 |
|
643 |
|
644 |
|
645 | const lastModifiedCheckMargin = this._lastModifiedCheckMargin;
|
646 | const ycontextModified = this._ycontext.get('last_modified');
|
647 |
|
648 | const modified = ycontextModified || ((_a = this.contentsModel) === null || _a === void 0 ? void 0 : _a.last_modified);
|
649 | const tClient = modified ? new Date(modified) : new Date();
|
650 | const tDisk = new Date(model.last_modified);
|
651 | if (modified &&
|
652 | tDisk.getTime() - tClient.getTime() > lastModifiedCheckMargin) {
|
653 | return this._timeConflict(tClient, model, options);
|
654 | }
|
655 | return this._manager.contents.save(path, options);
|
656 | }, err => {
|
657 | if (err.response && err.response.status === 404) {
|
658 | return this._manager.contents.save(path, options);
|
659 | }
|
660 | throw err;
|
661 | });
|
662 | }
|
663 | |
664 |
|
665 |
|
666 | async _handleError(err, title) {
|
667 | await showErrorMessage(title, err);
|
668 | return;
|
669 | }
|
670 | |
671 |
|
672 |
|
673 | _maybeCheckpoint(force) {
|
674 | let writable = this._contentsModel && this._contentsModel.writable;
|
675 | let promise = Promise.resolve(void 0);
|
676 | if (!writable) {
|
677 | return promise;
|
678 | }
|
679 | if (force) {
|
680 | promise = this.createCheckpoint().then( );
|
681 | }
|
682 | else {
|
683 | promise = this.listCheckpoints().then(checkpoints => {
|
684 | writable = this._contentsModel && this._contentsModel.writable;
|
685 | if (!this.isDisposed && !checkpoints.length && writable) {
|
686 | return this.createCheckpoint().then( );
|
687 | }
|
688 | });
|
689 | }
|
690 | return promise.catch(err => {
|
691 |
|
692 | if (!err.response || err.response.status !== 403) {
|
693 | throw err;
|
694 | }
|
695 | });
|
696 | }
|
697 | |
698 |
|
699 |
|
700 | _timeConflict(tClient, model, options) {
|
701 | const tDisk = new Date(model.last_modified);
|
702 | console.warn(`Last saving performed ${tClient} ` +
|
703 | `while the current file seems to have been saved ` +
|
704 | `${tDisk}`);
|
705 | if (this._timeConflictModalIsOpen) {
|
706 | return Promise.reject(new Error('Modal is already displayed'));
|
707 | }
|
708 | const body = this._trans.__(`"%1" has changed on disk since the last time it was opened or saved.
|
709 | Do you want to overwrite the file on disk with the version open here,
|
710 | or load the version on disk (revert)?`, this.path);
|
711 | const revertBtn = Dialog.okButton({ label: this._trans.__('Revert') });
|
712 | const overwriteBtn = Dialog.warnButton({
|
713 | label: this._trans.__('Overwrite')
|
714 | });
|
715 | this._timeConflictModalIsOpen = true;
|
716 | return showDialog({
|
717 | title: this._trans.__('File Changed'),
|
718 | body,
|
719 | buttons: [Dialog.cancelButton(), revertBtn, overwriteBtn]
|
720 | }).then(result => {
|
721 | this._timeConflictModalIsOpen = false;
|
722 | if (this.isDisposed) {
|
723 | return Promise.reject(new Error('Disposed'));
|
724 | }
|
725 | if (result.button.label === this._trans.__('Overwrite')) {
|
726 | return this._manager.contents.save(this._path, options);
|
727 | }
|
728 |
|
729 | if (result.button.label === this._trans.__('Revert')) {
|
730 | return this.revert().then(() => {
|
731 | return model;
|
732 | });
|
733 | }
|
734 | return Promise.reject(new Error('Cancel'));
|
735 | });
|
736 | }
|
737 | |
738 |
|
739 |
|
740 | _maybeOverWrite(path) {
|
741 | const body = this._trans.__('"%1" already exists. Do you want to replace it?', path);
|
742 | const overwriteBtn = Dialog.warnButton({
|
743 | label: this._trans.__('Overwrite')
|
744 | });
|
745 | return showDialog({
|
746 | title: this._trans.__('File Overwrite?'),
|
747 | body,
|
748 | buttons: [Dialog.cancelButton(), overwriteBtn]
|
749 | }).then(result => {
|
750 | if (this.isDisposed) {
|
751 | return Promise.reject(new Error('Disposed'));
|
752 | }
|
753 |
|
754 | if (result.button.label === this._trans.__('Overwrite')) {
|
755 | return this._manager.contents.delete(path).then(() => {
|
756 | return this._finishSaveAs(path);
|
757 | });
|
758 | }
|
759 | });
|
760 | }
|
761 | |
762 |
|
763 |
|
764 | async _finishSaveAs(newPath) {
|
765 | var _a, _b;
|
766 | this._path = newPath;
|
767 | await ((_a = this.sessionContext.session) === null || _a === void 0 ? void 0 : _a.setPath(newPath));
|
768 | await ((_b = this.sessionContext.session) === null || _b === void 0 ? void 0 : _b.setName(newPath.split('/').pop()));
|
769 | await this.save();
|
770 | this._ycontext.set('path', this._path);
|
771 | await this._maybeCheckpoint(true);
|
772 | }
|
773 | }
|
774 |
|
775 |
|
776 |
|
777 | var Private;
|
778 | (function (Private) {
|
779 | |
780 |
|
781 |
|
782 | function getSavePath(path, translator) {
|
783 | translator = translator || nullTranslator;
|
784 | const trans = translator.load('jupyterlab');
|
785 | const saveBtn = Dialog.okButton({ label: trans.__('Save') });
|
786 | return showDialog({
|
787 | title: trans.__('Save File As..'),
|
788 | body: new SaveWidget(path),
|
789 | buttons: [Dialog.cancelButton(), saveBtn]
|
790 | }).then(result => {
|
791 | var _a;
|
792 |
|
793 | if (result.button.label === trans.__('Save')) {
|
794 | return (_a = result.value) !== null && _a !== void 0 ? _a : undefined;
|
795 | }
|
796 | return;
|
797 | });
|
798 | }
|
799 | Private.getSavePath = getSavePath;
|
800 | |
801 |
|
802 |
|
803 | function noOp() {
|
804 |
|
805 | }
|
806 | Private.noOp = noOp;
|
807 | |
808 |
|
809 |
|
810 | class SaveWidget extends Widget {
|
811 | |
812 |
|
813 |
|
814 | constructor(path) {
|
815 | super({ node: createSaveNode(path) });
|
816 | }
|
817 | |
818 |
|
819 |
|
820 | getValue() {
|
821 | return this.node.value;
|
822 | }
|
823 | }
|
824 | |
825 |
|
826 |
|
827 | function createSaveNode(path) {
|
828 | const input = document.createElement('input');
|
829 | input.value = path;
|
830 | return input;
|
831 | }
|
832 | })(Private || (Private = {}));
|
833 |
|
\ | No newline at end of file |