1 |
|
2 |
|
3 |
|
4 | import { ISessionContext, SessionContextDialogs } from '@jupyterlab/apputils';
|
5 | import { IChangedArgs, PathExt } from '@jupyterlab/coreutils';
|
6 | import {
|
7 | Context,
|
8 | DocumentRegistry,
|
9 | IDocumentWidget
|
10 | } from '@jupyterlab/docregistry';
|
11 | import { Contents, Kernel, ServiceManager } from '@jupyterlab/services';
|
12 | import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
13 | import { ArrayExt, find } from '@lumino/algorithm';
|
14 | import { UUID } from '@lumino/coreutils';
|
15 | import { IDisposable } from '@lumino/disposable';
|
16 | import { AttachedProperty } from '@lumino/properties';
|
17 | import { ISignal, Signal } from '@lumino/signaling';
|
18 | import { Widget } from '@lumino/widgets';
|
19 | import { SaveHandler } from './savehandler';
|
20 | import {
|
21 | IDocumentManager,
|
22 | IDocumentWidgetOpener,
|
23 | IRecentsManager
|
24 | } from './tokens';
|
25 | import { DocumentWidgetManager } from './widgetmanager';
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | export class DocumentManager implements IDocumentManager {
|
38 | |
39 |
|
40 |
|
41 | constructor(options: DocumentManager.IOptions) {
|
42 | this.translator = options.translator || nullTranslator;
|
43 | this.registry = options.registry;
|
44 | this.services = options.manager;
|
45 | this._dialogs =
|
46 | options.sessionDialogs ??
|
47 | new SessionContextDialogs({ translator: options.translator });
|
48 | this._isConnectedCallback = options.isConnectedCallback || (() => true);
|
49 |
|
50 | this._opener = options.opener;
|
51 | this._when = options.when || options.manager.ready;
|
52 |
|
53 | const widgetManager = new DocumentWidgetManager({
|
54 | registry: this.registry,
|
55 | translator: this.translator,
|
56 | recentsManager: options.recentsManager
|
57 | });
|
58 | widgetManager.activateRequested.connect(this._onActivateRequested, this);
|
59 | widgetManager.stateChanged.connect(this._onWidgetStateChanged, this);
|
60 | this._widgetManager = widgetManager;
|
61 | this._setBusy = options.setBusy;
|
62 | }
|
63 |
|
64 | /**
|
65 | * The registry used by the manager.
|
66 | */
|
67 | readonly registry: DocumentRegistry;
|
68 |
|
69 | /**
|
70 | * The service manager used by the manager.
|
71 | */
|
72 | readonly services: ServiceManager.IManager;
|
73 |
|
74 | /**
|
75 | * A signal emitted when one of the documents is activated.
|
76 | */
|
77 | get activateRequested(): ISignal<this, string> {
|
78 | return this._activateRequested;
|
79 | }
|
80 |
|
81 | /**
|
82 | * Whether to autosave documents.
|
83 | */
|
84 | get autosave(): boolean {
|
85 | return this._autosave;
|
86 | }
|
87 |
|
88 | set autosave(value: boolean) {
|
89 | if (this._autosave !== value) {
|
90 | const oldValue = this._autosave;
|
91 | this._autosave = value;
|
92 |
|
93 | // For each existing context, start/stop the autosave handler as needed.
|
94 | this._contexts.forEach(context => {
|
95 | const handler = Private.saveHandlerProperty.get(context);
|
96 | if (!handler) {
|
97 | return;
|
98 | }
|
99 | if (value === true && !handler.isActive) {
|
100 | handler.start();
|
101 | } else if (value === false && handler.isActive) {
|
102 | handler.stop();
|
103 | }
|
104 | });
|
105 | this._stateChanged.emit({
|
106 | name: 'autosave',
|
107 | oldValue,
|
108 | newValue: value
|
109 | });
|
110 | }
|
111 | }
|
112 |
|
113 | /**
|
114 | * Determines the time interval for autosave in seconds.
|
115 | */
|
116 | get autosaveInterval(): number {
|
117 | return this._autosaveInterval;
|
118 | }
|
119 |
|
120 | set autosaveInterval(value: number) {
|
121 | if (this._autosaveInterval !== value) {
|
122 | const oldValue = this._autosaveInterval;
|
123 | this._autosaveInterval = value;
|
124 |
|
125 | // For each existing context, set the save interval as needed.
|
126 | this._contexts.forEach(context => {
|
127 | const handler = Private.saveHandlerProperty.get(context);
|
128 | if (!handler) {
|
129 | return;
|
130 | }
|
131 | handler.saveInterval = value || 120;
|
132 | });
|
133 | this._stateChanged.emit({
|
134 | name: 'autosaveInterval',
|
135 | oldValue,
|
136 | newValue: value
|
137 | });
|
138 | }
|
139 | }
|
140 |
|
141 | /**
|
142 | * Whether to ask confirmation to close a tab or not.
|
143 | */
|
144 | get confirmClosingDocument(): boolean {
|
145 | return this._widgetManager.confirmClosingDocument;
|
146 | }
|
147 | set confirmClosingDocument(value: boolean) {
|
148 | if (this._widgetManager.confirmClosingDocument !== value) {
|
149 | const oldValue = this._widgetManager.confirmClosingDocument;
|
150 | this._widgetManager.confirmClosingDocument = value;
|
151 | this._stateChanged.emit({
|
152 | name: 'confirmClosingDocument',
|
153 | oldValue,
|
154 | newValue: value
|
155 | });
|
156 | }
|
157 | }
|
158 |
|
159 | /**
|
160 | * Defines max acceptable difference, in milliseconds, between last modified timestamps on disk and client
|
161 | */
|
162 | get lastModifiedCheckMargin(): number {
|
163 | return this._lastModifiedCheckMargin;
|
164 | }
|
165 |
|
166 | set lastModifiedCheckMargin(value: number) {
|
167 | if (this._lastModifiedCheckMargin !== value) {
|
168 | const oldValue = this._lastModifiedCheckMargin;
|
169 | this._lastModifiedCheckMargin = value;
|
170 |
|
171 | // For each existing context, update the margin value.
|
172 | this._contexts.forEach(context => {
|
173 | context.lastModifiedCheckMargin = value;
|
174 | });
|
175 | this._stateChanged.emit({
|
176 | name: 'lastModifiedCheckMargin',
|
177 | oldValue,
|
178 | newValue: value
|
179 | });
|
180 | }
|
181 | }
|
182 |
|
183 | /**
|
184 | * Whether to ask the user to rename untitled file on first manual save.
|
185 | */
|
186 | get renameUntitledFileOnSave(): boolean {
|
187 | return this._renameUntitledFileOnSave;
|
188 | }
|
189 |
|
190 | set renameUntitledFileOnSave(value: boolean) {
|
191 | if (this._renameUntitledFileOnSave !== value) {
|
192 | const oldValue = this._renameUntitledFileOnSave;
|
193 | this._renameUntitledFileOnSave = value;
|
194 | this._stateChanged.emit({
|
195 | name: 'renameUntitledFileOnSave',
|
196 | oldValue,
|
197 | newValue: value
|
198 | });
|
199 | }
|
200 | }
|
201 |
|
202 | /**
|
203 | * Signal triggered when an attribute changes.
|
204 | */
|
205 | get stateChanged(): ISignal<IDocumentManager, IChangedArgs<any>> {
|
206 | return this._stateChanged;
|
207 | }
|
208 |
|
209 | /**
|
210 | * Get whether the document manager has been disposed.
|
211 | */
|
212 | get isDisposed(): boolean {
|
213 | return this._isDisposed;
|
214 | }
|
215 |
|
216 | /**
|
217 | * Dispose of the resources held by the document manager.
|
218 | */
|
219 | dispose(): void {
|
220 | if (this.isDisposed) {
|
221 | return;
|
222 | }
|
223 | this._isDisposed = true;
|
224 |
|
225 | // Clear any listeners for our signals.
|
226 | Signal.clearData(this);
|
227 |
|
228 | // Close all the widgets for our contexts and dispose the widget manager.
|
229 | this._contexts.forEach(context => {
|
230 | return this._widgetManager.closeWidgets(context);
|
231 | });
|
232 | this._widgetManager.dispose();
|
233 |
|
234 | // Clear the context list.
|
235 | this._contexts.length = 0;
|
236 | }
|
237 |
|
238 | /**
|
239 | * Clone a widget.
|
240 | *
|
241 | * @param widget - The source widget.
|
242 | *
|
243 | * @returns A new widget or `undefined`.
|
244 | *
|
245 | * #### Notes
|
246 | * Uses the same widget factory and context as the source, or returns
|
247 | * `undefined` if the source widget is not managed by this manager.
|
248 | */
|
249 | cloneWidget(widget: Widget): IDocumentWidget | undefined {
|
250 | return this._widgetManager.cloneWidget(widget);
|
251 | }
|
252 |
|
253 | /**
|
254 | * Close all of the open documents.
|
255 | *
|
256 | * @returns A promise resolving when the widgets are closed.
|
257 | */
|
258 | closeAll(): Promise<void> {
|
259 | return Promise.all(
|
260 | this._contexts.map(context => this._widgetManager.closeWidgets(context))
|
261 | ).then(() => undefined);
|
262 | }
|
263 |
|
264 | /**
|
265 | * Close the widgets associated with a given path.
|
266 | *
|
267 | * @param path - The target path.
|
268 | *
|
269 | * @returns A promise resolving when the widgets are closed.
|
270 | */
|
271 | closeFile(path: string): Promise<void> {
|
272 | const close = this._contextsForPath(path).map(c =>
|
273 | this._widgetManager.closeWidgets(c)
|
274 | );
|
275 | return Promise.all(close).then(x => undefined);
|
276 | }
|
277 |
|
278 | /**
|
279 | * Get the document context for a widget.
|
280 | *
|
281 | * @param widget - The widget of interest.
|
282 | *
|
283 | * @returns The context associated with the widget, or `undefined` if no such
|
284 | * context exists.
|
285 | */
|
286 | contextForWidget(widget: Widget): DocumentRegistry.Context | undefined {
|
287 | return this._widgetManager.contextForWidget(widget);
|
288 | }
|
289 |
|
290 | /**
|
291 | * Copy a file.
|
292 | *
|
293 | * @param fromFile - The full path of the original file.
|
294 | *
|
295 | * @param toDir - The full path to the target directory.
|
296 | *
|
297 | * @returns A promise which resolves to the contents of the file.
|
298 | */
|
299 | copy(fromFile: string, toDir: string): Promise<Contents.IModel> {
|
300 | return this.services.contents.copy(fromFile, toDir);
|
301 | }
|
302 |
|
303 | /**
|
304 | * Create a new file and return the widget used to view it.
|
305 | *
|
306 | * @param path - The file path to create.
|
307 | *
|
308 | * @param widgetName - The name of the widget factory to use. 'default' will use the default widget.
|
309 | *
|
310 | * @param kernel - An optional kernel name/id to override the default.
|
311 | *
|
312 | * @returns The created widget, or `undefined`.
|
313 | *
|
314 | * #### Notes
|
315 | * This function will return `undefined` if a valid widget factory
|
316 | * cannot be found.
|
317 | */
|
318 | createNew(
|
319 | path: string,
|
320 | widgetName = 'default',
|
321 | kernel?: Partial<Kernel.IModel>
|
322 | ): Widget | undefined {
|
323 | return this._createOrOpenDocument('create', path, widgetName, kernel);
|
324 | }
|
325 |
|
326 | /**
|
327 | * Delete a file.
|
328 | *
|
329 | * @param path - The full path to the file to be deleted.
|
330 | *
|
331 | * @returns A promise which resolves when the file is deleted.
|
332 | *
|
333 | * #### Notes
|
334 | * If there is a running session associated with the file and no other
|
335 | * sessions are using the kernel, the session will be shut down.
|
336 | */
|
337 | deleteFile(path: string): Promise<void> {
|
338 | return this.services.sessions
|
339 | .stopIfNeeded(path)
|
340 | .then(() => {
|
341 | return this.services.contents.delete(path);
|
342 | })
|
343 | .then(() => {
|
344 | this._contextsForPath(path).forEach(context =>
|
345 | this._widgetManager.deleteWidgets(context)
|
346 | );
|
347 | return Promise.resolve(void 0);
|
348 | });
|
349 | }
|
350 |
|
351 | /**
|
352 | * Duplicate a file.
|
353 | *
|
354 | * @param path - The full path to the file to be duplicated.
|
355 | *
|
356 | * @returns A promise which resolves when the file is duplicated.
|
357 | */
|
358 | duplicate(path: string): Promise<Contents.IModel> {
|
359 | const basePath = PathExt.dirname(path);
|
360 | return this.services.contents.copy(path, basePath);
|
361 | }
|
362 |
|
363 | /**
|
364 | * See if a widget already exists for the given path and widget name.
|
365 | *
|
366 | * @param path - The file path to use.
|
367 | *
|
368 | * @param widgetName - The name of the widget factory to use. 'default' will use the default widget.
|
369 | *
|
370 | * @returns The found widget, or `undefined`.
|
371 | *
|
372 | * #### Notes
|
373 | * This can be used to find an existing widget instead of opening
|
374 | * a new widget.
|
375 | */
|
376 | findWidget(
|
377 | path: string,
|
378 | widgetName: string | null = 'default'
|
379 | ): IDocumentWidget | undefined {
|
380 | const newPath = PathExt.normalize(path);
|
381 | let widgetNames = [widgetName];
|
382 | if (widgetName === 'default') {
|
383 | const factory = this.registry.defaultWidgetFactory(newPath);
|
384 | if (!factory) {
|
385 | return undefined;
|
386 | }
|
387 | widgetNames = [factory.name];
|
388 | } else if (widgetName === null) {
|
389 | widgetNames = this.registry
|
390 | .preferredWidgetFactories(newPath)
|
391 | .map(f => f.name);
|
392 | }
|
393 |
|
394 | for (const context of this._contextsForPath(newPath)) {
|
395 | for (const widgetName of widgetNames) {
|
396 | if (widgetName !== null) {
|
397 | const widget = this._widgetManager.findWidget(context, widgetName);
|
398 | if (widget) {
|
399 | return widget;
|
400 | }
|
401 | }
|
402 | }
|
403 | }
|
404 | return undefined;
|
405 | }
|
406 |
|
407 | /**
|
408 | * Create a new untitled file.
|
409 | *
|
410 | * @param options - The file content creation options.
|
411 | */
|
412 | newUntitled(options: Contents.ICreateOptions): Promise<Contents.IModel> {
|
413 | if (options.type === 'file') {
|
414 | options.ext = options.ext || '.txt';
|
415 | }
|
416 | return this.services.contents.newUntitled(options);
|
417 | }
|
418 |
|
419 | /**
|
420 | * Open a file and return the widget used to view it.
|
421 | *
|
422 | * @param path - The file path to open.
|
423 | *
|
424 | * @param widgetName - The name of the widget factory to use. 'default' will use the default widget.
|
425 | *
|
426 | * @param kernel - An optional kernel name/id to override the default.
|
427 | *
|
428 | * @returns The created widget, or `undefined`.
|
429 | *
|
430 | * #### Notes
|
431 | * This function will return `undefined` if a valid widget factory
|
432 | * cannot be found.
|
433 | */
|
434 | open(
|
435 | path: string,
|
436 | widgetName = 'default',
|
437 | kernel?: Partial<Kernel.IModel>,
|
438 | options?: DocumentRegistry.IOpenOptions
|
439 | ): IDocumentWidget | undefined {
|
440 | return this._createOrOpenDocument(
|
441 | 'open',
|
442 | path,
|
443 | widgetName,
|
444 | kernel,
|
445 | options
|
446 | );
|
447 | }
|
448 |
|
449 | /**
|
450 | * Open a file and return the widget used to view it.
|
451 | * Reveals an already existing editor.
|
452 | *
|
453 | * @param path - The file path to open.
|
454 | *
|
455 | * @param widgetName - The name of the widget factory to use. 'default' will use the default widget.
|
456 | *
|
457 | * @param kernel - An optional kernel name/id to override the default.
|
458 | *
|
459 | * @returns The created widget, or `undefined`.
|
460 | *
|
461 | * #### Notes
|
462 | * This function will return `undefined` if a valid widget factory
|
463 | * cannot be found.
|
464 | */
|
465 | openOrReveal(
|
466 | path: string,
|
467 | widgetName = 'default',
|
468 | kernel?: Partial<Kernel.IModel>,
|
469 | options?: DocumentRegistry.IOpenOptions
|
470 | ): IDocumentWidget | undefined {
|
471 | const widget = this.findWidget(path, widgetName);
|
472 | if (widget) {
|
473 | this._opener.open(widget, {
|
474 | type: widgetName,
|
475 | ...options
|
476 | });
|
477 | return widget;
|
478 | }
|
479 | return this.open(path, widgetName, kernel, options ?? {});
|
480 | }
|
481 |
|
482 | /**
|
483 | * Overwrite a file.
|
484 | *
|
485 | * @param oldPath - The full path to the original file.
|
486 | *
|
487 | * @param newPath - The full path to the new file.
|
488 | *
|
489 | * @returns A promise containing the new file contents model.
|
490 | */
|
491 | overwrite(oldPath: string, newPath: string): Promise<Contents.IModel> {
|
492 | // Cleanly overwrite the file by moving it, making sure the original does
|
493 | // not exist, and then renaming to the new path.
|
494 | const tempPath = `${newPath}.${UUID.uuid4()}`;
|
495 | const cb = () => this.rename(tempPath, newPath);
|
496 | return this.rename(oldPath, tempPath)
|
497 | .then(() => {
|
498 | return this.deleteFile(newPath);
|
499 | })
|
500 | .then(cb, cb);
|
501 | }
|
502 |
|
503 | |
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 |
|
514 | rename(oldPath: string, newPath: string): Promise<Contents.IModel> {
|
515 | return this.services.contents.rename(oldPath, newPath);
|
516 | }
|
517 |
|
518 | |
519 |
|
520 |
|
521 | private _findContext(
|
522 | path: string,
|
523 | factoryName: string
|
524 | ): Private.IContext | undefined {
|
525 | const normalizedPath = this.services.contents.normalize(path);
|
526 | return find(this._contexts, context => {
|
527 | return (
|
528 | context.path === normalizedPath && context.factoryName === factoryName
|
529 | );
|
530 | });
|
531 | }
|
532 |
|
533 | |
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 | private _contextsForPath(path: string): Private.IContext[] {
|
542 | const normalizedPath = this.services.contents.normalize(path);
|
543 | return this._contexts.filter(context => context.path === normalizedPath);
|
544 | }
|
545 |
|
546 | |
547 |
|
548 |
|
549 | private _createContext(
|
550 | path: string,
|
551 | factory: DocumentRegistry.ModelFactory,
|
552 | kernelPreference?: ISessionContext.IKernelPreference
|
553 | ): Private.IContext {
|
554 |
|
555 |
|
556 |
|
557 |
|
558 |
|
559 |
|
560 |
|
561 |
|
562 | const adopter = (
|
563 | widget: IDocumentWidget,
|
564 | options?: DocumentRegistry.IOpenOptions
|
565 | ) => {
|
566 | this._widgetManager.adoptWidget(context, widget);
|
567 |
|
568 | this._opener.open(widget, options);
|
569 | };
|
570 | const context = new Context({
|
571 | opener: adopter,
|
572 | manager: this.services,
|
573 | factory,
|
574 | path,
|
575 | kernelPreference,
|
576 | setBusy: this._setBusy,
|
577 | sessionDialogs: this._dialogs,
|
578 | lastModifiedCheckMargin: this._lastModifiedCheckMargin,
|
579 | translator: this.translator
|
580 | });
|
581 | const handler = new SaveHandler({
|
582 | context,
|
583 | isConnectedCallback: this._isConnectedCallback,
|
584 | saveInterval: this.autosaveInterval
|
585 | });
|
586 | Private.saveHandlerProperty.set(context, handler);
|
587 | void context.ready.then(() => {
|
588 | if (this.autosave) {
|
589 | handler.start();
|
590 | }
|
591 | });
|
592 | context.disposed.connect(this._onContextDisposed, this);
|
593 | this._contexts.push(context);
|
594 | return context;
|
595 | }
|
596 |
|
597 | |
598 |
|
599 |
|
600 | private _onContextDisposed(context: Private.IContext): void {
|
601 | ArrayExt.removeFirstOf(this._contexts, context);
|
602 | }
|
603 |
|
604 | |
605 |
|
606 |
|
607 | private _widgetFactoryFor(
|
608 | path: string,
|
609 | widgetName: string
|
610 | ): DocumentRegistry.WidgetFactory | undefined {
|
611 | const { registry } = this;
|
612 | if (widgetName === 'default') {
|
613 | const factory = registry.defaultWidgetFactory(path);
|
614 | if (!factory) {
|
615 | return undefined;
|
616 | }
|
617 | widgetName = factory.name;
|
618 | }
|
619 | return registry.getWidgetFactory(widgetName);
|
620 | }
|
621 |
|
622 | |
623 |
|
624 |
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 | private _createOrOpenDocument(
|
631 | which: 'open' | 'create',
|
632 | path: string,
|
633 | widgetName = 'default',
|
634 | kernel?: Partial<Kernel.IModel>,
|
635 | options?: DocumentRegistry.IOpenOptions
|
636 | ): IDocumentWidget | undefined {
|
637 | const widgetFactory = this._widgetFactoryFor(path, widgetName);
|
638 | if (!widgetFactory) {
|
639 | return undefined;
|
640 | }
|
641 | const modelName = widgetFactory.modelName || 'text';
|
642 | const factory = this.registry.getModelFactory(modelName);
|
643 | if (!factory) {
|
644 | return undefined;
|
645 | }
|
646 |
|
647 |
|
648 | const preference = this.registry.getKernelPreference(
|
649 | path,
|
650 | widgetFactory.name,
|
651 | kernel
|
652 | );
|
653 |
|
654 | let context: Private.IContext | null;
|
655 | let ready: Promise<void> = Promise.resolve(undefined);
|
656 |
|
657 |
|
658 | if (which === 'open') {
|
659 |
|
660 | context = this._findContext(path, factory.name) || null;
|
661 | if (!context) {
|
662 | context = this._createContext(path, factory, preference);
|
663 |
|
664 |
|
665 | ready = this._when.then(() => context!.initialize(false));
|
666 | }
|
667 | } else if (which === 'create') {
|
668 | context = this._createContext(path, factory, preference);
|
669 |
|
670 | ready = this._when.then(() => context!.initialize(true));
|
671 | } else {
|
672 | throw new Error(`Invalid argument 'which': ${which}`);
|
673 | }
|
674 |
|
675 | const widget = this._widgetManager.createWidget(widgetFactory, context);
|
676 | this._opener.open(widget, { type: widgetFactory.name, ...options });
|
677 |
|
678 |
|
679 | ready.catch(err => {
|
680 | console.error(
|
681 | `Failed to initialize the context with '${factory.name}' for ${path}`,
|
682 | err
|
683 | );
|
684 | widget.close();
|
685 | });
|
686 |
|
687 | return widget;
|
688 | }
|
689 |
|
690 | |
691 |
|
692 |
|
693 | private _onActivateRequested(
|
694 | sender: DocumentWidgetManager,
|
695 | args: string
|
696 | ): void {
|
697 | this._activateRequested.emit(args);
|
698 | }
|
699 |
|
700 | protected _onWidgetStateChanged(
|
701 | sender: DocumentWidgetManager,
|
702 | args: IChangedArgs<any>
|
703 | ): void {
|
704 | if (args.name === 'confirmClosingDocument') {
|
705 | this._stateChanged.emit(args);
|
706 | }
|
707 | }
|
708 |
|
709 | protected translator: ITranslator;
|
710 | private _activateRequested = new Signal<this, string>(this);
|
711 | private _contexts: Private.IContext[] = [];
|
712 | private _opener: IDocumentWidgetOpener;
|
713 | private _widgetManager: DocumentWidgetManager;
|
714 | private _isDisposed = false;
|
715 | private _autosave = true;
|
716 | private _autosaveInterval = 120;
|
717 | private _lastModifiedCheckMargin = 500;
|
718 | private _renameUntitledFileOnSave = true;
|
719 | private _when: Promise<void>;
|
720 | private _setBusy: (() => IDisposable) | undefined;
|
721 | private _dialogs: ISessionContext.IDialogs;
|
722 | private _isConnectedCallback: () => boolean;
|
723 | private _stateChanged = new Signal<DocumentManager, IChangedArgs<any>>(this);
|
724 | }
|
725 |
|
726 |
|
727 |
|
728 |
|
729 | export namespace DocumentManager {
|
730 | |
731 |
|
732 |
|
733 | export interface IOptions {
|
734 | |
735 |
|
736 |
|
737 | registry: DocumentRegistry;
|
738 |
|
739 | |
740 |
|
741 |
|
742 | manager: ServiceManager.IManager;
|
743 |
|
744 | |
745 |
|
746 |
|
747 | opener: IDocumentWidgetOpener;
|
748 |
|
749 | |
750 |
|
751 |
|
752 | when?: Promise<void>;
|
753 |
|
754 | |
755 |
|
756 |
|
757 | setBusy?: () => IDisposable;
|
758 |
|
759 | |
760 |
|
761 |
|
762 | sessionDialogs?: ISessionContext.IDialogs;
|
763 |
|
764 | |
765 |
|
766 |
|
767 | translator?: ITranslator;
|
768 |
|
769 | |
770 |
|
771 |
|
772 |
|
773 | isConnectedCallback?: () => boolean;
|
774 |
|
775 | |
776 |
|
777 |
|
778 | recentsManager?: IRecentsManager;
|
779 | }
|
780 | }
|
781 |
|
782 |
|
783 |
|
784 |
|
785 | namespace Private {
|
786 | |
787 |
|
788 |
|
789 | export const saveHandlerProperty = new AttachedProperty<
|
790 | DocumentRegistry.Context,
|
791 | SaveHandler | undefined
|
792 | >({
|
793 | name: 'saveHandler',
|
794 | create: () => undefined
|
795 | });
|
796 |
|
797 | |
798 |
|
799 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 | export interface IContext extends Context<DocumentRegistry.IModel> {
|
805 |
|
806 | }
|
807 | }
|